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.
Release Info
Developer | wfmatt |
Plugin | 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 +24 -4
- js/admin.js +16 -7
- js/admin.liveTraffic.js +13 -0
- lib/menu_activity.php +21 -22
- lib/menu_blockedIPs.php +4 -4
- lib/menu_diagnostic.php +8 -2
- lib/menu_waf.php +248 -18
- lib/wfConfig.php +5 -3
- lib/wfDiagnostic.php +51 -0
- lib/wordfenceClass.php +123 -13
- readme.txt +18 -1
- vendor/wordfence/wf-waf/src/lib/http.php +9 -9
- vendor/wordfence/wf-waf/src/lib/json.php +958 -0
- vendor/wordfence/wf-waf/src/lib/parser/lexer.php +13 -13
- vendor/wordfence/wf-waf/src/lib/parser/parser.php +4 -4
- vendor/wordfence/wf-waf/src/lib/parser/sqli.php +5 -5
- vendor/wordfence/wf-waf/src/lib/request.php +58 -57
- vendor/wordfence/wf-waf/src/lib/rules.php +11 -11
- vendor/wordfence/wf-waf/src/lib/storage/file.php +34 -33
- vendor/wordfence/wf-waf/src/lib/utils.php +216 -21
- vendor/wordfence/wf-waf/src/lib/waf.php +5 -5
- vendor/wordfence/wf-waf/src/views/403-roadblock.php +2 -2
- waf/bootstrap.php +18 -4
- wordfence.php +2 -2
@@ -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 |
+
}
|
@@ -65,7 +65,12 @@
|
|
65 |
});
|
66 |
|
67 |
$('#doSendEmail').click(function() {
|
68 |
-
|
|
|
|
|
|
|
|
|
|
|
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(
|
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 |
-
|
2424 |
-
'
|
|
|
|
|
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) {
|
@@ -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;
|
@@ -343,45 +343,44 @@
|
|
343 |
<tr>
|
344 |
<td>
|
345 |
<span data-bind="if: blocked()">
|
346 |
-
<
|
347 |
data-bind="click: $root.unblockIP">
|
348 |
Unblock this IP
|
349 |
-
</
|
350 |
</span>
|
351 |
<span data-bind="if: rangeBlocked()">
|
352 |
-
<
|
353 |
data-bind="click: $root.unblockNetwork">Unblock this range
|
354 |
-
</
|
355 |
</span>
|
356 |
<span data-bind="if: !blocked() && !rangeBlocked()">
|
357 |
-
<
|
358 |
data-bind="click: $root.blockIP">
|
359 |
Block this IP
|
360 |
-
</
|
361 |
</span>
|
362 |
-
<
|
363 |
-
data-bind="
|
364 |
Block this network
|
365 |
-
</
|
366 |
-
<
|
367 |
-
|
368 |
-
target="_blank"></
|
369 |
-
<
|
370 |
-
data-bind="
|
371 |
-
See
|
372 |
-
|
373 |
-
</button>
|
374 |
<span data-bind="if: action() == 'blocked:waf'">
|
375 |
-
<
|
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 |
-
</
|
380 |
<?php if (WFWAF_DEBUG): ?>
|
381 |
-
<
|
382 |
-
data-bind="
|
383 |
Debug this Request
|
384 |
-
</
|
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>
|
@@ -65,7 +65,8 @@
|
|
65 |
|
66 |
<script type="text/x-jquery-template" id="wfLockedOutIPsTmpl">
|
67 |
<div>
|
68 |
-
<
|
|
|
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 |
-
<
|
|
|
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 |
|
@@ -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="
|
297 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
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>
|
@@ -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
|
|
|
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
|
|
|
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
|
|
|
|
|
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 $
|
87 |
class="wafStatus-enabled" value="enabled">Enabled and Protecting
|
88 |
</option>
|
89 |
-
<option<?php echo $
|
90 |
class="wafStatus-learning-mode" value="learning-mode">Learning Mode
|
91 |
</option>
|
92 |
-
<option<?php echo $
|
93 |
class="wafStatus-disabled" value="disabled">Disabled
|
94 |
</option>
|
95 |
</select>
|
96 |
<script>
|
97 |
(function($) {
|
98 |
-
$('#input-wafStatus').val(<?php echo json_encode($
|
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"
|
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
|
|
|
137 |
mode. This allows
|
138 |
-
Wordfence to learn about your site so that we can understand how to protect it and
|
139 |
-
|
|
|
|
|
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="
|
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() {
|
@@ -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) *
|
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
|
724 |
} else {
|
725 |
-
self::getDB()->queryWrite("insert
|
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();
|
@@ -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 |
}
|
@@ -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 |
-
|
|
|
|
|
668 |
}
|
669 |
|
670 |
if (wfConfig::get('other_hideWPVersion')) {
|
@@ -998,14 +1015,25 @@ SQL
|
|
998 |
$waf->getStorageEngine()->setConfig($key, $value);
|
999 |
}
|
1000 |
|
1001 |
-
|
1002 |
-
|
1003 |
-
if (
|
1004 |
-
|
1005 |
-
|
1006 |
-
'
|
1007 |
-
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
/**
|
@@ -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.
|
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.
|
@@ -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);
|
@@ -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 |
+
}
|
@@ -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 |
}
|
@@ -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
|
@@ -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) {
|
@@ -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 (
|
265 |
-
$
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
|
|
272 |
|
273 |
-
|
274 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
|
|
|
|
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 |
}
|
@@ -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) {
|
@@ -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)
|
|
|
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 |
|
@@ -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 |
}
|
@@ -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() &&
|
@@ -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 |
|
@@ -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, '
|
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, '
|
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
|
@@ -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.
|
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.
|
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 |
|