Wordfence Security – Firewall & Malware Scan - Version 6.1.4

Version Description

Fix: Fixed potential bug with 'stored data not found after a fork. Got type: boolean'. Improvement: Added bulk actions and filters to WAF whitelist table. Improvement: Added a check while in learning mode to verify the response is not 404 before whitelising. Fix: Added index to attackLogTime. wfHits trimmed on runInstall now. Fix: Fixed attack data sync for hosts that cannot use wp-cron. Improvement: Use wftest@wordfence.com as the Diagnostics page default email address. Improvement: When WFWAF_ENABLED is set to false to disable the firewall, show this on the Firewall page. Fix: Prevent warnings when $_SERVER is empty. Fix: Bug fix for illegal string offset. Fix: Hooked up multibyte string functions to binary safe equivalents. Fix: Hooked up reverse IP lookup in Live Traffic. Fix: Add the user the web server (or PHP) is currently running as to Diagnostics page. Improvement: Pause Live Traffic after scrolling past the first entry. Improvement: Move "Permanently block all temporarily blocked IP addresses" button to top of blocked IP list. Fix: Added JSON fallback for PHP installations that don't have JSON enabled.

Download this release

Release Info

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

Code changes from version 6.1.3 to 6.1.4

css/main.css CHANGED
@@ -535,6 +535,8 @@ table.wf-table td {
535
  }
536
  table.wf-table thead th,
537
  table.wf-table thead td,
 
 
538
  table.wf-table tbody.thead th,
539
  table.wf-table tbody.thead td {
540
  background-color: #222;
@@ -543,13 +545,14 @@ table.wf-table tbody.thead td {
543
  border-color: #474747;
544
  text-align: left;
545
  }
546
- table.wf-table tbody tr td {
547
- background-color: #fff;
548
- }
549
  table.wf-table tbody tr.even td,
550
  table.wf-table tbody tr:nth-child(2n) td {
551
  background-color: #eee;
552
  }
 
 
 
 
553
  table.wf-table tbody tr:hover > td {
554
  background-color: #fffbd8;
555
  }
@@ -685,6 +688,9 @@ table.whitelist-table .edit-mode input.whitelist-edit {
685
  cursor: pointer;
686
  text-decoration: underline;
687
  }
 
 
 
688
  .wfActionBlocked {
689
  background-color: #fff6f6;
690
  }
@@ -977,7 +983,7 @@ table.whitelist-table .edit-mode input.whitelist-edit {
977
  border-color: #e5ae35;
978
  }
979
  /*.wafStatus-disabled,*/
980
- .wafStatus-disabled.select2-container--default .select2-selection--single {
981
  background-color: #ff6d69;
982
  border-color: #dd422c;
983
  }
@@ -1012,3 +1018,17 @@ pre.wf-pre {
1012
  border: 1px solid #999999;
1013
  overflow: auto;
1014
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535
  }
536
  table.wf-table thead th,
537
  table.wf-table thead td,
538
+ table.wf-table tfoot th,
539
+ table.wf-table tfoot td,
540
  table.wf-table tbody.thead th,
541
  table.wf-table tbody.thead td {
542
  background-color: #222;
545
  border-color: #474747;
546
  text-align: left;
547
  }
 
 
 
548
  table.wf-table tbody tr.even td,
549
  table.wf-table tbody tr:nth-child(2n) td {
550
  background-color: #eee;
551
  }
552
+ table.wf-table tbody tr td,
553
+ table.wf-table tbody tr.odd td {
554
+ background-color: #fff;
555
+ }
556
  table.wf-table tbody tr:hover > td {
557
  background-color: #fffbd8;
558
  }
688
  cursor: pointer;
689
  text-decoration: underline;
690
  }
691
+ #wf-lt-listings a.button {
692
+ text-decoration: none;
693
+ }
694
  .wfActionBlocked {
695
  background-color: #fff6f6;
696
  }
983
  border-color: #e5ae35;
984
  }
985
  /*.wafStatus-disabled,*/
986
+ .wafStatus-disabled.select2-container--default .select2-selection--single, .wafStatus-disabled.select2-container--default.select2-container--disabled .select2-selection--single, .wafStatus-learning-mode.select2-container--default.select2-container--disabled .select2-selection--single, .wafStatus-enabled.select2-container--default.select2-container--disabled .select2-selection--single {
987
  background-color: #ff6d69;
988
  border-color: #dd422c;
989
  }
1018
  border: 1px solid #999999;
1019
  overflow: auto;
1020
  }
1021
+
1022
+
1023
+ /*
1024
+ Whitelist table
1025
+ */
1026
+ .wf-bulk-action {
1027
+ margin: 12px 0;
1028
+ }
1029
+ tr.wf-table-filters {
1030
+
1031
+ }
1032
+ tr.wf-table-filters input {
1033
+ max-width: 140px;
1034
+ }
js/admin.js CHANGED
@@ -65,7 +65,12 @@
65
  });
66
 
67
  $('#doSendEmail').click(function() {
68
- WFAD.ajax('wordfence_sendDiagnostic', {email: $('#_email').val()}, function(res) {
 
 
 
 
 
69
  if (res.result) {
70
  self.colorbox('400px', "Email Diagnostic Report", "Diagnostic report has been sent successfully.");
71
  } else {
@@ -699,10 +704,10 @@
699
  var self = this;
700
  var ips = [];
701
  jQuery('.wfReverseLookup').each(function(idx, elem) {
702
- var txt = jQuery(elem).text();
703
  if (/^\d+\.\d+\.\d+\.\d+$/.test(txt) && (!jQuery(elem).data('wfReverseDone'))) {
704
  jQuery(elem).data('wfReverseDone', true);
705
- ips.push(jQuery(elem).text());
706
  }
707
  });
708
  if (ips.length < 1) {
@@ -722,7 +727,7 @@
722
  function(res) {
723
  if (res.ok) {
724
  jQuery('.wfReverseLookup').each(function(idx, elem) {
725
- var txt = jQuery(elem).text();
726
  for (var ip in res.ips) {
727
  if (txt == ip) {
728
  if (res.ips[ip]) {
@@ -2401,7 +2406,8 @@
2401
  whitelistedURLParams: []
2402
  },
2403
 
2404
- wafConfigSave: function(action, data, onSuccess) {
 
2405
  var self = this;
2406
  if (typeof(data) == 'string') {
2407
  if (data.length > 0) {
@@ -2420,8 +2426,10 @@
2420
 
2421
  this.ajax('wordfence_saveWAFConfig', data, function(res) {
2422
  if (typeof res === 'object' && res.success) {
2423
- self.colorbox('400px', 'Firewall Configuration', 'The Wordfence Web Application Firewall ' +
2424
- 'configuration was saved successfully.');
 
 
2425
  self.wafData = res.data;
2426
  self.wafConfigPageRender();
2427
  if (typeof onSuccess === 'function') {
@@ -2466,6 +2474,7 @@
2466
  var date = new Date(this.wafData['rulesLastUpdated'] * 1000);
2467
  this.renderWAFRulesLastUpdated(date);
2468
  }
 
2469
  },
2470
 
2471
  renderWAFRulesLastUpdated: function(date) {
65
  });
66
 
67
  $('#doSendEmail').click(function() {
68
+ var ticket = $('#_ticketnumber').val();
69
+ if (ticket === null || typeof ticket === "undefined" || ticket.length == 0) {
70
+ self.colorbox('400px', "Error", "Please include your support ticket number or forum username.");
71
+ return;
72
+ }
73
+ WFAD.ajax('wordfence_sendDiagnostic', {email: $('#_email').val(), ticket: ticket}, function(res) {
74
  if (res.result) {
75
  self.colorbox('400px', "Email Diagnostic Report", "Diagnostic report has been sent successfully.");
76
  } else {
704
  var self = this;
705
  var ips = [];
706
  jQuery('.wfReverseLookup').each(function(idx, elem) {
707
+ var txt = jQuery(elem).text().trim();
708
  if (/^\d+\.\d+\.\d+\.\d+$/.test(txt) && (!jQuery(elem).data('wfReverseDone'))) {
709
  jQuery(elem).data('wfReverseDone', true);
710
+ ips.push(txt);
711
  }
712
  });
713
  if (ips.length < 1) {
727
  function(res) {
728
  if (res.ok) {
729
  jQuery('.wfReverseLookup').each(function(idx, elem) {
730
+ var txt = jQuery(elem).text().trim();
731
  for (var ip in res.ips) {
732
  if (txt == ip) {
733
  if (res.ips[ip]) {
2406
  whitelistedURLParams: []
2407
  },
2408
 
2409
+ wafConfigSave: function(action, data, onSuccess, showColorBox) {
2410
+ showColorBox = showColorBox === undefined ? true : !!showColorBox;
2411
  var self = this;
2412
  if (typeof(data) == 'string') {
2413
  if (data.length > 0) {
2426
 
2427
  this.ajax('wordfence_saveWAFConfig', data, function(res) {
2428
  if (typeof res === 'object' && res.success) {
2429
+ if (showColorBox) {
2430
+ self.colorbox('400px', 'Firewall Configuration', 'The Wordfence Web Application Firewall ' +
2431
+ 'configuration was saved successfully.');
2432
+ }
2433
  self.wafData = res.data;
2434
  self.wafConfigPageRender();
2435
  if (typeof onSuccess === 'function') {
2474
  var date = new Date(this.wafData['rulesLastUpdated'] * 1000);
2475
  this.renderWAFRulesLastUpdated(date);
2476
  }
2477
+ $(window).trigger('wordfenceWAFConfigPageRender');
2478
  },
2479
 
2480
  renderWAFRulesLastUpdated: function(date) {
js/admin.liveTraffic.js CHANGED
@@ -648,6 +648,7 @@
648
 
649
  var legend = $('#wf-live-traffic-legend');
650
  var adminBar = $('#wpadminbar');
 
651
 
652
  var hasScrolled = false;
653
  var loadingListings = false;
@@ -659,6 +660,17 @@
659
  legend.removeClass('sticky');
660
  }
661
 
 
 
 
 
 
 
 
 
 
 
 
662
  // console.log(win.scrollTop() + window.innerHeight, liveTrafficWrapper.outerHeight() + liveTrafficWrapper.offset().top);
663
  var currentScrollBottom = win.scrollTop() + window.innerHeight;
664
  var scrollThreshold = liveTrafficWrapper.outerHeight() + liveTrafficWrapper.offset().top;
@@ -669,6 +681,7 @@
669
  hasScrolled = false;
670
  WFAD.wfLiveTraffic.loadNextListings(function() {
671
  loadingListings = false;
 
672
  });
673
  } else if (currentScrollBottom < scrollThreshold) {
674
  hasScrolled = true;
648
 
649
  var legend = $('#wf-live-traffic-legend');
650
  var adminBar = $('#wpadminbar');
651
+ var liveTrafficListings = $('#wf-lt-listings');
652
 
653
  var hasScrolled = false;
654
  var loadingListings = false;
660
  legend.removeClass('sticky');
661
  }
662
 
663
+ var firstRow = liveTrafficListings.children().first();
664
+ if (firstRow.offset().top + firstRow.height() < win.scrollTop() + adminBar.outerHeight() + 20) {
665
+ if (WFAD.mode != 'liveTraffic_paused') {
666
+ WFAD.mode = 'liveTraffic_paused';
667
+ }
668
+ } else {
669
+ if (WFAD.mode != 'liveTraffic') {
670
+ WFAD.mode = 'liveTraffic';
671
+ }
672
+ }
673
+
674
  // console.log(win.scrollTop() + window.innerHeight, liveTrafficWrapper.outerHeight() + liveTrafficWrapper.offset().top);
675
  var currentScrollBottom = win.scrollTop() + window.innerHeight;
676
  var scrollThreshold = liveTrafficWrapper.outerHeight() + liveTrafficWrapper.offset().top;
681
  hasScrolled = false;
682
  WFAD.wfLiveTraffic.loadNextListings(function() {
683
  loadingListings = false;
684
+ WFAD.reverseLookupIPs();
685
  });
686
  } else if (currentScrollBottom < scrollThreshold) {
687
  hasScrolled = true;
lib/menu_activity.php CHANGED
@@ -343,45 +343,44 @@
343
  <tr>
344
  <td>
345
  <span data-bind="if: blocked()">
346
- <button type="button" class="button button-small"
347
  data-bind="click: $root.unblockIP">
348
  Unblock this IP
349
- </button>
350
  </span>
351
  <span data-bind="if: rangeBlocked()">
352
- <button type="button" class="button button-small"
353
  data-bind="click: $root.unblockNetwork">Unblock this range
354
- </button>
355
  </span>
356
  <span data-bind="if: !blocked() && !rangeBlocked()">
357
- <button type="button" class="button button-small"
358
  data-bind="click: $root.blockIP">
359
  Block this IP
360
- </button>
361
  </span>
362
- <button type="button" class="button button-small"
363
- data-bind="click: function() { location = 'admin.php?page=WordfenceWhois&whoisval=' + IP() + '&wfnetworkblock=1' }">
364
  Block this network
365
- </button>
366
- <button type="button" class="button button-small" data-bind="text: 'Run WHOIS on ' + IP(),
367
- click: function() { window.open('admin.php?page=WordfenceWhois&whoisval=' + IP()) }"
368
- target="_blank"></button>
369
- <button type="button" class="button button-small"
370
- data-bind="click: function() { window.open(WFAD.makeIPTrafLink(IP())) }">
371
- See
372
- recent traffic
373
- </button>
374
  <span data-bind="if: action() == 'blocked:waf'">
375
- <button type="button" class="button button-small"
376
  data-bind="click: function () { $root.whitelistWAFParamKey(actionData().path, actionData().paramKey, actionData().failedRules) }"
377
  title="If this is a false positive, you can exclude this parameter from being filtered by the firewall">
378
  Whitelist param from Firewall
379
- </button>
380
  <?php if (WFWAF_DEBUG): ?>
381
- <button type="button" class="button button-small"
382
- data-bind="click: function() { window.open('<?php echo esc_js(home_url()) ?>?_wfsf=debugWAF&nonce=' + WFAD.nonce + '&hitid=' + id(), 'debugWAF');}">
383
  Debug this Request
384
- </button>
385
  <?php endif ?>
386
  </span>
387
  </td>
343
  <tr>
344
  <td>
345
  <span data-bind="if: blocked()">
346
+ <a href="#" class="button button-small"
347
  data-bind="click: $root.unblockIP">
348
  Unblock this IP
349
+ </a>
350
  </span>
351
  <span data-bind="if: rangeBlocked()">
352
+ <a href="#" class="button button-small"
353
  data-bind="click: $root.unblockNetwork">Unblock this range
354
+ </a>
355
  </span>
356
  <span data-bind="if: !blocked() && !rangeBlocked()">
357
+ <a href="#" class="button button-small"
358
  data-bind="click: $root.blockIP">
359
  Block this IP
360
+ </a>
361
  </span>
362
+ <a class="button button-small"
363
+ data-bind="attr: { href: 'admin.php?page=WordfenceWhois&whoisval=' + IP() + '&wfnetworkblock=1'}">
364
  Block this network
365
+ </a>
366
+ <a class="button button-small" data-bind="text: 'Run WHOIS on ' + IP(),
367
+ attr: { href: 'admin.php?page=WordfenceWhois&whoisval=' + IP() }"
368
+ target="_blank"></a>
369
+ <a class="button button-small"
370
+ data-bind="attr: { href: WFAD.makeIPTrafLink(IP()) }" target="_blank">
371
+ See recent traffic
372
+ </a>
 
373
  <span data-bind="if: action() == 'blocked:waf'">
374
+ <a href="#" class="button button-small"
375
  data-bind="click: function () { $root.whitelistWAFParamKey(actionData().path, actionData().paramKey, actionData().failedRules) }"
376
  title="If this is a false positive, you can exclude this parameter from being filtered by the firewall">
377
  Whitelist param from Firewall
378
+ </a>
379
  <?php if (WFWAF_DEBUG): ?>
380
+ <a href="#" class="button button-small"
381
+ data-bind="attr: { href: '<?php echo esc_js(home_url()) ?>?_wfsf=debugWAF&nonce=' + WFAD.nonce + '&hitid=' + id() }" target="_blank">
382
  Debug this Request
383
+ </a>
384
  <?php endif ?>
385
  </span>
386
  </td>
lib/menu_blockedIPs.php CHANGED
@@ -65,7 +65,8 @@
65
 
66
  <script type="text/x-jquery-template" id="wfLockedOutIPsTmpl">
67
  <div>
68
- <div style="border-bottom: 1px solid #CCC; padding-bottom: 10px; margin-bottom: 10px;">
 
69
  <table border="0" style="width: 100%">
70
  {{each(idx, elem) results}}
71
  <tr><td>
@@ -103,13 +104,13 @@
103
  {{/each}}
104
  </table>
105
  </div>
106
- <p><a class="button" href="#" onclick="WFAD.permanentlyBlockAllIPs('lockedOut'); return false;">Permanently block all locked out IP addresses</a></p>
107
  </div>
108
  </script>
109
 
110
  <script type="text/x-jquery-template" id="wfBlockedIPsTmpl">
111
  <div>
112
- <div style="border-bottom: 1px solid #CCC; padding-bottom: 10px; margin-bottom: 10px;">
 
113
  <table border="0" style="width: 100%">
114
  {{each(idx, elem) results}}
115
  <tr><td>
@@ -159,7 +160,6 @@
159
  {{/each}}
160
  </table>
161
  </div>
162
- <p><a class="button" href="#" onclick="WFAD.permanentlyBlockAllIPs('blocked'); return false;">Permanently block all temporarily blocked IP addresses</a></p>
163
  </div>
164
  </script>
165
 
65
 
66
  <script type="text/x-jquery-template" id="wfLockedOutIPsTmpl">
67
  <div>
68
+ <p><a class="button" href="#" onclick="WFAD.permanentlyBlockAllIPs('lockedOut'); return false;">Permanently block all locked out IP addresses</a></p>
69
+ <div style="border-top: 1px solid #CCC; padding-top: 10px; margin-top: 10px;">
70
  <table border="0" style="width: 100%">
71
  {{each(idx, elem) results}}
72
  <tr><td>
104
  {{/each}}
105
  </table>
106
  </div>
 
107
  </div>
108
  </script>
109
 
110
  <script type="text/x-jquery-template" id="wfBlockedIPsTmpl">
111
  <div>
112
+ <p><a class="button" href="#" onclick="WFAD.permanentlyBlockAllIPs('blocked'); return false;">Permanently block all temporarily blocked IP addresses</a></p>
113
+ <div style="border-top: 1px solid #CCC; padding-top: 10px; margin-top: 10px;">
114
  <table border="0" style="width: 100%">
115
  {{each(idx, elem) results}}
116
  <tr><td>
160
  {{/each}}
161
  </table>
162
  </div>
 
163
  </div>
164
  </script>
165
 
lib/menu_diagnostic.php CHANGED
@@ -293,8 +293,14 @@ $w = new wfConfig();
293
  <table class="wfConfigForm">
294
  <tr>
295
  <th>Email address:</th>
296
- <td><input type="email" id="_email" value="samples@wordfence.com"/></td>
297
- <td colspan="2"><input class="button" type="button" id="doSendEmail" value="Send"/></td>
 
 
 
 
 
 
298
  </tr>
299
  </table>
300
  </div>
293
  <table class="wfConfigForm">
294
  <tr>
295
  <th>Email address:</th>
296
+ <td><input type="email" id="_email" value="wftest@wordfence.com"/></td>
297
+ </tr>
298
+ <tr>
299
+ <th>Ticket Number/Forum Username:</th>
300
+ <td><input type="text" id="_ticketnumber" required/></td>
301
+ </tr>
302
+ <tr>
303
+ <td colspan="2" style="text-align: right;"><input class="button" type="button" id="doSendEmail" value="Send"/></td>
304
  </tr>
305
  </table>
306
  </div>
lib/menu_waf.php CHANGED
@@ -13,6 +13,33 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
13
  include('pageTitle.php');
14
  ?>
15
  <div class="wordfenceModeElem" id="wordfenceMode_waf"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  <?php if (!empty($storageExceptionMessage)): ?>
17
  <div style="font-weight: bold; margin: 20px 0px;;">
18
  <?php echo wp_kses($storageExceptionMessage, 'post') ?>
@@ -21,7 +48,8 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
21
  <?php echo $wafActionContent ?>
22
 
23
  <p class="wf-notice"><em>If you cannot complete the setup process,
24
- <a target="_blank" href="https://docs.wordfence.com/en/Web_Application_Firewall_Setup">click here for help</a>.</em></p>
 
25
 
26
  <?php else: ?>
27
 
@@ -37,7 +65,8 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
37
 
38
  <p>As new threats emerge, the Threat Defense Feed is updated to protect you from new attacks. The
39
  Premium version of the Threat Defense Feed is updated in real-time protecting you immediately. As a
40
- free user <strong>you are receiving the community version</strong> of the feed which is updated 30 days later.
 
41
  Upgrade now for less than $5 a month!</p>
42
 
43
  <p class="center"><a class="button button-primary"
@@ -55,7 +84,9 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
55
  <?php if (WFWAF_SUBDIRECTORY_INSTALL): ?>
56
  <div class="wf-notice">
57
  You are currently running the Wordfence Web Application Firewall from another WordPress installation.
58
- Please <a href="<?php echo network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend'); ?>">click here</a> to configure the Firewall to run correctly on this site.
 
 
59
  </div>
60
  <?php else: ?>
61
  <div class="wordfenceWrap" style="margin: 20px 20px 20px 30px;">
@@ -79,23 +110,26 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
79
  </td>
80
  </tr>
81
  <tr>
 
 
 
82
  <td><h2>Firewall Status:<a href="http://docs.wordfence.com/en/WAF#Firewall_Status"
83
  target="_blank" class="wfhelp"></a></h2></td>
84
  <td colspan="2">
85
- <select style="width: 300px" name="wafStatus" id="input-wafStatus">
86
- <option<?php echo $config->getConfig('wafStatus') == 'enabled' ? ' selected' : '' ?>
87
  class="wafStatus-enabled" value="enabled">Enabled and Protecting
88
  </option>
89
- <option<?php echo $config->getConfig('wafStatus') == 'learning-mode' ? ' selected' : '' ?>
90
  class="wafStatus-learning-mode" value="learning-mode">Learning Mode
91
  </option>
92
- <option<?php echo $config->getConfig('wafStatus') == 'disabled' ? ' selected' : '' ?>
93
  class="wafStatus-disabled" value="disabled">Disabled
94
  </option>
95
  </select>
96
  <script>
97
  (function($) {
98
- $('#input-wafStatus').val(<?php echo json_encode($config->getConfig('wafStatus')) ?>)
99
  .on('change', function() {
100
  var val = $(this).val();
101
  $('.wafStatus-description').hide();
@@ -125,7 +159,7 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
125
  </tr>
126
  <tr>
127
  <td style="text-align: center">
128
- <button type="submit" class="button button-primary">Save</button>
129
  </td>
130
  <td colspan="2">
131
  <div class="wafStatus-description" id="wafStatus-enabled-description">
@@ -133,10 +167,13 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
133
  matching known attack patterns, and is actively protecting your site from attackers.
134
  </div>
135
  <div class="wafStatus-description" id="wafStatus-learning-mode-description">
136
- When you first install the Wordfence Web Application Firewall, it will be in learning
 
137
  mode. This allows
138
- Wordfence to learn about your site so that we can understand how to protect it and how
139
- to allow normal visitors through the firewall. We recommend you let Wordfence learn for
 
 
140
  a week before you enable the firewall.
141
  </div>
142
  <div class="wafStatus-description" id="wafStatus-disabled-description">
@@ -169,7 +206,7 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
169
  <?php else: ?>
170
  You are running Wordfence community firewall rules.
171
  <?php endif ?>
172
- <!-- <em id="waf-rules-last-updated"></em>-->
173
  </p>
174
 
175
  </form>
@@ -247,9 +284,24 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
247
  </table>
248
  </script>
249
  <script type="text/x-jquery-template" id="waf-whitelisted-urls-tmpl">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  <table class="wf-table whitelist-table">
251
  <thead>
252
  <tr>
 
253
  <th style="width: 5%;">Enabled</th>
254
  <th>URL</th>
255
  <th>Param</th>
@@ -260,9 +312,35 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
260
  <th>Action</th>
261
  </tr>
262
  </thead>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  <tbody>
 
 
 
 
 
 
 
 
 
 
264
  {{each(idx, whitelistedURLParam) whitelistedURLParams}}
265
  <tr data-index="${idx}">
 
266
  <td style="text-align: center;">
267
  <input name="replaceWhitelistedEnabled" type="hidden" value="${whitelistedURLParam.data.disabled}">
268
  <input name="whitelistedEnabled" type="checkbox" value="1"
@@ -326,11 +404,13 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
326
  {{/each}}
327
  {{if (whitelistedURLParams.length == 0)}}
328
  <tr>
329
- <td colspan="8">No whitelisted URLs currently set.</td>
330
  </tr>
331
  {{/if}}
332
  </tbody>
333
  </table>
 
 
334
  </script>
335
 
336
  <script type="text/javascript">
@@ -351,7 +431,10 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
351
  whitelistedEnabled: 1,
352
  whitelistedPath: url,
353
  whitelistedParam: param + '[' + paramName + ']'
354
- });
 
 
 
355
  }
356
  });
357
 
@@ -403,6 +486,144 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
403
  }
404
  }).triggerHandler('click');
405
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406
  });
407
 
408
  $(document).on('click', '.whitelist-url-edit', function() {
@@ -418,7 +639,10 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
418
  WFAD.wafConfigSave('deleteWhitelist', {
419
  deletedWhitelistedPath: pathInput.val(),
420
  deletedWhitelistedParam: paramInput.val()
421
- });
 
 
 
422
  }
423
  });
424
  $(document).on('click', '.whitelist-url-save', function() {
@@ -439,7 +663,10 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
439
  newWhitelistedPath: newWhitelistedPath.val(),
440
  newWhitelistedParam: newWhitelistedParam.val(),
441
  newWhitelistedEnabled: newWhitelistedEnabled.val()
442
- });
 
 
 
443
  });
444
  $(document).on('click', '.whitelist-url-cancel', function() {
445
  var tr = $(this).closest('tr');
@@ -456,7 +683,10 @@ $wafConfigURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configu
456
  whitelistedPath: oldWhitelistedPath.val(),
457
  whitelistedParam: oldWhitelistedParam.val(),
458
  whitelistedEnabled: enabled
459
- });
 
 
 
460
  });
461
 
462
  $(document).on('click', 'input[name=ruleEnabled]', function() {
13
  include('pageTitle.php');
14
  ?>
15
  <div class="wordfenceModeElem" id="wordfenceMode_waf"></div>
16
+
17
+ <?php
18
+ if (defined('WFWAF_ENABLED') && !WFWAF_ENABLED) :
19
+ $message = 'To allow the firewall to re-enable, please remove this line from the appropriate file.';
20
+ $pattern = '/define\s*\(\s*(?:\'WFWAF_ENABLED\'|"WFWAF_ENABLED")\s*,(.+?)\)/x';
21
+ $checkFiles = array(
22
+ ABSPATH . 'wp-config.php',
23
+ wordfence::getWAFBootstrapPath(),
24
+ );
25
+
26
+ foreach($checkFiles as $path) {
27
+ if (!file_exists($path)) {
28
+ continue;
29
+ }
30
+
31
+ if (($contents = file_get_contents($path)) !== false) {
32
+ if (preg_match($pattern, $contents, $matches) && (trim($matches[1]) == 'false' || trim($matches[1]) == '0')) {
33
+ $message = "To allow the firewall to re-enable, please remove this line from the file '" . $path . "'.";
34
+ break;
35
+ }
36
+ }
37
+ }
38
+
39
+ ?>
40
+ <p class="wf-notice">The Wordfence Firewall is currently disabled because WFWAF_ENABLED is overridden and set to false. <?php echo $message; ?></p>
41
+ <?php endif ?>
42
+
43
  <?php if (!empty($storageExceptionMessage)): ?>
44
  <div style="font-weight: bold; margin: 20px 0px;;">
45
  <?php echo wp_kses($storageExceptionMessage, 'post') ?>
48
  <?php echo $wafActionContent ?>
49
 
50
  <p class="wf-notice"><em>If you cannot complete the setup process,
51
+ <a target="_blank" href="https://docs.wordfence.com/en/Web_Application_Firewall_Setup">click here for
52
+ help</a>.</em></p>
53
 
54
  <?php else: ?>
55
 
65
 
66
  <p>As new threats emerge, the Threat Defense Feed is updated to protect you from new attacks. The
67
  Premium version of the Threat Defense Feed is updated in real-time protecting you immediately. As a
68
+ free user <strong>you are receiving the community version</strong> of the feed which is updated 30
69
+ days later.
70
  Upgrade now for less than $5 a month!</p>
71
 
72
  <p class="center"><a class="button button-primary"
84
  <?php if (WFWAF_SUBDIRECTORY_INSTALL): ?>
85
  <div class="wf-notice">
86
  You are currently running the Wordfence Web Application Firewall from another WordPress installation.
87
+ Please <a
88
+ href="<?php echo network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend'); ?>">click
89
+ here</a> to configure the Firewall to run correctly on this site.
90
  </div>
91
  <?php else: ?>
92
  <div class="wordfenceWrap" style="margin: 20px 20px 20px 30px;">
110
  </td>
111
  </tr>
112
  <tr>
113
+ <?php
114
+ $wafStatus = (!WFWAF_ENABLED ? 'disabled' : $config->getConfig('wafStatus'));
115
+ ?>
116
  <td><h2>Firewall Status:<a href="http://docs.wordfence.com/en/WAF#Firewall_Status"
117
  target="_blank" class="wfhelp"></a></h2></td>
118
  <td colspan="2">
119
+ <select style="width: 300px" name="wafStatus" id="input-wafStatus"<?php echo !WFWAF_ENABLED ? ' disabled' : '' ?>>
120
+ <option<?php echo $wafStatus == 'enabled' ? ' selected' : '' ?>
121
  class="wafStatus-enabled" value="enabled">Enabled and Protecting
122
  </option>
123
+ <option<?php echo $wafStatus == 'learning-mode' ? ' selected' : '' ?>
124
  class="wafStatus-learning-mode" value="learning-mode">Learning Mode
125
  </option>
126
+ <option<?php echo $wafStatus == 'disabled' ? ' selected' : '' ?>
127
  class="wafStatus-disabled" value="disabled">Disabled
128
  </option>
129
  </select>
130
  <script>
131
  (function($) {
132
+ $('#input-wafStatus').val(<?php echo json_encode($wafStatus) ?>)
133
  .on('change', function() {
134
  var val = $(this).val();
135
  $('.wafStatus-description').hide();
159
  </tr>
160
  <tr>
161
  <td style="text-align: center">
162
+ <button type="submit" class="button button-primary"<?php echo !WFWAF_ENABLED ? ' disabled' : '' ?>>Save</button>
163
  </td>
164
  <td colspan="2">
165
  <div class="wafStatus-description" id="wafStatus-enabled-description">
167
  matching known attack patterns, and is actively protecting your site from attackers.
168
  </div>
169
  <div class="wafStatus-description" id="wafStatus-learning-mode-description">
170
+ When you first install the Wordfence Web Application Firewall, it will be in
171
+ learning
172
  mode. This allows
173
+ Wordfence to learn about your site so that we can understand how to protect it and
174
+ how
175
+ to allow normal visitors through the firewall. We recommend you let Wordfence learn
176
+ for
177
  a week before you enable the firewall.
178
  </div>
179
  <div class="wafStatus-description" id="wafStatus-disabled-description">
206
  <?php else: ?>
207
  You are running Wordfence community firewall rules.
208
  <?php endif ?>
209
+ <!-- <em id="waf-rules-last-updated"></em>-->
210
  </p>
211
 
212
  </form>
284
  </table>
285
  </script>
286
  <script type="text/x-jquery-template" id="waf-whitelisted-urls-tmpl">
287
+ <?php ob_start() ?>
288
+ <form action="javascript:void(0)" class="wf-bulk-action wf-whitelist-actions">
289
+ <select name="wf-bulk-action">
290
+ <option value="">Bulk Actions</option>
291
+ <option value="delete">Delete</option>
292
+ <option value="enable">Enable</option>
293
+ <option value="disable">Disable</option>
294
+ </select>
295
+ <button type="submit" class="button">Apply</button>
296
+ </form>
297
+ <?php
298
+ $bulkActionForm = ob_get_clean();
299
+ echo $bulkActionForm;
300
+ ?>
301
  <table class="wf-table whitelist-table">
302
  <thead>
303
  <tr>
304
+ <th style="width: 2%;text-align: center"><input type="checkbox" class="wf-whitelist-table-bulk-action"></th>
305
  <th style="width: 5%;">Enabled</th>
306
  <th>URL</th>
307
  <th>Param</th>
312
  <th>Action</th>
313
  </tr>
314
  </thead>
315
+ {{if whitelistedURLParams.length > 5}}
316
+ <tfoot>
317
+ <tr>
318
+ <th><input type="checkbox" class="wf-whitelist-table-bulk-action"></th>
319
+ <th style="width: 5%;">Enabled</th>
320
+ <th>URL</th>
321
+ <th>Param</th>
322
+ <th>Created</th>
323
+ <th>Source</th>
324
+ <th>User</th>
325
+ <th>IP</th>
326
+ <th>Action</th>
327
+ </tr>
328
+ {{/if}}
329
+ </tfoot>
330
  <tbody>
331
+ <tr class="wf-table-filters">
332
+ <td colspan="2"></td>
333
+ <td><input data-column-index="2" placeholder="Filter URL" type="text"></td>
334
+ <td><input data-column-index="3" placeholder="Filter Param" type="text"></td>
335
+ <td><input data-column-index="4" placeholder="Filter Created" type="text"></td>
336
+ <td><input data-column-index="5" placeholder="Filter Source" type="text"></td>
337
+ <td><input style="max-width:100px;" data-column-index="6" placeholder="Filter User" type="text"></td>
338
+ <td><input style="max-width:100px;" data-column-index="7" placeholder="Filter IP" type="text"></td>
339
+ <td></td>
340
+ </tr>
341
  {{each(idx, whitelistedURLParam) whitelistedURLParams}}
342
  <tr data-index="${idx}">
343
+ <td style="text-align: center;"><input type="checkbox" class="wf-whitelist-table-bulk-checkbox"></td>
344
  <td style="text-align: center;">
345
  <input name="replaceWhitelistedEnabled" type="hidden" value="${whitelistedURLParam.data.disabled}">
346
  <input name="whitelistedEnabled" type="checkbox" value="1"
404
  {{/each}}
405
  {{if (whitelistedURLParams.length == 0)}}
406
  <tr>
407
+ <td colspan="9">No whitelisted URLs currently set.</td>
408
  </tr>
409
  {{/if}}
410
  </tbody>
411
  </table>
412
+ <?php echo $bulkActionForm ?>
413
+
414
  </script>
415
 
416
  <script type="text/javascript">
431
  whitelistedEnabled: 1,
432
  whitelistedPath: url,
433
  whitelistedParam: param + '[' + paramName + ']'
434
+ }, function() {
435
+ WFAD.colorbox('400px', 'Firewall Configuration', 'The Wordfence Web Application Firewall ' +
436
+ 'whitelist was saved successfully.');
437
+ }, false);
438
  }
439
  });
440
 
486
  }
487
  }).triggerHandler('click');
488
 
489
+ var whitelistWrapper = $('#waf-whitelisted-urls-wrapper');
490
+
491
+ var whitelistTable = null;
492
+ var whitelistTableRows = null;
493
+ var bulkActionCheckboxes = null;
494
+ var bulkActionTriggerCheckboxes = null;
495
+ var filterInputs = [];
496
+
497
+ function requeryWhitelistDOMElements() {
498
+ whitelistWrapper = $('#waf-whitelisted-urls-wrapper');
499
+ whitelistTable = whitelistWrapper.find('.whitelist-table');
500
+ whitelistTableRows = whitelistTable.find('> tbody > tr[data-index]');
501
+ bulkActionCheckboxes = whitelistTable.find('> tbody > tr[data-index] > td > input[type=checkbox].wf-whitelist-table-bulk-checkbox');
502
+ filterInputs = [];
503
+
504
+ whitelistWrapper.find('.wf-table-filters input').each(function() {
505
+ var el = $(this);
506
+ var index = el.attr('data-column-index');
507
+ filterInputs[index] = function(td) {
508
+ if (el.val().length == 0) {
509
+ return true;
510
+ }
511
+ return $(td).text().indexOf(el.val()) > -1;
512
+ };
513
+ }).on('keydown', function(evt) {
514
+
515
+ if (evt.keyCode == 13) {
516
+ filterWhitelistTable();
517
+ return false;
518
+ }
519
+ }).on('blur', function() {
520
+ filterWhitelistTable();
521
+ });
522
+
523
+ // Bulk actions
524
+ bulkActionTriggerCheckboxes = whitelistWrapper.find('input[type=checkbox].wf-whitelist-table-bulk-action').on('click', function() {
525
+ if (this.checked) {
526
+ whitelistCheckAllVisible();
527
+ } else {
528
+ whitelistUncheckAll();
529
+ }
530
+ });
531
+
532
+ whitelistWrapper.find('form.wf-whitelist-actions').on('submit', function() {
533
+ var select = $(this).find('select[name=wf-bulk-action]');
534
+ var bulkActionCallback = function(res) {
535
+ if (typeof res === 'object' && res.success) {
536
+ WFAD.colorbox('400px', 'Firewall Configuration', 'The Wordfence Web Application Firewall ' +
537
+ 'whitelist was saved successfully.');
538
+ WFAD.wafData = res.data;
539
+ WFAD.wafConfigPageRender();
540
+ } else {
541
+ WFAD.colorbox('400px', 'Error saving Firewall configuration', 'There was an error saving the ' +
542
+ 'Web Application Firewall whitelist.');
543
+ }
544
+ };
545
+ switch (select.val()) {
546
+ case 'delete':
547
+ WFAD.ajax('wordfence_whitelistBulkDelete', {
548
+ items: JSON.stringify(getBulkWhitelistChecked())
549
+ }, bulkActionCallback);
550
+ break;
551
+
552
+ case 'enable':
553
+ WFAD.ajax('wordfence_whitelistBulkEnable', {
554
+ items: JSON.stringify(getBulkWhitelistChecked())
555
+ }, bulkActionCallback);
556
+ break;
557
+
558
+ case 'disable':
559
+ WFAD.ajax('wordfence_whitelistBulkDisable', {
560
+ items: JSON.stringify(getBulkWhitelistChecked())
561
+ }, bulkActionCallback);
562
+ break;
563
+ }
564
+ return false;
565
+ });
566
+ }
567
+
568
+ // Whitelist table filters
569
+ function filterWhitelistTable() {
570
+ var zebraCount = 0;
571
+ whitelistTableRows.each(function() {
572
+ var tr = $(this);
573
+ var isMatch = true;
574
+ tr.find('> td').each(function(index) {
575
+ if (typeof filterInputs[index] === 'function') {
576
+ isMatch = filterInputs[index](this);
577
+ if (!isMatch) {
578
+ return false;
579
+ }
580
+ }
581
+ });
582
+ tr.removeClass('even odd');
583
+ if (isMatch) {
584
+ tr.show();
585
+ tr.addClass(zebraCount++ % 2 === 0 ? 'even' : 'odd');
586
+ } else {
587
+ tr.hide();
588
+ }
589
+ });
590
+ whitelistUncheckAll();
591
+ }
592
+
593
+ var keyupTimeout = null;
594
+
595
+ function getBulkWhitelistChecked() {
596
+ var data = [];
597
+ bulkActionCheckboxes.each(function() {
598
+ if (this.checked) {
599
+ var tr = $(this).closest('tr');
600
+ if (tr.is(':visible')) {
601
+ var path = tr.find('input[name=whitelistedPath]').val();
602
+ var paramKey = tr.find('input[name=whitelistedParam]').val();
603
+ var enabled = tr.find('input[name=whitelistedEnabled]').attr('checked') ? 1 : 0;
604
+ data.push([encodeURIComponent(path), encodeURIComponent(paramKey), enabled]);
605
+ }
606
+ }
607
+ });
608
+ return data;
609
+ }
610
+
611
+ function whitelistCheckAllVisible() {
612
+ bulkActionTriggerCheckboxes.attr('checked', true);
613
+ bulkActionCheckboxes.each(function() {
614
+ this.checked = $(this).closest('tr').is(':visible');
615
+ });
616
+ }
617
+
618
+ function whitelistUncheckAll() {
619
+ bulkActionTriggerCheckboxes.attr('checked', false);
620
+ bulkActionCheckboxes.attr('checked', false);
621
+ }
622
+
623
+ requeryWhitelistDOMElements();
624
+ $(window).on('wordfenceWAFConfigPageRender', function() {
625
+ requeryWhitelistDOMElements();
626
+ })
627
  });
628
 
629
  $(document).on('click', '.whitelist-url-edit', function() {
639
  WFAD.wafConfigSave('deleteWhitelist', {
640
  deletedWhitelistedPath: pathInput.val(),
641
  deletedWhitelistedParam: paramInput.val()
642
+ }, function() {
643
+ WFAD.colorbox('400px', 'Firewall Configuration', 'The Wordfence Web Application Firewall ' +
644
+ 'whitelist was saved successfully.');
645
+ }, false);
646
  }
647
  });
648
  $(document).on('click', '.whitelist-url-save', function() {
663
  newWhitelistedPath: newWhitelistedPath.val(),
664
  newWhitelistedParam: newWhitelistedParam.val(),
665
  newWhitelistedEnabled: newWhitelistedEnabled.val()
666
+ }, function() {
667
+ WFAD.colorbox('400px', 'Firewall Configuration', 'The Wordfence Web Application Firewall ' +
668
+ 'whitelist was saved successfully.');
669
+ }, false);
670
  });
671
  $(document).on('click', '.whitelist-url-cancel', function() {
672
  var tr = $(this).closest('tr');
683
  whitelistedPath: oldWhitelistedPath.val(),
684
  whitelistedParam: oldWhitelistedParam.val(),
685
  whitelistedEnabled: enabled
686
+ }, function() {
687
+ WFAD.colorbox('400px', 'Firewall Configuration', 'The Wordfence Web Application Firewall ' +
688
+ 'whitelist was saved successfully.');
689
+ }, false);
690
  });
691
 
692
  $(document).on('click', 'input[name=ruleEnabled]', function() {
lib/wfConfig.php CHANGED
@@ -685,7 +685,7 @@ class wfConfig {
685
  // because we would have to concatenate $val twice into the query which could also exceed max packet for the mysql server
686
  $serialized = serialize($val);
687
  $tempFilename = 'wordfence_tmpfile_' . $key . '.php';
688
- if((strlen($serialized) * 1.1) > self::getDB()->getMaxAllowedPacketBytes()){ //If it's greater than max_allowed_packet + 10% for escaping and SQL
689
  if($canUseDisk){
690
  $dir = self::getTempDir();
691
  $potentialDirs = self::getPotentialTempDirs();
@@ -719,10 +719,12 @@ class wfConfig {
719
  self::deleteOldTempFile($tempDir . $tempFilename);
720
  }
721
  $exists = self::getDB()->querySingle("select name from " . self::table() . " where name='%s'", $key);
 
 
722
  if($exists){
723
- self::getDB()->queryWrite("update " . self::table() . " set val=%s where name=%s", $serialized, $key);
724
  } else {
725
- self::getDB()->queryWrite("insert IGNORE into " . self::table() . " (name, val) values (%s, %s)", $key, $serialized);
726
  }
727
  }
728
  self::getDB()->flush();
685
  // because we would have to concatenate $val twice into the query which could also exceed max packet for the mysql server
686
  $serialized = serialize($val);
687
  $tempFilename = 'wordfence_tmpfile_' . $key . '.php';
688
+ if((strlen($serialized) * 2) + 50 > self::getDB()->getMaxAllowedPacketBytes()){ //If it's greater than max_allowed_packet + 20% for escaping and SQL
689
  if($canUseDisk){
690
  $dir = self::getTempDir();
691
  $potentialDirs = self::getPotentialTempDirs();
719
  self::deleteOldTempFile($tempDir . $tempFilename);
720
  }
721
  $exists = self::getDB()->querySingle("select name from " . self::table() . " where name='%s'", $key);
722
+ $serializedHex = bin2hex($serialized);
723
+
724
  if($exists){
725
+ self::getDB()->queryWrite(sprintf("update " . self::table() . " set val=X'%s' where name=%%s", $serializedHex), $key);
726
  } else {
727
+ self::getDB()->queryWrite(sprintf("insert ignore into " . self::table() . " (name, val) values (%%s, X'%s')", $serializedHex), $key);
728
  }
729
  }
730
  self::getDB()->flush();
lib/wfDiagnostic.php CHANGED
@@ -64,6 +64,7 @@ class wfDiagnostic
64
  ),
65
  'PHP' => array(
66
  'phpVersion' => 'PHP version >= PHP 5.2.4<br><em> (<a href="https://wordpress.org/about/requirements/" target="_blank">Minimum version required by WordPress</a>)</em>',
 
67
  'hasOpenSSL' => 'Checking for OpenSSL support',
68
  'hasCurl' => 'Checking for cURL support',
69
  ),
@@ -156,6 +157,56 @@ class wfDiagnostic
156
  );
157
  }
158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  public function hasOpenSSL() {
160
  return is_callable('openssl_open');
161
  }
64
  ),
65
  'PHP' => array(
66
  'phpVersion' => 'PHP version >= PHP 5.2.4<br><em> (<a href="https://wordpress.org/about/requirements/" target="_blank">Minimum version required by WordPress</a>)</em>',
67
+ 'processOwner' => 'Process Owner',
68
  'hasOpenSSL' => 'Checking for OpenSSL support',
69
  'hasCurl' => 'Checking for cURL support',
70
  ),
157
  );
158
  }
159
 
160
+ public function processOwner() {
161
+ $disabledFunctions = explode(',', ini_get('disable_functions'));
162
+
163
+ if (is_callable('posix_geteuid')) {
164
+ if (!is_callable('posix_getpwuid') || in_array('posix_getpwuid', $disabledFunctions)) {
165
+ return array(
166
+ 'test' => false,
167
+ 'message' => 'Unavailable',
168
+ );
169
+ }
170
+
171
+ $processOwner = posix_getpwuid(posix_geteuid());
172
+ if ($processOwner !== null)
173
+ {
174
+ return array(
175
+ 'test' => true,
176
+ 'message' => $processOwner['name'],
177
+ );
178
+ }
179
+ }
180
+
181
+ $usernameOrUserEnv = getenv('USERNAME') ? getenv('USERNAME') : getenv('USER');
182
+ if (!empty($usernameOrUserEnv)) { //Check some environmental variable possibilities
183
+ return array(
184
+ 'test' => true,
185
+ 'message' => $usernameOrUserEnv,
186
+ );
187
+ }
188
+
189
+ $currentUser = get_current_user();
190
+ if (!empty($currentUser)) { //php.net comments indicate on Windows this returns the process owner rather than the file owner
191
+ return array(
192
+ 'test' => true,
193
+ 'message' => $currentUser,
194
+ );
195
+ }
196
+
197
+ if (!empty($_SERVER['LOGON_USER'])) { //Last resort for IIS since POSIX functions are unavailable, Source: https://msdn.microsoft.com/en-us/library/ms524602(v=vs.90).aspx
198
+ return array(
199
+ 'test' => true,
200
+ 'message' => $_SERVER['LOGON_USER'],
201
+ );
202
+ }
203
+
204
+ return array(
205
+ 'test' => false,
206
+ 'message' => 'Unknown',
207
+ );
208
+ }
209
+
210
  public function hasOpenSSL() {
211
  return is_callable('openssl_open');
212
  }
lib/wordfenceClass.php CHANGED
@@ -498,6 +498,21 @@ SQL
498
  }
499
  }
500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
  //Must be the final line
502
  }
503
  private static function doEarlyAccessLogging(){
@@ -664,7 +679,9 @@ SQL
664
  if (!empty($_GET['wordfence_syncAttackData']) && get_site_option('wordfence_syncingAttackData') <= time() - 60) {
665
  ignore_user_abort(true);
666
  update_site_option('wordfence_syncingAttackData', time());
667
- add_action('init', 'wordfence::syncAttackData');
 
 
668
  }
669
 
670
  if (wfConfig::get('other_hideWPVersion')) {
@@ -998,14 +1015,25 @@ SQL
998
  $waf->getStorageEngine()->setConfig($key, $value);
999
  }
1000
 
1001
- $lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$wpdb->base_prefix}wfHits");
1002
- if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) {
1003
- if (get_site_option('wordfence_syncingAttackData') <= time() - 60) {
1004
- wp_remote_post(add_query_arg('wordfence_syncAttackData', microtime(true), home_url('/')), array(
1005
- 'timeout' => 0.01,
1006
- 'blocking' => false,
1007
- 'sslverify' => apply_filters('https_local_ssl_verify', false)
1008
- ));
 
 
 
 
 
 
 
 
 
 
 
1009
  }
1010
  }
1011
 
@@ -1381,7 +1409,7 @@ SQL
1381
  }
1382
  public static function ajax_sendDiagnostic_callback(){
1383
  $inEmail = true;
1384
- $body = "This email is the diagnostic from " . site_url() . ".\nThe IP address that requested this was: " . wfUtils::getIP();
1385
  ob_start();
1386
  require 'menu_diagnostic.php';
1387
  $body = nl2br($body) . ob_get_clean();
@@ -1393,7 +1421,7 @@ SQL
1393
  '<td class="inactive"' => '<td style="font-weight:bold;color:#666666;" class="inactive"',
1394
  );
1395
  $body = str_replace(array_keys($findReplace), array_values($findReplace), $body);
1396
- $result = wfUtils::htmlEmail($_POST['email'], '[Wordfence] Diagnostic results', $body);
1397
  return compact('result');
1398
  }
1399
  public static function ajax_sendTestEmail_callback(){
@@ -2458,6 +2486,9 @@ SQL
2458
  $events = self::getLog()->getPerfStats($newestEventTime);
2459
 
2460
  } else if ($alsoGet == 'liveTraffic') {
 
 
 
2461
  $results = self::ajax_loadLiveTraffic_callback();
2462
  $events = $results['data'];
2463
  if (isset($results['sql'])) {
@@ -3446,6 +3477,7 @@ HTML;
3446
  'sendDiagnostic', 'saveWAFConfig', 'updateWAFRules', 'loadLiveTraffic', 'whitelistWAFParamKey',
3447
  'disableDirectoryListing', 'fixFPD', 'deleteAdminUser', 'revokeAdminUser',
3448
  'hideFileHtaccess', 'saveDebuggingConfig', 'wafConfigureAutoPrepend',
 
3449
  ) as $func){
3450
  add_action('wp_ajax_wordfence_' . $func, 'wordfence::ajaxReceiver');
3451
  }
@@ -4681,6 +4713,74 @@ to your httpd.conf if using Apache, or find documentation on how to disable dire
4681
  return false;
4682
  }
4683
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4684
  private static function _getWAFData() {
4685
  $data['learningMode'] = wfWAF::getInstance()->isInLearningMode();
4686
  $data['rules'] = wfWAF::getInstance()->getRules();
@@ -4867,7 +4967,7 @@ LIMIT %d", $lastSendTime, $limit));
4867
  self::trimWfHits();
4868
  }
4869
 
4870
- public static function syncAttackData() {
4871
  global $wpdb;
4872
  $waf = wfWAF::getInstance();
4873
  $lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$wpdb->base_prefix}wfHits");
@@ -4961,7 +5061,17 @@ LIMIT %d", $lastSendTime, $limit));
4961
  $waf->getStorageEngine()->truncateAttackData();
4962
  }
4963
  update_site_option('wordfence_syncingAttackData', 0);
4964
- exit;
 
 
 
 
 
 
 
 
 
 
4965
  }
4966
 
4967
  /**
498
  }
499
  }
500
 
501
+ // Call this before creating the index in cases where the wp-cron isn't running.
502
+ self::trimWfHits();
503
+ $hitsTable = "{$wpdb->base_prefix}wfHits";
504
+ $hasAttackLogTimeIndex = $wpdb->get_var($wpdb->prepare(<<<SQL
505
+ SELECT COLUMN_KEY FROM information_schema.COLUMNS
506
+ WHERE TABLE_SCHEMA = DATABASE()
507
+ AND TABLE_NAME = %s
508
+ AND COLUMN_NAME = 'attackLogTime'
509
+ SQL
510
+ , $hitsTable));
511
+
512
+ if (!$hasAttackLogTimeIndex) {
513
+ $wpdb->query("ALTER TABLE $hitsTable ADD INDEX `attackLogTime` (`attackLogTime`)");
514
+ }
515
+
516
  //Must be the final line
517
  }
518
  private static function doEarlyAccessLogging(){
679
  if (!empty($_GET['wordfence_syncAttackData']) && get_site_option('wordfence_syncingAttackData') <= time() - 60) {
680
  ignore_user_abort(true);
681
  update_site_option('wordfence_syncingAttackData', time());
682
+ header('Content-Type: text/javascript');
683
+ add_action('init', 'wordfence::syncAttackData', 10, 0);
684
+ add_filter('woocommerce_unforce_ssl_checkout', '__return_false');
685
  }
686
 
687
  if (wfConfig::get('other_hideWPVersion')) {
1015
  $waf->getStorageEngine()->setConfig($key, $value);
1016
  }
1017
 
1018
+ if (empty($_GET['wordfence_syncAttackData'])) {
1019
+ $lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$wpdb->base_prefix}wfHits");
1020
+ if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) {
1021
+ if (get_site_option('wordfence_syncingAttackData') <= time() - 60) {
1022
+ // Could be the request to itself is not completing, add ajax to the head as a workaround
1023
+ $attempts = get_site_option('wordfence_syncAttackDataAttempts', 0);
1024
+ if ($attempts > 10) {
1025
+ add_action('wp_head', 'wordfence::addSyncAttackDataAjax');
1026
+ add_action('login_head', 'wordfence::addSyncAttackDataAjax');
1027
+ add_action('admin_head', 'wordfence::addSyncAttackDataAjax');
1028
+ } else {
1029
+ update_site_option('wordfence_syncAttackDataAttempts', ++$attempts);
1030
+ wp_remote_post(add_query_arg('wordfence_syncAttackData', microtime(true), home_url('/')), array(
1031
+ 'timeout' => 0.01,
1032
+ 'blocking' => false,
1033
+ 'sslverify' => apply_filters('https_local_ssl_verify', false)
1034
+ ));
1035
+ }
1036
+ }
1037
  }
1038
  }
1039
 
1409
  }
1410
  public static function ajax_sendDiagnostic_callback(){
1411
  $inEmail = true;
1412
+ $body = "This email is the diagnostic from " . site_url() . ".\nThe IP address that requested this was: " . wfUtils::getIP() . "\nTicket Number/Forum Username: " . $_POST['ticket'];
1413
  ob_start();
1414
  require 'menu_diagnostic.php';
1415
  $body = nl2br($body) . ob_get_clean();
1421
  '<td class="inactive"' => '<td style="font-weight:bold;color:#666666;" class="inactive"',
1422
  );
1423
  $body = str_replace(array_keys($findReplace), array_values($findReplace), $body);
1424
+ $result = wfUtils::htmlEmail($_POST['email'], '[Wordfence] Diagnostic results (' . $_POST['ticket'] . ')', $body);
1425
  return compact('result');
1426
  }
1427
  public static function ajax_sendTestEmail_callback(){
2486
  $events = self::getLog()->getPerfStats($newestEventTime);
2487
 
2488
  } else if ($alsoGet == 'liveTraffic') {
2489
+ if (get_site_option('wordfence_syncAttackDataAttempts') > 10) {
2490
+ self::syncAttackData(false);
2491
+ }
2492
  $results = self::ajax_loadLiveTraffic_callback();
2493
  $events = $results['data'];
2494
  if (isset($results['sql'])) {
3477
  'sendDiagnostic', 'saveWAFConfig', 'updateWAFRules', 'loadLiveTraffic', 'whitelistWAFParamKey',
3478
  'disableDirectoryListing', 'fixFPD', 'deleteAdminUser', 'revokeAdminUser',
3479
  'hideFileHtaccess', 'saveDebuggingConfig', 'wafConfigureAutoPrepend',
3480
+ 'whitelistBulkDelete', 'whitelistBulkEnable', 'whitelistBulkDisable',
3481
  ) as $func){
3482
  add_action('wp_ajax_wordfence_' . $func, 'wordfence::ajaxReceiver');
3483
  }
4713
  return false;
4714
  }
4715
 
4716
+ public static function ajax_whitelistBulkDelete_callback() {
4717
+ if (class_exists('wfWAF') && $waf = wfWAF::getInstance()) {
4718
+ if (!empty($_POST['items']) && ($items = json_decode(stripslashes($_POST['items']), true)) !== false) {
4719
+ $whitelist = $waf->getStorageEngine()->getConfig('whitelistedURLParams');
4720
+ if (!is_array($whitelist)) {
4721
+ $whitelist = array();
4722
+ }
4723
+ foreach ($items as $key) {
4724
+ list($path, $paramKey, ) = $key;
4725
+ $whitelistKey = base64_encode(rawurldecode($path)) . '|' . base64_encode(rawurldecode($paramKey));
4726
+ if (array_key_exists($whitelistKey, $whitelist)) {
4727
+ unset($whitelist[$whitelistKey]);
4728
+ }
4729
+ }
4730
+ $waf->getStorageEngine()->setConfig('whitelistedURLParams', $whitelist);
4731
+ return array(
4732
+ 'data' => self::_getWAFData(),
4733
+ 'success' => true,
4734
+ );
4735
+ }
4736
+ }
4737
+ return false;
4738
+ }
4739
+
4740
+ public static function ajax_whitelistBulkEnable_callback() {
4741
+ if (class_exists('wfWAF') && $waf = wfWAF::getInstance()) {
4742
+ if (!empty($_POST['items']) && ($items = json_decode(stripslashes($_POST['items']), true)) !== false) {
4743
+ self::_whitelistBulkToggle($items, true);
4744
+ return array(
4745
+ 'data' => self::_getWAFData(),
4746
+ 'success' => true,
4747
+ );
4748
+ }
4749
+ }
4750
+ return false;
4751
+ }
4752
+
4753
+ public static function ajax_whitelistBulkDisable_callback() {
4754
+ if (class_exists('wfWAF') && $waf = wfWAF::getInstance()) {
4755
+ if (!empty($_POST['items']) && ($items = json_decode(stripslashes($_POST['items']), true)) !== false) {
4756
+ self::_whitelistBulkToggle($items, false);
4757
+ return array(
4758
+ 'data' => self::_getWAFData(),
4759
+ 'success' => true,
4760
+ );
4761
+ }
4762
+ }
4763
+ return false;
4764
+ }
4765
+
4766
+ private static function _whitelistBulkToggle($items, $enabled) {
4767
+ $waf = wfWAF::getInstance();
4768
+ $whitelist = $waf->getStorageEngine()->getConfig('whitelistedURLParams');
4769
+ if (!is_array($whitelist)) {
4770
+ $whitelist = array();
4771
+ }
4772
+ foreach ($items as $key) {
4773
+ list($path, $paramKey, ) = $key;
4774
+ $whitelistKey = base64_encode(rawurldecode($path)) . '|' . base64_encode(rawurldecode($paramKey));
4775
+ if (array_key_exists($whitelistKey, $whitelist) && is_array($whitelist[$whitelistKey])) {
4776
+ foreach ($whitelist[$whitelistKey] as $ruleID => $data) {
4777
+ $whitelist[$whitelistKey][$ruleID]['disabled'] = !$enabled;
4778
+ }
4779
+ }
4780
+ }
4781
+ $waf->getStorageEngine()->setConfig('whitelistedURLParams', $whitelist);
4782
+ }
4783
+
4784
  private static function _getWAFData() {
4785
  $data['learningMode'] = wfWAF::getInstance()->isInLearningMode();
4786
  $data['rules'] = wfWAF::getInstance()->getRules();
4967
  self::trimWfHits();
4968
  }
4969
 
4970
+ public static function syncAttackData($exit = true) {
4971
  global $wpdb;
4972
  $waf = wfWAF::getInstance();
4973
  $lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$wpdb->base_prefix}wfHits");
5061
  $waf->getStorageEngine()->truncateAttackData();
5062
  }
5063
  update_site_option('wordfence_syncingAttackData', 0);
5064
+ update_site_option('wordfence_syncAttackDataAttempts', 0);
5065
+ if ($exit) {
5066
+ exit;
5067
+ }
5068
+ }
5069
+
5070
+ public static function addSyncAttackDataAjax() {
5071
+ $URL = home_url('/?wordfence_syncAttackData=' . microtime(true));
5072
+ $URL = esc_url(preg_replace('/^https?:/i', '', $URL));
5073
+ // Load as external script async so we don't slow page down.
5074
+ echo "<script type=\"text/javascript\" src=\"$URL\" async></script>";
5075
  }
5076
 
5077
  /**
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: mmaunder
3
  Tags: wordpress, security, web application firewall, waf, performance, speed, caching, cache, caching plugin, wordpress cache, wordpress caching, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure, two factor, cellphone sign-in, cellphone signin, cellphone, twofactor, security, secure, htaccess, login, log, users, login alerts, lock, chmod, maintenance, plugin, private, privacy, protection, permissions, 503, base64, injection, code, encode, script, attack, hack, hackers, block, blocked, prevent, prevention, RFI, XSS, CRLF, CSRF, SQL Injection, vulnerability, website security, WordPress security, security log, logging, HTTP log, error log, login security, personal security, infrastructure security, firewall security, front-end security, web server security, proxy security, reverse proxy security, secure website, secure login, two factor security, two factor authentication, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm, woocommerce support, woocommerce caching, IPv6, IP version 6
4
  Requires at least: 3.9
5
  Tested up to: 4.5
6
- Stable tag: 6.1.3
7
 
8
  The Wordfence WordPress security plugin provides free enterprise-class WordPress security, protecting your website from hacks and malware.
9
  == Description ==
@@ -195,6 +195,23 @@ Designed for every skill level, [The WordPress Security Learning Center](https:/
195
 
196
  == Changelog ==
197
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  = 6.1.3 =
199
  * Improvement: Added dismiss button to the Wordfence WAF setup admin notice.
200
  * Fix: Removed .htaccess and .user.ini from publicly accessible config and backup file scan.
3
  Tags: wordpress, security, web application firewall, waf, performance, speed, caching, cache, caching plugin, wordpress cache, wordpress caching, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure, two factor, cellphone sign-in, cellphone signin, cellphone, twofactor, security, secure, htaccess, login, log, users, login alerts, lock, chmod, maintenance, plugin, private, privacy, protection, permissions, 503, base64, injection, code, encode, script, attack, hack, hackers, block, blocked, prevent, prevention, RFI, XSS, CRLF, CSRF, SQL Injection, vulnerability, website security, WordPress security, security log, logging, HTTP log, error log, login security, personal security, infrastructure security, firewall security, front-end security, web server security, proxy security, reverse proxy security, secure website, secure login, two factor security, two factor authentication, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm, woocommerce support, woocommerce caching, IPv6, IP version 6
4
  Requires at least: 3.9
5
  Tested up to: 4.5
6
+ Stable tag: 6.1.4
7
 
8
  The Wordfence WordPress security plugin provides free enterprise-class WordPress security, protecting your website from hacks and malware.
9
  == Description ==
195
 
196
  == Changelog ==
197
 
198
+ = 6.1.4 =
199
+ Fix: Fixed potential bug with 'stored data not found after a fork. Got type: boolean'.
200
+ Improvement: Added bulk actions and filters to WAF whitelist table.
201
+ Improvement: Added a check while in learning mode to verify the response is not 404 before whitelising.
202
+ Fix: Added index to attackLogTime. wfHits trimmed on runInstall now.
203
+ Fix: Fixed attack data sync for hosts that cannot use wp-cron.
204
+ Improvement: Use wftest@wordfence.com as the Diagnostics page default email address.
205
+ Improvement: When WFWAF_ENABLED is set to false to disable the firewall, show this on the Firewall page.
206
+ Fix: Prevent warnings when $_SERVER is empty.
207
+ Fix: Bug fix for illegal string offset.
208
+ Fix: Hooked up multibyte string functions to binary safe equivalents.
209
+ Fix: Hooked up reverse IP lookup in Live Traffic.
210
+ Fix: Add the user the web server (or PHP) is currently running as to Diagnostics page.
211
+ Improvement: Pause Live Traffic after scrolling past the first entry.
212
+ Improvement: Move "Permanently block all temporarily blocked IP addresses" button to top of blocked IP list.
213
+ Fix: Added JSON fallback for PHP installations that don't have JSON enabled.
214
+
215
  = 6.1.3 =
216
  * Improvement: Added dismiss button to the Wordfence WAF setup admin notice.
217
  * Fix: Removed .htaccess and .user.ini from publicly accessible config and backup file scan.
vendor/wordfence/wf-waf/src/lib/http.php CHANGED
@@ -292,11 +292,11 @@ class wfWAFHTTPTransportCurl extends wfWAFHTTPTransport {
292
  if (is_array($queryString)) {
293
  $queryString = http_build_query($queryString);
294
  }
295
- $url .= (strpos($url, '?') !== false ? '&' : '?') . $queryString;
296
  }
297
 
298
  $ch = curl_init($url);
299
- switch (strtolower($request->getMethod())) {
300
  case 'post':
301
  curl_setopt($ch, CURLOPT_POST, 1);
302
  break;
@@ -328,8 +328,8 @@ class wfWAFHTTPTransportCurl extends wfWAFHTTPTransport {
328
  $curlResponse = curl_exec($ch);
329
  if ($curlResponse !== false) {
330
  $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
331
- $header = substr($curlResponse, 0, $headerSize);
332
- $body = substr($curlResponse, $headerSize);
333
 
334
  $response = new wfWAFHTTPResponse();
335
  $response->setBody($body);
@@ -356,7 +356,7 @@ class wfWAFHTTPTransportStreams extends wfWAFHTTPTransport {
356
  if (is_array($queryString)) {
357
  $queryString = http_build_query($queryString);
358
  }
359
- $url .= (strpos($url, '?') !== false ? '&' : '?') . $queryString;
360
  }
361
 
362
  $urlParsed = parse_url($request->getUrl());
@@ -375,7 +375,7 @@ class wfWAFHTTPTransportStreams extends wfWAFHTTPTransport {
375
  if ($_headers = $request->getHeaders()) {
376
  if (is_array($_headers)) {
377
  foreach ($_headers as $header => $value) {
378
- if (trim(strtolower($header)) === 'user-agent') {
379
  $hasUA = true;
380
  }
381
  $headers .= $header . ': ' . $value . "\r\n";
@@ -393,14 +393,14 @@ class wfWAFHTTPTransportStreams extends wfWAFHTTPTransport {
393
  'follow_location' => 1,
394
  'max_redirects' => 5,
395
  );
396
- if (strlen($request->getBody()) > 0) {
397
  $httpOptions['content'] = $request->getBody();
398
- $headers .= 'Content-Length: ' . strlen($httpOptions['content']) . "\r\n";
399
  }
400
  $httpOptions['header'] = $headers;
401
 
402
  $options = array(
403
- strtolower($urlParsed['scheme']) => $httpOptions,
404
  );
405
 
406
  $context = stream_context_create($options);
292
  if (is_array($queryString)) {
293
  $queryString = http_build_query($queryString);
294
  }
295
+ $url .= (wfWAFUtils::strpos($url, '?') !== false ? '&' : '?') . $queryString;
296
  }
297
 
298
  $ch = curl_init($url);
299
+ switch (wfWAFUtils::strtolower($request->getMethod())) {
300
  case 'post':
301
  curl_setopt($ch, CURLOPT_POST, 1);
302
  break;
328
  $curlResponse = curl_exec($ch);
329
  if ($curlResponse !== false) {
330
  $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
331
+ $header = wfWAFUtils::substr($curlResponse, 0, $headerSize);
332
+ $body = wfWAFUtils::substr($curlResponse, $headerSize);
333
 
334
  $response = new wfWAFHTTPResponse();
335
  $response->setBody($body);
356
  if (is_array($queryString)) {
357
  $queryString = http_build_query($queryString);
358
  }
359
+ $url .= (wfWAFUtils::strpos($url, '?') !== false ? '&' : '?') . $queryString;
360
  }
361
 
362
  $urlParsed = parse_url($request->getUrl());
375
  if ($_headers = $request->getHeaders()) {
376
  if (is_array($_headers)) {
377
  foreach ($_headers as $header => $value) {
378
+ if (trim(wfWAFUtils::strtolower($header)) === 'user-agent') {
379
  $hasUA = true;
380
  }
381
  $headers .= $header . ': ' . $value . "\r\n";
393
  'follow_location' => 1,
394
  'max_redirects' => 5,
395
  );
396
+ if (wfWAFUtils::strlen($request->getBody()) > 0) {
397
  $httpOptions['content'] = $request->getBody();
398
+ $headers .= 'Content-Length: ' . wfWAFUtils::strlen($httpOptions['content']) . "\r\n";
399
  }
400
  $httpOptions['header'] = $headers;
401
 
402
  $options = array(
403
+ wfWAFUtils::strtolower($urlParsed['scheme']) => $httpOptions,
404
  );
405
 
406
  $context = stream_context_create($options);
vendor/wordfence/wf-waf/src/lib/json.php ADDED
@@ -0,0 +1,958 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
+ /**
5
+ * Converts to and from JSON format.
6
+ *
7
+ * JSON (JavaScript Object Notation) is a lightweight data-interchange
8
+ * format. It is easy for humans to read and write. It is easy for machines
9
+ * to parse and generate. It is based on a subset of the JavaScript
10
+ * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
11
+ * This feature can also be found in Python. JSON is a text format that is
12
+ * completely language independent but uses conventions that are familiar
13
+ * to programmers of the C-family of languages, including C, C++, C#, Java,
14
+ * JavaScript, Perl, TCL, and many others. These properties make JSON an
15
+ * ideal data-interchange language.
16
+ *
17
+ * This package provides a simple encoder and decoder for JSON notation. It
18
+ * is intended for use with client-side Javascript applications that make
19
+ * use of HTTPRequest to perform server communication functions - data can
20
+ * be encoded into JSON notation for use in a client-side javascript, or
21
+ * decoded from incoming Javascript requests. JSON format is native to
22
+ * Javascript, and can be directly eval()'ed with no further parsing
23
+ * overhead
24
+ *
25
+ * All strings should be in ASCII or UTF-8 format!
26
+ *
27
+ * LICENSE: Redistribution and use in source and binary forms, with or
28
+ * without modification, are permitted provided that the following
29
+ * conditions are met: Redistributions of source code must retain the
30
+ * above copyright notice, this list of conditions and the following
31
+ * disclaimer. Redistributions in binary form must reproduce the above
32
+ * copyright notice, this list of conditions and the following disclaimer
33
+ * in the documentation and/or other materials provided with the
34
+ * distribution.
35
+ *
36
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
37
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
38
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
39
+ * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
40
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
41
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
42
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
44
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
45
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
46
+ * DAMAGE.
47
+ *
48
+ * @category
49
+ * @package Services_JSON
50
+ * @author Michal Migurski <mike-json@teczno.com>
51
+ * @author Matt Knapp <mdknapp[at]gmail[dot]com>
52
+ * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
53
+ * @copyright 2005 Michal Migurski
54
+ * @version CVS: $Id: JSON.php 305040 2010-11-02 23:19:03Z alan_k $
55
+ * @license http://www.opensource.org/licenses/bsd-license.php
56
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
57
+ */
58
+
59
+ /**
60
+ * Marker constant for Services_JSON::decode(), used to flag stack state
61
+ */
62
+ define('WF_SERVICES_JSON_SLICE', 1);
63
+
64
+ /**
65
+ * Marker constant for Services_JSON::decode(), used to flag stack state
66
+ */
67
+ define('WF_SERVICES_JSON_IN_STR', 2);
68
+
69
+ /**
70
+ * Marker constant for Services_JSON::decode(), used to flag stack state
71
+ */
72
+ define('WF_SERVICES_JSON_IN_ARR', 3);
73
+
74
+ /**
75
+ * Marker constant for Services_JSON::decode(), used to flag stack state
76
+ */
77
+ define('WF_SERVICES_JSON_IN_OBJ', 4);
78
+
79
+ /**
80
+ * Marker constant for Services_JSON::decode(), used to flag stack state
81
+ */
82
+ define('WF_SERVICES_JSON_IN_CMT', 5);
83
+
84
+ /**
85
+ * Behavior switch for Services_JSON::decode()
86
+ */
87
+ define('WF_SERVICES_JSON_LOOSE_TYPE', 16);
88
+
89
+ /**
90
+ * Behavior switch for Services_JSON::decode()
91
+ */
92
+ define('WF_SERVICES_JSON_SUPPRESS_ERRORS', 32);
93
+
94
+ /**
95
+ * Behavior switch for Services_JSON::decode()
96
+ */
97
+ define('WF_SERVICES_JSON_USE_TO_JSON', 64);
98
+
99
+ /**
100
+ * Converts to and from JSON format.
101
+ *
102
+ * Brief example of use:
103
+ *
104
+ * <code>
105
+ * // create a new instance of Services_JSON
106
+ * $json = new Services_JSON();
107
+ *
108
+ * // convert a complexe value to JSON notation, and send it to the browser
109
+ * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
110
+ * $output = $json->encode($value);
111
+ *
112
+ * print($output);
113
+ * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
114
+ *
115
+ * // accept incoming POST data, assumed to be in JSON notation
116
+ * $input = file_get_contents('php://input', 1000000);
117
+ * $value = $json->decode($input);
118
+ * </code>
119
+ */
120
+ class wfServices_JSON
121
+ {
122
+ /**
123
+ * constructs a new JSON instance
124
+ *
125
+ * @param int $use object behavior flags; combine with boolean-OR
126
+ *
127
+ * possible values:
128
+ * - SERVICES_JSON_LOOSE_TYPE: loose typing.
129
+ * "{...}" syntax creates associative arrays
130
+ * instead of objects in decode().
131
+ * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
132
+ * Values which can't be encoded (e.g. resources)
133
+ * appear as NULL instead of throwing errors.
134
+ * By default, a deeply-nested resource will
135
+ * bubble up with an error, so all return values
136
+ * from encode() should be checked with isError()
137
+ * - SERVICES_JSON_USE_TO_JSON: call toJSON when serializing objects
138
+ * It serializes the return value from the toJSON call rather
139
+ * than the object itself, toJSON can return associative arrays,
140
+ * strings or numbers, if you return an object, make sure it does
141
+ * not have a toJSON method, otherwise an error will occur.
142
+ */
143
+ function __construct( $use = 0 )
144
+ {
145
+ $this->use = $use;
146
+ $this->_mb_strlen = function_exists('mb_strlen');
147
+ $this->_mb_convert_encoding = function_exists('mb_convert_encoding');
148
+ $this->_mb_substr = function_exists('mb_substr');
149
+ }
150
+
151
+ /**
152
+ * PHP4 constructor.
153
+ */
154
+ public function wfServices_JSON( $use = 0 ) {
155
+ self::__construct( $use );
156
+ }
157
+ // private - cache the mbstring lookup results..
158
+ var $_mb_strlen = false;
159
+ var $_mb_substr = false;
160
+ var $_mb_convert_encoding = false;
161
+
162
+ /**
163
+ * convert a string from one UTF-16 char to one UTF-8 char
164
+ *
165
+ * Normally should be handled by mb_convert_encoding, but
166
+ * provides a slower PHP-only method for installations
167
+ * that lack the multibye string extension.
168
+ *
169
+ * @param string $utf16 UTF-16 character
170
+ * @return string UTF-8 character
171
+ * @access private
172
+ */
173
+ function utf162utf8($utf16)
174
+ {
175
+ // oh please oh please oh please oh please oh please
176
+ if($this->_mb_convert_encoding) {
177
+ return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
178
+ }
179
+
180
+ $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
181
+
182
+ switch(true) {
183
+ case ((0x7F & $bytes) == $bytes):
184
+ // this case should never be reached, because we are in ASCII range
185
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
186
+ return chr(0x7F & $bytes);
187
+
188
+ case (0x07FF & $bytes) == $bytes:
189
+ // return a 2-byte UTF-8 character
190
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
191
+ return chr(0xC0 | (($bytes >> 6) & 0x1F))
192
+ . chr(0x80 | ($bytes & 0x3F));
193
+
194
+ case (0xFFFF & $bytes) == $bytes:
195
+ // return a 3-byte UTF-8 character
196
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
197
+ return chr(0xE0 | (($bytes >> 12) & 0x0F))
198
+ . chr(0x80 | (($bytes >> 6) & 0x3F))
199
+ . chr(0x80 | ($bytes & 0x3F));
200
+ }
201
+
202
+ // ignoring UTF-32 for now, sorry
203
+ return '';
204
+ }
205
+
206
+ /**
207
+ * convert a string from one UTF-8 char to one UTF-16 char
208
+ *
209
+ * Normally should be handled by mb_convert_encoding, but
210
+ * provides a slower PHP-only method for installations
211
+ * that lack the multibye string extension.
212
+ *
213
+ * @param string $utf8 UTF-8 character
214
+ * @return string UTF-16 character
215
+ * @access private
216
+ */
217
+ function utf82utf16($utf8)
218
+ {
219
+ // oh please oh please oh please oh please oh please
220
+ if($this->_mb_convert_encoding) {
221
+ return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
222
+ }
223
+
224
+ switch($this->strlen8($utf8)) {
225
+ case 1:
226
+ // this case should never be reached, because we are in ASCII range
227
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
228
+ return $utf8;
229
+
230
+ case 2:
231
+ // return a UTF-16 character from a 2-byte UTF-8 char
232
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
233
+ return chr(0x07 & (ord($utf8{0}) >> 2))
234
+ . chr((0xC0 & (ord($utf8{0}) << 6))
235
+ | (0x3F & ord($utf8{1})));
236
+
237
+ case 3:
238
+ // return a UTF-16 character from a 3-byte UTF-8 char
239
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
240
+ return chr((0xF0 & (ord($utf8{0}) << 4))
241
+ | (0x0F & (ord($utf8{1}) >> 2)))
242
+ . chr((0xC0 & (ord($utf8{1}) << 6))
243
+ | (0x7F & ord($utf8{2})));
244
+ }
245
+
246
+ // ignoring UTF-32 for now, sorry
247
+ return '';
248
+ }
249
+
250
+ /**
251
+ * encodes an arbitrary variable into JSON format (and sends JSON Header)
252
+ *
253
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
254
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
255
+ * if var is a strng, note that encode() always expects it
256
+ * to be in ASCII or UTF-8 format!
257
+ *
258
+ * @return mixed JSON string representation of input var or an error if a problem occurs
259
+ * @access public
260
+ */
261
+ function encode($var)
262
+ {
263
+ header('Content-type: application/json');
264
+ return $this->encodeUnsafe($var);
265
+ }
266
+ /**
267
+ * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow XSS!!!!)
268
+ *
269
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
270
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
271
+ * if var is a strng, note that encode() always expects it
272
+ * to be in ASCII or UTF-8 format!
273
+ *
274
+ * @return mixed JSON string representation of input var or an error if a problem occurs
275
+ * @access public
276
+ */
277
+ function encodeUnsafe($var)
278
+ {
279
+ // see bug #16908 - regarding numeric locale printing
280
+ $lc = setlocale(LC_NUMERIC, 0);
281
+ setlocale(LC_NUMERIC, 'C');
282
+ $ret = $this->_encode($var);
283
+ setlocale(LC_NUMERIC, $lc);
284
+ return $ret;
285
+
286
+ }
287
+ /**
288
+ * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format
289
+ *
290
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
291
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
292
+ * if var is a strng, note that encode() always expects it
293
+ * to be in ASCII or UTF-8 format!
294
+ *
295
+ * @return mixed JSON string representation of input var or an error if a problem occurs
296
+ * @access public
297
+ */
298
+ function _encode($var)
299
+ {
300
+
301
+ switch (gettype($var)) {
302
+ case 'boolean':
303
+ return $var ? 'true' : 'false';
304
+
305
+ case 'NULL':
306
+ return 'null';
307
+
308
+ case 'integer':
309
+ return (int) $var;
310
+
311
+ case 'double':
312
+ case 'float':
313
+ return (float) $var;
314
+
315
+ case 'string':
316
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
317
+ $ascii = '';
318
+ $strlen_var = $this->strlen8($var);
319
+
320
+ /*
321
+ * Iterate over every character in the string,
322
+ * escaping with a slash or encoding to UTF-8 where necessary
323
+ */
324
+ for ($c = 0; $c < $strlen_var; ++$c) {
325
+
326
+ $ord_var_c = ord($var{$c});
327
+
328
+ switch (true) {
329
+ case $ord_var_c == 0x08:
330
+ $ascii .= '\b';
331
+ break;
332
+ case $ord_var_c == 0x09:
333
+ $ascii .= '\t';
334
+ break;
335
+ case $ord_var_c == 0x0A:
336
+ $ascii .= '\n';
337
+ break;
338
+ case $ord_var_c == 0x0C:
339
+ $ascii .= '\f';
340
+ break;
341
+ case $ord_var_c == 0x0D:
342
+ $ascii .= '\r';
343
+ break;
344
+
345
+ case $ord_var_c == 0x22:
346
+ case $ord_var_c == 0x2F:
347
+ case $ord_var_c == 0x5C:
348
+ // double quote, slash, slosh
349
+ $ascii .= '\\'.$var{$c};
350
+ break;
351
+
352
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
353
+ // characters U-00000000 - U-0000007F (same as ASCII)
354
+ $ascii .= $var{$c};
355
+ break;
356
+
357
+ case (($ord_var_c & 0xE0) == 0xC0):
358
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
359
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
360
+ if ($c+1 >= $strlen_var) {
361
+ $c += 1;
362
+ $ascii .= '?';
363
+ break;
364
+ }
365
+
366
+ $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
367
+ $c += 1;
368
+ $utf16 = $this->utf82utf16($char);
369
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
370
+ break;
371
+
372
+ case (($ord_var_c & 0xF0) == 0xE0):
373
+ if ($c+2 >= $strlen_var) {
374
+ $c += 2;
375
+ $ascii .= '?';
376
+ break;
377
+ }
378
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
379
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
380
+ $char = pack('C*', $ord_var_c,
381
+ @ord($var{$c + 1}),
382
+ @ord($var{$c + 2}));
383
+ $c += 2;
384
+ $utf16 = $this->utf82utf16($char);
385
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
386
+ break;
387
+
388
+ case (($ord_var_c & 0xF8) == 0xF0):
389
+ if ($c+3 >= $strlen_var) {
390
+ $c += 3;
391
+ $ascii .= '?';
392
+ break;
393
+ }
394
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
395
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
396
+ $char = pack('C*', $ord_var_c,
397
+ ord($var{$c + 1}),
398
+ ord($var{$c + 2}),
399
+ ord($var{$c + 3}));
400
+ $c += 3;
401
+ $utf16 = $this->utf82utf16($char);
402
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
403
+ break;
404
+
405
+ case (($ord_var_c & 0xFC) == 0xF8):
406
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
407
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
408
+ if ($c+4 >= $strlen_var) {
409
+ $c += 4;
410
+ $ascii .= '?';
411
+ break;
412
+ }
413
+ $char = pack('C*', $ord_var_c,
414
+ ord($var{$c + 1}),
415
+ ord($var{$c + 2}),
416
+ ord($var{$c + 3}),
417
+ ord($var{$c + 4}));
418
+ $c += 4;
419
+ $utf16 = $this->utf82utf16($char);
420
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
421
+ break;
422
+
423
+ case (($ord_var_c & 0xFE) == 0xFC):
424
+ if ($c+5 >= $strlen_var) {
425
+ $c += 5;
426
+ $ascii .= '?';
427
+ break;
428
+ }
429
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
430
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
431
+ $char = pack('C*', $ord_var_c,
432
+ ord($var{$c + 1}),
433
+ ord($var{$c + 2}),
434
+ ord($var{$c + 3}),
435
+ ord($var{$c + 4}),
436
+ ord($var{$c + 5}));
437
+ $c += 5;
438
+ $utf16 = $this->utf82utf16($char);
439
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
440
+ break;
441
+ }
442
+ }
443
+ return '"'.$ascii.'"';
444
+
445
+ case 'array':
446
+ /*
447
+ * As per JSON spec if any array key is not an integer
448
+ * we must treat the whole array as an object. We
449
+ * also try to catch a sparsely populated associative
450
+ * array with numeric keys here because some JS engines
451
+ * will create an array with empty indexes up to
452
+ * max_index which can cause memory issues and because
453
+ * the keys, which may be relevant, will be remapped
454
+ * otherwise.
455
+ *
456
+ * As per the ECMA and JSON specification an object may
457
+ * have any string as a property. Unfortunately due to
458
+ * a hole in the ECMA specification if the key is a
459
+ * ECMA reserved word or starts with a digit the
460
+ * parameter is only accessible using ECMAScript's
461
+ * bracket notation.
462
+ */
463
+
464
+ // treat as a JSON object
465
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
466
+ $properties = array_map(array($this, 'name_value'),
467
+ array_keys($var),
468
+ array_values($var));
469
+
470
+ foreach($properties as $property) {
471
+ if(wfServices_JSON::isError($property)) {
472
+ return $property;
473
+ }
474
+ }
475
+
476
+ return '{' . join(',', $properties) . '}';
477
+ }
478
+
479
+ // treat it like a regular array
480
+ $elements = array_map(array($this, '_encode'), $var);
481
+
482
+ foreach($elements as $element) {
483
+ if(wfServices_JSON::isError($element)) {
484
+ return $element;
485
+ }
486
+ }
487
+
488
+ return '[' . join(',', $elements) . ']';
489
+
490
+ case 'object':
491
+
492
+ // support toJSON methods.
493
+ if (($this->use & WF_SERVICES_JSON_USE_TO_JSON) && method_exists($var, 'toJSON')) {
494
+ // this may end up allowing unlimited recursion
495
+ // so we check the return value to make sure it's not got the same method.
496
+ $recode = $var->toJSON();
497
+
498
+ if (method_exists($recode, 'toJSON')) {
499
+
500
+ return ($this->use & WF_SERVICES_JSON_SUPPRESS_ERRORS)
501
+ ? 'null'
502
+ : new wfServices_JSON_Error(get_class($var).
503
+ " toJSON returned an object with a toJSON method.");
504
+
505
+ }
506
+
507
+ return $this->_encode( $recode );
508
+ }
509
+
510
+ $vars = get_object_vars($var);
511
+
512
+ $properties = array_map(array($this, 'name_value'),
513
+ array_keys($vars),
514
+ array_values($vars));
515
+
516
+ foreach($properties as $property) {
517
+ if(wfServices_JSON::isError($property)) {
518
+ return $property;
519
+ }
520
+ }
521
+
522
+ return '{' . join(',', $properties) . '}';
523
+
524
+ default:
525
+ return ($this->use & WF_SERVICES_JSON_SUPPRESS_ERRORS)
526
+ ? 'null'
527
+ : new wfServices_JSON_Error(gettype($var)." can not be encoded as JSON string");
528
+ }
529
+ }
530
+
531
+ /**
532
+ * array-walking function for use in generating JSON-formatted name-value pairs
533
+ *
534
+ * @param string $name name of key to use
535
+ * @param mixed $value reference to an array element to be encoded
536
+ *
537
+ * @return string JSON-formatted name-value pair, like '"name":value'
538
+ * @access private
539
+ */
540
+ function name_value($name, $value)
541
+ {
542
+ $encoded_value = $this->_encode($value);
543
+
544
+ if(wfServices_JSON::isError($encoded_value)) {
545
+ return $encoded_value;
546
+ }
547
+
548
+ return $this->_encode(strval($name)) . ':' . $encoded_value;
549
+ }
550
+
551
+ /**
552
+ * reduce a string by removing leading and trailing comments and whitespace
553
+ *
554
+ * @param $str string string value to strip of comments and whitespace
555
+ *
556
+ * @return string string value stripped of comments and whitespace
557
+ * @access private
558
+ */
559
+ function reduce_string($str)
560
+ {
561
+ $str = preg_replace(array(
562
+
563
+ // eliminate single line comments in '// ...' form
564
+ '#^\s*//(.+)$#m',
565
+
566
+ // eliminate multi-line comments in '/* ... */' form, at start of string
567
+ '#^\s*/\*(.+)\*/#Us',
568
+
569
+ // eliminate multi-line comments in '/* ... */' form, at end of string
570
+ '#/\*(.+)\*/\s*$#Us'
571
+
572
+ ), '', $str);
573
+
574
+ // eliminate extraneous space
575
+ return trim($str);
576
+ }
577
+
578
+ /**
579
+ * decodes a JSON string into appropriate variable
580
+ *
581
+ * @param string $str JSON-formatted string
582
+ *
583
+ * @return mixed number, boolean, string, array, or object
584
+ * corresponding to given JSON input string.
585
+ * See argument 1 to Services_JSON() above for object-output behavior.
586
+ * Note that decode() always returns strings
587
+ * in ASCII or UTF-8 format!
588
+ * @access public
589
+ */
590
+ function decode($str)
591
+ {
592
+ $str = $this->reduce_string($str);
593
+
594
+ switch (strtolower($str)) {
595
+ case 'true':
596
+ return true;
597
+
598
+ case 'false':
599
+ return false;
600
+
601
+ case 'null':
602
+ return null;
603
+
604
+ default:
605
+ $m = array();
606
+
607
+ if (is_numeric($str)) {
608
+ // Lookie-loo, it's a number
609
+
610
+ // This would work on its own, but I'm trying to be
611
+ // good about returning integers where appropriate:
612
+ // return (float)$str;
613
+
614
+ // Return float or int, as appropriate
615
+ return ((float)$str == (integer)$str)
616
+ ? (integer)$str
617
+ : (float)$str;
618
+
619
+ } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
620
+ // STRINGS RETURNED IN UTF-8 FORMAT
621
+ $delim = $this->substr8($str, 0, 1);
622
+ $chrs = $this->substr8($str, 1, -1);
623
+ $utf8 = '';
624
+ $strlen_chrs = $this->strlen8($chrs);
625
+
626
+ for ($c = 0; $c < $strlen_chrs; ++$c) {
627
+
628
+ $substr_chrs_c_2 = $this->substr8($chrs, $c, 2);
629
+ $ord_chrs_c = ord($chrs{$c});
630
+
631
+ switch (true) {
632
+ case $substr_chrs_c_2 == '\b':
633
+ $utf8 .= chr(0x08);
634
+ ++$c;
635
+ break;
636
+ case $substr_chrs_c_2 == '\t':
637
+ $utf8 .= chr(0x09);
638
+ ++$c;
639
+ break;
640
+ case $substr_chrs_c_2 == '\n':
641
+ $utf8 .= chr(0x0A);
642
+ ++$c;
643
+ break;
644
+ case $substr_chrs_c_2 == '\f':
645
+ $utf8 .= chr(0x0C);
646
+ ++$c;
647
+ break;
648
+ case $substr_chrs_c_2 == '\r':
649
+ $utf8 .= chr(0x0D);
650
+ ++$c;
651
+ break;
652
+
653
+ case $substr_chrs_c_2 == '\\"':
654
+ case $substr_chrs_c_2 == '\\\'':
655
+ case $substr_chrs_c_2 == '\\\\':
656
+ case $substr_chrs_c_2 == '\\/':
657
+ if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
658
+ ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
659
+ $utf8 .= $chrs{++$c};
660
+ }
661
+ break;
662
+
663
+ case preg_match('/\\\u[0-9A-F]{4}/i', $this->substr8($chrs, $c, 6)):
664
+ // single, escaped unicode character
665
+ $utf16 = chr(hexdec($this->substr8($chrs, ($c + 2), 2)))
666
+ . chr(hexdec($this->substr8($chrs, ($c + 4), 2)));
667
+ $utf8 .= $this->utf162utf8($utf16);
668
+ $c += 5;
669
+ break;
670
+
671
+ case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
672
+ $utf8 .= $chrs{$c};
673
+ break;
674
+
675
+ case ($ord_chrs_c & 0xE0) == 0xC0:
676
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
677
+ //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
678
+ $utf8 .= $this->substr8($chrs, $c, 2);
679
+ ++$c;
680
+ break;
681
+
682
+ case ($ord_chrs_c & 0xF0) == 0xE0:
683
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
684
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
685
+ $utf8 .= $this->substr8($chrs, $c, 3);
686
+ $c += 2;
687
+ break;
688
+
689
+ case ($ord_chrs_c & 0xF8) == 0xF0:
690
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
691
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
692
+ $utf8 .= $this->substr8($chrs, $c, 4);
693
+ $c += 3;
694
+ break;
695
+
696
+ case ($ord_chrs_c & 0xFC) == 0xF8:
697
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
698
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
699
+ $utf8 .= $this->substr8($chrs, $c, 5);
700
+ $c += 4;
701
+ break;
702
+
703
+ case ($ord_chrs_c & 0xFE) == 0xFC:
704
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
705
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
706
+ $utf8 .= $this->substr8($chrs, $c, 6);
707
+ $c += 5;
708
+ break;
709
+
710
+ }
711
+
712
+ }
713
+
714
+ return $utf8;
715
+
716
+ } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
717
+ // array, or object notation
718
+
719
+ if ($str{0} == '[') {
720
+ $stk = array(WF_SERVICES_JSON_IN_ARR);
721
+ $arr = array();
722
+ } else {
723
+ if ($this->use & WF_SERVICES_JSON_LOOSE_TYPE) {
724
+ $stk = array(WF_SERVICES_JSON_IN_OBJ);
725
+ $obj = array();
726
+ } else {
727
+ $stk = array(WF_SERVICES_JSON_IN_OBJ);
728
+ $obj = new stdClass();
729
+ }
730
+ }
731
+
732
+ array_push($stk, array('what' => WF_SERVICES_JSON_SLICE,
733
+ 'where' => 0,
734
+ 'delim' => false));
735
+
736
+ $chrs = $this->substr8($str, 1, -1);
737
+ $chrs = $this->reduce_string($chrs);
738
+
739
+ if ($chrs == '') {
740
+ if (reset($stk) == WF_SERVICES_JSON_IN_ARR) {
741
+ return $arr;
742
+
743
+ } else {
744
+ return $obj;
745
+
746
+ }
747
+ }
748
+
749
+ //print("\nparsing {$chrs}\n");
750
+
751
+ $strlen_chrs = $this->strlen8($chrs);
752
+
753
+ for ($c = 0; $c <= $strlen_chrs; ++$c) {
754
+
755
+ $top = end($stk);
756
+ $substr_chrs_c_2 = $this->substr8($chrs, $c, 2);
757
+
758
+ if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == WF_SERVICES_JSON_SLICE))) {
759
+ // found a comma that is not inside a string, array, etc.,
760
+ // OR we've reached the end of the character list
761
+ $slice = $this->substr8($chrs, $top['where'], ($c - $top['where']));
762
+ array_push($stk, array('what' => WF_SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
763
+ //print("Found split at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
764
+
765
+ if (reset($stk) == WF_SERVICES_JSON_IN_ARR) {
766
+ // we are in an array, so just push an element onto the stack
767
+ array_push($arr, $this->decode($slice));
768
+
769
+ } elseif (reset($stk) == WF_SERVICES_JSON_IN_OBJ) {
770
+ // we are in an object, so figure
771
+ // out the property name and set an
772
+ // element in an associative array,
773
+ // for now
774
+ $parts = array();
775
+
776
+ if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:/Uis', $slice, $parts)) {
777
+ // "name":value pair
778
+ $key = $this->decode($parts[1]);
779
+ $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B"));
780
+ if ($this->use & WF_SERVICES_JSON_LOOSE_TYPE) {
781
+ $obj[$key] = $val;
782
+ } else {
783
+ $obj->$key = $val;
784
+ }
785
+ } elseif (preg_match('/^\s*(\w+)\s*:/Uis', $slice, $parts)) {
786
+ // name:value pair, where name is unquoted
787
+ $key = $parts[1];
788
+ $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B"));
789
+
790
+ if ($this->use & WF_SERVICES_JSON_LOOSE_TYPE) {
791
+ $obj[$key] = $val;
792
+ } else {
793
+ $obj->$key = $val;
794
+ }
795
+ }
796
+
797
+ }
798
+
799
+ } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != WF_SERVICES_JSON_IN_STR)) {
800
+ // found a quote, and we are not inside a string
801
+ array_push($stk, array('what' => WF_SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
802
+ //print("Found start of string at {$c}\n");
803
+
804
+ } elseif (($chrs{$c} == $top['delim']) &&
805
+ ($top['what'] == WF_SERVICES_JSON_IN_STR) &&
806
+ (($this->strlen8($this->substr8($chrs, 0, $c)) - $this->strlen8(rtrim($this->substr8($chrs, 0, $c), '\\'))) % 2 != 1)) {
807
+ // found a quote, we're in a string, and it's not escaped
808
+ // we know that it's not escaped becase there is _not_ an
809
+ // odd number of backslashes at the end of the string so far
810
+ array_pop($stk);
811
+ //print("Found end of string at {$c}: ".$this->substr8($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
812
+
813
+ } elseif (($chrs{$c} == '[') &&
814
+ in_array($top['what'], array(WF_SERVICES_JSON_SLICE, WF_SERVICES_JSON_IN_ARR, WF_SERVICES_JSON_IN_OBJ))) {
815
+ // found a left-bracket, and we are in an array, object, or slice
816
+ array_push($stk, array('what' => WF_SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
817
+ //print("Found start of array at {$c}\n");
818
+
819
+ } elseif (($chrs{$c} == ']') && ($top['what'] == WF_SERVICES_JSON_IN_ARR)) {
820
+ // found a right-bracket, and we're in an array
821
+ array_pop($stk);
822
+ //print("Found end of array at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
823
+
824
+ } elseif (($chrs{$c} == '{') &&
825
+ in_array($top['what'], array(WF_SERVICES_JSON_SLICE, WF_SERVICES_JSON_IN_ARR, WF_SERVICES_JSON_IN_OBJ))) {
826
+ // found a left-brace, and we are in an array, object, or slice
827
+ array_push($stk, array('what' => WF_SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
828
+ //print("Found start of object at {$c}\n");
829
+
830
+ } elseif (($chrs{$c} == '}') && ($top['what'] == WF_SERVICES_JSON_IN_OBJ)) {
831
+ // found a right-brace, and we're in an object
832
+ array_pop($stk);
833
+ //print("Found end of object at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
834
+
835
+ } elseif (($substr_chrs_c_2 == '/*') &&
836
+ in_array($top['what'], array(WF_SERVICES_JSON_SLICE, WF_SERVICES_JSON_IN_ARR, WF_SERVICES_JSON_IN_OBJ))) {
837
+ // found a comment start, and we are in an array, object, or slice
838
+ array_push($stk, array('what' => WF_SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
839
+ $c++;
840
+ //print("Found start of comment at {$c}\n");
841
+
842
+ } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == WF_SERVICES_JSON_IN_CMT)) {
843
+ // found a comment end, and we're in one now
844
+ array_pop($stk);
845
+ $c++;
846
+
847
+ for ($i = $top['where']; $i <= $c; ++$i)
848
+ $chrs = substr_replace($chrs, ' ', $i, 1);
849
+
850
+ //print("Found end of comment at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
851
+
852
+ }
853
+
854
+ }
855
+
856
+ if (reset($stk) == WF_SERVICES_JSON_IN_ARR) {
857
+ return $arr;
858
+
859
+ } elseif (reset($stk) == WF_SERVICES_JSON_IN_OBJ) {
860
+ return $obj;
861
+
862
+ }
863
+
864
+ }
865
+ }
866
+ }
867
+
868
+ /**
869
+ * @todo Ultimately, this should just call PEAR::isError()
870
+ */
871
+ function isError($data, $code = null)
872
+ {
873
+ if (class_exists('pear')) {
874
+ return PEAR::isError($data, $code);
875
+ } elseif (is_object($data) && (get_class($data) == 'wfservices_json_error' ||
876
+ is_subclass_of($data, 'wfServices_JSON_Error'))) {
877
+ return true;
878
+ }
879
+
880
+ return false;
881
+ }
882
+
883
+ /**
884
+ * Calculates length of string in bytes
885
+ * @param string
886
+ * @return integer length
887
+ */
888
+ function strlen8( $str )
889
+ {
890
+ if ( $this->_mb_strlen ) {
891
+ return mb_strlen( $str, "8bit" );
892
+ }
893
+ return strlen( $str );
894
+ }
895
+
896
+ /**
897
+ * Returns part of a string, interpreting $start and $length as number of bytes.
898
+ * @param string
899
+ * @param integer start
900
+ * @param integer length
901
+ * @return integer length
902
+ */
903
+ function substr8( $string, $start, $length=false )
904
+ {
905
+ if ( $length === false ) {
906
+ $length = $this->strlen8( $string ) - $start;
907
+ }
908
+ if ( $this->_mb_substr ) {
909
+ return mb_substr( $string, $start, $length, "8bit" );
910
+ }
911
+ return substr( $string, $start, $length );
912
+ }
913
+
914
+ }
915
+
916
+ if (class_exists('PEAR_Error')) {
917
+
918
+ class wfServices_JSON_Error extends PEAR_Error
919
+ {
920
+ function __construct($message = 'unknown error', $code = null,
921
+ $mode = null, $options = null, $userinfo = null)
922
+ {
923
+ parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
924
+ }
925
+
926
+ public function wfServices_JSON_Error($message = 'unknown error', $code = null,
927
+ $mode = null, $options = null, $userinfo = null) {
928
+ self::__construct($message = 'unknown error', $code = null,
929
+ $mode = null, $options = null, $userinfo = null);
930
+ }
931
+ }
932
+
933
+ } else {
934
+
935
+ /**
936
+ * @todo Ultimately, this class shall be descended from PEAR_Error
937
+ */
938
+ class wfServices_JSON_Error
939
+ {
940
+ /**
941
+ * PHP5 constructor.
942
+ */
943
+ function __construct( $message = 'unknown error', $code = null,
944
+ $mode = null, $options = null, $userinfo = null )
945
+ {
946
+
947
+ }
948
+
949
+ /**
950
+ * PHP4 constructor.
951
+ */
952
+ public function wfServices_JSON_Error( $message = 'unknown error', $code = null,
953
+ $mode = null, $options = null, $userinfo = null ) {
954
+ self::__construct( $message, $code, $mode, $options, $userinfo );
955
+ }
956
+ }
957
+
958
+ }
vendor/wordfence/wf-waf/src/lib/parser/lexer.php CHANGED
@@ -84,7 +84,7 @@ class wfWAFRuleLexer implements wfWAFLexerInterface {
84
  return false;
85
  }
86
  if (($match = $this->scanner->scan(self::MATCH_IDENTIFIER)) !== null)
87
- switch (strtolower($match)) {
88
  case 'if':
89
  return $this->createToken(self::T_RULE_START, $match);
90
  case 'and':
@@ -172,14 +172,14 @@ class wfWAFLexerToken {
172
  * @return string
173
  */
174
  public function getLowerCaseValue() {
175
- return strtolower($this->getValue());
176
  }
177
 
178
  /**
179
  * @return string
180
  */
181
  public function getUpperCaseValue() {
182
- return strtoupper($this->getValue());
183
  }
184
 
185
  /**
@@ -442,8 +442,8 @@ class wfWAFStringScanner {
442
  public function scan($regex) {
443
  $remaining = $this->getRemainingString();
444
  if ($this->regexMatch($regex, $remaining, $matches)) {
445
- $matchLen = strlen($matches[0]);
446
- if ($matchLen > 0 && strpos($remaining, $matches[0]) === 0) {
447
  return $this->setState($matches, $this->getPointer() + $matchLen, $this->getPointer());
448
  }
449
  }
@@ -455,7 +455,7 @@ class wfWAFStringScanner {
455
  * @return int|null
456
  */
457
  public function skip($regex) {
458
- return $this->scan($regex) ? strlen($this->getMatch()) : null;
459
  }
460
 
461
  /**
@@ -472,8 +472,8 @@ class wfWAFStringScanner {
472
  public function check($regex) {
473
  $remaining = $this->getRemainingString();
474
  if ($this->regexMatch($regex, $remaining, $matches)) {
475
- $matchLen = strlen($matches[0]);
476
- if ($matchLen > 0 && strpos($remaining, $matches[0]) === 0) {
477
  return $this->setState($matches);
478
  }
479
  }
@@ -504,7 +504,7 @@ class wfWAFStringScanner {
504
  * @return string
505
  */
506
  public function getRemainingString() {
507
- return substr($this->getString(), $this->getPointer());
508
  }
509
 
510
  /**
@@ -522,9 +522,9 @@ class wfWAFStringScanner {
522
  */
523
  public function getLine() {
524
  if ($this->getPointer() + 1 > $this->getLength()) {
525
- return substr_count($this->getString(), "\n") + 1;
526
  }
527
- return substr_count($this->getString(), "\n", 0, $this->getPointer() + 1) + 1;
528
  }
529
 
530
  /**
@@ -533,7 +533,7 @@ class wfWAFStringScanner {
533
  * @return int
534
  */
535
  public function getColumn() {
536
- return $this->getPointer() - ((int) strrpos(substr($this->getString(), 0, $this->getPointer() + 1), "\n")) + 1;
537
  }
538
 
539
  /**
@@ -577,7 +577,7 @@ class wfWAFStringScanner {
577
  if (!is_string($string)) {
578
  throw new InvalidArgumentException(sprintf('String expected, got [%s]', gettype($string)));
579
  }
580
- $this->setLength(strlen($string));
581
  $this->string = $string;
582
  $this->reset();
583
  }
84
  return false;
85
  }
86
  if (($match = $this->scanner->scan(self::MATCH_IDENTIFIER)) !== null)
87
+ switch (wfWAFUtils::strtolower($match)) {
88
  case 'if':
89
  return $this->createToken(self::T_RULE_START, $match);
90
  case 'and':
172
  * @return string
173
  */
174
  public function getLowerCaseValue() {
175
+ return wfWAFUtils::strtolower($this->getValue());
176
  }
177
 
178
  /**
179
  * @return string
180
  */
181
  public function getUpperCaseValue() {
182
+ return wfWAFUtils::strtoupper($this->getValue());
183
  }
184
 
185
  /**
442
  public function scan($regex) {
443
  $remaining = $this->getRemainingString();
444
  if ($this->regexMatch($regex, $remaining, $matches)) {
445
+ $matchLen = wfWAFUtils::strlen($matches[0]);
446
+ if ($matchLen > 0 && wfWAFUtils::strpos($remaining, $matches[0]) === 0) {
447
  return $this->setState($matches, $this->getPointer() + $matchLen, $this->getPointer());
448
  }
449
  }
455
  * @return int|null
456
  */
457
  public function skip($regex) {
458
+ return $this->scan($regex) ? wfWAFUtils::strlen($this->getMatch()) : null;
459
  }
460
 
461
  /**
472
  public function check($regex) {
473
  $remaining = $this->getRemainingString();
474
  if ($this->regexMatch($regex, $remaining, $matches)) {
475
+ $matchLen = wfWAFUtils::strlen($matches[0]);
476
+ if ($matchLen > 0 && wfWAFUtils::strpos($remaining, $matches[0]) === 0) {
477
  return $this->setState($matches);
478
  }
479
  }
504
  * @return string
505
  */
506
  public function getRemainingString() {
507
+ return wfWAFUtils::substr($this->getString(), $this->getPointer());
508
  }
509
 
510
  /**
522
  */
523
  public function getLine() {
524
  if ($this->getPointer() + 1 > $this->getLength()) {
525
+ return wfWAFUtils::substr_count($this->getString(), "\n") + 1;
526
  }
527
+ return wfWAFUtils::substr_count($this->getString(), "\n", 0, $this->getPointer() + 1) + 1;
528
  }
529
 
530
  /**
533
  * @return int
534
  */
535
  public function getColumn() {
536
+ return $this->getPointer() - ((int) wfWAFUtils::strrpos(wfWAFUtils::substr($this->getString(), 0, $this->getPointer() + 1), "\n")) + 1;
537
  }
538
 
539
  /**
577
  if (!is_string($string)) {
578
  throw new InvalidArgumentException(sprintf('String expected, got [%s]', gettype($string)));
579
  }
580
+ $this->setLength(wfWAFUtils::strlen($string));
581
  $this->string = $string;
582
  $this->reset();
583
  }
vendor/wordfence/wf-waf/src/lib/parser/parser.php CHANGED
@@ -87,10 +87,10 @@ class wfWAFRuleParser extends wfWAFBaseParser {
87
  wfWAFRuleLexer::T_NUMBER_LITERAL,
88
  ));
89
  if ($valueToken->getType() === wfWAFRuleLexer::T_SINGLE_STRING_LITERAL) {
90
- $value = substr($valueToken->getValue(), 1, -1);
91
  $value = str_replace("\\'", "'", $value);
92
  } else if ($valueToken->getType() === wfWAFRuleLexer::T_DOUBLE_STRING_LITERAL) {
93
- $value = substr($valueToken->getValue(), 1, -1);
94
  $value = str_replace('\\"', '"', $value);
95
  } else {
96
  $value = $valueToken->getValue();
@@ -383,11 +383,11 @@ class wfWAFRuleParser extends wfWAFBaseParser {
383
  ));
384
  if ($expectedToken->getType() === wfWAFRuleLexer::T_SINGLE_STRING_LITERAL) {
385
  // Remove quotes, strip slashes
386
- $value = substr($expectedToken->getValue(), 1, -1);
387
  $value = str_replace("\\'", "'", $value);
388
  } else if ($expectedToken->getType() === wfWAFRuleLexer::T_DOUBLE_STRING_LITERAL) {
389
  // Remove quotes, strip slashes
390
- $value = substr($expectedToken->getValue(), 1, -1);
391
  $value = str_replace('\\"', '"', $value);
392
  } else if ($expectedToken->getType() === wfWAFRuleLexer::T_IDENTIFIER) {
393
  // Remove quotes, strip slashes
87
  wfWAFRuleLexer::T_NUMBER_LITERAL,
88
  ));
89
  if ($valueToken->getType() === wfWAFRuleLexer::T_SINGLE_STRING_LITERAL) {
90
+ $value = wfWAFUtils::substr($valueToken->getValue(), 1, -1);
91
  $value = str_replace("\\'", "'", $value);
92
  } else if ($valueToken->getType() === wfWAFRuleLexer::T_DOUBLE_STRING_LITERAL) {
93
+ $value = wfWAFUtils::substr($valueToken->getValue(), 1, -1);
94
  $value = str_replace('\\"', '"', $value);
95
  } else {
96
  $value = $valueToken->getValue();
383
  ));
384
  if ($expectedToken->getType() === wfWAFRuleLexer::T_SINGLE_STRING_LITERAL) {
385
  // Remove quotes, strip slashes
386
+ $value = wfWAFUtils::substr($expectedToken->getValue(), 1, -1);
387
  $value = str_replace("\\'", "'", $value);
388
  } else if ($expectedToken->getType() === wfWAFRuleLexer::T_DOUBLE_STRING_LITERAL) {
389
  // Remove quotes, strip slashes
390
+ $value = wfWAFUtils::substr($expectedToken->getValue(), 1, -1);
391
  $value = str_replace('\\"', '"', $value);
392
  } else if ($expectedToken->getType() === wfWAFRuleLexer::T_IDENTIFIER) {
393
  // Remove quotes, strip slashes
vendor/wordfence/wf-waf/src/lib/parser/sqli.php CHANGED
@@ -649,7 +649,7 @@ class wfWAFSQLiParser extends wfWAFBaseParser {
649
  protected function expectNextIdentifierEquals($keyword) {
650
  $nextToken = $this->expectNextToken();
651
  $this->expectTokenTypeEquals($nextToken, wfWAFSQLiLexer::UNQUOTED_IDENTIFIER);
652
- if ($nextToken->getLowerCaseValue() !== strtolower($keyword)) {
653
  $this->triggerSyntaxError($nextToken);
654
  }
655
  return $nextToken;
@@ -2558,8 +2558,8 @@ class wfWAFSQLiParser extends wfWAFBaseParser {
2558
  */
2559
  private function isIdentifierWithValue($token, $value) {
2560
  return $token && $token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER &&
2561
- (is_array($value) ? in_array($token->getLowerCaseValue(), array_map('strtolower', $value)) :
2562
- $token->getLowerCaseValue() === strtolower($value));
2563
  }
2564
 
2565
  /**
@@ -2835,12 +2835,12 @@ class wfWAFSQLiLexer implements wfWAFLexerInterface {
2835
  }
2836
  if (($match2 = $this->scanner->check($tokenMatcher2->getMatch())) !== null) {
2837
  $biggestToken2 = $this->createToken($tokenMatcher2->getTokenID(), $match2);
2838
- if (strlen($biggestToken2->getValue()) > strlen($biggestToken->getValue())) {
2839
  $biggestToken = $biggestToken2;
2840
  }
2841
  }
2842
  }
2843
- $this->scanner->advancePointer(strlen($biggestToken->getValue()));
2844
  return $biggestToken;
2845
 
2846
  } else if (($match = $this->scanner->scan($tokenMatcher->getMatch())) !== null) {
649
  protected function expectNextIdentifierEquals($keyword) {
650
  $nextToken = $this->expectNextToken();
651
  $this->expectTokenTypeEquals($nextToken, wfWAFSQLiLexer::UNQUOTED_IDENTIFIER);
652
+ if ($nextToken->getLowerCaseValue() !== wfWAFUtils::strtolower($keyword)) {
653
  $this->triggerSyntaxError($nextToken);
654
  }
655
  return $nextToken;
2558
  */
2559
  private function isIdentifierWithValue($token, $value) {
2560
  return $token && $token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER &&
2561
+ (is_array($value) ? in_array($token->getLowerCaseValue(), array_map('wfWAFUtils::strtolower', $value)) :
2562
+ $token->getLowerCaseValue() === wfWAFUtils::strtolower($value));
2563
  }
2564
 
2565
  /**
2835
  }
2836
  if (($match2 = $this->scanner->check($tokenMatcher2->getMatch())) !== null) {
2837
  $biggestToken2 = $this->createToken($tokenMatcher2->getTokenID(), $match2);
2838
+ if (wfWAFUtils::strlen($biggestToken2->getValue()) > wfWAFUtils::strlen($biggestToken->getValue())) {
2839
  $biggestToken = $biggestToken2;
2840
  }
2841
  }
2842
  }
2843
+ $this->scanner->advancePointer(wfWAFUtils::strlen($biggestToken->getValue()));
2844
  return $biggestToken;
2845
 
2846
  } else if (($match = $this->scanner->scan($tokenMatcher->getMatch())) !== null) {
vendor/wordfence/wf-waf/src/lib/request.php CHANGED
@@ -77,12 +77,12 @@ class wfWAFRequest implements wfWAFRequestInterface {
77
  $request->setMethod($matches[1]);
78
  $uri = $matches[2];
79
  $request->setUri($uri);
80
- if (($pos = strpos($uri, '?')) !== false) {
81
- $queryString = substr($uri, $pos + 1);
82
  parse_str($queryString, $queryStringArray);
83
  $request->setQueryString($queryStringArray);
84
 
85
- $path = substr($uri, 0, $pos);
86
  $request->setPath($path);
87
  } else {
88
  $request->setPath($uri);
@@ -96,7 +96,7 @@ class wfWAFRequest implements wfWAFRequestInterface {
96
  $headerValue = trim($headerValue);
97
  $kvHeaders[$header] = $headerValue;
98
 
99
- switch (strtolower($header)) {
100
  case 'authorization':
101
  if (preg_match('/basic ([A-Za-z0-9\+\/=]+)/i', $headerValue, $matches)) {
102
  list($authUser, $authPass) = explode(':', base64_decode($matches[1]), 2);
@@ -114,7 +114,7 @@ class wfWAFRequest implements wfWAFRequestInterface {
114
  $cookieArray = array();
115
  $cookies = explode(';', $headerValue);
116
  foreach ($cookies as $cookie) {
117
- if (strpos($cookie, '=') !== false) {
118
  list($cookieName, $cookieValue) = explode('=', $cookie, 2);
119
  $cookieArray[trim($cookieName)] = urldecode(trim($cookieValue));
120
  }
@@ -126,7 +126,7 @@ class wfWAFRequest implements wfWAFRequestInterface {
126
  }
127
  $request->setHeaders($kvHeaders);
128
 
129
- if (strlen($bodyString) > 0) {
130
  if (preg_match('/^multipart\/form\-data; boundary=(.*?)$/i', $request->getHeaders('Content-Type'), $boundaryMatches)) {
131
  $body = '';
132
  $files = array();
@@ -142,10 +142,10 @@ class wfWAFRequest implements wfWAFRequestInterface {
142
  list($chunkHeaders, $chunkData) = explode("\n\n", $chunk, 2);
143
  $chunkHeaders = explode("\n", $chunkHeaders);
144
  $param = array(
145
- 'value' => substr($chunkData, 0, -1),
146
  );
147
  foreach ($chunkHeaders as $chunkHeader) {
148
- if (strpos($chunkHeader, ':') !== false) {
149
  list($chunkHeaderKey, $chunkHeaderValue) = explode(':', $chunkHeader, 2);
150
  $chunkHeaderKey = trim($chunkHeaderKey);
151
  $chunkHeaderValue = trim($chunkHeaderValue);
@@ -175,7 +175,7 @@ class wfWAFRequest implements wfWAFRequestInterface {
175
  $files[$param['name']] = array(
176
  'name' => $param['filename'],
177
  'type' => $param['type'],
178
- 'size' => strlen($param['value']),
179
  'content' => $param['value'],
180
  );
181
  $fileNames[$param['name']] = $param['filename'];
@@ -252,31 +252,32 @@ class wfWAFRequest implements wfWAFRequestInterface {
252
  }
253
  $request->setFileNames($fileNames);
254
  }
255
- $auth = array();
256
- if (array_key_exists('PHP_AUTH_USER', $_SERVER)) {
257
- $auth['user'] = wfWAFUtils::stripMagicQuotes($_SERVER['PHP_AUTH_USER']);
258
- }
259
- if (array_key_exists('PHP_AUTH_PW', $_SERVER)) {
260
- $auth['password'] = wfWAFUtils::stripMagicQuotes($_SERVER['PHP_AUTH_PW']);
261
- }
262
- $request->setAuth($auth);
263
 
264
- if (array_key_exists('REQUEST_TIME_FLOAT', $_SERVER)) {
265
- $timestamp = $_SERVER['REQUEST_TIME_FLOAT'];
266
- } else if (array_key_exists('REQUEST_TIME', $_SERVER)) {
267
- $timestamp = $_SERVER['REQUEST_TIME'];
268
- } else {
269
- $timestamp = time();
270
- }
271
- $request->setTimestamp($timestamp);
 
272
 
273
- $headers = array();
274
- if (!empty($_SERVER)) {
 
 
 
 
 
 
 
 
275
  foreach ($_SERVER as $key => $value) {
276
- if (strpos($key, 'HTTP_') === 0) {
277
- $header = substr($key, 5);
278
  $header = str_replace(array(' ', '_'), array('', ' '), $header);
279
- $header = ucwords(strtolower($header));
280
  $header = str_replace(' ', '-', $header);
281
  $headers[$header] = wfWAFUtils::stripMagicQuotes($value);
282
  }
@@ -287,28 +288,28 @@ class wfWAFRequest implements wfWAFRequestInterface {
287
  if (array_key_exists('CONTENT_LENGTH', $_SERVER)) {
288
  $headers['Content-Length'] = wfWAFUtils::stripMagicQuotes($_SERVER['CONTENT_LENGTH']);
289
  }
290
- }
291
- $request->setHeaders($headers);
292
 
293
- $host = '';
294
- if (array_key_exists('Host', $headers)) {
295
- $host = $headers['Host'];
296
- } else if (array_key_exists('SERVER_NAME', $_SERVER)) {
297
- $host = wfWAFUtils::stripMagicQuotes($_SERVER['SERVER_NAME']);
298
- }
299
- $request->setHost($host);
300
 
301
- $request->setMethod(array_key_exists('REQUEST_METHOD', $_SERVER) ? wfWAFUtils::stripMagicQuotes($_SERVER['REQUEST_METHOD']) : 'GET');
302
- $request->setProtocol((array_key_exists('HTTPS', $_SERVER) && $_SERVER['HTTPS'] && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http');
303
- $request->setUri(array_key_exists('REQUEST_URI', $_SERVER) ? wfWAFUtils::stripMagicQuotes($_SERVER['REQUEST_URI']) : '');
304
 
305
- $uri = parse_url($request->getURI());
306
- if (is_array($uri) && array_key_exists('path', $uri)) {
307
- $path = $uri['path'];
308
- } else {
309
- $path = $request->getURI();
 
 
310
  }
311
- $request->setPath($path);
312
 
313
  return $request;
314
  }
@@ -486,9 +487,9 @@ class wfWAFRequest implements wfWAFRequestInterface {
486
  }
487
 
488
  $uri = $this->getURI();
489
- $queryStringPos = strpos($uri, '?');
490
  if ($queryStringPos !== false) {
491
- $uri = substr($uri, 0, $queryStringPos);
492
  }
493
  $queryString = $this->getQueryString();
494
  if ($queryString) {
@@ -520,7 +521,7 @@ class wfWAFRequest implements wfWAFRequestInterface {
520
 
521
  if (is_array($this->getHeaders())) {
522
  foreach ($this->getHeaders() as $header => $value) {
523
- switch (strtolower($header)) {
524
  case 'cookie':
525
  // TODO: Hook up highlights to cookies
526
  $cookies = '';
@@ -555,7 +556,7 @@ class wfWAFRequest implements wfWAFRequestInterface {
555
  $body = $this->getBody();
556
  $contentType = $this->getHeaders('Content-Type');
557
  if (is_array($body)) {
558
- if (stripos($contentType, 'application/x-www-form-urlencoded') === 0) {
559
  $body = http_build_query($body);
560
  if (!empty($highlights['body'])) {
561
  foreach ($highlights['body'] as $matches) {
@@ -611,7 +612,7 @@ FORM;
611
  }
612
  $mime = array_key_exists('type', $file) ? $file['type'] : '';
613
  $value = '';
614
- $lenToRead = $maxRequestLen - (strlen($request) + strlen($body) + 1);
615
  if (array_key_exists('content', $file)) {
616
  $value = $file['content'];
617
  } else if ($lenToRead > 0 && file_exists($file['tmp_name'])) {
@@ -652,8 +653,8 @@ FORM;
652
 
653
  $request .= "\n" . $body;
654
 
655
- if (strlen($request) > $maxRequestLen) {
656
- $request = substr($request, 0, $maxRequestLen);
657
  }
658
  return $request;
659
  }
@@ -682,7 +683,7 @@ FORM;
682
  $value = str_replace($param, sprintf($this->highlightMatchFormat, $param), $matches[3]);
683
  }
684
  }
685
- if (strlen($value) === 0) {
686
  $value = sprintf($this->highlightMatchFormat, $value);
687
  }
688
 
@@ -706,7 +707,7 @@ FORM;
706
  if (is_array($value)) {
707
  $param = array();
708
  foreach ($value as $index => $val) {
709
- $param = array_merge($param, $this->reduceBodyParameter("$key[$index]", $val));
710
  }
711
  return $param;
712
  }
77
  $request->setMethod($matches[1]);
78
  $uri = $matches[2];
79
  $request->setUri($uri);
80
+ if (($pos = wfWAFUtils::strpos($uri, '?')) !== false) {
81
+ $queryString = wfWAFUtils::substr($uri, $pos + 1);
82
  parse_str($queryString, $queryStringArray);
83
  $request->setQueryString($queryStringArray);
84
 
85
+ $path = wfWAFUtils::substr($uri, 0, $pos);
86
  $request->setPath($path);
87
  } else {
88
  $request->setPath($uri);
96
  $headerValue = trim($headerValue);
97
  $kvHeaders[$header] = $headerValue;
98
 
99
+ switch (wfWAFUtils::strtolower($header)) {
100
  case 'authorization':
101
  if (preg_match('/basic ([A-Za-z0-9\+\/=]+)/i', $headerValue, $matches)) {
102
  list($authUser, $authPass) = explode(':', base64_decode($matches[1]), 2);
114
  $cookieArray = array();
115
  $cookies = explode(';', $headerValue);
116
  foreach ($cookies as $cookie) {
117
+ if (wfWAFUtils::strpos($cookie, '=') !== false) {
118
  list($cookieName, $cookieValue) = explode('=', $cookie, 2);
119
  $cookieArray[trim($cookieName)] = urldecode(trim($cookieValue));
120
  }
126
  }
127
  $request->setHeaders($kvHeaders);
128
 
129
+ if (wfWAFUtils::strlen($bodyString) > 0) {
130
  if (preg_match('/^multipart\/form\-data; boundary=(.*?)$/i', $request->getHeaders('Content-Type'), $boundaryMatches)) {
131
  $body = '';
132
  $files = array();
142
  list($chunkHeaders, $chunkData) = explode("\n\n", $chunk, 2);
143
  $chunkHeaders = explode("\n", $chunkHeaders);
144
  $param = array(
145
+ 'value' => wfWAFUtils::substr($chunkData, 0, -1),
146
  );
147
  foreach ($chunkHeaders as $chunkHeader) {
148
+ if (wfWAFUtils::strpos($chunkHeader, ':') !== false) {
149
  list($chunkHeaderKey, $chunkHeaderValue) = explode(':', $chunkHeader, 2);
150
  $chunkHeaderKey = trim($chunkHeaderKey);
151
  $chunkHeaderValue = trim($chunkHeaderValue);
175
  $files[$param['name']] = array(
176
  'name' => $param['filename'],
177
  'type' => $param['type'],
178
+ 'size' => wfWAFUtils::strlen($param['value']),
179
  'content' => $param['value'],
180
  );
181
  $fileNames[$param['name']] = $param['filename'];
252
  }
253
  $request->setFileNames($fileNames);
254
  }
 
 
 
 
 
 
 
 
255
 
256
+ if (is_array($_SERVER)) { //All of these depend on $_SERVER being non-null and an array
257
+ $auth = array();
258
+ if (array_key_exists('PHP_AUTH_USER', $_SERVER)) {
259
+ $auth['user'] = wfWAFUtils::stripMagicQuotes($_SERVER['PHP_AUTH_USER']);
260
+ }
261
+ if (array_key_exists('PHP_AUTH_PW', $_SERVER)) {
262
+ $auth['password'] = wfWAFUtils::stripMagicQuotes($_SERVER['PHP_AUTH_PW']);
263
+ }
264
+ $request->setAuth($auth);
265
 
266
+ if (array_key_exists('REQUEST_TIME_FLOAT', $_SERVER)) {
267
+ $timestamp = $_SERVER['REQUEST_TIME_FLOAT'];
268
+ } else if (array_key_exists('REQUEST_TIME', $_SERVER)) {
269
+ $timestamp = $_SERVER['REQUEST_TIME'];
270
+ } else {
271
+ $timestamp = time();
272
+ }
273
+ $request->setTimestamp($timestamp);
274
+
275
+ $headers = array();
276
  foreach ($_SERVER as $key => $value) {
277
+ if (wfWAFUtils::strpos($key, 'HTTP_') === 0) {
278
+ $header = wfWAFUtils::substr($key, 5);
279
  $header = str_replace(array(' ', '_'), array('', ' '), $header);
280
+ $header = ucwords(wfWAFUtils::strtolower($header));
281
  $header = str_replace(' ', '-', $header);
282
  $headers[$header] = wfWAFUtils::stripMagicQuotes($value);
283
  }
288
  if (array_key_exists('CONTENT_LENGTH', $_SERVER)) {
289
  $headers['Content-Length'] = wfWAFUtils::stripMagicQuotes($_SERVER['CONTENT_LENGTH']);
290
  }
291
+ $request->setHeaders($headers);
 
292
 
293
+ $host = '';
294
+ if (array_key_exists('Host', $headers)) {
295
+ $host = $headers['Host'];
296
+ } else if (array_key_exists('SERVER_NAME', $_SERVER)) {
297
+ $host = wfWAFUtils::stripMagicQuotes($_SERVER['SERVER_NAME']);
298
+ }
299
+ $request->setHost($host);
300
 
301
+ $request->setMethod(array_key_exists('REQUEST_METHOD', $_SERVER) ? wfWAFUtils::stripMagicQuotes($_SERVER['REQUEST_METHOD']) : 'GET');
302
+ $request->setProtocol((array_key_exists('HTTPS', $_SERVER) && $_SERVER['HTTPS'] && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http');
303
+ $request->setUri(array_key_exists('REQUEST_URI', $_SERVER) ? wfWAFUtils::stripMagicQuotes($_SERVER['REQUEST_URI']) : '');
304
 
305
+ $uri = parse_url($request->getURI());
306
+ if (is_array($uri) && array_key_exists('path', $uri)) {
307
+ $path = $uri['path'];
308
+ } else {
309
+ $path = $request->getURI();
310
+ }
311
+ $request->setPath($path);
312
  }
 
313
 
314
  return $request;
315
  }
487
  }
488
 
489
  $uri = $this->getURI();
490
+ $queryStringPos = wfWAFUtils::strpos($uri, '?');
491
  if ($queryStringPos !== false) {
492
+ $uri = wfWAFUtils::substr($uri, 0, $queryStringPos);
493
  }
494
  $queryString = $this->getQueryString();
495
  if ($queryString) {
521
 
522
  if (is_array($this->getHeaders())) {
523
  foreach ($this->getHeaders() as $header => $value) {
524
+ switch (wfWAFUtils::strtolower($header)) {
525
  case 'cookie':
526
  // TODO: Hook up highlights to cookies
527
  $cookies = '';
556
  $body = $this->getBody();
557
  $contentType = $this->getHeaders('Content-Type');
558
  if (is_array($body)) {
559
+ if (wfWAFUtils::stripos($contentType, 'application/x-www-form-urlencoded') === 0) {
560
  $body = http_build_query($body);
561
  if (!empty($highlights['body'])) {
562
  foreach ($highlights['body'] as $matches) {
612
  }
613
  $mime = array_key_exists('type', $file) ? $file['type'] : '';
614
  $value = '';
615
+ $lenToRead = $maxRequestLen - (wfWAFUtils::strlen($request) + wfWAFUtils::strlen($body) + 1);
616
  if (array_key_exists('content', $file)) {
617
  $value = $file['content'];
618
  } else if ($lenToRead > 0 && file_exists($file['tmp_name'])) {
653
 
654
  $request .= "\n" . $body;
655
 
656
+ if (wfWAFUtils::strlen($request) > $maxRequestLen) {
657
+ $request = wfWAFUtils::substr($request, 0, $maxRequestLen);
658
  }
659
  return $request;
660
  }
683
  $value = str_replace($param, sprintf($this->highlightMatchFormat, $param), $matches[3]);
684
  }
685
  }
686
+ if (wfWAFUtils::strlen($value) === 0) {
687
  $value = sprintf($this->highlightMatchFormat, $value);
688
  }
689
 
707
  if (is_array($value)) {
708
  $param = array();
709
  foreach ($value as $index => $val) {
710
+ $param = array_merge($param, $this->reduceBodyParameter("{$key}[$index]", $val));
711
  }
712
  return $param;
713
  }
vendor/wordfence/wf-waf/src/lib/rules.php CHANGED
@@ -302,7 +302,7 @@ class wfWAFRuleLogicalOperator implements wfWAFRuleInterface {
302
  if (!$this->isValid()) {
303
  throw new wfWAFRuleLogicalOperatorException(sprintf('Invalid logical operator "%s", must be one of %s', $this->getOperator(), join(", ", $this->validOperators)));
304
  }
305
- return sprintf("new %s(%s)", get_class($this), var_export(trim(strtoupper($this->getOperator())), true));
306
  }
307
 
308
  /**
@@ -313,14 +313,14 @@ class wfWAFRuleLogicalOperator implements wfWAFRuleInterface {
313
  if (!$this->isValid()) {
314
  throw new wfWAFRuleLogicalOperatorException(sprintf('Invalid logical operator "%s", must be one of %s', $this->getOperator(), join(", ", $this->validOperators)));
315
  }
316
- return trim(strtolower($this->getOperator()));
317
  }
318
 
319
  public function evaluate() {
320
  $currentValue = $this->getCurrentValue();
321
  $comparison = $this->getComparison();
322
  if (is_bool($currentValue) && $comparison instanceof wfWAFRuleInterface) {
323
- switch (strtolower($this->getOperator())) {
324
  case '&&':
325
  case 'and':
326
  return $currentValue && $comparison->evaluate();
@@ -340,7 +340,7 @@ class wfWAFRuleLogicalOperator implements wfWAFRuleInterface {
340
  * @return bool
341
  */
342
  public function isValid() {
343
- return in_array(strtolower($this->getOperator()), $this->validOperators);
344
  }
345
 
346
  /**
@@ -477,7 +477,7 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
477
  foreach ($this->getSubjects() as $subject) {
478
  $subjectExport .= $subject->render() . ",\n";
479
  }
480
- $subjectExport = 'array(' . substr($subjectExport, 0, -2) . ')';
481
 
482
  $expected = $this->getExpected();
483
  return sprintf('new %s($this, %s, %s, %s)', get_class($this), var_export((string) $this->getAction(), true),
@@ -497,7 +497,7 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
497
  foreach ($this->getSubjects() as $subject) {
498
  $subjectExport .= $subject->renderRule() . ", ";
499
  }
500
- $subjectExport = substr($subjectExport, 0, -2);
501
 
502
  $expected = $this->getExpected();
503
  return sprintf('%s(%s, %s)', $this->getAction(),
@@ -506,7 +506,7 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
506
  }
507
 
508
  public function isActionValid() {
509
- return in_array(strtolower($this->getAction()), self::$allowedActions);
510
  }
511
 
512
  public function evaluate() {
@@ -568,7 +568,7 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
568
  if (is_array($this->getExpected())) {
569
  return in_array($this->getExpected(), $subject);
570
  }
571
- return strpos((string) $subject, (string) $this->getExpected()) !== false;
572
  }
573
 
574
  public function notContains($subject) {
@@ -598,7 +598,7 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
598
  }
599
  return $this->multiplier > 0;
600
  }
601
- $this->multiplier = substr_count($subject, $this->getExpected());
602
  return $this->multiplier > 0;
603
  }
604
 
@@ -635,11 +635,11 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
635
  }
636
 
637
  public function lengthGreaterThan($subject) {
638
- return strlen(is_array($subject) ? join('', $subject) : (string) $subject) > $this->getExpected();
639
  }
640
 
641
  public function lengthLessThan($subject) {
642
- return strlen(is_array($subject) ? join('', $subject) : (string) $subject) < $this->getExpected();
643
  }
644
 
645
  public function currentUserIs($subject) {
302
  if (!$this->isValid()) {
303
  throw new wfWAFRuleLogicalOperatorException(sprintf('Invalid logical operator "%s", must be one of %s', $this->getOperator(), join(", ", $this->validOperators)));
304
  }
305
+ return sprintf("new %s(%s)", get_class($this), var_export(trim(wfWAFUtils::strtoupper($this->getOperator())), true));
306
  }
307
 
308
  /**
313
  if (!$this->isValid()) {
314
  throw new wfWAFRuleLogicalOperatorException(sprintf('Invalid logical operator "%s", must be one of %s', $this->getOperator(), join(", ", $this->validOperators)));
315
  }
316
+ return trim(wfWAFUtils::strtolower($this->getOperator()));
317
  }
318
 
319
  public function evaluate() {
320
  $currentValue = $this->getCurrentValue();
321
  $comparison = $this->getComparison();
322
  if (is_bool($currentValue) && $comparison instanceof wfWAFRuleInterface) {
323
+ switch (wfWAFUtils::strtolower($this->getOperator())) {
324
  case '&&':
325
  case 'and':
326
  return $currentValue && $comparison->evaluate();
340
  * @return bool
341
  */
342
  public function isValid() {
343
+ return in_array(wfWAFUtils::strtolower($this->getOperator()), $this->validOperators);
344
  }
345
 
346
  /**
477
  foreach ($this->getSubjects() as $subject) {
478
  $subjectExport .= $subject->render() . ",\n";
479
  }
480
+ $subjectExport = 'array(' . wfWAFUtils::substr($subjectExport, 0, -2) . ')';
481
 
482
  $expected = $this->getExpected();
483
  return sprintf('new %s($this, %s, %s, %s)', get_class($this), var_export((string) $this->getAction(), true),
497
  foreach ($this->getSubjects() as $subject) {
498
  $subjectExport .= $subject->renderRule() . ", ";
499
  }
500
+ $subjectExport = wfWAFUtils::substr($subjectExport, 0, -2);
501
 
502
  $expected = $this->getExpected();
503
  return sprintf('%s(%s, %s)', $this->getAction(),
506
  }
507
 
508
  public function isActionValid() {
509
+ return in_array(wfWAFUtils::strtolower($this->getAction()), self::$allowedActions);
510
  }
511
 
512
  public function evaluate() {
568
  if (is_array($this->getExpected())) {
569
  return in_array($this->getExpected(), $subject);
570
  }
571
+ return wfWAFUtils::strpos((string) $subject, (string) $this->getExpected()) !== false;
572
  }
573
 
574
  public function notContains($subject) {
598
  }
599
  return $this->multiplier > 0;
600
  }
601
+ $this->multiplier = wfWAFUtils::substr_count($subject, $this->getExpected());
602
  return $this->multiplier > 0;
603
  }
604
 
635
  }
636
 
637
  public function lengthGreaterThan($subject) {
638
+ return wfWAFUtils::strlen(is_array($subject) ? join('', $subject) : (string) $subject) > $this->getExpected();
639
  }
640
 
641
  public function lengthLessThan($subject) {
642
+ return wfWAFUtils::strlen(is_array($subject) ? join('', $subject) : (string) $subject) < $this->getExpected();
643
  }
644
 
645
  public function currentUserIs($subject) {
vendor/wordfence/wf-waf/src/lib/storage/file.php CHANGED
@@ -134,7 +134,7 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
134
  $this->open();
135
  $this->attackDataRows = array();
136
  $this->getAttackDataEngine()->scanRows(array($this, '_getAttackDataRowsSerialized'));
137
- return json_encode($this->attackDataRows);
138
  }
139
 
140
  /**
@@ -159,7 +159,7 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
159
  $binary = fread($fileHandle, $length);
160
  self::lock($fileHandle, LOCK_UN);
161
  $row = wfWAFAttackDataStorageFileEngineRow::unpack($binary);
162
- $data = json_decode($row->getData(), true);
163
  if (is_array($data)) {
164
  array_unshift($data, $row->getTimestamp());
165
  $this->attackDataRows[] = $data;
@@ -270,7 +270,7 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
270
  $failedRulesString .= $rule . '|';
271
  }
272
  }
273
- $failedRulesString = substr($failedRulesString, 0, -1);
274
  }
275
  $row[] = $failedRulesString;
276
  $row[] = $request->getProtocol() === 'https' ? 1 : 0;
@@ -308,12 +308,12 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
308
  public function isIPBlocked($ip) {
309
  $this->open();
310
  $ipBin = wfWAFUtils::inet_pton($ip);
311
- fseek($this->ipCacheFileHandle, strlen(self::LOG_FILE_HEADER), SEEK_SET);
312
  self::lock($this->ipCacheFileHandle, LOCK_SH);
313
  while (!feof($this->ipCacheFileHandle)) {
314
  $ipStr = fread($this->ipCacheFileHandle, 20);
315
- $ip2 = substr($ipStr, 0, 16);
316
- if ($ipBin === $ip2 && unpack('V', substr($ipStr, 16, 4)) >= time()) {
317
  self::lock($this->ipCacheFileHandle, LOCK_UN);
318
  return true;
319
  }
@@ -378,13 +378,13 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
378
  */
379
  public function vacuum() {
380
  $this->open();
381
- $readPointer = strlen(self::LOG_FILE_HEADER);
382
- $writePointer = strlen(self::LOG_FILE_HEADER);
383
  fseek($this->ipCacheFileHandle, $readPointer, SEEK_SET);
384
  self::lock($this->ipCacheFileHandle, LOCK_EX);
385
  while (!feof($this->ipCacheFileHandle)) {
386
  $ipCacheRow = fread($this->ipCacheFileHandle, 20);
387
- $expires = unpack('V', substr($ipCacheRow, 16, 4));
388
  if ($expires >= time()) {
389
  fseek($this->ipCacheFileHandle, $writePointer, SEEK_SET);
390
  fwrite($this->ipCacheFileHandle, $ipCacheRow);
@@ -453,7 +453,7 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
453
  $i = 0;
454
  // Attempt to read contents of the config file. This could be in the middle of a write, so we account for it and
455
  // wait for the operation to complete.
456
- fseek($this->configFileHandle, strlen(self::LOG_FILE_HEADER), SEEK_SET);
457
  $serializedData = '';
458
  while (!feof($this->configFileHandle)) {
459
  $serializedData .= fread($this->configFileHandle, 1024);
@@ -607,8 +607,8 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
607
  $row[$index] = base64_encode($row[$index]);
608
  }
609
  }
610
- $row = json_encode($row);
611
- if (is_string($row) && strlen($row) > 0) {
612
  return $row;
613
  }
614
  return false;
@@ -620,7 +620,7 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
620
  */
621
  private function unserializeRow($row) {
622
  if ($row) {
623
- $json = json_decode($row, true);
624
  if (is_array($json)) {
625
  foreach ($this->rowsToB64 as $index) {
626
  if (array_key_exists($index, $json)) {
@@ -680,9 +680,9 @@ class wfWAFAttackDataStorageFileEngine {
680
  * @return string
681
  */
682
  public static function unpackMicrotime($binary) {
683
- if (!is_string($binary) || strlen($binary) !== 8) {
684
  throw new InvalidArgumentException(__METHOD__ . ' $binary expected to be string with length of 8, received '
685
- . gettype($binary) . (is_string($binary) ? ' of length ' . strlen($binary) : ''));
686
  }
687
  list(, $attackLogSeconds, $attackLogMicroseconds) = unpack('V*', $binary);
688
  return sprintf('%d.%s', $attackLogSeconds, str_pad($attackLogMicroseconds, 6, '0', STR_PAD_LEFT));
@@ -791,7 +791,7 @@ class wfWAFAttackDataStorageFileEngine {
791
  * @return int
792
  */
793
  private function seekToData() {
794
- return $this->seek(strlen($this->getHeaderLength()));
795
  }
796
 
797
  /**
@@ -838,7 +838,7 @@ class wfWAFAttackDataStorageFileEngine {
838
  * @return int
839
  */
840
  public function getHeaderLength() {
841
- return strlen($this->getDefaultHeader());
842
  }
843
 
844
  /**
@@ -854,7 +854,8 @@ class wfWAFAttackDataStorageFileEngine {
854
  * 1600 offset table
855
  * 1 last length
856
  */
857
- $headerLength = strlen(wfWAFStorageFile::LOG_FILE_HEADER) + strlen(self::FILE_SIGNATURE) + 8 + 8 + 4 + (self::MAX_ROWS * 4);
 
858
  return wfWAFStorageFile::LOG_FILE_HEADER
859
  . self::FILE_SIGNATURE
860
  . str_repeat("\x00", 8 + 8 + 4)
@@ -874,16 +875,16 @@ class wfWAFAttackDataStorageFileEngine {
874
  $this->header = array();
875
  $this->seek(0);
876
  $this->lockRead();
877
- $this->header['phpHeader'] = $this->read(strlen(wfWAFStorageFile::LOG_FILE_HEADER));
878
- $this->header['signature'] = $this->read(strlen(self::FILE_SIGNATURE));
879
  if ($this->header['phpHeader'] !== wfWAFStorageFile::LOG_FILE_HEADER || $this->header['signature'] !== self::FILE_SIGNATURE) {
880
  $this->unlock();
881
  $this->truncate();
882
  $this->lockRead();
883
  $this->seek(0);
884
  $this->lockRead();
885
- $this->header['phpHeader'] = $this->read(strlen(wfWAFStorageFile::LOG_FILE_HEADER));
886
- $this->header['signature'] = $this->read(strlen(self::FILE_SIGNATURE));
887
  }
888
  $this->header['oldestTimestamp'] = self::unpackMicrotime($this->read(8));
889
  $this->header['newestTimestamp'] = self::unpackMicrotime($this->read(8));
@@ -901,7 +902,7 @@ class wfWAFAttackDataStorageFileEngine {
901
  return $this->offsetTable;
902
  }
903
  $rowCount = min($this->header['rowCount'], self::MAX_ROWS);
904
- $this->seek(strlen(wfWAFStorageFile::LOG_FILE_HEADER) + strlen(self::FILE_SIGNATURE) + 8 + 8 + 4);
905
  $offsetTableBinary = $this->read(($rowCount + 1) * 4);
906
  $this->offsetTable = array_values(unpack('V*', $offsetTableBinary));
907
  return $this->offsetTable;
@@ -995,7 +996,7 @@ class wfWAFAttackDataStorageFileEngine {
995
  public function addRow($row) {
996
  $this->open();
997
 
998
- $this->seek(strlen(wfWAFStorageFile::LOG_FILE_HEADER) + strlen(self::FILE_SIGNATURE) + 8 + 8);
999
  $this->lockRead();
1000
  list(, $rowCount) = unpack('V', $this->read(4));
1001
  if ($rowCount >= self::MAX_ROWS) {
@@ -1006,7 +1007,7 @@ class wfWAFAttackDataStorageFileEngine {
1006
  $this->header = array();
1007
  $this->offsetTable = array();
1008
 
1009
- $this->seek(strlen(wfWAFStorageFile::LOG_FILE_HEADER) + strlen(self::FILE_SIGNATURE) + 8 + 8 + 4 + ($rowCount * 4));
1010
  list(, $nextRowOffset) = unpack('V', $this->read(4));
1011
 
1012
  $rowString = $row->pack();
@@ -1014,26 +1015,26 @@ class wfWAFAttackDataStorageFileEngine {
1014
  $this->lockWrite();
1015
 
1016
  // Update offset table
1017
- $this->seek(strlen(wfWAFStorageFile::LOG_FILE_HEADER) + strlen(self::FILE_SIGNATURE) + 8 + 8 + 4 + (($rowCount + 1) * 4));
1018
- $this->write(pack('V', $nextRowOffset + strlen($rowString)));
1019
 
1020
  // Update rowCount
1021
- $this->seek(strlen(wfWAFStorageFile::LOG_FILE_HEADER) + strlen(self::FILE_SIGNATURE) + 8 + 8);
1022
  $this->write(pack('V', $rowCount + 1));
1023
 
1024
  // Write data
1025
  $this->seek($nextRowOffset);
1026
- $packedTimestamp = substr($rowString, 0, 8);
1027
  $this->write($rowString);
1028
 
1029
  // Update oldest timestamp
1030
  if ($rowCount === 0) {
1031
- $this->seek(strlen(wfWAFStorageFile::LOG_FILE_HEADER) + strlen(self::FILE_SIGNATURE));
1032
  $this->write($packedTimestamp);
1033
  }
1034
 
1035
  // Update newest timestamp
1036
- $this->seek(strlen(wfWAFStorageFile::LOG_FILE_HEADER) + strlen(self::FILE_SIGNATURE) + 8);
1037
  $this->write($packedTimestamp);
1038
 
1039
  $this->unlock();
@@ -1185,8 +1186,8 @@ class wfWAFAttackDataStorageFileEngineRow {
1185
  * @return wfWAFAttackDataStorageFileEngineRow
1186
  */
1187
  public static function unpack($binary) {
1188
- $attackLogTime = wfWAFAttackDataStorageFileEngine::unpackMicrotime(substr($binary, 0, 8));
1189
- $data = wfWAFAttackDataStorageFileEngine::decompress(substr($binary, 8));
1190
  return new self($attackLogTime, $data);
1191
  }
1192
 
134
  $this->open();
135
  $this->attackDataRows = array();
136
  $this->getAttackDataEngine()->scanRows(array($this, '_getAttackDataRowsSerialized'));
137
+ return wfWAFUtils::json_encode($this->attackDataRows);
138
  }
139
 
140
  /**
159
  $binary = fread($fileHandle, $length);
160
  self::lock($fileHandle, LOCK_UN);
161
  $row = wfWAFAttackDataStorageFileEngineRow::unpack($binary);
162
+ $data = wfWAFUtils::json_decode($row->getData(), true);
163
  if (is_array($data)) {
164
  array_unshift($data, $row->getTimestamp());
165
  $this->attackDataRows[] = $data;
270
  $failedRulesString .= $rule . '|';
271
  }
272
  }
273
+ $failedRulesString = wfWAFUtils::substr($failedRulesString, 0, -1);
274
  }
275
  $row[] = $failedRulesString;
276
  $row[] = $request->getProtocol() === 'https' ? 1 : 0;
308
  public function isIPBlocked($ip) {
309
  $this->open();
310
  $ipBin = wfWAFUtils::inet_pton($ip);
311
+ fseek($this->ipCacheFileHandle, wfWAFUtils::strlen(self::LOG_FILE_HEADER), SEEK_SET);
312
  self::lock($this->ipCacheFileHandle, LOCK_SH);
313
  while (!feof($this->ipCacheFileHandle)) {
314
  $ipStr = fread($this->ipCacheFileHandle, 20);
315
+ $ip2 = wfWAFUtils::substr($ipStr, 0, 16);
316
+ if ($ipBin === $ip2 && unpack('V', wfWAFUtils::substr($ipStr, 16, 4)) >= time()) {
317
  self::lock($this->ipCacheFileHandle, LOCK_UN);
318
  return true;
319
  }
378
  */
379
  public function vacuum() {
380
  $this->open();
381
+ $readPointer = wfWAFUtils::strlen(self::LOG_FILE_HEADER);
382
+ $writePointer = wfWAFUtils::strlen(self::LOG_FILE_HEADER);
383
  fseek($this->ipCacheFileHandle, $readPointer, SEEK_SET);
384
  self::lock($this->ipCacheFileHandle, LOCK_EX);
385
  while (!feof($this->ipCacheFileHandle)) {
386
  $ipCacheRow = fread($this->ipCacheFileHandle, 20);
387
+ $expires = unpack('V', wfWAFUtils::substr($ipCacheRow, 16, 4));
388
  if ($expires >= time()) {
389
  fseek($this->ipCacheFileHandle, $writePointer, SEEK_SET);
390
  fwrite($this->ipCacheFileHandle, $ipCacheRow);
453
  $i = 0;
454
  // Attempt to read contents of the config file. This could be in the middle of a write, so we account for it and
455
  // wait for the operation to complete.
456
+ fseek($this->configFileHandle, wfWAFUtils::strlen(self::LOG_FILE_HEADER), SEEK_SET);
457
  $serializedData = '';
458
  while (!feof($this->configFileHandle)) {
459
  $serializedData .= fread($this->configFileHandle, 1024);
607
  $row[$index] = base64_encode($row[$index]);
608
  }
609
  }
610
+ $row = wfWAFUtils::json_encode($row);
611
+ if (is_string($row) && wfWAFUtils::strlen($row) > 0) {
612
  return $row;
613
  }
614
  return false;
620
  */
621
  private function unserializeRow($row) {
622
  if ($row) {
623
+ $json = wfWAFUtils::json_decode($row, true);
624
  if (is_array($json)) {
625
  foreach ($this->rowsToB64 as $index) {
626
  if (array_key_exists($index, $json)) {
680
  * @return string
681
  */
682
  public static function unpackMicrotime($binary) {
683
+ if (!is_string($binary) || wfWAFUtils::strlen($binary) !== 8) {
684
  throw new InvalidArgumentException(__METHOD__ . ' $binary expected to be string with length of 8, received '
685
+ . gettype($binary) . (is_string($binary) ? ' of length ' . wfWAFUtils::strlen($binary) : ''));
686
  }
687
  list(, $attackLogSeconds, $attackLogMicroseconds) = unpack('V*', $binary);
688
  return sprintf('%d.%s', $attackLogSeconds, str_pad($attackLogMicroseconds, 6, '0', STR_PAD_LEFT));
791
  * @return int
792
  */
793
  private function seekToData() {
794
+ return $this->seek(wfWAFUtils::strlen($this->getHeaderLength()));
795
  }
796
 
797
  /**
838
  * @return int
839
  */
840
  public function getHeaderLength() {
841
+ return wfWAFUtils::strlen($this->getDefaultHeader());
842
  }
843
 
844
  /**
854
  * 1600 offset table
855
  * 1 last length
856
  */
857
+ $headerLength = wfWAFUtils::strlen(wfWAFStorageFile::LOG_FILE_HEADER) + wfWAFUtils::strlen(self::FILE_SIGNATURE)
858
+ + 8 + 8 + 4 + (self::MAX_ROWS * 4);
859
  return wfWAFStorageFile::LOG_FILE_HEADER
860
  . self::FILE_SIGNATURE
861
  . str_repeat("\x00", 8 + 8 + 4)
875
  $this->header = array();
876
  $this->seek(0);
877
  $this->lockRead();
878
+ $this->header['phpHeader'] = $this->read(wfWAFUtils::strlen(wfWAFStorageFile::LOG_FILE_HEADER));
879
+ $this->header['signature'] = $this->read(wfWAFUtils::strlen(self::FILE_SIGNATURE));
880
  if ($this->header['phpHeader'] !== wfWAFStorageFile::LOG_FILE_HEADER || $this->header['signature'] !== self::FILE_SIGNATURE) {
881
  $this->unlock();
882
  $this->truncate();
883
  $this->lockRead();
884
  $this->seek(0);
885
  $this->lockRead();
886
+ $this->header['phpHeader'] = $this->read(wfWAFUtils::strlen(wfWAFStorageFile::LOG_FILE_HEADER));
887
+ $this->header['signature'] = $this->read(wfWAFUtils::strlen(self::FILE_SIGNATURE));
888
  }
889
  $this->header['oldestTimestamp'] = self::unpackMicrotime($this->read(8));
890
  $this->header['newestTimestamp'] = self::unpackMicrotime($this->read(8));
902
  return $this->offsetTable;
903
  }
904
  $rowCount = min($this->header['rowCount'], self::MAX_ROWS);
905
+ $this->seek(wfWAFUtils::strlen(wfWAFStorageFile::LOG_FILE_HEADER) + wfWAFUtils::strlen(self::FILE_SIGNATURE) + 8 + 8 + 4);
906
  $offsetTableBinary = $this->read(($rowCount + 1) * 4);
907
  $this->offsetTable = array_values(unpack('V*', $offsetTableBinary));
908
  return $this->offsetTable;
996
  public function addRow($row) {
997
  $this->open();
998
 
999
+ $this->seek(wfWAFUtils::strlen(wfWAFStorageFile::LOG_FILE_HEADER) + wfWAFUtils::strlen(self::FILE_SIGNATURE) + 8 + 8);
1000
  $this->lockRead();
1001
  list(, $rowCount) = unpack('V', $this->read(4));
1002
  if ($rowCount >= self::MAX_ROWS) {
1007
  $this->header = array();
1008
  $this->offsetTable = array();
1009
 
1010
+ $this->seek(wfWAFUtils::strlen(wfWAFStorageFile::LOG_FILE_HEADER) + wfWAFUtils::strlen(self::FILE_SIGNATURE) + 8 + 8 + 4 + ($rowCount * 4));
1011
  list(, $nextRowOffset) = unpack('V', $this->read(4));
1012
 
1013
  $rowString = $row->pack();
1015
  $this->lockWrite();
1016
 
1017
  // Update offset table
1018
+ $this->seek(wfWAFUtils::strlen(wfWAFStorageFile::LOG_FILE_HEADER) + wfWAFUtils::strlen(self::FILE_SIGNATURE) + 8 + 8 + 4 + (($rowCount + 1) * 4));
1019
+ $this->write(pack('V', $nextRowOffset + wfWAFUtils::strlen($rowString)));
1020
 
1021
  // Update rowCount
1022
+ $this->seek(wfWAFUtils::strlen(wfWAFStorageFile::LOG_FILE_HEADER) + wfWAFUtils::strlen(self::FILE_SIGNATURE) + 8 + 8);
1023
  $this->write(pack('V', $rowCount + 1));
1024
 
1025
  // Write data
1026
  $this->seek($nextRowOffset);
1027
+ $packedTimestamp = wfWAFUtils::substr($rowString, 0, 8);
1028
  $this->write($rowString);
1029
 
1030
  // Update oldest timestamp
1031
  if ($rowCount === 0) {
1032
+ $this->seek(wfWAFUtils::strlen(wfWAFStorageFile::LOG_FILE_HEADER) + wfWAFUtils::strlen(self::FILE_SIGNATURE));
1033
  $this->write($packedTimestamp);
1034
  }
1035
 
1036
  // Update newest timestamp
1037
+ $this->seek(wfWAFUtils::strlen(wfWAFStorageFile::LOG_FILE_HEADER) + wfWAFUtils::strlen(self::FILE_SIGNATURE) + 8);
1038
  $this->write($packedTimestamp);
1039
 
1040
  $this->unlock();
1186
  * @return wfWAFAttackDataStorageFileEngineRow
1187
  */
1188
  public static function unpack($binary) {
1189
+ $attackLogTime = wfWAFAttackDataStorageFileEngine::unpackMicrotime(wfWAFUtils::substr($binary, 0, 8));
1190
+ $data = wfWAFAttackDataStorageFileEngine::decompress(wfWAFUtils::substr($binary, 8));
1191
  return new self($attackLogTime, $data);
1192
  }
1193
 
vendor/wordfence/wf-waf/src/lib/utils.php CHANGED
@@ -10,8 +10,8 @@ class wfWAFUtils {
10
  */
11
  public static function inet_ntop($ip) {
12
  // trim this to the IPv4 equiv if it's in the mapped range
13
- if (strlen($ip) == 16 && substr($ip, 0, 12) == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff") {
14
- $ip = substr($ip, 12, 4);
15
  }
16
  return self::hasIPv6Support() ? inet_ntop($ip) : self::_inet_ntop($ip);
17
  }
@@ -48,11 +48,11 @@ class wfWAFUtils {
48
  if ($ip === '::') {
49
  return "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
50
  }
51
- $colon_count = substr_count($ip, ':');
52
- $dbl_colon_pos = strpos($ip, '::');
53
  if ($dbl_colon_pos !== false) {
54
  $ip = str_replace('::', str_repeat(':0000',
55
- (($dbl_colon_pos === 0 || $dbl_colon_pos === strlen($ip) - 2) ? 9 : 8) - $colon_count) . ':', $ip);
56
  $ip = trim($ip, ':');
57
  }
58
 
@@ -62,7 +62,7 @@ class wfWAFUtils {
62
  $ipv6_bin .= pack('H*', str_pad($ip_group, 4, '0', STR_PAD_LEFT));
63
  }
64
 
65
- return strlen($ipv6_bin) === 16 ? $ipv6_bin : false;
66
  }
67
 
68
  // IPv4 mapped IPv6
@@ -82,15 +82,15 @@ class wfWAFUtils {
82
  */
83
  public static function _inet_ntop($ip) {
84
  // IPv4
85
- if (strlen($ip) === 4) {
86
  return ord($ip[0]) . '.' . ord($ip[1]) . '.' . ord($ip[2]) . '.' . ord($ip[3]);
87
  }
88
 
89
  // IPv6
90
- if (strlen($ip) === 16) {
91
 
92
  // IPv4 mapped IPv6
93
- if (substr($ip, 0, 12) == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff") {
94
  return "::ffff:" . ord($ip[12]) . '.' . ord($ip[13]) . '.' . ord($ip[14]) . '.' . ord($ip[15]);
95
  }
96
 
@@ -139,10 +139,50 @@ class wfWAFUtils {
139
  */
140
  public static function expandIPv6Address($ip) {
141
  $hex = bin2hex(self::inet_pton($ip));
142
- $ip = substr(preg_replace("/([a-f0-9]{4})/i", "$1:", $hex), 0, -1);
143
  return $ip;
144
  }
145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  /**
147
  * Compare two strings in constant time. It can leak the length of a string.
148
  *
@@ -151,8 +191,8 @@ class wfWAFUtils {
151
  * @return bool Whether strings are equal.
152
  */
153
  public static function hash_equals($a, $b) {
154
- $a_length = strlen($a);
155
- if ($a_length !== strlen($b)) {
156
  return false;
157
  }
158
  $result = 0;
@@ -194,13 +234,13 @@ class wfWAFUtils {
194
 
195
  $pack = $packs[$algo];
196
 
197
- if (strlen($key) > 64)
198
  $key = pack($pack, $algo($key));
199
 
200
  $key = str_pad($key, 64, chr(0));
201
 
202
- $ipad = (substr($key, 0, 64) ^ str_repeat(chr(0x36), 64));
203
- $opad = (substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64));
204
 
205
  $hmac = $algo($opad . pack($pack, $algo($ipad . $data)));
206
 
@@ -218,7 +258,7 @@ class wfWAFUtils {
218
  // This is faster than calling self::random_int for $length
219
  $bytes = self::random_bytes($length);
220
  $return = '';
221
- $maxIndex = strlen($chars) - 1;
222
  for ($i = 0; $i < $length; $i++) {
223
  $fp = (float) ord($bytes[$i]) / 255.0; // convert to [0,1]
224
  $index = (int) (round($fp * $maxIndex));
@@ -238,7 +278,7 @@ class wfWAFUtils {
238
  if (function_exists('random_bytes')) {
239
  try {
240
  $rand = random_bytes($bytes);
241
- if (is_string($rand) && strlen($rand) === $bytes) {
242
  return $rand;
243
  }
244
  } catch (Exception $e) {
@@ -251,13 +291,13 @@ class wfWAFUtils {
251
  }
252
  if (function_exists('mcrypt_create_iv')) {
253
  $rand = @mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);
254
- if (is_string($rand) && strlen($rand) === $bytes) {
255
  return $rand;
256
  }
257
  }
258
  if (function_exists('openssl_random_pseudo_bytes')) {
259
  $rand = @openssl_random_pseudo_bytes($bytes, $strong);
260
- if (is_string($rand) && strlen($rand) === $bytes) {
261
  return $rand;
262
  }
263
  }
@@ -290,7 +330,7 @@ class wfWAFUtils {
290
  }
291
  $diff = $max - $min;
292
  $bytes = self::random_bytes(4);
293
- if ($bytes === false || strlen($bytes) != 4) {
294
  throw new RuntimeException("Unable to get 4 bytes");
295
  }
296
  $val = unpack("Nint", $bytes);
@@ -306,7 +346,7 @@ class wfWAFUtils {
306
  public static function stripMagicQuotes($subject) {
307
  $sybase = ini_get('magic_quotes_sybase');
308
  $sybaseEnabled = ((is_numeric($sybase) && $sybase) ||
309
- (is_string($sybase) && $sybase && !in_array(strtolower($sybase), array(
310
  'off',
311
  'false'
312
  ))));
@@ -330,4 +370,159 @@ class wfWAFUtils {
330
  }
331
  return $subject;
332
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
  }
10
  */
11
  public static function inet_ntop($ip) {
12
  // trim this to the IPv4 equiv if it's in the mapped range
13
+ if (wfWAFUtils::strlen($ip) == 16 && wfWAFUtils::substr($ip, 0, 12) == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff") {
14
+ $ip = wfWAFUtils::substr($ip, 12, 4);
15
  }
16
  return self::hasIPv6Support() ? inet_ntop($ip) : self::_inet_ntop($ip);
17
  }
48
  if ($ip === '::') {
49
  return "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
50
  }
51
+ $colon_count = wfWAFUtils::substr_count($ip, ':');
52
+ $dbl_colon_pos = wfWAFUtils::strpos($ip, '::');
53
  if ($dbl_colon_pos !== false) {
54
  $ip = str_replace('::', str_repeat(':0000',
55
+ (($dbl_colon_pos === 0 || $dbl_colon_pos === wfWAFUtils::strlen($ip) - 2) ? 9 : 8) - $colon_count) . ':', $ip);
56
  $ip = trim($ip, ':');
57
  }
58
 
62
  $ipv6_bin .= pack('H*', str_pad($ip_group, 4, '0', STR_PAD_LEFT));
63
  }
64
 
65
+ return wfWAFUtils::strlen($ipv6_bin) === 16 ? $ipv6_bin : false;
66
  }
67
 
68
  // IPv4 mapped IPv6
82
  */
83
  public static function _inet_ntop($ip) {
84
  // IPv4
85
+ if (wfWAFUtils::strlen($ip) === 4) {
86
  return ord($ip[0]) . '.' . ord($ip[1]) . '.' . ord($ip[2]) . '.' . ord($ip[3]);
87
  }
88
 
89
  // IPv6
90
+ if (wfWAFUtils::strlen($ip) === 16) {
91
 
92
  // IPv4 mapped IPv6
93
+ if (wfWAFUtils::substr($ip, 0, 12) == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff") {
94
  return "::ffff:" . ord($ip[12]) . '.' . ord($ip[13]) . '.' . ord($ip[14]) . '.' . ord($ip[15]);
95
  }
96
 
139
  */
140
  public static function expandIPv6Address($ip) {
141
  $hex = bin2hex(self::inet_pton($ip));
142
+ $ip = wfWAFUtils::substr(preg_replace("/([a-f0-9]{4})/i", "$1:", $hex), 0, -1);
143
  return $ip;
144
  }
145
 
146
+ protected static $servicesJSON;
147
+
148
+ public static function json_encode($string) {
149
+ if (function_exists('json_encode')) {
150
+ return json_encode($string);
151
+ } else {
152
+ if (!self::$servicesJSON) {
153
+ require_once WFWAF_LIB_PATH . 'json.php';
154
+ self::$servicesJSON = new wfServices_JSON();
155
+ }
156
+ return self::$servicesJSON->encodeUnsafe($string);
157
+ }
158
+ }
159
+
160
+ public static function json_decode($string, $assoc_array = false) {
161
+ if (function_exists('json_decode')) {
162
+ return json_decode($string, $assoc_array);
163
+ } else {
164
+ if (!self::$servicesJSON) {
165
+ require_once WFWAF_LIB_PATH . 'json.php';
166
+ self::$servicesJSON = new wfServices_JSON();
167
+ }
168
+ $res = self::$servicesJSON->decode($string);
169
+ if ($assoc_array)
170
+ $res = self::_json_decode_object_helper($res);
171
+ return $res;
172
+
173
+ }
174
+ }
175
+
176
+ /**
177
+ * @param object $data
178
+ * @return array
179
+ */
180
+ protected static function _json_decode_object_helper($data) {
181
+ if (is_object($data))
182
+ $data = get_object_vars($data);
183
+ return is_array($data) ? array_map('wfWAFUtils::_json_decode_object_helper', $data) : $data;
184
+ }
185
+
186
  /**
187
  * Compare two strings in constant time. It can leak the length of a string.
188
  *
191
  * @return bool Whether strings are equal.
192
  */
193
  public static function hash_equals($a, $b) {
194
+ $a_length = wfWAFUtils::strlen($a);
195
+ if ($a_length !== wfWAFUtils::strlen($b)) {
196
  return false;
197
  }
198
  $result = 0;
234
 
235
  $pack = $packs[$algo];
236
 
237
+ if (wfWAFUtils::strlen($key) > 64)
238
  $key = pack($pack, $algo($key));
239
 
240
  $key = str_pad($key, 64, chr(0));
241
 
242
+ $ipad = (wfWAFUtils::substr($key, 0, 64) ^ str_repeat(chr(0x36), 64));
243
+ $opad = (wfWAFUtils::substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64));
244
 
245
  $hmac = $algo($opad . pack($pack, $algo($ipad . $data)));
246
 
258
  // This is faster than calling self::random_int for $length
259
  $bytes = self::random_bytes($length);
260
  $return = '';
261
+ $maxIndex = wfWAFUtils::strlen($chars) - 1;
262
  for ($i = 0; $i < $length; $i++) {
263
  $fp = (float) ord($bytes[$i]) / 255.0; // convert to [0,1]
264
  $index = (int) (round($fp * $maxIndex));
278
  if (function_exists('random_bytes')) {
279
  try {
280
  $rand = random_bytes($bytes);
281
+ if (is_string($rand) && wfWAFUtils::strlen($rand) === $bytes) {
282
  return $rand;
283
  }
284
  } catch (Exception $e) {
291
  }
292
  if (function_exists('mcrypt_create_iv')) {
293
  $rand = @mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);
294
+ if (is_string($rand) && wfWAFUtils::strlen($rand) === $bytes) {
295
  return $rand;
296
  }
297
  }
298
  if (function_exists('openssl_random_pseudo_bytes')) {
299
  $rand = @openssl_random_pseudo_bytes($bytes, $strong);
300
+ if (is_string($rand) && wfWAFUtils::strlen($rand) === $bytes) {
301
  return $rand;
302
  }
303
  }
330
  }
331
  $diff = $max - $min;
332
  $bytes = self::random_bytes(4);
333
+ if ($bytes === false || wfWAFUtils::strlen($bytes) != 4) {
334
  throw new RuntimeException("Unable to get 4 bytes");
335
  }
336
  $val = unpack("Nint", $bytes);
346
  public static function stripMagicQuotes($subject) {
347
  $sybase = ini_get('magic_quotes_sybase');
348
  $sybaseEnabled = ((is_numeric($sybase) && $sybase) ||
349
+ (is_string($sybase) && $sybase && !in_array(wfWAFUtils::strtolower($sybase), array(
350
  'off',
351
  'false'
352
  ))));
370
  }
371
  return $subject;
372
  }
373
+
374
+
375
+ /**
376
+ * Set the mbstring internal encoding to a binary safe encoding when func_overload
377
+ * is enabled.
378
+ *
379
+ * When mbstring.func_overload is in use for multi-byte encodings, the results from
380
+ * strlen() and similar functions respect the utf8 characters, causing binary data
381
+ * to return incorrect lengths.
382
+ *
383
+ * This function overrides the mbstring encoding to a binary-safe encoding, and
384
+ * resets it to the users expected encoding afterwards through the
385
+ * `reset_mbstring_encoding` function.
386
+ *
387
+ * It is safe to recursively call this function, however each
388
+ * `mbstring_binary_safe_encoding()` call must be followed up with an equal number
389
+ * of `reset_mbstring_encoding()` calls.
390
+ *
391
+ * @see wfWAFUtils::reset_mbstring_encoding
392
+ *
393
+ * @staticvar array $encodings
394
+ * @staticvar bool $overloaded
395
+ *
396
+ * @param bool $reset Optional. Whether to reset the encoding back to a previously-set encoding.
397
+ * Default false.
398
+ */
399
+ public static function mbstring_binary_safe_encoding($reset = false) {
400
+ static $encodings = array();
401
+ static $overloaded = null;
402
+
403
+ if (is_null($overloaded))
404
+ $overloaded = function_exists('mb_internal_encoding') && (ini_get('mbstring.func_overload') & 2);
405
+
406
+ if (false === $overloaded)
407
+ return;
408
+
409
+ if (!$reset) {
410
+ $encoding = mb_internal_encoding();
411
+ array_push($encodings, $encoding);
412
+ mb_internal_encoding('ISO-8859-1');
413
+ }
414
+
415
+ if ($reset && $encodings) {
416
+ $encoding = array_pop($encodings);
417
+ mb_internal_encoding($encoding);
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Reset the mbstring internal encoding to a users previously set encoding.
423
+ *
424
+ * @see wfWAFUtils::mbstring_binary_safe_encoding
425
+ */
426
+ public static function reset_mbstring_encoding() {
427
+ self::mbstring_binary_safe_encoding(true);
428
+ }
429
+
430
+ /**
431
+ * @param callable $function
432
+ * @param array $args
433
+ * @return mixed
434
+ */
435
+ protected static function callMBSafeStrFunction($function, $args) {
436
+ self::mbstring_binary_safe_encoding();
437
+ $return = call_user_func_array($function, $args);
438
+ self::reset_mbstring_encoding();
439
+ return $return;
440
+ }
441
+
442
+ /**
443
+ * Multibyte safe strlen.
444
+ *
445
+ * @param $binary
446
+ * @return int
447
+ */
448
+ public static function strlen($binary) {
449
+ $args = func_get_args();
450
+ return self::callMBSafeStrFunction('strlen', $args);
451
+ }
452
+
453
+ /**
454
+ * @param $haystack
455
+ * @param $needle
456
+ * @param int $offset
457
+ * @return int
458
+ */
459
+ public static function stripos($haystack, $needle, $offset = 0) {
460
+ $args = func_get_args();
461
+ return self::callMBSafeStrFunction('stripos', $args);
462
+ }
463
+
464
+ /**
465
+ * @param $string
466
+ * @return mixed
467
+ */
468
+ public static function strtolower($string) {
469
+ $args = func_get_args();
470
+ return self::callMBSafeStrFunction('strtolower', $args);
471
+ }
472
+
473
+ /**
474
+ * @param $string
475
+ * @param $start
476
+ * @param $length
477
+ * @return mixed
478
+ */
479
+ public static function substr($string, $start, $length = null) {
480
+ $args = func_get_args();
481
+ return self::callMBSafeStrFunction('substr', $args);
482
+ }
483
+
484
+ /**
485
+ * @param $haystack
486
+ * @param $needle
487
+ * @param int $offset
488
+ * @return mixed
489
+ */
490
+ public static function strpos($haystack, $needle, $offset = 0) {
491
+ $args = func_get_args();
492
+ return self::callMBSafeStrFunction('strpos', $args);
493
+ }
494
+
495
+ /**
496
+ * @param string $haystack
497
+ * @param string $needle
498
+ * @param int $offset
499
+ * @param int $length
500
+ * @return mixed
501
+ */
502
+ public static function substr_count($haystack, $needle, $offset = 0, $length = null) {
503
+ $haystack = self::substr($haystack, $offset, $length);
504
+ return self::callMBSafeStrFunction('substr_count', array(
505
+ $haystack, $needle,
506
+ ));
507
+ }
508
+
509
+ /**
510
+ * @param $string
511
+ * @return mixed
512
+ */
513
+ public static function strtoupper($string) {
514
+ $args = func_get_args();
515
+ return self::callMBSafeStrFunction('strtoupper', $args);
516
+ }
517
+
518
+ /**
519
+ * @param string $haystack
520
+ * @param string $needle
521
+ * @param int $offset
522
+ * @return mixed
523
+ */
524
+ public static function strrpos($haystack, $needle, $offset = 0) {
525
+ $args = func_get_args();
526
+ return self::callMBSafeStrFunction('strrpos', $args);
527
+ }
528
  }
vendor/wordfence/wf-waf/src/lib/waf.php CHANGED
@@ -72,7 +72,7 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
72
  }
73
 
74
  public function getGlobal($global) {
75
- if (strpos($global, '.') === false) {
76
  return null;
77
  }
78
  list($prefix, $_global) = explode('.', $global);
@@ -87,7 +87,7 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
87
  }
88
  break;
89
  case 'server':
90
- $key = strtoupper($_global);
91
  if (isset($_SERVER) && array_key_exists($key, $_SERVER)) {
92
  return $_SERVER[$key];
93
  }
@@ -141,7 +141,7 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
141
  if ($request->getBody('wfwaf-false-positive-verified') && $this->currentUserCanWhitelist() &&
142
  wfWAFUtils::hash_equals($request->getBody('wfwaf-false-positive-nonce'), $this->getAuthCookieValue('nonce', ''))
143
  ) {
144
- $urlParams = json_decode($request->getBody('wfwaf-false-positive-params'), true);
145
  if (is_array($urlParams) && $urlParams) {
146
  $whitelistCount = 0;
147
  foreach ($urlParams as $urlParam) {
@@ -882,7 +882,7 @@ HTML
882
  )), $this->getStorageEngine()->getAttackData(), $request);
883
 
884
  if ($response instanceof wfWAFHTTPResponse && $response->getBody()) {
885
- $jsonData = json_decode($response->getBody(), true);
886
  if (is_array($jsonData) && array_key_exists('success', $jsonData)) {
887
  $this->getStorageEngine()->truncateAttackData();
888
  $this->getStorageEngine()->unsetConfig('attackDataNextInterval');
@@ -1260,7 +1260,7 @@ class wfWAFCronFetchRulesEvent extends wfWAFCronEvent {
1260
  'betaFeed' => (int) $waf->getStorageEngine()->getConfig('betaThreatDefenseFeed'),
1261
  )));
1262
  if ($this->response) {
1263
- $jsonData = json_decode($this->response->getBody(), true);
1264
  if (is_array($jsonData)) {
1265
 
1266
  if ($waf->hasOpenSSL() &&
72
  }
73
 
74
  public function getGlobal($global) {
75
+ if (wfWAFUtils::strpos($global, '.') === false) {
76
  return null;
77
  }
78
  list($prefix, $_global) = explode('.', $global);
87
  }
88
  break;
89
  case 'server':
90
+ $key = wfWAFUtils::strtoupper($_global);
91
  if (isset($_SERVER) && array_key_exists($key, $_SERVER)) {
92
  return $_SERVER[$key];
93
  }
141
  if ($request->getBody('wfwaf-false-positive-verified') && $this->currentUserCanWhitelist() &&
142
  wfWAFUtils::hash_equals($request->getBody('wfwaf-false-positive-nonce'), $this->getAuthCookieValue('nonce', ''))
143
  ) {
144
+ $urlParams = wfWAFUtils::json_decode($request->getBody('wfwaf-false-positive-params'), true);
145
  if (is_array($urlParams) && $urlParams) {
146
  $whitelistCount = 0;
147
  foreach ($urlParams as $urlParam) {
882
  )), $this->getStorageEngine()->getAttackData(), $request);
883
 
884
  if ($response instanceof wfWAFHTTPResponse && $response->getBody()) {
885
+ $jsonData = wfWAFUtils::json_decode($response->getBody(), true);
886
  if (is_array($jsonData) && array_key_exists('success', $jsonData)) {
887
  $this->getStorageEngine()->truncateAttackData();
888
  $this->getStorageEngine()->unsetConfig('attackDataNextInterval');
1260
  'betaFeed' => (int) $waf->getStorageEngine()->getConfig('betaThreatDefenseFeed'),
1261
  )));
1262
  if ($this->response) {
1263
+ $jsonData = wfWAFUtils::json_decode($this->response->getBody(), true);
1264
  if (is_array($jsonData)) {
1265
 
1266
  if ($waf->hasOpenSSL() &&
vendor/wordfence/wf-waf/src/views/403-roadblock.php CHANGED
@@ -3,7 +3,7 @@
3
  /** @var wfWAF $waf */
4
  /** @var wfWAFView $this */
5
 
6
- $method = strtolower($waf->getRequest()->getMethod());
7
  $urlParamsToWhitelist = array();
8
  foreach ($waf->getFailedRules() as $paramKey => $categories) {
9
  foreach ($categories as $category => $failedRules) {
@@ -45,7 +45,7 @@ foreach ($waf->getFailedRules() as $paramKey => $categories) {
45
  <form id="whitelist-form" action="<?php echo htmlentities($waf->getRequest()->getPath(), ENT_QUOTES, 'utf-8') ?>"
46
  method="post">
47
  <input type="hidden" name="wfwaf-false-positive-params"
48
- value="<?php echo htmlentities(json_encode($urlParamsToWhitelist), ENT_QUOTES, 'utf-8') ?>">
49
  <input type="hidden" name="wfwaf-false-positive-nonce"
50
  value="<?php echo htmlentities($waf->getAuthCookieValue('nonce', ''), ENT_QUOTES, 'utf-8') ?>">
51
 
3
  /** @var wfWAF $waf */
4
  /** @var wfWAFView $this */
5
 
6
+ $method = wfWAFUtils::strtolower($waf->getRequest()->getMethod());
7
  $urlParamsToWhitelist = array();
8
  foreach ($waf->getFailedRules() as $paramKey => $categories) {
9
  foreach ($categories as $category => $failedRules) {
45
  <form id="whitelist-form" action="<?php echo htmlentities($waf->getRequest()->getPath(), ENT_QUOTES, 'utf-8') ?>"
46
  method="post">
47
  <input type="hidden" name="wfwaf-false-positive-params"
48
+ value="<?php echo htmlentities(wfWAFUtils::json_encode($urlParamsToWhitelist), ENT_QUOTES, 'utf-8') ?>">
49
  <input type="hidden" name="wfwaf-false-positive-nonce"
50
  value="<?php echo htmlentities($waf->getAuthCookieValue('nonce', ''), ENT_QUOTES, 'utf-8') ?>">
51
 
waf/bootstrap.php CHANGED
@@ -28,10 +28,10 @@ class wfWAFWordPressRequest extends wfWAFRequest {
28
 
29
  public function getIP() {
30
  $howGet = wfWAF::getInstance()->getStorageEngine()->getConfig('howGetIPs');
31
- if (is_string($howGet) && array_key_exists($howGet, $_SERVER)) {
32
  $ips[] = $_SERVER[$howGet];
33
  }
34
- $ips[] = $ip = array_key_exists('REMOTE_ADDR', $_SERVER) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
35
  foreach ($ips as $ip) {
36
  if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {
37
  return $ip;
@@ -94,7 +94,7 @@ class wfWAFWordPress extends wfWAF {
94
  public function blockAction($e, $httpCode = 403) {
95
  if ($this->isInLearningMode()) {
96
  register_shutdown_function(array(
97
- $this, 'whitelistFailedRules',
98
  ));
99
  $this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest());
100
  $this->setLearningModeAttackException($e);
@@ -110,7 +110,7 @@ class wfWAFWordPress extends wfWAF {
110
  public function blockXSSAction($e, $httpCode = 403) {
111
  if ($this->isInLearningMode()) {
112
  register_shutdown_function(array(
113
- $this, 'whitelistFailedRules',
114
  ));
115
  $this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest());
116
  $this->setLearningModeAttackException($e);
@@ -145,6 +145,20 @@ class wfWAFWordPress extends wfWAF {
145
  $this->getStorageEngine()->setConfig('cron', $cron);
146
  }
147
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
  /**
150
  * @param $ip
28
 
29
  public function getIP() {
30
  $howGet = wfWAF::getInstance()->getStorageEngine()->getConfig('howGetIPs');
31
+ if (is_string($howGet) && is_array($_SERVER) && array_key_exists($howGet, $_SERVER)) {
32
  $ips[] = $_SERVER[$howGet];
33
  }
34
+ $ips[] = $ip = (is_array($_SERVER) && array_key_exists('REMOTE_ADDR', $_SERVER)) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
35
  foreach ($ips as $ip) {
36
  if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {
37
  return $ip;
94
  public function blockAction($e, $httpCode = 403) {
95
  if ($this->isInLearningMode()) {
96
  register_shutdown_function(array(
97
+ $this, 'whitelistFailedRulesIfNot404',
98
  ));
99
  $this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest());
100
  $this->setLearningModeAttackException($e);
110
  public function blockXSSAction($e, $httpCode = 403) {
111
  if ($this->isInLearningMode()) {
112
  register_shutdown_function(array(
113
+ $this, 'whitelistFailedRulesIfNot404',
114
  ));
115
  $this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest());
116
  $this->setLearningModeAttackException($e);
145
  $this->getStorageEngine()->setConfig('cron', $cron);
146
  }
147
 
148
+ /**
149
+ *
150
+ */
151
+ public function whitelistFailedRulesIfNot404() {
152
+ /** @var WP_Query $wp_query */
153
+ global $wp_query;
154
+ if (defined('ABSPATH') &&
155
+ isset($wp_query) && class_exists('WP_Query') && $wp_query instanceof WP_Query &&
156
+ method_exists($wp_query, 'is_404') && $wp_query->is_404() &&
157
+ function_exists('is_admin') && !is_admin()) {
158
+ return;
159
+ }
160
+ $this->whitelistFailedRules();
161
+ }
162
 
163
  /**
164
  * @param $ip
wordfence.php CHANGED
@@ -4,14 +4,14 @@ Plugin Name: Wordfence Security
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and High Speed Cache
6
  Author: Wordfence
7
- Version: 6.1.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', '6.1.3');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17
 
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and High Speed Cache
6
  Author: Wordfence
7
+ Version: 6.1.4
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
+ define('WORDFENCE_VERSION', '6.1.4');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17