Version Description
- Enhancement: Added Web Application Firewall
- Enhancement: Added Diagnostics page
- Enhancement: Added new scans:
- Admins created outside of WordPress
- Publicly accessible common (database or wp-config.php) backup files
- Improvement: Updated Live Traffic with filters and to include blocked requests in the feed.
Download this release
Release Info
Developer | wfmatt |
Plugin | Wordfence Security – Firewall & Malware Scan |
Version | 6.1.1 |
Comparing to | |
See all releases |
Code changes from version 6.0.25 to 6.1.1
- css/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- css/images/ui-bg_flat_100_1997c7_40x100.png +0 -0
- css/images/ui-bg_flat_100_222_40x100.png +0 -0
- css/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- css/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- css/images/ui-bg_highlight-soft_75_a5a5a5_1x100.png +0 -0
- css/images/ui-icons_222222_256x240.png +0 -0
- css/images/ui-icons_cd0a0a_256x240.png +0 -0
- css/images/ui-icons_fbe569_256x240.png +0 -0
- css/images/ui-icons_fff_256x240.png +0 -0
- css/jquery-ui-timepicker-addon.css +27 -0
- css/jquery-ui.min.css +7 -0
- css/jquery-ui.structure.min.css +5 -0
- css/jquery-ui.theme.min.css +5 -0
- css/main.css +489 -5
- css/select2.min.css +1 -0
- js/admin.js +455 -39
- js/admin.liveTraffic.js +680 -0
- js/jquery-ui-timepicker-addon.js +2245 -0
- js/knockout-3.3.0.js +115 -0
- js/select2.min.js +2 -0
- lib/compat.php +17 -0
- lib/dashboard.php +0 -1
- lib/email_newIssues.php +11 -3
- lib/menu_activity.php +416 -209
- lib/menu_countryBlocking.php +7 -7
- lib/menu_diagnostic.php +404 -0
- lib/menu_options.php +61 -112
- lib/menu_passwd.php +5 -4
- lib/menu_scan.php +201 -18
- lib/menu_scanSchedule.php +6 -9
- lib/menu_twoFactor.php +7 -8
- lib/menu_waf.php +472 -0
- lib/sysinfo.php +1 -1
- lib/wf503.php +2 -2
- lib/wfAPI.php +6 -5
- lib/wfConfig.php +36 -11
- lib/wfCrawl.php +9 -11
- lib/wfDB.php +159 -0
- lib/wfDiagnostic.php +245 -0
- lib/wfIssues.php +5 -2
- lib/wfLog.php +979 -31
- lib/wfScanEngine.php +524 -87
- lib/wfSchema.php +2 -2
- lib/wfUtils.php +306 -19
- lib/wfView.php +2 -2
- lib/wordfenceClass.php +1794 -78
- lib/wordfenceConstants.php +3 -1
- lib/wordfenceHash.php +9 -14
- lib/wordfenceScanner.php +111 -96
- readme.txt +17 -4
- vendor/autoload.php +7 -0
- vendor/composer/ClassLoader.php +413 -0
- vendor/composer/LICENSE +21 -0
- vendor/composer/autoload_classmap.php +9 -0
- vendor/composer/autoload_namespaces.php +9 -0
- vendor/composer/autoload_psr4.php +9 -0
- vendor/composer/autoload_real.php +45 -0
- vendor/composer/installed.json +20 -0
- vendor/wordfence/wf-waf/src/baseRules.rules +187 -0
- vendor/wordfence/wf-waf/src/bootstrap-sample.php +57 -0
- vendor/wordfence/wf-waf/src/init.php +28 -0
- vendor/wordfence/wf-waf/src/lib/config.php +2 -0
- vendor/wordfence/wf-waf/src/lib/http.php +438 -0
- vendor/wordfence/wf-waf/src/lib/parser/lexer.php +667 -0
- vendor/wordfence/wf-waf/src/lib/parser/parser.php +754 -0
- vendor/wordfence/wf-waf/src/lib/parser/sqli.php +2971 -0
- vendor/wordfence/wf-waf/src/lib/request.php +816 -0
- vendor/wordfence/wf-waf/src/lib/rules.php +1229 -0
- vendor/wordfence/wf-waf/src/lib/storage.php +62 -0
- vendor/wordfence/wf-waf/src/lib/storage/file.php +1320 -0
- vendor/wordfence/wf-waf/src/lib/utils.php +321 -0
- vendor/wordfence/wf-waf/src/lib/view.php +127 -0
- vendor/wordfence/wf-waf/src/lib/waf.php +1545 -0
- vendor/wordfence/wf-waf/src/logs/.htaccess +7 -0
- vendor/wordfence/wf-waf/src/logs/attack-data.php +1 -0
- vendor/wordfence/wf-waf/src/logs/config.php +0 -0
- vendor/wordfence/wf-waf/src/logs/ips.php +1 -0
- vendor/wordfence/wf-waf/src/rules.key +9 -0
- vendor/wordfence/wf-waf/src/rules.php +185 -0
- vendor/wordfence/wf-waf/src/views/403-roadblock.php +110 -0
- vendor/wordfence/wf-waf/src/views/403.php +14 -0
- views/waf/debug.php +225 -0
- waf/bootstrap.php +249 -0
- waf/wfWAFUserIPRange.php +224 -0
- wordfence.php +37 -2
css/images/ui-bg_flat_0_aaaaaa_40x100.png
ADDED
Binary file
|
css/images/ui-bg_flat_100_1997c7_40x100.png
ADDED
Binary file
|
css/images/ui-bg_flat_100_222_40x100.png
ADDED
Binary file
|
css/images/ui-bg_flat_75_ffffff_40x100.png
ADDED
Binary file
|
css/images/ui-bg_glass_95_fef1ec_1x400.png
ADDED
Binary file
|
css/images/ui-bg_highlight-soft_75_a5a5a5_1x100.png
ADDED
Binary file
|
css/images/ui-icons_222222_256x240.png
ADDED
Binary file
|
css/images/ui-icons_cd0a0a_256x240.png
ADDED
Binary file
|
css/images/ui-icons_fbe569_256x240.png
ADDED
Binary file
|
css/images/ui-icons_fff_256x240.png
ADDED
Binary file
|
css/jquery-ui-timepicker-addon.css
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.ui-timepicker-div .ui-widget-header { margin-bottom: 8px; }
|
2 |
+
.ui-timepicker-div dl { text-align: left; }
|
3 |
+
.ui-timepicker-div dl dt { float: left; clear:left; padding: 0 0 0 5px; }
|
4 |
+
.ui-timepicker-div dl dd { margin: 0 10px 10px 40%; }
|
5 |
+
.ui-timepicker-div td { font-size: 90%; }
|
6 |
+
.ui-tpicker-grid-label { background: none; border: none; margin: 0; padding: 0; }
|
7 |
+
.ui-timepicker-div .ui_tpicker_unit_hide{ display: none; }
|
8 |
+
|
9 |
+
.ui-timepicker-rtl{ direction: rtl; }
|
10 |
+
.ui-timepicker-rtl dl { text-align: right; padding: 0 5px 0 0; }
|
11 |
+
.ui-timepicker-rtl dl dt{ float: right; clear: right; }
|
12 |
+
.ui-timepicker-rtl dl dd { margin: 0 40% 10px 10px; }
|
13 |
+
|
14 |
+
/* Shortened version style */
|
15 |
+
.ui-timepicker-div.ui-timepicker-oneLine { padding-right: 2px; }
|
16 |
+
.ui-timepicker-div.ui-timepicker-oneLine .ui_tpicker_time,
|
17 |
+
.ui-timepicker-div.ui-timepicker-oneLine dt { display: none; }
|
18 |
+
.ui-timepicker-div.ui-timepicker-oneLine .ui_tpicker_time_label { display: block; padding-top: 2px; }
|
19 |
+
.ui-timepicker-div.ui-timepicker-oneLine dl { text-align: right; }
|
20 |
+
.ui-timepicker-div.ui-timepicker-oneLine dl dd,
|
21 |
+
.ui-timepicker-div.ui-timepicker-oneLine dl dd > div { display:inline-block; margin:0; }
|
22 |
+
.ui-timepicker-div.ui-timepicker-oneLine dl dd.ui_tpicker_minute:before,
|
23 |
+
.ui-timepicker-div.ui-timepicker-oneLine dl dd.ui_tpicker_second:before { content:':'; display:inline-block; }
|
24 |
+
.ui-timepicker-div.ui-timepicker-oneLine dl dd.ui_tpicker_millisec:before,
|
25 |
+
.ui-timepicker-div.ui-timepicker-oneLine dl dd.ui_tpicker_microsec:before { content:'.'; display:inline-block; }
|
26 |
+
.ui-timepicker-div.ui-timepicker-oneLine .ui_tpicker_unit_hide,
|
27 |
+
.ui-timepicker-div.ui-timepicker-oneLine .ui_tpicker_unit_hide:before{ display: none; }
|
css/jquery-ui.min.css
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*! jQuery UI - v1.11.4 - 2015-10-13
|
2 |
+
* http://jqueryui.com
|
3 |
+
* Includes: core.css, draggable.css, resizable.css, selectable.css, sortable.css, accordion.css, autocomplete.css, button.css, datepicker.css, dialog.css, menu.css, progressbar.css, selectmenu.css, slider.css, spinner.css, tabs.css, tooltip.css, theme.css
|
4 |
+
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=3px&bgColorHeader=%23222&bgTextureHeader=flat&bgImgOpacityHeader=100&borderColorHeader=%23474747&fcHeader=%23fff&iconColorHeader=%23fff&bgColorContent=%23ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=%23aaaaaa&fcContent=%23222222&iconColorContent=%23222222&bgColorDefault=%23a5a5a5&bgTextureDefault=highlight_soft&bgImgOpacityDefault=75&borderColorDefault=%239f9f9f&fcDefault=%23fff&iconColorDefault=%23fff&bgColorHover=%231997c7&bgTextureHover=flat&bgImgOpacityHover=100&borderColorHover=%23198cb7&fcHover=%23fff&iconColorHover=%23fff&bgColorActive=%231997c7&bgTextureActive=flat&bgImgOpacityActive=100&borderColorActive=%23198cb7&fcActive=%23fff&iconColorActive=%23fff&bgColorHighlight=%23fffaba&bgTextureHighlight=flat&bgImgOpacityHighlight=100&borderColorHighlight=%23eac500&fcHighlight=%23222222&iconColorHighlight=%23ec882f&bgColorError=%23fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=%23cd0a0a&fcError=%23cd0a0a&iconColorError=%23cd0a0a&bgColorOverlay=%23aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=%23aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
|
5 |
+
* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */
|
6 |
+
|
7 |
+
.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0 0;padding:.5em .5em .5em .7em;min-height:0;font-size:100%}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:none}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{position:relative;margin:0;padding:3px 1em 3px .4em;cursor:pointer;min-height:0;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:bold;line-height:1.5;padding:2px 0.4em;margin:0.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-button{display:inline-block;overflow:hidden;position:relative;text-decoration:none;cursor:pointer}.ui-selectmenu-button span.ui-icon{right:0.5em;left:auto;margin-top:-8px;position:absolute;top:50%}.ui-selectmenu-button span.ui-selectmenu-text{text-align:left;padding:0.4em 2.1em 0.4em 1em;display:block;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #474747;background:#222 url("images/ui-bg_flat_100_222_40x100.png") 50% 50% repeat-x;color:#fff;font-weight:bold}.ui-widget-header a{color:#fff}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #9f9f9f;background:#a5a5a5 url("images/ui-bg_highlight-soft_75_a5a5a5_1x100.png") 50% 50% repeat-x;font-weight:normal;color:#fff}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#fff;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #198cb7;background:#1997c7 url("images/ui-bg_flat_100_1997c7_40x100.png") 50% 50% repeat-x;font-weight:normal;color:#fff}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#fff;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #198cb7;background:#1997c7 url("images/ui-bg_flat_100_1997c7_40x100.png") 50% 50% repeat-x;font-weight:normal;color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #eac500;background:#fffaba url("images/ui-bg_flat_100_fffaba_40x100.png") 50% 50% repeat-x;color:#222}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#222}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;color:#cd0a0a}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_fff_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_fff_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_fff_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_fff_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_ec882f_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cd0a0a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;background:#aaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30);border-radius:8px}
|
css/jquery-ui.structure.min.css
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*! jQuery UI - v1.11.4 - 2015-10-11
|
2 |
+
* http://jqueryui.com
|
3 |
+
* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */
|
4 |
+
|
5 |
+
.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0 0;padding:.5em .5em .5em .7em;min-height:0;font-size:100%}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:none}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{position:relative;margin:0;padding:3px 1em 3px .4em;cursor:pointer;min-height:0;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:bold;line-height:1.5;padding:2px 0.4em;margin:0.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-button{display:inline-block;overflow:hidden;position:relative;text-decoration:none;cursor:pointer}.ui-selectmenu-button span.ui-icon{right:0.5em;left:auto;margin-top:-8px;position:absolute;top:50%}.ui-selectmenu-button span.ui-selectmenu-text{text-align:left;padding:0.4em 2.1em 0.4em 1em;display:block;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}
|
css/jquery-ui.theme.min.css
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*! jQuery UI - v1.11.4 - 2015-10-13
|
2 |
+
* http://jqueryui.com
|
3 |
+
* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */
|
4 |
+
|
5 |
+
.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #474747;background:#222 url("images/ui-bg_flat_100_222_40x100.png") 50% 50% repeat-x;color:#fff;font-weight:bold}.ui-widget-header a{color:#fff}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #9f9f9f;background:#a5a5a5 url("images/ui-bg_highlight-soft_75_a5a5a5_1x100.png") 50% 50% repeat-x;font-weight:normal;color:#fff}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#fff;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #198cb7;background:#1997c7 url("images/ui-bg_flat_100_1997c7_40x100.png") 50% 50% repeat-x;font-weight:normal;color:#fff}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#fff;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #198cb7;background:#1997c7 url("images/ui-bg_flat_100_1997c7_40x100.png") 50% 50% repeat-x;font-weight:normal;color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #eac500;background:#fffaba url("images/ui-bg_flat_100_fffaba_40x100.png") 50% 50% repeat-x;color:#222}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#222}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;color:#cd0a0a}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_fff_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_fff_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_fff_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_fff_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_ec882f_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cd0a0a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;background:#aaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30);border-radius:8px}
|
css/main.css
CHANGED
@@ -18,6 +18,9 @@ div.wordfenceLive {
|
|
18 |
font-size: 14px;
|
19 |
-webkit-font-smoothing: antialiased;
|
20 |
}
|
|
|
|
|
|
|
21 |
div.wordfenceLive h2 {
|
22 |
font-weight: bold;
|
23 |
color: #888;
|
@@ -46,6 +49,13 @@ div.wordfenceLive p {
|
|
46 |
#wfHeading {
|
47 |
white-space: nowrap;
|
48 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
div.wordfence-lock-icon {
|
50 |
background-image: url(../images/wordfence-logo-32x32.png);
|
51 |
}
|
@@ -170,6 +180,9 @@ div.wfIssue table.wfIssueLinks td { border-width: 0; text-align: left; padding-r
|
|
170 |
display: block;
|
171 |
width: 60px;
|
172 |
}
|
|
|
|
|
|
|
173 |
.wfProbSev1, .wfProbSev2, .wfAjaxLight128, .wfResolved {
|
174 |
width: 128px;
|
175 |
height: 128px;
|
@@ -191,7 +204,77 @@ div.wfIssue table.wfIssueLinks td { border-width: 0; text-align: left; padding-r
|
|
191 |
img.wfFlag { vertical-align: middle; margin: -3px 4px 0 0; }
|
192 |
.wfHitTime { font-style: italic; }
|
193 |
.wfAvatar img { vertical-align: middle; }
|
194 |
-
.wfActEvent {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
195 |
.wfTimeAgo { font-family: Georgia, times; color: #999; font-weight: bold; font-style: italic; }
|
196 |
table.wfConfigForm th {
|
197 |
font-weight: normal;
|
@@ -284,7 +367,11 @@ h3.wfConfigHeading {
|
|
284 |
font-weight: bold;
|
285 |
color: #555;
|
286 |
}
|
287 |
-
|
|
|
|
|
|
|
|
|
288 |
.wferror {
|
289 |
color: #F00;
|
290 |
}
|
@@ -447,7 +534,9 @@ table.wf-table td {
|
|
447 |
border: 1px solid #ccc;
|
448 |
}
|
449 |
table.wf-table thead th,
|
450 |
-
table.wf-table thead td
|
|
|
|
|
451 |
background-color: #222;
|
452 |
color: #fff;
|
453 |
font-weight: bold;
|
@@ -461,9 +550,14 @@ table.wf-table tbody tr.even td,
|
|
461 |
table.wf-table tbody tr:nth-child(2n) td {
|
462 |
background-color: #eee;
|
463 |
}
|
464 |
-
table.wf-table tbody tr:hover td {
|
465 |
background-color: #fffbd8;
|
466 |
}
|
|
|
|
|
|
|
|
|
|
|
467 |
|
468 |
table.block-ranges-table {
|
469 |
border-collapse: collapse;
|
@@ -482,6 +576,13 @@ table.block-ranges-table tr td {
|
|
482 |
border: 1px solid #ffd975;
|
483 |
border-width: 1px 1px 1px 10px;
|
484 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
485 |
|
486 |
.wf-premium-callout {
|
487 |
border: 1px solid #00709E;
|
@@ -512,4 +613,387 @@ table.block-ranges-table tr td {
|
|
512 |
text-transform: uppercase;
|
513 |
font-weight: bold;
|
514 |
background-color: #00709E;
|
515 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
font-size: 14px;
|
19 |
-webkit-font-smoothing: antialiased;
|
20 |
}
|
21 |
+
.branch-4-4 div.wordfenceLive td {
|
22 |
+
padding: 5px 0px 0px;
|
23 |
+
}
|
24 |
div.wordfenceLive h2 {
|
25 |
font-weight: bold;
|
26 |
color: #888;
|
49 |
#wfHeading {
|
50 |
white-space: nowrap;
|
51 |
}
|
52 |
+
#wfHeading:after {
|
53 |
+
content: '.';
|
54 |
+
visibility: hidden;
|
55 |
+
display: block;
|
56 |
+
clear: both;
|
57 |
+
height: 0px;
|
58 |
+
}
|
59 |
div.wordfence-lock-icon {
|
60 |
background-image: url(../images/wordfence-logo-32x32.png);
|
61 |
}
|
180 |
display: block;
|
181 |
width: 60px;
|
182 |
}
|
183 |
+
.wfIssueOptions p {
|
184 |
+
margin:6px 0px 0px;
|
185 |
+
}
|
186 |
.wfProbSev1, .wfProbSev2, .wfAjaxLight128, .wfResolved {
|
187 |
width: 128px;
|
188 |
height: 128px;
|
204 |
img.wfFlag { vertical-align: middle; margin: -3px 4px 0 0; }
|
205 |
.wfHitTime { font-style: italic; }
|
206 |
.wfAvatar img { vertical-align: middle; }
|
207 |
+
.wfActEvent {
|
208 |
+
border-bottom: 1px solid #CCC;
|
209 |
+
padding: 10px 20px;
|
210 |
+
overflow: auto;
|
211 |
+
}
|
212 |
+
#wf-lt-listings .wfActEvent {
|
213 |
+
padding-left: 15px;
|
214 |
+
border-left: 5px solid #cccccc;
|
215 |
+
}
|
216 |
+
#wf-lt-listings .wfActEvent.wfHuman {
|
217 |
+
border-left: 5px solid #74cb76;
|
218 |
+
}
|
219 |
+
#wf-lt-listings .wfActEvent.wfActionBlocked {
|
220 |
+
border-left: 5px solid #d03935;
|
221 |
+
}
|
222 |
+
#wf-lt-listings .wfActEvent.wfNotice,
|
223 |
+
#wf-lt-listings .wfActEvent.wf404 {
|
224 |
+
border-left: 5px solid #ffeaa0;
|
225 |
+
}
|
226 |
+
#wf-lt-listings .wfActEvent.wfWarning {
|
227 |
+
border-left: 5px solid #ffa13f;
|
228 |
+
}
|
229 |
+
#wf-lt-listings .wfActEvent:hover {
|
230 |
+
background-color: #fff9e9 !important;
|
231 |
+
}
|
232 |
+
#wf-live-traffic {
|
233 |
+
position: relative;
|
234 |
+
overflow: visible;
|
235 |
+
}
|
236 |
+
#wf-live-traffic-legend {
|
237 |
+
position: absolute;
|
238 |
+
top: -1px;
|
239 |
+
left: auto;
|
240 |
+
right: -108px;
|
241 |
+
bottom: 100%;
|
242 |
+
}
|
243 |
+
#wf-live-traffic-legend.sticky {
|
244 |
+
position: fixed;
|
245 |
+
top: 51px;
|
246 |
+
right: auto;
|
247 |
+
left: 1150px;
|
248 |
+
}
|
249 |
+
#wf-live-traffic-legend ul {
|
250 |
+
margin: 0;
|
251 |
+
padding: 10px;
|
252 |
+
background-color: #fff;
|
253 |
+
border: 1px solid #CCC;
|
254 |
+
}
|
255 |
+
#wf-live-traffic-legend ul li {
|
256 |
+
margin: 0;
|
257 |
+
padding: 0;
|
258 |
+
}
|
259 |
+
#wf-live-traffic-legend ul li:before {
|
260 |
+
content: '';
|
261 |
+
display: block;
|
262 |
+
float: left;
|
263 |
+
margin: 3px 6px 0 0;
|
264 |
+
width: 12px;
|
265 |
+
height: 12px;
|
266 |
+
background-color: #CCC;
|
267 |
+
}
|
268 |
+
#wf-live-traffic-legend ul li.wfHuman:before {
|
269 |
+
background-color: #74cb76;
|
270 |
+
}
|
271 |
+
#wf-live-traffic-legend ul li.wfNotice:before {
|
272 |
+
background-color: #ffeaa0;
|
273 |
+
}
|
274 |
+
#wf-live-traffic-legend ul li.wfBlocked:before {
|
275 |
+
background-color: #d03935;
|
276 |
+
}
|
277 |
+
|
278 |
.wfTimeAgo { font-family: Georgia, times; color: #999; font-weight: bold; font-style: italic; }
|
279 |
table.wfConfigForm th {
|
280 |
font-weight: normal;
|
367 |
font-weight: bold;
|
368 |
color: #555;
|
369 |
}
|
370 |
+
.wfStartScanButton { text-align: center; }
|
371 |
+
.wf-spinner {
|
372 |
+
display: inline-block;
|
373 |
+
width: 4px;
|
374 |
+
}
|
375 |
.wferror {
|
376 |
color: #F00;
|
377 |
}
|
534 |
border: 1px solid #ccc;
|
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;
|
541 |
color: #fff;
|
542 |
font-weight: bold;
|
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 |
}
|
556 |
+
table.wf-table tbody.empty-row tr td {
|
557 |
+
border-width: 0;
|
558 |
+
padding: 8px 0;
|
559 |
+
background-color: transparent;
|
560 |
+
}
|
561 |
|
562 |
table.block-ranges-table {
|
563 |
border-collapse: collapse;
|
576 |
border: 1px solid #ffd975;
|
577 |
border-width: 1px 1px 1px 10px;
|
578 |
}
|
579 |
+
.wf-success {
|
580 |
+
margin: 12px 0;
|
581 |
+
padding: 8px;
|
582 |
+
background-color: #ffffff;
|
583 |
+
border: 1px solid #74cb76;
|
584 |
+
border-width: 1px 1px 1px 10px;
|
585 |
+
}
|
586 |
|
587 |
.wf-premium-callout {
|
588 |
border: 1px solid #00709E;
|
613 |
text-transform: uppercase;
|
614 |
font-weight: bold;
|
615 |
background-color: #00709E;
|
616 |
+
}
|
617 |
+
|
618 |
+
|
619 |
+
.wf-table td.error {
|
620 |
+
color: #d0514c;
|
621 |
+
font-weight: bold;
|
622 |
+
}
|
623 |
+
.wf-table td.success:before,
|
624 |
+
.wf-table td.error:before {
|
625 |
+
font-size: 16px;
|
626 |
+
display: inline-block;
|
627 |
+
margin: 0px 8px 0px 0px;
|
628 |
+
}
|
629 |
+
.wf-table td.error:before {
|
630 |
+
content: "\2718";
|
631 |
+
}
|
632 |
+
.wf-table td.success {
|
633 |
+
color: #008c10;
|
634 |
+
font-weight: bold;
|
635 |
+
|
636 |
+
max-width: 20%;
|
637 |
+
}
|
638 |
+
.wf-table td.success:before {
|
639 |
+
content: "\2713";
|
640 |
+
}
|
641 |
+
.wf-table td.inactive {
|
642 |
+
font-weight: bold;
|
643 |
+
color: #666666;
|
644 |
+
}
|
645 |
+
|
646 |
+
table.whitelist-table {
|
647 |
+
|
648 |
+
}
|
649 |
+
table.whitelist-table .whitelist-edit {
|
650 |
+
display: none;
|
651 |
+
}
|
652 |
+
table.whitelist-table .edit-mode .whitelist-display {
|
653 |
+
display: none;
|
654 |
+
}
|
655 |
+
table.whitelist-table .edit-mode .whitelist-edit {
|
656 |
+
display: block;
|
657 |
+
}
|
658 |
+
table.whitelist-table .edit-mode span.whitelist-edit,
|
659 |
+
table.whitelist-table .edit-mode input.whitelist-edit {
|
660 |
+
display: inline;
|
661 |
+
}
|
662 |
+
|
663 |
+
.wf-pad-small {
|
664 |
+
margin:8px 0;
|
665 |
+
}
|
666 |
+
#wf-lt-listings {
|
667 |
+
margin:0 0 0;
|
668 |
+
}
|
669 |
+
#wf-lt-listings a {
|
670 |
+
cursor: pointer;
|
671 |
+
text-decoration: underline;
|
672 |
+
}
|
673 |
+
.wfActionBlocked {
|
674 |
+
background-color: #fff6f6;
|
675 |
+
}
|
676 |
+
|
677 |
+
[class*="span"] {
|
678 |
+
float: left;
|
679 |
+
min-height: 1px;
|
680 |
+
margin-left: 30px;
|
681 |
+
}
|
682 |
+
.row-fluid {
|
683 |
+
width: 100%;
|
684 |
+
*zoom: 1;
|
685 |
+
}
|
686 |
+
.row-fluid:before,
|
687 |
+
.row-fluid:after {
|
688 |
+
display: table;
|
689 |
+
line-height: 0;
|
690 |
+
content: "";
|
691 |
+
}
|
692 |
+
.row-fluid:after {
|
693 |
+
clear: both;
|
694 |
+
}
|
695 |
+
.row-fluid [class*="span"] {
|
696 |
+
display: block;
|
697 |
+
float: left;
|
698 |
+
width: 100%;
|
699 |
+
min-height: 30px;
|
700 |
+
margin-left: 2.564102564102564%;
|
701 |
+
*margin-left: 2.5109110747408616%;
|
702 |
+
-webkit-box-sizing: border-box;
|
703 |
+
-moz-box-sizing: border-box;
|
704 |
+
box-sizing: border-box;
|
705 |
+
}
|
706 |
+
.row-fluid [class*="span"]:first-child {
|
707 |
+
margin-left: 0;
|
708 |
+
}
|
709 |
+
.row-fluid .controls-row [class*="span"] + [class*="span"] {
|
710 |
+
margin-left: 2.564102564102564%;
|
711 |
+
}
|
712 |
+
.row-fluid .span12 {
|
713 |
+
width: 100%;
|
714 |
+
*width: 99.94680851063829%;
|
715 |
+
}
|
716 |
+
.row-fluid .span11 {
|
717 |
+
width: 91.45299145299145%;
|
718 |
+
*width: 91.39979996362975%;
|
719 |
+
}
|
720 |
+
.row-fluid .span10 {
|
721 |
+
width: 82.90598290598291%;
|
722 |
+
*width: 82.8527914166212%;
|
723 |
+
}
|
724 |
+
.row-fluid .span9 {
|
725 |
+
width: 74.35897435897436%;
|
726 |
+
*width: 74.30578286961266%;
|
727 |
+
}
|
728 |
+
.row-fluid .span8 {
|
729 |
+
width: 65.81196581196582%;
|
730 |
+
*width: 65.75877432260411%;
|
731 |
+
}
|
732 |
+
.row-fluid .span7 {
|
733 |
+
width: 57.26495726495726%;
|
734 |
+
*width: 57.21176577559556%;
|
735 |
+
}
|
736 |
+
.row-fluid .span6 {
|
737 |
+
width: 48.717948717948715%;
|
738 |
+
*width: 48.664757228587014%;
|
739 |
+
}
|
740 |
+
.row-fluid .span5 {
|
741 |
+
width: 40.17094017094017%;
|
742 |
+
*width: 40.11774868157847%;
|
743 |
+
}
|
744 |
+
.row-fluid .span4 {
|
745 |
+
width: 31.623931623931625%;
|
746 |
+
*width: 31.570740134569924%;
|
747 |
+
}
|
748 |
+
.row-fluid .span3 {
|
749 |
+
width: 23.076923076923077%;
|
750 |
+
*width: 23.023731587561375%;
|
751 |
+
}
|
752 |
+
.row-fluid .span2 {
|
753 |
+
width: 14.52991452991453%;
|
754 |
+
*width: 14.476723040552828%;
|
755 |
+
}
|
756 |
+
.row-fluid .span1 {
|
757 |
+
width: 5.982905982905983%;
|
758 |
+
*width: 5.929714493544281%;
|
759 |
+
}
|
760 |
+
.row-fluid .offset12 {
|
761 |
+
margin-left: 105.12820512820512%;
|
762 |
+
*margin-left: 105.02182214948171%;
|
763 |
+
}
|
764 |
+
.row-fluid .offset12:first-child {
|
765 |
+
margin-left: 102.56410256410257%;
|
766 |
+
*margin-left: 102.45771958537915%;
|
767 |
+
}
|
768 |
+
.row-fluid .offset11 {
|
769 |
+
margin-left: 96.58119658119658%;
|
770 |
+
*margin-left: 96.47481360247316%;
|
771 |
+
}
|
772 |
+
.row-fluid .offset11:first-child {
|
773 |
+
margin-left: 94.01709401709402%;
|
774 |
+
*margin-left: 93.91071103837061%;
|
775 |
+
}
|
776 |
+
.row-fluid .offset10 {
|
777 |
+
margin-left: 88.03418803418803%;
|
778 |
+
*margin-left: 87.92780505546462%;
|
779 |
+
}
|
780 |
+
.row-fluid .offset10:first-child {
|
781 |
+
margin-left: 85.47008547008548%;
|
782 |
+
*margin-left: 85.36370249136206%;
|
783 |
+
}
|
784 |
+
.row-fluid .offset9 {
|
785 |
+
margin-left: 79.48717948717949%;
|
786 |
+
*margin-left: 79.38079650845607%;
|
787 |
+
}
|
788 |
+
.row-fluid .offset9:first-child {
|
789 |
+
margin-left: 76.92307692307693%;
|
790 |
+
*margin-left: 76.81669394435352%;
|
791 |
+
}
|
792 |
+
.row-fluid .offset8 {
|
793 |
+
margin-left: 70.94017094017094%;
|
794 |
+
*margin-left: 70.83378796144753%;
|
795 |
+
}
|
796 |
+
.row-fluid .offset8:first-child {
|
797 |
+
margin-left: 68.37606837606839%;
|
798 |
+
*margin-left: 68.26968539734497%;
|
799 |
+
}
|
800 |
+
.row-fluid .offset7 {
|
801 |
+
margin-left: 62.393162393162385%;
|
802 |
+
*margin-left: 62.28677941443899%;
|
803 |
+
}
|
804 |
+
.row-fluid .offset7:first-child {
|
805 |
+
margin-left: 59.82905982905982%;
|
806 |
+
*margin-left: 59.72267685033642%;
|
807 |
+
}
|
808 |
+
.row-fluid .offset6 {
|
809 |
+
margin-left: 53.84615384615384%;
|
810 |
+
*margin-left: 53.739770867430444%;
|
811 |
+
}
|
812 |
+
.row-fluid .offset6:first-child {
|
813 |
+
margin-left: 51.28205128205128%;
|
814 |
+
*margin-left: 51.175668303327875%;
|
815 |
+
}
|
816 |
+
.row-fluid .offset5 {
|
817 |
+
margin-left: 45.299145299145295%;
|
818 |
+
*margin-left: 45.1927623204219%;
|
819 |
+
}
|
820 |
+
.row-fluid .offset5:first-child {
|
821 |
+
margin-left: 42.73504273504273%;
|
822 |
+
*margin-left: 42.62865975631933%;
|
823 |
+
}
|
824 |
+
.row-fluid .offset4 {
|
825 |
+
margin-left: 36.75213675213675%;
|
826 |
+
*margin-left: 36.645753773413354%;
|
827 |
+
}
|
828 |
+
.row-fluid .offset4:first-child {
|
829 |
+
margin-left: 34.18803418803419%;
|
830 |
+
*margin-left: 34.081651209310785%;
|
831 |
+
}
|
832 |
+
.row-fluid .offset3 {
|
833 |
+
margin-left: 28.205128205128204%;
|
834 |
+
*margin-left: 28.0987452264048%;
|
835 |
+
}
|
836 |
+
.row-fluid .offset3:first-child {
|
837 |
+
margin-left: 25.641025641025642%;
|
838 |
+
*margin-left: 25.53464266230224%;
|
839 |
+
}
|
840 |
+
.row-fluid .offset2 {
|
841 |
+
margin-left: 19.65811965811966%;
|
842 |
+
*margin-left: 19.551736679396257%;
|
843 |
+
}
|
844 |
+
.row-fluid .offset2:first-child {
|
845 |
+
margin-left: 17.094017094017094%;
|
846 |
+
*margin-left: 16.98763411529369%;
|
847 |
+
}
|
848 |
+
.row-fluid .offset1 {
|
849 |
+
margin-left: 11.11111111111111%;
|
850 |
+
*margin-left: 11.004728132387708%;
|
851 |
+
}
|
852 |
+
.row-fluid .offset1:first-child {
|
853 |
+
margin-left: 8.547008547008547%;
|
854 |
+
*margin-left: 8.440625568285142%;
|
855 |
+
}
|
856 |
+
|
857 |
+
|
858 |
+
.highlighted {
|
859 |
+
-webkit-animation-duration: 1s;
|
860 |
+
animation-duration: 1s;
|
861 |
+
-webkit-animation-fill-mode: both;
|
862 |
+
animation-fill-mode: both;
|
863 |
+
-webkit-animation-timing-function: ease-out;
|
864 |
+
animation-timing-function: ease-out;
|
865 |
+
}
|
866 |
+
|
867 |
+
@-webkit-keyframes highlighted {
|
868 |
+
0% {
|
869 |
+
opacity: 0;
|
870 |
+
background-color: #ffeaa0;
|
871 |
+
}
|
872 |
+
100% {
|
873 |
+
opacity: 1;
|
874 |
+
background-color: #ffffff;
|
875 |
+
}
|
876 |
+
}
|
877 |
+
@keyframes highlighted {
|
878 |
+
0% {
|
879 |
+
opacity: 0;
|
880 |
+
background-color: #ffeaa0;
|
881 |
+
}
|
882 |
+
100% {
|
883 |
+
opacity: 1;
|
884 |
+
background-color: #ffffff;
|
885 |
+
}
|
886 |
+
}
|
887 |
+
@-webkit-keyframes highlightedBlocked {
|
888 |
+
0% {
|
889 |
+
opacity: 0;
|
890 |
+
background-color: #ffeaa0;
|
891 |
+
}
|
892 |
+
100% {
|
893 |
+
opacity: 1;
|
894 |
+
background-color: #fff6f6;
|
895 |
+
}
|
896 |
+
}
|
897 |
+
@keyframes highlightedBlocked {
|
898 |
+
0% {
|
899 |
+
opacity: 0;
|
900 |
+
background-color: #ffeaa0;
|
901 |
+
}
|
902 |
+
100% {
|
903 |
+
opacity: 1;
|
904 |
+
background-color: #fff6f6;
|
905 |
+
}
|
906 |
+
}
|
907 |
+
.highlighted {
|
908 |
+
-webkit-animation-name: highlighted;
|
909 |
+
animation-name: highlighted;
|
910 |
+
}
|
911 |
+
.highlighted.wfActionBlocked {
|
912 |
+
-webkit-animation-name: highlightedBlocked;
|
913 |
+
animation-name: highlightedBlocked;
|
914 |
+
}
|
915 |
+
|
916 |
+
#wf-lt-preset-filters {
|
917 |
+
min-width: 250px;
|
918 |
+
}
|
919 |
+
#wf-lt-advanced-filters > table {
|
920 |
+
width: 100%;
|
921 |
+
}
|
922 |
+
#wf-lt-advanced-filters > table > tr > td {
|
923 |
+
vertical-align: top;
|
924 |
+
}
|
925 |
+
.wf-lt-url {
|
926 |
+
white-space: nowrap;
|
927 |
+
}
|
928 |
+
|
929 |
+
#input-wafStatus,
|
930 |
+
#input-wafStatus option,
|
931 |
+
.select2-container--default {
|
932 |
+
font-size: 18px;
|
933 |
+
}
|
934 |
+
.wafStatus-enabled,
|
935 |
+
.wafStatus-learning-mode,
|
936 |
+
.wafStatus-disabled,
|
937 |
+
.wafStatus-enabled.select2-container--default .select2-selection--single .select2-selection__rendered,
|
938 |
+
.wafStatus-learning-mode.select2-container--default .select2-selection--single .select2-selection__rendered,
|
939 |
+
.wafStatus-disabled.select2-container--default .select2-selection--single .select2-selection__rendered {
|
940 |
+
color: #ffffff;
|
941 |
+
}
|
942 |
+
.wafStatus-learning-mode.select2-container--default .select2-selection--single .select2-selection__rendered {
|
943 |
+
/*color: #484d6a;*/
|
944 |
+
}
|
945 |
+
#waf-config-form .select2-container--default .select2-selection--single {
|
946 |
+
padding: 4px;
|
947 |
+
text-shadow: 0 0 3px #000000;
|
948 |
+
font-weight: bold;
|
949 |
+
border-radius: 3px;
|
950 |
+
}
|
951 |
+
#waf-config-form .select2-container .select2-selection--single {
|
952 |
+
height: auto;
|
953 |
+
}
|
954 |
+
/*.wafStatus-enabled,*/
|
955 |
+
.wafStatus-enabled.select2-container--default .select2-selection--single {
|
956 |
+
background-color: #61e157;
|
957 |
+
border-color: #43ad3f;
|
958 |
+
}
|
959 |
+
/*.wafStatus-learning-mode,*/
|
960 |
+
.wafStatus-learning-mode.select2-container--default .select2-selection--single {
|
961 |
+
background-color: #ffe674;
|
962 |
+
border-color: #e5ae35;
|
963 |
+
}
|
964 |
+
/*.wafStatus-disabled,*/
|
965 |
+
.wafStatus-disabled.select2-container--default .select2-selection--single {
|
966 |
+
background-color: #ff6d69;
|
967 |
+
border-color: #dd422c;
|
968 |
+
}
|
969 |
+
#waf-config-form .select2-container--default .select2-selection--single .select2-selection__arrow {
|
970 |
+
height: 100%;
|
971 |
+
top: 0;
|
972 |
+
}
|
973 |
+
.wafStatus-enabled.select2-container--default .select2-selection--single .select2-selection__arrow b,
|
974 |
+
.wafStatus-learning-mode.select2-container--default .select2-selection--single .select2-selection__arrow b,
|
975 |
+
.wafStatus-disabled.select2-container--default .select2-selection--single .select2-selection__arrow b {
|
976 |
+
border-color: #ffffff transparent transparent;
|
977 |
+
}
|
978 |
+
.wafStatus-enabled.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b,
|
979 |
+
.wafStatus-learning-mode.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b,
|
980 |
+
.wafStatus-disabled.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
|
981 |
+
border-color: transparent transparent #ffffff;
|
982 |
+
}
|
983 |
+
.wafStatus-description {
|
984 |
+
display:none;
|
985 |
+
max-width: 500px;
|
986 |
+
font-style: italic;
|
987 |
+
font-size: 14px;
|
988 |
+
line-height: 1.3;
|
989 |
+
}
|
990 |
+
|
991 |
+
pre.wf-pre {
|
992 |
+
margin:8px 0 20px;
|
993 |
+
}
|
994 |
+
pre.wf-pre {
|
995 |
+
padding: 12px;
|
996 |
+
background: #ffffff;
|
997 |
+
border: 1px solid #999999;
|
998 |
+
overflow: auto;
|
999 |
+
}
|
css/select2.min.css
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle;}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none;}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px;}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none;}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap;}.select2-container .select2-search--inline{float:left;}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none;}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051;}.select2-results{display:block;}.select2-results__options{list-style:none;margin:0;padding:0;}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none;}.select2-results__option[aria-selected]{cursor:pointer;}.select2-container--open .select2-dropdown{left:0;}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0;}.select2-search--dropdown{display:block;padding:4px;}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box;}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none;}.select2-search--dropdown.select2-search--hide{display:none;}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0);}.select2-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px;}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px;}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999;}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px;}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0;}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left;}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto;}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default;}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none;}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px;}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%;}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left;}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px;}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px;}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px;}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333;}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder{float:right;}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto;}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto;}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0;}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default;}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none;}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0;}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0;}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa;}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto;}.select2-container--default .select2-results__option[role=group]{padding:0;}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999;}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd;}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em;}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white;}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px;}.select2-container--classic .select2-selection--single{background-color:#f6f6f6;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #ffffff 50%, #eeeeee 100%);background-image:-o-linear-gradient(top, #ffffff 50%, #eeeeee 100%);background-image:linear-gradient(to bottom, #ffffff 50%, #eeeeee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb;}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px;}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px;}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999;}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);background-image:-o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);background-image:linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#cccccc', GradientType=0);}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0;}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left;}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto;}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb;}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none;}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px;}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #ffffff 0%, #eeeeee 50%);background-image:-o-linear-gradient(top, #ffffff 0%, #eeeeee 50%);background-image:linear-gradient(to bottom, #ffffff 0%, #eeeeee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eeeeee 50%, #ffffff 100%);background-image:-o-linear-gradient(top, #eeeeee 50%, #ffffff 100%);background-image:linear-gradient(to bottom, #eeeeee 50%, #ffffff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0;}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb;}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px;}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none;}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px;}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px;}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555;}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right;}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto;}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto;}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb;}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0;}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0;}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;}.select2-container--classic .select2-dropdown{background-color:white;border:1px solid transparent;}.select2-container--classic .select2-dropdown--above{border-bottom:none;}.select2-container--classic .select2-dropdown--below{border-top:none;}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto;}.select2-container--classic .select2-results__option[role=group]{padding:0;}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey;}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:white;}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px;}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb;}
|
js/admin.js
CHANGED
@@ -40,6 +40,8 @@
|
|
40 |
passwdAuditUpdateInt: false,
|
41 |
_windowHasFocus: true,
|
42 |
serverTimestampOffset: 0,
|
|
|
|
|
43 |
|
44 |
init: function() {
|
45 |
this.nonce = WordfenceAdminVars.firstNonce;
|
@@ -55,6 +57,28 @@
|
|
55 |
self._windowHasFocus = true;
|
56 |
}).focus();
|
57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
$(document).focus();
|
59 |
|
60 |
// (docs|support).wordfence.com GA links
|
@@ -87,6 +111,12 @@
|
|
87 |
if (this.needTour()) {
|
88 |
this.scanTourStart();
|
89 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
} else if (jQuery('#wordfenceMode_activity').length > 0) {
|
91 |
this.mode = 'activity';
|
92 |
this.setupSwitches('wfLiveTrafficOnOff', 'liveTrafficEnabled', function() {
|
@@ -126,7 +156,7 @@
|
|
126 |
if (this.needTour()) {
|
127 |
this.tour('wfContentBasicOptions', 'wfMarkerBasicOptions', 'top', 'left', "Learn about Live Traffic Options", function() {
|
128 |
self.tour('wfContentLiveTrafficOptions', 'wfMarkerLiveTrafficOptions', 'bottom', 'left', "Learn about Scanning Options", function() {
|
129 |
-
self.tour('wfContentScansToInclude', 'wfMarkerScansToInclude', 'bottom', 'left', "Learn about
|
130 |
self.tour('wfContentFirewallRules', 'wfMarkerFirewallRules', 'bottom', 'left', "Learn about Login Security", function() {
|
131 |
self.tour('wfContentLoginSecurity', 'wfMarkerLoginSecurity', 'bottom', 'left', "Learn about Other Options", function() {
|
132 |
self.tour('wfContentOtherOptions', 'wfMarkerOtherOptions', 'bottom', 'left', false, false);
|
@@ -240,7 +270,7 @@
|
|
240 |
this.ajax('wordfence_sendTestEmail', {email: email}, function(res) {
|
241 |
if (res.result) {
|
242 |
self.colorbox('400px', "Test Email Sent", "Your test email was sent to the requested email address. The result we received from the WordPress wp_mail() function was: " +
|
243 |
-
|
244 |
}
|
245 |
});
|
246 |
},
|
@@ -278,8 +308,8 @@
|
|
278 |
var self = this;
|
279 |
this.tour('wfWelcomeContent1', 'wfHeading', 'top', 'left', "Continue the Tour", function() {
|
280 |
self.tour('wfWelcomeContent2', 'wfHeading', 'top', 'left', "Learn how to use Wordfence", function() {
|
281 |
-
self.tour('wfWelcomeContent3', 'wfHeading', 'top', 'left', "Learn about
|
282 |
-
self.tourRedir('
|
283 |
});
|
284 |
});
|
285 |
});
|
@@ -388,9 +418,34 @@
|
|
388 |
this.lastALogCtime = res.items[res.items.length - 1].ctime;
|
389 |
this.processActQueue(res.currentScanID);
|
390 |
}
|
|
|
|
|
|
|
391 |
}
|
392 |
this.activityLogUpdatePending = false;
|
393 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
394 |
processActQueue: function(currentScanID) {
|
395 |
if (this.activityQueue.length > 0) {
|
396 |
this.addActItem(this.activityQueue.shift());
|
@@ -527,17 +582,23 @@
|
|
527 |
var self = this;
|
528 |
var alsoGet = '';
|
529 |
var otherParams = '';
|
530 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
531 |
alsoGet = 'logList_' + this.activityMode;
|
532 |
otherParams = this.newestActivityTime;
|
533 |
} else if (this.mode == 'perfStats') {
|
534 |
alsoGet = 'perfStats';
|
535 |
otherParams = this.newestActivityTime;
|
536 |
}
|
537 |
-
|
538 |
-
|
539 |
-
otherParams: otherParams
|
540 |
-
}, function(res) {
|
541 |
self.handleTickerReturn(res);
|
542 |
}, function() {
|
543 |
self.tickerUpdatePending = false;
|
@@ -557,8 +618,19 @@
|
|
557 |
}
|
558 |
var haveEvents, newElem;
|
559 |
this.serverTimestampOffset = (new Date().getTime() / 1000) - res.serverTime;
|
|
|
560 |
|
561 |
-
if (this.mode == '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
562 |
if (res.alsoGet != 'logList_' + this.activityMode) {
|
563 |
return;
|
564 |
} //user switched panels since ajax request started
|
@@ -624,6 +696,7 @@
|
|
624 |
}
|
625 |
},
|
626 |
reverseLookupIPs: function() {
|
|
|
627 |
var ips = [];
|
628 |
jQuery('.wfReverseLookup').each(function(idx, elem) {
|
629 |
var txt = jQuery(elem).text();
|
@@ -653,7 +726,7 @@
|
|
653 |
for (var ip in res.ips) {
|
654 |
if (txt == ip) {
|
655 |
if (res.ips[ip]) {
|
656 |
-
jQuery(elem).html('<strong>Hostname:</strong> ' + res.ips[ip]);
|
657 |
} else {
|
658 |
jQuery(elem).html('');
|
659 |
}
|
@@ -674,31 +747,20 @@
|
|
674 |
});
|
675 |
},
|
676 |
startScan: function() {
|
|
|
|
|
|
|
|
|
677 |
var scanReqAnimation = setInterval(function() {
|
678 |
-
var
|
679 |
-
|
680 |
-
if (ch == '/') {
|
681 |
-
ch = '-';
|
682 |
-
}
|
683 |
-
else if (ch == '-') {
|
684 |
-
ch = '\\';
|
685 |
-
}
|
686 |
-
else if (ch == '\\') {
|
687 |
-
ch = '|';
|
688 |
-
}
|
689 |
-
else if (ch == '|') {
|
690 |
-
ch = '/';
|
691 |
-
}
|
692 |
-
else {
|
693 |
-
ch = '/';
|
694 |
-
}
|
695 |
-
jQuery('#wfStartScanButton1,#wfStartScanButton2').prop('value', "Requesting a New Scan " + ch);
|
696 |
}, 100);
|
697 |
setTimeout(function(res) {
|
698 |
clearInterval(scanReqAnimation);
|
699 |
-
jQuery('#wfStartScanButton1,#wfStartScanButton2').
|
700 |
}, 3000);
|
701 |
this.ajax('wordfence_scan', {}, function(res) {
|
|
|
702 |
});
|
703 |
},
|
704 |
displayPWAuditJobs: function(res) {
|
@@ -833,6 +895,16 @@
|
|
833 |
data += '&';
|
834 |
}
|
835 |
data += 'action=' + action + '&nonce=' + this.nonce;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
836 |
} else if (typeof(data) == 'object') {
|
837 |
data['action'] = action;
|
838 |
data['nonce'] = this.nonce;
|
@@ -885,8 +957,19 @@
|
|
885 |
this.colorboxOpen(elem[0], elem[1], elem[2]);
|
886 |
},
|
887 |
colorboxOpen: function(width, heading, body) {
|
|
|
888 |
this.colorboxIsOpen = true;
|
889 |
-
jQuery.colorbox({
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
890 |
},
|
891 |
scanRunningMsg: function() {
|
892 |
this.colorbox('400px', "A scan is running", "A scan is currently in progress. Please wait until it finishes before starting another scan.");
|
@@ -979,6 +1062,88 @@
|
|
979 |
});
|
980 |
}
|
981 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
982 |
restoreFile: function(issueID) {
|
983 |
var self = this;
|
984 |
this.ajax('wordfence_restoreFile', {
|
@@ -999,6 +1164,46 @@
|
|
999 |
});
|
1000 |
}
|
1001 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1002 |
deleteIssue: function(id) {
|
1003 |
var self = this;
|
1004 |
this.ajax('wordfence_deleteIssue', {id: id}, function(res) {
|
@@ -1475,7 +1680,7 @@
|
|
1475 |
self.loadBlockRanges();
|
1476 |
});
|
1477 |
},
|
1478 |
-
blockIP: function(IP, reason) {
|
1479 |
var self = this;
|
1480 |
this.ajax('wordfence_blockIP', {
|
1481 |
IP: IP,
|
@@ -1485,6 +1690,7 @@
|
|
1485 |
return;
|
1486 |
} else {
|
1487 |
self.reloadActivities();
|
|
|
1488 |
}
|
1489 |
});
|
1490 |
},
|
@@ -1510,12 +1716,13 @@
|
|
1510 |
self.staticTabChanged();
|
1511 |
});
|
1512 |
},
|
1513 |
-
unblockIP: function(IP) {
|
1514 |
var self = this;
|
1515 |
this.ajax('wordfence_unblockIP', {
|
1516 |
IP: IP
|
1517 |
}, function(res) {
|
1518 |
self.reloadActivities();
|
|
|
1519 |
});
|
1520 |
},
|
1521 |
unblockNetwork: function(id) {
|
@@ -1646,6 +1853,22 @@
|
|
1646 |
}
|
1647 |
});
|
1648 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1649 |
changeSecurityLevel: function() {
|
1650 |
var level = jQuery('#securityLevel').val();
|
1651 |
for (var k in WFSLevels[level].checkboxes) {
|
@@ -1668,8 +1891,8 @@
|
|
1668 |
return;
|
1669 |
}
|
1670 |
this.colorbox('450px', "Please confirm", body +
|
1671 |
-
|
1672 |
-
|
1673 |
},
|
1674 |
confirmClearAllBlocked: function(op) {
|
1675 |
var self = this;
|
@@ -1735,7 +1958,7 @@
|
|
1735 |
this.countryCodesToSave = codesArr.join(',');
|
1736 |
if (ownCountryBlocked) {
|
1737 |
this.colorbox('400px', "Please confirm blocking yourself", "You are about to block your own country. This could lead to you being locked out. Please make sure that your user profile on this machine has a current and valid email address and make sure you know what it is. That way if you are locked out, you can send yourself an unlock email. If you're sure you want to block your own country, click 'Confirm' below, otherwise click 'Cancel'.<br />" +
|
1738 |
-
|
1739 |
} else {
|
1740 |
this.confirmSaveCountryBlocking();
|
1741 |
}
|
@@ -1908,7 +2131,7 @@
|
|
1908 |
if (res.users && res.users.length > 0) {
|
1909 |
for (var i = 0; i < res.users.length; i++) {
|
1910 |
jQuery('<div id="twoFacCont_' + res.users[i].userID + '">' +
|
1911 |
-
|
1912 |
}
|
1913 |
}
|
1914 |
});
|
@@ -2083,6 +2306,43 @@
|
|
2083 |
}
|
2084 |
});
|
2085 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2086 |
windowHasFocus: function() {
|
2087 |
if (typeof document.hasFocus === 'function') {
|
2088 |
return document.hasFocus();
|
@@ -2128,17 +2388,173 @@
|
|
2128 |
if (!timestamp) {
|
2129 |
timestamp = el.attr('data-timestamp');
|
2130 |
}
|
2131 |
-
var serverTime =
|
2132 |
var format = el.data('wfformat');
|
2133 |
if (!format) {
|
2134 |
format = el.attr('data-format');
|
2135 |
}
|
2136 |
el.html(self.showTimestamp(timestamp, serverTime, format));
|
2137 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2138 |
}
|
2139 |
};
|
2140 |
-
window['WFAD'] = window['wordfenceAdmin'];
|
2141 |
|
|
|
2142 |
setInterval(function() {
|
2143 |
WFAD.updateTimeAgo();
|
2144 |
}, 1000);
|
40 |
passwdAuditUpdateInt: false,
|
41 |
_windowHasFocus: true,
|
42 |
serverTimestampOffset: 0,
|
43 |
+
serverMicrotime: 0,
|
44 |
+
wfLiveTraffic: null,
|
45 |
|
46 |
init: function() {
|
47 |
this.nonce = WordfenceAdminVars.firstNonce;
|
57 |
self._windowHasFocus = true;
|
58 |
}).focus();
|
59 |
|
60 |
+
$('.do-show').click(function() {
|
61 |
+
var $this = $(this);
|
62 |
+
$this.hide();
|
63 |
+
$($this.data('selector')).show();
|
64 |
+
return false;
|
65 |
+
});
|
66 |
+
|
67 |
+
$('#doSendEmail').click(function() {
|
68 |
+
WFAD.ajax('wordfence_sendDiagnostic', {email: $('#_email').val()}, function(res) {
|
69 |
+
if (res.result) {
|
70 |
+
self.colorbox('400px', "Email Diagnostic Report", "Diagnostic report has been sent successfully.");
|
71 |
+
} else {
|
72 |
+
self.colorbox('400px', "Error", "There was an error while sending the email.");
|
73 |
+
}
|
74 |
+
});
|
75 |
+
});
|
76 |
+
|
77 |
+
$('#sendByEmail').click(function() {
|
78 |
+
$('#sendByEmailForm').removeClass('hidden');
|
79 |
+
$(this).hide();
|
80 |
+
});
|
81 |
+
|
82 |
$(document).focus();
|
83 |
|
84 |
// (docs|support).wordfence.com GA links
|
111 |
if (this.needTour()) {
|
112 |
this.scanTourStart();
|
113 |
}
|
114 |
+
} else if (jQuery('#wordfenceMode_waf').length > 0) {
|
115 |
+
if (this.needTour()) {
|
116 |
+
this.tour('wfWAFTour', 'wfHeading', 'top', 'left', "Learn about Live Traffic", function() {
|
117 |
+
self.tourRedir('WordfenceActivity');
|
118 |
+
});
|
119 |
+
}
|
120 |
} else if (jQuery('#wordfenceMode_activity').length > 0) {
|
121 |
this.mode = 'activity';
|
122 |
this.setupSwitches('wfLiveTrafficOnOff', 'liveTrafficEnabled', function() {
|
156 |
if (this.needTour()) {
|
157 |
this.tour('wfContentBasicOptions', 'wfMarkerBasicOptions', 'top', 'left', "Learn about Live Traffic Options", function() {
|
158 |
self.tour('wfContentLiveTrafficOptions', 'wfMarkerLiveTrafficOptions', 'bottom', 'left', "Learn about Scanning Options", function() {
|
159 |
+
self.tour('wfContentScansToInclude', 'wfMarkerScansToInclude', 'bottom', 'left', "Learn about Rate Limiting Rules", function() {
|
160 |
self.tour('wfContentFirewallRules', 'wfMarkerFirewallRules', 'bottom', 'left', "Learn about Login Security", function() {
|
161 |
self.tour('wfContentLoginSecurity', 'wfMarkerLoginSecurity', 'bottom', 'left', "Learn about Other Options", function() {
|
162 |
self.tour('wfContentOtherOptions', 'wfMarkerOtherOptions', 'bottom', 'left', false, false);
|
270 |
this.ajax('wordfence_sendTestEmail', {email: email}, function(res) {
|
271 |
if (res.result) {
|
272 |
self.colorbox('400px', "Test Email Sent", "Your test email was sent to the requested email address. The result we received from the WordPress wp_mail() function was: " +
|
273 |
+
res.result + "<br /><br />A 'True' result means WordPress thinks the mail was sent without errors. A 'False' result means that WordPress encountered an error sending your mail. Note that it's possible to get a 'True' response with an error elsewhere in your mail system that may cause emails to not be delivered.");
|
274 |
}
|
275 |
});
|
276 |
},
|
308 |
var self = this;
|
309 |
this.tour('wfWelcomeContent1', 'wfHeading', 'top', 'left', "Continue the Tour", function() {
|
310 |
self.tour('wfWelcomeContent2', 'wfHeading', 'top', 'left', "Learn how to use Wordfence", function() {
|
311 |
+
self.tour('wfWelcomeContent3', 'wfHeading', 'top', 'left', "Learn about the Firewall", function() {
|
312 |
+
self.tourRedir('WordfenceWAF');
|
313 |
});
|
314 |
});
|
315 |
});
|
418 |
this.lastALogCtime = res.items[res.items.length - 1].ctime;
|
419 |
this.processActQueue(res.currentScanID);
|
420 |
}
|
421 |
+
if (res.signatureUpdateTime) {
|
422 |
+
this.updateSignaturesTimestamp(res.signatureUpdateTime);
|
423 |
+
}
|
424 |
}
|
425 |
this.activityLogUpdatePending = false;
|
426 |
},
|
427 |
+
|
428 |
+
updateSignaturesTimestamp: function(signatureUpdateTime) {
|
429 |
+
var date = new Date(signatureUpdateTime * 1000);
|
430 |
+
|
431 |
+
var dateString = date.toString();
|
432 |
+
if (date.toLocaleString) {
|
433 |
+
dateString = date.toLocaleString();
|
434 |
+
}
|
435 |
+
|
436 |
+
var sigTimestampEl = $('#wf-scan-sigs-last-update');
|
437 |
+
var newText = 'Last Updated: ' + dateString;
|
438 |
+
if (sigTimestampEl.text() !== newText) {
|
439 |
+
sigTimestampEl.text(newText)
|
440 |
+
.css({
|
441 |
+
'opacity': 0
|
442 |
+
})
|
443 |
+
.animate({
|
444 |
+
'opacity': 1
|
445 |
+
}, 500);
|
446 |
+
}
|
447 |
+
},
|
448 |
+
|
449 |
processActQueue: function(currentScanID) {
|
450 |
if (this.activityQueue.length > 0) {
|
451 |
this.addActItem(this.activityQueue.shift());
|
582 |
var self = this;
|
583 |
var alsoGet = '';
|
584 |
var otherParams = '';
|
585 |
+
var data = '';
|
586 |
+
if (this.mode == 'liveTraffic') {
|
587 |
+
alsoGet = 'liveTraffic';
|
588 |
+
otherParams = this.newestActivityTime;
|
589 |
+
data += this.wfLiveTraffic.getCurrentQueryString({
|
590 |
+
since: this.newestActivityTime
|
591 |
+
});
|
592 |
+
|
593 |
+
} else if (this.mode == 'activity' && /^(?:404|hit|human|ruser|gCrawler|crawler|loginLogout)$/.test(this.activityMode)) {
|
594 |
alsoGet = 'logList_' + this.activityMode;
|
595 |
otherParams = this.newestActivityTime;
|
596 |
} else if (this.mode == 'perfStats') {
|
597 |
alsoGet = 'perfStats';
|
598 |
otherParams = this.newestActivityTime;
|
599 |
}
|
600 |
+
data += '&alsoGet=' + encodeURIComponent(alsoGet) + '&otherParams=' + encodeURIComponent(otherParams);
|
601 |
+
this.ajax('wordfence_ticker', data, function(res) {
|
|
|
|
|
602 |
self.handleTickerReturn(res);
|
603 |
}, function() {
|
604 |
self.tickerUpdatePending = false;
|
618 |
}
|
619 |
var haveEvents, newElem;
|
620 |
this.serverTimestampOffset = (new Date().getTime() / 1000) - res.serverTime;
|
621 |
+
this.serverMicrotime = res.serverMicrotime;
|
622 |
|
623 |
+
if (this.mode == 'liveTraffic') {
|
624 |
+
if (res.events.length > 0) {
|
625 |
+
this.newestActivityTime = res.events[0]['ctime'];
|
626 |
+
}
|
627 |
+
if (typeof WFAD.wfLiveTraffic !== undefined) {
|
628 |
+
WFAD.wfLiveTraffic.prependListings(res.events, res);
|
629 |
+
this.reverseLookupIPs();
|
630 |
+
this.updateTimeAgo();
|
631 |
+
}
|
632 |
+
|
633 |
+
} else if (this.mode == 'activity') { // This mode is deprecated as of 6.1.0
|
634 |
if (res.alsoGet != 'logList_' + this.activityMode) {
|
635 |
return;
|
636 |
} //user switched panels since ajax request started
|
696 |
}
|
697 |
},
|
698 |
reverseLookupIPs: function() {
|
699 |
+
var self = this;
|
700 |
var ips = [];
|
701 |
jQuery('.wfReverseLookup').each(function(idx, elem) {
|
702 |
var txt = jQuery(elem).text();
|
726 |
for (var ip in res.ips) {
|
727 |
if (txt == ip) {
|
728 |
if (res.ips[ip]) {
|
729 |
+
jQuery(elem).html('<strong>Hostname:</strong> ' + self.htmlEscape(res.ips[ip]));
|
730 |
} else {
|
731 |
jQuery(elem).html('');
|
732 |
}
|
747 |
});
|
748 |
},
|
749 |
startScan: function() {
|
750 |
+
var spinnerValues = [
|
751 |
+
'|', '/', '-', '\\'
|
752 |
+
];
|
753 |
+
var count = 0;
|
754 |
var scanReqAnimation = setInterval(function() {
|
755 |
+
var ch = spinnerValues[count++ % spinnerValues.length];
|
756 |
+
jQuery('#wfStartScanButton1,#wfStartScanButton2').html("Requesting a New Scan <span class='wf-spinner'>" + ch + "</span>");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
757 |
}, 100);
|
758 |
setTimeout(function(res) {
|
759 |
clearInterval(scanReqAnimation);
|
760 |
+
jQuery('#wfStartScanButton1,#wfStartScanButton2').text("Start a Wordfence Scan");
|
761 |
}, 3000);
|
762 |
this.ajax('wordfence_scan', {}, function(res) {
|
763 |
+
|
764 |
});
|
765 |
},
|
766 |
displayPWAuditJobs: function(res) {
|
895 |
data += '&';
|
896 |
}
|
897 |
data += 'action=' + action + '&nonce=' + this.nonce;
|
898 |
+
} else if (typeof(data) == 'object' && data instanceof Array) {
|
899 |
+
// jQuery serialized form data
|
900 |
+
data.push({
|
901 |
+
name: 'action',
|
902 |
+
value: action
|
903 |
+
});
|
904 |
+
data.push({
|
905 |
+
name: 'nonce',
|
906 |
+
value: this.nonce
|
907 |
+
});
|
908 |
} else if (typeof(data) == 'object') {
|
909 |
data['action'] = action;
|
910 |
data['nonce'] = this.nonce;
|
957 |
this.colorboxOpen(elem[0], elem[1], elem[2]);
|
958 |
},
|
959 |
colorboxOpen: function(width, heading, body) {
|
960 |
+
var self = this;
|
961 |
this.colorboxIsOpen = true;
|
962 |
+
jQuery.colorbox({
|
963 |
+
width: width,
|
964 |
+
html: "<h3>" + heading + "</h3><p>" + body + "</p>",
|
965 |
+
onClosed: function() {
|
966 |
+
self.colorboxClose();
|
967 |
+
}
|
968 |
+
});
|
969 |
+
},
|
970 |
+
colorboxClose: function() {
|
971 |
+
this.colorboxIsOpen = false;
|
972 |
+
jQuery.colorbox.close();
|
973 |
},
|
974 |
scanRunningMsg: function() {
|
975 |
this.colorbox('400px', "A scan is running", "A scan is currently in progress. Please wait until it finishes before starting another scan.");
|
1062 |
});
|
1063 |
}
|
1064 |
},
|
1065 |
+
fixFPD: function(issueID) {
|
1066 |
+
var self = this;
|
1067 |
+
var title = "Full Path Disclosure";
|
1068 |
+
issueID = parseInt(issueID);
|
1069 |
+
|
1070 |
+
this.ajax('wordfence_checkFalconHtaccess', {}, function(res) {
|
1071 |
+
if (res.ok) {
|
1072 |
+
self.colorbox("400px", title, 'We are about to change your <em>.htaccess</em> file. Please make a backup of this file proceeding'
|
1073 |
+
+ '<br/>'
|
1074 |
+
+ '<a href="' + WordfenceAdminVars.ajaxURL + '?action=wordfence_downloadHtaccess&nonce=' + self.nonce + '" onclick="jQuery(\'#wfFPDNextBut\').prop(\'disabled\', false); return true;">Click here to download a backup copy of your .htaccess file now</a><br /><br /><input type="button" name="but1" id="wfFPDNextBut" value="Click to fix .htaccess" disabled="disabled" onclick="WFAD.fixFPD_WriteHtAccess(' + issueID + ');" />');
|
1075 |
+
} else if (res.nginx) {
|
1076 |
+
self.colorbox("400px", title, 'You are using an Nginx web server and using a FastCGI processor like PHP5-FPM. You will need to manually modify your php.ini to disable <em>display_error</em>');
|
1077 |
+
} else if (res.err) {
|
1078 |
+
self.colorbox('400px', "We encountered a problem", "We can't modify your .htaccess file for you because: " + res.err);
|
1079 |
+
}
|
1080 |
+
});
|
1081 |
+
},
|
1082 |
+
fixFPD_WriteHtAccess: function(issueID) {
|
1083 |
+
var self = this;
|
1084 |
+
self.colorboxClose();
|
1085 |
+
this.ajax('wordfence_fixFPD', {
|
1086 |
+
issueID: issueID
|
1087 |
+
}, function(res) {
|
1088 |
+
if (res.ok) {
|
1089 |
+
self.loadIssues(function() {
|
1090 |
+
self.colorbox("400px", "File restored OK", "The Full Path disclosure issue has been fixed");
|
1091 |
+
});
|
1092 |
+
} else {
|
1093 |
+
self.loadIssues(function() {
|
1094 |
+
self.colorbox('400px', 'An error occurred', res.cerrorMsg);
|
1095 |
+
});
|
1096 |
+
}
|
1097 |
+
});
|
1098 |
+
},
|
1099 |
+
|
1100 |
+
_handleHtAccess: function(issueID, callback, title, nginx) {
|
1101 |
+
var self = this;
|
1102 |
+
return function(res) {
|
1103 |
+
if (res.ok) {
|
1104 |
+
self.colorbox("400px", title, 'We are about to change your <em>.htaccess</em> file. Please make a backup of this file proceeding'
|
1105 |
+
+ '<br/>'
|
1106 |
+
+ '<a id="dlButton" href="' + WordfenceAdminVars.ajaxURL + '?action=wordfence_downloadHtaccess&nonce=' + self.nonce + '">Click here to download a backup copy of your .htaccess file now</a>'
|
1107 |
+
+ '<br /><br /><input type="button" name="but1" id="wfFPDNextBut" value="Click to fix .htaccess" disabled="disabled" />'
|
1108 |
+
);
|
1109 |
+
jQuery('#dlButton').click('click', function() {
|
1110 |
+
jQuery('#wfFPDNextBut').prop('disabled', false);
|
1111 |
+
});
|
1112 |
+
jQuery('#wfFPDNextBut').one('click', function() {
|
1113 |
+
self[callback](issueID);
|
1114 |
+
});
|
1115 |
+
} else if (res.nginx) {
|
1116 |
+
self.colorbox("400px", title, 'You are using an Nginx web server and using a FastCGI processor like PHP5-FPM. ' + nginx);
|
1117 |
+
} else if (res.err) {
|
1118 |
+
self.colorbox('400px', "We encountered a problem", "We can't modify your .htaccess file for you because: " + res.err);
|
1119 |
+
}
|
1120 |
+
};
|
1121 |
+
},
|
1122 |
+
_hideFile: function(issueID) {
|
1123 |
+
var self = this;
|
1124 |
+
var title = 'Modifying .htaccess';
|
1125 |
+
this.ajax('wordfence_hideFileHtaccess', {
|
1126 |
+
issueID: issueID
|
1127 |
+
}, function(res) {
|
1128 |
+
jQuery.colorbox.close();
|
1129 |
+
self.loadIssues(function() {
|
1130 |
+
if (res.ok) {
|
1131 |
+
self.colorbox("400px", title, 'Your .htaccess file has been updated successfully.');
|
1132 |
+
} else {
|
1133 |
+
self.colorbox("400px", title, 'We encountered a problem while trying to update your .htaccess file.');
|
1134 |
+
}
|
1135 |
+
});
|
1136 |
+
});
|
1137 |
+
},
|
1138 |
+
hideFile: function(issueID) {
|
1139 |
+
var self = this;
|
1140 |
+
var title = "Backup your .htaccess file";
|
1141 |
+
var nginx = "You will need to manually delete those files";
|
1142 |
+
issueID = parseInt(issueID, 10);
|
1143 |
+
|
1144 |
+
this.ajax('wordfence_checkFalconHtaccess', {}, this._handleHtAccess(issueID, '_hideFile', title, nginx));
|
1145 |
+
},
|
1146 |
+
|
1147 |
restoreFile: function(issueID) {
|
1148 |
var self = this;
|
1149 |
this.ajax('wordfence_restoreFile', {
|
1164 |
});
|
1165 |
}
|
1166 |
},
|
1167 |
+
|
1168 |
+
disableDirectoryListing: function(issueID) {
|
1169 |
+
var self = this;
|
1170 |
+
var title = "Disable Directory Listing";
|
1171 |
+
issueID = parseInt(issueID);
|
1172 |
+
|
1173 |
+
this.ajax('wordfence_checkFalconHtaccess', {}, function(res) {
|
1174 |
+
if (res.ok) {
|
1175 |
+
self.colorbox("400px", title, 'We are about to change your <em>.htaccess</em> file. Please make a backup of this file proceeding'
|
1176 |
+
+ '<br/>'
|
1177 |
+
+ '<a href="' + WordfenceAdminVars.ajaxURL + '?action=wordfence_downloadHtaccess&nonce=' + self.nonce + '" onclick="jQuery(\'#wf-htaccess-confirm\').prop(\'disabled\', false); return true;">Click here to download a backup copy of your .htaccess file now</a>' +
|
1178 |
+
'<br /><br />' +
|
1179 |
+
'<button class="button" type="button" id="wf-htaccess-confirm" disabled="disabled" onclick="WFAD.confirmDisableDirectoryListing(' + issueID + ');">Add code to .htaccess</button>');
|
1180 |
+
} else if (res.nginx) {
|
1181 |
+
self.colorbox('400px', "You are using Nginx as your web server. " +
|
1182 |
+
"You'll need to disable autoindexing in your nginx.conf. " +
|
1183 |
+
"See the <a target='_blank' href='http://nginx.org/en/docs/http/ngx_http_autoindex_module.html'>Nginx docs for more info</a> on how to do this.");
|
1184 |
+
} else if (res.err) {
|
1185 |
+
self.colorbox('400px', "We encountered a problem", "We can't modify your .htaccess file for you because: " + res.err);
|
1186 |
+
}
|
1187 |
+
});
|
1188 |
+
},
|
1189 |
+
confirmDisableDirectoryListing: function(issueID) {
|
1190 |
+
var self = this;
|
1191 |
+
this.colorboxClose();
|
1192 |
+
this.ajax('wordfence_disableDirectoryListing', {
|
1193 |
+
issueID: issueID
|
1194 |
+
}, function(res) {
|
1195 |
+
if (res.ok) {
|
1196 |
+
self.loadIssues(function() {
|
1197 |
+
self.colorbox("400px", "Directory Listing Disabled", "Directory listing has been disabled on your server.");
|
1198 |
+
});
|
1199 |
+
} else {
|
1200 |
+
//self.loadIssues(function() {
|
1201 |
+
// self.colorbox('400px', 'An error occurred', res.errorMsg);
|
1202 |
+
//});
|
1203 |
+
}
|
1204 |
+
});
|
1205 |
+
},
|
1206 |
+
|
1207 |
deleteIssue: function(id) {
|
1208 |
var self = this;
|
1209 |
this.ajax('wordfence_deleteIssue', {id: id}, function(res) {
|
1680 |
self.loadBlockRanges();
|
1681 |
});
|
1682 |
},
|
1683 |
+
blockIP: function(IP, reason, callback) {
|
1684 |
var self = this;
|
1685 |
this.ajax('wordfence_blockIP', {
|
1686 |
IP: IP,
|
1690 |
return;
|
1691 |
} else {
|
1692 |
self.reloadActivities();
|
1693 |
+
typeof callback === 'function' && callback();
|
1694 |
}
|
1695 |
});
|
1696 |
},
|
1716 |
self.staticTabChanged();
|
1717 |
});
|
1718 |
},
|
1719 |
+
unblockIP: function(IP, callback) {
|
1720 |
var self = this;
|
1721 |
this.ajax('wordfence_unblockIP', {
|
1722 |
IP: IP
|
1723 |
}, function(res) {
|
1724 |
self.reloadActivities();
|
1725 |
+
typeof callback === 'function' && callback();
|
1726 |
});
|
1727 |
},
|
1728 |
unblockNetwork: function(id) {
|
1853 |
}
|
1854 |
});
|
1855 |
},
|
1856 |
+
saveDebuggingConfig: function() {
|
1857 |
+
var qstr = jQuery('#wfDebuggingConfigForm').serialize();
|
1858 |
+
var self = this;
|
1859 |
+
jQuery('.wfSavedMsg').hide();
|
1860 |
+
jQuery('.wfAjax24').show();
|
1861 |
+
this.ajax('wordfence_saveDebuggingConfig', qstr, function(res) {
|
1862 |
+
jQuery('.wfAjax24').hide();
|
1863 |
+
if (res.ok) {
|
1864 |
+
self.pulse('.wfSavedMsg');
|
1865 |
+
} else if (res.errorMsg) {
|
1866 |
+
return;
|
1867 |
+
} else {
|
1868 |
+
self.colorbox('400px', 'An error occurred', 'We encountered an error trying to save your changes.');
|
1869 |
+
}
|
1870 |
+
});
|
1871 |
+
},
|
1872 |
changeSecurityLevel: function() {
|
1873 |
var level = jQuery('#securityLevel').val();
|
1874 |
for (var k in WFSLevels[level].checkboxes) {
|
1891 |
return;
|
1892 |
}
|
1893 |
this.colorbox('450px', "Please confirm", body +
|
1894 |
+
'<br /><br /><center><input type="button" name="but1" value="Cancel" onclick="jQuery.colorbox.close();" /> ' +
|
1895 |
+
'<input type="button" name="but2" value="Yes I\'m sure" onclick="jQuery.colorbox.close(); WFAD.confirmClearAllBlocked(\'' + op + '\');"><br />');
|
1896 |
},
|
1897 |
confirmClearAllBlocked: function(op) {
|
1898 |
var self = this;
|
1958 |
this.countryCodesToSave = codesArr.join(',');
|
1959 |
if (ownCountryBlocked) {
|
1960 |
this.colorbox('400px', "Please confirm blocking yourself", "You are about to block your own country. This could lead to you being locked out. Please make sure that your user profile on this machine has a current and valid email address and make sure you know what it is. That way if you are locked out, you can send yourself an unlock email. If you're sure you want to block your own country, click 'Confirm' below, otherwise click 'Cancel'.<br />" +
|
1961 |
+
'<input type="button" name="but1" value="Confirm" onclick="jQuery.colorbox.close(); WFAD.confirmSaveCountryBlocking();" /> <input type="button" name="but1" value="Cancel" onclick="jQuery.colorbox.close();" />');
|
1962 |
} else {
|
1963 |
this.confirmSaveCountryBlocking();
|
1964 |
}
|
2131 |
if (res.users && res.users.length > 0) {
|
2132 |
for (var i = 0; i < res.users.length; i++) {
|
2133 |
jQuery('<div id="twoFacCont_' + res.users[i].userID + '">' +
|
2134 |
+
jQuery('#wfTwoFacUserTmpl').tmpl(res.users[i]).html() + '</div>').appendTo(jQuery('#wfTwoFacUsers'));
|
2135 |
}
|
2136 |
}
|
2137 |
});
|
2306 |
}
|
2307 |
});
|
2308 |
},
|
2309 |
+
|
2310 |
+
deleteAdminUser: function(issueID) {
|
2311 |
+
var self = this;
|
2312 |
+
this.ajax('wordfence_deleteAdminUser', {
|
2313 |
+
issueID: issueID
|
2314 |
+
}, function(res) {
|
2315 |
+
if (res.ok) {
|
2316 |
+
self.loadIssues(function() {
|
2317 |
+
self.colorbox('400px', "Successfully deleted admin", "The admin user " +
|
2318 |
+
self.htmlEscape(res.user_login) + " was successfully deleted.");
|
2319 |
+
});
|
2320 |
+
} else if (res.errorMsg) {
|
2321 |
+
self.loadIssues(function() {
|
2322 |
+
self.colorbox('400px', 'An error occurred', res.errorMsg);
|
2323 |
+
});
|
2324 |
+
}
|
2325 |
+
});
|
2326 |
+
},
|
2327 |
+
|
2328 |
+
revokeAdminUser: function(issueID) {
|
2329 |
+
var self = this;
|
2330 |
+
this.ajax('wordfence_revokeAdminUser', {
|
2331 |
+
issueID: issueID
|
2332 |
+
}, function(res) {
|
2333 |
+
if (res.ok) {
|
2334 |
+
self.loadIssues(function() {
|
2335 |
+
self.colorbox('400px', "Successfully revoked admin", "All capabilties of admin user " +
|
2336 |
+
self.htmlEscape(res.user_login) + " were successfully revoked.");
|
2337 |
+
});
|
2338 |
+
} else if (res.errorMsg) {
|
2339 |
+
self.loadIssues(function() {
|
2340 |
+
self.colorbox('400px', 'An error occurred', res.errorMsg);
|
2341 |
+
});
|
2342 |
+
}
|
2343 |
+
});
|
2344 |
+
},
|
2345 |
+
|
2346 |
windowHasFocus: function() {
|
2347 |
if (typeof document.hasFocus === 'function') {
|
2348 |
return document.hasFocus();
|
2388 |
if (!timestamp) {
|
2389 |
timestamp = el.attr('data-timestamp');
|
2390 |
}
|
2391 |
+
var serverTime = self.serverMicrotime;
|
2392 |
var format = el.data('wfformat');
|
2393 |
if (!format) {
|
2394 |
format = el.attr('data-format');
|
2395 |
}
|
2396 |
el.html(self.showTimestamp(timestamp, serverTime, format));
|
2397 |
});
|
2398 |
+
},
|
2399 |
+
|
2400 |
+
wafData: {
|
2401 |
+
whitelistedURLParams: []
|
2402 |
+
},
|
2403 |
+
|
2404 |
+
wafConfigSave: function(action, data, onSuccess) {
|
2405 |
+
var self = this;
|
2406 |
+
if (typeof(data) == 'string') {
|
2407 |
+
if (data.length > 0) {
|
2408 |
+
data += '&';
|
2409 |
+
}
|
2410 |
+
data += 'wafConfigAction=' + action;
|
2411 |
+
} else if (typeof(data) == 'object' && data instanceof Array) {
|
2412 |
+
// jQuery serialized form data
|
2413 |
+
data.push({
|
2414 |
+
name: 'wafConfigAction',
|
2415 |
+
value: action
|
2416 |
+
});
|
2417 |
+
} else if (typeof(data) == 'object') {
|
2418 |
+
data['wafConfigAction'] = action;
|
2419 |
+
}
|
2420 |
+
|
2421 |
+
this.ajax('wordfence_saveWAFConfig', data, function(res) {
|
2422 |
+
if (typeof res === 'object' && res.success) {
|
2423 |
+
self.colorbox('400px', 'Firewall Configuration', 'The Wordfence Web Application Firewall ' +
|
2424 |
+
'configuration was saved successfully.');
|
2425 |
+
self.wafData = res.data;
|
2426 |
+
self.wafConfigPageRender();
|
2427 |
+
if (typeof onSuccess === 'function') {
|
2428 |
+
return onSuccess.apply(this, arguments);
|
2429 |
+
}
|
2430 |
+
} else {
|
2431 |
+
self.colorbox('400px', 'Error saving Firewall configuration', 'There was an error saving the ' +
|
2432 |
+
'Web Application Firewall configuration settings.');
|
2433 |
+
}
|
2434 |
+
});
|
2435 |
+
},
|
2436 |
+
|
2437 |
+
wafWhitelistURLAdd: function(url, param, onSuccess) {
|
2438 |
+
this.wafData.whitelistedURLParams.push({
|
2439 |
+
'path': url,
|
2440 |
+
'paramKey': param,
|
2441 |
+
'ruleID': ['all']
|
2442 |
+
});
|
2443 |
+
var index = this.wafData.whitelistedURLParams.length;
|
2444 |
+
var inputPath = $('<input name="whitelistedURLParams[' + index + '][path]" type="hidden" />');
|
2445 |
+
var inputParam = $('<input name="whitelistedURLParams[' + index + '][paramKey]" type="hidden" />');
|
2446 |
+
var inputEnabled = $('<input name="whitelistedURLParams[' + index + '][enabled]" type="hidden" value="1" />');
|
2447 |
+
inputPath.val(url);
|
2448 |
+
inputParam.val(param);
|
2449 |
+
$('#waf-config-form').append(inputPath)
|
2450 |
+
.append(inputParam)
|
2451 |
+
.append(inputEnabled);
|
2452 |
+
this.wafConfigSave(onSuccess);
|
2453 |
+
inputPath.remove();
|
2454 |
+
inputParam.remove();
|
2455 |
+
inputEnabled.remove();
|
2456 |
+
},
|
2457 |
+
|
2458 |
+
wafConfigPageRender: function() {
|
2459 |
+
var whitelistedIPsEl = $('#waf-whitelisted-urls-tmpl').tmpl(this.wafData);
|
2460 |
+
$('#waf-whitelisted-urls-wrapper').html(whitelistedIPsEl);
|
2461 |
+
|
2462 |
+
var rulesEl = $('#waf-rules-tmpl').tmpl(this.wafData);
|
2463 |
+
$('#waf-rules-wrapper').html(rulesEl);
|
2464 |
+
|
2465 |
+
if (this.wafData['rulesLastUpdated']) {
|
2466 |
+
var date = new Date(this.wafData['rulesLastUpdated'] * 1000);
|
2467 |
+
this.renderWAFRulesLastUpdated(date);
|
2468 |
+
}
|
2469 |
+
},
|
2470 |
+
|
2471 |
+
renderWAFRulesLastUpdated: function(date) {
|
2472 |
+
var dateString = date.toString();
|
2473 |
+
if (date.toLocaleString) {
|
2474 |
+
dateString = date.toLocaleString();
|
2475 |
+
}
|
2476 |
+
$('#waf-rules-last-updated').text('Last Updated: ' + dateString)
|
2477 |
+
.css({
|
2478 |
+
'opacity': 0
|
2479 |
+
})
|
2480 |
+
.animate({
|
2481 |
+
'opacity': 1
|
2482 |
+
}, 500);
|
2483 |
+
},
|
2484 |
+
|
2485 |
+
wafUpdateRules: function(onSuccess) {
|
2486 |
+
var self = this;
|
2487 |
+
this.ajax('wordfence_updateWAFRules', {}, function(res) {
|
2488 |
+
self.wafData = res;
|
2489 |
+
self.wafConfigPageRender();
|
2490 |
+
if (!self.wafData['isPaid']) {
|
2491 |
+
self.colorbox('400px', 'Rules Updated', 'Your rules have been updated successfully. You are ' +
|
2492 |
+
'currently using the the free version of Wordfence. ' +
|
2493 |
+
'Upgrade to Wordfence premium to have your rules updated automatically as new threats emerge. ' +
|
2494 |
+
'<a href="https://www.wordfence.com/wafUpdateRules1/wordfence-signup/">Click here to purchase a premium API key</a>. ' +
|
2495 |
+
'<em>Note: Your rules will still update every 30 days as a free user.</em>');
|
2496 |
+
} else {
|
2497 |
+
self.colorbox('400px', 'Rules Updated', 'Your rules have been updated successfully.');
|
2498 |
+
}
|
2499 |
+
if (typeof onSuccess === 'function') {
|
2500 |
+
return onSuccess.apply(this, arguments);
|
2501 |
+
}
|
2502 |
+
});
|
2503 |
+
},
|
2504 |
+
|
2505 |
+
dateFormat: function(date) {
|
2506 |
+
if (date instanceof Date) {
|
2507 |
+
if (date.toLocaleString) {
|
2508 |
+
return date.toLocaleString();
|
2509 |
+
}
|
2510 |
+
return date.toString();
|
2511 |
+
}
|
2512 |
+
return date;
|
2513 |
+
},
|
2514 |
+
|
2515 |
+
wafAddBootstrap: function() {
|
2516 |
+
var self = this;
|
2517 |
+
this.ajax('wordfence_wafAddBootstrap', {}, function(res) {
|
2518 |
+
self.colorbox('400px', 'File Created', "");
|
2519 |
+
});
|
2520 |
+
},
|
2521 |
+
|
2522 |
+
wafConfigureAutoPrepend: function() {
|
2523 |
+
var self = this;
|
2524 |
+
self.colorbox("400px", 'Backup .htaccess before continuing', 'We are about to change your <em>.htaccess</em> file. Please make a backup of this file proceeding'
|
2525 |
+
+ '<br/>'
|
2526 |
+
+ '<a href="' + WordfenceAdminVars.ajaxURL + '?action=wordfence_downloadHtaccess&nonce=' + self.nonce + '" onclick="jQuery(\'#wf-htaccess-confirm\').prop(\'disabled\', false); return true;">Click here to download a backup copy of your .htaccess file now</a>' +
|
2527 |
+
'<br /><br />' +
|
2528 |
+
'<button class="button" type="button" id="wf-htaccess-confirm" disabled="disabled" onclick="WFAD.confirmWAFConfigureAutoPrepend();">Add code to .htaccess</button>');
|
2529 |
+
},
|
2530 |
+
|
2531 |
+
confirmWAFConfigureAutoPrepend: function() {
|
2532 |
+
var self = this;
|
2533 |
+
this.ajax('wordfence_wafConfigureAutoPrepend', {}, function(res) {
|
2534 |
+
self.colorbox('400px', '.htaccess Updated', "Your .htaccess has been updated successfully. Please " +
|
2535 |
+
"verify your site is functioning normally.");
|
2536 |
+
});
|
2537 |
+
},
|
2538 |
+
|
2539 |
+
base64_decode: function(s) {
|
2540 |
+
var e = {}, i, b = 0, c, x, l = 0, a, r = '', w = String.fromCharCode, L = s.length;
|
2541 |
+
var A = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
2542 |
+
for (i = 0; i < 64; i++) {
|
2543 |
+
e[A.charAt(i)] = i;
|
2544 |
+
}
|
2545 |
+
for (x = 0; x < L; x++) {
|
2546 |
+
c = e[s.charAt(x)];
|
2547 |
+
b = (b << 6) + c;
|
2548 |
+
l += 6;
|
2549 |
+
while (l >= 8) {
|
2550 |
+
((a = (b >>> (l -= 8)) & 0xff) || (x < (L - 2))) && (r += w(a));
|
2551 |
+
}
|
2552 |
+
}
|
2553 |
+
return r;
|
2554 |
}
|
2555 |
};
|
|
|
2556 |
|
2557 |
+
window['WFAD'] = window['wordfenceAdmin'];
|
2558 |
setInterval(function() {
|
2559 |
WFAD.updateTimeAgo();
|
2560 |
}, 1000);
|
js/admin.liveTraffic.js
ADDED
@@ -0,0 +1,680 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function($) {
|
2 |
+
|
3 |
+
var LISTING_LIMIT = 50;
|
4 |
+
|
5 |
+
var LiveTrafficViewModel = function(listings, filters) {
|
6 |
+
var self = this;
|
7 |
+
var listingIDTable = {};
|
8 |
+
self.listings = ko.observableArray(listings);
|
9 |
+
self.listings.subscribe(function(items) {
|
10 |
+
listingIDTable = {};
|
11 |
+
for (var i = 0; i < items.length; i++) {
|
12 |
+
listingIDTable[items[i].id()] = 1;
|
13 |
+
}
|
14 |
+
//console.log(items);
|
15 |
+
});
|
16 |
+
self.hasListing = function(id) {
|
17 |
+
return id in listingIDTable;
|
18 |
+
};
|
19 |
+
self.filters = ko.observableArray(filters);
|
20 |
+
|
21 |
+
var urlGroupBy = new GroupByModel('url', 'URL');
|
22 |
+
var groupBys = [
|
23 |
+
new GroupByModel('type', 'Type'),
|
24 |
+
new GroupByModel('user_login', 'Username'),
|
25 |
+
new GroupByModel('statusCode', 'HTTP Response Code'),
|
26 |
+
new GroupByModel('action', 'Firewall Response', 'enum', ['ok', 'throttled', 'lockedOut', 'blocked', 'blocked:waf']),
|
27 |
+
new GroupByModel('ip', 'IP'),
|
28 |
+
urlGroupBy,
|
29 |
+
new GroupByModel('host', 'Host')
|
30 |
+
];
|
31 |
+
|
32 |
+
self.presetFiltersOptions = ko.observableArray([
|
33 |
+
new PresetFilterModel('All Hits', "all", []),
|
34 |
+
new PresetFilterModel('Humans', "humans", [new ListingsFilterModel(self, 'type', 'human')]),
|
35 |
+
new PresetFilterModel('Registered Users', "users", [new ListingsFilterModel(self, 'userID', '0', '!=')]),
|
36 |
+
new PresetFilterModel('Crawlers', "crawlers", [new ListingsFilterModel(self, 'type', 'bot')]),
|
37 |
+
new PresetFilterModel('Google Crawlers', "google", [new ListingsFilterModel(self, 'isGoogle', '1')]),
|
38 |
+
new PresetFilterModel('Pages Not Found', "404s", [new ListingsFilterModel(self, 'statusCode', '404')]),
|
39 |
+
new PresetFilterModel('Logins and Logouts', "logins", [
|
40 |
+
new ListingsFilterModel(self, 'action', 'login', 'contains'),
|
41 |
+
new ListingsFilterModel(self, 'action', 'logout', 'contains')
|
42 |
+
]),
|
43 |
+
//new PresetFilterModel('Top Consumers', "top_consumers", [new ListingsFilterModel(self, 'statusCode', '200')], urlGroupBy),
|
44 |
+
//new PresetFilterModel('Top 404s', "top_404s", [new ListingsFilterModel(self, 'statusCode', '404')], urlGroupBy),
|
45 |
+
new PresetFilterModel('Locked Out', "lockedOut", [new ListingsFilterModel(self, 'action', 'lockedOut')]),
|
46 |
+
new PresetFilterModel('Blocked', "blocked", [new ListingsFilterModel(self, 'action', 'blocked', 'contains')]),
|
47 |
+
new PresetFilterModel('Blocked By Firewall', "blocked:waf", [new ListingsFilterModel(self, 'action', 'blocked:waf')])
|
48 |
+
]);
|
49 |
+
|
50 |
+
self.showAdvancedFilters = ko.observable(false);
|
51 |
+
self.showAdvancedFilters.subscribe(function(val) {
|
52 |
+
if (val && self.filters().length == 0) {
|
53 |
+
self.addFilter();
|
54 |
+
}
|
55 |
+
});
|
56 |
+
|
57 |
+
self.presetFiltersOptionsText = function(item) {
|
58 |
+
return item.text();
|
59 |
+
};
|
60 |
+
|
61 |
+
self.selectedPresetFilter = ko.observable();
|
62 |
+
self.selectedPresetFilter.subscribe(function(item) {
|
63 |
+
var clonedFilters = ko.toJS(item.filters());
|
64 |
+
var newFilters = [];
|
65 |
+
for (var i = 0; i < clonedFilters.length; i++) {
|
66 |
+
newFilters.push(new ListingsFilterModel(self, clonedFilters[i].param, clonedFilters[i].value, clonedFilters[i].operator));
|
67 |
+
}
|
68 |
+
self.filters(newFilters);
|
69 |
+
self.groupBy(item.groupBy());
|
70 |
+
});
|
71 |
+
|
72 |
+
self.filters.subscribe(function() {
|
73 |
+
self.checkQueryAndReloadListings();
|
74 |
+
});
|
75 |
+
|
76 |
+
self.addFilter = function() {
|
77 |
+
self.filters.push(new ListingsFilterModel(self));
|
78 |
+
};
|
79 |
+
|
80 |
+
self.removeFilter = function(item) {
|
81 |
+
self.filters.remove(item);
|
82 |
+
};
|
83 |
+
|
84 |
+
var currentFilterQuery = '';
|
85 |
+
var getURLEncodedFilters = function() {
|
86 |
+
var dataString = '';
|
87 |
+
ko.utils.arrayForEach(self.filters(), function(filter) {
|
88 |
+
if (filter.getValue() !== false) {
|
89 |
+
dataString += filter.urlEncoded() + '&';
|
90 |
+
}
|
91 |
+
});
|
92 |
+
var groupBy = self.groupBy();
|
93 |
+
if (groupBy) {
|
94 |
+
dataString += 'groupby=' + encodeURIComponent(groupBy.param()) + '&';
|
95 |
+
}
|
96 |
+
var startDate = self.startDate();
|
97 |
+
if (startDate) {
|
98 |
+
dataString += 'startDate=' + encodeURIComponent(startDate) + '&';
|
99 |
+
}
|
100 |
+
var endDate = self.endDate();
|
101 |
+
if (endDate) {
|
102 |
+
dataString += 'endDate=' + encodeURIComponent(endDate) + '&';
|
103 |
+
}
|
104 |
+
if (dataString.length > 1) {
|
105 |
+
return dataString.substring(0, dataString.length - 1);
|
106 |
+
}
|
107 |
+
return '';
|
108 |
+
};
|
109 |
+
|
110 |
+
self.filterGroupByOptions = ko.observableArray(groupBys);
|
111 |
+
|
112 |
+
self.filterGroupByOptionsText = function(item) {
|
113 |
+
return item.text() || item.param();
|
114 |
+
};
|
115 |
+
|
116 |
+
self.groupBy = ko.observable();
|
117 |
+
self.groupBy.subscribe(function() {
|
118 |
+
self.checkQueryAndReloadListings();
|
119 |
+
});
|
120 |
+
|
121 |
+
self.startDate = ko.observable();
|
122 |
+
self.startDate.subscribe(function() {
|
123 |
+
// console.log('start date change ' + self.startDate());
|
124 |
+
self.checkQueryAndReloadListings();
|
125 |
+
});
|
126 |
+
|
127 |
+
self.endDate = ko.observable();
|
128 |
+
self.endDate.subscribe(function() {
|
129 |
+
// console.log('end date change ' + self.endDate());
|
130 |
+
self.checkQueryAndReloadListings();
|
131 |
+
});
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Pulls down fresh traffic data and resets the list.
|
135 |
+
*
|
136 |
+
* @param options
|
137 |
+
*/
|
138 |
+
self.checkQueryAndReloadListings = function(options) {
|
139 |
+
if (currentFilterQuery !== getURLEncodedFilters()) {
|
140 |
+
self.reloadListings(options);
|
141 |
+
}
|
142 |
+
};
|
143 |
+
self.reloadListings = function(options) {
|
144 |
+
pullDownListings(options, function(listings) {
|
145 |
+
var newListings = [];
|
146 |
+
for (var i = 0; i < listings.length; i++) {
|
147 |
+
newListings.push(new ListingModel(listings[i]));
|
148 |
+
}
|
149 |
+
self.listings(newListings);
|
150 |
+
})
|
151 |
+
};
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Used in the infinite scroll
|
155 |
+
*/
|
156 |
+
self.loadNextListings = function(callback) {
|
157 |
+
var lastTimestamp = self.filters[0];
|
158 |
+
pullDownListings({
|
159 |
+
since: lastTimestamp,
|
160 |
+
limit: LISTING_LIMIT,
|
161 |
+
offset: self.listings().length
|
162 |
+
}, function() {
|
163 |
+
self.appendListings.apply(this, arguments);
|
164 |
+
typeof callback === 'function' && callback.apply(this, arguments);
|
165 |
+
});
|
166 |
+
};
|
167 |
+
|
168 |
+
self.getCurrentQueryString = function(options) {
|
169 |
+
var queryOptions = {
|
170 |
+
since: null,
|
171 |
+
limit: LISTING_LIMIT,
|
172 |
+
offset: 0
|
173 |
+
};
|
174 |
+
for (var prop in queryOptions) {
|
175 |
+
if (queryOptions.hasOwnProperty(prop) && options && prop in options) {
|
176 |
+
queryOptions[prop] = options[prop];
|
177 |
+
}
|
178 |
+
}
|
179 |
+
currentFilterQuery = getURLEncodedFilters();
|
180 |
+
var data = currentFilterQuery;
|
181 |
+
for (prop in queryOptions) {
|
182 |
+
if (queryOptions.hasOwnProperty(prop)) {
|
183 |
+
var val = queryOptions[prop];
|
184 |
+
if (val === null || val === undefined) {
|
185 |
+
val = '';
|
186 |
+
}
|
187 |
+
data += '&' + encodeURIComponent(prop) + '=' + encodeURIComponent(val);
|
188 |
+
}
|
189 |
+
}
|
190 |
+
return data;
|
191 |
+
};
|
192 |
+
|
193 |
+
var pullDownListings = function(options, callback) {
|
194 |
+
var data = self.getCurrentQueryString(options);
|
195 |
+
|
196 |
+
WFAD.ajax('wordfence_loadLiveTraffic', data, function(response) {
|
197 |
+
if (!response || !response.success) {
|
198 |
+
return;
|
199 |
+
}
|
200 |
+
callback && callback(response.data, response);
|
201 |
+
self.sql(response.sql);
|
202 |
+
});
|
203 |
+
};
|
204 |
+
|
205 |
+
self.prependListings = function(listings, response) {
|
206 |
+
for (var i = listings.length - 1; i >= 0; i--) {
|
207 |
+
// Prevent duplicates
|
208 |
+
if (self.hasListing(listings[i].id)) {
|
209 |
+
continue;
|
210 |
+
}
|
211 |
+
var listing = new ListingModel(listings[i]);
|
212 |
+
listing.highlighted(true);
|
213 |
+
self.listings.unshift(listing);
|
214 |
+
}
|
215 |
+
|
216 |
+
//self.listings.sort(function(a, b) {
|
217 |
+
// if (a.ctime() < b.ctime()) {
|
218 |
+
// return 1;
|
219 |
+
// } else if (a.ctime() > b.ctime()) {
|
220 |
+
// return -1;
|
221 |
+
// }
|
222 |
+
// return 0;
|
223 |
+
//});
|
224 |
+
};
|
225 |
+
|
226 |
+
self.appendListings = function(listings, response) {
|
227 |
+
var highlight = 3;
|
228 |
+
for (var i = 0; i < listings.length; i++) {
|
229 |
+
// Prevent duplicates
|
230 |
+
if (self.hasListing(listings[i].id)) {
|
231 |
+
continue;
|
232 |
+
}
|
233 |
+
var listing = new ListingModel(listings[i]);
|
234 |
+
listing.highlighted(highlight-- > 0);
|
235 |
+
self.listings.push(listing);
|
236 |
+
}
|
237 |
+
|
238 |
+
//self.listings.sort(function(a, b) {
|
239 |
+
// if (a.ctime() < b.ctime()) {
|
240 |
+
// return 1;
|
241 |
+
// } else if (a.ctime() > b.ctime()) {
|
242 |
+
// return -1;
|
243 |
+
// }
|
244 |
+
// return 0;
|
245 |
+
//});
|
246 |
+
};
|
247 |
+
|
248 |
+
self.whitelistWAFParamKey = function(path, paramKey, failedRules) {
|
249 |
+
WFAD.ajax('wordfence_whitelistWAFParamKey', {
|
250 |
+
path: path,
|
251 |
+
paramKey: paramKey,
|
252 |
+
failedRules: failedRules
|
253 |
+
}, function(response) {
|
254 |
+
|
255 |
+
});
|
256 |
+
};
|
257 |
+
|
258 |
+
/*
|
259 |
+
Blocking functions
|
260 |
+
*/
|
261 |
+
self.unblockIP = function(item) {
|
262 |
+
WFAD.unblockIP(item.IP(), function() {
|
263 |
+
ko.utils.arrayForEach(self.listings(), function(listing) {
|
264 |
+
if (listing.IP() == item.IP()) {
|
265 |
+
listing.blocked(false);
|
266 |
+
}
|
267 |
+
});
|
268 |
+
});
|
269 |
+
};
|
270 |
+
self.unblockNetwork = function(item) {
|
271 |
+
WFAD.unblockNetwork(item.ipRangeID());
|
272 |
+
};
|
273 |
+
self.blockIP = function(item) {
|
274 |
+
WFAD.blockIP(item.IP(), 'Manual block by administrator', function() {
|
275 |
+
ko.utils.arrayForEach(self.listings(), function(listing) {
|
276 |
+
if (listing.IP() == item.IP()) {
|
277 |
+
listing.blocked(true);
|
278 |
+
}
|
279 |
+
});
|
280 |
+
});
|
281 |
+
};
|
282 |
+
|
283 |
+
// For debuggering-a-ding
|
284 |
+
self.sql = ko.observable('');
|
285 |
+
};
|
286 |
+
|
287 |
+
var ListingModel = function(data) {
|
288 |
+
var self = this;
|
289 |
+
|
290 |
+
self.id = ko.observable(0);
|
291 |
+
self.ctime = ko.observable(0);
|
292 |
+
self.IP = ko.observable('');
|
293 |
+
self.jsRun = ko.observable(0);
|
294 |
+
self.statusCode = ko.observable(200);
|
295 |
+
self.isGoogle = ko.observable(0);
|
296 |
+
self.userID = ko.observable(0);
|
297 |
+
self.newVisit = ko.observable(0);
|
298 |
+
self.URL = ko.observable('');
|
299 |
+
self.referer = ko.observable('');
|
300 |
+
self.UA = ko.observable('');
|
301 |
+
self.loc = ko.observable();
|
302 |
+
self.type = ko.observable('');
|
303 |
+
self.blocked = ko.observable(false);
|
304 |
+
self.rangeBlocked = ko.observable(false);
|
305 |
+
self.ipRangeID = ko.observable(-1);
|
306 |
+
self.extReferer = ko.observable();
|
307 |
+
self.browser = ko.observable();
|
308 |
+
self.user = ko.observable();
|
309 |
+
self.hitCount = ko.observable();
|
310 |
+
self.username = ko.observable('');
|
311 |
+
|
312 |
+
// New fields/columns
|
313 |
+
self.action = ko.observable('');
|
314 |
+
self.actionDescription = ko.observable(false);
|
315 |
+
self.actionData = ko.observable();
|
316 |
+
|
317 |
+
self.highlighted = ko.observable(false);
|
318 |
+
//self.highlighted.subscribe(function(val) {
|
319 |
+
// if (val) {
|
320 |
+
// _classes += ' highlighted';
|
321 |
+
// self.cssClasses(_classes);
|
322 |
+
// } else {
|
323 |
+
// _classes.replace(/ highlighted(\s*|$)/, ' ');
|
324 |
+
// self.cssClasses(_classes);
|
325 |
+
// }
|
326 |
+
//});
|
327 |
+
|
328 |
+
for (var prop in data) {
|
329 |
+
if (data.hasOwnProperty(prop)) {
|
330 |
+
self[prop] !== undefined && self[prop](data[prop]);
|
331 |
+
}
|
332 |
+
}
|
333 |
+
|
334 |
+
// Use the same format as these update.
|
335 |
+
self.timeAgo = ko.pureComputed(function() {
|
336 |
+
var serverTime = WFAD.serverMicrotime;
|
337 |
+
return $(WFAD.showTimestamp(this.ctime(), serverTime)).text();
|
338 |
+
}, self);
|
339 |
+
|
340 |
+
var formatBlockedParam = function(text, maxLength) {
|
341 |
+
maxLength = maxLength || 100;
|
342 |
+
if (text && text.length > maxLength) {
|
343 |
+
return text.substring(0, Math.round(maxLength)) + "\u2026";
|
344 |
+
// return text.substring(0, Math.round(maxLength / 2)) + " ... " + text.substring(text.length - Math.round(maxLength / 2));
|
345 |
+
}
|
346 |
+
return text;
|
347 |
+
};
|
348 |
+
|
349 |
+
self.displayURL = ko.pureComputed(function() {
|
350 |
+
return formatBlockedParam(self.URL(), 135);
|
351 |
+
});
|
352 |
+
|
353 |
+
self.firewallAction = ko.pureComputed(function() {
|
354 |
+
var desc = '';
|
355 |
+
switch (self.action()) {
|
356 |
+
case 'lockedOut':
|
357 |
+
return 'locked out from logging in';
|
358 |
+
|
359 |
+
case 'blocked:wordfence':
|
360 |
+
desc = self.actionDescription();
|
361 |
+
if (desc && desc.toLowerCase().indexOf('block') === 0) {
|
362 |
+
return 'b' + desc.substring(1);
|
363 |
+
}
|
364 |
+
return 'blocked for ' + desc;
|
365 |
+
|
366 |
+
case 'blocked:wfsn':
|
367 |
+
return 'blocked by the Wordfence Security Network';
|
368 |
+
|
369 |
+
case 'blocked:waf':
|
370 |
+
var data = self.actionData();
|
371 |
+
if (typeof data === 'object') {
|
372 |
+
var paramKey = WFAD.base64_decode(data.paramKey);
|
373 |
+
var paramValue = WFAD.base64_decode(data.paramValue);
|
374 |
+
// var category = data.category;
|
375 |
+
|
376 |
+
var matches = paramKey.match(/([a-z0-9_]+\.[a-z0-9_]+)(?:\[(.+?)\](.*))?/i);
|
377 |
+
desc = self.actionDescription();
|
378 |
+
if (matches) {
|
379 |
+
switch (matches[1]) {
|
380 |
+
case 'request.queryString':
|
381 |
+
desc = self.actionDescription() + ' in query string: ' + matches[2] + '=' + formatBlockedParam(encodeURIComponent(paramValue));
|
382 |
+
break;
|
383 |
+
case 'request.body':
|
384 |
+
desc = self.actionDescription() + ' in POST body: ' + matches[2] + '=' + formatBlockedParam(encodeURIComponent(paramValue));
|
385 |
+
break;
|
386 |
+
case 'request.cookie':
|
387 |
+
desc = self.actionDescription() + ' in cookie: ' + matches[2] + '=' + formatBlockedParam(encodeURIComponent(paramValue));
|
388 |
+
break;
|
389 |
+
case 'request.fileNames':
|
390 |
+
desc = 'a ' + self.actionDescription() + ' in file: ' + matches[2] + '=' + formatBlockedParam(encodeURIComponent(paramValue));
|
391 |
+
break;
|
392 |
+
}
|
393 |
+
}
|
394 |
+
if (desc) {
|
395 |
+
return 'blocked by firewall for ' + desc;
|
396 |
+
}
|
397 |
+
return 'blocked by firewall';
|
398 |
+
}
|
399 |
+
return 'blocked by firewall for ' + self.actionDescription();
|
400 |
+
}
|
401 |
+
return desc;
|
402 |
+
});
|
403 |
+
|
404 |
+
self.cssClasses = ko.pureComputed(function() {
|
405 |
+
var classes = 'wfActEvent';
|
406 |
+
if (self.statusCode() == 403) {
|
407 |
+
classes += ' wfActionBlocked';
|
408 |
+
}
|
409 |
+
if (self.statusCode() == 404) {
|
410 |
+
classes += ' wf404';
|
411 |
+
}
|
412 |
+
if (self.jsRun() == 1) {
|
413 |
+
classes += ' wfHuman';
|
414 |
+
}
|
415 |
+
if (self.actionData() && self.actionData().learningMode) {
|
416 |
+
classes += ' wfWAFLearningMode';
|
417 |
+
}
|
418 |
+
if (self.highlighted()) {
|
419 |
+
classes += ' highlighted';
|
420 |
+
}
|
421 |
+
return classes;
|
422 |
+
});
|
423 |
+
};
|
424 |
+
|
425 |
+
var ListingsFilterModel = function(viewModel, param, value, operator) {
|
426 |
+
var self = this;
|
427 |
+
self.viewModel = viewModel;
|
428 |
+
self.param = ko.observable('');
|
429 |
+
self.value = ko.observable('');
|
430 |
+
self.operator = ko.observable('');
|
431 |
+
|
432 |
+
self.param(param);
|
433 |
+
self.value(value);
|
434 |
+
self.operator(operator || '=');
|
435 |
+
|
436 |
+
var filterChanged = function() {
|
437 |
+
self.viewModel && self.viewModel.checkQueryAndReloadListings && self.viewModel.checkQueryAndReloadListings();
|
438 |
+
};
|
439 |
+
self.param.subscribe(filterChanged);
|
440 |
+
self.operator.subscribe(filterChanged);
|
441 |
+
self.value.subscribe(function(value) {
|
442 |
+
if (value instanceof FilterParamEnumOptionModel && value.operator()) {
|
443 |
+
self.selectedFilterOperatorOptionValue(value.operator());
|
444 |
+
}
|
445 |
+
filterChanged();
|
446 |
+
});
|
447 |
+
|
448 |
+
var equalsOperator = new FilterOperatorModel('=');
|
449 |
+
var notEqualsOperator = new FilterOperatorModel('!=', '\u2260');
|
450 |
+
var containsOperator = new FilterOperatorModel('contains');
|
451 |
+
var matchOperator = new FilterOperatorModel('match');
|
452 |
+
self.filterOperatorOptions = ko.observableArray([
|
453 |
+
equalsOperator,
|
454 |
+
notEqualsOperator,
|
455 |
+
containsOperator,
|
456 |
+
matchOperator
|
457 |
+
]);
|
458 |
+
|
459 |
+
self.filterParamOptions = ko.observableArray([
|
460 |
+
new FilterParamModel('type', 'Type', 'enum', [
|
461 |
+
new FilterParamEnumOptionModel('human', 'Human'),
|
462 |
+
new FilterParamEnumOptionModel('bot', 'Bot')
|
463 |
+
]),
|
464 |
+
new FilterParamModel('user_login', 'Username'),
|
465 |
+
new FilterParamModel('userID', 'UserID'),
|
466 |
+
new FilterParamModel('isGoogle', 'Google Bot', 'bool'),
|
467 |
+
new FilterParamModel('ip', 'IP'),
|
468 |
+
new FilterParamModel('ua', 'User Agent'),
|
469 |
+
new FilterParamModel('referer', 'Referer'),
|
470 |
+
new FilterParamModel('url', 'URL'),
|
471 |
+
new FilterParamModel('statusCode', 'HTTP Response Code'),
|
472 |
+
new FilterParamModel('action', 'Firewall Response', 'enum', [
|
473 |
+
new FilterParamEnumOptionModel('', 'OK'),
|
474 |
+
new FilterParamEnumOptionModel('throttled', 'Throttled'),
|
475 |
+
new FilterParamEnumOptionModel('lockedOut', 'Locked Out'),
|
476 |
+
new FilterParamEnumOptionModel('blocked', 'Blocked', containsOperator),
|
477 |
+
new FilterParamEnumOptionModel('blocked:waf', 'Blocked WAF')
|
478 |
+
]),
|
479 |
+
new FilterParamModel('action', 'Logins', 'enum', [
|
480 |
+
new FilterParamEnumOptionModel('loginOK', 'Logged In'),
|
481 |
+
new FilterParamEnumOptionModel('loginFail', 'Failed Login'),
|
482 |
+
new FilterParamEnumOptionModel('loginFailInvalidUsername', 'Failed Login: Invalid Username'),
|
483 |
+
new FilterParamEnumOptionModel('loginFailValidUsername', 'Failed Login: Valid Username')
|
484 |
+
]),
|
485 |
+
new FilterParamModel('action', 'Security Event')
|
486 |
+
]);
|
487 |
+
|
488 |
+
self.filterParamOptionsText = function(item) {
|
489 |
+
return item.text() || item.param();
|
490 |
+
};
|
491 |
+
|
492 |
+
self.selectedFilterParamOptionValue = ko.observable();
|
493 |
+
self.selectedFilterParamOptionValue.subscribe(function(item) {
|
494 |
+
self.param(item && item.param ? item.param() : '');
|
495 |
+
});
|
496 |
+
|
497 |
+
ko.utils.arrayForEach(self.filterParamOptions(), function(item) {
|
498 |
+
if (self.param() == item.param()) {
|
499 |
+
switch (item.type()) {
|
500 |
+
case 'enum':
|
501 |
+
// console.log(self.param(), item.param(), self.value(), values);
|
502 |
+
switch (self.operator()) {
|
503 |
+
case '=':
|
504 |
+
ko.utils.arrayForEach(item.values(), function(enumOption) {
|
505 |
+
if (enumOption.value() == self.value()) {
|
506 |
+
self.selectedFilterParamOptionValue(item);
|
507 |
+
}
|
508 |
+
});
|
509 |
+
break;
|
510 |
+
}
|
511 |
+
break;
|
512 |
+
|
513 |
+
default:
|
514 |
+
self.selectedFilterParamOptionValue(item);
|
515 |
+
break;
|
516 |
+
}
|
517 |
+
}
|
518 |
+
});
|
519 |
+
|
520 |
+
self.filterOperatorOptionsText = function(item) {
|
521 |
+
return item.text() || item.operator();
|
522 |
+
};
|
523 |
+
|
524 |
+
self.selectedFilterOperatorOptionValue = ko.observable();
|
525 |
+
self.selectedFilterOperatorOptionValue.subscribe(function(item) {
|
526 |
+
self.operator(item.operator());
|
527 |
+
});
|
528 |
+
|
529 |
+
ko.utils.arrayForEach(self.filterOperatorOptions(), function(item) {
|
530 |
+
if (self.operator() == item.operator()) {
|
531 |
+
self.selectedFilterOperatorOptionValue(item);
|
532 |
+
}
|
533 |
+
});
|
534 |
+
|
535 |
+
self.getValue = function() {
|
536 |
+
var value = self.value() instanceof FilterParamEnumOptionModel ? self.value().value() : self.value();
|
537 |
+
return (typeof value === 'string' || typeof value === 'number') ? value : false;
|
538 |
+
};
|
539 |
+
self.urlEncoded = function() {
|
540 |
+
var value = self.getValue();
|
541 |
+
return 'param[]=' + encodeURIComponent(self.param()) + '&value[]=' + encodeURIComponent(value) +
|
542 |
+
'&operator[]=' + encodeURIComponent(self.operator());
|
543 |
+
};
|
544 |
+
};
|
545 |
+
|
546 |
+
var PresetFilterModel = function(text, value, filters, groupBy) {
|
547 |
+
this.text = ko.observable('');
|
548 |
+
this.value = ko.observable('');
|
549 |
+
this.filters = ko.observableArray(filters);
|
550 |
+
this.groupBy = ko.observable(groupBy);
|
551 |
+
|
552 |
+
this.text(text);
|
553 |
+
this.value(value);
|
554 |
+
};
|
555 |
+
|
556 |
+
var FilterParamModel = function(param, text, type, values) {
|
557 |
+
this.text = ko.observable('');
|
558 |
+
this.param = ko.observable('');
|
559 |
+
this.type = ko.observable('');
|
560 |
+
this.values = ko.observableArray(values);
|
561 |
+
|
562 |
+
this.text(text);
|
563 |
+
this.param(param);
|
564 |
+
this.type(type || 'text');
|
565 |
+
|
566 |
+
this.optionsText = function(item) {
|
567 |
+
if (item instanceof FilterParamEnumOptionModel) {
|
568 |
+
return item.label() || item.value();
|
569 |
+
}
|
570 |
+
return item;
|
571 |
+
}
|
572 |
+
};
|
573 |
+
|
574 |
+
var FilterParamEnumOptionModel = function(value, label, operator) {
|
575 |
+
this.value = ko.observable('');
|
576 |
+
this.label = ko.observable('');
|
577 |
+
this.operator = ko.observable('');
|
578 |
+
|
579 |
+
this.value = ko.observable(value);
|
580 |
+
this.label = ko.observable(label);
|
581 |
+
this.operator = ko.observable(operator);
|
582 |
+
|
583 |
+
this.toString = function() {
|
584 |
+
return this.value();
|
585 |
+
}
|
586 |
+
};
|
587 |
+
|
588 |
+
var FilterOperatorModel = function(operator, text) {
|
589 |
+
this.text = ko.observable('');
|
590 |
+
this.operator = ko.observable('');
|
591 |
+
|
592 |
+
this.text(text);
|
593 |
+
this.operator(operator);
|
594 |
+
};
|
595 |
+
|
596 |
+
var GroupByModel = function(param, text) {
|
597 |
+
this.text = ko.observable('');
|
598 |
+
this.param = ko.observable('');
|
599 |
+
|
600 |
+
this.text(text);
|
601 |
+
this.param(param);
|
602 |
+
};
|
603 |
+
|
604 |
+
ko.bindingHandlers.datetimepicker = {
|
605 |
+
init: function(element, valueAccessor, allBindingsAccessor) {
|
606 |
+
//initialize datepicker with some optional options
|
607 |
+
var options = allBindingsAccessor().datepickerOptions || {},
|
608 |
+
$el = $(element);
|
609 |
+
|
610 |
+
$el.datetimepicker(options);
|
611 |
+
|
612 |
+
//handle the field changing by registering datepicker's changeDate event
|
613 |
+
ko.utils.registerEventHandler(element, "changeDate", function() {
|
614 |
+
var observable = valueAccessor();
|
615 |
+
observable($el.datetimepicker("getDate"));
|
616 |
+
});
|
617 |
+
|
618 |
+
//handle disposal (if KO removes by the template binding)
|
619 |
+
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
|
620 |
+
$el.datetimepicker("destroy");
|
621 |
+
});
|
622 |
+
|
623 |
+
},
|
624 |
+
update: function(element, valueAccessor) {
|
625 |
+
var value = ko.utils.unwrapObservable(valueAccessor()),
|
626 |
+
$el = $(element);
|
627 |
+
|
628 |
+
//handle date data coming via json from Microsoft
|
629 |
+
if (String(value).indexOf('/Date(') == 0) {
|
630 |
+
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
|
631 |
+
}
|
632 |
+
|
633 |
+
var current = $el.datetimepicker("getDate");
|
634 |
+
|
635 |
+
if (value - current !== 0) {
|
636 |
+
$el.datetimepicker("setDate", value);
|
637 |
+
}
|
638 |
+
}
|
639 |
+
};
|
640 |
+
|
641 |
+
|
642 |
+
$(function() {
|
643 |
+
var liveTrafficWrapper = $('#wf-live-traffic');
|
644 |
+
WFAD.wfLiveTraffic = new LiveTrafficViewModel();
|
645 |
+
ko.applyBindings(WFAD.wfLiveTraffic, liveTrafficWrapper.get(0));
|
646 |
+
liveTrafficWrapper.find('form').submit();
|
647 |
+
WFAD.mode = 'liveTraffic';
|
648 |
+
|
649 |
+
var legend = $('#wf-live-traffic-legend');
|
650 |
+
var adminBar = $('#wpadminbar');
|
651 |
+
|
652 |
+
var hasScrolled = false;
|
653 |
+
var loadingListings = false;
|
654 |
+
$(window).on('scroll', function() {
|
655 |
+
var win = $(this);
|
656 |
+
if (liveTrafficWrapper.offset().top < win.scrollTop() + adminBar.outerHeight() + 20) {
|
657 |
+
legend.addClass('sticky');
|
658 |
+
} else {
|
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;
|
665 |
+
if (hasScrolled && !loadingListings && currentScrollBottom >= scrollThreshold) {
|
666 |
+
// console.log('infinite scroll');
|
667 |
+
|
668 |
+
loadingListings = true;
|
669 |
+
hasScrolled = false;
|
670 |
+
WFAD.wfLiveTraffic.loadNextListings(function() {
|
671 |
+
loadingListings = false;
|
672 |
+
});
|
673 |
+
} else if (currentScrollBottom < scrollThreshold) {
|
674 |
+
hasScrolled = true;
|
675 |
+
// console.log('no infinite scroll');
|
676 |
+
}
|
677 |
+
});
|
678 |
+
});
|
679 |
+
})
|
680 |
+
(jQuery);
|
js/jquery-ui-timepicker-addon.js
ADDED
@@ -0,0 +1,2245 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*! jQuery Timepicker Addon - v1.5.3 - 2015-04-19
|
2 |
+
* http://trentrichardson.com/examples/timepicker
|
3 |
+
* Copyright (c) 2015 Trent Richardson; Licensed MIT */
|
4 |
+
(function (factory) {
|
5 |
+
if (typeof define === 'function' && define.amd) {
|
6 |
+
define(['jquery', 'jquery.ui'], factory);
|
7 |
+
} else {
|
8 |
+
factory(jQuery);
|
9 |
+
}
|
10 |
+
}(function ($) {
|
11 |
+
|
12 |
+
/*
|
13 |
+
* Lets not redefine timepicker, Prevent "Uncaught RangeError: Maximum call stack size exceeded"
|
14 |
+
*/
|
15 |
+
$.ui.timepicker = $.ui.timepicker || {};
|
16 |
+
if ($.ui.timepicker.version) {
|
17 |
+
return;
|
18 |
+
}
|
19 |
+
|
20 |
+
/*
|
21 |
+
* Extend jQueryUI, get it started with our version number
|
22 |
+
*/
|
23 |
+
$.extend($.ui, {
|
24 |
+
timepicker: {
|
25 |
+
version: "1.5.3"
|
26 |
+
}
|
27 |
+
});
|
28 |
+
|
29 |
+
/*
|
30 |
+
* Timepicker manager.
|
31 |
+
* Use the singleton instance of this class, $.timepicker, to interact with the time picker.
|
32 |
+
* Settings for (groups of) time pickers are maintained in an instance object,
|
33 |
+
* allowing multiple different settings on the same page.
|
34 |
+
*/
|
35 |
+
var Timepicker = function () {
|
36 |
+
this.regional = []; // Available regional settings, indexed by language code
|
37 |
+
this.regional[''] = { // Default regional settings
|
38 |
+
currentText: 'Now',
|
39 |
+
closeText: 'Done',
|
40 |
+
amNames: ['AM', 'A'],
|
41 |
+
pmNames: ['PM', 'P'],
|
42 |
+
timeFormat: 'HH:mm',
|
43 |
+
timeSuffix: '',
|
44 |
+
timeOnlyTitle: 'Choose Time',
|
45 |
+
timeText: 'Time',
|
46 |
+
hourText: 'Hour',
|
47 |
+
minuteText: 'Minute',
|
48 |
+
secondText: 'Second',
|
49 |
+
millisecText: 'Millisecond',
|
50 |
+
microsecText: 'Microsecond',
|
51 |
+
timezoneText: 'Time Zone',
|
52 |
+
isRTL: false
|
53 |
+
};
|
54 |
+
this._defaults = { // Global defaults for all the datetime picker instances
|
55 |
+
showButtonPanel: true,
|
56 |
+
timeOnly: false,
|
57 |
+
timeOnlyShowDate: false,
|
58 |
+
showHour: null,
|
59 |
+
showMinute: null,
|
60 |
+
showSecond: null,
|
61 |
+
showMillisec: null,
|
62 |
+
showMicrosec: null,
|
63 |
+
showTimezone: null,
|
64 |
+
showTime: true,
|
65 |
+
stepHour: 1,
|
66 |
+
stepMinute: 1,
|
67 |
+
stepSecond: 1,
|
68 |
+
stepMillisec: 1,
|
69 |
+
stepMicrosec: 1,
|
70 |
+
hour: 0,
|
71 |
+
minute: 0,
|
72 |
+
second: 0,
|
73 |
+
millisec: 0,
|
74 |
+
microsec: 0,
|
75 |
+
timezone: null,
|
76 |
+
hourMin: 0,
|
77 |
+
minuteMin: 0,
|
78 |
+
secondMin: 0,
|
79 |
+
millisecMin: 0,
|
80 |
+
microsecMin: 0,
|
81 |
+
hourMax: 23,
|
82 |
+
minuteMax: 59,
|
83 |
+
secondMax: 59,
|
84 |
+
millisecMax: 999,
|
85 |
+
microsecMax: 999,
|
86 |
+
minDateTime: null,
|
87 |
+
maxDateTime: null,
|
88 |
+
maxTime: null,
|
89 |
+
minTime: null,
|
90 |
+
onSelect: null,
|
91 |
+
hourGrid: 0,
|
92 |
+
minuteGrid: 0,
|
93 |
+
secondGrid: 0,
|
94 |
+
millisecGrid: 0,
|
95 |
+
microsecGrid: 0,
|
96 |
+
alwaysSetTime: true,
|
97 |
+
separator: ' ',
|
98 |
+
altFieldTimeOnly: true,
|
99 |
+
altTimeFormat: null,
|
100 |
+
altSeparator: null,
|
101 |
+
altTimeSuffix: null,
|
102 |
+
altRedirectFocus: true,
|
103 |
+
pickerTimeFormat: null,
|
104 |
+
pickerTimeSuffix: null,
|
105 |
+
showTimepicker: true,
|
106 |
+
timezoneList: null,
|
107 |
+
addSliderAccess: false,
|
108 |
+
sliderAccessArgs: null,
|
109 |
+
controlType: 'slider',
|
110 |
+
oneLine: false,
|
111 |
+
defaultValue: null,
|
112 |
+
parse: 'strict',
|
113 |
+
afterInject: null
|
114 |
+
};
|
115 |
+
$.extend(this._defaults, this.regional['']);
|
116 |
+
};
|
117 |
+
|
118 |
+
$.extend(Timepicker.prototype, {
|
119 |
+
$input: null,
|
120 |
+
$altInput: null,
|
121 |
+
$timeObj: null,
|
122 |
+
inst: null,
|
123 |
+
hour_slider: null,
|
124 |
+
minute_slider: null,
|
125 |
+
second_slider: null,
|
126 |
+
millisec_slider: null,
|
127 |
+
microsec_slider: null,
|
128 |
+
timezone_select: null,
|
129 |
+
maxTime: null,
|
130 |
+
minTime: null,
|
131 |
+
hour: 0,
|
132 |
+
minute: 0,
|
133 |
+
second: 0,
|
134 |
+
millisec: 0,
|
135 |
+
microsec: 0,
|
136 |
+
timezone: null,
|
137 |
+
hourMinOriginal: null,
|
138 |
+
minuteMinOriginal: null,
|
139 |
+
secondMinOriginal: null,
|
140 |
+
millisecMinOriginal: null,
|
141 |
+
microsecMinOriginal: null,
|
142 |
+
hourMaxOriginal: null,
|
143 |
+
minuteMaxOriginal: null,
|
144 |
+
secondMaxOriginal: null,
|
145 |
+
millisecMaxOriginal: null,
|
146 |
+
microsecMaxOriginal: null,
|
147 |
+
ampm: '',
|
148 |
+
formattedDate: '',
|
149 |
+
formattedTime: '',
|
150 |
+
formattedDateTime: '',
|
151 |
+
timezoneList: null,
|
152 |
+
units: ['hour', 'minute', 'second', 'millisec', 'microsec'],
|
153 |
+
support: {},
|
154 |
+
control: null,
|
155 |
+
|
156 |
+
/*
|
157 |
+
* Override the default settings for all instances of the time picker.
|
158 |
+
* @param {Object} settings object - the new settings to use as defaults (anonymous object)
|
159 |
+
* @return {Object} the manager object
|
160 |
+
*/
|
161 |
+
setDefaults: function (settings) {
|
162 |
+
extendRemove(this._defaults, settings || {});
|
163 |
+
return this;
|
164 |
+
},
|
165 |
+
|
166 |
+
/*
|
167 |
+
* Create a new Timepicker instance
|
168 |
+
*/
|
169 |
+
_newInst: function ($input, opts) {
|
170 |
+
var tp_inst = new Timepicker(),
|
171 |
+
inlineSettings = {},
|
172 |
+
fns = {},
|
173 |
+
overrides, i;
|
174 |
+
|
175 |
+
for (var attrName in this._defaults) {
|
176 |
+
if (this._defaults.hasOwnProperty(attrName)) {
|
177 |
+
var attrValue = $input.attr('time:' + attrName);
|
178 |
+
if (attrValue) {
|
179 |
+
try {
|
180 |
+
inlineSettings[attrName] = eval(attrValue);
|
181 |
+
} catch (err) {
|
182 |
+
inlineSettings[attrName] = attrValue;
|
183 |
+
}
|
184 |
+
}
|
185 |
+
}
|
186 |
+
}
|
187 |
+
|
188 |
+
overrides = {
|
189 |
+
beforeShow: function (input, dp_inst) {
|
190 |
+
if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) {
|
191 |
+
return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst);
|
192 |
+
}
|
193 |
+
},
|
194 |
+
onChangeMonthYear: function (year, month, dp_inst) {
|
195 |
+
// Update the time as well : this prevents the time from disappearing from the $input field.
|
196 |
+
// tp_inst._updateDateTime(dp_inst);
|
197 |
+
if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) {
|
198 |
+
tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst);
|
199 |
+
}
|
200 |
+
},
|
201 |
+
onClose: function (dateText, dp_inst) {
|
202 |
+
if (tp_inst.timeDefined === true && $input.val() !== '') {
|
203 |
+
tp_inst._updateDateTime(dp_inst);
|
204 |
+
}
|
205 |
+
if ($.isFunction(tp_inst._defaults.evnts.onClose)) {
|
206 |
+
tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst);
|
207 |
+
}
|
208 |
+
}
|
209 |
+
};
|
210 |
+
for (i in overrides) {
|
211 |
+
if (overrides.hasOwnProperty(i)) {
|
212 |
+
fns[i] = opts[i] || null;
|
213 |
+
}
|
214 |
+
}
|
215 |
+
|
216 |
+
tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, opts, overrides, {
|
217 |
+
evnts: fns,
|
218 |
+
timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker');
|
219 |
+
});
|
220 |
+
tp_inst.amNames = $.map(tp_inst._defaults.amNames, function (val) {
|
221 |
+
return val.toUpperCase();
|
222 |
+
});
|
223 |
+
tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function (val) {
|
224 |
+
return val.toUpperCase();
|
225 |
+
});
|
226 |
+
|
227 |
+
// detect which units are supported
|
228 |
+
tp_inst.support = detectSupport(
|
229 |
+
tp_inst._defaults.timeFormat +
|
230 |
+
(tp_inst._defaults.pickerTimeFormat ? tp_inst._defaults.pickerTimeFormat : '') +
|
231 |
+
(tp_inst._defaults.altTimeFormat ? tp_inst._defaults.altTimeFormat : ''));
|
232 |
+
|
233 |
+
// controlType is string - key to our this._controls
|
234 |
+
if (typeof(tp_inst._defaults.controlType) === 'string') {
|
235 |
+
if (tp_inst._defaults.controlType === 'slider' && typeof($.ui.slider) === 'undefined') {
|
236 |
+
tp_inst._defaults.controlType = 'select';
|
237 |
+
}
|
238 |
+
tp_inst.control = tp_inst._controls[tp_inst._defaults.controlType];
|
239 |
+
}
|
240 |
+
// controlType is an object and must implement create, options, value methods
|
241 |
+
else {
|
242 |
+
tp_inst.control = tp_inst._defaults.controlType;
|
243 |
+
}
|
244 |
+
|
245 |
+
// prep the timezone options
|
246 |
+
var timezoneList = [-720, -660, -600, -570, -540, -480, -420, -360, -300, -270, -240, -210, -180, -120, -60,
|
247 |
+
0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 525, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840];
|
248 |
+
if (tp_inst._defaults.timezoneList !== null) {
|
249 |
+
timezoneList = tp_inst._defaults.timezoneList;
|
250 |
+
}
|
251 |
+
var tzl = timezoneList.length, tzi = 0, tzv = null;
|
252 |
+
if (tzl > 0 && typeof timezoneList[0] !== 'object') {
|
253 |
+
for (; tzi < tzl; tzi++) {
|
254 |
+
tzv = timezoneList[tzi];
|
255 |
+
timezoneList[tzi] = { value: tzv, label: $.timepicker.timezoneOffsetString(tzv, tp_inst.support.iso8601) };
|
256 |
+
}
|
257 |
+
}
|
258 |
+
tp_inst._defaults.timezoneList = timezoneList;
|
259 |
+
|
260 |
+
// set the default units
|
261 |
+
tp_inst.timezone = tp_inst._defaults.timezone !== null ? $.timepicker.timezoneOffsetNumber(tp_inst._defaults.timezone) :
|
262 |
+
((new Date()).getTimezoneOffset() * -1);
|
263 |
+
tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin ? tp_inst._defaults.hourMin :
|
264 |
+
tp_inst._defaults.hour > tp_inst._defaults.hourMax ? tp_inst._defaults.hourMax : tp_inst._defaults.hour;
|
265 |
+
tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin ? tp_inst._defaults.minuteMin :
|
266 |
+
tp_inst._defaults.minute > tp_inst._defaults.minuteMax ? tp_inst._defaults.minuteMax : tp_inst._defaults.minute;
|
267 |
+
tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin ? tp_inst._defaults.secondMin :
|
268 |
+
tp_inst._defaults.second > tp_inst._defaults.secondMax ? tp_inst._defaults.secondMax : tp_inst._defaults.second;
|
269 |
+
tp_inst.millisec = tp_inst._defaults.millisec < tp_inst._defaults.millisecMin ? tp_inst._defaults.millisecMin :
|
270 |
+
tp_inst._defaults.millisec > tp_inst._defaults.millisecMax ? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec;
|
271 |
+
tp_inst.microsec = tp_inst._defaults.microsec < tp_inst._defaults.microsecMin ? tp_inst._defaults.microsecMin :
|
272 |
+
tp_inst._defaults.microsec > tp_inst._defaults.microsecMax ? tp_inst._defaults.microsecMax : tp_inst._defaults.microsec;
|
273 |
+
tp_inst.ampm = '';
|
274 |
+
tp_inst.$input = $input;
|
275 |
+
|
276 |
+
if (tp_inst._defaults.altField) {
|
277 |
+
tp_inst.$altInput = $(tp_inst._defaults.altField);
|
278 |
+
if (tp_inst._defaults.altRedirectFocus === true) {
|
279 |
+
tp_inst.$altInput.css({
|
280 |
+
cursor: 'pointer'
|
281 |
+
}).focus(function () {
|
282 |
+
$input.trigger("focus");
|
283 |
+
});
|
284 |
+
}
|
285 |
+
}
|
286 |
+
|
287 |
+
if (tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0) {
|
288 |
+
tp_inst._defaults.minDate = new Date();
|
289 |
+
}
|
290 |
+
if (tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0) {
|
291 |
+
tp_inst._defaults.maxDate = new Date();
|
292 |
+
}
|
293 |
+
|
294 |
+
// datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime..
|
295 |
+
if (tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date) {
|
296 |
+
tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime());
|
297 |
+
}
|
298 |
+
if (tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date) {
|
299 |
+
tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime());
|
300 |
+
}
|
301 |
+
if (tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date) {
|
302 |
+
tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime());
|
303 |
+
}
|
304 |
+
if (tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) {
|
305 |
+
tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime());
|
306 |
+
}
|
307 |
+
tp_inst.$input.bind('focus', function () {
|
308 |
+
tp_inst._onFocus();
|
309 |
+
});
|
310 |
+
|
311 |
+
return tp_inst;
|
312 |
+
},
|
313 |
+
|
314 |
+
/*
|
315 |
+
* add our sliders to the calendar
|
316 |
+
*/
|
317 |
+
_addTimePicker: function (dp_inst) {
|
318 |
+
var currDT = $.trim((this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val());
|
319 |
+
|
320 |
+
this.timeDefined = this._parseTime(currDT);
|
321 |
+
this._limitMinMaxDateTime(dp_inst, false);
|
322 |
+
this._injectTimePicker();
|
323 |
+
this._afterInject();
|
324 |
+
},
|
325 |
+
|
326 |
+
/*
|
327 |
+
* parse the time string from input value or _setTime
|
328 |
+
*/
|
329 |
+
_parseTime: function (timeString, withDate) {
|
330 |
+
if (!this.inst) {
|
331 |
+
this.inst = $.datepicker._getInst(this.$input[0]);
|
332 |
+
}
|
333 |
+
|
334 |
+
if (withDate || !this._defaults.timeOnly) {
|
335 |
+
var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat');
|
336 |
+
try {
|
337 |
+
var parseRes = parseDateTimeInternal(dp_dateFormat, this._defaults.timeFormat, timeString, $.datepicker._getFormatConfig(this.inst), this._defaults);
|
338 |
+
if (!parseRes.timeObj) {
|
339 |
+
return false;
|
340 |
+
}
|
341 |
+
$.extend(this, parseRes.timeObj);
|
342 |
+
} catch (err) {
|
343 |
+
$.timepicker.log("Error parsing the date/time string: " + err +
|
344 |
+
"\ndate/time string = " + timeString +
|
345 |
+
"\ntimeFormat = " + this._defaults.timeFormat +
|
346 |
+
"\ndateFormat = " + dp_dateFormat);
|
347 |
+
return false;
|
348 |
+
}
|
349 |
+
return true;
|
350 |
+
} else {
|
351 |
+
var timeObj = $.datepicker.parseTime(this._defaults.timeFormat, timeString, this._defaults);
|
352 |
+
if (!timeObj) {
|
353 |
+
return false;
|
354 |
+
}
|
355 |
+
$.extend(this, timeObj);
|
356 |
+
return true;
|
357 |
+
}
|
358 |
+
},
|
359 |
+
|
360 |
+
/*
|
361 |
+
* Handle callback option after injecting timepicker
|
362 |
+
*/
|
363 |
+
_afterInject: function() {
|
364 |
+
var o = this.inst.settings;
|
365 |
+
if ($.isFunction(o.afterInject)) {
|
366 |
+
o.afterInject.call(this);
|
367 |
+
}
|
368 |
+
},
|
369 |
+
|
370 |
+
/*
|
371 |
+
* generate and inject html for timepicker into ui datepicker
|
372 |
+
*/
|
373 |
+
_injectTimePicker: function () {
|
374 |
+
var $dp = this.inst.dpDiv,
|
375 |
+
o = this.inst.settings,
|
376 |
+
tp_inst = this,
|
377 |
+
litem = '',
|
378 |
+
uitem = '',
|
379 |
+
show = null,
|
380 |
+
max = {},
|
381 |
+
gridSize = {},
|
382 |
+
size = null,
|
383 |
+
i = 0,
|
384 |
+
l = 0;
|
385 |
+
|
386 |
+
// Prevent displaying twice
|
387 |
+
if ($dp.find("div.ui-timepicker-div").length === 0 && o.showTimepicker) {
|
388 |
+
var noDisplay = ' ui_tpicker_unit_hide',
|
389 |
+
html = '<div class="ui-timepicker-div' + (o.isRTL ? ' ui-timepicker-rtl' : '') + (o.oneLine && o.controlType === 'select' ? ' ui-timepicker-oneLine' : '') + '"><dl>' + '<dt class="ui_tpicker_time_label"' + ((o.showTime) ? '' : noDisplay) + '>' + o.timeText + '</dt>' +
|
390 |
+
'<dd class="ui_tpicker_time '+ ((o.showTime) ? '' : noDisplay) + '"></dd>';
|
391 |
+
|
392 |
+
// Create the markup
|
393 |
+
for (i = 0, l = this.units.length; i < l; i++) {
|
394 |
+
litem = this.units[i];
|
395 |
+
uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);
|
396 |
+
show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];
|
397 |
+
|
398 |
+
// Added by Peter Medeiros:
|
399 |
+
// - Figure out what the hour/minute/second max should be based on the step values.
|
400 |
+
// - Example: if stepMinute is 15, then minMax is 45.
|
401 |
+
max[litem] = parseInt((o[litem + 'Max'] - ((o[litem + 'Max'] - o[litem + 'Min']) % o['step' + uitem])), 10);
|
402 |
+
gridSize[litem] = 0;
|
403 |
+
|
404 |
+
html += '<dt class="ui_tpicker_' + litem + '_label' + (show ? '' : noDisplay) + '">' + o[litem + 'Text'] + '</dt>' +
|
405 |
+
'<dd class="ui_tpicker_' + litem + (show ? '' : noDisplay) + '"><div class="ui_tpicker_' + litem + '_slider' + (show ? '' : noDisplay) + '"></div>';
|
406 |
+
|
407 |
+
if (show && o[litem + 'Grid'] > 0) {
|
408 |
+
html += '<div style="padding-left: 1px"><table class="ui-tpicker-grid-label"><tr>';
|
409 |
+
|
410 |
+
if (litem === 'hour') {
|
411 |
+
for (var h = o[litem + 'Min']; h <= max[litem]; h += parseInt(o[litem + 'Grid'], 10)) {
|
412 |
+
gridSize[litem]++;
|
413 |
+
var tmph = $.datepicker.formatTime(this.support.ampm ? 'hht' : 'HH', {hour: h}, o);
|
414 |
+
html += '<td data-for="' + litem + '">' + tmph + '</td>';
|
415 |
+
}
|
416 |
+
}
|
417 |
+
else {
|
418 |
+
for (var m = o[litem + 'Min']; m <= max[litem]; m += parseInt(o[litem + 'Grid'], 10)) {
|
419 |
+
gridSize[litem]++;
|
420 |
+
html += '<td data-for="' + litem + '">' + ((m < 10) ? '0' : '') + m + '</td>';
|
421 |
+
}
|
422 |
+
}
|
423 |
+
|
424 |
+
html += '</tr></table></div>';
|
425 |
+
}
|
426 |
+
html += '</dd>';
|
427 |
+
}
|
428 |
+
|
429 |
+
// Timezone
|
430 |
+
var showTz = o.showTimezone !== null ? o.showTimezone : this.support.timezone;
|
431 |
+
html += '<dt class="ui_tpicker_timezone_label' + (showTz ? '' : noDisplay) + '">' + o.timezoneText + '</dt>';
|
432 |
+
html += '<dd class="ui_tpicker_timezone' + (showTz ? '' : noDisplay) + '"></dd>';
|
433 |
+
|
434 |
+
// Create the elements from string
|
435 |
+
html += '</dl></div>';
|
436 |
+
var $tp = $(html);
|
437 |
+
|
438 |
+
// if we only want time picker...
|
439 |
+
if (o.timeOnly === true) {
|
440 |
+
$tp.prepend('<div class="ui-widget-header ui-helper-clearfix ui-corner-all">' + '<div class="ui-datepicker-title">' + o.timeOnlyTitle + '</div>' + '</div>');
|
441 |
+
$dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide();
|
442 |
+
}
|
443 |
+
|
444 |
+
// add sliders, adjust grids, add events
|
445 |
+
for (i = 0, l = tp_inst.units.length; i < l; i++) {
|
446 |
+
litem = tp_inst.units[i];
|
447 |
+
uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);
|
448 |
+
show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];
|
449 |
+
|
450 |
+
// add the slider
|
451 |
+
tp_inst[litem + '_slider'] = tp_inst.control.create(tp_inst, $tp.find('.ui_tpicker_' + litem + '_slider'), litem, tp_inst[litem], o[litem + 'Min'], max[litem], o['step' + uitem]);
|
452 |
+
|
453 |
+
// adjust the grid and add click event
|
454 |
+
if (show && o[litem + 'Grid'] > 0) {
|
455 |
+
size = 100 * gridSize[litem] * o[litem + 'Grid'] / (max[litem] - o[litem + 'Min']);
|
456 |
+
$tp.find('.ui_tpicker_' + litem + ' table').css({
|
457 |
+
width: size + "%",
|
458 |
+
marginLeft: o.isRTL ? '0' : ((size / (-2 * gridSize[litem])) + "%"),
|
459 |
+
marginRight: o.isRTL ? ((size / (-2 * gridSize[litem])) + "%") : '0',
|
460 |
+
borderCollapse: 'collapse'
|
461 |
+
}).find("td").click(function (e) {
|
462 |
+
var $t = $(this),
|
463 |
+
h = $t.html(),
|
464 |
+
n = parseInt(h.replace(/[^0-9]/g), 10),
|
465 |
+
ap = h.replace(/[^apm]/ig),
|
466 |
+
f = $t.data('for'); // loses scope, so we use data-for
|
467 |
+
|
468 |
+
if (f === 'hour') {
|
469 |
+
if (ap.indexOf('p') !== -1 && n < 12) {
|
470 |
+
n += 12;
|
471 |
+
}
|
472 |
+
else {
|
473 |
+
if (ap.indexOf('a') !== -1 && n === 12) {
|
474 |
+
n = 0;
|
475 |
+
}
|
476 |
+
}
|
477 |
+
}
|
478 |
+
|
479 |
+
tp_inst.control.value(tp_inst, tp_inst[f + '_slider'], litem, n);
|
480 |
+
|
481 |
+
tp_inst._onTimeChange();
|
482 |
+
tp_inst._onSelectHandler();
|
483 |
+
}).css({
|
484 |
+
cursor: 'pointer',
|
485 |
+
width: (100 / gridSize[litem]) + '%',
|
486 |
+
textAlign: 'center',
|
487 |
+
overflow: 'hidden'
|
488 |
+
});
|
489 |
+
} // end if grid > 0
|
490 |
+
} // end for loop
|
491 |
+
|
492 |
+
// Add timezone options
|
493 |
+
this.timezone_select = $tp.find('.ui_tpicker_timezone').append('<select></select>').find("select");
|
494 |
+
$.fn.append.apply(this.timezone_select,
|
495 |
+
$.map(o.timezoneList, function (val, idx) {
|
496 |
+
return $("<option />").val(typeof val === "object" ? val.value : val).text(typeof val === "object" ? val.label : val);
|
497 |
+
}));
|
498 |
+
if (typeof(this.timezone) !== "undefined" && this.timezone !== null && this.timezone !== "") {
|
499 |
+
var local_timezone = (new Date(this.inst.selectedYear, this.inst.selectedMonth, this.inst.selectedDay, 12)).getTimezoneOffset() * -1;
|
500 |
+
if (local_timezone === this.timezone) {
|
501 |
+
selectLocalTimezone(tp_inst);
|
502 |
+
} else {
|
503 |
+
this.timezone_select.val(this.timezone);
|
504 |
+
}
|
505 |
+
} else {
|
506 |
+
if (typeof(this.hour) !== "undefined" && this.hour !== null && this.hour !== "") {
|
507 |
+
this.timezone_select.val(o.timezone);
|
508 |
+
} else {
|
509 |
+
selectLocalTimezone(tp_inst);
|
510 |
+
}
|
511 |
+
}
|
512 |
+
this.timezone_select.change(function () {
|
513 |
+
tp_inst._onTimeChange();
|
514 |
+
tp_inst._onSelectHandler();
|
515 |
+
tp_inst._afterInject();
|
516 |
+
});
|
517 |
+
// End timezone options
|
518 |
+
|
519 |
+
// inject timepicker into datepicker
|
520 |
+
var $buttonPanel = $dp.find('.ui-datepicker-buttonpane');
|
521 |
+
if ($buttonPanel.length) {
|
522 |
+
$buttonPanel.before($tp);
|
523 |
+
} else {
|
524 |
+
$dp.append($tp);
|
525 |
+
}
|
526 |
+
|
527 |
+
this.$timeObj = $tp.find('.ui_tpicker_time');
|
528 |
+
|
529 |
+
if (this.inst !== null) {
|
530 |
+
var timeDefined = this.timeDefined;
|
531 |
+
this._onTimeChange();
|
532 |
+
this.timeDefined = timeDefined;
|
533 |
+
}
|
534 |
+
|
535 |
+
// slideAccess integration: http://trentrichardson.com/2011/11/11/jquery-ui-sliders-and-touch-accessibility/
|
536 |
+
if (this._defaults.addSliderAccess) {
|
537 |
+
var sliderAccessArgs = this._defaults.sliderAccessArgs,
|
538 |
+
rtl = this._defaults.isRTL;
|
539 |
+
sliderAccessArgs.isRTL = rtl;
|
540 |
+
|
541 |
+
setTimeout(function () { // fix for inline mode
|
542 |
+
if ($tp.find('.ui-slider-access').length === 0) {
|
543 |
+
$tp.find('.ui-slider:visible').sliderAccess(sliderAccessArgs);
|
544 |
+
|
545 |
+
// fix any grids since sliders are shorter
|
546 |
+
var sliderAccessWidth = $tp.find('.ui-slider-access:eq(0)').outerWidth(true);
|
547 |
+
if (sliderAccessWidth) {
|
548 |
+
$tp.find('table:visible').each(function () {
|
549 |
+
var $g = $(this),
|
550 |
+
oldWidth = $g.outerWidth(),
|
551 |
+
oldMarginLeft = $g.css(rtl ? 'marginRight' : 'marginLeft').toString().replace('%', ''),
|
552 |
+
newWidth = oldWidth - sliderAccessWidth,
|
553 |
+
newMarginLeft = ((oldMarginLeft * newWidth) / oldWidth) + '%',
|
554 |
+
css = { width: newWidth, marginRight: 0, marginLeft: 0 };
|
555 |
+
css[rtl ? 'marginRight' : 'marginLeft'] = newMarginLeft;
|
556 |
+
$g.css(css);
|
557 |
+
});
|
558 |
+
}
|
559 |
+
}
|
560 |
+
}, 10);
|
561 |
+
}
|
562 |
+
// end slideAccess integration
|
563 |
+
|
564 |
+
tp_inst._limitMinMaxDateTime(this.inst, true);
|
565 |
+
}
|
566 |
+
},
|
567 |
+
|
568 |
+
/*
|
569 |
+
* This function tries to limit the ability to go outside the
|
570 |
+
* min/max date range
|
571 |
+
*/
|
572 |
+
_limitMinMaxDateTime: function (dp_inst, adjustSliders) {
|
573 |
+
var o = this._defaults,
|
574 |
+
dp_date = new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay);
|
575 |
+
|
576 |
+
if (!this._defaults.showTimepicker) {
|
577 |
+
return;
|
578 |
+
} // No time so nothing to check here
|
579 |
+
|
580 |
+
if ($.datepicker._get(dp_inst, 'minDateTime') !== null && $.datepicker._get(dp_inst, 'minDateTime') !== undefined && dp_date) {
|
581 |
+
var minDateTime = $.datepicker._get(dp_inst, 'minDateTime'),
|
582 |
+
minDateTimeDate = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), 0, 0, 0, 0);
|
583 |
+
|
584 |
+
if (this.hourMinOriginal === null || this.minuteMinOriginal === null || this.secondMinOriginal === null || this.millisecMinOriginal === null || this.microsecMinOriginal === null) {
|
585 |
+
this.hourMinOriginal = o.hourMin;
|
586 |
+
this.minuteMinOriginal = o.minuteMin;
|
587 |
+
this.secondMinOriginal = o.secondMin;
|
588 |
+
this.millisecMinOriginal = o.millisecMin;
|
589 |
+
this.microsecMinOriginal = o.microsecMin;
|
590 |
+
}
|
591 |
+
|
592 |
+
if (dp_inst.settings.timeOnly || minDateTimeDate.getTime() === dp_date.getTime()) {
|
593 |
+
this._defaults.hourMin = minDateTime.getHours();
|
594 |
+
if (this.hour <= this._defaults.hourMin) {
|
595 |
+
this.hour = this._defaults.hourMin;
|
596 |
+
this._defaults.minuteMin = minDateTime.getMinutes();
|
597 |
+
if (this.minute <= this._defaults.minuteMin) {
|
598 |
+
this.minute = this._defaults.minuteMin;
|
599 |
+
this._defaults.secondMin = minDateTime.getSeconds();
|
600 |
+
if (this.second <= this._defaults.secondMin) {
|
601 |
+
this.second = this._defaults.secondMin;
|
602 |
+
this._defaults.millisecMin = minDateTime.getMilliseconds();
|
603 |
+
if (this.millisec <= this._defaults.millisecMin) {
|
604 |
+
this.millisec = this._defaults.millisecMin;
|
605 |
+
this._defaults.microsecMin = minDateTime.getMicroseconds();
|
606 |
+
} else {
|
607 |
+
if (this.microsec < this._defaults.microsecMin) {
|
608 |
+
this.microsec = this._defaults.microsecMin;
|
609 |
+
}
|
610 |
+
this._defaults.microsecMin = this.microsecMinOriginal;
|
611 |
+
}
|
612 |
+
} else {
|
613 |
+
this._defaults.millisecMin = this.millisecMinOriginal;
|
614 |
+
this._defaults.microsecMin = this.microsecMinOriginal;
|
615 |
+
}
|
616 |
+
} else {
|
617 |
+
this._defaults.secondMin = this.secondMinOriginal;
|
618 |
+
this._defaults.millisecMin = this.millisecMinOriginal;
|
619 |
+
this._defaults.microsecMin = this.microsecMinOriginal;
|
620 |
+
}
|
621 |
+
} else {
|
622 |
+
this._defaults.minuteMin = this.minuteMinOriginal;
|
623 |
+
this._defaults.secondMin = this.secondMinOriginal;
|
624 |
+
this._defaults.millisecMin = this.millisecMinOriginal;
|
625 |
+
this._defaults.microsecMin = this.microsecMinOriginal;
|
626 |
+
}
|
627 |
+
} else {
|
628 |
+
this._defaults.hourMin = this.hourMinOriginal;
|
629 |
+
this._defaults.minuteMin = this.minuteMinOriginal;
|
630 |
+
this._defaults.secondMin = this.secondMinOriginal;
|
631 |
+
this._defaults.millisecMin = this.millisecMinOriginal;
|
632 |
+
this._defaults.microsecMin = this.microsecMinOriginal;
|
633 |
+
}
|
634 |
+
}
|
635 |
+
|
636 |
+
if ($.datepicker._get(dp_inst, 'maxDateTime') !== null && $.datepicker._get(dp_inst, 'maxDateTime') !== undefined && dp_date) {
|
637 |
+
var maxDateTime = $.datepicker._get(dp_inst, 'maxDateTime'),
|
638 |
+
maxDateTimeDate = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), 0, 0, 0, 0);
|
639 |
+
|
640 |
+
if (this.hourMaxOriginal === null || this.minuteMaxOriginal === null || this.secondMaxOriginal === null || this.millisecMaxOriginal === null) {
|
641 |
+
this.hourMaxOriginal = o.hourMax;
|
642 |
+
this.minuteMaxOriginal = o.minuteMax;
|
643 |
+
this.secondMaxOriginal = o.secondMax;
|
644 |
+
this.millisecMaxOriginal = o.millisecMax;
|
645 |
+
this.microsecMaxOriginal = o.microsecMax;
|
646 |
+
}
|
647 |
+
|
648 |
+
if (dp_inst.settings.timeOnly || maxDateTimeDate.getTime() === dp_date.getTime()) {
|
649 |
+
this._defaults.hourMax = maxDateTime.getHours();
|
650 |
+
if (this.hour >= this._defaults.hourMax) {
|
651 |
+
this.hour = this._defaults.hourMax;
|
652 |
+
this._defaults.minuteMax = maxDateTime.getMinutes();
|
653 |
+
if (this.minute >= this._defaults.minuteMax) {
|
654 |
+
this.minute = this._defaults.minuteMax;
|
655 |
+
this._defaults.secondMax = maxDateTime.getSeconds();
|
656 |
+
if (this.second >= this._defaults.secondMax) {
|
657 |
+
this.second = this._defaults.secondMax;
|
658 |
+
this._defaults.millisecMax = maxDateTime.getMilliseconds();
|
659 |
+
if (this.millisec >= this._defaults.millisecMax) {
|
660 |
+
this.millisec = this._defaults.millisecMax;
|
661 |
+
this._defaults.microsecMax = maxDateTime.getMicroseconds();
|
662 |
+
} else {
|
663 |
+
if (this.microsec > this._defaults.microsecMax) {
|
664 |
+
this.microsec = this._defaults.microsecMax;
|
665 |
+
}
|
666 |
+
this._defaults.microsecMax = this.microsecMaxOriginal;
|
667 |
+
}
|
668 |
+
} else {
|
669 |
+
this._defaults.millisecMax = this.millisecMaxOriginal;
|
670 |
+
this._defaults.microsecMax = this.microsecMaxOriginal;
|
671 |
+
}
|
672 |
+
} else {
|
673 |
+
this._defaults.secondMax = this.secondMaxOriginal;
|
674 |
+
this._defaults.millisecMax = this.millisecMaxOriginal;
|
675 |
+
this._defaults.microsecMax = this.microsecMaxOriginal;
|
676 |
+
}
|
677 |
+
} else {
|
678 |
+
this._defaults.minuteMax = this.minuteMaxOriginal;
|
679 |
+
this._defaults.secondMax = this.secondMaxOriginal;
|
680 |
+
this._defaults.millisecMax = this.millisecMaxOriginal;
|
681 |
+
this._defaults.microsecMax = this.microsecMaxOriginal;
|
682 |
+
}
|
683 |
+
} else {
|
684 |
+
this._defaults.hourMax = this.hourMaxOriginal;
|
685 |
+
this._defaults.minuteMax = this.minuteMaxOriginal;
|
686 |
+
this._defaults.secondMax = this.secondMaxOriginal;
|
687 |
+
this._defaults.millisecMax = this.millisecMaxOriginal;
|
688 |
+
this._defaults.microsecMax = this.microsecMaxOriginal;
|
689 |
+
}
|
690 |
+
}
|
691 |
+
|
692 |
+
if (dp_inst.settings.minTime!==null) {
|
693 |
+
var tempMinTime=new Date("01/01/1970 " + dp_inst.settings.minTime);
|
694 |
+
if (this.hour<tempMinTime.getHours()) {
|
695 |
+
this.hour=this._defaults.hourMin=tempMinTime.getHours();
|
696 |
+
this.minute=this._defaults.minuteMin=tempMinTime.getMinutes();
|
697 |
+
} else if (this.hour===tempMinTime.getHours() && this.minute<tempMinTime.getMinutes()) {
|
698 |
+
this.minute=this._defaults.minuteMin=tempMinTime.getMinutes();
|
699 |
+
} else {
|
700 |
+
if (this._defaults.hourMin<tempMinTime.getHours()) {
|
701 |
+
this._defaults.hourMin=tempMinTime.getHours();
|
702 |
+
this._defaults.minuteMin=tempMinTime.getMinutes();
|
703 |
+
} else if (this._defaults.hourMin===tempMinTime.getHours()===this.hour && this._defaults.minuteMin<tempMinTime.getMinutes()) {
|
704 |
+
this._defaults.minuteMin=tempMinTime.getMinutes();
|
705 |
+
} else {
|
706 |
+
this._defaults.minuteMin=0;
|
707 |
+
}
|
708 |
+
}
|
709 |
+
}
|
710 |
+
|
711 |
+
if (dp_inst.settings.maxTime!==null) {
|
712 |
+
var tempMaxTime=new Date("01/01/1970 " + dp_inst.settings.maxTime);
|
713 |
+
if (this.hour>tempMaxTime.getHours()) {
|
714 |
+
this.hour=this._defaults.hourMax=tempMaxTime.getHours();
|
715 |
+
this.minute=this._defaults.minuteMax=tempMaxTime.getMinutes();
|
716 |
+
} else if (this.hour===tempMaxTime.getHours() && this.minute>tempMaxTime.getMinutes()) {
|
717 |
+
this.minute=this._defaults.minuteMax=tempMaxTime.getMinutes();
|
718 |
+
} else {
|
719 |
+
if (this._defaults.hourMax>tempMaxTime.getHours()) {
|
720 |
+
this._defaults.hourMax=tempMaxTime.getHours();
|
721 |
+
this._defaults.minuteMax=tempMaxTime.getMinutes();
|
722 |
+
} else if (this._defaults.hourMax===tempMaxTime.getHours()===this.hour && this._defaults.minuteMax>tempMaxTime.getMinutes()) {
|
723 |
+
this._defaults.minuteMax=tempMaxTime.getMinutes();
|
724 |
+
} else {
|
725 |
+
this._defaults.minuteMax=59;
|
726 |
+
}
|
727 |
+
}
|
728 |
+
}
|
729 |
+
|
730 |
+
if (adjustSliders !== undefined && adjustSliders === true) {
|
731 |
+
var hourMax = parseInt((this._defaults.hourMax - ((this._defaults.hourMax - this._defaults.hourMin) % this._defaults.stepHour)), 10),
|
732 |
+
minMax = parseInt((this._defaults.minuteMax - ((this._defaults.minuteMax - this._defaults.minuteMin) % this._defaults.stepMinute)), 10),
|
733 |
+
secMax = parseInt((this._defaults.secondMax - ((this._defaults.secondMax - this._defaults.secondMin) % this._defaults.stepSecond)), 10),
|
734 |
+
millisecMax = parseInt((this._defaults.millisecMax - ((this._defaults.millisecMax - this._defaults.millisecMin) % this._defaults.stepMillisec)), 10),
|
735 |
+
microsecMax = parseInt((this._defaults.microsecMax - ((this._defaults.microsecMax - this._defaults.microsecMin) % this._defaults.stepMicrosec)), 10);
|
736 |
+
|
737 |
+
if (this.hour_slider) {
|
738 |
+
this.control.options(this, this.hour_slider, 'hour', { min: this._defaults.hourMin, max: hourMax, step: this._defaults.stepHour });
|
739 |
+
this.control.value(this, this.hour_slider, 'hour', this.hour - (this.hour % this._defaults.stepHour));
|
740 |
+
}
|
741 |
+
if (this.minute_slider) {
|
742 |
+
this.control.options(this, this.minute_slider, 'minute', { min: this._defaults.minuteMin, max: minMax, step: this._defaults.stepMinute });
|
743 |
+
this.control.value(this, this.minute_slider, 'minute', this.minute - (this.minute % this._defaults.stepMinute));
|
744 |
+
}
|
745 |
+
if (this.second_slider) {
|
746 |
+
this.control.options(this, this.second_slider, 'second', { min: this._defaults.secondMin, max: secMax, step: this._defaults.stepSecond });
|
747 |
+
this.control.value(this, this.second_slider, 'second', this.second - (this.second % this._defaults.stepSecond));
|
748 |
+
}
|
749 |
+
if (this.millisec_slider) {
|
750 |
+
this.control.options(this, this.millisec_slider, 'millisec', { min: this._defaults.millisecMin, max: millisecMax, step: this._defaults.stepMillisec });
|
751 |
+
this.control.value(this, this.millisec_slider, 'millisec', this.millisec - (this.millisec % this._defaults.stepMillisec));
|
752 |
+
}
|
753 |
+
if (this.microsec_slider) {
|
754 |
+
this.control.options(this, this.microsec_slider, 'microsec', { min: this._defaults.microsecMin, max: microsecMax, step: this._defaults.stepMicrosec });
|
755 |
+
this.control.value(this, this.microsec_slider, 'microsec', this.microsec - (this.microsec % this._defaults.stepMicrosec));
|
756 |
+
}
|
757 |
+
}
|
758 |
+
|
759 |
+
},
|
760 |
+
|
761 |
+
/*
|
762 |
+
* when a slider moves, set the internal time...
|
763 |
+
* on time change is also called when the time is updated in the text field
|
764 |
+
*/
|
765 |
+
_onTimeChange: function () {
|
766 |
+
if (!this._defaults.showTimepicker) {
|
767 |
+
return;
|
768 |
+
}
|
769 |
+
var hour = (this.hour_slider) ? this.control.value(this, this.hour_slider, 'hour') : false,
|
770 |
+
minute = (this.minute_slider) ? this.control.value(this, this.minute_slider, 'minute') : false,
|
771 |
+
second = (this.second_slider) ? this.control.value(this, this.second_slider, 'second') : false,
|
772 |
+
millisec = (this.millisec_slider) ? this.control.value(this, this.millisec_slider, 'millisec') : false,
|
773 |
+
microsec = (this.microsec_slider) ? this.control.value(this, this.microsec_slider, 'microsec') : false,
|
774 |
+
timezone = (this.timezone_select) ? this.timezone_select.val() : false,
|
775 |
+
o = this._defaults,
|
776 |
+
pickerTimeFormat = o.pickerTimeFormat || o.timeFormat,
|
777 |
+
pickerTimeSuffix = o.pickerTimeSuffix || o.timeSuffix;
|
778 |
+
|
779 |
+
if (typeof(hour) === 'object') {
|
780 |
+
hour = false;
|
781 |
+
}
|
782 |
+
if (typeof(minute) === 'object') {
|
783 |
+
minute = false;
|
784 |
+
}
|
785 |
+
if (typeof(second) === 'object') {
|
786 |
+
second = false;
|
787 |
+
}
|
788 |
+
if (typeof(millisec) === 'object') {
|
789 |
+
millisec = false;
|
790 |
+
}
|
791 |
+
if (typeof(microsec) === 'object') {
|
792 |
+
microsec = false;
|
793 |
+
}
|
794 |
+
if (typeof(timezone) === 'object') {
|
795 |
+
timezone = false;
|
796 |
+
}
|
797 |
+
|
798 |
+
if (hour !== false) {
|
799 |
+
hour = parseInt(hour, 10);
|
800 |
+
}
|
801 |
+
if (minute !== false) {
|
802 |
+
minute = parseInt(minute, 10);
|
803 |
+
}
|
804 |
+
if (second !== false) {
|
805 |
+
second = parseInt(second, 10);
|
806 |
+
}
|
807 |
+
if (millisec !== false) {
|
808 |
+
millisec = parseInt(millisec, 10);
|
809 |
+
}
|
810 |
+
if (microsec !== false) {
|
811 |
+
microsec = parseInt(microsec, 10);
|
812 |
+
}
|
813 |
+
if (timezone !== false) {
|
814 |
+
timezone = timezone.toString();
|
815 |
+
}
|
816 |
+
|
817 |
+
var ampm = o[hour < 12 ? 'amNames' : 'pmNames'][0];
|
818 |
+
|
819 |
+
// If the update was done in the input field, the input field should not be updated.
|
820 |
+
// If the update was done using the sliders, update the input field.
|
821 |
+
var hasChanged = (
|
822 |
+
hour !== parseInt(this.hour,10) || // sliders should all be numeric
|
823 |
+
minute !== parseInt(this.minute,10) ||
|
824 |
+
second !== parseInt(this.second,10) ||
|
825 |
+
millisec !== parseInt(this.millisec,10) ||
|
826 |
+
microsec !== parseInt(this.microsec,10) ||
|
827 |
+
(this.ampm.length > 0 && (hour < 12) !== ($.inArray(this.ampm.toUpperCase(), this.amNames) !== -1)) ||
|
828 |
+
(this.timezone !== null && timezone !== this.timezone.toString()) // could be numeric or "EST" format, so use toString()
|
829 |
+
);
|
830 |
+
|
831 |
+
if (hasChanged) {
|
832 |
+
|
833 |
+
if (hour !== false) {
|
834 |
+
this.hour = hour;
|
835 |
+
}
|
836 |
+
if (minute !== false) {
|
837 |
+
this.minute = minute;
|
838 |
+
}
|
839 |
+
if (second !== false) {
|
840 |
+
this.second = second;
|
841 |
+
}
|
842 |
+
if (millisec !== false) {
|
843 |
+
this.millisec = millisec;
|
844 |
+
}
|
845 |
+
if (microsec !== false) {
|
846 |
+
this.microsec = microsec;
|
847 |
+
}
|
848 |
+
if (timezone !== false) {
|
849 |
+
this.timezone = timezone;
|
850 |
+
}
|
851 |
+
|
852 |
+
if (!this.inst) {
|
853 |
+
this.inst = $.datepicker._getInst(this.$input[0]);
|
854 |
+
}
|
855 |
+
|
856 |
+
this._limitMinMaxDateTime(this.inst, true);
|
857 |
+
}
|
858 |
+
if (this.support.ampm) {
|
859 |
+
this.ampm = ampm;
|
860 |
+
}
|
861 |
+
|
862 |
+
// Updates the time within the timepicker
|
863 |
+
this.formattedTime = $.datepicker.formatTime(o.timeFormat, this, o);
|
864 |
+
if (this.$timeObj) {
|
865 |
+
if (pickerTimeFormat === o.timeFormat) {
|
866 |
+
this.$timeObj.text(this.formattedTime + pickerTimeSuffix);
|
867 |
+
}
|
868 |
+
else {
|
869 |
+
this.$timeObj.text($.datepicker.formatTime(pickerTimeFormat, this, o) + pickerTimeSuffix);
|
870 |
+
}
|
871 |
+
}
|
872 |
+
|
873 |
+
this.timeDefined = true;
|
874 |
+
if (hasChanged) {
|
875 |
+
this._updateDateTime();
|
876 |
+
//this.$input.focus(); // may automatically open the picker on setDate
|
877 |
+
}
|
878 |
+
},
|
879 |
+
|
880 |
+
/*
|
881 |
+
* call custom onSelect.
|
882 |
+
* bind to sliders slidestop, and grid click.
|
883 |
+
*/
|
884 |
+
_onSelectHandler: function () {
|
885 |
+
var onSelect = this._defaults.onSelect || this.inst.settings.onSelect;
|
886 |
+
var inputEl = this.$input ? this.$input[0] : null;
|
887 |
+
if (onSelect && inputEl) {
|
888 |
+
onSelect.apply(inputEl, [this.formattedDateTime, this]);
|
889 |
+
}
|
890 |
+
},
|
891 |
+
|
892 |
+
/*
|
893 |
+
* update our input with the new date time..
|
894 |
+
*/
|
895 |
+
_updateDateTime: function (dp_inst) {
|
896 |
+
dp_inst = this.inst || dp_inst;
|
897 |
+
var dtTmp = (dp_inst.currentYear > 0?
|
898 |
+
new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay) :
|
899 |
+
new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),
|
900 |
+
dt = $.datepicker._daylightSavingAdjust(dtTmp),
|
901 |
+
//dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),
|
902 |
+
//dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay)),
|
903 |
+
dateFmt = $.datepicker._get(dp_inst, 'dateFormat'),
|
904 |
+
formatCfg = $.datepicker._getFormatConfig(dp_inst),
|
905 |
+
timeAvailable = dt !== null && this.timeDefined;
|
906 |
+
this.formattedDate = $.datepicker.formatDate(dateFmt, (dt === null ? new Date() : dt), formatCfg);
|
907 |
+
var formattedDateTime = this.formattedDate;
|
908 |
+
|
909 |
+
// if a slider was changed but datepicker doesn't have a value yet, set it
|
910 |
+
if (dp_inst.lastVal === "") {
|
911 |
+
dp_inst.currentYear = dp_inst.selectedYear;
|
912 |
+
dp_inst.currentMonth = dp_inst.selectedMonth;
|
913 |
+
dp_inst.currentDay = dp_inst.selectedDay;
|
914 |
+
}
|
915 |
+
|
916 |
+
/*
|
917 |
+
* remove following lines to force every changes in date picker to change the input value
|
918 |
+
* Bug descriptions: when an input field has a default value, and click on the field to pop up the date picker.
|
919 |
+
* If the user manually empty the value in the input field, the date picker will never change selected value.
|
920 |
+
*/
|
921 |
+
//if (dp_inst.lastVal !== undefined && (dp_inst.lastVal.length > 0 && this.$input.val().length === 0)) {
|
922 |
+
// return;
|
923 |
+
//}
|
924 |
+
|
925 |
+
if (this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === false) {
|
926 |
+
formattedDateTime = this.formattedTime;
|
927 |
+
} else if ((this._defaults.timeOnly !== true && (this._defaults.alwaysSetTime || timeAvailable)) || (this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === true)) {
|
928 |
+
formattedDateTime += this._defaults.separator + this.formattedTime + this._defaults.timeSuffix;
|
929 |
+
}
|
930 |
+
|
931 |
+
this.formattedDateTime = formattedDateTime;
|
932 |
+
|
933 |
+
if (!this._defaults.showTimepicker) {
|
934 |
+
this.$input.val(this.formattedDate);
|
935 |
+
} else if (this.$altInput && this._defaults.timeOnly === false && this._defaults.altFieldTimeOnly === true) {
|
936 |
+
this.$altInput.val(this.formattedTime);
|
937 |
+
this.$input.val(this.formattedDate);
|
938 |
+
} else if (this.$altInput) {
|
939 |
+
this.$input.val(formattedDateTime);
|
940 |
+
var altFormattedDateTime = '',
|
941 |
+
altSeparator = this._defaults.altSeparator !== null ? this._defaults.altSeparator : this._defaults.separator,
|
942 |
+
altTimeSuffix = this._defaults.altTimeSuffix !== null ? this._defaults.altTimeSuffix : this._defaults.timeSuffix;
|
943 |
+
|
944 |
+
if (!this._defaults.timeOnly) {
|
945 |
+
if (this._defaults.altFormat) {
|
946 |
+
altFormattedDateTime = $.datepicker.formatDate(this._defaults.altFormat, (dt === null ? new Date() : dt), formatCfg);
|
947 |
+
}
|
948 |
+
else {
|
949 |
+
altFormattedDateTime = this.formattedDate;
|
950 |
+
}
|
951 |
+
|
952 |
+
if (altFormattedDateTime) {
|
953 |
+
altFormattedDateTime += altSeparator;
|
954 |
+
}
|
955 |
+
}
|
956 |
+
|
957 |
+
if (this._defaults.altTimeFormat !== null) {
|
958 |
+
altFormattedDateTime += $.datepicker.formatTime(this._defaults.altTimeFormat, this, this._defaults) + altTimeSuffix;
|
959 |
+
}
|
960 |
+
else {
|
961 |
+
altFormattedDateTime += this.formattedTime + altTimeSuffix;
|
962 |
+
}
|
963 |
+
this.$altInput.val(altFormattedDateTime);
|
964 |
+
} else {
|
965 |
+
this.$input.val(formattedDateTime);
|
966 |
+
}
|
967 |
+
|
968 |
+
this.$input.trigger("change");
|
969 |
+
},
|
970 |
+
|
971 |
+
_onFocus: function () {
|
972 |
+
if (!this.$input.val() && this._defaults.defaultValue) {
|
973 |
+
this.$input.val(this._defaults.defaultValue);
|
974 |
+
var inst = $.datepicker._getInst(this.$input.get(0)),
|
975 |
+
tp_inst = $.datepicker._get(inst, 'timepicker');
|
976 |
+
if (tp_inst) {
|
977 |
+
if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {
|
978 |
+
try {
|
979 |
+
$.datepicker._updateDatepicker(inst);
|
980 |
+
} catch (err) {
|
981 |
+
$.timepicker.log(err);
|
982 |
+
}
|
983 |
+
}
|
984 |
+
}
|
985 |
+
}
|
986 |
+
},
|
987 |
+
|
988 |
+
/*
|
989 |
+
* Small abstraction to control types
|
990 |
+
* We can add more, just be sure to follow the pattern: create, options, value
|
991 |
+
*/
|
992 |
+
_controls: {
|
993 |
+
// slider methods
|
994 |
+
slider: {
|
995 |
+
create: function (tp_inst, obj, unit, val, min, max, step) {
|
996 |
+
var rtl = tp_inst._defaults.isRTL; // if rtl go -60->0 instead of 0->60
|
997 |
+
return obj.prop('slide', null).slider({
|
998 |
+
orientation: "horizontal",
|
999 |
+
value: rtl ? val * -1 : val,
|
1000 |
+
min: rtl ? max * -1 : min,
|
1001 |
+
max: rtl ? min * -1 : max,
|
1002 |
+
step: step,
|
1003 |
+
slide: function (event, ui) {
|
1004 |
+
tp_inst.control.value(tp_inst, $(this), unit, rtl ? ui.value * -1 : ui.value);
|
1005 |
+
tp_inst._onTimeChange();
|
1006 |
+
},
|
1007 |
+
stop: function (event, ui) {
|
1008 |
+
tp_inst._onSelectHandler();
|
1009 |
+
}
|
1010 |
+
});
|
1011 |
+
},
|
1012 |
+
options: function (tp_inst, obj, unit, opts, val) {
|
1013 |
+
if (tp_inst._defaults.isRTL) {
|
1014 |
+
if (typeof(opts) === 'string') {
|
1015 |
+
if (opts === 'min' || opts === 'max') {
|
1016 |
+
if (val !== undefined) {
|
1017 |
+
return obj.slider(opts, val * -1);
|
1018 |
+
}
|
1019 |
+
return Math.abs(obj.slider(opts));
|
1020 |
+
}
|
1021 |
+
return obj.slider(opts);
|
1022 |
+
}
|
1023 |
+
var min = opts.min,
|
1024 |
+
max = opts.max;
|
1025 |
+
opts.min = opts.max = null;
|
1026 |
+
if (min !== undefined) {
|
1027 |
+
opts.max = min * -1;
|
1028 |
+
}
|
1029 |
+
if (max !== undefined) {
|
1030 |
+
opts.min = max * -1;
|
1031 |
+
}
|
1032 |
+
return obj.slider(opts);
|
1033 |
+
}
|
1034 |
+
if (typeof(opts) === 'string' && val !== undefined) {
|
1035 |
+
return obj.slider(opts, val);
|
1036 |
+
}
|
1037 |
+
return obj.slider(opts);
|
1038 |
+
},
|
1039 |
+
value: function (tp_inst, obj, unit, val) {
|
1040 |
+
if (tp_inst._defaults.isRTL) {
|
1041 |
+
if (val !== undefined) {
|
1042 |
+
return obj.slider('value', val * -1);
|
1043 |
+
}
|
1044 |
+
return Math.abs(obj.slider('value'));
|
1045 |
+
}
|
1046 |
+
if (val !== undefined) {
|
1047 |
+
return obj.slider('value', val);
|
1048 |
+
}
|
1049 |
+
return obj.slider('value');
|
1050 |
+
}
|
1051 |
+
},
|
1052 |
+
// select methods
|
1053 |
+
select: {
|
1054 |
+
create: function (tp_inst, obj, unit, val, min, max, step) {
|
1055 |
+
var sel = '<select class="ui-timepicker-select ui-state-default ui-corner-all" data-unit="' + unit + '" data-min="' + min + '" data-max="' + max + '" data-step="' + step + '">',
|
1056 |
+
format = tp_inst._defaults.pickerTimeFormat || tp_inst._defaults.timeFormat;
|
1057 |
+
|
1058 |
+
for (var i = min; i <= max; i += step) {
|
1059 |
+
sel += '<option value="' + i + '"' + (i === val ? ' selected' : '') + '>';
|
1060 |
+
if (unit === 'hour') {
|
1061 |
+
sel += $.datepicker.formatTime($.trim(format.replace(/[^ht ]/ig, '')), {hour: i}, tp_inst._defaults);
|
1062 |
+
}
|
1063 |
+
else if (unit === 'millisec' || unit === 'microsec' || i >= 10) { sel += i; }
|
1064 |
+
else {sel += '0' + i.toString(); }
|
1065 |
+
sel += '</option>';
|
1066 |
+
}
|
1067 |
+
sel += '</select>';
|
1068 |
+
|
1069 |
+
obj.children('select').remove();
|
1070 |
+
|
1071 |
+
$(sel).appendTo(obj).change(function (e) {
|
1072 |
+
tp_inst._onTimeChange();
|
1073 |
+
tp_inst._onSelectHandler();
|
1074 |
+
tp_inst._afterInject();
|
1075 |
+
});
|
1076 |
+
|
1077 |
+
return obj;
|
1078 |
+
},
|
1079 |
+
options: function (tp_inst, obj, unit, opts, val) {
|
1080 |
+
var o = {},
|
1081 |
+
$t = obj.children('select');
|
1082 |
+
if (typeof(opts) === 'string') {
|
1083 |
+
if (val === undefined) {
|
1084 |
+
return $t.data(opts);
|
1085 |
+
}
|
1086 |
+
o[opts] = val;
|
1087 |
+
}
|
1088 |
+
else { o = opts; }
|
1089 |
+
return tp_inst.control.create(tp_inst, obj, $t.data('unit'), $t.val(), o.min>=0 ? o.min : $t.data('min'), o.max || $t.data('max'), o.step || $t.data('step'));
|
1090 |
+
},
|
1091 |
+
value: function (tp_inst, obj, unit, val) {
|
1092 |
+
var $t = obj.children('select');
|
1093 |
+
if (val !== undefined) {
|
1094 |
+
return $t.val(val);
|
1095 |
+
}
|
1096 |
+
return $t.val();
|
1097 |
+
}
|
1098 |
+
}
|
1099 |
+
} // end _controls
|
1100 |
+
|
1101 |
+
});
|
1102 |
+
|
1103 |
+
$.fn.extend({
|
1104 |
+
/*
|
1105 |
+
* shorthand just to use timepicker.
|
1106 |
+
*/
|
1107 |
+
timepicker: function (o) {
|
1108 |
+
o = o || {};
|
1109 |
+
var tmp_args = Array.prototype.slice.call(arguments);
|
1110 |
+
|
1111 |
+
if (typeof o === 'object') {
|
1112 |
+
tmp_args[0] = $.extend(o, {
|
1113 |
+
timeOnly: true
|
1114 |
+
});
|
1115 |
+
}
|
1116 |
+
|
1117 |
+
return $(this).each(function () {
|
1118 |
+
$.fn.datetimepicker.apply($(this), tmp_args);
|
1119 |
+
});
|
1120 |
+
},
|
1121 |
+
|
1122 |
+
/*
|
1123 |
+
* extend timepicker to datepicker
|
1124 |
+
*/
|
1125 |
+
datetimepicker: function (o) {
|
1126 |
+
o = o || {};
|
1127 |
+
var tmp_args = arguments;
|
1128 |
+
|
1129 |
+
if (typeof(o) === 'string') {
|
1130 |
+
if (o === 'getDate' || (o === 'option' && tmp_args.length === 2 && typeof (tmp_args[1]) === 'string')) {
|
1131 |
+
return $.fn.datepicker.apply($(this[0]), tmp_args);
|
1132 |
+
} else {
|
1133 |
+
return this.each(function () {
|
1134 |
+
var $t = $(this);
|
1135 |
+
$t.datepicker.apply($t, tmp_args);
|
1136 |
+
});
|
1137 |
+
}
|
1138 |
+
} else {
|
1139 |
+
return this.each(function () {
|
1140 |
+
var $t = $(this);
|
1141 |
+
$t.datepicker($.timepicker._newInst($t, o)._defaults);
|
1142 |
+
});
|
1143 |
+
}
|
1144 |
+
}
|
1145 |
+
});
|
1146 |
+
|
1147 |
+
/*
|
1148 |
+
* Public Utility to parse date and time
|
1149 |
+
*/
|
1150 |
+
$.datepicker.parseDateTime = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {
|
1151 |
+
var parseRes = parseDateTimeInternal(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings);
|
1152 |
+
if (parseRes.timeObj) {
|
1153 |
+
var t = parseRes.timeObj;
|
1154 |
+
parseRes.date.setHours(t.hour, t.minute, t.second, t.millisec);
|
1155 |
+
parseRes.date.setMicroseconds(t.microsec);
|
1156 |
+
}
|
1157 |
+
|
1158 |
+
return parseRes.date;
|
1159 |
+
};
|
1160 |
+
|
1161 |
+
/*
|
1162 |
+
* Public utility to parse time
|
1163 |
+
*/
|
1164 |
+
$.datepicker.parseTime = function (timeFormat, timeString, options) {
|
1165 |
+
var o = extendRemove(extendRemove({}, $.timepicker._defaults), options || {}),
|
1166 |
+
iso8601 = (timeFormat.replace(/\'.*?\'/g, '').indexOf('Z') !== -1);
|
1167 |
+
|
1168 |
+
// Strict parse requires the timeString to match the timeFormat exactly
|
1169 |
+
var strictParse = function (f, s, o) {
|
1170 |
+
|
1171 |
+
// pattern for standard and localized AM/PM markers
|
1172 |
+
var getPatternAmpm = function (amNames, pmNames) {
|
1173 |
+
var markers = [];
|
1174 |
+
if (amNames) {
|
1175 |
+
$.merge(markers, amNames);
|
1176 |
+
}
|
1177 |
+
if (pmNames) {
|
1178 |
+
$.merge(markers, pmNames);
|
1179 |
+
}
|
1180 |
+
markers = $.map(markers, function (val) {
|
1181 |
+
return val.replace(/[.*+?|()\[\]{}\\]/g, '\\$&');
|
1182 |
+
});
|
1183 |
+
return '(' + markers.join('|') + ')?';
|
1184 |
+
};
|
1185 |
+
|
1186 |
+
// figure out position of time elements.. cause js cant do named captures
|
1187 |
+
var getFormatPositions = function (timeFormat) {
|
1188 |
+
var finds = timeFormat.toLowerCase().match(/(h{1,2}|m{1,2}|s{1,2}|l{1}|c{1}|t{1,2}|z|'.*?')/g),
|
1189 |
+
orders = {
|
1190 |
+
h: -1,
|
1191 |
+
m: -1,
|
1192 |
+
s: -1,
|
1193 |
+
l: -1,
|
1194 |
+
c: -1,
|
1195 |
+
t: -1,
|
1196 |
+
z: -1
|
1197 |
+
};
|
1198 |
+
|
1199 |
+
if (finds) {
|
1200 |
+
for (var i = 0; i < finds.length; i++) {
|
1201 |
+
if (orders[finds[i].toString().charAt(0)] === -1) {
|
1202 |
+
orders[finds[i].toString().charAt(0)] = i + 1;
|
1203 |
+
}
|
1204 |
+
}
|
1205 |
+
}
|
1206 |
+
return orders;
|
1207 |
+
};
|
1208 |
+
|
1209 |
+
var regstr = '^' + f.toString()
|
1210 |
+
.replace(/([hH]{1,2}|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {
|
1211 |
+
var ml = match.length;
|
1212 |
+
switch (match.charAt(0).toLowerCase()) {
|
1213 |
+
case 'h':
|
1214 |
+
return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
|
1215 |
+
case 'm':
|
1216 |
+
return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
|
1217 |
+
case 's':
|
1218 |
+
return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
|
1219 |
+
case 'l':
|
1220 |
+
return '(\\d?\\d?\\d)';
|
1221 |
+
case 'c':
|
1222 |
+
return '(\\d?\\d?\\d)';
|
1223 |
+
case 'z':
|
1224 |
+
return '(z|[-+]\\d\\d:?\\d\\d|\\S+)?';
|
1225 |
+
case 't':
|
1226 |
+
return getPatternAmpm(o.amNames, o.pmNames);
|
1227 |
+
default: // literal escaped in quotes
|
1228 |
+
return '(' + match.replace(/\'/g, "").replace(/(\.|\$|\^|\\|\/|\(|\)|\[|\]|\?|\+|\*)/g, function (m) { return "\\" + m; }) + ')?';
|
1229 |
+
}
|
1230 |
+
})
|
1231 |
+
.replace(/\s/g, '\\s?') +
|
1232 |
+
o.timeSuffix + '$',
|
1233 |
+
order = getFormatPositions(f),
|
1234 |
+
ampm = '',
|
1235 |
+
treg;
|
1236 |
+
|
1237 |
+
treg = s.match(new RegExp(regstr, 'i'));
|
1238 |
+
|
1239 |
+
var resTime = {
|
1240 |
+
hour: 0,
|
1241 |
+
minute: 0,
|
1242 |
+
second: 0,
|
1243 |
+
millisec: 0,
|
1244 |
+
microsec: 0
|
1245 |
+
};
|
1246 |
+
|
1247 |
+
if (treg) {
|
1248 |
+
if (order.t !== -1) {
|
1249 |
+
if (treg[order.t] === undefined || treg[order.t].length === 0) {
|
1250 |
+
ampm = '';
|
1251 |
+
resTime.ampm = '';
|
1252 |
+
} else {
|
1253 |
+
ampm = $.inArray(treg[order.t].toUpperCase(), $.map(o.amNames, function (x,i) { return x.toUpperCase(); })) !== -1 ? 'AM' : 'PM';
|
1254 |
+
resTime.ampm = o[ampm === 'AM' ? 'amNames' : 'pmNames'][0];
|
1255 |
+
}
|
1256 |
+
}
|
1257 |
+
|
1258 |
+
if (order.h !== -1) {
|
1259 |
+
if (ampm === 'AM' && treg[order.h] === '12') {
|
1260 |
+
resTime.hour = 0; // 12am = 0 hour
|
1261 |
+
} else {
|
1262 |
+
if (ampm === 'PM' && treg[order.h] !== '12') {
|
1263 |
+
resTime.hour = parseInt(treg[order.h], 10) + 12; // 12pm = 12 hour, any other pm = hour + 12
|
1264 |
+
} else {
|
1265 |
+
resTime.hour = Number(treg[order.h]);
|
1266 |
+
}
|
1267 |
+
}
|
1268 |
+
}
|
1269 |
+
|
1270 |
+
if (order.m !== -1) {
|
1271 |
+
resTime.minute = Number(treg[order.m]);
|
1272 |
+
}
|
1273 |
+
if (order.s !== -1) {
|
1274 |
+
resTime.second = Number(treg[order.s]);
|
1275 |
+
}
|
1276 |
+
if (order.l !== -1) {
|
1277 |
+
resTime.millisec = Number(treg[order.l]);
|
1278 |
+
}
|
1279 |
+
if (order.c !== -1) {
|
1280 |
+
resTime.microsec = Number(treg[order.c]);
|
1281 |
+
}
|
1282 |
+
if (order.z !== -1 && treg[order.z] !== undefined) {
|
1283 |
+
resTime.timezone = $.timepicker.timezoneOffsetNumber(treg[order.z]);
|
1284 |
+
}
|
1285 |
+
|
1286 |
+
|
1287 |
+
return resTime;
|
1288 |
+
}
|
1289 |
+
return false;
|
1290 |
+
};// end strictParse
|
1291 |
+
|
1292 |
+
// First try JS Date, if that fails, use strictParse
|
1293 |
+
var looseParse = function (f, s, o) {
|
1294 |
+
try {
|
1295 |
+
var d = new Date('2012-01-01 ' + s);
|
1296 |
+
if (isNaN(d.getTime())) {
|
1297 |
+
d = new Date('2012-01-01T' + s);
|
1298 |
+
if (isNaN(d.getTime())) {
|
1299 |
+
d = new Date('01/01/2012 ' + s);
|
1300 |
+
if (isNaN(d.getTime())) {
|
1301 |
+
throw "Unable to parse time with native Date: " + s;
|
1302 |
+
}
|
1303 |
+
}
|
1304 |
+
}
|
1305 |
+
|
1306 |
+
return {
|
1307 |
+
hour: d.getHours(),
|
1308 |
+
minute: d.getMinutes(),
|
1309 |
+
second: d.getSeconds(),
|
1310 |
+
millisec: d.getMilliseconds(),
|
1311 |
+
microsec: d.getMicroseconds(),
|
1312 |
+
timezone: d.getTimezoneOffset() * -1
|
1313 |
+
};
|
1314 |
+
}
|
1315 |
+
catch (err) {
|
1316 |
+
try {
|
1317 |
+
return strictParse(f, s, o);
|
1318 |
+
}
|
1319 |
+
catch (err2) {
|
1320 |
+
$.timepicker.log("Unable to parse \ntimeString: " + s + "\ntimeFormat: " + f);
|
1321 |
+
}
|
1322 |
+
}
|
1323 |
+
return false;
|
1324 |
+
}; // end looseParse
|
1325 |
+
|
1326 |
+
if (typeof o.parse === "function") {
|
1327 |
+
return o.parse(timeFormat, timeString, o);
|
1328 |
+
}
|
1329 |
+
if (o.parse === 'loose') {
|
1330 |
+
return looseParse(timeFormat, timeString, o);
|
1331 |
+
}
|
1332 |
+
return strictParse(timeFormat, timeString, o);
|
1333 |
+
};
|
1334 |
+
|
1335 |
+
/**
|
1336 |
+
* Public utility to format the time
|
1337 |
+
* @param {string} format format of the time
|
1338 |
+
* @param {Object} time Object not a Date for timezones
|
1339 |
+
* @param {Object} [options] essentially the regional[].. amNames, pmNames, ampm
|
1340 |
+
* @returns {string} the formatted time
|
1341 |
+
*/
|
1342 |
+
$.datepicker.formatTime = function (format, time, options) {
|
1343 |
+
options = options || {};
|
1344 |
+
options = $.extend({}, $.timepicker._defaults, options);
|
1345 |
+
time = $.extend({
|
1346 |
+
hour: 0,
|
1347 |
+
minute: 0,
|
1348 |
+
second: 0,
|
1349 |
+
millisec: 0,
|
1350 |
+
microsec: 0,
|
1351 |
+
timezone: null
|
1352 |
+
}, time);
|
1353 |
+
|
1354 |
+
var tmptime = format,
|
1355 |
+
ampmName = options.amNames[0],
|
1356 |
+
hour = parseInt(time.hour, 10);
|
1357 |
+
|
1358 |
+
if (hour > 11) {
|
1359 |
+
ampmName = options.pmNames[0];
|
1360 |
+
}
|
1361 |
+
|
1362 |
+
tmptime = tmptime.replace(/(?:HH?|hh?|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {
|
1363 |
+
switch (match) {
|
1364 |
+
case 'HH':
|
1365 |
+
return ('0' + hour).slice(-2);
|
1366 |
+
case 'H':
|
1367 |
+
return hour;
|
1368 |
+
case 'hh':
|
1369 |
+
return ('0' + convert24to12(hour)).slice(-2);
|
1370 |
+
case 'h':
|
1371 |
+
return convert24to12(hour);
|
1372 |
+
case 'mm':
|
1373 |
+
return ('0' + time.minute).slice(-2);
|
1374 |
+
case 'm':
|
1375 |
+
return time.minute;
|
1376 |
+
case 'ss':
|
1377 |
+
return ('0' + time.second).slice(-2);
|
1378 |
+
case 's':
|
1379 |
+
return time.second;
|
1380 |
+
case 'l':
|
1381 |
+
return ('00' + time.millisec).slice(-3);
|
1382 |
+
case 'c':
|
1383 |
+
return ('00' + time.microsec).slice(-3);
|
1384 |
+
case 'z':
|
1385 |
+
return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, false);
|
1386 |
+
case 'Z':
|
1387 |
+
return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, true);
|
1388 |
+
case 'T':
|
1389 |
+
return ampmName.charAt(0).toUpperCase();
|
1390 |
+
case 'TT':
|
1391 |
+
return ampmName.toUpperCase();
|
1392 |
+
case 't':
|
1393 |
+
return ampmName.charAt(0).toLowerCase();
|
1394 |
+
case 'tt':
|
1395 |
+
return ampmName.toLowerCase();
|
1396 |
+
default:
|
1397 |
+
return match.replace(/'/g, "");
|
1398 |
+
}
|
1399 |
+
});
|
1400 |
+
|
1401 |
+
return tmptime;
|
1402 |
+
};
|
1403 |
+
|
1404 |
+
/*
|
1405 |
+
* the bad hack :/ override datepicker so it doesn't close on select
|
1406 |
+
// inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378
|
1407 |
+
*/
|
1408 |
+
$.datepicker._base_selectDate = $.datepicker._selectDate;
|
1409 |
+
$.datepicker._selectDate = function (id, dateStr) {
|
1410 |
+
var inst = this._getInst($(id)[0]),
|
1411 |
+
tp_inst = this._get(inst, 'timepicker'),
|
1412 |
+
was_inline;
|
1413 |
+
|
1414 |
+
if (tp_inst && inst.settings.showTimepicker) {
|
1415 |
+
tp_inst._limitMinMaxDateTime(inst, true);
|
1416 |
+
was_inline = inst.inline;
|
1417 |
+
inst.inline = inst.stay_open = true;
|
1418 |
+
//This way the onSelect handler called from calendarpicker get the full dateTime
|
1419 |
+
this._base_selectDate(id, dateStr);
|
1420 |
+
inst.inline = was_inline;
|
1421 |
+
inst.stay_open = false;
|
1422 |
+
this._notifyChange(inst);
|
1423 |
+
this._updateDatepicker(inst);
|
1424 |
+
} else {
|
1425 |
+
this._base_selectDate(id, dateStr);
|
1426 |
+
}
|
1427 |
+
};
|
1428 |
+
|
1429 |
+
/*
|
1430 |
+
* second bad hack :/ override datepicker so it triggers an event when changing the input field
|
1431 |
+
* and does not redraw the datepicker on every selectDate event
|
1432 |
+
*/
|
1433 |
+
$.datepicker._base_updateDatepicker = $.datepicker._updateDatepicker;
|
1434 |
+
$.datepicker._updateDatepicker = function (inst) {
|
1435 |
+
|
1436 |
+
// don't popup the datepicker if there is another instance already opened
|
1437 |
+
var input = inst.input[0];
|
1438 |
+
if ($.datepicker._curInst && $.datepicker._curInst !== inst && $.datepicker._datepickerShowing && $.datepicker._lastInput !== input) {
|
1439 |
+
return;
|
1440 |
+
}
|
1441 |
+
|
1442 |
+
if (typeof(inst.stay_open) !== 'boolean' || inst.stay_open === false) {
|
1443 |
+
|
1444 |
+
this._base_updateDatepicker(inst);
|
1445 |
+
|
1446 |
+
// Reload the time control when changing something in the input text field.
|
1447 |
+
var tp_inst = this._get(inst, 'timepicker');
|
1448 |
+
if (tp_inst) {
|
1449 |
+
tp_inst._addTimePicker(inst);
|
1450 |
+
}
|
1451 |
+
}
|
1452 |
+
};
|
1453 |
+
|
1454 |
+
/*
|
1455 |
+
* third bad hack :/ override datepicker so it allows spaces and colon in the input field
|
1456 |
+
*/
|
1457 |
+
$.datepicker._base_doKeyPress = $.datepicker._doKeyPress;
|
1458 |
+
$.datepicker._doKeyPress = function (event) {
|
1459 |
+
var inst = $.datepicker._getInst(event.target),
|
1460 |
+
tp_inst = $.datepicker._get(inst, 'timepicker');
|
1461 |
+
|
1462 |
+
if (tp_inst) {
|
1463 |
+
if ($.datepicker._get(inst, 'constrainInput')) {
|
1464 |
+
var ampm = tp_inst.support.ampm,
|
1465 |
+
tz = tp_inst._defaults.showTimezone !== null ? tp_inst._defaults.showTimezone : tp_inst.support.timezone,
|
1466 |
+
dateChars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')),
|
1467 |
+
datetimeChars = tp_inst._defaults.timeFormat.toString()
|
1468 |
+
.replace(/[hms]/g, '')
|
1469 |
+
.replace(/TT/g, ampm ? 'APM' : '')
|
1470 |
+
.replace(/Tt/g, ampm ? 'AaPpMm' : '')
|
1471 |
+
.replace(/tT/g, ampm ? 'AaPpMm' : '')
|
1472 |
+
.replace(/T/g, ampm ? 'AP' : '')
|
1473 |
+
.replace(/tt/g, ampm ? 'apm' : '')
|
1474 |
+
.replace(/t/g, ampm ? 'ap' : '') +
|
1475 |
+
" " + tp_inst._defaults.separator +
|
1476 |
+
tp_inst._defaults.timeSuffix +
|
1477 |
+
(tz ? tp_inst._defaults.timezoneList.join('') : '') +
|
1478 |
+
(tp_inst._defaults.amNames.join('')) + (tp_inst._defaults.pmNames.join('')) +
|
1479 |
+
dateChars,
|
1480 |
+
chr = String.fromCharCode(event.charCode === undefined ? event.keyCode : event.charCode);
|
1481 |
+
return event.ctrlKey || (chr < ' ' || !dateChars || datetimeChars.indexOf(chr) > -1);
|
1482 |
+
}
|
1483 |
+
}
|
1484 |
+
|
1485 |
+
return $.datepicker._base_doKeyPress(event);
|
1486 |
+
};
|
1487 |
+
|
1488 |
+
/*
|
1489 |
+
* Fourth bad hack :/ override _updateAlternate function used in inline mode to init altField
|
1490 |
+
* Update any alternate field to synchronise with the main field.
|
1491 |
+
*/
|
1492 |
+
$.datepicker._base_updateAlternate = $.datepicker._updateAlternate;
|
1493 |
+
$.datepicker._updateAlternate = function (inst) {
|
1494 |
+
var tp_inst = this._get(inst, 'timepicker');
|
1495 |
+
if (tp_inst) {
|
1496 |
+
var altField = tp_inst._defaults.altField;
|
1497 |
+
if (altField) { // update alternate field too
|
1498 |
+
var altFormat = tp_inst._defaults.altFormat || tp_inst._defaults.dateFormat,
|
1499 |
+
date = this._getDate(inst),
|
1500 |
+
formatCfg = $.datepicker._getFormatConfig(inst),
|
1501 |
+
altFormattedDateTime = '',
|
1502 |
+
altSeparator = tp_inst._defaults.altSeparator ? tp_inst._defaults.altSeparator : tp_inst._defaults.separator,
|
1503 |
+
altTimeSuffix = tp_inst._defaults.altTimeSuffix ? tp_inst._defaults.altTimeSuffix : tp_inst._defaults.timeSuffix,
|
1504 |
+
altTimeFormat = tp_inst._defaults.altTimeFormat !== null ? tp_inst._defaults.altTimeFormat : tp_inst._defaults.timeFormat;
|
1505 |
+
|
1506 |
+
altFormattedDateTime += $.datepicker.formatTime(altTimeFormat, tp_inst, tp_inst._defaults) + altTimeSuffix;
|
1507 |
+
if (!tp_inst._defaults.timeOnly && !tp_inst._defaults.altFieldTimeOnly && date !== null) {
|
1508 |
+
if (tp_inst._defaults.altFormat) {
|
1509 |
+
altFormattedDateTime = $.datepicker.formatDate(tp_inst._defaults.altFormat, date, formatCfg) + altSeparator + altFormattedDateTime;
|
1510 |
+
}
|
1511 |
+
else {
|
1512 |
+
altFormattedDateTime = tp_inst.formattedDate + altSeparator + altFormattedDateTime;
|
1513 |
+
}
|
1514 |
+
}
|
1515 |
+
$(altField).val( inst.input.val() ? altFormattedDateTime : "");
|
1516 |
+
}
|
1517 |
+
}
|
1518 |
+
else {
|
1519 |
+
$.datepicker._base_updateAlternate(inst);
|
1520 |
+
}
|
1521 |
+
};
|
1522 |
+
|
1523 |
+
/*
|
1524 |
+
* Override key up event to sync manual input changes.
|
1525 |
+
*/
|
1526 |
+
$.datepicker._base_doKeyUp = $.datepicker._doKeyUp;
|
1527 |
+
$.datepicker._doKeyUp = function (event) {
|
1528 |
+
var inst = $.datepicker._getInst(event.target),
|
1529 |
+
tp_inst = $.datepicker._get(inst, 'timepicker');
|
1530 |
+
|
1531 |
+
if (tp_inst) {
|
1532 |
+
if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {
|
1533 |
+
try {
|
1534 |
+
$.datepicker._updateDatepicker(inst);
|
1535 |
+
} catch (err) {
|
1536 |
+
$.timepicker.log(err);
|
1537 |
+
}
|
1538 |
+
}
|
1539 |
+
}
|
1540 |
+
|
1541 |
+
return $.datepicker._base_doKeyUp(event);
|
1542 |
+
};
|
1543 |
+
|
1544 |
+
/*
|
1545 |
+
* override "Today" button to also grab the time.
|
1546 |
+
*/
|
1547 |
+
$.datepicker._base_gotoToday = $.datepicker._gotoToday;
|
1548 |
+
$.datepicker._gotoToday = function (id) {
|
1549 |
+
var inst = this._getInst($(id)[0]),
|
1550 |
+
$dp = inst.dpDiv;
|
1551 |
+
var tp_inst = this._get(inst, 'timepicker');
|
1552 |
+
selectLocalTimezone(tp_inst);
|
1553 |
+
var now = new Date();
|
1554 |
+
this._setTime(inst, now);
|
1555 |
+
this._setDate(inst, now);
|
1556 |
+
this._base_gotoToday(id);
|
1557 |
+
};
|
1558 |
+
|
1559 |
+
/*
|
1560 |
+
* Disable & enable the Time in the datetimepicker
|
1561 |
+
*/
|
1562 |
+
$.datepicker._disableTimepickerDatepicker = function (target) {
|
1563 |
+
var inst = this._getInst(target);
|
1564 |
+
if (!inst) {
|
1565 |
+
return;
|
1566 |
+
}
|
1567 |
+
|
1568 |
+
var tp_inst = this._get(inst, 'timepicker');
|
1569 |
+
$(target).datepicker('getDate'); // Init selected[Year|Month|Day]
|
1570 |
+
if (tp_inst) {
|
1571 |
+
inst.settings.showTimepicker = false;
|
1572 |
+
tp_inst._defaults.showTimepicker = false;
|
1573 |
+
tp_inst._updateDateTime(inst);
|
1574 |
+
}
|
1575 |
+
};
|
1576 |
+
|
1577 |
+
$.datepicker._enableTimepickerDatepicker = function (target) {
|
1578 |
+
var inst = this._getInst(target);
|
1579 |
+
if (!inst) {
|
1580 |
+
return;
|
1581 |
+
}
|
1582 |
+
|
1583 |
+
var tp_inst = this._get(inst, 'timepicker');
|
1584 |
+
$(target).datepicker('getDate'); // Init selected[Year|Month|Day]
|
1585 |
+
if (tp_inst) {
|
1586 |
+
inst.settings.showTimepicker = true;
|
1587 |
+
tp_inst._defaults.showTimepicker = true;
|
1588 |
+
tp_inst._addTimePicker(inst); // Could be disabled on page load
|
1589 |
+
tp_inst._updateDateTime(inst);
|
1590 |
+
}
|
1591 |
+
};
|
1592 |
+
|
1593 |
+
/*
|
1594 |
+
* Create our own set time function
|
1595 |
+
*/
|
1596 |
+
$.datepicker._setTime = function (inst, date) {
|
1597 |
+
var tp_inst = this._get(inst, 'timepicker');
|
1598 |
+
if (tp_inst) {
|
1599 |
+
var defaults = tp_inst._defaults;
|
1600 |
+
|
1601 |
+
// calling _setTime with no date sets time to defaults
|
1602 |
+
tp_inst.hour = date ? date.getHours() : defaults.hour;
|
1603 |
+
tp_inst.minute = date ? date.getMinutes() : defaults.minute;
|
1604 |
+
tp_inst.second = date ? date.getSeconds() : defaults.second;
|
1605 |
+
tp_inst.millisec = date ? date.getMilliseconds() : defaults.millisec;
|
1606 |
+
tp_inst.microsec = date ? date.getMicroseconds() : defaults.microsec;
|
1607 |
+
|
1608 |
+
//check if within min/max times..
|
1609 |
+
tp_inst._limitMinMaxDateTime(inst, true);
|
1610 |
+
|
1611 |
+
tp_inst._onTimeChange();
|
1612 |
+
tp_inst._updateDateTime(inst);
|
1613 |
+
}
|
1614 |
+
};
|
1615 |
+
|
1616 |
+
/*
|
1617 |
+
* Create new public method to set only time, callable as $().datepicker('setTime', date)
|
1618 |
+
*/
|
1619 |
+
$.datepicker._setTimeDatepicker = function (target, date, withDate) {
|
1620 |
+
var inst = this._getInst(target);
|
1621 |
+
if (!inst) {
|
1622 |
+
return;
|
1623 |
+
}
|
1624 |
+
|
1625 |
+
var tp_inst = this._get(inst, 'timepicker');
|
1626 |
+
|
1627 |
+
if (tp_inst) {
|
1628 |
+
this._setDateFromField(inst);
|
1629 |
+
var tp_date;
|
1630 |
+
if (date) {
|
1631 |
+
if (typeof date === "string") {
|
1632 |
+
tp_inst._parseTime(date, withDate);
|
1633 |
+
tp_date = new Date();
|
1634 |
+
tp_date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);
|
1635 |
+
tp_date.setMicroseconds(tp_inst.microsec);
|
1636 |
+
} else {
|
1637 |
+
tp_date = new Date(date.getTime());
|
1638 |
+
tp_date.setMicroseconds(date.getMicroseconds());
|
1639 |
+
}
|
1640 |
+
if (tp_date.toString() === 'Invalid Date') {
|
1641 |
+
tp_date = undefined;
|
1642 |
+
}
|
1643 |
+
this._setTime(inst, tp_date);
|
1644 |
+
}
|
1645 |
+
}
|
1646 |
+
|
1647 |
+
};
|
1648 |
+
|
1649 |
+
/*
|
1650 |
+
* override setDate() to allow setting time too within Date object
|
1651 |
+
*/
|
1652 |
+
$.datepicker._base_setDateDatepicker = $.datepicker._setDateDatepicker;
|
1653 |
+
$.datepicker._setDateDatepicker = function (target, _date) {
|
1654 |
+
var inst = this._getInst(target);
|
1655 |
+
var date = _date;
|
1656 |
+
if (!inst) {
|
1657 |
+
return;
|
1658 |
+
}
|
1659 |
+
|
1660 |
+
if (typeof(_date) === 'string') {
|
1661 |
+
date = new Date(_date);
|
1662 |
+
if (!date.getTime()) {
|
1663 |
+
this._base_setDateDatepicker.apply(this, arguments);
|
1664 |
+
date = $(target).datepicker('getDate');
|
1665 |
+
}
|
1666 |
+
}
|
1667 |
+
|
1668 |
+
var tp_inst = this._get(inst, 'timepicker');
|
1669 |
+
var tp_date;
|
1670 |
+
if (date instanceof Date) {
|
1671 |
+
tp_date = new Date(date.getTime());
|
1672 |
+
tp_date.setMicroseconds(date.getMicroseconds());
|
1673 |
+
} else {
|
1674 |
+
tp_date = date;
|
1675 |
+
}
|
1676 |
+
|
1677 |
+
// This is important if you are using the timezone option, javascript's Date
|
1678 |
+
// object will only return the timezone offset for the current locale, so we
|
1679 |
+
// adjust it accordingly. If not using timezone option this won't matter..
|
1680 |
+
// If a timezone is different in tp, keep the timezone as is
|
1681 |
+
if (tp_inst && tp_date) {
|
1682 |
+
// look out for DST if tz wasn't specified
|
1683 |
+
if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {
|
1684 |
+
tp_inst.timezone = tp_date.getTimezoneOffset() * -1;
|
1685 |
+
}
|
1686 |
+
date = $.timepicker.timezoneAdjust(date, tp_inst.timezone);
|
1687 |
+
tp_date = $.timepicker.timezoneAdjust(tp_date, tp_inst.timezone);
|
1688 |
+
}
|
1689 |
+
|
1690 |
+
this._updateDatepicker(inst);
|
1691 |
+
this._base_setDateDatepicker.apply(this, arguments);
|
1692 |
+
this._setTimeDatepicker(target, tp_date, true);
|
1693 |
+
};
|
1694 |
+
|
1695 |
+
/*
|
1696 |
+
* override getDate() to allow getting time too within Date object
|
1697 |
+
*/
|
1698 |
+
$.datepicker._base_getDateDatepicker = $.datepicker._getDateDatepicker;
|
1699 |
+
$.datepicker._getDateDatepicker = function (target, noDefault) {
|
1700 |
+
var inst = this._getInst(target);
|
1701 |
+
if (!inst) {
|
1702 |
+
return;
|
1703 |
+
}
|
1704 |
+
|
1705 |
+
var tp_inst = this._get(inst, 'timepicker');
|
1706 |
+
|
1707 |
+
if (tp_inst) {
|
1708 |
+
// if it hasn't yet been defined, grab from field
|
1709 |
+
if (inst.lastVal === undefined) {
|
1710 |
+
this._setDateFromField(inst, noDefault);
|
1711 |
+
}
|
1712 |
+
|
1713 |
+
var date = this._getDate(inst);
|
1714 |
+
var currDT = $.trim((tp_inst.$altInput && tp_inst._defaults.altFieldTimeOnly) ? tp_inst.$input.val() + ' ' + tp_inst.$altInput.val() : tp_inst.$input.val());
|
1715 |
+
if (date && tp_inst._parseTime(currDT, !inst.settings.timeOnly)) {
|
1716 |
+
date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);
|
1717 |
+
date.setMicroseconds(tp_inst.microsec);
|
1718 |
+
|
1719 |
+
// This is important if you are using the timezone option, javascript's Date
|
1720 |
+
// object will only return the timezone offset for the current locale, so we
|
1721 |
+
// adjust it accordingly. If not using timezone option this won't matter..
|
1722 |
+
if (tp_inst.timezone != null) {
|
1723 |
+
// look out for DST if tz wasn't specified
|
1724 |
+
if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {
|
1725 |
+
tp_inst.timezone = date.getTimezoneOffset() * -1;
|
1726 |
+
}
|
1727 |
+
date = $.timepicker.timezoneAdjust(date, tp_inst.timezone);
|
1728 |
+
}
|
1729 |
+
}
|
1730 |
+
return date;
|
1731 |
+
}
|
1732 |
+
return this._base_getDateDatepicker(target, noDefault);
|
1733 |
+
};
|
1734 |
+
|
1735 |
+
/*
|
1736 |
+
* override parseDate() because UI 1.8.14 throws an error about "Extra characters"
|
1737 |
+
* An option in datapicker to ignore extra format characters would be nicer.
|
1738 |
+
*/
|
1739 |
+
$.datepicker._base_parseDate = $.datepicker.parseDate;
|
1740 |
+
$.datepicker.parseDate = function (format, value, settings) {
|
1741 |
+
var date;
|
1742 |
+
try {
|
1743 |
+
date = this._base_parseDate(format, value, settings);
|
1744 |
+
} catch (err) {
|
1745 |
+
// Hack! The error message ends with a colon, a space, and
|
1746 |
+
// the "extra" characters. We rely on that instead of
|
1747 |
+
// attempting to perfectly reproduce the parsing algorithm.
|
1748 |
+
if (err.indexOf(":") >= 0) {
|
1749 |
+
date = this._base_parseDate(format, value.substring(0, value.length - (err.length - err.indexOf(':') - 2)), settings);
|
1750 |
+
$.timepicker.log("Error parsing the date string: " + err + "\ndate string = " + value + "\ndate format = " + format);
|
1751 |
+
} else {
|
1752 |
+
throw err;
|
1753 |
+
}
|
1754 |
+
}
|
1755 |
+
return date;
|
1756 |
+
};
|
1757 |
+
|
1758 |
+
/*
|
1759 |
+
* override formatDate to set date with time to the input
|
1760 |
+
*/
|
1761 |
+
$.datepicker._base_formatDate = $.datepicker._formatDate;
|
1762 |
+
$.datepicker._formatDate = function (inst, day, month, year) {
|
1763 |
+
var tp_inst = this._get(inst, 'timepicker');
|
1764 |
+
if (tp_inst) {
|
1765 |
+
tp_inst._updateDateTime(inst);
|
1766 |
+
return tp_inst.$input.val();
|
1767 |
+
}
|
1768 |
+
return this._base_formatDate(inst);
|
1769 |
+
};
|
1770 |
+
|
1771 |
+
/*
|
1772 |
+
* override options setter to add time to maxDate(Time) and minDate(Time). MaxDate
|
1773 |
+
*/
|
1774 |
+
$.datepicker._base_optionDatepicker = $.datepicker._optionDatepicker;
|
1775 |
+
$.datepicker._optionDatepicker = function (target, name, value) {
|
1776 |
+
var inst = this._getInst(target),
|
1777 |
+
name_clone;
|
1778 |
+
if (!inst) {
|
1779 |
+
return null;
|
1780 |
+
}
|
1781 |
+
|
1782 |
+
var tp_inst = this._get(inst, 'timepicker');
|
1783 |
+
if (tp_inst) {
|
1784 |
+
var min = null,
|
1785 |
+
max = null,
|
1786 |
+
onselect = null,
|
1787 |
+
overrides = tp_inst._defaults.evnts,
|
1788 |
+
fns = {},
|
1789 |
+
prop,
|
1790 |
+
ret,
|
1791 |
+
oldVal,
|
1792 |
+
$target;
|
1793 |
+
if (typeof name === 'string') { // if min/max was set with the string
|
1794 |
+
if (name === 'minDate' || name === 'minDateTime') {
|
1795 |
+
min = value;
|
1796 |
+
} else if (name === 'maxDate' || name === 'maxDateTime') {
|
1797 |
+
max = value;
|
1798 |
+
} else if (name === 'onSelect') {
|
1799 |
+
onselect = value;
|
1800 |
+
} else if (overrides.hasOwnProperty(name)) {
|
1801 |
+
if (typeof (value) === 'undefined') {
|
1802 |
+
return overrides[name];
|
1803 |
+
}
|
1804 |
+
fns[name] = value;
|
1805 |
+
name_clone = {}; //empty results in exiting function after overrides updated
|
1806 |
+
}
|
1807 |
+
} else if (typeof name === 'object') { //if min/max was set with the JSON
|
1808 |
+
if (name.minDate) {
|
1809 |
+
min = name.minDate;
|
1810 |
+
} else if (name.minDateTime) {
|
1811 |
+
min = name.minDateTime;
|
1812 |
+
} else if (name.maxDate) {
|
1813 |
+
max = name.maxDate;
|
1814 |
+
} else if (name.maxDateTime) {
|
1815 |
+
max = name.maxDateTime;
|
1816 |
+
}
|
1817 |
+
for (prop in overrides) {
|
1818 |
+
if (overrides.hasOwnProperty(prop) && name[prop]) {
|
1819 |
+
fns[prop] = name[prop];
|
1820 |
+
}
|
1821 |
+
}
|
1822 |
+
}
|
1823 |
+
for (prop in fns) {
|
1824 |
+
if (fns.hasOwnProperty(prop)) {
|
1825 |
+
overrides[prop] = fns[prop];
|
1826 |
+
if (!name_clone) { name_clone = $.extend({}, name); }
|
1827 |
+
delete name_clone[prop];
|
1828 |
+
}
|
1829 |
+
}
|
1830 |
+
if (name_clone && isEmptyObject(name_clone)) { return; }
|
1831 |
+
if (min) { //if min was set
|
1832 |
+
if (min === 0) {
|
1833 |
+
min = new Date();
|
1834 |
+
} else {
|
1835 |
+
min = new Date(min);
|
1836 |
+
}
|
1837 |
+
tp_inst._defaults.minDate = min;
|
1838 |
+
tp_inst._defaults.minDateTime = min;
|
1839 |
+
} else if (max) { //if max was set
|
1840 |
+
if (max === 0) {
|
1841 |
+
max = new Date();
|
1842 |
+
} else {
|
1843 |
+
max = new Date(max);
|
1844 |
+
}
|
1845 |
+
tp_inst._defaults.maxDate = max;
|
1846 |
+
tp_inst._defaults.maxDateTime = max;
|
1847 |
+
} else if (onselect) {
|
1848 |
+
tp_inst._defaults.onSelect = onselect;
|
1849 |
+
}
|
1850 |
+
|
1851 |
+
// Datepicker will override our date when we call _base_optionDatepicker when
|
1852 |
+
// calling minDate/maxDate, so we will first grab the value, call
|
1853 |
+
// _base_optionDatepicker, then set our value back.
|
1854 |
+
if(min || max){
|
1855 |
+
$target = $(target);
|
1856 |
+
oldVal = $target.datetimepicker('getDate');
|
1857 |
+
ret = this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);
|
1858 |
+
$target.datetimepicker('setDate', oldVal);
|
1859 |
+
return ret;
|
1860 |
+
}
|
1861 |
+
}
|
1862 |
+
if (value === undefined) {
|
1863 |
+
return this._base_optionDatepicker.call($.datepicker, target, name);
|
1864 |
+
}
|
1865 |
+
return this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);
|
1866 |
+
};
|
1867 |
+
|
1868 |
+
/*
|
1869 |
+
* jQuery isEmptyObject does not check hasOwnProperty - if someone has added to the object prototype,
|
1870 |
+
* it will return false for all objects
|
1871 |
+
*/
|
1872 |
+
var isEmptyObject = function (obj) {
|
1873 |
+
var prop;
|
1874 |
+
for (prop in obj) {
|
1875 |
+
if (obj.hasOwnProperty(prop)) {
|
1876 |
+
return false;
|
1877 |
+
}
|
1878 |
+
}
|
1879 |
+
return true;
|
1880 |
+
};
|
1881 |
+
|
1882 |
+
/*
|
1883 |
+
* jQuery extend now ignores nulls!
|
1884 |
+
*/
|
1885 |
+
var extendRemove = function (target, props) {
|
1886 |
+
$.extend(target, props);
|
1887 |
+
for (var name in props) {
|
1888 |
+
if (props[name] === null || props[name] === undefined) {
|
1889 |
+
target[name] = props[name];
|
1890 |
+
}
|
1891 |
+
}
|
1892 |
+
return target;
|
1893 |
+
};
|
1894 |
+
|
1895 |
+
/*
|
1896 |
+
* Determine by the time format which units are supported
|
1897 |
+
* Returns an object of booleans for each unit
|
1898 |
+
*/
|
1899 |
+
var detectSupport = function (timeFormat) {
|
1900 |
+
var tf = timeFormat.replace(/'.*?'/g, '').toLowerCase(), // removes literals
|
1901 |
+
isIn = function (f, t) { // does the format contain the token?
|
1902 |
+
return f.indexOf(t) !== -1 ? true : false;
|
1903 |
+
};
|
1904 |
+
return {
|
1905 |
+
hour: isIn(tf, 'h'),
|
1906 |
+
minute: isIn(tf, 'm'),
|
1907 |
+
second: isIn(tf, 's'),
|
1908 |
+
millisec: isIn(tf, 'l'),
|
1909 |
+
microsec: isIn(tf, 'c'),
|
1910 |
+
timezone: isIn(tf, 'z'),
|
1911 |
+
ampm: isIn(tf, 't') && isIn(timeFormat, 'h'),
|
1912 |
+
iso8601: isIn(timeFormat, 'Z')
|
1913 |
+
};
|
1914 |
+
};
|
1915 |
+
|
1916 |
+
/*
|
1917 |
+
* Converts 24 hour format into 12 hour
|
1918 |
+
* Returns 12 hour without leading 0
|
1919 |
+
*/
|
1920 |
+
var convert24to12 = function (hour) {
|
1921 |
+
hour %= 12;
|
1922 |
+
|
1923 |
+
if (hour === 0) {
|
1924 |
+
hour = 12;
|
1925 |
+
}
|
1926 |
+
|
1927 |
+
return String(hour);
|
1928 |
+
};
|
1929 |
+
|
1930 |
+
var computeEffectiveSetting = function (settings, property) {
|
1931 |
+
return settings && settings[property] ? settings[property] : $.timepicker._defaults[property];
|
1932 |
+
};
|
1933 |
+
|
1934 |
+
/*
|
1935 |
+
* Splits datetime string into date and time substrings.
|
1936 |
+
* Throws exception when date can't be parsed
|
1937 |
+
* Returns {dateString: dateString, timeString: timeString}
|
1938 |
+
*/
|
1939 |
+
var splitDateTime = function (dateTimeString, timeSettings) {
|
1940 |
+
// The idea is to get the number separator occurrences in datetime and the time format requested (since time has
|
1941 |
+
// fewer unknowns, mostly numbers and am/pm). We will use the time pattern to split.
|
1942 |
+
var separator = computeEffectiveSetting(timeSettings, 'separator'),
|
1943 |
+
format = computeEffectiveSetting(timeSettings, 'timeFormat'),
|
1944 |
+
timeParts = format.split(separator), // how many occurrences of separator may be in our format?
|
1945 |
+
timePartsLen = timeParts.length,
|
1946 |
+
allParts = dateTimeString.split(separator),
|
1947 |
+
allPartsLen = allParts.length;
|
1948 |
+
|
1949 |
+
if (allPartsLen > 1) {
|
1950 |
+
return {
|
1951 |
+
dateString: allParts.splice(0, allPartsLen - timePartsLen).join(separator),
|
1952 |
+
timeString: allParts.splice(0, timePartsLen).join(separator)
|
1953 |
+
};
|
1954 |
+
}
|
1955 |
+
|
1956 |
+
return {
|
1957 |
+
dateString: dateTimeString,
|
1958 |
+
timeString: ''
|
1959 |
+
};
|
1960 |
+
};
|
1961 |
+
|
1962 |
+
/*
|
1963 |
+
* Internal function to parse datetime interval
|
1964 |
+
* Returns: {date: Date, timeObj: Object}, where
|
1965 |
+
* date - parsed date without time (type Date)
|
1966 |
+
* timeObj = {hour: , minute: , second: , millisec: , microsec: } - parsed time. Optional
|
1967 |
+
*/
|
1968 |
+
var parseDateTimeInternal = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {
|
1969 |
+
var date,
|
1970 |
+
parts,
|
1971 |
+
parsedTime;
|
1972 |
+
|
1973 |
+
parts = splitDateTime(dateTimeString, timeSettings);
|
1974 |
+
date = $.datepicker._base_parseDate(dateFormat, parts.dateString, dateSettings);
|
1975 |
+
|
1976 |
+
if (parts.timeString === '') {
|
1977 |
+
return {
|
1978 |
+
date: date
|
1979 |
+
};
|
1980 |
+
}
|
1981 |
+
|
1982 |
+
parsedTime = $.datepicker.parseTime(timeFormat, parts.timeString, timeSettings);
|
1983 |
+
|
1984 |
+
if (!parsedTime) {
|
1985 |
+
throw 'Wrong time format';
|
1986 |
+
}
|
1987 |
+
|
1988 |
+
return {
|
1989 |
+
date: date,
|
1990 |
+
timeObj: parsedTime
|
1991 |
+
};
|
1992 |
+
};
|
1993 |
+
|
1994 |
+
/*
|
1995 |
+
* Internal function to set timezone_select to the local timezone
|
1996 |
+
*/
|
1997 |
+
var selectLocalTimezone = function (tp_inst, date) {
|
1998 |
+
if (tp_inst && tp_inst.timezone_select) {
|
1999 |
+
var now = date || new Date();
|
2000 |
+
tp_inst.timezone_select.val(-now.getTimezoneOffset());
|
2001 |
+
}
|
2002 |
+
};
|
2003 |
+
|
2004 |
+
/*
|
2005 |
+
* Create a Singleton Instance
|
2006 |
+
*/
|
2007 |
+
$.timepicker = new Timepicker();
|
2008 |
+
|
2009 |
+
/**
|
2010 |
+
* Get the timezone offset as string from a date object (eg '+0530' for UTC+5.5)
|
2011 |
+
* @param {number} tzMinutes if not a number, less than -720 (-1200), or greater than 840 (+1400) this value is returned
|
2012 |
+
* @param {boolean} iso8601 if true formats in accordance to iso8601 "+12:45"
|
2013 |
+
* @return {string}
|
2014 |
+
*/
|
2015 |
+
$.timepicker.timezoneOffsetString = function (tzMinutes, iso8601) {
|
2016 |
+
if (isNaN(tzMinutes) || tzMinutes > 840 || tzMinutes < -720) {
|
2017 |
+
return tzMinutes;
|
2018 |
+
}
|
2019 |
+
|
2020 |
+
var off = tzMinutes,
|
2021 |
+
minutes = off % 60,
|
2022 |
+
hours = (off - minutes) / 60,
|
2023 |
+
iso = iso8601 ? ':' : '',
|
2024 |
+
tz = (off >= 0 ? '+' : '-') + ('0' + Math.abs(hours)).slice(-2) + iso + ('0' + Math.abs(minutes)).slice(-2);
|
2025 |
+
|
2026 |
+
if (tz === '+00:00') {
|
2027 |
+
return 'Z';
|
2028 |
+
}
|
2029 |
+
return tz;
|
2030 |
+
};
|
2031 |
+
|
2032 |
+
/**
|
2033 |
+
* Get the number in minutes that represents a timezone string
|
2034 |
+
* @param {string} tzString formatted like "+0500", "-1245", "Z"
|
2035 |
+
* @return {number} the offset minutes or the original string if it doesn't match expectations
|
2036 |
+
*/
|
2037 |
+
$.timepicker.timezoneOffsetNumber = function (tzString) {
|
2038 |
+
var normalized = tzString.toString().replace(':', ''); // excuse any iso8601, end up with "+1245"
|
2039 |
+
|
2040 |
+
if (normalized.toUpperCase() === 'Z') { // if iso8601 with Z, its 0 minute offset
|
2041 |
+
return 0;
|
2042 |
+
}
|
2043 |
+
|
2044 |
+
if (!/^(\-|\+)\d{4}$/.test(normalized)) { // possibly a user defined tz, so just give it back
|
2045 |
+
return tzString;
|
2046 |
+
}
|
2047 |
+
|
2048 |
+
return ((normalized.substr(0, 1) === '-' ? -1 : 1) * // plus or minus
|
2049 |
+
((parseInt(normalized.substr(1, 2), 10) * 60) + // hours (converted to minutes)
|
2050 |
+
parseInt(normalized.substr(3, 2), 10))); // minutes
|
2051 |
+
};
|
2052 |
+
|
2053 |
+
/**
|
2054 |
+
* No way to set timezone in js Date, so we must adjust the minutes to compensate. (think setDate, getDate)
|
2055 |
+
* @param {Date} date
|
2056 |
+
* @param {string} toTimezone formatted like "+0500", "-1245"
|
2057 |
+
* @return {Date}
|
2058 |
+
*/
|
2059 |
+
$.timepicker.timezoneAdjust = function (date, toTimezone) {
|
2060 |
+
var toTz = $.timepicker.timezoneOffsetNumber(toTimezone);
|
2061 |
+
if (!isNaN(toTz)) {
|
2062 |
+
date.setMinutes(date.getMinutes() + -date.getTimezoneOffset() - toTz);
|
2063 |
+
}
|
2064 |
+
return date;
|
2065 |
+
};
|
2066 |
+
|
2067 |
+
/**
|
2068 |
+
* Calls `timepicker()` on the `startTime` and `endTime` elements, and configures them to
|
2069 |
+
* enforce date range limits.
|
2070 |
+
* n.b. The input value must be correctly formatted (reformatting is not supported)
|
2071 |
+
* @param {Element} startTime
|
2072 |
+
* @param {Element} endTime
|
2073 |
+
* @param {Object} options Options for the timepicker() call
|
2074 |
+
* @return {jQuery}
|
2075 |
+
*/
|
2076 |
+
$.timepicker.timeRange = function (startTime, endTime, options) {
|
2077 |
+
return $.timepicker.handleRange('timepicker', startTime, endTime, options);
|
2078 |
+
};
|
2079 |
+
|
2080 |
+
/**
|
2081 |
+
* Calls `datetimepicker` on the `startTime` and `endTime` elements, and configures them to
|
2082 |
+
* enforce date range limits.
|
2083 |
+
* @param {Element} startTime
|
2084 |
+
* @param {Element} endTime
|
2085 |
+
* @param {Object} options Options for the `timepicker()` call. Also supports `reformat`,
|
2086 |
+
* a boolean value that can be used to reformat the input values to the `dateFormat`.
|
2087 |
+
* @param {string} method Can be used to specify the type of picker to be added
|
2088 |
+
* @return {jQuery}
|
2089 |
+
*/
|
2090 |
+
$.timepicker.datetimeRange = function (startTime, endTime, options) {
|
2091 |
+
$.timepicker.handleRange('datetimepicker', startTime, endTime, options);
|
2092 |
+
};
|
2093 |
+
|
2094 |
+
/**
|
2095 |
+
* Calls `datepicker` on the `startTime` and `endTime` elements, and configures them to
|
2096 |
+
* enforce date range limits.
|
2097 |
+
* @param {Element} startTime
|
2098 |
+
* @param {Element} endTime
|
2099 |
+
* @param {Object} options Options for the `timepicker()` call. Also supports `reformat`,
|
2100 |
+
* a boolean value that can be used to reformat the input values to the `dateFormat`.
|
2101 |
+
* @return {jQuery}
|
2102 |
+
*/
|
2103 |
+
$.timepicker.dateRange = function (startTime, endTime, options) {
|
2104 |
+
$.timepicker.handleRange('datepicker', startTime, endTime, options);
|
2105 |
+
};
|
2106 |
+
|
2107 |
+
/**
|
2108 |
+
* Calls `method` on the `startTime` and `endTime` elements, and configures them to
|
2109 |
+
* enforce date range limits.
|
2110 |
+
* @param {string} method Can be used to specify the type of picker to be added
|
2111 |
+
* @param {Element} startTime
|
2112 |
+
* @param {Element} endTime
|
2113 |
+
* @param {Object} options Options for the `timepicker()` call. Also supports `reformat`,
|
2114 |
+
* a boolean value that can be used to reformat the input values to the `dateFormat`.
|
2115 |
+
* @return {jQuery}
|
2116 |
+
*/
|
2117 |
+
$.timepicker.handleRange = function (method, startTime, endTime, options) {
|
2118 |
+
options = $.extend({}, {
|
2119 |
+
minInterval: 0, // min allowed interval in milliseconds
|
2120 |
+
maxInterval: 0, // max allowed interval in milliseconds
|
2121 |
+
start: {}, // options for start picker
|
2122 |
+
end: {} // options for end picker
|
2123 |
+
}, options);
|
2124 |
+
|
2125 |
+
// for the mean time this fixes an issue with calling getDate with timepicker()
|
2126 |
+
var timeOnly = false;
|
2127 |
+
if(method === 'timepicker'){
|
2128 |
+
timeOnly = true;
|
2129 |
+
method = 'datetimepicker';
|
2130 |
+
}
|
2131 |
+
|
2132 |
+
function checkDates(changed, other) {
|
2133 |
+
var startdt = startTime[method]('getDate'),
|
2134 |
+
enddt = endTime[method]('getDate'),
|
2135 |
+
changeddt = changed[method]('getDate');
|
2136 |
+
|
2137 |
+
if (startdt !== null) {
|
2138 |
+
var minDate = new Date(startdt.getTime()),
|
2139 |
+
maxDate = new Date(startdt.getTime());
|
2140 |
+
|
2141 |
+
minDate.setMilliseconds(minDate.getMilliseconds() + options.minInterval);
|
2142 |
+
maxDate.setMilliseconds(maxDate.getMilliseconds() + options.maxInterval);
|
2143 |
+
|
2144 |
+
if (options.minInterval > 0 && minDate > enddt) { // minInterval check
|
2145 |
+
endTime[method]('setDate', minDate);
|
2146 |
+
}
|
2147 |
+
else if (options.maxInterval > 0 && maxDate < enddt) { // max interval check
|
2148 |
+
endTime[method]('setDate', maxDate);
|
2149 |
+
}
|
2150 |
+
else if (startdt > enddt) {
|
2151 |
+
other[method]('setDate', changeddt);
|
2152 |
+
}
|
2153 |
+
}
|
2154 |
+
}
|
2155 |
+
|
2156 |
+
function selected(changed, other, option) {
|
2157 |
+
if (!changed.val()) {
|
2158 |
+
return;
|
2159 |
+
}
|
2160 |
+
var date = changed[method].call(changed, 'getDate');
|
2161 |
+
if (date !== null && options.minInterval > 0) {
|
2162 |
+
if (option === 'minDate') {
|
2163 |
+
date.setMilliseconds(date.getMilliseconds() + options.minInterval);
|
2164 |
+
}
|
2165 |
+
if (option === 'maxDate') {
|
2166 |
+
date.setMilliseconds(date.getMilliseconds() - options.minInterval);
|
2167 |
+
}
|
2168 |
+
}
|
2169 |
+
|
2170 |
+
if (date.getTime) {
|
2171 |
+
other[method].call(other, 'option', option, date);
|
2172 |
+
}
|
2173 |
+
}
|
2174 |
+
|
2175 |
+
$.fn[method].call(startTime, $.extend({
|
2176 |
+
timeOnly: timeOnly,
|
2177 |
+
onClose: function (dateText, inst) {
|
2178 |
+
checkDates($(this), endTime);
|
2179 |
+
},
|
2180 |
+
onSelect: function (selectedDateTime) {
|
2181 |
+
selected($(this), endTime, 'minDate');
|
2182 |
+
}
|
2183 |
+
}, options, options.start));
|
2184 |
+
$.fn[method].call(endTime, $.extend({
|
2185 |
+
timeOnly: timeOnly,
|
2186 |
+
onClose: function (dateText, inst) {
|
2187 |
+
checkDates($(this), startTime);
|
2188 |
+
},
|
2189 |
+
onSelect: function (selectedDateTime) {
|
2190 |
+
selected($(this), startTime, 'maxDate');
|
2191 |
+
}
|
2192 |
+
}, options, options.end));
|
2193 |
+
|
2194 |
+
checkDates(startTime, endTime);
|
2195 |
+
|
2196 |
+
selected(startTime, endTime, 'minDate');
|
2197 |
+
selected(endTime, startTime, 'maxDate');
|
2198 |
+
|
2199 |
+
return $([startTime.get(0), endTime.get(0)]);
|
2200 |
+
};
|
2201 |
+
|
2202 |
+
/**
|
2203 |
+
* Log error or data to the console during error or debugging
|
2204 |
+
* @param {Object} err pass any type object to log to the console during error or debugging
|
2205 |
+
* @return {void}
|
2206 |
+
*/
|
2207 |
+
$.timepicker.log = function () {
|
2208 |
+
if (window.console) {
|
2209 |
+
window.console.log.apply(window.console, Array.prototype.slice.call(arguments));
|
2210 |
+
}
|
2211 |
+
};
|
2212 |
+
|
2213 |
+
/*
|
2214 |
+
* Add util object to allow access to private methods for testability.
|
2215 |
+
*/
|
2216 |
+
$.timepicker._util = {
|
2217 |
+
_extendRemove: extendRemove,
|
2218 |
+
_isEmptyObject: isEmptyObject,
|
2219 |
+
_convert24to12: convert24to12,
|
2220 |
+
_detectSupport: detectSupport,
|
2221 |
+
_selectLocalTimezone: selectLocalTimezone,
|
2222 |
+
_computeEffectiveSetting: computeEffectiveSetting,
|
2223 |
+
_splitDateTime: splitDateTime,
|
2224 |
+
_parseDateTimeInternal: parseDateTimeInternal
|
2225 |
+
};
|
2226 |
+
|
2227 |
+
/*
|
2228 |
+
* Microsecond support
|
2229 |
+
*/
|
2230 |
+
if (!Date.prototype.getMicroseconds) {
|
2231 |
+
Date.prototype.microseconds = 0;
|
2232 |
+
Date.prototype.getMicroseconds = function () { return this.microseconds; };
|
2233 |
+
Date.prototype.setMicroseconds = function (m) {
|
2234 |
+
this.setMilliseconds(this.getMilliseconds() + Math.floor(m / 1000));
|
2235 |
+
this.microseconds = m % 1000;
|
2236 |
+
return this;
|
2237 |
+
};
|
2238 |
+
}
|
2239 |
+
|
2240 |
+
/*
|
2241 |
+
* Keep up with the version
|
2242 |
+
*/
|
2243 |
+
$.timepicker.version = "1.5.3";
|
2244 |
+
|
2245 |
+
}));
|
js/knockout-3.3.0.js
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Knockout JavaScript library v3.3.0
|
3 |
+
* (c) Steven Sanderson - http://knockoutjs.com/
|
4 |
+
* License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
5 |
+
*/
|
6 |
+
|
7 |
+
(function() {(function(p){var y=this||(0,eval)("this"),w=y.document,M=y.navigator,u=y.jQuery,E=y.JSON;(function(p){"function"===typeof define&&define.amd?define(["exports","require"],p):"function"===typeof require&&"object"===typeof exports&&"object"===typeof module?p(module.exports||exports):p(y.ko={})})(function(N,O){function J(a,d){return null===a||typeof a in Q?a===d:!1}function R(a,d){var c;return function(){c||(c=setTimeout(function(){c=p;a()},d))}}function S(a,d){var c;return function(){clearTimeout(c);
|
8 |
+
c=setTimeout(a,d)}}function K(b,d,c,e){a.d[b]={init:function(b,k,h,l,g){var m,x;a.w(function(){var q=a.a.c(k()),n=!c!==!q,r=!x;if(r||d||n!==m)r&&a.Z.oa()&&(x=a.a.la(a.e.childNodes(b),!0)),n?(r||a.e.T(b,a.a.la(x)),a.Ja(e?e(g,q):g,b)):a.e.ma(b),m=n},null,{q:b});return{controlsDescendantBindings:!0}}};a.h.ka[b]=!1;a.e.R[b]=!0}var a="undefined"!==typeof N?N:{};a.b=function(b,d){for(var c=b.split("."),e=a,f=0;f<c.length-1;f++)e=e[c[f]];e[c[c.length-1]]=d};a.D=function(a,d,c){a[d]=c};a.version="3.3.0";
|
9 |
+
a.b("version",a.version);a.a=function(){function b(a,b){for(var c in a)a.hasOwnProperty(c)&&b(c,a[c])}function d(a,b){if(b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}function c(a,b){a.__proto__=b;return a}function e(b,c,g,d){var e=b[c].match(m)||[];a.a.o(g.match(m),function(b){a.a.ga(e,b,d)});b[c]=e.join(" ")}var f={__proto__:[]}instanceof Array,k={},h={};k[M&&/Firefox\/2/i.test(M.userAgent)?"KeyboardEvent":"UIEvents"]=["keyup","keydown","keypress"];k.MouseEvents="click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave".split(" ");
|
10 |
+
b(k,function(a,b){if(b.length)for(var c=0,g=b.length;c<g;c++)h[b[c]]=a});var l={propertychange:!0},g=w&&function(){for(var a=3,b=w.createElement("div"),c=b.getElementsByTagName("i");b.innerHTML="\x3c!--[if gt IE "+ ++a+"]><i></i><![endif]--\x3e",c[0];);return 4<a?a:p}(),m=/\S+/g;return{Bb:["authenticity_token",/^__RequestVerificationToken(_.*)?$/],o:function(a,b){for(var c=0,g=a.length;c<g;c++)b(a[c],c)},m:function(a,b){if("function"==typeof Array.prototype.indexOf)return Array.prototype.indexOf.call(a,
|
11 |
+
b);for(var c=0,g=a.length;c<g;c++)if(a[c]===b)return c;return-1},vb:function(a,b,c){for(var g=0,d=a.length;g<d;g++)if(b.call(c,a[g],g))return a[g];return null},ya:function(b,c){var g=a.a.m(b,c);0<g?b.splice(g,1):0===g&&b.shift()},wb:function(b){b=b||[];for(var c=[],g=0,d=b.length;g<d;g++)0>a.a.m(c,b[g])&&c.push(b[g]);return c},Ka:function(a,b){a=a||[];for(var c=[],g=0,d=a.length;g<d;g++)c.push(b(a[g],g));return c},xa:function(a,b){a=a||[];for(var c=[],g=0,d=a.length;g<d;g++)b(a[g],g)&&c.push(a[g]);
|
12 |
+
return c},ia:function(a,b){if(b instanceof Array)a.push.apply(a,b);else for(var c=0,g=b.length;c<g;c++)a.push(b[c]);return a},ga:function(b,c,g){var d=a.a.m(a.a.cb(b),c);0>d?g&&b.push(c):g||b.splice(d,1)},za:f,extend:d,Fa:c,Ga:f?c:d,A:b,pa:function(a,b){if(!a)return a;var c={},g;for(g in a)a.hasOwnProperty(g)&&(c[g]=b(a[g],g,a));return c},Ra:function(b){for(;b.firstChild;)a.removeNode(b.firstChild)},Jb:function(b){b=a.a.O(b);for(var c=(b[0]&&b[0].ownerDocument||w).createElement("div"),g=0,d=b.length;g<
|
13 |
+
d;g++)c.appendChild(a.S(b[g]));return c},la:function(b,c){for(var g=0,d=b.length,e=[];g<d;g++){var m=b[g].cloneNode(!0);e.push(c?a.S(m):m)}return e},T:function(b,c){a.a.Ra(b);if(c)for(var g=0,d=c.length;g<d;g++)b.appendChild(c[g])},Qb:function(b,c){var g=b.nodeType?[b]:b;if(0<g.length){for(var d=g[0],e=d.parentNode,m=0,f=c.length;m<f;m++)e.insertBefore(c[m],d);m=0;for(f=g.length;m<f;m++)a.removeNode(g[m])}},na:function(a,b){if(a.length){for(b=8===b.nodeType&&b.parentNode||b;a.length&&a[0].parentNode!==
|
14 |
+
b;)a.splice(0,1);if(1<a.length){var c=a[0],g=a[a.length-1];for(a.length=0;c!==g;)if(a.push(c),c=c.nextSibling,!c)return;a.push(g)}}return a},Sb:function(a,b){7>g?a.setAttribute("selected",b):a.selected=b},ib:function(a){return null===a||a===p?"":a.trim?a.trim():a.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},Dc:function(a,b){a=a||"";return b.length>a.length?!1:a.substring(0,b.length)===b},jc:function(a,b){if(a===b)return!0;if(11===a.nodeType)return!1;if(b.contains)return b.contains(3===a.nodeType?
|
15 |
+
a.parentNode:a);if(b.compareDocumentPosition)return 16==(b.compareDocumentPosition(a)&16);for(;a&&a!=b;)a=a.parentNode;return!!a},Qa:function(b){return a.a.jc(b,b.ownerDocument.documentElement)},tb:function(b){return!!a.a.vb(b,a.a.Qa)},v:function(a){return a&&a.tagName&&a.tagName.toLowerCase()},n:function(b,c,d){var m=g&&l[c];if(!m&&u)u(b).bind(c,d);else if(m||"function"!=typeof b.addEventListener)if("undefined"!=typeof b.attachEvent){var e=function(a){d.call(b,a)},f="on"+c;b.attachEvent(f,e);a.a.C.fa(b,
|
16 |
+
function(){b.detachEvent(f,e)})}else throw Error("Browser doesn't support addEventListener or attachEvent");else b.addEventListener(c,d,!1)},qa:function(b,c){if(!b||!b.nodeType)throw Error("element must be a DOM node when calling triggerEvent");var g;"input"===a.a.v(b)&&b.type&&"click"==c.toLowerCase()?(g=b.type,g="checkbox"==g||"radio"==g):g=!1;if(u&&!g)u(b).trigger(c);else if("function"==typeof w.createEvent)if("function"==typeof b.dispatchEvent)g=w.createEvent(h[c]||"HTMLEvents"),g.initEvent(c,
|
17 |
+
!0,!0,y,0,0,0,0,0,!1,!1,!1,!1,0,b),b.dispatchEvent(g);else throw Error("The supplied element doesn't support dispatchEvent");else if(g&&b.click)b.click();else if("undefined"!=typeof b.fireEvent)b.fireEvent("on"+c);else throw Error("Browser doesn't support triggering events");},c:function(b){return a.F(b)?b():b},cb:function(b){return a.F(b)?b.B():b},Ia:function(b,c,g){var d;c&&("object"===typeof b.classList?(d=b.classList[g?"add":"remove"],a.a.o(c.match(m),function(a){d.call(b.classList,a)})):"string"===
|
18 |
+
typeof b.className.baseVal?e(b.className,"baseVal",c,g):e(b,"className",c,g))},Ha:function(b,c){var g=a.a.c(c);if(null===g||g===p)g="";var d=a.e.firstChild(b);!d||3!=d.nodeType||a.e.nextSibling(d)?a.e.T(b,[b.ownerDocument.createTextNode(g)]):d.data=g;a.a.mc(b)},Rb:function(a,b){a.name=b;if(7>=g)try{a.mergeAttributes(w.createElement("<input name='"+a.name+"'/>"),!1)}catch(c){}},mc:function(a){9<=g&&(a=1==a.nodeType?a:a.parentNode,a.style&&(a.style.zoom=a.style.zoom))},kc:function(a){if(g){var b=a.style.width;
|
19 |
+
a.style.width=0;a.style.width=b}},Bc:function(b,c){b=a.a.c(b);c=a.a.c(c);for(var g=[],d=b;d<=c;d++)g.push(d);return g},O:function(a){for(var b=[],c=0,g=a.length;c<g;c++)b.push(a[c]);return b},Hc:6===g,Ic:7===g,M:g,Db:function(b,c){for(var g=a.a.O(b.getElementsByTagName("input")).concat(a.a.O(b.getElementsByTagName("textarea"))),d="string"==typeof c?function(a){return a.name===c}:function(a){return c.test(a.name)},m=[],e=g.length-1;0<=e;e--)d(g[e])&&m.push(g[e]);return m},yc:function(b){return"string"==
|
20 |
+
typeof b&&(b=a.a.ib(b))?E&&E.parse?E.parse(b):(new Function("return "+b))():null},jb:function(b,c,g){if(!E||!E.stringify)throw Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");return E.stringify(a.a.c(b),c,g)},zc:function(c,g,d){d=d||{};var m=d.params||{},e=d.includeFields||this.Bb,f=c;if("object"==typeof c&&"form"===a.a.v(c))for(var f=c.action,
|
21 |
+
l=e.length-1;0<=l;l--)for(var k=a.a.Db(c,e[l]),h=k.length-1;0<=h;h--)m[k[h].name]=k[h].value;g=a.a.c(g);var s=w.createElement("form");s.style.display="none";s.action=f;s.method="post";for(var p in g)c=w.createElement("input"),c.type="hidden",c.name=p,c.value=a.a.jb(a.a.c(g[p])),s.appendChild(c);b(m,function(a,b){var c=w.createElement("input");c.type="hidden";c.name=a;c.value=b;s.appendChild(c)});w.body.appendChild(s);d.submitter?d.submitter(s):s.submit();setTimeout(function(){s.parentNode.removeChild(s)},
|
22 |
+
0)}}}();a.b("utils",a.a);a.b("utils.arrayForEach",a.a.o);a.b("utils.arrayFirst",a.a.vb);a.b("utils.arrayFilter",a.a.xa);a.b("utils.arrayGetDistinctValues",a.a.wb);a.b("utils.arrayIndexOf",a.a.m);a.b("utils.arrayMap",a.a.Ka);a.b("utils.arrayPushAll",a.a.ia);a.b("utils.arrayRemoveItem",a.a.ya);a.b("utils.extend",a.a.extend);a.b("utils.fieldsIncludedWithJsonPost",a.a.Bb);a.b("utils.getFormFields",a.a.Db);a.b("utils.peekObservable",a.a.cb);a.b("utils.postJson",a.a.zc);a.b("utils.parseJson",a.a.yc);a.b("utils.registerEventHandler",
|
23 |
+
a.a.n);a.b("utils.stringifyJson",a.a.jb);a.b("utils.range",a.a.Bc);a.b("utils.toggleDomNodeCssClass",a.a.Ia);a.b("utils.triggerEvent",a.a.qa);a.b("utils.unwrapObservable",a.a.c);a.b("utils.objectForEach",a.a.A);a.b("utils.addOrRemoveItem",a.a.ga);a.b("utils.setTextContent",a.a.Ha);a.b("unwrap",a.a.c);Function.prototype.bind||(Function.prototype.bind=function(a){var d=this;if(1===arguments.length)return function(){return d.apply(a,arguments)};var c=Array.prototype.slice.call(arguments,1);return function(){var e=
|
24 |
+
c.slice(0);e.push.apply(e,arguments);return d.apply(a,e)}});a.a.f=new function(){function a(b,k){var h=b[c];if(!h||"null"===h||!e[h]){if(!k)return p;h=b[c]="ko"+d++;e[h]={}}return e[h]}var d=0,c="__ko__"+(new Date).getTime(),e={};return{get:function(c,d){var e=a(c,!1);return e===p?p:e[d]},set:function(c,d,e){if(e!==p||a(c,!1)!==p)a(c,!0)[d]=e},clear:function(a){var b=a[c];return b?(delete e[b],a[c]=null,!0):!1},I:function(){return d++ +c}}};a.b("utils.domData",a.a.f);a.b("utils.domData.clear",a.a.f.clear);
|
25 |
+
a.a.C=new function(){function b(b,d){var e=a.a.f.get(b,c);e===p&&d&&(e=[],a.a.f.set(b,c,e));return e}function d(c){var e=b(c,!1);if(e)for(var e=e.slice(0),l=0;l<e.length;l++)e[l](c);a.a.f.clear(c);a.a.C.cleanExternalData(c);if(f[c.nodeType])for(e=c.firstChild;c=e;)e=c.nextSibling,8===c.nodeType&&d(c)}var c=a.a.f.I(),e={1:!0,8:!0,9:!0},f={1:!0,9:!0};return{fa:function(a,c){if("function"!=typeof c)throw Error("Callback must be a function");b(a,!0).push(c)},Pb:function(d,e){var f=b(d,!1);f&&(a.a.ya(f,
|
26 |
+
e),0==f.length&&a.a.f.set(d,c,p))},S:function(b){if(e[b.nodeType]&&(d(b),f[b.nodeType])){var c=[];a.a.ia(c,b.getElementsByTagName("*"));for(var l=0,g=c.length;l<g;l++)d(c[l])}return b},removeNode:function(b){a.S(b);b.parentNode&&b.parentNode.removeChild(b)},cleanExternalData:function(a){u&&"function"==typeof u.cleanData&&u.cleanData([a])}}};a.S=a.a.C.S;a.removeNode=a.a.C.removeNode;a.b("cleanNode",a.S);a.b("removeNode",a.removeNode);a.b("utils.domNodeDisposal",a.a.C);a.b("utils.domNodeDisposal.addDisposeCallback",
|
27 |
+
a.a.C.fa);a.b("utils.domNodeDisposal.removeDisposeCallback",a.a.C.Pb);(function(){a.a.ca=function(b,d){var c;if(u)if(u.parseHTML)c=u.parseHTML(b,d)||[];else{if((c=u.clean([b],d))&&c[0]){for(var e=c[0];e.parentNode&&11!==e.parentNode.nodeType;)e=e.parentNode;e.parentNode&&e.parentNode.removeChild(e)}}else{(e=d)||(e=w);c=e.parentWindow||e.defaultView||y;var f=a.a.ib(b).toLowerCase(),e=e.createElement("div"),f=f.match(/^<(thead|tbody|tfoot)/)&&[1,"<table>","</table>"]||!f.indexOf("<tr")&&[2,"<table><tbody>",
|
28 |
+
"</tbody></table>"]||(!f.indexOf("<td")||!f.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||[0,"",""],k="ignored<div>"+f[1]+b+f[2]+"</div>";for("function"==typeof c.innerShiv?e.appendChild(c.innerShiv(k)):e.innerHTML=k;f[0]--;)e=e.lastChild;c=a.a.O(e.lastChild.childNodes)}return c};a.a.gb=function(b,d){a.a.Ra(b);d=a.a.c(d);if(null!==d&&d!==p)if("string"!=typeof d&&(d=d.toString()),u)u(b).html(d);else for(var c=a.a.ca(d,b.ownerDocument),e=0;e<c.length;e++)b.appendChild(c[e])}})();
|
29 |
+
a.b("utils.parseHtmlFragment",a.a.ca);a.b("utils.setHtml",a.a.gb);a.H=function(){function b(c,d){if(c)if(8==c.nodeType){var f=a.H.Lb(c.nodeValue);null!=f&&d.push({ic:c,wc:f})}else if(1==c.nodeType)for(var f=0,k=c.childNodes,h=k.length;f<h;f++)b(k[f],d)}var d={};return{$a:function(a){if("function"!=typeof a)throw Error("You can only pass a function to ko.memoization.memoize()");var b=(4294967296*(1+Math.random())|0).toString(16).substring(1)+(4294967296*(1+Math.random())|0).toString(16).substring(1);
|
30 |
+
d[b]=a;return"\x3c!--[ko_memo:"+b+"]--\x3e"},Wb:function(a,b){var f=d[a];if(f===p)throw Error("Couldn't find any memo with ID "+a+". Perhaps it's already been unmemoized.");try{return f.apply(null,b||[]),!0}finally{delete d[a]}},Xb:function(c,d){var f=[];b(c,f);for(var k=0,h=f.length;k<h;k++){var l=f[k].ic,g=[l];d&&a.a.ia(g,d);a.H.Wb(f[k].wc,g);l.nodeValue="";l.parentNode&&l.parentNode.removeChild(l)}},Lb:function(a){return(a=a.match(/^\[ko_memo\:(.*?)\]$/))?a[1]:null}}}();a.b("memoization",a.H);
|
31 |
+
a.b("memoization.memoize",a.H.$a);a.b("memoization.unmemoize",a.H.Wb);a.b("memoization.parseMemoText",a.H.Lb);a.b("memoization.unmemoizeDomNodeAndDescendants",a.H.Xb);a.Sa={throttle:function(b,d){b.throttleEvaluation=d;var c=null;return a.j({read:b,write:function(a){clearTimeout(c);c=setTimeout(function(){b(a)},d)}})},rateLimit:function(a,d){var c,e,f;"number"==typeof d?c=d:(c=d.timeout,e=d.method);f="notifyWhenChangesStop"==e?S:R;a.Za(function(a){return f(a,c)})},notify:function(a,d){a.equalityComparer=
|
32 |
+
"always"==d?null:J}};var Q={undefined:1,"boolean":1,number:1,string:1};a.b("extenders",a.Sa);a.Ub=function(b,d,c){this.da=b;this.La=d;this.hc=c;this.Gb=!1;a.D(this,"dispose",this.p)};a.Ub.prototype.p=function(){this.Gb=!0;this.hc()};a.Q=function(){a.a.Ga(this,a.Q.fn);this.G={};this.rb=1};var z={U:function(b,d,c){var e=this;c=c||"change";var f=new a.Ub(e,d?b.bind(d):b,function(){a.a.ya(e.G[c],f);e.ua&&e.ua(c)});e.ja&&e.ja(c);e.G[c]||(e.G[c]=[]);e.G[c].push(f);return f},notifySubscribers:function(b,
|
33 |
+
d){d=d||"change";"change"===d&&this.Yb();if(this.Ba(d))try{a.k.xb();for(var c=this.G[d].slice(0),e=0,f;f=c[e];++e)f.Gb||f.La(b)}finally{a.k.end()}},Aa:function(){return this.rb},pc:function(a){return this.Aa()!==a},Yb:function(){++this.rb},Za:function(b){var d=this,c=a.F(d),e,f,k;d.ta||(d.ta=d.notifySubscribers,d.notifySubscribers=function(a,b){b&&"change"!==b?"beforeChange"===b?d.pb(a):d.ta(a,b):d.qb(a)});var h=b(function(){c&&k===d&&(k=d());e=!1;d.Wa(f,k)&&d.ta(f=k)});d.qb=function(a){e=!0;k=a;
|
34 |
+
h()};d.pb=function(a){e||(f=a,d.ta(a,"beforeChange"))}},Ba:function(a){return this.G[a]&&this.G[a].length},nc:function(b){if(b)return this.G[b]&&this.G[b].length||0;var d=0;a.a.A(this.G,function(a,b){d+=b.length});return d},Wa:function(a,d){return!this.equalityComparer||!this.equalityComparer(a,d)},extend:function(b){var d=this;b&&a.a.A(b,function(b,e){var f=a.Sa[b];"function"==typeof f&&(d=f(d,e)||d)});return d}};a.D(z,"subscribe",z.U);a.D(z,"extend",z.extend);a.D(z,"getSubscriptionsCount",z.nc);
|
35 |
+
a.a.za&&a.a.Fa(z,Function.prototype);a.Q.fn=z;a.Hb=function(a){return null!=a&&"function"==typeof a.U&&"function"==typeof a.notifySubscribers};a.b("subscribable",a.Q);a.b("isSubscribable",a.Hb);a.Z=a.k=function(){function b(a){c.push(e);e=a}function d(){e=c.pop()}var c=[],e,f=0;return{xb:b,end:d,Ob:function(b){if(e){if(!a.Hb(b))throw Error("Only subscribable things can act as dependencies");e.La(b,b.ac||(b.ac=++f))}},u:function(a,c,e){try{return b(),a.apply(c,e||[])}finally{d()}},oa:function(){if(e)return e.w.oa()},
|
36 |
+
Ca:function(){if(e)return e.Ca}}}();a.b("computedContext",a.Z);a.b("computedContext.getDependenciesCount",a.Z.oa);a.b("computedContext.isInitial",a.Z.Ca);a.b("computedContext.isSleeping",a.Z.Jc);a.b("ignoreDependencies",a.Gc=a.k.u);a.r=function(b){function d(){if(0<arguments.length)return d.Wa(c,arguments[0])&&(d.X(),c=arguments[0],d.W()),this;a.k.Ob(d);return c}var c=b;a.Q.call(d);a.a.Ga(d,a.r.fn);d.B=function(){return c};d.W=function(){d.notifySubscribers(c)};d.X=function(){d.notifySubscribers(c,
|
37 |
+
"beforeChange")};a.D(d,"peek",d.B);a.D(d,"valueHasMutated",d.W);a.D(d,"valueWillMutate",d.X);return d};a.r.fn={equalityComparer:J};var H=a.r.Ac="__ko_proto__";a.r.fn[H]=a.r;a.a.za&&a.a.Fa(a.r.fn,a.Q.fn);a.Ta=function(b,d){return null===b||b===p||b[H]===p?!1:b[H]===d?!0:a.Ta(b[H],d)};a.F=function(b){return a.Ta(b,a.r)};a.Da=function(b){return"function"==typeof b&&b[H]===a.r||"function"==typeof b&&b[H]===a.j&&b.qc?!0:!1};a.b("observable",a.r);a.b("isObservable",a.F);a.b("isWriteableObservable",a.Da);
|
38 |
+
a.b("isWritableObservable",a.Da);a.ba=function(b){b=b||[];if("object"!=typeof b||!("length"in b))throw Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");b=a.r(b);a.a.Ga(b,a.ba.fn);return b.extend({trackArrayChanges:!0})};a.ba.fn={remove:function(b){for(var d=this.B(),c=[],e="function"!=typeof b||a.F(b)?function(a){return a===b}:b,f=0;f<d.length;f++){var k=d[f];e(k)&&(0===c.length&&this.X(),c.push(k),d.splice(f,1),f--)}c.length&&this.W();return c},
|
39 |
+
removeAll:function(b){if(b===p){var d=this.B(),c=d.slice(0);this.X();d.splice(0,d.length);this.W();return c}return b?this.remove(function(c){return 0<=a.a.m(b,c)}):[]},destroy:function(b){var d=this.B(),c="function"!=typeof b||a.F(b)?function(a){return a===b}:b;this.X();for(var e=d.length-1;0<=e;e--)c(d[e])&&(d[e]._destroy=!0);this.W()},destroyAll:function(b){return b===p?this.destroy(function(){return!0}):b?this.destroy(function(d){return 0<=a.a.m(b,d)}):[]},indexOf:function(b){var d=this();return a.a.m(d,
|
40 |
+
b)},replace:function(a,d){var c=this.indexOf(a);0<=c&&(this.X(),this.B()[c]=d,this.W())}};a.a.o("pop push reverse shift sort splice unshift".split(" "),function(b){a.ba.fn[b]=function(){var a=this.B();this.X();this.yb(a,b,arguments);a=a[b].apply(a,arguments);this.W();return a}});a.a.o(["slice"],function(b){a.ba.fn[b]=function(){var a=this();return a[b].apply(a,arguments)}});a.a.za&&a.a.Fa(a.ba.fn,a.r.fn);a.b("observableArray",a.ba);a.Sa.trackArrayChanges=function(b){function d(){if(!c){c=!0;var g=
|
41 |
+
b.notifySubscribers;b.notifySubscribers=function(a,b){b&&"change"!==b||++k;return g.apply(this,arguments)};var d=[].concat(b.B()||[]);e=null;f=b.U(function(c){c=[].concat(c||[]);if(b.Ba("arrayChange")){var g;if(!e||1<k)e=a.a.Ma(d,c,{sparse:!0});g=e}d=c;e=null;k=0;g&&g.length&&b.notifySubscribers(g,"arrayChange")})}}if(!b.yb){var c=!1,e=null,f,k=0,h=b.ja,l=b.ua;b.ja=function(a){h&&h.call(b,a);"arrayChange"===a&&d()};b.ua=function(a){l&&l.call(b,a);"arrayChange"!==a||b.Ba("arrayChange")||(f.p(),c=!1)};
|
42 |
+
b.yb=function(b,d,f){function l(a,b,c){return h[h.length]={status:a,value:b,index:c}}if(c&&!k){var h=[],r=b.length,v=f.length,t=0;switch(d){case "push":t=r;case "unshift":for(d=0;d<v;d++)l("added",f[d],t+d);break;case "pop":t=r-1;case "shift":r&&l("deleted",b[t],t);break;case "splice":d=Math.min(Math.max(0,0>f[0]?r+f[0]:f[0]),r);for(var r=1===v?r:Math.min(d+(f[1]||0),r),v=d+v-2,t=Math.max(r,v),G=[],A=[],p=2;d<t;++d,++p)d<r&&A.push(l("deleted",b[d],d)),d<v&&G.push(l("added",f[p],d));a.a.Cb(A,G);break;
|
43 |
+
default:return}e=h}}}};a.w=a.j=function(b,d,c){function e(a,b,c){if(I&&b===g)throw Error("A 'pure' computed must not be called recursively");B[a]=c;c.sa=F++;c.ea=b.Aa()}function f(){var a,b;for(a in B)if(B.hasOwnProperty(a)&&(b=B[a],b.da.pc(b.ea)))return!0}function k(){!s&&B&&a.a.A(B,function(a,b){b.p&&b.p()});B=null;F=0;G=!0;s=r=!1}function h(){var a=g.throttleEvaluation;a&&0<=a?(clearTimeout(z),z=setTimeout(function(){l(!0)},a)):g.nb?g.nb():l(!0)}function l(b){if(!v&&!G){if(y&&y()){if(!t){w();return}}else t=
|
44 |
+
!1;v=!0;try{var c=B,m=F,f=I?p:!F;a.k.xb({La:function(a,b){G||(m&&c[b]?(e(b,a,c[b]),delete c[b],--m):B[b]||e(b,a,s?{da:a}:a.U(h)))},w:g,Ca:f});B={};F=0;try{var l=d?A.call(d):A()}finally{a.k.end(),m&&!s&&a.a.A(c,function(a,b){b.p&&b.p()}),r=!1}g.Wa(n,l)&&(s||q(n,"beforeChange"),n=l,s?g.Yb():b&&q(n));f&&q(n,"awake")}finally{v=!1}F||w()}}function g(){if(0<arguments.length){if("function"===typeof C)C.apply(d,arguments);else throw Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
|
45 |
+
return this}a.k.Ob(g);(r||s&&f())&&l();return n}function m(){(r&&!F||s&&f())&&l();return n}function x(){return r||0<F}function q(a,b){g.notifySubscribers(a,b)}var n,r=!0,v=!1,t=!1,G=!1,A=b,I=!1,s=!1;A&&"object"==typeof A?(c=A,A=c.read):(c=c||{},A||(A=c.read));if("function"!=typeof A)throw Error("Pass a function that returns the value of the ko.computed");var C=c.write,D=c.disposeWhenNodeIsRemoved||c.q||null,u=c.disposeWhen||c.Pa,y=u,w=k,B={},F=0,z=null;d||(d=c.owner);a.Q.call(g);a.a.Ga(g,a.j.fn);
|
46 |
+
g.B=m;g.oa=function(){return F};g.qc="function"===typeof C;g.p=function(){w()};g.$=x;var T=g.Za;g.Za=function(a){T.call(g,a);g.nb=function(){g.pb(n);r=!0;g.qb(g)}};c.pure?(s=I=!0,g.ja=function(b){if(!G&&s&&"change"==b){s=!1;if(r||f())B=null,F=0,r=!0,l();else{var c=[];a.a.A(B,function(a,b){c[b.sa]=a});a.a.o(c,function(a,b){var c=B[a],g=c.da.U(h);g.sa=b;g.ea=c.ea;B[a]=g})}G||q(n,"awake")}},g.ua=function(b){G||"change"!=b||g.Ba("change")||(a.a.A(B,function(a,b){b.p&&(B[a]={da:b.da,sa:b.sa,ea:b.ea},b.p())}),
|
47 |
+
s=!0,q(p,"asleep"))},g.bc=g.Aa,g.Aa=function(){s&&(r||f())&&l();return g.bc()}):c.deferEvaluation&&(g.ja=function(a){"change"!=a&&"beforeChange"!=a||m()});a.D(g,"peek",g.B);a.D(g,"dispose",g.p);a.D(g,"isActive",g.$);a.D(g,"getDependenciesCount",g.oa);D&&(t=!0,D.nodeType&&(y=function(){return!a.a.Qa(D)||u&&u()}));s||c.deferEvaluation||l();D&&x()&&D.nodeType&&(w=function(){a.a.C.Pb(D,w);k()},a.a.C.fa(D,w));return g};a.sc=function(b){return a.Ta(b,a.j)};z=a.r.Ac;a.j[z]=a.r;a.j.fn={equalityComparer:J};
|
48 |
+
a.j.fn[z]=a.j;a.a.za&&a.a.Fa(a.j.fn,a.Q.fn);a.b("dependentObservable",a.j);a.b("computed",a.j);a.b("isComputed",a.sc);a.Nb=function(b,d){if("function"===typeof b)return a.w(b,d,{pure:!0});b=a.a.extend({},b);b.pure=!0;return a.w(b,d)};a.b("pureComputed",a.Nb);(function(){function b(a,f,k){k=k||new c;a=f(a);if("object"!=typeof a||null===a||a===p||a instanceof Date||a instanceof String||a instanceof Number||a instanceof Boolean)return a;var h=a instanceof Array?[]:{};k.save(a,h);d(a,function(c){var g=
|
49 |
+
f(a[c]);switch(typeof g){case "boolean":case "number":case "string":case "function":h[c]=g;break;case "object":case "undefined":var d=k.get(g);h[c]=d!==p?d:b(g,f,k)}});return h}function d(a,b){if(a instanceof Array){for(var c=0;c<a.length;c++)b(c);"function"==typeof a.toJSON&&b("toJSON")}else for(c in a)b(c)}function c(){this.keys=[];this.mb=[]}a.Vb=function(c){if(0==arguments.length)throw Error("When calling ko.toJS, pass the object you want to convert.");return b(c,function(b){for(var c=0;a.F(b)&&
|
50 |
+
10>c;c++)b=b();return b})};a.toJSON=function(b,c,d){b=a.Vb(b);return a.a.jb(b,c,d)};c.prototype={save:function(b,c){var d=a.a.m(this.keys,b);0<=d?this.mb[d]=c:(this.keys.push(b),this.mb.push(c))},get:function(b){b=a.a.m(this.keys,b);return 0<=b?this.mb[b]:p}}})();a.b("toJS",a.Vb);a.b("toJSON",a.toJSON);(function(){a.i={s:function(b){switch(a.a.v(b)){case "option":return!0===b.__ko__hasDomDataOptionValue__?a.a.f.get(b,a.d.options.ab):7>=a.a.M?b.getAttributeNode("value")&&b.getAttributeNode("value").specified?
|
51 |
+
b.value:b.text:b.value;case "select":return 0<=b.selectedIndex?a.i.s(b.options[b.selectedIndex]):p;default:return b.value}},Y:function(b,d,c){switch(a.a.v(b)){case "option":switch(typeof d){case "string":a.a.f.set(b,a.d.options.ab,p);"__ko__hasDomDataOptionValue__"in b&&delete b.__ko__hasDomDataOptionValue__;b.value=d;break;default:a.a.f.set(b,a.d.options.ab,d),b.__ko__hasDomDataOptionValue__=!0,b.value="number"===typeof d?d:""}break;case "select":if(""===d||null===d)d=p;for(var e=-1,f=0,k=b.options.length,
|
52 |
+
h;f<k;++f)if(h=a.i.s(b.options[f]),h==d||""==h&&d===p){e=f;break}if(c||0<=e||d===p&&1<b.size)b.selectedIndex=e;break;default:if(null===d||d===p)d="";b.value=d}}}})();a.b("selectExtensions",a.i);a.b("selectExtensions.readValue",a.i.s);a.b("selectExtensions.writeValue",a.i.Y);a.h=function(){function b(b){b=a.a.ib(b);123===b.charCodeAt(0)&&(b=b.slice(1,-1));var c=[],d=b.match(e),x,h=[],n=0;if(d){d.push(",");for(var r=0,v;v=d[r];++r){var t=v.charCodeAt(0);if(44===t){if(0>=n){c.push(x&&h.length?{key:x,
|
53 |
+
value:h.join("")}:{unknown:x||h.join("")});x=n=0;h=[];continue}}else if(58===t){if(!n&&!x&&1===h.length){x=h.pop();continue}}else 47===t&&r&&1<v.length?(t=d[r-1].match(f))&&!k[t[0]]&&(b=b.substr(b.indexOf(v)+1),d=b.match(e),d.push(","),r=-1,v="/"):40===t||123===t||91===t?++n:41===t||125===t||93===t?--n:x||h.length||34!==t&&39!==t||(v=v.slice(1,-1));h.push(v)}}return c}var d=["true","false","null","undefined"],c=/^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i,e=RegExp("\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'|/(?:[^/\\\\]|\\\\.)*/w*|[^\\s:,/][^,\"'{}()/:[\\]]*[^\\s,\"'{}()/:[\\]]|[^\\s]",
|
54 |
+
"g"),f=/[\])"'A-Za-z0-9_$]+$/,k={"in":1,"return":1,"typeof":1},h={};return{ka:[],V:h,bb:b,Ea:function(e,g){function m(b,g){var e;if(!r){var l=a.getBindingHandler(b);if(l&&l.preprocess&&!(g=l.preprocess(g,b,m)))return;if(l=h[b])e=g,0<=a.a.m(d,e)?e=!1:(l=e.match(c),e=null===l?!1:l[1]?"Object("+l[1]+")"+l[2]:e),l=e;l&&k.push("'"+b+"':function(_z){"+e+"=_z}")}n&&(g="function(){return "+g+" }");f.push("'"+b+"':"+g)}g=g||{};var f=[],k=[],n=g.valueAccessors,r=g.bindingParams,v="string"===typeof e?b(e):e;
|
55 |
+
a.a.o(v,function(a){m(a.key||a.unknown,a.value)});k.length&&m("_ko_property_writers","{"+k.join(",")+" }");return f.join(",")},vc:function(a,b){for(var c=0;c<a.length;c++)if(a[c].key==b)return!0;return!1},ra:function(b,c,d,e,f){if(b&&a.F(b))!a.Da(b)||f&&b.B()===e||b(e);else if((b=c.get("_ko_property_writers"))&&b[d])b[d](e)}}}();a.b("expressionRewriting",a.h);a.b("expressionRewriting.bindingRewriteValidators",a.h.ka);a.b("expressionRewriting.parseObjectLiteral",a.h.bb);a.b("expressionRewriting.preProcessBindings",
|
56 |
+
a.h.Ea);a.b("expressionRewriting._twoWayBindings",a.h.V);a.b("jsonExpressionRewriting",a.h);a.b("jsonExpressionRewriting.insertPropertyAccessorsIntoJson",a.h.Ea);(function(){function b(a){return 8==a.nodeType&&k.test(f?a.text:a.nodeValue)}function d(a){return 8==a.nodeType&&h.test(f?a.text:a.nodeValue)}function c(a,c){for(var e=a,f=1,l=[];e=e.nextSibling;){if(d(e)&&(f--,0===f))return l;l.push(e);b(e)&&f++}if(!c)throw Error("Cannot find closing comment tag to match: "+a.nodeValue);return null}function e(a,
|
57 |
+
b){var d=c(a,b);return d?0<d.length?d[d.length-1].nextSibling:a.nextSibling:null}var f=w&&"\x3c!--test--\x3e"===w.createComment("test").text,k=f?/^\x3c!--\s*ko(?:\s+([\s\S]+))?\s*--\x3e$/:/^\s*ko(?:\s+([\s\S]+))?\s*$/,h=f?/^\x3c!--\s*\/ko\s*--\x3e$/:/^\s*\/ko\s*$/,l={ul:!0,ol:!0};a.e={R:{},childNodes:function(a){return b(a)?c(a):a.childNodes},ma:function(c){if(b(c)){c=a.e.childNodes(c);for(var d=0,e=c.length;d<e;d++)a.removeNode(c[d])}else a.a.Ra(c)},T:function(c,d){if(b(c)){a.e.ma(c);for(var e=c.nextSibling,
|
58 |
+
f=0,l=d.length;f<l;f++)e.parentNode.insertBefore(d[f],e)}else a.a.T(c,d)},Mb:function(a,c){b(a)?a.parentNode.insertBefore(c,a.nextSibling):a.firstChild?a.insertBefore(c,a.firstChild):a.appendChild(c)},Fb:function(c,d,e){e?b(c)?c.parentNode.insertBefore(d,e.nextSibling):e.nextSibling?c.insertBefore(d,e.nextSibling):c.appendChild(d):a.e.Mb(c,d)},firstChild:function(a){return b(a)?!a.nextSibling||d(a.nextSibling)?null:a.nextSibling:a.firstChild},nextSibling:function(a){b(a)&&(a=e(a));return a.nextSibling&&
|
59 |
+
d(a.nextSibling)?null:a.nextSibling},oc:b,Fc:function(a){return(a=(f?a.text:a.nodeValue).match(k))?a[1]:null},Kb:function(c){if(l[a.a.v(c)]){var m=c.firstChild;if(m){do if(1===m.nodeType){var f;f=m.firstChild;var h=null;if(f){do if(h)h.push(f);else if(b(f)){var k=e(f,!0);k?f=k:h=[f]}else d(f)&&(h=[f]);while(f=f.nextSibling)}if(f=h)for(h=m.nextSibling,k=0;k<f.length;k++)h?c.insertBefore(f[k],h):c.appendChild(f[k])}while(m=m.nextSibling)}}}}})();a.b("virtualElements",a.e);a.b("virtualElements.allowedBindings",
|
60 |
+
a.e.R);a.b("virtualElements.emptyNode",a.e.ma);a.b("virtualElements.insertAfter",a.e.Fb);a.b("virtualElements.prepend",a.e.Mb);a.b("virtualElements.setDomNodeChildren",a.e.T);(function(){a.L=function(){this.ec={}};a.a.extend(a.L.prototype,{nodeHasBindings:function(b){switch(b.nodeType){case 1:return null!=b.getAttribute("data-bind")||a.g.getComponentNameForNode(b);case 8:return a.e.oc(b);default:return!1}},getBindings:function(b,d){var c=this.getBindingsString(b,d),c=c?this.parseBindingsString(c,
|
61 |
+
d,b):null;return a.g.sb(c,b,d,!1)},getBindingAccessors:function(b,d){var c=this.getBindingsString(b,d),c=c?this.parseBindingsString(c,d,b,{valueAccessors:!0}):null;return a.g.sb(c,b,d,!0)},getBindingsString:function(b){switch(b.nodeType){case 1:return b.getAttribute("data-bind");case 8:return a.e.Fc(b);default:return null}},parseBindingsString:function(b,d,c,e){try{var f=this.ec,k=b+(e&&e.valueAccessors||""),h;if(!(h=f[k])){var l,g="with($context){with($data||{}){return{"+a.h.Ea(b,e)+"}}}";l=new Function("$context",
|
62 |
+
"$element",g);h=f[k]=l}return h(d,c)}catch(m){throw m.message="Unable to parse bindings.\nBindings value: "+b+"\nMessage: "+m.message,m;}}});a.L.instance=new a.L})();a.b("bindingProvider",a.L);(function(){function b(a){return function(){return a}}function d(a){return a()}function c(b){return a.a.pa(a.k.u(b),function(a,c){return function(){return b()[c]}})}function e(d,g,e){return"function"===typeof d?c(d.bind(null,g,e)):a.a.pa(d,b)}function f(a,b){return c(this.getBindings.bind(this,a,b))}function k(b,
|
63 |
+
c,d){var g,e=a.e.firstChild(c),f=a.L.instance,m=f.preprocessNode;if(m){for(;g=e;)e=a.e.nextSibling(g),m.call(f,g);e=a.e.firstChild(c)}for(;g=e;)e=a.e.nextSibling(g),h(b,g,d)}function h(b,c,d){var e=!0,f=1===c.nodeType;f&&a.e.Kb(c);if(f&&d||a.L.instance.nodeHasBindings(c))e=g(c,null,b,d).shouldBindDescendants;e&&!x[a.a.v(c)]&&k(b,c,!f)}function l(b){var c=[],d={},g=[];a.a.A(b,function I(e){if(!d[e]){var f=a.getBindingHandler(e);f&&(f.after&&(g.push(e),a.a.o(f.after,function(c){if(b[c]){if(-1!==a.a.m(g,
|
64 |
+
c))throw Error("Cannot combine the following bindings, because they have a cyclic dependency: "+g.join(", "));I(c)}}),g.length--),c.push({key:e,Eb:f}));d[e]=!0}});return c}function g(b,c,g,e){var m=a.a.f.get(b,q);if(!c){if(m)throw Error("You cannot apply bindings multiple times to the same element.");a.a.f.set(b,q,!0)}!m&&e&&a.Tb(b,g);var h;if(c&&"function"!==typeof c)h=c;else{var k=a.L.instance,x=k.getBindingAccessors||f,n=a.j(function(){(h=c?c(g,b):x.call(k,b,g))&&g.K&&g.K();return h},null,{q:b});
|
65 |
+
h&&n.$()||(n=null)}var u;if(h){var w=n?function(a){return function(){return d(n()[a])}}:function(a){return h[a]},y=function(){return a.a.pa(n?n():h,d)};y.get=function(a){return h[a]&&d(w(a))};y.has=function(a){return a in h};e=l(h);a.a.o(e,function(c){var d=c.Eb.init,e=c.Eb.update,f=c.key;if(8===b.nodeType&&!a.e.R[f])throw Error("The binding '"+f+"' cannot be used with virtual elements");try{"function"==typeof d&&a.k.u(function(){var a=d(b,w(f),y,g.$data,g);if(a&&a.controlsDescendantBindings){if(u!==
|
66 |
+
p)throw Error("Multiple bindings ("+u+" and "+f+") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");u=f}}),"function"==typeof e&&a.j(function(){e(b,w(f),y,g.$data,g)},null,{q:b})}catch(m){throw m.message='Unable to process binding "'+f+": "+h[f]+'"\nMessage: '+m.message,m;}})}return{shouldBindDescendants:u===p}}function m(b){return b&&b instanceof a.N?b:new a.N(b)}a.d={};var x={script:!0,textarea:!0};a.getBindingHandler=function(b){return a.d[b]};
|
67 |
+
a.N=function(b,c,d,g){var e=this,f="function"==typeof b&&!a.F(b),m,l=a.j(function(){var m=f?b():b,h=a.a.c(m);c?(c.K&&c.K(),a.a.extend(e,c),l&&(e.K=l)):(e.$parents=[],e.$root=h,e.ko=a);e.$rawData=m;e.$data=h;d&&(e[d]=h);g&&g(e,c,h);return e.$data},null,{Pa:function(){return m&&!a.a.tb(m)},q:!0});l.$()&&(e.K=l,l.equalityComparer=null,m=[],l.Zb=function(b){m.push(b);a.a.C.fa(b,function(b){a.a.ya(m,b);m.length||(l.p(),e.K=l=p)})})};a.N.prototype.createChildContext=function(b,c,d){return new a.N(b,this,
|
68 |
+
c,function(a,b){a.$parentContext=b;a.$parent=b.$data;a.$parents=(b.$parents||[]).slice(0);a.$parents.unshift(a.$parent);d&&d(a)})};a.N.prototype.extend=function(b){return new a.N(this.K||this.$data,this,null,function(c,d){c.$rawData=d.$rawData;a.a.extend(c,"function"==typeof b?b():b)})};var q=a.a.f.I(),n=a.a.f.I();a.Tb=function(b,c){if(2==arguments.length)a.a.f.set(b,n,c),c.K&&c.K.Zb(b);else return a.a.f.get(b,n)};a.va=function(b,c,d){1===b.nodeType&&a.e.Kb(b);return g(b,c,m(d),!0)};a.cc=function(b,
|
69 |
+
c,d){d=m(d);return a.va(b,e(c,d,b),d)};a.Ja=function(a,b){1!==b.nodeType&&8!==b.nodeType||k(m(a),b,!0)};a.ub=function(a,b){!u&&y.jQuery&&(u=y.jQuery);if(b&&1!==b.nodeType&&8!==b.nodeType)throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");b=b||y.document.body;h(m(a),b,!0)};a.Oa=function(b){switch(b.nodeType){case 1:case 8:var c=a.Tb(b);if(c)return c;if(b.parentNode)return a.Oa(b.parentNode)}return p};a.gc=function(b){return(b=a.Oa(b))?
|
70 |
+
b.$data:p};a.b("bindingHandlers",a.d);a.b("applyBindings",a.ub);a.b("applyBindingsToDescendants",a.Ja);a.b("applyBindingAccessorsToNode",a.va);a.b("applyBindingsToNode",a.cc);a.b("contextFor",a.Oa);a.b("dataFor",a.gc)})();(function(b){function d(d,e){var g=f.hasOwnProperty(d)?f[d]:b,m;g?g.U(e):(g=f[d]=new a.Q,g.U(e),c(d,function(a,b){var c=!(!b||!b.synchronous);k[d]={definition:a,tc:c};delete f[d];m||c?g.notifySubscribers(a):setTimeout(function(){g.notifySubscribers(a)},0)}),m=!0)}function c(a,b){e("getConfig",
|
71 |
+
[a],function(c){c?e("loadComponent",[a,c],function(a){b(a,c)}):b(null,null)})}function e(c,d,g,f){f||(f=a.g.loaders.slice(0));var k=f.shift();if(k){var q=k[c];if(q){var n=!1;if(q.apply(k,d.concat(function(a){n?g(null):null!==a?g(a):e(c,d,g,f)}))!==b&&(n=!0,!k.suppressLoaderExceptions))throw Error("Component loaders must supply values by invoking the callback, not by returning values synchronously.");}else e(c,d,g,f)}else g(null)}var f={},k={};a.g={get:function(c,e){var g=k.hasOwnProperty(c)?k[c]:
|
72 |
+
b;g?g.tc?a.k.u(function(){e(g.definition)}):setTimeout(function(){e(g.definition)},0):d(c,e)},zb:function(a){delete k[a]},ob:e};a.g.loaders=[];a.b("components",a.g);a.b("components.get",a.g.get);a.b("components.clearCachedDefinition",a.g.zb)})();(function(){function b(b,c,d,e){function k(){0===--v&&e(h)}var h={},v=2,t=d.template;d=d.viewModel;t?f(c,t,function(c){a.g.ob("loadTemplate",[b,c],function(a){h.template=a;k()})}):k();d?f(c,d,function(c){a.g.ob("loadViewModel",[b,c],function(a){h[l]=a;k()})}):
|
73 |
+
k()}function d(a,b,c){if("function"===typeof b)c(function(a){return new b(a)});else if("function"===typeof b[l])c(b[l]);else if("instance"in b){var e=b.instance;c(function(){return e})}else"viewModel"in b?d(a,b.viewModel,c):a("Unknown viewModel value: "+b)}function c(b){switch(a.a.v(b)){case "script":return a.a.ca(b.text);case "textarea":return a.a.ca(b.value);case "template":if(e(b.content))return a.a.la(b.content.childNodes)}return a.a.la(b.childNodes)}function e(a){return y.DocumentFragment?a instanceof
|
74 |
+
DocumentFragment:a&&11===a.nodeType}function f(a,b,c){"string"===typeof b.require?O||y.require?(O||y.require)([b.require],c):a("Uses require, but no AMD loader is present"):c(b)}function k(a){return function(b){throw Error("Component '"+a+"': "+b);}}var h={};a.g.register=function(b,c){if(!c)throw Error("Invalid configuration for "+b);if(a.g.Xa(b))throw Error("Component "+b+" is already registered");h[b]=c};a.g.Xa=function(a){return a in h};a.g.Ec=function(b){delete h[b];a.g.zb(b)};a.g.Ab={getConfig:function(a,
|
75 |
+
b){b(h.hasOwnProperty(a)?h[a]:null)},loadComponent:function(a,c,d){var e=k(a);f(e,c,function(c){b(a,e,c,d)})},loadTemplate:function(b,d,f){b=k(b);if("string"===typeof d)f(a.a.ca(d));else if(d instanceof Array)f(d);else if(e(d))f(a.a.O(d.childNodes));else if(d.element)if(d=d.element,y.HTMLElement?d instanceof HTMLElement:d&&d.tagName&&1===d.nodeType)f(c(d));else if("string"===typeof d){var l=w.getElementById(d);l?f(c(l)):b("Cannot find element with ID "+d)}else b("Unknown element type: "+d);else b("Unknown template value: "+
|
76 |
+
d)},loadViewModel:function(a,b,c){d(k(a),b,c)}};var l="createViewModel";a.b("components.register",a.g.register);a.b("components.isRegistered",a.g.Xa);a.b("components.unregister",a.g.Ec);a.b("components.defaultLoader",a.g.Ab);a.g.loaders.push(a.g.Ab);a.g.$b=h})();(function(){function b(b,e){var f=b.getAttribute("params");if(f){var f=d.parseBindingsString(f,e,b,{valueAccessors:!0,bindingParams:!0}),f=a.a.pa(f,function(d){return a.w(d,null,{q:b})}),k=a.a.pa(f,function(d){var e=d.B();return d.$()?a.w({read:function(){return a.a.c(d())},
|
77 |
+
write:a.Da(e)&&function(a){d()(a)},q:b}):e});k.hasOwnProperty("$raw")||(k.$raw=f);return k}return{$raw:{}}}a.g.getComponentNameForNode=function(b){b=a.a.v(b);return a.g.Xa(b)&&b};a.g.sb=function(c,d,f,k){if(1===d.nodeType){var h=a.g.getComponentNameForNode(d);if(h){c=c||{};if(c.component)throw Error('Cannot use the "component" binding on a custom element matching a component');var l={name:h,params:b(d,f)};c.component=k?function(){return l}:l}}return c};var d=new a.L;9>a.a.M&&(a.g.register=function(a){return function(b){w.createElement(b);
|
78 |
+
return a.apply(this,arguments)}}(a.g.register),w.createDocumentFragment=function(b){return function(){var d=b(),f=a.g.$b,k;for(k in f)f.hasOwnProperty(k)&&d.createElement(k);return d}}(w.createDocumentFragment))})();(function(b){function d(b,c,d){c=c.template;if(!c)throw Error("Component '"+b+"' has no template");b=a.a.la(c);a.e.T(d,b)}function c(a,b,c,d){var e=a.createViewModel;return e?e.call(a,d,{element:b,templateNodes:c}):d}var e=0;a.d.component={init:function(f,k,h,l,g){function m(){var a=x&&
|
79 |
+
x.dispose;"function"===typeof a&&a.call(x);q=null}var x,q,n=a.a.O(a.e.childNodes(f));a.a.C.fa(f,m);a.w(function(){var l=a.a.c(k()),h,t;"string"===typeof l?h=l:(h=a.a.c(l.name),t=a.a.c(l.params));if(!h)throw Error("No component name specified");var p=q=++e;a.g.get(h,function(e){if(q===p){m();if(!e)throw Error("Unknown component '"+h+"'");d(h,e,f);var l=c(e,f,n,t);e=g.createChildContext(l,b,function(a){a.$component=l;a.$componentTemplateNodes=n});x=l;a.Ja(e,f)}})},null,{q:f});return{controlsDescendantBindings:!0}}};
|
80 |
+
a.e.R.component=!0})();var P={"class":"className","for":"htmlFor"};a.d.attr={update:function(b,d){var c=a.a.c(d())||{};a.a.A(c,function(c,d){d=a.a.c(d);var k=!1===d||null===d||d===p;k&&b.removeAttribute(c);8>=a.a.M&&c in P?(c=P[c],k?b.removeAttribute(c):b[c]=d):k||b.setAttribute(c,d.toString());"name"===c&&a.a.Rb(b,k?"":d.toString())})}};(function(){a.d.checked={after:["value","attr"],init:function(b,d,c){function e(){var e=b.checked,f=x?k():e;if(!a.Z.Ca()&&(!l||e)){var h=a.k.u(d);g?m!==f?(e&&(a.a.ga(h,
|
81 |
+
f,!0),a.a.ga(h,m,!1)),m=f):a.a.ga(h,f,e):a.h.ra(h,c,"checked",f,!0)}}function f(){var c=a.a.c(d());b.checked=g?0<=a.a.m(c,k()):h?c:k()===c}var k=a.Nb(function(){return c.has("checkedValue")?a.a.c(c.get("checkedValue")):c.has("value")?a.a.c(c.get("value")):b.value}),h="checkbox"==b.type,l="radio"==b.type;if(h||l){var g=h&&a.a.c(d())instanceof Array,m=g?k():p,x=l||g;l&&!b.name&&a.d.uniqueName.init(b,function(){return!0});a.w(e,null,{q:b});a.a.n(b,"click",e);a.w(f,null,{q:b})}}};a.h.V.checked=!0;a.d.checkedValue=
|
82 |
+
{update:function(b,d){b.value=a.a.c(d())}}})();a.d.css={update:function(b,d){var c=a.a.c(d());null!==c&&"object"==typeof c?a.a.A(c,function(c,d){d=a.a.c(d);a.a.Ia(b,c,d)}):(c=String(c||""),a.a.Ia(b,b.__ko__cssValue,!1),b.__ko__cssValue=c,a.a.Ia(b,c,!0))}};a.d.enable={update:function(b,d){var c=a.a.c(d());c&&b.disabled?b.removeAttribute("disabled"):c||b.disabled||(b.disabled=!0)}};a.d.disable={update:function(b,d){a.d.enable.update(b,function(){return!a.a.c(d())})}};a.d.event={init:function(b,d,c,
|
83 |
+
e,f){var k=d()||{};a.a.A(k,function(h){"string"==typeof h&&a.a.n(b,h,function(b){var g,m=d()[h];if(m){try{var k=a.a.O(arguments);e=f.$data;k.unshift(e);g=m.apply(e,k)}finally{!0!==g&&(b.preventDefault?b.preventDefault():b.returnValue=!1)}!1===c.get(h+"Bubble")&&(b.cancelBubble=!0,b.stopPropagation&&b.stopPropagation())}})})}};a.d.foreach={Ib:function(b){return function(){var d=b(),c=a.a.cb(d);if(!c||"number"==typeof c.length)return{foreach:d,templateEngine:a.P.Va};a.a.c(d);return{foreach:c.data,as:c.as,
|
84 |
+
includeDestroyed:c.includeDestroyed,afterAdd:c.afterAdd,beforeRemove:c.beforeRemove,afterRender:c.afterRender,beforeMove:c.beforeMove,afterMove:c.afterMove,templateEngine:a.P.Va}}},init:function(b,d){return a.d.template.init(b,a.d.foreach.Ib(d))},update:function(b,d,c,e,f){return a.d.template.update(b,a.d.foreach.Ib(d),c,e,f)}};a.h.ka.foreach=!1;a.e.R.foreach=!0;a.d.hasfocus={init:function(b,d,c){function e(e){b.__ko_hasfocusUpdating=!0;var f=b.ownerDocument;if("activeElement"in f){var g;try{g=f.activeElement}catch(m){g=
|
85 |
+
f.body}e=g===b}f=d();a.h.ra(f,c,"hasfocus",e,!0);b.__ko_hasfocusLastValue=e;b.__ko_hasfocusUpdating=!1}var f=e.bind(null,!0),k=e.bind(null,!1);a.a.n(b,"focus",f);a.a.n(b,"focusin",f);a.a.n(b,"blur",k);a.a.n(b,"focusout",k)},update:function(b,d){var c=!!a.a.c(d());b.__ko_hasfocusUpdating||b.__ko_hasfocusLastValue===c||(c?b.focus():b.blur(),a.k.u(a.a.qa,null,[b,c?"focusin":"focusout"]))}};a.h.V.hasfocus=!0;a.d.hasFocus=a.d.hasfocus;a.h.V.hasFocus=!0;a.d.html={init:function(){return{controlsDescendantBindings:!0}},
|
86 |
+
update:function(b,d){a.a.gb(b,d())}};K("if");K("ifnot",!1,!0);K("with",!0,!1,function(a,d){return a.createChildContext(d)});var L={};a.d.options={init:function(b){if("select"!==a.a.v(b))throw Error("options binding applies only to SELECT elements");for(;0<b.length;)b.remove(0);return{controlsDescendantBindings:!0}},update:function(b,d,c){function e(){return a.a.xa(b.options,function(a){return a.selected})}function f(a,b,c){var d=typeof b;return"function"==d?b(a):"string"==d?a[b]:c}function k(d,e){if(r&&
|
87 |
+
m)a.i.Y(b,a.a.c(c.get("value")),!0);else if(n.length){var g=0<=a.a.m(n,a.i.s(e[0]));a.a.Sb(e[0],g);r&&!g&&a.k.u(a.a.qa,null,[b,"change"])}}var h=b.multiple,l=0!=b.length&&h?b.scrollTop:null,g=a.a.c(d()),m=c.get("valueAllowUnset")&&c.has("value"),x=c.get("optionsIncludeDestroyed");d={};var q,n=[];m||(h?n=a.a.Ka(e(),a.i.s):0<=b.selectedIndex&&n.push(a.i.s(b.options[b.selectedIndex])));g&&("undefined"==typeof g.length&&(g=[g]),q=a.a.xa(g,function(b){return x||b===p||null===b||!a.a.c(b._destroy)}),c.has("optionsCaption")&&
|
88 |
+
(g=a.a.c(c.get("optionsCaption")),null!==g&&g!==p&&q.unshift(L)));var r=!1;d.beforeRemove=function(a){b.removeChild(a)};g=k;c.has("optionsAfterRender")&&"function"==typeof c.get("optionsAfterRender")&&(g=function(b,d){k(0,d);a.k.u(c.get("optionsAfterRender"),null,[d[0],b!==L?b:p])});a.a.fb(b,q,function(d,e,g){g.length&&(n=!m&&g[0].selected?[a.i.s(g[0])]:[],r=!0);e=b.ownerDocument.createElement("option");d===L?(a.a.Ha(e,c.get("optionsCaption")),a.i.Y(e,p)):(g=f(d,c.get("optionsValue"),d),a.i.Y(e,a.a.c(g)),
|
89 |
+
d=f(d,c.get("optionsText"),g),a.a.Ha(e,d));return[e]},d,g);a.k.u(function(){m?a.i.Y(b,a.a.c(c.get("value")),!0):(h?n.length&&e().length<n.length:n.length&&0<=b.selectedIndex?a.i.s(b.options[b.selectedIndex])!==n[0]:n.length||0<=b.selectedIndex)&&a.a.qa(b,"change")});a.a.kc(b);l&&20<Math.abs(l-b.scrollTop)&&(b.scrollTop=l)}};a.d.options.ab=a.a.f.I();a.d.selectedOptions={after:["options","foreach"],init:function(b,d,c){a.a.n(b,"change",function(){var e=d(),f=[];a.a.o(b.getElementsByTagName("option"),
|
90 |
+
function(b){b.selected&&f.push(a.i.s(b))});a.h.ra(e,c,"selectedOptions",f)})},update:function(b,d){if("select"!=a.a.v(b))throw Error("values binding applies only to SELECT elements");var c=a.a.c(d());c&&"number"==typeof c.length&&a.a.o(b.getElementsByTagName("option"),function(b){var d=0<=a.a.m(c,a.i.s(b));a.a.Sb(b,d)})}};a.h.V.selectedOptions=!0;a.d.style={update:function(b,d){var c=a.a.c(d()||{});a.a.A(c,function(c,d){d=a.a.c(d);if(null===d||d===p||!1===d)d="";b.style[c]=d})}};a.d.submit={init:function(b,
|
91 |
+
d,c,e,f){if("function"!=typeof d())throw Error("The value for a submit binding must be a function");a.a.n(b,"submit",function(a){var c,e=d();try{c=e.call(f.$data,b)}finally{!0!==c&&(a.preventDefault?a.preventDefault():a.returnValue=!1)}})}};a.d.text={init:function(){return{controlsDescendantBindings:!0}},update:function(b,d){a.a.Ha(b,d())}};a.e.R.text=!0;(function(){if(y&&y.navigator)var b=function(a){if(a)return parseFloat(a[1])},d=y.opera&&y.opera.version&&parseInt(y.opera.version()),c=y.navigator.userAgent,
|
92 |
+
e=b(c.match(/^(?:(?!chrome).)*version\/([^ ]*) safari/i)),f=b(c.match(/Firefox\/([^ ]*)/));if(10>a.a.M)var k=a.a.f.I(),h=a.a.f.I(),l=function(b){var c=this.activeElement;(c=c&&a.a.f.get(c,h))&&c(b)},g=function(b,c){var d=b.ownerDocument;a.a.f.get(d,k)||(a.a.f.set(d,k,!0),a.a.n(d,"selectionchange",l));a.a.f.set(b,h,c)};a.d.textInput={init:function(b,c,l){function h(c,d){a.a.n(b,c,d)}function k(){var d=a.a.c(c());if(null===d||d===p)d="";w!==p&&d===w?setTimeout(k,4):b.value!==d&&(u=d,b.value=d)}function v(){A||
|
93 |
+
(w=b.value,A=setTimeout(t,4))}function t(){clearTimeout(A);w=A=p;var d=b.value;u!==d&&(u=d,a.h.ra(c(),l,"textInput",d))}var u=b.value,A,w;10>a.a.M?(h("propertychange",function(a){"value"===a.propertyName&&t()}),8==a.a.M&&(h("keyup",t),h("keydown",t)),8<=a.a.M&&(g(b,t),h("dragend",v))):(h("input",t),5>e&&"textarea"===a.a.v(b)?(h("keydown",v),h("paste",v),h("cut",v)):11>d?h("keydown",v):4>f&&(h("DOMAutoComplete",t),h("dragdrop",t),h("drop",t)));h("change",t);a.w(k,null,{q:b})}};a.h.V.textInput=!0;a.d.textinput=
|
94 |
+
{preprocess:function(a,b,c){c("textInput",a)}}})();a.d.uniqueName={init:function(b,d){if(d()){var c="ko_unique_"+ ++a.d.uniqueName.fc;a.a.Rb(b,c)}}};a.d.uniqueName.fc=0;a.d.value={after:["options","foreach"],init:function(b,d,c){if("input"!=b.tagName.toLowerCase()||"checkbox"!=b.type&&"radio"!=b.type){var e=["change"],f=c.get("valueUpdate"),k=!1,h=null;f&&("string"==typeof f&&(f=[f]),a.a.ia(e,f),e=a.a.wb(e));var l=function(){h=null;k=!1;var e=d(),g=a.i.s(b);a.h.ra(e,c,"value",g)};!a.a.M||"input"!=
|
95 |
+
b.tagName.toLowerCase()||"text"!=b.type||"off"==b.autocomplete||b.form&&"off"==b.form.autocomplete||-1!=a.a.m(e,"propertychange")||(a.a.n(b,"propertychange",function(){k=!0}),a.a.n(b,"focus",function(){k=!1}),a.a.n(b,"blur",function(){k&&l()}));a.a.o(e,function(c){var d=l;a.a.Dc(c,"after")&&(d=function(){h=a.i.s(b);setTimeout(l,0)},c=c.substring(5));a.a.n(b,c,d)});var g=function(){var e=a.a.c(d()),f=a.i.s(b);if(null!==h&&e===h)setTimeout(g,0);else if(e!==f)if("select"===a.a.v(b)){var l=c.get("valueAllowUnset"),
|
96 |
+
f=function(){a.i.Y(b,e,l)};f();l||e===a.i.s(b)?setTimeout(f,0):a.k.u(a.a.qa,null,[b,"change"])}else a.i.Y(b,e)};a.w(g,null,{q:b})}else a.va(b,{checkedValue:d})},update:function(){}};a.h.V.value=!0;a.d.visible={update:function(b,d){var c=a.a.c(d()),e="none"!=b.style.display;c&&!e?b.style.display="":!c&&e&&(b.style.display="none")}};(function(b){a.d[b]={init:function(d,c,e,f,k){return a.d.event.init.call(this,d,function(){var a={};a[b]=c();return a},e,f,k)}}})("click");a.J=function(){};a.J.prototype.renderTemplateSource=
|
97 |
+
function(){throw Error("Override renderTemplateSource");};a.J.prototype.createJavaScriptEvaluatorBlock=function(){throw Error("Override createJavaScriptEvaluatorBlock");};a.J.prototype.makeTemplateSource=function(b,d){if("string"==typeof b){d=d||w;var c=d.getElementById(b);if(!c)throw Error("Cannot find template with ID "+b);return new a.t.l(c)}if(1==b.nodeType||8==b.nodeType)return new a.t.ha(b);throw Error("Unknown template type: "+b);};a.J.prototype.renderTemplate=function(a,d,c,e){a=this.makeTemplateSource(a,
|
98 |
+
e);return this.renderTemplateSource(a,d,c,e)};a.J.prototype.isTemplateRewritten=function(a,d){return!1===this.allowTemplateRewriting?!0:this.makeTemplateSource(a,d).data("isRewritten")};a.J.prototype.rewriteTemplate=function(a,d,c){a=this.makeTemplateSource(a,c);d=d(a.text());a.text(d);a.data("isRewritten",!0)};a.b("templateEngine",a.J);a.kb=function(){function b(b,c,d,h){b=a.h.bb(b);for(var l=a.h.ka,g=0;g<b.length;g++){var m=b[g].key;if(l.hasOwnProperty(m)){var x=l[m];if("function"===typeof x){if(m=
|
99 |
+
x(b[g].value))throw Error(m);}else if(!x)throw Error("This template engine does not support the '"+m+"' binding within its templates");}}d="ko.__tr_ambtns(function($context,$element){return(function(){return{ "+a.h.Ea(b,{valueAccessors:!0})+" } })()},'"+d.toLowerCase()+"')";return h.createJavaScriptEvaluatorBlock(d)+c}var d=/(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'|[^>]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,c=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{lc:function(b,
|
100 |
+
c,d){c.isTemplateRewritten(b,d)||c.rewriteTemplate(b,function(b){return a.kb.xc(b,c)},d)},xc:function(a,f){return a.replace(d,function(a,c,d,e,m){return b(m,c,d,f)}).replace(c,function(a,c){return b(c,"\x3c!-- ko --\x3e","#comment",f)})},dc:function(b,c){return a.H.$a(function(d,h){var l=d.nextSibling;l&&l.nodeName.toLowerCase()===c&&a.va(l,b,h)})}}}();a.b("__tr_ambtns",a.kb.dc);(function(){a.t={};a.t.l=function(a){this.l=a};a.t.l.prototype.text=function(){var b=a.a.v(this.l),b="script"===b?"text":
|
101 |
+
"textarea"===b?"value":"innerHTML";if(0==arguments.length)return this.l[b];var d=arguments[0];"innerHTML"===b?a.a.gb(this.l,d):this.l[b]=d};var b=a.a.f.I()+"_";a.t.l.prototype.data=function(c){if(1===arguments.length)return a.a.f.get(this.l,b+c);a.a.f.set(this.l,b+c,arguments[1])};var d=a.a.f.I();a.t.ha=function(a){this.l=a};a.t.ha.prototype=new a.t.l;a.t.ha.prototype.text=function(){if(0==arguments.length){var b=a.a.f.get(this.l,d)||{};b.lb===p&&b.Na&&(b.lb=b.Na.innerHTML);return b.lb}a.a.f.set(this.l,
|
102 |
+
d,{lb:arguments[0]})};a.t.l.prototype.nodes=function(){if(0==arguments.length)return(a.a.f.get(this.l,d)||{}).Na;a.a.f.set(this.l,d,{Na:arguments[0]})};a.b("templateSources",a.t);a.b("templateSources.domElement",a.t.l);a.b("templateSources.anonymousTemplate",a.t.ha)})();(function(){function b(b,c,d){var e;for(c=a.e.nextSibling(c);b&&(e=b)!==c;)b=a.e.nextSibling(e),d(e,b)}function d(c,d){if(c.length){var e=c[0],f=c[c.length-1],h=e.parentNode,k=a.L.instance,r=k.preprocessNode;if(r){b(e,f,function(a,
|
103 |
+
b){var c=a.previousSibling,d=r.call(k,a);d&&(a===e&&(e=d[0]||b),a===f&&(f=d[d.length-1]||c))});c.length=0;if(!e)return;e===f?c.push(e):(c.push(e,f),a.a.na(c,h))}b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.ub(d,b)});b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.H.Xb(b,[d])});a.a.na(c,h)}}function c(a){return a.nodeType?a:0<a.length?a[0]:null}function e(b,e,f,h,q){q=q||{};var n=(b&&c(b)||f||{}).ownerDocument,r=q.templateEngine||k;a.kb.lc(f,r,n);f=r.renderTemplate(f,h,q,n);if("number"!=
|
104 |
+
typeof f.length||0<f.length&&"number"!=typeof f[0].nodeType)throw Error("Template engine must return an array of DOM nodes");n=!1;switch(e){case "replaceChildren":a.e.T(b,f);n=!0;break;case "replaceNode":a.a.Qb(b,f);n=!0;break;case "ignoreTargetNode":break;default:throw Error("Unknown renderMode: "+e);}n&&(d(f,h),q.afterRender&&a.k.u(q.afterRender,null,[f,h.$data]));return f}function f(b,c,d){return a.F(b)?b():"function"===typeof b?b(c,d):b}var k;a.hb=function(b){if(b!=p&&!(b instanceof a.J))throw Error("templateEngine must inherit from ko.templateEngine");
|
105 |
+
k=b};a.eb=function(b,d,h,x,q){h=h||{};if((h.templateEngine||k)==p)throw Error("Set a template engine before calling renderTemplate");q=q||"replaceChildren";if(x){var n=c(x);return a.j(function(){var k=d&&d instanceof a.N?d:new a.N(a.a.c(d)),p=f(b,k.$data,k),k=e(x,q,p,k,h);"replaceNode"==q&&(x=k,n=c(x))},null,{Pa:function(){return!n||!a.a.Qa(n)},q:n&&"replaceNode"==q?n.parentNode:n})}return a.H.$a(function(c){a.eb(b,d,h,c,"replaceNode")})};a.Cc=function(b,c,h,k,q){function n(a,b){d(b,v);h.afterRender&&
|
106 |
+
h.afterRender(b,a);v=null}function r(a,c){v=q.createChildContext(a,h.as,function(a){a.$index=c});var d=f(b,a,v);return e(null,"ignoreTargetNode",d,v,h)}var v;return a.j(function(){var b=a.a.c(c)||[];"undefined"==typeof b.length&&(b=[b]);b=a.a.xa(b,function(b){return h.includeDestroyed||b===p||null===b||!a.a.c(b._destroy)});a.k.u(a.a.fb,null,[k,b,r,h,n])},null,{q:k})};var h=a.a.f.I();a.d.template={init:function(b,c){var d=a.a.c(c());if("string"==typeof d||d.name)a.e.ma(b);else{if("nodes"in d){if(d=
|
107 |
+
d.nodes||[],a.F(d))throw Error('The "nodes" option must be a plain, non-observable array.');}else d=a.e.childNodes(b);d=a.a.Jb(d);(new a.t.ha(b)).nodes(d)}return{controlsDescendantBindings:!0}},update:function(b,c,d,e,f){var k=c(),r;c=a.a.c(k);d=!0;e=null;"string"==typeof c?c={}:(k=c.name,"if"in c&&(d=a.a.c(c["if"])),d&&"ifnot"in c&&(d=!a.a.c(c.ifnot)),r=a.a.c(c.data));"foreach"in c?e=a.Cc(k||b,d&&c.foreach||[],c,b,f):d?(f="data"in c?f.createChildContext(r,c.as):f,e=a.eb(k||b,f,c,b)):a.e.ma(b);f=
|
108 |
+
e;(r=a.a.f.get(b,h))&&"function"==typeof r.p&&r.p();a.a.f.set(b,h,f&&f.$()?f:p)}};a.h.ka.template=function(b){b=a.h.bb(b);return 1==b.length&&b[0].unknown||a.h.vc(b,"name")?null:"This template engine does not support anonymous templates nested within its templates"};a.e.R.template=!0})();a.b("setTemplateEngine",a.hb);a.b("renderTemplate",a.eb);a.a.Cb=function(a,d,c){if(a.length&&d.length){var e,f,k,h,l;for(e=f=0;(!c||e<c)&&(h=a[f]);++f){for(k=0;l=d[k];++k)if(h.value===l.value){h.moved=l.index;l.moved=
|
109 |
+
h.index;d.splice(k,1);e=k=0;break}e+=k}}};a.a.Ma=function(){function b(b,c,e,f,k){var h=Math.min,l=Math.max,g=[],m,p=b.length,q,n=c.length,r=n-p||1,v=p+n+1,t,u,w;for(m=0;m<=p;m++)for(u=t,g.push(t=[]),w=h(n,m+r),q=l(0,m-1);q<=w;q++)t[q]=q?m?b[m-1]===c[q-1]?u[q-1]:h(u[q]||v,t[q-1]||v)+1:q+1:m+1;h=[];l=[];r=[];m=p;for(q=n;m||q;)n=g[m][q]-1,q&&n===g[m][q-1]?l.push(h[h.length]={status:e,value:c[--q],index:q}):m&&n===g[m-1][q]?r.push(h[h.length]={status:f,value:b[--m],index:m}):(--q,--m,k.sparse||h.push({status:"retained",
|
110 |
+
value:c[q]}));a.a.Cb(l,r,10*p);return h.reverse()}return function(a,c,e){e="boolean"===typeof e?{dontLimitMoves:e}:e||{};a=a||[];c=c||[];return a.length<=c.length?b(a,c,"added","deleted",e):b(c,a,"deleted","added",e)}}();a.b("utils.compareArrays",a.a.Ma);(function(){function b(b,d,f,k,h){var l=[],g=a.j(function(){var g=d(f,h,a.a.na(l,b))||[];0<l.length&&(a.a.Qb(l,g),k&&a.k.u(k,null,[f,g,h]));l.length=0;a.a.ia(l,g)},null,{q:b,Pa:function(){return!a.a.tb(l)}});return{aa:l,j:g.$()?g:p}}var d=a.a.f.I();
|
111 |
+
a.a.fb=function(c,e,f,k,h){function l(b,d){s=u[d];t!==d&&(z[b]=s);s.Ua(t++);a.a.na(s.aa,c);r.push(s);y.push(s)}function g(b,c){if(b)for(var d=0,e=c.length;d<e;d++)c[d]&&a.a.o(c[d].aa,function(a){b(a,d,c[d].wa)})}e=e||[];k=k||{};var m=a.a.f.get(c,d)===p,u=a.a.f.get(c,d)||[],q=a.a.Ka(u,function(a){return a.wa}),n=a.a.Ma(q,e,k.dontLimitMoves),r=[],v=0,t=0,w=[],y=[];e=[];for(var z=[],q=[],s,C=0,D,E;D=n[C];C++)switch(E=D.moved,D.status){case "deleted":E===p&&(s=u[v],s.j&&s.j.p(),w.push.apply(w,a.a.na(s.aa,
|
112 |
+
c)),k.beforeRemove&&(e[C]=s,y.push(s)));v++;break;case "retained":l(C,v++);break;case "added":E!==p?l(C,E):(s={wa:D.value,Ua:a.r(t++)},r.push(s),y.push(s),m||(q[C]=s))}g(k.beforeMove,z);a.a.o(w,k.beforeRemove?a.S:a.removeNode);for(var C=0,m=a.e.firstChild(c),H;s=y[C];C++){s.aa||a.a.extend(s,b(c,f,s.wa,h,s.Ua));for(v=0;n=s.aa[v];m=n.nextSibling,H=n,v++)n!==m&&a.e.Fb(c,n,H);!s.rc&&h&&(h(s.wa,s.aa,s.Ua),s.rc=!0)}g(k.beforeRemove,e);g(k.afterMove,z);g(k.afterAdd,q);a.a.f.set(c,d,r)}})();a.b("utils.setDomNodeChildrenFromArrayMapping",
|
113 |
+
a.a.fb);a.P=function(){this.allowTemplateRewriting=!1};a.P.prototype=new a.J;a.P.prototype.renderTemplateSource=function(b,d,c,e){if(d=(9>a.a.M?0:b.nodes)?b.nodes():null)return a.a.O(d.cloneNode(!0).childNodes);b=b.text();return a.a.ca(b,e)};a.P.Va=new a.P;a.hb(a.P.Va);a.b("nativeTemplateEngine",a.P);(function(){a.Ya=function(){var a=this.uc=function(){if(!u||!u.tmpl)return 0;try{if(0<=u.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}();this.renderTemplateSource=function(b,
|
114 |
+
e,f,k){k=k||w;f=f||{};if(2>a)throw Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");var h=b.data("precompiled");h||(h=b.text()||"",h=u.template(null,"{{ko_with $item.koBindingContext}}"+h+"{{/ko_with}}"),b.data("precompiled",h));b=[e.$data];e=u.extend({koBindingContext:e},f.templateOptions);e=u.tmpl(h,b,e);e.appendTo(k.createElement("div"));u.fragments={};return e};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+
|
115 |
+
a+" })()) }}"};this.addTemplate=function(a,b){w.write("<script type='text/html' id='"+a+"'>"+b+"\x3c/script>")};0<a&&(u.tmpl.tag.ko_code={open:"__.push($1 || '');"},u.tmpl.tag.ko_with={open:"with($1) {",close:"} "})};a.Ya.prototype=new a.J;var b=new a.Ya;0<b.uc&&a.hb(b);a.b("jqueryTmplTemplateEngine",a.Ya)})()})})();})();
|
js/select2.min.js
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
/*! Select2 4.0.0 | https://github.com/select2/select2/blob/master/LICENSE.md */!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(n=n.slice(0,n.length-1),a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.concat(a),k=0;k<a.length;k+=1)if(m=a[k],"."===m)a.splice(k,1),k-=1;else if(".."===m){if(1===k&&(".."===a[2]||".."===a[0]))break;k>0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){return n.apply(b,v.call(arguments,0).concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n<c.length;n+=1)if(m=o(c[n],f),k=m.f,"require"===k)u[n]=p.require(a);else if("exports"===k)u[n]=p.exports(a),s=!0;else if("module"===k)h=u[n]=p.module(a);else if(e(q,k)||e(r,k)||e(t,k))u[n]=j(k);else{if(!m.p)throw new Error(a+" missing "+k);m.p.load(m.n,g(f,!0),i(k),{}),u[n]=q[k]}l=d?d.apply(q[a],u):void 0,a&&(h&&h.exports!==b&&h.exports!==q[a]?q[a]=h.exports:l===b&&s||(q[a]=l))}else a&&(q[a]=d)},a=c=n=function(a,c,d,e,f){if("string"==typeof a)return p[a]?p[a](c):j(o(a,c).f);if(!a.splice){if(s=a,s.deps&&n(s.deps,s.callback),!c)return;c.splice?(a=c,c=d,d=null):a=b}return c=c||function(){},"function"==typeof d&&(d=e,e=f),e?m(b,a,c,d):setTimeout(function(){m(b,a,c,d)},4),n},n.config=function(a){return n(a)},a._defined=q,d=function(a,b,c){b.splice||(c=b,b=[]),e(q,a)||e(r,a)||(r[a]=[a,b,c])},d.amd={jQuery:!0}}(),b.requirejs=a,b.require=c,b.define=d}}(),b.define("almond",function(){}),b.define("jquery",[],function(){var b=a||$;return null==b&&console&&console.error&&console.error("Select2: An instance of jQuery or a jQuery-compatible library was not found. Make sure that you are including jQuery before Select2 on your web page."),b}),b.define("select2/utils",["jquery"],function(a){function b(a){var b=a.prototype,c=[];for(var d in b){var e=b[d];"function"==typeof e&&"constructor"!==d&&c.push(d)}return c}var c={};c.Extend=function(a,b){function c(){this.constructor=a}var d={}.hasOwnProperty;for(var e in b)d.call(b,e)&&(a[e]=b[e]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},c.Decorate=function(a,c){function d(){var b=Array.prototype.unshift,d=c.prototype.constructor.length,e=a.prototype.constructor;d>0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;h<g.length;h++){var i=g[h];d.prototype[i]=a.prototype[i]}for(var j=(function(a){var b=function(){};a in d.prototype&&(b=d.prototype[a]);var e=c.prototype[a];return function(){var a=Array.prototype.unshift;return a.call(arguments,b),e.apply(this,arguments)}}),k=0;k<f.length;k++){var l=f[k];d.prototype[l]=j(l)}return d};var d=function(){this.listeners={}};return d.prototype.on=function(a,b){this.listeners=this.listeners||{},a in this.listeners?this.listeners[a].push(b):this.listeners[a]=[b]},d.prototype.trigger=function(a){var b=Array.prototype.slice;this.listeners=this.listeners||{},a in this.listeners&&this.invoke(this.listeners[a],b.call(arguments,1)),"*"in this.listeners&&this.invoke(this.listeners["*"],arguments)},d.prototype.invoke=function(a,b){for(var c=0,d=a.length;d>c;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},c.bind=function(a,b){return function(){a.apply(b,arguments)}},c._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e<c.length;e++){var f=c[e];f=f.substring(0,1).toLowerCase()+f.substring(1),f in d||(d[f]={}),e==c.length-1&&(d[f]=a[b]),d=d[f]}delete a[b]}}return a},c.hasScroll=function(b,c){var d=a(c),e=c.style.overflowX,f=c.style.overflowY;return e!==f||"hidden"!==f&&"visible"!==f?"scroll"===e||"scroll"===f?!0:d.innerHeight()<c.scrollHeight||d.innerWidth()<c.scrollWidth:!1},c.escapeMarkup=function(a){var b={"\\":"\","&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return"string"!=typeof a?a:String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})},c.appendMany=function(b,c){if("1.7"===a.fn.jquery.substr(0,3)){var d=a();a.map(c,function(a){d=d.add(a)}),c=d}b.append(c)},c}),b.define("select2/results",["jquery","./utils"],function(a,b){function c(a,b,d){this.$element=a,this.data=d,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<ul class="select2-results__options" role="tree"></ul>');return this.options.get("multiple")&&b.attr("aria-multiselectable","true"),this.$results=b,b},c.prototype.clear=function(){this.$results.empty()},c.prototype.displayMessage=function(b){var c=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var d=a('<li role="treeitem" class="select2-results__option"></li>'),e=this.options.get("translations").get(b.message);d.append(c(e(b.args))),this.$results.append(d)},c.prototype.append=function(a){this.hideLoading();var b=[];if(null==a.results||0===a.results.length)return void(0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"}));a.results=this.sort(a.results);for(var c=0;c<a.results.length;c++){var d=a.results[c],e=this.option(d);b.push(e)}this.$results.append(b)},c.prototype.position=function(a,b){var c=b.find(".select2-results");c.append(a)},c.prototype.sort=function(a){var b=this.options.get("sorter");return b(a)},c.prototype.setClasses=function(){var b=this;this.data.current(function(c){var d=a.map(c,function(a){return a.id.toString()}),e=b.$results.find(".select2-results__option[aria-selected]");e.each(function(){var b=a(this),c=a.data(this,"data"),e=""+c.id;null!=c.element&&c.element.selected||null==c.element&&a.inArray(e,d)>-1?b.attr("aria-selected","true"):b.attr("aria-selected","false")});var f=e.filter("[aria-selected=true]");f.length>0?f.first().trigger("mouseenter"):e.first().trigger("mouseenter")})},c.prototype.showLoading=function(a){this.hideLoading();var b=this.options.get("translations").get("searching"),c={disabled:!0,loading:!0,text:b(a)},d=this.option(c);d.className+=" loading-results",this.$results.prepend(d)},c.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},c.prototype.option=function(b){var c=document.createElement("li");c.className="select2-results__option";var d={role:"treeitem","aria-selected":"false"};b.disabled&&(delete d["aria-selected"],d["aria-disabled"]="true"),null==b.id&&delete d["aria-selected"],null!=b._resultId&&(c.id=b._resultId),b.title&&(c.title=b.title),b.children&&(d.role="group",d["aria-label"]=b.text,delete d["aria-selected"]);for(var e in d){var f=d[e];c.setAttribute(e,f)}if(b.children){var g=a(c),h=document.createElement("strong");h.className="select2-results__group";{a(h)}this.template(b,h);for(var i=[],j=0;j<b.children.length;j++){var k=b.children[j],l=this.option(k);i.push(l)}var m=a("<ul></ul>",{"class":"select2-results__options select2-results__options--nested"});m.append(i),g.append(h),g.append(m)}else this.template(b,c);return a.data(c,"data",b),c},c.prototype.bind=function(b){var c=this,d=b.id+"-results";this.$results.attr("id",d),b.on("results:all",function(a){c.clear(),c.append(a.data),b.isOpen()&&c.setClasses()}),b.on("results:append",function(a){c.append(a.data),b.isOpen()&&c.setClasses()}),b.on("query",function(a){c.showLoading(a)}),b.on("select",function(){b.isOpen()&&c.setClasses()}),b.on("unselect",function(){b.isOpen()&&c.setClasses()}),b.on("open",function(){c.$results.attr("aria-expanded","true"),c.$results.attr("aria-hidden","false"),c.setClasses(),c.ensureHighlightVisible()}),b.on("close",function(){c.$results.attr("aria-expanded","false"),c.$results.attr("aria-hidden","true"),c.$results.removeAttr("aria-activedescendant")}),b.on("results:toggle",function(){var a=c.getHighlightedResults();0!==a.length&&a.trigger("mouseup")}),b.on("results:select",function(){var a=c.getHighlightedResults();if(0!==a.length){var b=a.data("data");"true"==a.attr("aria-selected")?c.trigger("close"):c.trigger("select",{data:b})}}),b.on("results:previous",function(){var a=c.getHighlightedResults(),b=c.$results.find("[aria-selected]"),d=b.index(a);if(0!==d){var e=d-1;0===a.length&&(e=0);var f=b.eq(e);f.trigger("mouseenter");var g=c.$results.offset().top,h=f.offset().top,i=c.$results.scrollTop()+(h-g);0===e?c.$results.scrollTop(0):0>h-g&&c.$results.scrollTop(i)}}),b.on("results:next",function(){var a=c.getHighlightedResults(),b=c.$results.find("[aria-selected]"),d=b.index(a),e=d+1;if(!(e>=b.length)){var f=b.eq(e);f.trigger("mouseenter");var g=c.$results.offset().top+c.$results.outerHeight(!1),h=f.offset().top+f.outerHeight(!1),i=c.$results.scrollTop()+h-g;0===e?c.$results.scrollTop(0):h>g&&c.$results.scrollTop(i)}}),b.on("results:focus",function(a){a.element.addClass("select2-results__option--highlighted")}),b.on("results:message",function(a){c.displayMessage(a)}),a.fn.mousewheel&&this.$results.on("mousewheel",function(a){var b=c.$results.scrollTop(),d=c.$results.get(0).scrollHeight-c.$results.scrollTop()+a.deltaY,e=a.deltaY>0&&b-a.deltaY<=0,f=a.deltaY<0&&d<=c.$results.height();e?(c.$results.scrollTop(0),a.preventDefault(),a.stopPropagation()):f&&(c.$results.scrollTop(c.$results.get(0).scrollHeight-c.$results.height()),a.preventDefault(),a.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(b){var d=a(this),e=d.data("data");return"true"===d.attr("aria-selected")?void(c.options.get("multiple")?c.trigger("unselect",{originalEvent:b,data:e}):c.trigger("close")):void c.trigger("select",{originalEvent:b,data:e})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(){var b=a(this).data("data");c.getHighlightedResults().removeClass("select2-results__option--highlighted"),c.trigger("results:focus",{data:b,element:a(this)})})},c.prototype.getHighlightedResults=function(){var a=this.$results.find(".select2-results__option--highlighted");return a},c.prototype.destroy=function(){this.$results.remove()},c.prototype.ensureHighlightVisible=function(){var a=this.getHighlightedResults();if(0!==a.length){var b=this.$results.find("[aria-selected]"),c=b.index(a),d=this.$results.offset().top,e=a.offset().top,f=this.$results.scrollTop()+(e-d),g=e-d;f-=2*a.outerHeight(!1),2>=c?this.$results.scrollTop(0):(g>this.$results.outerHeight()||0>g)&&this.$results.scrollTop(f)}},c.prototype.template=function(b,c){var d=this.options.get("templateResult"),e=this.options.get("escapeMarkup"),f=d(b);null==f?c.style.display="none":"string"==typeof f?c.innerHTML=e(f):a(c).append(f)},c}),b.define("select2/keys",[],function(){var a={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46};return a}),b.define("select2/selection/base",["jquery","../utils","../keys"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,b.Observable),d.prototype.render=function(){var b=a('<span class="select2-selection" role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-expanded="false"></span>');return this._tabindex=0,null!=this.$element.data("old-tabindex")?this._tabindex=this.$element.data("old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),b.attr("title",this.$element.attr("title")),b.attr("tabindex",this._tabindex),this.$selection=b,b},d.prototype.bind=function(a){var b=this,d=(a.id+"-container",a.id+"-results");this.container=a,this.$selection.on("focus",function(a){b.trigger("focus",a)}),this.$selection.on("blur",function(a){b.trigger("blur",a)}),this.$selection.on("keydown",function(a){b.trigger("keypress",a),a.which===c.SPACE&&a.preventDefault()}),a.on("results:focus",function(a){b.$selection.attr("aria-activedescendant",a.data._resultId)}),a.on("selection:update",function(a){b.update(a.data)}),a.on("open",function(){b.$selection.attr("aria-expanded","true"),b.$selection.attr("aria-owns",d),b._attachCloseHandler(a)}),a.on("close",function(){b.$selection.attr("aria-expanded","false"),b.$selection.removeAttr("aria-activedescendant"),b.$selection.removeAttr("aria-owns"),b.$selection.focus(),b._detachCloseHandler(a)}),a.on("enable",function(){b.$selection.attr("tabindex",b._tabindex)}),a.on("disable",function(){b.$selection.attr("tabindex","-1")})},d.prototype._attachCloseHandler=function(b){a(document.body).on("mousedown.select2."+b.id,function(b){var c=a(b.target),d=c.closest(".select2"),e=a(".select2.select2-container--open");e.each(function(){var b=a(this);if(this!=d[0]){var c=b.data("element");c.select2("close")}})})},d.prototype._detachCloseHandler=function(b){a(document.body).off("mousedown.select2."+b.id)},d.prototype.position=function(a,b){var c=b.find(".selection");c.append(a)},d.prototype.destroy=function(){this._detachCloseHandler(this.container)},d.prototype.update=function(){throw new Error("The `update` method must be defined in child classes.")},d}),b.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(a,b,c){function d(){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--single"),a.html('<span class="select2-selection__rendered"></span><span class="select2-selection__arrow" role="presentation"><b role="presentation"></b></span>'),a},d.prototype.bind=function(a){var b=this;d.__super__.bind.apply(this,arguments);var c=a.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",c),this.$selection.attr("aria-labelledby",c),this.$selection.on("mousedown",function(a){1===a.which&&b.trigger("toggle",{originalEvent:a})}),this.$selection.on("focus",function(){}),this.$selection.on("blur",function(){}),a.on("selection:update",function(a){b.update(a.data)})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a){var b=this.options.get("templateSelection"),c=this.options.get("escapeMarkup");return c(b(a))},d.prototype.selectionContainer=function(){return a("<span></span>")},d.prototype.update=function(a){if(0===a.length)return void this.clear();var b=a[0],c=this.display(b),d=this.$selection.find(".select2-selection__rendered");d.empty().append(c),d.prop("title",b.title||b.text)},d}),b.define("select2/selection/multiple",["jquery","./base","../utils"],function(a,b,c){function d(){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--multiple"),a.html('<ul class="select2-selection__rendered"></ul>'),a},d.prototype.bind=function(){var b=this;d.__super__.bind.apply(this,arguments),this.$selection.on("click",function(a){b.trigger("toggle",{originalEvent:a})}),this.$selection.on("click",".select2-selection__choice__remove",function(c){var d=a(this),e=d.parent(),f=e.data("data");b.trigger("unselect",{originalEvent:c,data:f})})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a){var b=this.options.get("templateSelection"),c=this.options.get("escapeMarkup");return c(b(a))},d.prototype.selectionContainer=function(){var b=a('<li class="select2-selection__choice"><span class="select2-selection__choice__remove" role="presentation">×</span></li>');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],d=0;d<a.length;d++){var e=a[d],f=this.display(e),g=this.selectionContainer();g.append(f),g.prop("title",e.title||e.text),g.data("data",e),b.push(g)}var h=this.$selection.find(".select2-selection__rendered");c.appendMany(h,b)}},d}),b.define("select2/selection/placeholder",["../utils"],function(){function a(a,b,c){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c)}return a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.createPlaceholder=function(a,b){var c=this.selectionContainer();return c.html(this.display(b)),c.addClass("select2-selection__placeholder").removeClass("select2-selection__choice"),c},a.prototype.update=function(a,b){var c=1==b.length&&b[0].id!=this.placeholder.id,d=b.length>1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},a}),b.define("select2/selection/allowClear",["jquery","../keys"],function(a,b){function c(){}return c.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(a){d._handleClear(a)}),b.on("keypress",function(a){d._handleKeyboardClear(a,b)})},c.prototype._handleClear=function(a,b){if(!this.options.get("disabled")){var c=this.$selection.find(".select2-selection__clear");if(0!==c.length){b.stopPropagation();for(var d=c.data("data"),e=0;e<d.length;e++){var f={data:d[e]};if(this.trigger("unselect",f),f.prevented)return}this.$element.val(this.placeholder.id).trigger("change"),this.trigger("toggle")}}},c.prototype._handleKeyboardClear=function(a,c,d){d.isOpen()||(c.which==b.DELETE||c.which==b.BACKSPACE)&&this._handleClear(c)},c.prototype.update=function(b,c){if(b.call(this,c),!(this.$selection.find(".select2-selection__placeholder").length>0||0===c.length)){var d=a('<span class="select2-selection__clear">×</span>');d.data("data",c),this.$selection.find(".select2-selection__rendered").prepend(d)}},c}),b.define("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('<li class="select2-search select2-search--inline"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" /></li>');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus()}),b.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val(""),e.$search.focus()}),b.on("enable",function(){e.$search.prop("disabled",!1)}),b.on("disable",function(){e.$search.prop("disabled",!0)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e.trigger("blur",a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}}),this.$selection.on("input",".select2-search--inline",function(){e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input",".select2-search--inline",function(a){e.handleSearch(a)})},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.trigger("open"),this.$search.val(b.text+" ")},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change");if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f<a.length;f++){var g=a[f].id;-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")});else{var d=a.id;this.$element.val(d),this.$element.trigger("change")}},d.prototype.unselect=function(a){var b=this;if(this.$element.prop("multiple"))return a.selected=!1,c(a.element).is("option")?(a.element.selected=!1,void this.$element.trigger("change")):void this.current(function(d){for(var e=[],f=0;f<d.length;f++){var g=d[f].id;g!==a.id&&-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")})},d.prototype.bind=function(a){var b=this;this.container=a,a.on("select",function(a){b.select(a.data)}),a.on("unselect",function(a){b.unselect(a.data)})},d.prototype.destroy=function(){this.$element.find("*").each(function(){c.removeData(this,"data")})},d.prototype.query=function(a,b){var d=[],e=this,f=this.$element.children();f.each(function(){var b=c(this);if(b.is("option")||b.is("optgroup")){var f=e.item(b),g=e.matches(a,f);null!==g&&d.push(g)}}),b({results:d})},d.prototype.addOptions=function(a){b.appendMany(this.$element,a)},d.prototype.option=function(a){var b;a.children?(b=document.createElement("optgroup"),b.label=a.text):(b=document.createElement("option"),void 0!==b.textContent?b.textContent=a.text:b.innerText=a.text),a.id&&(b.value=a.id),a.disabled&&(b.disabled=!0),a.selected&&(b.selected=!0),a.title&&(b.title=a.title);var d=c(b),e=this._normalizeItem(a);return e.element=b,c.data(b,"data",e),d},d.prototype.item=function(a){var b={};
|
2 |
+
if(b=c.data(a[0],"data"),null!=b)return b;if(a.is("option"))b={id:a.val(),text:a.text(),disabled:a.prop("disabled"),selected:a.prop("selected"),title:a.prop("title")};else if(a.is("optgroup")){b={text:a.prop("label"),children:[],title:a.prop("title")};for(var d=a.children("option"),e=[],f=0;f<d.length;f++){var g=c(d[f]),h=this.item(g);e.push(h)}b.children=e}return b=this._normalizeItem(b),b.element=a[0],c.data(a[0],"data",b),b},d.prototype._normalizeItem=function(a){c.isPlainObject(a)||(a={id:a,text:a}),a=c.extend({},{text:""},a);var b={selected:!1,disabled:!1};return null!=a.id&&(a.id=a.id.toString()),null!=a.text&&(a.text=a.text.toString()),null==a._resultId&&a.id&&null!=this.container&&(a._resultId=this.generateResultId(this.container,a)),c.extend({},b,a)},d.prototype.matches=function(a,b){var c=this.options.get("matcher");return c(a,b)},d}),b.define("select2/data/array",["./select","../utils","jquery"],function(a,b,c){function d(a,b){var c=b.get("data")||[];d.__super__.constructor.call(this,a,b),this.addOptions(this.convertToOptions(c))}return b.Extend(d,a),d.prototype.select=function(a){var b=this.$element.find("option").filter(function(b,c){return c.value==a.id.toString()});0===b.length&&(b=this.option(a),this.addOptions(b)),d.__super__.select.call(this,a)},d.prototype.convertToOptions=function(a){function d(a){return function(){return c(this).val()==a.id}}for(var e=this,f=this.$element.find("option"),g=f.map(function(){return e.item(c(this)).id}).get(),h=[],i=0;i<a.length;i++){var j=this._normalizeItem(a[i]);if(c.inArray(j.id,g)>=0){var k=f.filter(d(j)),l=this.item(k),m=(c.extend(!0,{},l,j),this.option(l));k.replaceWith(m)}else{var n=this.option(j);if(j.children){var o=this.convertToOptions(j.children);b.appendMany(n,o)}h.push(n)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(b,c){this.ajaxOptions=this._applyDefaults(c.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),a.__super__.constructor.call(this,b,c)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return{q:a.term}},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url(a)),"function"==typeof f.data&&(f.data=f.data(a)),this.ajaxOptions.delay&&""!==a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");if(void 0!==f&&(this.createTag=f),b.call(this,c,d),a.isArray(e))for(var g=0;g<e.length;g++){var h=e[g],i=this._normalizeItem(h),j=this.option(i);this.$element.append(j)}}return b.prototype.query=function(a,b,c){function d(a,f){for(var g=a.results,h=0;h<g.length;h++){var i=g[h],j=null!=i.children&&!d({results:i.children},!0),k=i.text===b.term;if(k||j)return f?!1:(a.data=g,void c(a))}if(f)return!0;var l=e.createTag(b);if(null!=l){var m=e.option(l);m.attr("data-select2-tag",!0),e.addOptions([m]),e.insertTag(g,l)}a.results=g,c(a)}var e=this;return this._removeOldTags(),null==b.term||null!=b.page?void a.call(this,b,c):void a.call(this,b,d)},b.prototype.createTag=function(b,c){var d=a.trim(c.term);return""===d?null:{id:d,text:d}},b.prototype.insertTag=function(a,b,c){b.unshift(c)},b.prototype._removeOldTags=function(){var b=(this._lastTag,this.$element.find("option[data-select2-tag]"));b.each(function(){this.selected||a(this).remove()})},b}),b.define("select2/data/tokenizer",["jquery"],function(a){function b(a,b,c){var d=c.get("tokenizer");void 0!==d&&(this.tokenizer=d),a.call(this,b,c)}return b.prototype.bind=function(a,b,c){a.call(this,b,c),this.$search=b.dropdown.$search||b.selection.$search||c.find(".select2-search__field")},b.prototype.query=function(a,b,c){function d(a){e.select(a)}var e=this;b.term=b.term||"";var f=this.tokenizer(b,this.options,d);f.term!==b.term&&(this.$search.length&&(this.$search.val(f.term),this.$search.focus()),b.term=f.term),a.call(this,b,c)},b.prototype.tokenizer=function(b,c,d,e){for(var f=d.get("tokenSeparators")||[],g=c.term,h=0,i=this.createTag||function(a){return{id:a.term,text:a.term}};h<g.length;){var j=g[h];if(-1!==a.inArray(j,f)){var k=g.substr(0,h),l=a.extend({},c,{term:k}),m=i(l);e(m),g=g.substr(h+1)||"",h=0}else h++}return{term:g}},b}),b.define("select2/data/minimumInputLength",[],function(){function a(a,b,c){this.minimumInputLength=c.get("minimumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",b.term.length<this.minimumInputLength?void this.trigger("results:message",{message:"inputTooShort",args:{minimum:this.minimumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumInputLength",[],function(){function a(a,b,c){this.maximumInputLength=c.get("maximumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",this.maximumInputLength>0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<span class="select2-dropdown"><span class="select2-results"></span></span>');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.position=function(){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a){function b(){}return b.prototype.render=function(b){var c=b.call(this),d=a('<span class="select2-search select2-search--dropdown"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" /></span>');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},b.prototype.handleSearch=function(){if(!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},b.prototype.showSearch=function(){return!0},b}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('<li class="option load-more" role="treeitem"></li>'),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(a,b,c){this.$dropdownParent=c.get("dropdownParent")||document.body,a.call(this,b,c)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a("<span></span>"),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c){var d=this,e="scroll.select2."+c.id,f="resize.select2."+c.id,g="orientationchange.select2."+c.id,h=this.$container.parents().filter(b.hasScroll);h.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),h.on(e,function(){var b=a(this).data("select2-scroll-position");a(this).scrollTop(b.y)}),a(window).on(e+" "+f+" "+g,function(){d._positionDropdown(),d._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c){var d="scroll.select2."+c.id,e="resize.select2."+c.id,f="orientationchange.select2."+c.id,g=this.$container.parents().filter(b.hasScroll);g.off(d),a(window).off(d+" "+e+" "+f)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=(this.$container.position(),this.$container.offset());f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.top<f.top-h.height,k=i.bottom>f.bottom+h.height,l={left:f.left,top:g.bottom};c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){this.$dropdownContainer.width();var a={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(a.minWidth=a.width,a.width="auto"),this.$dropdown.css(a)},c.prototype._showDropdown=function(){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d<b.length;d++){var e=b[d];e.children?c+=a(e.children):c++}return c}function b(a,b,c,d){this.minimumResultsForSearch=c.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),a.call(this,b,c,d)}return b.prototype.showSearch=function(b,c){return a(c.data.results)<this.minimumResultsForSearch?!1:b.call(this,c)},b}),b.define("select2/dropdown/selectOnClose",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("close",function(){d._handleSelectOnClose()})},a.prototype._handleSelectOnClose=function(){var a=this.getHighlightedResults();a.length<1||this.trigger("select",{data:a.data("data")})},a}),b.define("select2/dropdown/closeOnSelect",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("select",function(a){d._selectTriggered(a)}),b.on("unselect",function(a){d._selectTriggered(a)})},a.prototype._selectTriggered=function(a,b){var c=b.originalEvent;c&&c.ctrlKey||this.trigger("close")},a}),b.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(a){var b=a.input.length-a.maximum,c="Please delete "+b+" character";return 1!=b&&(c+="s"),c},inputTooShort:function(a){var b=a.minimum-a.input.length,c="Please enter "+b+" or more characters";return c},loadingMore:function(){return"Loading more results…"},maximumSelected:function(a){var b="You can only select "+a.maximum+" item";return 1!=a.maximum&&(b+="s"),b},noResults:function(){return"No results found"},searching:function(){return"Searching…"}}}),b.define("select2/defaults",["jquery","require","./results","./selection/single","./selection/multiple","./selection/placeholder","./selection/allowClear","./selection/search","./selection/eventRelay","./utils","./translation","./diacritics","./data/select","./data/array","./data/ajax","./data/tags","./data/tokenizer","./data/minimumInputLength","./data/maximumInputLength","./data/maximumSelectionLength","./dropdown","./dropdown/search","./dropdown/hidePlaceholder","./dropdown/infiniteScroll","./dropdown/attachBody","./dropdown/minimumResultsForSearch","./dropdown/selectOnClose","./dropdown/closeOnSelect","./i18n/en"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C){function D(){this.reset()}D.prototype.apply=function(l){if(l=a.extend({},this.defaults,l),null==l.dataAdapter){if(l.dataAdapter=null!=l.ajax?o:null!=l.data?n:m,l.minimumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.selectionAdapter=l.multiple?e:d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L<K.length;L++){var M=K[L],N={};try{N=k.loadPath(M)}catch(O){try{M=this.defaults.amdLanguageBase+M,N=k.loadPath(M)}catch(P){l.debug&&window.console&&console.warn&&console.warn('Select2: The language file for "'+M+'" could not be automatically loaded. A fallback will be used instead.');continue}}J.extend(N)}l.translations=J}else{var Q=k.loadPath(this.defaults.amdLanguageBase+"en"),R=new k(l.language);R.extend(Q),l.translations=R}return l},D.prototype.reset=function(){function b(a){function b(a){return l[a]||a}return a.replace(/[^\u0000-\u007E]/g,b)}function c(d,e){if(""===a.trim(d.term))return e;if(e.children&&e.children.length>0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(this.options.dir=a.prop("dir")?a.prop("dir"):a.closest("[dir]").prop("dir")?a.closest("[dir]").prop("dir"):"ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this._sync=c.bind(this._syncAttributes,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._sync);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._sync)}),this._observer.observe(this.$element[0],{attributes:!0,subtree:!1})):this.$element[0].addEventListener&&this.$element[0].addEventListener("DOMAttrModified",b._sync,!1)},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("focus",function(){a.$container.addClass("select2-container--focus")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open"),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ENTER?(a.trigger("results:select"),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle"),b.preventDefault()):c===d.UP?(a.trigger("results:previous"),b.preventDefault()):c===d.DOWN?(a.trigger("results:next"),b.preventDefault()):(c===d.ESC||c===d.TAB)&&(a.close(),b.preventDefault()):(c===d.ENTER||c===d.SPACE||(c===d.DOWN||c===d.UP)&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable")):this.trigger("enable")},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||(this.trigger("query",{}),this.trigger("open"))},e.prototype.close=function(){this.isOpen()&&this.trigger("close")},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._sync),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&this.$element[0].removeEventListener("DOMAttrModified",this._sync,!1),this._sync=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null},e.prototype.render=function(){var b=a('<span class="select2 select2-container"><span class="selection"></span><span class="dropdown-wrapper" aria-hidden="true"></span></span>');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),b.define("jquery.select2",["jquery","require","./select2/core","./select2/defaults"],function(a,b,c,d){if(b("jquery.mousewheel"),null==a.fn.select2){var e=["open","close","destroy"];a.fn.select2=function(b){if(b=b||{},"object"==typeof b)return this.each(function(){{var d=a.extend({},b,!0);new c(a(this),d)}}),this;if("string"==typeof b){var d=this.data("select2");null==d&&window.console&&console.error&&console.error("The select2('"+b+"') method was called on an element that is not using Select2.");var f=Array.prototype.slice.call(arguments,1),g=d[b](f);return a.inArray(b,e)>-1?this:g}throw new Error("Invalid arguments for Select2: "+b)}}return null==a.fn.select2.defaults&&(a.fn.select2.defaults=d),c}),b.define("jquery.mousewheel",["jquery"],function(a){return a}),{define:b.define,require:b.require}}(),c=b.require("jquery.select2");return a.fn.select2.amd=b,c});
|
lib/compat.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if (!function_exists('str_getcsv')) {
|
4 |
+
|
5 |
+
function str_getcsv($input, $delimiter = ',', $enclosure = '"', $escape = null, $eol = null) {
|
6 |
+
$temp = fopen("php://memory", "rw");
|
7 |
+
fwrite($temp, $input);
|
8 |
+
fseek($temp, 0);
|
9 |
+
$r = array();
|
10 |
+
while (($data = fgetcsv($temp, 0, $delimiter, $enclosure, $escape)) !== false) {
|
11 |
+
$r[] = $data;
|
12 |
+
}
|
13 |
+
fclose($temp);
|
14 |
+
return $r;
|
15 |
+
}
|
16 |
+
|
17 |
+
}
|
lib/dashboard.php
CHANGED
@@ -32,7 +32,6 @@
|
|
32 |
<?php if(wfConfig::get('scansEnabled_themes')){ ?><tr><td style="padding-right: 20px;">Scan Themes:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
33 |
<?php if(wfConfig::get('scansEnabled_plugins')){ ?><tr><td style="padding-right: 20px;">Scan Plugins:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
34 |
<?php if(wfConfig::get('scansEnabled_fileContents')){ ?><tr><td style="padding-right: 20px;">Scan Other Files:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
35 |
-
<?php if(wfConfig::get('scansEnabled_database')){ ?><tr><td style="padding-right: 20px;">Scan Database:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
36 |
<?php if(wfConfig::get('scansEnabled_posts')){ ?><tr><td style="padding-right: 20px;">Scan Posts:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
37 |
<?php if(wfConfig::get('scansEnabled_comments')){ ?><tr><td style="padding-right: 20px;">Scan Comments:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
38 |
<?php if(wfConfig::get('scansEnabled_oldVersions')){ ?><tr><td style="padding-right: 20px;">Scan for Old Software:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
32 |
<?php if(wfConfig::get('scansEnabled_themes')){ ?><tr><td style="padding-right: 20px;">Scan Themes:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
33 |
<?php if(wfConfig::get('scansEnabled_plugins')){ ?><tr><td style="padding-right: 20px;">Scan Plugins:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
34 |
<?php if(wfConfig::get('scansEnabled_fileContents')){ ?><tr><td style="padding-right: 20px;">Scan Other Files:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
|
|
35 |
<?php if(wfConfig::get('scansEnabled_posts')){ ?><tr><td style="padding-right: 20px;">Scan Posts:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
36 |
<?php if(wfConfig::get('scansEnabled_comments')){ ?><tr><td style="padding-right: 20px;">Scan Comments:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
37 |
<?php if(wfConfig::get('scansEnabled_oldVersions')){ ?><tr><td style="padding-right: 20px;">Scan for Old Software:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
lib/email_newIssues.php
CHANGED
@@ -17,7 +17,13 @@
|
|
17 |
<?php foreach($issues as $i){ if($i['severity'] == 1){ ?>
|
18 |
<p>* <?php echo htmlspecialchars($i['shortMsg']) ?></p>
|
19 |
<?php if (!empty($i['tmplData']['badURL'])): ?>
|
20 |
-
<p><img src="<?php echo
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
<?php endif ?>
|
22 |
|
23 |
<?php } } } ?>
|
@@ -33,11 +39,13 @@
|
|
33 |
|
34 |
<?php if(! $isPaid){ ?>
|
35 |
<p>NOTE: You are using the free version of Wordfence. Upgrade to Premium today for less than $5 per month!</p>
|
|
|
36 |
<ul>
|
37 |
-
<li>
|
|
|
38 |
<li>Remote, frequent and scheduled scans</li>
|
39 |
<li>Access to Premium Support</li>
|
40 |
-
<li>Discounts of up to
|
41 |
</ul>
|
42 |
|
43 |
<p>
|
17 |
<?php foreach($issues as $i){ if($i['severity'] == 1){ ?>
|
18 |
<p>* <?php echo htmlspecialchars($i['shortMsg']) ?></p>
|
19 |
<?php if (!empty($i['tmplData']['badURL'])): ?>
|
20 |
+
<p><img src="<?php echo WORDFENCE_API_URL_BASE_NONSEC . "?" . http_build_query(array(
|
21 |
+
'v' => wfUtils::getWPVersion(),
|
22 |
+
's' => home_url(),
|
23 |
+
'k' => wfConfig::get('apiKey'),
|
24 |
+
'action' => 'image',
|
25 |
+
'txt' => base64_encode($i['tmplData']['badURL'])
|
26 |
+
), '', '&') ?>" alt="" /></p>
|
27 |
<?php endif ?>
|
28 |
|
29 |
<?php } } } ?>
|
39 |
|
40 |
<?php if(! $isPaid){ ?>
|
41 |
<p>NOTE: You are using the free version of Wordfence. Upgrade to Premium today for less than $5 per month!</p>
|
42 |
+
|
43 |
<ul>
|
44 |
+
<li>Receive real-time Firewall and Scan engine rule updates for protection as threats emerge</li>
|
45 |
+
<li>Other advanced features like IP reputation monitoring, country blocking, an advanced comment spam filter and cell phone sign-in give you the best protection available</li>
|
46 |
<li>Remote, frequent and scheduled scans</li>
|
47 |
<li>Access to Premium Support</li>
|
48 |
+
<li>Discounts of up to 75% for multiyear and multi-license purchases</li>
|
49 |
</ul>
|
50 |
|
51 |
<p>
|
lib/menu_activity.php
CHANGED
@@ -1,14 +1,16 @@
|
|
1 |
-
<div class="wordfenceModeElem" id="wordfenceMode_activity"></div>
|
2 |
<div class="wrap wordfence">
|
3 |
<?php require('menuHeader.php'); ?>
|
4 |
|
5 |
<h2 id="wfHeading">
|
6 |
<div style="float: left;">
|
7 |
-
Your Site Activity in Real-Time
|
8 |
</div>
|
9 |
<div class="wordfenceWrap" style="margin: 5px 0 0 15px; float: left;">
|
10 |
<div class="wfOnOffSwitch" id="wfOnOffSwitchID">
|
11 |
-
<input type="checkbox" name="wfOnOffSwitch" class="wfOnOffSwitch-checkbox"
|
|
|
|
|
|
|
12 |
<label class="wfOnOffSwitch-label" for="wfLiveTrafficOnOff">
|
13 |
<div class="wfOnOffSwitch-inner"></div>
|
14 |
<div class="wfOnOffSwitch-switch"></div>
|
@@ -16,224 +18,429 @@
|
|
16 |
</div>
|
17 |
</div>
|
18 |
</h2>
|
19 |
-
<
|
20 |
-
|
|
|
|
|
21 |
<div class="wordfenceLive">
|
22 |
<table border="0" cellpadding="0" cellspacing="0">
|
23 |
-
|
|
|
|
|
|
|
24 |
</table>
|
25 |
</div>
|
26 |
<div class="wordfenceWrap">
|
27 |
-
|
28 |
-
|
29 |
<div style="color: #F00;">
|
30 |
Live Traffic is disabled.
|
31 |
-
<?php if(wfConfig::get('cacheType') == 'falcon'){ ?>This is done to improve performance because you have Wordfence Falcon Engine enabled.<?php } ?>
|
32 |
</div>
|
33 |
-
|
34 |
-
<div id="
|
35 |
-
|
36 |
-
<
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
<
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
</div>
|
61 |
-
|
62 |
</div>
|
63 |
</div>
|
64 |
|
65 |
-
<script type="text/x-jquery-template" id="wfLeechersTmpl">
|
66 |
-
<div>
|
67 |
-
<div style="border-bottom: 1px solid #CCC; padding-bottom: 10px; margin-bottom: 10px;">
|
68 |
-
<table border="0" style="width: 100%">
|
69 |
-
{{each(idx, elem) results}}
|
70 |
-
<tr><td>
|
71 |
-
<div>
|
72 |
-
{{if loc}}
|
73 |
-
<img src="//www.wordfence.com/images/flags/${loc.countryCode.toLowerCase()}.png" width="16" height="11" alt="${loc.countryName}" title="${loc.countryName}" class="wfFlag" />
|
74 |
-
<a href="http://maps.google.com/maps?q=${loc.lat},${loc.lon}&z=6" target="_blank">{{if loc.city}}${loc.city}, {{/if}}${loc.countryName}</a>
|
75 |
-
{{else}}
|
76 |
-
An unknown location at IP <a href="${WFAD.makeIPTrafLink(IP)}" target="_blank">${IP}</a>
|
77 |
-
{{/if}}
|
78 |
-
</div>
|
79 |
-
<div>
|
80 |
-
<strong>IP:</strong> <a href="${WFAD.makeIPTrafLink(IP)}" target="_blank">${IP}</a>
|
81 |
-
{{if elem.blocked}}
|
82 |
-
[<a href="#" onclick="WFAD.unblockIP('${IP}'); return false;">unblock</a>]
|
83 |
-
{{else}}
|
84 |
-
[<a href="#" onclick="WFAD.blockIP('${IP}', 'Manual block by administrator'); return false;">block</a>]
|
85 |
-
{{/if}}
|
86 |
-
</div>
|
87 |
-
<div>
|
88 |
-
<span class="wfReverseLookup"><span style="display:none;">${elem.IP}</span></span>
|
89 |
-
</div>
|
90 |
-
<div>
|
91 |
-
<span class="wfTimeAgo wfTimeAgo-timestamp" data-timestamp="${elem.timestamp}">Last hit was ${elem.timeAgo} ago.</span>
|
92 |
-
</div>
|
93 |
-
</td>
|
94 |
-
<td style="font-size: 28px; color: #999;">
|
95 |
-
${elem.totalHits} hits
|
96 |
-
</td>
|
97 |
-
</tr>
|
98 |
-
{{/each}}
|
99 |
-
</table>
|
100 |
-
</div>
|
101 |
-
</div>
|
102 |
-
</script>
|
103 |
-
<script type="text/x-jquery-template" id="wfLoginLogoutEventTmpl">
|
104 |
-
<div style="display: none;">
|
105 |
-
<div class="wfActEvent" id="wfActEvent_${id}">
|
106 |
-
<div>
|
107 |
-
{{if loc}}
|
108 |
-
<img src="//www.wordfence.com/images/flags/${loc.countryCode.toLowerCase()}.png" width="16" height="11" alt="${loc.countryName}" title="${loc.countryName}" class="wfFlag" />
|
109 |
-
<a href="http://maps.google.com/maps?q=${loc.lat},${loc.lon}&z=6" target="_blank">{{if loc.city}}${loc.city}, {{/if}}${loc.countryName}</a>
|
110 |
-
{{else}}
|
111 |
-
An unknown location at IP ${IP}
|
112 |
-
{{/if}}
|
113 |
-
{{if action == 'loginOK'}}
|
114 |
-
logged in successfully as <strong>"${username}"</strong>
|
115 |
-
{{else action == 'logout'}}
|
116 |
-
logged out as <strong>"${username}"</strong>
|
117 |
-
{{else action == 'loginFailValidUsername'}}
|
118 |
-
attempted a failed login as <strong>"${username}"</strong>.
|
119 |
-
{{else action == 'loginFailInvalidUsername'}}
|
120 |
-
attempted a failed login using an invalid username <strong>"${username}"</strong>.
|
121 |
-
{{/if}}
|
122 |
-
</div>
|
123 |
-
<div>
|
124 |
-
<strong>IP:</strong> <a href="${WFAD.makeIPTrafLink(IP)}" target="_blank">${IP}</a>
|
125 |
-
{{if blocked}}
|
126 |
-
[<a href="#" onclick="WFAD.unblockIP('${IP}'); return false;">unblock</a>]
|
127 |
-
{{else}}
|
128 |
-
[<a href="#" onclick="WFAD.blockIP('${IP}', 'Manual block by administrator'); return false;">block</a>]
|
129 |
-
{{/if}}
|
130 |
-
</div>
|
131 |
-
<div>
|
132 |
-
<span class="wfReverseLookup"><span style="display:none;">${IP}</span></span>
|
133 |
-
</div>
|
134 |
-
<div>
|
135 |
-
<span class="wfTimeAgo wfTimeAgo-timestamp">${timeAgo} ago</span>
|
136 |
-
</div>
|
137 |
-
</div>
|
138 |
-
</div>
|
139 |
-
</script>
|
140 |
-
<script type="text/x-jquery-template" id="wfHitsEventTmpl">
|
141 |
-
<div style="display: none;">
|
142 |
-
<div class="wfActEvent" id="wfActEvent_${id}">
|
143 |
-
<table border="0" cellpadding="1" cellspacing="0">
|
144 |
-
<tr>
|
145 |
-
<td>
|
146 |
-
{{if user}}
|
147 |
-
<span class="wfAvatar">{{html user.avatar}}</span>
|
148 |
-
<a href="${user.editLink}" target="_blank">${user.display_name}</a>
|
149 |
-
{{/if}}
|
150 |
-
{{if loc}}
|
151 |
-
{{if user}}in {{/if}}
|
152 |
-
<img src="//www.wordfence.com/images/flags/${loc.countryCode.toLowerCase()}.png" width="16" height="11" alt="${loc.countryName}" title="${loc.countryName}" class="wfFlag" />
|
153 |
-
<a href="http://maps.google.com/maps?q=${loc.lat},${loc.lon}&z=6" target="_blank">{{if loc.city}}${loc.city}, {{/if}}${loc.countryName}</a>
|
154 |
-
{{else}}
|
155 |
-
An unknown location at IP <a href="${WFAD.makeIPTrafLink(IP)}" target="_blank">${IP}</a>
|
156 |
-
{{/if}}
|
157 |
-
{{if referer}}
|
158 |
-
{{if extReferer}}
|
159 |
-
arrived from <a href="${referer}" target="_blank" style="color: #A00; font-weight: bold;">${referer}</a> and
|
160 |
-
{{else}}
|
161 |
-
left <a href="${referer}" target="_blank" style="color: #999; font-weight: normal;">${referer}</a> and
|
162 |
-
{{/if}}
|
163 |
-
{{/if}}
|
164 |
-
{{if is404 == '1'}}
|
165 |
-
tried to access <span style="color: #F00;">non-existent page</span>
|
166 |
-
{{else}}
|
167 |
-
visited
|
168 |
-
{{/if}}
|
169 |
-
<a href="${URL}" target="_blank">${URL}</a>
|
170 |
-
</td></tr>
|
171 |
-
<tr><td><span class="wfTimeAgo wfTimeAgo-timestamp">${timeAgo} ago</span> <strong>IP:</strong> <a href="${WFAD.makeIPTrafLink(IP)}" target="_blank">${IP}</a>
|
172 |
-
{{if blocked}}
|
173 |
-
[<a href="#" onclick="WFAD.unblockIP('${IP}'); return false;">unblock</a>]
|
174 |
-
{{else rangeBlocked}}
|
175 |
-
[<a href="#" onclick="WFAD.unblockNetwork('${ipRangeID}'); return false;">unblock this range</a>]
|
176 |
-
{{else}}
|
177 |
-
[<a href="#" onclick="WFAD.blockIP('${IP}', 'Manual block by administrator'); return false;">block</a>]
|
178 |
-
{{/if}}
|
179 |
-
<span class="wfReverseLookup"><span style="display:none;">${IP}</span></span>
|
180 |
-
</td></tr>
|
181 |
-
{{if browser && browser.browser != 'Default Browser'}}<tr><td><strong>Browser:</strong> ${browser.browser}{{if browser.version}} version ${browser.version}{{/if}}{{if browser.platform && browser.platform != 'unknown'}} running on ${browser.platform}{{/if}}</td></tr>{{/if}}
|
182 |
-
<tr><td style="color: #AAA;">${UA}</td></tr>
|
183 |
-
<tr><td>
|
184 |
-
{{if blocked}}
|
185 |
-
[<a href="#" onclick="WFAD.unblockIP('${IP}'); return false;">Unblock this IP</a>]
|
186 |
-
{{else rangeBlocked}}
|
187 |
-
[<a href="#" onclick="WFAD.unblockNetwork('${ipRangeID}'); return false;">Unblock this range</a>]
|
188 |
-
{{else}}
|
189 |
-
[<a href="#" onclick="WFAD.blockIP('${IP}', 'Manual block by administrator'); return false;">Block this IP</a>]
|
190 |
-
{{/if}}
|
191 |
-
—
|
192 |
-
[<a href="admin.php?page=WordfenceWhois&whoisval=${IP}&wfnetworkblock=1">Block this network</a>]
|
193 |
-
—
|
194 |
-
[<a href="admin.php?page=WordfenceWhois&whoisval=${IP}">Run WHOIS on ${IP}</a>]
|
195 |
-
—
|
196 |
-
[<a href="${WFAD.makeIPTrafLink(IP)}" target="_blank">See recent traffic</a>]
|
197 |
-
<tr><td></td></tr>
|
198 |
-
</table>
|
199 |
-
</div>
|
200 |
-
</div>
|
201 |
-
</script>
|
202 |
<script type="text/x-jquery-template" id="wfWelcomeContent3">
|
203 |
-
<div>
|
204 |
-
<h3>Welcome to ALL Your Site Visits, Live!</h3>
|
205 |
-
<strong><p>Traffic you've never seen before</p></strong>
|
206 |
-
<p>
|
207 |
-
Google Analytics and other Javascript analytics packages can't show you crawlers, RSS feed readers, hack attempts and other non-human traffic that hits your site.
|
208 |
-
Wordfence runs on your server and shows you, in real-time, all the traffic that is hitting your server right now, including those non-human crawlers, feed readers and hackers that Analytics can't track.
|
209 |
-
</p>
|
210 |
-
<strong><p>Separated into the important categories</p></strong>
|
211 |
-
<p>
|
212 |
-
You'll notice we have divided your traffic into tabs. These include an "All Hits" tab to simply view everything that is hitting your server right now.
|
213 |
-
We then sub-divide that into Human traffic, your site members, crawlers - which we further break down into Google crawlers.
|
214 |
-
</p>
|
215 |
-
<p>
|
216 |
-
<strong>How to use this page when your site is being attacked</strong>
|
217 |
-
</p>
|
218 |
-
<p>
|
219 |
-
Start by looking at "All Hits" because you may notice that a single IP address is generating most of your traffic.
|
220 |
-
This could be a denial of service attack, someone stealing your content or a hacker probing for weaknesses.
|
221 |
-
If you see a suspicious pattern, simply block that IP address. If they attack from a different IP on the same network, simply block that network.
|
222 |
-
You can also run a WHOIS on any IP address to find the host and report abuse via email.
|
223 |
-
</p>
|
224 |
-
<p>
|
225 |
-
If you don't see any clear patterns of attack, take a look at "Top 404s" which will show you IP addresses that are generating excessive page not found errors.
|
226 |
-
It's common for an attacker probing for weaknesses to generate a lot of page not found errors. If you see one IP
|
227 |
-
address that is generating many of these requests, and it's not Google or another trusted crawler, then you should consider
|
228 |
-
blocking them.
|
229 |
-
</p>
|
230 |
-
<p>
|
231 |
-
Next look at "Logins and Logouts". If you see a large number of failed logins from an IP address, block them if you don't recognize who they are.
|
232 |
-
</p>
|
233 |
-
<p>
|
234 |
-
Finally, take a look at "Top Consumers". These are the top IP addresses who are "consuming" or accessing most of your content.
|
235 |
-
If you're trying to protect yourself against a content thief, this is the first place to look.
|
236 |
-
</p>
|
237 |
|
238 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
239 |
</script>
|
|
|
1 |
<div class="wrap wordfence">
|
2 |
<?php require('menuHeader.php'); ?>
|
3 |
|
4 |
<h2 id="wfHeading">
|
5 |
<div style="float: left;">
|
6 |
+
Your Site Activity in Real-Time
|
7 |
</div>
|
8 |
<div class="wordfenceWrap" style="margin: 5px 0 0 15px; float: left;">
|
9 |
<div class="wfOnOffSwitch" id="wfOnOffSwitchID">
|
10 |
+
<input type="checkbox" name="wfOnOffSwitch" class="wfOnOffSwitch-checkbox"
|
11 |
+
id="wfLiveTrafficOnOff" <?php if (wfConfig::liveTrafficEnabled()) {
|
12 |
+
echo ' checked ';
|
13 |
+
} ?>>
|
14 |
<label class="wfOnOffSwitch-label" for="wfLiveTrafficOnOff">
|
15 |
<div class="wfOnOffSwitch-inner"></div>
|
16 |
<div class="wfOnOffSwitch-switch"></div>
|
18 |
</div>
|
19 |
</div>
|
20 |
</h2>
|
21 |
+
<a href="http://docs.wordfence.com/en/Live_traffic" target="_blank" class="wfhelp"></a><a
|
22 |
+
href="http://docs.wordfence.com/en/Live_traffic" target="_blank">Learn more about Wordfence Live Traffic</a>
|
23 |
+
|
24 |
+
<div class="wordfenceModeElem" id="wordfenceMode_activity"></div>
|
25 |
<div class="wordfenceLive">
|
26 |
<table border="0" cellpadding="0" cellspacing="0">
|
27 |
+
<tr>
|
28 |
+
<td><h2>Wordfence Live Activity:</h2></td>
|
29 |
+
<td id="wfLiveStatus"></td>
|
30 |
+
</tr>
|
31 |
</table>
|
32 |
</div>
|
33 |
<div class="wordfenceWrap">
|
34 |
+
|
35 |
+
<?php if (!wfConfig::liveTrafficEnabled()): ?>
|
36 |
<div style="color: #F00;">
|
37 |
Live Traffic is disabled.
|
38 |
+
<?php if (wfConfig::get('cacheType') == 'falcon') { ?>This is done to improve performance because you have Wordfence Falcon Engine enabled.<?php } ?>
|
39 |
</div>
|
40 |
+
<?php else: ?>
|
41 |
+
<div id="wf-live-traffic" class="wfTabsContainer">
|
42 |
+
|
43 |
+
<div id="wf-live-traffic-legend">
|
44 |
+
<ul>
|
45 |
+
<li class="wfHuman">Human</li>
|
46 |
+
<li class="wfBot">Bot</li>
|
47 |
+
<li class="wfNotice">Warning</li>
|
48 |
+
<li class="wfBlocked">Blocked</li>
|
49 |
+
</ul>
|
50 |
+
</div>
|
51 |
+
|
52 |
+
<form data-bind="submit: reloadListings">
|
53 |
+
|
54 |
+
<?php if (defined('WP_DEBUG') && WP_DEBUG): ?>
|
55 |
+
<pre data-bind="text: 'DEBUG: ' + sql(), visible: sql"></pre>
|
56 |
+
<?php endif ?>
|
57 |
+
|
58 |
+
<div class="wfActEvent">
|
59 |
+
<h2 style="float: left;padding: 0;margin: 0 10px 0 0;">Filter Traffic: </h2>
|
60 |
+
|
61 |
+
<select id="wf-lt-preset-filters" data-bind="options: presetFiltersOptions, optionsText: presetFiltersOptionsText,
|
62 |
+
value: selectedPresetFilter">
|
63 |
+
</select>
|
64 |
+
|
65 |
+
<label>
|
66 |
+
<input data-bind="checked: showAdvancedFilters" type="checkbox">
|
67 |
+
Show Advanced Filters
|
68 |
+
</label>
|
69 |
+
</div>
|
70 |
+
|
71 |
+
<div class="wfActEvent" data-bind="visible: showAdvancedFilters" id="wf-lt-advanced-filters">
|
72 |
+
<table>
|
73 |
+
<tr>
|
74 |
+
<td>
|
75 |
+
<table>
|
76 |
+
<tbody data-bind="foreach: filters">
|
77 |
+
<tr>
|
78 |
+
<td>
|
79 |
+
<select name="param[]" class="wf-lt-advanced-filters-param" data-bind="options: filterParamOptions,
|
80 |
+
optionsText: filterParamOptionsText, value: selectedFilterParamOptionValue, optionsCaption: 'Filter...'"></select>
|
81 |
+
</td>
|
82 |
+
<td data-bind="visible: selectedFilterParamOptionValue() && selectedFilterParamOptionValue().type() != 'bool'">
|
83 |
+
<select name="operator[]" class="wf-lt-advanced-filters-operator"
|
84 |
+
data-bind="options: filterOperatorOptions,
|
85 |
+
optionsText: filterOperatorOptionsText, value: selectedFilterOperatorOptionValue"></select>
|
86 |
+
</td>
|
87 |
+
<td data-bind="attr: {colSpan: (selectedFilterParamOptionValue() &&
|
88 |
+
selectedFilterParamOptionValue().type() == 'bool' ? 2 : 1)}"
|
89 |
+
class="wf-lt-advanced-filters-value-cell">
|
90 |
+
|
91 |
+
<span
|
92 |
+
data-bind="if: selectedFilterParamOptionValue() && selectedFilterParamOptionValue().type() == 'enum'">
|
93 |
+
<select
|
94 |
+
data-bind="options: selectedFilterParamOptionValue().values,
|
95 |
+
optionsText: selectedFilterParamOptionValue().optionsText,
|
96 |
+
value: value"></select>
|
97 |
+
</span>
|
98 |
+
|
99 |
+
<span
|
100 |
+
data-bind="if: selectedFilterParamOptionValue() && selectedFilterParamOptionValue().type() == 'text'">
|
101 |
+
<input data-bind="value: value" type="text"/>
|
102 |
+
</span>
|
103 |
+
|
104 |
+
<span
|
105 |
+
data-bind="if: selectedFilterParamOptionValue() && selectedFilterParamOptionValue().type() == 'bool'">
|
106 |
+
<label>Yes <input data-bind="checked: value" type="radio"
|
107 |
+
value="1"></label>
|
108 |
+
<label>No <input data-bind="checked: value" type="radio"
|
109 |
+
value="0"></label>
|
110 |
+
</span>
|
111 |
+
|
112 |
+
</td>
|
113 |
+
<td>
|
114 |
+
<button data-bind="click: $root.removeFilter" type="button"
|
115 |
+
class="button">
|
116 |
+
Remove
|
117 |
+
</button>
|
118 |
+
</td>
|
119 |
+
</tr>
|
120 |
+
</tbody>
|
121 |
+
<tbody>
|
122 |
+
<tr>
|
123 |
+
<td colspan="3">
|
124 |
+
<div class="wf-pad-small">
|
125 |
+
<button type="button" class="button" data-bind="click: addFilter">
|
126 |
+
Add Filter
|
127 |
+
</button>
|
128 |
+
</div>
|
129 |
+
</td>
|
130 |
+
</tr>
|
131 |
+
</tbody>
|
132 |
+
</table>
|
133 |
+
</td>
|
134 |
+
<td>
|
135 |
+
<table>
|
136 |
+
<tbody>
|
137 |
+
<tr>
|
138 |
+
<td>
|
139 |
+
<label for="wf-live-traffic-from">From: </label>
|
140 |
+
</td>
|
141 |
+
<td><input placeholder="Start date" id="wf-live-traffic-from" type="text"
|
142 |
+
class="wf-datetime"
|
143 |
+
data-bind="value: startDate, datetimepicker: null, datepickerOptions: { timeFormat: 'hh:mm tt z' }">
|
144 |
+
</td>
|
145 |
+
<td>
|
146 |
+
<button data-bind="click: startDate('')" class="button small"
|
147 |
+
type="button">
|
148 |
+
Clear
|
149 |
+
</button>
|
150 |
+
</td>
|
151 |
+
</tr>
|
152 |
+
<tr>
|
153 |
+
<td>
|
154 |
+
<label for="wf-live-traffic-to">To: </label>
|
155 |
+
</td>
|
156 |
+
<td><input placeholder="End date" id="wf-live-traffic-to" type="text"
|
157 |
+
class="wf-datetime"
|
158 |
+
data-bind="value: endDate, datetimepicker: null, datepickerOptions: { timeFormat: 'hh:mm tt z' }">
|
159 |
+
</td>
|
160 |
+
<td>
|
161 |
+
<button data-bind="click: endDate('')" class="button small"
|
162 |
+
type="button">
|
163 |
+
Clear
|
164 |
+
</button>
|
165 |
+
</td>
|
166 |
+
</tr>
|
167 |
+
<tr>
|
168 |
+
<td>
|
169 |
+
<label for="wf-live-traffic-group-by">Group By: </label>
|
170 |
+
</td>
|
171 |
+
<td>
|
172 |
+
<select id="wf-live-traffic-group-by" name="groupby"
|
173 |
+
class="wf-lt-advanced-filters-groupby"
|
174 |
+
data-bind="options: filterGroupByOptions,
|
175 |
+
optionsText: filterGroupByOptionsText, value: groupBy, optionsCaption: 'None'"></select>
|
176 |
+
</td>
|
177 |
+
</tr>
|
178 |
+
</tbody>
|
179 |
+
</table>
|
180 |
+
</td>
|
181 |
+
</tr>
|
182 |
+
</table>
|
183 |
+
</div>
|
184 |
+
</form>
|
185 |
+
|
186 |
+
<table data-bind="if: groupBy()" border="0" style="width: 100%">
|
187 |
+
<tbody data-bind="foreach: listings">
|
188 |
+
<tr>
|
189 |
+
<td>
|
190 |
+
<div data-bind="if: loc()">
|
191 |
+
<img data-bind="attr: { src: '//www.wordfence.com/images/flags/' + loc().countryCode.toLowerCase() + '.png',
|
192 |
+
alt: loc().countryName, title: loc().countryName }" width="16" height="11"
|
193 |
+
class="wfFlag"/>
|
194 |
+
<a data-bind="text: (loc().city ? loc().city + ', ' : '') + loc().countryName,
|
195 |
+
attr: { href: 'http://maps.google.com/maps?q=' + loc().lat + ',' + loc().lon + '&z=6' }"
|
196 |
+
target="_blank"></a>
|
197 |
+
</div>
|
198 |
+
<div data-bind="if: !loc()">
|
199 |
+
An unknown location at IP <a
|
200 |
+
data-bind="text: IP, attr: { href: WFAD.makeIPTrafLink(IP()) }" target="_blank"></a>
|
201 |
+
</div>
|
202 |
+
|
203 |
+
<div>
|
204 |
+
<strong>IP:</strong> <a
|
205 |
+
data-bind="text: IP, attr: { href: WFAD.makeIPTrafLink(IP()) }" target="_blank"></a>
|
206 |
+
<span data-bind="if: blocked()">
|
207 |
+
[<a data-bind="click: $root.unblockIP">unblock</a>]
|
208 |
+
</span>
|
209 |
+
<span data-bind="if: rangeBlocked()">
|
210 |
+
[<a data-bind="click: $root.unblockNetwork">unblock this range</a>]
|
211 |
+
</span>
|
212 |
+
<span data-bind="if: !blocked() && !rangeBlocked()">
|
213 |
+
[<a data-bind="click: $root.blockIP">block</a>]
|
214 |
+
</span>
|
215 |
+
</div>
|
216 |
+
<div>
|
217 |
+
<span class="wfReverseLookup"><span data-bind="text: IP"
|
218 |
+
style="display:none;"></span></span>
|
219 |
+
</div>
|
220 |
+
<div>
|
221 |
+
<span
|
222 |
+
data-bind="attr: { 'data-timestamp': ctime, text: 'Last hit was ' + ctime() + ' ago.' }"
|
223 |
+
class="wfTimeAgo wfTimeAgo-timestamp"></span>
|
224 |
+
</div>
|
225 |
+
</td>
|
226 |
+
<td style="font-size: 28px; color: #999;">
|
227 |
+
<span data-bind="text: hitCount"></span> hits
|
228 |
+
</td>
|
229 |
+
</tr>
|
230 |
+
</tbody>
|
231 |
+
</table>
|
232 |
+
|
233 |
+
<div data-bind="if: !groupBy()">
|
234 |
+
<div id="wf-lt-listings" data-bind="foreach: listings">
|
235 |
+
<div data-bind="attr: { id: ('wfActEvent_' + id()), 'class': cssClasses }">
|
236 |
+
<table border="0" cellpadding="1" cellspacing="0">
|
237 |
+
<tr>
|
238 |
+
<td>
|
239 |
+
<span data-bind="if: action() != 'loginOK' && user()">
|
240 |
+
<span data-bind="html: user.avatar" class="wfAvatar"></span>
|
241 |
+
<a data-bind="attr: { href: user.editLink }, text: user().display_name"
|
242 |
+
target="_blank"></a>
|
243 |
+
</span>
|
244 |
+
<span data-bind="if: loc()">
|
245 |
+
<span data-bind="if: action() != 'loginOK' && user()"> in</span>
|
246 |
+
<img data-bind="attr: { src: '//www.wordfence.com/images/flags/' + loc().countryCode.toLowerCase() + '.png',
|
247 |
+
alt: loc().countryName, title: loc().countryName }" width="16"
|
248 |
+
height="11"
|
249 |
+
class="wfFlag"/>
|
250 |
+
<a data-bind="text: (loc().city ? loc().city + ', ' : '') + loc().countryName,
|
251 |
+
attr: { href: 'http://maps.google.com/maps?q=' + loc().lat + ',' + loc().lon + '&z=6' }"
|
252 |
+
target="_blank"></a>
|
253 |
+
</span>
|
254 |
+
<span data-bind="if: !loc()">
|
255 |
+
<span
|
256 |
+
data-bind="text: action() != 'loginOK' && user() ? 'at an' : 'An'"></span> unknown location at IP <a
|
257 |
+
data-bind="text: IP, attr: { href: WFAD.makeIPTrafLink(IP()) }"
|
258 |
+
target="_blank"></a>
|
259 |
+
</span>
|
260 |
+
<span data-bind="if: referer()">
|
261 |
+
<span data-bind="if: extReferer()">
|
262 |
+
arrived from <a data-bind="text: referer, attr: { href: referer }"
|
263 |
+
target="_blank"
|
264 |
+
style="color: #A00; font-weight: bold;"></a> and
|
265 |
+
</span>
|
266 |
+
<span data-bind="if: !extReferer()">
|
267 |
+
left <a data-bind="text: referer, attr: { href: referer }"
|
268 |
+
target="_blank"
|
269 |
+
style="color: #999; font-weight: normal;"></a> and
|
270 |
+
</span>
|
271 |
+
</span>
|
272 |
+
<span data-bind="if: statusCode() == 404">
|
273 |
+
tried to access <span style="color: #F00;">non-existent page</span>
|
274 |
+
</span>
|
275 |
+
|
276 |
+
<span data-bind="if: statusCode() == 200 && !action()">
|
277 |
+
visited
|
278 |
+
</span>
|
279 |
+
<span data-bind="if: statusCode() == 403">
|
280 |
+
was <span data-bind="text: firewallAction" style="color: #F00;"></span> at
|
281 |
+
</span>
|
282 |
+
|
283 |
+
<span data-bind="if: action() == 'loginOK'">
|
284 |
+
logged in successfully as "<strong data-bind="text: username"></strong>".
|
285 |
+
</span>
|
286 |
+
<span data-bind="if: action() == 'logout'">
|
287 |
+
logged out successfully.
|
288 |
+
</span>
|
289 |
+
<span data-bind="if: action() == 'lostPassword'">
|
290 |
+
requested a password reset.
|
291 |
+
</span>
|
292 |
+
<span data-bind="if: action() == 'loginFailValidUsername'">
|
293 |
+
attempted a failed login as "<strong data-bind="text: username"></strong>".
|
294 |
+
</span>
|
295 |
+
<span data-bind="if: action() == 'loginFailInvalidUsername'">
|
296 |
+
attempted a failed login using an invalid username "<strong
|
297 |
+
data-bind="text: username"></strong>".
|
298 |
+
</span>
|
299 |
+
<span data-bind="if: action() == 'user:passwordReset'">
|
300 |
+
changed their password.
|
301 |
+
</span>
|
302 |
+
<a class="wf-lt-url"
|
303 |
+
data-bind="text: displayURL, attr: { href: URL, title: URL }"
|
304 |
+
target="_blank"></a>
|
305 |
+
</td>
|
306 |
+
</tr>
|
307 |
+
<tr>
|
308 |
+
<td><span data-bind="text: timeAgo, attr: { 'data-timestamp': ctime }"
|
309 |
+
class="wfTimeAgo wfTimeAgo-timestamp"></span>
|
310 |
+
<strong>IP:</strong> <a
|
311 |
+
data-bind="attr: { href: WFAD.makeIPTrafLink(IP()) }, text: IP"
|
312 |
+
target="_blank"></a>
|
313 |
+
<span data-bind="if: blocked()">
|
314 |
+
[<a data-bind="click: $root.unblockIP">unblock</a>]
|
315 |
+
</span>
|
316 |
+
<span data-bind="if: rangeBlocked()">
|
317 |
+
[<a data-bind="click: $root.unblockNetwork">unblock this range</a>]
|
318 |
+
</span>
|
319 |
+
<span data-bind="if: !blocked() && !rangeBlocked()">
|
320 |
+
[<a data-bind="click: $root.blockIP">block</a>]
|
321 |
+
</span>
|
322 |
+
|
323 |
+
<span class="wfReverseLookup">
|
324 |
+
<span data-bind="text: IP"
|
325 |
+
style="display:none;"></span>
|
326 |
+
</span>
|
327 |
+
</td>
|
328 |
+
</tr>
|
329 |
+
|
330 |
+
<tr data-bind="if: browser() && browser().browser != 'Default Browser'">
|
331 |
+
<td>
|
332 |
+
<strong>Browser:</strong>
|
333 |
+
<span data-bind="text: browser().browser +
|
334 |
+
(browser().version ? ' version ' + browser().version : '') +
|
335 |
+
(browser().platform && browser().platform != 'unknown' ? ' running on ' + browser().platform : '')
|
336 |
+
">
|
337 |
+
</span>
|
338 |
+
</td>
|
339 |
+
</tr>
|
340 |
+
<tr>
|
341 |
+
<td data-bind="text: UA" style="color: #AAA;"></td>
|
342 |
+
</tr>
|
343 |
+
<tr>
|
344 |
+
<td>
|
345 |
+
<span data-bind="if: blocked()">
|
346 |
+
<button type="button" class="button button-small"
|
347 |
+
data-bind="click: $root.unblockIP">
|
348 |
+
Unblock this IP
|
349 |
+
</button>
|
350 |
+
</span>
|
351 |
+
<span data-bind="if: rangeBlocked()">
|
352 |
+
<button type="button" class="button button-small"
|
353 |
+
data-bind="click: $root.unblockNetwork">Unblock this range
|
354 |
+
</button>
|
355 |
+
</span>
|
356 |
+
<span data-bind="if: !blocked() && !rangeBlocked()">
|
357 |
+
<button type="button" class="button button-small"
|
358 |
+
data-bind="click: $root.blockIP">
|
359 |
+
Block this IP
|
360 |
+
</button>
|
361 |
+
</span>
|
362 |
+
<button type="button" class="button button-small"
|
363 |
+
data-bind="click: function() { location = 'admin.php?page=WordfenceWhois&whoisval=' + IP() + '&wfnetworkblock=1' }">
|
364 |
+
Block this network
|
365 |
+
</button>
|
366 |
+
<button type="button" class="button button-small" data-bind="text: 'Run WHOIS on ' + IP(),
|
367 |
+
click: function() { window.open('admin.php?page=WordfenceWhois&whoisval=' + IP()) }"
|
368 |
+
target="_blank"></button>
|
369 |
+
<button type="button" class="button button-small"
|
370 |
+
data-bind="click: function() { window.open(WFAD.makeIPTrafLink(IP())) }">
|
371 |
+
See
|
372 |
+
recent traffic
|
373 |
+
</button>
|
374 |
+
<span data-bind="if: action() == 'blocked:waf'">
|
375 |
+
<button type="button" class="button button-small"
|
376 |
+
data-bind="click: function () { $root.whitelistWAFParamKey(actionData().path, actionData().paramKey, actionData().failedRules) }"
|
377 |
+
title="If this is a false positive, you can exclude this parameter from being filtered by the firewall">
|
378 |
+
Whitelist param from Firewall
|
379 |
+
</button>
|
380 |
+
<?php if (WFWAF_DEBUG): ?>
|
381 |
+
<button type="button" class="button button-small"
|
382 |
+
data-bind="click: function() { window.open('<?php echo esc_js(home_url()) ?>?_wfsf=debugWAF&nonce=' + WFAD.nonce + '&hitid=' + id(), 'debugWAF');}">
|
383 |
+
Debug this Request
|
384 |
+
</button>
|
385 |
+
<?php endif ?>
|
386 |
+
</span>
|
387 |
+
</td>
|
388 |
+
</tr>
|
389 |
+
</table>
|
390 |
+
</div>
|
391 |
+
</div>
|
392 |
+
</div>
|
393 |
+
<div data-bind="if: !listings">
|
394 |
+
No events to report yet.
|
395 |
+
</div>
|
396 |
</div>
|
397 |
+
<?php endif ?>
|
398 |
</div>
|
399 |
</div>
|
400 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
401 |
<script type="text/x-jquery-template" id="wfWelcomeContent3">
|
402 |
+
<div>
|
403 |
+
<h3>Welcome to ALL Your Site Visits, Live!</h3>
|
404 |
+
<strong><p>Traffic you've never seen before</p></strong>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
405 |
|
406 |
+
<p>
|
407 |
+
Google Analytics and other Javascript analytics packages can't show you crawlers, RSS feed readers, hack
|
408 |
+
attempts and other non-human traffic that hits your site.
|
409 |
+
Wordfence runs on your server and shows you, in real-time, all the traffic that is hitting your server right
|
410 |
+
now, including those non-human crawlers, feed readers and hackers that Analytics can't track.
|
411 |
+
</p>
|
412 |
+
<strong><p>Separated into the important categories</p></strong>
|
413 |
+
|
414 |
+
<p>
|
415 |
+
You'll notice that you can filter traffic. The options include "All Hits" to simply view everything that is
|
416 |
+
hitting your server right now. We then sub-divide that into human visits, your site members, crawlers -
|
417 |
+
which we further break down into Google crawlers - and various other choices.
|
418 |
+
</p>
|
419 |
+
|
420 |
+
<p>
|
421 |
+
<strong>How to use this page when your site is being attacked</strong>
|
422 |
+
</p>
|
423 |
+
|
424 |
+
<p>
|
425 |
+
Start by looking at "All Hits" because you may notice that a single IP address is generating most of your
|
426 |
+
traffic.
|
427 |
+
This could be a denial of service attack, someone stealing your content or a hacker probing for weaknesses.
|
428 |
+
If you see a suspicious pattern, simply block that IP address. If they attack from a different IP on the
|
429 |
+
same network, simply block that network.
|
430 |
+
You can also run a WHOIS on any IP address to find the host and report abuse via email.
|
431 |
+
</p>
|
432 |
+
|
433 |
+
<p>
|
434 |
+
If you don't see any clear patterns of attack, take a look at "Pages Not Found" which will show you IP
|
435 |
+
addresses that are generating excessive page not found errors. It's common for an attacker probing for
|
436 |
+
weaknesses to generate a lot of these errors. If you see one IP address that is generating many of these
|
437 |
+
requests, and it's not Google or another trusted crawler, then you should consider blocking them.
|
438 |
+
</p>
|
439 |
+
|
440 |
+
<p>
|
441 |
+
Next look at "Logins and Logouts". If you see a large number of failed logins from an IP address, block them
|
442 |
+
if you don't recognize who they are.
|
443 |
+
</p>
|
444 |
+
|
445 |
+
</div>
|
446 |
</script>
|
lib/menu_countryBlocking.php
CHANGED
@@ -11,16 +11,16 @@ WFAD.countryMap = <?php echo json_encode($wfBulkCountries); ?>;
|
|
11 |
<?php if(! wfConfig::get('isPaid')){ ?>
|
12 |
<div class="wf-premium-callout" style="margin: 20px">
|
13 |
<h3>Country Blocking is only available to Premium Members</h3>
|
14 |
-
<p>Country
|
15 |
-
|
|
|
16 |
<ul>
|
17 |
-
<li>
|
18 |
-
<li>Other advanced features like IP reputation monitoring, an advanced comment spam filter, advanced
|
19 |
-
scanning options and cell phone sign-in give you the best protection available
|
20 |
-
</li>
|
21 |
<li>Access to Premium Support</li>
|
22 |
-
<li>Discounts of up to
|
23 |
</ul>
|
|
|
24 |
<p class="center"><a class="button button-primary"
|
25 |
href="https://www.wordfence.com/gnl1countryBlock1/wordfence-signup/">Get Premium</a></p>
|
26 |
</div>
|
11 |
<?php if(! wfConfig::get('isPaid')){ ?>
|
12 |
<div class="wf-premium-callout" style="margin: 20px">
|
13 |
<h3>Country Blocking is only available to Premium Members</h3>
|
14 |
+
<p>Country blocking is a premium feature that lets you block attacks or malicious activity that originates in a specific country</p>
|
15 |
+
|
16 |
+
<p>Upgrade to Premium today for less than $5 per month:</p>
|
17 |
<ul>
|
18 |
+
<li>Receive real-time Firewall and Scan engine rule updates for protection as threats emerge</li>
|
19 |
+
<li>Other advanced features like IP reputation monitoring, an advanced comment spam filter, advanced scanning options and cell phone sign-in give you the best protection available</li>
|
|
|
|
|
20 |
<li>Access to Premium Support</li>
|
21 |
+
<li>Discounts of up to 75% available for multiyear and multi-license purchases</li>
|
22 |
</ul>
|
23 |
+
|
24 |
<p class="center"><a class="button button-primary"
|
25 |
href="https://www.wordfence.com/gnl1countryBlock1/wordfence-signup/">Get Premium</a></p>
|
26 |
</div>
|
lib/menu_diagnostic.php
ADDED
@@ -0,0 +1,404 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
$diagnostic = new wfDiagnostic;
|
3 |
+
$plugins = get_plugins();
|
4 |
+
$activePlugins = array_flip(get_option('active_plugins'));
|
5 |
+
$activeNetworkPlugins = is_multisite() ? array_flip(wp_get_active_network_plugins()) : array();
|
6 |
+
$muPlugins = get_mu_plugins();
|
7 |
+
$themes = wp_get_themes();
|
8 |
+
$currentTheme = wp_get_theme();
|
9 |
+
$cols = 3;
|
10 |
+
|
11 |
+
$w = new wfConfig();
|
12 |
+
?>
|
13 |
+
|
14 |
+
<div class="wrap wordfence">
|
15 |
+
<?php require('menuHeader.php'); ?>
|
16 |
+
<h2 id="wfHeading">
|
17 |
+
Diagnostics
|
18 |
+
</h2>
|
19 |
+
<br clear="both"/>
|
20 |
+
|
21 |
+
<form id="wfConfigForm">
|
22 |
+
<table class="wf-table"<?php echo !empty($inEmail) ? ' border=1' : '' ?>>
|
23 |
+
<?php foreach ($diagnostic->getResults() as $title => $tests): ?>
|
24 |
+
<tbody class="thead">
|
25 |
+
<tr>
|
26 |
+
<th colspan="<?php echo $cols ?>"><?php echo esc_html($title) ?></th>
|
27 |
+
</tr>
|
28 |
+
</tbody>
|
29 |
+
<tbody>
|
30 |
+
<?php foreach ($tests as $result): ?>
|
31 |
+
<tr>
|
32 |
+
<td style="width: 75%;"
|
33 |
+
colspan="<?php echo $cols - 1 ?>"><?php echo wp_kses($result['label'], array(
|
34 |
+
'code' => array(),
|
35 |
+
'strong' => array(),
|
36 |
+
'em' => array(),
|
37 |
+
'a' => array('href' => true),
|
38 |
+
)) ?></td>
|
39 |
+
<?php if ($result['test']): ?>
|
40 |
+
<td class="success"><?php echo esc_html($result['message']) ?></td>
|
41 |
+
<?php else: ?>
|
42 |
+
<td class="error"><?php echo esc_html($result['message']) ?></td>
|
43 |
+
<?php endif ?>
|
44 |
+
</tr>
|
45 |
+
<?php endforeach ?>
|
46 |
+
</tbody>
|
47 |
+
<tbody class="empty-row">
|
48 |
+
<tr>
|
49 |
+
<td colspan="<?php echo $cols ?>"></td>
|
50 |
+
</tr>
|
51 |
+
</tbody>
|
52 |
+
<?php endforeach ?>
|
53 |
+
|
54 |
+
<tbody class="thead">
|
55 |
+
<tr>
|
56 |
+
<th>IPs</th>
|
57 |
+
<th>Value</th>
|
58 |
+
<th>Used</th>
|
59 |
+
</tr>
|
60 |
+
</tbody>
|
61 |
+
<tbody>
|
62 |
+
<?php
|
63 |
+
$howGet = wfConfig::get('howGetIPs', false);
|
64 |
+
list($currentIP, $currentServerVarForIP) = wfUtils::getIPAndServerVarible();
|
65 |
+
foreach (array(
|
66 |
+
'REMOTE_ADDR' => 'REMOTE_ADDR',
|
67 |
+
'HTTP_CF_CONNECTING_IP' => 'CF-Connecting-IP',
|
68 |
+
'HTTP_X_REAL_IP' => 'X-Real-IP',
|
69 |
+
'HTTP_X_FORWARDED_FOR' => 'X-Forwarded-For',
|
70 |
+
) as $variable => $label): ?>
|
71 |
+
<tr>
|
72 |
+
<td><?php echo $label ?></td>
|
73 |
+
<td><?php echo esc_html(array_key_exists($variable, $_SERVER) ? $_SERVER[$variable] : '(not set)') ?></td>
|
74 |
+
<?php if ($currentServerVarForIP && $currentServerVarForIP === $variable): ?>
|
75 |
+
<td class="success">In use</td>
|
76 |
+
<?php elseif ($howGet === $variable): ?>
|
77 |
+
<td class="error">Configured, but not valid</td>
|
78 |
+
<?php else: ?>
|
79 |
+
<td></td>
|
80 |
+
<?php endif ?>
|
81 |
+
</tr>
|
82 |
+
<?php endforeach ?>
|
83 |
+
</tbody>
|
84 |
+
<tbody class="empty-row">
|
85 |
+
<tr>
|
86 |
+
<td colspan="<?php echo $cols ?>"></td>
|
87 |
+
</tr>
|
88 |
+
</tbody>
|
89 |
+
|
90 |
+
<tbody class="thead">
|
91 |
+
<tr>
|
92 |
+
<th colspan="<?php echo $cols ?>">WordPress Plugins</th>
|
93 |
+
</tr>
|
94 |
+
</tbody>
|
95 |
+
<tbody>
|
96 |
+
<?php foreach ($plugins as $plugin => $pluginData): ?>
|
97 |
+
<tr>
|
98 |
+
<td colspan="<?php echo $cols - 1 ?>"><strong><?php echo esc_html($pluginData['Name']) ?></strong>
|
99 |
+
<?php if (!empty($pluginData['Version'])): ?>
|
100 |
+
- Version <?php echo esc_html($pluginData['Version']) ?>
|
101 |
+
<?php endif ?>
|
102 |
+
</td>
|
103 |
+
<?php if (array_key_exists(trailingslashit(WP_PLUGIN_DIR) . $plugin, $activeNetworkPlugins)): ?>
|
104 |
+
<td class="success">Network Activated</td>
|
105 |
+
<?php elseif (array_key_exists($plugin, $activePlugins)): ?>
|
106 |
+
<td class="success">Active</td>
|
107 |
+
<?php else: ?>
|
108 |
+
<td class="inactive">Inactive</td>
|
109 |
+
<?php endif ?>
|
110 |
+
</tr>
|
111 |
+
<?php endforeach ?>
|
112 |
+
</tbody>
|
113 |
+
|
114 |
+
<tbody class="empty-row">
|
115 |
+
<tr>
|
116 |
+
<td colspan="<?php echo $cols ?>"></td>
|
117 |
+
</tr>
|
118 |
+
</tbody>
|
119 |
+
<tbody class="thead">
|
120 |
+
<tr>
|
121 |
+
<th colspan="<?php echo $cols ?>">Must-Use WordPress Plugins</th>
|
122 |
+
</tr>
|
123 |
+
</tbody>
|
124 |
+
<?php if (!empty($muPlugins)): ?>
|
125 |
+
<tbody>
|
126 |
+
<?php foreach ($muPlugins as $plugin => $pluginData): ?>
|
127 |
+
<tr>
|
128 |
+
<td colspan="<?php echo $cols - 1 ?>">
|
129 |
+
<strong><?php echo esc_html($pluginData['Name']) ?></strong>
|
130 |
+
<?php if (!empty($pluginData['Version'])): ?>
|
131 |
+
- Version <?php echo esc_html($pluginData['Version']) ?>
|
132 |
+
<?php endif ?>
|
133 |
+
</td>
|
134 |
+
<td class="success">Active</td>
|
135 |
+
</tr>
|
136 |
+
<?php endforeach ?>
|
137 |
+
</tbody>
|
138 |
+
<?php else: ?>
|
139 |
+
<tbody>
|
140 |
+
<tr>
|
141 |
+
<td colspan="<?php echo $cols ?>">No MU-Plugins</td>
|
142 |
+
</tr>
|
143 |
+
</tbody>
|
144 |
+
|
145 |
+
<?php endif ?>
|
146 |
+
|
147 |
+
<tbody class="empty-row">
|
148 |
+
<tr>
|
149 |
+
<td colspan="<?php echo $cols ?>"></td>
|
150 |
+
</tr>
|
151 |
+
</tbody>
|
152 |
+
<tbody class="thead">
|
153 |
+
<tr>
|
154 |
+
<th colspan="<?php echo $cols ?>">Themes</th>
|
155 |
+
</tr>
|
156 |
+
</tbody>
|
157 |
+
<?php if (!empty($themes)): ?>
|
158 |
+
<tbody>
|
159 |
+
<?php foreach ($themes as $theme => $themeData): ?>
|
160 |
+
<tr>
|
161 |
+
<td colspan="<?php echo $cols - 1 ?>">
|
162 |
+
<strong><?php echo esc_html($themeData['Name']) ?></strong>
|
163 |
+
Version <?php echo esc_html($themeData['Version']) ?></td>
|
164 |
+
<?php if ($currentTheme instanceof WP_Theme && $theme === $currentTheme->get_stylesheet()): ?>
|
165 |
+
<td class="success">Active</td>
|
166 |
+
<?php else: ?>
|
167 |
+
<td class="inactive">Inactive</td>
|
168 |
+
<?php endif ?>
|
169 |
+
</tr>
|
170 |
+
<?php endforeach ?>
|
171 |
+
</tbody>
|
172 |
+
<?php else: ?>
|
173 |
+
<tbody>
|
174 |
+
<tr>
|
175 |
+
<td colspan="<?php echo $cols ?>">No MU-Plugins</td>
|
176 |
+
</tr>
|
177 |
+
</tbody>
|
178 |
+
|
179 |
+
<?php endif ?>
|
180 |
+
|
181 |
+
<tbody class="empty-row">
|
182 |
+
<tr>
|
183 |
+
<td colspan="<?php echo $cols ?>"></td>
|
184 |
+
</tr>
|
185 |
+
</tbody>
|
186 |
+
<tbody class="thead">
|
187 |
+
<tr>
|
188 |
+
<th colspan="<?php echo $cols ?>">Cron Jobs</th>
|
189 |
+
</tr>
|
190 |
+
</tbody>
|
191 |
+
<tbody>
|
192 |
+
<?php
|
193 |
+
$cron = _get_cron_array();
|
194 |
+
|
195 |
+
foreach ($cron as $timestamp => $values) {
|
196 |
+
if (is_array($values)) {
|
197 |
+
foreach ($values as $cron_job => $v) {
|
198 |
+
if (is_numeric($timestamp)) {
|
199 |
+
?>
|
200 |
+
<tr>
|
201 |
+
<td colspan="<?php echo $cols - 1 ?>"><?php echo esc_html(date('r', $timestamp)) ?></td>
|
202 |
+
<td><?php echo esc_html($cron_job) ?></td>
|
203 |
+
</tr>
|
204 |
+
<?php
|
205 |
+
}
|
206 |
+
}
|
207 |
+
}
|
208 |
+
}
|
209 |
+
?>
|
210 |
+
</tbody>
|
211 |
+
</table>
|
212 |
+
<?php
|
213 |
+
$wfdb = new wfDB();
|
214 |
+
$q = $wfdb->querySelect("show table status");
|
215 |
+
if ($q):
|
216 |
+
$databaseCols = count($q[0]);
|
217 |
+
?>
|
218 |
+
<div style="max-width: 100%; overflow: auto; padding: 1px;">
|
219 |
+
<table class="wf-table"<?php echo !empty($inEmail) ? ' border=1' : '' ?>>
|
220 |
+
<tbody class="empty-row">
|
221 |
+
<tr>
|
222 |
+
<td colspan="<?php echo $databaseCols ?>"></td>
|
223 |
+
</tr>
|
224 |
+
</tbody>
|
225 |
+
<tbody class="thead">
|
226 |
+
<tr>
|
227 |
+
<th colspan="<?php echo $databaseCols ?>">Database Tables</th>
|
228 |
+
</tr>
|
229 |
+
</tbody>
|
230 |
+
<tbody class="thead thead-subhead" style="font-size: 85%">
|
231 |
+
<?php
|
232 |
+
$val = array_shift($q);
|
233 |
+
?>
|
234 |
+
<tr>
|
235 |
+
<?php foreach ($val as $tkey => $tval): ?>
|
236 |
+
<th><?php echo esc_html($tkey) ?></th>
|
237 |
+
<?php endforeach; ?>
|
238 |
+
</tr>
|
239 |
+
</tbody>
|
240 |
+
<tbody style="font-size: 85%">
|
241 |
+
<?php
|
242 |
+
foreach ($q as $val): ?>
|
243 |
+
<tr>
|
244 |
+
<?php foreach ($val as $tkey => $tval): ?>
|
245 |
+
<td><?php echo esc_html($tval) ?></td>
|
246 |
+
<?php endforeach; ?>
|
247 |
+
</tr>
|
248 |
+
<?php endforeach; ?>
|
249 |
+
</tbody>
|
250 |
+
|
251 |
+
</table>
|
252 |
+
</div>
|
253 |
+
<?php endif ?>
|
254 |
+
</form>
|
255 |
+
</div>
|
256 |
+
|
257 |
+
<?php if (!empty($inEmail)): ?>
|
258 |
+
<?php phpinfo(); ?>
|
259 |
+
<?php endif ?>
|
260 |
+
|
261 |
+
<?php if (!empty($emailForm)): ?>
|
262 |
+
<h3>Other Tests</h3>
|
263 |
+
|
264 |
+
<ul>
|
265 |
+
<li>
|
266 |
+
<a href="<?php echo wfUtils::siteURLRelative(); ?>?_wfsf=sysinfo&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>"
|
267 |
+
target="_blank">Click to view your system's configuration in a new window</a>
|
268 |
+
<a href="http://docs.wordfence.com/en/Wordfence_options#Click_to_view_your_system.27s_configuration_in_a_new_window"
|
269 |
+
target="_blank" class="wfhelp"></a></li>
|
270 |
+
<li>
|
271 |
+
<a href="<?php echo wfUtils::siteURLRelative(); ?>?_wfsf=testmem&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>"
|
272 |
+
target="_blank">Test your WordPress host's available memory</a>
|
273 |
+
<a href="http://docs.wordfence.com/en/Wordfence_options#Test_your_WordPress_host.27s_available_memory"
|
274 |
+
target="_blank" class="wfhelp"></a>
|
275 |
+
</li>
|
276 |
+
<li>
|
277 |
+
Send a test email from this WordPress server to an email address:<a
|
278 |
+
href="http://docs.wordfence.com/en/Wordfence_options#Send_a_test_email_from_this_WordPress_server_to_an_email_address"
|
279 |
+
target="_blank" class="wfhelp"></a>
|
280 |
+
<input type="text" id="testEmailDest" value="" size="20" maxlength="255" class="wfConfigElem"/>
|
281 |
+
<input class="button" type="button" value="Send Test Email"
|
282 |
+
onclick="WFAD.sendTestEmail(jQuery('#testEmailDest').val());"/>
|
283 |
+
</li>
|
284 |
+
</ul>
|
285 |
+
|
286 |
+
<div id="sendByEmailThanks" class="hidden">
|
287 |
+
<h3>Thanks for sending your diagnostic page over email</h3>
|
288 |
+
</div>
|
289 |
+
<div id="sendByEmailDiv">
|
290 |
+
<h3>Send Report by Email</h3>
|
291 |
+
|
292 |
+
<div id="sendByEmailForm" class="hidden">
|
293 |
+
<table class="wfConfigForm">
|
294 |
+
<tr>
|
295 |
+
<th>Email address:</th>
|
296 |
+
<td><input type="email" id="_email" value="samples@wordfence.com"/></td>
|
297 |
+
<td colspan="2"><input class="button" type="button" id="doSendEmail" value="Send"/></td>
|
298 |
+
</tr>
|
299 |
+
</table>
|
300 |
+
</div>
|
301 |
+
<input class="button" type="submit" id="sendByEmail" value="Send Report by Email"/>
|
302 |
+
</div>
|
303 |
+
|
304 |
+
<?php if (!WFWAF_SUBDIRECTORY_INSTALL): ?>
|
305 |
+
<div id="updateWAFRules">
|
306 |
+
<h3>Firewall Rules</h3>
|
307 |
+
|
308 |
+
<p>
|
309 |
+
<button type="button" onclick="WFAD.wafUpdateRules()" class="button button-primary">
|
310 |
+
Manually refresh firewall rules
|
311 |
+
</button>
|
312 |
+
<!-- <em id="waf-rules-last-updated"></em>-->
|
313 |
+
</p>
|
314 |
+
<?php
|
315 |
+
try {
|
316 |
+
$lastUpdated = wfWAF::getInstance()->getStorageEngine()->getConfig('rulesLastUpdated');
|
317 |
+
} catch (wfWAFStorageFileException $e) {
|
318 |
+
error_log($e->getMessage());
|
319 |
+
}
|
320 |
+
if (!empty($lastUpdated)): ?>
|
321 |
+
<script>
|
322 |
+
var lastUpdated = <?php echo (int) $lastUpdated ?>;
|
323 |
+
WFAD.renderWAFRulesLastUpdated(new Date(lastUpdated * 1000));
|
324 |
+
</script>
|
325 |
+
<?php endif ?>
|
326 |
+
|
327 |
+
</div>
|
328 |
+
<?php endif ?>
|
329 |
+
|
330 |
+
<h3>Debugging Options</h3>
|
331 |
+
<form action="#" id="wfDebuggingConfigForm">
|
332 |
+
<table class="wfConfigForm">
|
333 |
+
<tr>
|
334 |
+
<th>Add a debugging comment to HTML source of cached pages.<a
|
335 |
+
href="http://docs.wordfence.com/en/Wordfence_options#Add_a_debugging_comment_to_HTML_source_of_cached_pages"
|
336 |
+
target="_blank" class="wfhelp"></a></th>
|
337 |
+
<td><input type="checkbox" id="addCacheComment" class="wfConfigElem" name="addCacheComment"
|
338 |
+
value="1" <?php $w->cb('addCacheComment'); ?> />
|
339 |
+
</td>
|
340 |
+
</tr>
|
341 |
+
|
342 |
+
<tr>
|
343 |
+
<th>Enable debugging mode (increases database load)<a
|
344 |
+
href="http://docs.wordfence.com/en/Wordfence_options#Enable_debugging_mode_.28increases_database_load.29"
|
345 |
+
target="_blank" class="wfhelp"></a></th>
|
346 |
+
<td><input type="checkbox" id="debugOn" class="wfConfigElem" name="debugOn"
|
347 |
+
value="1" <?php $w->cb('debugOn'); ?> /></td>
|
348 |
+
</tr>
|
349 |
+
|
350 |
+
<tr>
|
351 |
+
<th>Start all scans remotely<a
|
352 |
+
href="http://docs.wordfence.com/en/Wordfence_options#Start_all_scans_remotely"
|
353 |
+
target="_blank" class="wfhelp"></a></th>
|
354 |
+
<td><input type="checkbox" id="startScansRemotely" class="wfConfigElem" name="startScansRemotely"
|
355 |
+
value="1" <?php $w->cb('startScansRemotely'); ?> />
|
356 |
+
(Try this if your scans aren't starting and your site is publicly accessible)
|
357 |
+
</td>
|
358 |
+
</tr>
|
359 |
+
<tr>
|
360 |
+
<th>Disable config caching<a
|
361 |
+
href="http://docs.wordfence.com/en/Wordfence_options#Disable_config_caching" target="_blank"
|
362 |
+
class="wfhelp"></a></th>
|
363 |
+
<td><input type="checkbox" id="disableConfigCaching" class="wfConfigElem"
|
364 |
+
name="disableConfigCaching" value="1" <?php $w->cb('disableConfigCaching'); ?> />
|
365 |
+
(Try this if your options aren't saving)
|
366 |
+
</td>
|
367 |
+
</tr>
|
368 |
+
|
369 |
+
<tr>
|
370 |
+
<th><label for="ssl_verify">Enable SSL Verification</label><a
|
371 |
+
href="http://docs.wordfence.com/en/Wordfence_options#Enable_SSL_Verification"
|
372 |
+
target="_blank" class="wfhelp"></a>
|
373 |
+
</th>
|
374 |
+
<td style="vertical-align: top;"><input type="checkbox" id="ssl_verify" class="wfConfigElem"
|
375 |
+
name="ssl_verify"
|
376 |
+
value="1" <?php $w->cb('ssl_verify'); ?> />
|
377 |
+
(Disable this if you are <strong><em>consistently</em></strong> unable to connect to the Wordfence
|
378 |
+
servers.)
|
379 |
+
</td>
|
380 |
+
</tr>
|
381 |
+
|
382 |
+
<tr>
|
383 |
+
<th><label for="betaThreatDefenseFeed">Enable beta threat defense feed</label></th>
|
384 |
+
<td style="vertical-align: top;"><input type="checkbox" id="betaThreatDefenseFeed"
|
385 |
+
class="wfConfigElem"
|
386 |
+
name="betaThreatDefenseFeed"
|
387 |
+
value="1" <?php $w->cb('betaThreatDefenseFeed'); ?> />
|
388 |
+
</td>
|
389 |
+
</tr>
|
390 |
+
|
391 |
+
</table>
|
392 |
+
<br>
|
393 |
+
<table border="0" cellpadding="0" cellspacing="0">
|
394 |
+
<tr>
|
395 |
+
<td><input type="button" id="button1" name="button1" class="button-primary" value="Save Changes"
|
396 |
+
onclick="WFAD.saveDebuggingConfig();"/></td>
|
397 |
+
<td style="height: 24px;">
|
398 |
+
<div class="wfAjax24"></div>
|
399 |
+
<span class="wfSavedMsg"> Your changes have been saved!</span></td>
|
400 |
+
</tr>
|
401 |
+
</table>
|
402 |
+
</form>
|
403 |
+
|
404 |
+
<?php endif ?>
|
lib/menu_options.php
CHANGED
@@ -60,12 +60,11 @@ $w = new wfConfig();
|
|
60 |
<div class="wf-premium-callout">
|
61 |
<h3>Upgrade to Wordfence Premium today for less than $5 per month</h3>
|
62 |
<ul>
|
63 |
-
<li>
|
64 |
-
|
65 |
-
</li>
|
66 |
<li>Remote, frequent and scheduled scans</li>
|
67 |
<li>Access to Premium Support</li>
|
68 |
-
<li>Discounts of up to
|
69 |
</ul>
|
70 |
<p class="center">
|
71 |
<a class="button button-primary"
|
@@ -80,13 +79,13 @@ $w = new wfConfig();
|
|
80 |
target="_blank" class="wfhelp"></a></h2></td>
|
81 |
</tr>
|
82 |
<tr>
|
83 |
-
<th class="wfConfigEnable">Enable
|
84 |
-
href="
|
85 |
class="wfhelp"></a></th>
|
86 |
<td><input type="checkbox" id="firewallEnabled" class="wfConfigElem" name="firewallEnabled"
|
87 |
value="1" <?php $w->cb( 'firewallEnabled' ); ?> /> <span
|
88 |
-
style="color: #F00;">NOTE:</span> This checkbox enables ALL
|
89 |
-
country and advanced blocking and the "
|
90 |
</td>
|
91 |
</tr>
|
92 |
<tr>
|
@@ -407,6 +406,11 @@ $w = new wfConfig();
|
|
407 |
<td><input type="text" name="liveTraf_ignoreUA" id="liveTraf_ignoreUA"
|
408 |
value="<?php $w->f( 'liveTraf_ignoreUA' ); ?>"/></td>
|
409 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
410 |
<tr>
|
411 |
<td colspan="2">
|
412 |
<div class="wfMarker" id="wfMarkerScansToInclude"></div>
|
@@ -441,6 +445,30 @@ $w = new wfConfig();
|
|
441 |
name="scansEnabled_heartbleed" value="1" <?php $w->cb( 'scansEnabled_heartbleed' ); ?> />
|
442 |
</td>
|
443 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
444 |
<tr>
|
445 |
<th>Scan core files against repository versions for changes<a
|
446 |
href="http://docs.wordfence.com/en/Wordfence_options#Scan_core_files_against_repository_version_for_changes"
|
@@ -476,15 +504,16 @@ $w = new wfConfig();
|
|
476 |
target="_blank" class="wfhelp"></a></th>
|
477 |
<td><input type="checkbox" id="scansEnabled_fileContents" class="wfConfigElem"
|
478 |
name="scansEnabled_fileContents"
|
479 |
-
value="1" <?php $w->cb( 'scansEnabled_fileContents' );
|
|
|
|
|
|
|
480 |
</tr>
|
481 |
-
<tr>
|
482 |
-
<th
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
name="scansEnabled_database"
|
487 |
-
value="1" <?php $w->cb( 'scansEnabled_database' ); ?>/></td>
|
488 |
</tr>
|
489 |
<tr>
|
490 |
<th>Scan posts for known dangerous URLs and suspicious content<a
|
@@ -508,6 +537,14 @@ $w = new wfConfig();
|
|
508 |
name="scansEnabled_oldVersions"
|
509 |
value="1" <?php $w->cb( 'scansEnabled_oldVersions' ); ?>/></td>
|
510 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
511 |
<tr>
|
512 |
<th>Check the strength of passwords<a
|
513 |
href="http://docs.wordfence.com/en/Wordfence_options#Check_the_strength_of_passwords"
|
@@ -564,8 +601,8 @@ $w = new wfConfig();
|
|
564 |
<tr>
|
565 |
<td colspan="2">
|
566 |
<div class="wfMarker" id="wfMarkerFirewallRules"></div>
|
567 |
-
<h3 class="wfConfigHeading">
|
568 |
-
href="http://docs.wordfence.com/en/Wordfence_options#
|
569 |
class="wfhelp"></a></h3>
|
570 |
</td>
|
571 |
</tr>
|
@@ -810,9 +847,11 @@ $w = new wfConfig();
|
|
810 |
<th>Immediately block the IP of users who try to sign in as these usernames<a
|
811 |
href="http://docs.wordfence.com/en/Wordfence_options#Immediately_block_the_IP_of_users_who_try_to_sign_in_as_these_usernames"
|
812 |
target="_blank" class="wfhelp"></a></th>
|
813 |
-
<td
|
814 |
-
|
815 |
-
|
|
|
|
|
816 |
</td>
|
817 |
</tr>
|
818 |
<tr>
|
@@ -933,13 +972,6 @@ $w = new wfConfig();
|
|
933 |
browser traffic but slow scan starts, live traffic & status updates.
|
934 |
</td>
|
935 |
</tr>
|
936 |
-
<tr>
|
937 |
-
<th>Enable debugging mode (increases database load)<a
|
938 |
-
href="http://docs.wordfence.com/en/Wordfence_options#Enable_debugging_mode_.28increases_database_load.29"
|
939 |
-
target="_blank" class="wfhelp"></a></th>
|
940 |
-
<td><input type="checkbox" id="debugOn" class="wfConfigElem" name="debugOn"
|
941 |
-
value="1" <?php $w->cb( 'debugOn' ); ?> /></td>
|
942 |
-
</tr>
|
943 |
<tr>
|
944 |
<th>Delete Wordfence tables and data on deactivation?<a
|
945 |
href="http://docs.wordfence.com/en/Wordfence_options#Delete_Wordfence_tables_and_data_on_deactivation.3F"
|
@@ -958,35 +990,6 @@ $w = new wfConfig();
|
|
958 |
will appear to be new visits)
|
959 |
</td>
|
960 |
</tr>
|
961 |
-
<tr>
|
962 |
-
<th>Start all scans remotely<a
|
963 |
-
href="http://docs.wordfence.com/en/Wordfence_options#Start_all_scans_remotely"
|
964 |
-
target="_blank" class="wfhelp"></a></th>
|
965 |
-
<td><input type="checkbox" id="startScansRemotely" class="wfConfigElem" name="startScansRemotely"
|
966 |
-
value="1" <?php $w->cb( 'startScansRemotely' ); ?> />(Try this if your scans aren't
|
967 |
-
starting and your site is publicly accessible)
|
968 |
-
</td>
|
969 |
-
</tr>
|
970 |
-
<tr>
|
971 |
-
<th>Disable config caching<a
|
972 |
-
href="http://docs.wordfence.com/en/Wordfence_options#Disable_config_caching" target="_blank"
|
973 |
-
class="wfhelp"></a></th>
|
974 |
-
<td><input type="checkbox" id="disableConfigCaching" class="wfConfigElem"
|
975 |
-
name="disableConfigCaching" value="1" <?php $w->cb( 'disableConfigCaching' ); ?> />(Try
|
976 |
-
this if your options aren't saving)
|
977 |
-
</td>
|
978 |
-
</tr>
|
979 |
-
<tr>
|
980 |
-
<th>Add a debugging comment to HTML source of cached pages.<a
|
981 |
-
href="http://docs.wordfence.com/en/Wordfence_options#Add_a_debugging_comment_to_HTML_source_of_cached_pages"
|
982 |
-
target="_blank" class="wfhelp"></a></th>
|
983 |
-
<td><input type="checkbox" id="addCacheComment" class="wfConfigElem" name="addCacheComment"
|
984 |
-
value="1" <?php $w->cb( 'addCacheComment' ); ?> />
|
985 |
-
<?php if ($w->get('allowHTTPSCaching')): ?>
|
986 |
-
<input type="hidden" name="allowHTTPSCaching" value="1"/>
|
987 |
-
<?php endif ?>
|
988 |
-
</td>
|
989 |
-
</tr>
|
990 |
<tr>
|
991 |
<th><label for="disableCodeExecutionUploads">Disable Code Execution for Uploads directory</label><a
|
992 |
href="http://docs.wordfence.com/en/Wordfence_options#Disable_Code_Execution_for_Uploads_directory"
|
@@ -995,60 +998,6 @@ $w = new wfConfig();
|
|
995 |
name="disableCodeExecutionUploads"
|
996 |
value="1" <?php $w->cb( 'disableCodeExecutionUploads' ); ?> /></td>
|
997 |
</tr>
|
998 |
-
<tr>
|
999 |
-
<th><label for="ssl_verify">Enable SSL Verification</label><a
|
1000 |
-
href="http://docs.wordfence.com/en/Wordfence_options#Enable_SSL_Verification"
|
1001 |
-
target="_blank" class="wfhelp"></a>
|
1002 |
-
</th>
|
1003 |
-
<td style="vertical-align: top;"><input type="checkbox" id="ssl_verify" class="wfConfigElem"
|
1004 |
-
name="ssl_verify"
|
1005 |
-
value="1" <?php $w->cb( 'ssl_verify' ); ?> />
|
1006 |
-
(Disable this if you are <strong><em>consistently</em></strong> unable to connect to the Wordfence servers.)
|
1007 |
-
</td>
|
1008 |
-
</tr>
|
1009 |
-
<tr>
|
1010 |
-
<th colspan="2"><a
|
1011 |
-
href="<?php echo wfUtils::siteURLRelative(); ?>?_wfsf=conntest&nonce=<?php echo wp_create_nonce( 'wp-ajax' ); ?>"
|
1012 |
-
target="_blank">Click to test connectivity to the Wordfence API servers</a><a
|
1013 |
-
href="http://docs.wordfence.com/en/Wordfence_options#Click_to_test_connectivity_to_the_Wordfence_API_servers"
|
1014 |
-
target="_blank" class="wfhelp"></a></th>
|
1015 |
-
</tr>
|
1016 |
-
<tr>
|
1017 |
-
<th colspan="2"><a
|
1018 |
-
href="<?php echo wfUtils::siteURLRelative(); ?>?_wfsf=sysinfo&nonce=<?php echo wp_create_nonce( 'wp-ajax' ); ?>"
|
1019 |
-
target="_blank">Click to view your system's configuration in a new window</a><a
|
1020 |
-
href="http://docs.wordfence.com/en/Wordfence_options#Click_to_view_your_system.27s_configuration_in_a_new_window"
|
1021 |
-
target="_blank" class="wfhelp"></a></th>
|
1022 |
-
</tr>
|
1023 |
-
<tr>
|
1024 |
-
<th colspan="2"><a
|
1025 |
-
href="<?php echo wfUtils::siteURLRelative(); ?>?_wfsf=cronview&nonce=<?php echo wp_create_nonce( 'wp-ajax' ); ?>"
|
1026 |
-
target="_blank">Click to view your systems scheduled jobs in a new window</a><a
|
1027 |
-
href="http://docs.wordfence.com/en/Wordfence_options#Click_to_view_your_system.27s_scheduled_jobs_in_a_new_window"
|
1028 |
-
target="_blank" class="wfhelp"></a></th>
|
1029 |
-
</tr>
|
1030 |
-
<tr>
|
1031 |
-
<th colspan="2"><a
|
1032 |
-
href="<?php echo wfUtils::siteURLRelative(); ?>?_wfsf=dbview&nonce=<?php echo wp_create_nonce( 'wp-ajax' ); ?>"
|
1033 |
-
target="_blank">Click to see a list of your system's database tables in a new window</a><a
|
1034 |
-
href="http://docs.wordfence.com/en/Wordfence_options#Click_to_see_a_list_of_your_system.27s_database_tables_in_a_new_window"
|
1035 |
-
target="_blank" class="wfhelp"></a></th>
|
1036 |
-
</tr>
|
1037 |
-
<tr>
|
1038 |
-
<th colspan="2"><a
|
1039 |
-
href="<?php echo wfUtils::siteURLRelative(); ?>?_wfsf=testmem&nonce=<?php echo wp_create_nonce( 'wp-ajax' ); ?>"
|
1040 |
-
target="_blank">Test your WordPress host's available memory</a><a
|
1041 |
-
href="http://docs.wordfence.com/en/Wordfence_options#Test_your_WordPress_host.27s_available_memory"
|
1042 |
-
target="_blank" class="wfhelp"></a></th>
|
1043 |
-
</tr>
|
1044 |
-
<tr>
|
1045 |
-
<th>Send a test email from this WordPress server to an email address:<a
|
1046 |
-
href="http://docs.wordfence.com/en/Wordfence_options#Send_a_test_email_from_this_WordPress_server_to_an_email_address"
|
1047 |
-
target="_blank" class="wfhelp"></a></th>
|
1048 |
-
<td><input type="text" id="testEmailDest" value="" size="20" maxlength="255" class="wfConfigElem"/>
|
1049 |
-
<input type="button" value="Send Test Email"
|
1050 |
-
onclick="WFAD.sendTestEmail(jQuery('#testEmailDest').val());"/></td>
|
1051 |
-
</tr>
|
1052 |
|
1053 |
<tr>
|
1054 |
<td colspan="2">
|
@@ -1143,7 +1092,7 @@ $w = new wfConfig();
|
|
1143 |
</script>
|
1144 |
<script type="text/x-jquery-template" id="wfContentFirewallRules">
|
1145 |
<div>
|
1146 |
-
<h3>
|
1147 |
|
1148 |
<p>
|
1149 |
<strong>NOTE:</strong> Before modifying these rules, make sure you have access to the email address
|
60 |
<div class="wf-premium-callout">
|
61 |
<h3>Upgrade to Wordfence Premium today for less than $5 per month</h3>
|
62 |
<ul>
|
63 |
+
<li>Receive real-time Firewall and Scan engine rule updates for protection as threats emerge</li>
|
64 |
+
<li>Advanced features like IP reputation monitoring, country blocking, an advanced comment spam filter and cell phone sign-in give you the best protection available</li>
|
|
|
65 |
<li>Remote, frequent and scheduled scans</li>
|
66 |
<li>Access to Premium Support</li>
|
67 |
+
<li>Discounts of up to 75% for multiyear and multi-license purchases</li>
|
68 |
</ul>
|
69 |
<p class="center">
|
70 |
<a class="button button-primary"
|
79 |
target="_blank" class="wfhelp"></a></h2></td>
|
80 |
</tr>
|
81 |
<tr>
|
82 |
+
<th class="wfConfigEnable">Enable Rate Limiting and Advanced Blocking<a
|
83 |
+
href="https://docs.wordfence.com/en/Wordfence_options#Enable_Rate_Limiting_and_Advanced_Blocking" target="_blank"
|
84 |
class="wfhelp"></a></th>
|
85 |
<td><input type="checkbox" id="firewallEnabled" class="wfConfigElem" name="firewallEnabled"
|
86 |
value="1" <?php $w->cb( 'firewallEnabled' ); ?> /> <span
|
87 |
+
style="color: #F00;">NOTE:</span> This checkbox enables ALL blocking/throttling functions including IP,
|
88 |
+
country and advanced blocking and the "Rate Limiting Rules" below.
|
89 |
</td>
|
90 |
</tr>
|
91 |
<tr>
|
406 |
<td><input type="text" name="liveTraf_ignoreUA" id="liveTraf_ignoreUA"
|
407 |
value="<?php $w->f( 'liveTraf_ignoreUA' ); ?>"/></td>
|
408 |
</tr>
|
409 |
+
<tr>
|
410 |
+
<th>Amount of Live Traffic data to store (number of rows):</th>
|
411 |
+
<td><input type="text" name="liveTraf_maxRows" id="liveTraf_maxRows"
|
412 |
+
value="<?php $w->f( 'liveTraf_maxRows' ); ?>"/></td>
|
413 |
+
</tr>
|
414 |
<tr>
|
415 |
<td colspan="2">
|
416 |
<div class="wfMarker" id="wfMarkerScansToInclude"></div>
|
445 |
name="scansEnabled_heartbleed" value="1" <?php $w->cb( 'scansEnabled_heartbleed' ); ?> />
|
446 |
</td>
|
447 |
</tr>
|
448 |
+
<tr>
|
449 |
+
<th>Scan for publically accessible configuration, backup, or log files<a
|
450 |
+
href="http://docs.wordfence.com/en/Wordfence_options#Configuration_Readable"
|
451 |
+
target="_blank" class="wfhelp"></a></th>
|
452 |
+
<td><input type="checkbox" id="scansEnabled_checkReadableConfig" class="wfConfigElem"
|
453 |
+
name="scansEnabled_checkReadableConfig" value="1" <?php $w->cb( 'scansEnabled_checkReadableConfig' ); ?> />
|
454 |
+
</td>
|
455 |
+
</tr>
|
456 |
+
<!-- <tr>-->
|
457 |
+
<!-- <th>Scan for Full Path Disclosure?<a-->
|
458 |
+
<!-- href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_Full_Path_Disclosure"-->
|
459 |
+
<!-- target="_blank" class="wfhelp"></a></th>-->
|
460 |
+
<!-- <td><input type="checkbox" id="scansEnabled_wpscan_fullPathDisclosure" class="wfConfigElem"-->
|
461 |
+
<!-- name="scansEnabled_wpscan_fullPathDisclosure" value="1" --><?php //$w->cb( 'scansEnabled_wpscan_fullPathDisclosure' ); ?><!-- />-->
|
462 |
+
<!-- </td>-->
|
463 |
+
<!-- </tr>-->
|
464 |
+
<!-- <tr>-->
|
465 |
+
<!-- <th>Scan for Directory Listing?<a-->
|
466 |
+
<!-- href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_Directory_Listing"-->
|
467 |
+
<!-- target="_blank" class="wfhelp"></a></th>-->
|
468 |
+
<!-- <td><input type="checkbox" id="scansEnabled_wpscan_directoryListingEnabled" class="wfConfigElem"-->
|
469 |
+
<!-- name="scansEnabled_wpscan_directoryListingEnabled" value="1" --><?php //$w->cb( 'scansEnabled_wpscan_directoryListingEnabled' ); ?><!-- />-->
|
470 |
+
<!-- </td>-->
|
471 |
+
<!-- </tr>-->
|
472 |
<tr>
|
473 |
<th>Scan core files against repository versions for changes<a
|
474 |
href="http://docs.wordfence.com/en/Wordfence_options#Scan_core_files_against_repository_version_for_changes"
|
504 |
target="_blank" class="wfhelp"></a></th>
|
505 |
<td><input type="checkbox" id="scansEnabled_fileContents" class="wfConfigElem"
|
506 |
name="scansEnabled_fileContents"
|
507 |
+
value="1" <?php $w->cb( 'scansEnabled_fileContents' ); ?>/>
|
508 |
+
|
509 |
+
<a href="#add-more-rules" class="do-show" data-selector="#scan_include_extra">+ Add additional signatures</a>
|
510 |
+
</td>
|
511 |
</tr>
|
512 |
+
<tr class="hidden" id="scan_include_extra">
|
513 |
+
<th style="vertical-align: top;">Additional scan signatures</th>
|
514 |
+
<td><textarea class="wfConfigElement" cols="40" rows="4"
|
515 |
+
name="scan_include_extra"><?php echo $w->getHTML('scan_include_extra'); ?></textarea>
|
516 |
+
</td>
|
|
|
|
|
517 |
</tr>
|
518 |
<tr>
|
519 |
<th>Scan posts for known dangerous URLs and suspicious content<a
|
537 |
name="scansEnabled_oldVersions"
|
538 |
value="1" <?php $w->cb( 'scansEnabled_oldVersions' ); ?>/></td>
|
539 |
</tr>
|
540 |
+
<tr>
|
541 |
+
<th>Scan for admin users created outside of WordPress<a
|
542 |
+
href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_admin_users_created_outside_of_WordPress"
|
543 |
+
target="_blank" class="wfhelp"></a></th>
|
544 |
+
<td><input type="checkbox" id="scansEnabled_suspiciousAdminUsers" class="wfConfigElem"
|
545 |
+
name="scansEnabled_suspiciousAdminUsers"
|
546 |
+
value="1" <?php $w->cb( 'scansEnabled_suspiciousAdminUsers' ); ?>/></td>
|
547 |
+
</tr>
|
548 |
<tr>
|
549 |
<th>Check the strength of passwords<a
|
550 |
href="http://docs.wordfence.com/en/Wordfence_options#Check_the_strength_of_passwords"
|
601 |
<tr>
|
602 |
<td colspan="2">
|
603 |
<div class="wfMarker" id="wfMarkerFirewallRules"></div>
|
604 |
+
<h3 class="wfConfigHeading">Rate Limiting Rules<a
|
605 |
+
href="http://docs.wordfence.com/en/Wordfence_options#Rate_Limiting_Rules" target="_blank"
|
606 |
class="wfhelp"></a></h3>
|
607 |
</td>
|
608 |
</tr>
|
847 |
<th>Immediately block the IP of users who try to sign in as these usernames<a
|
848 |
href="http://docs.wordfence.com/en/Wordfence_options#Immediately_block_the_IP_of_users_who_try_to_sign_in_as_these_usernames"
|
849 |
target="_blank" class="wfhelp"></a></th>
|
850 |
+
<td>
|
851 |
+
<textarea name="loginSec_userBlacklist" cols="40" rows="4" id="loginSec_userBlacklist"><?php
|
852 |
+
echo wfUtils::cleanupOneEntryPerLine($w->getHTML( 'loginSec_userBlacklist' ))
|
853 |
+
?></textarea><br/>
|
854 |
+
(One per line. Existing users won't be blocked.)
|
855 |
</td>
|
856 |
</tr>
|
857 |
<tr>
|
972 |
browser traffic but slow scan starts, live traffic & status updates.
|
973 |
</td>
|
974 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
975 |
<tr>
|
976 |
<th>Delete Wordfence tables and data on deactivation?<a
|
977 |
href="http://docs.wordfence.com/en/Wordfence_options#Delete_Wordfence_tables_and_data_on_deactivation.3F"
|
990 |
will appear to be new visits)
|
991 |
</td>
|
992 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
993 |
<tr>
|
994 |
<th><label for="disableCodeExecutionUploads">Disable Code Execution for Uploads directory</label><a
|
995 |
href="http://docs.wordfence.com/en/Wordfence_options#Disable_Code_Execution_for_Uploads_directory"
|
998 |
name="disableCodeExecutionUploads"
|
999 |
value="1" <?php $w->cb( 'disableCodeExecutionUploads' ); ?> /></td>
|
1000 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1001 |
|
1002 |
<tr>
|
1003 |
<td colspan="2">
|
1092 |
</script>
|
1093 |
<script type="text/x-jquery-template" id="wfContentFirewallRules">
|
1094 |
<div>
|
1095 |
+
<h3>Rate Limiting Rules</h3>
|
1096 |
|
1097 |
<p>
|
1098 |
<strong>NOTE:</strong> Before modifying these rules, make sure you have access to the email address
|
lib/menu_passwd.php
CHANGED
@@ -8,14 +8,15 @@
|
|
8 |
<?php if (!wfConfig::get('isPaid')) { ?>
|
9 |
<div class="wf-premium-callout" style="margin: 20px 0 20px 20px; width: 700px;">
|
10 |
<h3>Password Auditing is only available to Premium Members</h3>
|
11 |
-
<p>Wordfence Password Auditing uses our high performance password auditing cluster to test the strength of your admin and user passwords. We securely simulate a high-performance password cracking attack on your password database and will alert you to weak passwords. We then provide a way to change weak passwords or alert members that they need to improve their password strength
|
|
|
|
|
12 |
<ul>
|
13 |
-
<li>
|
14 |
<li>Other advanced features like IP reputation monitoring, an advanced comment spam filter, advanced scanning options, cell phone sign-in and country blocking give you the best protection available</li>
|
15 |
<li>Access to Premium Support</li>
|
16 |
-
<li>Discounts of up to
|
17 |
</ul>
|
18 |
-
|
19 |
<p class="center"><a class="button button-primary" href="https://www.wordfence.com/gnl1pwAuditUp1/wordfence-signup/">Get Premium</a></p>
|
20 |
</div>
|
21 |
<?php } ?>
|
8 |
<?php if (!wfConfig::get('isPaid')) { ?>
|
9 |
<div class="wf-premium-callout" style="margin: 20px 0 20px 20px; width: 700px;">
|
10 |
<h3>Password Auditing is only available to Premium Members</h3>
|
11 |
+
<p>Wordfence Password Auditing uses our high performance password auditing cluster to test the strength of your admin and user passwords. We securely simulate a high-performance password cracking attack on your password database and will alert you to weak passwords. We then provide a way to change weak passwords or alert members that they need to improve their password strength.</p>
|
12 |
+
|
13 |
+
<p>Upgrade to Premium today for less than $5 per month:</p>
|
14 |
<ul>
|
15 |
+
<li>Receive real-time Firewall and Scan engine rule updates for protection as threats emerge</li>
|
16 |
<li>Other advanced features like IP reputation monitoring, an advanced comment spam filter, advanced scanning options, cell phone sign-in and country blocking give you the best protection available</li>
|
17 |
<li>Access to Premium Support</li>
|
18 |
+
<li>Discounts of up to 75% available for multiyear and multi-license purchases</li>
|
19 |
</ul>
|
|
|
20 |
<p class="center"><a class="button button-primary" href="https://www.wordfence.com/gnl1pwAuditUp1/wordfence-signup/">Get Premium</a></p>
|
21 |
</div>
|
22 |
<?php } ?>
|
lib/menu_scan.php
CHANGED
@@ -1,3 +1,6 @@
|
|
|
|
|
|
|
|
1 |
<div class="wordfenceModeElem" id="wordfenceMode_scan"></div>
|
2 |
<div class="wrap wordfence">
|
3 |
<?php require('menuHeader.php'); ?>
|
@@ -7,7 +10,7 @@
|
|
7 |
<table border="0" cellpadding="0" cellspacing="0" style="width: 800px;">
|
8 |
<tr>
|
9 |
<td style="width: 250px; padding-top: 10px;">
|
10 |
-
<
|
11 |
<a href="#" onclick="WFAD.killScan(); return false;" style="font-size: 10px; color: #AAA;">Click to kill the current scan.</a>
|
12 |
</td>
|
13 |
<td>
|
@@ -33,28 +36,42 @@
|
|
33 |
</div>
|
34 |
<?php } ?>
|
35 |
</div></div></div>
|
36 |
-
<?php if(wfConfig::get('isPaid')){ ?>
|
37 |
-
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
40 |
<?php } else { ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
<div class="wf-premium-callout" style="margin: 20px 0 20px 2px;width: 765px;">
|
42 |
-
<h3>
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
</
|
47 |
-
|
48 |
-
<li>Access to Premium Support</li>
|
49 |
-
<li>Discounts of up to 90% for multiyear and multi-license purchases</li>
|
50 |
-
</ul>
|
51 |
<p class="center"><a class="button button-primary"
|
52 |
href="https://www.wordfence.com/gnl1scanUpgrade/wordfence-signup/">
|
53 |
Get Premium</a></p>
|
54 |
</div>
|
55 |
|
56 |
-
|
57 |
<?php } ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
<div class="consoleHead" style="margin-top: 20px;">
|
59 |
<span class="consoleHeadText">Scan Detailed Activity</span>
|
60 |
<a href="#" class="wfALogMailLink" onclick="WFAD.emailActivityLog(); return false;">Email activity log</a>
|
@@ -155,6 +172,140 @@
|
|
155 |
</div>
|
156 |
</div>
|
157 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
158 |
<script type="text/x-jquery-template" id="issueTmpl_wfThemeUpgrade">
|
159 |
<div>
|
160 |
<div class="wfIssue">
|
@@ -183,7 +334,7 @@
|
|
183 |
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'ignoreC'); return false;">Ignore this issue</a>
|
184 |
{{/if}}
|
185 |
{{if status == 'ignoreC' || status == 'ignoreP'}}
|
186 |
-
<a href="#" onclick="WFAD.updateIssueStatus('${id}', '
|
187 |
{{/if}}
|
188 |
</div>
|
189 |
</div>
|
@@ -793,6 +944,39 @@
|
|
793 |
</div>
|
794 |
</script>
|
795 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
796 |
|
797 |
|
798 |
|
@@ -807,7 +991,7 @@
|
|
807 |
and you will see the scan details in the "Activity Log" above in a few seconds.
|
808 |
</td></tr>
|
809 |
<tr><td>
|
810 |
-
<div class="wordfenceScanButton"><
|
811 |
</td></tr>
|
812 |
</table>
|
813 |
</td>
|
@@ -822,7 +1006,6 @@
|
|
822 |
<p>
|
823 |
Wordfence is a robust and complete security system and performance enhancer for WordPress. It protects your WordPress site
|
824 |
from security threats and keeps you off Google's SEO black-list by providing a firewall, brute force protection, continuous scanning and many other security enhancements.
|
825 |
-
Wordfence will also make your site <strong>up to 50 times faster</strong> than a standard WordPress site by installing Falcon Engine, the high performance web engine available exclusively with Wordfence.
|
826 |
</p>
|
827 |
<p>
|
828 |
Wordfence also detects if there are any security problems on
|
1 |
+
<?php
|
2 |
+
$sigUpdateTime = wfConfig::get('signatureUpdateTime');
|
3 |
+
?>
|
4 |
<div class="wordfenceModeElem" id="wordfenceMode_scan"></div>
|
5 |
<div class="wrap wordfence">
|
6 |
<?php require('menuHeader.php'); ?>
|
10 |
<table border="0" cellpadding="0" cellspacing="0" style="width: 800px;">
|
11 |
<tr>
|
12 |
<td style="width: 250px; padding-top: 10px;">
|
13 |
+
<button type="button" id="wfStartScanButton1" class="wfStartScanButton button-primary" onclick="wordfenceAdmin.startScan();">Start a Wordfence Scan</button><br />
|
14 |
<a href="#" onclick="WFAD.killScan(); return false;" style="font-size: 10px; color: #AAA;">Click to kill the current scan.</a>
|
15 |
</td>
|
16 |
<td>
|
36 |
</div>
|
37 |
<?php } ?>
|
38 |
</div></div></div>
|
39 |
+
<?php if (wfConfig::get('isPaid')) { ?>
|
40 |
+
<?php if (wfConfig::get('scansEnabled_fileContents')): ?>
|
41 |
+
<div style="width: 800px; ">
|
42 |
+
<p class="wf-success">You are running the Premium version of the Threat Defense Feed which is
|
43 |
+
updated in real-time as new threats emerge.</p>
|
44 |
+
</div>
|
45 |
+
<?php else: ?>
|
46 |
+
<div class="wfSecure">Premium scanning enabled</div>
|
47 |
+
<?php endif ?>
|
48 |
<?php } else { ?>
|
49 |
+
<?php if (wfConfig::get('scansEnabled_fileContents')): ?>
|
50 |
+
<p>You are running the Wordfence Community Scan signatures.
|
51 |
+
<!-- <em id="wf-scan-sigs-last-update"></em>-->
|
52 |
+
</p>
|
53 |
+
<?php endif ?>
|
54 |
+
|
55 |
<div class="wf-premium-callout" style="margin: 20px 0 20px 2px;width: 765px;">
|
56 |
+
<h3>The Wordfence Scan alerts you if you've been hacked</h3>
|
57 |
+
|
58 |
+
<p>As new threats emerge, the Threat Defense Feed is updated to detect these new hacks. The Premium
|
59 |
+
version of the Threat Defense Feed is updated in real-time protecting you immediately. As a free
|
60 |
+
user <strong>you are receiving the community version</strong> of the feed which is updated 30 days later. Upgrade
|
61 |
+
now for less than $5 a month!</p>
|
|
|
|
|
|
|
62 |
<p class="center"><a class="button button-primary"
|
63 |
href="https://www.wordfence.com/gnl1scanUpgrade/wordfence-signup/">
|
64 |
Get Premium</a></p>
|
65 |
</div>
|
66 |
|
|
|
67 |
<?php } ?>
|
68 |
+
|
69 |
+
<?php if ($sigUpdateTime ): ?>
|
70 |
+
<script>
|
71 |
+
WFAD.updateSignaturesTimestamp(<?php echo (int) $sigUpdateTime ?>);
|
72 |
+
</script>
|
73 |
+
<?php endif ?>
|
74 |
+
|
75 |
<div class="consoleHead" style="margin-top: 20px;">
|
76 |
<span class="consoleHeadText">Scan Detailed Activity</span>
|
77 |
<a href="#" class="wfALogMailLink" onclick="WFAD.emailActivityLog(); return false;">Email activity log</a>
|
172 |
</div>
|
173 |
</div>
|
174 |
</div>
|
175 |
+
<script type="text/x-jquery-template" id="issueTmpl_configReadable">
|
176 |
+
<div>
|
177 |
+
<div class="wfIssue">
|
178 |
+
<h2>${shortMsg}</h2>
|
179 |
+
<table border="0" class="wfIssue" cellspacing="0" cellpadding="0">
|
180 |
+
<tr>
|
181 |
+
<th>URL:</th>
|
182 |
+
<td><a href="${data.url}" target="_blank">${data.url}</a></td>
|
183 |
+
<tr>
|
184 |
+
<th>Severity:</th>
|
185 |
+
<td>{{if severity == '1'}}Critical{{else}}Warning{{/if}}</td>
|
186 |
+
</tr>
|
187 |
+
<tr>
|
188 |
+
<th>Status</th>
|
189 |
+
<td>
|
190 |
+
{{if status == 'new' }}New{{/if}}
|
191 |
+
{{if status == 'ignoreP' || status == 'ignoreC' }}Ignored{{/if}}
|
192 |
+
</td>
|
193 |
+
</tr>
|
194 |
+
</table>
|
195 |
+
<p>
|
196 |
+
{{html longMsg}}
|
197 |
+
</p>
|
198 |
+
<div class="wfIssueOptions">
|
199 |
+
<strong>Tools:</strong>
|
200 |
+
{{if data.fileExists}}
|
201 |
+
<a target="_blank" href="${WFAD.makeViewFileLink(data.file)}">View the file</a>
|
202 |
+
{{/if}}
|
203 |
+
<a href="#" onclick="WFAD.hideFile('${id}', 'delete'); return false;">Hide this file in <em>.htaccess</em></a>
|
204 |
+
{{if data.canDelete}}
|
205 |
+
<a href="#" onclick="WFAD.deleteFile('${id}'); return false;">Delete this file (can't be undone).</a>
|
206 |
+
<p>
|
207 |
+
<label><input type="checkbox" class="wfdelCheckbox" value="${id}" /> Select for bulk delete</label>
|
208 |
+
</p>
|
209 |
+
{{/if}}
|
210 |
+
</div>
|
211 |
+
<div class="wfIssueOptions">
|
212 |
+
{{if status == 'new'}}
|
213 |
+
<strong>Resolve:</strong>
|
214 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">I have fixed this issue</a>
|
215 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'ignoreC'); return false;">Ignore this issue</a>
|
216 |
+
{{/if}}
|
217 |
+
{{if status == 'ignoreC' || status == 'ignoreP'}}
|
218 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">Stop ignoring this issue</a>
|
219 |
+
{{/if}}
|
220 |
+
</div>
|
221 |
+
</div>
|
222 |
+
</div>
|
223 |
+
</script>
|
224 |
+
<script type="text/x-jquery-template" id="issueTmpl_wpscan_fullPathDiscl">
|
225 |
+
<div>
|
226 |
+
<div class="wfIssue">
|
227 |
+
<h2>${shortMsg}</h2>
|
228 |
+
<p>
|
229 |
+
<table border="0" class="wfIssue" cellspacing="0" cellpadding="0">
|
230 |
+
<tr><th>URL:</th><td><a href="${data.url}" target="_blank">${data.url}</a></td>
|
231 |
+
<tr><th>Severity:</th><td>{{if severity == '1'}}Critical{{else}}Warning{{/if}}</td></tr>
|
232 |
+
<tr><th>Status</th><td>
|
233 |
+
{{if status == 'new' }}New{{/if}}
|
234 |
+
{{if status == 'ignoreP' || status == 'ignoreC' }}Ignored{{/if}}
|
235 |
+
</td></tr>
|
236 |
+
</table>
|
237 |
+
</p>
|
238 |
+
<p>
|
239 |
+
{{html longMsg}}
|
240 |
+
</p>
|
241 |
+
<div class="wfIssueOptions">
|
242 |
+
{{if (status == 'new')}}
|
243 |
+
<strong>Resolve:</strong>
|
244 |
+
<?php if (!wfUtils::isNginx()): ?>
|
245 |
+
<a href="#" onclick="WFAD.fixFPD('${id}'); return false;">Fix this issue</a>
|
246 |
+
<?php endif ?>
|
247 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">I have fixed this issue</a>
|
248 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'ignoreC'); return false;">Ignore this issue</a>
|
249 |
+
{{/if}}
|
250 |
+
{{if status == 'ignoreC' || status == 'ignoreP'}}
|
251 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">Stop ignoring this issue</a>
|
252 |
+
{{/if}}
|
253 |
+
</div>
|
254 |
+
{{if (status == 'new')}}
|
255 |
+
<div class="wfIssueOptions">
|
256 |
+
<strong style="width: auto;">Manual Fix:</strong>
|
257 |
+
|
258 |
+
Set <code>display_errors</code> to <code>Off</code> in your php.ini file.
|
259 |
+
</div>
|
260 |
+
{{/if}}
|
261 |
+
|
262 |
+
</div>
|
263 |
+
</div>
|
264 |
+
</script>
|
265 |
+
<script type="text/x-jquery-template" id="issueTmpl_wpscan_directoryList">
|
266 |
+
<div>
|
267 |
+
<div class="wfIssue">
|
268 |
+
<h2>${shortMsg}</h2>
|
269 |
+
<p>
|
270 |
+
<table border="0" class="wfIssue" cellspacing="0" cellpadding="0">
|
271 |
+
<tr><th>URL:</th><td><a href="${data.url}" target="_blank">${data.url}</a></td>
|
272 |
+
<tr><th>Severity:</th><td>{{if severity == '1'}}Critical{{else}}Warning{{/if}}</td></tr>
|
273 |
+
<tr><th>Status</th><td>
|
274 |
+
{{if status == 'new' }}New{{/if}}
|
275 |
+
{{if status == 'ignoreP' || status == 'ignoreC' }}Ignored{{/if}}
|
276 |
+
</td></tr>
|
277 |
+
</table>
|
278 |
+
</p>
|
279 |
+
<p>
|
280 |
+
{{html longMsg}}
|
281 |
+
</p>
|
282 |
+
|
283 |
+
<div class="wfIssueOptions">
|
284 |
+
{{if (status == 'new')}}
|
285 |
+
<strong>Resolve:</strong>
|
286 |
+
<?php if (!wfUtils::isNginx()): ?>
|
287 |
+
<a href="#" onclick="WFAD.disableDirectoryListing('${id}'); return false;">Fix this issue</a>
|
288 |
+
<?php endif ?>
|
289 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">I have fixed this issue</a>
|
290 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'ignoreC'); return false;">Ignore this issue</a>
|
291 |
+
{{/if}}
|
292 |
+
{{if status == 'ignoreC' || status == 'ignoreP'}}
|
293 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">Stop ignoring this issue</a>
|
294 |
+
{{/if}}
|
295 |
+
</div>
|
296 |
+
<?php if (!wfUtils::isNginx()): ?>
|
297 |
+
{{if (status == 'new')}}
|
298 |
+
<div class="wfIssueOptions">
|
299 |
+
<strong style="width: auto;">Manual Fix:</strong>
|
300 |
+
|
301 |
+
Add <code>Options -Indexes</code> to your .htaccess file.
|
302 |
+
</div>
|
303 |
+
{{/if}}
|
304 |
+
<?php endif ?>
|
305 |
+
|
306 |
+
</div>
|
307 |
+
</div>
|
308 |
+
</script>
|
309 |
<script type="text/x-jquery-template" id="issueTmpl_wfThemeUpgrade">
|
310 |
<div>
|
311 |
<div class="wfIssue">
|
334 |
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'ignoreC'); return false;">Ignore this issue</a>
|
335 |
{{/if}}
|
336 |
{{if status == 'ignoreC' || status == 'ignoreP'}}
|
337 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">Stop ignoring this issue</a>
|
338 |
{{/if}}
|
339 |
</div>
|
340 |
</div>
|
944 |
</div>
|
945 |
</script>
|
946 |
|
947 |
+
<script type="text/x-jquery-template" id="issueTmpl_suspiciousAdminUsers">
|
948 |
+
<div>
|
949 |
+
<div class="wfIssue">
|
950 |
+
<h2>${shortMsg}</h2>
|
951 |
+
<p>
|
952 |
+
<table border="0" class="wfIssue" cellspacing="0" cellpadding="0">
|
953 |
+
<tr><th>Severity:</th><td>{{if severity == '1'}}Critical{{else}}Warning{{/if}}</td></tr>
|
954 |
+
<tr><th>Status</th><td>
|
955 |
+
{{if status == 'new' }}New{{/if}}
|
956 |
+
{{if status == 'ignoreC' }}This issue will be ignored until it changes.{{/if}}
|
957 |
+
{{if status == 'ignoreP' }}This issue is permanently ignored.{{/if}}
|
958 |
+
</td></tr>
|
959 |
+
</table>
|
960 |
+
</p>
|
961 |
+
<p>
|
962 |
+
{{html longMsg}}
|
963 |
+
</p>
|
964 |
+
<div class="wfIssueOptions">
|
965 |
+
{{if status == 'new'}}
|
966 |
+
<strong>Resolve:</strong>
|
967 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">I have fixed this issue</a>
|
968 |
+
<a href="#" onclick="WFAD.deleteAdminUser('${id}'); return false;">Delete this user</a>
|
969 |
+
<a href="#" onclick="WFAD.revokeAdminUser('${id}'); return false;">Revoke all capabilities from this user</a>
|
970 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'ignoreP'); return false;">Ignore this problem</a>
|
971 |
+
{{/if}}
|
972 |
+
{{if status == 'ignoreP' || status == 'ignoreC'}}
|
973 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">Stop ignoring this issue</a>
|
974 |
+
{{/if}}
|
975 |
+
</div>
|
976 |
+
</div>
|
977 |
+
</div>
|
978 |
+
</script>
|
979 |
+
|
980 |
|
981 |
|
982 |
|
991 |
and you will see the scan details in the "Activity Log" above in a few seconds.
|
992 |
</td></tr>
|
993 |
<tr><td>
|
994 |
+
<div class="wordfenceScanButton"><button type="button" id="wfStartScanButton2" class="wfStartScanButton button-primary">Start a Wordfence Scan</button></div>
|
995 |
</td></tr>
|
996 |
</table>
|
997 |
</td>
|
1006 |
<p>
|
1007 |
Wordfence is a robust and complete security system and performance enhancer for WordPress. It protects your WordPress site
|
1008 |
from security threats and keeps you off Google's SEO black-list by providing a firewall, brute force protection, continuous scanning and many other security enhancements.
|
|
|
1009 |
</p>
|
1010 |
<p>
|
1011 |
Wordfence also detects if there are any security problems on
|
lib/menu_scanSchedule.php
CHANGED
@@ -5,17 +5,14 @@
|
|
5 |
<?php if(! wfConfig::get('isPaid')){ ?>
|
6 |
<div class="wf-premium-callout" style="margin: 20px;">
|
7 |
<h3>Scan Scheduling is only available to Premium Members</h3>
|
8 |
-
<p>
|
9 |
-
|
10 |
-
|
11 |
-
high-traffic or optimal usage of your site. Upgrade to Premium today:</p>
|
12 |
<ul>
|
13 |
-
<li>
|
14 |
-
<li>Other advanced features like IP reputation monitoring, an advanced comment spam filter, country blocking
|
15 |
-
and cell phone sign-in give you the best protection available
|
16 |
-
</li>
|
17 |
<li>Access to Premium Support</li>
|
18 |
-
<li>Discounts of up to
|
19 |
</ul>
|
20 |
<p class="center"><a class="button button-primary"
|
21 |
href="https://www.wordfence.com/gnl1scanSched1/wordfence-signup/">Get Premium</a></p>
|
5 |
<?php if(! wfConfig::get('isPaid')){ ?>
|
6 |
<div class="wf-premium-callout" style="margin: 20px;">
|
7 |
<h3>Scan Scheduling is only available to Premium Members</h3>
|
8 |
+
<p>Premium users can increase their WordPress protection by controlling scan frequency up to once per hour. Premium also allows you to control when Wordfence initiates a scan, selecting optimal times that don’t interfere with high-traffic or optimal usage of your site.</p>
|
9 |
+
|
10 |
+
<p>Upgrade to Premium today for less than $5 per month:</p>
|
|
|
11 |
<ul>
|
12 |
+
<li>Receive real-time Firewall and Scan engine rule updates for protection as threats emerge</li>
|
13 |
+
<li>Other advanced features like IP reputation monitoring, an advanced comment spam filter, country blocking and cell phone sign-in give you the best protection available</li>
|
|
|
|
|
14 |
<li>Access to Premium Support</li>
|
15 |
+
<li>Discounts of up to 75% available for multiyear and multi-license purchases</li>
|
16 |
</ul>
|
17 |
<p class="center"><a class="button button-primary"
|
18 |
href="https://www.wordfence.com/gnl1scanSched1/wordfence-signup/">Get Premium</a></p>
|
lib/menu_twoFactor.php
CHANGED
@@ -5,21 +5,20 @@
|
|
5 |
<?php if(! wfConfig::get('isPaid')){ ?>
|
6 |
<div class="wf-premium-callout" style="margin: 20px 0 20px 20px; width: 700px;">
|
7 |
<h3>Cellphone Sign-in is only available to Premium Members</h3>
|
|
|
|
|
|
|
|
|
8 |
|
9 |
-
<p>
|
10 |
-
today:</p>
|
11 |
<ul>
|
12 |
-
<li>
|
13 |
<li>Other advanced features like IP reputation monitoring, an advanced comment spam filter, advanced
|
14 |
scanning options and country blocking give you the best protection available
|
15 |
</li>
|
16 |
<li>Access to Premium Support</li>
|
17 |
-
<li>Discounts of up to
|
18 |
</ul>
|
19 |
-
<p>Wordfence's Cellphone Sign-in uses a technique called "Two Factor Authentication" which is used by banks,
|
20 |
-
government agencies and military world-wide as one of the most secure forms of remote system authentication.
|
21 |
-
It's now available from Wordfence for your WordPress website. We recommend you enable Cellphone Sign-in for
|
22 |
-
all Administrator level accounts.</p>
|
23 |
|
24 |
<p class="center"><a class="button button-primary"
|
25 |
href="https://www.wordfence.com/gnl1twoFac1/wordfence-signup/">Get Premium</a></p>
|
5 |
<?php if(! wfConfig::get('isPaid')){ ?>
|
6 |
<div class="wf-premium-callout" style="margin: 20px 0 20px 20px; width: 700px;">
|
7 |
<h3>Cellphone Sign-in is only available to Premium Members</h3>
|
8 |
+
<p>Our Cellphone Sign-in uses a technique called "Two Factor Authentication" which is used by banks, government
|
9 |
+
agencies and military world-wide as one of the most secure forms of remote system authentication. It's now
|
10 |
+
available from Wordfence for your WordPress website. We recommend you enable Cellphone Sign-in for all
|
11 |
+
Administrator level accounts.</p>
|
12 |
|
13 |
+
<p>Upgrade to Premium today for less than $5 per month:</p>
|
|
|
14 |
<ul>
|
15 |
+
<li>Receive real-time Firewall and Scan engine rule updates for protection as threats emerge</li>
|
16 |
<li>Other advanced features like IP reputation monitoring, an advanced comment spam filter, advanced
|
17 |
scanning options and country blocking give you the best protection available
|
18 |
</li>
|
19 |
<li>Access to Premium Support</li>
|
20 |
+
<li>Discounts of up to 75% available for multiyear and multi-license purchases</li>
|
21 |
</ul>
|
|
|
|
|
|
|
|
|
22 |
|
23 |
<p class="center"><a class="button button-primary"
|
24 |
href="https://www.wordfence.com/gnl1twoFac1/wordfence-signup/">Get Premium</a></p>
|
lib/menu_waf.php
ADDED
@@ -0,0 +1,472 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
$waf = wfWAF::getInstance();
|
3 |
+
$config = $waf->getStorageEngine();
|
4 |
+
/** @var array $wafData */
|
5 |
+
?>
|
6 |
+
<div class="wrap" id="paidWrap">
|
7 |
+
<?php require('menuHeader.php'); ?>
|
8 |
+
<?php
|
9 |
+
$pageTitle = "Wordfence Web Application Firewall";
|
10 |
+
$helpLink = "http://docs.wordfence.com/en/WAF";
|
11 |
+
$helpLabel = "Learn more about the Wordfence Web Application Firewall";
|
12 |
+
include('pageTitle.php');
|
13 |
+
?>
|
14 |
+
<div class="wordfenceModeElem" id="wordfenceMode_waf"></div>
|
15 |
+
<?php if (!empty($storageExceptionMessage)): ?>
|
16 |
+
<div style="font-weight: bold; margin: 20px 0px;;">
|
17 |
+
<?php echo wp_kses($storageExceptionMessage, 'post') ?>
|
18 |
+
</div>
|
19 |
+
<?php elseif (!empty($wafActionContent)): ?>
|
20 |
+
<?php echo $wafActionContent ?>
|
21 |
+
|
22 |
+
<p class="wf-notice"><em>If you cannot complete the setup process,
|
23 |
+
<a target="_blank" href="https://docs.wordfence.com/en/Web_Application_Firewall_Setup">click here for help</a>.</em></p>
|
24 |
+
|
25 |
+
<?php else: ?>
|
26 |
+
|
27 |
+
<?php if (!empty($configExceptionMessage)): ?>
|
28 |
+
<div style="font-weight: bold; margin: 20px 0px;;">
|
29 |
+
<?php echo wp_kses($configExceptionMessage, 'post') ?>
|
30 |
+
</div>
|
31 |
+
<?php endif ?>
|
32 |
+
|
33 |
+
<?php if (!wfConfig::get('isPaid')) { ?>
|
34 |
+
<div class="wf-premium-callout" style="margin: 20px 0 20px 2px;width: 700px;">
|
35 |
+
<h3>The Wordfence Firewall stops you from getting hacked</h3>
|
36 |
+
|
37 |
+
<p>As new threats emerge, the Threat Defense Feed is updated to protect you from new attacks. The
|
38 |
+
Premium version of the Threat Defense Feed is updated in real-time protecting you immediately. As a
|
39 |
+
free user <strong>you are receiving the community version</strong> of the feed which is updated 30 days later.
|
40 |
+
Upgrade now for less than $5 a month!</p>
|
41 |
+
|
42 |
+
<p class="center"><a class="button button-primary"
|
43 |
+
href="https://www.wordfence.com/wafOptions1/wordfence-signup/">
|
44 |
+
Get Premium</a></p>
|
45 |
+
</div>
|
46 |
+
<?php } else { ?>
|
47 |
+
<div class="wf-success">
|
48 |
+
You are running the Premium version of the Threat Defense Feed which is updated in real-time as new
|
49 |
+
threats emerge.
|
50 |
+
</div>
|
51 |
+
<?php } ?>
|
52 |
+
|
53 |
+
|
54 |
+
<?php if (WFWAF_SUBDIRECTORY_INSTALL): ?>
|
55 |
+
<div class="wf-notice">
|
56 |
+
You are currently running the Wordfence Web Application Firewall from another WordPress installation.
|
57 |
+
Please <a href="<?php echo network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend'); ?>">click here</a> to configure the Firewall to run correctly on this site.
|
58 |
+
</div>
|
59 |
+
<?php else: ?>
|
60 |
+
<div class="wordfenceWrap" style="margin: 20px 20px 20px 30px;">
|
61 |
+
<form action="javascript:void(0)" id="waf-config-form">
|
62 |
+
|
63 |
+
<table class="wfConfigForm">
|
64 |
+
<tr>
|
65 |
+
<td><h2>Firewall Status:<a href="http://docs.wordfence.com/en/WAF#Firewall_Status"
|
66 |
+
target="_blank" class="wfhelp"></a></h2></td>
|
67 |
+
<td colspan="2">
|
68 |
+
<select style="width: 300px" name="wafStatus" id="input-wafStatus">
|
69 |
+
<option<?php echo $config->getConfig('wafStatus') == 'enabled' ? ' selected' : '' ?>
|
70 |
+
class="wafStatus-enabled" value="enabled">Enabled and Protecting
|
71 |
+
</option>
|
72 |
+
<option<?php echo $config->getConfig('wafStatus') == 'learning-mode' ? ' selected' : '' ?>
|
73 |
+
class="wafStatus-learning-mode" value="learning-mode">Learning Mode
|
74 |
+
</option>
|
75 |
+
<option<?php echo $config->getConfig('wafStatus') == 'disabled' ? ' selected' : '' ?>
|
76 |
+
class="wafStatus-disabled" value="disabled">Disabled
|
77 |
+
</option>
|
78 |
+
</select>
|
79 |
+
<script>
|
80 |
+
(function($) {
|
81 |
+
$('#input-wafStatus').val(<?php echo json_encode($config->getConfig('wafStatus')) ?>)
|
82 |
+
.on('change', function() {
|
83 |
+
var val = $(this).val();
|
84 |
+
$('.wafStatus-description').hide();
|
85 |
+
$('#wafStatus-' + val + '-description').show();
|
86 |
+
});
|
87 |
+
})(jQuery);
|
88 |
+
</script>
|
89 |
+
</td>
|
90 |
+
</tr>
|
91 |
+
<tr id="waf-learning-mode-grace-row">
|
92 |
+
<td></td>
|
93 |
+
<td>
|
94 |
+
<label>
|
95 |
+
<input type="checkbox" name="learningModeGracePeriodEnabled"
|
96 |
+
value="1"<?php echo $config->getConfig('learningModeGracePeriodEnabled') ? ' checked' : ''; ?>>
|
97 |
+
Automatically switch to Enabled Mode on:
|
98 |
+
</label>
|
99 |
+
</td>
|
100 |
+
<th>
|
101 |
+
|
102 |
+
<input type="text" name="learningModeGracePeriod" id="input-learningModeGracePeriod"
|
103 |
+
class="wf-datetime"
|
104 |
+
placeholder="Enabled until..."
|
105 |
+
data-value="<?php echo esc_attr($config->getConfig('learningModeGracePeriod') ? (int) $config->getConfig('learningModeGracePeriod') : '') ?>"
|
106 |
+
>
|
107 |
+
</th>
|
108 |
+
</tr>
|
109 |
+
<tr>
|
110 |
+
<td style="text-align: center">
|
111 |
+
<button type="submit" class="button button-primary">Save</button>
|
112 |
+
</td>
|
113 |
+
<td colspan="2">
|
114 |
+
<div class="wafStatus-description" id="wafStatus-enabled-description">
|
115 |
+
In this mode, the Wordfence Web Application Firewall is actively blocking requests
|
116 |
+
matching known attack patterns, and is actively protecting your site from attackers.
|
117 |
+
</div>
|
118 |
+
<div class="wafStatus-description" id="wafStatus-learning-mode-description">
|
119 |
+
When you first install the Wordfence Web Application Firewall, it will be in learning
|
120 |
+
mode. This allows
|
121 |
+
Wordfence to learn about your site so that we can understand how to protect it and how
|
122 |
+
to allow normal visitors through the firewall. We recommend you let Wordfence learn for
|
123 |
+
a week before you enable the firewall.
|
124 |
+
</div>
|
125 |
+
<div class="wafStatus-description" id="wafStatus-disabled-description">
|
126 |
+
In this mode, the Wordfence Web Application Firewall is functionally turned off and
|
127 |
+
does not run any of its rules or analyze the request in any way.
|
128 |
+
</div>
|
129 |
+
</td>
|
130 |
+
</tr>
|
131 |
+
<?php /* ?>
|
132 |
+
<tr>
|
133 |
+
<td>
|
134 |
+
<input type="checkbox" name="throttleServerSideAttacks" id="input-throttleServerSideAttacks"
|
135 |
+
value="1"<?php echo $config->getConfig('throttleServerSideAttacks') ? ' checked' : ''; ?>>
|
136 |
+
</td>
|
137 |
+
<th><label for="input-throttleServerSideAttacks">Throttle IPs that trip rules matching a server-side
|
138 |
+
vulnerability (SQLi, RCE, LFI, etc)</label></th>
|
139 |
+
</tr>
|
140 |
+
<?php */ ?>
|
141 |
+
</table>
|
142 |
+
|
143 |
+
<br>
|
144 |
+
|
145 |
+
<h2>Rules<a href="http://docs.wordfence.com/en/WAF#Rules" target="_blank" class="wfhelp"></a></h2>
|
146 |
+
|
147 |
+
<div id="waf-rules-wrapper"></div>
|
148 |
+
|
149 |
+
<p>
|
150 |
+
<?php if (wfConfig::get('isPaid')): ?>
|
151 |
+
You are running Wordfence Premium firewall rules.
|
152 |
+
<?php else: ?>
|
153 |
+
You are running Wordfence community firewall rules.
|
154 |
+
<?php endif ?>
|
155 |
+
<!-- <em id="waf-rules-last-updated"></em>-->
|
156 |
+
</p>
|
157 |
+
|
158 |
+
</form>
|
159 |
+
|
160 |
+
<br>
|
161 |
+
|
162 |
+
<h2>Whitelisted URLs<a href="http://docs.wordfence.com/en/WAF#Whitelisted_URLs" target="_blank"
|
163 |
+
class="wfhelp"></a></h2>
|
164 |
+
|
165 |
+
<p><em>The URL/parameters in this table will not be tested by the firewall. They are typically added
|
166 |
+
while the firewall is in Learning Mode or by an admin who identifies a particular action/request
|
167 |
+
is a false positive.</em></p>
|
168 |
+
|
169 |
+
<p id="whitelist-form">
|
170 |
+
<strong>Add Whitelisted URL/Param:</strong><br>
|
171 |
+
<label>
|
172 |
+
URL:
|
173 |
+
<input type="text" name="whitelistURL">
|
174 |
+
</label>
|
175 |
+
|
176 |
+
<label>
|
177 |
+
Param:
|
178 |
+
<select name="whitelistParam">
|
179 |
+
<option value="request.body">POST Body</option>
|
180 |
+
<option value="request.cookies">Cookie</option>
|
181 |
+
<option value="request.fileNames">File Name</option>
|
182 |
+
<option value="request.headers">Header</option>
|
183 |
+
<option value="request.queryString">Query String</option>
|
184 |
+
</select>
|
185 |
+
</label>
|
186 |
+
|
187 |
+
<label>
|
188 |
+
Param Name:
|
189 |
+
<input type="text" name="whitelistParamName">
|
190 |
+
</label>
|
191 |
+
<button type="button" class="button button-small" id="waf-whitelisted-urls-add">Add</button>
|
192 |
+
</p>
|
193 |
+
|
194 |
+
<div id="waf-whitelisted-urls-wrapper"></div>
|
195 |
+
</div>
|
196 |
+
<?php endif ?>
|
197 |
+
<?php endif ?>
|
198 |
+
|
199 |
+
</div>
|
200 |
+
|
201 |
+
<script type="text/x-jquery-template" id="waf-rules-tmpl">
|
202 |
+
<table class="wf-table">
|
203 |
+
<thead>
|
204 |
+
<tr>
|
205 |
+
<th style="width: 5%">Enabled</th>
|
206 |
+
<th>Category</th>
|
207 |
+
<th>Description</th>
|
208 |
+
</tr>
|
209 |
+
</thead>
|
210 |
+
<tbody>
|
211 |
+
{{each(idx, rule) rules}}
|
212 |
+
<tr>
|
213 |
+
<td style="text-align: center">
|
214 |
+
<input type="checkbox" name="ruleEnabled"
|
215 |
+
value="${rule.ruleID}" {{if (!disabledRules[rule.ruleID])}} checked{{/if}}>
|
216 |
+
</td>
|
217 |
+
<td>${rule.category}</td>
|
218 |
+
<td>${rule.description}</td>
|
219 |
+
</tr>
|
220 |
+
{{/each}}
|
221 |
+
{{if (rules.length == 0)}}
|
222 |
+
<tr>
|
223 |
+
<td colspan="4">No rules currently set.
|
224 |
+
<a href="#" onclick="WFAD.wafUpdateRules();return false;">Click here</a> to pull down the latest from
|
225 |
+
the Wordfence servers.
|
226 |
+
</td>
|
227 |
+
</tr>
|
228 |
+
{{/if}}
|
229 |
+
</tbody>
|
230 |
+
</table>
|
231 |
+
</script>
|
232 |
+
<script type="text/x-jquery-template" id="waf-whitelisted-urls-tmpl">
|
233 |
+
<table class="wf-table whitelist-table">
|
234 |
+
<thead>
|
235 |
+
<tr>
|
236 |
+
<th style="width: 5%;">Enabled</th>
|
237 |
+
<th>URL</th>
|
238 |
+
<th>Param</th>
|
239 |
+
<th>Created</th>
|
240 |
+
<th>Source</th>
|
241 |
+
<th>User</th>
|
242 |
+
<th>IP</th>
|
243 |
+
<th>Action</th>
|
244 |
+
</tr>
|
245 |
+
</thead>
|
246 |
+
<tbody>
|
247 |
+
{{each(idx, whitelistedURLParam) whitelistedURLParams}}
|
248 |
+
<tr data-index="${idx}">
|
249 |
+
<td style="text-align: center;">
|
250 |
+
<input name="replaceWhitelistedEnabled" type="hidden" value="${whitelistedURLParam.data.disabled}">
|
251 |
+
<input name="whitelistedEnabled" type="checkbox" value="1"
|
252 |
+
{{if (!whitelistedURLParam.data.disabled)}} checked{{/if}}>
|
253 |
+
</td>
|
254 |
+
<td>
|
255 |
+
<input name="replaceWhitelistedPath" type="hidden" value="${whitelistedURLParam.path}">
|
256 |
+
<span class="whitelist-display">${WFAD.base64_decode(whitelistedURLParam.path)}</span>
|
257 |
+
<input name="whitelistedPath" class="whitelist-edit whitelist-path" type="text"
|
258 |
+
value="${WFAD.base64_decode(whitelistedURLParam.path)}">
|
259 |
+
</td>
|
260 |
+
<td>
|
261 |
+
<input name="replaceWhitelistedParam" type="hidden" value="${whitelistedURLParam.paramKey}">
|
262 |
+
<span class="whitelist-display">${WFAD.base64_decode(whitelistedURLParam.paramKey)}</span>
|
263 |
+
<input name="whitelistedParam" class="whitelist-edit whitelist-param-key"
|
264 |
+
type="text" value="${WFAD.base64_decode(whitelistedURLParam.paramKey)}">
|
265 |
+
</td>
|
266 |
+
<td>
|
267 |
+
{{if (whitelistedURLParam.data.timestamp)}}
|
268 |
+
${WFAD.dateFormat((new Date(whitelistedURLParam.data.timestamp * 1000)))}
|
269 |
+
{{else}}
|
270 |
+
-
|
271 |
+
{{/if}}
|
272 |
+
</td>
|
273 |
+
<td>
|
274 |
+
{{if (whitelistedURLParam.data.description)}}
|
275 |
+
${whitelistedURLParam.data.description}
|
276 |
+
{{else}}
|
277 |
+
-
|
278 |
+
{{/if}}
|
279 |
+
</td>
|
280 |
+
<td>
|
281 |
+
{{if (whitelistedURLParam.data.userID)}}
|
282 |
+
{{if (whitelistedURLParam.data.username)}}
|
283 |
+
${whitelistedURLParam.data.username}
|
284 |
+
{{else}}
|
285 |
+
${whitelistedURLParam.data.userID}
|
286 |
+
{{/if}}
|
287 |
+
{{else}}
|
288 |
+
-
|
289 |
+
{{/if}}
|
290 |
+
</td>
|
291 |
+
<td>
|
292 |
+
{{if (whitelistedURLParam.data.ip)}}
|
293 |
+
${whitelistedURLParam.data.ip}
|
294 |
+
{{else}}
|
295 |
+
-
|
296 |
+
{{/if}}
|
297 |
+
</td>
|
298 |
+
<td>
|
299 |
+
<span class="whitelist-display" style="white-space: nowrap">
|
300 |
+
<button type="button" class="button button-small whitelist-url-edit">Edit</button>
|
301 |
+
<button type="button" class="button button-small whitelist-url-delete">Delete</button>
|
302 |
+
</span>
|
303 |
+
<span class="whitelist-edit" style="white-space: nowrap">
|
304 |
+
<button type="button" class="button button-small whitelist-url-save">Save</button>
|
305 |
+
<button type="button" class="button button-small whitelist-url-cancel">Cancel</button>
|
306 |
+
</span>
|
307 |
+
</td>
|
308 |
+
</tr>
|
309 |
+
{{/each}}
|
310 |
+
{{if (whitelistedURLParams.length == 0)}}
|
311 |
+
<tr>
|
312 |
+
<td colspan="8">No whitelisted URLs currently set.</td>
|
313 |
+
</tr>
|
314 |
+
{{/if}}
|
315 |
+
</tbody>
|
316 |
+
</table>
|
317 |
+
</script>
|
318 |
+
|
319 |
+
<script type="text/javascript">
|
320 |
+
(function($) {
|
321 |
+
WFAD.wafData = <?php echo json_encode($wafData); ?>;
|
322 |
+
$('#waf-whitelisted-urls-add').on('click', function() {
|
323 |
+
var form = $('#whitelist-form');
|
324 |
+
|
325 |
+
var inputURL = form.find('[name=whitelistURL]');
|
326 |
+
var inputParam = form.find('[name=whitelistParam]');
|
327 |
+
var inputParamName = form.find('[name=whitelistParamName]');
|
328 |
+
|
329 |
+
var url = inputURL.val();
|
330 |
+
var param = inputParam.val();
|
331 |
+
var paramName = inputParamName.val();
|
332 |
+
if (url && param) {
|
333 |
+
WFAD.wafConfigSave('addWhitelist', {
|
334 |
+
whitelistedEnabled: 1,
|
335 |
+
whitelistedPath: url,
|
336 |
+
whitelistedParam: param + '[' + paramName + ']'
|
337 |
+
});
|
338 |
+
}
|
339 |
+
});
|
340 |
+
|
341 |
+
$('#input-wafStatus').on('change', function() {
|
342 |
+
var gracePeriodRow = $('#waf-learning-mode-grace-row');
|
343 |
+
if ($(this).val() == 'learning-mode') {
|
344 |
+
gracePeriodRow.show();
|
345 |
+
} else {
|
346 |
+
gracePeriodRow.hide();
|
347 |
+
}
|
348 |
+
}).triggerHandler('change');
|
349 |
+
|
350 |
+
$('#waf-config-form').on("submit", function() {
|
351 |
+
WFAD.wafConfigSave('config', $(this).serializeArray());
|
352 |
+
});
|
353 |
+
$(function() {
|
354 |
+
WFAD.wafConfigPageRender();
|
355 |
+
|
356 |
+
$('#input-wafStatus').select2({
|
357 |
+
minimumResultsForSearch: -1
|
358 |
+
}).on('change', function() {
|
359 |
+
var select = $(this);
|
360 |
+
var container = $($(this).data('select2').$container);
|
361 |
+
container.removeClass('wafStatus-enabled wafStatus-learning-mode wafStatus-disabled')
|
362 |
+
.addClass('wafStatus-' + select.val());
|
363 |
+
}).triggerHandler('change');
|
364 |
+
|
365 |
+
$('.wf-datetime').datetimepicker({
|
366 |
+
timeFormat: 'hh:mmtt z'
|
367 |
+
}).each(function() {
|
368 |
+
var el = $(this);
|
369 |
+
if (el.attr('data-value')) {
|
370 |
+
el.datetimepicker('setDate', new Date(el.attr('data-value') * 1000));
|
371 |
+
}
|
372 |
+
});
|
373 |
+
|
374 |
+
var learningModeGracePeriod = $('input[name=learningModeGracePeriod]');
|
375 |
+
$('input[name=learningModeGracePeriodEnabled]').on('click', function() {
|
376 |
+
if (this.value == '1' && this.checked) {
|
377 |
+
learningModeGracePeriod.attr('disabled', false);
|
378 |
+
if (!learningModeGracePeriod.val()) {
|
379 |
+
var date = new Date();
|
380 |
+
date.setDate(date.getDate() + 7);
|
381 |
+
learningModeGracePeriod.datetimepicker('setDate', date);
|
382 |
+
}
|
383 |
+
} else {
|
384 |
+
learningModeGracePeriod.attr('disabled', true);
|
385 |
+
learningModeGracePeriod.val('');
|
386 |
+
}
|
387 |
+
}).triggerHandler('click');
|
388 |
+
|
389 |
+
});
|
390 |
+
|
391 |
+
$(document).on('click', '.whitelist-url-edit', function() {
|
392 |
+
var tr = $(this).closest('tr');
|
393 |
+
tr.addClass('edit-mode');
|
394 |
+
});
|
395 |
+
$(document).on('click', '.whitelist-url-delete', function() {
|
396 |
+
if (confirm('Are you sure you\'d like to delete this URL?')) {
|
397 |
+
var tr = $(this).closest('tr');
|
398 |
+
|
399 |
+
var pathInput = tr.find('input.whitelist-path');
|
400 |
+
var paramInput = tr.find('input.whitelist-param-key');
|
401 |
+
WFAD.wafConfigSave('deleteWhitelist', {
|
402 |
+
deletedWhitelistedPath: pathInput.val(),
|
403 |
+
deletedWhitelistedParam: paramInput.val()
|
404 |
+
});
|
405 |
+
}
|
406 |
+
});
|
407 |
+
$(document).on('click', '.whitelist-url-save', function() {
|
408 |
+
var tr = $(this).closest('tr');
|
409 |
+
|
410 |
+
var oldWhitelistedPath = tr.find('input[name=replaceWhitelistedPath]');
|
411 |
+
var oldWhitelistedParam = tr.find('input[name=replaceWhitelistedParam]');
|
412 |
+
var oldWhitelistedEnabled = tr.find('input[name=replaceWhitelistedEnabled]');
|
413 |
+
|
414 |
+
var newWhitelistedPath = tr.find('input[name=whitelistedPath]');
|
415 |
+
var newWhitelistedParam = tr.find('input[name=whitelistedParam]');
|
416 |
+
var newWhitelistedEnabled = tr.find('input[name=whitelistedEnabled]');
|
417 |
+
|
418 |
+
WFAD.wafConfigSave('replaceWhitelist', {
|
419 |
+
oldWhitelistedPath: oldWhitelistedPath.val(),
|
420 |
+
oldWhitelistedParam: oldWhitelistedParam.val(),
|
421 |
+
oldWhitelistedEnabled: oldWhitelistedEnabled.val(),
|
422 |
+
newWhitelistedPath: newWhitelistedPath.val(),
|
423 |
+
newWhitelistedParam: newWhitelistedParam.val(),
|
424 |
+
newWhitelistedEnabled: newWhitelistedEnabled.val()
|
425 |
+
});
|
426 |
+
});
|
427 |
+
$(document).on('click', '.whitelist-url-cancel', function() {
|
428 |
+
var tr = $(this).closest('tr');
|
429 |
+
tr.removeClass('edit-mode');
|
430 |
+
});
|
431 |
+
$(document).on('click', 'input[name=whitelistedEnabled]', function() {
|
432 |
+
var tr = $(this).closest('tr');
|
433 |
+
|
434 |
+
var oldWhitelistedPath = tr.find('input[name=replaceWhitelistedPath]');
|
435 |
+
var oldWhitelistedParam = tr.find('input[name=replaceWhitelistedParam]');
|
436 |
+
var enabled = this.checked ? 1 : 0;
|
437 |
+
|
438 |
+
WFAD.wafConfigSave('enableWhitelist', {
|
439 |
+
whitelistedPath: oldWhitelistedPath.val(),
|
440 |
+
whitelistedParam: oldWhitelistedParam.val(),
|
441 |
+
whitelistedEnabled: enabled
|
442 |
+
});
|
443 |
+
});
|
444 |
+
|
445 |
+
$(document).on('click', 'input[name=ruleEnabled]', function() {
|
446 |
+
var enabled = this.checked ? 1 : 0;
|
447 |
+
|
448 |
+
WFAD.wafConfigSave('enableRule', {
|
449 |
+
ruleID: this.value,
|
450 |
+
ruleEnabled: enabled
|
451 |
+
});
|
452 |
+
});
|
453 |
+
|
454 |
+
})(jQuery);
|
455 |
+
</script>
|
456 |
+
|
457 |
+
<script type="text/x-jquery-template" id="wfWAFTour">
|
458 |
+
<div>
|
459 |
+
<h3>Wordfence Web Application Firewall</h3>
|
460 |
+
<p>The Wordfence Web Application Firewall filters out malicious requests before they reach your site. Once it
|
461 |
+
is enabled, it runs before WordPress itself, to filter attacks before plugins or themes can run any
|
462 |
+
potentially vulnerable code. As new threats emerge, the rules are updated in real-time from the Wordfence
|
463 |
+
servers for Premium members. Free users receive the community version of the rules which are updated
|
464 |
+
30 days later.</p>
|
465 |
+
|
466 |
+
<?php if (!wfConfig::get('isPaid')): ?>
|
467 |
+
<p>If you would like to get real-time updates to firewall rules, please <a
|
468 |
+
href="https://www.wordfence.com/wafOptions2/wordfence-signup/">upgrade to our premium version</a>.
|
469 |
+
</p>
|
470 |
+
<?php endif ?>
|
471 |
+
</div>
|
472 |
+
</script>
|
lib/sysinfo.php
CHANGED
@@ -10,7 +10,7 @@ ob_start();
|
|
10 |
phpinfo(INFO_ALL);
|
11 |
$out = ob_get_clean();
|
12 |
$out = str_replace('width="600"','width="900"', $out);
|
13 |
-
$out = preg_replace('/<hr.*?PHP Credits.*?<\/h1>/s', '', $out);
|
14 |
$out = preg_replace('/<a [^>]+>/', '', $out);
|
15 |
$out = preg_replace('/<\/a>/', '', $out);
|
16 |
$out = preg_replace('/<title>[^<]*<\/title>/','', $out);
|
10 |
phpinfo(INFO_ALL);
|
11 |
$out = ob_get_clean();
|
12 |
$out = str_replace('width="600"','width="900"', $out);
|
13 |
+
// $out = preg_replace('/<hr.*?PHP Credits.*?<\/h1>/s', '', $out);
|
14 |
$out = preg_replace('/<a [^>]+>/', '', $out);
|
15 |
$out = preg_replace('/<\/a>/', '', $out);
|
16 |
$out = preg_replace('/<title>[^<]*<\/title>/','', $out);
|
lib/wf503.php
CHANGED
@@ -6,10 +6,10 @@
|
|
6 |
<p>Your access to this service has been temporarily limited. Please try again in a few minutes. (HTTP response code 503)</p>
|
7 |
<p>Reason: <span style="color: #F00;"><?php echo $reason; ?></span></p>
|
8 |
<p style="width: 600px;"><b>Important note for site admins: </b>If you are the administrator of this website note that your access has been limited because you broke one of the Wordfence firewall rules.
|
9 |
-
The reason
|
10 |
<br /><br />
|
11 |
If this is a false positive, meaning that your access to your own site has been limited incorrectly, then you
|
12 |
-
will need to regain access to your site, go to the Wordfence "options" page, go to the section for
|
13 |
if you were blocked because it was detected that you are a fake Google crawler, then disable the rule that blocks fake google crawlers. Or if you were blocked because you
|
14 |
were accessing your site too quickly, then increase the number of accesses allowed per minute.
|
15 |
<br /><br />
|
6 |
<p>Your access to this service has been temporarily limited. Please try again in a few minutes. (HTTP response code 503)</p>
|
7 |
<p>Reason: <span style="color: #F00;"><?php echo $reason; ?></span></p>
|
8 |
<p style="width: 600px;"><b>Important note for site admins: </b>If you are the administrator of this website note that your access has been limited because you broke one of the Wordfence firewall rules.
|
9 |
+
The reason your access was limited is: <b>"<?php echo $reason; ?>"</b>.
|
10 |
<br /><br />
|
11 |
If this is a false positive, meaning that your access to your own site has been limited incorrectly, then you
|
12 |
+
will need to regain access to your site, go to the Wordfence "options" page, go to the section for Rate Limiting Rules and disable the rule that caused you to be blocked. For example,
|
13 |
if you were blocked because it was detected that you are a fake Google crawler, then disable the rule that blocks fake google crawlers. Or if you were blocked because you
|
14 |
were accessing your site too quickly, then increase the number of accesses allowed per minute.
|
15 |
<br /><br />
|
lib/wfAPI.php
CHANGED
@@ -118,11 +118,12 @@ class wfAPI {
|
|
118 |
}
|
119 |
}
|
120 |
return self::buildQuery(array(
|
121 |
-
'v'
|
122 |
-
's'
|
123 |
-
'k'
|
124 |
-
'openssl'
|
125 |
-
'phpv'
|
|
|
126 |
));
|
127 |
}
|
128 |
|
118 |
}
|
119 |
}
|
120 |
return self::buildQuery(array(
|
121 |
+
'v' => $this->wordpressVersion,
|
122 |
+
's' => $siteurl,
|
123 |
+
'k' => $this->APIKey,
|
124 |
+
'openssl' => function_exists('openssl_verify') && defined('OPENSSL_VERSION_NUMBER') ? OPENSSL_VERSION_NUMBER : '0.0.0',
|
125 |
+
'phpv' => phpversion(),
|
126 |
+
'betaFeed' => (int) wfConfig::get('betaThreatDefenseFeed'),
|
127 |
));
|
128 |
}
|
129 |
|
lib/wfConfig.php
CHANGED
@@ -34,16 +34,18 @@ class wfConfig {
|
|
34 |
"scansEnabled_plugins" => false,
|
35 |
"scansEnabled_malware" => false,
|
36 |
"scansEnabled_fileContents" => false,
|
37 |
-
"scansEnabled_database" => false,
|
38 |
"scansEnabled_posts" => false,
|
39 |
"scansEnabled_comments" => false,
|
40 |
"scansEnabled_passwds" => false,
|
41 |
"scansEnabled_diskSpace" => false,
|
42 |
"scansEnabled_options" => false,
|
|
|
|
|
43 |
"scansEnabled_dns" => false,
|
44 |
"scansEnabled_scanImages" => false,
|
45 |
"scansEnabled_highSense" => false,
|
46 |
"scansEnabled_oldVersions" => false,
|
|
|
47 |
"firewallEnabled" => false,
|
48 |
"blockFakeBots" => false,
|
49 |
"autoBlockScanners" => false,
|
@@ -74,7 +76,8 @@ class wfConfig {
|
|
74 |
),
|
75 |
"otherParams" => array(
|
76 |
'securityLevel' => '0',
|
77 |
-
"alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "",
|
|
|
78 |
"neverBlockBG" => "neverBlockVerified",
|
79 |
"loginSec_countFailMins" => "5",
|
80 |
"loginSec_lockoutMins" => "5",
|
@@ -124,16 +127,18 @@ class wfConfig {
|
|
124 |
"scansEnabled_plugins" => false,
|
125 |
"scansEnabled_malware" => true,
|
126 |
"scansEnabled_fileContents" => true,
|
127 |
-
"scansEnabled_database" => true,
|
128 |
"scansEnabled_posts" => true,
|
129 |
"scansEnabled_comments" => true,
|
130 |
"scansEnabled_passwds" => true,
|
131 |
"scansEnabled_diskSpace" => true,
|
132 |
"scansEnabled_options" => true,
|
|
|
|
|
133 |
"scansEnabled_dns" => true,
|
134 |
"scansEnabled_scanImages" => false,
|
135 |
"scansEnabled_highSense" => false,
|
136 |
"scansEnabled_oldVersions" => true,
|
|
|
137 |
"firewallEnabled" => true,
|
138 |
"blockFakeBots" => false,
|
139 |
"autoBlockScanners" => true,
|
@@ -165,6 +170,7 @@ class wfConfig {
|
|
165 |
"otherParams" => array(
|
166 |
'securityLevel' => '1',
|
167 |
"alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'scan_exclude' => '', 'whitelisted' => '', 'bannedURLs' => '', 'maxExecutionTime' => '', 'howGetIPs' => '', 'actUpdateInterval' => '', 'alert_maxHourly' => 0, 'loginSec_userBlacklist' => '',
|
|
|
168 |
"neverBlockBG" => "neverBlockVerified",
|
169 |
"loginSec_countFailMins" => "5",
|
170 |
"loginSec_lockoutMins" => "5",
|
@@ -201,6 +207,7 @@ class wfConfig {
|
|
201 |
"alertOn_adminLogin" => true,
|
202 |
"alertOn_nonAdminLogin" => false,
|
203 |
"liveTrafficEnabled" => true,
|
|
|
204 |
"advancedCommentScanning" => false,
|
205 |
"checkSpamIP" => false,
|
206 |
"spamvertizeCheck" => false,
|
@@ -214,16 +221,18 @@ class wfConfig {
|
|
214 |
"scansEnabled_plugins" => false,
|
215 |
"scansEnabled_malware" => true,
|
216 |
"scansEnabled_fileContents" => true,
|
217 |
-
"scansEnabled_database" => true,
|
218 |
"scansEnabled_posts" => true,
|
219 |
"scansEnabled_comments" => true,
|
220 |
"scansEnabled_passwds" => true,
|
221 |
"scansEnabled_diskSpace" => true,
|
222 |
"scansEnabled_options" => true,
|
|
|
|
|
223 |
"scansEnabled_dns" => true,
|
224 |
"scansEnabled_scanImages" => false,
|
225 |
"scansEnabled_highSense" => false,
|
226 |
"scansEnabled_oldVersions" => true,
|
|
|
227 |
"firewallEnabled" => true,
|
228 |
"blockFakeBots" => false,
|
229 |
"autoBlockScanners" => true,
|
@@ -253,8 +262,10 @@ class wfConfig {
|
|
253 |
'ssl_verify' => true,
|
254 |
),
|
255 |
"otherParams" => array(
|
|
|
256 |
'securityLevel' => '2',
|
257 |
"alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'scan_exclude' => '', 'whitelisted' => '', 'bannedURLs' => '', 'maxExecutionTime' => '', 'howGetIPs' => '', 'actUpdateInterval' => '', 'alert_maxHourly' => 0, 'loginSec_userBlacklist' => '',
|
|
|
258 |
"neverBlockBG" => "neverBlockVerified",
|
259 |
"loginSec_countFailMins" => "240",
|
260 |
"loginSec_lockoutMins" => "240",
|
@@ -304,16 +315,18 @@ class wfConfig {
|
|
304 |
"scansEnabled_plugins" => false,
|
305 |
"scansEnabled_malware" => true,
|
306 |
"scansEnabled_fileContents" => true,
|
307 |
-
"scansEnabled_database" => true,
|
308 |
"scansEnabled_posts" => true,
|
309 |
"scansEnabled_comments" => true,
|
310 |
"scansEnabled_passwds" => true,
|
311 |
"scansEnabled_diskSpace" => true,
|
312 |
"scansEnabled_options" => true,
|
|
|
|
|
313 |
"scansEnabled_dns" => true,
|
314 |
"scansEnabled_scanImages" => false,
|
315 |
"scansEnabled_highSense" => false,
|
316 |
"scansEnabled_oldVersions" => true,
|
|
|
317 |
"firewallEnabled" => true,
|
318 |
"blockFakeBots" => false,
|
319 |
"autoBlockScanners" => true,
|
@@ -345,6 +358,7 @@ class wfConfig {
|
|
345 |
"otherParams" => array(
|
346 |
'securityLevel' => '3',
|
347 |
"alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'scan_exclude' => '', 'whitelisted' => '', 'bannedURLs' => '', 'maxExecutionTime' => '', 'howGetIPs' => '', 'actUpdateInterval' => '', 'alert_maxHourly' => 0, 'loginSec_userBlacklist' => '',
|
|
|
348 |
"neverBlockBG" => "neverBlockVerified",
|
349 |
"loginSec_countFailMins" => "1440",
|
350 |
"loginSec_lockoutMins" => "1440",
|
@@ -394,16 +408,18 @@ class wfConfig {
|
|
394 |
"scansEnabled_plugins" => false,
|
395 |
"scansEnabled_malware" => true,
|
396 |
"scansEnabled_fileContents" => true,
|
397 |
-
"scansEnabled_database" => true,
|
398 |
"scansEnabled_posts" => true,
|
399 |
"scansEnabled_comments" => true,
|
400 |
"scansEnabled_passwds" => true,
|
401 |
"scansEnabled_diskSpace" => true,
|
402 |
"scansEnabled_options" => true,
|
|
|
|
|
403 |
"scansEnabled_dns" => true,
|
404 |
"scansEnabled_scanImages" => false,
|
405 |
"scansEnabled_highSense" => false,
|
406 |
"scansEnabled_oldVersions" => true,
|
|
|
407 |
"firewallEnabled" => true,
|
408 |
"blockFakeBots" => true,
|
409 |
"autoBlockScanners" => true,
|
@@ -435,6 +451,7 @@ class wfConfig {
|
|
435 |
"otherParams" => array(
|
436 |
'securityLevel' => '4',
|
437 |
"alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'scan_exclude' => '', 'whitelisted' => '', 'bannedURLs' => '', 'maxExecutionTime' => '', 'howGetIPs' => '', 'actUpdateInterval' => '', 'alert_maxHourly' => 0, 'loginSec_userBlacklist' => '',
|
|
|
438 |
"neverBlockBG" => "neverBlockVerified",
|
439 |
"loginSec_countFailMins" => "1440",
|
440 |
"loginSec_lockoutMins" => "1440",
|
@@ -551,12 +568,20 @@ class wfConfig {
|
|
551 |
return;
|
552 |
}
|
553 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
554 |
self::getDB()->queryWrite("insert into " . self::table() . " (name, val) values ('%s', '%s') ON DUPLICATE KEY UPDATE val='%s'", $key, $val, $val);
|
555 |
self::$cache[$key] = $val;
|
556 |
self::clearDiskCache();
|
557 |
}
|
558 |
private static function getCacheFile(){
|
559 |
-
return
|
560 |
}
|
561 |
public static function clearDiskCache(){
|
562 |
//When we write to the cache we just trash the whole cache on the first write. Second write won't get called because we've disabled the cache.
|
@@ -614,7 +639,7 @@ class wfConfig {
|
|
614 |
}
|
615 |
}
|
616 |
$val = self::getDB()->querySingle("select val from " . self::table() . " where name='%s'", $key);
|
617 |
-
if(self::$diskCacheDisabled){
|
618 |
return $val;
|
619 |
}
|
620 |
wfConfig::$diskCache[$key] = isset($val) ? $val : '';
|
@@ -734,7 +759,7 @@ class wfConfig {
|
|
734 |
}
|
735 |
}
|
736 |
private static function getPotentialTempDirs() {
|
737 |
-
return array(
|
738 |
}
|
739 |
public static function f($key){
|
740 |
echo esc_attr(self::get($key));
|
@@ -843,9 +868,9 @@ class wfConfig {
|
|
843 |
wp_update_plugins();
|
844 |
ob_start();
|
845 |
$upgrader = new Plugin_Upgrader();
|
846 |
-
$upret = $upgrader->upgrade(
|
847 |
if($upret){
|
848 |
-
$cont = file_get_contents(
|
849 |
if(wfConfig::get('alertOn_update') == '1' && preg_match('/Version: (\d+\.\d+\.\d+)/', $cont, $matches) ){
|
850 |
wordfence::alert("Wordfence Upgraded to version " . $matches[1], "Your Wordfence installation has been upgraded to version " . $matches[1], '127.0.0.1');
|
851 |
}
|
34 |
"scansEnabled_plugins" => false,
|
35 |
"scansEnabled_malware" => false,
|
36 |
"scansEnabled_fileContents" => false,
|
|
|
37 |
"scansEnabled_posts" => false,
|
38 |
"scansEnabled_comments" => false,
|
39 |
"scansEnabled_passwds" => false,
|
40 |
"scansEnabled_diskSpace" => false,
|
41 |
"scansEnabled_options" => false,
|
42 |
+
"scansEnabled_wpscan_fullPathDisclosure" => true,
|
43 |
+
"scansEnabled_wpscan_directoryListingEnabled" => true,
|
44 |
"scansEnabled_dns" => false,
|
45 |
"scansEnabled_scanImages" => false,
|
46 |
"scansEnabled_highSense" => false,
|
47 |
"scansEnabled_oldVersions" => false,
|
48 |
+
"scansEnabled_suspiciousAdminUsers" => false,
|
49 |
"firewallEnabled" => false,
|
50 |
"blockFakeBots" => false,
|
51 |
"autoBlockScanners" => false,
|
76 |
),
|
77 |
"otherParams" => array(
|
78 |
'securityLevel' => '0',
|
79 |
+
"alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'scan_exclude' => '', 'whitelisted' => '', 'bannedURLs' => '', 'maxExecutionTime' => '', 'howGetIPs' => '', 'actUpdateInterval' => '', 'alert_maxHourly' => 0, 'loginSec_userBlacklist' => '',
|
80 |
+
'liveTraf_maxRows' => 2000,
|
81 |
"neverBlockBG" => "neverBlockVerified",
|
82 |
"loginSec_countFailMins" => "5",
|
83 |
"loginSec_lockoutMins" => "5",
|
127 |
"scansEnabled_plugins" => false,
|
128 |
"scansEnabled_malware" => true,
|
129 |
"scansEnabled_fileContents" => true,
|
|
|
130 |
"scansEnabled_posts" => true,
|
131 |
"scansEnabled_comments" => true,
|
132 |
"scansEnabled_passwds" => true,
|
133 |
"scansEnabled_diskSpace" => true,
|
134 |
"scansEnabled_options" => true,
|
135 |
+
"scansEnabled_wpscan_fullPathDisclosure" => true,
|
136 |
+
"scansEnabled_wpscan_directoryListingEnabled" => true,
|
137 |
"scansEnabled_dns" => true,
|
138 |
"scansEnabled_scanImages" => false,
|
139 |
"scansEnabled_highSense" => false,
|
140 |
"scansEnabled_oldVersions" => true,
|
141 |
+
"scansEnabled_suspiciousAdminUsers" => true,
|
142 |
"firewallEnabled" => true,
|
143 |
"blockFakeBots" => false,
|
144 |
"autoBlockScanners" => true,
|
170 |
"otherParams" => array(
|
171 |
'securityLevel' => '1',
|
172 |
"alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'scan_exclude' => '', 'whitelisted' => '', 'bannedURLs' => '', 'maxExecutionTime' => '', 'howGetIPs' => '', 'actUpdateInterval' => '', 'alert_maxHourly' => 0, 'loginSec_userBlacklist' => '',
|
173 |
+
'liveTraf_maxRows' => 2000,
|
174 |
"neverBlockBG" => "neverBlockVerified",
|
175 |
"loginSec_countFailMins" => "5",
|
176 |
"loginSec_lockoutMins" => "5",
|
207 |
"alertOn_adminLogin" => true,
|
208 |
"alertOn_nonAdminLogin" => false,
|
209 |
"liveTrafficEnabled" => true,
|
210 |
+
"scansEnabled_checkReadableConfig" => true,
|
211 |
"advancedCommentScanning" => false,
|
212 |
"checkSpamIP" => false,
|
213 |
"spamvertizeCheck" => false,
|
221 |
"scansEnabled_plugins" => false,
|
222 |
"scansEnabled_malware" => true,
|
223 |
"scansEnabled_fileContents" => true,
|
|
|
224 |
"scansEnabled_posts" => true,
|
225 |
"scansEnabled_comments" => true,
|
226 |
"scansEnabled_passwds" => true,
|
227 |
"scansEnabled_diskSpace" => true,
|
228 |
"scansEnabled_options" => true,
|
229 |
+
"scansEnabled_wpscan_fullPathDisclosure" => true,
|
230 |
+
"scansEnabled_wpscan_directoryListingEnabled" => true,
|
231 |
"scansEnabled_dns" => true,
|
232 |
"scansEnabled_scanImages" => false,
|
233 |
"scansEnabled_highSense" => false,
|
234 |
"scansEnabled_oldVersions" => true,
|
235 |
+
"scansEnabled_suspiciousAdminUsers" => true,
|
236 |
"firewallEnabled" => true,
|
237 |
"blockFakeBots" => false,
|
238 |
"autoBlockScanners" => true,
|
262 |
'ssl_verify' => true,
|
263 |
),
|
264 |
"otherParams" => array(
|
265 |
+
"scan_include_extra" => "",
|
266 |
'securityLevel' => '2',
|
267 |
"alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'scan_exclude' => '', 'whitelisted' => '', 'bannedURLs' => '', 'maxExecutionTime' => '', 'howGetIPs' => '', 'actUpdateInterval' => '', 'alert_maxHourly' => 0, 'loginSec_userBlacklist' => '',
|
268 |
+
'liveTraf_maxRows' => 2000,
|
269 |
"neverBlockBG" => "neverBlockVerified",
|
270 |
"loginSec_countFailMins" => "240",
|
271 |
"loginSec_lockoutMins" => "240",
|
315 |
"scansEnabled_plugins" => false,
|
316 |
"scansEnabled_malware" => true,
|
317 |
"scansEnabled_fileContents" => true,
|
|
|
318 |
"scansEnabled_posts" => true,
|
319 |
"scansEnabled_comments" => true,
|
320 |
"scansEnabled_passwds" => true,
|
321 |
"scansEnabled_diskSpace" => true,
|
322 |
"scansEnabled_options" => true,
|
323 |
+
"scansEnabled_wpscan_fullPathDisclosure" => true,
|
324 |
+
"scansEnabled_wpscan_directoryListingEnabled" => true,
|
325 |
"scansEnabled_dns" => true,
|
326 |
"scansEnabled_scanImages" => false,
|
327 |
"scansEnabled_highSense" => false,
|
328 |
"scansEnabled_oldVersions" => true,
|
329 |
+
"scansEnabled_suspiciousAdminUsers" => true,
|
330 |
"firewallEnabled" => true,
|
331 |
"blockFakeBots" => false,
|
332 |
"autoBlockScanners" => true,
|
358 |
"otherParams" => array(
|
359 |
'securityLevel' => '3',
|
360 |
"alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'scan_exclude' => '', 'whitelisted' => '', 'bannedURLs' => '', 'maxExecutionTime' => '', 'howGetIPs' => '', 'actUpdateInterval' => '', 'alert_maxHourly' => 0, 'loginSec_userBlacklist' => '',
|
361 |
+
'liveTraf_maxRows' => 2000,
|
362 |
"neverBlockBG" => "neverBlockVerified",
|
363 |
"loginSec_countFailMins" => "1440",
|
364 |
"loginSec_lockoutMins" => "1440",
|
408 |
"scansEnabled_plugins" => false,
|
409 |
"scansEnabled_malware" => true,
|
410 |
"scansEnabled_fileContents" => true,
|
|
|
411 |
"scansEnabled_posts" => true,
|
412 |
"scansEnabled_comments" => true,
|
413 |
"scansEnabled_passwds" => true,
|
414 |
"scansEnabled_diskSpace" => true,
|
415 |
"scansEnabled_options" => true,
|
416 |
+
"scansEnabled_wpscan_fullPathDisclosure" => true,
|
417 |
+
"scansEnabled_wpscan_directoryListingEnabled" => true,
|
418 |
"scansEnabled_dns" => true,
|
419 |
"scansEnabled_scanImages" => false,
|
420 |
"scansEnabled_highSense" => false,
|
421 |
"scansEnabled_oldVersions" => true,
|
422 |
+
"scansEnabled_suspiciousAdminUsers" => true,
|
423 |
"firewallEnabled" => true,
|
424 |
"blockFakeBots" => true,
|
425 |
"autoBlockScanners" => true,
|
451 |
"otherParams" => array(
|
452 |
'securityLevel' => '4',
|
453 |
"alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'scan_exclude' => '', 'whitelisted' => '', 'bannedURLs' => '', 'maxExecutionTime' => '', 'howGetIPs' => '', 'actUpdateInterval' => '', 'alert_maxHourly' => 0, 'loginSec_userBlacklist' => '',
|
454 |
+
'liveTraf_maxRows' => 2000,
|
455 |
"neverBlockBG" => "neverBlockVerified",
|
456 |
"loginSec_countFailMins" => "1440",
|
457 |
"loginSec_lockoutMins" => "1440",
|
568 |
return;
|
569 |
}
|
570 |
|
571 |
+
if ($key == 'apiKey' && wfWAF::getInstance() && !WFWAF_SUBDIRECTORY_INSTALL) {
|
572 |
+
try {
|
573 |
+
wfWAF::getInstance()->getStorageEngine()->setConfig('apiKey', $val);
|
574 |
+
} catch (wfWAFStorageFileException $e) {
|
575 |
+
error_log($e->getMessage());
|
576 |
+
}
|
577 |
+
}
|
578 |
+
|
579 |
self::getDB()->queryWrite("insert into " . self::table() . " (name, val) values ('%s', '%s') ON DUPLICATE KEY UPDATE val='%s'", $key, $val, $val);
|
580 |
self::$cache[$key] = $val;
|
581 |
self::clearDiskCache();
|
582 |
}
|
583 |
private static function getCacheFile(){
|
584 |
+
return WORDFENCE_PATH . 'tmp/configCache.php';
|
585 |
}
|
586 |
public static function clearDiskCache(){
|
587 |
//When we write to the cache we just trash the whole cache on the first write. Second write won't get called because we've disabled the cache.
|
639 |
}
|
640 |
}
|
641 |
$val = self::getDB()->querySingle("select val from " . self::table() . " where name='%s'", $key);
|
642 |
+
if(self::$diskCacheDisabled){
|
643 |
return $val;
|
644 |
}
|
645 |
wfConfig::$diskCache[$key] = isset($val) ? $val : '';
|
759 |
}
|
760 |
}
|
761 |
private static function getPotentialTempDirs() {
|
762 |
+
return array(WORDFENCE_PATH . 'tmp/', sys_get_temp_dir(), ABSPATH . 'wp-content/uploads/');
|
763 |
}
|
764 |
public static function f($key){
|
765 |
echo esc_attr(self::get($key));
|
868 |
wp_update_plugins();
|
869 |
ob_start();
|
870 |
$upgrader = new Plugin_Upgrader();
|
871 |
+
$upret = $upgrader->upgrade(WORDFENCE_BASENAME);
|
872 |
if($upret){
|
873 |
+
$cont = file_get_contents(WORDFENCE_FCPATH);
|
874 |
if(wfConfig::get('alertOn_update') == '1' && preg_match('/Version: (\d+\.\d+\.\d+)/', $cont, $matches) ){
|
875 |
wordfence::alert("Wordfence Upgraded to version " . $matches[1], "Your Wordfence installation has been upgraded to version " . $matches[1], '127.0.0.1');
|
876 |
}
|
lib/wfCrawl.php
CHANGED
@@ -47,20 +47,18 @@ class wfCrawl {
|
|
47 |
return false;
|
48 |
}
|
49 |
}
|
50 |
-
public static function isGooglebot(){
|
51 |
-
|
52 |
-
|
53 |
-
return true;
|
54 |
}
|
55 |
-
return
|
56 |
}
|
57 |
-
public static function isGoogleCrawler($
|
58 |
-
if ($
|
59 |
-
$
|
60 |
}
|
61 |
-
|
62 |
-
|
63 |
-
if(preg_match($pat . 'i', $UA)){
|
64 |
return true;
|
65 |
}
|
66 |
}
|
47 |
return false;
|
48 |
}
|
49 |
}
|
50 |
+
public static function isGooglebot($userAgent = null){
|
51 |
+
if ($userAgent === null) {
|
52 |
+
$userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
|
|
|
53 |
}
|
54 |
+
return (bool) preg_match('/Googlebot\/\d\.\d/', $userAgent);
|
55 |
}
|
56 |
+
public static function isGoogleCrawler($userAgent = null){
|
57 |
+
if ($userAgent === null) {
|
58 |
+
$userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
|
59 |
}
|
60 |
+
foreach (self::$googPat as $pat) {
|
61 |
+
if (preg_match($pat . 'i', $userAgent)) {
|
|
|
62 |
return true;
|
63 |
}
|
64 |
}
|
lib/wfDB.php
CHANGED
@@ -102,7 +102,166 @@ class wfDB {
|
|
102 |
global $wpdb;
|
103 |
return $wpdb->_real_escape($str);
|
104 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
}
|
107 |
|
|
|
108 |
?>
|
102 |
global $wpdb;
|
103 |
return $wpdb->_real_escape($str);
|
104 |
}
|
105 |
+
}
|
106 |
+
|
107 |
+
abstract class wfModel {
|
108 |
+
|
109 |
+
private $data;
|
110 |
+
private $db;
|
111 |
+
private $dirty = false;
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Column name of the primary key field.
|
115 |
+
*
|
116 |
+
* @return string
|
117 |
+
*/
|
118 |
+
abstract public function getIDColumn();
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Table name.
|
122 |
+
*
|
123 |
+
* @return mixed
|
124 |
+
*/
|
125 |
+
abstract public function getTable();
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Checks if this is a valid column in the table before setting data on the model.
|
129 |
+
*
|
130 |
+
* @param string $column
|
131 |
+
* @return boolean
|
132 |
+
*/
|
133 |
+
abstract public function hasColumn($column);
|
134 |
|
135 |
+
/**
|
136 |
+
* wfModel constructor.
|
137 |
+
* @param array|int|string $data
|
138 |
+
*/
|
139 |
+
public function __construct($data = array()) {
|
140 |
+
if (is_array($data) || is_object($data)) {
|
141 |
+
$this->setData($data);
|
142 |
+
} else if (is_numeric($data)) {
|
143 |
+
$this->fetchByID($data);
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
public function fetchByID($id) {
|
148 |
+
$id = absint($id);
|
149 |
+
$data = $this->getDB()->get_row($this->getDB()->prepare('SELECT * FROM ' . $this->getTable() .
|
150 |
+
' WHERE ' . $this->getIDColumn() . ' = %d', $id));
|
151 |
+
if ($data) {
|
152 |
+
$this->setData($data);
|
153 |
+
return true;
|
154 |
+
}
|
155 |
+
return false;
|
156 |
+
}
|
157 |
+
|
158 |
+
/**
|
159 |
+
* @return bool
|
160 |
+
*/
|
161 |
+
public function save() {
|
162 |
+
if (!$this->dirty) {
|
163 |
+
return false;
|
164 |
+
}
|
165 |
+
$this->dirty = ($this->getPrimaryKey() ? $this->update() : $this->insert()) === false;
|
166 |
+
return !$this->dirty;
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* @return false|int
|
171 |
+
*/
|
172 |
+
public function insert() {
|
173 |
+
$data = $this->getData();
|
174 |
+
unset($data[$this->getPrimaryKey()]);
|
175 |
+
$rowsAffected = $this->getDB()->insert($this->getTable(), $data);
|
176 |
+
$this->setPrimaryKey($this->getDB()->insert_id);
|
177 |
+
return $rowsAffected;
|
178 |
+
}
|
179 |
+
|
180 |
+
/**
|
181 |
+
* @return false|int
|
182 |
+
*/
|
183 |
+
public function update() {
|
184 |
+
return $this->getDB()->update($this->getTable(), $this->getData(), array(
|
185 |
+
$this->getIDColumn() => $this->getPrimaryKey(),
|
186 |
+
));
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* @param $name string
|
191 |
+
* @return mixed
|
192 |
+
*/
|
193 |
+
public function __get($name) {
|
194 |
+
if (!$this->hasColumn($name)) {
|
195 |
+
return null;
|
196 |
+
}
|
197 |
+
return array_key_exists($name, $this->data) ? $this->data[$name] : null;
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* @param $name string
|
202 |
+
* @param $value mixed
|
203 |
+
*/
|
204 |
+
public function __set($name, $value) {
|
205 |
+
if (!$this->hasColumn($name)) {
|
206 |
+
return;
|
207 |
+
}
|
208 |
+
$this->data[$name] = $value;
|
209 |
+
$this->dirty = true;
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* @return array
|
214 |
+
*/
|
215 |
+
public function getData() {
|
216 |
+
return $this->data;
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* @param array $data
|
221 |
+
* @param bool $flagDirty
|
222 |
+
*/
|
223 |
+
public function setData($data, $flagDirty = true) {
|
224 |
+
$this->data = array();
|
225 |
+
foreach ($data as $column => $value) {
|
226 |
+
if ($this->hasColumn($column)) {
|
227 |
+
$this->data[$column] = $value;
|
228 |
+
$this->dirty = (bool) $flagDirty;
|
229 |
+
}
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* @return wpdb
|
235 |
+
*/
|
236 |
+
public function getDB() {
|
237 |
+
if ($this->db === null) {
|
238 |
+
global $wpdb;
|
239 |
+
$this->db = $wpdb;
|
240 |
+
}
|
241 |
+
return $this->db;
|
242 |
+
}
|
243 |
+
|
244 |
+
/**
|
245 |
+
* @param wpdb $db
|
246 |
+
*/
|
247 |
+
public function setDB($db) {
|
248 |
+
$this->db = $db;
|
249 |
+
}
|
250 |
+
|
251 |
+
/**
|
252 |
+
* @return int
|
253 |
+
*/
|
254 |
+
public function getPrimaryKey() {
|
255 |
+
return $this->{$this->getIDColumn()};
|
256 |
+
}
|
257 |
+
|
258 |
+
/**
|
259 |
+
* @param int $value
|
260 |
+
*/
|
261 |
+
public function setPrimaryKey($value) {
|
262 |
+
$this->{$this->getIDColumn()} = $value;
|
263 |
+
}
|
264 |
}
|
265 |
|
266 |
+
|
267 |
?>
|
lib/wfDiagnostic.php
ADDED
@@ -0,0 +1,245 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class wfGrant
|
4 |
+
{
|
5 |
+
public $select = false;
|
6 |
+
public $update = false;
|
7 |
+
public $insert = false;
|
8 |
+
public $delete = false;
|
9 |
+
public $alter = false;
|
10 |
+
public $create = false;
|
11 |
+
public $drop = false;
|
12 |
+
|
13 |
+
public static function get()
|
14 |
+
{
|
15 |
+
static $instance;
|
16 |
+
if ($instance === null) {
|
17 |
+
$instance = new self;
|
18 |
+
}
|
19 |
+
return $instance;
|
20 |
+
}
|
21 |
+
|
22 |
+
private function __construct()
|
23 |
+
{
|
24 |
+
global $wpdb;
|
25 |
+
$rows = $wpdb->get_results("SHOW GRANTS FOR current_user()", ARRAY_N);
|
26 |
+
|
27 |
+
foreach ($rows as $row) {
|
28 |
+
preg_match("/GRANT (.+) ON (.+) TO/", $row[0], $matches);
|
29 |
+
foreach (explode(",", $matches[1]) as $permission) {
|
30 |
+
$permission = str_replace(" ", "_", trim(strtolower($permission)));
|
31 |
+
if ($permission === 'all_privileges') {
|
32 |
+
foreach ($this as $key => $value) {
|
33 |
+
$this->$key = true;
|
34 |
+
}
|
35 |
+
break 2;
|
36 |
+
}
|
37 |
+
$this->$permission = true;
|
38 |
+
}
|
39 |
+
}
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
class wfDiagnostic
|
44 |
+
{
|
45 |
+
protected $minVersion = array(
|
46 |
+
'PHP' => '5.2.4',
|
47 |
+
'cURL' => '1.0',
|
48 |
+
);
|
49 |
+
|
50 |
+
protected $description = array(
|
51 |
+
'Filesystem' => array(
|
52 |
+
'isTmpReadable' => 'Checking if web server can read from <code>~/plugins/wordfence/tmp</code>',
|
53 |
+
'isTmpWritable' => 'Checking if web server can write to <code>~/plugins/wordfence/tmp</code>',
|
54 |
+
'testWfCache' => 'Checking if web server can write to <code>~/wp-content/wfcache</code>',
|
55 |
+
),
|
56 |
+
'MySQL' => array(
|
57 |
+
'userCanDelete' => 'Checking if MySQL user has <code>DELETE</code> privilege',
|
58 |
+
'userCanInsert' => 'Checking if MySQL user has <code>INSERT</code> privilege',
|
59 |
+
'userCanSelect' => 'Checking if MySQL user has <code>SELECT</code> privilege',
|
60 |
+
'userCanCreate' => 'Checking if MySQL user has <code>CREATE TABLE</code> privilege',
|
61 |
+
'userCanAlter' => 'Checking if MySQL user has <code>ALTER TABLE</code> privilege',
|
62 |
+
'userCanDrop' => 'Checking if MySQL user has <code>DROP</code> privilege',
|
63 |
+
'userCanTruncate' => 'Checking if MySQL user has <code>TRUNCATE</code> privilege',
|
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 |
+
),
|
70 |
+
'Connectivity' => array(
|
71 |
+
'connectToServer1' => 'Connecting to Wordfence servers (http)',
|
72 |
+
'connectToServer2' => 'Connecting to Wordfence servers (https)',
|
73 |
+
),
|
74 |
+
// 'Configuration' => array(
|
75 |
+
// 'howGetIPs' => 'How does get IPs',
|
76 |
+
// ),
|
77 |
+
);
|
78 |
+
|
79 |
+
protected $results = array();
|
80 |
+
|
81 |
+
public function __construct()
|
82 |
+
{
|
83 |
+
foreach ($this->description as $title => $tests) {
|
84 |
+
$this->results[$title] = array();
|
85 |
+
foreach ($tests as $name => $description) {
|
86 |
+
$result = $this->$name();
|
87 |
+
|
88 |
+
if (is_bool($result)) {
|
89 |
+
$result = array(
|
90 |
+
'test' => $result,
|
91 |
+
'message' => $result ? 'OK' : 'FAIL',
|
92 |
+
);
|
93 |
+
}
|
94 |
+
|
95 |
+
$result['label'] = $description;
|
96 |
+
|
97 |
+
$this->results[$title][] = $result;
|
98 |
+
}
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
public function getResults()
|
103 |
+
{
|
104 |
+
return $this->results;
|
105 |
+
}
|
106 |
+
|
107 |
+
public function isTmpReadable() {
|
108 |
+
return is_readable(WORDFENCE_PATH . 'tmp');
|
109 |
+
}
|
110 |
+
|
111 |
+
public function isTmpWritable() {
|
112 |
+
return is_writable(WORDFENCE_PATH . 'tmp');
|
113 |
+
}
|
114 |
+
|
115 |
+
public function userCanInsert() {
|
116 |
+
return wfGrant::get()->insert;
|
117 |
+
}
|
118 |
+
|
119 |
+
public function testWfCache() {
|
120 |
+
$result = wfCache::cacheDirectoryTest();
|
121 |
+
return array(
|
122 |
+
'test' => $result === false,
|
123 |
+
'message' => is_string($result) ? $result : 'OK'
|
124 |
+
);
|
125 |
+
}
|
126 |
+
|
127 |
+
public function userCanDelete() {
|
128 |
+
return wfGrant::get()->delete;
|
129 |
+
}
|
130 |
+
|
131 |
+
public function userCanSelect() {
|
132 |
+
return wfGrant::get()->select;
|
133 |
+
}
|
134 |
+
|
135 |
+
public function userCanCreate() {
|
136 |
+
return wfGrant::get()->create;
|
137 |
+
}
|
138 |
+
|
139 |
+
public function userCanDrop() {
|
140 |
+
return wfGrant::get()->drop;
|
141 |
+
}
|
142 |
+
|
143 |
+
public function userCanTruncate() {
|
144 |
+
return wfGrant::get()->drop && wfGrant::get()->delete;
|
145 |
+
}
|
146 |
+
|
147 |
+
public function userCanAlter() {
|
148 |
+
return wfGrant::get()->alter;
|
149 |
+
}
|
150 |
+
|
151 |
+
public function phpVersion()
|
152 |
+
{
|
153 |
+
return array(
|
154 |
+
'test' => version_compare(phpversion(), $this->minVersion['PHP'], '>='),
|
155 |
+
'message' => phpversion(),
|
156 |
+
);
|
157 |
+
}
|
158 |
+
|
159 |
+
public function hasOpenSSL() {
|
160 |
+
return is_callable('openssl_open');
|
161 |
+
}
|
162 |
+
|
163 |
+
public function hasCurl() {
|
164 |
+
if (!is_callable('curl_version')) {
|
165 |
+
return false;
|
166 |
+
}
|
167 |
+
$version = curl_version();
|
168 |
+
return array(
|
169 |
+
'test' => version_compare($version['version'], $this->minVersion['cURL'], '>='),
|
170 |
+
'message' => $version['version'],
|
171 |
+
);
|
172 |
+
}
|
173 |
+
|
174 |
+
public function connectToServer1() {
|
175 |
+
return $this->_connectToServer('http');
|
176 |
+
}
|
177 |
+
|
178 |
+
public function connectToServer2() {
|
179 |
+
return $this->_connectToServer('https');
|
180 |
+
}
|
181 |
+
|
182 |
+
public function _connectToServer($protocol) {
|
183 |
+
$cronURL = admin_url('admin-ajax.php');
|
184 |
+
$cronURL = preg_replace('/^(https?:\/\/)/i', '://noc1.wordfence.com/scanptest/', $cronURL);
|
185 |
+
$cronURL .= '?action=wordfence_doScan&isFork=0&cronKey=47e9d1fa6a675b5999999333';
|
186 |
+
$cronURL = $protocol . $cronURL;
|
187 |
+
$result = wp_remote_post($cronURL, array(
|
188 |
+
'timeout' => 10, //Must be less than max execution time or more than 2 HTTP children will be occupied by scan
|
189 |
+
'blocking' => true, //Non-blocking seems to block anyway, so we use blocking
|
190 |
+
// This causes cURL to throw errors in some versions since WordPress uses its own certificate bundle ('CA certificate set, but certificate verification is disabled')
|
191 |
+
// 'sslverify' => false,
|
192 |
+
'headers' => array()
|
193 |
+
));
|
194 |
+
if( (! is_wp_error($result)) && $result['response']['code'] == 200 && strpos($result['body'], "scanptestok") !== false){
|
195 |
+
return true;
|
196 |
+
}
|
197 |
+
|
198 |
+
ob_start();
|
199 |
+
if(is_wp_error($result)){
|
200 |
+
echo "wp_remote_post() test to noc1.wordfence.com failed! Response was: " . $result->get_error_message() . "<br />\n";
|
201 |
+
} else {
|
202 |
+
echo "wp_remote_post() test to noc1.wordfence.com failed! Response was: " . $result['response']['code'] . " " . $result['response']['message'] . "<br />\n";
|
203 |
+
echo "This likely means that your hosting provider is blocking requests to noc1.wordfence.com or has set up a proxy that is not behaving itself.<br />\n";
|
204 |
+
echo "This additional info may help you diagnose the issue. The response headers we received were:<br />\n";
|
205 |
+
foreach($result['headers'] as $key => $value){
|
206 |
+
echo "$key => $value<br />\n";
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
return array(
|
211 |
+
'test' => false,
|
212 |
+
'message' => ob_get_clean()
|
213 |
+
);
|
214 |
+
}
|
215 |
+
|
216 |
+
public function howGetIPs()
|
217 |
+
{
|
218 |
+
$howGet = wfConfig::get('howGetIPs', false);
|
219 |
+
if ($howGet) {
|
220 |
+
if (empty($_SERVER[$howGet])) {
|
221 |
+
return array(
|
222 |
+
'test' => false,
|
223 |
+
'message' => 'We cannot read $_SERVER[' . $howGet . ']',
|
224 |
+
);
|
225 |
+
}
|
226 |
+
return array(
|
227 |
+
'test' => true,
|
228 |
+
'message' => $howGet,
|
229 |
+
);
|
230 |
+
}
|
231 |
+
foreach (array('HTTP_CF_CONNECTING_IP', 'HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR') as $test) {
|
232 |
+
if (!empty($_SERVER[$test])) {
|
233 |
+
return array(
|
234 |
+
'test' => false,
|
235 |
+
'message' => 'Should be: ' . $test
|
236 |
+
);
|
237 |
+
}
|
238 |
+
}
|
239 |
+
return array(
|
240 |
+
'test' => true,
|
241 |
+
'message' => 'REMOTE_ADDR',
|
242 |
+
);
|
243 |
+
}
|
244 |
+
}
|
245 |
+
|
lib/wfIssues.php
CHANGED
@@ -178,8 +178,11 @@ class wfIssues {
|
|
178 |
}
|
179 |
}
|
180 |
if ($issueList[$i]['type'] == 'database') {
|
181 |
-
$
|
182 |
-
|
|
|
|
|
|
|
183 |
}
|
184 |
$issueList[$i]['issueIDX'] = $i;
|
185 |
}
|
178 |
}
|
179 |
}
|
180 |
if ($issueList[$i]['type'] == 'database') {
|
181 |
+
$issueList[$i]['data']['optionExists'] = false;
|
182 |
+
if (!empty($issueList[$i]['data']['site_id'])) {
|
183 |
+
$prefix = $wpdb->get_blog_prefix($issueList[$i]['data']['site_id']);
|
184 |
+
$issueList[$i]['data']['optionExists'] = $wpdb->get_var($wpdb->prepare("SELECT count(*) FROM {$prefix}options WHERE option_name = %s", $issueList[$i]['data']['option_name'])) > 0;
|
185 |
+
}
|
186 |
}
|
187 |
$issueList[$i]['issueIDX'] = $i;
|
188 |
}
|
lib/wfLog.php
CHANGED
@@ -3,12 +3,19 @@ require_once('wfDB.php');
|
|
3 |
require_once('wfUtils.php');
|
4 |
require_once('wfBrowscap.php');
|
5 |
class wfLog {
|
|
|
6 |
private $hitsTable = '';
|
7 |
private $apiKey = '';
|
8 |
private $wp_version = '';
|
9 |
private $db = false;
|
10 |
private $googlePattern = '/\.(?:googlebot\.com|google\.[a-z]{2,3}|google\.[a-z]{2}\.[a-z]{2}|1e100\.net)$/i';
|
11 |
private static $gbSafeCache = array();
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
public function __construct($apiKey, $wp_version){
|
13 |
$this->apiKey = $apiKey;
|
14 |
$this->wp_version = $wp_version;
|
@@ -25,6 +32,96 @@ class wfLog {
|
|
25 |
$this->ipRangesTable = $wpdb->base_prefix . 'wfBlocksAdv';
|
26 |
$this->perfTable = $wpdb->base_prefix . 'wfPerfLog';
|
27 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
public function logPerf($IP, $UA, $URL, $data){
|
29 |
$IP = wfUtils::inet_pton($IP);
|
30 |
$this->getDB()->queryWrite("insert into " . $this->perfTable . " (IP, userID, UA, URL, ctime, fetchStart, domainLookupStart, domainLookupEnd, connectStart, connectEnd, requestStart, responseStart, responseEnd, domReady, loaded) values (%s, %d, '%s', '%s', unix_timestamp(), %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)",
|
@@ -60,8 +157,18 @@ class wfLog {
|
|
60 |
if ($action == 'loginFailValidUsername' && $userID == 0) {
|
61 |
$action = 'loginFailInvalidUsername';
|
62 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
//Else userID stays 0 but we do log this even though the user doesn't exist.
|
64 |
-
$this->getDB()->queryWrite("insert into " . $this->loginsTable . " (ctime, fail, action, username, userID, IP, UA) values (%f, %d, '%s', '%s', %s, %s, '%s')",
|
|
|
65 |
sprintf('%.6f', microtime(true)),
|
66 |
$fail,
|
67 |
$action,
|
@@ -303,6 +410,12 @@ class wfLog {
|
|
303 |
|
304 |
wfActivityReport::logBlockedIP($IP);
|
305 |
|
|
|
|
|
|
|
|
|
|
|
|
|
306 |
wfCache::updateBlockedIPs('add');
|
307 |
wfConfig::inc('totalIPsBlocked');
|
308 |
return true;
|
@@ -318,6 +431,12 @@ class wfLog {
|
|
318 |
|
319 |
wfActivityReport::logBlockedIP($IP);
|
320 |
|
|
|
|
|
|
|
|
|
|
|
|
|
321 |
wfConfig::inc('totalIPsLocked');
|
322 |
return true;
|
323 |
}
|
@@ -418,29 +537,22 @@ class wfLog {
|
|
418 |
}
|
419 |
return $results;
|
420 |
}
|
|
|
|
|
|
|
|
|
421 |
public function logHit(){
|
422 |
-
if(!
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
|
|
427 |
}
|
428 |
}
|
429 |
-
|
430 |
-
$this->getDB()->queryWrite("insert into " . $this->hitsTable . " (ctime, is404, isGoogle, IP, userID, newVisit, URL, referer, UA, jsRun) values (%f, %d, %d, %s, %s, %d, '%s', '%s', '%s', %d)",
|
431 |
-
sprintf('%.6f', microtime(true)),
|
432 |
-
(is_404() ? 1 : 0),
|
433 |
-
(wfCrawl::isGoogleCrawler() ? 1 : 0),
|
434 |
-
wfUtils::inet_pton(wfUtils::getIP()),
|
435 |
-
$this->getCurrentUserID(),
|
436 |
-
(wordfence::$newVisit ? 1 : 0),
|
437 |
-
wfUtils::getRequestedURL(),
|
438 |
-
(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''),
|
439 |
-
$ua,
|
440 |
-
(int) (isset($_COOKIE['wordfence_verifiedHuman']) && wp_verify_nonce($_COOKIE['wordfence_verifiedHuman'], 'wordfence_verifiedHuman' . $ua . wfUtils::getIP()))
|
441 |
-
);
|
442 |
-
return $this->getDB()->querySingle("select last_insert_id()");
|
443 |
}
|
|
|
444 |
public function getPerfStats($afterTime, $limit = 50){
|
445 |
$serverTime = $this->getDB()->querySingle("select unix_timestamp()");
|
446 |
$results = $this->getDB()->querySelect("select * from " . $this->perfTable . " where ctime > %f order by ctime desc limit %d", $afterTime, $limit);
|
@@ -479,7 +591,7 @@ class wfLog {
|
|
479 |
return $results;
|
480 |
}
|
481 |
public function getHits($hitType /* 'hits' or 'logins' */, $type, $afterTime, $limit = 50, $IP = false){
|
482 |
-
|
483 |
$IPSQL = "";
|
484 |
if($IP){
|
485 |
$IPSQL = " and IP=%s ";
|
@@ -496,7 +608,7 @@ class wfLog {
|
|
496 |
} else if($type == 'gCrawler'){
|
497 |
$typeSQL = " and isGoogle = 1 ";
|
498 |
} else if($type == '404'){
|
499 |
-
$typeSQL = " and
|
500 |
} else if($type == 'human'){
|
501 |
$typeSQL = " and jsRun = 1 ";
|
502 |
} else if($type == 'ruser'){
|
@@ -505,17 +617,33 @@ class wfLog {
|
|
505 |
wordfence::status(1, 'error', "Invalid log type to wfLog: $type");
|
506 |
return false;
|
507 |
}
|
508 |
-
array_unshift($sqlArgs, "select
|
|
|
|
|
509 |
$results = call_user_func_array(array($this->getDB(), 'querySelect'), $sqlArgs);
|
510 |
|
511 |
} else if($hitType == 'logins'){
|
512 |
-
array_unshift($sqlArgs, "select
|
|
|
|
|
513 |
$results = call_user_func_array(array($this->getDB(), 'querySelect'), $sqlArgs );
|
514 |
|
515 |
} else {
|
516 |
wordfence::status(1, 'error', "getHits got invalid hitType: $hitType");
|
517 |
return false;
|
518 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
519 |
$this->resolveIPs($results);
|
520 |
$ourURL = parse_url(site_url());
|
521 |
$ourHost = strtolower($ourURL['host']);
|
@@ -577,7 +705,7 @@ class wfLog {
|
|
577 |
if( isset( $refURL['query'] ) ) {
|
578 |
parse_str($refURL['query'], $queryVars);
|
579 |
if(isset($queryVars[$q])){
|
580 |
-
$res['searchTerms'] = $queryVars[$q];
|
581 |
}
|
582 |
}
|
583 |
}
|
@@ -587,7 +715,7 @@ class wfLog {
|
|
587 |
if ( isset( $referringPage ) && stristr( $referringPage['host'], 'google.' ) )
|
588 |
{
|
589 |
parse_str( $referringPage['query'], $queryVars );
|
590 |
-
echo $queryVars['q']; // This is the search term used
|
591 |
}
|
592 |
}
|
593 |
}
|
@@ -605,23 +733,23 @@ class wfLog {
|
|
605 |
}
|
606 |
}
|
607 |
|
608 |
-
|
609 |
if($res['userID']){
|
610 |
$ud = get_userdata($res['userID']);
|
611 |
if($ud){
|
612 |
$res['user'] = array(
|
613 |
'editLink' => wfUtils::editUserLink($res['userID']),
|
614 |
-
'display_name' => $
|
615 |
'ID' => $res['userID']
|
616 |
-
|
617 |
$res['user']['avatar'] = get_avatar($res['userID'], 16);
|
618 |
}
|
619 |
} else {
|
620 |
$res['user'] = false;
|
621 |
}
|
622 |
}
|
623 |
-
return $results;
|
624 |
}
|
|
|
625 |
public function resolveIPs(&$results){
|
626 |
if(sizeof($results) < 1){ return; }
|
627 |
$IPs = array();
|
@@ -642,6 +770,9 @@ class wfLog {
|
|
642 |
}
|
643 |
}
|
644 |
public function logHitOK(){
|
|
|
|
|
|
|
645 |
if(is_admin()){ return false; } //Don't log admin pageviews
|
646 |
if(isset($_SERVER['HTTP_USER_AGENT'])){
|
647 |
if(preg_match('/WordPress\/' . $this->wp_version . '/i', $_SERVER['HTTP_USER_AGENT'])){ return false; } //Ignore requests generated by WP UA.
|
@@ -771,6 +902,7 @@ class wfLog {
|
|
771 |
if($doBlock){
|
772 |
$this->getDB()->queryWrite("update " . $this->ipRangesTable . " set totalBlocked = totalBlocked + 1, lastBlocked = unix_timestamp() where id=%d", $blockRec['id']);
|
773 |
wfActivityReport::logBlockedIP($IP);
|
|
|
774 |
$this->do503(3600, "Advanced blocking in effect.");
|
775 |
}
|
776 |
}
|
@@ -828,6 +960,7 @@ class wfLog {
|
|
828 |
$this->redirect(wfConfig::get('cbl_redirURL'));
|
829 |
}
|
830 |
} else {
|
|
|
831 |
$this->do503(3600, "Access from your area has been temporarily limited for security reasons");
|
832 |
wfConfig::inc('totalCountryBlocked');
|
833 |
}
|
@@ -895,6 +1028,15 @@ class wfLog {
|
|
895 |
}
|
896 |
}
|
897 |
public function do503($secsToGo, $reason){
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
898 |
wfConfig::inc('total503s');
|
899 |
wfUtils::doNotCache();
|
900 |
header('HTTP/1.1 503 Service Temporarily Unavailable');
|
@@ -1206,4 +1348,810 @@ class wfUserIPRange {
|
|
1206 |
}
|
1207 |
}
|
1208 |
|
1209 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
require_once('wfUtils.php');
|
4 |
require_once('wfBrowscap.php');
|
5 |
class wfLog {
|
6 |
+
public $canLogHit = true;
|
7 |
private $hitsTable = '';
|
8 |
private $apiKey = '';
|
9 |
private $wp_version = '';
|
10 |
private $db = false;
|
11 |
private $googlePattern = '/\.(?:googlebot\.com|google\.[a-z]{2,3}|google\.[a-z]{2}\.[a-z]{2}|1e100\.net)$/i';
|
12 |
private static $gbSafeCache = array();
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @var wfRequestModel
|
16 |
+
*/
|
17 |
+
private $currentRequest;
|
18 |
+
|
19 |
public function __construct($apiKey, $wp_version){
|
20 |
$this->apiKey = $apiKey;
|
21 |
$this->wp_version = $wp_version;
|
32 |
$this->ipRangesTable = $wpdb->base_prefix . 'wfBlocksAdv';
|
33 |
$this->perfTable = $wpdb->base_prefix . 'wfPerfLog';
|
34 |
}
|
35 |
+
|
36 |
+
public function initLogRequest() {
|
37 |
+
if ($this->currentRequest === null) {
|
38 |
+
$this->currentRequest = new wfRequestModel();
|
39 |
+
|
40 |
+
$this->currentRequest->ctime = sprintf('%.6f', microtime(true));
|
41 |
+
$this->currentRequest->statusCode = 200;
|
42 |
+
$this->currentRequest->isGoogle = (wfCrawl::isGoogleCrawler() ? 1 : 0);
|
43 |
+
$this->currentRequest->IP = wfUtils::inet_pton(wfUtils::getIP());
|
44 |
+
$this->currentRequest->userID = $this->getCurrentUserID();
|
45 |
+
$this->currentRequest->newVisit = (wordfence::$newVisit ? 1 : 0);
|
46 |
+
$this->currentRequest->URL = wfUtils::getRequestedURL();
|
47 |
+
$this->currentRequest->referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
|
48 |
+
$this->currentRequest->UA = (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '');
|
49 |
+
$this->currentRequest->jsRun = 0;
|
50 |
+
|
51 |
+
if (!function_exists('wp_verify_nonce')) {
|
52 |
+
add_action('plugins_loaded', array($this, 'actionSetRequestJSEnabled'));
|
53 |
+
} else {
|
54 |
+
$this->actionSetRequestJSEnabled();
|
55 |
+
}
|
56 |
+
|
57 |
+
add_action('init', array($this, 'actionSetRequestOnInit'), 9999);
|
58 |
+
|
59 |
+
if (function_exists('register_shutdown_function')) {
|
60 |
+
register_shutdown_function(array($this, 'logHit'));
|
61 |
+
}
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
public function actionSetRequestJSEnabled() {
|
66 |
+
$UA = $this->currentRequest->UA;
|
67 |
+
$IP = wfUtils::getIP();
|
68 |
+
$jsRun = (int) (isset($_COOKIE['wordfence_verifiedHuman']) &&
|
69 |
+
$this->validateVerifiedHumanCookie($_COOKIE['wordfence_verifiedHuman'], $UA, $IP));
|
70 |
+
$this->currentRequest->jsRun = $jsRun;
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* CloudFlare's plugin changes $_SERVER['REMOTE_ADDR'] on init.
|
75 |
+
*/
|
76 |
+
public function actionSetRequestOnInit() {
|
77 |
+
$this->currentRequest->IP = wfUtils::inet_pton(wfUtils::getIP());
|
78 |
+
$this->currentRequest->userID = $this->getCurrentUserID();
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* @param string $cookieVal
|
83 |
+
* @param string $ua
|
84 |
+
* @param string $ip
|
85 |
+
* @return string
|
86 |
+
*/
|
87 |
+
public function validateVerifiedHumanCookie($cookieVal, $ua = null, $ip = null) {
|
88 |
+
if ($ua === null) {
|
89 |
+
$ua = !empty($this->currentRequest) ? $this->currentRequest->UA : '';
|
90 |
+
}
|
91 |
+
if ($ip === null) {
|
92 |
+
$ip = wfUtils::getIP();
|
93 |
+
}
|
94 |
+
if (!function_exists('hash_equals')) {
|
95 |
+
require_once ABSPATH . WPINC . '/compat.php';
|
96 |
+
}
|
97 |
+
return hash_equals($cookieVal, $this->getVerifiedHumanCookieValue($ua, $ip));
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* @param string $ua
|
102 |
+
* @param string $ip
|
103 |
+
* @return string
|
104 |
+
*/
|
105 |
+
public function getVerifiedHumanCookieValue($ua = null, $ip = null) {
|
106 |
+
if ($ua === null) {
|
107 |
+
$ua = !empty($this->currentRequest) ? $this->currentRequest->UA : '';
|
108 |
+
}
|
109 |
+
if ($ip === null) {
|
110 |
+
$ip = wfUtils::getIP();
|
111 |
+
}
|
112 |
+
if (!function_exists('wp_hash')) {
|
113 |
+
require_once ABSPATH . WPINC . '/pluggable.php';
|
114 |
+
}
|
115 |
+
return wp_hash('wordfence_verifiedHuman' . $ua . $ip, 'nonce');
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* @return wfRequestModel
|
120 |
+
*/
|
121 |
+
public function getCurrentRequest() {
|
122 |
+
return $this->currentRequest;
|
123 |
+
}
|
124 |
+
|
125 |
public function logPerf($IP, $UA, $URL, $data){
|
126 |
$IP = wfUtils::inet_pton($IP);
|
127 |
$this->getDB()->queryWrite("insert into " . $this->perfTable . " (IP, userID, UA, URL, ctime, fetchStart, domainLookupStart, domainLookupEnd, connectStart, connectEnd, requestStart, responseStart, responseEnd, domReady, loaded) values (%s, %d, '%s', '%s', unix_timestamp(), %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)",
|
157 |
if ($action == 'loginFailValidUsername' && $userID == 0) {
|
158 |
$action = 'loginFailInvalidUsername';
|
159 |
}
|
160 |
+
|
161 |
+
$hitID = 0;
|
162 |
+
if ($this->currentRequest !== null) {
|
163 |
+
$this->currentRequest->userID = $userID;
|
164 |
+
$this->currentRequest->action = $action;
|
165 |
+
$this->currentRequest->save();
|
166 |
+
$hitID = $this->currentRequest->getPrimaryKey();
|
167 |
+
}
|
168 |
+
|
169 |
//Else userID stays 0 but we do log this even though the user doesn't exist.
|
170 |
+
$this->getDB()->queryWrite("insert into " . $this->loginsTable . " (hitID, ctime, fail, action, username, userID, IP, UA) values (%d, %f, %d, '%s', '%s', %s, %s, '%s')",
|
171 |
+
$hitID,
|
172 |
sprintf('%.6f', microtime(true)),
|
173 |
$fail,
|
174 |
$action,
|
410 |
|
411 |
wfActivityReport::logBlockedIP($IP);
|
412 |
|
413 |
+
if ($this->currentRequest !== null) {
|
414 |
+
$this->currentRequest->statusCode = 403;
|
415 |
+
$this->currentRequest->action = 'blocked:' . ($wfsn ? 'wfsn' : 'wordfence');
|
416 |
+
$this->currentRequest->actionDescription = $reason;
|
417 |
+
}
|
418 |
+
|
419 |
wfCache::updateBlockedIPs('add');
|
420 |
wfConfig::inc('totalIPsBlocked');
|
421 |
return true;
|
431 |
|
432 |
wfActivityReport::logBlockedIP($IP);
|
433 |
|
434 |
+
if ($this->currentRequest !== null) {
|
435 |
+
$this->currentRequest->statusCode = 403;
|
436 |
+
$this->currentRequest->action = 'lockedOut';
|
437 |
+
$this->currentRequest->actionDescription = $reason;
|
438 |
+
}
|
439 |
+
|
440 |
wfConfig::inc('totalIPsLocked');
|
441 |
return true;
|
442 |
}
|
537 |
}
|
538 |
return $results;
|
539 |
}
|
540 |
+
|
541 |
+
/**
|
542 |
+
* @return bool|int
|
543 |
+
*/
|
544 |
public function logHit(){
|
545 |
+
if (!wfConfig::liveTrafficEnabled() || !$this->logHitOK()) {
|
546 |
+
return false;
|
547 |
+
}
|
548 |
+
if ($this->currentRequest !== null) {
|
549 |
+
if ($this->currentRequest->save()) {
|
550 |
+
return $this->currentRequest->getPrimaryKey();
|
551 |
}
|
552 |
}
|
553 |
+
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
554 |
}
|
555 |
+
|
556 |
public function getPerfStats($afterTime, $limit = 50){
|
557 |
$serverTime = $this->getDB()->querySingle("select unix_timestamp()");
|
558 |
$results = $this->getDB()->querySelect("select * from " . $this->perfTable . " where ctime > %f order by ctime desc limit %d", $afterTime, $limit);
|
591 |
return $results;
|
592 |
}
|
593 |
public function getHits($hitType /* 'hits' or 'logins' */, $type, $afterTime, $limit = 50, $IP = false){
|
594 |
+
global $wpdb;
|
595 |
$IPSQL = "";
|
596 |
if($IP){
|
597 |
$IPSQL = " and IP=%s ";
|
608 |
} else if($type == 'gCrawler'){
|
609 |
$typeSQL = " and isGoogle = 1 ";
|
610 |
} else if($type == '404'){
|
611 |
+
$typeSQL = " and statusCode = 404 ";
|
612 |
} else if($type == 'human'){
|
613 |
$typeSQL = " and jsRun = 1 ";
|
614 |
} else if($type == 'ruser'){
|
617 |
wordfence::status(1, 'error', "Invalid log type to wfLog: $type");
|
618 |
return false;
|
619 |
}
|
620 |
+
array_unshift($sqlArgs, "select h.*, u.display_name from {$this->hitsTable} h
|
621 |
+
LEFT JOIN {$wpdb->users} u on h.userID = u.ID
|
622 |
+
where ctime > %f $IPSQL $typeSQL order by ctime desc limit %d");
|
623 |
$results = call_user_func_array(array($this->getDB(), 'querySelect'), $sqlArgs);
|
624 |
|
625 |
} else if($hitType == 'logins'){
|
626 |
+
array_unshift($sqlArgs, "select l.*, u.display_name from {$this->loginsTable} l
|
627 |
+
LEFT JOIN {$wpdb->users} u on l.userID = u.ID
|
628 |
+
where ctime > %f $IPSQL order by ctime desc limit %d");
|
629 |
$results = call_user_func_array(array($this->getDB(), 'querySelect'), $sqlArgs );
|
630 |
|
631 |
} else {
|
632 |
wordfence::status(1, 'error', "getHits got invalid hitType: $hitType");
|
633 |
return false;
|
634 |
}
|
635 |
+
$this->processGetHitsResults($type, $results);
|
636 |
+
return $results;
|
637 |
+
}
|
638 |
+
|
639 |
+
/**
|
640 |
+
* @param string $type
|
641 |
+
* @param array $results
|
642 |
+
* @throws Exception
|
643 |
+
*/
|
644 |
+
public function processGetHitsResults($type, &$results) {
|
645 |
+
$serverTime = $this->getDB()->querySingle("select unix_timestamp()");
|
646 |
+
|
647 |
$this->resolveIPs($results);
|
648 |
$ourURL = parse_url(site_url());
|
649 |
$ourHost = strtolower($ourURL['host']);
|
705 |
if( isset( $refURL['query'] ) ) {
|
706 |
parse_str($refURL['query'], $queryVars);
|
707 |
if(isset($queryVars[$q])){
|
708 |
+
$res['searchTerms'] = urlencode($queryVars[$q]);
|
709 |
}
|
710 |
}
|
711 |
}
|
715 |
if ( isset( $referringPage ) && stristr( $referringPage['host'], 'google.' ) )
|
716 |
{
|
717 |
parse_str( $referringPage['query'], $queryVars );
|
718 |
+
// echo $queryVars['q']; // This is the search term used
|
719 |
}
|
720 |
}
|
721 |
}
|
733 |
}
|
734 |
}
|
735 |
|
736 |
+
|
737 |
if($res['userID']){
|
738 |
$ud = get_userdata($res['userID']);
|
739 |
if($ud){
|
740 |
$res['user'] = array(
|
741 |
'editLink' => wfUtils::editUserLink($res['userID']),
|
742 |
+
'display_name' => $res['display_name'],
|
743 |
'ID' => $res['userID']
|
744 |
+
);
|
745 |
$res['user']['avatar'] = get_avatar($res['userID'], 16);
|
746 |
}
|
747 |
} else {
|
748 |
$res['user'] = false;
|
749 |
}
|
750 |
}
|
|
|
751 |
}
|
752 |
+
|
753 |
public function resolveIPs(&$results){
|
754 |
if(sizeof($results) < 1){ return; }
|
755 |
$IPs = array();
|
770 |
}
|
771 |
}
|
772 |
public function logHitOK(){
|
773 |
+
if (!$this->canLogHit) {
|
774 |
+
return false;
|
775 |
+
}
|
776 |
if(is_admin()){ return false; } //Don't log admin pageviews
|
777 |
if(isset($_SERVER['HTTP_USER_AGENT'])){
|
778 |
if(preg_match('/WordPress\/' . $this->wp_version . '/i', $_SERVER['HTTP_USER_AGENT'])){ return false; } //Ignore requests generated by WP UA.
|
902 |
if($doBlock){
|
903 |
$this->getDB()->queryWrite("update " . $this->ipRangesTable . " set totalBlocked = totalBlocked + 1, lastBlocked = unix_timestamp() where id=%d", $blockRec['id']);
|
904 |
wfActivityReport::logBlockedIP($IP);
|
905 |
+
$this->currentRequest->actionDescription = 'UA/Referrer/IP Range not allowed';
|
906 |
$this->do503(3600, "Advanced blocking in effect.");
|
907 |
}
|
908 |
}
|
960 |
$this->redirect(wfConfig::get('cbl_redirURL'));
|
961 |
}
|
962 |
} else {
|
963 |
+
$this->currentRequest->actionDescription = 'blocked access via country blocking';
|
964 |
$this->do503(3600, "Access from your area has been temporarily limited for security reasons");
|
965 |
wfConfig::inc('totalCountryBlocked');
|
966 |
}
|
1028 |
}
|
1029 |
}
|
1030 |
public function do503($secsToGo, $reason){
|
1031 |
+
$this->initLogRequest();
|
1032 |
+
$this->currentRequest->statusCode = 403;
|
1033 |
+
if (!$this->currentRequest->action) {
|
1034 |
+
$this->currentRequest->action = 'blocked:wordfence';
|
1035 |
+
}
|
1036 |
+
if (!$this->currentRequest->actionDescription) {
|
1037 |
+
$this->currentRequest->actionDescription = "blocked: " . $reason;
|
1038 |
+
}
|
1039 |
+
|
1040 |
wfConfig::inc('total503s');
|
1041 |
wfUtils::doNotCache();
|
1042 |
header('HTTP/1.1 503 Service Temporarily Unavailable');
|
1348 |
}
|
1349 |
}
|
1350 |
|
1351 |
+
/**
|
1352 |
+
* The function of this class is to detect admin users created via direct access to the database (in other words, not
|
1353 |
+
* through WordPress).
|
1354 |
+
*/
|
1355 |
+
class wfAdminUserMonitor {
|
1356 |
+
|
1357 |
+
public function isEnabled() {
|
1358 |
+
$enabled = wfConfig::get('scansEnabled_suspiciousAdminUsers');
|
1359 |
+
if ($enabled && is_multisite()) {
|
1360 |
+
if (!function_exists('wp_is_large_network')) {
|
1361 |
+
require_once ABSPATH . WPINC . '/ms-functions.php';
|
1362 |
+
}
|
1363 |
+
$enabled = !wp_is_large_network('sites') && !wp_is_large_network('users');
|
1364 |
+
}
|
1365 |
+
return $enabled;
|
1366 |
+
}
|
1367 |
+
|
1368 |
+
/**
|
1369 |
+
*
|
1370 |
+
*/
|
1371 |
+
public function createInitialList() {
|
1372 |
+
$admins = $this->getCurrentAdmins();
|
1373 |
+
wfConfig::set_ser('adminUserList', $admins);
|
1374 |
+
}
|
1375 |
+
|
1376 |
+
/**
|
1377 |
+
* @param int $userID
|
1378 |
+
*/
|
1379 |
+
public function grantSuperAdmin($userID = null) {
|
1380 |
+
if ($userID) {
|
1381 |
+
$this->addAdmin($userID);
|
1382 |
+
}
|
1383 |
+
}
|
1384 |
+
|
1385 |
+
/**
|
1386 |
+
* @param int $userID
|
1387 |
+
*/
|
1388 |
+
public function revokeSuperAdmin($userID = null) {
|
1389 |
+
if ($userID) {
|
1390 |
+
$this->removeAdmin($userID);
|
1391 |
+
}
|
1392 |
+
}
|
1393 |
+
|
1394 |
+
/**
|
1395 |
+
* @param int $ID
|
1396 |
+
* @param mixed $role
|
1397 |
+
* @param mixed $old_roles
|
1398 |
+
*/
|
1399 |
+
public function updateToUserRole($ID = null, $role = null, $old_roles = null) {
|
1400 |
+
$admins = $this->getLoggedAdmins();
|
1401 |
+
if ($role !== 'administrator' && array_key_exists($ID, $admins)) {
|
1402 |
+
$this->removeAdmin($ID);
|
1403 |
+
} else if ($role === 'administrator') {
|
1404 |
+
$this->addAdmin($ID);
|
1405 |
+
}
|
1406 |
+
}
|
1407 |
+
|
1408 |
+
/**
|
1409 |
+
* @return array|bool
|
1410 |
+
*/
|
1411 |
+
public function checkNewAdmins() {
|
1412 |
+
$loggedAdmins = $this->getLoggedAdmins();
|
1413 |
+
$admins = $this->getCurrentAdmins();
|
1414 |
+
$suspiciousAdmins = array();
|
1415 |
+
foreach ($admins as $adminID => $v) {
|
1416 |
+
if (!array_key_exists($adminID, $loggedAdmins)) {
|
1417 |
+
$suspiciousAdmins[] = $adminID;
|
1418 |
+
}
|
1419 |
+
}
|
1420 |
+
return $suspiciousAdmins ? $suspiciousAdmins : false;
|
1421 |
+
}
|
1422 |
+
|
1423 |
+
/**
|
1424 |
+
* Checks if the supplied user ID is suspicious.
|
1425 |
+
*
|
1426 |
+
* @param int $userID
|
1427 |
+
* @return bool
|
1428 |
+
*/
|
1429 |
+
public function isAdminUserLogged($userID) {
|
1430 |
+
$loggedAdmins = $this->getLoggedAdmins();
|
1431 |
+
return array_key_exists($userID, $loggedAdmins);
|
1432 |
+
}
|
1433 |
+
|
1434 |
+
/**
|
1435 |
+
* @return array
|
1436 |
+
*/
|
1437 |
+
public function getCurrentAdmins() {
|
1438 |
+
require_once ABSPATH . WPINC . '/user.php';
|
1439 |
+
if (is_multisite()) {
|
1440 |
+
$sites = wp_get_sites(array(
|
1441 |
+
'network_id' => null,
|
1442 |
+
));
|
1443 |
+
} else {
|
1444 |
+
$sites = array(array(
|
1445 |
+
'blog_id' => get_current_blog_id(),
|
1446 |
+
));
|
1447 |
+
}
|
1448 |
+
|
1449 |
+
// not very efficient, but the WordPress API doesn't provide a good way to do this.
|
1450 |
+
$admins = array();
|
1451 |
+
foreach ($sites as $siteRow) {
|
1452 |
+
$user_query = new WP_User_Query(array(
|
1453 |
+
'blog_id' => $siteRow['blog_id'],
|
1454 |
+
'role' => 'administrator',
|
1455 |
+
));
|
1456 |
+
$users = $user_query->get_results();
|
1457 |
+
if (is_array($users)) {
|
1458 |
+
/** @var WP_User $user */
|
1459 |
+
foreach ($users as $user) {
|
1460 |
+
$admins[$user->ID] = 1;
|
1461 |
+
}
|
1462 |
+
}
|
1463 |
+
}
|
1464 |
+
|
1465 |
+
// Add any super admins that aren't also admins on a network
|
1466 |
+
$superAdmins = get_super_admins();
|
1467 |
+
foreach ($superAdmins as $userLogin) {
|
1468 |
+
$user = get_user_by('login', $userLogin);
|
1469 |
+
if ($user) {
|
1470 |
+
$admins[$user->ID] = 1;
|
1471 |
+
}
|
1472 |
+
}
|
1473 |
+
return $admins;
|
1474 |
+
}
|
1475 |
+
|
1476 |
+
public function getLoggedAdmins() {
|
1477 |
+
$loggedAdmins = wfConfig::get_ser('adminUserList', false);
|
1478 |
+
if (!is_array($loggedAdmins)) {
|
1479 |
+
$this->createInitialList();
|
1480 |
+
$loggedAdmins = wfConfig::get_ser('adminUserList', false);
|
1481 |
+
}
|
1482 |
+
if (!is_array($loggedAdmins)) {
|
1483 |
+
$loggedAdmins = array();
|
1484 |
+
}
|
1485 |
+
return $loggedAdmins;
|
1486 |
+
}
|
1487 |
+
|
1488 |
+
/**
|
1489 |
+
* @param int $userID
|
1490 |
+
*/
|
1491 |
+
public function addAdmin($userID) {
|
1492 |
+
$loggedAdmins = $this->getLoggedAdmins();
|
1493 |
+
if (!array_key_exists($userID, $loggedAdmins)) {
|
1494 |
+
$loggedAdmins[$userID] = 1;
|
1495 |
+
wfConfig::set_ser('adminUserList', $loggedAdmins);
|
1496 |
+
}
|
1497 |
+
}
|
1498 |
+
|
1499 |
+
/**
|
1500 |
+
* @param int $userID
|
1501 |
+
*/
|
1502 |
+
public function removeAdmin($userID) {
|
1503 |
+
$loggedAdmins = $this->getLoggedAdmins();
|
1504 |
+
if (array_key_exists($userID, $loggedAdmins) && !array_key_exists($userID, $this->getCurrentAdmins())) {
|
1505 |
+
unset($loggedAdmins[$userID]);
|
1506 |
+
wfConfig::set_ser('adminUserList', $loggedAdmins);
|
1507 |
+
}
|
1508 |
+
}
|
1509 |
+
}
|
1510 |
+
|
1511 |
+
/**
|
1512 |
+
*
|
1513 |
+
*/
|
1514 |
+
class wfRequestModel extends wfModel {
|
1515 |
+
|
1516 |
+
private static $actionDataEncodedParams = array(
|
1517 |
+
'paramKey',
|
1518 |
+
'paramValue',
|
1519 |
+
'path',
|
1520 |
+
);
|
1521 |
+
|
1522 |
+
/**
|
1523 |
+
* @param $actionData
|
1524 |
+
* @return mixed|string|void
|
1525 |
+
*/
|
1526 |
+
public static function serializeActionData($actionData) {
|
1527 |
+
if (is_array($actionData)) {
|
1528 |
+
foreach (self::$actionDataEncodedParams as $key) {
|
1529 |
+
if (array_key_exists($key, $actionData)) {
|
1530 |
+
$actionData[$key] = base64_encode($actionData[$key]);
|
1531 |
+
}
|
1532 |
+
}
|
1533 |
+
}
|
1534 |
+
return json_encode($actionData);
|
1535 |
+
}
|
1536 |
+
|
1537 |
+
/**
|
1538 |
+
* @param $actionDataJSON
|
1539 |
+
* @return mixed|string|void
|
1540 |
+
*/
|
1541 |
+
public static function unserializeActionData($actionDataJSON) {
|
1542 |
+
$actionData = json_decode($actionDataJSON, true);
|
1543 |
+
if (is_array($actionData)) {
|
1544 |
+
foreach (self::$actionDataEncodedParams as $key) {
|
1545 |
+
if (array_key_exists($key, $actionData)) {
|
1546 |
+
$actionData[$key] = base64_decode($actionData[$key]);
|
1547 |
+
}
|
1548 |
+
}
|
1549 |
+
}
|
1550 |
+
return $actionData;
|
1551 |
+
}
|
1552 |
+
|
1553 |
+
private $columns = array(
|
1554 |
+
'id',
|
1555 |
+
'attackLogTime',
|
1556 |
+
'ctime',
|
1557 |
+
'IP',
|
1558 |
+
'jsRun',
|
1559 |
+
'statusCode',
|
1560 |
+
'isGoogle',
|
1561 |
+
'userID',
|
1562 |
+
'newVisit',
|
1563 |
+
'URL',
|
1564 |
+
'referer',
|
1565 |
+
'UA',
|
1566 |
+
'action',
|
1567 |
+
'actionDescription',
|
1568 |
+
'actionData',
|
1569 |
+
);
|
1570 |
+
|
1571 |
+
public function getIDColumn() {
|
1572 |
+
return 'id';
|
1573 |
+
}
|
1574 |
+
|
1575 |
+
public function getTable() {
|
1576 |
+
return $this->getDB()->base_prefix . 'wfHits';
|
1577 |
+
}
|
1578 |
+
|
1579 |
+
public function hasColumn($column) {
|
1580 |
+
return in_array($column, $this->columns);
|
1581 |
+
}
|
1582 |
+
}
|
1583 |
+
|
1584 |
+
|
1585 |
+
class wfLiveTrafficQuery {
|
1586 |
+
|
1587 |
+
protected $validParams = array(
|
1588 |
+
'id' => 'h.id',
|
1589 |
+
'ctime' => 'h.ctime',
|
1590 |
+
'ip' => 'h.ip',
|
1591 |
+
'jsrun' => 'h.jsrun',
|
1592 |
+
'statuscode' => 'h.statuscode',
|
1593 |
+
'isgoogle' => 'h.isgoogle',
|
1594 |
+
'userid' => 'h.userid',
|
1595 |
+
'newvisit' => 'h.newvisit',
|
1596 |
+
'url' => 'h.url',
|
1597 |
+
'referer' => 'h.referer',
|
1598 |
+
'ua' => 'h.ua',
|
1599 |
+
'action' => 'h.action',
|
1600 |
+
'actiondescription' => 'h.actiondescription',
|
1601 |
+
'actiondata' => 'h.actiondata',
|
1602 |
+
|
1603 |
+
// wfLogins
|
1604 |
+
'user_login' => 'u.user_login',
|
1605 |
+
'username' => 'l.username',
|
1606 |
+
);
|
1607 |
+
|
1608 |
+
/** @var wfLiveTrafficQueryFilterCollection */
|
1609 |
+
private $filters = array();
|
1610 |
+
|
1611 |
+
/** @var wfLiveTrafficQueryGroupBy */
|
1612 |
+
private $groupBy;
|
1613 |
+
/**
|
1614 |
+
* @var float|null
|
1615 |
+
*/
|
1616 |
+
private $startDate;
|
1617 |
+
/**
|
1618 |
+
* @var float|null
|
1619 |
+
*/
|
1620 |
+
private $endDate;
|
1621 |
+
/**
|
1622 |
+
* @var int
|
1623 |
+
*/
|
1624 |
+
private $limit;
|
1625 |
+
/**
|
1626 |
+
* @var int
|
1627 |
+
*/
|
1628 |
+
private $offset;
|
1629 |
+
|
1630 |
+
private $tableName;
|
1631 |
+
|
1632 |
+
/** @var wfLog */
|
1633 |
+
private $wfLog;
|
1634 |
+
|
1635 |
+
/**
|
1636 |
+
* wfLiveTrafficQuery constructor.
|
1637 |
+
*
|
1638 |
+
* @param wfLog $wfLog
|
1639 |
+
* @param wfLiveTrafficQueryFilterCollection $filters
|
1640 |
+
* @param wfLiveTrafficQueryGroupBy $groupBy
|
1641 |
+
* @param float $startDate
|
1642 |
+
* @param float $endDate
|
1643 |
+
* @param int $limit
|
1644 |
+
* @param int $offset
|
1645 |
+
*/
|
1646 |
+
public function __construct($wfLog, $filters = null, $groupBy = null, $startDate = null, $endDate = null, $limit = 20, $offset = 0) {
|
1647 |
+
$this->wfLog = $wfLog;
|
1648 |
+
$this->filters = $filters;
|
1649 |
+
$this->groupBy = $groupBy;
|
1650 |
+
$this->startDate = $startDate;
|
1651 |
+
$this->endDate = $endDate;
|
1652 |
+
$this->limit = $limit;
|
1653 |
+
$this->offset = $offset;
|
1654 |
+
}
|
1655 |
+
|
1656 |
+
/**
|
1657 |
+
* @return array|null|object
|
1658 |
+
*/
|
1659 |
+
public function execute() {
|
1660 |
+
global $wpdb;
|
1661 |
+
$sql = $this->buildQuery();
|
1662 |
+
$results = $wpdb->get_results($sql, ARRAY_A);
|
1663 |
+
$this->getWFLog()->processGetHitsResults('', $results);
|
1664 |
+
|
1665 |
+
foreach ($results as &$row) {
|
1666 |
+
$row['actionData'] = (array) json_decode($row['actionData'], true);
|
1667 |
+
}
|
1668 |
+
return $results;
|
1669 |
+
}
|
1670 |
+
|
1671 |
+
/**
|
1672 |
+
* @return string
|
1673 |
+
* @throws wfLiveTrafficQueryException
|
1674 |
+
*/
|
1675 |
+
public function buildQuery() {
|
1676 |
+
global $wpdb;
|
1677 |
+
$filters = $this->getFilters();
|
1678 |
+
$groupBy = $this->getGroupBy();
|
1679 |
+
$startDate = $this->getStartDate();
|
1680 |
+
$endDate = $this->getEndDate();
|
1681 |
+
$limit = absint($this->getLimit());
|
1682 |
+
$offset = absint($this->getOffset());
|
1683 |
+
|
1684 |
+
$wheres = array();
|
1685 |
+
if ($startDate) {
|
1686 |
+
$wheres[] = $wpdb->prepare('h.ctime > %f', $startDate);
|
1687 |
+
}
|
1688 |
+
if ($endDate) {
|
1689 |
+
$wheres[] = $wpdb->prepare('h.ctime < %f', $endDate);
|
1690 |
+
}
|
1691 |
+
|
1692 |
+
if ($filters instanceof wfLiveTrafficQueryFilterCollection) {
|
1693 |
+
$filtersSQL = $filters->toSQL();
|
1694 |
+
if ($filtersSQL) {
|
1695 |
+
$wheres[] = $filtersSQL;
|
1696 |
+
}
|
1697 |
+
}
|
1698 |
+
$where = join(' AND ', $wheres);
|
1699 |
+
|
1700 |
+
$orderBy = 'ORDER BY h.ctime DESC';
|
1701 |
+
$select = '';
|
1702 |
+
$groupBySQL = '';
|
1703 |
+
if ($groupBy && $groupBy->validate()) {
|
1704 |
+
$groupBySQL = "GROUP BY {$groupBy->getParam()}";
|
1705 |
+
$orderBy = 'ORDER BY hitCount DESC';
|
1706 |
+
$select .= ', COUNT(h.id) as hitCount';
|
1707 |
+
}
|
1708 |
+
|
1709 |
+
if ($where) {
|
1710 |
+
$where = 'WHERE ' . $where;
|
1711 |
+
}
|
1712 |
+
if (!$limit || $limit > 1000) {
|
1713 |
+
$limit = 20;
|
1714 |
+
}
|
1715 |
+
$limitSQL = $wpdb->prepare('LIMIT %d, %d', $offset, $limit);
|
1716 |
+
|
1717 |
+
$sql = <<<SQL
|
1718 |
+
SELECT h.*, u.display_name, l.username{$select} FROM {$this->getTableName()} h
|
1719 |
+
LEFT JOIN {$wpdb->users} u on h.userID = u.ID
|
1720 |
+
LEFT JOIN {$wpdb->base_prefix}wfLogins l on h.id = l.hitID
|
1721 |
+
$where
|
1722 |
+
$groupBySQL
|
1723 |
+
$orderBy
|
1724 |
+
$limitSQL
|
1725 |
+
SQL;
|
1726 |
+
|
1727 |
+
return $sql;
|
1728 |
+
}
|
1729 |
+
|
1730 |
+
/**
|
1731 |
+
* @param $param
|
1732 |
+
* @return bool
|
1733 |
+
*/
|
1734 |
+
public function isValidParam($param) {
|
1735 |
+
return array_key_exists(strtolower($param), $this->validParams);
|
1736 |
+
}
|
1737 |
+
|
1738 |
+
/**
|
1739 |
+
* @param $getParam
|
1740 |
+
* @return bool|string
|
1741 |
+
*/
|
1742 |
+
public function getColumnFromParam($getParam) {
|
1743 |
+
$getParam = strtolower($getParam);
|
1744 |
+
if (array_key_exists($getParam, $this->validParams)) {
|
1745 |
+
return $this->validParams[$getParam];
|
1746 |
+
}
|
1747 |
+
return false;
|
1748 |
+
}
|
1749 |
+
|
1750 |
+
/**
|
1751 |
+
* @return wfLiveTrafficQueryFilterCollection
|
1752 |
+
*/
|
1753 |
+
public function getFilters() {
|
1754 |
+
return $this->filters;
|
1755 |
+
}
|
1756 |
+
|
1757 |
+
/**
|
1758 |
+
* @param wfLiveTrafficQueryFilterCollection $filters
|
1759 |
+
*/
|
1760 |
+
public function setFilters($filters) {
|
1761 |
+
$this->filters = $filters;
|
1762 |
+
}
|
1763 |
+
|
1764 |
+
/**
|
1765 |
+
* @return float|null
|
1766 |
+
*/
|
1767 |
+
public function getStartDate() {
|
1768 |
+
return $this->startDate;
|
1769 |
+
}
|
1770 |
+
|
1771 |
+
/**
|
1772 |
+
* @param float|null $startDate
|
1773 |
+
*/
|
1774 |
+
public function setStartDate($startDate) {
|
1775 |
+
$this->startDate = $startDate;
|
1776 |
+
}
|
1777 |
+
|
1778 |
+
/**
|
1779 |
+
* @return float|null
|
1780 |
+
*/
|
1781 |
+
public function getEndDate() {
|
1782 |
+
return $this->endDate;
|
1783 |
+
}
|
1784 |
+
|
1785 |
+
/**
|
1786 |
+
* @param float|null $endDate
|
1787 |
+
*/
|
1788 |
+
public function setEndDate($endDate) {
|
1789 |
+
$this->endDate = $endDate;
|
1790 |
+
}
|
1791 |
+
|
1792 |
+
/**
|
1793 |
+
* @return wfLiveTrafficQueryGroupBy
|
1794 |
+
*/
|
1795 |
+
public function getGroupBy() {
|
1796 |
+
return $this->groupBy;
|
1797 |
+
}
|
1798 |
+
|
1799 |
+
/**
|
1800 |
+
* @param wfLiveTrafficQueryGroupBy $groupBy
|
1801 |
+
*/
|
1802 |
+
public function setGroupBy($groupBy) {
|
1803 |
+
$this->groupBy = $groupBy;
|
1804 |
+
}
|
1805 |
+
|
1806 |
+
/**
|
1807 |
+
* @return int
|
1808 |
+
*/
|
1809 |
+
public function getLimit() {
|
1810 |
+
return $this->limit;
|
1811 |
+
}
|
1812 |
+
|
1813 |
+
/**
|
1814 |
+
* @param int $limit
|
1815 |
+
*/
|
1816 |
+
public function setLimit($limit) {
|
1817 |
+
$this->limit = $limit;
|
1818 |
+
}
|
1819 |
+
|
1820 |
+
/**
|
1821 |
+
* @return int
|
1822 |
+
*/
|
1823 |
+
public function getOffset() {
|
1824 |
+
return $this->offset;
|
1825 |
+
}
|
1826 |
+
|
1827 |
+
/**
|
1828 |
+
* @param int $offset
|
1829 |
+
*/
|
1830 |
+
public function setOffset($offset) {
|
1831 |
+
$this->offset = $offset;
|
1832 |
+
}
|
1833 |
+
|
1834 |
+
/**
|
1835 |
+
* @return string
|
1836 |
+
*/
|
1837 |
+
public function getTableName() {
|
1838 |
+
if ($this->tableName === null) {
|
1839 |
+
global $wpdb;
|
1840 |
+
$this->tableName = $wpdb->base_prefix . 'wfHits';
|
1841 |
+
}
|
1842 |
+
return $this->tableName;
|
1843 |
+
}
|
1844 |
+
|
1845 |
+
/**
|
1846 |
+
* @param string $tableName
|
1847 |
+
*/
|
1848 |
+
public function setTableName($tableName) {
|
1849 |
+
$this->tableName = $tableName;
|
1850 |
+
}
|
1851 |
+
|
1852 |
+
/**
|
1853 |
+
* @return wfLog
|
1854 |
+
*/
|
1855 |
+
public function getWFLog() {
|
1856 |
+
return $this->wfLog;
|
1857 |
+
}
|
1858 |
+
|
1859 |
+
/**
|
1860 |
+
* @param wfLog $wfLog
|
1861 |
+
*/
|
1862 |
+
public function setWFLog($wfLog) {
|
1863 |
+
$this->wfLog = $wfLog;
|
1864 |
+
}
|
1865 |
+
}
|
1866 |
+
|
1867 |
+
class wfLiveTrafficQueryFilterCollection {
|
1868 |
+
|
1869 |
+
private $filters = array();
|
1870 |
+
|
1871 |
+
/**
|
1872 |
+
* wfLiveTrafficQueryFilterCollection constructor.
|
1873 |
+
*
|
1874 |
+
* @param array $filters
|
1875 |
+
*/
|
1876 |
+
public function __construct($filters = array()) {
|
1877 |
+
$this->filters = $filters;
|
1878 |
+
}
|
1879 |
+
|
1880 |
+
public function toSQL() {
|
1881 |
+
$params = array();
|
1882 |
+
$sql = '';
|
1883 |
+
$filters = $this->getFilters();
|
1884 |
+
if ($filters) {
|
1885 |
+
/** @var wfLiveTrafficQueryFilter $filter */
|
1886 |
+
foreach ($filters as $filter) {
|
1887 |
+
$params[$filter->getParam()][] = $filter;
|
1888 |
+
}
|
1889 |
+
}
|
1890 |
+
|
1891 |
+
foreach ($params as $param => $filters) {
|
1892 |
+
// $sql .= '(';
|
1893 |
+
$filtersSQL = '';
|
1894 |
+
foreach ($filters as $filter) {
|
1895 |
+
$filterSQL = $filter->toSQL();
|
1896 |
+
if ($filterSQL) {
|
1897 |
+
$filtersSQL .= $filterSQL . ' OR ';
|
1898 |
+
}
|
1899 |
+
}
|
1900 |
+
if ($filtersSQL) {
|
1901 |
+
$sql .= '(' . substr($filtersSQL, 0, -4) . ') AND ';
|
1902 |
+
}
|
1903 |
+
}
|
1904 |
+
if ($sql) {
|
1905 |
+
$sql = substr($sql, 0, -5);
|
1906 |
+
}
|
1907 |
+
return $sql;
|
1908 |
+
}
|
1909 |
+
|
1910 |
+
public function addFilter($filter) {
|
1911 |
+
$this->filters[] = $filter;
|
1912 |
+
}
|
1913 |
+
|
1914 |
+
/**
|
1915 |
+
* @return array
|
1916 |
+
*/
|
1917 |
+
public function getFilters() {
|
1918 |
+
return $this->filters;
|
1919 |
+
}
|
1920 |
+
|
1921 |
+
/**
|
1922 |
+
* @param array $filters
|
1923 |
+
*/
|
1924 |
+
public function setFilters($filters) {
|
1925 |
+
$this->filters = $filters;
|
1926 |
+
}
|
1927 |
+
}
|
1928 |
+
|
1929 |
+
class wfLiveTrafficQueryFilter {
|
1930 |
+
|
1931 |
+
private $param;
|
1932 |
+
private $operator;
|
1933 |
+
private $value;
|
1934 |
+
|
1935 |
+
protected $validOperators = array(
|
1936 |
+
'=',
|
1937 |
+
'!=',
|
1938 |
+
'contains',
|
1939 |
+
'match',
|
1940 |
+
);
|
1941 |
+
|
1942 |
+
/**
|
1943 |
+
* @var wfLiveTrafficQuery
|
1944 |
+
*/
|
1945 |
+
private $query;
|
1946 |
+
|
1947 |
+
/**
|
1948 |
+
* wfLiveTrafficQueryFilter constructor.
|
1949 |
+
*
|
1950 |
+
* @param wfLiveTrafficQuery $query
|
1951 |
+
* @param string $param
|
1952 |
+
* @param string $operator
|
1953 |
+
* @param string $value
|
1954 |
+
*/
|
1955 |
+
public function __construct($query, $param, $operator, $value) {
|
1956 |
+
$this->query = $query;
|
1957 |
+
$this->param = $param;
|
1958 |
+
$this->operator = $operator;
|
1959 |
+
$this->value = $value;
|
1960 |
+
}
|
1961 |
+
|
1962 |
+
/**
|
1963 |
+
* @return string|void
|
1964 |
+
*/
|
1965 |
+
public function toSQL() {
|
1966 |
+
$sql = '';
|
1967 |
+
if ($this->validate()) {
|
1968 |
+
/** @var wpdb $wpdb */
|
1969 |
+
global $wpdb;
|
1970 |
+
$operator = $this->getOperator();
|
1971 |
+
$param = $this->getQuery()->getColumnFromParam($this->getParam());
|
1972 |
+
if (!$param) {
|
1973 |
+
return $sql;
|
1974 |
+
}
|
1975 |
+
$value = $this->getValue();
|
1976 |
+
switch ($operator) {
|
1977 |
+
case 'contains':
|
1978 |
+
$like = addcslashes($value, '_%\\');
|
1979 |
+
$sql = $wpdb->prepare("$param LIKE %s", "%$like%");
|
1980 |
+
break;
|
1981 |
+
|
1982 |
+
case 'match':
|
1983 |
+
$sql = $wpdb->prepare("$param LIKE %s", $value);
|
1984 |
+
break;
|
1985 |
+
|
1986 |
+
default:
|
1987 |
+
$sql = $wpdb->prepare("$param $operator %s", $value);
|
1988 |
+
break;
|
1989 |
+
}
|
1990 |
+
}
|
1991 |
+
return $sql;
|
1992 |
+
}
|
1993 |
+
|
1994 |
+
/**
|
1995 |
+
* @return bool
|
1996 |
+
*/
|
1997 |
+
public function validate() {
|
1998 |
+
$valid = $this->isValidParam($this->getParam()) && $this->isValidOperator($this->getOperator());
|
1999 |
+
if (defined('WP_DEBUG') && WP_DEBUG) {
|
2000 |
+
if (!$valid) {
|
2001 |
+
throw new wfLiveTrafficQueryException("Invalid param/operator [{$this->getParam()}]/[{$this->getOperator()}] passed to " . get_class($this));
|
2002 |
+
}
|
2003 |
+
return true;
|
2004 |
+
}
|
2005 |
+
return $valid;
|
2006 |
+
}
|
2007 |
+
|
2008 |
+
/**
|
2009 |
+
* @param string $param
|
2010 |
+
* @return bool
|
2011 |
+
*/
|
2012 |
+
public function isValidParam($param) {
|
2013 |
+
return $this->getQuery() && $this->getQuery()->isValidParam($param);
|
2014 |
+
}
|
2015 |
+
|
2016 |
+
/**
|
2017 |
+
* @param string $operator
|
2018 |
+
* @return bool
|
2019 |
+
*/
|
2020 |
+
public function isValidOperator($operator) {
|
2021 |
+
return in_array($operator, $this->validOperators);
|
2022 |
+
}
|
2023 |
+
|
2024 |
+
/**
|
2025 |
+
* @return mixed
|
2026 |
+
*/
|
2027 |
+
public function getParam() {
|
2028 |
+
return $this->param;
|
2029 |
+
}
|
2030 |
+
|
2031 |
+
/**
|
2032 |
+
* @param mixed $param
|
2033 |
+
*/
|
2034 |
+
public function setParam($param) {
|
2035 |
+
$this->param = $param;
|
2036 |
+
}
|
2037 |
+
|
2038 |
+
/**
|
2039 |
+
* @return mixed
|
2040 |
+
*/
|
2041 |
+
public function getOperator() {
|
2042 |
+
return $this->operator;
|
2043 |
+
}
|
2044 |
+
|
2045 |
+
/**
|
2046 |
+
* @param mixed $operator
|
2047 |
+
*/
|
2048 |
+
public function setOperator($operator) {
|
2049 |
+
$this->operator = $operator;
|
2050 |
+
}
|
2051 |
+
|
2052 |
+
/**
|
2053 |
+
* @return mixed
|
2054 |
+
*/
|
2055 |
+
public function getValue() {
|
2056 |
+
return $this->value;
|
2057 |
+
}
|
2058 |
+
|
2059 |
+
/**
|
2060 |
+
* @param mixed $value
|
2061 |
+
*/
|
2062 |
+
public function setValue($value) {
|
2063 |
+
$this->value = $value;
|
2064 |
+
}
|
2065 |
+
|
2066 |
+
/**
|
2067 |
+
* @return wfLiveTrafficQuery
|
2068 |
+
*/
|
2069 |
+
public function getQuery() {
|
2070 |
+
return $this->query;
|
2071 |
+
}
|
2072 |
+
|
2073 |
+
/**
|
2074 |
+
* @param wfLiveTrafficQuery $query
|
2075 |
+
*/
|
2076 |
+
public function setQuery($query) {
|
2077 |
+
$this->query = $query;
|
2078 |
+
}
|
2079 |
+
}
|
2080 |
+
|
2081 |
+
class wfLiveTrafficQueryGroupBy {
|
2082 |
+
|
2083 |
+
private $param;
|
2084 |
+
|
2085 |
+
/**
|
2086 |
+
* @var wfLiveTrafficQuery
|
2087 |
+
*/
|
2088 |
+
private $query;
|
2089 |
+
|
2090 |
+
/**
|
2091 |
+
* wfLiveTrafficQueryGroupBy constructor.
|
2092 |
+
*
|
2093 |
+
* @param wfLiveTrafficQuery $query
|
2094 |
+
* @param string $param
|
2095 |
+
*/
|
2096 |
+
public function __construct($query, $param) {
|
2097 |
+
$this->query = $query;
|
2098 |
+
$this->param = $param;
|
2099 |
+
}
|
2100 |
+
|
2101 |
+
/**
|
2102 |
+
* @return bool
|
2103 |
+
* @throws wfLiveTrafficQueryException
|
2104 |
+
*/
|
2105 |
+
public function validate() {
|
2106 |
+
$valid = $this->isValidParam($this->getParam());
|
2107 |
+
if (defined('WP_DEBUG') && WP_DEBUG) {
|
2108 |
+
if (!$valid) {
|
2109 |
+
throw new wfLiveTrafficQueryException("Invalid param [{$this->getParam()}] passed to " . get_class($this));
|
2110 |
+
}
|
2111 |
+
return true;
|
2112 |
+
}
|
2113 |
+
return $valid;
|
2114 |
+
}
|
2115 |
+
|
2116 |
+
/**
|
2117 |
+
* @param string $param
|
2118 |
+
* @return bool
|
2119 |
+
*/
|
2120 |
+
public function isValidParam($param) {
|
2121 |
+
return $this->getQuery() && $this->getQuery()->isValidParam($param);
|
2122 |
+
}
|
2123 |
+
|
2124 |
+
/**
|
2125 |
+
* @return wfLiveTrafficQuery
|
2126 |
+
*/
|
2127 |
+
public function getQuery() {
|
2128 |
+
return $this->query;
|
2129 |
+
}
|
2130 |
+
|
2131 |
+
/**
|
2132 |
+
* @param wfLiveTrafficQuery $query
|
2133 |
+
*/
|
2134 |
+
public function setQuery($query) {
|
2135 |
+
$this->query = $query;
|
2136 |
+
}
|
2137 |
+
|
2138 |
+
/**
|
2139 |
+
* @return mixed
|
2140 |
+
*/
|
2141 |
+
public function getParam() {
|
2142 |
+
return $this->param;
|
2143 |
+
}
|
2144 |
+
|
2145 |
+
/**
|
2146 |
+
* @param mixed $param
|
2147 |
+
*/
|
2148 |
+
public function setParam($param) {
|
2149 |
+
$this->param = $param;
|
2150 |
+
}
|
2151 |
+
|
2152 |
+
}
|
2153 |
+
|
2154 |
+
|
2155 |
+
class wfLiveTrafficQueryException extends Exception {
|
2156 |
+
|
2157 |
+
}
|
lib/wfScanEngine.php
CHANGED
@@ -37,18 +37,41 @@ class wfScanEngine {
|
|
37 |
private $userPasswdQueue = "";
|
38 |
private $passwdHasIssues = false;
|
39 |
|
40 |
-
/**
|
41 |
-
* @var array
|
42 |
-
*/
|
43 |
-
private $databaseResults;
|
44 |
|
45 |
/**
|
46 |
* @var wordfenceDBScanner
|
47 |
*/
|
48 |
private $dbScanner;
|
49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
public function __sleep(){ //Same order here as above for properties that are included in serialization
|
51 |
-
return array('hasher', 'jobList', 'i', 'wp_version', 'apiKey', 'startTime', 'maxExecTime', 'publicScanEnabled', 'fileContentsResults', 'scanner', 'scanQueue', 'hoover', 'scanData', 'statusIDX', 'userPasswdQueue', 'passwdHasIssues', '
|
52 |
}
|
53 |
public function __construct(){
|
54 |
$this->startTime = time();
|
@@ -67,7 +90,9 @@ class wfScanEngine {
|
|
67 |
$this->jobList[] = 'knownFiles_init';
|
68 |
$this->jobList[] = 'knownFiles_main';
|
69 |
$this->jobList[] = 'knownFiles_finish';
|
70 |
-
foreach (array('knownFiles', '
|
|
|
|
|
71 |
if (wfConfig::get('scansEnabled_' . $scanType)) {
|
72 |
if (method_exists($this, 'scan_' . $scanType . '_init')) {
|
73 |
foreach (array('init', 'main', 'finish') as $op) {
|
@@ -216,6 +241,127 @@ class wfScanEngine {
|
|
216 |
sleep(2);
|
217 |
}
|
218 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
219 |
private function scan_checkSpamvertized(){
|
220 |
if(wfConfig::get('isPaid')){
|
221 |
if(wfConfig::get('spamvertizeCheck')){
|
@@ -263,46 +409,13 @@ class wfScanEngine {
|
|
263 |
}
|
264 |
}
|
265 |
|
266 |
-
if(! function_exists( 'get_plugins')){
|
267 |
-
require_once ABSPATH . '/wp-admin/includes/plugin.php';
|
268 |
-
}
|
269 |
$this->status(2, 'info', "Getting plugin list from WordPress");
|
270 |
-
$
|
271 |
-
$knownFilesPlugins = array();
|
272 |
-
foreach($pluginData as $key => $data){
|
273 |
-
if(preg_match('/^([^\/]+)\//', $key, $matches)){
|
274 |
-
$pluginDir = $matches[1];
|
275 |
-
$pluginFullDir = "wp-content/plugins/" . $pluginDir;
|
276 |
-
$knownFilesPlugins[$key] = array(
|
277 |
-
'Name' => $data['Name'],
|
278 |
-
'Version' => $data['Version'],
|
279 |
-
'ShortDir' => $pluginDir,
|
280 |
-
'FullDir' => $pluginFullDir
|
281 |
-
);
|
282 |
-
}
|
283 |
-
}
|
284 |
-
|
285 |
$this->status(2, 'info', "Found " . sizeof($knownFilesPlugins) . " plugins");
|
286 |
$this->i->updateSummaryItem('totalPlugins', sizeof($knownFilesPlugins));
|
287 |
|
288 |
-
if (!function_exists('wp_get_themes')) {
|
289 |
-
require_once ABSPATH . '/wp-includes/theme.php';
|
290 |
-
}
|
291 |
$this->status(2, 'info', "Getting theme list from WordPress");
|
292 |
-
$
|
293 |
-
foreach ($themes as $themeName => $themeVal) {
|
294 |
-
if (preg_match('/\/([^\/]+)$/', $themeVal['Stylesheet Dir'], $matches)) {
|
295 |
-
$shortDir = $matches[1]; //e.g. evo4cms
|
296 |
-
$fullDir = substr($themeVal['Stylesheet Dir'], strlen(ABSPATH)); //e.g. wp-content/themes/evo4cms
|
297 |
-
$knownFilesThemes[$themeName] = array(
|
298 |
-
'Name' => $themeVal['Name'],
|
299 |
-
'Version' => $themeVal['Version'],
|
300 |
-
'ShortDir' => $shortDir,
|
301 |
-
'FullDir' => $fullDir
|
302 |
-
);
|
303 |
-
}
|
304 |
-
}
|
305 |
-
|
306 |
$this->status(2, 'info', "Found " . sizeof($knownFilesThemes) . " themes");
|
307 |
$this->i->updateSummaryItem('totalThemes', sizeof($knownFilesThemes));
|
308 |
|
@@ -351,52 +464,7 @@ class wfScanEngine {
|
|
351 |
wordfence::statusEnd($this->statusIDX['GSB'], $haveIssuesGSB);
|
352 |
}
|
353 |
|
354 |
-
private function scan_database_init() {
|
355 |
-
$this->statusIDX['db_infect'] = wordfence::statusStart('Scanning database for infections and vulnerabilities');
|
356 |
-
$this->dbScanner = new wordfenceDBScanner($this->apiKey, $this->wp_version, ABSPATH);
|
357 |
-
$this->status(2, 'info', "Starting scan of database");
|
358 |
-
}
|
359 |
|
360 |
-
private function scan_database_main() {
|
361 |
-
if (!$this->dbScanner) {
|
362 |
-
$this->dbScanner = new wordfenceDBScanner($this->apiKey, $this->wp_version, ABSPATH);
|
363 |
-
}
|
364 |
-
$this->databaseResults = $this->dbScanner->scan($this);
|
365 |
-
}
|
366 |
-
|
367 |
-
private function scan_database_finish() {
|
368 |
-
$this->status(2, 'info', "Done database scan");
|
369 |
-
if ($this->dbScanner->errorMsg) {
|
370 |
-
throw new Exception($this->dbScanner->errorMsg);
|
371 |
-
}
|
372 |
-
$this->dbScanner = null;
|
373 |
-
$haveIssues = false;
|
374 |
-
foreach ($this->databaseResults as $issue) {
|
375 |
-
$this->status(2, 'info', "Adding issue: " . $issue['shortMsg']);
|
376 |
-
$issue_success = $this->addIssue($issue['type'], $issue['severity'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data']);
|
377 |
-
if ($issue_success) {
|
378 |
-
$haveIssues = true;
|
379 |
-
}
|
380 |
-
}
|
381 |
-
$this->databaseResults = null;
|
382 |
-
|
383 |
-
$blogsToScan = self::getBlogsToScan('options');
|
384 |
-
$wfdb = new wfDB();
|
385 |
-
foreach ($blogsToScan as $blog) {
|
386 |
-
$charset = $wfdb->querySingle("SELECT option_value FROM " . $blog['table'] . " WHERE option_name='blog_charset'");
|
387 |
-
if (strtolower($charset) == 'utf-7') {
|
388 |
-
$this->addIssue('database', 1, $blog['blog_id'] . 'blog_charset', $blog['blog_id'] . 'blog_charset', "An option was found in your site that indicates it may have been hacked.", "The 'blog_charset' option in your database is set to '" . $charset . "' which indicates your site may have been hacked. If hackers can gain access to your database via phpMyAdmin for example, they will change this value in order to inject malicious code into other parts of your site or allow XSS attacks. The 'badi' hack does this.", array(
|
389 |
-
'isMultisite' => $blog['isMultisite'],
|
390 |
-
'domain' => $blog['domain'],
|
391 |
-
'path' => $blog['path'],
|
392 |
-
'blog_id' => $blog['blog_id']
|
393 |
-
));
|
394 |
-
$haveIssues = true;
|
395 |
-
}
|
396 |
-
}
|
397 |
-
|
398 |
-
wordfence::statusEnd($this->statusIDX['db_infect'], $haveIssues);
|
399 |
-
}
|
400 |
private function scan_posts_init(){
|
401 |
$this->statusIDX['posts'] = wordfence::statusStart('Scanning posts for URL\'s in Google\'s Safe Browsing List');
|
402 |
$blogsToScan = self::getBlogsToScan('posts');
|
@@ -950,6 +1018,32 @@ class wfScanEngine {
|
|
950 |
|
951 |
wordfence::statusEnd($this->statusIDX['oldVersions'], $haveIssues);
|
952 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
953 |
public function status($level, $type, $msg){
|
954 |
wordfence::status($level, $type, $msg);
|
955 |
}
|
@@ -1039,6 +1133,349 @@ class wfScanEngine {
|
|
1039 |
wordfence::status(4, 'info', "getMaxExecutionTime() returning default of: 15");
|
1040 |
return 15;
|
1041 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1042 |
}
|
1043 |
|
1044 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
private $userPasswdQueue = "";
|
38 |
private $passwdHasIssues = false;
|
39 |
|
|
|
|
|
|
|
|
|
40 |
|
41 |
/**
|
42 |
* @var wordfenceDBScanner
|
43 |
*/
|
44 |
private $dbScanner;
|
45 |
|
46 |
+
/**
|
47 |
+
* @var wfScanKnownFilesLoader
|
48 |
+
*/
|
49 |
+
private $knownFilesLoader;
|
50 |
+
|
51 |
+
public static function testForFullPathDisclosure($url = null, $filePath = null) {
|
52 |
+
if ($url === null && $filePath === null) {
|
53 |
+
$url = includes_url('rss-functions.php');
|
54 |
+
$filePath = ABSPATH . WPINC . '/rss-functions.php';
|
55 |
+
}
|
56 |
+
|
57 |
+
$response = wp_remote_get($url);
|
58 |
+
$html = wp_remote_retrieve_body($response);
|
59 |
+
return preg_match("/" . preg_quote(realpath($filePath), "/") . "/i", $html);
|
60 |
+
}
|
61 |
+
|
62 |
+
public static function isDirectoryListingEnabled($url = null) {
|
63 |
+
if ($url === null) {
|
64 |
+
$uploadPaths = wp_upload_dir();
|
65 |
+
$url = $uploadPaths['baseurl'];
|
66 |
+
}
|
67 |
+
|
68 |
+
$response = wp_remote_get($url);
|
69 |
+
return !is_wp_error($response) && ($responseBody = wp_remote_retrieve_body($response)) &&
|
70 |
+
stripos($responseBody, '<title>Index of') !== false;
|
71 |
+
}
|
72 |
+
|
73 |
public function __sleep(){ //Same order here as above for properties that are included in serialization
|
74 |
+
return array('hasher', 'jobList', 'i', 'wp_version', 'apiKey', 'startTime', 'maxExecTime', 'publicScanEnabled', 'fileContentsResults', 'scanner', 'scanQueue', 'hoover', 'scanData', 'statusIDX', 'userPasswdQueue', 'passwdHasIssues', 'dbScanner');
|
75 |
}
|
76 |
public function __construct(){
|
77 |
$this->startTime = time();
|
90 |
$this->jobList[] = 'knownFiles_init';
|
91 |
$this->jobList[] = 'knownFiles_main';
|
92 |
$this->jobList[] = 'knownFiles_finish';
|
93 |
+
foreach (array('knownFiles', 'checkReadableConfig', 'fileContents',
|
94 |
+
// 'wpscan_fullPathDisclosure', 'wpscan_directoryListingEnabled',
|
95 |
+
'posts', 'comments', 'passwds', 'dns', 'diskSpace', 'oldVersions', 'suspiciousAdminUsers') as $scanType) {
|
96 |
if (wfConfig::get('scansEnabled_' . $scanType)) {
|
97 |
if (method_exists($this, 'scan_' . $scanType . '_init')) {
|
98 |
foreach (array('init', 'main', 'finish') as $op) {
|
241 |
sleep(2);
|
242 |
}
|
243 |
}
|
244 |
+
|
245 |
+
private function scan_checkReadableConfig() {
|
246 |
+
$haveIssues = false;
|
247 |
+
$status = wordfence::statusStart("Check for publicly accessible configuration files, backup files and logs");
|
248 |
+
|
249 |
+
$backupFileTests = array(
|
250 |
+
wfCommonBackupFileTest::createFromRootPath('.user.ini'),
|
251 |
+
wfCommonBackupFileTest::createFromRootPath('.htaccess'),
|
252 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.php.bak'),
|
253 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.php.swo'),
|
254 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.php.save'),
|
255 |
+
new wfCommonBackupFileTest(home_url('%23wp-config.php%23'), ABSPATH . '#wp-config.php#'),
|
256 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.php~'),
|
257 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.old'),
|
258 |
+
wfCommonBackupFileTest::createFromRootPath('.wp-config.php.swp'),
|
259 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.bak'),
|
260 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.save'),
|
261 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.php_bak'),
|
262 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.php.swp'),
|
263 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.php.old'),
|
264 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.php.original'),
|
265 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.php.orig'),
|
266 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.txt'),
|
267 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.original'),
|
268 |
+
wfCommonBackupFileTest::createFromRootPath('wp-config.orig'),
|
269 |
+
wfCommonBackupFileTest::createFromRootPath('searchreplacedb2.php'),
|
270 |
+
new wfCommonBackupFileTest(content_url('/debug.log'), WP_CONTENT_DIR . '/debug.log', array(
|
271 |
+
'headers' => array(
|
272 |
+
'Range' => 'bytes=0-700',
|
273 |
+
),
|
274 |
+
)),
|
275 |
+
);
|
276 |
+
$userIniFilename = ini_get('user_ini.filename');
|
277 |
+
if ($userIniFilename && $userIniFilename !== '.user.ini') {
|
278 |
+
$backupFileTests[] = wfCommonBackupFileTest::createFromRootPath($userIniFilename);
|
279 |
+
}
|
280 |
+
|
281 |
+
|
282 |
+
/** @var wfCommonBackupFileTest $test */
|
283 |
+
foreach ($backupFileTests as $test) {
|
284 |
+
$pathFromRoot = (strpos($test->getPath(), ABSPATH) === 0) ? substr($test->getPath(), strlen(ABSPATH)) : $test->getPath();
|
285 |
+
if ($test->fileExists() && $test->isPubliclyAccessible()) {
|
286 |
+
$key = "configReadable" . bin2hex($test->getUrl());
|
287 |
+
if ($this->addIssue(
|
288 |
+
'configReadable',
|
289 |
+
2,
|
290 |
+
$key,
|
291 |
+
$key,
|
292 |
+
'Publicly accessible config, backup, or log file found: ' . esc_html($pathFromRoot),
|
293 |
+
'<a href="' . $test->getUrl() . '" target="_blank">' . $test->getUrl() . '</a> is publicly
|
294 |
+
accessible and may expose sensitive information about your site. Files such as this one are commonly
|
295 |
+
checked for by scanners such as WPScan and should be removed or made inaccessible.',
|
296 |
+
array(
|
297 |
+
'url' => $test->getUrl(),
|
298 |
+
'file' => $pathFromRoot,
|
299 |
+
'canDelete' => true,
|
300 |
+
)
|
301 |
+
)) {
|
302 |
+
$haveIssues = true;
|
303 |
+
}
|
304 |
+
}
|
305 |
+
}
|
306 |
+
|
307 |
+
wordfence::statusEnd($status, $haveIssues);
|
308 |
+
}
|
309 |
+
|
310 |
+
private function scan_wpscan_fullPathDisclosure() {
|
311 |
+
$file = realpath(ABSPATH . WPINC . "/rss-functions.php");
|
312 |
+
if (!$file) {
|
313 |
+
return;
|
314 |
+
}
|
315 |
+
|
316 |
+
$haveIssues = false;
|
317 |
+
$status = wordfence::statusStart("Checking if your server discloses the path to the document root");
|
318 |
+
$testPage = includes_url() . basename($file);
|
319 |
+
|
320 |
+
if (self::testForFullPathDisclosure($testPage, $file)) {
|
321 |
+
$key = 'wpscan_fullPathDisclosure' . $testPage;
|
322 |
+
if ($this->addIssue(
|
323 |
+
'wpscan_fullPathDisclosure',
|
324 |
+
2,
|
325 |
+
$key,
|
326 |
+
$key,
|
327 |
+
'Web server exposes the document root',
|
328 |
+
'Full Path Disclosure (FPD) vulnerabilities enable the attacker to see the path to the webroot/file. e.g.:
|
329 |
+
/home/user/htdocs/file/. Certain vulnerabilities, such as using the load_file() (within a SQL Injection)
|
330 |
+
query to view the page source, require the attacker to have the full path to the file they wish to view.',
|
331 |
+
array('url' => $testPage)
|
332 |
+
)) {
|
333 |
+
$haveIssues = true;
|
334 |
+
}
|
335 |
+
}
|
336 |
+
|
337 |
+
wordfence::statusEnd($status, $haveIssues);
|
338 |
+
}
|
339 |
+
|
340 |
+
private function scan_wpscan_directoryListingEnabled() {
|
341 |
+
$this->statusIDX['wpscan_directoryListingEnabled'] = wordfence::statusStart("Checking to see if directory listing is enabled");
|
342 |
+
|
343 |
+
$uploadPaths = wp_upload_dir();
|
344 |
+
$enabled = self::isDirectoryListingEnabled($uploadPaths['baseurl']);
|
345 |
+
|
346 |
+
$haveIssues = false;
|
347 |
+
if ($enabled) {
|
348 |
+
if ($this->addIssue(
|
349 |
+
'wpscan_directoryListingEnabled',
|
350 |
+
2,
|
351 |
+
'wpscan_directoryListingEnabled',
|
352 |
+
'wpscan_directoryListingEnabled',
|
353 |
+
"Directory listing is enabled",
|
354 |
+
"Directory listing provides an attacker with the complete index of all the resources located inside of the directory. The specific risks and consequences vary depending on which files are listed and accessible, but it is recommended that you disable it unless it is needed.",
|
355 |
+
array(
|
356 |
+
'url' => $uploadPaths['baseurl'],
|
357 |
+
)
|
358 |
+
)) {
|
359 |
+
$haveIssues = true;
|
360 |
+
}
|
361 |
+
}
|
362 |
+
wordfence::statusEnd($this->statusIDX['wpscan_directoryListingEnabled'], $haveIssues);
|
363 |
+
}
|
364 |
+
|
365 |
private function scan_checkSpamvertized(){
|
366 |
if(wfConfig::get('isPaid')){
|
367 |
if(wfConfig::get('spamvertizeCheck')){
|
409 |
}
|
410 |
}
|
411 |
|
|
|
|
|
|
|
412 |
$this->status(2, 'info', "Getting plugin list from WordPress");
|
413 |
+
$knownFilesPlugins = $this->getPlugins();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
414 |
$this->status(2, 'info', "Found " . sizeof($knownFilesPlugins) . " plugins");
|
415 |
$this->i->updateSummaryItem('totalPlugins', sizeof($knownFilesPlugins));
|
416 |
|
|
|
|
|
|
|
417 |
$this->status(2, 'info', "Getting theme list from WordPress");
|
418 |
+
$knownFilesThemes = $this->getThemes();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
419 |
$this->status(2, 'info', "Found " . sizeof($knownFilesThemes) . " themes");
|
420 |
$this->i->updateSummaryItem('totalThemes', sizeof($knownFilesThemes));
|
421 |
|
464 |
wordfence::statusEnd($this->statusIDX['GSB'], $haveIssuesGSB);
|
465 |
}
|
466 |
|
|
|
|
|
|
|
|
|
|
|
467 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
468 |
private function scan_posts_init(){
|
469 |
$this->statusIDX['posts'] = wordfence::statusStart('Scanning posts for URL\'s in Google\'s Safe Browsing List');
|
470 |
$blogsToScan = self::getBlogsToScan('posts');
|
1018 |
|
1019 |
wordfence::statusEnd($this->statusIDX['oldVersions'], $haveIssues);
|
1020 |
}
|
1021 |
+
|
1022 |
+
public function scan_suspiciousAdminUsers() {
|
1023 |
+
$this->statusIDX['suspiciousAdminUsers'] = wordfence::statusStart("Scanning for admin users not created through WordPress");
|
1024 |
+
$haveIssues = false;
|
1025 |
+
|
1026 |
+
$adminUsers = new wfAdminUserMonitor();
|
1027 |
+
if ($adminUsers->isEnabled() && $suspiciousAdmins = $adminUsers->checkNewAdmins()) {
|
1028 |
+
foreach ($suspiciousAdmins as $userID) {
|
1029 |
+
$user = new WP_User($userID);
|
1030 |
+
$key = 'suspiciousAdminUsers' . $userID;
|
1031 |
+
if ($this->addIssue('suspiciousAdminUsers', 1, $key, $key,
|
1032 |
+
"An admin user with the username " . esc_html($user->user_login) . " was created outside of WordPress.",
|
1033 |
+
"An admin user with the username " . esc_html($user->user_login) . " was created outside of WordPress. It's
|
1034 |
+
possible a plugin could have created the account, but if you do not recognize the user, we suggest you remove
|
1035 |
+
it.",
|
1036 |
+
array(
|
1037 |
+
'userID' => $userID,
|
1038 |
+
))) {
|
1039 |
+
$haveIssues = true;
|
1040 |
+
}
|
1041 |
+
}
|
1042 |
+
}
|
1043 |
+
|
1044 |
+
wordfence::statusEnd($this->statusIDX['suspiciousAdminUsers'], $haveIssues);
|
1045 |
+
}
|
1046 |
+
|
1047 |
public function status($level, $type, $msg){
|
1048 |
wordfence::status($level, $type, $msg);
|
1049 |
}
|
1133 |
wordfence::status(4, 'info', "getMaxExecutionTime() returning default of: 15");
|
1134 |
return 15;
|
1135 |
}
|
1136 |
+
|
1137 |
+
/**
|
1138 |
+
* @return wfScanKnownFilesLoader
|
1139 |
+
*/
|
1140 |
+
public function getKnownFilesLoader() {
|
1141 |
+
if ($this->knownFilesLoader === null) {
|
1142 |
+
$this->knownFilesLoader = new wfScanKnownFilesLoader($this->api, $this->getPlugins(), $this->getThemes());
|
1143 |
+
}
|
1144 |
+
return $this->knownFilesLoader;
|
1145 |
+
}
|
1146 |
+
|
1147 |
+
/**
|
1148 |
+
* @return array
|
1149 |
+
*/
|
1150 |
+
public function getPlugins() {
|
1151 |
+
if(! function_exists( 'get_plugins')){
|
1152 |
+
require_once ABSPATH . '/wp-admin/includes/plugin.php';
|
1153 |
+
}
|
1154 |
+
$pluginData = get_plugins();
|
1155 |
+
$plugins = array();
|
1156 |
+
foreach ($pluginData as $key => $data) {
|
1157 |
+
if (preg_match('/^([^\/]+)\//', $key, $matches)) {
|
1158 |
+
$pluginDir = $matches[1];
|
1159 |
+
$pluginFullDir = "wp-content/plugins/" . $pluginDir;
|
1160 |
+
$plugins[$key] = array(
|
1161 |
+
'Name' => $data['Name'],
|
1162 |
+
'Version' => $data['Version'],
|
1163 |
+
'ShortDir' => $pluginDir,
|
1164 |
+
'FullDir' => $pluginFullDir
|
1165 |
+
);
|
1166 |
+
}
|
1167 |
+
}
|
1168 |
+
return $plugins;
|
1169 |
+
}
|
1170 |
+
|
1171 |
+
/**
|
1172 |
+
* @return array
|
1173 |
+
*/
|
1174 |
+
public function getThemes() {
|
1175 |
+
if (!function_exists('wp_get_themes')) {
|
1176 |
+
require_once ABSPATH . '/wp-includes/theme.php';
|
1177 |
+
}
|
1178 |
+
$themeData = wp_get_themes();
|
1179 |
+
$themes = array();
|
1180 |
+
foreach ($themeData as $themeName => $themeVal) {
|
1181 |
+
if (preg_match('/\/([^\/]+)$/', $themeVal['Stylesheet Dir'], $matches)) {
|
1182 |
+
$shortDir = $matches[1]; //e.g. evo4cms
|
1183 |
+
$fullDir = substr($themeVal['Stylesheet Dir'], strlen(ABSPATH)); //e.g. wp-content/themes/evo4cms
|
1184 |
+
$themes[$themeName] = array(
|
1185 |
+
'Name' => $themeVal['Name'],
|
1186 |
+
'Version' => $themeVal['Version'],
|
1187 |
+
'ShortDir' => $shortDir,
|
1188 |
+
'FullDir' => $fullDir
|
1189 |
+
);
|
1190 |
+
}
|
1191 |
+
}
|
1192 |
+
return $themes;
|
1193 |
+
}
|
1194 |
+
}
|
1195 |
+
|
1196 |
+
class wfScanKnownFilesLoader {
|
1197 |
+
/**
|
1198 |
+
* @var array
|
1199 |
+
*/
|
1200 |
+
private $plugins;
|
1201 |
+
|
1202 |
+
/**
|
1203 |
+
* @var array
|
1204 |
+
*/
|
1205 |
+
private $themes;
|
1206 |
+
|
1207 |
+
/**
|
1208 |
+
* @var array
|
1209 |
+
*/
|
1210 |
+
private $knownFiles = array();
|
1211 |
+
|
1212 |
+
/**
|
1213 |
+
* @var wfAPI
|
1214 |
+
*/
|
1215 |
+
private $api;
|
1216 |
+
|
1217 |
+
|
1218 |
+
/**
|
1219 |
+
* @param wfAPI $api
|
1220 |
+
* @param array $plugins
|
1221 |
+
* @param array $themes
|
1222 |
+
*/
|
1223 |
+
public function __construct($api, $plugins = null, $themes = null) {
|
1224 |
+
$this->api = $api;
|
1225 |
+
$this->plugins = $plugins;
|
1226 |
+
$this->themes = $themes;
|
1227 |
+
}
|
1228 |
+
|
1229 |
+
/**
|
1230 |
+
* @return bool
|
1231 |
+
*/
|
1232 |
+
public function isLoaded() {
|
1233 |
+
return is_array($this->knownFiles) && count($this->knownFiles) > 0;
|
1234 |
+
}
|
1235 |
+
|
1236 |
+
/**
|
1237 |
+
* @param $file
|
1238 |
+
* @return bool
|
1239 |
+
* @throws wfScanKnownFilesException
|
1240 |
+
*/
|
1241 |
+
public function isKnownFile($file) {
|
1242 |
+
if (!$this->isLoaded()) {
|
1243 |
+
$this->fetchKnownFiles();
|
1244 |
+
}
|
1245 |
+
|
1246 |
+
return isset($this->knownFiles['core'][$file]) ||
|
1247 |
+
isset($this->knownFiles['plugins'][$file]) ||
|
1248 |
+
isset($this->knownFiles['themes'][$file]);
|
1249 |
+
}
|
1250 |
+
|
1251 |
+
/**
|
1252 |
+
* @param $file
|
1253 |
+
* @return bool
|
1254 |
+
* @throws wfScanKnownFilesException
|
1255 |
+
*/
|
1256 |
+
public function isKnownCoreFile($file) {
|
1257 |
+
if (!$this->isLoaded()) {
|
1258 |
+
$this->fetchKnownFiles();
|
1259 |
+
}
|
1260 |
+
return isset($this->knownFiles['core'][$file]);
|
1261 |
+
}
|
1262 |
+
|
1263 |
+
/**
|
1264 |
+
* @param $file
|
1265 |
+
* @return bool
|
1266 |
+
* @throws wfScanKnownFilesException
|
1267 |
+
*/
|
1268 |
+
public function isKnownPluginFile($file) {
|
1269 |
+
if (!$this->isLoaded()) {
|
1270 |
+
$this->fetchKnownFiles();
|
1271 |
+
}
|
1272 |
+
return isset($this->knownFiles['plugins'][$file]);
|
1273 |
+
}
|
1274 |
+
|
1275 |
+
/**
|
1276 |
+
* @param $file
|
1277 |
+
* @return bool
|
1278 |
+
* @throws wfScanKnownFilesException
|
1279 |
+
*/
|
1280 |
+
public function isKnownThemeFile($file) {
|
1281 |
+
if (!$this->isLoaded()) {
|
1282 |
+
$this->fetchKnownFiles();
|
1283 |
+
}
|
1284 |
+
return isset($this->knownFiles['themes'][$file]);
|
1285 |
+
}
|
1286 |
+
|
1287 |
+
/**
|
1288 |
+
* @throws wfScanKnownFilesException
|
1289 |
+
*/
|
1290 |
+
public function fetchKnownFiles() {
|
1291 |
+
try {
|
1292 |
+
$dataArr = $this->api->binCall('get_known_files', json_encode(array(
|
1293 |
+
'plugins' => $this->plugins,
|
1294 |
+
'themes' => $this->themes
|
1295 |
+
)));
|
1296 |
+
|
1297 |
+
if ($dataArr['code'] != 200) {
|
1298 |
+
throw new wfScanKnownFilesException("Got error response from Wordfence servers: " . $dataArr['code'], $dataArr['code']);
|
1299 |
+
}
|
1300 |
+
$this->knownFiles = @json_decode($dataArr['data'], true);
|
1301 |
+
if (!is_array($this->knownFiles)) {
|
1302 |
+
throw new wfScanKnownFilesException("Invalid response from Wordfence servers.");
|
1303 |
+
}
|
1304 |
+
} catch (Exception $e) {
|
1305 |
+
throw new wfScanKnownFilesException($e->getMessage(), $e->getCode(), $e);
|
1306 |
+
}
|
1307 |
+
}
|
1308 |
+
|
1309 |
+
public function getKnownPluginData($file) {
|
1310 |
+
if ($this->isKnownPluginFile($file)) {
|
1311 |
+
return $this->knownFiles['plugins'][$file];
|
1312 |
+
}
|
1313 |
+
return null;
|
1314 |
+
}
|
1315 |
+
|
1316 |
+
public function getKnownThemeData($file) {
|
1317 |
+
if ($this->isKnownThemeFile($file)) {
|
1318 |
+
return $this->knownFiles['themes'][$file];
|
1319 |
+
}
|
1320 |
+
return null;
|
1321 |
+
}
|
1322 |
+
|
1323 |
+
/**
|
1324 |
+
* @return array
|
1325 |
+
*/
|
1326 |
+
public function getPlugins() {
|
1327 |
+
return $this->plugins;
|
1328 |
+
}
|
1329 |
+
|
1330 |
+
/**
|
1331 |
+
* @param array $plugins
|
1332 |
+
*/
|
1333 |
+
public function setPlugins($plugins) {
|
1334 |
+
$this->plugins = $plugins;
|
1335 |
+
}
|
1336 |
+
|
1337 |
+
/**
|
1338 |
+
* @return array
|
1339 |
+
*/
|
1340 |
+
public function getThemes() {
|
1341 |
+
return $this->themes;
|
1342 |
+
}
|
1343 |
+
|
1344 |
+
/**
|
1345 |
+
* @param array $themes
|
1346 |
+
*/
|
1347 |
+
public function setThemes($themes) {
|
1348 |
+
$this->themes = $themes;
|
1349 |
+
}
|
1350 |
+
|
1351 |
+
/**
|
1352 |
+
* @return array
|
1353 |
+
* @throws wfScanKnownFilesException
|
1354 |
+
*/
|
1355 |
+
public function getKnownFiles() {
|
1356 |
+
if (!$this->isLoaded()) {
|
1357 |
+
$this->fetchKnownFiles();
|
1358 |
+
}
|
1359 |
+
return $this->knownFiles;
|
1360 |
+
}
|
1361 |
+
|
1362 |
+
/**
|
1363 |
+
* @param array $knownFiles
|
1364 |
+
*/
|
1365 |
+
public function setKnownFiles($knownFiles) {
|
1366 |
+
$this->knownFiles = $knownFiles;
|
1367 |
+
}
|
1368 |
+
|
1369 |
+
/**
|
1370 |
+
* @return wfAPI
|
1371 |
+
*/
|
1372 |
+
public function getAPI() {
|
1373 |
+
return $this->api;
|
1374 |
+
}
|
1375 |
+
|
1376 |
+
/**
|
1377 |
+
* @param wfAPI $api
|
1378 |
+
*/
|
1379 |
+
public function setAPI($api) {
|
1380 |
+
$this->api = $api;
|
1381 |
+
}
|
1382 |
}
|
1383 |
|
1384 |
+
class wfScanKnownFilesException extends Exception {
|
1385 |
+
|
1386 |
+
}
|
1387 |
+
|
1388 |
+
class wfCommonBackupFileTest {
|
1389 |
+
|
1390 |
+
/**
|
1391 |
+
* @param string $path
|
1392 |
+
* @return wfCommonBackupFileTest
|
1393 |
+
*/
|
1394 |
+
public static function createFromRootPath($path) {
|
1395 |
+
return new self(home_url($path), ABSPATH . $path);
|
1396 |
+
}
|
1397 |
+
|
1398 |
+
private $url;
|
1399 |
+
private $path;
|
1400 |
+
/**
|
1401 |
+
* @var array
|
1402 |
+
*/
|
1403 |
+
private $requestArgs;
|
1404 |
+
private $response;
|
1405 |
+
|
1406 |
+
|
1407 |
+
/**
|
1408 |
+
* @param string $url
|
1409 |
+
* @param string $path
|
1410 |
+
* @param array $requestArgs
|
1411 |
+
*/
|
1412 |
+
public function __construct($url, $path, $requestArgs = array()) {
|
1413 |
+
$this->url = $url;
|
1414 |
+
$this->path = $path;
|
1415 |
+
$this->requestArgs = $requestArgs;
|
1416 |
+
}
|
1417 |
+
|
1418 |
+
/**
|
1419 |
+
* @return bool
|
1420 |
+
*/
|
1421 |
+
public function fileExists() {
|
1422 |
+
return file_exists($this->path);
|
1423 |
+
}
|
1424 |
+
|
1425 |
+
/**
|
1426 |
+
* @return bool
|
1427 |
+
*/
|
1428 |
+
public function isPubliclyAccessible() {
|
1429 |
+
$this->response = wp_remote_get($this->url, $this->requestArgs);
|
1430 |
+
return wp_remote_retrieve_response_code($this->response) === 200;
|
1431 |
+
}
|
1432 |
+
|
1433 |
+
/**
|
1434 |
+
* @return string
|
1435 |
+
*/
|
1436 |
+
public function getUrl() {
|
1437 |
+
return $this->url;
|
1438 |
+
}
|
1439 |
+
|
1440 |
+
/**
|
1441 |
+
* @param string $url
|
1442 |
+
*/
|
1443 |
+
public function setUrl($url) {
|
1444 |
+
$this->url = $url;
|
1445 |
+
}
|
1446 |
+
|
1447 |
+
/**
|
1448 |
+
* @return string
|
1449 |
+
*/
|
1450 |
+
public function getPath() {
|
1451 |
+
return $this->path;
|
1452 |
+
}
|
1453 |
+
|
1454 |
+
/**
|
1455 |
+
* @param string $path
|
1456 |
+
*/
|
1457 |
+
public function setPath($path) {
|
1458 |
+
$this->path = $path;
|
1459 |
+
}
|
1460 |
+
|
1461 |
+
/**
|
1462 |
+
* @return array
|
1463 |
+
*/
|
1464 |
+
public function getRequestArgs() {
|
1465 |
+
return $this->requestArgs;
|
1466 |
+
}
|
1467 |
+
|
1468 |
+
/**
|
1469 |
+
* @param array $requestArgs
|
1470 |
+
*/
|
1471 |
+
public function setRequestArgs($requestArgs) {
|
1472 |
+
$this->requestArgs = $requestArgs;
|
1473 |
+
}
|
1474 |
+
|
1475 |
+
/**
|
1476 |
+
* @return mixed
|
1477 |
+
*/
|
1478 |
+
public function getResponse() {
|
1479 |
+
return $this->response;
|
1480 |
+
}
|
1481 |
+
}
|
lib/wfSchema.php
CHANGED
@@ -45,7 +45,7 @@ class wfSchema {
|
|
45 |
ctime DOUBLE(17,6) UNSIGNED NOT NULL,
|
46 |
IP int UNSIGNED NOT NULL,
|
47 |
jsRun tinyint default 0,
|
48 |
-
|
49 |
isGoogle tinyint NOT NULL,
|
50 |
userID int UNSIGNED NOT NULL,
|
51 |
newVisit tinyint UNSIGNED NOT NULL,
|
@@ -166,7 +166,7 @@ class wfSchema {
|
|
166 |
blockCount int UNSIGNED NOT NULL DEFAULT 0,
|
167 |
unixday int UNSIGNED NOT NULL,
|
168 |
PRIMARY KEY(IP, unixday)
|
169 |
-
) default charset=utf8"
|
170 |
/*
|
171 |
'wfPerfLog' => "(
|
172 |
id int UNSIGNED NOT NULL auto_increment PRIMARY KEY,
|
45 |
ctime DOUBLE(17,6) UNSIGNED NOT NULL,
|
46 |
IP int UNSIGNED NOT NULL,
|
47 |
jsRun tinyint default 0,
|
48 |
+
statusCode int NOT NULL default 200,
|
49 |
isGoogle tinyint NOT NULL,
|
50 |
userID int UNSIGNED NOT NULL,
|
51 |
newVisit tinyint UNSIGNED NOT NULL,
|
166 |
blockCount int UNSIGNED NOT NULL DEFAULT 0,
|
167 |
unixday int UNSIGNED NOT NULL,
|
168 |
PRIMARY KEY(IP, unixday)
|
169 |
+
) default charset=utf8",
|
170 |
/*
|
171 |
'wfPerfLog' => "(
|
172 |
id int UNSIGNED NOT NULL auto_increment PRIMARY KEY,
|
lib/wfUtils.php
CHANGED
@@ -155,6 +155,11 @@ class wfUtils {
|
|
155 |
$first = self::inet_ntop($binary_first);
|
156 |
$last = self::inet_ntop($binary_last);
|
157 |
|
|
|
|
|
|
|
|
|
|
|
158 |
// Split addresses into segments
|
159 |
$first_array = preg_split('/[\.\:]/', $first);
|
160 |
$last_array = preg_split('/[\.\:]/', $last);
|
@@ -167,11 +172,12 @@ class wfUtils {
|
|
167 |
|
168 |
foreach ($first_array as $index => $segment) {
|
169 |
if ($segment === $last_array[$index]) {
|
170 |
-
$range_segments[] = $segment;
|
171 |
} else if ($segment === '' || $last_array[$index] === '') {
|
172 |
$range_segments[] = '';
|
173 |
} else {
|
174 |
-
$range_segments[] = "[
|
|
|
175 |
}
|
176 |
}
|
177 |
|
@@ -298,23 +304,27 @@ class wfUtils {
|
|
298 |
|
299 |
$hex = bin2hex($ip);
|
300 |
$groups = str_split($hex, 4);
|
301 |
-
$
|
302 |
$done_collapse = false;
|
303 |
foreach ($groups as $index => $group) {
|
304 |
if ($group == '0000' && !$done_collapse) {
|
305 |
-
if (
|
306 |
-
$groups[$index] = ':';
|
307 |
-
} else {
|
308 |
$groups[$index] = '';
|
|
|
309 |
}
|
310 |
-
$
|
311 |
-
|
|
|
|
|
|
|
312 |
$done_collapse = true;
|
313 |
-
$collapse = false;
|
314 |
}
|
315 |
$groups[$index] = ltrim($groups[$index], '0');
|
|
|
|
|
|
|
316 |
}
|
317 |
-
$ip = join(':', array_filter($groups));
|
318 |
$ip = str_replace(':::', '::', $ip);
|
319 |
return $ip == ':' ? '::' : $ip;
|
320 |
}
|
@@ -346,7 +356,7 @@ class wfUtils {
|
|
346 |
return false;
|
347 |
}
|
348 |
public static function getBaseURL(){
|
349 |
-
return plugins_url() . '/
|
350 |
}
|
351 |
public static function getPluginBaseDir(){
|
352 |
if(function_exists('wp_normalize_path')){ //Older WP versions don't have this func and we had many complaints before this check.
|
@@ -483,6 +493,73 @@ class wfUtils {
|
|
483 |
return false;
|
484 |
}
|
485 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
486 |
public static function extractHostname($str){
|
487 |
if(preg_match('/https?:\/\/([a-zA-Z0-9\.\-]+)(?:\/|$)/i', $str, $matches)){
|
488 |
return strtolower($matches[1]);
|
@@ -496,22 +573,41 @@ class wfUtils {
|
|
496 |
//return self::makeRandomIP();
|
497 |
|
498 |
// if no REMOTE_ADDR, it's probably running from the command line
|
499 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
500 |
|
501 |
$howGet = wfConfig::get('howGetIPs', false);
|
502 |
if($howGet){
|
503 |
if($howGet == 'REMOTE_ADDR'){
|
504 |
-
|
505 |
} else {
|
506 |
-
$
|
|
|
|
|
|
|
|
|
507 |
}
|
508 |
} else {
|
509 |
-
$
|
510 |
-
|
511 |
-
|
512 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
513 |
}
|
514 |
-
return
|
515 |
}
|
516 |
public static function isValidIP($IP){
|
517 |
return filter_var($IP, FILTER_VALIDATE_IP) !== false;
|
@@ -1069,6 +1165,17 @@ class wfUtils {
|
|
1069 |
return $ip;
|
1070 |
}
|
1071 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1072 |
/**
|
1073 |
* @param string $readmePath
|
1074 |
* @return bool
|
@@ -1103,6 +1210,46 @@ class wfUtils {
|
|
1103 |
}
|
1104 |
return false;
|
1105 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1106 |
}
|
1107 |
|
1108 |
// GeoIP lib uses these as well
|
@@ -1118,4 +1265,144 @@ if (!function_exists('inet_pton')) {
|
|
1118 |
}
|
1119 |
|
1120 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1121 |
?>
|
155 |
$first = self::inet_ntop($binary_first);
|
156 |
$last = self::inet_ntop($binary_last);
|
157 |
|
158 |
+
if (filter_var($network, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
159 |
+
$first = self::expandIPv6Address($first);
|
160 |
+
$last = self::expandIPv6Address($last);
|
161 |
+
}
|
162 |
+
|
163 |
// Split addresses into segments
|
164 |
$first_array = preg_split('/[\.\:]/', $first);
|
165 |
$last_array = preg_split('/[\.\:]/', $last);
|
172 |
|
173 |
foreach ($first_array as $index => $segment) {
|
174 |
if ($segment === $last_array[$index]) {
|
175 |
+
$range_segments[] = str_pad(ltrim($segment, '0'), 1, '0');
|
176 |
} else if ($segment === '' || $last_array[$index] === '') {
|
177 |
$range_segments[] = '';
|
178 |
} else {
|
179 |
+
$range_segments[] = "[". str_pad(ltrim($segment, '0'), 1, '0') . "-" .
|
180 |
+
str_pad(ltrim($last_array[$index], '0'), 1, '0') . "]";
|
181 |
}
|
182 |
}
|
183 |
|
304 |
|
305 |
$hex = bin2hex($ip);
|
306 |
$groups = str_split($hex, 4);
|
307 |
+
$in_collapse = false;
|
308 |
$done_collapse = false;
|
309 |
foreach ($groups as $index => $group) {
|
310 |
if ($group == '0000' && !$done_collapse) {
|
311 |
+
if ($in_collapse) {
|
|
|
|
|
312 |
$groups[$index] = '';
|
313 |
+
continue;
|
314 |
}
|
315 |
+
$groups[$index] = ':';
|
316 |
+
$in_collapse = true;
|
317 |
+
continue;
|
318 |
+
}
|
319 |
+
if ($in_collapse) {
|
320 |
$done_collapse = true;
|
|
|
321 |
}
|
322 |
$groups[$index] = ltrim($groups[$index], '0');
|
323 |
+
if (strlen($groups[$index]) === 0) {
|
324 |
+
$groups[$index] = '0';
|
325 |
+
}
|
326 |
}
|
327 |
+
$ip = join(':', array_filter($groups, 'strlen'));
|
328 |
$ip = str_replace(':::', '::', $ip);
|
329 |
return $ip == ':' ? '::' : $ip;
|
330 |
}
|
356 |
return false;
|
357 |
}
|
358 |
public static function getBaseURL(){
|
359 |
+
return plugins_url('', WORDFENCE_FCPATH) . '/';
|
360 |
}
|
361 |
public static function getPluginBaseDir(){
|
362 |
if(function_exists('wp_normalize_path')){ //Older WP versions don't have this func and we had many complaints before this check.
|
493 |
return false;
|
494 |
}
|
495 |
}
|
496 |
+
|
497 |
+
/**
|
498 |
+
* Expects an array of items. The items are either IP's or IP's separated by comma, space or tab. Or an array of IP's.
|
499 |
+
* We then examine all IP's looking for a public IP and storing private IP's in an array. If we find no public IPs we return the first private addr we found.
|
500 |
+
*
|
501 |
+
* @param array $arr
|
502 |
+
* @return bool|mixed
|
503 |
+
*/
|
504 |
+
private static function getCleanIPAndServerVar($arr){
|
505 |
+
$privates = array(); //Store private addrs until end as last resort.
|
506 |
+
for($i = 0; $i < count($arr); $i++){
|
507 |
+
list($item, $var) = $arr[$i];
|
508 |
+
if(is_array($item)){
|
509 |
+
foreach($item as $j){
|
510 |
+
// try verifying the IP is valid before stripping the port off
|
511 |
+
if (!self::isValidIP($j)) {
|
512 |
+
$j = preg_replace('/:\d+$/', '', $j); //Strip off port
|
513 |
+
}
|
514 |
+
if (self::isValidIP($j)) {
|
515 |
+
if (self::isPrivateAddress($j)) {
|
516 |
+
$privates[] = array($j, $var);
|
517 |
+
} else {
|
518 |
+
return array($j, $var);
|
519 |
+
}
|
520 |
+
}
|
521 |
+
}
|
522 |
+
continue; //This was an array so we can skip to the next item
|
523 |
+
}
|
524 |
+
$skipToNext = false;
|
525 |
+
foreach(array(',', ' ', "\t") as $char){
|
526 |
+
if(strpos($item, $char) !== false){
|
527 |
+
$sp = explode($char, $item);
|
528 |
+
foreach($sp as $j){
|
529 |
+
if (!self::isValidIP($j)) {
|
530 |
+
$j = preg_replace('/:\d+$/', '', $j); //Strip off port
|
531 |
+
}
|
532 |
+
if(self::isValidIP($j)){
|
533 |
+
if(self::isPrivateAddress($j)){
|
534 |
+
$privates[] = array($j, $var);
|
535 |
+
} else {
|
536 |
+
return array($j, $var);
|
537 |
+
}
|
538 |
+
}
|
539 |
+
}
|
540 |
+
$skipToNext = true;
|
541 |
+
break;
|
542 |
+
}
|
543 |
+
}
|
544 |
+
if($skipToNext){ continue; } //Skip to next item because this one had a comma, space or tab so was delimited and we didn't find anything.
|
545 |
+
|
546 |
+
if (!self::isValidIP($item)) {
|
547 |
+
$item = preg_replace('/:\d+$/', '', $item); //Strip off port
|
548 |
+
}
|
549 |
+
if(self::isValidIP($item)){
|
550 |
+
if(self::isPrivateAddress($item)){
|
551 |
+
$privates[] = array($item, $var);
|
552 |
+
} else {
|
553 |
+
return array($item, $var);
|
554 |
+
}
|
555 |
+
}
|
556 |
+
}
|
557 |
+
if(sizeof($privates) > 0){
|
558 |
+
return $privates[0]; //Return the first private we found so that we respect the order the IP's were passed to this function.
|
559 |
+
} else {
|
560 |
+
return false;
|
561 |
+
}
|
562 |
+
}
|
563 |
public static function extractHostname($str){
|
564 |
if(preg_match('/https?:\/\/([a-zA-Z0-9\.\-]+)(?:\/|$)/i', $str, $matches)){
|
565 |
return strtolower($matches[1]);
|
573 |
//return self::makeRandomIP();
|
574 |
|
575 |
// if no REMOTE_ADDR, it's probably running from the command line
|
576 |
+
$ip = self::getIPAndServerVarible();
|
577 |
+
if (is_array($ip)) {
|
578 |
+
list($IP, $variable) = $ip;
|
579 |
+
return $IP;
|
580 |
+
}
|
581 |
+
return false;
|
582 |
+
}
|
583 |
+
|
584 |
+
public static function getIPAndServerVarible() {
|
585 |
+
$connectionIP = array_key_exists('REMOTE_ADDR', $_SERVER) ? array($_SERVER['REMOTE_ADDR'], 'REMOTE_ADDR') : array('127.0.0.1', 'REMOTE_ADDR');
|
586 |
|
587 |
$howGet = wfConfig::get('howGetIPs', false);
|
588 |
if($howGet){
|
589 |
if($howGet == 'REMOTE_ADDR'){
|
590 |
+
return self::getCleanIPAndServerVar(array($connectionIP));
|
591 |
} else {
|
592 |
+
$ipsToCheck = array(
|
593 |
+
array($_SERVER[$howGet], $howGet),
|
594 |
+
$connectionIP,
|
595 |
+
);
|
596 |
+
return self::getCleanIPAndServerVar($ipsToCheck);
|
597 |
}
|
598 |
} else {
|
599 |
+
$ipsToCheck = array(
|
600 |
+
$connectionIP,
|
601 |
+
);
|
602 |
+
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
603 |
+
$ipsToCheck[] = array($_SERVER['HTTP_X_FORWARDED_FOR'], 'HTTP_X_FORWARDED_FOR');
|
604 |
+
}
|
605 |
+
if (isset($_SERVER['HTTP_X_REAL_IP'])) {
|
606 |
+
$ipsToCheck[] = array($_SERVER['HTTP_X_REAL_IP'], 'HTTP_X_REAL_IP');
|
607 |
+
}
|
608 |
+
return self::getCleanIPAndServerVar($ipsToCheck);
|
609 |
}
|
610 |
+
return false; //Returns an array with a valid IP and the server variable, or false.
|
611 |
}
|
612 |
public static function isValidIP($IP){
|
613 |
return filter_var($IP, FILTER_VALIDATE_IP) !== false;
|
1165 |
return $ip;
|
1166 |
}
|
1167 |
|
1168 |
+
public static function set_html_content_type() {
|
1169 |
+
return 'text/html';
|
1170 |
+
}
|
1171 |
+
|
1172 |
+
public static function htmlEmail($to, $subject, $body) {
|
1173 |
+
add_filter( 'wp_mail_content_type', 'wfUtils::set_html_content_type' );
|
1174 |
+
$result = wp_mail($to, $subject, $body);
|
1175 |
+
remove_filter( 'wp_mail_content_type', 'wfUtils::set_html_content_type' );
|
1176 |
+
return $result;
|
1177 |
+
}
|
1178 |
+
|
1179 |
/**
|
1180 |
* @param string $readmePath
|
1181 |
* @return bool
|
1210 |
}
|
1211 |
return false;
|
1212 |
}
|
1213 |
+
|
1214 |
+
public static function htaccessAppend($code)
|
1215 |
+
{
|
1216 |
+
$htaccess = ABSPATH . '/.htaccess';
|
1217 |
+
$content = self::htaccess();
|
1218 |
+
if (wfUtils::isNginx() || !is_writable($htaccess)) {
|
1219 |
+
return false;
|
1220 |
+
}
|
1221 |
+
|
1222 |
+
if (strpos($content, $code) === false) {
|
1223 |
+
// make sure we write this once
|
1224 |
+
file_put_contents($htaccess, $content . "\n" . trim($code), LOCK_EX);
|
1225 |
+
}
|
1226 |
+
|
1227 |
+
return true;
|
1228 |
+
}
|
1229 |
+
|
1230 |
+
public static function htaccess() {
|
1231 |
+
if (is_readable(ABSPATH . '/.htaccess') && !wfUtils::isNginx()) {
|
1232 |
+
return file_get_contents(ABSPATH . '/.htaccess');
|
1233 |
+
}
|
1234 |
+
return "";
|
1235 |
+
}
|
1236 |
+
|
1237 |
+
/**
|
1238 |
+
* @param array $array
|
1239 |
+
* @param mixed $oldKey
|
1240 |
+
* @param mixed $newKey
|
1241 |
+
* @return array
|
1242 |
+
* @throws Exception
|
1243 |
+
*/
|
1244 |
+
public static function arrayReplaceKey($array, $oldKey, $newKey) {
|
1245 |
+
$keys = array_keys($array);
|
1246 |
+
if (($index = array_search($oldKey, $keys)) === false) {
|
1247 |
+
throw new Exception(sprintf('Key "%s" does not exist', $oldKey));
|
1248 |
+
}
|
1249 |
+
$keys[$index] = $newKey;
|
1250 |
+
return array_combine($keys, array_values($array));
|
1251 |
+
}
|
1252 |
+
|
1253 |
}
|
1254 |
|
1255 |
// GeoIP lib uses these as well
|
1265 |
}
|
1266 |
|
1267 |
|
1268 |
+
class wfWebServerInfo {
|
1269 |
+
|
1270 |
+
const APACHE = 1;
|
1271 |
+
const NGINX = 2;
|
1272 |
+
const LITESPEED = 4;
|
1273 |
+
const IIS = 8;
|
1274 |
+
|
1275 |
+
private $handler;
|
1276 |
+
private $software;
|
1277 |
+
private $softwareName;
|
1278 |
+
|
1279 |
+
/**
|
1280 |
+
*
|
1281 |
+
*/
|
1282 |
+
public static function createFromEnvironment() {
|
1283 |
+
$serverInfo = new self;
|
1284 |
+
if (stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== false) {
|
1285 |
+
$serverInfo->setSoftware(self::APACHE);
|
1286 |
+
$serverInfo->setSoftwareName('apache');
|
1287 |
+
}
|
1288 |
+
if (stripos($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false) {
|
1289 |
+
$serverInfo->setSoftware(self::LITESPEED);
|
1290 |
+
$serverInfo->setSoftwareName('litespeed');
|
1291 |
+
}
|
1292 |
+
if (strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) {
|
1293 |
+
$serverInfo->setSoftware(self::NGINX);
|
1294 |
+
$serverInfo->setSoftwareName('nginx');
|
1295 |
+
}
|
1296 |
+
if (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer') !== false) {
|
1297 |
+
$serverInfo->setSoftware(self::IIS);
|
1298 |
+
$serverInfo->setSoftwareName('iis');
|
1299 |
+
}
|
1300 |
+
|
1301 |
+
$serverInfo->setHandler(php_sapi_name());
|
1302 |
+
|
1303 |
+
return $serverInfo;
|
1304 |
+
}
|
1305 |
+
|
1306 |
+
/**
|
1307 |
+
* @return bool
|
1308 |
+
*/
|
1309 |
+
public function isApache() {
|
1310 |
+
return $this->getSoftware() === self::APACHE;
|
1311 |
+
}
|
1312 |
+
|
1313 |
+
/**
|
1314 |
+
* @return bool
|
1315 |
+
*/
|
1316 |
+
public function isNGINX() {
|
1317 |
+
return $this->getSoftware() === self::NGINX;
|
1318 |
+
}
|
1319 |
+
|
1320 |
+
/**
|
1321 |
+
* @return bool
|
1322 |
+
*/
|
1323 |
+
public function isLiteSpeed() {
|
1324 |
+
return $this->getSoftware() === self::LITESPEED;
|
1325 |
+
}
|
1326 |
+
|
1327 |
+
/**
|
1328 |
+
* @return bool
|
1329 |
+
*/
|
1330 |
+
public function isIIS() {
|
1331 |
+
return $this->getSoftware() === self::IIS;
|
1332 |
+
}
|
1333 |
+
|
1334 |
+
/**
|
1335 |
+
* @return bool
|
1336 |
+
*/
|
1337 |
+
public function isApacheModPHP() {
|
1338 |
+
return $this->isApache() && function_exists('apache_get_modules');
|
1339 |
+
}
|
1340 |
+
|
1341 |
+
/**
|
1342 |
+
* Not sure if this can be implemented at the PHP level.
|
1343 |
+
* @return bool
|
1344 |
+
*/
|
1345 |
+
public function isApacheSuPHP() {
|
1346 |
+
return $this->isApache() && $this->isCGI() &&
|
1347 |
+
function_exists('posix_getuid') &&
|
1348 |
+
getmyuid() === posix_getuid();
|
1349 |
+
}
|
1350 |
+
|
1351 |
+
/**
|
1352 |
+
* @return bool
|
1353 |
+
*/
|
1354 |
+
public function isCGI() {
|
1355 |
+
return !$this->isFastCGI() && stripos($this->getHandler(), 'cgi') !== false;
|
1356 |
+
}
|
1357 |
+
|
1358 |
+
/**
|
1359 |
+
* @return bool
|
1360 |
+
*/
|
1361 |
+
public function isFastCGI() {
|
1362 |
+
return stripos($this->getHandler(), 'fastcgi') !== false || stripos($this->getHandler(), 'fpm-fcgi') !== false;
|
1363 |
+
}
|
1364 |
+
|
1365 |
+
/**
|
1366 |
+
* @return mixed
|
1367 |
+
*/
|
1368 |
+
public function getHandler() {
|
1369 |
+
return $this->handler;
|
1370 |
+
}
|
1371 |
+
|
1372 |
+
/**
|
1373 |
+
* @param mixed $handler
|
1374 |
+
*/
|
1375 |
+
public function setHandler($handler) {
|
1376 |
+
$this->handler = $handler;
|
1377 |
+
}
|
1378 |
+
|
1379 |
+
/**
|
1380 |
+
* @return mixed
|
1381 |
+
*/
|
1382 |
+
public function getSoftware() {
|
1383 |
+
return $this->software;
|
1384 |
+
}
|
1385 |
+
|
1386 |
+
/**
|
1387 |
+
* @param mixed $software
|
1388 |
+
*/
|
1389 |
+
public function setSoftware($software) {
|
1390 |
+
$this->software = $software;
|
1391 |
+
}
|
1392 |
+
|
1393 |
+
/**
|
1394 |
+
* @return mixed
|
1395 |
+
*/
|
1396 |
+
public function getSoftwareName() {
|
1397 |
+
return $this->softwareName;
|
1398 |
+
}
|
1399 |
+
|
1400 |
+
/**
|
1401 |
+
* @param mixed $softwareName
|
1402 |
+
*/
|
1403 |
+
public function setSoftwareName($softwareName) {
|
1404 |
+
$this->softwareName = $softwareName;
|
1405 |
+
}
|
1406 |
+
}
|
1407 |
+
|
1408 |
?>
|
lib/wfView.php
CHANGED
@@ -36,7 +36,7 @@ class wfView {
|
|
36 |
* @param array $data
|
37 |
*/
|
38 |
public function __construct($view, $data = array()) {
|
39 |
-
$this->view_path =
|
40 |
$this->view = $view;
|
41 |
$this->data = $data;
|
42 |
}
|
@@ -116,7 +116,7 @@ class wfView {
|
|
116 |
* Prevent POP
|
117 |
*/
|
118 |
public function __wakeup() {
|
119 |
-
$this->view_path =
|
120 |
$this->view = null;
|
121 |
$this->data = array();
|
122 |
$this->view_file_extension = '.php';
|
36 |
* @param array $data
|
37 |
*/
|
38 |
public function __construct($view, $data = array()) {
|
39 |
+
$this->view_path = WORDFENCE_PATH . 'views';
|
40 |
$this->view = $view;
|
41 |
$this->data = $data;
|
42 |
}
|
116 |
* Prevent POP
|
117 |
*/
|
118 |
public function __wakeup() {
|
119 |
+
$this->view_path = WORDFENCE_PATH . 'views';
|
120 |
$this->view = null;
|
121 |
$this->data = array();
|
122 |
$this->view_file_extension = '.php';
|
lib/wordfenceClass.php
CHANGED
@@ -19,6 +19,7 @@ require_once 'wfDirectoryIterator.php';
|
|
19 |
require_once 'wfUpdateCheck.php';
|
20 |
require_once 'wfActivityReport.php';
|
21 |
require_once 'wfHelperBin.php';
|
|
|
22 |
|
23 |
class wordfence {
|
24 |
public static $printStatus = false;
|
@@ -75,13 +76,40 @@ class wordfence {
|
|
75 |
// Remove cron for email summary
|
76 |
wfActivityReport::clearCronJobs();
|
77 |
|
|
|
|
|
|
|
78 |
wfConfig::clearDiskCache();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
if(wfConfig::get('deleteTablesOnDeact')){
|
80 |
$schema = new wfSchema();
|
81 |
$schema->dropAll();
|
82 |
foreach(array('wordfence_version', 'wordfenceActivated') as $opt){
|
83 |
delete_option($opt);
|
84 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
}
|
86 |
}
|
87 |
public static function hourlyCron(){
|
@@ -125,12 +153,13 @@ class wordfence {
|
|
125 |
$len = strlen($resp['data']);
|
126 |
$reason = "WFSN: Blocked by Wordfence Security Network";
|
127 |
$wfdb->queryWrite("delete from $p"."wfBlocks where wfsn=1 and permanent=0");
|
|
|
128 |
if($len > 0 && $len % 16 == 0){
|
129 |
for($i = 0; $i < $len; $i += 16){
|
130 |
$ip_bin = substr($resp['data'], $i, 16);
|
131 |
$IPStr = wfUtils::inet_ntop($ip_bin);
|
132 |
-
if(!
|
133 |
-
|
134 |
}
|
135 |
}
|
136 |
}
|
@@ -208,14 +237,7 @@ class wordfence {
|
|
208 |
// So if we do a once a day truncate to be safe, we'll only potentially lose the hour right before the truncate.
|
209 |
// Worth it to clean out the table completely once a day.
|
210 |
|
211 |
-
|
212 |
-
$count = $wfdb->querySingle("select count(*) as cnt from $p"."wfHits");
|
213 |
-
if($count > 20000){
|
214 |
-
$wfdb->truncate($p . "wfHits"); //So we don't slow down sites that have very large wfHits tables
|
215 |
-
} else if($count > 2000){
|
216 |
-
$wfdb->queryWrite("delete from $p"."wfHits order by id asc limit %d", ($count - 100));
|
217 |
-
}
|
218 |
-
|
219 |
/*
|
220 |
$count6 = $wfdb->querySingle("select count(*) as cnt from $p"."wfPerfLog");
|
221 |
if($count6 > 20000){
|
@@ -414,28 +436,87 @@ class wordfence {
|
|
414 |
wfUtils::hideReadme();
|
415 |
}
|
416 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
417 |
//Must be the final line
|
418 |
}
|
419 |
private static function doEarlyAccessLogging(){
|
420 |
$wfLog = self::getLog();
|
421 |
if($wfLog->logHitOK()){
|
422 |
-
|
|
|
|
|
|
|
|
|
|
|
423 |
$wfLog->logLeechAndBlock('404');
|
424 |
} else {
|
425 |
$wfLog->logLeechAndBlock('hit');
|
426 |
}
|
427 |
-
if(wfConfig::liveTrafficEnabled()){
|
428 |
-
self::$hitID = $wfLog->logHit();
|
429 |
-
add_action('wp_head', 'wordfence::wfLogHumanHeader');
|
430 |
-
}
|
431 |
-
/*
|
432 |
-
if(wfConfig::get('perfLoggingEnabled', false)){
|
433 |
-
add_action('wp_head', 'wordfence::wfLogPerfHeader');
|
434 |
-
}
|
435 |
-
*/
|
436 |
}
|
437 |
}
|
438 |
public static function initProtection(){
|
|
|
439 |
if(preg_match('/\/wp\-admin\/admin\-ajax\.php/', $_SERVER['REQUEST_URI'])){
|
440 |
if(
|
441 |
(isset($_GET['action']) && $_GET['action'] == 'revslider_show_image' && isset($_GET['img']) && preg_match('/\.php$/i', $_GET['img'])) ||
|
@@ -447,17 +528,17 @@ class wordfence {
|
|
447 |
}
|
448 |
}
|
449 |
public static function install_actions(){
|
450 |
-
|
451 |
-
|
452 |
-
register_activation_hook(WP_PLUGIN_DIR . '/wordfence/wordfence.php', 'wordfence::installPlugin');
|
453 |
-
register_deactivation_hook(WP_PLUGIN_DIR . '/wordfence/wordfence.php', 'wordfence::uninstallPlugin');
|
454 |
-
}
|
455 |
|
456 |
$versionInOptions = get_option('wordfence_version', false);
|
457 |
if( (! $versionInOptions) || version_compare(WORDFENCE_VERSION, $versionInOptions, '>')){
|
458 |
//Either there is no version in options or the version in options is greater and we need to run the upgrade
|
459 |
self::runInstall();
|
460 |
}
|
|
|
|
|
|
|
461 |
//These access wfConfig::get('apiKey') and will fail if runInstall hasn't executed.
|
462 |
wfCache::setupCaching();
|
463 |
|
@@ -562,6 +643,34 @@ class wordfence {
|
|
562 |
}
|
563 |
|
564 |
add_action('request', 'wordfence::preventAuthorNScans');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
565 |
}
|
566 |
/*
|
567 |
public static function cronAddSchedules($schedules){
|
@@ -613,6 +722,7 @@ class wordfence {
|
|
613 |
die(json_encode(array('ok' => 1)));
|
614 |
}
|
615 |
public static function ajax_logHuman_callback(){
|
|
|
616 |
$browscap = new wfBrowscap();
|
617 |
$UA = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
|
618 |
$isCrawler = false;
|
@@ -630,7 +740,7 @@ class wordfence {
|
|
630 |
header("Content-Length: 0");
|
631 |
header("X-Robots-Tag: noindex");
|
632 |
if (!$isCrawler) {
|
633 |
-
setcookie('wordfence_verifiedHuman',
|
634 |
}
|
635 |
}
|
636 |
flush();
|
@@ -731,6 +841,10 @@ class wordfence {
|
|
731 |
}
|
732 |
public static function lostPasswordPost(){
|
733 |
$IP = wfUtils::getIP();
|
|
|
|
|
|
|
|
|
734 |
if(self::getLog()->isWhitelisted($IP)){
|
735 |
return;
|
736 |
}
|
@@ -775,7 +889,11 @@ class wordfence {
|
|
775 |
public static function isLockedOut($IP){
|
776 |
return self::getLog()->isIPLockedOut($IP);
|
777 |
}
|
778 |
-
|
|
|
|
|
|
|
|
|
779 |
$wfFunc = isset($_GET['_wfsf']) ? @$_GET['_wfsf'] : false;
|
780 |
if($wfFunc == 'unlockEmail'){
|
781 |
if(! wp_verify_nonce(@$_POST['nonce'], 'wf-form')){
|
@@ -865,6 +983,70 @@ class wordfence {
|
|
865 |
}
|
866 |
}
|
867 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
868 |
if(wfConfig::get('firewallEnabled')){
|
869 |
$wfLog = self::getLog();
|
870 |
$wfLog->firewallBadIPs();
|
@@ -905,6 +1087,7 @@ class wordfence {
|
|
905 |
}
|
906 |
}
|
907 |
}
|
|
|
908 |
public static function loginAction($username){
|
909 |
if(sizeof($_POST) < 1){ return; } //only execute if login form is posted
|
910 |
if(! $username){ return; }
|
@@ -1032,13 +1215,15 @@ class wordfence {
|
|
1032 |
if(wfConfig::get('other_WFNet') && is_wp_error($authUser) && ($authUser->get_error_code() == 'invalid_username' || $authUser->get_error_code() == 'incorrect_password') ){
|
1033 |
if($maxBlockTime = self::wfsnIsBlocked($IP, 'brute')){
|
1034 |
self::getLog()->blockIP($IP, "Blocked by Wordfence Security Network", true, false, $maxBlockTime);
|
|
|
|
|
1035 |
}
|
1036 |
|
1037 |
}
|
1038 |
if($secEnabled){
|
1039 |
if(is_wp_error($authUser) && $authUser->get_error_code() == 'invalid_username'){
|
1040 |
if($blacklist = wfConfig::get('loginSec_userBlacklist')){
|
1041 |
-
$users = explode(
|
1042 |
foreach($users as $user){
|
1043 |
if(strtolower($username) == strtolower($user)){
|
1044 |
self::getLog()->blockIP($IP, "Blocked by login security setting.");
|
@@ -1121,6 +1306,10 @@ class wordfence {
|
|
1121 |
if(is_object($userDat)){
|
1122 |
self::getLog()->logLogin('logout', 0, $userDat->user_login);
|
1123 |
}
|
|
|
|
|
|
|
|
|
1124 |
}
|
1125 |
public static function loginInitAction(){
|
1126 |
if(self::isLockedOut(wfUtils::getIP())){
|
@@ -1190,6 +1379,23 @@ class wordfence {
|
|
1190 |
return array('errorMsg' => wp_kses($e->getMessage(), array()));
|
1191 |
}
|
1192 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1193 |
public static function ajax_sendTestEmail_callback(){
|
1194 |
$result = wp_mail($_POST['email'], "Wordfence Test Email", "This is a test email from " . site_url() . ".\nThe IP address that requested this was: " . wfUtils::getIP());
|
1195 |
$result = $result ? 'True' : 'False';
|
@@ -1785,6 +1991,12 @@ class wordfence {
|
|
1785 |
public static function ajax_saveConfig_callback(){
|
1786 |
$reload = '';
|
1787 |
$opts = wfConfig::parseOptions();
|
|
|
|
|
|
|
|
|
|
|
|
|
1788 |
$emails = array();
|
1789 |
foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['alertEmails'])) as $email){
|
1790 |
if(strlen($email) > 0){
|
@@ -1806,6 +2018,13 @@ class wordfence {
|
|
1806 |
$opts['alertEmails'] = '';
|
1807 |
}
|
1808 |
$opts['scan_exclude'] = wfUtils::cleanupOneEntryPerLine($opts['scan_exclude']);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1809 |
$whiteIPs = array();
|
1810 |
foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['whitelisted'])) as $whiteIP){
|
1811 |
if(strlen($whiteIP) > 0){
|
@@ -1840,18 +2059,7 @@ class wordfence {
|
|
1840 |
}
|
1841 |
}
|
1842 |
}
|
1843 |
-
$
|
1844 |
-
foreach(explode(',', $opts['loginSec_userBlacklist']) as $user){
|
1845 |
-
$user = trim($user);
|
1846 |
-
if(strlen($user) > 0){
|
1847 |
-
$userBlacklist[] = $user;
|
1848 |
-
}
|
1849 |
-
}
|
1850 |
-
if(sizeof($userBlacklist) > 0){
|
1851 |
-
$opts['loginSec_userBlacklist'] = implode(',', $userBlacklist);
|
1852 |
-
} else {
|
1853 |
-
$opts['loginSec_userBlacklist'] = '';
|
1854 |
-
}
|
1855 |
|
1856 |
$opts['apiKey'] = trim($opts['apiKey']);
|
1857 |
if($opts['apiKey'] && (! preg_match('/^[a-fA-F0-9]+$/', $opts['apiKey'])) ){ //User entered something but it's garbage.
|
@@ -1904,6 +2112,13 @@ class wordfence {
|
|
1904 |
$regenerateHtaccess = true;
|
1905 |
}
|
1906 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1907 |
foreach($opts as $key => $val){
|
1908 |
if($key != 'apiKey'){ //Don't save API key yet
|
1909 |
wfConfig::set($key, $val);
|
@@ -1985,6 +2200,59 @@ class wordfence {
|
|
1985 |
}
|
1986 |
return array('ok' => 1, 'reload' => $reload, 'paidKeyMsg' => $paidKeyMsg );
|
1987 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1988 |
public static function ajax_clearAllBlocked_callback(){
|
1989 |
$op = $_POST['op'];
|
1990 |
$wfLog = self::getLog();
|
@@ -2008,7 +2276,8 @@ class wordfence {
|
|
2008 |
}
|
2009 |
public static function ajax_permBlockIP_callback(){
|
2010 |
$IP = $_POST['IP'];
|
2011 |
-
|
|
|
2012 |
return array('ok' => 1);
|
2013 |
}
|
2014 |
public static function ajax_loadStaticPanel_callback(){
|
@@ -2089,13 +2358,14 @@ class wordfence {
|
|
2089 |
public static function ajax_blockIP_callback(){
|
2090 |
$IP = trim($_POST['IP']);
|
2091 |
$perm = (isset($_POST['perm']) && $_POST['perm'] == '1') ? true : false;
|
|
|
2092 |
if (!wfUtils::isValidIP($IP)) {
|
2093 |
return array('err' => 1, 'errorMsg' => "Please enter a valid IP address to block.");
|
2094 |
}
|
2095 |
if ($IP == wfUtils::getIP()) {
|
2096 |
return array('err' => 1, 'errorMsg' => "You can't block your own IP address.");
|
2097 |
}
|
2098 |
-
if (
|
2099 |
return array('err' => 1, 'errorMsg' => "The IP address " . wp_kses($IP, array()) . " is whitelisted and can't be blocked or it is in a range of internal IP addresses that Wordfence does not block. You can remove this IP from the whitelist on the Wordfence options page.");
|
2100 |
}
|
2101 |
if (wfConfig::get('neverBlockBG') != 'treatAsOtherCrawlers') { //Either neverBlockVerified or neverBlockUA is selected which means the user doesn't want to block google
|
@@ -2103,7 +2373,7 @@ class wordfence {
|
|
2103 |
return array('err' => 1, 'errorMsg' => "The IP address you're trying to block belongs to Google. Your options are currently set to not block these crawlers. Change this in Wordfence options if you want to manually block Google.");
|
2104 |
}
|
2105 |
}
|
2106 |
-
|
2107 |
return array('ok' => 1);
|
2108 |
}
|
2109 |
public static function ajax_reverseLookup_callback(){
|
@@ -2170,6 +2440,7 @@ class wordfence {
|
|
2170 |
$serverTime = $wfdb->querySingle("select unix_timestamp()");
|
2171 |
$jsonData = array(
|
2172 |
'serverTime' => $serverTime,
|
|
|
2173 |
'msg' => wp_kses_data( (string) $wfdb->querySingle("select msg from $p"."wfStatus where level < 3 order by ctime desc limit 1"))
|
2174 |
);
|
2175 |
$events = array();
|
@@ -2185,6 +2456,13 @@ class wordfence {
|
|
2185 |
} else if($alsoGet == 'perfStats'){
|
2186 |
$newestEventTime = $_POST['otherParams'];
|
2187 |
$events = self::getLog()->getPerfStats($newestEventTime);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2188 |
}
|
2189 |
/*
|
2190 |
$longest = 0;
|
@@ -2201,10 +2479,11 @@ class wordfence {
|
|
2201 |
public static function ajax_activityLogUpdate_callback(){
|
2202 |
$issues = new wfIssues();
|
2203 |
return array(
|
2204 |
-
'ok'
|
2205 |
-
'items'
|
2206 |
-
'currentScanID'
|
2207 |
-
)
|
|
|
2208 |
}
|
2209 |
public static function ajax_updateAlertEmail_callback(){
|
2210 |
$email = trim($_POST['email']);
|
@@ -2229,7 +2508,7 @@ class wordfence {
|
|
2229 |
continue;
|
2230 |
}
|
2231 |
$file = $issue['data']['file'];
|
2232 |
-
$localFile = ABSPATH . '/' .
|
2233 |
$localFile = realpath($localFile);
|
2234 |
if(strpos($localFile, ABSPATH) !== 0){
|
2235 |
$errors[] = "An invalid file was requested: " . wp_kses($file, array());
|
@@ -2313,7 +2592,7 @@ class wordfence {
|
|
2313 |
return array('errorMsg' => "Could not delete file because that issue does not appear to be a file related issue.");
|
2314 |
}
|
2315 |
$file = $issue['data']['file'];
|
2316 |
-
$localFile = ABSPATH . '/' .
|
2317 |
$localFile = realpath($localFile);
|
2318 |
if(strpos($localFile, ABSPATH) !== 0){
|
2319 |
return array('errorMsg' => "An invalid file was requested for deletion.");
|
@@ -2364,6 +2643,36 @@ class wordfence {
|
|
2364 |
return array('errorMsg' => "Could not remove the option " . esc_html($issue['data']['option_name']) . ". The error was: " . esc_html($wpdb->last_error));
|
2365 |
}
|
2366 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2367 |
public static function ajax_restoreFile_callback(){
|
2368 |
$issueID = intval($_POST['issueID']);
|
2369 |
$wfIssues = new wfIssues();
|
@@ -2654,14 +2963,14 @@ class wordfence {
|
|
2654 |
} else {
|
2655 |
return array('ok' => 1); //fail silently
|
2656 |
}
|
2657 |
-
}
|
2658 |
public static function ajax_passwdLoadJobs_callback(){
|
2659 |
if(! wfAPI::SSLEnabled()){ return array('ok' => 1); } //If user hits start passwd audit they will get a helpful message. We don't want an error popping up for every ajax call if SSL is not supported.
|
2660 |
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
|
2661 |
try {
|
2662 |
$res = $api->call('password_load_jobs', array(), array(), true);
|
2663 |
} catch(Exception $e){
|
2664 |
-
return array('errorMsg' => "Could not load password audit jobs: " . $e);
|
2665 |
}
|
2666 |
if(is_array($res) && $res['ok']){
|
2667 |
return array(
|
@@ -2724,7 +3033,7 @@ class wordfence {
|
|
2724 |
//End logging
|
2725 |
|
2726 |
|
2727 |
-
if(! ($wfFunc == 'diff' || $wfFunc == 'view' || $wfFunc == 'viewOption' || $wfFunc == 'sysinfo' || $wfFunc == 'cronview' || $wfFunc == 'dbview' || $wfFunc == 'conntest' || $wfFunc == 'unknownFiles' || $wfFunc == 'IPTraf' || $wfFunc == 'viewActivityLog' || $wfFunc == 'testmem' || $wfFunc == 'testtime' || $wfFunc == 'download')){
|
2728 |
return;
|
2729 |
}
|
2730 |
if(! wfUtils::isAdmin()){
|
@@ -2762,6 +3071,8 @@ class wordfence {
|
|
2762 |
self::wfFunc_testtime();
|
2763 |
} else if($wfFunc == 'download'){
|
2764 |
self::wfFunc_download();
|
|
|
|
|
2765 |
}
|
2766 |
exit(0);
|
2767 |
}
|
@@ -2845,10 +3156,12 @@ wfscr.src = url;
|
|
2845 |
EOL;
|
2846 |
}
|
2847 |
public static function wfLogHumanHeader(){
|
2848 |
-
|
2849 |
-
|
2850 |
-
|
2851 |
-
|
|
|
|
|
2852 |
<script type="text/javascript">
|
2853 |
(function(url){
|
2854 |
if(/(?:Chrome\/26\.0\.1410\.63 Safari\/537\.31|WordfenceTestMonBot)/.test(navigator.userAgent)){ return; }
|
@@ -2883,6 +3196,7 @@ EOL;
|
|
2883 |
})('$URL');
|
2884 |
</script>
|
2885 |
HTML;
|
|
|
2886 |
}
|
2887 |
public static function shutdownAction(){
|
2888 |
}
|
@@ -3022,10 +3336,88 @@ HTML;
|
|
3022 |
exit;
|
3023 |
}
|
3024 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3025 |
public static function initAction(){
|
3026 |
if(wfConfig::liveTrafficEnabled() && (! wfConfig::get('disableCookies', false)) ){
|
3027 |
self::setCookie();
|
3028 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3029 |
}
|
3030 |
private static function setCookie(){
|
3031 |
$cookieName = 'wfvt_' . crc32(site_url());
|
@@ -3051,6 +3443,9 @@ HTML;
|
|
3051 |
'welcomeClosed', 'startTourAgain', 'downgradeLicense', 'addTwoFactor', 'twoFacActivate', 'twoFacDel',
|
3052 |
'loadTwoFactor', 'loadAvgSitePerf', 'sendTestEmail', 'addCacheExclusion', 'removeCacheExclusion',
|
3053 |
'loadCacheExclusions', 'email_summary_email_address_debug', 'unblockNetwork', 'permanentlyBlockAllIPs',
|
|
|
|
|
|
|
3054 |
) as $func){
|
3055 |
add_action('wp_ajax_wordfence_' . $func, 'wordfence::ajaxReceiver');
|
3056 |
}
|
@@ -3078,6 +3473,40 @@ HTML;
|
|
3078 |
self::setupAdminVars();
|
3079 |
}
|
3080 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3081 |
}
|
3082 |
private static function setupAdminVars(){
|
3083 |
$updateInt = wfConfig::get('actUpdateInterval', 2);
|
@@ -3157,8 +3586,27 @@ HTML;
|
|
3157 |
}
|
3158 |
}
|
3159 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3160 |
add_submenu_page("Wordfence", "Scan", "Scan", "activate_plugins", "Wordfence", 'wordfence::menu_scan');
|
3161 |
add_menu_page('Wordfence', 'Wordfence', 'activate_plugins', 'Wordfence', 'wordfence::menu_scan', wfUtils::getBaseURL() . 'images/wordfence-logo-16x16.png');
|
|
|
3162 |
add_submenu_page("Wordfence", "Live Traffic", "Live Traffic", "activate_plugins", "WordfenceActivity", 'wordfence::menu_activity');
|
3163 |
/* add_submenu_page('Wordfence', 'Site Performance', 'Site Performance', 'activate_plugins', 'WordfenceSitePerfStats', 'wordfence::menu_sitePerfStats'); */
|
3164 |
add_submenu_page('Wordfence', 'Performance Setup', 'Performance Setup', 'activate_plugins', 'WordfenceSitePerf', 'wordfence::menu_sitePerf');
|
@@ -3171,6 +3619,7 @@ HTML;
|
|
3171 |
add_submenu_page("Wordfence", "Whois Lookup", "Whois Lookup", "activate_plugins", "WordfenceWhois", 'wordfence::menu_whois');
|
3172 |
add_submenu_page("Wordfence", "Advanced Blocking", "Advanced Blocking", "activate_plugins", "WordfenceRangeBlocking", 'wordfence::menu_rangeBlocking');
|
3173 |
add_submenu_page("Wordfence", "Options", "Options", "activate_plugins", "WordfenceSecOpt", 'wordfence::menu_options');
|
|
|
3174 |
}
|
3175 |
public static function menu_options(){
|
3176 |
require 'menu_options.php';
|
@@ -3203,7 +3652,246 @@ HTML;
|
|
3203 |
public static function menu_rangeBlocking(){
|
3204 |
require 'menu_rangeBlocking.php';
|
3205 |
}
|
3206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3207 |
echo self::cachingWarning("W3 Total Cache");
|
3208 |
}
|
3209 |
public static function liveTrafficSuperCacheWarning(){
|
@@ -3212,7 +3900,20 @@ HTML;
|
|
3212 |
public static function cachingWarning($plugin){
|
3213 |
return '<div id="wordfenceConfigWarning" class="error fade"><p><strong>The Wordfence Live Traffic feature has been disabled because you have ' . $plugin . ' active which is not compatible with Wordfence Live Traffic.</strong> If you want to reenable Wordfence Live Traffic, you need to deactivate ' . $plugin . ' and then go to the Wordfence options page and reenable Live Traffic there. Wordfence does work with ' . $plugin . ', however Live Traffic will be disabled and the Wordfence firewall will also count less hits per visitor because of the ' . $plugin . ' caching function. All other functions should work correctly.</p></div>';
|
3214 |
}
|
3215 |
-
public static function
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3216 |
require 'menu_activity.php';
|
3217 |
}
|
3218 |
public static function menu_scan(){
|
@@ -3240,15 +3941,16 @@ HTML;
|
|
3240 |
}
|
3241 |
}
|
3242 |
}
|
3243 |
-
|
3244 |
-
{
|
|
|
|
|
|
|
|
|
3245 |
global $wp_version;
|
3246 |
-
|
3247 |
-
if ($version === null) {
|
3248 |
-
$version = wp_hash($wp_version . WORDFENCE_VERSION);
|
3249 |
-
}
|
3250 |
-
return preg_replace("/([&;\?]ver)=[0-9\.]+/", "$1={$version}", $url);
|
3251 |
}
|
|
|
3252 |
public static function genFilter($gen, $type){
|
3253 |
if(wfConfig::get('other_hideWPVersion')){
|
3254 |
return '';
|
@@ -3555,9 +4257,10 @@ HTML;
|
|
3555 |
AND blockedTime + %d > UNIX_TIMESTAMP()', $blockedTime));
|
3556 |
break;
|
3557 |
}
|
|
|
3558 |
if ($IPs && is_array($IPs)) {
|
3559 |
foreach ($IPs as $IP) {
|
3560 |
-
|
3561 |
}
|
3562 |
}
|
3563 |
switch ($type) {
|
@@ -3573,27 +4276,136 @@ HTML;
|
|
3573 |
return array('ok' => 1);
|
3574 |
}
|
3575 |
|
3576 |
-
|
3577 |
/**
|
3578 |
-
* Modify the query to prevent username enumeration.
|
3579 |
-
*
|
3580 |
-
* @param array $query_vars
|
3581 |
* @return array
|
3582 |
*/
|
3583 |
-
public static function
|
3584 |
-
|
3585 |
-
|
3586 |
-
|
3587 |
-
|
3588 |
-
|
3589 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3590 |
) {
|
3591 |
$query_vars['author'] = -1;
|
3592 |
}
|
3593 |
return $query_vars;
|
3594 |
}
|
3595 |
|
3596 |
-
|
3597 |
/**
|
3598 |
* @param WP_Upgrader $updater
|
3599 |
* @param array $hook_extra
|
@@ -3603,5 +4415,909 @@ HTML;
|
|
3603 |
wfUtils::hideReadme();
|
3604 |
}
|
3605 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3606 |
}
|
|
|
|
|
|
|
|
|
3607 |
?>
|
19 |
require_once 'wfUpdateCheck.php';
|
20 |
require_once 'wfActivityReport.php';
|
21 |
require_once 'wfHelperBin.php';
|
22 |
+
require_once 'wfDiagnostic.php';
|
23 |
|
24 |
class wordfence {
|
25 |
public static $printStatus = false;
|
76 |
// Remove cron for email summary
|
77 |
wfActivityReport::clearCronJobs();
|
78 |
|
79 |
+
// Remove the admin user list so it can be regenerated if Wordfence is reactivated.
|
80 |
+
wfConfig::set_ser('adminUserList', false);
|
81 |
+
|
82 |
wfConfig::clearDiskCache();
|
83 |
+
|
84 |
+
if (!WFWAF_SUBDIRECTORY_INSTALL) {
|
85 |
+
try {
|
86 |
+
wfWAF::getInstance()->getStorageEngine()->setConfig('wafDisabled', true);
|
87 |
+
} catch (wfWAFStorageFileException $e) {
|
88 |
+
error_log($e->getMessage());
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
if(wfConfig::get('deleteTablesOnDeact')){
|
93 |
$schema = new wfSchema();
|
94 |
$schema->dropAll();
|
95 |
foreach(array('wordfence_version', 'wordfenceActivated') as $opt){
|
96 |
delete_option($opt);
|
97 |
}
|
98 |
+
|
99 |
+
if (!WFWAF_SUBDIRECTORY_INSTALL) {
|
100 |
+
try {
|
101 |
+
if (WFWAF_AUTO_PREPEND) {
|
102 |
+
$helper = new wfWAFAutoPrependHelper();
|
103 |
+
if ($helper->uninstall()) {
|
104 |
+
wfWAF::getInstance()->uninstall();
|
105 |
+
}
|
106 |
+
} else {
|
107 |
+
wfWAF::getInstance()->uninstall();
|
108 |
+
}
|
109 |
+
} catch (wfWAFStorageFileException $e) {
|
110 |
+
error_log($e->getMessage());
|
111 |
+
}
|
112 |
+
}
|
113 |
}
|
114 |
}
|
115 |
public static function hourlyCron(){
|
153 |
$len = strlen($resp['data']);
|
154 |
$reason = "WFSN: Blocked by Wordfence Security Network";
|
155 |
$wfdb->queryWrite("delete from $p"."wfBlocks where wfsn=1 and permanent=0");
|
156 |
+
$log = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
|
157 |
if($len > 0 && $len % 16 == 0){
|
158 |
for($i = 0; $i < $len; $i += 16){
|
159 |
$ip_bin = substr($resp['data'], $i, 16);
|
160 |
$IPStr = wfUtils::inet_ntop($ip_bin);
|
161 |
+
if(! $log->isWhitelisted($IPStr)){
|
162 |
+
$log->blockIP($IPStr, $reason, true);
|
163 |
}
|
164 |
}
|
165 |
}
|
237 |
// So if we do a once a day truncate to be safe, we'll only potentially lose the hour right before the truncate.
|
238 |
// Worth it to clean out the table completely once a day.
|
239 |
|
240 |
+
self::trimWfHits();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
/*
|
242 |
$count6 = $wfdb->querySingle("select count(*) as cnt from $p"."wfPerfLog");
|
243 |
if($count6 > 20000){
|
436 |
wfUtils::hideReadme();
|
437 |
}
|
438 |
|
439 |
+
$colsFor610 = array(
|
440 |
+
'attackLogTime' => '`attackLogTime` double(17,6) unsigned NOT NULL AFTER `id`',
|
441 |
+
'statusCode' => '`statusCode` int(11) NOT NULL DEFAULT 0 AFTER `jsRun`',
|
442 |
+
'action' => "`action` varchar(64) NOT NULL DEFAULT '' AFTER `UA`",
|
443 |
+
'actionDescription' => '`actionDescription` text AFTER `action`',
|
444 |
+
'actionData' => '`actionData` text AFTER `actionDescription`',
|
445 |
+
);
|
446 |
+
|
447 |
+
$hitTable = $wpdb->base_prefix . 'wfHits';
|
448 |
+
foreach ($colsFor610 as $col => $colDefintion) {
|
449 |
+
$count = $wpdb->get_col($wpdb->prepare(<<<SQL
|
450 |
+
SELECT * FROM information_schema.COLUMNS
|
451 |
+
WHERE TABLE_SCHEMA=DATABASE()
|
452 |
+
AND COLUMN_NAME=%s
|
453 |
+
AND TABLE_NAME=%s
|
454 |
+
SQL
|
455 |
+
, $col, $hitTable));
|
456 |
+
if (!$count) {
|
457 |
+
$wpdb->query("ALTER TABLE $hitTable ADD COLUMN $colDefintion");
|
458 |
+
}
|
459 |
+
}
|
460 |
+
|
461 |
+
$has404 = $wpdb->get_col($wpdb->prepare(<<<SQL
|
462 |
+
SELECT * FROM information_schema.COLUMNS
|
463 |
+
WHERE TABLE_SCHEMA=DATABASE()
|
464 |
+
AND COLUMN_NAME='is404'
|
465 |
+
AND TABLE_NAME=%s
|
466 |
+
SQL
|
467 |
+
, $hitTable));
|
468 |
+
if ($has404) {
|
469 |
+
$wpdb->query(<<<SQL
|
470 |
+
UPDATE $hitTable
|
471 |
+
SET statusCode= CASE
|
472 |
+
WHEN is404=1 THEN 404
|
473 |
+
ELSE 200
|
474 |
+
END
|
475 |
+
SQL
|
476 |
+
);
|
477 |
+
|
478 |
+
$wpdb->query("ALTER TABLE $hitTable DROP COLUMN `is404`");
|
479 |
+
}
|
480 |
+
|
481 |
+
$loginsTable = "{$wpdb->base_prefix}wfLogins";
|
482 |
+
$hasHitID = $wpdb->get_col($wpdb->prepare(<<<SQL
|
483 |
+
SELECT * FROM information_schema.COLUMNS
|
484 |
+
WHERE TABLE_SCHEMA=DATABASE()
|
485 |
+
AND COLUMN_NAME='hitID'
|
486 |
+
AND TABLE_NAME=%s
|
487 |
+
SQL
|
488 |
+
, $loginsTable));
|
489 |
+
if (!$hasHitID) {
|
490 |
+
$wpdb->query("ALTER TABLE $loginsTable ADD COLUMN hitID int(11) DEFAULT NULL AFTER `id`, ADD INDEX(hitID)");
|
491 |
+
}
|
492 |
+
|
493 |
+
if (!WFWAF_SUBDIRECTORY_INSTALL) {
|
494 |
+
try {
|
495 |
+
wfWAF::getInstance()->getStorageEngine()->setConfig('wafDisabled', false);
|
496 |
+
} catch (wfWAFStorageFileException $e) {
|
497 |
+
error_log($e);
|
498 |
+
}
|
499 |
+
}
|
500 |
+
|
501 |
//Must be the final line
|
502 |
}
|
503 |
private static function doEarlyAccessLogging(){
|
504 |
$wfLog = self::getLog();
|
505 |
if($wfLog->logHitOK()){
|
506 |
+
$request = $wfLog->getCurrentRequest();
|
507 |
+
|
508 |
+
if(is_404()){
|
509 |
+
if ($request) {
|
510 |
+
$request->statusCode = 404;
|
511 |
+
}
|
512 |
$wfLog->logLeechAndBlock('404');
|
513 |
} else {
|
514 |
$wfLog->logLeechAndBlock('hit');
|
515 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
516 |
}
|
517 |
}
|
518 |
public static function initProtection(){
|
519 |
+
self::getLog()->initLogRequest();
|
520 |
if(preg_match('/\/wp\-admin\/admin\-ajax\.php/', $_SERVER['REQUEST_URI'])){
|
521 |
if(
|
522 |
(isset($_GET['action']) && $_GET['action'] == 'revslider_show_image' && isset($_GET['img']) && preg_match('/\.php$/i', $_GET['img'])) ||
|
528 |
}
|
529 |
}
|
530 |
public static function install_actions(){
|
531 |
+
register_activation_hook(WORDFENCE_FCPATH, 'wordfence::installPlugin');
|
532 |
+
register_deactivation_hook(WORDFENCE_FCPATH, 'wordfence::uninstallPlugin');
|
|
|
|
|
|
|
533 |
|
534 |
$versionInOptions = get_option('wordfence_version', false);
|
535 |
if( (! $versionInOptions) || version_compare(WORDFENCE_VERSION, $versionInOptions, '>')){
|
536 |
//Either there is no version in options or the version in options is greater and we need to run the upgrade
|
537 |
self::runInstall();
|
538 |
}
|
539 |
+
|
540 |
+
self::initProtection();
|
541 |
+
|
542 |
//These access wfConfig::get('apiKey') and will fail if runInstall hasn't executed.
|
543 |
wfCache::setupCaching();
|
544 |
|
643 |
}
|
644 |
|
645 |
add_action('request', 'wordfence::preventAuthorNScans');
|
646 |
+
add_action('password_reset', 'wordfence::actionPasswordReset');
|
647 |
+
|
648 |
+
$adminUsers = new wfAdminUserMonitor();
|
649 |
+
if ($adminUsers->isEnabled()) {
|
650 |
+
add_action('set_user_role', array($adminUsers, 'updateToUserRole'), 10, 3);
|
651 |
+
add_action('grant_super_admin', array($adminUsers, 'grantSuperAdmin'), 10, 1);
|
652 |
+
add_action('revoke_super_admin', array($adminUsers, 'revokeSuperAdmin'), 10, 1);
|
653 |
+
} else if (wfConfig::get_ser('adminUserList', false)) {
|
654 |
+
// reset this in the event it's disabled or the network is too large
|
655 |
+
wfConfig::set_ser('adminUserList', false);
|
656 |
+
}
|
657 |
+
|
658 |
+
if (!self::getLog()->getCurrentRequest()->jsRun && wfConfig::liveTrafficEnabled()) {
|
659 |
+
add_action('wp_head', 'wordfence::wfLogHumanHeader');
|
660 |
+
add_action('login_head', 'wordfence::wfLogHumanHeader');
|
661 |
+
}
|
662 |
+
|
663 |
+
add_action('wordfence_processAttackData', 'wordfence::processAttackData');
|
664 |
+
if (!empty($_GET['wordfence_syncAttackData']) && get_site_option('wordfence_syncingAttackData') <= time() - 60) {
|
665 |
+
ignore_user_abort(true);
|
666 |
+
update_site_option('wordfence_syncingAttackData', time());
|
667 |
+
add_action('init', 'wordfence::syncAttackData');
|
668 |
+
}
|
669 |
+
|
670 |
+
if (wfConfig::get('other_hideWPVersion')) {
|
671 |
+
add_filter('update_feedback', 'wordfence::restoreReadmeForUpgrade');
|
672 |
+
}
|
673 |
+
|
674 |
}
|
675 |
/*
|
676 |
public static function cronAddSchedules($schedules){
|
722 |
die(json_encode(array('ok' => 1)));
|
723 |
}
|
724 |
public static function ajax_logHuman_callback(){
|
725 |
+
self::getLog()->canLogHit = false;
|
726 |
$browscap = new wfBrowscap();
|
727 |
$UA = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
|
728 |
$isCrawler = false;
|
740 |
header("Content-Length: 0");
|
741 |
header("X-Robots-Tag: noindex");
|
742 |
if (!$isCrawler) {
|
743 |
+
setcookie('wordfence_verifiedHuman', self::getLog()->getVerifiedHumanCookieValue($UA, wfUtils::getIP()), time() + 86400, '/');
|
744 |
}
|
745 |
}
|
746 |
flush();
|
841 |
}
|
842 |
public static function lostPasswordPost(){
|
843 |
$IP = wfUtils::getIP();
|
844 |
+
if ($request = self::getLog()->getCurrentRequest()) {
|
845 |
+
$request->action = 'lostPassword';
|
846 |
+
$request->save();
|
847 |
+
}
|
848 |
if(self::getLog()->isWhitelisted($IP)){
|
849 |
return;
|
850 |
}
|
889 |
public static function isLockedOut($IP){
|
890 |
return self::getLog()->isIPLockedOut($IP);
|
891 |
}
|
892 |
+
|
893 |
+
public static function veryFirstAction() {
|
894 |
+
/** @var wpdb $wpdb ; */
|
895 |
+
global $wpdb;
|
896 |
+
|
897 |
$wfFunc = isset($_GET['_wfsf']) ? @$_GET['_wfsf'] : false;
|
898 |
if($wfFunc == 'unlockEmail'){
|
899 |
if(! wp_verify_nonce(@$_POST['nonce'], 'wf-form')){
|
983 |
}
|
984 |
}
|
985 |
|
986 |
+
// Sync the WAF data with the database.
|
987 |
+
if (!WFWAF_SUBDIRECTORY_INSTALL && $waf = wfWAF::getInstance()) {
|
988 |
+
try {
|
989 |
+
$configDefaults = array(
|
990 |
+
'apiKey' => wfConfig::get('apiKey'),
|
991 |
+
'isPaid' => wfConfig::get('isPaid'),
|
992 |
+
'siteURL' => site_url(),
|
993 |
+
'homeURL' => home_url(),
|
994 |
+
'whitelistedIPs' => (string) wfConfig::get('whitelisted'),
|
995 |
+
'howGetIPs' => (string) wfConfig::get('howGetIPs'),
|
996 |
+
);
|
997 |
+
foreach ($configDefaults as $key => $value) {
|
998 |
+
$waf->getStorageEngine()->setConfig($key, $value);
|
999 |
+
}
|
1000 |
+
|
1001 |
+
$lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$wpdb->base_prefix}wfHits");
|
1002 |
+
if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) {
|
1003 |
+
if (get_site_option('wordfence_syncingAttackData') <= time() - 60) {
|
1004 |
+
wp_remote_post(add_query_arg('wordfence_syncAttackData', microtime(true), home_url('/')), array(
|
1005 |
+
'timeout' => 0.01,
|
1006 |
+
'blocking' => false,
|
1007 |
+
'sslverify' => apply_filters('https_local_ssl_verify', false)
|
1008 |
+
));
|
1009 |
+
}
|
1010 |
+
}
|
1011 |
+
|
1012 |
+
if ($waf instanceof wfWAFWordPress && ($learningModeAttackException = $waf->getLearningModeAttackException())) {
|
1013 |
+
$log = self::getLog();
|
1014 |
+
$log->initLogRequest();
|
1015 |
+
$request = $log->getCurrentRequest();
|
1016 |
+
$request->action = 'learned:waf';
|
1017 |
+
$request->attackLogTime = microtime(true);
|
1018 |
+
|
1019 |
+
$ruleIDs = array();
|
1020 |
+
/** @var wfWAFRule $failedRule */
|
1021 |
+
foreach ($learningModeAttackException->getFailedRules() as $failedRule) {
|
1022 |
+
$ruleIDs[] = $failedRule->getRuleID();
|
1023 |
+
}
|
1024 |
+
|
1025 |
+
$actionData = array(
|
1026 |
+
'learningMode' => 1,
|
1027 |
+
'failedRules' => $ruleIDs,
|
1028 |
+
'paramKey' => $learningModeAttackException->getParamKey(),
|
1029 |
+
'paramValue' => $learningModeAttackException->getParamValue(),
|
1030 |
+
);
|
1031 |
+
if ($ruleIDs && $ruleIDs[0]) {
|
1032 |
+
$rule = $waf->getRule($ruleIDs[0]);
|
1033 |
+
if ($rule) {
|
1034 |
+
$request->actionDescription = $rule->getDescription();
|
1035 |
+
$actionData['category'] = $rule->getCategory();
|
1036 |
+
$actionData['ssl'] = $waf->getRequest()->getProtocol() === 'https';
|
1037 |
+
$actionData['fullRequest'] = base64_encode($waf->getRequest());
|
1038 |
+
}
|
1039 |
+
}
|
1040 |
+
$request->actionData = wfRequestModel::serializeActionData($actionData);
|
1041 |
+
register_shutdown_function(array($request, 'save'));
|
1042 |
+
|
1043 |
+
self::scheduleSendAttackData();
|
1044 |
+
}
|
1045 |
+
} catch (wfWAFStorageFileException $e) {
|
1046 |
+
// We don't have anywhere to write files in this scenario.
|
1047 |
+
}
|
1048 |
+
}
|
1049 |
+
|
1050 |
if(wfConfig::get('firewallEnabled')){
|
1051 |
$wfLog = self::getLog();
|
1052 |
$wfLog->firewallBadIPs();
|
1087 |
}
|
1088 |
}
|
1089 |
}
|
1090 |
+
|
1091 |
public static function loginAction($username){
|
1092 |
if(sizeof($_POST) < 1){ return; } //only execute if login form is posted
|
1093 |
if(! $username){ return; }
|
1215 |
if(wfConfig::get('other_WFNet') && is_wp_error($authUser) && ($authUser->get_error_code() == 'invalid_username' || $authUser->get_error_code() == 'incorrect_password') ){
|
1216 |
if($maxBlockTime = self::wfsnIsBlocked($IP, 'brute')){
|
1217 |
self::getLog()->blockIP($IP, "Blocked by Wordfence Security Network", true, false, $maxBlockTime);
|
1218 |
+
$secsToGo = wfConfig::get('blockedTime');
|
1219 |
+
self::getLog()->do503($secsToGo, "Blocked by Wordfence Security Network");
|
1220 |
}
|
1221 |
|
1222 |
}
|
1223 |
if($secEnabled){
|
1224 |
if(is_wp_error($authUser) && $authUser->get_error_code() == 'invalid_username'){
|
1225 |
if($blacklist = wfConfig::get('loginSec_userBlacklist')){
|
1226 |
+
$users = explode("\n", wfUtils::cleanupOneEntryPerLine($blacklist));
|
1227 |
foreach($users as $user){
|
1228 |
if(strtolower($username) == strtolower($user)){
|
1229 |
self::getLog()->blockIP($IP, "Blocked by login security setting.");
|
1306 |
if(is_object($userDat)){
|
1307 |
self::getLog()->logLogin('logout', 0, $userDat->user_login);
|
1308 |
}
|
1309 |
+
// Unset the roadblock cookie
|
1310 |
+
if (!WFWAF_SUBDIRECTORY_INSTALL) {
|
1311 |
+
wfUtils::setcookie(wfWAF::getInstance()->getAuthCookieName(), ' ', time() - (86400 * 365), '/', null, null, true);
|
1312 |
+
}
|
1313 |
}
|
1314 |
public static function loginInitAction(){
|
1315 |
if(self::isLockedOut(wfUtils::getIP())){
|
1379 |
return array('errorMsg' => wp_kses($e->getMessage(), array()));
|
1380 |
}
|
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();
|
1388 |
+
$findReplace = array(
|
1389 |
+
'<th ' => '<th style="text-align:left;background-color:#222;color:#fff;"',
|
1390 |
+
'<th>' => '<th style="text-align:left;background-color:#222;color:#fff;">',
|
1391 |
+
'<td class="success"' => '<td style="font-weight:bold;color:#008c10;" class="success"',
|
1392 |
+
'<td class="error"' => '<td style="font-weight:bold;color:#d0514c;" class="error"',
|
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(){
|
1400 |
$result = wp_mail($_POST['email'], "Wordfence Test Email", "This is a test email from " . site_url() . ".\nThe IP address that requested this was: " . wfUtils::getIP());
|
1401 |
$result = $result ? 'True' : 'False';
|
1991 |
public static function ajax_saveConfig_callback(){
|
1992 |
$reload = '';
|
1993 |
$opts = wfConfig::parseOptions();
|
1994 |
+
|
1995 |
+
// These are now on the Diagnostics page, so they aren't sent across.
|
1996 |
+
foreach (self::$diagnosticParams as $param) {
|
1997 |
+
$opts[$param] = wfConfig::get($param);
|
1998 |
+
}
|
1999 |
+
|
2000 |
$emails = array();
|
2001 |
foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['alertEmails'])) as $email){
|
2002 |
if(strlen($email) > 0){
|
2018 |
$opts['alertEmails'] = '';
|
2019 |
}
|
2020 |
$opts['scan_exclude'] = wfUtils::cleanupOneEntryPerLine($opts['scan_exclude']);
|
2021 |
+
|
2022 |
+
foreach (explode("\n", $opts['scan_include_extra']) as $regex) {
|
2023 |
+
if (@preg_match("/$regex/", "") === FALSE) {
|
2024 |
+
return array('errorMsg' => "\"" . esc_html($regex). "\" is not a valid regular expression");
|
2025 |
+
}
|
2026 |
+
}
|
2027 |
+
|
2028 |
$whiteIPs = array();
|
2029 |
foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['whitelisted'])) as $whiteIP){
|
2030 |
if(strlen($whiteIP) > 0){
|
2059 |
}
|
2060 |
}
|
2061 |
}
|
2062 |
+
$opts['loginSec_userBlacklist'] = wfUtils::cleanupOneEntryPerLine($opts['loginSec_userBlacklist']);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2063 |
|
2064 |
$opts['apiKey'] = trim($opts['apiKey']);
|
2065 |
if($opts['apiKey'] && (! preg_match('/^[a-fA-F0-9]+$/', $opts['apiKey'])) ){ //User entered something but it's garbage.
|
2112 |
$regenerateHtaccess = true;
|
2113 |
}
|
2114 |
|
2115 |
+
if (!is_numeric($opts['liveTraf_maxRows'])) {
|
2116 |
+
return array(
|
2117 |
+
'errorMsg' => 'Please enter a number for the amount of Live Traffic data to store.',
|
2118 |
+
);
|
2119 |
+
}
|
2120 |
+
|
2121 |
+
|
2122 |
foreach($opts as $key => $val){
|
2123 |
if($key != 'apiKey'){ //Don't save API key yet
|
2124 |
wfConfig::set($key, $val);
|
2200 |
}
|
2201 |
return array('ok' => 1, 'reload' => $reload, 'paidKeyMsg' => $paidKeyMsg );
|
2202 |
}
|
2203 |
+
|
2204 |
+
public static $diagnosticParams = array(
|
2205 |
+
'addCacheComment',
|
2206 |
+
'debugOn',
|
2207 |
+
'startScansRemotely',
|
2208 |
+
'ssl_verify',
|
2209 |
+
'disableConfigCaching',
|
2210 |
+
'betaThreatDefenseFeed',
|
2211 |
+
);
|
2212 |
+
|
2213 |
+
public static function ajax_saveDebuggingConfig_callback() {
|
2214 |
+
foreach (self::$diagnosticParams as $param) {
|
2215 |
+
wfConfig::set($param, array_key_exists($param, $_POST) ? '1' : '0');
|
2216 |
+
}
|
2217 |
+
try {
|
2218 |
+
wfWAF::getInstance()->getStorageEngine()
|
2219 |
+
->setConfig('betaThreatDefenseFeed', wfConfig::get('betaThreatDefenseFeed'));
|
2220 |
+
} catch (wfWAFStorageFileException $e) {
|
2221 |
+
error_log($e->getMessage());
|
2222 |
+
}
|
2223 |
+
return array('ok' => 1, 'reload' => false, 'paidKeyMsg' => '');
|
2224 |
+
}
|
2225 |
+
|
2226 |
+
|
2227 |
+
public static function ajax_hideFileHtaccess_callback(){
|
2228 |
+
$issues = new wfIssues();
|
2229 |
+
$issue = $issues->getIssueByID($_POST['issueID']);
|
2230 |
+
if (!$issue) {
|
2231 |
+
return array('cerrorMsg' => "We could not find that issue in our database.");
|
2232 |
+
}
|
2233 |
+
|
2234 |
+
$file = $issue['data']['file'];
|
2235 |
+
$localFile = ABSPATH . '/' . $file;
|
2236 |
+
$localFile = realpath($localFile);
|
2237 |
+
if (strpos($localFile, ABSPATH) !== 0) {
|
2238 |
+
return array('cerrorMsg' => "An invalid file was requested for deletion.");
|
2239 |
+
}
|
2240 |
+
$localFile = substr($localFile, strlen(ABSPATH));
|
2241 |
+
|
2242 |
+
if (!wfUtils::htaccessAppend("<Files \"{$localFile}\">
|
2243 |
+
<IfModule mod_authz_core.c>
|
2244 |
+
Require all denied
|
2245 |
+
</IfModule>
|
2246 |
+
<IfModule !mod_authz_core.c>
|
2247 |
+
Order deny,allow
|
2248 |
+
Deny from all
|
2249 |
+
</IfModule>
|
2250 |
+
</Files>")) {
|
2251 |
+
return array('cerrorMsg' => "You don't have permission to repair .htaccess. You need to either fix the file manually using FTP or change the file permissions and ownership so that your web server has write access to repair the file.");
|
2252 |
+
}
|
2253 |
+
$issues->updateIssue($_POST['issueID'], 'delete');
|
2254 |
+
return array('ok' => 1);
|
2255 |
+
}
|
2256 |
public static function ajax_clearAllBlocked_callback(){
|
2257 |
$op = $_POST['op'];
|
2258 |
$wfLog = self::getLog();
|
2276 |
}
|
2277 |
public static function ajax_permBlockIP_callback(){
|
2278 |
$IP = $_POST['IP'];
|
2279 |
+
$log = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
|
2280 |
+
$log->blockIP($IP, "Manual permanent block by admin", false, true);
|
2281 |
return array('ok' => 1);
|
2282 |
}
|
2283 |
public static function ajax_loadStaticPanel_callback(){
|
2358 |
public static function ajax_blockIP_callback(){
|
2359 |
$IP = trim($_POST['IP']);
|
2360 |
$perm = (isset($_POST['perm']) && $_POST['perm'] == '1') ? true : false;
|
2361 |
+
$log = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
|
2362 |
if (!wfUtils::isValidIP($IP)) {
|
2363 |
return array('err' => 1, 'errorMsg' => "Please enter a valid IP address to block.");
|
2364 |
}
|
2365 |
if ($IP == wfUtils::getIP()) {
|
2366 |
return array('err' => 1, 'errorMsg' => "You can't block your own IP address.");
|
2367 |
}
|
2368 |
+
if ($log->isWhitelisted($IP)) {
|
2369 |
return array('err' => 1, 'errorMsg' => "The IP address " . wp_kses($IP, array()) . " is whitelisted and can't be blocked or it is in a range of internal IP addresses that Wordfence does not block. You can remove this IP from the whitelist on the Wordfence options page.");
|
2370 |
}
|
2371 |
if (wfConfig::get('neverBlockBG') != 'treatAsOtherCrawlers') { //Either neverBlockVerified or neverBlockUA is selected which means the user doesn't want to block google
|
2373 |
return array('err' => 1, 'errorMsg' => "The IP address you're trying to block belongs to Google. Your options are currently set to not block these crawlers. Change this in Wordfence options if you want to manually block Google.");
|
2374 |
}
|
2375 |
}
|
2376 |
+
$log->blockIP($IP, $_POST['reason'], false, $perm);
|
2377 |
return array('ok' => 1);
|
2378 |
}
|
2379 |
public static function ajax_reverseLookup_callback(){
|
2440 |
$serverTime = $wfdb->querySingle("select unix_timestamp()");
|
2441 |
$jsonData = array(
|
2442 |
'serverTime' => $serverTime,
|
2443 |
+
'serverMicrotime' => microtime(true),
|
2444 |
'msg' => wp_kses_data( (string) $wfdb->querySingle("select msg from $p"."wfStatus where level < 3 order by ctime desc limit 1"))
|
2445 |
);
|
2446 |
$events = array();
|
2456 |
} else if($alsoGet == 'perfStats'){
|
2457 |
$newestEventTime = $_POST['otherParams'];
|
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'])) {
|
2464 |
+
$jsonData['sql'] = $results['sql'];
|
2465 |
+
}
|
2466 |
}
|
2467 |
/*
|
2468 |
$longest = 0;
|
2479 |
public static function ajax_activityLogUpdate_callback(){
|
2480 |
$issues = new wfIssues();
|
2481 |
return array(
|
2482 |
+
'ok' => 1,
|
2483 |
+
'items' => self::getLog()->getStatusEvents($_POST['lastctime']),
|
2484 |
+
'currentScanID' => $issues->getScanTime(),
|
2485 |
+
'signatureUpdateTime' => wfConfig::get('signatureUpdateTime'),
|
2486 |
+
);
|
2487 |
}
|
2488 |
public static function ajax_updateAlertEmail_callback(){
|
2489 |
$email = trim($_POST['email']);
|
2508 |
continue;
|
2509 |
}
|
2510 |
$file = $issue['data']['file'];
|
2511 |
+
$localFile = ABSPATH . '/' . $file;
|
2512 |
$localFile = realpath($localFile);
|
2513 |
if(strpos($localFile, ABSPATH) !== 0){
|
2514 |
$errors[] = "An invalid file was requested: " . wp_kses($file, array());
|
2592 |
return array('errorMsg' => "Could not delete file because that issue does not appear to be a file related issue.");
|
2593 |
}
|
2594 |
$file = $issue['data']['file'];
|
2595 |
+
$localFile = ABSPATH . '/' . $file;
|
2596 |
$localFile = realpath($localFile);
|
2597 |
if(strpos($localFile, ABSPATH) !== 0){
|
2598 |
return array('errorMsg' => "An invalid file was requested for deletion.");
|
2643 |
return array('errorMsg' => "Could not remove the option " . esc_html($issue['data']['option_name']) . ". The error was: " . esc_html($wpdb->last_error));
|
2644 |
}
|
2645 |
}
|
2646 |
+
public static function ajax_fixFPD_callback(){
|
2647 |
+
$issues = new wfIssues();
|
2648 |
+
$issue = $issues->getIssueByID($_POST['issueID']);
|
2649 |
+
if (!$issue) {
|
2650 |
+
return array('cerrorMsg' => "We could not find that issue in our database.");
|
2651 |
+
}
|
2652 |
+
|
2653 |
+
$htaccess = ABSPATH . '/.htaccess';
|
2654 |
+
$change = "<IfModule mod_php5.c>\n\tphp_value display_errors 0\n</IfModule>";
|
2655 |
+
$content = "";
|
2656 |
+
if (file_exists($htaccess)) {
|
2657 |
+
$content = file_get_contents($htaccess);
|
2658 |
+
}
|
2659 |
+
|
2660 |
+
if (@file_put_contents($htaccess, trim($content . "\n" . $change), LOCK_EX) === false) {
|
2661 |
+
return array('cerrorMsg' => "You don't have permission to repair .htaccess. You need to either fix the file
|
2662 |
+
manually using FTP or change the file permissions and ownership so that your web server has write access to repair the file.");
|
2663 |
+
}
|
2664 |
+
if (wfScanEngine::testForFullPathDisclosure()) {
|
2665 |
+
// Didn't fix it, so revert the changes and return an error
|
2666 |
+
file_put_contents($htaccess, $content, LOCK_EX);
|
2667 |
+
return array(
|
2668 |
+
'cerrorMsg' => "Modifying the .htaccess file did not resolve the issue, so the original .htaccess file
|
2669 |
+
was restored. You can fix this manually by setting <code>display_errors</code> to <code>Off</code> in
|
2670 |
+
your php.ini if your site is on a VPS or dedicated server that you control.",
|
2671 |
+
);
|
2672 |
+
}
|
2673 |
+
$issues->updateIssue($_POST['issueID'], 'delete');
|
2674 |
+
return array('ok' => 1);
|
2675 |
+
}
|
2676 |
public static function ajax_restoreFile_callback(){
|
2677 |
$issueID = intval($_POST['issueID']);
|
2678 |
$wfIssues = new wfIssues();
|
2963 |
} else {
|
2964 |
return array('ok' => 1); //fail silently
|
2965 |
}
|
2966 |
+
}
|
2967 |
public static function ajax_passwdLoadJobs_callback(){
|
2968 |
if(! wfAPI::SSLEnabled()){ return array('ok' => 1); } //If user hits start passwd audit they will get a helpful message. We don't want an error popping up for every ajax call if SSL is not supported.
|
2969 |
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
|
2970 |
try {
|
2971 |
$res = $api->call('password_load_jobs', array(), array(), true);
|
2972 |
} catch(Exception $e){
|
2973 |
+
return array('errorMsg' => "Could not load password audit jobs: " . $e);
|
2974 |
}
|
2975 |
if(is_array($res) && $res['ok']){
|
2976 |
return array(
|
3033 |
//End logging
|
3034 |
|
3035 |
|
3036 |
+
if(! ($wfFunc == 'diff' || $wfFunc == 'view' || $wfFunc == 'viewOption' || $wfFunc == 'sysinfo' || $wfFunc == 'cronview' || $wfFunc == 'dbview' || $wfFunc == 'conntest' || $wfFunc == 'unknownFiles' || $wfFunc == 'IPTraf' || $wfFunc == 'viewActivityLog' || $wfFunc == 'testmem' || $wfFunc == 'testtime' || $wfFunc == 'download' || ($wfFunc == 'debugWAF' && WFWAF_DEBUG))){
|
3037 |
return;
|
3038 |
}
|
3039 |
if(! wfUtils::isAdmin()){
|
3071 |
self::wfFunc_testtime();
|
3072 |
} else if($wfFunc == 'download'){
|
3073 |
self::wfFunc_download();
|
3074 |
+
} else if($wfFunc == 'debugWAF' && WFWAF_DEBUG){
|
3075 |
+
self::wfFunc_debugWAF();
|
3076 |
}
|
3077 |
exit(0);
|
3078 |
}
|
3156 |
EOL;
|
3157 |
}
|
3158 |
public static function wfLogHumanHeader(){
|
3159 |
+
self::$hitID = self::getLog()->logHit();
|
3160 |
+
if (self::$hitID) {
|
3161 |
+
$URL = home_url('/?wordfence_logHuman=1&hid=' . wfUtils::encrypt(self::$hitID));
|
3162 |
+
$URL = addslashes(preg_replace('/^https?:/i', '', $URL));
|
3163 |
+
#Load as external script async so we don't slow page down.
|
3164 |
+
echo <<<HTML
|
3165 |
<script type="text/javascript">
|
3166 |
(function(url){
|
3167 |
if(/(?:Chrome\/26\.0\.1410\.63 Safari\/537\.31|WordfenceTestMonBot)/.test(navigator.userAgent)){ return; }
|
3196 |
})('$URL');
|
3197 |
</script>
|
3198 |
HTML;
|
3199 |
+
}
|
3200 |
}
|
3201 |
public static function shutdownAction(){
|
3202 |
}
|
3336 |
exit;
|
3337 |
}
|
3338 |
|
3339 |
+
/**
|
3340 |
+
*
|
3341 |
+
*/
|
3342 |
+
public static function wfFunc_debugWAF() {
|
3343 |
+
$data = array();
|
3344 |
+
if (!empty($_GET['hitid'])) {
|
3345 |
+
$data['hit'] = new wfRequestModel($_GET['hitid']);
|
3346 |
+
if ($data['hit']->actionData) {
|
3347 |
+
$data['hitData'] = (object) wfRequestModel::unserializeActionData($data['hit']->actionData);
|
3348 |
+
}
|
3349 |
+
echo wfView::create('waf/debug', $data);
|
3350 |
+
}
|
3351 |
+
}
|
3352 |
+
|
3353 |
public static function initAction(){
|
3354 |
if(wfConfig::liveTrafficEnabled() && (! wfConfig::get('disableCookies', false)) ){
|
3355 |
self::setCookie();
|
3356 |
}
|
3357 |
+
// This is more of a hurdle, but might stop an automated process.
|
3358 |
+
if (current_user_can('administrator')) {
|
3359 |
+
$adminUsers = new wfAdminUserMonitor();
|
3360 |
+
if ($adminUsers->isEnabled() && !$adminUsers->isAdminUserLogged(get_current_user_id())) {
|
3361 |
+
define('DISALLOW_FILE_MODS', true);
|
3362 |
+
}
|
3363 |
+
}
|
3364 |
+
|
3365 |
+
$currentUserID = get_current_user_id();
|
3366 |
+
$role = wordfence::getCurrentUserRole();
|
3367 |
+
if (!WFWAF_SUBDIRECTORY_INSTALL) {
|
3368 |
+
try {
|
3369 |
+
$authCookie = wfWAF::getInstance()->parseAuthCookie();
|
3370 |
+
if (is_user_logged_in() &&
|
3371 |
+
(
|
3372 |
+
!$authCookie ||
|
3373 |
+
(int) $currentUserID !== (int) $authCookie['userID'] ||
|
3374 |
+
$role !== $authCookie['role']
|
3375 |
+
)
|
3376 |
+
) {
|
3377 |
+
$secureLoggedInCookie = is_ssl() && parse_url(get_option('home'), PHP_URL_SCHEME) === 'https';
|
3378 |
+
|
3379 |
+
wfUtils::setcookie(wfWAF::getInstance()->getAuthCookieName(),
|
3380 |
+
$currentUserID . '|' . $role . '|' .
|
3381 |
+
wfWAF::getInstance()->getAuthCookieValue($currentUserID, $role),
|
3382 |
+
time() + 43200, COOKIEPATH, COOKIE_DOMAIN, $secureLoggedInCookie, true);
|
3383 |
+
}
|
3384 |
+
} catch (wfWAFStorageFileException $e) {
|
3385 |
+
error_log($e->getMessage());
|
3386 |
+
}
|
3387 |
+
}
|
3388 |
+
|
3389 |
+
if (wfConfig::get('other_hideWPVersion')) {
|
3390 |
+
|
3391 |
+
global $wp_version;
|
3392 |
+
global $wp_styles;
|
3393 |
+
|
3394 |
+
if (!($wp_styles instanceof WP_Styles)) {
|
3395 |
+
$wp_styles = new WP_Styles();
|
3396 |
+
}
|
3397 |
+
if ($wp_styles->default_version === $wp_version) {
|
3398 |
+
$wp_styles->default_version = wp_hash($wp_styles->default_version);
|
3399 |
+
}
|
3400 |
+
|
3401 |
+
foreach ($wp_styles->registered as $key => $val) {
|
3402 |
+
if ($wp_styles->registered[$key]->ver === $wp_version) {
|
3403 |
+
$wp_styles->registered[$key]->ver = wp_hash($wp_styles->registered[$key]->ver);
|
3404 |
+
}
|
3405 |
+
}
|
3406 |
+
|
3407 |
+
global $wp_scripts;
|
3408 |
+
if (!($wp_scripts instanceof WP_Scripts)) {
|
3409 |
+
$wp_scripts = new WP_Scripts();
|
3410 |
+
}
|
3411 |
+
if ($wp_scripts->default_version === $wp_version) {
|
3412 |
+
$wp_scripts->default_version = wp_hash($wp_scripts->default_version);
|
3413 |
+
}
|
3414 |
+
|
3415 |
+
foreach ($wp_scripts->registered as $key => $val) {
|
3416 |
+
if ($wp_scripts->registered[$key]->ver === $wp_version) {
|
3417 |
+
$wp_scripts->registered[$key]->ver = wp_hash($wp_scripts->registered[$key]->ver);
|
3418 |
+
}
|
3419 |
+
}
|
3420 |
+
}
|
3421 |
}
|
3422 |
private static function setCookie(){
|
3423 |
$cookieName = 'wfvt_' . crc32(site_url());
|
3443 |
'welcomeClosed', 'startTourAgain', 'downgradeLicense', 'addTwoFactor', 'twoFacActivate', 'twoFacDel',
|
3444 |
'loadTwoFactor', 'loadAvgSitePerf', 'sendTestEmail', 'addCacheExclusion', 'removeCacheExclusion',
|
3445 |
'loadCacheExclusions', 'email_summary_email_address_debug', 'unblockNetwork', 'permanentlyBlockAllIPs',
|
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 |
}
|
3473 |
self::setupAdminVars();
|
3474 |
}
|
3475 |
|
3476 |
+
if (!WFWAF_AUTO_PREPEND || WFWAF_SUBDIRECTORY_INSTALL) {
|
3477 |
+
if (empty($_GET['wafAction'])) {
|
3478 |
+
if (is_multisite()) {
|
3479 |
+
add_action('network_admin_notices', 'wordfence::wafAutoPrependNotice');
|
3480 |
+
} else {
|
3481 |
+
add_action('admin_notices', 'wordfence::wafAutoPrependNotice');
|
3482 |
+
}
|
3483 |
+
}
|
3484 |
+
|
3485 |
+
if (!empty($_GET['wafAction'])) {
|
3486 |
+
switch ($_GET['wafAction']) {
|
3487 |
+
case 'configureAutoPrepend':
|
3488 |
+
if (isset($_REQUEST['serverConfiguration'])) {
|
3489 |
+
check_admin_referer('wfWAFAutoPrepend', 'wfnonce');
|
3490 |
+
$helper = new wfWAFAutoPrependHelper($_REQUEST['serverConfiguration']);
|
3491 |
+
if (!empty($_REQUEST['downloadBackup'])) {
|
3492 |
+
$helper->downloadBackups(!empty($_REQUEST['backupIndex']) ? absint($_REQUEST['backupIndex']) : 0);
|
3493 |
+
}
|
3494 |
+
|
3495 |
+
// $adminURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend');
|
3496 |
+
// request_filesystem_credentials($adminURL);
|
3497 |
+
}
|
3498 |
+
break;
|
3499 |
+
}
|
3500 |
+
}
|
3501 |
+
}
|
3502 |
+
|
3503 |
+
if (!empty($_REQUEST['wafVerify']) && wp_verify_nonce($_REQUEST['wafVerify'], 'wfWAFAutoPrepend')) {
|
3504 |
+
if (is_multisite()) {
|
3505 |
+
add_action('network_admin_notices', 'wordfence::wafAutoPrependVerify');
|
3506 |
+
} else {
|
3507 |
+
add_action('admin_notices', 'wordfence::wafAutoPrependVerify');
|
3508 |
+
}
|
3509 |
+
}
|
3510 |
}
|
3511 |
private static function setupAdminVars(){
|
3512 |
$updateInt = wfConfig::get('actUpdateInterval', 2);
|
3586 |
}
|
3587 |
}
|
3588 |
|
3589 |
+
if (!empty($_GET['page']) && $_GET['page'] === 'WordfenceWAF' && !empty($_GET['wafconfigrebuild']) && !WFWAF_SUBDIRECTORY_INSTALL) {
|
3590 |
+
check_admin_referer('wafconfigrebuild', 'waf-nonce');
|
3591 |
+
|
3592 |
+
$storage = wfWAF::getInstance()->getStorageEngine();
|
3593 |
+
if ($storage instanceof wfWAFStorageFile) {
|
3594 |
+
$configFile = $storage->getConfigFile();
|
3595 |
+
if (@unlink($configFile)) {
|
3596 |
+
if (function_exists('network_admin_url') && is_multisite()) {
|
3597 |
+
$wafMenuURL = network_admin_url('admin.php?page=WordfenceWAF');
|
3598 |
+
} else {
|
3599 |
+
$wafMenuURL = admin_url('admin.php?page=WordfenceWAF');
|
3600 |
+
}
|
3601 |
+
wp_redirect($wafMenuURL);
|
3602 |
+
exit;
|
3603 |
+
}
|
3604 |
+
}
|
3605 |
+
}
|
3606 |
+
|
3607 |
add_submenu_page("Wordfence", "Scan", "Scan", "activate_plugins", "Wordfence", 'wordfence::menu_scan');
|
3608 |
add_menu_page('Wordfence', 'Wordfence', 'activate_plugins', 'Wordfence', 'wordfence::menu_scan', wfUtils::getBaseURL() . 'images/wordfence-logo-16x16.png');
|
3609 |
+
add_submenu_page("Wordfence", "Firewall", "Firewall", "activate_plugins", "WordfenceWAF", 'wordfence::menu_waf');
|
3610 |
add_submenu_page("Wordfence", "Live Traffic", "Live Traffic", "activate_plugins", "WordfenceActivity", 'wordfence::menu_activity');
|
3611 |
/* add_submenu_page('Wordfence', 'Site Performance', 'Site Performance', 'activate_plugins', 'WordfenceSitePerfStats', 'wordfence::menu_sitePerfStats'); */
|
3612 |
add_submenu_page('Wordfence', 'Performance Setup', 'Performance Setup', 'activate_plugins', 'WordfenceSitePerf', 'wordfence::menu_sitePerf');
|
3619 |
add_submenu_page("Wordfence", "Whois Lookup", "Whois Lookup", "activate_plugins", "WordfenceWhois", 'wordfence::menu_whois');
|
3620 |
add_submenu_page("Wordfence", "Advanced Blocking", "Advanced Blocking", "activate_plugins", "WordfenceRangeBlocking", 'wordfence::menu_rangeBlocking');
|
3621 |
add_submenu_page("Wordfence", "Options", "Options", "activate_plugins", "WordfenceSecOpt", 'wordfence::menu_options');
|
3622 |
+
add_submenu_page("Wordfence", "Diagnostics", "Diagnostics", "activate_plugins", "WordfenceDiagnostic", 'wordfence::menu_diagnostic');
|
3623 |
}
|
3624 |
public static function menu_options(){
|
3625 |
require 'menu_options.php';
|
3652 |
public static function menu_rangeBlocking(){
|
3653 |
require 'menu_rangeBlocking.php';
|
3654 |
}
|
3655 |
+
|
3656 |
+
public static function menu_waf() {
|
3657 |
+
global $wp_filesystem;
|
3658 |
+
|
3659 |
+
wp_enqueue_style('wordfence-jquery-ui-css', wfUtils::getBaseURL() . 'css/jquery-ui.min.css', array(), WORDFENCE_VERSION);
|
3660 |
+
wp_enqueue_style('wordfence-jquery-ui-structure-css', wfUtils::getBaseURL() . 'css/jquery-ui.structure.min.css', array(), WORDFENCE_VERSION);
|
3661 |
+
wp_enqueue_style('wordfence-jquery-ui-theme-css', wfUtils::getBaseURL() . 'css/jquery-ui.theme.min.css', array(), WORDFENCE_VERSION);
|
3662 |
+
wp_enqueue_style('wordfence-jquery-ui-timepicker-css', wfUtils::getBaseURL() . 'css/jquery-ui-timepicker-addon.css', array(), WORDFENCE_VERSION);
|
3663 |
+
wp_enqueue_style('wordfence-select2-css', wfUtils::getBaseURL() . 'css/select2.min.css', array(), WORDFENCE_VERSION);
|
3664 |
+
|
3665 |
+
wp_enqueue_script('wordfence-timepicker-js', wfUtils::getBaseURL() . 'js/jquery-ui-timepicker-addon.js', array('jquery', 'jquery-ui-datepicker', 'jquery-ui-slider'), WORDFENCE_VERSION);
|
3666 |
+
wp_enqueue_script('wordfence-select2-js', wfUtils::getBaseURL() . 'js/select2.min.js', array('jquery'), WORDFENCE_VERSION);
|
3667 |
+
|
3668 |
+
try {
|
3669 |
+
$wafData = self::_getWAFData();
|
3670 |
+
} catch (wfWAFStorageFileConfigException $e) {
|
3671 |
+
// We don't have anywhere to write files in this scenario. Let's notify the user to update the permissions.
|
3672 |
+
$wafData = array();
|
3673 |
+
$logPath = str_replace(ABSPATH, '~/', WFWAF_LOG_PATH);
|
3674 |
+
if (function_exists('network_admin_url') && is_multisite()) {
|
3675 |
+
$wafMenuURL = network_admin_url('admin.php?page=WordfenceWAF&wafconfigrebuild=1');
|
3676 |
+
} else {
|
3677 |
+
$wafMenuURL = admin_url('admin.php?page=WordfenceWAF&wafconfigrebuild=1');
|
3678 |
+
}
|
3679 |
+
$wafMenuURL = add_query_arg(array(
|
3680 |
+
'waf-nonce' => wp_create_nonce('wafconfigrebuild'),
|
3681 |
+
), $wafMenuURL);
|
3682 |
+
$storageExceptionMessage = $e->getMessage() . ' <a href="' . esc_url($wafMenuURL) . '">Click here</a> to rebuild the configuration file.';
|
3683 |
+
} catch (wfWAFStorageFileException $e) {
|
3684 |
+
// We don't have anywhere to write files in this scenario. Let's notify the user to update the permissions.
|
3685 |
+
$wafData = array();
|
3686 |
+
$logPath = str_replace(ABSPATH, '~/', WFWAF_LOG_PATH);
|
3687 |
+
$storageExceptionMessage = 'We were unable to write to ' . $logPath . ' which the WAF uses for storage. Please
|
3688 |
+
update permissions on the parent directory so the web server can write to it.';
|
3689 |
+
}
|
3690 |
+
|
3691 |
+
if (!empty($_GET['wafAction'])) {
|
3692 |
+
switch ($_GET['wafAction']) {
|
3693 |
+
case 'configureAutoPrepend':
|
3694 |
+
if (WFWAF_AUTO_PREPEND && !WFWAF_SUBDIRECTORY_INSTALL) {
|
3695 |
+
break;
|
3696 |
+
}
|
3697 |
+
$wfnonce = wp_create_nonce('wfWAFAutoPrepend');
|
3698 |
+
|
3699 |
+
$currentAutoPrependFile = ini_get('auto_prepend_file');
|
3700 |
+
$currentAutoPrepend = !empty($_REQUEST['currentAutoPrepend']) ? $_REQUEST['currentAutoPrepend'] : null;
|
3701 |
+
$adminURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend¤tAutoPrepend=' . $currentAutoPrepend);
|
3702 |
+
if ($currentAutoPrependFile &&
|
3703 |
+
is_file($currentAutoPrependFile) &&
|
3704 |
+
empty($currentAutoPrepend) &&
|
3705 |
+
!WFWAF_SUBDIRECTORY_INSTALL
|
3706 |
+
) {
|
3707 |
+
$wafActionContent = sprintf("<p>The Wordfence Web Application Firewall is designed
|
3708 |
+
to run via a PHP ini setting called <code>auto_prepend_file</code> in order to ensure it runs before any potentially
|
3709 |
+
vulnerable code runs. This PHP setting is currently in use, and is including this file:</p>
|
3710 |
+
|
3711 |
+
<pre class='wf-pre'>%s</pre>
|
3712 |
+
|
3713 |
+
<p>If you don't recognize this file, please <a href='https://wordpress.org/support/plugin/wordfence'>contact us on the
|
3714 |
+
WordPress support forums</a> before proceeding.</p>
|
3715 |
+
|
3716 |
+
<p>You can proceed with the installation and we will include this from within our <code>wordfence-waf.php</code> file
|
3717 |
+
which should maintain compatibility with your site, or you can opt to override the existing PHP setting.</p>
|
3718 |
+
|
3719 |
+
<p>
|
3720 |
+
<a class='button button-primary' href='%s'>Include this file (Recommended)</a>
|
3721 |
+
<a class='button' href='%s'>Override this value</a>
|
3722 |
+
</p>
|
3723 |
+
",
|
3724 |
+
esc_html($currentAutoPrependFile),
|
3725 |
+
esc_url(network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend¤tAutoPrepend=include')),
|
3726 |
+
esc_url(network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend¤tAutoPrepend=override'))
|
3727 |
+
);
|
3728 |
+
break;
|
3729 |
+
} else if (isset($_REQUEST['serverConfiguration'])) {
|
3730 |
+
check_admin_referer('wfWAFAutoPrepend', 'wfnonce');
|
3731 |
+
$allow_relaxed_file_ownership = true;
|
3732 |
+
$helper = new wfWAFAutoPrependHelper($_REQUEST['serverConfiguration'],
|
3733 |
+
$currentAutoPrepend === 'override' ? null : $currentAutoPrependFile);
|
3734 |
+
if (($backups = $helper->getFilesNeededForBackup()) && empty($_REQUEST['confirmedBackup'])) {
|
3735 |
+
$wafActionContent = '<p>Please download a backup copy of the following files before we make the necessary changes:</p>';
|
3736 |
+
$wafActionContent .= '<ul>';
|
3737 |
+
foreach ($backups as $index => $backup) {
|
3738 |
+
$wafActionContent .= '<li><a class="button" onclick="wfWAFConfirmBackup(' . $index . ');" href="' .
|
3739 |
+
esc_url(add_query_arg(array(
|
3740 |
+
'downloadBackup' => 1,
|
3741 |
+
'backupIndex' => $index,
|
3742 |
+
'serverConfiguration' => $helper->getServerConfig(),
|
3743 |
+
'wfnonce' => $wfnonce,
|
3744 |
+
), $adminURL)) . '">Download ' . esc_html(basename($backup)) . '</a></li>';
|
3745 |
+
}
|
3746 |
+
$serverConfig = esc_attr($helper->getServerConfig());
|
3747 |
+
$jsonBackups = json_encode(array_map('basename', $backups));
|
3748 |
+
$adminURL = esc_url($adminURL);
|
3749 |
+
$wafActionContent .= "</ul>
|
3750 |
+
<form action='$adminURL' method='post'>
|
3751 |
+
<input type='hidden' name='wfnonce' value='$wfnonce'>
|
3752 |
+
<input type='hidden' value='$serverConfig' name='serverConfiguration'>
|
3753 |
+
<input type='hidden' value='1' name='confirmedBackup'>
|
3754 |
+
<button id='confirmed-backups' disabled class='button button-primary' type='submit'>Continue</button>
|
3755 |
+
</form>
|
3756 |
+
<script>
|
3757 |
+
var wfWAFBackups = $jsonBackups;
|
3758 |
+
var wfWAFConfirmedBackups = [];
|
3759 |
+
function wfWAFConfirmBackup(index) {
|
3760 |
+
wfWAFBackups[index] = false;
|
3761 |
+
var confirmed = true;
|
3762 |
+
for (var i = 0; i < wfWAFBackups.length; i++) {
|
3763 |
+
if (wfWAFBackups[i] !== false) {
|
3764 |
+
confirmed = false;
|
3765 |
+
}
|
3766 |
+
}
|
3767 |
+
if (confirmed) {
|
3768 |
+
document.getElementById('confirmed-backups').disabled = false;
|
3769 |
+
}
|
3770 |
+
}
|
3771 |
+
</script>";
|
3772 |
+
break;
|
3773 |
+
}
|
3774 |
+
|
3775 |
+
ob_start();
|
3776 |
+
if (false === ($credentials = request_filesystem_credentials($adminURL, '', false, ABSPATH,
|
3777 |
+
array('version', 'locale'), $allow_relaxed_file_ownership))
|
3778 |
+
) {
|
3779 |
+
$wafActionContent = ob_get_clean();
|
3780 |
+
break;
|
3781 |
+
}
|
3782 |
+
|
3783 |
+
if (!WP_Filesystem($credentials, ABSPATH, $allow_relaxed_file_ownership)) {
|
3784 |
+
// Failed to connect, Error and request again
|
3785 |
+
request_filesystem_credentials($adminURL, '', true, ABSPATH, array('version', 'locale'),
|
3786 |
+
$allow_relaxed_file_ownership);
|
3787 |
+
$wafActionContent = ob_get_clean();
|
3788 |
+
break;
|
3789 |
+
}
|
3790 |
+
|
3791 |
+
if ($wp_filesystem->errors->get_error_code()) {
|
3792 |
+
foreach ($wp_filesystem->errors->get_error_messages() as $message)
|
3793 |
+
show_message($message);
|
3794 |
+
$wafActionContent = ob_get_clean();
|
3795 |
+
break;
|
3796 |
+
}
|
3797 |
+
ob_end_clean();
|
3798 |
+
|
3799 |
+
try {
|
3800 |
+
$helper->performInstallation($wp_filesystem);
|
3801 |
+
|
3802 |
+
$adminURL = json_encode(esc_url_raw(network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend&wafVerify='
|
3803 |
+
. $wfnonce . '¤tAutoPrepend=' . $currentAutoPrepend)));
|
3804 |
+
$wafActionContent = "<script>
|
3805 |
+
document.location.href=$adminURL;
|
3806 |
+
</script>";
|
3807 |
+
break;
|
3808 |
+
} catch (wfWAFAutoPrependHelperException $e) {
|
3809 |
+
$wafActionContent = "<p>" . $e->getMessage() . "</p>";
|
3810 |
+
break;
|
3811 |
+
}
|
3812 |
+
}
|
3813 |
+
|
3814 |
+
|
3815 |
+
$bootstrap = self::getWAFBootstrapPath();
|
3816 |
+
|
3817 |
+
// Auto populate drop down with server configuration
|
3818 |
+
// If no preconfiguration routine exists, output instructions for manual configuration
|
3819 |
+
$serverInfo = wfWebServerInfo::createFromEnvironment();
|
3820 |
+
|
3821 |
+
$dropdown = array(
|
3822 |
+
array("apache-mod_php", 'Apache + mod_php', $serverInfo->isApacheModPHP()),
|
3823 |
+
array("apache-suphp", 'Apache + suPHP', $serverInfo->isApacheSuPHP()),
|
3824 |
+
array("cgi", 'Apache + CGI/FastCGI', $serverInfo->isApache() &&
|
3825 |
+
!$serverInfo->isApacheSuPHP() &&
|
3826 |
+
($serverInfo->isCGI() || $serverInfo->isFastCGI())),
|
3827 |
+
array("litespeed", 'LiteSpeed', $serverInfo->isLiteSpeed()),
|
3828 |
+
array("nginx", 'NGINX', $serverInfo->isNGINX()),
|
3829 |
+
// array("iis", 'Windows (IIS)', $serverInfo->isIIS()),
|
3830 |
+
);
|
3831 |
+
$wafActionContent = '<p>To be as secure as possible, the Wordfence Web Application Firewall is designed
|
3832 |
+
to run via a PHP ini setting called <code>auto_prepend_file</code> in order to ensure it runs before any potentially
|
3833 |
+
vulnerable code runs.</p>
|
3834 |
+
|
3835 |
+
<div class="wf-notice"><strong>NOTE:</strong> If you have separate WordPress installations with Wordfence installed within a subdirectory of
|
3836 |
+
this site, it is recommended that you perform the Firewall installation procedure on those sites before this one.</div>
|
3837 |
+
';
|
3838 |
+
$hasRecommendedOption = false;
|
3839 |
+
$wafPrependOptions = '';
|
3840 |
+
foreach ($dropdown as $option) {
|
3841 |
+
list($optionValue, $optionText, $selected) = $option;
|
3842 |
+
$wafPrependOptions .= "<option value=\"$optionValue\"" . ($selected ? ' selected' : '')
|
3843 |
+
. ">$optionText" . ($selected ? ' (recommended based on our tests)' : '') . "</option>\n";
|
3844 |
+
if ($selected) {
|
3845 |
+
$hasRecommendedOption = true;
|
3846 |
+
}
|
3847 |
+
}
|
3848 |
+
|
3849 |
+
if (!$hasRecommendedOption) {
|
3850 |
+
$wafActionContent .= "<p>If you know your web server's configuration, please select it from the
|
3851 |
+
list below:</p>";
|
3852 |
+
} else {
|
3853 |
+
$wafActionContent .= "<p>We've preselected your server configuration based on our tests, but if
|
3854 |
+
you know your web server's configuration, please select it now.</p>";
|
3855 |
+
}
|
3856 |
+
|
3857 |
+
$wafActionContent .= "
|
3858 |
+
<form action='$adminURL' method='post'>
|
3859 |
+
<input type='hidden' name='wfnonce' value='$wfnonce'>
|
3860 |
+
<select name='serverConfiguration'>
|
3861 |
+
$wafPrependOptions
|
3862 |
+
</select>
|
3863 |
+
<button class='button button-primary' type='submit'>Continue</button>
|
3864 |
+
</form>
|
3865 |
+
";
|
3866 |
+
|
3867 |
+
$wafActionContent .= "
|
3868 |
+
<h3>Alternate method:</h3>
|
3869 |
+
<p>We've also included instructions to manually perform the change if you are using a web server other than what is listed in the drop-down, or if file permissions prevent this change.</p>";
|
3870 |
+
|
3871 |
+
$additionally = 'You';
|
3872 |
+
if (!self::checkAndCreateBootstrap()) {
|
3873 |
+
$wafActionContent .= "<p>You will need create the following file in your WordPress root:</p>
|
3874 |
+
<pre class='wf-pre'>" . esc_html(self::getWAFBootstrapPath()) . "</pre>
|
3875 |
+
<p>You can create the file and set the permissions to allow WordPress to write to it, or you can add the code yourself:</p>
|
3876 |
+
<pre class='wf-pre'>" . esc_textarea(self::getWAFBootstrapContent()) . "</pre>";
|
3877 |
+
|
3878 |
+
$additionally = 'Additionally, you';
|
3879 |
+
}
|
3880 |
+
$wafActionContent .= "<p>{$additionally} will need to append the following code to your <code>php.ini</code>:</p>
|
3881 |
+
<pre class='wf-pre'>auto_prepend_file = '" . esc_textarea($bootstrap) . "'</pre>";
|
3882 |
+
|
3883 |
+
|
3884 |
+
$wafActionContent = sprintf('<div style="margin: 20px 0;">%s</div>', $wafActionContent);
|
3885 |
+
break;
|
3886 |
+
|
3887 |
+
case '':
|
3888 |
+
break;
|
3889 |
+
}
|
3890 |
+
}
|
3891 |
+
require 'menu_waf.php';
|
3892 |
+
}
|
3893 |
+
|
3894 |
+
public static function liveTrafficW3TCWarning() {
|
3895 |
echo self::cachingWarning("W3 Total Cache");
|
3896 |
}
|
3897 |
public static function liveTrafficSuperCacheWarning(){
|
3900 |
public static function cachingWarning($plugin){
|
3901 |
return '<div id="wordfenceConfigWarning" class="error fade"><p><strong>The Wordfence Live Traffic feature has been disabled because you have ' . $plugin . ' active which is not compatible with Wordfence Live Traffic.</strong> If you want to reenable Wordfence Live Traffic, you need to deactivate ' . $plugin . ' and then go to the Wordfence options page and reenable Live Traffic there. Wordfence does work with ' . $plugin . ', however Live Traffic will be disabled and the Wordfence firewall will also count less hits per visitor because of the ' . $plugin . ' caching function. All other functions should work correctly.</p></div>';
|
3902 |
}
|
3903 |
+
public static function menu_diagnostic(){
|
3904 |
+
$emailForm = true;
|
3905 |
+
require 'menu_diagnostic.php';
|
3906 |
+
}
|
3907 |
+
public static function menu_activity() {
|
3908 |
+
wp_enqueue_style('wordfence-jquery-ui-css', wfUtils::getBaseURL() . 'css/jquery-ui.min.css', array(), WORDFENCE_VERSION);
|
3909 |
+
wp_enqueue_style('wordfence-jquery-ui-structure-css', wfUtils::getBaseURL() . 'css/jquery-ui.structure.min.css', array(), WORDFENCE_VERSION);
|
3910 |
+
wp_enqueue_style('wordfence-jquery-ui-theme-css', wfUtils::getBaseURL() . 'css/jquery-ui.theme.min.css', array(), WORDFENCE_VERSION);
|
3911 |
+
wp_enqueue_style('wordfence-jquery-ui-timepicker-css', wfUtils::getBaseURL() . 'css/jquery-ui-timepicker-addon.css', array(), WORDFENCE_VERSION);
|
3912 |
+
|
3913 |
+
wp_enqueue_script('wordfence-timepicker-js', wfUtils::getBaseURL() . 'js/jquery-ui-timepicker-addon.js', array('jquery', 'jquery-ui-datepicker', 'jquery-ui-slider'), WORDFENCE_VERSION);
|
3914 |
+
wp_enqueue_script('wordfence-knockout-js', wfUtils::getBaseURL() . 'js/knockout-3.3.0.js', array(), WORDFENCE_VERSION);
|
3915 |
+
wp_enqueue_script('wordfence-live-traffic-js', wfUtils::getBaseURL() . 'js/admin.liveTraffic.js', array('jquery'), WORDFENCE_VERSION);
|
3916 |
+
|
3917 |
require 'menu_activity.php';
|
3918 |
}
|
3919 |
public static function menu_scan(){
|
3941 |
}
|
3942 |
}
|
3943 |
}
|
3944 |
+
|
3945 |
+
public static function replaceVersion($url) {
|
3946 |
+
return preg_replace_callback("/([&;\?]ver)=(.+?)(?:&|$)/", "wordfence::replaceVersionCallback", $url);
|
3947 |
+
}
|
3948 |
+
|
3949 |
+
public static function replaceVersionCallback($matches) {
|
3950 |
global $wp_version;
|
3951 |
+
return $matches[1] . '=' . ($wp_version === $matches[2] ? wp_hash($matches[2]) : $matches[2]);
|
|
|
|
|
|
|
|
|
3952 |
}
|
3953 |
+
|
3954 |
public static function genFilter($gen, $type){
|
3955 |
if(wfConfig::get('other_hideWPVersion')){
|
3956 |
return '';
|
4257 |
AND blockedTime + %d > UNIX_TIMESTAMP()', $blockedTime));
|
4258 |
break;
|
4259 |
}
|
4260 |
+
$log = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
|
4261 |
if ($IPs && is_array($IPs)) {
|
4262 |
foreach ($IPs as $IP) {
|
4263 |
+
$log->blockIP(wfUtils::inet_ntop($IP), $reason, false, true);
|
4264 |
}
|
4265 |
}
|
4266 |
switch ($type) {
|
4276 |
return array('ok' => 1);
|
4277 |
}
|
4278 |
|
|
|
4279 |
/**
|
|
|
|
|
|
|
4280 |
* @return array
|
4281 |
*/
|
4282 |
+
public static function ajax_deleteAdminUser_callback() {
|
4283 |
+
/** @var wpdb $wpdb */
|
4284 |
+
global $wpdb;
|
4285 |
+
$issueID = absint(!empty($_POST['issueID']) ? $_POST['issueID'] : 0);
|
4286 |
+
$wfIssues = new wfIssues();
|
4287 |
+
$issue = $wfIssues->getIssueByID($issueID);
|
4288 |
+
if (!$issue) {
|
4289 |
+
return array('errorMsg' => "We could not find that issue in our database.");
|
4290 |
+
}
|
4291 |
+
$data = $issue['data'];
|
4292 |
+
if (empty($data['userID'])) {
|
4293 |
+
return array('errorMsg' => "We could not find that user in the database.");
|
4294 |
+
}
|
4295 |
+
$user = new WP_User($data['userID']);
|
4296 |
+
if (!$user->exists()) {
|
4297 |
+
return array('errorMsg' => "We could not find that user in the database.");
|
4298 |
+
}
|
4299 |
+
$userLogin = $user->user_login;
|
4300 |
+
if (is_multisite() && strcasecmp($user->user_email, get_site_option('admin_email')) === 0) {
|
4301 |
+
return array('errorMsg' => "This user's email is the network admin email. It will need to be changed before deleting this user.");
|
4302 |
+
}
|
4303 |
+
if (is_multisite()) {
|
4304 |
+
revoke_super_admin($data['userID']);
|
4305 |
+
}
|
4306 |
+
wp_delete_user($data['userID']);
|
4307 |
+
if (is_multisite()) {
|
4308 |
+
$wpdb->delete($wpdb->users, array('ID' => $data['userID']));
|
4309 |
+
}
|
4310 |
+
$wfIssues->deleteIssue($issueID);
|
4311 |
+
|
4312 |
+
return array(
|
4313 |
+
'ok' => 1,
|
4314 |
+
'user_login' => $userLogin,
|
4315 |
+
);
|
4316 |
+
}
|
4317 |
+
|
4318 |
+
public static function ajax_revokeAdminUser_callback() {
|
4319 |
+
$issueID = absint(!empty($_POST['issueID']) ? $_POST['issueID'] : 0);
|
4320 |
+
$wfIssues = new wfIssues();
|
4321 |
+
$issue = $wfIssues->getIssueByID($issueID);
|
4322 |
+
if (!$issue) {
|
4323 |
+
return array('errorMsg' => "We could not find that issue in our database.");
|
4324 |
+
}
|
4325 |
+
$data = $issue['data'];
|
4326 |
+
if (empty($data['userID'])) {
|
4327 |
+
return array('errorMsg' => "We could not find that user in the database.");
|
4328 |
+
}
|
4329 |
+
$user = new WP_User($data['userID']);
|
4330 |
+
$userLogin = $user->user_login;
|
4331 |
+
wp_revoke_user($data['userID']);
|
4332 |
+
if (is_multisite()) {
|
4333 |
+
revoke_super_admin($data['userID']);
|
4334 |
+
}
|
4335 |
+
|
4336 |
+
$wfIssues->deleteIssue($issueID);
|
4337 |
+
|
4338 |
+
return array(
|
4339 |
+
'ok' => 1,
|
4340 |
+
'user_login' => $userLogin,
|
4341 |
+
);
|
4342 |
+
}
|
4343 |
+
|
4344 |
+
/**
|
4345 |
+
*
|
4346 |
+
*/
|
4347 |
+
public static function ajax_disableDirectoryListing_callback() {
|
4348 |
+
$issueID = absint($_POST['issueID']);
|
4349 |
+
$wfIssues = new wfIssues();
|
4350 |
+
$issue = $wfIssues->getIssueByID($issueID);
|
4351 |
+
if (!$issue) {
|
4352 |
+
return array(
|
4353 |
+
'err' => 1,
|
4354 |
+
'errorMsg' => "We could not find that issue in our database.",
|
4355 |
+
);
|
4356 |
+
}
|
4357 |
+
$wfIssues->deleteIssue($issueID);
|
4358 |
+
|
4359 |
+
$htaccessPath = wfCache::getHtaccessPath();
|
4360 |
+
if (!$htaccessPath) {
|
4361 |
+
return array(
|
4362 |
+
'err' => 1,
|
4363 |
+
'errorMsg' => "Wordfence could not find your .htaccess file.",
|
4364 |
+
);
|
4365 |
+
}
|
4366 |
+
|
4367 |
+
$fileContents = file_get_contents($htaccessPath);
|
4368 |
+
if (file_put_contents($htaccessPath, "# Added by Wordfence " . date('r') . "\nOptions -Indexes\n\n" . $fileContents, LOCK_EX)) {
|
4369 |
+
$uploadPaths = wp_upload_dir();
|
4370 |
+
if (!wfScanEngine::isDirectoryListingEnabled($uploadPaths['baseurl'])) {
|
4371 |
+
return array(
|
4372 |
+
'ok' => 1,
|
4373 |
+
);
|
4374 |
+
} else {
|
4375 |
+
// Revert any changes done to .htaccess
|
4376 |
+
file_put_contents($htaccessPath, $fileContents, LOCK_EX);
|
4377 |
+
return array(
|
4378 |
+
'err' => 1,
|
4379 |
+
'errorMsg' => "Updating the .htaccess did not fix the issue. You may need to add <code>Options -Indexes</code>
|
4380 |
+
to your httpd.conf if using Apache, or find documentation on how to disable directory listing for your web server.",
|
4381 |
+
);
|
4382 |
+
}
|
4383 |
+
}
|
4384 |
+
return array(
|
4385 |
+
'err' => 1,
|
4386 |
+
'errorMsg' => "There was an error writing to your .htaccess file.",
|
4387 |
+
);
|
4388 |
+
}
|
4389 |
+
|
4390 |
+
/**
|
4391 |
+
* Modify the query to prevent username enumeration.
|
4392 |
+
*
|
4393 |
+
* @param array $query_vars
|
4394 |
+
* @return array
|
4395 |
+
*/
|
4396 |
+
public static function preventAuthorNScans($query_vars) {
|
4397 |
+
if (wfConfig::get('loginSec_disableAuthorScan') && !is_admin() &&
|
4398 |
+
!empty($query_vars['author']) && is_numeric(preg_replace('/[^0-9]/', '', $query_vars['author'])) &&
|
4399 |
+
(
|
4400 |
+
(isset($_GET['author']) && is_numeric(preg_replace('/[^0-9]/', '', $_GET['author']))) ||
|
4401 |
+
(isset($_POST['author']) && is_numeric(preg_replace('/[^0-9]/', '', $_POST['author'])))
|
4402 |
+
)
|
4403 |
) {
|
4404 |
$query_vars['author'] = -1;
|
4405 |
}
|
4406 |
return $query_vars;
|
4407 |
}
|
4408 |
|
|
|
4409 |
/**
|
4410 |
* @param WP_Upgrader $updater
|
4411 |
* @param array $hook_extra
|
4415 |
wfUtils::hideReadme();
|
4416 |
}
|
4417 |
}
|
4418 |
+
|
4419 |
+
public static function ajax_saveWAFConfig_callback() {
|
4420 |
+
if (isset($_POST['wafConfigAction'])) {
|
4421 |
+
switch ($_POST['wafConfigAction']) {
|
4422 |
+
case 'config':
|
4423 |
+
if (!empty($_POST['wafStatus'])) {
|
4424 |
+
if ($_POST['wafStatus'] == 'learning-mode' && !empty($_POST['learningModeGracePeriodEnabled'])) {
|
4425 |
+
$gracePeriodEnd = strtotime(isset($_POST['learningModeGracePeriod']) ? $_POST['learningModeGracePeriod'] : '');
|
4426 |
+
if ($gracePeriodEnd > time()) {
|
4427 |
+
wfWAF::getInstance()->getStorageEngine()->setConfig('learningModeGracePeriodEnabled', 1);
|
4428 |
+
wfWAF::getInstance()->getStorageEngine()->setConfig('learningModeGracePeriod', $gracePeriodEnd);
|
4429 |
+
} else {
|
4430 |
+
return array(
|
4431 |
+
'err' => 1,
|
4432 |
+
'errorMsg' => "The grace period end time must be in the future.",
|
4433 |
+
);
|
4434 |
+
}
|
4435 |
+
} else {
|
4436 |
+
wfWAF::getInstance()->getStorageEngine()->setConfig('learningModeGracePeriodEnabled', 0);
|
4437 |
+
wfWAF::getInstance()->getStorageEngine()->unsetConfig('learningModeGracePeriod');
|
4438 |
+
}
|
4439 |
+
wfWAF::getInstance()->getStorageEngine()->setConfig('wafStatus', $_POST['wafStatus']);
|
4440 |
+
}
|
4441 |
+
|
4442 |
+
break;
|
4443 |
+
|
4444 |
+
case 'addWhitelist':
|
4445 |
+
if (isset($_POST['whitelistedPath']) && isset($_POST['whitelistedParam'])) {
|
4446 |
+
$path = stripslashes($_POST['whitelistedPath']);
|
4447 |
+
$paramKey = stripslashes($_POST['whitelistedParam']);
|
4448 |
+
if (!$path || !$paramKey) {
|
4449 |
+
break;
|
4450 |
+
}
|
4451 |
+
$data = array(
|
4452 |
+
'timestamp' => time(),
|
4453 |
+
'description' => 'Whitelisted via Firewall Options page',
|
4454 |
+
'ip' => wfUtils::getIP(),
|
4455 |
+
'disabled' => empty($_POST['whitelistedEnabled']),
|
4456 |
+
);
|
4457 |
+
if (function_exists('get_current_user_id')) {
|
4458 |
+
$data['userID'] = get_current_user_id();
|
4459 |
+
}
|
4460 |
+
wfWAF::getInstance()->whitelistRuleForParam($path, $paramKey, 'all', $data);
|
4461 |
+
}
|
4462 |
+
break;
|
4463 |
+
|
4464 |
+
case 'replaceWhitelist':
|
4465 |
+
if (
|
4466 |
+
!empty($_POST['oldWhitelistedPath']) && !empty($_POST['oldWhitelistedParam']) &&
|
4467 |
+
!empty($_POST['newWhitelistedPath']) && !empty($_POST['newWhitelistedParam'])
|
4468 |
+
) {
|
4469 |
+
$oldWhitelistedPath = stripslashes($_POST['oldWhitelistedPath']);
|
4470 |
+
$oldWhitelistedParam = stripslashes($_POST['oldWhitelistedParam']);
|
4471 |
+
|
4472 |
+
$newWhitelistedPath = stripslashes($_POST['newWhitelistedPath']);
|
4473 |
+
$newWhitelistedParam = stripslashes($_POST['newWhitelistedParam']);
|
4474 |
+
|
4475 |
+
$savedWhitelistedURLParams = (array) wfWAF::getInstance()->getStorageEngine()->getConfig('whitelistedURLParams');
|
4476 |
+
// These are already base64'd
|
4477 |
+
$oldKey = $oldWhitelistedPath . '|' . $oldWhitelistedParam;
|
4478 |
+
$newKey = base64_encode($newWhitelistedPath) . '|' . base64_encode($newWhitelistedParam);
|
4479 |
+
try {
|
4480 |
+
$savedWhitelistedURLParams = wfUtils::arrayReplaceKey($savedWhitelistedURLParams, $oldKey, $newKey);
|
4481 |
+
} catch (Exception $e) {
|
4482 |
+
error_log("Caught exception from 'wfUtils::arrayReplaceKey' with message: " . $e->getMessage());
|
4483 |
+
}
|
4484 |
+
wfWAF::getInstance()->getStorageEngine()->setConfig('whitelistedURLParams', $savedWhitelistedURLParams);
|
4485 |
+
}
|
4486 |
+
break;
|
4487 |
+
|
4488 |
+
case 'deleteWhitelist':
|
4489 |
+
if (
|
4490 |
+
isset($_POST['deletedWhitelistedPath']) && is_string($_POST['deletedWhitelistedPath']) &&
|
4491 |
+
isset($_POST['deletedWhitelistedParam']) && is_string($_POST['deletedWhitelistedParam'])
|
4492 |
+
) {
|
4493 |
+
$deletedWhitelistedPath = stripslashes($_POST['deletedWhitelistedPath']);
|
4494 |
+
$deletedWhitelistedParam = stripslashes($_POST['deletedWhitelistedParam']);
|
4495 |
+
$savedWhitelistedURLParams = (array) wfWAF::getInstance()->getStorageEngine()->getConfig('whitelistedURLParams');
|
4496 |
+
$key = base64_encode($deletedWhitelistedPath) . '|' . base64_encode($deletedWhitelistedParam);
|
4497 |
+
unset($savedWhitelistedURLParams[$key]);
|
4498 |
+
wfWAF::getInstance()->getStorageEngine()->setConfig('whitelistedURLParams', $savedWhitelistedURLParams);
|
4499 |
+
}
|
4500 |
+
break;
|
4501 |
+
|
4502 |
+
case 'enableWhitelist':
|
4503 |
+
if (isset($_POST['whitelistedPath']) && isset($_POST['whitelistedParam'])) {
|
4504 |
+
$path = stripslashes($_POST['whitelistedPath']);
|
4505 |
+
$paramKey = stripslashes($_POST['whitelistedParam']);
|
4506 |
+
if (!$path || !$paramKey) {
|
4507 |
+
break;
|
4508 |
+
}
|
4509 |
+
$enabled = !empty($_POST['whitelistedEnabled']);
|
4510 |
+
|
4511 |
+
$savedWhitelistedURLParams = (array) wfWAF::getInstance()->getStorageEngine()->getConfig('whitelistedURLParams');
|
4512 |
+
$key = $path . '|' . $paramKey;
|
4513 |
+
if (array_key_exists($key, $savedWhitelistedURLParams) && is_array($savedWhitelistedURLParams[$key])) {
|
4514 |
+
foreach ($savedWhitelistedURLParams[$key] as $ruleID => $data) {
|
4515 |
+
$savedWhitelistedURLParams[$key][$ruleID]['disabled'] = !$enabled;
|
4516 |
+
}
|
4517 |
+
}
|
4518 |
+
wfWAF::getInstance()->getStorageEngine()->setConfig('whitelistedURLParams', $savedWhitelistedURLParams);
|
4519 |
+
}
|
4520 |
+
break;
|
4521 |
+
|
4522 |
+
case 'enableRule':
|
4523 |
+
$ruleEnabled = !empty($_POST['ruleEnabled']);
|
4524 |
+
$ruleID = !empty($_POST['ruleID']) ? (int) $_POST['ruleID'] : false;
|
4525 |
+
if ($ruleID) {
|
4526 |
+
$disabledRules = (array) wfWAF::getInstance()->getStorageEngine()->getConfig('disabledRules');
|
4527 |
+
if ($ruleEnabled) {
|
4528 |
+
unset($disabledRules[$ruleID]);
|
4529 |
+
} else {
|
4530 |
+
$disabledRules[$ruleID] = true;
|
4531 |
+
}
|
4532 |
+
wfWAF::getInstance()->getStorageEngine()->setConfig('disabledRules', $disabledRules);
|
4533 |
+
}
|
4534 |
+
break;
|
4535 |
+
}
|
4536 |
+
}
|
4537 |
+
|
4538 |
+
return array(
|
4539 |
+
'success' => true,
|
4540 |
+
'data' => self::_getWAFData(),
|
4541 |
+
);
|
4542 |
+
}
|
4543 |
+
|
4544 |
+
public static function ajax_updateWAFRules_callback() {
|
4545 |
+
$event = new wfWAFCronFetchRulesEvent(time() - 2);
|
4546 |
+
$event->setWaf(wfWAF::getInstance());
|
4547 |
+
$event->fire();
|
4548 |
+
|
4549 |
+
return self::_getWAFData();
|
4550 |
+
}
|
4551 |
+
|
4552 |
+
public static function ajax_loadLiveTraffic_callback() {
|
4553 |
+
$return = array();
|
4554 |
+
|
4555 |
+
$filters = new wfLiveTrafficQueryFilterCollection();
|
4556 |
+
$query = new wfLiveTrafficQuery(self::getLog());
|
4557 |
+
$query->setFilters($filters);
|
4558 |
+
if (array_key_exists('groupby', $_REQUEST)) {
|
4559 |
+
$param = $_REQUEST['groupby'];
|
4560 |
+
if ($param === 'type') {
|
4561 |
+
$param = 'jsRun';
|
4562 |
+
}
|
4563 |
+
$query->setGroupBy(new wfLiveTrafficQueryGroupBy($query, $param));
|
4564 |
+
}
|
4565 |
+
$query->setLimit(isset($_REQUEST['limit']) ? absint($_REQUEST['limit']) : 20);
|
4566 |
+
$query->setOffset(isset($_REQUEST['offset']) ? absint($_REQUEST['offset']) : 0);
|
4567 |
+
|
4568 |
+
if (!empty($_REQUEST['since'])) {
|
4569 |
+
$query->setStartDate($_REQUEST['since']);
|
4570 |
+
} else if (!empty($_REQUEST['startDate'])) {
|
4571 |
+
$query->setStartDate(is_numeric($_REQUEST['startDate']) ? $_REQUEST['startDate'] : strtotime($_REQUEST['startDate']));
|
4572 |
+
}
|
4573 |
+
|
4574 |
+
if (!empty($_REQUEST['endDate'])) {
|
4575 |
+
$query->setEndDate(is_numeric($_REQUEST['endDate']) ? $_REQUEST['endDate'] : strtotime($_REQUEST['endDate']));
|
4576 |
+
}
|
4577 |
+
|
4578 |
+
if (
|
4579 |
+
array_key_exists('param', $_REQUEST) && is_array($_REQUEST['param']) &&
|
4580 |
+
array_key_exists('operator', $_REQUEST) && is_array($_REQUEST['operator']) &&
|
4581 |
+
array_key_exists('value', $_REQUEST) && is_array($_REQUEST['value'])
|
4582 |
+
) {
|
4583 |
+
for ($i = 0; $i < count($_REQUEST['param']); $i++) {
|
4584 |
+
if (
|
4585 |
+
array_key_exists($i, $_REQUEST['param']) &&
|
4586 |
+
array_key_exists($i, $_REQUEST['operator']) &&
|
4587 |
+
array_key_exists($i, $_REQUEST['value'])
|
4588 |
+
) {
|
4589 |
+
$param = $_REQUEST['param'][$i];
|
4590 |
+
$operator = $_REQUEST['operator'][$i];
|
4591 |
+
$value = $_REQUEST['value'][$i];
|
4592 |
+
|
4593 |
+
switch (strtolower($param)) {
|
4594 |
+
case 'type':
|
4595 |
+
$param = 'jsRun';
|
4596 |
+
$value = strtolower($value) === 'human' ? 1 : 0;
|
4597 |
+
break;
|
4598 |
+
case 'ip':
|
4599 |
+
$value = wfUtils::inet_pton($value);
|
4600 |
+
break;
|
4601 |
+
case 'userid':
|
4602 |
+
$value = absint($value);
|
4603 |
+
break;
|
4604 |
+
}
|
4605 |
+
if ($operator === 'match' && $param !== 'ip') {
|
4606 |
+
$value = str_replace('*', '%', $value);
|
4607 |
+
}
|
4608 |
+
$filters->addFilter(new wfLiveTrafficQueryFilter($query, $param, $operator, $value));
|
4609 |
+
}
|
4610 |
+
}
|
4611 |
+
}
|
4612 |
+
|
4613 |
+
try {
|
4614 |
+
$return['data'] = $query->execute();
|
4615 |
+
if (defined('WP_DEBUG') && WP_DEBUG) {
|
4616 |
+
$return['sql'] = $query->buildQuery();
|
4617 |
+
}
|
4618 |
+
} catch (wfLiveTrafficQueryException $e) {
|
4619 |
+
$return['data'] = array();
|
4620 |
+
$return['sql'] = $e->getMessage();
|
4621 |
+
}
|
4622 |
+
|
4623 |
+
$return['success'] = true;
|
4624 |
+
|
4625 |
+
return $return;
|
4626 |
+
}
|
4627 |
+
|
4628 |
+
public static function ajax_whitelistWAFParamKey_callback() {
|
4629 |
+
if (class_exists('wfWAF') && $waf = wfWAF::getInstance()) {
|
4630 |
+
if (isset($_POST['path']) && isset($_POST['paramKey']) && isset($_POST['failedRules'])) {
|
4631 |
+
$data = array(
|
4632 |
+
'timestamp' => time(),
|
4633 |
+
'description' => 'Whitelisted via Live Traffic',
|
4634 |
+
'ip' => wfUtils::getIP(),
|
4635 |
+
);
|
4636 |
+
if (function_exists('get_current_user_id')) {
|
4637 |
+
$data['userID'] = get_current_user_id();
|
4638 |
+
}
|
4639 |
+
$waf->whitelistRuleForParam(base64_decode($_POST['path']), base64_decode($_POST['paramKey']),
|
4640 |
+
$_POST['failedRules'], $data);
|
4641 |
+
|
4642 |
+
return array(
|
4643 |
+
'success' => true,
|
4644 |
+
);
|
4645 |
+
}
|
4646 |
+
}
|
4647 |
+
return false;
|
4648 |
+
}
|
4649 |
+
|
4650 |
+
private static function _getWAFData() {
|
4651 |
+
$data['learningMode'] = wfWAF::getInstance()->isInLearningMode();
|
4652 |
+
$data['rules'] = wfWAF::getInstance()->getRules();
|
4653 |
+
/** @var wfWAFRule $rule */
|
4654 |
+
foreach ($data['rules'] as $ruleID => $rule) {
|
4655 |
+
$data['rules'][$ruleID] = $rule->toArray();
|
4656 |
+
}
|
4657 |
+
|
4658 |
+
$whitelistedURLParams = wfWAF::getInstance()->getStorageEngine()->getConfig('whitelistedURLParams', array());
|
4659 |
+
$data['whitelistedURLParams'] = array();
|
4660 |
+
foreach ($whitelistedURLParams as $urlParamKey => $rules) {
|
4661 |
+
list($path, $paramKey) = explode('|', $urlParamKey);
|
4662 |
+
$whitelistData = null;
|
4663 |
+
foreach ($rules as $ruleID => $whitelistedData) {
|
4664 |
+
if ($whitelistData === null) {
|
4665 |
+
$whitelistData = $whitelistedData;
|
4666 |
+
continue;
|
4667 |
+
}
|
4668 |
+
if ($ruleID === 'all') {
|
4669 |
+
$whitelistData = $whitelistedData;
|
4670 |
+
break;
|
4671 |
+
}
|
4672 |
+
}
|
4673 |
+
|
4674 |
+
if (is_array($whitelistData) && array_key_exists('userID', $whitelistData) && function_exists('get_user_by')) {
|
4675 |
+
$user = get_user_by('id', $whitelistData['userID']);
|
4676 |
+
if ($user) {
|
4677 |
+
$whitelistData['username'] = $user->user_login;
|
4678 |
+
}
|
4679 |
+
}
|
4680 |
+
|
4681 |
+
$data['whitelistedURLParams'][] = array(
|
4682 |
+
'path' => $path,
|
4683 |
+
'paramKey' => $paramKey,
|
4684 |
+
'ruleID' => array_keys($rules),
|
4685 |
+
'data' => $whitelistData,
|
4686 |
+
);
|
4687 |
+
}
|
4688 |
+
|
4689 |
+
$data['disabledRules'] = (array) wfWAF::getInstance()->getStorageEngine()->getConfig('disabledRules');
|
4690 |
+
if ($lastUpdated = wfWAF::getInstance()->getStorageEngine()->getConfig('rulesLastUpdated')) {
|
4691 |
+
$data['rulesLastUpdated'] = $lastUpdated;
|
4692 |
+
}
|
4693 |
+
$data['isPaid'] = (bool) wfConfig::get('isPaid', 0);
|
4694 |
+
return $data;
|
4695 |
+
}
|
4696 |
+
|
4697 |
+
public static function actionUserRegistration($user_id) {
|
4698 |
+
if (user_can($user_id, 'manage_options') && ($request = self::getLog()->getCurrentRequest())) {
|
4699 |
+
//self::getLog()->canLogHit = true;
|
4700 |
+
$request->action = 'user:adminCreate';
|
4701 |
+
$request->save();
|
4702 |
+
}
|
4703 |
+
}
|
4704 |
+
|
4705 |
+
public static function actionPasswordReset($user = null, $new_pass = null) {
|
4706 |
+
if ($request = self::getLog()->getCurrentRequest()) {
|
4707 |
+
//self::getLog()->canLogHit = true;
|
4708 |
+
$request->action = 'user:passwordReset';
|
4709 |
+
$request->save();
|
4710 |
+
}
|
4711 |
+
}
|
4712 |
+
|
4713 |
+
public static function trimWfHits() {
|
4714 |
+
global $wpdb;
|
4715 |
+
$p = $wpdb->base_prefix;
|
4716 |
+
$wfdb = new wfDB();
|
4717 |
+
$count = $wfdb->querySingle("select count(*) as cnt from $p"."wfHits");
|
4718 |
+
$liveTrafficMaxRows = absint(wfConfig::get('liveTraf_maxRows', 2000));
|
4719 |
+
if ($count > $liveTrafficMaxRows * 10) {
|
4720 |
+
$wfdb->truncate($p . "wfHits"); //So we don't slow down sites that have very large wfHits tables
|
4721 |
+
} else if ($count > $liveTrafficMaxRows) {
|
4722 |
+
$wfdb->queryWrite("delete from $p" . "wfHits order by id asc limit %d", ($count - $liveTrafficMaxRows) + ($liveTrafficMaxRows * .2));
|
4723 |
+
}
|
4724 |
+
}
|
4725 |
+
|
4726 |
+
private static function scheduleSendAttackData($timeToSend = null) {
|
4727 |
+
if ($timeToSend === null) {
|
4728 |
+
$timeToSend = time() + (60 * 5);
|
4729 |
+
}
|
4730 |
+
$notMainSite = is_multisite() && !is_main_site();
|
4731 |
+
if ($notMainSite) {
|
4732 |
+
global $current_site;
|
4733 |
+
switch_to_blog($current_site->blog_id);
|
4734 |
+
}
|
4735 |
+
if (!wp_next_scheduled('wordfence_processAttackData')) {
|
4736 |
+
wp_schedule_single_event($timeToSend, 'wordfence_processAttackData');
|
4737 |
+
}
|
4738 |
+
if ($notMainSite) {
|
4739 |
+
restore_current_blog();
|
4740 |
+
}
|
4741 |
+
}
|
4742 |
+
|
4743 |
+
/**
|
4744 |
+
*
|
4745 |
+
*/
|
4746 |
+
public static function processAttackData() {
|
4747 |
+
global $wpdb;
|
4748 |
+
$waf = wfWAF::getInstance();
|
4749 |
+
if ($waf->getStorageEngine()->getConfig('attackDataKey', false) === false) {
|
4750 |
+
$waf->getStorageEngine()->setConfig('attackDataKey', mt_rand(0, 0xfff));
|
4751 |
+
}
|
4752 |
+
|
4753 |
+
$limit = 500;
|
4754 |
+
$lastSendTime = wfConfig::get('lastAttackDataSendTime');
|
4755 |
+
$attackData = $wpdb->get_results($wpdb->prepare("SELECT SQL_CALC_FOUND_ROWS * FROM {$wpdb->base_prefix}wfHits
|
4756 |
+
WHERE action in ('blocked:waf', 'learned:waf')
|
4757 |
+
AND attackLogTime > %.6f
|
4758 |
+
LIMIT %d", $lastSendTime, $limit));
|
4759 |
+
$totalRows = $wpdb->get_var('SELECT FOUND_ROWS()');
|
4760 |
+
|
4761 |
+
if ($attackData) {
|
4762 |
+
$response = wp_remote_get(sprintf(WFWAF_API_URL_SEC . "waf-rules/%d.txt", $waf->getStorageEngine()->getConfig('attackDataKey')));
|
4763 |
+
|
4764 |
+
if (!is_wp_error($response)) {
|
4765 |
+
$okToSendBody = wp_remote_retrieve_body($response);
|
4766 |
+
if ($okToSendBody === 'ok') {
|
4767 |
+
// Build JSON to send
|
4768 |
+
$dataToSend = array();
|
4769 |
+
$attackDataToUpdate = array();
|
4770 |
+
foreach ($attackData as $attackDataRow) {
|
4771 |
+
$actionData = (array) wfRequestModel::unserializeActionData($attackDataRow->actionData);
|
4772 |
+
$dataToSend[] = array(
|
4773 |
+
$attackDataRow->attackLogTime,
|
4774 |
+
$attackDataRow->ctime,
|
4775 |
+
wfUtils::inet_ntop($attackDataRow->IP),
|
4776 |
+
(array_key_exists('learningMode', $actionData) ? $actionData['learningMode'] : 0),
|
4777 |
+
(array_key_exists('paramKey', $actionData) ? base64_encode($actionData['paramKey']) : false),
|
4778 |
+
(array_key_exists('paramValue', $actionData) ? base64_encode($actionData['paramValue']) : false),
|
4779 |
+
(array_key_exists('failedRules', $actionData) ? $actionData['failedRules'] : ''),
|
4780 |
+
strpos($attackDataRow->URL, 'https') === 0 ? 1 : 0,
|
4781 |
+
(array_key_exists('fullRequest', $actionData) ? $actionData['fullRequest'] : ''),
|
4782 |
+
);
|
4783 |
+
if (array_key_exists('fullRequest', $actionData)) {
|
4784 |
+
unset($actionData['fullRequest']);
|
4785 |
+
$attackDataToUpdate[$attackDataRow->id] = array(
|
4786 |
+
'actionData' => wfRequestModel::serializeActionData($actionData),
|
4787 |
+
);
|
4788 |
+
}
|
4789 |
+
if ($attackDataRow->attackLogTime > $lastSendTime) {
|
4790 |
+
$lastSendTime = $attackDataRow->attackLogTime;
|
4791 |
+
}
|
4792 |
+
}
|
4793 |
+
|
4794 |
+
$response = wp_remote_post(WFWAF_API_URL_SEC . "?" . http_build_query(array(
|
4795 |
+
'action' => 'send_waf_attack_data',
|
4796 |
+
'k' => $waf->getStorageEngine()->getConfig('apiKey'),
|
4797 |
+
's' => $waf->getStorageEngine()->getConfig('siteURL') ? $waf->getStorageEngine()->getConfig('siteURL') :
|
4798 |
+
sprintf('%s://%s/', $waf->getRequest()->getProtocol(), rawurlencode($waf->getRequest()->getHost())),
|
4799 |
+
)),
|
4800 |
+
array(
|
4801 |
+
'body' => json_encode($dataToSend),
|
4802 |
+
'headers' => array(
|
4803 |
+
'Content-Type' => 'application/json',
|
4804 |
+
),
|
4805 |
+
'timeout' => 30,
|
4806 |
+
));
|
4807 |
+
|
4808 |
+
if (!is_wp_error($response) && ($body = wp_remote_retrieve_body($response))) {
|
4809 |
+
$jsonData = json_decode($body, true);
|
4810 |
+
if (is_array($jsonData) && array_key_exists('success', $jsonData)) {
|
4811 |
+
// Successfully sent data, remove the full request from the table to reduce storage size
|
4812 |
+
foreach ($attackDataToUpdate as $hitID => $dataToUpdate) {
|
4813 |
+
$wpdb->update($wpdb->base_prefix . 'wfHits', $dataToUpdate, array(
|
4814 |
+
'id' => $hitID,
|
4815 |
+
));
|
4816 |
+
}
|
4817 |
+
wfConfig::set('lastAttackDataSendTime', $lastSendTime);
|
4818 |
+
if ($totalRows > $limit) {
|
4819 |
+
self::scheduleSendAttackData();
|
4820 |
+
}
|
4821 |
+
}
|
4822 |
+
}
|
4823 |
+
} else if (is_string($okToSendBody) && preg_match('/next check in: ([0-9]+)/', $okToSendBody, $matches)) {
|
4824 |
+
self::scheduleSendAttackData(time() + $matches[1]);
|
4825 |
+
}
|
4826 |
+
|
4827 |
+
// Could be that the server is down, so hold off on sending data for a little while.
|
4828 |
+
} else {
|
4829 |
+
self::scheduleSendAttackData(time() + 7200);
|
4830 |
+
}
|
4831 |
+
}
|
4832 |
+
|
4833 |
+
self::trimWfHits();
|
4834 |
+
}
|
4835 |
+
|
4836 |
+
public static function syncAttackData() {
|
4837 |
+
global $wpdb;
|
4838 |
+
$waf = wfWAF::getInstance();
|
4839 |
+
$lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$wpdb->base_prefix}wfHits");
|
4840 |
+
if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) {
|
4841 |
+
$attackData = $waf->getStorageEngine()->getNewestAttackDataArray($lastAttackMicroseconds);
|
4842 |
+
if ($attackData) {
|
4843 |
+
foreach ($attackData as $request) {
|
4844 |
+
if (count($request) !== 9) {
|
4845 |
+
continue;
|
4846 |
+
}
|
4847 |
+
|
4848 |
+
list($logTimeMicroseconds, $requestTime, $ip, $learningMode, $paramKey, $paramValue, $failedRules, $ssl, $requestString) = $request;
|
4849 |
+
|
4850 |
+
// Skip old entries and hits in learning mode, since they'll get picked up anyways.
|
4851 |
+
if ($logTimeMicroseconds <= $lastAttackMicroseconds || $learningMode) {
|
4852 |
+
continue;
|
4853 |
+
}
|
4854 |
+
|
4855 |
+
$hit = new wfRequestModel();
|
4856 |
+
$hit->attackLogTime = $logTimeMicroseconds;
|
4857 |
+
$hit->statusCode = 403;
|
4858 |
+
$hit->ctime = $requestTime;
|
4859 |
+
$hit->IP = wfUtils::inet_pton($ip);
|
4860 |
+
|
4861 |
+
if (preg_match('/user\-agent:(.*?)\n/i', $requestString, $matches)) {
|
4862 |
+
$hit->UA = trim($matches[1]);
|
4863 |
+
$hit->isGoogle = wfCrawl::isGoogleCrawler($hit->UA);
|
4864 |
+
}
|
4865 |
+
|
4866 |
+
if (preg_match('/Referer:(.*?)\n/i', $requestString, $matches)) {
|
4867 |
+
$hit->referer = trim($matches[1]);
|
4868 |
+
}
|
4869 |
+
|
4870 |
+
if (preg_match('/^[a-z]+\s+(.*?)\s+/i', $requestString, $uriMatches) && preg_match('/Host:(.*?)\n/i', $requestString, $hostMatches)) {
|
4871 |
+
$hit->URL = 'http' . ($ssl ? 's' : '') . '://' . trim($hostMatches[1]) . trim($uriMatches[1]);
|
4872 |
+
}
|
4873 |
+
|
4874 |
+
if (preg_match('/cookie:(.*?)\n/i', $requestString, $matches)) {
|
4875 |
+
$hit->newVisit = strpos($matches[1], 'wfvt_' . crc32(site_url())) !== false ? 1 : 0;
|
4876 |
+
$hasVerifiedHumanCookie = strpos($matches[1], 'wordfence_verifiedHuman') !== false;
|
4877 |
+
if ($hasVerifiedHumanCookie && preg_match('/wordfence_verifiedHuman=(.*?);/', $matches[1], $cookieMatches)) {
|
4878 |
+
$hit->jsRun = (int) wp_verify_nonce($cookieMatches[1], 'wordfence_verifiedHuman' . $hit->UA . $ip);
|
4879 |
+
}
|
4880 |
+
|
4881 |
+
$hasLoginCookie = strpos($matches[1], $ssl ? SECURE_AUTH_COOKIE : AUTH_COOKIE) !== false;
|
4882 |
+
if ($hasLoginCookie && preg_match('/' . ($ssl ? SECURE_AUTH_COOKIE : AUTH_COOKIE) . '=(.*?);/', $matches[1], $cookieMatches)) {
|
4883 |
+
$authCookie = rawurldecode($cookieMatches[1]);
|
4884 |
+
$authID = $ssl ? wp_validate_auth_cookie($authCookie, 'secure_auth') : wp_validate_auth_cookie($authCookie, 'auth');
|
4885 |
+
if ($authID) {
|
4886 |
+
$hit->userID = $authID;
|
4887 |
+
}
|
4888 |
+
}
|
4889 |
+
}
|
4890 |
+
|
4891 |
+
$path = '/';
|
4892 |
+
if (preg_match('/^[A-Z]+ (.*?) HTTP\\/1\\.1/', $requestString, $matches)) {
|
4893 |
+
if (($pos = strpos($matches[1], '?')) !== false) {
|
4894 |
+
$path = substr($matches[1], 0, $pos);
|
4895 |
+
} else {
|
4896 |
+
$path = $matches[1];
|
4897 |
+
}
|
4898 |
+
}
|
4899 |
+
|
4900 |
+
$hit->action = 'blocked:waf';
|
4901 |
+
|
4902 |
+
/** @var wfWAFRule $rule */
|
4903 |
+
$ruleIDs = explode('|', $failedRules);
|
4904 |
+
$actionData = array(
|
4905 |
+
'learningMode' => $learningMode,
|
4906 |
+
'failedRules' => $failedRules,
|
4907 |
+
'paramKey' => $paramKey,
|
4908 |
+
'paramValue' => $paramValue,
|
4909 |
+
'path' => $path,
|
4910 |
+
);
|
4911 |
+
if ($ruleIDs && $ruleIDs[0]) {
|
4912 |
+
$rule = $waf->getRule($ruleIDs[0]);
|
4913 |
+
if ($rule) {
|
4914 |
+
$hit->actionDescription = $rule->getDescription();
|
4915 |
+
$actionData['category'] = $rule->getCategory();
|
4916 |
+
$actionData['ssl'] = $ssl;
|
4917 |
+
$actionData['fullRequest'] = base64_encode($requestString);
|
4918 |
+
}
|
4919 |
+
}
|
4920 |
+
|
4921 |
+
$hit->actionData = wfRequestModel::serializeActionData($actionData);
|
4922 |
+
$hit->save();
|
4923 |
+
|
4924 |
+
self::scheduleSendAttackData();
|
4925 |
+
}
|
4926 |
+
}
|
4927 |
+
$waf->getStorageEngine()->truncateAttackData();
|
4928 |
+
}
|
4929 |
+
update_site_option('wordfence_syncingAttackData', 0);
|
4930 |
+
exit;
|
4931 |
+
}
|
4932 |
+
|
4933 |
+
/**
|
4934 |
+
* This is the only hook I see to tie into WP's core update process.
|
4935 |
+
* Since we hide the readme.html to prevent the WordPress version from being discovered, it breaks the upgrade
|
4936 |
+
* process because it cannot copy the previous readme.html.
|
4937 |
+
*
|
4938 |
+
* @param string $string
|
4939 |
+
* @return string
|
4940 |
+
*/
|
4941 |
+
public static function restoreReadmeForUpgrade($string) {
|
4942 |
+
static $didRun;
|
4943 |
+
if (!isset($didRun)) {
|
4944 |
+
$didRun = true;
|
4945 |
+
wfUtils::showReadme();
|
4946 |
+
register_shutdown_function('wfUtils::hideReadme');
|
4947 |
+
}
|
4948 |
+
|
4949 |
+
return $string;
|
4950 |
+
}
|
4951 |
+
|
4952 |
+
public static function wafAutoPrependNotice() {
|
4953 |
+
$url = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend');
|
4954 |
+
echo '<div class="update-nag">To make your site as secure as possible, take a moment to setup the Wordfence Web
|
4955 |
+
Application Firewall: <a class="button button-small" href="' . esc_url($url) . '">Click here to configure.</a><br>
|
4956 |
+
<em style="font-size: 85%;">If you cannot complete the setup process,
|
4957 |
+
<a target="_blank" href="https://docs.wordfence.com/en/Web_Application_Firewall_Setup">click here for help</a>.</em>
|
4958 |
+
</div>';
|
4959 |
+
}
|
4960 |
+
|
4961 |
+
public static function wafAutoPrependVerify() {
|
4962 |
+
if (WFWAF_AUTO_PREPEND && !WFWAF_SUBDIRECTORY_INSTALL) {
|
4963 |
+
echo '<div class="updated is-dismissible"><p>The installation was successful! Your site is protected to the fullest extent!</p></div>';
|
4964 |
+
} else {
|
4965 |
+
echo '<div class="notice notice-error"><p>The changes have not yet taken effect. If you are using LiteSpeed
|
4966 |
+
as your web server or CGI/FastCGI interface, you may need to wait a few minutes for the changes to take effect since the
|
4967 |
+
configuration files are sometimes cached. You also may need to select a different server configuration in order to
|
4968 |
+
complete this step, but wait for a few minutes before trying. You can try refreshing this page. </p></div>';
|
4969 |
+
}
|
4970 |
+
}
|
4971 |
+
|
4972 |
+
public static function getWAFBootstrapPath() {
|
4973 |
+
return ABSPATH . 'wordfence-waf.php';
|
4974 |
+
}
|
4975 |
+
|
4976 |
+
public static function getWAFBootstrapContent($currentAutoPrependedFile = null) {
|
4977 |
+
$currentAutoPrepend = '';
|
4978 |
+
if ($currentAutoPrependedFile && is_file($currentAutoPrependedFile) && !WFWAF_SUBDIRECTORY_INSTALL) {
|
4979 |
+
$currentAutoPrepend = sprintf('
|
4980 |
+
// This file was the current value of auto_prepend_file during the Wordfence WAF installation (%2$s)
|
4981 |
+
if (file_exists(%1$s)) {
|
4982 |
+
include_once %1$s;
|
4983 |
+
}', var_export($currentAutoPrependedFile, true), date('r'));
|
4984 |
+
}
|
4985 |
+
return sprintf('<?php
|
4986 |
+
// Before removing this file, please verify the PHP ini setting `auto_prepend_file` does not point to this.
|
4987 |
+
%3$s
|
4988 |
+
if (file_exists(%1$s)) {
|
4989 |
+
define("WFWAF_LOG_PATH", %2$s);
|
4990 |
+
include_once %1$s;
|
4991 |
+
}
|
4992 |
+
?>',
|
4993 |
+
var_export(WORDFENCE_PATH . 'waf/bootstrap.php', true),
|
4994 |
+
var_export(WFWAF_SUBDIRECTORY_INSTALL ? WP_CONTENT_DIR . '/wflogs/' : WFWAF_LOG_PATH, true),
|
4995 |
+
$currentAutoPrepend);
|
4996 |
+
}
|
4997 |
+
|
4998 |
+
public static function checkAndCreateBootstrap() {
|
4999 |
+
$bootstrapPath = self::getWAFBootstrapPath();
|
5000 |
+
if (!file_exists($bootstrapPath) || !filesize($bootstrapPath)) {
|
5001 |
+
@file_put_contents($bootstrapPath, self::getWAFBootstrapContent(), LOCK_EX);
|
5002 |
+
clearstatcache();
|
5003 |
+
}
|
5004 |
+
return file_exists($bootstrapPath) && filesize($bootstrapPath);
|
5005 |
+
}
|
5006 |
+
|
5007 |
+
/**
|
5008 |
+
* @return bool|string
|
5009 |
+
*/
|
5010 |
+
private static function getCurrentUserRole() {
|
5011 |
+
if (current_user_can('administrator') || is_super_admin()) {
|
5012 |
+
return 'administrator';
|
5013 |
+
}
|
5014 |
+
$roles = array('editor', 'author', 'contributor', 'subscriber');
|
5015 |
+
foreach ($roles as $role) {
|
5016 |
+
if (current_user_can($role)) {
|
5017 |
+
return $role;
|
5018 |
+
}
|
5019 |
+
}
|
5020 |
+
return false;
|
5021 |
+
}
|
5022 |
+
}
|
5023 |
+
|
5024 |
+
class wfWAFAutoPrependHelper {
|
5025 |
+
|
5026 |
+
private $serverConfig;
|
5027 |
+
/**
|
5028 |
+
* @var string
|
5029 |
+
*/
|
5030 |
+
private $currentAutoPrependedFile;
|
5031 |
+
|
5032 |
+
/**
|
5033 |
+
* @param string|null $serverConfig
|
5034 |
+
* @param string|null $currentAutoPrependedFile
|
5035 |
+
*/
|
5036 |
+
public function __construct($serverConfig = null, $currentAutoPrependedFile = null) {
|
5037 |
+
$this->serverConfig = $serverConfig;
|
5038 |
+
$this->currentAutoPrependedFile = $currentAutoPrependedFile;
|
5039 |
+
}
|
5040 |
+
|
5041 |
+
public function getFilesNeededForBackup() {
|
5042 |
+
$backups = array();
|
5043 |
+
$htaccess = $this->getHtaccessPath();
|
5044 |
+
switch ($this->getServerConfig()) {
|
5045 |
+
case 'apache-mod_php':
|
5046 |
+
case 'apache-suphp':
|
5047 |
+
case 'litespeed':
|
5048 |
+
case 'cgi':
|
5049 |
+
if (file_exists($htaccess)) {
|
5050 |
+
$backups[] = $htaccess;
|
5051 |
+
}
|
5052 |
+
break;
|
5053 |
+
}
|
5054 |
+
if ($userIni = ini_get('user_ini.filename')) {
|
5055 |
+
$userIniPath = $this->getUserIniPath();
|
5056 |
+
switch ($this->getServerConfig()) {
|
5057 |
+
case 'cgi':
|
5058 |
+
case 'apache-suphp':
|
5059 |
+
case 'nginx':
|
5060 |
+
case 'litespeed':
|
5061 |
+
if (file_exists($userIniPath)) {
|
5062 |
+
$backups[] = $userIniPath;
|
5063 |
+
}
|
5064 |
+
break;
|
5065 |
+
}
|
5066 |
+
}
|
5067 |
+
return $backups;
|
5068 |
+
}
|
5069 |
+
|
5070 |
+
public function downloadBackups($index = 0) {
|
5071 |
+
$backups = $this->getFilesNeededForBackup();
|
5072 |
+
if ($backups && array_key_exists($index, $backups)) {
|
5073 |
+
$url = site_url();
|
5074 |
+
$url = preg_replace('/^https?:\/\//i', '', $url);
|
5075 |
+
$url = preg_replace('/[^a-zA-Z0-9\.]+/', '_', $url);
|
5076 |
+
$url = preg_replace('/^_+/', '', $url);
|
5077 |
+
$url = preg_replace('/_+$/', '', $url);
|
5078 |
+
header('Content-Type: application/octet-stream');
|
5079 |
+
$backupFileName = ltrim(basename($backups[$index]), '.');
|
5080 |
+
header('Content-Disposition: attachment; filename="' . $backupFileName . '_Backup_for_' . $url . '.txt"');
|
5081 |
+
readfile($backups[$index]);
|
5082 |
+
die();
|
5083 |
+
}
|
5084 |
+
}
|
5085 |
+
|
5086 |
+
/**
|
5087 |
+
* @return mixed
|
5088 |
+
*/
|
5089 |
+
public function getServerConfig() {
|
5090 |
+
return $this->serverConfig;
|
5091 |
+
}
|
5092 |
+
|
5093 |
+
/**
|
5094 |
+
* @param mixed $serverConfig
|
5095 |
+
*/
|
5096 |
+
public function setServerConfig($serverConfig) {
|
5097 |
+
$this->serverConfig = $serverConfig;
|
5098 |
+
}
|
5099 |
+
|
5100 |
+
/**
|
5101 |
+
* @param WP_Filesystem_Base $wp_filesystem
|
5102 |
+
* @throws wfWAFAutoPrependHelperException
|
5103 |
+
*/
|
5104 |
+
public function performInstallation($wp_filesystem) {
|
5105 |
+
$bootstrapPath = wordfence::getWAFBootstrapPath();
|
5106 |
+
if (!$wp_filesystem->put_contents($bootstrapPath, wordfence::getWAFBootstrapContent($this->currentAutoPrependedFile))) {
|
5107 |
+
throw new wfWAFAutoPrependHelperException('We were unable to create the <code>wordfence-waf.php</code> file
|
5108 |
+
in the root of the WordPress installation. It\'s possible WordPress cannot write to the <code>wordfence-waf.php</code>
|
5109 |
+
file because of file permissions. Please verify the permissions are correct and retry the installation.');
|
5110 |
+
}
|
5111 |
+
|
5112 |
+
$serverConfig = $this->getServerConfig();
|
5113 |
+
|
5114 |
+
$htaccessPath = $this->getHtaccessPath();
|
5115 |
+
$homePath = dirname($htaccessPath);
|
5116 |
+
|
5117 |
+
$userIniPath = $this->getUserIniPath();
|
5118 |
+
$userIni = ini_get('user_ini.filename');
|
5119 |
+
|
5120 |
+
$userIniHtaccessDirectives = '';
|
5121 |
+
if ($userIni) {
|
5122 |
+
$userIniHtaccessDirectives = sprintf('<Files "%s">
|
5123 |
+
<IfModule mod_authz_core.c>
|
5124 |
+
Require all denied
|
5125 |
+
</IfModule>
|
5126 |
+
<IfModule !mod_authz_core.c>
|
5127 |
+
Order deny,allow
|
5128 |
+
Deny from all
|
5129 |
+
</IfModule>
|
5130 |
+
</Files>
|
5131 |
+
', addcslashes($userIni, '"'));
|
5132 |
+
}
|
5133 |
+
|
5134 |
+
|
5135 |
+
// .htaccess configuration
|
5136 |
+
switch ($serverConfig) {
|
5137 |
+
case 'apache-mod_php':
|
5138 |
+
$autoPrependDirective = sprintf("# Wordfence WAF
|
5139 |
+
<IfModule mod_php%d.c>
|
5140 |
+
php_value auto_prepend_file '%s'
|
5141 |
+
</IfModule>
|
5142 |
+
$userIniHtaccessDirectives
|
5143 |
+
# END Wordfence WAF
|
5144 |
+
", PHP_MAJOR_VERSION, addcslashes($bootstrapPath, "'"));
|
5145 |
+
break;
|
5146 |
+
|
5147 |
+
case 'litespeed':
|
5148 |
+
$autoPrependDirective = sprintf("# Wordfence WAF
|
5149 |
+
<IfModule LiteSpeed>
|
5150 |
+
php_value auto_prepend_file '%s'
|
5151 |
+
</IfModule>
|
5152 |
+
$userIniHtaccessDirectives
|
5153 |
+
# END Wordfence WAF
|
5154 |
+
", addcslashes($bootstrapPath, "'"));
|
5155 |
+
break;
|
5156 |
+
|
5157 |
+
case 'apache-suphp':
|
5158 |
+
$autoPrependDirective = sprintf("# Wordfence WAF
|
5159 |
+
<IfModule mod_suphp.c>
|
5160 |
+
suPHP_ConfigPath '%s'
|
5161 |
+
</IfModule>
|
5162 |
+
$userIniHtaccessDirectives
|
5163 |
+
# END Wordfence WAF
|
5164 |
+
", addcslashes($homePath, "'"));
|
5165 |
+
break;
|
5166 |
+
|
5167 |
+
case 'cgi':
|
5168 |
+
if ($userIniHtaccessDirectives) {
|
5169 |
+
$autoPrependDirective = sprintf("# Wordfence WAF
|
5170 |
+
$userIniHtaccessDirectives
|
5171 |
+
# END Wordfence WAF
|
5172 |
+
", addcslashes($homePath, "'"));
|
5173 |
+
}
|
5174 |
+
break;
|
5175 |
+
|
5176 |
+
}
|
5177 |
+
|
5178 |
+
if (!empty($autoPrependDirective)) {
|
5179 |
+
// Modify .htaccess
|
5180 |
+
$htaccessContent = $wp_filesystem->get_contents($htaccessPath);
|
5181 |
+
|
5182 |
+
if ($htaccessContent) {
|
5183 |
+
$regex = '/# Wordfence WAF.*?# END Wordfence WAF/is';
|
5184 |
+
if (preg_match($regex, $htaccessContent, $matches)) {
|
5185 |
+
$htaccessContent = preg_replace($regex, $autoPrependDirective, $htaccessContent);
|
5186 |
+
} else {
|
5187 |
+
$htaccessContent .= "\n\n" . $autoPrependDirective;
|
5188 |
+
}
|
5189 |
+
} else {
|
5190 |
+
$htaccessContent = $autoPrependDirective;
|
5191 |
+
}
|
5192 |
+
|
5193 |
+
if (!$wp_filesystem->put_contents($htaccessPath, $htaccessContent)) {
|
5194 |
+
throw new wfWAFAutoPrependHelperException('We were unable to make changes to the .htaccess file. It\'s
|
5195 |
+
possible WordPress cannot write to the .htaccess file because of file permissions, which may have been
|
5196 |
+
set by another security plugin, or you may have set them manually. Please verify the permissions allow
|
5197 |
+
the web server to write to the file, and retry the installation.');
|
5198 |
+
}
|
5199 |
+
if ($serverConfig == 'litespeed') {
|
5200 |
+
// sleep(2);
|
5201 |
+
$wp_filesystem->touch($htaccessPath);
|
5202 |
+
}
|
5203 |
+
|
5204 |
+
}
|
5205 |
+
if ($userIni) {
|
5206 |
+
// .user.ini configuration
|
5207 |
+
switch ($serverConfig) {
|
5208 |
+
case 'cgi':
|
5209 |
+
case 'nginx':
|
5210 |
+
case 'apache-suphp':
|
5211 |
+
case 'litespeed':
|
5212 |
+
$autoPrependIni = sprintf("; Wordfence WAF
|
5213 |
+
auto_prepend_file = '%s'
|
5214 |
+
; END Wordfence WAF
|
5215 |
+
", addcslashes($bootstrapPath, "'"));
|
5216 |
+
|
5217 |
+
break;
|
5218 |
+
}
|
5219 |
+
|
5220 |
+
if (!empty($autoPrependIni)) {
|
5221 |
+
|
5222 |
+
// Modify .user.ini
|
5223 |
+
$userIniContent = $wp_filesystem->get_contents($userIniPath);
|
5224 |
+
if (is_string($userIniContent)) {
|
5225 |
+
$userIniContent = str_replace('auto_prepend_file', ';auto_prepend_file', $userIniContent);
|
5226 |
+
$regex = '/; Wordfence WAF.*?; END Wordfence WAF/is';
|
5227 |
+
if (preg_match($regex, $userIniContent, $matches)) {
|
5228 |
+
$userIniContent = preg_replace($regex, $autoPrependIni, $userIniContent);
|
5229 |
+
} else {
|
5230 |
+
$userIniContent .= "\n\n" . $autoPrependIni;
|
5231 |
+
}
|
5232 |
+
} else {
|
5233 |
+
$userIniContent = $autoPrependIni;
|
5234 |
+
}
|
5235 |
+
|
5236 |
+
if (!$wp_filesystem->put_contents($userIniPath, $userIniContent)) {
|
5237 |
+
throw new wfWAFAutoPrependHelperException(sprintf('We were unable to make changes to the %1$s file.
|
5238 |
+
It\'s possible WordPress cannot write to the %1$s file because of file permissions.
|
5239 |
+
Please verify the permissions are correct and retry the installation.', basename($userIniPath)));
|
5240 |
+
}
|
5241 |
+
}
|
5242 |
+
}
|
5243 |
+
}
|
5244 |
+
|
5245 |
+
public function getHtaccessPath() {
|
5246 |
+
return get_home_path() . '.htaccess';
|
5247 |
+
}
|
5248 |
+
|
5249 |
+
public function getUserIniPath() {
|
5250 |
+
$userIni = ini_get('user_ini.filename');
|
5251 |
+
if ($userIni) {
|
5252 |
+
return get_home_path() . $userIni;
|
5253 |
+
}
|
5254 |
+
return false;
|
5255 |
+
}
|
5256 |
+
|
5257 |
+
public function uninstall() {
|
5258 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
5259 |
+
global $wp_filesystem;
|
5260 |
+
|
5261 |
+
$htaccessPath = $this->getHtaccessPath();
|
5262 |
+
$userIniPath = $this->getUserIniPath();
|
5263 |
+
|
5264 |
+
$adminURL = admin_url('/');
|
5265 |
+
$allow_relaxed_file_ownership = true;
|
5266 |
+
$homePath = dirname($htaccessPath);
|
5267 |
+
|
5268 |
+
ob_start();
|
5269 |
+
if (false === ($credentials = request_filesystem_credentials($adminURL, '', false, $homePath,
|
5270 |
+
array('version', 'locale'), $allow_relaxed_file_ownership))
|
5271 |
+
) {
|
5272 |
+
ob_end_clean();
|
5273 |
+
return false;
|
5274 |
+
}
|
5275 |
+
|
5276 |
+
if (!WP_Filesystem($credentials, $homePath, $allow_relaxed_file_ownership)) {
|
5277 |
+
// Failed to connect, Error and request again
|
5278 |
+
request_filesystem_credentials($adminURL, '', true, ABSPATH, array('version', 'locale'),
|
5279 |
+
$allow_relaxed_file_ownership);
|
5280 |
+
ob_end_clean();
|
5281 |
+
return false;
|
5282 |
+
}
|
5283 |
+
|
5284 |
+
if ($wp_filesystem->errors->get_error_code()) {
|
5285 |
+
ob_end_clean();
|
5286 |
+
return false;
|
5287 |
+
}
|
5288 |
+
ob_end_clean();
|
5289 |
+
|
5290 |
+
if ($wp_filesystem->is_file($htaccessPath)) {
|
5291 |
+
$htaccessContent = $wp_filesystem->get_contents($htaccessPath);
|
5292 |
+
$regex = '/# Wordfence WAF.*?# END Wordfence WAF/is';
|
5293 |
+
if (preg_match($regex, $htaccessContent, $matches)) {
|
5294 |
+
$htaccessContent = preg_replace($regex, '', $htaccessContent);
|
5295 |
+
if (!$wp_filesystem->put_contents($htaccessPath, $htaccessContent)) {
|
5296 |
+
return false;
|
5297 |
+
}
|
5298 |
+
}
|
5299 |
+
}
|
5300 |
+
|
5301 |
+
if ($wp_filesystem->is_file($userIniPath)) {
|
5302 |
+
$userIniContent = $wp_filesystem->get_contents($userIniPath);
|
5303 |
+
$regex = '/; Wordfence WAF.*?; END Wordfence WAF/is';
|
5304 |
+
if (preg_match($regex, $userIniContent, $matches)) {
|
5305 |
+
$userIniContent = preg_replace($regex, '', $userIniContent);
|
5306 |
+
if (!$wp_filesystem->put_contents($userIniPath, $userIniContent)) {
|
5307 |
+
return false;
|
5308 |
+
}
|
5309 |
+
}
|
5310 |
+
}
|
5311 |
+
|
5312 |
+
$bootstrapPath = wordfence::getWAFBootstrapPath();
|
5313 |
+
if ($wp_filesystem->is_file($bootstrapPath)) {
|
5314 |
+
$wp_filesystem->delete($bootstrapPath);
|
5315 |
+
}
|
5316 |
+
return true;
|
5317 |
+
}
|
5318 |
}
|
5319 |
+
|
5320 |
+
class wfWAFAutoPrependHelperException extends Exception {
|
5321 |
+
}
|
5322 |
+
|
5323 |
?>
|
lib/wordfenceConstants.php
CHANGED
@@ -1,7 +1,9 @@
|
|
1 |
<?php
|
2 |
-
define('WORDFENCE_API_VERSION', '2.
|
3 |
define('WORDFENCE_API_URL_SEC', 'https://noc1.wordfence.com/');
|
4 |
define('WORDFENCE_API_URL_NONSEC', 'http://noc1.wordfence.com/');
|
|
|
|
|
5 |
define('WORDFENCE_HACKATTEMPT_URL', 'http://noc3.wordfence.com:9050/');
|
6 |
define('WORDFENCE_MAX_SCAN_TIME', 86400); //Increased this from 10 mins to 1 day because very big scans run for a long time. Users can use kill.
|
7 |
define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
|
1 |
<?php
|
2 |
+
define('WORDFENCE_API_VERSION', '2.22');
|
3 |
define('WORDFENCE_API_URL_SEC', 'https://noc1.wordfence.com/');
|
4 |
define('WORDFENCE_API_URL_NONSEC', 'http://noc1.wordfence.com/');
|
5 |
+
define('WORDFENCE_API_URL_BASE_SEC', WORDFENCE_API_URL_SEC . '/v' . WORDFENCE_API_VERSION . '/');
|
6 |
+
define('WORDFENCE_API_URL_BASE_NONSEC', WORDFENCE_API_URL_NONSEC . '/v' . WORDFENCE_API_VERSION . '/');
|
7 |
define('WORDFENCE_HACKATTEMPT_URL', 'http://noc3.wordfence.com:9050/');
|
8 |
define('WORDFENCE_MAX_SCAN_TIME', 86400); //Increased this from 10 mins to 1 day because very big scans run for a long time. Users can use kill.
|
9 |
define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
|
lib/wordfenceHash.php
CHANGED
@@ -39,7 +39,8 @@ class wordfenceHash {
|
|
39 |
$this->striplen = $striplen;
|
40 |
$this->path = $path;
|
41 |
$this->only = $only;
|
42 |
-
|
|
|
43 |
$this->startTime = microtime(true);
|
44 |
|
45 |
if(wfConfig::get('scansEnabled_core')){
|
@@ -58,20 +59,14 @@ class wordfenceHash {
|
|
58 |
|
59 |
//Doing a delete for now. Later we can optimize this to only scan modified files.
|
60 |
//$this->db->queryWrite("update " . $this->db->prefix() . "wfFileMods set oldMD5 = newMD5");
|
61 |
-
$this->db->
|
62 |
-
$fetchCoreHashesStatus = wordfence::statusStart("Fetching core, theme and plugin file signatures from Wordfence");
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
if($dataArr['code'] != 200){
|
68 |
-
wordfence::statusEndErr();
|
69 |
-
throw new Exception("Got error response from Wordfence servers: " . $dataArr['code']);
|
70 |
-
}
|
71 |
-
$this->knownFiles = @json_decode($dataArr['data'], true);
|
72 |
-
if(! is_array($this->knownFiles)){
|
73 |
wordfence::statusEndErr();
|
74 |
-
throw
|
75 |
}
|
76 |
wordfence::statusEnd($fetchCoreHashesStatus, false, true);
|
77 |
if($this->malwareEnabled){
|
39 |
$this->striplen = $striplen;
|
40 |
$this->path = $path;
|
41 |
$this->only = $only;
|
42 |
+
$this->engine = $engine;
|
43 |
+
|
44 |
$this->startTime = microtime(true);
|
45 |
|
46 |
if(wfConfig::get('scansEnabled_core')){
|
59 |
|
60 |
//Doing a delete for now. Later we can optimize this to only scan modified files.
|
61 |
//$this->db->queryWrite("update " . $this->db->prefix() . "wfFileMods set oldMD5 = newMD5");
|
62 |
+
$this->db->truncate($this->db->prefix() . "wfFileMods");
|
63 |
+
$fetchCoreHashesStatus = wordfence::statusStart("Fetching core, theme and plugin file signatures from Wordfence");
|
64 |
+
try {
|
65 |
+
$this->knownFiles = $this->engine->getKnownFilesLoader()
|
66 |
+
->getKnownFiles();
|
67 |
+
} catch (wfScanKnownFilesException $e) {
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
wordfence::statusEndErr();
|
69 |
+
throw $e;
|
70 |
}
|
71 |
wordfence::statusEnd($fetchCoreHashesStatus, false, true);
|
72 |
if($this->malwareEnabled){
|
lib/wordfenceScanner.php
CHANGED
@@ -15,8 +15,12 @@ class wordfenceScanner {
|
|
15 |
protected $patterns = "";
|
16 |
protected $api = false;
|
17 |
protected static $excludePattern = NULL;
|
|
|
|
|
|
|
18 |
public function __sleep(){
|
19 |
-
return array('path', 'results', 'errorMsg', 'apiKey', 'wordpressVersion', 'urlHoover', 'totalFilesScanned',
|
|
|
20 |
}
|
21 |
public function __wakeup(){
|
22 |
}
|
@@ -38,18 +42,48 @@ class wordfenceScanner {
|
|
38 |
}
|
39 |
|
40 |
/**
|
|
|
41 |
* @todo add caching to this.
|
42 |
* @throws Exception
|
43 |
*/
|
44 |
protected function setupSigs() {
|
45 |
$this->api = new wfAPI($this->apiKey, $this->wordpressVersion);
|
46 |
$sigData = $this->api->call('get_patterns', array(), array());
|
47 |
-
|
48 |
-
//$sigData = wfSigs::getSigData();
|
49 |
-
if(! (is_array($sigData) && isset($sigData['sigPattern'])) ){
|
50 |
throw new Exception("Wordfence could not get the attack signature patterns from the scanning server.");
|
51 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
$this->patterns = $sigData;
|
|
|
|
|
|
|
53 |
}
|
54 |
|
55 |
/**
|
@@ -78,7 +112,13 @@ class wordfenceScanner {
|
|
78 |
return self::$excludePattern;
|
79 |
}
|
80 |
|
|
|
|
|
|
|
|
|
81 |
public function scan($forkObj){
|
|
|
|
|
82 |
if(! $this->startTime){
|
83 |
$this->startTime = microtime(true);
|
84 |
}
|
@@ -154,6 +194,9 @@ class wordfenceScanner {
|
|
154 |
continue;
|
155 |
}
|
156 |
$totalRead = 0;
|
|
|
|
|
|
|
157 |
while(! feof($fh)){
|
158 |
$data = fread($fh, 1 * 1024 * 1024); //read 1 megs max per chunk
|
159 |
$totalRead += strlen($data);
|
@@ -170,52 +213,34 @@ class wordfenceScanner {
|
|
170 |
'ignoreC' => $fileSum,
|
171 |
'shortMsg' => "File is an old version of TimThumb which is vulnerable.",
|
172 |
'longMsg' => "This file appears to be an old version of the TimThumb script which makes your system vulnerable to attackers. Please upgrade the theme or plugin that uses this or remove it.",
|
173 |
-
'data' => array(
|
174 |
-
'file' => $file,
|
175 |
-
'canDiff' => false,
|
176 |
-
'canFix' => false,
|
177 |
-
'canDelete' => true
|
178 |
-
)
|
179 |
-
));
|
180 |
-
break;
|
181 |
-
}
|
182 |
-
} else if(strpos($file, 'lib/wordfenceScanner.php') === false && preg_match($this->patterns['sigPattern'], $data, $matches)){
|
183 |
-
if(! $this->isSafeFile($this->path . $file)){
|
184 |
-
$this->addResult(array(
|
185 |
-
'type' => 'file',
|
186 |
-
'severity' => 1,
|
187 |
-
'ignoreP' => $this->path . $file,
|
188 |
-
'ignoreC' => $fileSum,
|
189 |
-
'shortMsg' => "File appears to be malicious: " . esc_html($file),
|
190 |
-
'longMsg' => "This file appears to be installed by a hacker to perform malicious activity. If you know about this file you can choose to ignore it to exclude it from future scans. The text we found in this file that matches a known malicious file is: <strong style=\"color: #F00;\">\"" . esc_html($matches[1]) . "\"</strong>.",
|
191 |
-
'data' => array(
|
192 |
-
'file' => $file,
|
193 |
-
'canDiff' => false,
|
194 |
-
'canFix' => false,
|
195 |
-
'canDelete' => true
|
196 |
-
)));
|
197 |
-
break;
|
198 |
-
}
|
199 |
-
|
200 |
-
}
|
201 |
-
if(preg_match($this->patterns['pat2'], $data)){
|
202 |
-
if(! $this->isSafeFile($this->path . $file)){
|
203 |
-
$this->addResult(array(
|
204 |
-
'type' => 'file',
|
205 |
-
'severity' => 1,
|
206 |
-
'ignoreP' => $this->path . $file,
|
207 |
-
'ignoreC' => $fileSum,
|
208 |
-
'shortMsg' => "This file may contain malicious executable code: " . esc_html($this->path . $file),
|
209 |
-
'longMsg' => "This file is a PHP executable file and contains an " . esc_html($this->patterns['word1']) . " function and " . esc_html($this->patterns['word2']) . " decoding function on the same line. This is a common technique used by hackers to hide and execute code. If you know about this file you can choose to ignore it to exclude it from future scans.",
|
210 |
-
'data' => array(
|
211 |
'file' => $file,
|
212 |
-
|
213 |
-
'canFix' => false,
|
214 |
-
'canDelete' => true
|
215 |
-
)
|
216 |
));
|
217 |
break;
|
218 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
219 |
}
|
220 |
if(wfConfig::get('scansEnabled_highSense')){
|
221 |
$badStringFound = false;
|
@@ -236,13 +261,10 @@ class wordfenceScanner {
|
|
236 |
'ignoreC' => $fileSum,
|
237 |
'shortMsg' => "This file may contain malicious executable code: " . esc_html($this->path . $file),
|
238 |
'longMsg' => "This file is a PHP executable file and contains the word 'eval' (without quotes) and the word '" . esc_html($badStringFound) . "' (without quotes). The eval() function along with an encoding function like the one mentioned are commonly used by hackers to hide their code. If you know about this file you can choose to ignore it to exclude it from future scans.",
|
239 |
-
'data' => array(
|
240 |
'file' => $file,
|
241 |
-
|
242 |
-
|
243 |
-
'canDelete' => true
|
244 |
-
)
|
245 |
-
));
|
246 |
break;
|
247 |
}
|
248 |
}
|
@@ -278,6 +300,8 @@ class wordfenceScanner {
|
|
278 |
}
|
279 |
$this->urlHoover->cleanup();
|
280 |
foreach($hooverResults as $file => $hresults){
|
|
|
|
|
281 |
foreach($hresults as $result){
|
282 |
if(preg_match('/wfBrowscapCache\.php$/', $file)){
|
283 |
continue;
|
@@ -291,14 +315,11 @@ class wordfenceScanner {
|
|
291 |
'ignoreC' => md5_file($this->path . $file),
|
292 |
'shortMsg' => "File contains suspected malware URL: " . esc_html($this->path . $file),
|
293 |
'longMsg' => "This file contains a suspected malware URL listed on Google's list of malware sites. Wordfence decodes " . esc_html($this->patterns['word3']) . " when scanning files so the URL may not be visible if you view this file. The URL is: " . esc_html($result['URL']) . " - More info available at <a href=\"http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=" . urlencode($result['URL']) . "&client=googlechrome&hl=en-US\" target=\"_blank\">Google Safe Browsing diagnostic page</a>.",
|
294 |
-
'data' => array(
|
295 |
'file' => $file,
|
296 |
'badURL' => $result['URL'],
|
297 |
-
'canDiff' => false,
|
298 |
-
'canFix' => false,
|
299 |
-
'canDelete' => true,
|
300 |
'gsb' => 'goog-malware-shavar'
|
301 |
-
)
|
302 |
));
|
303 |
}
|
304 |
} else if($result['badList'] == 'googpub-phish-shavar'){
|
@@ -310,14 +331,11 @@ class wordfenceScanner {
|
|
310 |
'ignoreC' => md5_file($this->path . $file),
|
311 |
'shortMsg' => "File contains suspected phishing URL: " . esc_html($this->path . $file),
|
312 |
'longMsg' => "This file contains a URL that is a suspected phishing site that is currently listed on Google's list of known phishing sites. The URL is: " . esc_html($result['URL']),
|
313 |
-
'data' => array(
|
314 |
'file' => $file,
|
315 |
'badURL' => $result['URL'],
|
316 |
-
'canDiff' => false,
|
317 |
-
'canFix' => false,
|
318 |
-
'canDelete' => true,
|
319 |
'gsb' => 'googpub-phish-shavar'
|
320 |
-
)
|
321 |
));
|
322 |
}
|
323 |
}
|
@@ -356,48 +374,45 @@ class wordfenceScanner {
|
|
356 |
}
|
357 |
return false;
|
358 |
}
|
359 |
-
}
|
360 |
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
if (
|
369 |
-
$
|
370 |
-
|
371 |
-
if (!$this->lastStatusTime) {
|
372 |
-
$this->lastStatusTime = microtime(true);
|
373 |
-
}
|
374 |
-
$db = new wfDB();
|
375 |
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
|
|
|
|
|
|
|
|
380 |
|
381 |
-
|
382 |
-
|
383 |
-
$
|
384 |
-
|
385 |
-
'
|
386 |
-
'
|
387 |
-
'
|
388 |
-
'shortMsg' => "This option may contain malicious executable code: " . esc_html($row['option_name']),
|
389 |
-
'longMsg' => "This option appears to be inserted by a hacker to perform malicious activity. If you know about this option you can choose to ignore it to exclude it from future scans. The text we found in this file that matches a known malicious file is: <strong style=\"color: #F00;\">\"" . esc_html($matches[1]). "\"</strong>.",
|
390 |
-
'data' => array(
|
391 |
-
'option_name' => $row['option_name'],
|
392 |
-
'site_id' => $blog['blog_id'],
|
393 |
-
'canDelete' => true,
|
394 |
-
),
|
395 |
));
|
396 |
}
|
397 |
}
|
398 |
|
399 |
-
|
|
|
|
|
|
|
|
|
400 |
}
|
401 |
}
|
402 |
|
|
|
403 |
?>
|
15 |
protected $patterns = "";
|
16 |
protected $api = false;
|
17 |
protected static $excludePattern = NULL;
|
18 |
+
/** @var wfScanEngine */
|
19 |
+
protected $scanEngine;
|
20 |
+
|
21 |
public function __sleep(){
|
22 |
+
return array('path', 'results', 'errorMsg', 'apiKey', 'wordpressVersion', 'urlHoover', 'totalFilesScanned',
|
23 |
+
'startTime', 'lastStatusTime', 'patterns', 'scanEngine');
|
24 |
}
|
25 |
public function __wakeup(){
|
26 |
}
|
42 |
}
|
43 |
|
44 |
/**
|
45 |
+
* Get scan regexes from noc1 and add any user defined regexes, including descriptions, ID's and time added.
|
46 |
* @todo add caching to this.
|
47 |
* @throws Exception
|
48 |
*/
|
49 |
protected function setupSigs() {
|
50 |
$this->api = new wfAPI($this->apiKey, $this->wordpressVersion);
|
51 |
$sigData = $this->api->call('get_patterns', array(), array());
|
52 |
+
if(! (is_array($sigData) && isset($sigData['rules'])) ){
|
|
|
|
|
53 |
throw new Exception("Wordfence could not get the attack signature patterns from the scanning server.");
|
54 |
}
|
55 |
+
|
56 |
+
if (is_array($sigData['rules'])) {
|
57 |
+
$finalPattern = '/';
|
58 |
+
foreach ($sigData['rules'] as $signatureRow) {
|
59 |
+
list($id, $timeAdded, $pattern, $description) = $signatureRow;
|
60 |
+
$finalPattern .= ($pattern . '|');
|
61 |
+
}
|
62 |
+
$finalPattern = substr($finalPattern, 0, -1) . '/i';
|
63 |
+
if(@preg_match($finalPattern, null) === false){
|
64 |
+
throw new Exception("The regex Wordfence received from it's servers is invalid. The pattern is: " . $finalPattern);
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
$extra = wfConfig::get('scan_include_extra');
|
69 |
+
if (!empty($extra)) {
|
70 |
+
$regexs = explode("\n", $extra);
|
71 |
+
$id = 1000001;
|
72 |
+
foreach($regexs as $r){
|
73 |
+
$r = rtrim($r, "\r");
|
74 |
+
try {
|
75 |
+
preg_match('/' . $r . '/i', "");
|
76 |
+
} catch(Exception $e){
|
77 |
+
throw new Exception("The following user defined scan pattern has an error: $r");
|
78 |
+
}
|
79 |
+
$sigData['rules'][] = array($id++, time(), $r, "User defined scan pattern");
|
80 |
+
}
|
81 |
+
}
|
82 |
+
|
83 |
$this->patterns = $sigData;
|
84 |
+
if (isset($this->patterns['signatureUpdateTime'])) {
|
85 |
+
wfConfig::set('signatureUpdateTime', $this->patterns['signatureUpdateTime']);
|
86 |
+
}
|
87 |
}
|
88 |
|
89 |
/**
|
112 |
return self::$excludePattern;
|
113 |
}
|
114 |
|
115 |
+
/**
|
116 |
+
* @param wfScanEngine $forkObj
|
117 |
+
* @return array
|
118 |
+
*/
|
119 |
public function scan($forkObj){
|
120 |
+
$this->scanEngine = $forkObj;
|
121 |
+
$loader = $this->scanEngine->getKnownFilesLoader();
|
122 |
if(! $this->startTime){
|
123 |
$this->startTime = microtime(true);
|
124 |
}
|
194 |
continue;
|
195 |
}
|
196 |
$totalRead = 0;
|
197 |
+
|
198 |
+
$dataForFile = $this->dataForFile($file);
|
199 |
+
|
200 |
while(! feof($fh)){
|
201 |
$data = fread($fh, 1 * 1024 * 1024); //read 1 megs max per chunk
|
202 |
$totalRead += strlen($data);
|
213 |
'ignoreC' => $fileSum,
|
214 |
'shortMsg' => "File is an old version of TimThumb which is vulnerable.",
|
215 |
'longMsg' => "This file appears to be an old version of the TimThumb script which makes your system vulnerable to attackers. Please upgrade the theme or plugin that uses this or remove it.",
|
216 |
+
'data' => array_merge(array(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
'file' => $file,
|
218 |
+
), $dataForFile),
|
|
|
|
|
|
|
219 |
));
|
220 |
break;
|
221 |
}
|
222 |
+
} else if(strpos($file, 'lib/wordfenceScanner.php') === false){ // && preg_match($this->patterns['sigPattern'], $data, $matches)){
|
223 |
+
$regexMatched = false;
|
224 |
+
foreach($this->patterns['rules'] as $rule){
|
225 |
+
if(preg_match('/(' . $rule[2] . ')/i', $data, $matches)){
|
226 |
+
if(! $this->isSafeFile($this->path . $file)){
|
227 |
+
$this->addResult(array(
|
228 |
+
'type' => 'file',
|
229 |
+
'severity' => 1,
|
230 |
+
'ignoreP' => $this->path . $file,
|
231 |
+
'ignoreC' => $fileSum,
|
232 |
+
'shortMsg' => "File appears to be malicious: " . esc_html($file),
|
233 |
+
'longMsg' => "This file appears to be installed by a hacker to perform malicious activity. If you know about this file you can choose to ignore it to exclude it from future scans. The text we found in this file that matches a known malicious file is: <strong style=\"color: #F00;\">\"" . esc_html((strlen($matches[1]) > 200 ? substr($matches[1], 0, 200) . '...' : $matches[1])) . "\"</strong>. The infection type is: <strong>" . esc_html($rule[3]) . '</strong>',
|
234 |
+
'data' => array_merge(array(
|
235 |
+
'file' => $file,
|
236 |
+
), $dataForFile),
|
237 |
+
));
|
238 |
+
$regexMatched = true;
|
239 |
+
break;
|
240 |
+
}
|
241 |
+
}
|
242 |
+
}
|
243 |
+
if($regexMatched){ break; }
|
244 |
}
|
245 |
if(wfConfig::get('scansEnabled_highSense')){
|
246 |
$badStringFound = false;
|
261 |
'ignoreC' => $fileSum,
|
262 |
'shortMsg' => "This file may contain malicious executable code: " . esc_html($this->path . $file),
|
263 |
'longMsg' => "This file is a PHP executable file and contains the word 'eval' (without quotes) and the word '" . esc_html($badStringFound) . "' (without quotes). The eval() function along with an encoding function like the one mentioned are commonly used by hackers to hide their code. If you know about this file you can choose to ignore it to exclude it from future scans.",
|
264 |
+
'data' => array_merge(array(
|
265 |
'file' => $file,
|
266 |
+
), $dataForFile),
|
267 |
+
));
|
|
|
|
|
|
|
268 |
break;
|
269 |
}
|
270 |
}
|
300 |
}
|
301 |
$this->urlHoover->cleanup();
|
302 |
foreach($hooverResults as $file => $hresults){
|
303 |
+
$dataForFile = $this->dataForFile($file);
|
304 |
+
|
305 |
foreach($hresults as $result){
|
306 |
if(preg_match('/wfBrowscapCache\.php$/', $file)){
|
307 |
continue;
|
315 |
'ignoreC' => md5_file($this->path . $file),
|
316 |
'shortMsg' => "File contains suspected malware URL: " . esc_html($this->path . $file),
|
317 |
'longMsg' => "This file contains a suspected malware URL listed on Google's list of malware sites. Wordfence decodes " . esc_html($this->patterns['word3']) . " when scanning files so the URL may not be visible if you view this file. The URL is: " . esc_html($result['URL']) . " - More info available at <a href=\"http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=" . urlencode($result['URL']) . "&client=googlechrome&hl=en-US\" target=\"_blank\">Google Safe Browsing diagnostic page</a>.",
|
318 |
+
'data' => array_merge(array(
|
319 |
'file' => $file,
|
320 |
'badURL' => $result['URL'],
|
|
|
|
|
|
|
321 |
'gsb' => 'goog-malware-shavar'
|
322 |
+
), $dataForFile),
|
323 |
));
|
324 |
}
|
325 |
} else if($result['badList'] == 'googpub-phish-shavar'){
|
331 |
'ignoreC' => md5_file($this->path . $file),
|
332 |
'shortMsg' => "File contains suspected phishing URL: " . esc_html($this->path . $file),
|
333 |
'longMsg' => "This file contains a URL that is a suspected phishing site that is currently listed on Google's list of known phishing sites. The URL is: " . esc_html($result['URL']),
|
334 |
+
'data' => array_merge(array(
|
335 |
'file' => $file,
|
336 |
'badURL' => $result['URL'],
|
|
|
|
|
|
|
337 |
'gsb' => 'googpub-phish-shavar'
|
338 |
+
), $dataForFile),
|
339 |
));
|
340 |
}
|
341 |
}
|
374 |
}
|
375 |
return false;
|
376 |
}
|
|
|
377 |
|
378 |
+
/**
|
379 |
+
* @param string $file
|
380 |
+
* @return array
|
381 |
+
*/
|
382 |
+
private function dataForFile($file) {
|
383 |
+
$loader = $this->scanEngine->getKnownFilesLoader();
|
384 |
+
$data = array();
|
385 |
+
if ($isKnownFile = $loader->isKnownFile($file)) {
|
386 |
+
if ($loader->isKnownCoreFile($file)) {
|
387 |
+
$data['cType'] = 'core';
|
|
|
|
|
|
|
|
|
388 |
|
389 |
+
} else if ($loader->isKnownPluginFile($file)) {
|
390 |
+
$data['cType'] = 'plugin';
|
391 |
+
list($itemName, $itemVersion, $cKey) = $loader->getKnownPluginData($file);
|
392 |
+
$data = array_merge($data, array(
|
393 |
+
'cName' => $itemName,
|
394 |
+
'cVersion' => $itemVersion,
|
395 |
+
'cKey' => $cKey
|
396 |
+
));
|
397 |
|
398 |
+
} else if ($loader->isKnownThemeFile($file)) {
|
399 |
+
$data['cType'] = 'theme';
|
400 |
+
list($itemName, $itemVersion, $cKey) = $loader->getKnownThemeData($file);
|
401 |
+
$data = array_merge($data, array(
|
402 |
+
'cName' => $itemName,
|
403 |
+
'cVersion' => $itemVersion,
|
404 |
+
'cKey' => $cKey
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
405 |
));
|
406 |
}
|
407 |
}
|
408 |
|
409 |
+
$data['canDiff'] = $isKnownFile;
|
410 |
+
$data['canFix'] = $isKnownFile;
|
411 |
+
$data['canDelete'] = !$isKnownFile;
|
412 |
+
|
413 |
+
return $data;
|
414 |
}
|
415 |
}
|
416 |
|
417 |
+
|
418 |
?>
|
readme.txt
CHANGED
@@ -1,15 +1,15 @@
|
|
1 |
=== Wordfence Security ===
|
2 |
Contributors: mmaunder
|
3 |
-
Tags: wordpress, security, 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, 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.
|
6 |
-
Stable tag: 6.
|
7 |
|
8 |
The Wordfence WordPress security plugin provides free enterprise-class WordPress security, protecting your website from hacks and malware.
|
9 |
== Description ==
|
10 |
= THE MOST DOWNLOADED WORDPRESS SECURITY PLUGIN =
|
11 |
|
12 |
-
Wordfence
|
13 |
|
14 |
Wordfence Security is 100% free and open source. We also offer a Premium API key that gives you Premium Support, Country Blocking, Scheduled Scans, Password Auditing and we even check if your website IP address is being used to Spamvertize. [Click here to sign-up for Wordfence Premium now](http://www.wordfence.com/?utm_source=repo&utm_medium=web&utm_campaign=pluginDescCTA) or simply install Wordfence free and start protecting your website.
|
15 |
|
@@ -23,6 +23,11 @@ Wordfence Security is now Multi-Site compatible and includes Cellphone Sign-in w
|
|
23 |
|
24 |
= WORDFENCE WORDPRESS SECURITY FEATURES =
|
25 |
|
|
|
|
|
|
|
|
|
|
|
26 |
= Blocking Features =
|
27 |
* Real-time blocking of known attackers. If another site using Wordfence is attacked and blocks the attacker, your site is automatically protected.
|
28 |
* Block entire malicious networks. Includes advanced IP and Domain WHOIS to report malicious IP's or networks and block entire networks using the firewall. Report security threats to network owner.
|
@@ -190,6 +195,14 @@ Designed for every skill level, [The WordPress Security Learning Center](https:/
|
|
190 |
|
191 |
== Changelog ==
|
192 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
193 |
= 6.0.25 =
|
194 |
* Improvement: Added help callout for compromised sites.
|
195 |
* Improvement: Updated local GeoIP database.
|
1 |
=== Wordfence Security ===
|
2 |
Contributors: mmaunder
|
3 |
+
Tags: wordpress, security, web application firewall, waf, performance, speed, caching, cache, caching plugin, wordpress cache, wordpress caching, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure, two factor, cellphone sign-in, cellphone signin, cellphone, twofactor, security, secure, htaccess, login, log, users, login alerts, lock, chmod, maintenance, plugin, private, privacy, protection, permissions, 503, base64, injection, code, encode, script, attack, hack, hackers, block, blocked, prevent, prevention, RFI, XSS, CRLF, CSRF, SQL Injection, vulnerability, website security, WordPress security, security log, logging, HTTP log, error log, login security, personal security, infrastructure security, firewall security, front-end security, web server security, proxy security, reverse proxy security, secure website, secure login, two factor security, two factor authentication, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm, woocommerce support, woocommerce caching, IPv6, IP version 6
|
4 |
Requires at least: 3.9
|
5 |
+
Tested up to: 4.5
|
6 |
+
Stable tag: 6.1.1
|
7 |
|
8 |
The Wordfence WordPress security plugin provides free enterprise-class WordPress security, protecting your website from hacks and malware.
|
9 |
== Description ==
|
10 |
= THE MOST DOWNLOADED WORDPRESS SECURITY PLUGIN =
|
11 |
|
12 |
+
Wordfence provides the best protection available for your website. Powered by the constantly updated Threat Defense Feed, our Web Application Firewall stops you from getting hacked. Wordfence Scan leverages the same proprietary feed, alerting you quickly in the event your site is compromised. Our Live Traffic view gives you real-time visibility into traffic and hack attempts on your website. A deep set of addtional tools round out the most complete WordPress security solution available.
|
13 |
|
14 |
Wordfence Security is 100% free and open source. We also offer a Premium API key that gives you Premium Support, Country Blocking, Scheduled Scans, Password Auditing and we even check if your website IP address is being used to Spamvertize. [Click here to sign-up for Wordfence Premium now](http://www.wordfence.com/?utm_source=repo&utm_medium=web&utm_campaign=pluginDescCTA) or simply install Wordfence free and start protecting your website.
|
15 |
|
23 |
|
24 |
= WORDFENCE WORDPRESS SECURITY FEATURES =
|
25 |
|
26 |
+
= WordPress Firewall =
|
27 |
+
* Web Application Firewall stops you from getting hacked by identifying malicious traffic, blocking attackers before they can access your website.
|
28 |
+
* Threat Defense Feed automatically updates firewall rules that protect you from the latest threats. Premium members receive the real-time version.
|
29 |
+
* Block common security threats like fake Googlebots, malicious scans from hackers and botnets.
|
30 |
+
|
31 |
= Blocking Features =
|
32 |
* Real-time blocking of known attackers. If another site using Wordfence is attacked and blocks the attacker, your site is automatically protected.
|
33 |
* Block entire malicious networks. Includes advanced IP and Domain WHOIS to report malicious IP's or networks and block entire networks using the firewall. Report security threats to network owner.
|
195 |
|
196 |
== Changelog ==
|
197 |
|
198 |
+
= 6.1.1 =
|
199 |
+
* Enhancement: Added Web Application Firewall
|
200 |
+
* Enhancement: Added Diagnostics page
|
201 |
+
* Enhancement: Added new scans:
|
202 |
+
* Admins created outside of WordPress
|
203 |
+
* Publicly accessible common (database or wp-config.php) backup files
|
204 |
+
* Improvement: Updated Live Traffic with filters and to include blocked requests in the feed.
|
205 |
+
|
206 |
= 6.0.25 =
|
207 |
* Improvement: Added help callout for compromised sites.
|
208 |
* Improvement: Updated local GeoIP database.
|
vendor/autoload.php
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload.php @generated by Composer
|
4 |
+
|
5 |
+
require_once __DIR__ . '/composer' . '/autoload_real.php';
|
6 |
+
|
7 |
+
return ComposerAutoloaderInit8dd399f3311032288f54211d3b2a0e01::getLoader();
|
vendor/composer/ClassLoader.php
ADDED
@@ -0,0 +1,413 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Composer.
|
5 |
+
*
|
6 |
+
* (c) Nils Adermann <naderman@naderman.de>
|
7 |
+
* Jordi Boggiano <j.boggiano@seld.be>
|
8 |
+
*
|
9 |
+
* For the full copyright and license information, please view the LICENSE
|
10 |
+
* file that was distributed with this source code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace Composer\Autoload;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
17 |
+
*
|
18 |
+
* $loader = new \Composer\Autoload\ClassLoader();
|
19 |
+
*
|
20 |
+
* // register classes with namespaces
|
21 |
+
* $loader->add('Symfony\Component', __DIR__.'/component');
|
22 |
+
* $loader->add('Symfony', __DIR__.'/framework');
|
23 |
+
*
|
24 |
+
* // activate the autoloader
|
25 |
+
* $loader->register();
|
26 |
+
*
|
27 |
+
* // to enable searching the include path (eg. for PEAR packages)
|
28 |
+
* $loader->setUseIncludePath(true);
|
29 |
+
*
|
30 |
+
* In this example, if you try to use a class in the Symfony\Component
|
31 |
+
* namespace or one of its children (Symfony\Component\Console for instance),
|
32 |
+
* the autoloader will first look for the class under the component/
|
33 |
+
* directory, and it will then fallback to the framework/ directory if not
|
34 |
+
* found before giving up.
|
35 |
+
*
|
36 |
+
* This class is loosely based on the Symfony UniversalClassLoader.
|
37 |
+
*
|
38 |
+
* @author Fabien Potencier <fabien@symfony.com>
|
39 |
+
* @author Jordi Boggiano <j.boggiano@seld.be>
|
40 |
+
* @see http://www.php-fig.org/psr/psr-0/
|
41 |
+
* @see http://www.php-fig.org/psr/psr-4/
|
42 |
+
*/
|
43 |
+
class ClassLoader
|
44 |
+
{
|
45 |
+
// PSR-4
|
46 |
+
private $prefixLengthsPsr4 = array();
|
47 |
+
private $prefixDirsPsr4 = array();
|
48 |
+
private $fallbackDirsPsr4 = array();
|
49 |
+
|
50 |
+
// PSR-0
|
51 |
+
private $prefixesPsr0 = array();
|
52 |
+
private $fallbackDirsPsr0 = array();
|
53 |
+
|
54 |
+
private $useIncludePath = false;
|
55 |
+
private $classMap = array();
|
56 |
+
|
57 |
+
private $classMapAuthoritative = false;
|
58 |
+
|
59 |
+
public function getPrefixes()
|
60 |
+
{
|
61 |
+
if (!empty($this->prefixesPsr0)) {
|
62 |
+
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
63 |
+
}
|
64 |
+
|
65 |
+
return array();
|
66 |
+
}
|
67 |
+
|
68 |
+
public function getPrefixesPsr4()
|
69 |
+
{
|
70 |
+
return $this->prefixDirsPsr4;
|
71 |
+
}
|
72 |
+
|
73 |
+
public function getFallbackDirs()
|
74 |
+
{
|
75 |
+
return $this->fallbackDirsPsr0;
|
76 |
+
}
|
77 |
+
|
78 |
+
public function getFallbackDirsPsr4()
|
79 |
+
{
|
80 |
+
return $this->fallbackDirsPsr4;
|
81 |
+
}
|
82 |
+
|
83 |
+
public function getClassMap()
|
84 |
+
{
|
85 |
+
return $this->classMap;
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* @param array $classMap Class to filename map
|
90 |
+
*/
|
91 |
+
public function addClassMap(array $classMap)
|
92 |
+
{
|
93 |
+
if ($this->classMap) {
|
94 |
+
$this->classMap = array_merge($this->classMap, $classMap);
|
95 |
+
} else {
|
96 |
+
$this->classMap = $classMap;
|
97 |
+
}
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Registers a set of PSR-0 directories for a given prefix, either
|
102 |
+
* appending or prepending to the ones previously set for this prefix.
|
103 |
+
*
|
104 |
+
* @param string $prefix The prefix
|
105 |
+
* @param array|string $paths The PSR-0 root directories
|
106 |
+
* @param bool $prepend Whether to prepend the directories
|
107 |
+
*/
|
108 |
+
public function add($prefix, $paths, $prepend = false)
|
109 |
+
{
|
110 |
+
if (!$prefix) {
|
111 |
+
if ($prepend) {
|
112 |
+
$this->fallbackDirsPsr0 = array_merge(
|
113 |
+
(array) $paths,
|
114 |
+
$this->fallbackDirsPsr0
|
115 |
+
);
|
116 |
+
} else {
|
117 |
+
$this->fallbackDirsPsr0 = array_merge(
|
118 |
+
$this->fallbackDirsPsr0,
|
119 |
+
(array) $paths
|
120 |
+
);
|
121 |
+
}
|
122 |
+
|
123 |
+
return;
|
124 |
+
}
|
125 |
+
|
126 |
+
$first = $prefix[0];
|
127 |
+
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
128 |
+
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
129 |
+
|
130 |
+
return;
|
131 |
+
}
|
132 |
+
if ($prepend) {
|
133 |
+
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
134 |
+
(array) $paths,
|
135 |
+
$this->prefixesPsr0[$first][$prefix]
|
136 |
+
);
|
137 |
+
} else {
|
138 |
+
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
139 |
+
$this->prefixesPsr0[$first][$prefix],
|
140 |
+
(array) $paths
|
141 |
+
);
|
142 |
+
}
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Registers a set of PSR-4 directories for a given namespace, either
|
147 |
+
* appending or prepending to the ones previously set for this namespace.
|
148 |
+
*
|
149 |
+
* @param string $prefix The prefix/namespace, with trailing '\\'
|
150 |
+
* @param array|string $paths The PSR-0 base directories
|
151 |
+
* @param bool $prepend Whether to prepend the directories
|
152 |
+
*
|
153 |
+
* @throws \InvalidArgumentException
|
154 |
+
*/
|
155 |
+
public function addPsr4($prefix, $paths, $prepend = false)
|
156 |
+
{
|
157 |
+
if (!$prefix) {
|
158 |
+
// Register directories for the root namespace.
|
159 |
+
if ($prepend) {
|
160 |
+
$this->fallbackDirsPsr4 = array_merge(
|
161 |
+
(array) $paths,
|
162 |
+
$this->fallbackDirsPsr4
|
163 |
+
);
|
164 |
+
} else {
|
165 |
+
$this->fallbackDirsPsr4 = array_merge(
|
166 |
+
$this->fallbackDirsPsr4,
|
167 |
+
(array) $paths
|
168 |
+
);
|
169 |
+
}
|
170 |
+
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
171 |
+
// Register directories for a new namespace.
|
172 |
+
$length = strlen($prefix);
|
173 |
+
if ('\\' !== $prefix[$length - 1]) {
|
174 |
+
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
175 |
+
}
|
176 |
+
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
177 |
+
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
178 |
+
} elseif ($prepend) {
|
179 |
+
// Prepend directories for an already registered namespace.
|
180 |
+
$this->prefixDirsPsr4[$prefix] = array_merge(
|
181 |
+
(array) $paths,
|
182 |
+
$this->prefixDirsPsr4[$prefix]
|
183 |
+
);
|
184 |
+
} else {
|
185 |
+
// Append directories for an already registered namespace.
|
186 |
+
$this->prefixDirsPsr4[$prefix] = array_merge(
|
187 |
+
$this->prefixDirsPsr4[$prefix],
|
188 |
+
(array) $paths
|
189 |
+
);
|
190 |
+
}
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* Registers a set of PSR-0 directories for a given prefix,
|
195 |
+
* replacing any others previously set for this prefix.
|
196 |
+
*
|
197 |
+
* @param string $prefix The prefix
|
198 |
+
* @param array|string $paths The PSR-0 base directories
|
199 |
+
*/
|
200 |
+
public function set($prefix, $paths)
|
201 |
+
{
|
202 |
+
if (!$prefix) {
|
203 |
+
$this->fallbackDirsPsr0 = (array) $paths;
|
204 |
+
} else {
|
205 |
+
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Registers a set of PSR-4 directories for a given namespace,
|
211 |
+
* replacing any others previously set for this namespace.
|
212 |
+
*
|
213 |
+
* @param string $prefix The prefix/namespace, with trailing '\\'
|
214 |
+
* @param array|string $paths The PSR-4 base directories
|
215 |
+
*
|
216 |
+
* @throws \InvalidArgumentException
|
217 |
+
*/
|
218 |
+
public function setPsr4($prefix, $paths)
|
219 |
+
{
|
220 |
+
if (!$prefix) {
|
221 |
+
$this->fallbackDirsPsr4 = (array) $paths;
|
222 |
+
} else {
|
223 |
+
$length = strlen($prefix);
|
224 |
+
if ('\\' !== $prefix[$length - 1]) {
|
225 |
+
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
226 |
+
}
|
227 |
+
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
228 |
+
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
229 |
+
}
|
230 |
+
}
|
231 |
+
|
232 |
+
/**
|
233 |
+
* Turns on searching the include path for class files.
|
234 |
+
*
|
235 |
+
* @param bool $useIncludePath
|
236 |
+
*/
|
237 |
+
public function setUseIncludePath($useIncludePath)
|
238 |
+
{
|
239 |
+
$this->useIncludePath = $useIncludePath;
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* Can be used to check if the autoloader uses the include path to check
|
244 |
+
* for classes.
|
245 |
+
*
|
246 |
+
* @return bool
|
247 |
+
*/
|
248 |
+
public function getUseIncludePath()
|
249 |
+
{
|
250 |
+
return $this->useIncludePath;
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Turns off searching the prefix and fallback directories for classes
|
255 |
+
* that have not been registered with the class map.
|
256 |
+
*
|
257 |
+
* @param bool $classMapAuthoritative
|
258 |
+
*/
|
259 |
+
public function setClassMapAuthoritative($classMapAuthoritative)
|
260 |
+
{
|
261 |
+
$this->classMapAuthoritative = $classMapAuthoritative;
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Should class lookup fail if not found in the current class map?
|
266 |
+
*
|
267 |
+
* @return bool
|
268 |
+
*/
|
269 |
+
public function isClassMapAuthoritative()
|
270 |
+
{
|
271 |
+
return $this->classMapAuthoritative;
|
272 |
+
}
|
273 |
+
|
274 |
+
/**
|
275 |
+
* Registers this instance as an autoloader.
|
276 |
+
*
|
277 |
+
* @param bool $prepend Whether to prepend the autoloader or not
|
278 |
+
*/
|
279 |
+
public function register($prepend = false)
|
280 |
+
{
|
281 |
+
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* Unregisters this instance as an autoloader.
|
286 |
+
*/
|
287 |
+
public function unregister()
|
288 |
+
{
|
289 |
+
spl_autoload_unregister(array($this, 'loadClass'));
|
290 |
+
}
|
291 |
+
|
292 |
+
/**
|
293 |
+
* Loads the given class or interface.
|
294 |
+
*
|
295 |
+
* @param string $class The name of the class
|
296 |
+
* @return bool|null True if loaded, null otherwise
|
297 |
+
*/
|
298 |
+
public function loadClass($class)
|
299 |
+
{
|
300 |
+
if ($file = $this->findFile($class)) {
|
301 |
+
includeFile($file);
|
302 |
+
|
303 |
+
return true;
|
304 |
+
}
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* Finds the path to the file where the class is defined.
|
309 |
+
*
|
310 |
+
* @param string $class The name of the class
|
311 |
+
*
|
312 |
+
* @return string|false The path if found, false otherwise
|
313 |
+
*/
|
314 |
+
public function findFile($class)
|
315 |
+
{
|
316 |
+
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
|
317 |
+
if ('\\' == $class[0]) {
|
318 |
+
$class = substr($class, 1);
|
319 |
+
}
|
320 |
+
|
321 |
+
// class map lookup
|
322 |
+
if (isset($this->classMap[$class])) {
|
323 |
+
return $this->classMap[$class];
|
324 |
+
}
|
325 |
+
if ($this->classMapAuthoritative) {
|
326 |
+
return false;
|
327 |
+
}
|
328 |
+
|
329 |
+
$file = $this->findFileWithExtension($class, '.php');
|
330 |
+
|
331 |
+
// Search for Hack files if we are running on HHVM
|
332 |
+
if ($file === null && defined('HHVM_VERSION')) {
|
333 |
+
$file = $this->findFileWithExtension($class, '.hh');
|
334 |
+
}
|
335 |
+
|
336 |
+
if ($file === null) {
|
337 |
+
// Remember that this class does not exist.
|
338 |
+
return $this->classMap[$class] = false;
|
339 |
+
}
|
340 |
+
|
341 |
+
return $file;
|
342 |
+
}
|
343 |
+
|
344 |
+
private function findFileWithExtension($class, $ext)
|
345 |
+
{
|
346 |
+
// PSR-4 lookup
|
347 |
+
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
348 |
+
|
349 |
+
$first = $class[0];
|
350 |
+
if (isset($this->prefixLengthsPsr4[$first])) {
|
351 |
+
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
|
352 |
+
if (0 === strpos($class, $prefix)) {
|
353 |
+
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
|
354 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
355 |
+
return $file;
|
356 |
+
}
|
357 |
+
}
|
358 |
+
}
|
359 |
+
}
|
360 |
+
}
|
361 |
+
|
362 |
+
// PSR-4 fallback dirs
|
363 |
+
foreach ($this->fallbackDirsPsr4 as $dir) {
|
364 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
365 |
+
return $file;
|
366 |
+
}
|
367 |
+
}
|
368 |
+
|
369 |
+
// PSR-0 lookup
|
370 |
+
if (false !== $pos = strrpos($class, '\\')) {
|
371 |
+
// namespaced class name
|
372 |
+
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
373 |
+
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
374 |
+
} else {
|
375 |
+
// PEAR-like class name
|
376 |
+
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
377 |
+
}
|
378 |
+
|
379 |
+
if (isset($this->prefixesPsr0[$first])) {
|
380 |
+
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
381 |
+
if (0 === strpos($class, $prefix)) {
|
382 |
+
foreach ($dirs as $dir) {
|
383 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
384 |
+
return $file;
|
385 |
+
}
|
386 |
+
}
|
387 |
+
}
|
388 |
+
}
|
389 |
+
}
|
390 |
+
|
391 |
+
// PSR-0 fallback dirs
|
392 |
+
foreach ($this->fallbackDirsPsr0 as $dir) {
|
393 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
394 |
+
return $file;
|
395 |
+
}
|
396 |
+
}
|
397 |
+
|
398 |
+
// PSR-0 include paths.
|
399 |
+
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
400 |
+
return $file;
|
401 |
+
}
|
402 |
+
}
|
403 |
+
}
|
404 |
+
|
405 |
+
/**
|
406 |
+
* Scope isolated include.
|
407 |
+
*
|
408 |
+
* Prevents access to $this/self from included files.
|
409 |
+
*/
|
410 |
+
function includeFile($file)
|
411 |
+
{
|
412 |
+
include $file;
|
413 |
+
}
|
vendor/composer/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
Copyright (c) 2015 Nils Adermann, Jordi Boggiano
|
3 |
+
|
4 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5 |
+
of this software and associated documentation files (the "Software"), to deal
|
6 |
+
in the Software without restriction, including without limitation the rights
|
7 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8 |
+
copies of the Software, and to permit persons to whom the Software is furnished
|
9 |
+
to do so, subject to the following conditions:
|
10 |
+
|
11 |
+
The above copyright notice and this permission notice shall be included in all
|
12 |
+
copies or substantial portions of the Software.
|
13 |
+
|
14 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20 |
+
THE SOFTWARE.
|
21 |
+
|
vendor/composer/autoload_classmap.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_classmap.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
);
|
vendor/composer/autoload_namespaces.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_namespaces.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
);
|
vendor/composer/autoload_psr4.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_psr4.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
);
|
vendor/composer/autoload_real.php
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_real.php @generated by Composer
|
4 |
+
|
5 |
+
class ComposerAutoloaderInit8dd399f3311032288f54211d3b2a0e01
|
6 |
+
{
|
7 |
+
private static $loader;
|
8 |
+
|
9 |
+
public static function loadClassLoader($class)
|
10 |
+
{
|
11 |
+
if ('Composer\Autoload\ClassLoader' === $class) {
|
12 |
+
require __DIR__ . '/ClassLoader.php';
|
13 |
+
}
|
14 |
+
}
|
15 |
+
|
16 |
+
public static function getLoader()
|
17 |
+
{
|
18 |
+
if (null !== self::$loader) {
|
19 |
+
return self::$loader;
|
20 |
+
}
|
21 |
+
|
22 |
+
spl_autoload_register(array('ComposerAutoloaderInit8dd399f3311032288f54211d3b2a0e01', 'loadClassLoader'), true, true);
|
23 |
+
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
24 |
+
spl_autoload_unregister(array('ComposerAutoloaderInit8dd399f3311032288f54211d3b2a0e01', 'loadClassLoader'));
|
25 |
+
|
26 |
+
$map = require __DIR__ . '/autoload_namespaces.php';
|
27 |
+
foreach ($map as $namespace => $path) {
|
28 |
+
$loader->set($namespace, $path);
|
29 |
+
}
|
30 |
+
|
31 |
+
$map = require __DIR__ . '/autoload_psr4.php';
|
32 |
+
foreach ($map as $namespace => $path) {
|
33 |
+
$loader->setPsr4($namespace, $path);
|
34 |
+
}
|
35 |
+
|
36 |
+
$classMap = require __DIR__ . '/autoload_classmap.php';
|
37 |
+
if ($classMap) {
|
38 |
+
$loader->addClassMap($classMap);
|
39 |
+
}
|
40 |
+
|
41 |
+
$loader->register(true);
|
42 |
+
|
43 |
+
return $loader;
|
44 |
+
}
|
45 |
+
}
|
vendor/composer/installed.json
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"name": "wordfence/wf-waf",
|
4 |
+
"version": "1.0.0",
|
5 |
+
"version_normalized": "1.0.0.0",
|
6 |
+
"source": {
|
7 |
+
"type": "git",
|
8 |
+
"url": "https://github.com/wordfence/wf-waf.git",
|
9 |
+
"reference": "origin/master"
|
10 |
+
},
|
11 |
+
"dist": {
|
12 |
+
"type": "zip",
|
13 |
+
"url": "https://github.com/wordfence/wf-waf/zipball/master",
|
14 |
+
"reference": null,
|
15 |
+
"shasum": null
|
16 |
+
},
|
17 |
+
"type": "library",
|
18 |
+
"installation-source": "source"
|
19 |
+
}
|
20 |
+
]
|
vendor/wordfence/wf-waf/src/baseRules.rules
ADDED
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#Thresholds which, if exceeded will cause a rule group to block
|
2 |
+
scores.sqli = 100
|
3 |
+
scores.xss = 100
|
4 |
+
scores.rce = 100
|
5 |
+
|
6 |
+
# Blacklisted URL/params to not whitelist
|
7 |
+
blacklistParam(url='/\/wp\-admin[\/]+admin\-ajax\.php/i', param=request.queryString.action)
|
8 |
+
blacklistParam(url='/\/wp\-admin[\/]+admin\-ajax\.php/i', param=request.queryString.img)
|
9 |
+
blacklistParam(url='/\/wp\-admin[\/]+admin\-ajax\.php/i', param=request.body.action)
|
10 |
+
blacklistParam(url='/\/wp\-admin[\/]+admin\-ajax\.php/i', param=request.body.img)
|
11 |
+
|
12 |
+
# Netsparker
|
13 |
+
blacklistParam(url='/.*/', param=request.body.nsextt)
|
14 |
+
|
15 |
+
# File uploads
|
16 |
+
blacklistParam(url='/\/uploadify\.php$/i', param=request.fileNames.Filedata)
|
17 |
+
blacklistParam(url='/.*/', param=request.fileNames.yiw_contact)
|
18 |
+
blacklistParam(url='/\/license\.php$/i', param=request.fileNames.filename)
|
19 |
+
blacklistParam(url='/\/wp\-admin[\/]+admin\-ajax\.php$/i', param=request.fileNames.update_file)
|
20 |
+
blacklistParam(url='/tiny_mce[\/]+plugins[\/]+tinybrowser[\/]+upload_file\.php$/i', param=request.fileNames.Filedata)
|
21 |
+
blacklistParam(url='/elfinder[\/]+php[\/]+connector\.minimal\.php$/i', param=request.fileNames.upload)
|
22 |
+
|
23 |
+
|
24 |
+
# Whitelisted URL/params to not run through the rules
|
25 |
+
# Trackbacks
|
26 |
+
whitelistParam(url='/.*/', param=request.body.excerpt)
|
27 |
+
|
28 |
+
# Comments
|
29 |
+
whitelistParam(url='/wp-comments-post\.php$/i', param=request.body.comment, rules=[3, 12])
|
30 |
+
whitelistParam(url='/\/wp-admin\/admin-ajax\.php$/i', param=request.body.content)
|
31 |
+
|
32 |
+
# WordPress SEO / Auto-saving
|
33 |
+
whitelistParam(url='/\/wp-admin\/admin-ajax\.php$/i', param=request.body.data)
|
34 |
+
|
35 |
+
# WordPress Plugins/Posts Search
|
36 |
+
whitelistParam(url='/\/wp-admin\/(?:network\/)?(?:plugin(?:s|-install)|edit)\.php$/i', param=request.queryString.s)
|
37 |
+
|
38 |
+
# WAF config page
|
39 |
+
whitelistParam(url='/\/wp-admin\/admin-ajax\.php$/i', param=request.body.whitelistedPath)
|
40 |
+
whitelistParam(url='/\/wp-admin\/admin-ajax\.php$/i', param=request.body.whitelistedParam)
|
41 |
+
whitelistParam(url='/\/wp-admin\/admin-ajax\.php$/i', param=request.body.oldWhitelistedPath)
|
42 |
+
whitelistParam(url='/\/wp-admin\/admin-ajax\.php$/i', param=request.body.oldWhitelistedParam)
|
43 |
+
whitelistParam(url='/\/wp-admin\/admin-ajax\.php$/i', param=request.body.newWhitelistedPath)
|
44 |
+
whitelistParam(url='/\/wp-admin\/admin-ajax\.php$/i', param=request.body.newWhitelistedParam)
|
45 |
+
whitelistParam(url='/\/wp-admin\/admin-ajax\.php$/i', param=request.body.bannedURLs)
|
46 |
+
whitelistParam(url='/\/wp-admin\/admin-ajax\.php$/i', param=request.body.scan_include_extra)
|
47 |
+
|
48 |
+
# WordPress misc
|
49 |
+
whitelistParam(url='/\/wp-admin\/(?:network\/)?(?:plugin|theme)-editor\.php$/i', param=request.body.newcontent)
|
50 |
+
whitelistParam(url='/.{0,1}/', param=request.queryString._wp_http_referer)
|
51 |
+
|
52 |
+
# Plugins page
|
53 |
+
whitelistParam(url='/\/wp-admin\/(?:network\/)?plugins\.php$/i', param=request.queryString.plugin)
|
54 |
+
whitelistParam(url='/\/wp-admin\/(?:network\/)?plugins\.php$/i', param=request.queryString.action)
|
55 |
+
whitelistParam(url='/\/wp-admin\/(?:network\/)?plugins\.php$/i', param=request.queryString.checked)
|
56 |
+
whitelistParam(url='/\/wp-admin\/(?:network\/)?plugins\.php$/i', param=request.body.action)
|
57 |
+
whitelistParam(url='/\/wp-admin\/(?:network\/)?plugins\.php$/i', param=request.body.checked)
|
58 |
+
whitelistParam(url='/\/wp-admin\/(?:network\/)?plugins\.php$/i', param=request.body.submit)
|
59 |
+
|
60 |
+
# Options page
|
61 |
+
whitelistParam(url='/\/wp-admin\/options\.php$/i', param=request.body.blogname)
|
62 |
+
whitelistParam(url='/\/wp-admin\/options\.php$/i', param=request.body.blogdescription)
|
63 |
+
whitelistParam(url='/\/wp-admin\/options\.php$/i', param=request.body.siteurl)
|
64 |
+
whitelistParam(url='/\/wp-admin\/options\.php$/i', param=request.body.home)
|
65 |
+
whitelistParam(url='/\/wp-admin\/options\.php$/i', param=request.body.admin_email)
|
66 |
+
whitelistParam(url='/\/wp-admin\/options\.php$/i', param=request.body.moderation_keys)
|
67 |
+
whitelistParam(url='/\/wp-admin\/options\.php$/i', param=request.body.blacklist_keys)
|
68 |
+
whitelistParam(url='/\/wp-admin\/options\.php$/i', param=request.body.permalink_structure)
|
69 |
+
whitelistParam(url='/\/wp-admin\/options\.php$/i', param=request.body.category_base)
|
70 |
+
whitelistParam(url='/\/wp-admin\/options\.php$/i', param=request.body.tag_base)
|
71 |
+
|
72 |
+
whitelistParam(url='/\/wp-admin\/edit-comments\.php$/i', param=request.queryString.s)
|
73 |
+
|
74 |
+
# WordPress login page
|
75 |
+
whitelistParam(url='/\/wp-login\.php$/i', param=request.body.log)
|
76 |
+
whitelistParam(url='/\/wp-login\.php$/i', param=request.body.pwd)
|
77 |
+
whitelistParam(url='/\/wp-login\.php$/i', param=request.body.redirect_to)
|
78 |
+
|
79 |
+
# Multisite Admin Pages
|
80 |
+
whitelistParam(url='/\/wp-admin\/network\/(?:user|site)s\.php$/i', param=request.queryString.s)
|
81 |
+
whitelistParam(url='/\/wp-admin\/network\/site-new\.php$/i', param=request.body.blog)
|
82 |
+
|
83 |
+
# Deleting WAF whitelist items
|
84 |
+
whitelistParam(url='/\/wp-admin\/admin-ajax\.php$/i', param=request.body.deletedWhitelistedPath)
|
85 |
+
whitelistParam(url='/\/wp-admin\/admin-ajax\.php$/i', param=request.body.deletedWhitelistedParam)
|
86 |
+
|
87 |
+
# iThemes Security
|
88 |
+
whitelistParam(url='/\/wp-admin\/options\.php$/i', param=request.body.itsec_global.log_location)
|
89 |
+
whitelistParam(url='/\/wp-admin\/options\.php$/i', param=request.body.itsec_backup.location)
|
90 |
+
whitelistParam(url='/\/wp-admin\/admin-ajax\.php$/i', param=request.body.dir)
|
91 |
+
|
92 |
+
# PHPMyAdmin
|
93 |
+
whitelistParam(url='/(?:lint|import)\.php$/i', param=request.body.sql_query)
|
94 |
+
|
95 |
+
# SQLi detection
|
96 |
+
sqliRegex = '/(?:[^\w<]|\/\*\![0-9]*|^)(?:
|
97 |
+
@@HOSTNAME|
|
98 |
+
ALTER|ANALYZE|ASENSITIVE|
|
99 |
+
BEFORE|BENCHMARK|BETWEEN|BIGINT|BINARY|BLOB|
|
100 |
+
CALL|CASE|CHANGE|CHAR|CHARACTER|CHAR_LENGTH|COLLATE|COLUMN|CONCAT|CONDITION|CONSTRAINT|CONTINUE|CONVERT|CREATE|CROSS|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|
|
101 |
+
DATABASE|DATABASES|DAY_HOUR|DAY_MICROSECOND|DAY_MINUTE|DAY_SECOND|DECIMAL|DECLARE|DEFAULT|DELAYED|DELETE|DESCRIBE|DETERMINISTIC|DISTINCT|DISTINCTROW|DOUBLE|DROP|DUAL|DUMPFILE|
|
102 |
+
EACH|ELSE|ELSEIF|ELT|ENCLOSED|ESCAPED|EXISTS|EXIT|EXPLAIN|EXTRACTVALUE|
|
103 |
+
FETCH|FLOAT|FLOAT4|FLOAT8|FORCE|FOREIGN|FROM|FULLTEXT|
|
104 |
+
GRANT|GROUP|HAVING|HEX|HIGH_PRIORITY|HOUR_MICROSECOND|HOUR_MINUTE|HOUR_SECOND|
|
105 |
+
IFNULL|IGNORE|INDEX|INFILE|INNER|INOUT|INSENSITIVE|INSERT|INTERVAL|ISNULL|ITERATE|
|
106 |
+
JOIN|KILL|LEADING|LEAVE|LIMIT|LINEAR|LINES|LOAD|LOAD_FILE|LOCALTIME|LOCALTIMESTAMP|LOCK|LONG|LONGBLOB|LONGTEXT|LOOP|LOW_PRIORITY|
|
107 |
+
MASTER_SSL_VERIFY_SERVER_CERT|MATCH|MAXVALUE|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MID|MIDDLEINT|MINUTE_MICROSECOND|MINUTE_SECOND|MODIFIES|
|
108 |
+
NATURAL|NO_WRITE_TO_BINLOG|NULL|NUMERIC|OPTION|ORD|ORDER|OUTER|OUTFILE|
|
109 |
+
PRECISION|PRIMARY|PRIVILEGES|PROCEDURE|PROCESSLIST|PURGE|
|
110 |
+
RANGE|READ_WRITE|REGEXP|RELEASE|REPEAT|REQUIRE|RESIGNAL|RESTRICT|RETURN|REVOKE|RLIKE|ROLLBACK|
|
111 |
+
SCHEMA|SCHEMAS|SECOND_MICROSECOND|SELECT|SENSITIVE|SEPARATOR|SHOW|SIGNAL|SLEEP|SMALLINT|SPATIAL|SPECIFIC|SQLEXCEPTION|SQLSTATE|SQLWARNING|SQL_BIG_RESULT|SQL_CALC_FOUND_ROWS|SQL_SMALL_RESULT|STARTING|STRAIGHT_JOIN|SUBSTR|
|
112 |
+
TABLE|TERMINATED|TINYBLOB|TINYINT|TINYTEXT|TRAILING|TRANSACTION|TRIGGER|
|
113 |
+
UNDO|UNHEX|UNION|UNLOCK|UNSIGNED|UPDATE|UPDATEXML|USAGE|USING|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|
|
114 |
+
VALUES|VARBINARY|VARCHAR|VARCHARACTER|VARYING|WHEN|WHERE|WHILE|WRITE|YEAR_MONTH|ZEROFILL)(?=[^\w]|$)/ix'
|
115 |
+
|
116 |
+
|
117 |
+
xssRegex = '/(?:
|
118 |
+
#tags
|
119 |
+
(?:\<|\+ADw\-|\xC2\xBC)(script|iframe|svg|object|embed|applet|link|style|meta|\/\/|\?xml\-stylesheet)(?:[^\w]|\xC2\xBE)|
|
120 |
+
#protocols
|
121 |
+
(?:^|[^\w])(?:(?:\s*(?:&\#(?:x0*6a|0*106)|j)\s*(?:&\#(?:x0*61|0*97)|a)\s*(?:&\#(?:x0*76|0*118)|v)\s*(?:&\#(?:x0*61|0*97)|a)|\s*(?:&\#(?:x0*76|0*118)|v)\s*(?:&\#(?:x0*62|0*98)|b)|\s*(?:&\#(?:x0*65|0*101)|e)\s*(?:&\#(?:x0*63|0*99)|c)\s*(?:&\#(?:x0*6d|0*109)|m)\s*(?:&\#(?:x0*61|0*97)|a)|\s*(?:&\#(?:x0*6c|0*108)|l)\s*(?:&\#(?:x0*69|0*105)|i)\s*(?:&\#(?:x0*76|0*118)|v)\s*(?:&\#(?:x0*65|0*101)|e))\s*(?:&\#(?:x0*73|0*115)|s)\s*(?:&\#(?:x0*63|0*99)|c)\s*(?:&\#(?:x0*72|0*114)|r)\s*(?:&\#(?:x0*69|0*105)|i)\s*(?:&\#(?:x0*70|0*112)|p)\s*(?:&\#(?:x0*74|0*116)|t)|\s*(?:&\#(?:x0*6d|0*109)|m)\s*(?:&\#(?:x0*68|0*104)|h)\s*(?:&\#(?:x0*74|0*116)|t)\s*(?:&\#(?:x0*6d|0*109)|m)\s*(?:&\#(?:x0*6c|0*108)|l)|\s*(?:&\#(?:x0*6d|0*109)|m)\s*(?:&\#(?:x0*6f|0*111)|o)\s*(?:&\#(?:x0*63|0*99)|c)\s*(?:&\#(?:x0*68|0*104)|h)\s*(?:&\#(?:x0*61|0*97)|a)|\s*(?:&\#(?:x0*64|0*100)|d)\s*(?:&\#(?:x0*61|0*97)|a)\s*(?:&\#(?:x0*74|0*116)|t)\s*(?:&\#(?:x0*61|0*97)|a))\s*(?:&\#(?:x0*3a|0*58)|\:)|
|
122 |
+
#css expression
|
123 |
+
(?:^|[^\w])(?:(?:\\0*65|\\0*45|e)(?:\/\*.*?\*\/)*(?:\\0*78|\\0*58|x)(?:\/\*.*?\*\/)*(?:\\0*70|\\0*50|p)(?:\/\*.*?\*\/)*(?:\\0*72|\\0*52|r)(?:\/\*.*?\*\/)*(?:\\0*65|\\0*45|e)(?:\/\*.*?\*\/)*(?:\\0*73|\\0*53|s)(?:\/\*.*?\*\/)*(?:\\0*73|\\0*53|s)(?:\/\*.*?\*\/)*(?:\\0*69|\\0*49|i)(?:\/\*.*?\*\/)*(?:\\0*6f|\\0*4f|o)(?:\/\*.*?\*\/)*(?:\\0*6e|\\0*4e|n))[^\w]*?(?:\\0*28|\()|
|
124 |
+
#css properties
|
125 |
+
(?:^|[^\w])(?:(?:(?:\\0*62|\\0*42|b)(?:\/\*.*?\*\/)*(?:\\0*65|\\0*45|e)(?:\/\*.*?\*\/)*(?:\\0*68|\\0*48|h)(?:\/\*.*?\*\/)*(?:\\0*61|\\0*41|a)(?:\/\*.*?\*\/)*(?:\\0*76|\\0*56|v)(?:\/\*.*?\*\/)*(?:\\0*69|\\0*49|i)(?:\/\*.*?\*\/)*(?:\\0*6f|\\0*4f|o)(?:\/\*.*?\*\/)*(?:\\0*72|\\0*52|r)(?:\/\*.*?\*\/)*)|(?:(?:\\0*2d|\\0*2d|-)(?:\/\*.*?\*\/)*(?:\\0*6d|\\0*4d|m)(?:\/\*.*?\*\/)*(?:\\0*6f|\\0*4f|o)(?:\/\*.*?\*\/)*(?:\\0*7a|\\0*5a|z)(?:\/\*.*?\*\/)*(?:\\0*2d|\\0*2d|-)(?:\/\*.*?\*\/)*(?:\\0*62|\\0*42|b)(?:\/\*.*?\*\/)*(?:\\0*69|\\0*49|i)(?:\/\*.*?\*\/)*(?:\\0*6e|\\0*4e|n)(?:\/\*.*?\*\/)*(?:\\0*64|\\0*44|d)(?:\/\*.*?\*\/)*(?:\\0*69|\\0*49|i)(?:\/\*.*?\*\/)*(?:\\0*6e|\\0*4e|n)(?:\/\*.*?\*\/)*(?:\\0*67|\\0*47|g)(?:\/\*.*?\*\/)*))[^\w]*(?:\\0*3a|\\0*3a|:)[^\w]*(?:\\0*75|\\0*55|u)(?:\\0*72|\\0*52|r)(?:\\0*6c|\\0*4c|l)|
|
126 |
+
#properties
|
127 |
+
(?:^|[^\w])(?:on(?:abort|activate|afterprint|afterupdate|autocomplete|autocompleteerror|beforeactivate|beforecopy|beforecut|beforedeactivate|beforeeditfocus|beforepaste|beforeprint|beforeunload|beforeupdate|blur|bounce|cancel|canplay|canplaythrough|cellchange|change|click|close|contextmenu|controlselect|copy|cuechange|cut|dataavailable|datasetchanged|datasetcomplete|dblclick|deactivate|drag|dragend|dragenter|dragleave|dragover|dragstart|drop|durationchange|emptied|encrypted|ended|error|errorupdate|filterchange|finish|focus|focusin|focusout|formaction|formchange|forminput|hashchange|help|input|invalid|keydown|keypress|keyup|languagechange|layoutcomplete|load|loadeddata|loadedmetadata|loadstart|losecapture|message|mousedown|mouseenter|mouseleave|mousemove|mouseout|mouseover|mouseup|mousewheel|move|moveend|movestart|mozfullscreenchange|mozfullscreenerror|mozpointerlockchange|mozpointerlockerror|offline|online|page|pagehide|pageshow|paste|pause|play|playing|popstate|progress|propertychange|ratechange|readystatechange|reset|resize|resizeend|resizestart|rowenter|rowexit|rowsdelete|rowsinserted|scroll|search|seeked|seeking|select|selectstart|show|stalled|start|storage|submit|suspend|timer|timeupdate|toggle|unload|volumechange|waiting|webkitfullscreenchange|webkitfullscreenerror|wheel)|data\-bind|ev:event)[^\w]
|
128 |
+
)/ix'
|
129 |
+
|
130 |
+
# User Roles Manager Priviledge Escalation <= 4.24
|
131 |
+
if (notEquals('', request.body.ure_other_roles) and
|
132 |
+
match('#/wp\-admin/(network/)?(profile|user-new)\.php#i', request.path) and
|
133 |
+
currentUserIsNot('administrator', server.empty)):
|
134 |
+
block(id=18, category='priv-esc', description='User Roles Manager Priviledge Escalation <= 4.24')
|
135 |
+
|
136 |
+
# Whitelisted WordPress URLs
|
137 |
+
if ((match('#/wp\-admin/(network/)?(post|profile|user-new|settings)\.php$#i', server.script_filename)) or
|
138 |
+
(match('#/wp\-admin/admin\-ajax\.php$#i', server.script_filename) and (
|
139 |
+
equals('wordfence_loadLiveTraffic', request.body.action) or
|
140 |
+
equals('wordfence_ticker', request.body.action)
|
141 |
+
))):
|
142 |
+
allow(id=1, category='whitelist', description='Whitelisted URL')
|
143 |
+
|
144 |
+
# Slider revolution
|
145 |
+
if (match('/\/wp\-admin[\/]+admin\-ajax\.php/', request.path) and (
|
146 |
+
(equals('revslider_show_image', request.queryString.action) and match('/\.php$/i', request.queryString.img)) or
|
147 |
+
(equals('revslider_show_image', request.body.action) and match('/\.php$/i', request.body.img))
|
148 |
+
)):
|
149 |
+
block(id=2, category='lfi', description='Slider Revolution: Local File Inclusion')
|
150 |
+
|
151 |
+
# dzs-videogallery 8.80
|
152 |
+
if (match('/dzs\-videogallery[\/]+admin[\/]+(?:playlist|tag)seditor[\/]+popup\.php/', request.path) and contains("'", request.queryString.initer)):
|
153 |
+
blockXSS(id=15, category='xss', description='dzs-videogallery 8.80 XSS HTML injection in inline JavaScript')
|
154 |
+
|
155 |
+
# Simple Ads Manager <= 2.9.4.116 - SQL Injection: https://wpvulndb.com/vulnerabilities/8357
|
156 |
+
if (match('/simple-ads-manager[\/]+sam-ajax-loader\.php/', request.path) and
|
157 |
+
match(sqliRegex, base64decode(request.body.wc))):
|
158 |
+
block(id=16, category='sqli', description='Simple Ads Manager <= 2.9.4.116 - SQL Injection')
|
159 |
+
|
160 |
+
# Gwolle Guestbook <= 1.5.3 - Remote File Inclusion (RFI): https://wpvulndb.com/vulnerabilities/8218
|
161 |
+
if (match('/gwolle\-gb[\/]+frontend[\/]+captcha[\/]+ajaxresponse\.php/', request.path) and match('/.*/', request.queryString.abspath)):
|
162 |
+
block(id=17, category='rfi', description='Gwolle Guestbook <= 1.5.3 - Remote File Inclusion')
|
163 |
+
|
164 |
+
# SQLi detection
|
165 |
+
if (matchCount(sqliRegex, request.body, request.queryString)):
|
166 |
+
failSQLi(id=3, category='sqli', score=40, description='SQL Injection')
|
167 |
+
|
168 |
+
# blacklisted tags, protocols and attributes
|
169 |
+
if (matchCount(xssRegex, request.body, request.queryString)):
|
170 |
+
failXSS(id=9, category='xss', score=100, description='XSS: Cross Site Scripting')
|
171 |
+
|
172 |
+
# Shell file uploads
|
173 |
+
if (match('/\.(p(h(p|tml)[0-9]?|l|y)|(j|a)sp|aspx|sh|shtml|html?|cgi|htaccess)($|\.)/i', request.fileNames)):
|
174 |
+
block(id=11, category='file_upload', description='Malicous File Upload')
|
175 |
+
|
176 |
+
# Directory traversal
|
177 |
+
if (match('/(^|\/|\\)\.\.(\\|\/)/', request.body, request.queryString)):
|
178 |
+
block(id=12, category='lfi', description='Directory Traversal')
|
179 |
+
|
180 |
+
# LFI absolute paths
|
181 |
+
if (match('/^\/(?:\.\/)*(?:var|home|usr|mnt|media|etc|tmp|dev|proc)\//i', request.body, request.queryString)):
|
182 |
+
block(id=13, category='lfi', description='LFI: Local File Inclusion')
|
183 |
+
|
184 |
+
# XXE
|
185 |
+
if (match('/<\!(?:DOCTYPE|ENTITY)\s+(?:%\s*)?\w+\s+SYSTEM/i', request.body, request.queryString)):
|
186 |
+
block(id=14, category='xxe', description='XXE: External Entity Expansion')
|
187 |
+
|
vendor/wordfence/wf-waf/src/bootstrap-sample.php
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
php_value auto_prepend_file ~/wp-content/plugins/wordfence/waf/bootstrap.php
|
5 |
+
*/
|
6 |
+
|
7 |
+
require_once dirname(__FILE__) . '/init.php';
|
8 |
+
if (!defined('WFWAF_LOG_PATH')) {
|
9 |
+
define('WFWAF_LOG_PATH', WFWAF_PATH . 'logs/');
|
10 |
+
}
|
11 |
+
|
12 |
+
wfWAF::setInstance(new wfWAF(
|
13 |
+
wfWAFRequest::createFromGlobals(),
|
14 |
+
new wfWAFStorageFile(
|
15 |
+
WFWAF_LOG_PATH . 'attack-data.php',
|
16 |
+
WFWAF_LOG_PATH . 'ips.php',
|
17 |
+
WFWAF_LOG_PATH . 'config.php',
|
18 |
+
WFWAF_LOG_PATH . 'wafRules.rules'
|
19 |
+
)
|
20 |
+
));
|
21 |
+
wfWAF::getInstance()->getEventBus()->attach(new wfWAFBaseObserver);
|
22 |
+
|
23 |
+
$rulesFiles = array(
|
24 |
+
WFWAF_PATH . 'rules.php',
|
25 |
+
WFWAF_LOG_PATH . 'rules.php',
|
26 |
+
);
|
27 |
+
foreach ($rulesFiles as $rulesFile) {
|
28 |
+
if (!file_exists($rulesFile)) {
|
29 |
+
@touch($rulesFile);
|
30 |
+
}
|
31 |
+
if (is_writable($rulesFile)) {
|
32 |
+
wfWAF::getInstance()->setCompiledRulesFile($rulesFile);
|
33 |
+
break;
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
try {
|
38 |
+
if (!file_exists(wfWAF::getInstance()->getCompiledRulesFile()) || !filesize(wfWAF::getInstance()->getCompiledRulesFile())) {
|
39 |
+
try {
|
40 |
+
wfWAF::getInstance()->updateRuleSet(file_get_contents(WFWAF_PATH . 'baseRules.rules'));
|
41 |
+
} catch (wfWAFBuildRulesException $e) {
|
42 |
+
error_log($e->getMessage());
|
43 |
+
} catch (Exception $e) {
|
44 |
+
error_log($e->getMessage());
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
try {
|
49 |
+
wfWAF::getInstance()->run();
|
50 |
+
} catch (wfWAFBuildRulesException $e) {
|
51 |
+
error_log($e->getMessage());
|
52 |
+
} catch (Exception $e) {
|
53 |
+
error_log($e->getMessage());
|
54 |
+
}
|
55 |
+
} catch (wfWAFStorageFileException $e) {
|
56 |
+
// Choose another storage engine here.
|
57 |
+
}
|
vendor/wordfence/wf-waf/src/init.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
define('WFWAF_VERSION', '1.0.0');
|
4 |
+
define('WFWAF_PATH', dirname(__FILE__) . '/');
|
5 |
+
define('WFWAF_LIB_PATH', WFWAF_PATH . 'lib/');
|
6 |
+
define('WFWAF_VIEW_PATH', WFWAF_PATH . 'views/');
|
7 |
+
define('WFWAF_API_URL_SEC', 'https://noc4.wordfence.com/v1.1/');
|
8 |
+
if (!defined('WFWAF_DEBUG')) {
|
9 |
+
define('WFWAF_DEBUG', false);
|
10 |
+
}
|
11 |
+
if (!defined('WFWAF_ENABLED')) {
|
12 |
+
define('WFWAF_ENABLED', true);
|
13 |
+
}
|
14 |
+
|
15 |
+
require_once WFWAF_LIB_PATH . 'waf.php';
|
16 |
+
require_once WFWAF_LIB_PATH . 'utils.php';
|
17 |
+
|
18 |
+
require_once WFWAF_LIB_PATH . 'storage.php';
|
19 |
+
require_once WFWAF_LIB_PATH . 'storage/file.php';
|
20 |
+
|
21 |
+
require_once WFWAF_LIB_PATH . 'rules.php';
|
22 |
+
require_once WFWAF_LIB_PATH . 'parser/lexer.php';
|
23 |
+
require_once WFWAF_LIB_PATH . 'parser/parser.php';
|
24 |
+
require_once WFWAF_LIB_PATH . 'parser/sqli.php';
|
25 |
+
|
26 |
+
require_once WFWAF_LIB_PATH . 'request.php';
|
27 |
+
require_once WFWAF_LIB_PATH . 'http.php';
|
28 |
+
require_once WFWAF_LIB_PATH . 'view.php';
|
vendor/wordfence/wf-waf/src/lib/config.php
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
vendor/wordfence/wf-waf/src/lib/http.php
ADDED
@@ -0,0 +1,438 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class wfWAFHTTP {
|
4 |
+
|
5 |
+
private $url;
|
6 |
+
private $auth;
|
7 |
+
private $body;
|
8 |
+
private $cookies;
|
9 |
+
// private $fileNames;
|
10 |
+
// private $files;
|
11 |
+
private $headers;
|
12 |
+
private $method;
|
13 |
+
private $queryString;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @var wfWAFHTTPTransport
|
17 |
+
*/
|
18 |
+
private $transport;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @param string $url
|
22 |
+
* @param wfWAFHTTP $request
|
23 |
+
* @return wfWAFHTTPResponse|bool
|
24 |
+
* @throws wfWAFHTTPTransportException
|
25 |
+
*/
|
26 |
+
public static function get($url, $request = null) {
|
27 |
+
if (!$request) {
|
28 |
+
$request = new self();
|
29 |
+
}
|
30 |
+
$request->setUrl($url);
|
31 |
+
$request->setMethod('GET');
|
32 |
+
$request->setTransport(wfWAFHTTPTransport::getInstance());
|
33 |
+
// $request->setCookies("XDEBUG_SESSION=netbeans-xdebug");
|
34 |
+
return $request->send();
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @param string $url
|
39 |
+
* @param array $post
|
40 |
+
* @param wfWAFHTTP $request
|
41 |
+
* @return wfWAFHTTPResponse|bool
|
42 |
+
* @throws wfWAFHTTPTransportException
|
43 |
+
*/
|
44 |
+
public static function post($url, $post = array(), $request = null) {
|
45 |
+
if (!$request) {
|
46 |
+
$request = new self();
|
47 |
+
}
|
48 |
+
$request->setUrl($url);
|
49 |
+
$request->setMethod('POST');
|
50 |
+
$request->setBody($post);
|
51 |
+
$request->setTransport(wfWAFHTTPTransport::getInstance());
|
52 |
+
return $request->send();
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* @return wfWAFHTTPResponse|bool
|
57 |
+
* @throws wfWAFHTTPTransportException
|
58 |
+
*/
|
59 |
+
public function send() {
|
60 |
+
if (!$this->getTransport()) {
|
61 |
+
throw new wfWAFHTTPTransportException('Need to provide a valid HTTP transport before calling ' . __METHOD__);
|
62 |
+
}
|
63 |
+
return $this->getTransport()->send($this);
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @return mixed
|
68 |
+
*/
|
69 |
+
public function getUrl() {
|
70 |
+
return $this->url;
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* @param mixed $url
|
75 |
+
*/
|
76 |
+
public function setUrl($url) {
|
77 |
+
$this->url = $url;
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* @return mixed
|
82 |
+
*/
|
83 |
+
public function getAuth() {
|
84 |
+
return $this->auth;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* @param mixed $auth
|
89 |
+
*/
|
90 |
+
public function setAuth($auth) {
|
91 |
+
$this->auth = $auth;
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* @return mixed
|
96 |
+
*/
|
97 |
+
public function getBody() {
|
98 |
+
return $this->body;
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* @param mixed $body
|
103 |
+
*/
|
104 |
+
public function setBody($body) {
|
105 |
+
$this->body = $body;
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* @return mixed
|
110 |
+
*/
|
111 |
+
public function getCookies() {
|
112 |
+
return $this->cookies;
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* @param mixed $cookies
|
117 |
+
*/
|
118 |
+
public function setCookies($cookies) {
|
119 |
+
$this->cookies = $cookies;
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* @return mixed
|
124 |
+
*/
|
125 |
+
public function getHeaders() {
|
126 |
+
return $this->headers;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* @param mixed $headers
|
131 |
+
*/
|
132 |
+
public function setHeaders($headers) {
|
133 |
+
$this->headers = $headers;
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* @return mixed
|
138 |
+
*/
|
139 |
+
public function getMethod() {
|
140 |
+
return $this->method;
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* @param mixed $method
|
145 |
+
*/
|
146 |
+
public function setMethod($method) {
|
147 |
+
$this->method = $method;
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* @return mixed
|
152 |
+
*/
|
153 |
+
public function getQueryString() {
|
154 |
+
return $this->queryString;
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* @param mixed $queryString
|
159 |
+
*/
|
160 |
+
public function setQueryString($queryString) {
|
161 |
+
$this->queryString = $queryString;
|
162 |
+
}
|
163 |
+
|
164 |
+
/**
|
165 |
+
* @return wfWAFHTTPTransport
|
166 |
+
*/
|
167 |
+
public function getTransport() {
|
168 |
+
return $this->transport;
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* @param wfWAFHTTPTransport $transport
|
173 |
+
*/
|
174 |
+
public function setTransport($transport) {
|
175 |
+
$this->transport = $transport;
|
176 |
+
}
|
177 |
+
}
|
178 |
+
|
179 |
+
class wfWAFHTTPResponse {
|
180 |
+
|
181 |
+
private $body;
|
182 |
+
private $headers;
|
183 |
+
private $statusCode;
|
184 |
+
|
185 |
+
/**
|
186 |
+
* @return mixed
|
187 |
+
*/
|
188 |
+
public function getBody() {
|
189 |
+
return $this->body;
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* @param mixed $body
|
194 |
+
*/
|
195 |
+
public function setBody($body) {
|
196 |
+
$this->body = $body;
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* @return mixed
|
201 |
+
*/
|
202 |
+
public function getHeaders() {
|
203 |
+
return $this->headers;
|
204 |
+
}
|
205 |
+
|
206 |
+
/**
|
207 |
+
* @param mixed $headers
|
208 |
+
*/
|
209 |
+
public function setHeaders($headers) {
|
210 |
+
$this->headers = $headers;
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* @return mixed
|
215 |
+
*/
|
216 |
+
public function getStatusCode() {
|
217 |
+
return $this->statusCode;
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* @param mixed $statusCode
|
222 |
+
*/
|
223 |
+
public function setStatusCode($statusCode) {
|
224 |
+
$this->statusCode = $statusCode;
|
225 |
+
}
|
226 |
+
}
|
227 |
+
|
228 |
+
abstract class wfWAFHTTPTransport {
|
229 |
+
|
230 |
+
private static $instance;
|
231 |
+
|
232 |
+
/**
|
233 |
+
* @return mixed
|
234 |
+
*/
|
235 |
+
public static function getInstance() {
|
236 |
+
if (!self::$instance) {
|
237 |
+
self::$instance = self::getFirstTransport();
|
238 |
+
}
|
239 |
+
return self::$instance;
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* @param mixed $instance
|
244 |
+
*/
|
245 |
+
public static function setInstance($instance) {
|
246 |
+
self::$instance = $instance;
|
247 |
+
}
|
248 |
+
|
249 |
+
/**
|
250 |
+
* @return wfWAFHTTPTransport
|
251 |
+
* @throws wfWAFHTTPTransportException
|
252 |
+
*/
|
253 |
+
public static function getFirstTransport() {
|
254 |
+
if (function_exists('curl_init')) {
|
255 |
+
return new wfWAFHTTPTransportCurl();
|
256 |
+
} else if (function_exists('file_get_contents')) {
|
257 |
+
return new wfWAFHTTPTransportStreams();
|
258 |
+
}
|
259 |
+
throw new wfWAFHTTPTransportException('No valid HTTP transport found.');
|
260 |
+
}
|
261 |
+
|
262 |
+
/**
|
263 |
+
* @param array $cookieArray
|
264 |
+
* @return string
|
265 |
+
*/
|
266 |
+
public static function buildCookieString($cookieArray) {
|
267 |
+
$cookies = '';
|
268 |
+
foreach ($cookieArray as $cookieName => $value) {
|
269 |
+
$cookies .= "$cookieName=" . urlencode($value) . '; ';
|
270 |
+
}
|
271 |
+
$cookies = rtrim($cookies);
|
272 |
+
return $cookies;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* @param wfWAFHTTP $request
|
277 |
+
* @return wfWAFHTTPResponse|bool
|
278 |
+
*/
|
279 |
+
abstract public function send($request);
|
280 |
+
}
|
281 |
+
|
282 |
+
class wfWAFHTTPTransportCurl extends wfWAFHTTPTransport {
|
283 |
+
|
284 |
+
/**
|
285 |
+
* @todo Proxy settings
|
286 |
+
* @param wfWAFHTTP $request
|
287 |
+
* @return wfWAFHTTPResponse|bool
|
288 |
+
*/
|
289 |
+
public function send($request) {
|
290 |
+
$url = $request->getUrl();
|
291 |
+
if ($queryString = $request->getQueryString()) {
|
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;
|
303 |
+
}
|
304 |
+
if ($body = $request->getBody()) {
|
305 |
+
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
306 |
+
}
|
307 |
+
if ($auth = $request->getAuth()) {
|
308 |
+
curl_setopt($ch, CURLOPT_USERPWD, $auth['user'] . ':' . $auth['password']);
|
309 |
+
}
|
310 |
+
if ($cookies = $request->getCookies()) {
|
311 |
+
if (is_array($cookies)) {
|
312 |
+
$cookies = self::buildCookieString($cookies);
|
313 |
+
}
|
314 |
+
curl_setopt($ch, CURLOPT_COOKIE, $cookies);
|
315 |
+
}
|
316 |
+
if ($headers = $request->getHeaders()) {
|
317 |
+
if (is_array($headers)) {
|
318 |
+
$_headers = array();
|
319 |
+
foreach ($headers as $header => $value) {
|
320 |
+
$_headers[] = $header . ': ' . $value;
|
321 |
+
}
|
322 |
+
curl_setopt($ch, CURLOPT_HTTPHEADER, $_headers);
|
323 |
+
}
|
324 |
+
}
|
325 |
+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
326 |
+
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
327 |
+
curl_setopt($ch, CURLOPT_HEADER, 1);
|
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);
|
336 |
+
$response->setHeaders($header);
|
337 |
+
return $response;
|
338 |
+
}
|
339 |
+
return false;
|
340 |
+
}
|
341 |
+
}
|
342 |
+
|
343 |
+
class wfWAFHTTPTransportStreams extends wfWAFHTTPTransport {
|
344 |
+
|
345 |
+
/**
|
346 |
+
* @todo Implement wfWAFHTTPTransportStreams::send.
|
347 |
+
* @param wfWAFHTTP $request
|
348 |
+
* @return mixed
|
349 |
+
* @throws wfWAFHTTPTransportException
|
350 |
+
*/
|
351 |
+
public function send($request) {
|
352 |
+
$timeout = 5;
|
353 |
+
|
354 |
+
$url = $request->getUrl();
|
355 |
+
if ($queryString = $request->getQueryString()) {
|
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());
|
363 |
+
|
364 |
+
$headers = "Host: $urlParsed[host]\r\n";
|
365 |
+
if ($auth = $request->getAuth()) {
|
366 |
+
$headers .= 'Authorization: Basic ' . base64_encode($auth['user'] . ':' . $auth['password']) . "\r\n";
|
367 |
+
}
|
368 |
+
if ($cookies = $request->getCookies()) {
|
369 |
+
if (is_array($cookies)) {
|
370 |
+
$cookies = self::buildCookieString($cookies);
|
371 |
+
}
|
372 |
+
$headers .= "Cookie: $cookies\r\n";
|
373 |
+
}
|
374 |
+
$hasUA = false;
|
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";
|
382 |
+
}
|
383 |
+
}
|
384 |
+
}
|
385 |
+
if (!$hasUA) {
|
386 |
+
$headers .= "User-Agent: Wordfence Streams UA\r\n";
|
387 |
+
}
|
388 |
+
|
389 |
+
$httpOptions = array(
|
390 |
+
'method' => $request->getMethod(),
|
391 |
+
'ignore_errors' => true,
|
392 |
+
'timeout' => $timeout,
|
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);
|
407 |
+
$stream = fopen($request->getUrl(), 'r', false, $context);
|
408 |
+
if (!is_resource($stream)) {
|
409 |
+
return false;
|
410 |
+
}
|
411 |
+
|
412 |
+
$metaData = stream_get_meta_data($stream);
|
413 |
+
|
414 |
+
// Get the HTTP response code
|
415 |
+
$httpResponse = array_shift($metaData['wrapper_data']);
|
416 |
+
|
417 |
+
if (preg_match_all('/(\w+\/\d\.\d) (\d{3})/', $httpResponse, $matches) !== false) {
|
418 |
+
// $protocol = $matches[1][0];
|
419 |
+
$status = (int) $matches[2][0];
|
420 |
+
} else {
|
421 |
+
// $protocol = null;
|
422 |
+
$status = null;
|
423 |
+
}
|
424 |
+
|
425 |
+
$responseObj = new wfWAFHTTPResponse();
|
426 |
+
$responseObj->setHeaders(join("\r\n", $metaData['wrapper_data']));
|
427 |
+
$responseObj->setBody(stream_get_contents($stream));
|
428 |
+
$responseObj->setStatusCode($status);
|
429 |
+
|
430 |
+
// Close the stream after use
|
431 |
+
fclose($stream);
|
432 |
+
|
433 |
+
return $responseObj;
|
434 |
+
}
|
435 |
+
}
|
436 |
+
|
437 |
+
class wfWAFHTTPTransportException extends wfWAFException {
|
438 |
+
}
|
vendor/wordfence/wf-waf/src/lib/parser/lexer.php
ADDED
@@ -0,0 +1,667 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
interface wfWAFLexerInterface {
|
4 |
+
|
5 |
+
public function nextToken();
|
6 |
+
|
7 |
+
}
|
8 |
+
|
9 |
+
class wfWAFRuleLexer implements wfWAFLexerInterface {
|
10 |
+
|
11 |
+
const MATCH_IDENTIFIER = '/[a-zA-Z_][\\w_]*/';
|
12 |
+
const MATCH_SINGLE_STRING_LITERAL = '/\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
|
13 |
+
const MATCH_DOUBLE_STRING_LITERAL = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"/As';
|
14 |
+
const MATCH_NUMBER_LITERAL = '/-?\d+(\.\d+)?/';
|
15 |
+
const MATCH_DOT = '/\./';
|
16 |
+
const MATCH_COMPARISON_OPERATOR = '/\|\||&&/';
|
17 |
+
const MATCH_OPEN_PARENTHESIS = '/\(/';
|
18 |
+
const MATCH_CLOSE_PARENTHESIS = '/\)/';
|
19 |
+
const MATCH_COMMA = '/,/';
|
20 |
+
const MATCH_RULE_COMPARISON_END = '/:/';
|
21 |
+
const MATCH_ASSIGNMENT = '/=/';
|
22 |
+
const MATCH_SINGLE_LINE_COMMENT = '/(?:#|\/\/)[^\n]*/';
|
23 |
+
const MATCH_MULTIPLE_LINE_COMMENT = '/\/\*.*?\*\//s';
|
24 |
+
const MATCH_OPEN_BRACKET = '/\[/';
|
25 |
+
const MATCH_CLOSE_BRACKET = '/\]/';
|
26 |
+
|
27 |
+
const T_RULE_START = 'T_RULE_START';
|
28 |
+
const T_IDENTIFIER = 'T_IDENTIFIER';
|
29 |
+
const T_SINGLE_STRING_LITERAL = 'T_SINGLE_STRING_LITERAL';
|
30 |
+
const T_DOUBLE_STRING_LITERAL = 'T_DOUBLE_STRING_LITERAL';
|
31 |
+
const T_NUMBER_LITERAL = 'T_NUMBER_LITERAL';
|
32 |
+
const T_DOT = 'T_DOT';
|
33 |
+
const T_COMPARISON_OPERATOR = 'T_COMPARISON_OPERATOR';
|
34 |
+
const T_OPEN_PARENTHESIS = 'T_OPEN_PARENTHESIS';
|
35 |
+
const T_CLOSE_PARENTHESIS = 'T_CLOSE_PARENTHESIS';
|
36 |
+
const T_COMMA = 'T_COMMA';
|
37 |
+
const T_RULE_COMPARISON_END = 'T_RULE_COMPARISON_END';
|
38 |
+
const T_ASSIGNMENT = 'T_ASSIGNMENT';
|
39 |
+
const T_SINGLE_LINE_COMMENT = 'T_SINGLE_LINE_COMMENT';
|
40 |
+
const T_MULTIPLE_LINE_COMMENT = 'T_MULTIPLE_LINE_COMMENT';
|
41 |
+
const T_OPEN_BRACKET = 'T_OPEN_BRACKET';
|
42 |
+
const T_CLOSE_BRACKET = 'T_CLOSE_BRACKET';
|
43 |
+
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @var string
|
47 |
+
*/
|
48 |
+
private $rules;
|
49 |
+
|
50 |
+
/**
|
51 |
+
* @var wfWAFStringScanner
|
52 |
+
*/
|
53 |
+
private $scanner;
|
54 |
+
|
55 |
+
/**
|
56 |
+
* wfWAFRuleLexer constructor.
|
57 |
+
* @param $rules
|
58 |
+
*/
|
59 |
+
public function __construct($rules) {
|
60 |
+
$this->setRules($rules);
|
61 |
+
$this->scanner = new wfWAFStringScanner($rules);
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* @return array
|
66 |
+
* @throws wfWAFParserSyntaxError
|
67 |
+
*/
|
68 |
+
public function tokenize() {
|
69 |
+
$tokens = array();
|
70 |
+
while ($token = $this->nextToken()) {
|
71 |
+
$tokens[] = $token;
|
72 |
+
}
|
73 |
+
return $tokens;
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* @return bool|wfWAFLexerToken
|
78 |
+
* @throws wfWAFParserSyntaxError
|
79 |
+
*/
|
80 |
+
public function nextToken() {
|
81 |
+
if (!$this->scanner->eos()) {
|
82 |
+
$this->scanner->skip('/\s+/s');
|
83 |
+
if ($this->scanner->eos()) {
|
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':
|
91 |
+
case 'or':
|
92 |
+
case 'xor':
|
93 |
+
return $this->createToken(self::T_COMPARISON_OPERATOR, $match);
|
94 |
+
default:
|
95 |
+
return $this->createToken(self::T_IDENTIFIER, $match);
|
96 |
+
}
|
97 |
+
else if (($match = $this->scanner->scan(self::MATCH_SINGLE_STRING_LITERAL)) !== null) return $this->createToken(self::T_SINGLE_STRING_LITERAL, $match);
|
98 |
+
else if (($match = $this->scanner->scan(self::MATCH_DOUBLE_STRING_LITERAL)) !== null) return $this->createToken(self::T_DOUBLE_STRING_LITERAL, $match);
|
99 |
+
else if (($match = $this->scanner->scan(self::MATCH_NUMBER_LITERAL)) !== null) return $this->createToken(self::T_NUMBER_LITERAL, $match);
|
100 |
+
else if (($match = $this->scanner->scan(self::MATCH_DOT)) !== null) return $this->createToken(self::T_DOT, $match);
|
101 |
+
else if (($match = $this->scanner->scan(self::MATCH_COMPARISON_OPERATOR)) !== null) return $this->createToken(self::T_COMPARISON_OPERATOR, $match);
|
102 |
+
else if (($match = $this->scanner->scan(self::MATCH_OPEN_PARENTHESIS)) !== null) return $this->createToken(self::T_OPEN_PARENTHESIS, $match);
|
103 |
+
else if (($match = $this->scanner->scan(self::MATCH_CLOSE_PARENTHESIS)) !== null) return $this->createToken(self::T_CLOSE_PARENTHESIS, $match);
|
104 |
+
else if (($match = $this->scanner->scan(self::MATCH_COMMA)) !== null) return $this->createToken(self::T_COMMA, $match);
|
105 |
+
else if (($match = $this->scanner->scan(self::MATCH_RULE_COMPARISON_END)) !== null) return $this->createToken(self::T_RULE_COMPARISON_END, $match);
|
106 |
+
else if (($match = $this->scanner->scan(self::MATCH_ASSIGNMENT)) !== null) return $this->createToken(self::T_ASSIGNMENT, $match);
|
107 |
+
else if (($match = $this->scanner->scan(self::MATCH_OPEN_BRACKET)) !== null) return $this->createToken(self::T_OPEN_BRACKET, $match);
|
108 |
+
else if (($match = $this->scanner->scan(self::MATCH_CLOSE_BRACKET)) !== null) return $this->createToken(self::T_CLOSE_BRACKET, $match);
|
109 |
+
else if (($match = $this->scanner->scan(self::MATCH_SINGLE_LINE_COMMENT)) !== null) return $this->createToken(self::T_SINGLE_LINE_COMMENT, $match);
|
110 |
+
else if (($match = $this->scanner->scan(self::MATCH_MULTIPLE_LINE_COMMENT)) !== null) return $this->createToken(self::T_MULTIPLE_LINE_COMMENT, $match);
|
111 |
+
else {
|
112 |
+
$e = new wfWAFParserSyntaxError(sprintf('Invalid character "%s" found on line %d, column %d',
|
113 |
+
$this->scanner->scanChar(), $this->scanner->getLine(), $this->scanner->getColumn()));
|
114 |
+
$e->setParseLine($this->scanner->getLine());
|
115 |
+
$e->setParseColumn($this->scanner->getColumn());
|
116 |
+
throw $e;
|
117 |
+
}
|
118 |
+
}
|
119 |
+
return false;
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* @param $type
|
124 |
+
* @param $value
|
125 |
+
* @return wfWAFLexerToken
|
126 |
+
*/
|
127 |
+
protected function createToken($type, $value) {
|
128 |
+
return new wfWAFLexerToken($type, $value, $this->scanner->getLine(), $this->scanner->getColumn());
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* @return string
|
133 |
+
*/
|
134 |
+
public function getRules() {
|
135 |
+
return $this->rules;
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* @param string $rules
|
140 |
+
*/
|
141 |
+
public function setRules($rules) {
|
142 |
+
$this->rules = rtrim($rules);
|
143 |
+
}
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
*
|
148 |
+
*/
|
149 |
+
class wfWAFLexerToken {
|
150 |
+
|
151 |
+
private $type;
|
152 |
+
private $value;
|
153 |
+
private $line;
|
154 |
+
private $column;
|
155 |
+
|
156 |
+
/**
|
157 |
+
* wfWAFRuleToken constructor.
|
158 |
+
*
|
159 |
+
* @param $type
|
160 |
+
* @param $value
|
161 |
+
* @param $line
|
162 |
+
* @param $column
|
163 |
+
*/
|
164 |
+
public function __construct($type, $value, $line, $column) {
|
165 |
+
$this->setType($type);
|
166 |
+
$this->setValue($value);
|
167 |
+
$this->setLine($line);
|
168 |
+
$this->setColumn($column);
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
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 |
+
/**
|
186 |
+
* @return mixed
|
187 |
+
*/
|
188 |
+
public function getType() {
|
189 |
+
return $this->type;
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* @param mixed $type
|
194 |
+
*/
|
195 |
+
public function setType($type) {
|
196 |
+
$this->type = $type;
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* @return mixed
|
201 |
+
*/
|
202 |
+
public function getValue() {
|
203 |
+
return $this->value;
|
204 |
+
}
|
205 |
+
|
206 |
+
/**
|
207 |
+
* @param mixed $value
|
208 |
+
*/
|
209 |
+
public function setValue($value) {
|
210 |
+
$this->value = $value;
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* @return mixed
|
215 |
+
*/
|
216 |
+
public function getLine() {
|
217 |
+
return $this->line;
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* @param mixed $line
|
222 |
+
*/
|
223 |
+
public function setLine($line) {
|
224 |
+
$this->line = $line;
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* @return mixed
|
229 |
+
*/
|
230 |
+
public function getColumn() {
|
231 |
+
return $this->column;
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* @param mixed $column
|
236 |
+
*/
|
237 |
+
public function setColumn($column) {
|
238 |
+
$this->column = $column;
|
239 |
+
}
|
240 |
+
}
|
241 |
+
|
242 |
+
|
243 |
+
class wfWAFParserSyntaxError extends wfWAFException {
|
244 |
+
|
245 |
+
private $parseLine;
|
246 |
+
private $parseColumn;
|
247 |
+
private $token;
|
248 |
+
|
249 |
+
/**
|
250 |
+
* @return mixed
|
251 |
+
*/
|
252 |
+
public function getToken() {
|
253 |
+
return $this->token;
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* @param mixed $token
|
258 |
+
*/
|
259 |
+
public function setToken($token) {
|
260 |
+
$this->token = $token;
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* @return mixed
|
265 |
+
*/
|
266 |
+
public function getParseLine() {
|
267 |
+
return $this->parseLine;
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* @param mixed $parseLine
|
272 |
+
*/
|
273 |
+
public function setParseLine($parseLine) {
|
274 |
+
$this->parseLine = $parseLine;
|
275 |
+
}
|
276 |
+
|
277 |
+
/**
|
278 |
+
* @return mixed
|
279 |
+
*/
|
280 |
+
public function getParseColumn() {
|
281 |
+
return $this->parseColumn;
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* @param mixed $parseColumn
|
286 |
+
*/
|
287 |
+
public function setParseColumn($parseColumn) {
|
288 |
+
$this->parseColumn = $parseColumn;
|
289 |
+
}
|
290 |
+
|
291 |
+
}
|
292 |
+
|
293 |
+
class wfWAFBaseParser {
|
294 |
+
|
295 |
+
protected $tokens;
|
296 |
+
protected $index;
|
297 |
+
/** @var wfWAFLexerInterface */
|
298 |
+
protected $lexer;
|
299 |
+
|
300 |
+
public function __construct($lexer) {
|
301 |
+
$this->lexer = $lexer;
|
302 |
+
}
|
303 |
+
|
304 |
+
/**
|
305 |
+
* @param wfWAFLexerToken $token
|
306 |
+
* @param int $type
|
307 |
+
* @param string $message
|
308 |
+
* @throws wfWAFParserSyntaxError
|
309 |
+
*/
|
310 |
+
protected function expectTokenTypeEquals($token, $type, $message = 'Wordfence WAF Syntax Error: Unexpected %s found on line %d, column %d. Expected %s.') {
|
311 |
+
if ($token->getType() !== $type) {
|
312 |
+
$this->triggerSyntaxError($token, sprintf($message, $token->getType(),
|
313 |
+
$token->getLine(), $token->getColumn(), $type));
|
314 |
+
}
|
315 |
+
}
|
316 |
+
|
317 |
+
/**
|
318 |
+
* @param wfWAFLexerToken $token
|
319 |
+
* @param array $types
|
320 |
+
* @param string $message
|
321 |
+
* @throws wfWAFParserSyntaxError
|
322 |
+
*/
|
323 |
+
protected function expectTokenTypeInArray($token, $types, $message = 'Wordfence WAF Syntax Error: Unexpected %s found on line %d, column %d') {
|
324 |
+
if (!in_array($token->getType(), $types)) {
|
325 |
+
$this->triggerSyntaxError($token, sprintf($message, $token->getType(),
|
326 |
+
$token->getLine(), $token->getColumn()));
|
327 |
+
}
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* @param wfWAFLexerToken $token
|
332 |
+
* @param string $message
|
333 |
+
* @throws wfWAFParserSyntaxError
|
334 |
+
*/
|
335 |
+
protected function triggerSyntaxError($token, $message = 'Wordfence WAF Syntax Error: Unexpected %s %s found on line %d, column %d') {
|
336 |
+
$e = new wfWAFParserSyntaxError(sprintf($message, $token->getType(), $token->getValue(),
|
337 |
+
$token->getLine(), $token->getColumn()));
|
338 |
+
$e->setToken($token);
|
339 |
+
$e->setParseLine($token->getLine());
|
340 |
+
$e->setParseColumn($token->getColumn());
|
341 |
+
throw $e;
|
342 |
+
}
|
343 |
+
|
344 |
+
/**
|
345 |
+
* @return wfWAFLexerToken
|
346 |
+
*/
|
347 |
+
protected function currentToken() {
|
348 |
+
return $this->getToken($this->index);
|
349 |
+
}
|
350 |
+
|
351 |
+
/**
|
352 |
+
* @return bool|wfWAFLexerToken
|
353 |
+
*/
|
354 |
+
protected function nextToken() {
|
355 |
+
$this->index++;
|
356 |
+
return $this->getToken($this->index);
|
357 |
+
}
|
358 |
+
|
359 |
+
/**
|
360 |
+
* @param string $message
|
361 |
+
* @return wfWAFLexerToken
|
362 |
+
* @throws wfWAFParserSyntaxError
|
363 |
+
*/
|
364 |
+
protected function expectNextToken($message = 'Expected statement') {
|
365 |
+
$this->index++;
|
366 |
+
if ($token = $this->getToken($this->index)) {
|
367 |
+
return $token;
|
368 |
+
}
|
369 |
+
throw new wfWAFParserSyntaxError($message);
|
370 |
+
}
|
371 |
+
|
372 |
+
/**
|
373 |
+
* @param int $index
|
374 |
+
* @return mixed
|
375 |
+
*/
|
376 |
+
protected function getToken($index) {
|
377 |
+
if (is_array($this->tokens) && array_key_exists($index, $this->tokens)) {
|
378 |
+
return $this->tokens[$index];
|
379 |
+
}
|
380 |
+
if ($token = $this->getLexer()->nextToken()) {
|
381 |
+
$this->tokens[$index] = $token;
|
382 |
+
return $this->tokens[$index];
|
383 |
+
}
|
384 |
+
return false;
|
385 |
+
}
|
386 |
+
|
387 |
+
/**
|
388 |
+
* @return wfWAFLexerInterface
|
389 |
+
*/
|
390 |
+
public function getLexer() {
|
391 |
+
return $this->lexer;
|
392 |
+
}
|
393 |
+
|
394 |
+
/**
|
395 |
+
* @param wfWAFLexerInterface $lexer
|
396 |
+
*/
|
397 |
+
public function setLexer($lexer) {
|
398 |
+
$this->lexer = $lexer;
|
399 |
+
}
|
400 |
+
|
401 |
+
/**
|
402 |
+
* @return mixed
|
403 |
+
*/
|
404 |
+
public function getTokens() {
|
405 |
+
return $this->tokens;
|
406 |
+
}
|
407 |
+
|
408 |
+
/**
|
409 |
+
* @param mixed $tokens
|
410 |
+
*/
|
411 |
+
public function setTokens($tokens) {
|
412 |
+
$this->tokens = $tokens;
|
413 |
+
}
|
414 |
+
}
|
415 |
+
|
416 |
+
/**
|
417 |
+
*
|
418 |
+
*/
|
419 |
+
class wfWAFStringScanner {
|
420 |
+
|
421 |
+
private $string;
|
422 |
+
private $length;
|
423 |
+
private $pointer;
|
424 |
+
private $prevPointer;
|
425 |
+
private $match;
|
426 |
+
private $captures;
|
427 |
+
|
428 |
+
/**
|
429 |
+
* wfWAFStringScanner constructor.
|
430 |
+
* @param $string
|
431 |
+
*/
|
432 |
+
public function __construct($string = null) {
|
433 |
+
if (is_string($string)) {
|
434 |
+
$this->setString($string);
|
435 |
+
}
|
436 |
+
}
|
437 |
+
|
438 |
+
/**
|
439 |
+
* @param $regex
|
440 |
+
* @return mixed
|
441 |
+
*/
|
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 |
+
}
|
450 |
+
return $this->setState();
|
451 |
+
}
|
452 |
+
|
453 |
+
/**
|
454 |
+
* @param $regex
|
455 |
+
* @return int|null
|
456 |
+
*/
|
457 |
+
public function skip($regex) {
|
458 |
+
return $this->scan($regex) ? strlen($this->getMatch()) : null;
|
459 |
+
}
|
460 |
+
|
461 |
+
/**
|
462 |
+
* @return mixed
|
463 |
+
*/
|
464 |
+
public function scanChar() {
|
465 |
+
return $this->scan('/./s');
|
466 |
+
}
|
467 |
+
|
468 |
+
/**
|
469 |
+
* @param string $regex
|
470 |
+
* @return mixed
|
471 |
+
*/
|
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 |
+
}
|
480 |
+
return $this->setState();
|
481 |
+
}
|
482 |
+
|
483 |
+
/**
|
484 |
+
* @param string $regex
|
485 |
+
* @param string $remaining
|
486 |
+
* @param $matches
|
487 |
+
* @return int
|
488 |
+
*/
|
489 |
+
public function regexMatch($regex, $remaining, &$matches) {
|
490 |
+
// $startTime = microtime(true);
|
491 |
+
$result = preg_match($regex, $remaining, $matches);
|
492 |
+
// printf("%s took %f seconds\n", $regex, microtime(true) - $startTime);
|
493 |
+
return $result;
|
494 |
+
}
|
495 |
+
|
496 |
+
/**
|
497 |
+
* @return bool
|
498 |
+
*/
|
499 |
+
public function eos() {
|
500 |
+
return $this->getPointer() === $this->getLength();
|
501 |
+
}
|
502 |
+
|
503 |
+
/**
|
504 |
+
* @return string
|
505 |
+
*/
|
506 |
+
public function getRemainingString() {
|
507 |
+
return substr($this->getString(), $this->getPointer());
|
508 |
+
}
|
509 |
+
|
510 |
+
/**
|
511 |
+
* @return $this
|
512 |
+
*/
|
513 |
+
public function reset() {
|
514 |
+
$this->setState(array(), 0, 0);
|
515 |
+
return $this;
|
516 |
+
}
|
517 |
+
|
518 |
+
/**
|
519 |
+
* The current line of the scanned string.
|
520 |
+
*
|
521 |
+
* @return int
|
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 |
+
/**
|
531 |
+
* The current column of the line of the scanned string.
|
532 |
+
*
|
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 |
+
/**
|
540 |
+
* @param array $matches
|
541 |
+
* @param int|null $pointer
|
542 |
+
* @param int|null $prevPointer
|
543 |
+
* @return mixed
|
544 |
+
*/
|
545 |
+
protected function setState($matches = array(), $pointer = null, $prevPointer = null) {
|
546 |
+
if ($pointer !== null) {
|
547 |
+
$this->setPointer($pointer);
|
548 |
+
}
|
549 |
+
if ($prevPointer !== null) {
|
550 |
+
$this->setPrevPointer($prevPointer);
|
551 |
+
}
|
552 |
+
if (is_array($matches)) {
|
553 |
+
$this->setCaptures(array_slice($matches, 1));
|
554 |
+
if (count($matches) > 0) {
|
555 |
+
$this->setMatch($matches[0]);
|
556 |
+
} else {
|
557 |
+
$this->setMatch(null);
|
558 |
+
}
|
559 |
+
} else {
|
560 |
+
$this->setMatch(null);
|
561 |
+
}
|
562 |
+
return $this->getMatch();
|
563 |
+
}
|
564 |
+
|
565 |
+
/**
|
566 |
+
* @return string
|
567 |
+
*/
|
568 |
+
public function getString() {
|
569 |
+
return $this->string;
|
570 |
+
}
|
571 |
+
|
572 |
+
/**
|
573 |
+
* @param string $string
|
574 |
+
* @throws InvalidArgumentException
|
575 |
+
*/
|
576 |
+
public function setString($string) {
|
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 |
+
}
|
584 |
+
|
585 |
+
/**
|
586 |
+
* @return int
|
587 |
+
*/
|
588 |
+
public function getLength() {
|
589 |
+
return $this->length;
|
590 |
+
}
|
591 |
+
|
592 |
+
/**
|
593 |
+
* @param int $length
|
594 |
+
*/
|
595 |
+
protected function setLength($length) {
|
596 |
+
$this->length = $length;
|
597 |
+
}
|
598 |
+
|
599 |
+
/**
|
600 |
+
* @param int $length
|
601 |
+
*/
|
602 |
+
public function advancePointer($length) {
|
603 |
+
$this->setPointer($this->getPointer() + $length);
|
604 |
+
}
|
605 |
+
|
606 |
+
/**
|
607 |
+
* @return int
|
608 |
+
*/
|
609 |
+
public function getPointer() {
|
610 |
+
return $this->pointer;
|
611 |
+
}
|
612 |
+
|
613 |
+
/**
|
614 |
+
* @param int $pointer
|
615 |
+
*/
|
616 |
+
protected function setPointer($pointer) {
|
617 |
+
$this->pointer = $pointer;
|
618 |
+
}
|
619 |
+
|
620 |
+
/**
|
621 |
+
* @return int
|
622 |
+
*/
|
623 |
+
public function getPrevPointer() {
|
624 |
+
return $this->prevPointer;
|
625 |
+
}
|
626 |
+
|
627 |
+
/**
|
628 |
+
* @param int $prevPointer
|
629 |
+
*/
|
630 |
+
protected function setPrevPointer($prevPointer) {
|
631 |
+
$this->prevPointer = $prevPointer;
|
632 |
+
}
|
633 |
+
|
634 |
+
/**
|
635 |
+
* @return mixed
|
636 |
+
*/
|
637 |
+
public function getMatch() {
|
638 |
+
return $this->match;
|
639 |
+
}
|
640 |
+
|
641 |
+
/**
|
642 |
+
* @param mixed $match
|
643 |
+
*/
|
644 |
+
protected function setMatch($match) {
|
645 |
+
$this->match = $match;
|
646 |
+
}
|
647 |
+
|
648 |
+
/**
|
649 |
+
* @param null $index
|
650 |
+
* @return mixed
|
651 |
+
*/
|
652 |
+
public function getCaptures($index = null) {
|
653 |
+
if (is_numeric($index)) {
|
654 |
+
return isset($this->captures[$index]) ? $this->captures[$index] : null;
|
655 |
+
}
|
656 |
+
return $this->captures;
|
657 |
+
}
|
658 |
+
|
659 |
+
/**
|
660 |
+
* @param mixed $captures
|
661 |
+
*/
|
662 |
+
protected function setCaptures($captures) {
|
663 |
+
$this->captures = $captures;
|
664 |
+
}
|
665 |
+
}
|
666 |
+
|
667 |
+
|
vendor/wordfence/wf-waf/src/lib/parser/parser.php
ADDED
@@ -0,0 +1,754 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once dirname(__FILE__) . '/lexer.php';
|
4 |
+
|
5 |
+
class wfWAFRuleParser extends wfWAFBaseParser {
|
6 |
+
|
7 |
+
/**
|
8 |
+
* @var wfWAF
|
9 |
+
*/
|
10 |
+
private $waf;
|
11 |
+
private $parenCount = 0;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* wfWAFRuleParser constructor.
|
15 |
+
* @param $lexer
|
16 |
+
* @param wfWAF $waf
|
17 |
+
*/
|
18 |
+
public function __construct($lexer, $waf) {
|
19 |
+
parent::__construct($lexer);
|
20 |
+
$this->setWAF($waf);
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @return array
|
25 |
+
* @throws wfWAFParserSyntaxError
|
26 |
+
* @throws wfWAFRuleParserSyntaxError
|
27 |
+
*/
|
28 |
+
public function parse() {
|
29 |
+
$rules = array();
|
30 |
+
$scores = array();
|
31 |
+
$blacklistedParams = array();
|
32 |
+
$whitelistedParams = array();
|
33 |
+
$variables = array();
|
34 |
+
$this->index = -1;
|
35 |
+
while ($token = $this->nextToken()) {
|
36 |
+
|
37 |
+
// Rule parsing
|
38 |
+
if ($token->getType() == wfWAFRuleLexer::T_RULE_START) {
|
39 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_OPEN_PARENTHESIS);
|
40 |
+
|
41 |
+
$comparisonGroup = $this->parseConditional();
|
42 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_CLOSE_PARENTHESIS);
|
43 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_RULE_COMPARISON_END);
|
44 |
+
$action = $this->parseAction();
|
45 |
+
|
46 |
+
$rules[] = new wfWAFRule(
|
47 |
+
$this->getWAF(),
|
48 |
+
$action->getRuleID(),
|
49 |
+
$action->getType(),
|
50 |
+
$action->getCategory(),
|
51 |
+
$action->getScore(),
|
52 |
+
$action->getDescription(),
|
53 |
+
$action->getAction(),
|
54 |
+
$comparisonGroup
|
55 |
+
);
|
56 |
+
}
|
57 |
+
|
58 |
+
// Score/config parsing
|
59 |
+
if ($token->getType() == wfWAFRuleLexer::T_IDENTIFIER) {
|
60 |
+
switch ($token->getValue()) {
|
61 |
+
case 'scores':
|
62 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_DOT);
|
63 |
+
$scoreCategoryToken = $this->expectNextToken();
|
64 |
+
$this->expectTokenTypeEquals($scoreCategoryToken, wfWAFRuleLexer::T_IDENTIFIER);
|
65 |
+
|
66 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_ASSIGNMENT);
|
67 |
+
|
68 |
+
$scoreToken = $this->expectNextToken();
|
69 |
+
$this->expectTokenTypeEquals($scoreToken, wfWAFRuleLexer::T_NUMBER_LITERAL);
|
70 |
+
$scores[$scoreCategoryToken->getValue()] = $scoreToken->getValue();
|
71 |
+
break;
|
72 |
+
|
73 |
+
case 'blacklistParam':
|
74 |
+
$blacklistedParams[] = $this->parseURLParams();
|
75 |
+
break;
|
76 |
+
|
77 |
+
case 'whitelistParam':
|
78 |
+
$whitelistedParams[] = $this->parseURLParams();
|
79 |
+
break;
|
80 |
+
|
81 |
+
default:
|
82 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_ASSIGNMENT);
|
83 |
+
$valueToken = $this->expectNextToken();
|
84 |
+
$this->expectTokenTypeInArray($valueToken, array(
|
85 |
+
wfWAFRuleLexer::T_SINGLE_STRING_LITERAL,
|
86 |
+
wfWAFRuleLexer::T_DOUBLE_STRING_LITERAL,
|
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();
|
97 |
+
}
|
98 |
+
$variables[$token->getValue()] = new wfWAFRuleVariable($this->getWAF(), $token->getValue(), $value);
|
99 |
+
break;
|
100 |
+
}
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
return array(
|
105 |
+
'scores' => $scores,
|
106 |
+
'blacklistedParams' => $blacklistedParams,
|
107 |
+
'whitelistedParams' => $whitelistedParams,
|
108 |
+
'variables' => $variables,
|
109 |
+
'rules' => $rules,
|
110 |
+
);
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* @param array $vars
|
115 |
+
* @return string
|
116 |
+
*/
|
117 |
+
public function renderRules($vars) {
|
118 |
+
$rules = '';
|
119 |
+
if (array_key_exists('scores', $vars)) {
|
120 |
+
foreach ($vars['scores'] as $category => $score) {
|
121 |
+
// scores.sqli = 100
|
122 |
+
$rules .= sprintf("scores.%s = %d\n", $category, $score);
|
123 |
+
}
|
124 |
+
$rules .= "\n";
|
125 |
+
}
|
126 |
+
|
127 |
+
$params = array(
|
128 |
+
'blacklistParam' => 'blacklistedParams',
|
129 |
+
'whitelistParam' => 'whitelistedParams',
|
130 |
+
);
|
131 |
+
foreach ($params as $action => $key) {
|
132 |
+
if (array_key_exists($key, $vars)) {
|
133 |
+
/** @var wfWAFRuleParserURLParam $urlParam */
|
134 |
+
foreach ($vars[$key] as $urlParam) {
|
135 |
+
$rules .= $urlParam->renderRule($action) . "\n";
|
136 |
+
}
|
137 |
+
$rules .= "\n";
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
+
if (array_key_exists('variables', $vars)) {
|
142 |
+
/** @var wfWAFRuleVariable $variable */
|
143 |
+
foreach ($vars['variables'] as $variableName => $variable) {
|
144 |
+
$rules .= sprintf("%s = %s\n", $variable->renderRule(), $variable->renderValue());
|
145 |
+
}
|
146 |
+
$rules .= "\n";
|
147 |
+
}
|
148 |
+
|
149 |
+
if (array_key_exists('rules', $vars)) {
|
150 |
+
/** @var wfWAFRule $rule */
|
151 |
+
foreach ($vars['rules'] as $rule) {
|
152 |
+
$rules .= $rule->renderRule() . "\n";
|
153 |
+
}
|
154 |
+
$rules .= "\n";
|
155 |
+
}
|
156 |
+
return $rules;
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* @param int $index
|
161 |
+
* @return mixed
|
162 |
+
*/
|
163 |
+
public function getToken($index) {
|
164 |
+
if (is_array($this->tokens) && array_key_exists($index, $this->tokens)) {
|
165 |
+
return $this->tokens[$index];
|
166 |
+
}
|
167 |
+
if ($token = $this->getLexer()->nextToken()) {
|
168 |
+
$this->tokens[$index] = $token;
|
169 |
+
return $this->tokens[$index];
|
170 |
+
}
|
171 |
+
return false;
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* @return wfWAFRuleComparisonGroup
|
176 |
+
*/
|
177 |
+
private function parseConditional() {
|
178 |
+
$comparisonGroup = new wfWAFRuleComparisonGroup();
|
179 |
+
while ($token = $this->nextToken()) {
|
180 |
+
switch ($token->getType()) {
|
181 |
+
case wfWAFRuleLexer::T_IDENTIFIER:
|
182 |
+
$comparisonGroup->add($this->parseComparison());
|
183 |
+
break;
|
184 |
+
|
185 |
+
case wfWAFRuleLexer::T_COMPARISON_OPERATOR:
|
186 |
+
$comparisonGroup->add(new wfWAFRuleLogicalOperator($token->getValue()));
|
187 |
+
break;
|
188 |
+
|
189 |
+
case wfWAFRuleLexer::T_OPEN_PARENTHESIS:
|
190 |
+
$this->parenCount++;
|
191 |
+
$comparisonGroup->add($this->parseConditional());
|
192 |
+
break;
|
193 |
+
|
194 |
+
case wfWAFRuleLexer::T_CLOSE_PARENTHESIS:
|
195 |
+
if ($this->parenCount === 0) {
|
196 |
+
$this->index--;
|
197 |
+
return $comparisonGroup;
|
198 |
+
}
|
199 |
+
$this->parenCount--;
|
200 |
+
return $comparisonGroup;
|
201 |
+
}
|
202 |
+
|
203 |
+
}
|
204 |
+
return $comparisonGroup;
|
205 |
+
}
|
206 |
+
|
207 |
+
private function parseComparison() {
|
208 |
+
/**
|
209 |
+
* @var wfWAFLexerToken $actionToken
|
210 |
+
* @var wfWAFLexerToken $expectedToken
|
211 |
+
*/
|
212 |
+
$actionToken = $this->currentToken();
|
213 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_OPEN_PARENTHESIS);
|
214 |
+
$value = $this->expectLiteral();
|
215 |
+
|
216 |
+
$subjects = array();
|
217 |
+
while (true) {
|
218 |
+
$commaToken = $this->nextToken();
|
219 |
+
if (!($commaToken && $commaToken->getType() === wfWAFRuleLexer::T_COMMA)) {
|
220 |
+
$this->index--;
|
221 |
+
break;
|
222 |
+
}
|
223 |
+
list($filters, $subject) = $this->parseFilters();
|
224 |
+
$subjects[] = new wfWAFRuleComparisonSubject($this->getWAF(), $subject, $filters);
|
225 |
+
}
|
226 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_CLOSE_PARENTHESIS);
|
227 |
+
|
228 |
+
$comparison = new wfWAFRuleComparison($this->getWAF(), $actionToken->getValue(), $value, $subjects);
|
229 |
+
return $comparison;
|
230 |
+
}
|
231 |
+
|
232 |
+
/**
|
233 |
+
* @return wfWAFRuleParserAction
|
234 |
+
*/
|
235 |
+
private function parseAction() {
|
236 |
+
$action = new wfWAFRuleParserAction();
|
237 |
+
|
238 |
+
$actionToken = $this->expectNextToken();
|
239 |
+
$this->expectTokenTypeEquals($actionToken, wfWAFRuleLexer::T_IDENTIFIER);
|
240 |
+
$action->setAction($actionToken->getValue());
|
241 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_OPEN_PARENTHESIS);
|
242 |
+
|
243 |
+
while (true) {
|
244 |
+
$token = $this->expectNextToken();
|
245 |
+
switch ($token->getType()) {
|
246 |
+
case wfWAFRuleLexer::T_IDENTIFIER:
|
247 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_ASSIGNMENT);
|
248 |
+
$valueToken = $this->expectNextToken();
|
249 |
+
$this->expectTokenTypeInArray($valueToken, array(
|
250 |
+
wfWAFRuleLexer::T_SINGLE_STRING_LITERAL,
|
251 |
+
wfWAFRuleLexer::T_DOUBLE_STRING_LITERAL,
|
252 |
+
wfWAFRuleLexer::T_NUMBER_LITERAL,
|
253 |
+
));
|
254 |
+
$action->set($token->getValue(), $valueToken->getValue());
|
255 |
+
break;
|
256 |
+
|
257 |
+
case wfWAFRuleLexer::T_COMMA:
|
258 |
+
break;
|
259 |
+
|
260 |
+
case wfWAFRuleLexer::T_CLOSE_PARENTHESIS:
|
261 |
+
break 2;
|
262 |
+
|
263 |
+
default:
|
264 |
+
$this->triggerSyntaxError($token, sprintf('Wordfence WAF Rules Syntax Error: Unexpected %s found on line %d, column %d',
|
265 |
+
$token->getType(), $token->getLine(), $token->getColumn()));
|
266 |
+
}
|
267 |
+
}
|
268 |
+
return $action;
|
269 |
+
}
|
270 |
+
|
271 |
+
private function parseFilters() {
|
272 |
+
$filters = array();
|
273 |
+
$subject = null;
|
274 |
+
do {
|
275 |
+
$globalToken = $this->expectNextToken();
|
276 |
+
$this->expectTokenTypeEquals($globalToken, wfWAFRuleLexer::T_IDENTIFIER);
|
277 |
+
$parenToken = $this->expectNextToken();
|
278 |
+
switch ($parenToken->getType()) {
|
279 |
+
case wfWAFRuleLexer::T_DOT:
|
280 |
+
$this->index -= 2;
|
281 |
+
$subject = $this->parseSubject();
|
282 |
+
break 2;
|
283 |
+
|
284 |
+
case wfWAFRuleLexer::T_OPEN_PARENTHESIS:
|
285 |
+
$filters[] = $globalToken->getValue();
|
286 |
+
break;
|
287 |
+
|
288 |
+
default:
|
289 |
+
$this->triggerSyntaxError($parenToken,
|
290 |
+
sprintf('Wordfence WAF Rules Syntax Error: Unexpected %s found on line %d, column %d.',
|
291 |
+
$parenToken->getType(), $parenToken->getLine(), $parenToken->getColumn()));
|
292 |
+
}
|
293 |
+
} while (true);
|
294 |
+
if ($subject === null) {
|
295 |
+
throw new wfWAFParserSyntaxError('No subject supplied to filter');
|
296 |
+
}
|
297 |
+
for ($i = 0; $i < count($filters); $i++) {
|
298 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_CLOSE_PARENTHESIS);
|
299 |
+
}
|
300 |
+
return array($filters, $subject);
|
301 |
+
}
|
302 |
+
|
303 |
+
/**
|
304 |
+
* @throws wfWAFParserSyntaxError
|
305 |
+
*/
|
306 |
+
private function parseSubject() {
|
307 |
+
$globalToken = $this->expectNextToken();
|
308 |
+
$this->expectTokenTypeEquals($globalToken, wfWAFRuleLexer::T_IDENTIFIER);
|
309 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_DOT);
|
310 |
+
$globalToken2 = $this->expectNextToken();
|
311 |
+
$this->expectTokenTypeEquals($globalToken2, wfWAFRuleLexer::T_IDENTIFIER);
|
312 |
+
$subject = array(
|
313 |
+
$globalToken->getValue() . '.' . $globalToken2->getValue(),
|
314 |
+
);
|
315 |
+
while (true) {
|
316 |
+
$dotToken = $this->expectNextToken();
|
317 |
+
if ($dotToken->getType() !== wfWAFRuleLexer::T_DOT) {
|
318 |
+
$this->index--;
|
319 |
+
break;
|
320 |
+
}
|
321 |
+
$paramToken = $this->expectNextToken();
|
322 |
+
$this->expectTokenTypeEquals($paramToken, wfWAFRuleLexer::T_IDENTIFIER);
|
323 |
+
$subject[] = $paramToken->getValue();
|
324 |
+
}
|
325 |
+
if (count($subject) === 1) {
|
326 |
+
list($subject) = $subject;
|
327 |
+
}
|
328 |
+
return $subject;
|
329 |
+
}
|
330 |
+
|
331 |
+
/**
|
332 |
+
* @return wfWAFRuleParserURLParam
|
333 |
+
* @throws wfWAFParserSyntaxError
|
334 |
+
*/
|
335 |
+
private function parseURLParams() {
|
336 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_OPEN_PARENTHESIS);
|
337 |
+
|
338 |
+
$urlParam = new wfWAFRuleParserURLParam();
|
339 |
+
while (true) {
|
340 |
+
$token = $this->expectNextToken();
|
341 |
+
switch ($token->getType()) {
|
342 |
+
case wfWAFRuleLexer::T_IDENTIFIER:
|
343 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_ASSIGNMENT);
|
344 |
+
if ($token->getValue() === 'url') {
|
345 |
+
$url = $this->expectLiteral();
|
346 |
+
$urlParam->setUrl($url);
|
347 |
+
} else if ($token->getValue() === 'param') {
|
348 |
+
$subject = $this->parseSubject();
|
349 |
+
$urlParam->setParam(wfWAFRuleComparison::getSubjectKey($subject));
|
350 |
+
} else if ($token->getValue() === 'rules') {
|
351 |
+
$rules = $this->expectLiteral();
|
352 |
+
$urlParam->setRules($rules);
|
353 |
+
}
|
354 |
+
|
355 |
+
break;
|
356 |
+
|
357 |
+
case wfWAFRuleLexer::T_COMMA:
|
358 |
+
break;
|
359 |
+
|
360 |
+
case wfWAFRuleLexer::T_CLOSE_PARENTHESIS:
|
361 |
+
break 2;
|
362 |
+
|
363 |
+
default:
|
364 |
+
$this->triggerSyntaxError($token, sprintf('Wordfence WAF Rules Syntax Error: Unexpected %s found on line %d, column %d',
|
365 |
+
$token->getType(), $token->getLine(), $token->getColumn()));
|
366 |
+
}
|
367 |
+
}
|
368 |
+
return $urlParam;
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* @return mixed|string
|
373 |
+
* @throws wfWAFRuleParserSyntaxError
|
374 |
+
*/
|
375 |
+
private function expectLiteral() {
|
376 |
+
$expectedToken = $this->expectNextToken();
|
377 |
+
$this->expectTokenTypeInArray($expectedToken, array(
|
378 |
+
wfWAFRuleLexer::T_SINGLE_STRING_LITERAL,
|
379 |
+
wfWAFRuleLexer::T_DOUBLE_STRING_LITERAL,
|
380 |
+
wfWAFRuleLexer::T_IDENTIFIER,
|
381 |
+
wfWAFRuleLexer::T_NUMBER_LITERAL,
|
382 |
+
wfWAFRuleLexer::T_OPEN_BRACKET,
|
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
|
394 |
+
$value = new wfWAFRuleVariable($this->getWAF(), $expectedToken->getValue());
|
395 |
+
} else if ($expectedToken->getType() === wfWAFRuleLexer::T_OPEN_BRACKET) {
|
396 |
+
$value = array();
|
397 |
+
while (true) {
|
398 |
+
$nextToken = $this->expectNextToken();
|
399 |
+
if ($nextToken->getType() === wfWAFRuleLexer::T_CLOSE_BRACKET) {
|
400 |
+
break;
|
401 |
+
}
|
402 |
+
if ($nextToken->getType() === wfWAFRuleLexer::T_COMMA) {
|
403 |
+
continue;
|
404 |
+
}
|
405 |
+
$this->index--;
|
406 |
+
$value[] = $this->expectLiteral();
|
407 |
+
}
|
408 |
+
} else {
|
409 |
+
$value = $expectedToken->getValue();
|
410 |
+
}
|
411 |
+
return $value;
|
412 |
+
}
|
413 |
+
|
414 |
+
/**
|
415 |
+
* @param wfWAFLexerToken $token
|
416 |
+
* @return bool
|
417 |
+
*/
|
418 |
+
protected function isCommentToken($token) {
|
419 |
+
return $token->getType() === wfWAFRuleLexer::T_MULTIPLE_LINE_COMMENT || $token->getType() === wfWAFRuleLexer::T_SINGLE_LINE_COMMENT;
|
420 |
+
}
|
421 |
+
|
422 |
+
/**
|
423 |
+
* @return wfWAF
|
424 |
+
*/
|
425 |
+
public function getWAF() {
|
426 |
+
return $this->waf;
|
427 |
+
}
|
428 |
+
|
429 |
+
/**
|
430 |
+
* @param wfWAF $waf
|
431 |
+
*/
|
432 |
+
public function setWAF($waf) {
|
433 |
+
$this->waf = $waf;
|
434 |
+
}
|
435 |
+
}
|
436 |
+
|
437 |
+
class wfWAFRuleParserAction {
|
438 |
+
|
439 |
+
private $ruleID;
|
440 |
+
private $type;
|
441 |
+
private $category;
|
442 |
+
private $score;
|
443 |
+
private $description;
|
444 |
+
private $action;
|
445 |
+
|
446 |
+
/**
|
447 |
+
* @param string $param
|
448 |
+
* @param mixed $value
|
449 |
+
*/
|
450 |
+
public function set($param, $value) {
|
451 |
+
$propLinkTable = array(
|
452 |
+
'id' => 'ruleID',
|
453 |
+
);
|
454 |
+
if (array_key_exists($param, $propLinkTable)) {
|
455 |
+
$param = $propLinkTable[$param];
|
456 |
+
}
|
457 |
+
if (property_exists($this, $param)) {
|
458 |
+
$this->$param = trim($value, '\'"');
|
459 |
+
}
|
460 |
+
}
|
461 |
+
|
462 |
+
/**
|
463 |
+
* @return mixed
|
464 |
+
*/
|
465 |
+
public function getRuleID() {
|
466 |
+
return $this->ruleID;
|
467 |
+
}
|
468 |
+
|
469 |
+
/**
|
470 |
+
* @param mixed $ruleID
|
471 |
+
*/
|
472 |
+
public function setRuleID($ruleID) {
|
473 |
+
$this->ruleID = $ruleID;
|
474 |
+
}
|
475 |
+
|
476 |
+
/**
|
477 |
+
* @return mixed
|
478 |
+
*/
|
479 |
+
public function getType() {
|
480 |
+
return $this->type;
|
481 |
+
}
|
482 |
+
|
483 |
+
/**
|
484 |
+
* @param mixed $type
|
485 |
+
*/
|
486 |
+
public function setType($type) {
|
487 |
+
$this->type = $type;
|
488 |
+
}
|
489 |
+
|
490 |
+
/**
|
491 |
+
* @return mixed
|
492 |
+
*/
|
493 |
+
public function getCategory() {
|
494 |
+
return $this->category;
|
495 |
+
}
|
496 |
+
|
497 |
+
/**
|
498 |
+
* @param mixed $category
|
499 |
+
*/
|
500 |
+
public function setCategory($category) {
|
501 |
+
$this->category = $category;
|
502 |
+
}
|
503 |
+
|
504 |
+
/**
|
505 |
+
* @return mixed
|
506 |
+
*/
|
507 |
+
public function getScore() {
|
508 |
+
return $this->score;
|
509 |
+
}
|
510 |
+
|
511 |
+
/**
|
512 |
+
* @param mixed $score
|
513 |
+
*/
|
514 |
+
public function setScore($score) {
|
515 |
+
$this->score = $score;
|
516 |
+
}
|
517 |
+
|
518 |
+
/**
|
519 |
+
* @return mixed
|
520 |
+
*/
|
521 |
+
public function getDescription() {
|
522 |
+
return $this->description;
|
523 |
+
}
|
524 |
+
|
525 |
+
/**
|
526 |
+
* @param mixed $description
|
527 |
+
*/
|
528 |
+
public function setDescription($description) {
|
529 |
+
$this->description = $description;
|
530 |
+
}
|
531 |
+
|
532 |
+
/**
|
533 |
+
* @return mixed
|
534 |
+
*/
|
535 |
+
public function getAction() {
|
536 |
+
return $this->action;
|
537 |
+
}
|
538 |
+
|
539 |
+
/**
|
540 |
+
* @param mixed $action
|
541 |
+
*/
|
542 |
+
public function setAction($action) {
|
543 |
+
$this->action = $action;
|
544 |
+
}
|
545 |
+
}
|
546 |
+
|
547 |
+
|
548 |
+
class wfWAFRuleParserURLParam {
|
549 |
+
/**
|
550 |
+
* @var string
|
551 |
+
*/
|
552 |
+
private $url;
|
553 |
+
/**
|
554 |
+
* @var string
|
555 |
+
*/
|
556 |
+
private $param;
|
557 |
+
/**
|
558 |
+
* @var null
|
559 |
+
*/
|
560 |
+
private $rules;
|
561 |
+
|
562 |
+
/**
|
563 |
+
* @param string $param
|
564 |
+
* @param mixed $value
|
565 |
+
*/
|
566 |
+
public function set($param, $value) {
|
567 |
+
if (property_exists($this, $param)) {
|
568 |
+
$this->$param = trim($value, '\'"');
|
569 |
+
}
|
570 |
+
}
|
571 |
+
|
572 |
+
/**
|
573 |
+
* @param string $url
|
574 |
+
* @param string $param
|
575 |
+
* @param null $rules
|
576 |
+
*/
|
577 |
+
public function __construct($url = null, $param = null, $rules = null) {
|
578 |
+
$this->url = $url;
|
579 |
+
$this->param = $param;
|
580 |
+
$this->rules = $rules;
|
581 |
+
}
|
582 |
+
|
583 |
+
/**
|
584 |
+
* Return format:
|
585 |
+
* blacklistParam(url='/\/uploadify\.php$/i', param=request.fileNames.Filedata, rules=[3, 14])
|
586 |
+
*
|
587 |
+
* @param string $action
|
588 |
+
* @return string
|
589 |
+
*/
|
590 |
+
public function renderRule($action) {
|
591 |
+
return sprintf('%s(url=%s, param=%s%s)', $action, wfWAFRule::exportString($this->getUrl()), $this->renderParam($this->getParam()),
|
592 |
+
$this->getRules() ? ', rules=[' . join(', ', array_map('intval', $this->getRules())) . ']' : '');
|
593 |
+
}
|
594 |
+
|
595 |
+
/**
|
596 |
+
* @param string $param
|
597 |
+
* @return mixed
|
598 |
+
*/
|
599 |
+
private function renderParam($param) {
|
600 |
+
return str_replace(array('[', ']'), array('.', ''), $param);
|
601 |
+
}
|
602 |
+
|
603 |
+
/**
|
604 |
+
* @return string
|
605 |
+
*/
|
606 |
+
public function getUrl() {
|
607 |
+
return $this->url;
|
608 |
+
}
|
609 |
+
|
610 |
+
/**
|
611 |
+
* @param string $url
|
612 |
+
*/
|
613 |
+
public function setUrl($url) {
|
614 |
+
$this->url = $url;
|
615 |
+
}
|
616 |
+
|
617 |
+
/**
|
618 |
+
* @return string
|
619 |
+
*/
|
620 |
+
public function getParam() {
|
621 |
+
return $this->param;
|
622 |
+
}
|
623 |
+
|
624 |
+
/**
|
625 |
+
* @param string $param
|
626 |
+
*/
|
627 |
+
public function setParam($param) {
|
628 |
+
$this->param = $param;
|
629 |
+
}
|
630 |
+
|
631 |
+
/**
|
632 |
+
* @return null
|
633 |
+
*/
|
634 |
+
public function getRules() {
|
635 |
+
return $this->rules;
|
636 |
+
}
|
637 |
+
|
638 |
+
/**
|
639 |
+
* @param null $rules
|
640 |
+
*/
|
641 |
+
public function setRules($rules) {
|
642 |
+
$this->rules = $rules;
|
643 |
+
}
|
644 |
+
}
|
645 |
+
|
646 |
+
class wfWAFRuleParserSyntaxError extends wfWAFParserSyntaxError {
|
647 |
+
|
648 |
+
private $token;
|
649 |
+
|
650 |
+
/**
|
651 |
+
* @return mixed
|
652 |
+
*/
|
653 |
+
public function getToken() {
|
654 |
+
return $this->token;
|
655 |
+
}
|
656 |
+
|
657 |
+
/**
|
658 |
+
* @param mixed $token
|
659 |
+
*/
|
660 |
+
public function setToken($token) {
|
661 |
+
$this->token = $token;
|
662 |
+
}
|
663 |
+
}
|
664 |
+
|
665 |
+
class wfWAFRuleVariable {
|
666 |
+
/**
|
667 |
+
* @var string
|
668 |
+
*/
|
669 |
+
private $name;
|
670 |
+
/**
|
671 |
+
* @var mixed|null
|
672 |
+
*/
|
673 |
+
private $value;
|
674 |
+
/**
|
675 |
+
* @var wfWAF
|
676 |
+
*/
|
677 |
+
private $waf;
|
678 |
+
|
679 |
+
|
680 |
+
/**
|
681 |
+
* wfWAFRuleVariable constructor.
|
682 |
+
* @param wfWAF $waf
|
683 |
+
* @param string $name
|
684 |
+
* @param mixed $value
|
685 |
+
*/
|
686 |
+
public function __construct($waf, $name, $value = null) {
|
687 |
+
$this->waf = $waf;
|
688 |
+
$this->name = $name;
|
689 |
+
$this->value = $value;
|
690 |
+
}
|
691 |
+
|
692 |
+
public function render() {
|
693 |
+
return sprintf('new %s($this, %s, %s)', get_class($this),
|
694 |
+
var_export($this->getName(), true), var_export($this->getValue(), true));
|
695 |
+
}
|
696 |
+
|
697 |
+
public function renderRule() {
|
698 |
+
return sprintf('%s', $this->getName());
|
699 |
+
}
|
700 |
+
|
701 |
+
public function renderValue() {
|
702 |
+
return wfWAFRule::exportString($this);
|
703 |
+
}
|
704 |
+
|
705 |
+
public function __toString() {
|
706 |
+
$value = $this->getValue();
|
707 |
+
if (is_string($value)) {
|
708 |
+
return $value;
|
709 |
+
}
|
710 |
+
return (string) $this->getWAF()->getVariable($this->getName());
|
711 |
+
}
|
712 |
+
|
713 |
+
/**
|
714 |
+
* @return string
|
715 |
+
*/
|
716 |
+
public function getName() {
|
717 |
+
return $this->name;
|
718 |
+
}
|
719 |
+
|
720 |
+
/**
|
721 |
+
* @param string $name
|
722 |
+
*/
|
723 |
+
public function setName($name) {
|
724 |
+
$this->name = $name;
|
725 |
+
}
|
726 |
+
|
727 |
+
/**
|
728 |
+
* @return mixed|null
|
729 |
+
*/
|
730 |
+
public function getValue() {
|
731 |
+
return $this->value;
|
732 |
+
}
|
733 |
+
|
734 |
+
/**
|
735 |
+
* @param mixed|null $value
|
736 |
+
*/
|
737 |
+
public function setValue($value) {
|
738 |
+
$this->value = $value;
|
739 |
+
}
|
740 |
+
|
741 |
+
/**
|
742 |
+
* @return wfWAF
|
743 |
+
*/
|
744 |
+
public function getWAF() {
|
745 |
+
return $this->waf;
|
746 |
+
}
|
747 |
+
|
748 |
+
/**
|
749 |
+
* @param wfWAF $waf
|
750 |
+
*/
|
751 |
+
public function setWAF($waf) {
|
752 |
+
$this->waf = $waf;
|
753 |
+
}
|
754 |
+
}
|
vendor/wordfence/wf-waf/src/lib/parser/sqli.php
ADDED
@@ -0,0 +1,2971 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class wfWAFSQLiParser extends wfWAFBaseParser {
|
4 |
+
|
5 |
+
const FLAG_PARSE_MYSQL_PORTABLE_COMMENTS = wfWAFSQLiLexer::FLAG_TOKENIZE_MYSQL_PORTABLE_COMMENTS;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* @param string $param
|
9 |
+
* @return bool
|
10 |
+
*/
|
11 |
+
public static function testForSQLi($param) {
|
12 |
+
static $instance;
|
13 |
+
static $tests;
|
14 |
+
if (!$instance) {
|
15 |
+
$instance = new self(new wfWAFSQLiLexer());
|
16 |
+
}
|
17 |
+
if (!$tests) {
|
18 |
+
// SQL statement and token count for lexer
|
19 |
+
$tests = array(
|
20 |
+
array('%s', 1),
|
21 |
+
array('SELECT * FROM t WHERE i = %s ', 8),
|
22 |
+
array("SELECT * FROM t WHERE i = '%s' ", 8),
|
23 |
+
array('SELECT * FROM t WHERE i = "%s" ', 8),
|
24 |
+
array('SELECT * FROM t WHERE i = (%s) ', 10),
|
25 |
+
array("SELECT * FROM t WHERE i = ('%s') ", 10),
|
26 |
+
array('SELECT * FROM t WHERE i = ("%s") ', 10),
|
27 |
+
array('SELECT * FROM t WHERE i = ((%s)) ', 12),
|
28 |
+
array("SELECT * FROM t WHERE i = (('%s')) ", 12),
|
29 |
+
array('SELECT * FROM t WHERE i = (("%s")) ', 12),
|
30 |
+
array('SELECT * FROM t WHERE i = (((%s))) ', 14),
|
31 |
+
array("SELECT * FROM t WHERE i = ((('%s'))) ", 14),
|
32 |
+
array('SELECT * FROM t WHERE i = ((("%s"))) ', 14),
|
33 |
+
|
34 |
+
array('SELECT * FROM t WHERE i = %s and j = (1
|
35 |
+
) ', 14),
|
36 |
+
array("SELECT * FROM t WHERE i = '%s' and j = (1
|
37 |
+
) ", 14),
|
38 |
+
array('SELECT * FROM t WHERE i = "%s" and j = (1
|
39 |
+
) ', 14),
|
40 |
+
|
41 |
+
array('SELECT MATCH(t) AGAINST (%s) from t ', 11),
|
42 |
+
array("SELECT MATCH(t) AGAINST ('%s') from t ", 11),
|
43 |
+
array('SELECT MATCH(t) AGAINST ("%s") from t ', 11),
|
44 |
+
|
45 |
+
// array('SELECT CASE WHEN %s THEN 1 ELSE 0 END from t ', 11),
|
46 |
+
// array("SELECT CASE WHEN '%s' THEN 1 ELSE 0 END from t ", 11),
|
47 |
+
// array('SELECT CASE WHEN "%s" THEN 1 ELSE 0 END from t ', 11),
|
48 |
+
//
|
49 |
+
// array('SELECT (CASE WHEN (%s) THEN 1 ELSE 0 END) from t ', 15),
|
50 |
+
// array("SELECT (CASE WHEN ('%s') THEN 1 ELSE 0 END) from t ", 15),
|
51 |
+
// array('SELECT (CASE WHEN ("%s") THEN 1 ELSE 0 END) from t ', 15),
|
52 |
+
|
53 |
+
array('SELECT * FROM (select %s) ', 7),
|
54 |
+
array("SELECT * FROM (select '%s') ", 7),
|
55 |
+
array('SELECT * FROM (select "%s") ', 7),
|
56 |
+
array('SELECT * FROM (select (%s)) ', 9),
|
57 |
+
array("SELECT * FROM (select ('%s')) ", 9),
|
58 |
+
array('SELECT * FROM (select ("%s")) ', 9),
|
59 |
+
array('SELECT * FROM (select ((%s))) ', 11),
|
60 |
+
array("SELECT * FROM (select (('%s'))) ", 11),
|
61 |
+
array('SELECT * FROM (select (("%s"))) ', 11),
|
62 |
+
//
|
63 |
+
// array('SELECT * FROM t JOIN t2 on i = %s ', 10),
|
64 |
+
// array("SELECT * FROM t JOIN t2 on i = '%s' ", 10),
|
65 |
+
// array('SELECT * FROM t JOIN t2 on i = "%s" ', 10),
|
66 |
+
// array('SELECT * FROM t JOIN t2 on i = (%s) ', 12),
|
67 |
+
// array("SELECT * FROM t JOIN t2 on i = ('%s') ", 12),
|
68 |
+
// array('SELECT * FROM t JOIN t2 on i = ("%s") ', 12),
|
69 |
+
// array('SELECT * FROM t JOIN t2 on i = ((%s)) ', 14),
|
70 |
+
// array("SELECT * FROM t JOIN t2 on i = (('%s')) ", 14),
|
71 |
+
// array('SELECT * FROM t JOIN t2 on i = (("%s")) ', 14),
|
72 |
+
// array('SELECT * FROM t JOIN t2 on i = (((%s))) ', 16),
|
73 |
+
// array("SELECT * FROM t JOIN t2 on i = ((('%s'))) ", 16),
|
74 |
+
// array('SELECT * FROM t JOIN t2 on i = ((("%s"))) ', 16),
|
75 |
+
|
76 |
+
array('SELECT * FROM %s ', 4),
|
77 |
+
array('INSERT INTO t (col) VALUES (%s) ', 10),
|
78 |
+
array("INSERT INTO t (col) VALUES ('%s') ", 10),
|
79 |
+
array('INSERT INTO t (col) VALUES ("%s") ', 10),
|
80 |
+
array('UPDATE t1 SET col1 = %s ', 6),
|
81 |
+
array('UPDATE t1 SET col1 = \'%s\' ', 6),
|
82 |
+
);
|
83 |
+
}
|
84 |
+
$lexerFlags = array(0, wfWAFSQLiLexer::FLAG_TOKENIZE_MYSQL_PORTABLE_COMMENTS);
|
85 |
+
foreach ($lexerFlags as $flags) {
|
86 |
+
foreach ($tests as $test) {
|
87 |
+
// $startTime = microtime(true);
|
88 |
+
list($sql, $expectedTokenCount) = $test;
|
89 |
+
try {
|
90 |
+
$instance->setFlags($flags);
|
91 |
+
$instance->setSubject(sprintf($sql, $param));
|
92 |
+
if (($instance->hasMoreThanNumTokens($expectedTokenCount) && $instance->evaluate())
|
93 |
+
|| $instance->hasMultiplePortableCommentVersions()) {
|
94 |
+
// printf("%s took %f seconds\n", $sql, microtime(true) - $startTime);
|
95 |
+
return true;
|
96 |
+
}
|
97 |
+
// printf("%s took %f seconds\n", $sql, microtime(true) - $startTime);
|
98 |
+
} catch (wfWAFParserSyntaxError $e) {
|
99 |
+
|
100 |
+
}
|
101 |
+
}
|
102 |
+
}
|
103 |
+
return false;
|
104 |
+
}
|
105 |
+
|
106 |
+
private $subject;
|
107 |
+
|
108 |
+
/**
|
109 |
+
* @var int
|
110 |
+
*/
|
111 |
+
private $flags;
|
112 |
+
/** @var wfWAFSQLiLexer */
|
113 |
+
protected $lexer;
|
114 |
+
private $portableCommentVersions = array();
|
115 |
+
|
116 |
+
private $intervalUnits = array(
|
117 |
+
'SECOND',
|
118 |
+
'MINUTE',
|
119 |
+
'HOUR',
|
120 |
+
'DAY_SYM',
|
121 |
+
'WEEK',
|
122 |
+
'MONTH',
|
123 |
+
'QUARTER',
|
124 |
+
'YEAR',
|
125 |
+
'SECOND_MICROSECOND',
|
126 |
+
'MINUTE_MICROSECOND',
|
127 |
+
'MINUTE_SECOND',
|
128 |
+
'HOUR_MICROSECOND',
|
129 |
+
'HOUR_SECOND',
|
130 |
+
'HOUR_MINUTE',
|
131 |
+
'DAY_MICROSECOND',
|
132 |
+
'DAY_SECOND',
|
133 |
+
'DAY_MINUTE',
|
134 |
+
'DAY_HOUR',
|
135 |
+
'YEAR_MONTH',
|
136 |
+
);
|
137 |
+
|
138 |
+
private $keywords = array(
|
139 |
+
'ID',
|
140 |
+
'TIME',
|
141 |
+
'DATE',
|
142 |
+
'SQLTIME',
|
143 |
+
'ACCESSIBLE',
|
144 |
+
'ADD',
|
145 |
+
'ALL',
|
146 |
+
'ALTER',
|
147 |
+
'ANALYZE',
|
148 |
+
'AND',
|
149 |
+
'AS',
|
150 |
+
'ASC',
|
151 |
+
'ASENSITIVE',
|
152 |
+
'BEFORE',
|
153 |
+
'BETWEEN',
|
154 |
+
'BIGINT',
|
155 |
+
'BINARY',
|
156 |
+
'BLOB',
|
157 |
+
'BOTH',
|
158 |
+
'BY',
|
159 |
+
'CALL',
|
160 |
+
'CASCADE',
|
161 |
+
'CASE',
|
162 |
+
'CHANGE',
|
163 |
+
'CHAR',
|
164 |
+
'CHARACTER',
|
165 |
+
'CHECK',
|
166 |
+
'COLLATE',
|
167 |
+
'COLUMN',
|
168 |
+
'CONDITION',
|
169 |
+
'CONSTRAINT',
|
170 |
+
'CONTINUE',
|
171 |
+
'CONVERT',
|
172 |
+
'CREATE',
|
173 |
+
'CROSS',
|
174 |
+
'CURRENT_DATE',
|
175 |
+
'CURRENT_TIME',
|
176 |
+
'CURRENT_TIMESTAMP',
|
177 |
+
'CURRENT_USER',
|
178 |
+
'CURSOR',
|
179 |
+
'DATABASE',
|
180 |
+
'DATABASES',
|
181 |
+
'DAY_HOUR',
|
182 |
+
'DAY_MICROSECOND',
|
183 |
+
'DAY_MINUTE',
|
184 |
+
'DAY_SECOND',
|
185 |
+
'DEC',
|
186 |
+
'DECIMAL',
|
187 |
+
'DECLARE',
|
188 |
+
'DEFAULT',
|
189 |
+
'DELAYED',
|
190 |
+
'DELETE',
|
191 |
+
'DESC',
|
192 |
+
'DESCRIBE',
|
193 |
+
'DETERMINISTIC',
|
194 |
+
'DISTINCT',
|
195 |
+
'DISTINCTROW',
|
196 |
+
'DIV',
|
197 |
+
'DOUBLE',
|
198 |
+
'DROP',
|
199 |
+
'DUAL',
|
200 |
+
'EACH',
|
201 |
+
'ELSE',
|
202 |
+
'ELSEIF',
|
203 |
+
'ENCLOSED',
|
204 |
+
'ESCAPED',
|
205 |
+
'EXISTS',
|
206 |
+
'EXIT',
|
207 |
+
'EXPLAIN',
|
208 |
+
'FALSE',
|
209 |
+
'FETCH',
|
210 |
+
'FLOAT',
|
211 |
+
'FLOAT4',
|
212 |
+
'FLOAT8',
|
213 |
+
'FOR',
|
214 |
+
'FORCE',
|
215 |
+
'FOREIGN',
|
216 |
+
'FROM',
|
217 |
+
'FULLTEXT',
|
218 |
+
'GRANT',
|
219 |
+
'GROUP',
|
220 |
+
'HAVING',
|
221 |
+
'HIGH_PRIORITY',
|
222 |
+
'HOUR_MICROSECOND',
|
223 |
+
'HOUR_MINUTE',
|
224 |
+
'HOUR_SECOND',
|
225 |
+
'IF',
|
226 |
+
'IGNORE',
|
227 |
+
'IN',
|
228 |
+
'INDEX',
|
229 |
+
'INFILE',
|
230 |
+
'INNER',
|
231 |
+
'INOUT',
|
232 |
+
'INSENSITIVE',
|
233 |
+
'INSERT',
|
234 |
+
'INT',
|
235 |
+
'INT1',
|
236 |
+
'INT2',
|
237 |
+
'INT3',
|
238 |
+
'INT4',
|
239 |
+
'INT8',
|
240 |
+
'INTEGER',
|
241 |
+
'INTERVAL',
|
242 |
+
'INTO',
|
243 |
+
'IS',
|
244 |
+
'ITERATE',
|
245 |
+
'JOIN',
|
246 |
+
'KEY',
|
247 |
+
'KEYS',
|
248 |
+
'KILL',
|
249 |
+
'LEADING',
|
250 |
+
'LEAVE',
|
251 |
+
'LEFT',
|
252 |
+
'LIKE',
|
253 |
+
'LIMIT',
|
254 |
+
'LINEAR',
|
255 |
+
'LINES',
|
256 |
+
'LOAD',
|
257 |
+
'LOCALTIME',
|
258 |
+
'LOCALTIMESTAMP',
|
259 |
+
'LOCK',
|
260 |
+
'LONG',
|
261 |
+
'LONGBLOB',
|
262 |
+
'LONGTEXT',
|
263 |
+
'LOOP',
|
264 |
+
'LOW_PRIORITY',
|
265 |
+
'MASTER_SSL_VERIFY_SERVER_CERT',
|
266 |
+
'MATCH',
|
267 |
+
'MEDIUMBLOB',
|
268 |
+
'MEDIUMINT',
|
269 |
+
'MEDIUMTEXT',
|
270 |
+
'MIDDLEINT',
|
271 |
+
'MINUTE_MICROSECOND',
|
272 |
+
'MINUTE_SECOND',
|
273 |
+
'MOD',
|
274 |
+
'MODIFIES',
|
275 |
+
'NATURAL',
|
276 |
+
'NOT',
|
277 |
+
'NO_WRITE_TO_BINLOG',
|
278 |
+
'NULL',
|
279 |
+
'NUMERIC',
|
280 |
+
'ON',
|
281 |
+
'OPTIMIZE',
|
282 |
+
'OPTION',
|
283 |
+
'OPTIONALLY',
|
284 |
+
'OR',
|
285 |
+
'ORDER',
|
286 |
+
'OUT',
|
287 |
+
'OUTER',
|
288 |
+
'OUTFILE',
|
289 |
+
'PRECISION',
|
290 |
+
'PRIMARY',
|
291 |
+
'PROCEDURE',
|
292 |
+
'PURGE',
|
293 |
+
'RANGE',
|
294 |
+
'READ',
|
295 |
+
'READS',
|
296 |
+
'READ_WRITE',
|
297 |
+
'REAL',
|
298 |
+
'REFERENCES',
|
299 |
+
'REGEXP',
|
300 |
+
'RELEASE',
|
301 |
+
'RENAME',
|
302 |
+
'REPEAT',
|
303 |
+
'REPLACE',
|
304 |
+
'REQUIRE',
|
305 |
+
'RESTRICT',
|
306 |
+
'RETURN',
|
307 |
+
'REVOKE',
|
308 |
+
'RIGHT',
|
309 |
+
'RLIKE',
|
310 |
+
'SCHEMA',
|
311 |
+
'SCHEMAS',
|
312 |
+
'SECOND_MICROSECOND',
|
313 |
+
'SELECT',
|
314 |
+
'SENSITIVE',
|
315 |
+
'SEPARATOR',
|
316 |
+
'SET',
|
317 |
+
'SHOW',
|
318 |
+
'SMALLINT',
|
319 |
+
'SPATIAL',
|
320 |
+
'SPECIFIC',
|
321 |
+
'SQL',
|
322 |
+
'SQLEXCEPTION',
|
323 |
+
'SQLSTATE',
|
324 |
+
'SQLWARNING',
|
325 |
+
'SQL_BIG_RESULT',
|
326 |
+
'SQL_CALC_FOUND_ROWS',
|
327 |
+
'SQL_SMALL_RESULT',
|
328 |
+
'SSL',
|
329 |
+
'STARTING',
|
330 |
+
'STRAIGHT_JOIN',
|
331 |
+
'TABLE',
|
332 |
+
'TERMINATED',
|
333 |
+
'THEN',
|
334 |
+
'TINYBLOB',
|
335 |
+
'TINYINT',
|
336 |
+
'TINYTEXT',
|
337 |
+
'TO',
|
338 |
+
'TRAILING',
|
339 |
+
'TRIGGER',
|
340 |
+
'TRUE',
|
341 |
+
'UNDO',
|
342 |
+
'UNION',
|
343 |
+
'UNIQUE',
|
344 |
+
'UNLOCK',
|
345 |
+
'UNSIGNED',
|
346 |
+
'UPDATE',
|
347 |
+
'USAGE',
|
348 |
+
'USE',
|
349 |
+
'USING',
|
350 |
+
'UTC_DATE',
|
351 |
+
'UTC_TIME',
|
352 |
+
'UTC_TIMESTAMP',
|
353 |
+
'VALUES',
|
354 |
+
'VARBINARY',
|
355 |
+
'VARCHAR',
|
356 |
+
'VARCHARACTER',
|
357 |
+
'VARYING',
|
358 |
+
'WHEN',
|
359 |
+
'WHERE',
|
360 |
+
'WHILE',
|
361 |
+
'WITH',
|
362 |
+
'WRITE',
|
363 |
+
'XOR',
|
364 |
+
'YEAR_MONTH',
|
365 |
+
'ZEROFILL',
|
366 |
+
'ACCESSIBLE',
|
367 |
+
'LINEAR',
|
368 |
+
'MASTER_SSL_VERIFY_SERVER_CERT',
|
369 |
+
'RANGE',
|
370 |
+
'READ_ONLY',
|
371 |
+
'READ_WRITE',
|
372 |
+
);
|
373 |
+
|
374 |
+
private $numberFunctions = array(
|
375 |
+
'ABS',
|
376 |
+
'ACOS',
|
377 |
+
'ASIN',
|
378 |
+
'ATAN2',
|
379 |
+
'ATAN',
|
380 |
+
'CEIL',
|
381 |
+
'CEILING',
|
382 |
+
'CONV',
|
383 |
+
'COS',
|
384 |
+
'COT',
|
385 |
+
'CRC32',
|
386 |
+
'DEGREES',
|
387 |
+
'EXP',
|
388 |
+
'FLOOR',
|
389 |
+
'LN',
|
390 |
+
'LOG10',
|
391 |
+
'LOG2',
|
392 |
+
'LOG',
|
393 |
+
'MOD',
|
394 |
+
'PI',
|
395 |
+
'POW',
|
396 |
+
'POWER',
|
397 |
+
'RADIANS',
|
398 |
+
'RAND',
|
399 |
+
'ROUND',
|
400 |
+
'SIGN',
|
401 |
+
'SIN',
|
402 |
+
'SQRT',
|
403 |
+
'TAN',
|
404 |
+
'TRUNCATE',
|
405 |
+
);
|
406 |
+
|
407 |
+
private $charFunctions = array(
|
408 |
+
'ASCII_SYM',
|
409 |
+
'BIN',
|
410 |
+
'BIT_LENGTH',
|
411 |
+
'CHAR_LENGTH',
|
412 |
+
'CHAR',
|
413 |
+
'CONCAT_WS',
|
414 |
+
'CONCAT',
|
415 |
+
'ELT',
|
416 |
+
'EXPORT_SET',
|
417 |
+
'FIELD',
|
418 |
+
'FIND_IN_SET',
|
419 |
+
'FORMAT',
|
420 |
+
'FROM_BASE64',
|
421 |
+
'HEX',
|
422 |
+
'INSERT',
|
423 |
+
'INSTR',
|
424 |
+
'LEFT',
|
425 |
+
'LENGTH',
|
426 |
+
'LOAD_FILE',
|
427 |
+
'LOCATE',
|
428 |
+
'LOWER',
|
429 |
+
'LPAD',
|
430 |
+
'LTRIM',
|
431 |
+
'MAKE_SET',
|
432 |
+
'MID',
|
433 |
+
'OCT',
|
434 |
+
'ORD',
|
435 |
+
'QUOTE',
|
436 |
+
'REPEAT',
|
437 |
+
'REPLACE',
|
438 |
+
'REVERSE',
|
439 |
+
'RIGHT',
|
440 |
+
'RPAD',
|
441 |
+
'RTRIM',
|
442 |
+
'SOUNDEX',
|
443 |
+
'SPACE',
|
444 |
+
'STRCMP',
|
445 |
+
'SUBSTRING_INDEX',
|
446 |
+
'SUBSTRING',
|
447 |
+
'TO_BASE64',
|
448 |
+
'TRIM',
|
449 |
+
'UNHEX',
|
450 |
+
'UPPER',
|
451 |
+
'WEIGHT_STRING',
|
452 |
+
);
|
453 |
+
|
454 |
+
private $timeFunctions = array(
|
455 |
+
'ADDDATE',
|
456 |
+
'ADDTIME',
|
457 |
+
'CONVERT_TZ',
|
458 |
+
'CURDATE',
|
459 |
+
'CURTIME',
|
460 |
+
'DATE_ADD',
|
461 |
+
'DATE_FORMAT',
|
462 |
+
'DATE_SUB',
|
463 |
+
'DATE_SYM',
|
464 |
+
'DATEDIFF',
|
465 |
+
'DAYNAME',
|
466 |
+
'DAYOFMONTH',
|
467 |
+
'DAYOFWEEK',
|
468 |
+
'DAYOFYEAR',
|
469 |
+
'EXTRACT',
|
470 |
+
'FROM_DAYS',
|
471 |
+
'FROM_UNIXTIME',
|
472 |
+
'GET_FORMAT',
|
473 |
+
'HOUR',
|
474 |
+
'LAST_DAY ',
|
475 |
+
'MAKEDATE',
|
476 |
+
'MAKETIME ',
|
477 |
+
'MICROSECOND',
|
478 |
+
'MINUTE',
|
479 |
+
'MONTH',
|
480 |
+
'MONTHNAME',
|
481 |
+
'NOW',
|
482 |
+
'PERIOD_ADD',
|
483 |
+
'PERIOD_DIFF',
|
484 |
+
'QUARTER',
|
485 |
+
'SEC_TO_TIME',
|
486 |
+
'SECOND',
|
487 |
+
'STR_TO_DATE',
|
488 |
+
'SUBTIME',
|
489 |
+
'SYSDATE',
|
490 |
+
'TIME_FORMAT',
|
491 |
+
'TIME_TO_SEC',
|
492 |
+
'TIME_SYM',
|
493 |
+
'TIMEDIFF',
|
494 |
+
'TIMESTAMP',
|
495 |
+
'TIMESTAMPADD',
|
496 |
+
'TIMESTAMPDIFF',
|
497 |
+
'TO_DAYS',
|
498 |
+
'TO_SECONDS',
|
499 |
+
'UNIX_TIMESTAMP',
|
500 |
+
'UTC_DATE',
|
501 |
+
'UTC_TIME',
|
502 |
+
'UTC_TIMESTAMP',
|
503 |
+
'WEEK',
|
504 |
+
'WEEKDAY',
|
505 |
+
'WEEKOFYEAR',
|
506 |
+
'YEAR',
|
507 |
+
'YEARWEEK',
|
508 |
+
);
|
509 |
+
|
510 |
+
private $otherFunctions = array(
|
511 |
+
'MAKE_SET', 'LOAD_FILE',
|
512 |
+
'IF', 'IFNULL',
|
513 |
+
'AES_ENCRYPT', 'AES_DECRYPT',
|
514 |
+
'DECODE', 'ENCODE',
|
515 |
+
'DES_DECRYPT', 'DES_ENCRYPT',
|
516 |
+
'ENCRYPT', 'MD5',
|
517 |
+
'OLD_PASSWORD', 'PASSWORD',
|
518 |
+
'BENCHMARK', 'CHARSET', 'COERCIBILITY', 'COLLATION', 'CONNECTION_ID',
|
519 |
+
'CURRENT_USER', 'DATABASE', 'SCHEMA', 'USER', 'SESSION_USER', 'SYSTEM_USER',
|
520 |
+
'VERSION_SYM',
|
521 |
+
'FOUND_ROWS', 'LAST_INSERT_ID', 'DEFAULT',
|
522 |
+
'GET_LOCK', 'RELEASE_LOCK', 'IS_FREE_LOCK', 'IS_USED_LOCK', 'MASTER_POS_WAIT',
|
523 |
+
'INET_ATON', 'INET_NTOA',
|
524 |
+
'NAME_CONST',
|
525 |
+
'SLEEP',
|
526 |
+
'UUID',
|
527 |
+
'VALUES',
|
528 |
+
);
|
529 |
+
|
530 |
+
private $groupFunctions = array(
|
531 |
+
'AVG', 'COUNT', 'MAX_SYM', 'MIN_SYM', 'SUM',
|
532 |
+
'BIT_AND', 'BIT_OR', 'BIT_XOR',
|
533 |
+
'GROUP_CONCAT',
|
534 |
+
'STD', 'STDDEV', 'STDDEV_POP', 'STDDEV_SAMP',
|
535 |
+
'VAR_POP', 'VAR_SAMP', 'VARIANCE',
|
536 |
+
);
|
537 |
+
|
538 |
+
|
539 |
+
/**
|
540 |
+
* @param wfWAFSQLiLexer $lexer
|
541 |
+
* @param string $subject
|
542 |
+
* @param int $flags
|
543 |
+
*/
|
544 |
+
public function __construct($lexer, $subject = null, $flags = 0) {
|
545 |
+
parent::__construct($lexer);
|
546 |
+
$this->setSubject($subject);
|
547 |
+
$this->setFlags($flags);
|
548 |
+
}
|
549 |
+
|
550 |
+
protected function _init() {
|
551 |
+
$this->portableCommentVersions = array();
|
552 |
+
$this->index = -1;
|
553 |
+
}
|
554 |
+
|
555 |
+
/**
|
556 |
+
* @param int $num
|
557 |
+
* @return bool
|
558 |
+
*/
|
559 |
+
public function hasMoreThanNumTokens($num) {
|
560 |
+
$this->_init();
|
561 |
+
|
562 |
+
$savePoint = $this->index;
|
563 |
+
for ($i = 0; $i <= $num; $i++) {
|
564 |
+
if (!$this->nextToken()) {
|
565 |
+
$this->index = $savePoint;
|
566 |
+
return false;
|
567 |
+
}
|
568 |
+
}
|
569 |
+
$this->index = $savePoint;
|
570 |
+
return true;
|
571 |
+
}
|
572 |
+
|
573 |
+
/**
|
574 |
+
* @return bool
|
575 |
+
*/
|
576 |
+
public function evaluate() {
|
577 |
+
try {
|
578 |
+
$this->parse();
|
579 |
+
return true;
|
580 |
+
} catch (wfWAFParserSyntaxError $e) {
|
581 |
+
return false;
|
582 |
+
}
|
583 |
+
}
|
584 |
+
|
585 |
+
public function parse() {
|
586 |
+
$this->_init();
|
587 |
+
if (
|
588 |
+
$this->parseSelectStatement()
|
589 |
+
|| $this->parseInsertStatement()
|
590 |
+
|| $this->parseUpdateStatement()
|
591 |
+
// || $this->parseDeleteStatement()
|
592 |
+
// || $this->parseReplaceStatement()
|
593 |
+
) {
|
594 |
+
$token = $this->nextToken();
|
595 |
+
if ($token && !$this->isTokenOfType($token, wfWAFSQLiLexer::SEMICOLON)) {
|
596 |
+
$this->triggerSyntaxError($this->currentToken());
|
597 |
+
}
|
598 |
+
} else {
|
599 |
+
$this->triggerSyntaxError($this->expectNextToken());
|
600 |
+
}
|
601 |
+
}
|
602 |
+
|
603 |
+
/**
|
604 |
+
* @param int $index
|
605 |
+
* @return bool
|
606 |
+
*/
|
607 |
+
protected function getToken($index) {
|
608 |
+
if (array_key_exists($index, $this->tokens)) {
|
609 |
+
return $this->tokens[$index];
|
610 |
+
}
|
611 |
+
while ($token = $this->getLexer()->nextToken()) {
|
612 |
+
if (!$this->isCommentToken($token)) {
|
613 |
+
$this->tokens[$index] = $token;
|
614 |
+
return $this->tokens[$index];
|
615 |
+
}
|
616 |
+
}
|
617 |
+
return false;
|
618 |
+
}
|
619 |
+
|
620 |
+
|
621 |
+
/**
|
622 |
+
* @param wfWAFLexerToken $token
|
623 |
+
* @return bool
|
624 |
+
*/
|
625 |
+
public function isCommentToken($token) {
|
626 |
+
if ($this->isTokenOfType($token, wfWAFSQLiLexer::MYSQL_PORTABLE_COMMENT_START)) {
|
627 |
+
$this->portableCommentVersions[(int) preg_replace('/[^\d]/', '', $token->getValue())] = 1;
|
628 |
+
}
|
629 |
+
|
630 |
+
return $this->isTokenOfType($token, array(
|
631 |
+
wfWAFSQLiLexer::SINGLE_LINE_COMMENT,
|
632 |
+
wfWAFSQLiLexer::MULTI_LINE_COMMENT,
|
633 |
+
wfWAFSQLiLexer::MYSQL_PORTABLE_COMMENT_START,
|
634 |
+
wfWAFSQLiLexer::MYSQL_PORTABLE_COMMENT_END,
|
635 |
+
));
|
636 |
+
}
|
637 |
+
|
638 |
+
public function hasMultiplePortableCommentVersions() {
|
639 |
+
return count($this->portableCommentVersions) > 1;
|
640 |
+
}
|
641 |
+
|
642 |
+
/**
|
643 |
+
* Expects the next token to be an identifier with the supplied case-insensitive value
|
644 |
+
*
|
645 |
+
* @param $keyword
|
646 |
+
* @return wfWAFLexerToken
|
647 |
+
* @throws wfWAFParserSyntaxError
|
648 |
+
*/
|
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;
|
656 |
+
}
|
657 |
+
|
658 |
+
private function parseSelectStatement() {
|
659 |
+
$startIndex = $this->index;
|
660 |
+
$hasSelect = false;
|
661 |
+
while ($this->parseSelectExpression()) {
|
662 |
+
$hasSelect = true;
|
663 |
+
$savePoint = $this->index;
|
664 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'union')) {
|
665 |
+
$hasSelect = false;
|
666 |
+
if (!$this->isIdentifierWithValue($this->nextToken(), 'all')) {
|
667 |
+
$this->index--;
|
668 |
+
}
|
669 |
+
continue;
|
670 |
+
}
|
671 |
+
$this->index = $savePoint;
|
672 |
+
break;
|
673 |
+
}
|
674 |
+
if ($hasSelect) {
|
675 |
+
return true;
|
676 |
+
}
|
677 |
+
$this->index = $startIndex;
|
678 |
+
return false;
|
679 |
+
}
|
680 |
+
|
681 |
+
private function parseSelectExpression() {
|
682 |
+
$savePoint = $this->index;
|
683 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
|
684 |
+
$this->parseSelectExpression() &&
|
685 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
|
686 |
+
) {
|
687 |
+
return true;
|
688 |
+
}
|
689 |
+
$this->index = $savePoint;
|
690 |
+
|
691 |
+
if ($this->parseSelect()) {
|
692 |
+
if ($this->parseFrom()) {
|
693 |
+
$this->parseWhere();
|
694 |
+
$this->parseProcedure();
|
695 |
+
$this->parseGroupBy();
|
696 |
+
$this->parseHaving();
|
697 |
+
}
|
698 |
+
$this->parseOrderBy();
|
699 |
+
$this->parseLimit();
|
700 |
+
|
701 |
+
return true;
|
702 |
+
}
|
703 |
+
return false;
|
704 |
+
}
|
705 |
+
|
706 |
+
/**
|
707 |
+
* @throws wfWAFParserSyntaxError
|
708 |
+
*/
|
709 |
+
private function parseSelect() {
|
710 |
+
$startPoint = $this->index;
|
711 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'select')) {
|
712 |
+
$optionalSelectParamsRegex = '/ALL|DISTINCT(?:ROW)?|HIGH_PRIORITY|MAX_STATEMENT_TIME|STRAIGHT_JOIN|SQL_SMALL_RESULT|SQL_BIG_RESULT|SQL_BUFFER_RESULT|SQL_CACHE|SQL_NO_CACHE|SQL_CALC_FOUND_ROWS/i';
|
713 |
+
while (true) {
|
714 |
+
$savePoint = $this->index;
|
715 |
+
$token = $this->nextToken();
|
716 |
+
if ($token) {
|
717 |
+
$value = $token->getLowerCaseValue();
|
718 |
+
if (preg_match($optionalSelectParamsRegex, $value)) {
|
719 |
+
if ($value == 'max_statement_time') {
|
720 |
+
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFSQLiLexer::EQUALS_SYMBOL);
|
721 |
+
$this->expectTokenTypeInArray($this->expectNextToken(), array(
|
722 |
+
wfWAFSQLiLexer::INTEGER_LITERAL,
|
723 |
+
wfWAFSQLiLexer::BINARY_NUMBER_LITERAL,
|
724 |
+
wfWAFSQLiLexer::HEX_NUMBER_LITERAL,
|
725 |
+
wfWAFSQLiLexer::BINARY_NUMBER_LITERAL,
|
726 |
+
));
|
727 |
+
}
|
728 |
+
continue;
|
729 |
+
}
|
730 |
+
}
|
731 |
+
$this->index = $savePoint;
|
732 |
+
break;
|
733 |
+
}
|
734 |
+
return $this->parseSelectList();
|
735 |
+
}
|
736 |
+
$this->index = $startPoint;
|
737 |
+
return false;
|
738 |
+
}
|
739 |
+
|
740 |
+
/**
|
741 |
+
* @throws wfWAFParserSyntaxError
|
742 |
+
*/
|
743 |
+
private function parseSelectList() {
|
744 |
+
$startPoint = $this->index;
|
745 |
+
$hasSelects = false;
|
746 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::ASTERISK)) {
|
747 |
+
$hasSelects = true;
|
748 |
+
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
749 |
+
// Just SELECT * [FROM ...]
|
750 |
+
$this->index--;
|
751 |
+
return true;
|
752 |
+
}
|
753 |
+
} else {
|
754 |
+
$this->index = $startPoint;
|
755 |
+
}
|
756 |
+
while ($this->parseDisplayedColumn()) {
|
757 |
+
$hasSelects = true;
|
758 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
759 |
+
continue;
|
760 |
+
}
|
761 |
+
$this->index--;
|
762 |
+
break;
|
763 |
+
}
|
764 |
+
if ($hasSelects) {
|
765 |
+
return true;
|
766 |
+
}
|
767 |
+
$this->index = $startPoint;
|
768 |
+
return false;
|
769 |
+
}
|
770 |
+
|
771 |
+
private function parseDisplayedColumn() {
|
772 |
+
/*
|
773 |
+
( table_spec DOT ASTERISK )
|
774 |
+
|
|
775 |
+
( column_spec (alias)? )
|
776 |
+
|
|
777 |
+
( bit_expr (alias)? )
|
778 |
+
*/
|
779 |
+
$savePoint = $this->index;
|
780 |
+
if ($this->parseTableSpec() &&
|
781 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::DOT) &&
|
782 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::ASTERISK)
|
783 |
+
) {
|
784 |
+
return true;
|
785 |
+
}
|
786 |
+
$this->index = $savePoint;
|
787 |
+
|
788 |
+
$savePoint = $this->index;
|
789 |
+
if ($this->parseExpression()) {
|
790 |
+
$this->parseAlias();
|
791 |
+
return true;
|
792 |
+
}
|
793 |
+
$this->index = $savePoint;
|
794 |
+
|
795 |
+
$savePoint = $this->index;
|
796 |
+
if ($this->parseColumnSpec()) {
|
797 |
+
$this->parseAlias();
|
798 |
+
return true;
|
799 |
+
}
|
800 |
+
$this->index = $savePoint;
|
801 |
+
|
802 |
+
return false;
|
803 |
+
}
|
804 |
+
|
805 |
+
/**
|
806 |
+
* @return bool
|
807 |
+
*/
|
808 |
+
private function parseExpressionList() {
|
809 |
+
$startIndex = $this->index;
|
810 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)) {
|
811 |
+
$hasExpressions = false;
|
812 |
+
while ($this->parseExpression()) {
|
813 |
+
$hasExpressions = true;
|
814 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
815 |
+
continue;
|
816 |
+
}
|
817 |
+
$this->index--;
|
818 |
+
break;
|
819 |
+
}
|
820 |
+
if ($hasExpressions && $this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
|
821 |
+
return true;
|
822 |
+
}
|
823 |
+
}
|
824 |
+
$this->index = $startIndex;
|
825 |
+
return false;
|
826 |
+
}
|
827 |
+
|
828 |
+
private function parseExpression() {
|
829 |
+
// Combines these:
|
830 |
+
// exp_factor3 ( AND_SYM exp_factor3 )* ;
|
831 |
+
// expression: exp_factor1 ( OR_SYM exp_factor1 )* ;
|
832 |
+
// exp_factor1: exp_factor2 ( XOR exp_factor2 )* ;
|
833 |
+
// exp_factor2: exp_factor3 ( AND_SYM exp_factor3 )* ;
|
834 |
+
|
835 |
+
$savePoint = $this->index;
|
836 |
+
$hasExpression = false;
|
837 |
+
while ($this->parseExpressionFactor3()) {
|
838 |
+
$hasExpression = true;
|
839 |
+
$savePoint2 = $this->index;
|
840 |
+
$token = $this->nextToken();
|
841 |
+
if ($this->isOrToken($token) || $this->isAndToken($token) || $this->isIdentifierWithValue($token, 'xor')) {
|
842 |
+
continue;
|
843 |
+
}
|
844 |
+
$this->index = $savePoint2;
|
845 |
+
break;
|
846 |
+
}
|
847 |
+
if ($hasExpression) {
|
848 |
+
return true;
|
849 |
+
}
|
850 |
+
$this->index = $savePoint;
|
851 |
+
return false;
|
852 |
+
}
|
853 |
+
|
854 |
+
private function parseExpressionFactor3() {
|
855 |
+
// (NOT_SYM)? exp_factor4 ;
|
856 |
+
$savePoint = $this->index;
|
857 |
+
if (!$this->isNotSymbolToken($this->nextToken())) {
|
858 |
+
$this->index--;
|
859 |
+
}
|
860 |
+
if ($this->parseExpressionFactor4()) {
|
861 |
+
return true;
|
862 |
+
}
|
863 |
+
$this->index = $savePoint;
|
864 |
+
return false;
|
865 |
+
}
|
866 |
+
|
867 |
+
private function parseExpressionFactor4() {
|
868 |
+
// bool_primary ( IS_SYM (NOT_SYM)? (boolean_literal|NULL_SYM) )? ;
|
869 |
+
$savePoint = $this->index;
|
870 |
+
if ($this->parseBoolPrimary()) {
|
871 |
+
$savePoint = $this->index;
|
872 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'is')) {
|
873 |
+
if (!$this->isNotSymbolToken($this->nextToken())) {
|
874 |
+
$this->index--;
|
875 |
+
}
|
876 |
+
if ($this->isIdentifierWithValue($this->nextToken(), array(
|
877 |
+
'true', 'false', 'null',
|
878 |
+
))
|
879 |
+
) {
|
880 |
+
return true;
|
881 |
+
}
|
882 |
+
}
|
883 |
+
$this->index = $savePoint;
|
884 |
+
return true;
|
885 |
+
}
|
886 |
+
$this->index = $savePoint;
|
887 |
+
return false;
|
888 |
+
}
|
889 |
+
|
890 |
+
/**
|
891 |
+
* @return bool
|
892 |
+
*/
|
893 |
+
private function parseBoolPrimary() {
|
894 |
+
$startIndex = $this->index;
|
895 |
+
$token = $this->nextToken();
|
896 |
+
if ($token) {
|
897 |
+
$hasNot = false;
|
898 |
+
if ($this->isNotSymbolToken($token)) {
|
899 |
+
$hasNot = true;
|
900 |
+
$token = $this->nextToken();
|
901 |
+
}
|
902 |
+
$val = $token->getLowerCaseValue();
|
903 |
+
if ($token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER) {
|
904 |
+
if ($val === 'exists' && $this->parseSubquery()) {
|
905 |
+
return true;
|
906 |
+
} else if ($hasNot) {
|
907 |
+
$this->index = $startIndex;
|
908 |
+
return false;
|
909 |
+
}
|
910 |
+
}
|
911 |
+
if (!$hasNot) {
|
912 |
+
$this->index = $startIndex;
|
913 |
+
}
|
914 |
+
}
|
915 |
+
|
916 |
+
if ($this->parsePredicate()) {
|
917 |
+
$savePoint = $this->index;
|
918 |
+
$opToken = $this->nextToken();
|
919 |
+
if ($opToken) {
|
920 |
+
switch ($opToken->getType()) {
|
921 |
+
case wfWAFSQLiLexer::EQUALS_SYMBOL:
|
922 |
+
case wfWAFSQLiLexer::LESS_THAN:
|
923 |
+
case wfWAFSQLiLexer::GREATER_THAN:
|
924 |
+
case wfWAFSQLiLexer::LESS_THAN_EQUAL_TO:
|
925 |
+
case wfWAFSQLiLexer::GREATER_THAN_EQUAL_TO:
|
926 |
+
case wfWAFSQLiLexer::NOT_EQUALS:
|
927 |
+
case wfWAFSQLiLexer::SET_VAR:
|
928 |
+
$savePoint2 = $this->index;
|
929 |
+
if ($this->isIdentifierWithValue($this->nextToken(), array(
|
930 |
+
'any', 'all'
|
931 |
+
)) &&
|
932 |
+
$this->parseSubquery()
|
933 |
+
) {
|
934 |
+
return true;
|
935 |
+
}
|
936 |
+
$this->index = $savePoint2;
|
937 |
+
|
938 |
+
$savePoint2 = $this->index;
|
939 |
+
if ($this->testForSubquery() && $this->parseSubquery()) {
|
940 |
+
return true;
|
941 |
+
}
|
942 |
+
$this->index = $savePoint2;
|
943 |
+
|
944 |
+
if ($this->parsePredicate()) {
|
945 |
+
return true;
|
946 |
+
}
|
947 |
+
$this->index = $startIndex;
|
948 |
+
return false;
|
949 |
+
}
|
950 |
+
}
|
951 |
+
$this->index = $savePoint;
|
952 |
+
return true;
|
953 |
+
}
|
954 |
+
$this->index = $startIndex;
|
955 |
+
return false;
|
956 |
+
}
|
957 |
+
|
958 |
+
private function parsePredicate() {
|
959 |
+
$startIndex = $this->index;
|
960 |
+
if ($this->parseBitExpression()) {
|
961 |
+
$savePoint = $this->index;
|
962 |
+
$token = $this->nextToken();
|
963 |
+
if ($token) {
|
964 |
+
if ($hasNot = $this->isNotSymbolToken($token)) {
|
965 |
+
$token = $this->nextToken();
|
966 |
+
if (!$token) {
|
967 |
+
$this->index = $startIndex;
|
968 |
+
return false;
|
969 |
+
}
|
970 |
+
}
|
971 |
+
$val = $token->getLowerCaseValue();
|
972 |
+
|
973 |
+
if ($token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER) {
|
974 |
+
switch ($val) {
|
975 |
+
case 'in':
|
976 |
+
if ($this->parseSubquery() || $this->parseExpressionList()) {
|
977 |
+
return true;
|
978 |
+
}
|
979 |
+
break;
|
980 |
+
|
981 |
+
case 'between':
|
982 |
+
if ($this->parseBitExpression() && $this->isIdentifierWithValue($this->nextToken(), 'and') &&
|
983 |
+
$this->parsePredicate()
|
984 |
+
) {
|
985 |
+
return true;
|
986 |
+
}
|
987 |
+
break;
|
988 |
+
|
989 |
+
case 'sounds':
|
990 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'like') &&
|
991 |
+
$this->parseBitExpression()
|
992 |
+
) {
|
993 |
+
return true;
|
994 |
+
}
|
995 |
+
break;
|
996 |
+
|
997 |
+
case 'like':
|
998 |
+
case 'rlike':
|
999 |
+
if ($this->parseSimpleExpression()) {
|
1000 |
+
// We've got a LIKE statement at this point
|
1001 |
+
$savePoint = $this->index;
|
1002 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'escape') &&
|
1003 |
+
$this->parseSimpleExpression()
|
1004 |
+
) {
|
1005 |
+
return true;
|
1006 |
+
}
|
1007 |
+
$this->index = $savePoint;
|
1008 |
+
return true;
|
1009 |
+
}
|
1010 |
+
break;
|
1011 |
+
|
1012 |
+
case 'regexp':
|
1013 |
+
if ($this->parseBitExpression()) {
|
1014 |
+
return true;
|
1015 |
+
}
|
1016 |
+
break;
|
1017 |
+
|
1018 |
+
default:
|
1019 |
+
if ($hasNot) {
|
1020 |
+
$this->index = $startIndex;
|
1021 |
+
return false;
|
1022 |
+
}
|
1023 |
+
break;
|
1024 |
+
}
|
1025 |
+
}
|
1026 |
+
}
|
1027 |
+
$this->index = $savePoint;
|
1028 |
+
return true;
|
1029 |
+
}
|
1030 |
+
$this->index = $startIndex;
|
1031 |
+
return false;
|
1032 |
+
}
|
1033 |
+
|
1034 |
+
/**
|
1035 |
+
* @return bool
|
1036 |
+
*/
|
1037 |
+
private function parseBitExpression() {
|
1038 |
+
// factor1 ( VERTBAR factor1 )? ;
|
1039 |
+
$savePoint = $this->index;
|
1040 |
+
if ($this->parseBitExprFactor5()) {
|
1041 |
+
$savePoint = $this->index;
|
1042 |
+
$token = $this->nextToken();
|
1043 |
+
if (($this->isTokenOfType($token, array(
|
1044 |
+
wfWAFSQLiLexer::BIT_OR,
|
1045 |
+
wfWAFSQLiLexer::BIT_AND,
|
1046 |
+
wfWAFSQLiLexer::BIT_XOR,
|
1047 |
+
wfWAFSQLiLexer::BIT_LEFT_SHIFT,
|
1048 |
+
wfWAFSQLiLexer::BIT_RIGHT_SHIFT,
|
1049 |
+
wfWAFSQLiLexer::BIT_INVERSION,
|
1050 |
+
wfWAFSQLiLexer::PLUS,
|
1051 |
+
wfWAFSQLiLexer::MINUS,
|
1052 |
+
wfWAFSQLiLexer::ASTERISK,
|
1053 |
+
wfWAFSQLiLexer::DIVISION,
|
1054 |
+
wfWAFSQLiLexer::MOD,
|
1055 |
+
))
|
1056 |
+
||
|
1057 |
+
$this->isIdentifierWithValue($token, array(
|
1058 |
+
'div', 'mod'
|
1059 |
+
))) &&
|
1060 |
+
$this->parseBitExpression()
|
1061 |
+
) {
|
1062 |
+
return true;
|
1063 |
+
}
|
1064 |
+
$this->index = $savePoint;
|
1065 |
+
return true;
|
1066 |
+
}
|
1067 |
+
$this->index = $savePoint;
|
1068 |
+
return false;
|
1069 |
+
}
|
1070 |
+
|
1071 |
+
|
1072 |
+
private function parseBitExprFactor5() {
|
1073 |
+
// factor6 ( (PLUS|MINUS) interval_expr )? ;
|
1074 |
+
$savePoint = $this->index;
|
1075 |
+
if ($this->parseBitExprFactor6()) {
|
1076 |
+
$savePoint = $this->index;
|
1077 |
+
if ($this->isTokenOfType($this->nextToken(), array(
|
1078 |
+
wfWAFSQLiLexer::PLUS,
|
1079 |
+
wfWAFSQLiLexer::MINUS,
|
1080 |
+
)) && $this->parseIntervalExpression()
|
1081 |
+
) {
|
1082 |
+
return true;
|
1083 |
+
}
|
1084 |
+
$this->index = $savePoint;
|
1085 |
+
return true;
|
1086 |
+
}
|
1087 |
+
$this->index = $savePoint;
|
1088 |
+
return false;
|
1089 |
+
}
|
1090 |
+
|
1091 |
+
private function parseBitExprFactor6() {
|
1092 |
+
// (PLUS | MINUS | NEGATION | BINARY) simple_expr
|
1093 |
+
// | simple_expr ;
|
1094 |
+
|
1095 |
+
$startPoint = $this->index;
|
1096 |
+
$savePoint = $this->index;
|
1097 |
+
while (
|
1098 |
+
($token = $this->nextToken()) &&
|
1099 |
+
(
|
1100 |
+
$this->isTokenOfType($token, array(
|
1101 |
+
wfWAFSQLiLexer::PLUS,
|
1102 |
+
wfWAFSQLiLexer::MINUS,
|
1103 |
+
)) ||
|
1104 |
+
($this->isTokenOfType($token, wfWAFSQLiLexer::BIT_INVERSION)) ||
|
1105 |
+
($this->isIdentifierWithValue($token, 'BINARY'))
|
1106 |
+
)
|
1107 |
+
) {
|
1108 |
+
$savePoint = $this->index;
|
1109 |
+
}
|
1110 |
+
$this->index = $savePoint;
|
1111 |
+
|
1112 |
+
if ($this->parseSimpleExpression()) {
|
1113 |
+
return true;
|
1114 |
+
}
|
1115 |
+
$this->index = $startPoint;
|
1116 |
+
return false;
|
1117 |
+
|
1118 |
+
}
|
1119 |
+
|
1120 |
+
/**
|
1121 |
+
* literal_value
|
1122 |
+
* | column_spec
|
1123 |
+
* | function_call
|
1124 |
+
* | USER_VAR
|
1125 |
+
* | expression_list
|
1126 |
+
* | (ROW_SYM expression_list)
|
1127 |
+
* | subquery
|
1128 |
+
* | EXISTS subquery
|
1129 |
+
* | match_against_statement
|
1130 |
+
* | case_when_statement
|
1131 |
+
* | interval_expr
|
1132 |
+
*
|
1133 |
+
* @return bool
|
1134 |
+
*/
|
1135 |
+
private function parseSimpleExpression() {
|
1136 |
+
$startPoint = $this->index;
|
1137 |
+
$simple = ($parseLiteral = $this->parseLiteral()) ||
|
1138 |
+
($parseMatchAgainst = $this->parseMatchAgainst()) ||
|
1139 |
+
($parseFunctionCall = $this->parseFunctionCall()) ||
|
1140 |
+
($parseVariable = $this->parseVariable()) ||
|
1141 |
+
($parseExpressionList = $this->parseExpressionList()) ||
|
1142 |
+
($parseSubquery = $this->parseSubquery()) ||
|
1143 |
+
($parseExistsSubquery = $this->parseExistsSubquery()) ||
|
1144 |
+
($parseCaseWhen = $this->parseCaseWhen()) ||
|
1145 |
+
($parseIntervalExpression = $this->parseIntervalExpression()) ||
|
1146 |
+
($parseColumnSpec = $this->parseColumnSpec());
|
1147 |
+
|
1148 |
+
if ($simple) {
|
1149 |
+
$token = $this->nextToken();
|
1150 |
+
if ($token && $token->getLowerCaseValue() == 'collate') {
|
1151 |
+
$savePoint = $this->index;
|
1152 |
+
if ($this->parseCollationName()) {
|
1153 |
+
return true;
|
1154 |
+
}
|
1155 |
+
$this->index = $savePoint;
|
1156 |
+
} else {
|
1157 |
+
$this->index--;
|
1158 |
+
}
|
1159 |
+
return true;
|
1160 |
+
}
|
1161 |
+
$this->index = $startPoint;
|
1162 |
+
return false;
|
1163 |
+
}
|
1164 |
+
|
1165 |
+
/**
|
1166 |
+
* @return bool
|
1167 |
+
*/
|
1168 |
+
private function parseLiteral() {
|
1169 |
+
$startIndex = $this->index;
|
1170 |
+
$savePoint = $this->index;
|
1171 |
+
while ($this->isTokenOfType($this->nextToken(), array(
|
1172 |
+
wfWAFSQLiLexer::PLUS,
|
1173 |
+
wfWAFSQLiLexer::MINUS,
|
1174 |
+
))) {
|
1175 |
+
$savePoint = $this->index;
|
1176 |
+
}
|
1177 |
+
$this->index = $savePoint;
|
1178 |
+
|
1179 |
+
$nextToken = $this->nextToken();
|
1180 |
+
if ($nextToken) {
|
1181 |
+
switch ($nextToken->getType()) {
|
1182 |
+
case wfWAFSQLiLexer::INTEGER_LITERAL:
|
1183 |
+
case wfWAFSQLiLexer::BINARY_NUMBER_LITERAL:
|
1184 |
+
case wfWAFSQLiLexer::HEX_NUMBER_LITERAL:
|
1185 |
+
case wfWAFSQLiLexer::REAL_NUMBER_LITERAL:
|
1186 |
+
return true;
|
1187 |
+
// Allow concatenation: 'test' 'test' is valid
|
1188 |
+
case wfWAFSQLiLexer::DOUBLE_STRING_LITERAL:
|
1189 |
+
case wfWAFSQLiLexer::SINGLE_STRING_LITERAL:
|
1190 |
+
$savePoint = $this->index;
|
1191 |
+
while ($this->isTokenOfType($this->nextToken(), array(
|
1192 |
+
wfWAFSQLiLexer::DOUBLE_STRING_LITERAL,
|
1193 |
+
wfWAFSQLiLexer::SINGLE_STRING_LITERAL
|
1194 |
+
))) {
|
1195 |
+
$savePoint = $this->index;
|
1196 |
+
}
|
1197 |
+
$this->index = $savePoint;
|
1198 |
+
return true;
|
1199 |
+
|
1200 |
+
case wfWAFSQLiLexer::UNQUOTED_IDENTIFIER:
|
1201 |
+
if ($nextToken->getLowerCaseValue() === 'null') {
|
1202 |
+
return true;
|
1203 |
+
}
|
1204 |
+
break;
|
1205 |
+
}
|
1206 |
+
}
|
1207 |
+
$this->index = $startIndex;
|
1208 |
+
return false;
|
1209 |
+
}
|
1210 |
+
|
1211 |
+
/**
|
1212 |
+
* @return bool
|
1213 |
+
*/
|
1214 |
+
private function parseColumnSpec() {
|
1215 |
+
$savePoint = $this->index;
|
1216 |
+
if ($this->parseTableSpec()) {
|
1217 |
+
$savePoint = $this->index;
|
1218 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::DOT)) {
|
1219 |
+
$nextToken = $this->nextToken();
|
1220 |
+
if ($nextToken && ($nextToken->getType() == wfWAFSQLiLexer::UNQUOTED_IDENTIFIER ||
|
1221 |
+
$nextToken->getType() == wfWAFSQLiLexer::QUOTED_IDENTIFIER)
|
1222 |
+
) {
|
1223 |
+
return true;
|
1224 |
+
}
|
1225 |
+
$this->index = $savePoint;
|
1226 |
+
return false;
|
1227 |
+
}
|
1228 |
+
|
1229 |
+
$this->index = $savePoint;
|
1230 |
+
return true;
|
1231 |
+
}
|
1232 |
+
$this->index = $savePoint;
|
1233 |
+
return false;
|
1234 |
+
}
|
1235 |
+
|
1236 |
+
/**
|
1237 |
+
* CAST_SYM LPAREN expression AS_SYM cast_data_type RPAREN )
|
1238 |
+
* | ( CONVERT_SYM LPAREN expression COMMA cast_data_type RPAREN )
|
1239 |
+
* | ( CONVERT_SYM LPAREN expression USING_SYM transcoding_name RPAREN )
|
1240 |
+
* | ( group_functions LPAREN ( ASTERISK | ALL | DISTINCT )? bit_expr RPAREN )
|
1241 |
+
*
|
1242 |
+
* @return bool
|
1243 |
+
*/
|
1244 |
+
private function parseFunctionCall() {
|
1245 |
+
$startPoint = $this->index;
|
1246 |
+
$functionToken = $this->nextToken();
|
1247 |
+
if ($functionToken && $functionToken->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER) {
|
1248 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)) {
|
1249 |
+
switch ($functionToken->getLowerCaseValue()) {
|
1250 |
+
case 'cast':
|
1251 |
+
if ($this->parseExpression() &&
|
1252 |
+
$this->isIdentifierWithValue($this->nextToken(), 'as') &&
|
1253 |
+
$this->parseCastDataType() &&
|
1254 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
|
1255 |
+
) {
|
1256 |
+
return true;
|
1257 |
+
}
|
1258 |
+
break;
|
1259 |
+
|
1260 |
+
case 'convert':
|
1261 |
+
if ($this->parseExpression()) {
|
1262 |
+
$savePoint = $this->index;
|
1263 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA) &&
|
1264 |
+
$this->parseCastDataType() &&
|
1265 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
|
1266 |
+
) {
|
1267 |
+
return true;
|
1268 |
+
}
|
1269 |
+
$this->index = $savePoint;
|
1270 |
+
$savePoint = $this->index;
|
1271 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'using') &&
|
1272 |
+
$this->parseTranscodingName() &&
|
1273 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
|
1274 |
+
) {
|
1275 |
+
return true;
|
1276 |
+
}
|
1277 |
+
$this->index = $savePoint;
|
1278 |
+
}
|
1279 |
+
break;
|
1280 |
+
|
1281 |
+
default:
|
1282 |
+
$savePoint = $this->index;
|
1283 |
+
if (in_array($functionToken->getUpperCaseValue(), $this->groupFunctions)) {
|
1284 |
+
$token = $this->nextToken();
|
1285 |
+
if (!$this->isIdentifierWithValue($token, array(
|
1286 |
+
'all', 'distinct',
|
1287 |
+
)) && !$this->isTokenOfType($token, wfWAFSQLiLexer::ASTERISK)
|
1288 |
+
) {
|
1289 |
+
$this->index--;
|
1290 |
+
}
|
1291 |
+
$this->parseBitExpression();
|
1292 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
|
1293 |
+
return true;
|
1294 |
+
}
|
1295 |
+
}
|
1296 |
+
$this->index = $savePoint;
|
1297 |
+
|
1298 |
+
while ($this->parseExpression()) {
|
1299 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
1300 |
+
continue;
|
1301 |
+
}
|
1302 |
+
$this->index--;
|
1303 |
+
break;
|
1304 |
+
}
|
1305 |
+
|
1306 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
|
1307 |
+
return true;
|
1308 |
+
}
|
1309 |
+
break;
|
1310 |
+
}
|
1311 |
+
}
|
1312 |
+
}
|
1313 |
+
$this->index = $startPoint;
|
1314 |
+
return false;
|
1315 |
+
}
|
1316 |
+
|
1317 |
+
/**
|
1318 |
+
* BINARY (INTEGER_NUM)?
|
1319 |
+
* | CHAR (INTEGER_NUM)?
|
1320 |
+
* | DATE_SYM
|
1321 |
+
* | DATETIME
|
1322 |
+
* | DECIMAL_SYM ( INTEGER_NUM (COMMA INTEGER_NUM)? )?
|
1323 |
+
* | SIGNED_SYM (INTEGER_SYM)?
|
1324 |
+
* | TIME_SYM
|
1325 |
+
* | UNSIGNED_SYM (INTEGER_SYM)?
|
1326 |
+
*
|
1327 |
+
* @return bool
|
1328 |
+
*/
|
1329 |
+
private function parseCastDataType() {
|
1330 |
+
$startPoint = $this->index;
|
1331 |
+
$token = $this->nextToken();
|
1332 |
+
if ($this->isKeywordToken($token)) {
|
1333 |
+
switch ($token->getLowerCaseValue()) {
|
1334 |
+
case 'binary':
|
1335 |
+
case 'char':
|
1336 |
+
$savePoint = $this->index;
|
1337 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::INTEGER_LITERAL)) {
|
1338 |
+
return true;
|
1339 |
+
}
|
1340 |
+
$this->index = $savePoint;
|
1341 |
+
return true;
|
1342 |
+
|
1343 |
+
case 'date':
|
1344 |
+
case 'datetime':
|
1345 |
+
case 'time':
|
1346 |
+
return true;
|
1347 |
+
|
1348 |
+
case 'signed':
|
1349 |
+
case 'unsigned':
|
1350 |
+
if (!$this->isIdentifierWithValue($this->nextToken(), 'integer')) {
|
1351 |
+
$this->index--;
|
1352 |
+
}
|
1353 |
+
return true;
|
1354 |
+
|
1355 |
+
case 'decimal':
|
1356 |
+
$savePoint = $this->index;
|
1357 |
+
while ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::INTEGER_LITERAL)) {
|
1358 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
1359 |
+
continue;
|
1360 |
+
}
|
1361 |
+
$this->index--;
|
1362 |
+
return true;
|
1363 |
+
}
|
1364 |
+
$this->index = $savePoint;
|
1365 |
+
return true;
|
1366 |
+
}
|
1367 |
+
}
|
1368 |
+
$this->index = $startPoint;
|
1369 |
+
return false;
|
1370 |
+
}
|
1371 |
+
|
1372 |
+
private function parseTranscodingName() {
|
1373 |
+
$savePoint = $this->index;
|
1374 |
+
$token = $this->nextToken();
|
1375 |
+
if ($token && $token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER) {
|
1376 |
+
return false;
|
1377 |
+
}
|
1378 |
+
$this->index = $savePoint;
|
1379 |
+
return false;
|
1380 |
+
}
|
1381 |
+
|
1382 |
+
private function parseVariable() {
|
1383 |
+
$nextToken = $this->nextToken();
|
1384 |
+
if ($nextToken && $nextToken->getType() === wfWAFSQLiLexer::VARIABLE) {
|
1385 |
+
return true;
|
1386 |
+
}
|
1387 |
+
$this->index--;
|
1388 |
+
return false;
|
1389 |
+
}
|
1390 |
+
|
1391 |
+
/**
|
1392 |
+
* @return bool
|
1393 |
+
*/
|
1394 |
+
private function parseSubquery() {
|
1395 |
+
$startIndex = $this->index;
|
1396 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
|
1397 |
+
$this->parseSelectStatement() &&
|
1398 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
|
1399 |
+
) {
|
1400 |
+
return true;
|
1401 |
+
}
|
1402 |
+
$this->index = $startIndex;
|
1403 |
+
return false;
|
1404 |
+
}
|
1405 |
+
|
1406 |
+
private function testForSubquery() {
|
1407 |
+
$startIndex = $this->index;
|
1408 |
+
$nextToken = $this->nextToken();
|
1409 |
+
if ($nextToken && $nextToken->getType() === wfWAFSQLiLexer::OPEN_PARENTHESIS) {
|
1410 |
+
$selectToken = $this->nextToken();
|
1411 |
+
if ($this->isIdentifierWithValue($selectToken, 'select')) {
|
1412 |
+
$this->index = $startIndex;
|
1413 |
+
return true;
|
1414 |
+
}
|
1415 |
+
}
|
1416 |
+
$this->index = $startIndex;
|
1417 |
+
return false;
|
1418 |
+
}
|
1419 |
+
|
1420 |
+
/**
|
1421 |
+
*
|
1422 |
+
*
|
1423 |
+
* @return bool
|
1424 |
+
*/
|
1425 |
+
private function parseExistsSubquery() {
|
1426 |
+
$startIndex = $this->index;
|
1427 |
+
$existsToken = $this->nextToken();
|
1428 |
+
if ($this->isIdentifierWithValue($existsToken, 'exists')) {
|
1429 |
+
if ($this->parseSubquery()) {
|
1430 |
+
return true;
|
1431 |
+
}
|
1432 |
+
}
|
1433 |
+
$this->index = $startIndex;
|
1434 |
+
return false;
|
1435 |
+
}
|
1436 |
+
|
1437 |
+
/**
|
1438 |
+
* MATCH (column_spec (COMMA column_spec)* ) AGAINST (expression (search_modifier)? )
|
1439 |
+
*
|
1440 |
+
* @return bool
|
1441 |
+
*/
|
1442 |
+
private function parseMatchAgainst() {
|
1443 |
+
$startIndex = $this->index;
|
1444 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'match')) {
|
1445 |
+
$savePoint = $this->index;
|
1446 |
+
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)) {
|
1447 |
+
$this->index = $savePoint;
|
1448 |
+
}
|
1449 |
+
$hasColumns = false;
|
1450 |
+
while ($this->parseColumnSpec()) {
|
1451 |
+
$hasColumns = true;
|
1452 |
+
$savePoint = $this->index;
|
1453 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
1454 |
+
continue;
|
1455 |
+
}
|
1456 |
+
$this->index = $savePoint;
|
1457 |
+
break;
|
1458 |
+
}
|
1459 |
+
$savePoint = $this->index;
|
1460 |
+
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
|
1461 |
+
$this->index = $savePoint;
|
1462 |
+
}
|
1463 |
+
if ($hasColumns && $this->isIdentifierWithValue($this->nextToken(), 'against') &&
|
1464 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
|
1465 |
+
$this->parseExpression() &&
|
1466 |
+
($this->parseSearchModifier() || true) &&
|
1467 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
|
1468 |
+
) {
|
1469 |
+
return true;
|
1470 |
+
}
|
1471 |
+
}
|
1472 |
+
$this->index = $startIndex;
|
1473 |
+
return false;
|
1474 |
+
}
|
1475 |
+
|
1476 |
+
/**
|
1477 |
+
* Used in match/against
|
1478 |
+
*
|
1479 |
+
* @link https://dev.mysql.com/doc/refman/5.6/en/fulltext-search.html
|
1480 |
+
* @return bool
|
1481 |
+
*/
|
1482 |
+
private function parseSearchModifier() {
|
1483 |
+
$startIndex = $this->index;
|
1484 |
+
|
1485 |
+
$startToken = $this->nextToken();
|
1486 |
+
if ($this->isIdentifierWithValue($startToken, 'in')) {
|
1487 |
+
$next = $this->nextToken();
|
1488 |
+
if ($this->isIdentifierWithValue($next, 'natural') &&
|
1489 |
+
$this->isIdentifierWithValue($this->nextToken(), 'language') &&
|
1490 |
+
$this->isIdentifierWithValue($this->nextToken(), 'mode')
|
1491 |
+
) {
|
1492 |
+
$saveIndex = $this->index;
|
1493 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'with') &&
|
1494 |
+
$this->isIdentifierWithValue($this->nextToken(), 'query') &&
|
1495 |
+
$this->isIdentifierWithValue($this->nextToken(), 'expansion')
|
1496 |
+
) {
|
1497 |
+
return true;
|
1498 |
+
}
|
1499 |
+
$this->index = $saveIndex;
|
1500 |
+
return true;
|
1501 |
+
|
1502 |
+
} else if ($this->isIdentifierWithValue($next, 'boolean') &&
|
1503 |
+
$this->isIdentifierWithValue($this->nextToken(), 'mode')
|
1504 |
+
) {
|
1505 |
+
return true;
|
1506 |
+
}
|
1507 |
+
} else if ($this->isIdentifierWithValue($startToken, 'with')) {
|
1508 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'query') &&
|
1509 |
+
$this->isIdentifierWithValue($this->nextToken(), 'expansion')
|
1510 |
+
) {
|
1511 |
+
return true;
|
1512 |
+
}
|
1513 |
+
}
|
1514 |
+
|
1515 |
+
$this->index = $startIndex;
|
1516 |
+
return false;
|
1517 |
+
}
|
1518 |
+
|
1519 |
+
/**
|
1520 |
+
* @return bool
|
1521 |
+
*/
|
1522 |
+
private function parseCaseWhen() {
|
1523 |
+
$startIndex = $this->index;
|
1524 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'case')) {
|
1525 |
+
$hasWhen = false;
|
1526 |
+
while (true) {
|
1527 |
+
if (!$this->isIdentifierWithValue($this->nextToken(), 'when')) {
|
1528 |
+
$this->index--;
|
1529 |
+
break;
|
1530 |
+
}
|
1531 |
+
if ($this->parseExpression()) {
|
1532 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'then') && $this->parseBitExpression()) {
|
1533 |
+
$hasWhen = true;
|
1534 |
+
continue;
|
1535 |
+
}
|
1536 |
+
$this->index--;
|
1537 |
+
}
|
1538 |
+
$this->index--;
|
1539 |
+
break;
|
1540 |
+
}
|
1541 |
+
if ($hasWhen) {
|
1542 |
+
$endToken = $this->nextToken();
|
1543 |
+
if ($this->isIdentifierWithValue($endToken, 'else')) {
|
1544 |
+
if (!$this->parseBitExpression()) {
|
1545 |
+
$this->index = $startIndex;
|
1546 |
+
return false;
|
1547 |
+
}
|
1548 |
+
$endToken = $this->nextToken();
|
1549 |
+
}
|
1550 |
+
if ($this->isIdentifierWithValue($endToken, 'end')) {
|
1551 |
+
return true;
|
1552 |
+
}
|
1553 |
+
}
|
1554 |
+
}
|
1555 |
+
$this->index = $startIndex;
|
1556 |
+
return false;
|
1557 |
+
}
|
1558 |
+
|
1559 |
+
/**
|
1560 |
+
* @return bool
|
1561 |
+
*/
|
1562 |
+
private function parseIntervalExpression() {
|
1563 |
+
$startIndex = $this->index;
|
1564 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'interval') && $this->parseExpression()) {
|
1565 |
+
$intervalUnitToken = $this->nextToken();
|
1566 |
+
if ($intervalUnitToken && in_array($intervalUnitToken->getType(), $this->intervalUnits)) {
|
1567 |
+
return true;
|
1568 |
+
}
|
1569 |
+
}
|
1570 |
+
$this->index = $startIndex;
|
1571 |
+
return false;
|
1572 |
+
}
|
1573 |
+
|
1574 |
+
/**
|
1575 |
+
* @return bool
|
1576 |
+
*/
|
1577 |
+
public function parseCollationName() {
|
1578 |
+
$startIndex = $this->index;
|
1579 |
+
$token = $this->nextToken();
|
1580 |
+
if ($token && $token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER) {
|
1581 |
+
return true;
|
1582 |
+
}
|
1583 |
+
$this->index = $startIndex;
|
1584 |
+
return false;
|
1585 |
+
}
|
1586 |
+
|
1587 |
+
/**
|
1588 |
+
* @return bool
|
1589 |
+
*/
|
1590 |
+
private function parseFrom() {
|
1591 |
+
$startIndex = $this->index;
|
1592 |
+
$token = $this->nextToken();
|
1593 |
+
if ($this->isIdentifierWithValue($token, 'from')) {
|
1594 |
+
return $this->parseTableReferences();
|
1595 |
+
}
|
1596 |
+
$this->index = $startIndex;
|
1597 |
+
return false;
|
1598 |
+
}
|
1599 |
+
|
1600 |
+
/**
|
1601 |
+
* @link http://dev.mysql.com/doc/refman/5.6/en/join.html
|
1602 |
+
* @return bool
|
1603 |
+
*/
|
1604 |
+
private function parseTableReferences() {
|
1605 |
+
$startPoint = $this->index;
|
1606 |
+
$hasReferences = false;
|
1607 |
+
while ($this->parseEscapedTableReference()) {
|
1608 |
+
$hasReferences = true;
|
1609 |
+
$savePoint = $this->index;
|
1610 |
+
$token = $this->nextToken();
|
1611 |
+
if ($this->isTokenOfType($token, wfWAFSQLiLexer::COMMA)) {
|
1612 |
+
continue;
|
1613 |
+
}
|
1614 |
+
$this->index = $savePoint;
|
1615 |
+
break;
|
1616 |
+
}
|
1617 |
+
if ($hasReferences) {
|
1618 |
+
return true;
|
1619 |
+
}
|
1620 |
+
$this->index = $startPoint;
|
1621 |
+
return false;
|
1622 |
+
}
|
1623 |
+
|
1624 |
+
/**
|
1625 |
+
* @return bool
|
1626 |
+
*/
|
1627 |
+
private function parseEscapedTableReference() {
|
1628 |
+
$startPoint = $this->index;
|
1629 |
+
if ($this->parseTableReference() ||
|
1630 |
+
(
|
1631 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_BRACKET) &&
|
1632 |
+
$this->isIdentifierWithValue($this->nextToken(), 'oj') &&
|
1633 |
+
$this->parseTableReference() &&
|
1634 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_BRACKET)
|
1635 |
+
)
|
1636 |
+
) {
|
1637 |
+
return true;
|
1638 |
+
}
|
1639 |
+
|
1640 |
+
$this->index = $startPoint;
|
1641 |
+
return false;
|
1642 |
+
}
|
1643 |
+
|
1644 |
+
/**
|
1645 |
+
* @return bool
|
1646 |
+
*/
|
1647 |
+
private function parseTableReference() {
|
1648 |
+
$savePoint = $this->index;
|
1649 |
+
$hasTables = false;
|
1650 |
+
if ($this->parseTableFactor()) {
|
1651 |
+
$hasTables = true;
|
1652 |
+
while ($this->parseJoinTable()) {
|
1653 |
+
|
1654 |
+
}
|
1655 |
+
}
|
1656 |
+
if ($hasTables) {
|
1657 |
+
return true;
|
1658 |
+
}
|
1659 |
+
$this->index = $savePoint;
|
1660 |
+
return false;
|
1661 |
+
}
|
1662 |
+
|
1663 |
+
/**
|
1664 |
+
* table_factor:
|
1665 |
+
* tbl_name [PARTITION (partition_names)] [[AS] alias] [index_hint_list]
|
1666 |
+
* | table_subquery [AS] alias
|
1667 |
+
* | ( table_references )
|
1668 |
+
*/
|
1669 |
+
private function parseTableFactor() {
|
1670 |
+
$savePoint = $this->index;
|
1671 |
+
if ($this->parseTableSpec()) {
|
1672 |
+
$savePoint2 = $this->index;
|
1673 |
+
if (!$this->parsePartitionClause()) {
|
1674 |
+
$this->index = $savePoint2;
|
1675 |
+
}
|
1676 |
+
|
1677 |
+
$this->parseAlias();
|
1678 |
+
$this->parseIndexHintList();
|
1679 |
+
|
1680 |
+
return true;
|
1681 |
+
}
|
1682 |
+
$this->index = $savePoint;
|
1683 |
+
|
1684 |
+
$savePoint = $this->index;
|
1685 |
+
if ($this->parseSubquery() && $this->parseAlias()) {
|
1686 |
+
return true;
|
1687 |
+
}
|
1688 |
+
$this->index = $savePoint;
|
1689 |
+
|
1690 |
+
$savePoint = $this->index;
|
1691 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
|
1692 |
+
$this->parseTableReferences() &&
|
1693 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
|
1694 |
+
) {
|
1695 |
+
return true;
|
1696 |
+
}
|
1697 |
+
$this->index = $savePoint;
|
1698 |
+
return false;
|
1699 |
+
}
|
1700 |
+
|
1701 |
+
/**
|
1702 |
+
* PARTITION (partition_names)
|
1703 |
+
*
|
1704 |
+
* @return bool
|
1705 |
+
*/
|
1706 |
+
private function parsePartitionClause() {
|
1707 |
+
$startIndex = $this->index;
|
1708 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'partition') &&
|
1709 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
|
1710 |
+
$this->parsePartitionNames() &&
|
1711 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
|
1712 |
+
) {
|
1713 |
+
return true;
|
1714 |
+
}
|
1715 |
+
$this->index = $startIndex;
|
1716 |
+
return false;
|
1717 |
+
}
|
1718 |
+
|
1719 |
+
/**
|
1720 |
+
* @return bool
|
1721 |
+
*/
|
1722 |
+
private function parsePartitionNames() {
|
1723 |
+
$startPoint = $this->index;
|
1724 |
+
$hasPartition = false;
|
1725 |
+
while ($this->parsePartitionName()) {
|
1726 |
+
$hasPartition = true;
|
1727 |
+
$savePoint = $this->index;
|
1728 |
+
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
1729 |
+
$this->index = $savePoint;
|
1730 |
+
break;
|
1731 |
+
}
|
1732 |
+
}
|
1733 |
+
if ($hasPartition) {
|
1734 |
+
return true;
|
1735 |
+
}
|
1736 |
+
$this->index = $startPoint;
|
1737 |
+
return false;
|
1738 |
+
}
|
1739 |
+
|
1740 |
+
/**
|
1741 |
+
* @return bool
|
1742 |
+
*/
|
1743 |
+
private function parsePartitionName() {
|
1744 |
+
$startPoint = $this->index;
|
1745 |
+
$token = $this->nextToken();
|
1746 |
+
if ($this->isTokenOfType($token, wfWAFSQLiLexer::QUOTED_IDENTIFIER) ||
|
1747 |
+
$this->isValidNonKeywordIdentifier($token)
|
1748 |
+
) {
|
1749 |
+
return true;
|
1750 |
+
}
|
1751 |
+
$this->index = $startPoint;
|
1752 |
+
return false;
|
1753 |
+
}
|
1754 |
+
|
1755 |
+
/**
|
1756 |
+
* join_table:
|
1757 |
+
* table_reference [INNER | CROSS] JOIN table_factor [join_condition]
|
1758 |
+
* | table_reference STRAIGHT_JOIN table_factor
|
1759 |
+
* | table_reference STRAIGHT_JOIN table_factor ON conditional_expr
|
1760 |
+
* | table_reference {LEFT|RIGHT} [OUTER] JOIN table_reference join_condition
|
1761 |
+
* | table_reference NATURAL [{LEFT|RIGHT} [OUTER]] JOIN table_factor
|
1762 |
+
*
|
1763 |
+
* @return bool
|
1764 |
+
*/
|
1765 |
+
private function parseJoinTable() {
|
1766 |
+
$savePoint = $this->index;
|
1767 |
+
if (!$this->isIdentifierWithValue($this->nextToken(), array(
|
1768 |
+
'inner', 'cross',
|
1769 |
+
))
|
1770 |
+
) {
|
1771 |
+
$this->index = $savePoint;
|
1772 |
+
}
|
1773 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'join') && $this->parseTableFactor()) {
|
1774 |
+
$this->parseJoinCondition();
|
1775 |
+
return true;
|
1776 |
+
}
|
1777 |
+
$this->index = $savePoint;
|
1778 |
+
|
1779 |
+
$savePoint = $this->index;
|
1780 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'straight_join') &&
|
1781 |
+
$this->parseTableFactor()
|
1782 |
+
) {
|
1783 |
+
$savePoint = $this->index;
|
1784 |
+
if (!($this->isIdentifierWithValue($this->nextToken(), 'on') && $this->parseExpression())) {
|
1785 |
+
$this->index = $savePoint;
|
1786 |
+
}
|
1787 |
+
return true;
|
1788 |
+
}
|
1789 |
+
$this->index = $savePoint;
|
1790 |
+
|
1791 |
+
$savePoint = $this->index;
|
1792 |
+
if ($this->isIdentifierWithValue($this->nextToken(), array(
|
1793 |
+
'left', 'right',
|
1794 |
+
))
|
1795 |
+
) {
|
1796 |
+
$savePoint2 = $this->index;
|
1797 |
+
if (!$this->isIdentifierWithValue($this->nextToken(), array(
|
1798 |
+
'outer',
|
1799 |
+
))
|
1800 |
+
) {
|
1801 |
+
$this->index = $savePoint2;
|
1802 |
+
}
|
1803 |
+
} else {
|
1804 |
+
$this->index = $savePoint;
|
1805 |
+
}
|
1806 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'join') &&
|
1807 |
+
$this->parseTableReference() &&
|
1808 |
+
$this->parseJoinCondition()
|
1809 |
+
) {
|
1810 |
+
return true;
|
1811 |
+
}
|
1812 |
+
$this->index = $savePoint;
|
1813 |
+
|
1814 |
+
$savePoint = $this->index;
|
1815 |
+
if ($this->isIdentifierWithValue($this->nextToken(), array(
|
1816 |
+
'natural',
|
1817 |
+
))
|
1818 |
+
) {
|
1819 |
+
if ($this->isIdentifierWithValue($this->nextToken(), array(
|
1820 |
+
'left', 'right',
|
1821 |
+
))
|
1822 |
+
) {
|
1823 |
+
$savePoint2 = $this->index;
|
1824 |
+
if (!$this->isIdentifierWithValue($this->nextToken(), array(
|
1825 |
+
'outer',
|
1826 |
+
))
|
1827 |
+
) {
|
1828 |
+
$this->index = $savePoint2;
|
1829 |
+
}
|
1830 |
+
} else {
|
1831 |
+
$this->index = $savePoint;
|
1832 |
+
}
|
1833 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'join') &&
|
1834 |
+
$this->parseTableFactor()
|
1835 |
+
) {
|
1836 |
+
return true;
|
1837 |
+
}
|
1838 |
+
|
1839 |
+
}
|
1840 |
+
$this->index = $savePoint;
|
1841 |
+
return false;
|
1842 |
+
}
|
1843 |
+
|
1844 |
+
/**
|
1845 |
+
* (ON expression) | (USING_SYM column_list)
|
1846 |
+
*
|
1847 |
+
* @return bool
|
1848 |
+
*/
|
1849 |
+
private function parseJoinCondition() {
|
1850 |
+
$savePoint = $this->index;
|
1851 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'on') && $this->parseExpression()) {
|
1852 |
+
return true;
|
1853 |
+
}
|
1854 |
+
$this->index = $savePoint;
|
1855 |
+
|
1856 |
+
$savePoint = $this->index;
|
1857 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'using') && $this->parseColumnList()) {
|
1858 |
+
return true;
|
1859 |
+
}
|
1860 |
+
$this->index = $savePoint;
|
1861 |
+
return false;
|
1862 |
+
}
|
1863 |
+
|
1864 |
+
/**
|
1865 |
+
* @return bool
|
1866 |
+
*/
|
1867 |
+
private function parseTableSpec() {
|
1868 |
+
$savePoint = $this->index;
|
1869 |
+
if ($this->isTokenOfType($this->nextToken(), array(
|
1870 |
+
wfWAFSQLiLexer::UNQUOTED_IDENTIFIER,
|
1871 |
+
wfWAFSQLiLexer::QUOTED_IDENTIFIER,
|
1872 |
+
))
|
1873 |
+
) {
|
1874 |
+
$savePoint = $this->index;
|
1875 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::DOT) &&
|
1876 |
+
$this->isTokenOfType($this->nextToken(), array(
|
1877 |
+
wfWAFSQLiLexer::UNQUOTED_IDENTIFIER,
|
1878 |
+
wfWAFSQLiLexer::QUOTED_IDENTIFIER,
|
1879 |
+
))
|
1880 |
+
) {
|
1881 |
+
return true;
|
1882 |
+
}
|
1883 |
+
$this->index = $savePoint;
|
1884 |
+
return true;
|
1885 |
+
}
|
1886 |
+
$this->index = $savePoint;
|
1887 |
+
return false;
|
1888 |
+
}
|
1889 |
+
|
1890 |
+
/**
|
1891 |
+
* @return bool
|
1892 |
+
*/
|
1893 |
+
private function parseAlias() {
|
1894 |
+
$savePoint = $this->index;
|
1895 |
+
$token = $this->nextToken();
|
1896 |
+
if ($this->isIdentifierWithValue($token, 'as')) {
|
1897 |
+
$token = $this->nextToken();
|
1898 |
+
}
|
1899 |
+
if ($this->isValidNonKeywordIdentifier($token)) {
|
1900 |
+
return true;
|
1901 |
+
}
|
1902 |
+
$this->index = $savePoint;
|
1903 |
+
return false;
|
1904 |
+
}
|
1905 |
+
|
1906 |
+
/**
|
1907 |
+
* @return bool
|
1908 |
+
*/
|
1909 |
+
private function parseIndexHintList() {
|
1910 |
+
$startPoint = $this->index;
|
1911 |
+
$hasHints = false;
|
1912 |
+
while ($this->parseIndexHint()) {
|
1913 |
+
$hasHints = true;
|
1914 |
+
$savePoint = $this->index;
|
1915 |
+
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
1916 |
+
$this->index = $savePoint;
|
1917 |
+
break;
|
1918 |
+
}
|
1919 |
+
}
|
1920 |
+
if ($hasHints) {
|
1921 |
+
return true;
|
1922 |
+
}
|
1923 |
+
$this->index = $startPoint;
|
1924 |
+
return false;
|
1925 |
+
}
|
1926 |
+
|
1927 |
+
/**
|
1928 |
+
* @return bool
|
1929 |
+
*/
|
1930 |
+
private function parseIndexHint() {
|
1931 |
+
// USE_SYM index_options LPAREN (index_list)? RPAREN
|
1932 |
+
$savePoint = $this->index;
|
1933 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'use') &&
|
1934 |
+
$this->parseIndexOptions() &&
|
1935 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)
|
1936 |
+
) {
|
1937 |
+
$this->parseIndexList();
|
1938 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
|
1939 |
+
return true;
|
1940 |
+
}
|
1941 |
+
}
|
1942 |
+
$this->index = $savePoint;
|
1943 |
+
|
1944 |
+
// IGNORE_SYM index_options LPAREN index_list RPAREN
|
1945 |
+
$savePoint = $this->index;
|
1946 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'ignore') &&
|
1947 |
+
$this->parseIndexOptions() &&
|
1948 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
|
1949 |
+
$this->parseIndexList() &&
|
1950 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
|
1951 |
+
) {
|
1952 |
+
return true;
|
1953 |
+
}
|
1954 |
+
$this->index = $savePoint;
|
1955 |
+
|
1956 |
+
// FORCE_SYM index_options LPAREN index_list RPAREN
|
1957 |
+
$savePoint = $this->index;
|
1958 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'force') &&
|
1959 |
+
$this->parseIndexOptions() &&
|
1960 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
|
1961 |
+
$this->parseIndexList() &&
|
1962 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
|
1963 |
+
) {
|
1964 |
+
return true;
|
1965 |
+
}
|
1966 |
+
$this->index = $savePoint;
|
1967 |
+
|
1968 |
+
return false;
|
1969 |
+
}
|
1970 |
+
|
1971 |
+
/**
|
1972 |
+
* (INDEX_SYM | KEY_SYM) ( FOR_SYM ((JOIN_SYM) | (ORDER_SYM BY_SYM) | (GROUP_SYM BY_SYM)) )?
|
1973 |
+
*
|
1974 |
+
* @return bool
|
1975 |
+
*/
|
1976 |
+
private function parseIndexOptions() {
|
1977 |
+
$savePoint = $this->index;
|
1978 |
+
$token = $this->nextToken();
|
1979 |
+
if ($this->isIdentifierWithValue($token, 'index') ||
|
1980 |
+
$this->isIdentifierWithValue($token, 'key')
|
1981 |
+
) {
|
1982 |
+
$savePoint = $this->index;
|
1983 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'for')) {
|
1984 |
+
|
1985 |
+
$savePoint = $this->index;
|
1986 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'join')) {
|
1987 |
+
return true;
|
1988 |
+
}
|
1989 |
+
$this->index = $savePoint;
|
1990 |
+
|
1991 |
+
$savePoint = $this->index;
|
1992 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'order') &&
|
1993 |
+
$this->isIdentifierWithValue($this->nextToken(), 'by')
|
1994 |
+
) {
|
1995 |
+
return true;
|
1996 |
+
}
|
1997 |
+
$this->index = $savePoint;
|
1998 |
+
|
1999 |
+
$savePoint = $this->index;
|
2000 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'group') &&
|
2001 |
+
$this->isIdentifierWithValue($this->nextToken(), 'by')
|
2002 |
+
) {
|
2003 |
+
return true;
|
2004 |
+
}
|
2005 |
+
$this->index = $savePoint;
|
2006 |
+
|
2007 |
+
return true;
|
2008 |
+
}
|
2009 |
+
$this->index = $savePoint;
|
2010 |
+
return true;
|
2011 |
+
}
|
2012 |
+
$this->index = $savePoint;
|
2013 |
+
return false;
|
2014 |
+
}
|
2015 |
+
|
2016 |
+
private function parseIndexList() {
|
2017 |
+
$startPoint = $this->index;
|
2018 |
+
$hasIndex = false;
|
2019 |
+
while ($this->parseIndexName()) {
|
2020 |
+
$hasIndex = true;
|
2021 |
+
$savePoint = $this->index;
|
2022 |
+
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
2023 |
+
$this->index = $savePoint;
|
2024 |
+
break;
|
2025 |
+
}
|
2026 |
+
}
|
2027 |
+
if ($hasIndex) {
|
2028 |
+
return true;
|
2029 |
+
}
|
2030 |
+
$this->index = $startPoint;
|
2031 |
+
return false;
|
2032 |
+
}
|
2033 |
+
|
2034 |
+
private function parseIndexName() {
|
2035 |
+
$startPoint = $this->index;
|
2036 |
+
$token = $this->nextToken();
|
2037 |
+
if ($this->isValidNonKeywordIdentifier($token)) {
|
2038 |
+
return true;
|
2039 |
+
}
|
2040 |
+
$this->index = $startPoint;
|
2041 |
+
return false;
|
2042 |
+
}
|
2043 |
+
|
2044 |
+
/**
|
2045 |
+
* LPAREN column_spec (COMMA column_spec)* RPAREN
|
2046 |
+
*
|
2047 |
+
* @return bool
|
2048 |
+
*/
|
2049 |
+
private function parseColumnList() {
|
2050 |
+
$startPoint = $this->index;
|
2051 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)) {
|
2052 |
+
$hasColumn = false;
|
2053 |
+
while ($this->parseColumnSpec()) {
|
2054 |
+
$hasColumn = true;
|
2055 |
+
$savePoint = $this->index;
|
2056 |
+
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
2057 |
+
$this->index = $savePoint;
|
2058 |
+
break;
|
2059 |
+
}
|
2060 |
+
}
|
2061 |
+
if ($hasColumn && $this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
|
2062 |
+
return true;
|
2063 |
+
}
|
2064 |
+
}
|
2065 |
+
$this->index = $startPoint;
|
2066 |
+
return false;
|
2067 |
+
}
|
2068 |
+
|
2069 |
+
private function parseWhere() {
|
2070 |
+
$startIndex = $this->index;
|
2071 |
+
$token = $this->nextToken();
|
2072 |
+
if ($this->isIdentifierWithValue($token, 'where')) {
|
2073 |
+
if ($this->parseExpression()) {
|
2074 |
+
return true;
|
2075 |
+
}
|
2076 |
+
}
|
2077 |
+
$this->index = $startIndex;
|
2078 |
+
return false;
|
2079 |
+
}
|
2080 |
+
|
2081 |
+
private function parseProcedure() {
|
2082 |
+
$startIndex = $this->index;
|
2083 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'PROCEDURE') &&
|
2084 |
+
$this->isIdentifierWithValue($this->nextToken(), 'ANALYSE') &&
|
2085 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)
|
2086 |
+
) {
|
2087 |
+
$savePoint = $this->index;
|
2088 |
+
if ($this->parseExpression()) {
|
2089 |
+
$savePoint = $this->index;
|
2090 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA) &&
|
2091 |
+
$this->parseExpression() &&
|
2092 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
|
2093 |
+
) {
|
2094 |
+
return true;
|
2095 |
+
}
|
2096 |
+
$this->index = $savePoint;
|
2097 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
|
2098 |
+
return true;
|
2099 |
+
}
|
2100 |
+
}
|
2101 |
+
$this->index = $savePoint;
|
2102 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
|
2103 |
+
return true;
|
2104 |
+
}
|
2105 |
+
}
|
2106 |
+
$this->index = $startIndex;
|
2107 |
+
return false;
|
2108 |
+
}
|
2109 |
+
|
2110 |
+
/**
|
2111 |
+
* GROUP_SYM BY_SYM groupby_item (COMMA groupby_item)* (WITH ROLLUP_SYM)?
|
2112 |
+
*
|
2113 |
+
* @return bool
|
2114 |
+
*/
|
2115 |
+
private function parseGroupBy() {
|
2116 |
+
$startIndex = $this->index;
|
2117 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'group') &&
|
2118 |
+
$this->isIdentifierWithValue($this->nextToken(), 'by')
|
2119 |
+
) {
|
2120 |
+
$hasItems = false;
|
2121 |
+
while ($this->parseGroupByItem()) {
|
2122 |
+
$hasItems = true;
|
2123 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
2124 |
+
continue;
|
2125 |
+
}
|
2126 |
+
$this->index--;
|
2127 |
+
break;
|
2128 |
+
}
|
2129 |
+
if ($hasItems) {
|
2130 |
+
$savePoint = $this->index;
|
2131 |
+
if (!($this->isIdentifierWithValue($this->nextToken(), 'with') &&
|
2132 |
+
$this->isIdentifierWithValue($this->nextToken(), 'rollup'))
|
2133 |
+
) {
|
2134 |
+
$this->index = $savePoint;
|
2135 |
+
}
|
2136 |
+
return true;
|
2137 |
+
}
|
2138 |
+
}
|
2139 |
+
$this->index = $startIndex;
|
2140 |
+
return false;
|
2141 |
+
}
|
2142 |
+
|
2143 |
+
/**
|
2144 |
+
* column_spec | INTEGER_NUM | bit_expr ;
|
2145 |
+
*
|
2146 |
+
* @return bool
|
2147 |
+
*/
|
2148 |
+
private function parseGroupByItem() {
|
2149 |
+
$startIndex = $this->index;
|
2150 |
+
if ($this->parseBitExpression()) {
|
2151 |
+
return true;
|
2152 |
+
}
|
2153 |
+
$this->index = $startIndex;
|
2154 |
+
return false;
|
2155 |
+
}
|
2156 |
+
|
2157 |
+
/**
|
2158 |
+
* HAVING expression
|
2159 |
+
*
|
2160 |
+
* @return bool
|
2161 |
+
*/
|
2162 |
+
private function parseHaving() {
|
2163 |
+
$startIndex = $this->index;
|
2164 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'having')
|
2165 |
+
&& $this->parseExpression()
|
2166 |
+
) {
|
2167 |
+
return true;
|
2168 |
+
}
|
2169 |
+
$this->index = $startIndex;
|
2170 |
+
return false;
|
2171 |
+
}
|
2172 |
+
|
2173 |
+
/**
|
2174 |
+
* ORDER_SYM BY_SYM orderby_item (COMMA orderby_item)*
|
2175 |
+
*
|
2176 |
+
* @return bool
|
2177 |
+
*/
|
2178 |
+
private function parseOrderBy() {
|
2179 |
+
$startIndex = $this->index;
|
2180 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'order') &&
|
2181 |
+
$this->isIdentifierWithValue($this->nextToken(), 'by')
|
2182 |
+
) {
|
2183 |
+
$hasItems = false;
|
2184 |
+
while ($this->parseOrderByItem()) {
|
2185 |
+
$hasItems = true;
|
2186 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
2187 |
+
continue;
|
2188 |
+
}
|
2189 |
+
$this->index--;
|
2190 |
+
break;
|
2191 |
+
}
|
2192 |
+
if ($hasItems) {
|
2193 |
+
return true;
|
2194 |
+
}
|
2195 |
+
}
|
2196 |
+
$this->index = $startIndex;
|
2197 |
+
return false;
|
2198 |
+
}
|
2199 |
+
|
2200 |
+
/**
|
2201 |
+
* groupby_item (ASC | DESC)? ;
|
2202 |
+
*
|
2203 |
+
* @return bool
|
2204 |
+
*/
|
2205 |
+
private function parseOrderByItem() {
|
2206 |
+
$startIndex = $this->index;
|
2207 |
+
if ($this->parseGroupByItem()) {
|
2208 |
+
$savePoint = $this->index;
|
2209 |
+
if (!$this->isIdentifierWithValue($this->nextToken(), array(
|
2210 |
+
'asc', 'desc',
|
2211 |
+
))
|
2212 |
+
) {
|
2213 |
+
$this->index = $savePoint;
|
2214 |
+
}
|
2215 |
+
return true;
|
2216 |
+
}
|
2217 |
+
$this->index = $startIndex;
|
2218 |
+
return false;
|
2219 |
+
}
|
2220 |
+
|
2221 |
+
private function parseLimit() {
|
2222 |
+
// LIMIT ((offset COMMA)? row_count) | (row_count OFFSET_SYM offset)
|
2223 |
+
$startIndex = $this->index;
|
2224 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'limit') &&
|
2225 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::INTEGER_LITERAL)
|
2226 |
+
) {
|
2227 |
+
$savePoint = $this->index;
|
2228 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA) &&
|
2229 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::INTEGER_LITERAL)
|
2230 |
+
) {
|
2231 |
+
return true;
|
2232 |
+
}
|
2233 |
+
$this->index = $savePoint;
|
2234 |
+
|
2235 |
+
$savePoint = $this->index;
|
2236 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'offset') &&
|
2237 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::INTEGER_LITERAL)
|
2238 |
+
) {
|
2239 |
+
return true;
|
2240 |
+
}
|
2241 |
+
$this->index = $savePoint;
|
2242 |
+
return true;
|
2243 |
+
}
|
2244 |
+
$this->index = $startIndex;
|
2245 |
+
return false;
|
2246 |
+
}
|
2247 |
+
|
2248 |
+
/**
|
2249 |
+
* @link http://dev.mysql.com/doc/refman/5.6/en/insert.html
|
2250 |
+
* @return bool
|
2251 |
+
*/
|
2252 |
+
private function parseInsertStatement() {
|
2253 |
+
$startIndex = $this->index;
|
2254 |
+
if ($this->parseInsertStatement1() ||
|
2255 |
+
$this->parseInsertStatement2() ||
|
2256 |
+
$this->parseInsertStatement3()
|
2257 |
+
) {
|
2258 |
+
return true;
|
2259 |
+
}
|
2260 |
+
$this->index = $startIndex;
|
2261 |
+
return false;
|
2262 |
+
}
|
2263 |
+
|
2264 |
+
/**
|
2265 |
+
* insert_header
|
2266 |
+
* (column_list)?
|
2267 |
+
* value_list_clause
|
2268 |
+
* ( insert_subfix )?
|
2269 |
+
*
|
2270 |
+
* @return bool
|
2271 |
+
*/
|
2272 |
+
private function parseInsertStatement1() {
|
2273 |
+
$startIndex = $this->index;
|
2274 |
+
if ($this->parseInsertHeader()) {
|
2275 |
+
$this->parseColumnList();
|
2276 |
+
if ($this->parseValueListClause()) {
|
2277 |
+
$this->parseInsertSubfix();
|
2278 |
+
return true;
|
2279 |
+
}
|
2280 |
+
}
|
2281 |
+
$this->index = $startIndex;
|
2282 |
+
return false;
|
2283 |
+
}
|
2284 |
+
|
2285 |
+
/**
|
2286 |
+
* insert_header
|
2287 |
+
* set_columns_cluase
|
2288 |
+
* ( insert_subfix )?
|
2289 |
+
*
|
2290 |
+
* @return bool
|
2291 |
+
*/
|
2292 |
+
private function parseInsertStatement2() {
|
2293 |
+
$startIndex = $this->index;
|
2294 |
+
if ($this->parseInsertHeader() && $this->parseSetColumnsClause()) {
|
2295 |
+
$this->parseInsertSubfix();
|
2296 |
+
return true;
|
2297 |
+
}
|
2298 |
+
$this->index = $startIndex;
|
2299 |
+
return false;
|
2300 |
+
}
|
2301 |
+
|
2302 |
+
/**
|
2303 |
+
* insert_header
|
2304 |
+
* (column_list)?
|
2305 |
+
* select_expression
|
2306 |
+
* ( insert_subfix )?
|
2307 |
+
*
|
2308 |
+
* @return bool
|
2309 |
+
*/
|
2310 |
+
private function parseInsertStatement3() {
|
2311 |
+
$startIndex = $this->index;
|
2312 |
+
if ($this->parseInsertHeader()) {
|
2313 |
+
$this->parseColumnList();
|
2314 |
+
if ($this->parseSelectStatement()) {
|
2315 |
+
$this->parseInsertSubfix();
|
2316 |
+
return true;
|
2317 |
+
}
|
2318 |
+
}
|
2319 |
+
$this->index = $startIndex;
|
2320 |
+
return false;
|
2321 |
+
}
|
2322 |
+
|
2323 |
+
/**
|
2324 |
+
* INSERT (LOW_PRIORITY | HIGH_PRIORITY)? (IGNORE_SYM)?
|
2325 |
+
* (INTO)? table_spec
|
2326 |
+
* (partition_clause)?
|
2327 |
+
*
|
2328 |
+
* @return bool
|
2329 |
+
*/
|
2330 |
+
private function parseInsertHeader() {
|
2331 |
+
$startIndex = $this->index;
|
2332 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'insert')) {
|
2333 |
+
$savePoint = $this->index;
|
2334 |
+
// (LOW_PRIORITY | HIGH_PRIORITY)?
|
2335 |
+
if (!$this->isIdentifierWithValue($this->nextToken(), array(
|
2336 |
+
'LOW_PRIORITY', 'HIGH_PRIORITY'
|
2337 |
+
))
|
2338 |
+
) {
|
2339 |
+
$this->index = $savePoint;
|
2340 |
+
}
|
2341 |
+
|
2342 |
+
// (IGNORE_SYM)?
|
2343 |
+
$savePoint = $this->index;
|
2344 |
+
if (!$this->isIdentifierWithValue($this->nextToken(), array(
|
2345 |
+
'IGNORE'
|
2346 |
+
))
|
2347 |
+
) {
|
2348 |
+
$this->index = $savePoint;
|
2349 |
+
}
|
2350 |
+
|
2351 |
+
// (INTO)?
|
2352 |
+
$savePoint = $this->index;
|
2353 |
+
if (!$this->isIdentifierWithValue($this->nextToken(), array(
|
2354 |
+
'into'
|
2355 |
+
))
|
2356 |
+
) {
|
2357 |
+
$this->index = $savePoint;
|
2358 |
+
}
|
2359 |
+
|
2360 |
+
// table_spec
|
2361 |
+
if ($this->parseTableSpec()) {
|
2362 |
+
$savePoint = $this->index;
|
2363 |
+
// (partition_clause)?
|
2364 |
+
if (!$this->parsePartitionClause()) {
|
2365 |
+
$this->index = $savePoint;
|
2366 |
+
}
|
2367 |
+
return true;
|
2368 |
+
}
|
2369 |
+
}
|
2370 |
+
$this->index = $startIndex;
|
2371 |
+
return false;
|
2372 |
+
}
|
2373 |
+
|
2374 |
+
/**
|
2375 |
+
* (VALUES | VALUE_SYM) column_value_list (COMMA column_value_list)*;
|
2376 |
+
*
|
2377 |
+
* @return bool
|
2378 |
+
*/
|
2379 |
+
private function parseValueListClause() {
|
2380 |
+
$startIndex = $this->index;
|
2381 |
+
if ($this->isIdentifierWithValue($this->nextToken(), array(
|
2382 |
+
'value', 'values',
|
2383 |
+
))
|
2384 |
+
) {
|
2385 |
+
$hasValues = false;
|
2386 |
+
while ($this->parseColumnValueList()) {
|
2387 |
+
$hasValues = true;
|
2388 |
+
$savePoint = $this->index;
|
2389 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
2390 |
+
$hasValues = false;
|
2391 |
+
continue;
|
2392 |
+
}
|
2393 |
+
$this->index = $savePoint;
|
2394 |
+
break;
|
2395 |
+
}
|
2396 |
+
if ($hasValues) {
|
2397 |
+
return true;
|
2398 |
+
}
|
2399 |
+
}
|
2400 |
+
$this->index = $startIndex;
|
2401 |
+
return false;
|
2402 |
+
}
|
2403 |
+
|
2404 |
+
/**
|
2405 |
+
* LPAREN (bit_expr|DEFAULT) (COMMA (bit_expr|DEFAULT) )* RPAREN ;
|
2406 |
+
*
|
2407 |
+
* @return bool
|
2408 |
+
*/
|
2409 |
+
private function parseColumnValueList() {
|
2410 |
+
$startIndex = $this->index;
|
2411 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)) {
|
2412 |
+
$hasValues = false;
|
2413 |
+
while (true) {
|
2414 |
+
$savePoint = $this->index;
|
2415 |
+
if (!$this->parseBitExpression() && !$this->isIdentifierWithValue($this->nextToken(), 'DEFAULT')) {
|
2416 |
+
$this->index = $savePoint;
|
2417 |
+
break;
|
2418 |
+
}
|
2419 |
+
$hasValues = true;
|
2420 |
+
$savePoint = $this->index;
|
2421 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
2422 |
+
$hasValues = false;
|
2423 |
+
continue;
|
2424 |
+
}
|
2425 |
+
$this->index = $savePoint;
|
2426 |
+
break;
|
2427 |
+
}
|
2428 |
+
if ($hasValues && $this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
|
2429 |
+
return true;
|
2430 |
+
}
|
2431 |
+
}
|
2432 |
+
$this->index = $startIndex;
|
2433 |
+
return false;
|
2434 |
+
}
|
2435 |
+
|
2436 |
+
private function parseInsertSubfix() {
|
2437 |
+
// ON DUPLICATE_SYM KEY_SYM UPDATE column_spec EQ_SYM expression (COMMA column_spec EQ_SYM expression)*
|
2438 |
+
$startIndex = $this->index;
|
2439 |
+
if (
|
2440 |
+
$this->isIdentifierWithValue($this->nextToken(), 'on') &&
|
2441 |
+
$this->isIdentifierWithValue($this->nextToken(), 'duplicate') &&
|
2442 |
+
$this->isIdentifierWithValue($this->nextToken(), 'key') &&
|
2443 |
+
$this->isIdentifierWithValue($this->nextToken(), 'update')
|
2444 |
+
) {
|
2445 |
+
$hasValues = false;
|
2446 |
+
while (true) {
|
2447 |
+
$savePoint = $this->index;
|
2448 |
+
if ($this->parseColumnSpec() &&
|
2449 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::EQUALS_SYMBOL) &&
|
2450 |
+
$this->parseExpression()
|
2451 |
+
) {
|
2452 |
+
$hasValues = true;
|
2453 |
+
$savePoint = $this->index;
|
2454 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
2455 |
+
$hasValues = false;
|
2456 |
+
continue;
|
2457 |
+
}
|
2458 |
+
$this->index = $savePoint;
|
2459 |
+
break;
|
2460 |
+
}
|
2461 |
+
$this->index = $savePoint;
|
2462 |
+
break;
|
2463 |
+
}
|
2464 |
+
if ($hasValues) {
|
2465 |
+
return true;
|
2466 |
+
}
|
2467 |
+
}
|
2468 |
+
$this->index = $startIndex;
|
2469 |
+
return false;
|
2470 |
+
}
|
2471 |
+
|
2472 |
+
/**
|
2473 |
+
* SET_SYM set_column_cluase ( COMMA set_column_cluase )*;
|
2474 |
+
*
|
2475 |
+
* @return bool
|
2476 |
+
*/
|
2477 |
+
private function parseSetColumnsClause() {
|
2478 |
+
$startIndex = $this->index;
|
2479 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'set')) {
|
2480 |
+
$hasValues = true;
|
2481 |
+
while ($this->parseSetColumnClause()) {
|
2482 |
+
$hasValues = true;
|
2483 |
+
$savePoint = $this->index;
|
2484 |
+
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
|
2485 |
+
$hasValues = false;
|
2486 |
+
continue;
|
2487 |
+
}
|
2488 |
+
$this->index = $savePoint;
|
2489 |
+
break;
|
2490 |
+
}
|
2491 |
+
if ($hasValues) {
|
2492 |
+
return true;
|
2493 |
+
}
|
2494 |
+
}
|
2495 |
+
$this->index = $startIndex;
|
2496 |
+
return false;
|
2497 |
+
}
|
2498 |
+
|
2499 |
+
/**
|
2500 |
+
* column_spec EQ_SYM (expression|DEFAULT) ;
|
2501 |
+
*
|
2502 |
+
* @return bool
|
2503 |
+
*/
|
2504 |
+
private function parseSetColumnClause() {
|
2505 |
+
$startIndex = $this->index;
|
2506 |
+
if ($this->parseColumnSpec() &&
|
2507 |
+
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::EQUALS_SYMBOL) &&
|
2508 |
+
($this->parseExpression() || $this->isIdentifierWithValue($this->nextToken(), 'default'))
|
2509 |
+
) {
|
2510 |
+
return true;
|
2511 |
+
}
|
2512 |
+
$this->index = $startIndex;
|
2513 |
+
return false;
|
2514 |
+
}
|
2515 |
+
|
2516 |
+
/**
|
2517 |
+
* UPDATE (LOW_PRIORITY)? (IGNORE_SYM)? table_reference
|
2518 |
+
* set_columns_cluase
|
2519 |
+
* (where_clause)?
|
2520 |
+
* (orderby_clause)?
|
2521 |
+
* (limit_clause)?
|
2522 |
+
* |
|
2523 |
+
* UPDATE (LOW_PRIORITY)? (IGNORE_SYM)? table_references
|
2524 |
+
* set_columns_cluase
|
2525 |
+
* (where_clause)?
|
2526 |
+
*
|
2527 |
+
* @return bool
|
2528 |
+
*/
|
2529 |
+
private function parseUpdateStatement() {
|
2530 |
+
$startIndex = $this->index;
|
2531 |
+
if ($this->isIdentifierWithValue($this->nextToken(), 'update')) {
|
2532 |
+
$savePoint = $this->index;
|
2533 |
+
if (!$this->isIdentifierWithValue($this->nextToken(), 'LOW_PRIORITY')) {
|
2534 |
+
$this->index = $savePoint;
|
2535 |
+
}
|
2536 |
+
|
2537 |
+
$savePoint = $this->index;
|
2538 |
+
if (!$this->isIdentifierWithValue($this->nextToken(), 'ignore')) {
|
2539 |
+
$this->index = $savePoint;
|
2540 |
+
}
|
2541 |
+
|
2542 |
+
if ($this->parseTableReferences() && $this->parseSetColumnsClause()) {
|
2543 |
+
$this->parseWhere();
|
2544 |
+
$this->parseOrderBy();
|
2545 |
+
$this->parseLimit();
|
2546 |
+
return true;
|
2547 |
+
}
|
2548 |
+
}
|
2549 |
+
$this->index = $startIndex;
|
2550 |
+
return false;
|
2551 |
+
|
2552 |
+
}
|
2553 |
+
|
2554 |
+
/**
|
2555 |
+
* @param wfWAFLexerToken $token
|
2556 |
+
* @param string|array $value
|
2557 |
+
* @return bool
|
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 |
+
/**
|
2566 |
+
* @param wfWAFLexerToken $token
|
2567 |
+
* @param mixed $type
|
2568 |
+
* @return bool
|
2569 |
+
*/
|
2570 |
+
private function isTokenOfType($token, $type) {
|
2571 |
+
if (is_array($type)) {
|
2572 |
+
return $token && in_array($token->getType(), $type);
|
2573 |
+
}
|
2574 |
+
return $token && $token->getType() === $type;
|
2575 |
+
}
|
2576 |
+
|
2577 |
+
/**
|
2578 |
+
* @param wfWAFLexerToken $token
|
2579 |
+
* @return bool
|
2580 |
+
*/
|
2581 |
+
private function isNotSymbolToken($token) {
|
2582 |
+
return $token &&
|
2583 |
+
(
|
2584 |
+
($token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER && $token->getLowerCaseValue() === 'not') ||
|
2585 |
+
($token->getType() === wfWAFSQLiLexer::EXPR_NOT)
|
2586 |
+
);
|
2587 |
+
}
|
2588 |
+
|
2589 |
+
/**
|
2590 |
+
* @param wfWAFLexerToken $token
|
2591 |
+
* @return bool
|
2592 |
+
*/
|
2593 |
+
private function isKeywordToken($token) {
|
2594 |
+
return $token && $token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER &&
|
2595 |
+
in_array($token->getUpperCaseValue(), $this->keywords);
|
2596 |
+
}
|
2597 |
+
|
2598 |
+
/**
|
2599 |
+
* @param wfWAFLexerToken $token
|
2600 |
+
* @return bool
|
2601 |
+
*/
|
2602 |
+
private function isValidNonKeywordIdentifier($token) {
|
2603 |
+
return $token && (
|
2604 |
+
$token->getType() === wfWAFSQLiLexer::QUOTED_IDENTIFIER ||
|
2605 |
+
($token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER && !$this->isKeywordToken($token))
|
2606 |
+
);
|
2607 |
+
}
|
2608 |
+
|
2609 |
+
/**
|
2610 |
+
* @param wfWAFLexerToken $token
|
2611 |
+
* @return bool
|
2612 |
+
*/
|
2613 |
+
private function isOrToken($token) {
|
2614 |
+
return $token && ($this->isIdentifierWithValue($token, 'or') || $this->isTokenOfType($token, wfWAFSQLiLexer::EXPR_OR));
|
2615 |
+
}
|
2616 |
+
|
2617 |
+
/**
|
2618 |
+
* @param wfWAFLexerToken $token
|
2619 |
+
* @return bool
|
2620 |
+
*/
|
2621 |
+
private function isAndToken($token) {
|
2622 |
+
return $token && ($this->isIdentifierWithValue($token, 'and') || $this->isTokenOfType($token, wfWAFSQLiLexer::EXPR_AND));
|
2623 |
+
}
|
2624 |
+
|
2625 |
+
/**
|
2626 |
+
* @return string
|
2627 |
+
*/
|
2628 |
+
public function getSubject() {
|
2629 |
+
return $this->subject;
|
2630 |
+
}
|
2631 |
+
|
2632 |
+
/**
|
2633 |
+
* @param string $subject
|
2634 |
+
*/
|
2635 |
+
public function setSubject($subject) {
|
2636 |
+
$this->subject = $subject;
|
2637 |
+
$this->setTokens(array());
|
2638 |
+
$this->lexer->setSQL($this->subject);
|
2639 |
+
}
|
2640 |
+
|
2641 |
+
/**
|
2642 |
+
* @return int
|
2643 |
+
*/
|
2644 |
+
public function getFlags() {
|
2645 |
+
return $this->flags;
|
2646 |
+
}
|
2647 |
+
|
2648 |
+
/**
|
2649 |
+
* @param int $flags
|
2650 |
+
*/
|
2651 |
+
public function setFlags($flags) {
|
2652 |
+
$this->flags = $flags;
|
2653 |
+
$this->lexer->setFlags($this->flags);
|
2654 |
+
}
|
2655 |
+
}
|
2656 |
+
|
2657 |
+
|
2658 |
+
class wfWAFSQLiLexer implements wfWAFLexerInterface {
|
2659 |
+
|
2660 |
+
const FLAG_TOKENIZE_MYSQL_PORTABLE_COMMENTS = 0x1;
|
2661 |
+
|
2662 |
+
const UNQUOTED_IDENTIFIER = 'UNQUOTED_IDENTIFIER';
|
2663 |
+
const VARIABLE = 'VARIABLE';
|
2664 |
+
const QUOTED_IDENTIFIER = 'QUOTED_IDENTIFIER';
|
2665 |
+
const DOUBLE_STRING_LITERAL = 'DOUBLE_STRING_LITERAL';
|
2666 |
+
const SINGLE_STRING_LITERAL = 'SINGLE_STRING_LITERAL';
|
2667 |
+
const INTEGER_LITERAL = 'INTEGER_LITERAL';
|
2668 |
+
const REAL_NUMBER_LITERAL = 'REAL_NUMBER_LITERAL';
|
2669 |
+
const BINARY_NUMBER_LITERAL = 'BINARY_NUMBER_LITERAL';
|
2670 |
+
const HEX_NUMBER_LITERAL = 'HEX_NUMBER_LITERAL';
|
2671 |
+
const DOT = 'DOT';
|
2672 |
+
const OPEN_PARENTHESIS = 'OPEN_PARENTHESIS';
|
2673 |
+
const CLOSE_PARENTHESIS = 'CLOSE_PARENTHESIS';
|
2674 |
+
const OPEN_BRACKET = 'OPEN_BRACKET';
|
2675 |
+
const CLOSE_BRACKET = 'CLOSE_BRACKET';
|
2676 |
+
const COMMA = 'COMMA';
|
2677 |
+
const EXPR_OR = 'EXPR_OR';
|
2678 |
+
const EXPR_AND = 'EXPR_AND';
|
2679 |
+
const EXPR_NOT = 'EXPR_NOT';
|
2680 |
+
const BIT_AND = 'BIT_AND';
|
2681 |
+
const BIT_LEFT_SHIFT = 'BIT_LEFT_SHIFT';
|
2682 |
+
const BIT_RIGHT_SHIFT = 'BIT_RIGHT_SHIFT';
|
2683 |
+
const BIT_XOR = 'BIT_XOR';
|
2684 |
+
const BIT_INVERSION = 'BIT_INVERSION';
|
2685 |
+
const BIT_OR = 'BIT_OR';
|
2686 |
+
const PLUS = 'PLUS';
|
2687 |
+
const MINUS = 'MINUS';
|
2688 |
+
const ASTERISK = 'ASTERISK';
|
2689 |
+
const DIVISION = 'DIVISION';
|
2690 |
+
const MOD = 'MOD';
|
2691 |
+
const ARROW = 'ARROW';
|
2692 |
+
const EQUALS_SYMBOL = 'EQUALS_SYMBOL';
|
2693 |
+
const NOT_EQUALS = 'NOT_EQUALS';
|
2694 |
+
const LESS_THAN = 'LESS_THAN';
|
2695 |
+
const GREATER_THAN = 'GREATER_THAN';
|
2696 |
+
const LESS_THAN_EQUAL_TO = 'LESS_THAN_EQUAL_TO';
|
2697 |
+
const GREATER_THAN_EQUAL_TO = 'GREATER_THAN_EQUAL_TO';
|
2698 |
+
const SET_VAR = 'SET_VAR';
|
2699 |
+
const RIGHT_BRACKET = 'RIGHT_BRACKET';
|
2700 |
+
const LEFT_BRACKET = 'LEFT_BRACKET';
|
2701 |
+
const SEMICOLON = 'SEMICOLON';
|
2702 |
+
const COLON = 'COLON';
|
2703 |
+
const MYSQL_PORTABLE_COMMENT_START = 'MYSQL_PORTABLE_COMMENT_START';
|
2704 |
+
const MYSQL_PORTABLE_COMMENT_END = 'MYSQL_PORTABLE_COMMENT_END';
|
2705 |
+
const SINGLE_LINE_COMMENT = 'SINGLE_LINE_COMMENT';
|
2706 |
+
const MULTI_LINE_COMMENT = 'MULTI_LINE_COMMENT';
|
2707 |
+
/**
|
2708 |
+
* @var int
|
2709 |
+
*/
|
2710 |
+
private $flags;
|
2711 |
+
private $tokenMatchers;
|
2712 |
+
private $hasPortableCommentStart = false;
|
2713 |
+
|
2714 |
+
public static function getLexerTokenMatchers() {
|
2715 |
+
static $tokenMatchers;
|
2716 |
+
if ($tokenMatchers === null) {
|
2717 |
+
$tokenMatchers = array(
|
2718 |
+
new wfWAFLexerTokenMatcher(self::REAL_NUMBER_LITERAL, '/^(?:[0-9]+\\.[0-9]+|[0-9]+\\.|\\.[0-9]+|[Ee][\\+\\-][0-9]+)/'),
|
2719 |
+
new wfWAFLexerTokenMatcher(self::BINARY_NUMBER_LITERAL, '/^(?:0b[01]+|[bB]\'[01]+\')/', true),
|
2720 |
+
new wfWAFLexerTokenMatcher(self::HEX_NUMBER_LITERAL, '/^(?:0x[0-9a-fA-F]+|[xX]\'[0-9a-fA-F]+\')/', true),
|
2721 |
+
new wfWAFLexerTokenMatcher(self::INTEGER_LITERAL, '/^[0-9]+/', true),
|
2722 |
+
new wfWAFLexerTokenMatcher(self::VARIABLE, '/^(?:@(?:`([^`\\\\]{0,256}(?:\\\\.[^`\\\\]{0,256}){0,256})`|
|
2723 |
+
"(?:[^#"\\\\]{0,256}(?:\\\\.[^#"\\\\]{0,256}){0,256})"|
|
2724 |
+
\'(?:[^\'\\\\]{0,256}(?:\\\\.[^\'\\\\]{0,256}){0,256})\'|
|
2725 |
+
[a-zA-Z_\\$\\.]+|
|
2726 |
+
@[a-zA-Z_\\$][a-zA-Z_\\$0-9]{0,256}){0,1})
|
2727 |
+
/Asx'),
|
2728 |
+
new wfWAFLexerTokenMatcher(self::QUOTED_IDENTIFIER, '/^`(?:[^`\\\\]{0,256}(?:\\\\.[^`\\\\]{0,256}){0,256})`/As'),
|
2729 |
+
new wfWAFLexerTokenMatcher(self::DOUBLE_STRING_LITERAL, '/^(?:[nN]|_[0-9a-zA-Z\\$_]{0,256})?"(?:[^#"\\\\]{0,256}(?:\\\\.[^#"\\\\]{0,256}){0,256})"/As'),
|
2730 |
+
new wfWAFLexerTokenMatcher(self::SINGLE_STRING_LITERAL, '/^(?:[nN]|_[0-9a-zA-Z\\$_]{0,256})?\'(?:[^\'\\\\]{0,256}(?:\\\\.[^\'\\\\]{0,256}){0,256})\'/As'),
|
2731 |
+
// U+0080 .. U+FFFF
|
2732 |
+
new wfWAFLexerTokenMatcher(self::UNQUOTED_IDENTIFIER, '/^[0-9a-zA-Z\\$_\\x{0080}-\\x{FFFF}]{1,256}/u'),
|
2733 |
+
new wfWAFLexerTokenMatcher(self::MYSQL_PORTABLE_COMMENT_START, '/^\\/\\*\\![0-9]{0,5}/s'),
|
2734 |
+
new wfWAFLexerTokenMatcher(self::MYSQL_PORTABLE_COMMENT_END, '/^\\*\\//s'),
|
2735 |
+
new wfWAFLexerTokenMatcher(self::SINGLE_LINE_COMMENT, '/^(?:#[^\n]*|--(?:[ \t\r][^\n]*|[\n]))/'),
|
2736 |
+
new wfWAFLexerTokenMatcher(self::MULTI_LINE_COMMENT, '/^\\/\\*.*?\\*\\//s'),
|
2737 |
+
new wfWAFLexerTokenMatcher(self::DOT, '/^\\./'),
|
2738 |
+
new wfWAFLexerTokenMatcher(self::OPEN_PARENTHESIS, '/^\\(/'),
|
2739 |
+
new wfWAFLexerTokenMatcher(self::CLOSE_PARENTHESIS, '/^\\)/'),
|
2740 |
+
new wfWAFLexerTokenMatcher(self::COMMA, '/^,/'),
|
2741 |
+
new wfWAFLexerTokenMatcher(self::EXPR_OR, '/^\\|\\|/'),
|
2742 |
+
new wfWAFLexerTokenMatcher(self::EXPR_AND, '/^&&/'),
|
2743 |
+
new wfWAFLexerTokenMatcher(self::BIT_LEFT_SHIFT, '/^\\<\\</'),
|
2744 |
+
new wfWAFLexerTokenMatcher(self::BIT_RIGHT_SHIFT, '/^\\>\\>/'),
|
2745 |
+
new wfWAFLexerTokenMatcher(self::EQUALS_SYMBOL, '/^(?:\\=|\\<\\=\\>)/'),
|
2746 |
+
new wfWAFLexerTokenMatcher(self::ARROW, '/^\\=\\>/'),
|
2747 |
+
new wfWAFLexerTokenMatcher(self::LESS_THAN_EQUAL_TO, '/^\\<\\=/'),
|
2748 |
+
new wfWAFLexerTokenMatcher(self::GREATER_THAN_EQUAL_TO, '/^\\>\\=/'),
|
2749 |
+
new wfWAFLexerTokenMatcher(self::NOT_EQUALS, '/^(?:\\<\\>|(?:\\!|\\~|\\^)\\=)/'),
|
2750 |
+
new wfWAFLexerTokenMatcher(self::LESS_THAN, '/^\\</'),
|
2751 |
+
new wfWAFLexerTokenMatcher(self::GREATER_THAN, '/^\\>/'),
|
2752 |
+
new wfWAFLexerTokenMatcher(self::SET_VAR, '/^:\\=/'),
|
2753 |
+
new wfWAFLexerTokenMatcher(self::BIT_XOR, '/^\\^/'),
|
2754 |
+
new wfWAFLexerTokenMatcher(self::BIT_INVERSION, '/^\\~/'),
|
2755 |
+
new wfWAFLexerTokenMatcher(self::BIT_OR, '/^\\|/'),
|
2756 |
+
new wfWAFLexerTokenMatcher(self::PLUS, '/^\\+/'),
|
2757 |
+
new wfWAFLexerTokenMatcher(self::MINUS, '/^\\-/'),
|
2758 |
+
new wfWAFLexerTokenMatcher(self::ASTERISK, '/^\\*/'),
|
2759 |
+
new wfWAFLexerTokenMatcher(self::DIVISION, '/^\\//'),
|
2760 |
+
new wfWAFLexerTokenMatcher(self::MOD, '/^%/'),
|
2761 |
+
new wfWAFLexerTokenMatcher(self::EXPR_NOT, '/^\\!/'),
|
2762 |
+
new wfWAFLexerTokenMatcher(self::BIT_AND, '/^&/'),
|
2763 |
+
new wfWAFLexerTokenMatcher(self::RIGHT_BRACKET, '/^\\]/'),
|
2764 |
+
new wfWAFLexerTokenMatcher(self::LEFT_BRACKET, '/^\\[/'),
|
2765 |
+
new wfWAFLexerTokenMatcher(self::SEMICOLON, '/^;/'),
|
2766 |
+
new wfWAFLexerTokenMatcher(self::COLON, '/^:/'),
|
2767 |
+
);
|
2768 |
+
}
|
2769 |
+
return $tokenMatchers;
|
2770 |
+
}
|
2771 |
+
|
2772 |
+
/**
|
2773 |
+
* @var string
|
2774 |
+
*/
|
2775 |
+
private $sql;
|
2776 |
+
|
2777 |
+
/**
|
2778 |
+
* @var wfWAFStringScanner
|
2779 |
+
*/
|
2780 |
+
private $scanner;
|
2781 |
+
|
2782 |
+
/**
|
2783 |
+
* wfWAFRuleLexer constructor.
|
2784 |
+
* @param $sql
|
2785 |
+
* @param int $flags
|
2786 |
+
*/
|
2787 |
+
public function __construct($sql = null, $flags = 0) {
|
2788 |
+
$this->scanner = new wfWAFStringScanner();
|
2789 |
+
$this->tokenMatchers = self::getLexerTokenMatchers();
|
2790 |
+
$this->setSQL($sql);
|
2791 |
+
$this->setFlags($flags);
|
2792 |
+
}
|
2793 |
+
|
2794 |
+
/**
|
2795 |
+
* @return array
|
2796 |
+
* @throws wfWAFParserSyntaxError
|
2797 |
+
*/
|
2798 |
+
public function tokenize() {
|
2799 |
+
$tokens = array();
|
2800 |
+
while ($token = $this->nextToken()) {
|
2801 |
+
$tokens[] = $token;
|
2802 |
+
}
|
2803 |
+
return $tokens;
|
2804 |
+
}
|
2805 |
+
|
2806 |
+
/**
|
2807 |
+
* @return bool|wfWAFLexerToken
|
2808 |
+
* @throws wfWAFParserSyntaxError
|
2809 |
+
*/
|
2810 |
+
public function nextToken() {
|
2811 |
+
if (!$this->scanner->eos()) {
|
2812 |
+
/** @var wfWAFLexerTokenMatcher $tokenMatcher */
|
2813 |
+
foreach ($this->tokenMatchers as $tokenMatcher) {
|
2814 |
+
$this->scanner->skip('/^\s+/s');
|
2815 |
+
if ($this->scanner->eos()) {
|
2816 |
+
return false;
|
2817 |
+
}
|
2818 |
+
|
2819 |
+
if (($this->flags & self::FLAG_TOKENIZE_MYSQL_PORTABLE_COMMENTS) === 0 &&
|
2820 |
+
($tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_START ||
|
2821 |
+
$tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_END)
|
2822 |
+
) {
|
2823 |
+
continue;
|
2824 |
+
}
|
2825 |
+
if (!$this->hasPortableCommentStart && $tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_END) {
|
2826 |
+
continue;
|
2827 |
+
}
|
2828 |
+
|
2829 |
+
if ($tokenMatcher->useMaximalMunch() && ($match = $this->scanner->check($tokenMatcher->getMatch())) !== null) {
|
2830 |
+
$biggestToken = $this->createToken($tokenMatcher->getTokenID(), $match);
|
2831 |
+
/** @var wfWAFLexerTokenMatcher $tokenMatcher2 */
|
2832 |
+
foreach ($this->tokenMatchers as $tokenMatcher2) {
|
2833 |
+
if ($tokenMatcher === $tokenMatcher2) {
|
2834 |
+
continue;
|
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) {
|
2847 |
+
$token = $this->createToken($tokenMatcher->getTokenID(), $match);
|
2848 |
+
if ($tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_START) {
|
2849 |
+
$this->hasPortableCommentStart = true;
|
2850 |
+
} else if ($tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_END) {
|
2851 |
+
$this->hasPortableCommentStart = false;
|
2852 |
+
}
|
2853 |
+
return $token;
|
2854 |
+
}
|
2855 |
+
}
|
2856 |
+
$char = $this->scanner->scanChar();
|
2857 |
+
$e = new wfWAFParserSyntaxError(sprintf('Invalid character "%s" (\x%02x) found on line %d, column %d',
|
2858 |
+
$char, ord($char), $this->scanner->getLine(), $this->scanner->getColumn()));
|
2859 |
+
$e->setParseLine($this->scanner->getLine());
|
2860 |
+
$e->setParseColumn($this->scanner->getColumn());
|
2861 |
+
throw $e;
|
2862 |
+
}
|
2863 |
+
return false;
|
2864 |
+
}
|
2865 |
+
|
2866 |
+
public function hasMoreTokens() {
|
2867 |
+
|
2868 |
+
}
|
2869 |
+
|
2870 |
+
/**
|
2871 |
+
* @param $type
|
2872 |
+
* @param $value
|
2873 |
+
* @return wfWAFLexerToken
|
2874 |
+
*/
|
2875 |
+
protected function createToken($type, $value) {
|
2876 |
+
return new wfWAFLexerToken($type, $value, $this->scanner->getLine(), $this->scanner->getColumn());
|
2877 |
+
}
|
2878 |
+
|
2879 |
+
/**
|
2880 |
+
* @return string
|
2881 |
+
*/
|
2882 |
+
public function getSQL() {
|
2883 |
+
return $this->sql;
|
2884 |
+
}
|
2885 |
+
|
2886 |
+
/**
|
2887 |
+
* @param string $sql
|
2888 |
+
*/
|
2889 |
+
public function setSQL($sql) {
|
2890 |
+
if (is_string($sql)) {
|
2891 |
+
$this->scanner->setString($sql);
|
2892 |
+
}
|
2893 |
+
$this->sql = $sql;
|
2894 |
+
}
|
2895 |
+
|
2896 |
+
/**
|
2897 |
+
* @return int
|
2898 |
+
*/
|
2899 |
+
public function getFlags() {
|
2900 |
+
return $this->flags;
|
2901 |
+
}
|
2902 |
+
|
2903 |
+
/**
|
2904 |
+
* @param int $flags
|
2905 |
+
*/
|
2906 |
+
public function setFlags($flags) {
|
2907 |
+
$this->flags = $flags;
|
2908 |
+
}
|
2909 |
+
}
|
2910 |
+
|
2911 |
+
class wfWAFLexerTokenMatcher {
|
2912 |
+
/**
|
2913 |
+
* @var mixed
|
2914 |
+
*/
|
2915 |
+
private $tokenID;
|
2916 |
+
/**
|
2917 |
+
* @var string
|
2918 |
+
*/
|
2919 |
+
private $match;
|
2920 |
+
/**
|
2921 |
+
* @var bool
|
2922 |
+
*/
|
2923 |
+
private $useMaximalMunch;
|
2924 |
+
|
2925 |
+
|
2926 |
+
/**
|
2927 |
+
* @param mixed $tokenID
|
2928 |
+
* @param string $match
|
2929 |
+
* @param bool $useMaximalMunch
|
2930 |
+
*/
|
2931 |
+
public function __construct($tokenID, $match, $useMaximalMunch = false) {
|
2932 |
+
$this->tokenID = $tokenID;
|
2933 |
+
$this->match = $match;
|
2934 |
+
$this->useMaximalMunch = $useMaximalMunch;
|
2935 |
+
}
|
2936 |
+
|
2937 |
+
/**
|
2938 |
+
* @return bool
|
2939 |
+
*/
|
2940 |
+
public function useMaximalMunch() {
|
2941 |
+
return $this->useMaximalMunch;
|
2942 |
+
}
|
2943 |
+
|
2944 |
+
/**
|
2945 |
+
* @return mixed
|
2946 |
+
*/
|
2947 |
+
public function getTokenID() {
|
2948 |
+
return $this->tokenID;
|
2949 |
+
}
|
2950 |
+
|
2951 |
+
/**
|
2952 |
+
* @param mixed $tokenID
|
2953 |
+
*/
|
2954 |
+
public function setTokenID($tokenID) {
|
2955 |
+
$this->tokenID = $tokenID;
|
2956 |
+
}
|
2957 |
+
|
2958 |
+
/**
|
2959 |
+
* @return string
|
2960 |
+
*/
|
2961 |
+
public function getMatch() {
|
2962 |
+
return $this->match;
|
2963 |
+
}
|
2964 |
+
|
2965 |
+
/**
|
2966 |
+
* @param string $match
|
2967 |
+
*/
|
2968 |
+
public function setMatch($match) {
|
2969 |
+
$this->match = $match;
|
2970 |
+
}
|
2971 |
+
}
|
vendor/wordfence/wf-waf/src/lib/request.php
ADDED
@@ -0,0 +1,816 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
interface wfWAFRequestInterface {
|
4 |
+
|
5 |
+
public function getBody();
|
6 |
+
|
7 |
+
public function getQueryString();
|
8 |
+
|
9 |
+
public function getHeaders();
|
10 |
+
|
11 |
+
public function getCookies();
|
12 |
+
|
13 |
+
public function getFiles();
|
14 |
+
|
15 |
+
public function getFileNames();
|
16 |
+
|
17 |
+
public function getHost();
|
18 |
+
|
19 |
+
public function getURI();
|
20 |
+
|
21 |
+
public function getPath();
|
22 |
+
|
23 |
+
public function getIP();
|
24 |
+
|
25 |
+
public function getMethod();
|
26 |
+
|
27 |
+
public function getProtocol();
|
28 |
+
|
29 |
+
public function getAuth();
|
30 |
+
|
31 |
+
public function getTimestamp();
|
32 |
+
|
33 |
+
public function __toString();
|
34 |
+
|
35 |
+
}
|
36 |
+
|
37 |
+
|
38 |
+
class wfWAFRequest implements wfWAFRequestInterface {
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @param string $requestString
|
42 |
+
* @return wfWAFRequest
|
43 |
+
*/
|
44 |
+
public static function parseString($requestString) {
|
45 |
+
if (!is_string($requestString)) {
|
46 |
+
throw new InvalidArgumentException(__METHOD__ . ' expects a string for first parameter, recieved ' . gettype($requestString));
|
47 |
+
}
|
48 |
+
|
49 |
+
if (version_compare(phpversion(), '5.3.0') > 0) {
|
50 |
+
$class = get_called_class();
|
51 |
+
$request = new $class();
|
52 |
+
} else {
|
53 |
+
$request = new self();
|
54 |
+
}
|
55 |
+
|
56 |
+
$request->setAuth(array());
|
57 |
+
$request->setBody(array());
|
58 |
+
$request->setCookies(array());
|
59 |
+
$request->setFileNames(array());
|
60 |
+
$request->setFiles(array());
|
61 |
+
$request->setHeaders(array());
|
62 |
+
$request->setHost('');
|
63 |
+
$request->setIP('');
|
64 |
+
$request->setMethod('');
|
65 |
+
$request->setPath('');
|
66 |
+
$request->setProtocol('');
|
67 |
+
$request->setQueryString(array());
|
68 |
+
$request->setTimestamp('');
|
69 |
+
$request->setURI('');
|
70 |
+
|
71 |
+
list($headersString, $bodyString) = explode("\n\n", $requestString, 2);
|
72 |
+
$headersString = trim($headersString);
|
73 |
+
$bodyString = trim($bodyString);
|
74 |
+
$headers = explode("\n", $headersString);
|
75 |
+
// Assume first is method
|
76 |
+
if (preg_match('/^([a-z]+) (.*?) HTTP\/1.[0-9]/i', $headers[0], $matches)) {
|
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);
|
89 |
+
}
|
90 |
+
}
|
91 |
+
$kvHeaders = array();
|
92 |
+
for ($i = 1; $i < count($headers); $i++) {
|
93 |
+
$headerString = $headers[$i];
|
94 |
+
list($header, $headerValue) = explode(':', $headerString, 2);
|
95 |
+
$header = trim($header);
|
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);
|
103 |
+
$auth['user'] = $authUser;
|
104 |
+
$auth['password'] = $authPass;
|
105 |
+
$request->setAuth($auth);
|
106 |
+
}
|
107 |
+
break;
|
108 |
+
|
109 |
+
case 'host':
|
110 |
+
$request->setHost($headerValue);
|
111 |
+
break;
|
112 |
+
|
113 |
+
case 'cookie':
|
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 |
+
}
|
121 |
+
}
|
122 |
+
$request->setCookies($cookieArray);
|
123 |
+
break;
|
124 |
+
}
|
125 |
+
|
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();
|
133 |
+
$fileNames = array();
|
134 |
+
|
135 |
+
$boundary = $boundaryMatches[1];
|
136 |
+
$bodyChunks = explode("--$boundary", $bodyString);
|
137 |
+
foreach ($bodyChunks as $chunk) {
|
138 |
+
if (!$chunk || $chunk == '--') {
|
139 |
+
continue;
|
140 |
+
}
|
141 |
+
|
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);
|
152 |
+
switch ($chunkHeaderKey) {
|
153 |
+
case 'Content-Disposition':
|
154 |
+
$dataAttributes = explode(';', $chunkHeaderValue);
|
155 |
+
foreach ($dataAttributes as $attr) {
|
156 |
+
$attr = trim($attr);
|
157 |
+
if (preg_match('/^name="(.*?)"$/i', $attr, $attrMatch)) {
|
158 |
+
$param['name'] = $attrMatch[1];
|
159 |
+
continue;
|
160 |
+
}
|
161 |
+
if (preg_match('/^filename="(.*?)"$/i', $attr, $attrMatch)) {
|
162 |
+
$param['filename'] = $attrMatch[1];
|
163 |
+
continue;
|
164 |
+
}
|
165 |
+
}
|
166 |
+
break;
|
167 |
+
case 'Content-Type':
|
168 |
+
$param['type'] = $chunkHeaderValue;
|
169 |
+
break;
|
170 |
+
}
|
171 |
+
}
|
172 |
+
}
|
173 |
+
if (array_key_exists('name', $param)) {
|
174 |
+
if (array_key_exists('filename', $param)) {
|
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'];
|
182 |
+
} else {
|
183 |
+
$body .= urlencode($param['name']) . '=' . urlencode($param['value']) . '&';
|
184 |
+
}
|
185 |
+
}
|
186 |
+
}
|
187 |
+
|
188 |
+
if ($body) {
|
189 |
+
parse_str($body, $postBody);
|
190 |
+
if (is_array($postBody)) {
|
191 |
+
$request->setBody($postBody);
|
192 |
+
} else {
|
193 |
+
$request->setBody($body);
|
194 |
+
}
|
195 |
+
}
|
196 |
+
if ($files) {
|
197 |
+
$request->setFiles($files);
|
198 |
+
}
|
199 |
+
if ($fileNames) {
|
200 |
+
$request->setFileNames($fileNames);
|
201 |
+
}
|
202 |
+
|
203 |
+
} else {
|
204 |
+
parse_str($bodyString, $postBody);
|
205 |
+
if (is_array($postBody)) {
|
206 |
+
$request->setBody($postBody);
|
207 |
+
} else {
|
208 |
+
$request->setBody($bodyString);
|
209 |
+
}
|
210 |
+
}
|
211 |
+
}
|
212 |
+
|
213 |
+
return $request;
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* @param wfWAFRequest|null $request
|
218 |
+
* @return wfWAFRequest
|
219 |
+
*/
|
220 |
+
public static function createFromGlobals($request = null) {
|
221 |
+
if ($request === null) {
|
222 |
+
if (version_compare(phpversion(), '5.3.0') > 0) {
|
223 |
+
$class = get_called_class();
|
224 |
+
$request = new $class();
|
225 |
+
} else {
|
226 |
+
$request = new self();
|
227 |
+
}
|
228 |
+
}
|
229 |
+
|
230 |
+
$request->setAuth(array());
|
231 |
+
$request->setCookies(array());
|
232 |
+
$request->setFileNames(array());
|
233 |
+
$request->setFiles(array());
|
234 |
+
$request->setHeaders(array());
|
235 |
+
$request->setHost('');
|
236 |
+
$request->setIP('');
|
237 |
+
$request->setMethod('');
|
238 |
+
$request->setPath('');
|
239 |
+
$request->setProtocol('');
|
240 |
+
$request->setTimestamp('');
|
241 |
+
$request->setURI('');
|
242 |
+
|
243 |
+
$request->setBody(wfWAFUtils::stripMagicQuotes($_POST));
|
244 |
+
$request->setQueryString(wfWAFUtils::stripMagicQuotes($_GET));
|
245 |
+
$request->setCookies(wfWAFUtils::stripMagicQuotes($_COOKIE));
|
246 |
+
$request->setFiles(wfWAFUtils::stripMagicQuotes($_FILES));
|
247 |
+
|
248 |
+
if (!empty($_FILES)) {
|
249 |
+
$fileNames = array();
|
250 |
+
foreach ($_FILES as $input => $file) {
|
251 |
+
$fileNames[$input] = wfWAFUtils::stripMagicQuotes($file['name']);
|
252 |
+
}
|
253 |
+
$request->setFileNames($fileNames);
|
254 |
+
}
|
255 |
+
$auth = array();
|
256 |
+
if (array_key_exists('PHP_AUTH_USER', $_SERVER)) {
|
257 |
+
$auth['user'] = wfWAFUtils::stripMagicQuotes($_SERVER['PHP_AUTH_USER']);
|
258 |
+
}
|
259 |
+
if (array_key_exists('PHP_AUTH_PW', $_SERVER)) {
|
260 |
+
$auth['password'] = wfWAFUtils::stripMagicQuotes($_SERVER['PHP_AUTH_PW']);
|
261 |
+
}
|
262 |
+
$request->setAuth($auth);
|
263 |
+
|
264 |
+
if (array_key_exists('REQUEST_TIME_FLOAT', $_SERVER)) {
|
265 |
+
$timestamp = $_SERVER['REQUEST_TIME_FLOAT'];
|
266 |
+
} else if (array_key_exists('REQUEST_TIME', $_SERVER)) {
|
267 |
+
$timestamp = $_SERVER['REQUEST_TIME'];
|
268 |
+
} else {
|
269 |
+
$timestamp = time();
|
270 |
+
}
|
271 |
+
$request->setTimestamp($timestamp);
|
272 |
+
|
273 |
+
$headers = array();
|
274 |
+
if (!empty($_SERVER)) {
|
275 |
+
foreach ($_SERVER as $key => $value) {
|
276 |
+
if (strpos($key, 'HTTP_') === 0) {
|
277 |
+
$header = substr($key, 5);
|
278 |
+
$header = str_replace(array(' ', '_'), array('', ' '), $header);
|
279 |
+
$header = ucwords(strtolower($header));
|
280 |
+
$header = str_replace(' ', '-', $header);
|
281 |
+
$headers[$header] = wfWAFUtils::stripMagicQuotes($value);
|
282 |
+
}
|
283 |
+
}
|
284 |
+
if (array_key_exists('CONTENT_TYPE', $_SERVER)) {
|
285 |
+
$headers['Content-Type'] = wfWAFUtils::stripMagicQuotes($_SERVER['CONTENT_TYPE']);
|
286 |
+
}
|
287 |
+
if (array_key_exists('CONTENT_LENGTH', $_SERVER)) {
|
288 |
+
$headers['Content-Length'] = wfWAFUtils::stripMagicQuotes($_SERVER['CONTENT_LENGTH']);
|
289 |
+
}
|
290 |
+
}
|
291 |
+
$request->setHeaders($headers);
|
292 |
+
|
293 |
+
$host = '';
|
294 |
+
if (array_key_exists('Host', $headers)) {
|
295 |
+
$host = $headers['Host'];
|
296 |
+
} else if (array_key_exists('SERVER_NAME', $_SERVER)) {
|
297 |
+
$host = wfWAFUtils::stripMagicQuotes($_SERVER['SERVER_NAME']);
|
298 |
+
}
|
299 |
+
$request->setHost($host);
|
300 |
+
|
301 |
+
$request->setMethod(array_key_exists('REQUEST_METHOD', $_SERVER) ? wfWAFUtils::stripMagicQuotes($_SERVER['REQUEST_METHOD']) : 'GET');
|
302 |
+
$request->setProtocol((array_key_exists('HTTPS', $_SERVER) && $_SERVER['HTTPS'] && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http');
|
303 |
+
$request->setUri(array_key_exists('REQUEST_URI', $_SERVER) ? wfWAFUtils::stripMagicQuotes($_SERVER['REQUEST_URI']) : '');
|
304 |
+
|
305 |
+
$uri = parse_url($request->getURI());
|
306 |
+
if (is_array($uri) && array_key_exists('path', $uri)) {
|
307 |
+
$path = $uri['path'];
|
308 |
+
} else {
|
309 |
+
$path = $request->getURI();
|
310 |
+
}
|
311 |
+
$request->setPath($path);
|
312 |
+
|
313 |
+
return $request;
|
314 |
+
}
|
315 |
+
|
316 |
+
private $auth;
|
317 |
+
private $body;
|
318 |
+
private $cookies;
|
319 |
+
private $fileNames;
|
320 |
+
private $files;
|
321 |
+
private $headers;
|
322 |
+
private $host;
|
323 |
+
private $ip;
|
324 |
+
private $method;
|
325 |
+
private $path;
|
326 |
+
private $protocol;
|
327 |
+
private $queryString;
|
328 |
+
private $timestamp;
|
329 |
+
private $uri;
|
330 |
+
|
331 |
+
private $highlightParamFormat;
|
332 |
+
private $highlightMatchFormat;
|
333 |
+
private $highlightMatches;
|
334 |
+
private $highlightMatchFilter = 'urlencode';
|
335 |
+
|
336 |
+
|
337 |
+
protected function _arrayValueByKeys($global, $key) {
|
338 |
+
if (is_array($global)) {
|
339 |
+
if (is_array($key)) {
|
340 |
+
$_key = array_shift($key);
|
341 |
+
if (array_key_exists($_key, $global)) {
|
342 |
+
if (count($key) > 0) {
|
343 |
+
return $this->_arrayValueByKeys($global[$_key], $key);
|
344 |
+
} else {
|
345 |
+
return $global[$_key];
|
346 |
+
}
|
347 |
+
}
|
348 |
+
} else {
|
349 |
+
return array_key_exists($key, $global) ? $global[$key] : null;
|
350 |
+
}
|
351 |
+
}
|
352 |
+
return null;
|
353 |
+
}
|
354 |
+
|
355 |
+
public function getBody() {
|
356 |
+
if (func_num_args() > 0) {
|
357 |
+
$args = func_get_args();
|
358 |
+
return $this->_arrayValueByKeys($this->body, $args);
|
359 |
+
}
|
360 |
+
return $this->body;
|
361 |
+
}
|
362 |
+
|
363 |
+
public function getQueryString() {
|
364 |
+
if (func_num_args() > 0) {
|
365 |
+
$args = func_get_args();
|
366 |
+
return $this->_arrayValueByKeys($this->queryString, $args);
|
367 |
+
}
|
368 |
+
return $this->queryString;
|
369 |
+
}
|
370 |
+
|
371 |
+
public function getHeaders() {
|
372 |
+
if (func_num_args() > 0) {
|
373 |
+
$args = func_get_args();
|
374 |
+
return $this->_arrayValueByKeys($this->headers, $args);
|
375 |
+
}
|
376 |
+
return $this->headers;
|
377 |
+
}
|
378 |
+
|
379 |
+
public function getCookies() {
|
380 |
+
if (func_num_args() > 0) {
|
381 |
+
$args = func_get_args();
|
382 |
+
return $this->_arrayValueByKeys($this->cookies, $args);
|
383 |
+
}
|
384 |
+
return $this->cookies;
|
385 |
+
}
|
386 |
+
|
387 |
+
public function getFiles() {
|
388 |
+
if (func_num_args() > 0) {
|
389 |
+
$args = func_get_args();
|
390 |
+
return $this->_arrayValueByKeys($this->files, $args);
|
391 |
+
}
|
392 |
+
return $this->files;
|
393 |
+
}
|
394 |
+
|
395 |
+
public function getFileNames() {
|
396 |
+
if (func_num_args() > 0) {
|
397 |
+
$args = func_get_args();
|
398 |
+
return $this->_arrayValueByKeys($this->fileNames, $args);
|
399 |
+
}
|
400 |
+
return $this->fileNames;
|
401 |
+
}
|
402 |
+
|
403 |
+
public function getHost() {
|
404 |
+
return $this->host;
|
405 |
+
}
|
406 |
+
|
407 |
+
public function getURI() {
|
408 |
+
return $this->uri;
|
409 |
+
}
|
410 |
+
|
411 |
+
public function getPath() {
|
412 |
+
return $this->path;
|
413 |
+
}
|
414 |
+
|
415 |
+
public function getIP() {
|
416 |
+
return $this->ip;
|
417 |
+
}
|
418 |
+
|
419 |
+
public function getMethod() {
|
420 |
+
return $this->method;
|
421 |
+
}
|
422 |
+
|
423 |
+
public function getProtocol() {
|
424 |
+
return $this->protocol;
|
425 |
+
}
|
426 |
+
|
427 |
+
public function getAuth($arg1 = null) {
|
428 |
+
if ($arg1) {
|
429 |
+
if (is_array($this->auth) && array_key_exists($arg1, $this->auth)) {
|
430 |
+
return $this->auth[$arg1];
|
431 |
+
}
|
432 |
+
return null;
|
433 |
+
}
|
434 |
+
return $this->auth;
|
435 |
+
}
|
436 |
+
|
437 |
+
public function getTimestamp() {
|
438 |
+
return $this->timestamp;
|
439 |
+
}
|
440 |
+
|
441 |
+
public function __toString() {
|
442 |
+
return $this->highlightFailedParams();
|
443 |
+
}
|
444 |
+
|
445 |
+
/**
|
446 |
+
* @param array $failedParams
|
447 |
+
* @param string $highlightParamFormat
|
448 |
+
* @param string $highlightMatchFormat
|
449 |
+
* @return string
|
450 |
+
*/
|
451 |
+
public function highlightFailedParams($failedParams = array(), $highlightParamFormat = '[param]%s[/param]',
|
452 |
+
$highlightMatchFormat = '[match]%s[/match]') {
|
453 |
+
$highlights = array();
|
454 |
+
|
455 |
+
// Cap at 50kb
|
456 |
+
$maxRequestLen = 1024 * 50;
|
457 |
+
|
458 |
+
$this->highlightParamFormat = $highlightParamFormat;
|
459 |
+
$this->highlightMatchFormat = $highlightMatchFormat;
|
460 |
+
|
461 |
+
if (is_array($failedParams)) {
|
462 |
+
foreach ($failedParams as $paramKey => $categories) {
|
463 |
+
foreach ($categories as $categoryKey => $failedRules) {
|
464 |
+
foreach ($failedRules as $failedRule) {
|
465 |
+
$rule = $failedRule['rule'];
|
466 |
+
/** @var wfWAFRuleComparisonFailure $failedComparison */
|
467 |
+
$failedComparison = $failedRule['failedComparison'];
|
468 |
+
$action = $failedRule['action'];
|
469 |
+
|
470 |
+
$paramKey = $failedComparison->getParamKey();
|
471 |
+
if (preg_match('/request\.([a-z0-9]+)(?:\[(.*?)\](.*?))?$/i', $paramKey, $matches)) {
|
472 |
+
$global = $matches[1];
|
473 |
+
if (method_exists('wfWAFRequestInterface', "get" . ucfirst($global))) {
|
474 |
+
$highlight = array(
|
475 |
+
'match' => $failedComparison->getMatches(),
|
476 |
+
);
|
477 |
+
if (isset($matches[2])) {
|
478 |
+
$highlight['param'] = "$matches[2]$matches[3]";
|
479 |
+
}
|
480 |
+
$highlights[$global][] = $highlight;
|
481 |
+
}
|
482 |
+
}
|
483 |
+
}
|
484 |
+
}
|
485 |
+
}
|
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) {
|
495 |
+
$uri .= '?' . http_build_query($queryString);
|
496 |
+
}
|
497 |
+
if (!empty($highlights['queryString'])) {
|
498 |
+
foreach ($highlights['queryString'] as $matches) {
|
499 |
+
if (!empty($matches['param'])) {
|
500 |
+
$this->highlightMatches = $matches['match'];
|
501 |
+
$uri = preg_replace_callback('/(&|\?|^)(' . preg_quote(urlencode($matches['param']), '/') . ')=(.*?)(&|$)/', array(
|
502 |
+
$this, 'highlightParam',
|
503 |
+
), $uri);
|
504 |
+
}
|
505 |
+
}
|
506 |
+
}
|
507 |
+
|
508 |
+
if (!empty($highlights['uri'])) {
|
509 |
+
foreach ($highlights['uri'] as $matches) {
|
510 |
+
if ($matches) {
|
511 |
+
|
512 |
+
}
|
513 |
+
}
|
514 |
+
$uri = sprintf($highlightParamFormat, $uri);
|
515 |
+
}
|
516 |
+
|
517 |
+
$request = "{$this->getMethod()} $uri HTTP/1.1\n";
|
518 |
+
$hasAuth = false;
|
519 |
+
$auth = $this->getAuth();
|
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 = '';
|
527 |
+
foreach ($this->getCookies() as $cookieName => $cookieValue) {
|
528 |
+
$cookies .= $cookieName . '=' . urlencode($cookieValue) . '; ';
|
529 |
+
}
|
530 |
+
$request .= 'Cookie: ' . trim($cookies) . "\n";
|
531 |
+
break;
|
532 |
+
|
533 |
+
case 'host':
|
534 |
+
$request .= 'Host: ' . $this->getHost() . "\n";
|
535 |
+
break;
|
536 |
+
|
537 |
+
case 'authorization':
|
538 |
+
$hasAuth = true;
|
539 |
+
if ($auth) {
|
540 |
+
$request .= 'Authorization: Basic ' . base64_encode($auth['user'] . ':' . $auth['password']) . "\n";
|
541 |
+
}
|
542 |
+
break;
|
543 |
+
|
544 |
+
default:
|
545 |
+
$request .= $header . ': ' . $value . "\n";
|
546 |
+
break;
|
547 |
+
}
|
548 |
+
}
|
549 |
+
}
|
550 |
+
|
551 |
+
if (!$hasAuth && $auth) {
|
552 |
+
$request .= 'Authorization: Basic ' . base64_encode($auth['user'] . ':' . $auth['password']) . "\n";
|
553 |
+
}
|
554 |
+
|
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) {
|
562 |
+
if (!empty($matches['param'])) {
|
563 |
+
$this->highlightMatches = $matches['match'];
|
564 |
+
$body = preg_replace_callback('/(&|^)(' . preg_quote(urlencode($matches['param']), '/') . ')=(.*?)(&|$)/', array(
|
565 |
+
$this, 'highlightParam',
|
566 |
+
), $body);
|
567 |
+
}
|
568 |
+
}
|
569 |
+
}
|
570 |
+
} else if (preg_match('/^multipart\/form\-data; boundary=(.*?)$/i', $contentType, $boundaryMatches)) {
|
571 |
+
$boundary = $boundaryMatches[1];
|
572 |
+
$bodyArray = array();
|
573 |
+
foreach ($body as $key => $value) {
|
574 |
+
$bodyArray = array_merge($bodyArray, $this->reduceBodyParameter($key, $value));
|
575 |
+
}
|
576 |
+
$body = '';
|
577 |
+
foreach ($bodyArray as $param => $value) {
|
578 |
+
if (!empty($highlights['body'])) {
|
579 |
+
foreach ($highlights['body'] as $matches) {
|
580 |
+
if (!empty($matches['param']) && $matches['param'] === $param) {
|
581 |
+
$value = sprintf($this->highlightParamFormat, $value);
|
582 |
+
if (is_array($matches['match'][0])) {
|
583 |
+
$replace = array();
|
584 |
+
foreach ($matches['match'][0] as $key => $match) {
|
585 |
+
$replace[$match] = sprintf($this->highlightMatchFormat, $match);
|
586 |
+
}
|
587 |
+
if ($replace) {
|
588 |
+
$value = str_replace(array_keys($replace), $replace, $value);
|
589 |
+
}
|
590 |
+
} else { // preg_match
|
591 |
+
$value = str_replace($matches['match'][0], sprintf($this->highlightMatchFormat, $matches['match'][0]), $value);
|
592 |
+
}
|
593 |
+
break;
|
594 |
+
}
|
595 |
+
}
|
596 |
+
}
|
597 |
+
|
598 |
+
$body .= <<<FORM
|
599 |
+
--$boundary
|
600 |
+
Content-Disposition: form-data; name="$param"
|
601 |
+
|
602 |
+
$value
|
603 |
+
|
604 |
+
FORM;
|
605 |
+
}
|
606 |
+
|
607 |
+
foreach ($this->getFiles() as $param => $file) {
|
608 |
+
$name = array_key_exists('name', $file) ? $file['name'] : '';
|
609 |
+
if (is_array($name)) {
|
610 |
+
continue; // TODO: implement files as arrays
|
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'])) {
|
618 |
+
$handle = fopen($file['tmp_name'], 'r');
|
619 |
+
$value = fread($handle, $lenToRead);
|
620 |
+
fclose($handle);
|
621 |
+
}
|
622 |
+
|
623 |
+
if (!empty($highlights['fileNames'])) {
|
624 |
+
foreach ($highlights['fileNames'] as $matches) {
|
625 |
+
if (!empty($matches['param']) && $matches['param'] === $param) {
|
626 |
+
$name = sprintf($this->highlightParamFormat, $name);
|
627 |
+
$name = str_replace($matches['match'][0], sprintf($this->highlightMatchFormat, $matches['match'][0]), $name);
|
628 |
+
break;
|
629 |
+
}
|
630 |
+
}
|
631 |
+
}
|
632 |
+
|
633 |
+
$body .= <<<FORM
|
634 |
+
--$boundary
|
635 |
+
Content-Disposition: form-data; name="$param"; filename="$name"
|
636 |
+
Content-Type: $mime
|
637 |
+
Expires: 0
|
638 |
+
|
639 |
+
$value
|
640 |
+
|
641 |
+
FORM;
|
642 |
+
}
|
643 |
+
|
644 |
+
if ($body) {
|
645 |
+
$body .= "--$boundary--\n";
|
646 |
+
}
|
647 |
+
}
|
648 |
+
}
|
649 |
+
if (!is_string($body)) {
|
650 |
+
$body = '';
|
651 |
+
}
|
652 |
+
|
653 |
+
$request .= "\n" . $body;
|
654 |
+
|
655 |
+
if (strlen($request) > $maxRequestLen) {
|
656 |
+
$request = substr($request, 0, $maxRequestLen);
|
657 |
+
}
|
658 |
+
return $request;
|
659 |
+
}
|
660 |
+
|
661 |
+
/**
|
662 |
+
* @param array $matches
|
663 |
+
* @return string
|
664 |
+
*/
|
665 |
+
private function highlightParam($matches) {
|
666 |
+
$value = '';
|
667 |
+
if (is_array($this->highlightMatches)) {
|
668 |
+
// preg_match_all
|
669 |
+
if (is_array($this->highlightMatches[0])) {
|
670 |
+
$value = $matches[3];
|
671 |
+
$replace = array();
|
672 |
+
foreach ($this->highlightMatches[0] as $key => $match) {
|
673 |
+
$this->highlightMatches[0][$key] = $this->callHighlightMatchFilter($match);
|
674 |
+
$replace[] = sprintf($this->highlightMatchFormat, $this->callHighlightMatchFilter($match));
|
675 |
+
}
|
676 |
+
if ($replace) {
|
677 |
+
$value = str_replace($this->highlightMatches[0], $replace, $value);
|
678 |
+
}
|
679 |
+
|
680 |
+
} else { // preg_match
|
681 |
+
$param = $this->callHighlightMatchFilter($this->highlightMatches[0]);
|
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 |
+
|
689 |
+
return $matches[1] . sprintf($this->highlightParamFormat, $matches[2] . '=' . $value) . $matches[4];
|
690 |
+
}
|
691 |
+
|
692 |
+
/**
|
693 |
+
* @param $match
|
694 |
+
* @return mixed
|
695 |
+
*/
|
696 |
+
private function callHighlightMatchFilter($match) {
|
697 |
+
return is_callable($this->highlightMatchFilter) ? call_user_func($this->highlightMatchFilter, $match) : $match;
|
698 |
+
}
|
699 |
+
|
700 |
+
/**
|
701 |
+
* @param string $key
|
702 |
+
* @param string|array $value
|
703 |
+
* @return array
|
704 |
+
*/
|
705 |
+
private function reduceBodyParameter($key, $value) {
|
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 |
+
}
|
713 |
+
return array(
|
714 |
+
$key => $value,
|
715 |
+
);
|
716 |
+
}
|
717 |
+
|
718 |
+
/**
|
719 |
+
* @param mixed $auth
|
720 |
+
*/
|
721 |
+
public function setAuth($auth) {
|
722 |
+
$this->auth = $auth;
|
723 |
+
}
|
724 |
+
|
725 |
+
/**
|
726 |
+
* @param mixed $body
|
727 |
+
*/
|
728 |
+
public function setBody($body) {
|
729 |
+
$this->body = $body;
|
730 |
+
}
|
731 |
+
|
732 |
+
/**
|
733 |
+
* @param mixed $cookies
|
734 |
+
*/
|
735 |
+
public function setCookies($cookies) {
|
736 |
+
$this->cookies = $cookies;
|
737 |
+
}
|
738 |
+
|
739 |
+
/**
|
740 |
+
* @param mixed $fileNames
|
741 |
+
*/
|
742 |
+
public function setFileNames($fileNames) {
|
743 |
+
$this->fileNames = $fileNames;
|
744 |
+
}
|
745 |
+
|
746 |
+
/**
|
747 |
+
* @param mixed $files
|
748 |
+
*/
|
749 |
+
public function setFiles($files) {
|
750 |
+
$this->files = $files;
|
751 |
+
}
|
752 |
+
|
753 |
+
/**
|
754 |
+
* @param mixed $headers
|
755 |
+
*/
|
756 |
+
public function setHeaders($headers) {
|
757 |
+
$this->headers = $headers;
|
758 |
+
}
|
759 |
+
|
760 |
+
/**
|
761 |
+
* @param mixed $host
|
762 |
+
*/
|
763 |
+
public function setHost($host) {
|
764 |
+
$this->host = $host;
|
765 |
+
}
|
766 |
+
|
767 |
+
/**
|
768 |
+
* @param mixed $ip
|
769 |
+
*/
|
770 |
+
public function setIP($ip) {
|
771 |
+
$this->ip = $ip;
|
772 |
+
}
|
773 |
+
|
774 |
+
/**
|
775 |
+
* @param mixed $method
|
776 |
+
*/
|
777 |
+
public function setMethod($method) {
|
778 |
+
$this->method = $method;
|
779 |
+
}
|
780 |
+
|
781 |
+
/**
|
782 |
+
* @param mixed $path
|
783 |
+
*/
|
784 |
+
public function setPath($path) {
|
785 |
+
$this->path = $path;
|
786 |
+
}
|
787 |
+
|
788 |
+
/**
|
789 |
+
* @param mixed $protocol
|
790 |
+
*/
|
791 |
+
public function setProtocol($protocol) {
|
792 |
+
$this->protocol = $protocol;
|
793 |
+
}
|
794 |
+
|
795 |
+
/**
|
796 |
+
* @param mixed $queryString
|
797 |
+
*/
|
798 |
+
public function setQueryString($queryString) {
|
799 |
+
$this->queryString = $queryString;
|
800 |
+
}
|
801 |
+
|
802 |
+
/**
|
803 |
+
* @param mixed $timestamp
|
804 |
+
*/
|
805 |
+
public function setTimestamp($timestamp) {
|
806 |
+
$this->timestamp = $timestamp;
|
807 |
+
}
|
808 |
+
|
809 |
+
/**
|
810 |
+
* @param mixed $uri
|
811 |
+
*/
|
812 |
+
public function setUri($uri) {
|
813 |
+
$this->uri = $uri;
|
814 |
+
}
|
815 |
+
}
|
816 |
+
|
vendor/wordfence/wf-waf/src/lib/rules.php
ADDED
@@ -0,0 +1,1229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
interface wfWAFRuleInterface {
|
4 |
+
|
5 |
+
/**
|
6 |
+
* @return string
|
7 |
+
*/
|
8 |
+
public function render();
|
9 |
+
|
10 |
+
public function renderRule();
|
11 |
+
|
12 |
+
public function evaluate();
|
13 |
+
}
|
14 |
+
|
15 |
+
class wfWAFRuleException extends wfWAFException {
|
16 |
+
}
|
17 |
+
|
18 |
+
class wfWAFRuleLogicalOperatorException extends wfWAFException {
|
19 |
+
}
|
20 |
+
|
21 |
+
class wfWAFRule implements wfWAFRuleInterface {
|
22 |
+
|
23 |
+
private $ruleID;
|
24 |
+
private $type;
|
25 |
+
private $category;
|
26 |
+
private $score;
|
27 |
+
private $description;
|
28 |
+
private $action;
|
29 |
+
private $comparisonGroup;
|
30 |
+
/**
|
31 |
+
* @var wfWAF
|
32 |
+
*/
|
33 |
+
private $waf;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @param wfWAF $waf
|
37 |
+
* @param int $ruleID
|
38 |
+
* @param string $type
|
39 |
+
* @param string $category
|
40 |
+
* @param int $score
|
41 |
+
* @param string $description
|
42 |
+
* @param string $action
|
43 |
+
* @param wfWAFRuleComparisonGroup $comparisonGroup
|
44 |
+
* @return wfWAFRule
|
45 |
+
*/
|
46 |
+
public static function create($waf, $ruleID, $type, $category, $score, $description, $action, $comparisonGroup) {
|
47 |
+
return new self($waf, $ruleID, $type, $category, $score, $description, $action, $comparisonGroup);
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* @param string $value
|
52 |
+
* @return string
|
53 |
+
*/
|
54 |
+
public static function exportString($value) {
|
55 |
+
return sprintf("'%s'", str_replace("'", "\\'", $value));
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* @param wfWAF $waf
|
60 |
+
* @param int $ruleID
|
61 |
+
* @param string $type
|
62 |
+
* @param string $category
|
63 |
+
* @param int $score
|
64 |
+
* @param string $description
|
65 |
+
* @param string $action
|
66 |
+
* @param wfWAFRuleComparisonGroup $comparisonGroup
|
67 |
+
*/
|
68 |
+
public function __construct($waf, $ruleID, $type, $category, $score, $description, $action, $comparisonGroup) {
|
69 |
+
$this->setWAF($waf);
|
70 |
+
$this->setRuleID($ruleID);
|
71 |
+
$this->setType($type);
|
72 |
+
$this->setCategory($category);
|
73 |
+
$this->setScore($score);
|
74 |
+
$this->setDescription($description);
|
75 |
+
$this->setAction($action);
|
76 |
+
$this->setComparisonGroup($comparisonGroup);
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* @return string
|
81 |
+
*/
|
82 |
+
public function render() {
|
83 |
+
return sprintf('%s::create($this, %d, %s, %s, %s, %s, %s, %s)', get_class($this),
|
84 |
+
$this->getRuleID(),
|
85 |
+
var_export($this->getType(), true),
|
86 |
+
var_export($this->getCategory(), true),
|
87 |
+
var_export($this->getScore(), true),
|
88 |
+
var_export($this->getDescription(), true),
|
89 |
+
var_export($this->getAction(), true),
|
90 |
+
$this->getComparisonGroup()->render()
|
91 |
+
);
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* @return string
|
96 |
+
*/
|
97 |
+
public function renderRule() {
|
98 |
+
return sprintf(<<<RULE
|
99 |
+
if %s:
|
100 |
+
%s(%s)
|
101 |
+
RULE
|
102 |
+
,
|
103 |
+
$this->getComparisonGroup()->renderRule(),
|
104 |
+
$this->getAction(),
|
105 |
+
join(', ', array_filter(array(
|
106 |
+
$this->getRuleID() ? 'id=' . (int) $this->getRuleID() : '',
|
107 |
+
$this->getCategory() ? 'category=' . self::exportString($this->getCategory()) : '',
|
108 |
+
$this->getScore() > 0 ? 'score=' . (int) $this->getScore() : '',
|
109 |
+
$this->getCategory() ? 'description=' . self::exportString($this->getDescription()) : '',
|
110 |
+
)))
|
111 |
+
);
|
112 |
+
}
|
113 |
+
|
114 |
+
public function evaluate() {
|
115 |
+
$comparisons = $this->getComparisonGroup();
|
116 |
+
$waf = $this->getWAF();
|
117 |
+
if ($comparisons instanceof wfWAFRuleComparisonGroup && $waf instanceof wfWAF) {
|
118 |
+
$comparisons->setRule($this);
|
119 |
+
if ($comparisons->evaluate()) {
|
120 |
+
$waf->tripRule($this);
|
121 |
+
return true;
|
122 |
+
}
|
123 |
+
}
|
124 |
+
return false;
|
125 |
+
}
|
126 |
+
|
127 |
+
public function debug() {
|
128 |
+
return $this->getComparisonGroup()->debug();
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* For JSON.
|
133 |
+
*
|
134 |
+
* @return array
|
135 |
+
*/
|
136 |
+
public function toArray() {
|
137 |
+
return array(
|
138 |
+
'ruleID' => $this->getRuleID(),
|
139 |
+
'type' => $this->getType(),
|
140 |
+
'category' => $this->getCategory(),
|
141 |
+
'score' => $this->getScore(),
|
142 |
+
'description' => $this->getDescription(),
|
143 |
+
'action' => $this->getAction(),
|
144 |
+
);
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* @return int
|
149 |
+
*/
|
150 |
+
public function getRuleID() {
|
151 |
+
return $this->ruleID;
|
152 |
+
}
|
153 |
+
|
154 |
+
/**
|
155 |
+
* @param int $ruleID
|
156 |
+
*/
|
157 |
+
public function setRuleID($ruleID) {
|
158 |
+
$this->ruleID = $ruleID;
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* @return string
|
163 |
+
*/
|
164 |
+
public function getType() {
|
165 |
+
return $this->type;
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* @param string $type
|
170 |
+
*/
|
171 |
+
public function setType($type) {
|
172 |
+
$this->type = $type;
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* @return string
|
177 |
+
*/
|
178 |
+
public function getCategory() {
|
179 |
+
return $this->category;
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* @param string $category
|
184 |
+
*/
|
185 |
+
public function setCategory($category) {
|
186 |
+
$this->category = $category;
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* @return int
|
191 |
+
*/
|
192 |
+
public function getScore() {
|
193 |
+
return $this->score;
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* @param int $score
|
198 |
+
*/
|
199 |
+
public function setScore($score) {
|
200 |
+
$this->score = $score;
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* @return string
|
205 |
+
*/
|
206 |
+
public function getDescription() {
|
207 |
+
return $this->description;
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* @param string $description
|
212 |
+
*/
|
213 |
+
public function setDescription($description) {
|
214 |
+
$this->description = $description;
|
215 |
+
}
|
216 |
+
|
217 |
+
/**
|
218 |
+
* @return string
|
219 |
+
*/
|
220 |
+
public function getAction() {
|
221 |
+
return $this->action;
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* @param string $action
|
226 |
+
*/
|
227 |
+
public function setAction($action) {
|
228 |
+
$this->action = $action;
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* @return wfWAFRuleComparisonGroup
|
233 |
+
*/
|
234 |
+
public function getComparisonGroup() {
|
235 |
+
return $this->comparisonGroup;
|
236 |
+
}
|
237 |
+
|
238 |
+
/**
|
239 |
+
* @param wfWAFRuleComparisonGroup $comparisonGroup
|
240 |
+
*/
|
241 |
+
public function setComparisonGroup($comparisonGroup) {
|
242 |
+
$this->comparisonGroup = $comparisonGroup;
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* @return wfWAF
|
247 |
+
*/
|
248 |
+
public function getWAF() {
|
249 |
+
return $this->waf;
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* @param wfWAF $waf
|
254 |
+
*/
|
255 |
+
public function setWAF($waf) {
|
256 |
+
$this->waf = $waf;
|
257 |
+
}
|
258 |
+
}
|
259 |
+
|
260 |
+
class wfWAFRuleLogicalOperator implements wfWAFRuleInterface {
|
261 |
+
|
262 |
+
/**
|
263 |
+
* @var string
|
264 |
+
*/
|
265 |
+
private $operator;
|
266 |
+
|
267 |
+
/**
|
268 |
+
* @var array
|
269 |
+
*/
|
270 |
+
protected $validOperators = array(
|
271 |
+
'||',
|
272 |
+
'&&',
|
273 |
+
'and',
|
274 |
+
'or',
|
275 |
+
'xor',
|
276 |
+
);
|
277 |
+
/**
|
278 |
+
* @var bool
|
279 |
+
*/
|
280 |
+
private $currentValue = false;
|
281 |
+
/**
|
282 |
+
* @var wfWAFRuleInterface
|
283 |
+
*/
|
284 |
+
private $comparison;
|
285 |
+
|
286 |
+
/**
|
287 |
+
* @param string $operator
|
288 |
+
* @param bool $currentValue
|
289 |
+
* @param wfWAFRuleInterface $comparison
|
290 |
+
*/
|
291 |
+
public function __construct($operator, $currentValue = false, $comparison = null) {
|
292 |
+
$this->setOperator($operator);
|
293 |
+
$this->setCurrentValue($currentValue);
|
294 |
+
$this->setComparison($comparison);
|
295 |
+
}
|
296 |
+
|
297 |
+
/**
|
298 |
+
* @return string
|
299 |
+
* @throws wfWAFRuleLogicalOperatorException
|
300 |
+
*/
|
301 |
+
public function render() {
|
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 |
+
/**
|
309 |
+
* @return string
|
310 |
+
* @throws wfWAFRuleLogicalOperatorException
|
311 |
+
*/
|
312 |
+
public function renderRule() {
|
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();
|
327 |
+
|
328 |
+
case '||':
|
329 |
+
case 'or':
|
330 |
+
return $currentValue || $comparison->evaluate();
|
331 |
+
|
332 |
+
case 'xor':
|
333 |
+
return $currentValue xor $comparison->evaluate();
|
334 |
+
}
|
335 |
+
}
|
336 |
+
return false;
|
337 |
+
}
|
338 |
+
|
339 |
+
/**
|
340 |
+
* @return bool
|
341 |
+
*/
|
342 |
+
public function isValid() {
|
343 |
+
return in_array(strtolower($this->getOperator()), $this->validOperators);
|
344 |
+
}
|
345 |
+
|
346 |
+
/**
|
347 |
+
* @return string
|
348 |
+
*/
|
349 |
+
public function getOperator() {
|
350 |
+
return $this->operator;
|
351 |
+
}
|
352 |
+
|
353 |
+
/**
|
354 |
+
* @param string $operator
|
355 |
+
*/
|
356 |
+
public function setOperator($operator) {
|
357 |
+
$this->operator = $operator;
|
358 |
+
}
|
359 |
+
|
360 |
+
/**
|
361 |
+
* @return boolean
|
362 |
+
*/
|
363 |
+
public function getCurrentValue() {
|
364 |
+
return $this->currentValue;
|
365 |
+
}
|
366 |
+
|
367 |
+
/**
|
368 |
+
* @param boolean $currentValue
|
369 |
+
*/
|
370 |
+
public function setCurrentValue($currentValue) {
|
371 |
+
$this->currentValue = $currentValue;
|
372 |
+
}
|
373 |
+
|
374 |
+
/**
|
375 |
+
* @return wfWAFRuleInterface
|
376 |
+
*/
|
377 |
+
public function getComparison() {
|
378 |
+
return $this->comparison;
|
379 |
+
}
|
380 |
+
|
381 |
+
/**
|
382 |
+
* @param wfWAFRuleInterface $comparison
|
383 |
+
*/
|
384 |
+
public function setComparison($comparison) {
|
385 |
+
$this->comparison = $comparison;
|
386 |
+
}
|
387 |
+
}
|
388 |
+
|
389 |
+
class wfWAFRuleComparison implements wfWAFRuleInterface {
|
390 |
+
|
391 |
+
private $matches;
|
392 |
+
private $failedSubjects;
|
393 |
+
private $result;
|
394 |
+
/**
|
395 |
+
* @var wfWAFRule
|
396 |
+
*/
|
397 |
+
private $rule;
|
398 |
+
|
399 |
+
protected static $allowedActions = array(
|
400 |
+
'contains',
|
401 |
+
'notcontains',
|
402 |
+
'match',
|
403 |
+
'notmatch',
|
404 |
+
'matchcount',
|
405 |
+
'containscount',
|
406 |
+
'equals',
|
407 |
+
'notequals',
|
408 |
+
'identical',
|
409 |
+
'notidentical',
|
410 |
+
'greaterthan',
|
411 |
+
'greaterthanequalto',
|
412 |
+
'lessthan',
|
413 |
+
'lessthanequalto',
|
414 |
+
'lengthgreaterthan',
|
415 |
+
'lengthlessthan',
|
416 |
+
'currentuseris',
|
417 |
+
'currentuserisnot',
|
418 |
+
);
|
419 |
+
|
420 |
+
/**
|
421 |
+
* @var mixed
|
422 |
+
*/
|
423 |
+
private $expected;
|
424 |
+
/**
|
425 |
+
* @var mixed
|
426 |
+
*/
|
427 |
+
private $subjects;
|
428 |
+
/**
|
429 |
+
* @var string
|
430 |
+
*/
|
431 |
+
private $action;
|
432 |
+
private $multiplier;
|
433 |
+
/**
|
434 |
+
* @var wfWAF
|
435 |
+
*/
|
436 |
+
private $waf;
|
437 |
+
|
438 |
+
/**
|
439 |
+
* @param wfWAF $waf
|
440 |
+
* @param string $action
|
441 |
+
* @param mixed $expected
|
442 |
+
* @param mixed $subjects
|
443 |
+
*/
|
444 |
+
public function __construct($waf, $action, $expected, $subjects = null) {
|
445 |
+
$this->setWAF($waf);
|
446 |
+
$this->setAction($action);
|
447 |
+
$this->setExpected($expected);
|
448 |
+
$this->setSubjects($subjects);
|
449 |
+
}
|
450 |
+
|
451 |
+
/**
|
452 |
+
* @param string|array $subject
|
453 |
+
* @return string
|
454 |
+
*/
|
455 |
+
public static function getSubjectKey($subject) {
|
456 |
+
if (!is_array($subject)) {
|
457 |
+
return (string) $subject;
|
458 |
+
}
|
459 |
+
$return = '';
|
460 |
+
$global = array_shift($subject);
|
461 |
+
foreach ($subject as $key) {
|
462 |
+
$return .= '[' . $key . ']';
|
463 |
+
}
|
464 |
+
return $global . $return;
|
465 |
+
}
|
466 |
+
|
467 |
+
/**
|
468 |
+
* @return string
|
469 |
+
* @throws wfWAFRuleException
|
470 |
+
*/
|
471 |
+
public function render() {
|
472 |
+
if (!$this->isActionValid()) {
|
473 |
+
throw new wfWAFRuleException('Invalid action passed to ' . get_class($this) . ', action: ' . var_export($this->getAction(), true));
|
474 |
+
}
|
475 |
+
$subjectExport = '';
|
476 |
+
/** @var wfWAFRuleComparisonSubject $subject */
|
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),
|
484 |
+
($expected instanceof wfWAFRuleVariable ? $expected->render() : var_export($expected, true)), $subjectExport);
|
485 |
+
}
|
486 |
+
|
487 |
+
/**
|
488 |
+
* @return string
|
489 |
+
* @throws wfWAFRuleException
|
490 |
+
*/
|
491 |
+
public function renderRule() {
|
492 |
+
if (!$this->isActionValid()) {
|
493 |
+
throw new wfWAFRuleException('Invalid action passed to ' . get_class($this) . ', action: ' . var_export($this->getAction(), true));
|
494 |
+
}
|
495 |
+
$subjectExport = '';
|
496 |
+
/** @var wfWAFRuleComparisonSubject $subject */
|
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(),
|
504 |
+
($expected instanceof wfWAFRuleVariable ? $expected->renderRule() : wfWAFRule::exportString($expected)),
|
505 |
+
$subjectExport);
|
506 |
+
}
|
507 |
+
|
508 |
+
public function isActionValid() {
|
509 |
+
return in_array(strtolower($this->getAction()), self::$allowedActions);
|
510 |
+
}
|
511 |
+
|
512 |
+
public function evaluate() {
|
513 |
+
if (!$this->isActionValid()) {
|
514 |
+
return false;
|
515 |
+
}
|
516 |
+
$subjects = $this->getSubjects();
|
517 |
+
if (!is_array($subjects)) {
|
518 |
+
return false;
|
519 |
+
}
|
520 |
+
|
521 |
+
$this->result = false;
|
522 |
+
/** @var wfWAFRuleComparisonSubject $subject */
|
523 |
+
foreach ($subjects as $subject) {
|
524 |
+
$global = $subject->getValue();
|
525 |
+
$subjectKey = $subject->getKey();
|
526 |
+
|
527 |
+
if ($this->_evaluate(array($this, $this->getAction()), $global, $subjectKey)) {
|
528 |
+
$this->result = true;
|
529 |
+
}
|
530 |
+
}
|
531 |
+
return $this->result;
|
532 |
+
}
|
533 |
+
|
534 |
+
/**
|
535 |
+
* @param callback $callback
|
536 |
+
* @param mixed $global
|
537 |
+
* @param string $subjectKey
|
538 |
+
* @return bool
|
539 |
+
*/
|
540 |
+
private function _evaluate($callback, $global, $subjectKey) {
|
541 |
+
$result = false;
|
542 |
+
|
543 |
+
if ($this->getWAF() && $this->getRule() &&
|
544 |
+
$this->getWAF()->isRuleParamWhitelisted($this->getRule()->getRuleID(), $this->getWAF()->getRequest()->getPath(), $subjectKey)
|
545 |
+
) {
|
546 |
+
return $result;
|
547 |
+
}
|
548 |
+
|
549 |
+
if (is_array($global)) {
|
550 |
+
foreach ($global as $key => $value) {
|
551 |
+
if ($this->_evaluate($callback, $value, $subjectKey . '[' . $key . ']')) {
|
552 |
+
$result = true;
|
553 |
+
}
|
554 |
+
}
|
555 |
+
} else if (call_user_func($callback, $global)) {
|
556 |
+
$result = true;
|
557 |
+
$this->failedSubjects[] = array(
|
558 |
+
'subject' => $subjectKey,
|
559 |
+
'value' => $global,
|
560 |
+
'multiplier' => $this->getMultiplier(),
|
561 |
+
'matches' => $this->getMatches(),
|
562 |
+
);
|
563 |
+
}
|
564 |
+
return $result;
|
565 |
+
}
|
566 |
+
|
567 |
+
public function contains($subject) {
|
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) {
|
575 |
+
return !$this->contains($subject);
|
576 |
+
}
|
577 |
+
|
578 |
+
public function match($subject) {
|
579 |
+
return preg_match((string) $this->getExpected(), (string) $subject, $this->matches) > 0;
|
580 |
+
}
|
581 |
+
|
582 |
+
public function notMatch($subject) {
|
583 |
+
return !$this->match($subject);
|
584 |
+
}
|
585 |
+
|
586 |
+
public function matchCount($subject) {
|
587 |
+
$this->multiplier = preg_match_all((string) $this->getExpected(), (string) $subject, $this->matches);
|
588 |
+
return $this->multiplier > 0;
|
589 |
+
}
|
590 |
+
|
591 |
+
public function containsCount($subject) {
|
592 |
+
if (is_array($this->getExpected())) {
|
593 |
+
$this->multiplier = 0;
|
594 |
+
foreach ($this->getExpected() as $val) {
|
595 |
+
if ($val == $subject) {
|
596 |
+
$this->multiplier++;
|
597 |
+
}
|
598 |
+
}
|
599 |
+
return $this->multiplier > 0;
|
600 |
+
}
|
601 |
+
$this->multiplier = substr_count($subject, $this->getExpected());
|
602 |
+
return $this->multiplier > 0;
|
603 |
+
}
|
604 |
+
|
605 |
+
public function equals($subject) {
|
606 |
+
return $this->getExpected() == $subject;
|
607 |
+
}
|
608 |
+
|
609 |
+
public function notEquals($subject) {
|
610 |
+
return $this->getExpected() != $subject;
|
611 |
+
}
|
612 |
+
|
613 |
+
public function identical($subject) {
|
614 |
+
return $this->getExpected() === $subject;
|
615 |
+
}
|
616 |
+
|
617 |
+
public function notIdentical($subject) {
|
618 |
+
return $this->getExpected() !== $subject;
|
619 |
+
}
|
620 |
+
|
621 |
+
public function greaterThan($subject) {
|
622 |
+
return $subject > $this->getExpected();
|
623 |
+
}
|
624 |
+
|
625 |
+
public function greaterThanEqualTo($subject) {
|
626 |
+
return $subject >= $this->getExpected();
|
627 |
+
}
|
628 |
+
|
629 |
+
public function lessThan($subject) {
|
630 |
+
return $subject < $this->getExpected();
|
631 |
+
}
|
632 |
+
|
633 |
+
public function lessThanEqualTo($subject) {
|
634 |
+
return $subject <= $this->getExpected();
|
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) {
|
646 |
+
if ($authCookie = $this->getWAF()->parseAuthCookie()) {
|
647 |
+
return $authCookie['role'] === $this->getExpected();
|
648 |
+
}
|
649 |
+
return false;
|
650 |
+
}
|
651 |
+
|
652 |
+
public function currentUserIsNot($subject) {
|
653 |
+
return !$this->currentUserIs($subject);
|
654 |
+
}
|
655 |
+
|
656 |
+
/**
|
657 |
+
* @return mixed
|
658 |
+
*/
|
659 |
+
public function getAction() {
|
660 |
+
return $this->action;
|
661 |
+
}
|
662 |
+
|
663 |
+
/**
|
664 |
+
* @param mixed $action
|
665 |
+
*/
|
666 |
+
public function setAction($action) {
|
667 |
+
$this->action = $action;
|
668 |
+
}
|
669 |
+
|
670 |
+
/**
|
671 |
+
* @return mixed
|
672 |
+
*/
|
673 |
+
public function getExpected() {
|
674 |
+
return $this->expected;
|
675 |
+
}
|
676 |
+
|
677 |
+
/**
|
678 |
+
* @param mixed $expected
|
679 |
+
*/
|
680 |
+
public function setExpected($expected) {
|
681 |
+
$this->expected = $expected;
|
682 |
+
}
|
683 |
+
|
684 |
+
/**
|
685 |
+
* @return mixed
|
686 |
+
*/
|
687 |
+
public function getSubjects() {
|
688 |
+
return $this->subjects;
|
689 |
+
}
|
690 |
+
|
691 |
+
/**
|
692 |
+
* @param mixed $subjects
|
693 |
+
* @return $this
|
694 |
+
*/
|
695 |
+
public function setSubjects($subjects) {
|
696 |
+
$this->subjects = $subjects;
|
697 |
+
return $this;
|
698 |
+
}
|
699 |
+
|
700 |
+
/**
|
701 |
+
* @return mixed
|
702 |
+
*/
|
703 |
+
public function getMatches() {
|
704 |
+
return $this->matches;
|
705 |
+
}
|
706 |
+
|
707 |
+
/**
|
708 |
+
* @return mixed
|
709 |
+
*/
|
710 |
+
public function getFailedSubjects() {
|
711 |
+
return $this->failedSubjects;
|
712 |
+
}
|
713 |
+
|
714 |
+
/**
|
715 |
+
* @return mixed
|
716 |
+
*/
|
717 |
+
public function getResult() {
|
718 |
+
return $this->result;
|
719 |
+
}
|
720 |
+
|
721 |
+
/**
|
722 |
+
* @return mixed
|
723 |
+
*/
|
724 |
+
public function getMultiplier() {
|
725 |
+
return $this->multiplier;
|
726 |
+
}
|
727 |
+
|
728 |
+
/**
|
729 |
+
* @return wfWAF
|
730 |
+
*/
|
731 |
+
public function getWAF() {
|
732 |
+
return $this->waf;
|
733 |
+
}
|
734 |
+
|
735 |
+
/**
|
736 |
+
* @param wfWAF $waf
|
737 |
+
*/
|
738 |
+
public function setWAF($waf) {
|
739 |
+
$this->waf = $waf;
|
740 |
+
}
|
741 |
+
|
742 |
+
/**
|
743 |
+
* @return wfWAFRule
|
744 |
+
*/
|
745 |
+
public function getRule() {
|
746 |
+
return $this->rule;
|
747 |
+
}
|
748 |
+
|
749 |
+
/**
|
750 |
+
* @param wfWAFRule $rule
|
751 |
+
*/
|
752 |
+
public function setRule($rule) {
|
753 |
+
$this->rule = $rule;
|
754 |
+
}
|
755 |
+
}
|
756 |
+
|
757 |
+
class wfWAFRuleComparisonGroup implements wfWAFRuleInterface {
|
758 |
+
|
759 |
+
private $items = array();
|
760 |
+
private $failedComparisons = array();
|
761 |
+
private $result = false;
|
762 |
+
/**
|
763 |
+
* @var wfWAFRule
|
764 |
+
*/
|
765 |
+
private $rule;
|
766 |
+
|
767 |
+
public function __construct() {
|
768 |
+
$args = func_get_args();
|
769 |
+
foreach ($args as $arg) {
|
770 |
+
$this->add($arg);
|
771 |
+
}
|
772 |
+
}
|
773 |
+
|
774 |
+
public function add($item) {
|
775 |
+
$this->items[] = $item;
|
776 |
+
}
|
777 |
+
|
778 |
+
public function remove($item) {
|
779 |
+
$key = array_search($item, $this->items);
|
780 |
+
if ($key !== false) {
|
781 |
+
unset($this->items[$key]);
|
782 |
+
}
|
783 |
+
}
|
784 |
+
|
785 |
+
/**
|
786 |
+
*
|
787 |
+
* @throws wfWAFRuleException
|
788 |
+
*/
|
789 |
+
public function evaluate() {
|
790 |
+
if (count($this->items) % 2 != 1) {
|
791 |
+
throw new wfWAFRuleException('Invalid number of rules and logical operators. Should be odd number of rules and logical operators.');
|
792 |
+
}
|
793 |
+
|
794 |
+
$this->result = false;
|
795 |
+
$operator = null;
|
796 |
+
/** @var wfWAFRuleComparison|wfWAFRuleLogicalOperator|wfWAFRuleComparisonGroup $comparison */
|
797 |
+
for ($i = 0; $i < count($this->items); $i++) {
|
798 |
+
$comparison = $this->items[$i];
|
799 |
+
if ($i % 2 == 1 && !($comparison instanceof wfWAFRuleLogicalOperator)) {
|
800 |
+
throw new wfWAFRuleException('Invalid WAF rule format, expected wfWAFRuleLogicalOperator, got ' . get_class($comparison));
|
801 |
+
}
|
802 |
+
if ($i % 2 == 0 && !($comparison instanceof wfWAFRuleComparison || $comparison instanceof wfWAFRuleComparisonGroup)) {
|
803 |
+
throw new wfWAFRuleException('Invalid WAF rule format, expected wfWAFRuleComparison or wfWAFRuleComparisonGroup, got ' . get_class($comparison));
|
804 |
+
}
|
805 |
+
|
806 |
+
if ($comparison instanceof wfWAFRuleLogicalOperator) {
|
807 |
+
$operator = $comparison;
|
808 |
+
continue;
|
809 |
+
}
|
810 |
+
if ($comparison instanceof wfWAFRuleComparison || $comparison instanceof wfWAFRuleComparisonGroup) {
|
811 |
+
$comparison->setRule($this->getRule());
|
812 |
+
if ($operator instanceof wfWAFRuleLogicalOperator) {
|
813 |
+
$operator->setCurrentValue($this->result);
|
814 |
+
$operator->setComparison($comparison);
|
815 |
+
$this->result = $operator->evaluate();
|
816 |
+
} else {
|
817 |
+
$this->result = $comparison->evaluate();
|
818 |
+
}
|
819 |
+
}
|
820 |
+
if ($comparison instanceof wfWAFRuleComparison && $comparison->getResult()) {
|
821 |
+
foreach ($comparison->getFailedSubjects() as $failedSubject) {
|
822 |
+
$this->failedComparisons[] = new wfWAFRuleComparisonFailure(
|
823 |
+
$failedSubject['subject'], $failedSubject['value'], $comparison->getExpected(),
|
824 |
+
$comparison->getAction(), $failedSubject['multiplier'], $failedSubject['matches']
|
825 |
+
);
|
826 |
+
}
|
827 |
+
}
|
828 |
+
if ($comparison instanceof wfWAFRuleComparisonGroup && $comparison->getResult()) {
|
829 |
+
foreach ($comparison->getFailedComparisons() as $comparisonFail) {
|
830 |
+
$this->failedComparisons[] = $comparisonFail;
|
831 |
+
}
|
832 |
+
}
|
833 |
+
}
|
834 |
+
return $this->result;
|
835 |
+
}
|
836 |
+
|
837 |
+
/**
|
838 |
+
* @return string
|
839 |
+
* @throws wfWAFRuleException
|
840 |
+
*/
|
841 |
+
public function render() {
|
842 |
+
if (count($this->items) % 2 != 1) {
|
843 |
+
throw new wfWAFRuleException('Invalid number of rules and logical operators. Should be odd number of rules and logical operators.');
|
844 |
+
}
|
845 |
+
|
846 |
+
$return = array();
|
847 |
+
/**
|
848 |
+
* @var wfWAFRuleInterface $item
|
849 |
+
*/
|
850 |
+
for ($i = 0; $i < count($this->items); $i++) {
|
851 |
+
$item = $this->items[$i];
|
852 |
+
if ($i % 2 == 1 && !($item instanceof wfWAFRuleLogicalOperator)) {
|
853 |
+
throw new wfWAFRuleException('Invalid WAF rule format, expected wfWAFRuleLogicalOperator, got ' . get_class($item));
|
854 |
+
}
|
855 |
+
if ($i % 2 == 0 && !($item instanceof wfWAFRuleComparison || $item instanceof wfWAFRuleComparisonGroup)) {
|
856 |
+
throw new wfWAFRuleException('Invalid WAF rule format, expected wfWAFRule or wfWAFRuleComparisonGroup, got ' . get_class($item));
|
857 |
+
}
|
858 |
+
$return[] = $item->render();
|
859 |
+
}
|
860 |
+
return sprintf('new %s(%s)', get_class($this), join(', ', $return));
|
861 |
+
}
|
862 |
+
|
863 |
+
/**
|
864 |
+
* @return string
|
865 |
+
* @throws wfWAFRuleException
|
866 |
+
*/
|
867 |
+
public function renderRule() {
|
868 |
+
if (count($this->items) % 2 != 1) {
|
869 |
+
throw new wfWAFRuleException('Invalid number of rules and logical operators. Should be odd number of rules and logical operators.');
|
870 |
+
}
|
871 |
+
|
872 |
+
$return = array();
|
873 |
+
/**
|
874 |
+
* @var wfWAFRuleInterface $item
|
875 |
+
*/
|
876 |
+
for ($i = 0; $i < count($this->items); $i++) {
|
877 |
+
$item = $this->items[$i];
|
878 |
+
if ($i % 2 == 1 && !($item instanceof wfWAFRuleLogicalOperator)) {
|
879 |
+
throw new wfWAFRuleException('Invalid WAF rule format, expected wfWAFRuleLogicalOperator, got ' . get_class($item));
|
880 |
+
}
|
881 |
+
if ($i % 2 == 0 && !($item instanceof wfWAFRuleComparison || $item instanceof wfWAFRuleComparisonGroup)) {
|
882 |
+
throw new wfWAFRuleException('Invalid WAF rule format, expected wfWAFRule or wfWAFRuleComparisonGroup, got ' . get_class($item));
|
883 |
+
}
|
884 |
+
$return[] = $item->renderRule();
|
885 |
+
}
|
886 |
+
return sprintf('(%s)', join(' ', $return));
|
887 |
+
}
|
888 |
+
|
889 |
+
public function debug() {
|
890 |
+
$debug = '';
|
891 |
+
/** @var wfWAFRuleComparisonFailure $failedComparison */
|
892 |
+
foreach ($this->getFailedComparisons() as $failedComparison) {
|
893 |
+
$debug .= $failedComparison->getParamKey() . ' ' . $failedComparison->getAction() . ' ' . $failedComparison->getExpected() . "\n";
|
894 |
+
}
|
895 |
+
return $debug;
|
896 |
+
}
|
897 |
+
|
898 |
+
/**
|
899 |
+
* @return array
|
900 |
+
*/
|
901 |
+
public function getItems() {
|
902 |
+
return $this->items;
|
903 |
+
}
|
904 |
+
|
905 |
+
/**
|
906 |
+
* @param array $items
|
907 |
+
*/
|
908 |
+
public function setItems($items) {
|
909 |
+
$this->items = $items;
|
910 |
+
}
|
911 |
+
|
912 |
+
/**
|
913 |
+
* @return mixed
|
914 |
+
*/
|
915 |
+
public function getFailedComparisons() {
|
916 |
+
return $this->failedComparisons;
|
917 |
+
}
|
918 |
+
|
919 |
+
/**
|
920 |
+
* @return boolean
|
921 |
+
*/
|
922 |
+
public function getResult() {
|
923 |
+
return $this->result;
|
924 |
+
}
|
925 |
+
|
926 |
+
/**
|
927 |
+
* @return wfWAFRule
|
928 |
+
*/
|
929 |
+
public function getRule() {
|
930 |
+
return $this->rule;
|
931 |
+
}
|
932 |
+
|
933 |
+
/**
|
934 |
+
* @param wfWAFRule $rule
|
935 |
+
*/
|
936 |
+
public function setRule($rule) {
|
937 |
+
$this->rule = $rule;
|
938 |
+
}
|
939 |
+
}
|
940 |
+
|
941 |
+
class wfWAFRuleComparisonFailure {
|
942 |
+
|
943 |
+
private $paramKey;
|
944 |
+
private $expected;
|
945 |
+
private $action;
|
946 |
+
/**
|
947 |
+
* @var null|int
|
948 |
+
*/
|
949 |
+
private $multiplier;
|
950 |
+
/**
|
951 |
+
* @var string
|
952 |
+
*/
|
953 |
+
private $paramValue;
|
954 |
+
/**
|
955 |
+
* @var mixed
|
956 |
+
*/
|
957 |
+
private $matches;
|
958 |
+
|
959 |
+
/**
|
960 |
+
* @param string $paramKey
|
961 |
+
* @param string $paramValue
|
962 |
+
* @param string $expected
|
963 |
+
* @param string $action
|
964 |
+
* @param mixed $multiplier
|
965 |
+
* @param mixed $matches
|
966 |
+
*/
|
967 |
+
public function __construct($paramKey, $paramValue, $expected, $action, $multiplier = null, $matches = null) {
|
968 |
+
$this->setParamKey($paramKey);
|
969 |
+
$this->setExpected($expected);
|
970 |
+
$this->setAction($action);
|
971 |
+
$this->setMultiplier($multiplier);
|
972 |
+
$this->setParamValue($paramValue);
|
973 |
+
$this->setMatches($matches);
|
974 |
+
}
|
975 |
+
|
976 |
+
/**
|
977 |
+
* @return mixed
|
978 |
+
*/
|
979 |
+
public function getParamKey() {
|
980 |
+
return $this->paramKey;
|
981 |
+
}
|
982 |
+
|
983 |
+
/**
|
984 |
+
* @param mixed $paramKey
|
985 |
+
*/
|
986 |
+
public function setParamKey($paramKey) {
|
987 |
+
$this->paramKey = $paramKey;
|
988 |
+
}
|
989 |
+
|
990 |
+
/**
|
991 |
+
* @return mixed
|
992 |
+
*/
|
993 |
+
public function getExpected() {
|
994 |
+
return $this->expected;
|
995 |
+
}
|
996 |
+
|
997 |
+
/**
|
998 |
+
* @param mixed $expected
|
999 |
+
*/
|
1000 |
+
public function setExpected($expected) {
|
1001 |
+
$this->expected = $expected;
|
1002 |
+
}
|
1003 |
+
|
1004 |
+
/**
|
1005 |
+
* @return mixed
|
1006 |
+
*/
|
1007 |
+
public function getAction() {
|
1008 |
+
return $this->action;
|
1009 |
+
}
|
1010 |
+
|
1011 |
+
/**
|
1012 |
+
* @param mixed $action
|
1013 |
+
*/
|
1014 |
+
public function setAction($action) {
|
1015 |
+
$this->action = $action;
|
1016 |
+
}
|
1017 |
+
|
1018 |
+
/**
|
1019 |
+
* @return int|null
|
1020 |
+
*/
|
1021 |
+
public function getMultiplier() {
|
1022 |
+
return $this->multiplier;
|
1023 |
+
}
|
1024 |
+
|
1025 |
+
/**
|
1026 |
+
* @param int|null $multiplier
|
1027 |
+
*/
|
1028 |
+
public function setMultiplier($multiplier) {
|
1029 |
+
$this->multiplier = $multiplier;
|
1030 |
+
}
|
1031 |
+
|
1032 |
+
/**
|
1033 |
+
* @return bool
|
1034 |
+
*/
|
1035 |
+
public function hasMultiplier() {
|
1036 |
+
return $this->getMultiplier() > 1;
|
1037 |
+
}
|
1038 |
+
|
1039 |
+
/**
|
1040 |
+
* @return string
|
1041 |
+
*/
|
1042 |
+
public function getParamValue() {
|
1043 |
+
return $this->paramValue;
|
1044 |
+
}
|
1045 |
+
|
1046 |
+
/**
|
1047 |
+
* @param string $paramValue
|
1048 |
+
*/
|
1049 |
+
public function setParamValue($paramValue) {
|
1050 |
+
$this->paramValue = $paramValue;
|
1051 |
+
}
|
1052 |
+
|
1053 |
+
/**
|
1054 |
+
* @return mixed
|
1055 |
+
*/
|
1056 |
+
public function getMatches() {
|
1057 |
+
return $this->matches;
|
1058 |
+
}
|
1059 |
+
|
1060 |
+
/**
|
1061 |
+
* @param mixed $matches
|
1062 |
+
*/
|
1063 |
+
public function setMatches($matches) {
|
1064 |
+
$this->matches = $matches;
|
1065 |
+
}
|
1066 |
+
}
|
1067 |
+
|
1068 |
+
class wfWAFRuleComparisonSubject {
|
1069 |
+
|
1070 |
+
/**
|
1071 |
+
* @var array
|
1072 |
+
*/
|
1073 |
+
private $subject;
|
1074 |
+
/**
|
1075 |
+
* @var array
|
1076 |
+
*/
|
1077 |
+
private $filters;
|
1078 |
+
|
1079 |
+
/** @var wfWAF */
|
1080 |
+
private $waf;
|
1081 |
+
|
1082 |
+
public static function create($waf, $subject, $filters) {
|
1083 |
+
return new self($waf, $subject, $filters);
|
1084 |
+
}
|
1085 |
+
|
1086 |
+
/**
|
1087 |
+
* wfWAFRuleComparisonSubject constructor.
|
1088 |
+
* @param wfWAF $waf
|
1089 |
+
* @param array $subject
|
1090 |
+
* @param array $filters
|
1091 |
+
*/
|
1092 |
+
public function __construct($waf, $subject, $filters) {
|
1093 |
+
$this->waf = $waf;
|
1094 |
+
$this->subject = $subject;
|
1095 |
+
$this->filters = $filters;
|
1096 |
+
}
|
1097 |
+
|
1098 |
+
/**
|
1099 |
+
* @return mixed|null
|
1100 |
+
*/
|
1101 |
+
public function getValue() {
|
1102 |
+
$subject = $this->getSubject();
|
1103 |
+
if (!is_array($subject)) {
|
1104 |
+
return $this->runFilters($this->getWAF()->getGlobal($subject));
|
1105 |
+
}
|
1106 |
+
if (is_array($subject) && count($subject) > 0) {
|
1107 |
+
$globalKey = array_shift($subject);
|
1108 |
+
return $this->runFilters($this->_getValue($subject, $this->getWAF()->getGlobal($globalKey)));
|
1109 |
+
}
|
1110 |
+
return null;
|
1111 |
+
}
|
1112 |
+
|
1113 |
+
/**
|
1114 |
+
* @param array $subjectKey
|
1115 |
+
* @param array $global
|
1116 |
+
* @return null
|
1117 |
+
*/
|
1118 |
+
private function _getValue($subjectKey, $global) {
|
1119 |
+
if (!is_array($global) || !is_array($subjectKey)) {
|
1120 |
+
return null;
|
1121 |
+
}
|
1122 |
+
|
1123 |
+
$key = array_shift($subjectKey);
|
1124 |
+
if (array_key_exists($key, $global)) {
|
1125 |
+
if (count($subjectKey) > 0) {
|
1126 |
+
return $this->_getValue($subjectKey, $global[$key]);
|
1127 |
+
} else {
|
1128 |
+
return $global[$key];
|
1129 |
+
}
|
1130 |
+
}
|
1131 |
+
return null;
|
1132 |
+
}
|
1133 |
+
|
1134 |
+
|
1135 |
+
/**
|
1136 |
+
* @return string
|
1137 |
+
*/
|
1138 |
+
public function getKey() {
|
1139 |
+
return wfWAFRuleComparison::getSubjectKey($this->getSubject());
|
1140 |
+
}
|
1141 |
+
|
1142 |
+
/**
|
1143 |
+
* @param mixed $value
|
1144 |
+
* @return mixed
|
1145 |
+
*/
|
1146 |
+
private function runFilters($value) {
|
1147 |
+
$filters = $this->getFilters();
|
1148 |
+
if (is_array($filters)) {
|
1149 |
+
foreach ($filters as $filter) {
|
1150 |
+
if (method_exists($this, 'filter' . $filter)) {
|
1151 |
+
$value = call_user_func(array($this, 'filter' . $filter), $value);
|
1152 |
+
}
|
1153 |
+
}
|
1154 |
+
}
|
1155 |
+
return $value;
|
1156 |
+
}
|
1157 |
+
|
1158 |
+
/**
|
1159 |
+
* @param mixed $value
|
1160 |
+
* @return string
|
1161 |
+
*/
|
1162 |
+
public function filterBase64decode($value) {
|
1163 |
+
if (is_string($value)) {
|
1164 |
+
return base64_decode($value);
|
1165 |
+
}
|
1166 |
+
return $value;
|
1167 |
+
}
|
1168 |
+
|
1169 |
+
/**
|
1170 |
+
* @return string
|
1171 |
+
*/
|
1172 |
+
public function render() {
|
1173 |
+
return sprintf('%s::create($this, %s, %s)', get_class($this), var_export($this->getSubject(), true),
|
1174 |
+
var_export($this->getFilters(), true));
|
1175 |
+
}
|
1176 |
+
|
1177 |
+
/**
|
1178 |
+
* @return string
|
1179 |
+
*/
|
1180 |
+
public function renderRule() {
|
1181 |
+
$rule = is_array($this->getSubject()) ? join('.', $this->getSubject()) : $this->getSubject();
|
1182 |
+
foreach ($this->getFilters() as $filter) {
|
1183 |
+
$rule = $filter . '(' . $rule . ')';
|
1184 |
+
}
|
1185 |
+
return $rule;
|
1186 |
+
}
|
1187 |
+
|
1188 |
+
/**
|
1189 |
+
* @return array
|
1190 |
+
*/
|
1191 |
+
public function getSubject() {
|
1192 |
+
return $this->subject;
|
1193 |
+
}
|
1194 |
+
|
1195 |
+
/**
|
1196 |
+
* @param array $subject
|
1197 |
+
*/
|
1198 |
+
public function setSubject($subject) {
|
1199 |
+
$this->subject = $subject;
|
1200 |
+
}
|
1201 |
+
|
1202 |
+
/**
|
1203 |
+
* @return array
|
1204 |
+
*/
|
1205 |
+
public function getFilters() {
|
1206 |
+
return $this->filters;
|
1207 |
+
}
|
1208 |
+
|
1209 |
+
/**
|
1210 |
+
* @param array $filters
|
1211 |
+
*/
|
1212 |
+
public function setFilters($filters) {
|
1213 |
+
$this->filters = $filters;
|
1214 |
+
}
|
1215 |
+
|
1216 |
+
/**
|
1217 |
+
* @return wfWAF
|
1218 |
+
*/
|
1219 |
+
public function getWAF() {
|
1220 |
+
return $this->waf;
|
1221 |
+
}
|
1222 |
+
|
1223 |
+
/**
|
1224 |
+
* @param wfWAF $waf
|
1225 |
+
*/
|
1226 |
+
public function setWAF($waf) {
|
1227 |
+
$this->waf = $waf;
|
1228 |
+
}
|
1229 |
+
}
|
vendor/wordfence/wf-waf/src/lib/storage.php
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
interface wfWAFStorageInterface {
|
4 |
+
|
5 |
+
public function hasPreviousAttackData($olderThan);
|
6 |
+
|
7 |
+
public function hasNewerAttackData($newerThan);
|
8 |
+
|
9 |
+
public function getAttackData();
|
10 |
+
|
11 |
+
public function getAttackDataArray();
|
12 |
+
|
13 |
+
public function getNewestAttackDataArray($newerThan);
|
14 |
+
|
15 |
+
public function truncateAttackData();
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @param array $failedRules
|
19 |
+
* @param string $failedParamKey
|
20 |
+
* @param string $failedParamValue
|
21 |
+
* @param wfWAFRequestInterface $request
|
22 |
+
* @param mixed $_
|
23 |
+
* @return mixed
|
24 |
+
*/
|
25 |
+
public function logAttack($failedRules, $failedParamKey, $failedParamValue, $request, $_ = null);
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @param int $timestamp
|
29 |
+
* @param string $ip
|
30 |
+
* @param bool $ssl
|
31 |
+
* @param array $failedRuleIDs
|
32 |
+
* @param wfWAFRequestInterface|string $request
|
33 |
+
* @param mixed $_
|
34 |
+
* @return mixed
|
35 |
+
*/
|
36 |
+
// public function logAttack($timestamp, $ip, $ssl, $failedRuleIDs, $request, $_ = null);
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @param float $timestamp
|
40 |
+
* @param string $ip
|
41 |
+
* @return mixed
|
42 |
+
*/
|
43 |
+
public function blockIP($timestamp, $ip);
|
44 |
+
|
45 |
+
public function isIPBlocked($ip);
|
46 |
+
|
47 |
+
public function getConfig($key, $default = null);
|
48 |
+
|
49 |
+
public function setConfig($key, $value);
|
50 |
+
|
51 |
+
public function unsetConfig($key);
|
52 |
+
|
53 |
+
public function uninstall();
|
54 |
+
|
55 |
+
public function isInLearningMode();
|
56 |
+
|
57 |
+
public function isDisabled();
|
58 |
+
|
59 |
+
public function getRulesDSLCacheFile();
|
60 |
+
|
61 |
+
public function isAttackDataFull();
|
62 |
+
}
|
vendor/wordfence/wf-waf/src/lib/storage/file.php
ADDED
@@ -0,0 +1,1320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class wfWAFStorageFile implements wfWAFStorageInterface {
|
4 |
+
|
5 |
+
const LOG_FILE_HEADER = "<?php exit('Access denied'); __halt_compiler(); ?>\n";
|
6 |
+
|
7 |
+
public static function atomicFilePutContents($file, $content, $prefix = 'config') {
|
8 |
+
$tmpFile = @tempnam(dirname($file), $prefix . '.tmp.');
|
9 |
+
if (!$tmpFile) {
|
10 |
+
$tmpFile = @tempnam(sys_get_temp_dir(), $prefix . '.tmp.');
|
11 |
+
}
|
12 |
+
if (!$tmpFile) {
|
13 |
+
throw new wfWAFStorageFileException('Unable to save temporary file for atomic writing.');
|
14 |
+
}
|
15 |
+
$tmpHandle = @fopen($tmpFile, 'w');
|
16 |
+
if (!$tmpHandle) {
|
17 |
+
throw new wfWAFStorageFileException('Unable to save temporary file ' . $tmpFile . ' for atomic writing.');
|
18 |
+
}
|
19 |
+
|
20 |
+
self::lock($tmpHandle, LOCK_EX);
|
21 |
+
fwrite($tmpHandle, $content);
|
22 |
+
fflush($tmpHandle);
|
23 |
+
self::lock($tmpHandle, LOCK_UN);
|
24 |
+
fclose($tmpHandle);
|
25 |
+
|
26 |
+
// Attempt to verify file has finished writing (sometimes the disk will lie for better benchmarks)
|
27 |
+
$tmpContents = file_get_contents($tmpFile);
|
28 |
+
if ($tmpContents !== $content) {
|
29 |
+
throw new wfWAFStorageFileException('Unable to verify temporary file contents for atomic writing.');
|
30 |
+
}
|
31 |
+
|
32 |
+
if (!@rename($tmpFile, $file)) {
|
33 |
+
$backFile = @tempnam(dirname($file), $prefix . '.bak.');
|
34 |
+
if (!$backFile) {
|
35 |
+
$backFile = @tempnam(sys_get_temp_dir(), $prefix . '.bak.');
|
36 |
+
}
|
37 |
+
if (!$backFile) {
|
38 |
+
throw new wfWAFStorageFileException('Unable to save temporary file for atomic writing.');
|
39 |
+
}
|
40 |
+
if (WFWAF_DEBUG) {
|
41 |
+
rename($file, $backFile);
|
42 |
+
rename($tmpFile, $file);
|
43 |
+
unlink($backFile);
|
44 |
+
unlink($tmpFile);
|
45 |
+
} else {
|
46 |
+
@rename($file, $backFile);
|
47 |
+
@rename($tmpFile, $file);
|
48 |
+
@unlink($backFile);
|
49 |
+
@unlink($tmpFile);
|
50 |
+
}
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
public static function lock($handle, $lock, $wouldLock = 1) {
|
55 |
+
$locked = flock($handle, $lock, $wouldLock);
|
56 |
+
if (!$locked) {
|
57 |
+
error_log('Lock not acquired ' . $locked);
|
58 |
+
}
|
59 |
+
return $locked;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* @var resource
|
64 |
+
*/
|
65 |
+
private $ipCacheFileHandle;
|
66 |
+
|
67 |
+
/**
|
68 |
+
* @var string|null
|
69 |
+
*/
|
70 |
+
private $attackDataFile;
|
71 |
+
/**
|
72 |
+
* @var wfWAFAttackDataStorageFileEngine
|
73 |
+
*/
|
74 |
+
private $attackDataEngine;
|
75 |
+
|
76 |
+
/**
|
77 |
+
* @var string|null
|
78 |
+
*/
|
79 |
+
private $ipCacheFile;
|
80 |
+
private $configFile;
|
81 |
+
private $rulesDSLCacheFile;
|
82 |
+
private $dataChanged = false;
|
83 |
+
private $data = false;
|
84 |
+
/**
|
85 |
+
* @var resource
|
86 |
+
*/
|
87 |
+
private $configFileHandle;
|
88 |
+
private $uninstalled;
|
89 |
+
private $attackDataRows;
|
90 |
+
private $attackDataNewerThan;
|
91 |
+
|
92 |
+
|
93 |
+
/**
|
94 |
+
* @param string|null $attackDataFile
|
95 |
+
* @param string|null $ipCacheFile
|
96 |
+
* @param string|null $configFile
|
97 |
+
* @param null $rulesDSLCacheFile
|
98 |
+
*/
|
99 |
+
public function __construct($attackDataFile = null, $ipCacheFile = null, $configFile = null, $rulesDSLCacheFile = null) {
|
100 |
+
$this->setAttackDataFile($attackDataFile);
|
101 |
+
$this->setIPCacheFile($ipCacheFile);
|
102 |
+
$this->setConfigFile($configFile);
|
103 |
+
$this->setRulesDSLCacheFile($rulesDSLCacheFile);
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* @param float $olderThan
|
108 |
+
* @return bool
|
109 |
+
* @throws wfWAFStorageFileException
|
110 |
+
*/
|
111 |
+
public function hasPreviousAttackData($olderThan) {
|
112 |
+
$this->open();
|
113 |
+
$timestamp = $this->getAttackDataEngine()->getOldestTimestamp();
|
114 |
+
return $timestamp && $timestamp < $olderThan;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* @param float $newerThan
|
119 |
+
* @return bool
|
120 |
+
* @throws wfWAFStorageFileException
|
121 |
+
*/
|
122 |
+
public function hasNewerAttackData($newerThan) {
|
123 |
+
$this->open();
|
124 |
+
$timestamp = $this->getAttackDataEngine()->getNewestTimestamp();
|
125 |
+
return $timestamp && $timestamp > $newerThan;
|
126 |
+
}
|
127 |
+
|
128 |
+
|
129 |
+
/**
|
130 |
+
* @return mixed|string|void
|
131 |
+
* @throws wfWAFStorageFileException
|
132 |
+
*/
|
133 |
+
public function getAttackData() {
|
134 |
+
$this->open();
|
135 |
+
$this->attackDataRows = array();
|
136 |
+
$this->getAttackDataEngine()->scanRows(array($this, '_getAttackDataRowsSerialized'));
|
137 |
+
return json_encode($this->attackDataRows);
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* @return array
|
142 |
+
* @throws wfWAFStorageFileException
|
143 |
+
*/
|
144 |
+
public function getAttackDataArray() {
|
145 |
+
$this->open();
|
146 |
+
$this->attackDataRows = array();
|
147 |
+
$this->getAttackDataEngine()->scanRows(array($this, '_getAttackDataRows'));
|
148 |
+
return $this->attackDataRows;
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* @param resource $fileHandle
|
153 |
+
* @param int $offset
|
154 |
+
* @param int $length
|
155 |
+
*/
|
156 |
+
public function _getAttackDataRowsSerialized($fileHandle, $offset, $length) {
|
157 |
+
fseek($fileHandle, $offset);
|
158 |
+
self::lock($fileHandle, LOCK_SH);
|
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;
|
166 |
+
}
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* @param resource $fileHandle
|
171 |
+
* @param int $offset
|
172 |
+
* @param int $length
|
173 |
+
*/
|
174 |
+
public function _getAttackDataRows($fileHandle, $offset, $length) {
|
175 |
+
fseek($fileHandle, $offset);
|
176 |
+
self::lock($fileHandle, LOCK_SH);
|
177 |
+
$binary = fread($fileHandle, $length);
|
178 |
+
self::lock($fileHandle, LOCK_UN);
|
179 |
+
$row = wfWAFAttackDataStorageFileEngineRow::unpack($binary);
|
180 |
+
$data = $this->unserializeRow($row->getData());
|
181 |
+
array_unshift($data, $row->getTimestamp());
|
182 |
+
$this->attackDataRows[] = $data;
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* @param $newerThan
|
187 |
+
* @return array
|
188 |
+
* @throws wfWAFStorageFileException
|
189 |
+
*/
|
190 |
+
public function getNewestAttackDataArray($newerThan) {
|
191 |
+
$this->open();
|
192 |
+
$this->attackDataRows = array();
|
193 |
+
$this->attackDataNewerThan = $newerThan;
|
194 |
+
$this->getAttackDataEngine()->scanRowsReverse(array($this, '_getAttackDataRowsNewerThan'));
|
195 |
+
return $this->attackDataRows;
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* @param resource $fileHandle
|
200 |
+
* @param int $offset
|
201 |
+
* @param int $length
|
202 |
+
* @return bool
|
203 |
+
*/
|
204 |
+
public function _getAttackDataRowsNewerThan($fileHandle, $offset, $length) {
|
205 |
+
fseek($fileHandle, $offset);
|
206 |
+
self::lock($fileHandle, LOCK_SH);
|
207 |
+
$binaryTimestamp = fread($fileHandle, 8);
|
208 |
+
self::lock($fileHandle, LOCK_UN);
|
209 |
+
$timestamp = wfWAFAttackDataStorageFileEngine::unpackMicrotime($binaryTimestamp);
|
210 |
+
if ($timestamp > $this->attackDataNewerThan) {
|
211 |
+
$binary = $binaryTimestamp . fread($fileHandle, $length - 8);
|
212 |
+
$row = wfWAFAttackDataStorageFileEngineRow::unpack($binary);
|
213 |
+
$data = $this->unserializeRow($row->getData());
|
214 |
+
if (is_array($data)) {
|
215 |
+
array_unshift($data, $row->getTimestamp());
|
216 |
+
$this->attackDataRows[] = $data;
|
217 |
+
}
|
218 |
+
return true;
|
219 |
+
}
|
220 |
+
return false;
|
221 |
+
}
|
222 |
+
|
223 |
+
/**
|
224 |
+
* @return bool
|
225 |
+
* @throws wfWAFStorageFileException
|
226 |
+
*/
|
227 |
+
public function truncateAttackData() {
|
228 |
+
$this->open();
|
229 |
+
$this->getAttackDataEngine()->truncate();
|
230 |
+
return $this->getAttackDataEngine()->getRowCount() === 0;
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* @return bool
|
235 |
+
* @throws wfWAFStorageFileException
|
236 |
+
*/
|
237 |
+
public function isAttackDataFull() {
|
238 |
+
$this->open();
|
239 |
+
return $this->getAttackDataEngine()->getRowCount() === wfWAFAttackDataStorageFileEngine::MAX_ROWS;
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* @param array $failedRules
|
244 |
+
* @param string $failedParamKey
|
245 |
+
* @param string $failedParamValue
|
246 |
+
* @param wfWAFRequestInterface $request
|
247 |
+
* @param mixed $_
|
248 |
+
* @return mixed
|
249 |
+
*/
|
250 |
+
public function logAttack($failedRules, $failedParamKey, $failedParamValue, $request, $_ = null) {
|
251 |
+
$this->open();
|
252 |
+
$row = array(
|
253 |
+
$request->getTimestamp(),
|
254 |
+
$request->getIP(),
|
255 |
+
(int) $this->isInLearningMode(),
|
256 |
+
$failedParamKey,
|
257 |
+
$failedParamValue,
|
258 |
+
);
|
259 |
+
|
260 |
+
$failedRulesString = '';
|
261 |
+
if (is_array($failedRules)) {
|
262 |
+
/**
|
263 |
+
* @var int $index
|
264 |
+
* @var wfWAFRule|int $rule
|
265 |
+
*/
|
266 |
+
foreach ($failedRules as $index => $rule) {
|
267 |
+
if ($rule instanceof wfWAFRule) {
|
268 |
+
$failedRulesString .= $rule->getRuleID() . '|';
|
269 |
+
} else {
|
270 |
+
$failedRulesString .= $rule . '|';
|
271 |
+
}
|
272 |
+
}
|
273 |
+
$failedRulesString = substr($failedRulesString, 0, -1);
|
274 |
+
}
|
275 |
+
$row[] = $failedRulesString;
|
276 |
+
$row[] = $request->getProtocol() === 'https' ? 1 : 0;
|
277 |
+
$row[] = (string) $request;
|
278 |
+
$args = func_get_args();
|
279 |
+
$row = array_merge($row, array_slice($args, 4));
|
280 |
+
|
281 |
+
if (($rowString = $this->serializeRow($row)) !== false) {
|
282 |
+
$attackRow = new wfWAFAttackDataStorageFileEngineRow(microtime(false), $rowString);
|
283 |
+
$this->getAttackDataEngine()->addRow($attackRow);
|
284 |
+
}
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* @param int $timestamp
|
289 |
+
* @param string $ip
|
290 |
+
* @return mixed|void
|
291 |
+
* @throws wfWAFStorageFileException
|
292 |
+
*/
|
293 |
+
public function blockIP($timestamp, $ip) {
|
294 |
+
$this->open();
|
295 |
+
if (!$this->isIPBlocked($ip)) {
|
296 |
+
self::lock($this->ipCacheFileHandle, LOCK_EX);
|
297 |
+
fseek($this->ipCacheFileHandle, 0, SEEK_END);
|
298 |
+
fwrite($this->ipCacheFileHandle, wfWAFUtils::inet_pton($ip) . pack('V', $timestamp));
|
299 |
+
fflush($this->ipCacheFileHandle);
|
300 |
+
self::lock($this->ipCacheFileHandle, LOCK_UN);
|
301 |
+
}
|
302 |
+
}
|
303 |
+
|
304 |
+
/**
|
305 |
+
* @param string $ip
|
306 |
+
* @return bool
|
307 |
+
*/
|
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 |
+
}
|
320 |
+
}
|
321 |
+
self::lock($this->ipCacheFileHandle, LOCK_UN);
|
322 |
+
return false;
|
323 |
+
}
|
324 |
+
|
325 |
+
/**
|
326 |
+
* @return bool
|
327 |
+
*/
|
328 |
+
public function isOpened() {
|
329 |
+
return is_resource($this->configFileHandle);
|
330 |
+
}
|
331 |
+
|
332 |
+
/**
|
333 |
+
* @throws wfWAFStorageFileException
|
334 |
+
*/
|
335 |
+
public function open() {
|
336 |
+
if ($this->isOpened()) {
|
337 |
+
return;
|
338 |
+
}
|
339 |
+
if ($this->uninstalled) {
|
340 |
+
throw new wfWAFStorageFileException('Unable to open WAF file storage, WAF has been uninstalled.');
|
341 |
+
}
|
342 |
+
|
343 |
+
$files = array(
|
344 |
+
array($this->getIPCacheFile(), 'ipCacheFileHandle', self::LOG_FILE_HEADER),
|
345 |
+
array($this->getConfigFile(), 'configFileHandle', self::LOG_FILE_HEADER . serialize($this->getDefaultConfiguration())),
|
346 |
+
);
|
347 |
+
foreach ($files as $file) {
|
348 |
+
list($filePath, $fileHandle, $defaultContents) = $file;
|
349 |
+
if (!file_exists($filePath)) {
|
350 |
+
@file_put_contents($filePath, $defaultContents, LOCK_EX);
|
351 |
+
}
|
352 |
+
$this->$fileHandle = @fopen($filePath, 'r+');
|
353 |
+
if (!$this->$fileHandle) {
|
354 |
+
throw new wfWAFStorageFileException('Unable to open ' . $filePath . ' for reading and writing.');
|
355 |
+
}
|
356 |
+
}
|
357 |
+
|
358 |
+
$this->setAttackDataEngine(new wfWAFAttackDataStorageFileEngine($this->getAttackDataFile()));
|
359 |
+
$this->getAttackDataEngine()->open();
|
360 |
+
}
|
361 |
+
|
362 |
+
/**
|
363 |
+
*
|
364 |
+
*/
|
365 |
+
public function close() {
|
366 |
+
if (!$this->isOpened()) {
|
367 |
+
return;
|
368 |
+
}
|
369 |
+
fclose($this->ipCacheFileHandle);
|
370 |
+
fclose($this->configFileHandle);
|
371 |
+
$this->ipCacheFileHandle = null;
|
372 |
+
$this->configFileHandle = null;
|
373 |
+
$this->getAttackDataEngine()->close();
|
374 |
+
}
|
375 |
+
|
376 |
+
/**
|
377 |
+
* Clean up old expired IP blocks.
|
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);
|
391 |
+
$writePointer += 20;
|
392 |
+
fseek($this->ipCacheFileHandle, $readPointer, SEEK_SET);
|
393 |
+
}
|
394 |
+
$readPointer += 20;
|
395 |
+
fseek($this->ipCacheFileHandle, $readPointer, SEEK_SET);
|
396 |
+
}
|
397 |
+
ftruncate($this->ipCacheFileHandle, $writePointer);
|
398 |
+
self::lock($this->ipCacheFileHandle, LOCK_UN);
|
399 |
+
}
|
400 |
+
|
401 |
+
/**
|
402 |
+
* @param string $key
|
403 |
+
* @param mixed $default
|
404 |
+
* @return mixed
|
405 |
+
*/
|
406 |
+
public function getConfig($key, $default = null) {
|
407 |
+
if (!$this->data) {
|
408 |
+
$this->fetchConfigData();
|
409 |
+
}
|
410 |
+
return array_key_exists($key, $this->data) ? $this->data[$key] : $default;
|
411 |
+
}
|
412 |
+
|
413 |
+
/**
|
414 |
+
* @param string $key
|
415 |
+
* @param mixed $value
|
416 |
+
*/
|
417 |
+
public function setConfig($key, $value) {
|
418 |
+
if (!$this->data) {
|
419 |
+
$this->fetchConfigData();
|
420 |
+
}
|
421 |
+
if (!$this->dataChanged && (
|
422 |
+
(array_key_exists($key, $this->data) && $this->data[$key] !== $value) ||
|
423 |
+
!array_key_exists($key, $this->data)
|
424 |
+
)
|
425 |
+
) {
|
426 |
+
$this->dataChanged = array($key, true);
|
427 |
+
register_shutdown_function(array($this, 'saveConfig'));
|
428 |
+
}
|
429 |
+
$this->data[$key] = $value;
|
430 |
+
}
|
431 |
+
|
432 |
+
/**
|
433 |
+
* @param string $key
|
434 |
+
*/
|
435 |
+
public function unsetConfig($key) {
|
436 |
+
if (!$this->data) {
|
437 |
+
$this->fetchConfigData();
|
438 |
+
}
|
439 |
+
if (!$this->dataChanged && array_key_exists($key, $this->data)) {
|
440 |
+
$this->dataChanged = array($key, true);
|
441 |
+
register_shutdown_function(array($this, 'saveConfig'));
|
442 |
+
}
|
443 |
+
unset($this->data[$key]);
|
444 |
+
}
|
445 |
+
|
446 |
+
/**
|
447 |
+
* @throws wfWAFStorageFileException
|
448 |
+
*/
|
449 |
+
public function fetchConfigData() {
|
450 |
+
$this->configFileHandle = null;
|
451 |
+
$this->open();
|
452 |
+
self::lock($this->configFileHandle, LOCK_SH);
|
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);
|
460 |
+
}
|
461 |
+
$this->data = @unserialize($serializedData);
|
462 |
+
|
463 |
+
if ($this->data === false) {
|
464 |
+
throw new wfWAFStorageFileConfigException('Error reading config data, configuration file could be corrupted.');
|
465 |
+
}
|
466 |
+
|
467 |
+
self::lock($this->configFileHandle, LOCK_UN);
|
468 |
+
}
|
469 |
+
|
470 |
+
/**
|
471 |
+
* @throws wfWAFStorageFileException
|
472 |
+
*/
|
473 |
+
public function saveConfig() {
|
474 |
+
if (WFWAF_DEBUG) {
|
475 |
+
error_log('Saving WAF config for change in key ' . $this->dataChanged[0] . ', value: ' .
|
476 |
+
((is_object($this->data[$this->dataChanged[0]]) || $this->dataChanged[0] === 'cron') ?
|
477 |
+
gettype($this->data[$this->dataChanged[0]]) :
|
478 |
+
var_export($this->data[$this->dataChanged[0]], true)));
|
479 |
+
}
|
480 |
+
|
481 |
+
if ($this->uninstalled) {
|
482 |
+
return;
|
483 |
+
}
|
484 |
+
|
485 |
+
wfWAFStorageFile::atomicFilePutContents($this->getConfigFile(), self::LOG_FILE_HEADER . serialize($this->data));
|
486 |
+
}
|
487 |
+
|
488 |
+
/**
|
489 |
+
*
|
490 |
+
*/
|
491 |
+
public function uninstall() {
|
492 |
+
$this->uninstalled = true;
|
493 |
+
$this->close();
|
494 |
+
@unlink($this->getConfigFile());
|
495 |
+
@unlink($this->getAttackDataFile());
|
496 |
+
@unlink($this->getIPCacheFile());
|
497 |
+
@unlink($this->getRulesDSLCacheFile());
|
498 |
+
}
|
499 |
+
|
500 |
+
/**
|
501 |
+
* @return bool
|
502 |
+
*/
|
503 |
+
public function isInLearningMode() {
|
504 |
+
if ($this->getConfig('wafStatus') == 'learning-mode') {
|
505 |
+
if ($this->getConfig('learningModeGracePeriodEnabled')) {
|
506 |
+
if ($this->getConfig('learningModeGracePeriod') > time()) {
|
507 |
+
return true;
|
508 |
+
} else {
|
509 |
+
// Reached the end of the grace period, activate the WAF.
|
510 |
+
$this->setConfig('wafStatus', 'enabled');
|
511 |
+
$this->setConfig('learningModeGracePeriodEnabled', 0);
|
512 |
+
$this->unsetConfig('learningModeGracePeriod');
|
513 |
+
}
|
514 |
+
} else {
|
515 |
+
return true;
|
516 |
+
}
|
517 |
+
}
|
518 |
+
return false;
|
519 |
+
}
|
520 |
+
|
521 |
+
public function isDisabled() {
|
522 |
+
return $this->getConfig('wafStatus') === 'disabled' || $this->getConfig('wafDisabled');
|
523 |
+
}
|
524 |
+
|
525 |
+
/**
|
526 |
+
* @return array
|
527 |
+
*/
|
528 |
+
public function getDefaultConfiguration() {
|
529 |
+
return array(
|
530 |
+
'wafStatus' => 'learning-mode',
|
531 |
+
'learningModeGracePeriodEnabled' => 1,
|
532 |
+
'learningModeGracePeriod' => time() + (86400 * 7),
|
533 |
+
'authKey' => wfWAFUtils::getRandomString(64),
|
534 |
+
);
|
535 |
+
}
|
536 |
+
|
537 |
+
/**
|
538 |
+
* @return mixed
|
539 |
+
*/
|
540 |
+
public function getConfigFile() {
|
541 |
+
return $this->configFile;
|
542 |
+
}
|
543 |
+
|
544 |
+
/**
|
545 |
+
* @param mixed $configFile
|
546 |
+
*/
|
547 |
+
public function setConfigFile($configFile) {
|
548 |
+
$this->configFile = $configFile;
|
549 |
+
}
|
550 |
+
|
551 |
+
/**
|
552 |
+
* @return string|null
|
553 |
+
*/
|
554 |
+
public function getAttackDataFile() {
|
555 |
+
return $this->attackDataFile;
|
556 |
+
}
|
557 |
+
|
558 |
+
/**
|
559 |
+
* @param string|null $attackDataFile
|
560 |
+
*/
|
561 |
+
public function setAttackDataFile($attackDataFile) {
|
562 |
+
$this->attackDataFile = $attackDataFile;
|
563 |
+
}
|
564 |
+
|
565 |
+
/**
|
566 |
+
* @return string|null
|
567 |
+
*/
|
568 |
+
public function getIPCacheFile() {
|
569 |
+
return $this->ipCacheFile;
|
570 |
+
}
|
571 |
+
|
572 |
+
/**
|
573 |
+
* @param string|null $ipCacheFile
|
574 |
+
*/
|
575 |
+
public function setIPCacheFile($ipCacheFile) {
|
576 |
+
$this->ipCacheFile = $ipCacheFile;
|
577 |
+
}
|
578 |
+
|
579 |
+
/**
|
580 |
+
* @return mixed
|
581 |
+
*/
|
582 |
+
public function getRulesDSLCacheFile() {
|
583 |
+
return $this->rulesDSLCacheFile;
|
584 |
+
}
|
585 |
+
|
586 |
+
/**
|
587 |
+
* @param mixed $rulesDSLCacheFile
|
588 |
+
*/
|
589 |
+
public function setRulesDSLCacheFile($rulesDSLCacheFile) {
|
590 |
+
$this->rulesDSLCacheFile = $rulesDSLCacheFile;
|
591 |
+
}
|
592 |
+
|
593 |
+
/**
|
594 |
+
* param key, param value, request string
|
595 |
+
*
|
596 |
+
* @var array
|
597 |
+
*/
|
598 |
+
private $rowsToB64 = array(3, 4, 7);
|
599 |
+
|
600 |
+
/**
|
601 |
+
* @param $row
|
602 |
+
* @return bool|string
|
603 |
+
*/
|
604 |
+
private function serializeRow($row) {
|
605 |
+
foreach ($this->rowsToB64 as $index) {
|
606 |
+
if (array_key_exists($index, $row)) {
|
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;
|
615 |
+
}
|
616 |
+
|
617 |
+
/**
|
618 |
+
* @param $row
|
619 |
+
* @return array|bool|mixed|object
|
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)) {
|
627 |
+
$json[$index] = base64_decode($json[$index]);
|
628 |
+
}
|
629 |
+
}
|
630 |
+
return $json;
|
631 |
+
}
|
632 |
+
}
|
633 |
+
return false;
|
634 |
+
}
|
635 |
+
|
636 |
+
/**
|
637 |
+
* @return wfWAFAttackDataStorageFileEngine
|
638 |
+
*/
|
639 |
+
public function getAttackDataEngine() {
|
640 |
+
return $this->attackDataEngine;
|
641 |
+
}
|
642 |
+
|
643 |
+
/**
|
644 |
+
* @param wfWAFAttackDataStorageFileEngine $attackDataEngine
|
645 |
+
*/
|
646 |
+
public function setAttackDataEngine($attackDataEngine) {
|
647 |
+
$this->attackDataEngine = $attackDataEngine;
|
648 |
+
}
|
649 |
+
}
|
650 |
+
|
651 |
+
class wfWAFAttackDataStorageFileEngine {
|
652 |
+
|
653 |
+
const MAX_ROWS = 10000;
|
654 |
+
const MAX_READ_LENGTH = 51200;
|
655 |
+
const FILE_SIGNATURE = "wfWAF\x00\x00\x00";
|
656 |
+
|
657 |
+
/**
|
658 |
+
* @param string|float|null $microtime
|
659 |
+
* @return string
|
660 |
+
*/
|
661 |
+
public static function packMicrotime($microtime = null) {
|
662 |
+
if ($microtime === null) {
|
663 |
+
$microtime = microtime();
|
664 |
+
}
|
665 |
+
if (is_string($microtime)) {
|
666 |
+
list($msec, $sec) = explode(' ', $microtime, 2);
|
667 |
+
} else if (is_float($microtime)) {
|
668 |
+
list($sec, $msec) = explode('.', (string) $microtime, 2);
|
669 |
+
$msec = '0.' . $msec;
|
670 |
+
} else {
|
671 |
+
throw new InvalidArgumentException(__METHOD__ . ' $microtime expected to be string or float, received '
|
672 |
+
. gettype($microtime));
|
673 |
+
}
|
674 |
+
$msec = $msec * 1000000;
|
675 |
+
return pack('V*', $sec, $msec);
|
676 |
+
}
|
677 |
+
|
678 |
+
/**
|
679 |
+
* @param string $binary
|
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));
|
689 |
+
}
|
690 |
+
|
691 |
+
public static function getCompressionAlgos() {
|
692 |
+
static $compressionFunctions;
|
693 |
+
if ($compressionFunctions === null) {
|
694 |
+
$compressionFunctions = array(
|
695 |
+
new wfWAFStorageFileCompressionGZDeflate(),
|
696 |
+
new wfWAFStorageFileCompressionGZCompress(),
|
697 |
+
new wfWAFStorageFileCompressionGZEncode(),
|
698 |
+
);
|
699 |
+
}
|
700 |
+
return $compressionFunctions;
|
701 |
+
}
|
702 |
+
|
703 |
+
/**
|
704 |
+
* @param string $decompressed
|
705 |
+
* @return mixed
|
706 |
+
*/
|
707 |
+
public static function compress($decompressed) {
|
708 |
+
if (empty($decompressed))
|
709 |
+
return $decompressed;
|
710 |
+
|
711 |
+
$compressionAlgos = self::getCompressionAlgos();
|
712 |
+
/** @var wfWAFStorageFileCompressionAlgo $algo */
|
713 |
+
foreach ($compressionAlgos as $algo) {
|
714 |
+
if ($algo->isUsable() && ($compressed = $algo->testCompression($decompressed)) !== false) {
|
715 |
+
return $compressed;
|
716 |
+
}
|
717 |
+
}
|
718 |
+
return $decompressed;
|
719 |
+
}
|
720 |
+
|
721 |
+
/**
|
722 |
+
* @param string $compressed
|
723 |
+
* @return mixed
|
724 |
+
*/
|
725 |
+
public static function decompress($compressed) {
|
726 |
+
if (empty($compressed))
|
727 |
+
return $compressed;
|
728 |
+
|
729 |
+
$compressionAlgos = self::getCompressionAlgos();
|
730 |
+
/** @var wfWAFStorageFileCompressionAlgo $algo */
|
731 |
+
foreach ($compressionAlgos as $algo) {
|
732 |
+
if ($algo->isUsable() && ($decompressed = $algo->decompress($compressed)) !== false) {
|
733 |
+
return $decompressed;
|
734 |
+
}
|
735 |
+
}
|
736 |
+
return $compressed;
|
737 |
+
}
|
738 |
+
|
739 |
+
|
740 |
+
private $file;
|
741 |
+
private $fileHandle;
|
742 |
+
|
743 |
+
private $header = array();
|
744 |
+
private $offsetTable = array();
|
745 |
+
|
746 |
+
/**
|
747 |
+
* wfWAFStorageFileEngine constructor.
|
748 |
+
* @param string $file
|
749 |
+
*/
|
750 |
+
public function __construct($file) {
|
751 |
+
$this->file = $file;
|
752 |
+
}
|
753 |
+
|
754 |
+
/**
|
755 |
+
* @throws wfWAFStorageFileException
|
756 |
+
*/
|
757 |
+
public function open() {
|
758 |
+
if (is_resource($this->fileHandle)) {
|
759 |
+
return;
|
760 |
+
}
|
761 |
+
if (!file_exists($this->file)) {
|
762 |
+
@file_put_contents($this->file, $this->getDefaultHeader(), LOCK_EX);
|
763 |
+
}
|
764 |
+
$this->fileHandle = @fopen($this->file, 'r+');
|
765 |
+
if (!$this->fileHandle) {
|
766 |
+
throw new wfWAFStorageFileException('Unable to open ' . $this->file . ' for reading and writing.');
|
767 |
+
}
|
768 |
+
}
|
769 |
+
|
770 |
+
/**
|
771 |
+
*
|
772 |
+
*/
|
773 |
+
public function close() {
|
774 |
+
if (is_resource($this->fileHandle)) {
|
775 |
+
fclose($this->fileHandle);
|
776 |
+
}
|
777 |
+
$this->fileHandle = null;
|
778 |
+
$this->header = array();
|
779 |
+
$this->offsetTable = array();
|
780 |
+
}
|
781 |
+
|
782 |
+
/**
|
783 |
+
* @param int $offset
|
784 |
+
* @return int
|
785 |
+
*/
|
786 |
+
private function seek($offset) {
|
787 |
+
return fseek($this->fileHandle, $offset, SEEK_SET);
|
788 |
+
}
|
789 |
+
|
790 |
+
/**
|
791 |
+
* @return int
|
792 |
+
*/
|
793 |
+
private function seekToData() {
|
794 |
+
return $this->seek(strlen($this->getHeaderLength()));
|
795 |
+
}
|
796 |
+
|
797 |
+
/**
|
798 |
+
* @param int $length
|
799 |
+
* @return string
|
800 |
+
*/
|
801 |
+
private function read($length) {
|
802 |
+
if ($length > self::MAX_READ_LENGTH) {
|
803 |
+
$length = self::MAX_READ_LENGTH;
|
804 |
+
}
|
805 |
+
return fread($this->fileHandle, $length);
|
806 |
+
}
|
807 |
+
|
808 |
+
/**
|
809 |
+
* @param string $data
|
810 |
+
* @return int
|
811 |
+
*/
|
812 |
+
private function write($data) {
|
813 |
+
return fwrite($this->fileHandle, $data);
|
814 |
+
}
|
815 |
+
|
816 |
+
/**
|
817 |
+
* @return bool
|
818 |
+
*/
|
819 |
+
private function lockRead() {
|
820 |
+
return wfWAFStorageFile::lock($this->fileHandle, LOCK_SH);
|
821 |
+
}
|
822 |
+
|
823 |
+
/**
|
824 |
+
* @return bool
|
825 |
+
*/
|
826 |
+
private function lockWrite() {
|
827 |
+
return wfWAFStorageFile::lock($this->fileHandle, LOCK_EX);
|
828 |
+
}
|
829 |
+
|
830 |
+
/**
|
831 |
+
* @return bool
|
832 |
+
*/
|
833 |
+
private function unlock() {
|
834 |
+
return wfWAFStorageFile::lock($this->fileHandle, LOCK_UN);
|
835 |
+
}
|
836 |
+
|
837 |
+
/**
|
838 |
+
* @return int
|
839 |
+
*/
|
840 |
+
public function getHeaderLength() {
|
841 |
+
return strlen($this->getDefaultHeader());
|
842 |
+
}
|
843 |
+
|
844 |
+
/**
|
845 |
+
* @return string
|
846 |
+
*/
|
847 |
+
public function getDefaultHeader() {
|
848 |
+
/**
|
849 |
+
* 51 PHP die() header
|
850 |
+
* 8 Signature
|
851 |
+
* 8 oldest 64bit timestamp
|
852 |
+
* 8 newest 64bit timestamp
|
853 |
+
* 4 row count
|
854 |
+
* 1600 offset table
|
855 |
+
* 1 last length
|
856 |
+
*/
|
857 |
+
$headerLength = strlen(wfWAFStorageFile::LOG_FILE_HEADER) + strlen(self::FILE_SIGNATURE) + 8 + 8 + 4 + (self::MAX_ROWS * 4);
|
858 |
+
return wfWAFStorageFile::LOG_FILE_HEADER
|
859 |
+
. self::FILE_SIGNATURE
|
860 |
+
. str_repeat("\x00", 8 + 8 + 4)
|
861 |
+
. pack('V', $headerLength)
|
862 |
+
. str_repeat("\x00", self::MAX_ROWS * 4);
|
863 |
+
}
|
864 |
+
|
865 |
+
/**
|
866 |
+
* @throws wfWAFStorageFileException
|
867 |
+
*/
|
868 |
+
public function unpackHeader() {
|
869 |
+
if ($this->header) {
|
870 |
+
return $this->header;
|
871 |
+
}
|
872 |
+
|
873 |
+
$this->open();
|
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));
|
890 |
+
list(, $this->header['rowCount']) = unpack('V', $this->read(4));
|
891 |
+
$this->header['offsetTable'] = $this->unpackOffsetTable();
|
892 |
+
$this->unlock();
|
893 |
+
return $this->header;
|
894 |
+
}
|
895 |
+
|
896 |
+
/**
|
897 |
+
* @return array
|
898 |
+
*/
|
899 |
+
private function unpackOffsetTable() {
|
900 |
+
if ($this->offsetTable) {
|
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;
|
908 |
+
}
|
909 |
+
|
910 |
+
/**
|
911 |
+
* @param callable $callback
|
912 |
+
*/
|
913 |
+
public function scanRows($callback) {
|
914 |
+
if (!is_callable($callback)) {
|
915 |
+
throw new InvalidArgumentException(__METHOD__ . ' $callback expected to be callable, received ' . gettype($callback));
|
916 |
+
}
|
917 |
+
$this->open();
|
918 |
+
$header = $this->unpackHeader();
|
919 |
+
$this->seekToData();
|
920 |
+
for ($index = 0; $index < $header['rowCount'] && $index < self::MAX_ROWS; $index++) {
|
921 |
+
$offset = $header['offsetTable'][$index];
|
922 |
+
$length = $header['offsetTable'][$index + 1] - $offset;
|
923 |
+
if ($length > self::MAX_READ_LENGTH) {
|
924 |
+
$length = self::MAX_READ_LENGTH;
|
925 |
+
}
|
926 |
+
$result = call_user_func($callback, $this->fileHandle, $offset, $length);
|
927 |
+
if ($result === false) {
|
928 |
+
break;
|
929 |
+
}
|
930 |
+
}
|
931 |
+
}
|
932 |
+
|
933 |
+
/**
|
934 |
+
* @param callable $callback
|
935 |
+
*/
|
936 |
+
public function scanRowsReverse($callback) {
|
937 |
+
if (!is_callable($callback)) {
|
938 |
+
throw new InvalidArgumentException(__METHOD__ . ' $callback expected to be callable, received ' . gettype($callback));
|
939 |
+
}
|
940 |
+
$this->open();
|
941 |
+
$header = $this->unpackHeader();
|
942 |
+
// $this->seekToData();
|
943 |
+
for ($index = min($header['rowCount'], self::MAX_ROWS) - 1; $index >= 0; $index--) {
|
944 |
+
$offset = $header['offsetTable'][$index];
|
945 |
+
$length = $header['offsetTable'][$index + 1] - $offset;
|
946 |
+
if ($length > self::MAX_READ_LENGTH) {
|
947 |
+
$length = self::MAX_READ_LENGTH;
|
948 |
+
}
|
949 |
+
$result = call_user_func($callback, $this->fileHandle, $offset, $length);
|
950 |
+
if ($result === false) {
|
951 |
+
break;
|
952 |
+
}
|
953 |
+
}
|
954 |
+
}
|
955 |
+
|
956 |
+
/**
|
957 |
+
* @param $index
|
958 |
+
* @return wfWAFAttackDataStorageFileEngineRow
|
959 |
+
* @throws wfWAFStorageFileException
|
960 |
+
*/
|
961 |
+
public function getRow($index) {
|
962 |
+
$this->open();
|
963 |
+
$this->header = array();
|
964 |
+
$this->offsetTable = array();
|
965 |
+
$header = $this->unpackHeader();
|
966 |
+
$this->seekToData();
|
967 |
+
if ($index < $header['rowCount'] && $index >= 0) {
|
968 |
+
$offset = $header['offsetTable'][$index];
|
969 |
+
$length = $header['offsetTable'][$index + 1] - $offset;
|
970 |
+
} else {
|
971 |
+
return false;
|
972 |
+
}
|
973 |
+
$this->seek($offset);
|
974 |
+
$this->lockRead();
|
975 |
+
$binary = $this->read($length);
|
976 |
+
$this->unlock();
|
977 |
+
return wfWAFAttackDataStorageFileEngineRow::unpack($binary);
|
978 |
+
}
|
979 |
+
|
980 |
+
/**
|
981 |
+
* @return mixed
|
982 |
+
* @throws wfWAFStorageFileException
|
983 |
+
*/
|
984 |
+
public function getRowCount() {
|
985 |
+
$this->open();
|
986 |
+
$header = $this->unpackHeader();
|
987 |
+
return $header['rowCount'];
|
988 |
+
}
|
989 |
+
|
990 |
+
/**
|
991 |
+
* @param wfWAFAttackDataStorageFileEngineRow $row
|
992 |
+
* @return bool
|
993 |
+
* @throws wfWAFStorageFileException
|
994 |
+
*/
|
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) {
|
1002 |
+
$this->unlock();
|
1003 |
+
return false;
|
1004 |
+
}
|
1005 |
+
|
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();
|
1013 |
+
|
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();
|
1040 |
+
|
1041 |
+
$this->header = array();
|
1042 |
+
$this->offsetTable = array();
|
1043 |
+
|
1044 |
+
return true;
|
1045 |
+
}
|
1046 |
+
|
1047 |
+
/**
|
1048 |
+
*
|
1049 |
+
*/
|
1050 |
+
public function truncate() {
|
1051 |
+
$defaultHeader = $this->getDefaultHeader();
|
1052 |
+
$this->close();
|
1053 |
+
wfWAFStorageFile::atomicFilePutContents($this->getFile(), $defaultHeader, 'attack');
|
1054 |
+
$this->header = array();
|
1055 |
+
$this->offsetTable = array();
|
1056 |
+
$this->open();
|
1057 |
+
}
|
1058 |
+
|
1059 |
+
/**
|
1060 |
+
* @return mixed
|
1061 |
+
* @throws wfWAFStorageFileException
|
1062 |
+
*/
|
1063 |
+
public function getOldestTimestamp() {
|
1064 |
+
$this->open();
|
1065 |
+
if ($this->getRowCount() === 0) {
|
1066 |
+
return false;
|
1067 |
+
}
|
1068 |
+
$header = $this->unpackHeader();
|
1069 |
+
return $header['oldestTimestamp'];
|
1070 |
+
}
|
1071 |
+
|
1072 |
+
/**
|
1073 |
+
* @return mixed
|
1074 |
+
* @throws wfWAFStorageFileException
|
1075 |
+
*/
|
1076 |
+
public function getNewestTimestamp() {
|
1077 |
+
$this->open();
|
1078 |
+
if ($this->getRowCount() === 0) {
|
1079 |
+
return false;
|
1080 |
+
}
|
1081 |
+
$header = $this->unpackHeader();
|
1082 |
+
return $header['newestTimestamp'];
|
1083 |
+
}
|
1084 |
+
|
1085 |
+
/**
|
1086 |
+
* @return string
|
1087 |
+
*/
|
1088 |
+
public function getFile() {
|
1089 |
+
return $this->file;
|
1090 |
+
}
|
1091 |
+
|
1092 |
+
/**
|
1093 |
+
* @param string $file
|
1094 |
+
*/
|
1095 |
+
public function setFile($file) {
|
1096 |
+
$this->file = $file;
|
1097 |
+
}
|
1098 |
+
}
|
1099 |
+
|
1100 |
+
interface wfWAFAttackDataStorageFileEngineScanRowCallback {
|
1101 |
+
|
1102 |
+
public function scanRow($handle, $offset, $length);
|
1103 |
+
}
|
1104 |
+
|
1105 |
+
class wfWAFAttackDataStorageFileEngineResultSet implements wfWAFAttackDataStorageFileEngineScanRowCallback {
|
1106 |
+
|
1107 |
+
private $rows = array();
|
1108 |
+
|
1109 |
+
public function scanRow($handle, $offset, $length) {
|
1110 |
+
fseek($handle, $offset);
|
1111 |
+
$binary = fread($handle, $length);
|
1112 |
+
$this->rows = wfWAFAttackDataStorageFileEngineRow::unpack($binary);
|
1113 |
+
}
|
1114 |
+
|
1115 |
+
/**
|
1116 |
+
* @return array
|
1117 |
+
*/
|
1118 |
+
public function getRows() {
|
1119 |
+
return $this->rows;
|
1120 |
+
}
|
1121 |
+
}
|
1122 |
+
|
1123 |
+
class wfWAFAttackDataStorageFileEngineScanRowAttackDataNewer implements wfWAFAttackDataStorageFileEngineScanRowCallback {
|
1124 |
+
|
1125 |
+
/**
|
1126 |
+
* @var int
|
1127 |
+
*/
|
1128 |
+
private $newerThan;
|
1129 |
+
|
1130 |
+
/**
|
1131 |
+
* wfWAFStorageFileEngineScanRowAttackDataNewer constructor.
|
1132 |
+
* @param int $newerThan
|
1133 |
+
*/
|
1134 |
+
public function __construct($newerThan) {
|
1135 |
+
$this->newerThan = $newerThan;
|
1136 |
+
}
|
1137 |
+
|
1138 |
+
/**
|
1139 |
+
* @param resource $handle
|
1140 |
+
* @param int $offset
|
1141 |
+
* @param int $length
|
1142 |
+
* @return bool
|
1143 |
+
*/
|
1144 |
+
public function scanRow($handle, $offset, $length) {
|
1145 |
+
$attackLogTimeBin = fread($handle, 8);
|
1146 |
+
list(, $attackLogSeconds, $attackLogMicroseconds) = unpack('VV', $attackLogTimeBin);
|
1147 |
+
$attackLogTime = $attackLogSeconds . '.' . $attackLogMicroseconds;
|
1148 |
+
return $this->newerThan < $attackLogTime;
|
1149 |
+
}
|
1150 |
+
}
|
1151 |
+
|
1152 |
+
class wfWAFAttackDataStorageFileEngineScanRowAttackDataOlder implements wfWAFAttackDataStorageFileEngineScanRowCallback {
|
1153 |
+
|
1154 |
+
/**
|
1155 |
+
* @var int
|
1156 |
+
*/
|
1157 |
+
private $olderThan;
|
1158 |
+
|
1159 |
+
/**
|
1160 |
+
* wfWAFStorageFileEngineScanRowAttackDataNewer constructor.
|
1161 |
+
* @param int $olderThan
|
1162 |
+
*/
|
1163 |
+
public function __construct($olderThan) {
|
1164 |
+
$this->olderThan = $olderThan;
|
1165 |
+
}
|
1166 |
+
|
1167 |
+
/**
|
1168 |
+
* @param resource $handle
|
1169 |
+
* @param int $offset
|
1170 |
+
* @param int $length
|
1171 |
+
* @return bool
|
1172 |
+
*/
|
1173 |
+
public function scanRow($handle, $offset, $length) {
|
1174 |
+
$attackLogTimeBin = fread($handle, 8);
|
1175 |
+
list(, $attackLogSeconds, $attackLogMicroseconds) = unpack('VV', $attackLogTimeBin);
|
1176 |
+
$attackLogTime = $attackLogSeconds . '.' . $attackLogMicroseconds;
|
1177 |
+
return $this->olderThan > $attackLogTime;
|
1178 |
+
}
|
1179 |
+
}
|
1180 |
+
|
1181 |
+
class wfWAFAttackDataStorageFileEngineRow {
|
1182 |
+
|
1183 |
+
/**
|
1184 |
+
* @param string $binary
|
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 |
+
|
1193 |
+
/**
|
1194 |
+
* @var float|string
|
1195 |
+
*/
|
1196 |
+
private $timestamp;
|
1197 |
+
/**
|
1198 |
+
* @var string
|
1199 |
+
*/
|
1200 |
+
private $data;
|
1201 |
+
|
1202 |
+
/**
|
1203 |
+
* @param float $timestamp
|
1204 |
+
* @param string $data
|
1205 |
+
*/
|
1206 |
+
public function __construct($timestamp, $data) {
|
1207 |
+
$this->timestamp = $timestamp;
|
1208 |
+
$this->data = $data;
|
1209 |
+
}
|
1210 |
+
|
1211 |
+
/**
|
1212 |
+
* @return string
|
1213 |
+
*/
|
1214 |
+
public function pack() {
|
1215 |
+
return wfWAFAttackDataStorageFileEngine::packMicrotime($this->getTimestamp()) . wfWAFAttackDataStorageFileEngine::compress($this->getData());
|
1216 |
+
}
|
1217 |
+
|
1218 |
+
/**
|
1219 |
+
* @return float|string
|
1220 |
+
*/
|
1221 |
+
public function getTimestamp() {
|
1222 |
+
return $this->timestamp;
|
1223 |
+
}
|
1224 |
+
|
1225 |
+
/**
|
1226 |
+
* @param float|string $timestamp
|
1227 |
+
*/
|
1228 |
+
public function setTimestamp($timestamp) {
|
1229 |
+
$this->timestamp = $timestamp;
|
1230 |
+
}
|
1231 |
+
|
1232 |
+
/**
|
1233 |
+
* @return string
|
1234 |
+
*/
|
1235 |
+
public function getData() {
|
1236 |
+
return $this->data;
|
1237 |
+
}
|
1238 |
+
|
1239 |
+
/**
|
1240 |
+
* @param string $data
|
1241 |
+
*/
|
1242 |
+
public function setData($data) {
|
1243 |
+
$this->data = $data;
|
1244 |
+
}
|
1245 |
+
}
|
1246 |
+
|
1247 |
+
abstract class wfWAFStorageFileCompressionAlgo {
|
1248 |
+
|
1249 |
+
abstract public function isUsable();
|
1250 |
+
|
1251 |
+
abstract public function compress($string);
|
1252 |
+
|
1253 |
+
abstract public function decompress($binary);
|
1254 |
+
|
1255 |
+
/**
|
1256 |
+
* @param string $string
|
1257 |
+
* @return bool
|
1258 |
+
*/
|
1259 |
+
public function testCompression($string) {
|
1260 |
+
$compressed = $this->compress($string);
|
1261 |
+
if ($string === $this->decompress($compressed)) {
|
1262 |
+
return $compressed;
|
1263 |
+
}
|
1264 |
+
return false;
|
1265 |
+
}
|
1266 |
+
}
|
1267 |
+
|
1268 |
+
class wfWAFStorageFileCompressionGZDeflate extends wfWAFStorageFileCompressionAlgo {
|
1269 |
+
|
1270 |
+
public function isUsable() {
|
1271 |
+
return function_exists('gzinflate') && function_exists('gzdeflate');
|
1272 |
+
}
|
1273 |
+
|
1274 |
+
public function compress($string) {
|
1275 |
+
return @gzdeflate($string);
|
1276 |
+
}
|
1277 |
+
|
1278 |
+
public function decompress($binary) {
|
1279 |
+
return @gzinflate($binary);
|
1280 |
+
}
|
1281 |
+
}
|
1282 |
+
|
1283 |
+
class wfWAFStorageFileCompressionGZCompress extends wfWAFStorageFileCompressionAlgo {
|
1284 |
+
|
1285 |
+
public function isUsable() {
|
1286 |
+
return function_exists('gzuncompress') && function_exists('gzcompress');
|
1287 |
+
}
|
1288 |
+
|
1289 |
+
public function compress($string) {
|
1290 |
+
return @gzcompress($string);
|
1291 |
+
}
|
1292 |
+
|
1293 |
+
public function decompress($binary) {
|
1294 |
+
return @gzuncompress($binary);
|
1295 |
+
}
|
1296 |
+
}
|
1297 |
+
|
1298 |
+
class wfWAFStorageFileCompressionGZEncode extends wfWAFStorageFileCompressionAlgo {
|
1299 |
+
|
1300 |
+
public function isUsable() {
|
1301 |
+
return function_exists('gzencode') && function_exists('gzdecode');
|
1302 |
+
}
|
1303 |
+
|
1304 |
+
public function compress($string) {
|
1305 |
+
return @gzencode($string);
|
1306 |
+
}
|
1307 |
+
|
1308 |
+
public function decompress($binary) {
|
1309 |
+
return @gzdecode($binary);
|
1310 |
+
}
|
1311 |
+
}
|
1312 |
+
|
1313 |
+
class wfWAFStorageFileException extends wfWAFException {
|
1314 |
+
|
1315 |
+
}
|
1316 |
+
|
1317 |
+
class wfWAFStorageFileConfigException extends wfWAFStorageFileException {
|
1318 |
+
|
1319 |
+
}
|
1320 |
+
|
vendor/wordfence/wf-waf/src/lib/utils.php
ADDED
@@ -0,0 +1,321 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class wfWAFUtils {
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Return dot or colon notation of IPv4 or IPv6 address.
|
7 |
+
*
|
8 |
+
* @param string $ip
|
9 |
+
* @return string|bool
|
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 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Return the packed binary string of an IPv4 or IPv6 address.
|
21 |
+
*
|
22 |
+
* @param string $ip
|
23 |
+
* @return string
|
24 |
+
*/
|
25 |
+
public static function inet_pton($ip) {
|
26 |
+
// convert the 4 char IPv4 to IPv6 mapped version.
|
27 |
+
$pton = str_pad(self::hasIPv6Support() ? inet_pton($ip) : self::_inet_pton($ip), 16,
|
28 |
+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00", STR_PAD_LEFT);
|
29 |
+
return $pton;
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Added compatibility for hosts that do not have inet_pton.
|
34 |
+
*
|
35 |
+
* @param $ip
|
36 |
+
* @return bool|string
|
37 |
+
*/
|
38 |
+
public static function _inet_pton($ip) {
|
39 |
+
// IPv4
|
40 |
+
if (preg_match('/^(?:\d{1,3}(?:\.|$)){4}/', $ip)) {
|
41 |
+
$octets = explode('.', $ip);
|
42 |
+
$bin = chr($octets[0]) . chr($octets[1]) . chr($octets[2]) . chr($octets[3]);
|
43 |
+
return $bin;
|
44 |
+
}
|
45 |
+
|
46 |
+
// IPv6
|
47 |
+
if (preg_match('/^((?:[\da-f]{1,4}(?::|)){0,8})(::)?((?:[\da-f]{1,4}(?::|)){0,8})$/i', $ip)) {
|
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 |
+
|
59 |
+
$ip_groups = explode(':', $ip);
|
60 |
+
$ipv6_bin = '';
|
61 |
+
foreach ($ip_groups as $ip_group) {
|
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
|
69 |
+
if (preg_match('/^((?:0{1,4}(?::|)){0,5})(::)?ffff:((?:\d{1,3}(?:\.|$)){4})$/i', $ip, $matches)) {
|
70 |
+
$octets = explode('.', $matches[3]);
|
71 |
+
return "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff" . chr($octets[0]) . chr($octets[1]) . chr($octets[2]) . chr($octets[3]);
|
72 |
+
}
|
73 |
+
|
74 |
+
return false;
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Added compatibility for hosts that do not have inet_ntop.
|
79 |
+
*
|
80 |
+
* @param $ip
|
81 |
+
* @return bool|string
|
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 |
+
|
97 |
+
$hex = bin2hex($ip);
|
98 |
+
$groups = str_split($hex, 4);
|
99 |
+
$collapse = false;
|
100 |
+
$done_collapse = false;
|
101 |
+
foreach ($groups as $index => $group) {
|
102 |
+
if ($group == '0000' && !$done_collapse) {
|
103 |
+
if (!$collapse) {
|
104 |
+
$groups[$index] = ':';
|
105 |
+
} else {
|
106 |
+
$groups[$index] = '';
|
107 |
+
}
|
108 |
+
$collapse = true;
|
109 |
+
} else if ($collapse) {
|
110 |
+
$done_collapse = true;
|
111 |
+
$collapse = false;
|
112 |
+
}
|
113 |
+
$groups[$index] = ltrim($groups[$index], '0');
|
114 |
+
}
|
115 |
+
$ip = join(':', array_filter($groups));
|
116 |
+
$ip = str_replace(':::', '::', $ip);
|
117 |
+
return $ip == ':' ? '::' : $ip;
|
118 |
+
}
|
119 |
+
|
120 |
+
return false;
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Verify PHP was compiled with IPv6 support.
|
125 |
+
*
|
126 |
+
* Some hosts appear to not have inet_ntop, and others appear to have inet_ntop but are unable to process IPv6 addresses.
|
127 |
+
*
|
128 |
+
* @return bool
|
129 |
+
*/
|
130 |
+
public static function hasIPv6Support() {
|
131 |
+
return defined('AF_INET6');
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Compare two strings in constant time. It can leak the length of a string.
|
136 |
+
*
|
137 |
+
* @param string $a Expected string.
|
138 |
+
* @param string $b Actual string.
|
139 |
+
* @return bool Whether strings are equal.
|
140 |
+
*/
|
141 |
+
public static function hash_equals($a, $b) {
|
142 |
+
$a_length = strlen($a);
|
143 |
+
if ($a_length !== strlen($b)) {
|
144 |
+
return false;
|
145 |
+
}
|
146 |
+
$result = 0;
|
147 |
+
|
148 |
+
// Do not attempt to "optimize" this.
|
149 |
+
for ($i = 0; $i < $a_length; $i++) {
|
150 |
+
$result |= ord($a[$i]) ^ ord($b[$i]);
|
151 |
+
}
|
152 |
+
|
153 |
+
return $result === 0;
|
154 |
+
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* @param $algo
|
158 |
+
* @param $data
|
159 |
+
* @param $key
|
160 |
+
* @param bool|false $raw_output
|
161 |
+
* @return bool|string
|
162 |
+
*/
|
163 |
+
public static function hash_hmac($algo, $data, $key, $raw_output = false) {
|
164 |
+
if (function_exists('hash_hmac')) {
|
165 |
+
return hash_hmac($algo, $data, $key, $raw_output);
|
166 |
+
}
|
167 |
+
return self::_hash_hmac($algo, $data, $key, $raw_output);
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* @param $algo
|
172 |
+
* @param $data
|
173 |
+
* @param $key
|
174 |
+
* @param bool|false $raw_output
|
175 |
+
* @return bool|string
|
176 |
+
*/
|
177 |
+
private static function _hash_hmac($algo, $data, $key, $raw_output = false) {
|
178 |
+
$packs = array('md5' => 'H32', 'sha1' => 'H40');
|
179 |
+
|
180 |
+
if (!isset($packs[$algo]))
|
181 |
+
return false;
|
182 |
+
|
183 |
+
$pack = $packs[$algo];
|
184 |
+
|
185 |
+
if (strlen($key) > 64)
|
186 |
+
$key = pack($pack, $algo($key));
|
187 |
+
|
188 |
+
$key = str_pad($key, 64, chr(0));
|
189 |
+
|
190 |
+
$ipad = (substr($key, 0, 64) ^ str_repeat(chr(0x36), 64));
|
191 |
+
$opad = (substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64));
|
192 |
+
|
193 |
+
$hmac = $algo($opad . pack($pack, $algo($ipad . $data)));
|
194 |
+
|
195 |
+
if ($raw_output)
|
196 |
+
return pack($pack, $hmac);
|
197 |
+
return $hmac;
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* @param int $length
|
202 |
+
* @param string $chars
|
203 |
+
* @return string
|
204 |
+
*/
|
205 |
+
public static function getRandomString($length = 16, $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_ []{}<>~`+=,.;:/?|') {
|
206 |
+
// This is faster than calling self::random_int for $length
|
207 |
+
$bytes = self::random_bytes($length);
|
208 |
+
$return = '';
|
209 |
+
$maxIndex = strlen($chars) - 1;
|
210 |
+
for ($i = 0; $i < $length; $i++) {
|
211 |
+
$fp = (float) ord($bytes[$i]) / 255.0; // convert to [0,1]
|
212 |
+
$index = (int) (round($fp * $maxIndex));
|
213 |
+
$return .= $chars[$index];
|
214 |
+
}
|
215 |
+
return $return;
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* Polyfill for random_bytes.
|
220 |
+
*
|
221 |
+
* @param int $bytes
|
222 |
+
* @return string
|
223 |
+
*/
|
224 |
+
public static function random_bytes($bytes) {
|
225 |
+
$bytes = (int) $bytes;
|
226 |
+
if (function_exists('random_bytes')) {
|
227 |
+
try {
|
228 |
+
$rand = random_bytes($bytes);
|
229 |
+
if (is_string($rand) && strlen($rand) === $bytes) {
|
230 |
+
return $rand;
|
231 |
+
}
|
232 |
+
} catch (Exception $e) {
|
233 |
+
// Fall through
|
234 |
+
} catch (TypeError $e) {
|
235 |
+
// Fall through
|
236 |
+
} catch (Error $e) {
|
237 |
+
// Fall through
|
238 |
+
}
|
239 |
+
}
|
240 |
+
if (function_exists('mcrypt_create_iv')) {
|
241 |
+
$rand = @mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);
|
242 |
+
if (is_string($rand) && strlen($rand) === $bytes) {
|
243 |
+
return $rand;
|
244 |
+
}
|
245 |
+
}
|
246 |
+
if (function_exists('openssl_random_pseudo_bytes')) {
|
247 |
+
$rand = @openssl_random_pseudo_bytes($bytes, $strong);
|
248 |
+
if (is_string($rand) && strlen($rand) === $bytes) {
|
249 |
+
return $rand;
|
250 |
+
}
|
251 |
+
}
|
252 |
+
// Last resort is insecure
|
253 |
+
$return = '';
|
254 |
+
for ($i = 0; $i < $bytes; $i++) {
|
255 |
+
$return .= chr(mt_rand(0, 255));
|
256 |
+
}
|
257 |
+
return $return;
|
258 |
+
}
|
259 |
+
|
260 |
+
/**
|
261 |
+
* Polyfill for random_int.
|
262 |
+
*
|
263 |
+
* @param int $min
|
264 |
+
* @param int $max
|
265 |
+
* @return int
|
266 |
+
*/
|
267 |
+
public static function random_int($min = 0, $max = 0x7FFFFFFF) {
|
268 |
+
if (function_exists('random_int')) {
|
269 |
+
try {
|
270 |
+
return random_int($min, $max);
|
271 |
+
} catch (Exception $e) {
|
272 |
+
// Fall through
|
273 |
+
} catch (TypeError $e) {
|
274 |
+
// Fall through
|
275 |
+
} catch (Error $e) {
|
276 |
+
// Fall through
|
277 |
+
}
|
278 |
+
}
|
279 |
+
$diff = $max - $min;
|
280 |
+
$bytes = self::random_bytes(4);
|
281 |
+
if ($bytes === false || strlen($bytes) != 4) {
|
282 |
+
throw new RuntimeException("Unable to get 4 bytes");
|
283 |
+
}
|
284 |
+
$val = unpack("Nint", $bytes);
|
285 |
+
$val = $val['int'] & 0x7FFFFFFF;
|
286 |
+
$fp = (float) $val / 2147483647.0; // convert to [0,1]
|
287 |
+
return (int) (round($fp * $diff) + $min);
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* @param mixed $subject
|
292 |
+
* @return array|string
|
293 |
+
*/
|
294 |
+
public static function stripMagicQuotes($subject) {
|
295 |
+
$sybase = ini_get('magic_quotes_sybase');
|
296 |
+
$sybaseEnabled = ((is_numeric($sybase) && $sybase) ||
|
297 |
+
(is_string($sybase) && $sybase && !in_array(strtolower($sybase), array(
|
298 |
+
'off',
|
299 |
+
'false'
|
300 |
+
))));
|
301 |
+
if ((function_exists("get_magic_quotes_gpc") && get_magic_quotes_gpc()) || $sybaseEnabled) {
|
302 |
+
return self::stripslashes_deep($subject);
|
303 |
+
}
|
304 |
+
return $subject;
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* @param mixed $subject
|
309 |
+
* @return array|string
|
310 |
+
*/
|
311 |
+
public static function stripslashes_deep($subject) {
|
312 |
+
if (is_array($subject)) {
|
313 |
+
return array_map(array(
|
314 |
+
'self', 'stripslashes_deep',
|
315 |
+
), $subject);
|
316 |
+
} else if (is_string($subject)) {
|
317 |
+
return stripslashes($subject);
|
318 |
+
}
|
319 |
+
return $subject;
|
320 |
+
}
|
321 |
+
}
|
vendor/wordfence/wf-waf/src/lib/view.php
ADDED
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class wfWAFView {
|
4 |
+
|
5 |
+
/**
|
6 |
+
* @var string
|
7 |
+
*/
|
8 |
+
protected $viewPath;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @var string
|
12 |
+
*/
|
13 |
+
protected $viewFileExtension = '.php';
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @var string
|
17 |
+
*/
|
18 |
+
protected $view;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var array
|
22 |
+
*/
|
23 |
+
protected $data;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @param string $view
|
27 |
+
* @param array $data
|
28 |
+
* @return wfWAFView
|
29 |
+
*/
|
30 |
+
public static function create($view, $data = array()) {
|
31 |
+
return new self($view, $data);
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @param string $view
|
36 |
+
* @param array $data
|
37 |
+
*/
|
38 |
+
public function __construct($view, $data = array()) {
|
39 |
+
$this->viewPath = WFWAF_VIEW_PATH;
|
40 |
+
$this->view = $view;
|
41 |
+
$this->data = $data;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* @return string
|
46 |
+
* @throws wfWAFViewNotFoundException
|
47 |
+
*/
|
48 |
+
public function render() {
|
49 |
+
$view = preg_replace('/\.{2,}/', '.', $this->view);
|
50 |
+
$viewPath = $this->viewPath . '/' . $view . $this->viewFileExtension;
|
51 |
+
if (!file_exists($viewPath)) {
|
52 |
+
throw new wfWAFViewNotFoundException('The view ' . $viewPath . ' does not exist or is not readable.');
|
53 |
+
}
|
54 |
+
|
55 |
+
extract($this->data, EXTR_SKIP);
|
56 |
+
|
57 |
+
ob_start();
|
58 |
+
/** @noinspection PhpIncludeInspection */
|
59 |
+
include $viewPath;
|
60 |
+
return ob_get_clean();
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* @return string
|
65 |
+
*/
|
66 |
+
public function __toString() {
|
67 |
+
try {
|
68 |
+
return $this->render();
|
69 |
+
} catch (wfWAFViewNotFoundException $e) {
|
70 |
+
return defined('WFWAF_DEBUG') && WFWAF_DEBUG ? $e->getMessage() : 'The view could not be loaded.';
|
71 |
+
}
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @param $data
|
76 |
+
* @return $this
|
77 |
+
*/
|
78 |
+
public function addData($data) {
|
79 |
+
$this->data = array_merge($data, $this->data);
|
80 |
+
return $this;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* @return array
|
85 |
+
*/
|
86 |
+
public function getData() {
|
87 |
+
return $this->data;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* @param array $data
|
92 |
+
* @return $this
|
93 |
+
*/
|
94 |
+
public function setData($data) {
|
95 |
+
$this->data = $data;
|
96 |
+
return $this;
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* @return string
|
101 |
+
*/
|
102 |
+
public function getView() {
|
103 |
+
return $this->view;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* @param string $view
|
108 |
+
* @return $this
|
109 |
+
*/
|
110 |
+
public function setView($view) {
|
111 |
+
$this->view = $view;
|
112 |
+
return $this;
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Prevent POP
|
117 |
+
*/
|
118 |
+
public function __wakeup() {
|
119 |
+
$this->viewPath = WFWAF_VIEW_PATH;
|
120 |
+
$this->view = null;
|
121 |
+
$this->data = array();
|
122 |
+
$this->viewFileExtension = '.php';
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
class wfWAFViewNotFoundException extends Exception {
|
127 |
+
}
|
vendor/wordfence/wf-waf/src/lib/waf.php
ADDED
@@ -0,0 +1,1545 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class wfWAF {
|
4 |
+
|
5 |
+
const AUTH_COOKIE = 'wfwaf-authcookie';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* @var wfWAF
|
9 |
+
*/
|
10 |
+
private static $instance;
|
11 |
+
private $blacklistedParams;
|
12 |
+
private $whitelistedParams;
|
13 |
+
private $variables = array();
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @return wfWAF
|
17 |
+
*/
|
18 |
+
public static function getInstance() {
|
19 |
+
return self::$instance;
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @param wfWAF $instance
|
24 |
+
*/
|
25 |
+
public static function setInstance($instance) {
|
26 |
+
self::$instance = $instance;
|
27 |
+
}
|
28 |
+
|
29 |
+
protected $rulesFile;
|
30 |
+
protected $trippedRules = array();
|
31 |
+
protected $failedRules = array();
|
32 |
+
protected $scores = array();
|
33 |
+
protected $scoresXSS = array();
|
34 |
+
protected $failScores = array();
|
35 |
+
protected $rules = array();
|
36 |
+
/**
|
37 |
+
* @var wfWAFRequestInterface
|
38 |
+
*/
|
39 |
+
private $request;
|
40 |
+
/**
|
41 |
+
* @var wfWAFStorageInterface
|
42 |
+
*/
|
43 |
+
private $storageEngine;
|
44 |
+
/**
|
45 |
+
* @var wfWAFEventBus
|
46 |
+
*/
|
47 |
+
private $eventBus;
|
48 |
+
private $debug = array();
|
49 |
+
private $disabledRules;
|
50 |
+
private $publicKey = '-----BEGIN PUBLIC KEY-----
|
51 |
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzovUDp/qu7r6LT5d8dLL
|
52 |
+
H/87aRrCjUd6XtnG+afAPVfMKNp4u4L+UuYfw1RfpfquP/zLMGdfmJCUp/oJywkW
|
53 |
+
Rkqo+y7pDuqIFQ59dHvizmYQRvaZgvincBDpey5Ek9AFfB9fqYYnH9+eQw8eLdQi
|
54 |
+
h6Zsh8RsuxFM2BW6JD9Km7L5Lyxw9jU+lye7I3ICYtUOVxc3n3bJT2SiIwHK57pW
|
55 |
+
g/asJEUDiYQzsaa90YPOLdf1Ysz2rkgnCduQaEGz/RPhgUrmZfKwq8puEmkh7Yee
|
56 |
+
auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
|
57 |
+
1QIDAQAB
|
58 |
+
-----END PUBLIC KEY-----';
|
59 |
+
|
60 |
+
|
61 |
+
/**
|
62 |
+
* @param wfWAFRequestInterface $request
|
63 |
+
* @param wfWAFStorageInterface $storageEngine
|
64 |
+
* @param wfWAFEventBus $eventBus
|
65 |
+
* @param string|null $rulesFile
|
66 |
+
*/
|
67 |
+
public function __construct($request, $storageEngine, $eventBus = null, $rulesFile = null) {
|
68 |
+
$this->setRequest($request);
|
69 |
+
$this->setStorageEngine($storageEngine);
|
70 |
+
$this->setEventBus($eventBus ? $eventBus : new wfWAFEventBus);
|
71 |
+
$this->setCompiledRulesFile($rulesFile === null ? WFWAF_PATH . 'rules.php' : $rulesFile);
|
72 |
+
}
|
73 |
+
|
74 |
+
public function getGlobal($global) {
|
75 |
+
if (strpos($global, '.') === false) {
|
76 |
+
return null;
|
77 |
+
}
|
78 |
+
list($prefix, $_global) = explode('.', $global);
|
79 |
+
switch ($prefix) {
|
80 |
+
case 'request':
|
81 |
+
$method = "get" . ucfirst($_global);
|
82 |
+
if (method_exists('wfWAFRequestInterface', $method)) {
|
83 |
+
return call_user_func(array(
|
84 |
+
$this->getRequest(),
|
85 |
+
$method,
|
86 |
+
));
|
87 |
+
}
|
88 |
+
break;
|
89 |
+
case 'server':
|
90 |
+
$key = strtoupper($_global);
|
91 |
+
if (isset($_SERVER) && array_key_exists($key, $_SERVER)) {
|
92 |
+
return $_SERVER[$key];
|
93 |
+
}
|
94 |
+
break;
|
95 |
+
}
|
96 |
+
return null;
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
*
|
101 |
+
*/
|
102 |
+
public function runCron() {
|
103 |
+
if ((
|
104 |
+
$this->getStorageEngine()->getConfig('attackDataNextInterval', null) === null ||
|
105 |
+
$this->getStorageEngine()->getConfig('attackDataNextInterval', time() + 0xffff) <= time()
|
106 |
+
) &&
|
107 |
+
$this->getStorageEngine()->hasPreviousAttackData(microtime(true) - (60 * 5))
|
108 |
+
) {
|
109 |
+
$this->sendAttackData();
|
110 |
+
}
|
111 |
+
$cron = $this->getStorageEngine()->getConfig('cron');
|
112 |
+
if (is_array($cron)) {
|
113 |
+
/** @var wfWAFCronEvent $event */
|
114 |
+
foreach ($cron as $index => $event) {
|
115 |
+
$event->setWaf($this);
|
116 |
+
if ($event->isInPast()) {
|
117 |
+
$event->fire();
|
118 |
+
$newEvent = $event->reschedule();
|
119 |
+
if ($newEvent instanceof wfWAFCronEvent && $newEvent !== $event) {
|
120 |
+
$cron[$index] = $newEvent;
|
121 |
+
} else {
|
122 |
+
unset($cron[$index]);
|
123 |
+
}
|
124 |
+
}
|
125 |
+
}
|
126 |
+
}
|
127 |
+
$this->getStorageEngine()->setConfig('cron', $cron);
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
*
|
132 |
+
*/
|
133 |
+
public function run() {
|
134 |
+
$this->loadRules();
|
135 |
+
if ($this->isDisabled()) {
|
136 |
+
$this->eventBus->wafDisabled();
|
137 |
+
return;
|
138 |
+
}
|
139 |
+
$this->runMigrations();
|
140 |
+
$request = $this->getRequest();
|
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) {
|
148 |
+
$path = isset($urlParam['path']) ? $urlParam['path'] : false;
|
149 |
+
$paramKey = isset($urlParam['paramKey']) ? $urlParam['paramKey'] : false;
|
150 |
+
$ruleID = isset($urlParam['ruleID']) ? $urlParam['ruleID'] : false;
|
151 |
+
if ($path && $paramKey && $ruleID) {
|
152 |
+
$this->whitelistRuleForParam($path, $paramKey, $ruleID, array(
|
153 |
+
'timestamp' => time(),
|
154 |
+
'description' => 'Whitelisted by via false positive dialog',
|
155 |
+
'ip' => $request->getIP(),
|
156 |
+
));
|
157 |
+
$whitelistCount++;
|
158 |
+
}
|
159 |
+
}
|
160 |
+
exit("Successfully whitelisted $whitelistCount params.");
|
161 |
+
}
|
162 |
+
}
|
163 |
+
|
164 |
+
$ip = $this->getRequest()->getIP();
|
165 |
+
if ($this->isIPBlocked($ip)) {
|
166 |
+
$this->eventBus->prevBlocked($ip);
|
167 |
+
$e = new wfWAFBlockException();
|
168 |
+
$this->blockAction($e);
|
169 |
+
}
|
170 |
+
|
171 |
+
try {
|
172 |
+
$this->eventBus->beforeRunRules();
|
173 |
+
$this->runRules();
|
174 |
+
$this->eventBus->afterRunRules();
|
175 |
+
|
176 |
+
} catch (wfWAFAllowException $e) {
|
177 |
+
// Do nothing
|
178 |
+
$this->eventBus->allow($ip, $e);
|
179 |
+
|
180 |
+
} catch (wfWAFBlockException $e) {
|
181 |
+
$this->eventBus->block($ip, $e);
|
182 |
+
$this->blockAction($e);
|
183 |
+
|
184 |
+
} catch (wfWAFBlockXSSException $e) {
|
185 |
+
$this->eventBus->blockXSS($ip, $e);
|
186 |
+
$this->blockXSSAction($e);
|
187 |
+
|
188 |
+
} catch (wfWAFBlockSQLiException $e) {
|
189 |
+
$this->eventBus->blockSQLi($ip, $e);
|
190 |
+
$this->blockAction($e);
|
191 |
+
}
|
192 |
+
|
193 |
+
$this->runCron();
|
194 |
+
|
195 |
+
// Check if this is signed request and update ruleset.
|
196 |
+
|
197 |
+
$ping = $this->getRequest()->getBody('ping');
|
198 |
+
$pingResponse = $this->getRequest()->getBody('ping_response');
|
199 |
+
$wfIP = $this->isWordfenceIP($this->getRequest()->getIP());
|
200 |
+
$pingIsApiKey = wfWAFUtils::hash_equals($ping, sha1($this->getStorageEngine()->getConfig('apiKey')));
|
201 |
+
|
202 |
+
if ($ping && $pingResponse && $pingIsApiKey &&
|
203 |
+
$this->verifySignedRequest($this->getRequest()->getBody('signature'), $this->getStorageEngine()->getConfig('apiKey'))
|
204 |
+
) {
|
205 |
+
// $this->updateRuleSet(base64_decode($this->getRequest()->body('ping')));
|
206 |
+
$event = new wfWAFCronFetchRulesEvent(time() - 2);
|
207 |
+
$event->setWaf($this);
|
208 |
+
$event->fire();
|
209 |
+
|
210 |
+
header('Content-type: text/plain');
|
211 |
+
$pingResponse = preg_replace('/[a-zA-Z0-9]/', '', $this->getRequest()->getBody('ping_response'));
|
212 |
+
exit('Success: ' . sha1($this->getStorageEngine()->getConfig('apiKey') . $pingResponse));
|
213 |
+
}
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
*
|
218 |
+
*/
|
219 |
+
public function loadRules() {
|
220 |
+
if (file_exists($this->getCompiledRulesFile())) {
|
221 |
+
// Acquire lock on this file so we're not including it while it's being written in another process.
|
222 |
+
$handle = fopen($this->getCompiledRulesFile(), 'r');
|
223 |
+
flock($handle, LOCK_SH);
|
224 |
+
/** @noinspection PhpIncludeInspection */
|
225 |
+
include $this->getCompiledRulesFile();
|
226 |
+
flock($handle, LOCK_UN);
|
227 |
+
fclose($handle);
|
228 |
+
}
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* @throws wfWAFAllowException|wfWAFBlockException|wfWAFBlockXSSException
|
233 |
+
*/
|
234 |
+
public function runRules() {
|
235 |
+
/**
|
236 |
+
* @var int $ruleID
|
237 |
+
* @var wfWAFRule $rule
|
238 |
+
*/
|
239 |
+
foreach ($this->getRules() as $ruleID => $rule) {
|
240 |
+
if (!$this->isRuleDisabled($ruleID)) {
|
241 |
+
$rule->evaluate();
|
242 |
+
}
|
243 |
+
}
|
244 |
+
|
245 |
+
$blockActions = array();
|
246 |
+
foreach ($this->failedRules as $paramKey => $categories) {
|
247 |
+
foreach ($categories as $category => $failedRules) {
|
248 |
+
foreach ($failedRules as $failedRule) {
|
249 |
+
/**
|
250 |
+
* @var wfWAFRule $rule
|
251 |
+
* @var wfWAFRuleComparisonFailure $failedComparison
|
252 |
+
*/
|
253 |
+
$rule = $failedRule['rule'];
|
254 |
+
$failedComparison = $failedRule['failedComparison'];
|
255 |
+
$action = $failedRule['action'];
|
256 |
+
|
257 |
+
$score = $rule->getScore();
|
258 |
+
if ($failedComparison->hasMultiplier()) {
|
259 |
+
$score *= $failedComparison->getMultiplier();
|
260 |
+
}
|
261 |
+
if (!isset($this->failScores[$category])) {
|
262 |
+
$this->failScores[$category] = 100;
|
263 |
+
}
|
264 |
+
if (!isset($this->scores[$paramKey][$category])) {
|
265 |
+
$this->scores[$paramKey][$category] = 0;
|
266 |
+
}
|
267 |
+
$this->scores[$paramKey][$category] += $score;
|
268 |
+
if ($this->scores[$paramKey][$category] >= $this->failScores[$category]) {
|
269 |
+
$blockActions[$category] = array(
|
270 |
+
'paramKey' => $paramKey,
|
271 |
+
'score' => $this->scores[$paramKey][$category],
|
272 |
+
'action' => $action,
|
273 |
+
'rule' => $rule,
|
274 |
+
'failedComparison' => $failedComparison,
|
275 |
+
);
|
276 |
+
}
|
277 |
+
$this->debug[] = sprintf("%s tripped %s for %s->%s('%s'). Score %d/%d", $paramKey, $action,
|
278 |
+
$category, $failedComparison->getAction(), $failedComparison->getExpected(),
|
279 |
+
$this->scores[$paramKey][$category], $this->failScores[$category]);
|
280 |
+
}
|
281 |
+
}
|
282 |
+
}
|
283 |
+
|
284 |
+
uasort($blockActions, array($this, 'sortBlockActions'));
|
285 |
+
foreach ($blockActions as $blockAction) {
|
286 |
+
call_user_func(array($this, $blockAction['action']), $blockAction['rule'], $blockAction['failedComparison'], false);
|
287 |
+
}
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* @param array $a
|
292 |
+
* @param array $b
|
293 |
+
* @return int
|
294 |
+
*/
|
295 |
+
private function sortBlockActions($a, $b) {
|
296 |
+
if ($a['score'] == $b['score']) {
|
297 |
+
return 0;
|
298 |
+
}
|
299 |
+
return ($a['score'] > $b['score']) ? -1 : 1;
|
300 |
+
}
|
301 |
+
|
302 |
+
protected function runMigrations() {
|
303 |
+
$currentVersion = $this->getStorageEngine()->getConfig('version');
|
304 |
+
if (!$currentVersion || version_compare($currentVersion, WFWAF_VERSION) === -1) {
|
305 |
+
if (!$currentVersion) {
|
306 |
+
$cron = array(
|
307 |
+
new wfWAFCronFetchRulesEvent(time() +
|
308 |
+
(86400 * ($this->getStorageEngine()->getConfig('isPaid') ? .5 : 7))),
|
309 |
+
);
|
310 |
+
$this->getStorageEngine()->setConfig('cron', $cron);
|
311 |
+
}
|
312 |
+
|
313 |
+
// Any migrations to newer versions go here.
|
314 |
+
$this->getStorageEngine()->setConfig('version', WFWAF_VERSION);
|
315 |
+
}
|
316 |
+
}
|
317 |
+
|
318 |
+
/**
|
319 |
+
* @param wfWAFRule $rule
|
320 |
+
*/
|
321 |
+
public function tripRule($rule) {
|
322 |
+
$this->trippedRules[] = $rule;
|
323 |
+
$action = $rule->getAction();
|
324 |
+
$scores = $rule->getScore();
|
325 |
+
$categories = $rule->getCategory();
|
326 |
+
if (is_array($categories)) {
|
327 |
+
for ($i = 0; $i < count($categories); $i++) {
|
328 |
+
if (is_array($action) && !empty($action[$i])) {
|
329 |
+
$a = $action[$i];
|
330 |
+
} else {
|
331 |
+
$a = $action;
|
332 |
+
}
|
333 |
+
if ($this->isAllowedAction($a)) {
|
334 |
+
$r = clone $rule;
|
335 |
+
$r->setScore($scores[$i]);
|
336 |
+
$r->setCategory($categories[$i]);
|
337 |
+
/** @var wfWAFRuleComparisonFailure $failed */
|
338 |
+
foreach ($r->getComparisonGroup()->getFailedComparisons() as $failed) {
|
339 |
+
call_user_func(array($this, $a), $r, $failed);
|
340 |
+
}
|
341 |
+
}
|
342 |
+
}
|
343 |
+
} else {
|
344 |
+
if ($this->isAllowedAction($action)) {
|
345 |
+
/** @var wfWAFRuleComparisonFailure $failed */
|
346 |
+
foreach ($rule->getComparisonGroup()->getFailedComparisons() as $failed) {
|
347 |
+
call_user_func(array($this, $action), $rule, $failed);
|
348 |
+
}
|
349 |
+
}
|
350 |
+
}
|
351 |
+
}
|
352 |
+
|
353 |
+
/**
|
354 |
+
* @return bool
|
355 |
+
*/
|
356 |
+
public function isInLearningMode() {
|
357 |
+
return $this->getStorageEngine()->isInLearningMode();
|
358 |
+
}
|
359 |
+
|
360 |
+
/**
|
361 |
+
* @return bool
|
362 |
+
*/
|
363 |
+
public function isDisabled() {
|
364 |
+
return $this->getStorageEngine()->isDisabled() || !WFWAF_ENABLED;
|
365 |
+
}
|
366 |
+
|
367 |
+
public function hasOpenSSL() {
|
368 |
+
return function_exists('openssl_verify');
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* @param string $signature
|
373 |
+
* @param string $data
|
374 |
+
* @return bool
|
375 |
+
*/
|
376 |
+
public function verifySignedRequest($signature, $data) {
|
377 |
+
if (!$this->hasOpenSSL()) {
|
378 |
+
return false;
|
379 |
+
}
|
380 |
+
$valid = openssl_verify($data, $signature, $this->getPublicKey(), OPENSSL_ALGO_SHA1);
|
381 |
+
return $valid === 1;
|
382 |
+
}
|
383 |
+
|
384 |
+
/**
|
385 |
+
* @param string $hash
|
386 |
+
* @param string $data
|
387 |
+
* @return bool
|
388 |
+
*/
|
389 |
+
public function verifyHashedRequest($hash, $data) {
|
390 |
+
if ($this->hasOpenSSL()) {
|
391 |
+
return false;
|
392 |
+
}
|
393 |
+
return wfWAFUtils::hash_equals($hash,
|
394 |
+
wfWAFUtils::hash_hmac('sha1', $data, $this->getStorageEngine()->getConfig('apiKey')));
|
395 |
+
}
|
396 |
+
|
397 |
+
/**
|
398 |
+
* @param string $ip
|
399 |
+
* @return bool
|
400 |
+
*/
|
401 |
+
public function isWordfenceIP($ip) {
|
402 |
+
if (preg_match('/69.46.36.(\d+)/', $ip, $matches)) {
|
403 |
+
return $matches[1] >= 1 && $matches[1] <= 32;
|
404 |
+
}
|
405 |
+
return false;
|
406 |
+
}
|
407 |
+
|
408 |
+
/**
|
409 |
+
* @param $rules
|
410 |
+
* @param bool|int $updateLastUpdatedTimestamp
|
411 |
+
* @throws wfWAFBuildRulesException
|
412 |
+
*/
|
413 |
+
public function updateRuleSet($rules, $updateLastUpdatedTimestamp = true) {
|
414 |
+
try {
|
415 |
+
if (is_string($rules)) {
|
416 |
+
$ruleString = $rules;
|
417 |
+
$parser = new wfWAFRuleParser(new wfWAFRuleLexer($rules), $this);
|
418 |
+
$rules = $parser->parse();
|
419 |
+
}
|
420 |
+
|
421 |
+
if (!is_writable($this->getCompiledRulesFile())) {
|
422 |
+
throw new wfWAFBuildRulesException('Rules file not writable.');
|
423 |
+
}
|
424 |
+
|
425 |
+
file_put_contents($this->getCompiledRulesFile(), sprintf(<<<PHP
|
426 |
+
<?php
|
427 |
+
if (!defined('WFWAF_VERSION')) {
|
428 |
+
exit('Access denied');
|
429 |
+
}
|
430 |
+
/*
|
431 |
+
This file is generated automatically. Any changes made will be lost.
|
432 |
+
*/
|
433 |
+
|
434 |
+
%s?>
|
435 |
+
PHP
|
436 |
+
, $this->buildRuleSet($rules)), LOCK_EX);
|
437 |
+
if (!empty($ruleString) && !WFWAF_DEBUG) {
|
438 |
+
file_put_contents($this->getStorageEngine()->getRulesDSLCacheFile(), $ruleString, LOCK_EX);
|
439 |
+
}
|
440 |
+
|
441 |
+
if ($updateLastUpdatedTimestamp) {
|
442 |
+
$this->getStorageEngine()->setConfig('rulesLastUpdated',
|
443 |
+
is_int($updateLastUpdatedTimestamp) ? $updateLastUpdatedTimestamp : time());
|
444 |
+
}
|
445 |
+
|
446 |
+
} catch (wfWAFBuildRulesException $e) {
|
447 |
+
// Do something.
|
448 |
+
throw $e;
|
449 |
+
}
|
450 |
+
}
|
451 |
+
|
452 |
+
/**
|
453 |
+
* @param string $rules
|
454 |
+
* @return string
|
455 |
+
* @throws wfWAFException
|
456 |
+
*/
|
457 |
+
public function buildRuleSet($rules) {
|
458 |
+
if (is_string($rules)) {
|
459 |
+
$parser = new wfWAFRuleParser(new wfWAFRuleLexer($rules), $this);
|
460 |
+
$rules = $parser->parse();
|
461 |
+
}
|
462 |
+
|
463 |
+
if (!array_key_exists('rules', $rules) || !is_array($rules['rules'])) {
|
464 |
+
throw new wfWAFBuildRulesException('Invalid rule format passed to buildRuleSet.');
|
465 |
+
}
|
466 |
+
$exportedCode = '';
|
467 |
+
|
468 |
+
if (isset($rules['scores']) && is_array($rules['scores'])) {
|
469 |
+
foreach ($rules['scores'] as $category => $score) {
|
470 |
+
$exportedCode .= sprintf("\$this->failScores[%s] = %d;\n", var_export($category, true), $score);
|
471 |
+
}
|
472 |
+
$exportedCode .= "\n";
|
473 |
+
}
|
474 |
+
|
475 |
+
if (isset($rules['variables']) && is_array($rules['variables'])) {
|
476 |
+
foreach ($rules['variables'] as $var => $value) {
|
477 |
+
$exportedCode .= sprintf("\$this->variables[%s] = %s;\n", var_export($var, true),
|
478 |
+
($value instanceof wfWAFRuleVariable) ? $value->render() : var_export($value, true));
|
479 |
+
}
|
480 |
+
$exportedCode .= "\n";
|
481 |
+
}
|
482 |
+
|
483 |
+
foreach (array('blacklistedParams', 'whitelistedParams') as $key) {
|
484 |
+
if (isset($rules[$key]) && is_array($rules[$key])) {
|
485 |
+
/** @var wfWAFRuleParserURLParam $urlParam */
|
486 |
+
foreach ($rules[$key] as $urlParam) {
|
487 |
+
if ($urlParam->getRules()) {
|
488 |
+
$url = array(
|
489 |
+
'url' => $urlParam->getUrl(),
|
490 |
+
'rules' => $urlParam->getRules(),
|
491 |
+
);
|
492 |
+
} else {
|
493 |
+
$url = $urlParam->getUrl();
|
494 |
+
}
|
495 |
+
|
496 |
+
$exportedCode .= sprintf("\$this->{$key}[%s][] = %s;\n", var_export($urlParam->getParam(), true),
|
497 |
+
var_export($url, true));
|
498 |
+
}
|
499 |
+
$exportedCode .= "\n";
|
500 |
+
}
|
501 |
+
}
|
502 |
+
|
503 |
+
/** @var wfWAFRule $rule */
|
504 |
+
foreach ($rules['rules'] as $rule) {
|
505 |
+
$rule->setWAF($this);
|
506 |
+
$exportedCode .= sprintf(<<<HTML
|
507 |
+
\$this->rules[%d] = %s;
|
508 |
+
|
509 |
+
HTML
|
510 |
+
,
|
511 |
+
$rule->getRuleID(),
|
512 |
+
$rule->render()
|
513 |
+
);
|
514 |
+
}
|
515 |
+
|
516 |
+
return $exportedCode;
|
517 |
+
}
|
518 |
+
|
519 |
+
/**
|
520 |
+
* @param $rules
|
521 |
+
* @return wfWAFRuleComparisonGroup
|
522 |
+
* @throws wfWAFBuildRulesException
|
523 |
+
*/
|
524 |
+
protected function _buildRuleSet($rules) {
|
525 |
+
$ruleGroup = new wfWAFRuleComparisonGroup();
|
526 |
+
foreach ($rules as $rule) {
|
527 |
+
if (!array_key_exists('type', $rule)) {
|
528 |
+
throw new wfWAFBuildRulesException('Invalid rule: type not set.');
|
529 |
+
}
|
530 |
+
switch ($rule['type']) {
|
531 |
+
case 'comparison_group':
|
532 |
+
if (!array_key_exists('comparisons', $rule) || !is_array($rule['comparisons'])) {
|
533 |
+
throw new wfWAFBuildRulesException('Invalid rule format passed to _buildRuleSet.');
|
534 |
+
}
|
535 |
+
$ruleGroup->add($this->_buildRuleSet($rule['comparisons']));
|
536 |
+
break;
|
537 |
+
|
538 |
+
case 'comparison':
|
539 |
+
if (array_key_exists('parameter', $rule)) {
|
540 |
+
$rule['parameters'] = array($rule['parameter']);
|
541 |
+
}
|
542 |
+
|
543 |
+
foreach (array('action', 'expected', 'parameters') as $ruleRequirement) {
|
544 |
+
if (!array_key_exists($ruleRequirement, $rule)) {
|
545 |
+
throw new wfWAFBuildRulesException("Invalid rule: $ruleRequirement not set.");
|
546 |
+
}
|
547 |
+
}
|
548 |
+
|
549 |
+
$ruleGroup->add(new wfWAFRuleComparison($this, $rule['action'], $rule['expected'], $rule['parameters']));
|
550 |
+
break;
|
551 |
+
|
552 |
+
case 'operator':
|
553 |
+
if (!array_key_exists('operator', $rule)) {
|
554 |
+
throw new wfWAFBuildRulesException('Invalid rule format passed to _buildRuleSet. operator not passed.');
|
555 |
+
}
|
556 |
+
$ruleGroup->add(new wfWAFRuleLogicalOperator($rule['operator']));
|
557 |
+
break;
|
558 |
+
|
559 |
+
default:
|
560 |
+
throw new wfWAFBuildRulesException("Invalid rule type [{$rule['type']}] passed to _buildRuleSet.");
|
561 |
+
}
|
562 |
+
}
|
563 |
+
return $ruleGroup;
|
564 |
+
}
|
565 |
+
|
566 |
+
public function isRuleDisabled($ruleID) {
|
567 |
+
if ($this->disabledRules === null) {
|
568 |
+
$this->disabledRules = $this->getStorageEngine()->getConfig('disabledRules');
|
569 |
+
if (!is_array($this->disabledRules)) {
|
570 |
+
$this->disabledRules = array();
|
571 |
+
}
|
572 |
+
}
|
573 |
+
return !empty($this->disabledRules[$ruleID]);
|
574 |
+
}
|
575 |
+
|
576 |
+
/**
|
577 |
+
* @param wfWAFRule $rule
|
578 |
+
* @param wfWAFRuleComparisonFailure $failedComparison
|
579 |
+
* @throws wfWAFBlockException
|
580 |
+
*/
|
581 |
+
public function fail($rule, $failedComparison) {
|
582 |
+
$category = $rule->getCategory();
|
583 |
+
$paramKey = $failedComparison->getParamKey();
|
584 |
+
$this->failedRules[$paramKey][$category][] = array(
|
585 |
+
'rule' => $rule,
|
586 |
+
'failedComparison' => $failedComparison,
|
587 |
+
'action' => 'block',
|
588 |
+
);
|
589 |
+
}
|
590 |
+
|
591 |
+
/**
|
592 |
+
* @param wfWAFRule $rule
|
593 |
+
* @param wfWAFRuleComparisonFailure $failedComparison
|
594 |
+
* @throws wfWAFBlockException
|
595 |
+
*/
|
596 |
+
public function failXSS($rule, $failedComparison) {
|
597 |
+
$category = $rule->getCategory();
|
598 |
+
$paramKey = $failedComparison->getParamKey();
|
599 |
+
$this->failedRules[$paramKey][$category][] = array(
|
600 |
+
'rule' => $rule,
|
601 |
+
'failedComparison' => $failedComparison,
|
602 |
+
'action' => 'blockXSS',
|
603 |
+
);
|
604 |
+
}
|
605 |
+
|
606 |
+
/**
|
607 |
+
* @param wfWAFRule $rule
|
608 |
+
* @param wfWAFRuleComparisonFailure $failedComparison
|
609 |
+
* @throws wfWAFBlockException
|
610 |
+
*/
|
611 |
+
public function failSQLi($rule, $failedComparison) {
|
612 |
+
$category = $rule->getCategory();
|
613 |
+
$paramKey = $failedComparison->getParamKey();
|
614 |
+
$this->failedRules[$paramKey][$category][] = array(
|
615 |
+
'rule' => $rule,
|
616 |
+
'failedComparison' => $failedComparison,
|
617 |
+
'action' => 'blockSQLi',
|
618 |
+
);
|
619 |
+
}
|
620 |
+
|
621 |
+
/**
|
622 |
+
* @param wfWAFRule $rule
|
623 |
+
* @param wfWAFRuleComparisonFailure $failedComparison
|
624 |
+
* @throws wfWAFAllowException
|
625 |
+
*/
|
626 |
+
public function allow($rule, $failedComparison) {
|
627 |
+
// Exclude this request from further blocking
|
628 |
+
$e = new wfWAFAllowException();
|
629 |
+
$e->setFailedRules(array($rule));
|
630 |
+
$e->setParamKey($failedComparison->getParamKey());
|
631 |
+
$e->setParamValue($failedComparison->getParamValue());
|
632 |
+
$e->setRequest($this->getRequest());
|
633 |
+
throw $e;
|
634 |
+
}
|
635 |
+
|
636 |
+
/**
|
637 |
+
* @param wfWAFRule $rule
|
638 |
+
* @param wfWAFRuleComparisonFailure $failedComparison
|
639 |
+
* @param bool $updateFailedRules
|
640 |
+
* @throws wfWAFBlockException
|
641 |
+
*/
|
642 |
+
public function block($rule, $failedComparison, $updateFailedRules = true) {
|
643 |
+
$paramKey = $failedComparison->getParamKey();
|
644 |
+
$category = $rule->getCategory();
|
645 |
+
|
646 |
+
if ($updateFailedRules) {
|
647 |
+
$this->failedRules[$paramKey][$category][] = array(
|
648 |
+
'rule' => $rule,
|
649 |
+
'failedComparison' => $failedComparison,
|
650 |
+
'action' => 'block',
|
651 |
+
);
|
652 |
+
}
|
653 |
+
|
654 |
+
$e = new wfWAFBlockException();
|
655 |
+
$e->setFailedRules(array($rule));
|
656 |
+
$e->setParamKey($failedComparison->getParamKey());
|
657 |
+
$e->setParamValue($failedComparison->getParamValue());
|
658 |
+
$e->setRequest($this->getRequest());
|
659 |
+
throw $e;
|
660 |
+
}
|
661 |
+
|
662 |
+
/**
|
663 |
+
* @param wfWAFRule $rule
|
664 |
+
* @param wfWAFRuleComparisonFailure $failedComparison
|
665 |
+
* @param bool $updateFailedRules
|
666 |
+
* @throws wfWAFBlockXSSException
|
667 |
+
*/
|
668 |
+
public function blockXSS($rule, $failedComparison, $updateFailedRules = true) {
|
669 |
+
$paramKey = $failedComparison->getParamKey();
|
670 |
+
$category = $rule->getCategory();
|
671 |
+
|
672 |
+
if ($updateFailedRules) {
|
673 |
+
$this->failedRules[$paramKey][$category][] = array(
|
674 |
+
'rule' => $rule,
|
675 |
+
'failedComparison' => $failedComparison,
|
676 |
+
'action' => 'blockXSS',
|
677 |
+
);
|
678 |
+
}
|
679 |
+
$e = new wfWAFBlockXSSException();
|
680 |
+
$e->setFailedRules(array($rule));
|
681 |
+
$e->setParamKey($failedComparison->getParamKey());
|
682 |
+
$e->setParamValue($failedComparison->getParamValue());
|
683 |
+
$e->setRequest($this->getRequest());
|
684 |
+
throw $e;
|
685 |
+
}
|
686 |
+
|
687 |
+
/**
|
688 |
+
* @param wfWAFRule $rule
|
689 |
+
* @param wfWAFRuleComparisonFailure $failedComparison
|
690 |
+
* @param bool $updateFailedRules
|
691 |
+
* @throws wfWAFBlockSQLiException
|
692 |
+
*/
|
693 |
+
public function blockSQLi($rule, $failedComparison, $updateFailedRules = true) {
|
694 |
+
// Verify the param looks like SQLi to help reduce false positives.
|
695 |
+
if (!wfWAFSQLiParser::testForSQLi($failedComparison->getParamValue())) {
|
696 |
+
return;
|
697 |
+
}
|
698 |
+
|
699 |
+
$paramKey = $failedComparison->getParamKey();
|
700 |
+
$category = $rule->getCategory();
|
701 |
+
|
702 |
+
if ($updateFailedRules) {
|
703 |
+
$this->failedRules[$paramKey][$category][] = array(
|
704 |
+
'rule' => $rule,
|
705 |
+
'failedComparison' => $failedComparison,
|
706 |
+
'action' => 'blockXSS',
|
707 |
+
);
|
708 |
+
}
|
709 |
+
$e = new wfWAFBlockSQLiException();
|
710 |
+
$e->setFailedRules(array($rule));
|
711 |
+
$e->setParamKey($failedComparison->getParamKey());
|
712 |
+
$e->setParamValue($failedComparison->getParamValue());
|
713 |
+
$e->setRequest($this->getRequest());
|
714 |
+
throw $e;
|
715 |
+
}
|
716 |
+
|
717 |
+
/**
|
718 |
+
* @todo Hook up $httpCode
|
719 |
+
* @param wfWAFBlockException $e
|
720 |
+
* @param int $httpCode
|
721 |
+
*/
|
722 |
+
public function blockAction($e, $httpCode = 403) {
|
723 |
+
$this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest());
|
724 |
+
$this->getStorageEngine()->blockIP($this->getRequest()->getTimestamp(), $this->getRequest()->getIP());
|
725 |
+
header('HTTP/1.0 403 Forbidden');
|
726 |
+
exit($this->getBlockedMessage());
|
727 |
+
}
|
728 |
+
|
729 |
+
/**
|
730 |
+
* @todo Hook up $httpCode
|
731 |
+
* @param wfWAFBlockXSSException $e
|
732 |
+
* @param int $httpCode
|
733 |
+
*/
|
734 |
+
public function blockXSSAction($e, $httpCode = 403) {
|
735 |
+
$this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest());
|
736 |
+
header('HTTP/1.0 403 Forbidden');
|
737 |
+
exit($this->getBlockedMessage());
|
738 |
+
}
|
739 |
+
|
740 |
+
/**
|
741 |
+
* @return string
|
742 |
+
*/
|
743 |
+
public function getBlockedMessage() {
|
744 |
+
if ($this->currentUserCanWhitelist()) {
|
745 |
+
return wfWAFView::create('403-roadblock', array(
|
746 |
+
'waf' => $this,
|
747 |
+
))->render();
|
748 |
+
}
|
749 |
+
return wfWAFView::create('403', array(
|
750 |
+
'waf' => $this,
|
751 |
+
))->render();
|
752 |
+
}
|
753 |
+
|
754 |
+
/**
|
755 |
+
*
|
756 |
+
*/
|
757 |
+
public function whitelistFailedRules() {
|
758 |
+
foreach ($this->failedRules as $paramKey => $categories) {
|
759 |
+
foreach ($categories as $category => $failedRules) {
|
760 |
+
foreach ($failedRules as $failedRule) {
|
761 |
+
/**
|
762 |
+
* @var wfWAFRule $rule
|
763 |
+
* @var wfWAFRuleComparisonFailure $failedComparison
|
764 |
+
*/
|
765 |
+
$rule = $failedRule['rule'];
|
766 |
+
$failedComparison = $failedRule['failedComparison'];
|
767 |
+
|
768 |
+
$data = array(
|
769 |
+
'timestamp' => time(),
|
770 |
+
'description' => 'Whitelisted while in Learning Mode.',
|
771 |
+
'ip' => $this->getRequest()->getIP(),
|
772 |
+
);
|
773 |
+
if (function_exists('get_current_user_id')) {
|
774 |
+
$data['userID'] = get_current_user_id();
|
775 |
+
}
|
776 |
+
$this->whitelistRuleForParam($this->getRequest()->getPath(), $failedComparison->getParamKey(),
|
777 |
+
$rule->getRuleID(), $data);
|
778 |
+
}
|
779 |
+
}
|
780 |
+
}
|
781 |
+
}
|
782 |
+
|
783 |
+
/**
|
784 |
+
* @param string $path
|
785 |
+
* @param string $paramKey
|
786 |
+
* @param int $ruleID
|
787 |
+
* @param array $data
|
788 |
+
*/
|
789 |
+
public function whitelistRuleForParam($path, $paramKey, $ruleID, $data = array()) {
|
790 |
+
if ($this->isParamKeyURLBlacklisted($ruleID, $paramKey, $path)) {
|
791 |
+
return;
|
792 |
+
}
|
793 |
+
|
794 |
+
$whitelist = $this->getStorageEngine()->getConfig('whitelistedURLParams');
|
795 |
+
if (!is_array($whitelist)) {
|
796 |
+
$whitelist = array();
|
797 |
+
}
|
798 |
+
if (is_array($ruleID)) {
|
799 |
+
foreach ($ruleID as $id) {
|
800 |
+
$whitelist[base64_encode($path) . "|" . base64_encode($paramKey)][$id] = $data;
|
801 |
+
}
|
802 |
+
} else {
|
803 |
+
$whitelist[base64_encode($path) . "|" . base64_encode($paramKey)][$ruleID] = $data;
|
804 |
+
}
|
805 |
+
|
806 |
+
$this->getStorageEngine()->setConfig('whitelistedURLParams', $whitelist);
|
807 |
+
}
|
808 |
+
|
809 |
+
/**
|
810 |
+
* @param int $ruleID
|
811 |
+
* @param string $urlPath
|
812 |
+
* @param string $paramKey
|
813 |
+
* @return bool
|
814 |
+
*/
|
815 |
+
public function isRuleParamWhitelisted($ruleID, $urlPath, $paramKey) {
|
816 |
+
if ($this->isParamKeyURLBlacklisted($ruleID, $paramKey, $urlPath)) {
|
817 |
+
return false;
|
818 |
+
}
|
819 |
+
|
820 |
+
if (is_array($this->whitelistedParams) && array_key_exists($paramKey, $this->whitelistedParams)
|
821 |
+
&& is_array($this->whitelistedParams[$paramKey])
|
822 |
+
) {
|
823 |
+
foreach ($this->whitelistedParams[$paramKey] as $urlRegex) {
|
824 |
+
if (is_array($urlRegex)) {
|
825 |
+
if (!in_array($ruleID, $urlRegex['rules'])) {
|
826 |
+
continue;
|
827 |
+
}
|
828 |
+
$urlRegex = $urlRegex['url'];
|
829 |
+
}
|
830 |
+
if (preg_match($urlRegex, $urlPath)) {
|
831 |
+
return true;
|
832 |
+
}
|
833 |
+
}
|
834 |
+
}
|
835 |
+
|
836 |
+
$whitelistKey = base64_encode($urlPath) . "|" . base64_encode($paramKey);
|
837 |
+
$whitelist = $this->getStorageEngine()->getConfig('whitelistedURLParams', array());
|
838 |
+
if (!is_array($whitelist)) {
|
839 |
+
$whitelist = array();
|
840 |
+
}
|
841 |
+
|
842 |
+
if (array_key_exists($whitelistKey, $whitelist)) {
|
843 |
+
foreach (array('all', $ruleID) as $key) {
|
844 |
+
if (array_key_exists($key, $whitelist[$whitelistKey])) {
|
845 |
+
$ruleData = $whitelist[$whitelistKey][$key];
|
846 |
+
if (is_array($ruleData) && array_key_exists('disabled', $ruleData)) {
|
847 |
+
return !$ruleData['disabled'];
|
848 |
+
} else if ($ruleData) {
|
849 |
+
return true;
|
850 |
+
}
|
851 |
+
}
|
852 |
+
}
|
853 |
+
}
|
854 |
+
return false;
|
855 |
+
}
|
856 |
+
|
857 |
+
/**
|
858 |
+
*
|
859 |
+
*/
|
860 |
+
public function sendAttackData() {
|
861 |
+
if ($this->getStorageEngine()->getConfig('attackDataKey', false) === false) {
|
862 |
+
$this->getStorageEngine()->setConfig('attackDataKey', mt_rand(0, 0xfff));
|
863 |
+
}
|
864 |
+
|
865 |
+
$request = new wfWAFHTTP();
|
866 |
+
try {
|
867 |
+
$response = wfWAFHTTP::get(
|
868 |
+
sprintf(WFWAF_API_URL_SEC . "waf-rules/%d.txt", $this->getStorageEngine()->getConfig('attackDataKey')),
|
869 |
+
$request);
|
870 |
+
|
871 |
+
if ($response instanceof wfWAFHTTPResponse) {
|
872 |
+
if ($response->getBody() === 'ok') {
|
873 |
+
$request = new wfWAFHTTP();
|
874 |
+
$request->setHeaders(array(
|
875 |
+
'Content-Type' => 'application/json',
|
876 |
+
));
|
877 |
+
$response = wfWAFHTTP::post(WFWAF_API_URL_SEC . "?" . http_build_query(array(
|
878 |
+
'action' => 'send_waf_attack_data',
|
879 |
+
'k' => $this->getStorageEngine()->getConfig('apiKey'),
|
880 |
+
's' => $this->getStorageEngine()->getConfig('siteURL') ? $this->getStorageEngine()->getConfig('siteURL') :
|
881 |
+
sprintf('%s://%s/', $this->getRequest()->getProtocol(), rawurlencode($this->getRequest()->getHost())),
|
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');
|
889 |
+
}
|
890 |
+
}
|
891 |
+
} else if (is_string($response->getBody()) && preg_match('/next check in: ([0-9]+)/', $response->getBody(), $matches)) {
|
892 |
+
$this->getStorageEngine()->setConfig('attackDataNextInterval', time() + $matches[1]);
|
893 |
+
if ($this->getStorageEngine()->isAttackDataFull()) {
|
894 |
+
$this->getStorageEngine()->truncateAttackData();
|
895 |
+
}
|
896 |
+
}
|
897 |
+
|
898 |
+
// Could be that the server is down, so hold off on sending data for a little while.
|
899 |
+
} else {
|
900 |
+
$this->getStorageEngine()->setConfig('attackDataNextInterval', time() + 7200);
|
901 |
+
}
|
902 |
+
|
903 |
+
} catch (wfWAFHTTPTransportException $e) {
|
904 |
+
error_log($e->getMessage());
|
905 |
+
}
|
906 |
+
}
|
907 |
+
|
908 |
+
/**
|
909 |
+
* @param string $action
|
910 |
+
* @return array
|
911 |
+
*/
|
912 |
+
public function isAllowedAction($action) {
|
913 |
+
static $actions;
|
914 |
+
if (!isset($actions)) {
|
915 |
+
$actions = array_flip($this->getAllowedActions());
|
916 |
+
}
|
917 |
+
return array_key_exists($action, $actions);
|
918 |
+
}
|
919 |
+
|
920 |
+
/**
|
921 |
+
* @return array
|
922 |
+
*/
|
923 |
+
public function getAllowedActions() {
|
924 |
+
return array('fail', 'allow', 'block', 'failXSS', 'blockXSS', 'failSQLi', 'blockSQLi');
|
925 |
+
}
|
926 |
+
|
927 |
+
/**
|
928 |
+
*
|
929 |
+
*/
|
930 |
+
public function uninstall() {
|
931 |
+
@unlink($this->getCompiledRulesFile());
|
932 |
+
$this->getStorageEngine()->uninstall();
|
933 |
+
}
|
934 |
+
|
935 |
+
/**
|
936 |
+
* @param int $ruleID
|
937 |
+
* @param string $paramKey
|
938 |
+
* @param string $urlPath
|
939 |
+
* @return bool
|
940 |
+
*/
|
941 |
+
public function isParamKeyURLBlacklisted($ruleID, $paramKey, $urlPath) {
|
942 |
+
if (is_array($this->blacklistedParams) && array_key_exists($paramKey, $this->blacklistedParams)
|
943 |
+
&& is_array($this->blacklistedParams[$paramKey])
|
944 |
+
) {
|
945 |
+
foreach ($this->blacklistedParams[$paramKey] as $urlRegex) {
|
946 |
+
if (is_array($urlRegex)) {
|
947 |
+
if (!in_array($ruleID, $urlRegex['rules'])) {
|
948 |
+
continue;
|
949 |
+
}
|
950 |
+
$urlRegex = $urlRegex['url'];
|
951 |
+
}
|
952 |
+
if (preg_match($urlRegex, $urlPath)) {
|
953 |
+
return true;
|
954 |
+
}
|
955 |
+
}
|
956 |
+
}
|
957 |
+
return false;
|
958 |
+
}
|
959 |
+
|
960 |
+
/**
|
961 |
+
* @return bool
|
962 |
+
*/
|
963 |
+
public function currentUserCanWhitelist() {
|
964 |
+
if ($authCookie = $this->parseAuthCookie()) {
|
965 |
+
return $authCookie['role'] === 'administrator';
|
966 |
+
}
|
967 |
+
return false;
|
968 |
+
}
|
969 |
+
|
970 |
+
/**
|
971 |
+
* @param string|null $cookieVal
|
972 |
+
* @return bool
|
973 |
+
*/
|
974 |
+
public function parseAuthCookie($cookieVal = null) {
|
975 |
+
if ($cookieVal === null) {
|
976 |
+
$cookieName = $this->getAuthCookieName();
|
977 |
+
$cookieVal = !empty($_COOKIE[$cookieName]) && is_string($_COOKIE[$cookieName]) ? $_COOKIE[$cookieName] : '';
|
978 |
+
}
|
979 |
+
$pieces = explode('|', $cookieVal);
|
980 |
+
if (count($pieces) !== 3) {
|
981 |
+
return false;
|
982 |
+
}
|
983 |
+
list($userID, $role, $signature) = $pieces;
|
984 |
+
if (wfWAFUtils::hash_equals($signature, $this->getAuthCookieValue($userID, $role))) {
|
985 |
+
return array(
|
986 |
+
'userID' => $userID,
|
987 |
+
'role' => $role,
|
988 |
+
);
|
989 |
+
}
|
990 |
+
return false;
|
991 |
+
}
|
992 |
+
|
993 |
+
/**
|
994 |
+
* @param int|string $userID
|
995 |
+
* @param string $role
|
996 |
+
* @return bool|string
|
997 |
+
*/
|
998 |
+
public function getAuthCookieValue($userID, $role) {
|
999 |
+
$algo = function_exists('hash') ? 'sha256' : 'sha1';
|
1000 |
+
return wfWAFUtils::hash_hmac($algo, $userID . $role . floor(time() / 43200), $this->getStorageEngine()->getConfig('authKey'));
|
1001 |
+
}
|
1002 |
+
|
1003 |
+
/**
|
1004 |
+
* @param string|null $host
|
1005 |
+
* @return string
|
1006 |
+
*/
|
1007 |
+
public function getAuthCookieName($host = null) {
|
1008 |
+
if ($host === null) {
|
1009 |
+
$host = $this->getRequest()->getHost();
|
1010 |
+
}
|
1011 |
+
return self::AUTH_COOKIE . '-' . md5($host);
|
1012 |
+
}
|
1013 |
+
|
1014 |
+
/**
|
1015 |
+
* @return string
|
1016 |
+
*/
|
1017 |
+
public function getCompiledRulesFile() {
|
1018 |
+
return $this->rulesFile;
|
1019 |
+
}
|
1020 |
+
|
1021 |
+
/**
|
1022 |
+
* @param string $rulesFile
|
1023 |
+
*/
|
1024 |
+
public function setCompiledRulesFile($rulesFile) {
|
1025 |
+
$this->rulesFile = $rulesFile;
|
1026 |
+
}
|
1027 |
+
|
1028 |
+
/**
|
1029 |
+
* @param $ip
|
1030 |
+
* @return mixed
|
1031 |
+
*/
|
1032 |
+
public function isIPBlocked($ip) {
|
1033 |
+
return $this->getStorageEngine()->isIPBlocked($ip);
|
1034 |
+
}
|
1035 |
+
|
1036 |
+
/**
|
1037 |
+
* @return array
|
1038 |
+
*/
|
1039 |
+
public function getTrippedRules() {
|
1040 |
+
return $this->trippedRules;
|
1041 |
+
}
|
1042 |
+
|
1043 |
+
/**
|
1044 |
+
* @return array
|
1045 |
+
*/
|
1046 |
+
public function getTrippedRuleIDs() {
|
1047 |
+
$ret = array();
|
1048 |
+
/** @var wfWAFRule $rule */
|
1049 |
+
foreach ($this->getTrippedRules() as $rule) {
|
1050 |
+
$ret[] = $rule->getRuleID();
|
1051 |
+
}
|
1052 |
+
return $ret;
|
1053 |
+
}
|
1054 |
+
|
1055 |
+
public function showBench() {
|
1056 |
+
return sprintf("Bench: %f seconds\n\n", microtime(true) - $this->getRequest()->getTimestamp());
|
1057 |
+
}
|
1058 |
+
|
1059 |
+
public function debug() {
|
1060 |
+
return join("\n", $this->debug) . "\n\n" . $this->showBench();
|
1061 |
+
// $debug = '';
|
1062 |
+
// /** @var wfWAFRule $rule */
|
1063 |
+
// foreach ($this->trippedRules as $rule) {
|
1064 |
+
// $debug .= $rule->debug();
|
1065 |
+
// }
|
1066 |
+
// return $debug;
|
1067 |
+
}
|
1068 |
+
|
1069 |
+
/**
|
1070 |
+
* @return array
|
1071 |
+
*/
|
1072 |
+
public function getScores() {
|
1073 |
+
return $this->scores;
|
1074 |
+
}
|
1075 |
+
|
1076 |
+
/**
|
1077 |
+
* @param string $var
|
1078 |
+
* @return null
|
1079 |
+
*/
|
1080 |
+
public function getVariable($var) {
|
1081 |
+
if (array_key_exists($var, $this->variables)) {
|
1082 |
+
return $this->variables[$var];
|
1083 |
+
}
|
1084 |
+
return null;
|
1085 |
+
}
|
1086 |
+
|
1087 |
+
/**
|
1088 |
+
* @return wfWAFRequestInterface
|
1089 |
+
*/
|
1090 |
+
public function getRequest() {
|
1091 |
+
return $this->request;
|
1092 |
+
}
|
1093 |
+
|
1094 |
+
/**
|
1095 |
+
* @param wfWAFRequestInterface $request
|
1096 |
+
*/
|
1097 |
+
public function setRequest($request) {
|
1098 |
+
$this->request = $request;
|
1099 |
+
}
|
1100 |
+
|
1101 |
+
/**
|
1102 |
+
* @return wfWAFStorageInterface
|
1103 |
+
*/
|
1104 |
+
public function getStorageEngine() {
|
1105 |
+
return $this->storageEngine;
|
1106 |
+
}
|
1107 |
+
|
1108 |
+
/**
|
1109 |
+
* @param wfWAFStorageInterface $storageEngine
|
1110 |
+
*/
|
1111 |
+
public function setStorageEngine($storageEngine) {
|
1112 |
+
$this->storageEngine = $storageEngine;
|
1113 |
+
}
|
1114 |
+
|
1115 |
+
/**
|
1116 |
+
* @return wfWAFEventBus
|
1117 |
+
*/
|
1118 |
+
public function getEventBus() {
|
1119 |
+
return $this->eventBus;
|
1120 |
+
}
|
1121 |
+
|
1122 |
+
/**
|
1123 |
+
* @param wfWAFEventBus $eventBus
|
1124 |
+
*/
|
1125 |
+
public function setEventBus($eventBus) {
|
1126 |
+
$this->eventBus = $eventBus;
|
1127 |
+
}
|
1128 |
+
|
1129 |
+
/**
|
1130 |
+
* @return array
|
1131 |
+
*/
|
1132 |
+
public function getRules() {
|
1133 |
+
return $this->rules;
|
1134 |
+
}
|
1135 |
+
|
1136 |
+
/**
|
1137 |
+
* @param array $rules
|
1138 |
+
*/
|
1139 |
+
public function setRules($rules) {
|
1140 |
+
$this->rules = $rules;
|
1141 |
+
}
|
1142 |
+
|
1143 |
+
/**
|
1144 |
+
* @param int $ruleID
|
1145 |
+
* @return null|wfWAFRule
|
1146 |
+
*/
|
1147 |
+
public function getRule($ruleID) {
|
1148 |
+
$rules = $this->getRules();
|
1149 |
+
if (is_array($rules) && array_key_exists($ruleID, $rules)) {
|
1150 |
+
return $rules[$ruleID];
|
1151 |
+
}
|
1152 |
+
return null;
|
1153 |
+
}
|
1154 |
+
|
1155 |
+
/**
|
1156 |
+
* @return string
|
1157 |
+
*/
|
1158 |
+
public function getPublicKey() {
|
1159 |
+
return $this->publicKey;
|
1160 |
+
}
|
1161 |
+
|
1162 |
+
/**
|
1163 |
+
* @param string $publicKey
|
1164 |
+
*/
|
1165 |
+
public function setPublicKey($publicKey) {
|
1166 |
+
$this->publicKey = $publicKey;
|
1167 |
+
}
|
1168 |
+
|
1169 |
+
/**
|
1170 |
+
* @return array
|
1171 |
+
*/
|
1172 |
+
public function getFailedRules() {
|
1173 |
+
return $this->failedRules;
|
1174 |
+
}
|
1175 |
+
}
|
1176 |
+
|
1177 |
+
/**
|
1178 |
+
* Serialized for use with the WAF cron.
|
1179 |
+
*/
|
1180 |
+
abstract class wfWAFCronEvent {
|
1181 |
+
|
1182 |
+
abstract public function fire();
|
1183 |
+
|
1184 |
+
abstract public function reschedule();
|
1185 |
+
|
1186 |
+
protected $fireTime;
|
1187 |
+
private $waf;
|
1188 |
+
|
1189 |
+
/**
|
1190 |
+
* @param int $fireTime
|
1191 |
+
*/
|
1192 |
+
public function __construct($fireTime) {
|
1193 |
+
$this->setFireTime($fireTime);
|
1194 |
+
}
|
1195 |
+
|
1196 |
+
/**
|
1197 |
+
* @param int|null $time
|
1198 |
+
* @return bool
|
1199 |
+
*/
|
1200 |
+
public function isInPast($time = null) {
|
1201 |
+
if ($time === null) {
|
1202 |
+
$time = time();
|
1203 |
+
}
|
1204 |
+
return $this->getFireTime() <= $time;
|
1205 |
+
}
|
1206 |
+
|
1207 |
+
public function __sleep() {
|
1208 |
+
return array('fireTime');
|
1209 |
+
}
|
1210 |
+
|
1211 |
+
/**
|
1212 |
+
* @return mixed
|
1213 |
+
*/
|
1214 |
+
public function getFireTime() {
|
1215 |
+
return $this->fireTime;
|
1216 |
+
}
|
1217 |
+
|
1218 |
+
/**
|
1219 |
+
* @param mixed $fireTime
|
1220 |
+
*/
|
1221 |
+
public function setFireTime($fireTime) {
|
1222 |
+
$this->fireTime = $fireTime;
|
1223 |
+
}
|
1224 |
+
|
1225 |
+
/**
|
1226 |
+
* @return wfWAF
|
1227 |
+
*/
|
1228 |
+
public function getWaf() {
|
1229 |
+
return $this->waf;
|
1230 |
+
}
|
1231 |
+
|
1232 |
+
/**
|
1233 |
+
* @param wfWAF $waf
|
1234 |
+
*/
|
1235 |
+
public function setWaf($waf) {
|
1236 |
+
$this->waf = $waf;
|
1237 |
+
}
|
1238 |
+
}
|
1239 |
+
|
1240 |
+
class wfWAFCronFetchRulesEvent extends wfWAFCronEvent {
|
1241 |
+
|
1242 |
+
/**
|
1243 |
+
* @var wfWAFHTTPResponse
|
1244 |
+
*/
|
1245 |
+
private $response;
|
1246 |
+
|
1247 |
+
public function fire() {
|
1248 |
+
$waf = $this->getWaf();
|
1249 |
+
if (!$waf) {
|
1250 |
+
return;
|
1251 |
+
}
|
1252 |
+
$guessSiteURL = sprintf('%s://%s/', $waf->getRequest()->getProtocol(), $waf->getRequest()->getHost());
|
1253 |
+
try {
|
1254 |
+
$this->response = wfWAFHTTP::get(WFWAF_API_URL_SEC . "?" . http_build_query(array(
|
1255 |
+
'action' => 'get_waf_rules',
|
1256 |
+
'k' => $waf->getStorageEngine()->getConfig('apiKey'),
|
1257 |
+
's' => $waf->getStorageEngine()->getConfig('siteURL') ? $waf->getStorageEngine()->getConfig('siteURL') : $guessSiteURL,
|
1258 |
+
'h' => $waf->getStorageEngine()->getConfig('homeURL') ? $waf->getStorageEngine()->getConfig('homeURL') : $guessSiteURL,
|
1259 |
+
'openssl' => $waf->hasOpenSSL() ? 1 : 0,
|
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() &&
|
1267 |
+
isset($jsonData['data']['signature']) &&
|
1268 |
+
isset($jsonData['data']['rules']) &&
|
1269 |
+
$waf->verifySignedRequest(base64_decode($jsonData['data']['signature']), $jsonData['data']['rules'])
|
1270 |
+
) {
|
1271 |
+
$waf->updateRuleSet(base64_decode($jsonData['data']['rules']),
|
1272 |
+
isset($jsonData['data']['timestamp']) ? $jsonData['data']['timestamp'] : true);
|
1273 |
+
if (array_key_exists('premiumCount', $jsonData['data'])) {
|
1274 |
+
$waf->getStorageEngine()->setConfig('premiumCount', $jsonData['data']['premiumCount']);
|
1275 |
+
}
|
1276 |
+
|
1277 |
+
} else if (!$waf->hasOpenSSL() &&
|
1278 |
+
isset($jsonData['data']['hash']) &&
|
1279 |
+
isset($jsonData['data']['rules']) &&
|
1280 |
+
$waf->verifyHashedRequest($jsonData['data']['hash'], $jsonData['data']['rules'])
|
1281 |
+
) {
|
1282 |
+
$waf->updateRuleSet(base64_decode($jsonData['data']['rules']),
|
1283 |
+
isset($jsonData['data']['timestamp']) ? $jsonData['data']['timestamp'] : true);
|
1284 |
+
if (array_key_exists('premiumCount', $jsonData['data'])) {
|
1285 |
+
$waf->getStorageEngine()->setConfig('premiumCount', $jsonData['data']['premiumCount']);
|
1286 |
+
}
|
1287 |
+
}
|
1288 |
+
|
1289 |
+
}
|
1290 |
+
}
|
1291 |
+
} catch (wfWAFHTTPTransportException $e) {
|
1292 |
+
error_log($e->getMessage());
|
1293 |
+
} catch (wfWAFBuildRulesException $e) {
|
1294 |
+
error_log($e->getMessage());
|
1295 |
+
}
|
1296 |
+
}
|
1297 |
+
|
1298 |
+
/**
|
1299 |
+
* @return wfWAFCronEvent|bool
|
1300 |
+
*/
|
1301 |
+
public function reschedule() {
|
1302 |
+
$waf = $this->getWaf();
|
1303 |
+
if (!$waf) {
|
1304 |
+
return false;
|
1305 |
+
}
|
1306 |
+
$newEvent = new self(time() + (86400 * ($waf->getStorageEngine()->getConfig('isPaid') ? .5 : 7)));
|
1307 |
+
if ($this->response) {
|
1308 |
+
$headers = $this->response->getHeaders();
|
1309 |
+
if (isset($headers['Expires'])) {
|
1310 |
+
$timestamp = strtotime($headers['Expires']);
|
1311 |
+
// Make sure it's at least 2 hours ahead.
|
1312 |
+
if ($timestamp && $timestamp > (time() + 7200)) {
|
1313 |
+
$newEvent->setFireTime($timestamp);
|
1314 |
+
}
|
1315 |
+
}
|
1316 |
+
}
|
1317 |
+
return $newEvent;
|
1318 |
+
}
|
1319 |
+
}
|
1320 |
+
|
1321 |
+
class wfWAFEventBus implements wfWAFObserver {
|
1322 |
+
|
1323 |
+
private $observers = array();
|
1324 |
+
|
1325 |
+
/**
|
1326 |
+
* @param wfWAFObserver $observer
|
1327 |
+
* @throws wfWAFEventBusException
|
1328 |
+
*/
|
1329 |
+
public function attach($observer) {
|
1330 |
+
if (!($observer instanceof wfWAFObserver)) {
|
1331 |
+
throw new wfWAFEventBusException('Observer supplied to wfWAFEventBus::attach must implement wfWAFObserver');
|
1332 |
+
}
|
1333 |
+
$this->observers[] = $observer;
|
1334 |
+
}
|
1335 |
+
|
1336 |
+
/**
|
1337 |
+
* @param wfWAFObserver $observer
|
1338 |
+
*/
|
1339 |
+
public function detach($observer) {
|
1340 |
+
$key = array_search($observer, $this->observers, true);
|
1341 |
+
if ($key !== false) {
|
1342 |
+
unset($this->observers[$key]);
|
1343 |
+
}
|
1344 |
+
}
|
1345 |
+
|
1346 |
+
public function prevBlocked($ip) {
|
1347 |
+
/** @var wfWAFObserver $observer */
|
1348 |
+
foreach ($this->observers as $observer) {
|
1349 |
+
$observer->prevBlocked($ip);
|
1350 |
+
}
|
1351 |
+
}
|
1352 |
+
|
1353 |
+
public function block($ip, $exception) {
|
1354 |
+
/** @var wfWAFObserver $observer */
|
1355 |
+
foreach ($this->observers as $observer) {
|
1356 |
+
$observer->block($ip, $exception);
|
1357 |
+
}
|
1358 |
+
}
|
1359 |
+
|
1360 |
+
public function allow($ip, $exception) {
|
1361 |
+
/** @var wfWAFObserver $observer */
|
1362 |
+
foreach ($this->observers as $observer) {
|
1363 |
+
$observer->allow($ip, $exception);
|
1364 |
+
}
|
1365 |
+
}
|
1366 |
+
|
1367 |
+
public function blockXSS($ip, $exception) {
|
1368 |
+
/** @var wfWAFObserver $observer */
|
1369 |
+
foreach ($this->observers as $observer) {
|
1370 |
+
$observer->blockXSS($ip, $exception);
|
1371 |
+
}
|
1372 |
+
}
|
1373 |
+
|
1374 |
+
public function blockSQLi($ip, $exception) {
|
1375 |
+
/** @var wfWAFObserver $observer */
|
1376 |
+
foreach ($this->observers as $observer) {
|
1377 |
+
$observer->blockSQLi($ip, $exception);
|
1378 |
+
}
|
1379 |
+
}
|
1380 |
+
|
1381 |
+
|
1382 |
+
public function wafDisabled() {
|
1383 |
+
/** @var wfWAFObserver $observer */
|
1384 |
+
foreach ($this->observers as $observer) {
|
1385 |
+
$observer->wafDisabled();
|
1386 |
+
}
|
1387 |
+
}
|
1388 |
+
|
1389 |
+
public function beforeRunRules() {
|
1390 |
+
/** @var wfWAFObserver $observer */
|
1391 |
+
foreach ($this->observers as $observer) {
|
1392 |
+
$observer->beforeRunRules();
|
1393 |
+
}
|
1394 |
+
}
|
1395 |
+
|
1396 |
+
public function afterRunRules() {
|
1397 |
+
/** @var wfWAFObserver $observer */
|
1398 |
+
foreach ($this->observers as $observer) {
|
1399 |
+
$observer->afterRunRules();
|
1400 |
+
}
|
1401 |
+
}
|
1402 |
+
}
|
1403 |
+
|
1404 |
+
interface wfWAFObserver {
|
1405 |
+
|
1406 |
+
public function prevBlocked($ip);
|
1407 |
+
|
1408 |
+
public function block($ip, $exception);
|
1409 |
+
|
1410 |
+
public function allow($ip, $exception);
|
1411 |
+
|
1412 |
+
public function blockXSS($ip, $exception);
|
1413 |
+
|
1414 |
+
public function blockSQLi($ip, $exception);
|
1415 |
+
|
1416 |
+
public function wafDisabled();
|
1417 |
+
|
1418 |
+
public function beforeRunRules();
|
1419 |
+
|
1420 |
+
public function afterRunRules();
|
1421 |
+
}
|
1422 |
+
|
1423 |
+
class wfWAFBaseObserver implements wfWAFObserver {
|
1424 |
+
|
1425 |
+
public function prevBlocked($ip) {
|
1426 |
+
|
1427 |
+
}
|
1428 |
+
|
1429 |
+
public function block($ip, $exception) {
|
1430 |
+
|
1431 |
+
}
|
1432 |
+
|
1433 |
+
public function allow($ip, $exception) {
|
1434 |
+
|
1435 |
+
}
|
1436 |
+
|
1437 |
+
public function blockXSS($ip, $exception) {
|
1438 |
+
|
1439 |
+
}
|
1440 |
+
|
1441 |
+
public function blockSQLi($ip, $exception) {
|
1442 |
+
|
1443 |
+
}
|
1444 |
+
|
1445 |
+
public function wafDisabled() {
|
1446 |
+
|
1447 |
+
}
|
1448 |
+
|
1449 |
+
public function beforeRunRules() {
|
1450 |
+
|
1451 |
+
}
|
1452 |
+
|
1453 |
+
public function afterRunRules() {
|
1454 |
+
|
1455 |
+
}
|
1456 |
+
}
|
1457 |
+
|
1458 |
+
class wfWAFException extends Exception {
|
1459 |
+
}
|
1460 |
+
|
1461 |
+
class wfWAFRunException extends Exception {
|
1462 |
+
|
1463 |
+
/** @var array */
|
1464 |
+
private $failedRules;
|
1465 |
+
/** @var string */
|
1466 |
+
private $paramKey;
|
1467 |
+
/** @var string */
|
1468 |
+
private $paramValue;
|
1469 |
+
/** @var wfWAFRequestInterface */
|
1470 |
+
private $request;
|
1471 |
+
|
1472 |
+
/**
|
1473 |
+
* @return array
|
1474 |
+
*/
|
1475 |
+
public function getFailedRules() {
|
1476 |
+
return $this->failedRules;
|
1477 |
+
}
|
1478 |
+
|
1479 |
+
/**
|
1480 |
+
* @param array $failedRules
|
1481 |
+
*/
|
1482 |
+
public function setFailedRules($failedRules) {
|
1483 |
+
$this->failedRules = $failedRules;
|
1484 |
+
}
|
1485 |
+
|
1486 |
+
/**
|
1487 |
+
* @return string
|
1488 |
+
*/
|
1489 |
+
public function getParamKey() {
|
1490 |
+
return $this->paramKey;
|
1491 |
+
}
|
1492 |
+
|
1493 |
+
/**
|
1494 |
+
* @param string $paramKey
|
1495 |
+
*/
|
1496 |
+
public function setParamKey($paramKey) {
|
1497 |
+
$this->paramKey = $paramKey;
|
1498 |
+
}
|
1499 |
+
|
1500 |
+
/**
|
1501 |
+
* @return string
|
1502 |
+
*/
|
1503 |
+
public function getParamValue() {
|
1504 |
+
return $this->paramValue;
|
1505 |
+
}
|
1506 |
+
|
1507 |
+
/**
|
1508 |
+
* @param string $paramValue
|
1509 |
+
*/
|
1510 |
+
public function setParamValue($paramValue) {
|
1511 |
+
$this->paramValue = $paramValue;
|
1512 |
+
}
|
1513 |
+
|
1514 |
+
/**
|
1515 |
+
* @return wfWAFRequestInterface
|
1516 |
+
*/
|
1517 |
+
public function getRequest() {
|
1518 |
+
return $this->request;
|
1519 |
+
}
|
1520 |
+
|
1521 |
+
/**
|
1522 |
+
* @param wfWAFRequestInterface $request
|
1523 |
+
*/
|
1524 |
+
public function setRequest($request) {
|
1525 |
+
$this->request = $request;
|
1526 |
+
}
|
1527 |
+
}
|
1528 |
+
|
1529 |
+
class wfWAFAllowException extends wfWAFRunException {
|
1530 |
+
}
|
1531 |
+
|
1532 |
+
class wfWAFBlockException extends wfWAFRunException {
|
1533 |
+
}
|
1534 |
+
|
1535 |
+
class wfWAFBlockXSSException extends wfWAFRunException {
|
1536 |
+
}
|
1537 |
+
|
1538 |
+
class wfWAFBlockSQLiException extends wfWAFRunException {
|
1539 |
+
}
|
1540 |
+
|
1541 |
+
class wfWAFBuildRulesException extends wfWAFException {
|
1542 |
+
}
|
1543 |
+
|
1544 |
+
class wfWAFEventBusException extends wfWAFException {
|
1545 |
+
}
|
vendor/wordfence/wf-waf/src/logs/.htaccess
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<IfModule mod_authz_core.c>
|
2 |
+
Require all denied
|
3 |
+
</IfModule>
|
4 |
+
<IfModule !mod_authz_core.c>
|
5 |
+
Order deny,allow
|
6 |
+
Deny from all
|
7 |
+
</IfModule>
|
vendor/wordfence/wf-waf/src/logs/attack-data.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php exit('Access denied'); __halt_compiler(); ?>
|
vendor/wordfence/wf-waf/src/logs/config.php
ADDED
Binary file
|
vendor/wordfence/wf-waf/src/logs/ips.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php exit('Access denied'); __halt_compiler(); ?>
|
vendor/wordfence/wf-waf/src/rules.key
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-----BEGIN PUBLIC KEY-----
|
2 |
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzovUDp/qu7r6LT5d8dLL
|
3 |
+
H/87aRrCjUd6XtnG+afAPVfMKNp4u4L+UuYfw1RfpfquP/zLMGdfmJCUp/oJywkW
|
4 |
+
Rkqo+y7pDuqIFQ59dHvizmYQRvaZgvincBDpey5Ek9AFfB9fqYYnH9+eQw8eLdQi
|
5 |
+
h6Zsh8RsuxFM2BW6JD9Km7L5Lyxw9jU+lye7I3ICYtUOVxc3n3bJT2SiIwHK57pW
|
6 |
+
g/asJEUDiYQzsaa90YPOLdf1Ysz2rkgnCduQaEGz/RPhgUrmZfKwq8puEmkh7Yee
|
7 |
+
auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
|
8 |
+
1QIDAQAB
|
9 |
+
-----END PUBLIC KEY-----
|
vendor/wordfence/wf-waf/src/rules.php
ADDED
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if (!defined('WFWAF_VERSION')) {
|
3 |
+
exit('Access denied');
|
4 |
+
}
|
5 |
+
/*
|
6 |
+
This file is generated automatically. Any changes made will be lost.
|
7 |
+
*/
|
8 |
+
|
9 |
+
$this->failScores['sqli'] = 100;
|
10 |
+
$this->failScores['xss'] = 100;
|
11 |
+
$this->failScores['rce'] = 100;
|
12 |
+
|
13 |
+
$this->variables['sqliRegex'] = new wfWAFRuleVariable($this, 'sqliRegex', '/(?:[^\\w<]|\\/\\*\\![0-9]*|^)(?:
|
14 |
+
@@HOSTNAME|
|
15 |
+
ALTER|ANALYZE|ASENSITIVE|
|
16 |
+
BEFORE|BENCHMARK|BETWEEN|BIGINT|BINARY|BLOB|
|
17 |
+
CALL|CASE|CHANGE|CHAR|CHARACTER|CHAR_LENGTH|COLLATE|COLUMN|CONCAT|CONDITION|CONSTRAINT|CONTINUE|CONVERT|CREATE|CROSS|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|
|
18 |
+
DATABASE|DATABASES|DAY_HOUR|DAY_MICROSECOND|DAY_MINUTE|DAY_SECOND|DECIMAL|DECLARE|DEFAULT|DELAYED|DELETE|DESCRIBE|DETERMINISTIC|DISTINCT|DISTINCTROW|DOUBLE|DROP|DUAL|DUMPFILE|
|
19 |
+
EACH|ELSE|ELSEIF|ELT|ENCLOSED|ESCAPED|EXISTS|EXIT|EXPLAIN|EXTRACTVALUE|
|
20 |
+
FETCH|FLOAT|FLOAT4|FLOAT8|FORCE|FOREIGN|FROM|FULLTEXT|
|
21 |
+
GRANT|GROUP|HAVING|HEX|HIGH_PRIORITY|HOUR_MICROSECOND|HOUR_MINUTE|HOUR_SECOND|
|
22 |
+
IFNULL|IGNORE|INDEX|INFILE|INNER|INOUT|INSENSITIVE|INSERT|INTERVAL|ISNULL|ITERATE|
|
23 |
+
JOIN|KILL|LEADING|LEAVE|LIMIT|LINEAR|LINES|LOAD|LOAD_FILE|LOCALTIME|LOCALTIMESTAMP|LOCK|LONG|LONGBLOB|LONGTEXT|LOOP|LOW_PRIORITY|
|
24 |
+
MASTER_SSL_VERIFY_SERVER_CERT|MATCH|MAXVALUE|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MID|MIDDLEINT|MINUTE_MICROSECOND|MINUTE_SECOND|MODIFIES|
|
25 |
+
NATURAL|NO_WRITE_TO_BINLOG|NULL|NUMERIC|OPTION|ORD|ORDER|OUTER|OUTFILE|
|
26 |
+
PRECISION|PRIMARY|PRIVILEGES|PROCEDURE|PROCESSLIST|PURGE|
|
27 |
+
RANGE|READ_WRITE|REGEXP|RELEASE|REPEAT|REQUIRE|RESIGNAL|RESTRICT|RETURN|REVOKE|RLIKE|ROLLBACK|
|
28 |
+
SCHEMA|SCHEMAS|SECOND_MICROSECOND|SELECT|SENSITIVE|SEPARATOR|SHOW|SIGNAL|SLEEP|SMALLINT|SPATIAL|SPECIFIC|SQLEXCEPTION|SQLSTATE|SQLWARNING|SQL_BIG_RESULT|SQL_CALC_FOUND_ROWS|SQL_SMALL_RESULT|STARTING|STRAIGHT_JOIN|SUBSTR|
|
29 |
+
TABLE|TERMINATED|TINYBLOB|TINYINT|TINYTEXT|TRAILING|TRANSACTION|TRIGGER|
|
30 |
+
UNDO|UNHEX|UNION|UNLOCK|UNSIGNED|UPDATE|UPDATEXML|USAGE|USING|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|
|
31 |
+
VALUES|VARBINARY|VARCHAR|VARCHARACTER|VARYING|WHEN|WHERE|WHILE|WRITE|YEAR_MONTH|ZEROFILL)(?=[^\\w]|$)/ix');
|
32 |
+
$this->variables['xssRegex'] = new wfWAFRuleVariable($this, 'xssRegex', '/(?:
|
33 |
+
#tags
|
34 |
+
(?:\\<|\\+ADw\\-|\\xC2\\xBC)(script|iframe|svg|object|embed|applet|link|style|meta|\\/\\/|\\?xml\\-stylesheet)(?:[^\\w]|\\xC2\\xBE)|
|
35 |
+
#protocols
|
36 |
+
(?:^|[^\\w])(?:(?:\\s*(?:&\\#(?:x0*6a|0*106)|j)\\s*(?:&\\#(?:x0*61|0*97)|a)\\s*(?:&\\#(?:x0*76|0*118)|v)\\s*(?:&\\#(?:x0*61|0*97)|a)|\\s*(?:&\\#(?:x0*76|0*118)|v)\\s*(?:&\\#(?:x0*62|0*98)|b)|\\s*(?:&\\#(?:x0*65|0*101)|e)\\s*(?:&\\#(?:x0*63|0*99)|c)\\s*(?:&\\#(?:x0*6d|0*109)|m)\\s*(?:&\\#(?:x0*61|0*97)|a)|\\s*(?:&\\#(?:x0*6c|0*108)|l)\\s*(?:&\\#(?:x0*69|0*105)|i)\\s*(?:&\\#(?:x0*76|0*118)|v)\\s*(?:&\\#(?:x0*65|0*101)|e))\\s*(?:&\\#(?:x0*73|0*115)|s)\\s*(?:&\\#(?:x0*63|0*99)|c)\\s*(?:&\\#(?:x0*72|0*114)|r)\\s*(?:&\\#(?:x0*69|0*105)|i)\\s*(?:&\\#(?:x0*70|0*112)|p)\\s*(?:&\\#(?:x0*74|0*116)|t)|\\s*(?:&\\#(?:x0*6d|0*109)|m)\\s*(?:&\\#(?:x0*68|0*104)|h)\\s*(?:&\\#(?:x0*74|0*116)|t)\\s*(?:&\\#(?:x0*6d|0*109)|m)\\s*(?:&\\#(?:x0*6c|0*108)|l)|\\s*(?:&\\#(?:x0*6d|0*109)|m)\\s*(?:&\\#(?:x0*6f|0*111)|o)\\s*(?:&\\#(?:x0*63|0*99)|c)\\s*(?:&\\#(?:x0*68|0*104)|h)\\s*(?:&\\#(?:x0*61|0*97)|a)|\\s*(?:&\\#(?:x0*64|0*100)|d)\\s*(?:&\\#(?:x0*61|0*97)|a)\\s*(?:&\\#(?:x0*74|0*116)|t)\\s*(?:&\\#(?:x0*61|0*97)|a))\\s*(?:&\\#(?:x0*3a|0*58)|\\:)|
|
37 |
+
#css expression
|
38 |
+
(?:^|[^\\w])(?:(?:\\\\0*65|\\\\0*45|e)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*78|\\\\0*58|x)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*70|\\\\0*50|p)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*72|\\\\0*52|r)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*65|\\\\0*45|e)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*73|\\\\0*53|s)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*73|\\\\0*53|s)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*69|\\\\0*49|i)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*6f|\\\\0*4f|o)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*6e|\\\\0*4e|n))[^\\w]*?(?:\\\\0*28|\\()|
|
39 |
+
#css properties
|
40 |
+
(?:^|[^\\w])(?:(?:(?:\\\\0*62|\\\\0*42|b)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*65|\\\\0*45|e)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*68|\\\\0*48|h)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*61|\\\\0*41|a)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*76|\\\\0*56|v)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*69|\\\\0*49|i)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*6f|\\\\0*4f|o)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*72|\\\\0*52|r)(?:\\/\\*.*?\\*\\/)*)|(?:(?:\\\\0*2d|\\\\0*2d|-)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*6d|\\\\0*4d|m)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*6f|\\\\0*4f|o)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*7a|\\\\0*5a|z)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*2d|\\\\0*2d|-)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*62|\\\\0*42|b)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*69|\\\\0*49|i)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*6e|\\\\0*4e|n)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*64|\\\\0*44|d)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*69|\\\\0*49|i)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*6e|\\\\0*4e|n)(?:\\/\\*.*?\\*\\/)*(?:\\\\0*67|\\\\0*47|g)(?:\\/\\*.*?\\*\\/)*))[^\\w]*(?:\\\\0*3a|\\\\0*3a|:)[^\\w]*(?:\\\\0*75|\\\\0*55|u)(?:\\\\0*72|\\\\0*52|r)(?:\\\\0*6c|\\\\0*4c|l)|
|
41 |
+
#properties
|
42 |
+
(?:^|[^\\w])(?:on(?:abort|activate|afterprint|afterupdate|autocomplete|autocompleteerror|beforeactivate|beforecopy|beforecut|beforedeactivate|beforeeditfocus|beforepaste|beforeprint|beforeunload|beforeupdate|blur|bounce|cancel|canplay|canplaythrough|cellchange|change|click|close|contextmenu|controlselect|copy|cuechange|cut|dataavailable|datasetchanged|datasetcomplete|dblclick|deactivate|drag|dragend|dragenter|dragleave|dragover|dragstart|drop|durationchange|emptied|encrypted|ended|error|errorupdate|filterchange|finish|focus|focusin|focusout|formaction|formchange|forminput|hashchange|help|input|invalid|keydown|keypress|keyup|languagechange|layoutcomplete|load|loadeddata|loadedmetadata|loadstart|losecapture|message|mousedown|mouseenter|mouseleave|mousemove|mouseout|mouseover|mouseup|mousewheel|move|moveend|movestart|mozfullscreenchange|mozfullscreenerror|mozpointerlockchange|mozpointerlockerror|offline|online|page|pagehide|pageshow|paste|pause|play|playing|popstate|progress|propertychange|ratechange|readystatechange|reset|resize|resizeend|resizestart|rowenter|rowexit|rowsdelete|rowsinserted|scroll|search|seeked|seeking|select|selectstart|show|stalled|start|storage|submit|suspend|timer|timeupdate|toggle|unload|volumechange|waiting|webkitfullscreenchange|webkitfullscreenerror|wheel)|data\\-bind|ev:event)[^\\w]
|
43 |
+
)/ix');
|
44 |
+
|
45 |
+
$this->blacklistedParams['request.queryString[action]'][] = '/\\/wp\\-admin[\\/]+admin\\-ajax\\.php/i';
|
46 |
+
$this->blacklistedParams['request.queryString[img]'][] = '/\\/wp\\-admin[\\/]+admin\\-ajax\\.php/i';
|
47 |
+
$this->blacklistedParams['request.body[action]'][] = '/\\/wp\\-admin[\\/]+admin\\-ajax\\.php/i';
|
48 |
+
$this->blacklistedParams['request.body[img]'][] = '/\\/wp\\-admin[\\/]+admin\\-ajax\\.php/i';
|
49 |
+
$this->blacklistedParams['request.body[nsextt]'][] = '/.*/';
|
50 |
+
$this->blacklistedParams['request.fileNames[Filedata]'][] = '/\\/uploadify\\.php$/i';
|
51 |
+
$this->blacklistedParams['request.fileNames[yiw_contact]'][] = '/.*/';
|
52 |
+
$this->blacklistedParams['request.fileNames[filename]'][] = '/\\/license\\.php$/i';
|
53 |
+
$this->blacklistedParams['request.fileNames[update_file]'][] = '/\\/wp\\-admin[\\/]+admin\\-ajax\\.php$/i';
|
54 |
+
$this->blacklistedParams['request.fileNames[Filedata]'][] = '/tiny_mce[\\/]+plugins[\\/]+tinybrowser[\\/]+upload_file\\.php$/i';
|
55 |
+
$this->blacklistedParams['request.fileNames[upload]'][] = '/elfinder[\\/]+php[\\/]+connector\\.minimal\\.php$/i';
|
56 |
+
|
57 |
+
$this->whitelistedParams['request.body[excerpt]'][] = '/.*/';
|
58 |
+
$this->whitelistedParams['request.body[comment]'][] = array (
|
59 |
+
'url' => '/wp-comments-post\\.php$/i',
|
60 |
+
'rules' =>
|
61 |
+
array (
|
62 |
+
0 => '3',
|
63 |
+
1 => '12',
|
64 |
+
),
|
65 |
+
);
|
66 |
+
$this->whitelistedParams['request.body[content]'][] = '/\\/wp-admin\\/admin-ajax\\.php$/i';
|
67 |
+
$this->whitelistedParams['request.body[data]'][] = '/\\/wp-admin\\/admin-ajax\\.php$/i';
|
68 |
+
$this->whitelistedParams['request.queryString[s]'][] = '/\\/wp-admin\\/(?:network\\/)?(?:plugin(?:s|-install)|edit)\\.php$/i';
|
69 |
+
$this->whitelistedParams['request.body[whitelistedPath]'][] = '/\\/wp-admin\\/admin-ajax\\.php$/i';
|
70 |
+
$this->whitelistedParams['request.body[whitelistedParam]'][] = '/\\/wp-admin\\/admin-ajax\\.php$/i';
|
71 |
+
$this->whitelistedParams['request.body[oldWhitelistedPath]'][] = '/\\/wp-admin\\/admin-ajax\\.php$/i';
|
72 |
+
$this->whitelistedParams['request.body[oldWhitelistedParam]'][] = '/\\/wp-admin\\/admin-ajax\\.php$/i';
|
73 |
+
$this->whitelistedParams['request.body[newWhitelistedPath]'][] = '/\\/wp-admin\\/admin-ajax\\.php$/i';
|
74 |
+
$this->whitelistedParams['request.body[newWhitelistedParam]'][] = '/\\/wp-admin\\/admin-ajax\\.php$/i';
|
75 |
+
$this->whitelistedParams['request.body[bannedURLs]'][] = '/\\/wp-admin\\/admin-ajax\\.php$/i';
|
76 |
+
$this->whitelistedParams['request.body[scan_include_extra]'][] = '/\\/wp-admin\\/admin-ajax\\.php$/i';
|
77 |
+
$this->whitelistedParams['request.body[newcontent]'][] = '/\\/wp-admin\\/(?:network\\/)?(?:plugin|theme)-editor\\.php$/i';
|
78 |
+
$this->whitelistedParams['request.queryString[_wp_http_referer]'][] = '/.{0,1}/';
|
79 |
+
$this->whitelistedParams['request.queryString[plugin]'][] = '/\\/wp-admin\\/(?:network\\/)?plugins\\.php$/i';
|
80 |
+
$this->whitelistedParams['request.queryString[action]'][] = '/\\/wp-admin\\/(?:network\\/)?plugins\\.php$/i';
|
81 |
+
$this->whitelistedParams['request.queryString[checked]'][] = '/\\/wp-admin\\/(?:network\\/)?plugins\\.php$/i';
|
82 |
+
$this->whitelistedParams['request.body[action]'][] = '/\\/wp-admin\\/(?:network\\/)?plugins\\.php$/i';
|
83 |
+
$this->whitelistedParams['request.body[checked]'][] = '/\\/wp-admin\\/(?:network\\/)?plugins\\.php$/i';
|
84 |
+
$this->whitelistedParams['request.body[submit]'][] = '/\\/wp-admin\\/(?:network\\/)?plugins\\.php$/i';
|
85 |
+
$this->whitelistedParams['request.body[blogname]'][] = '/\\/wp-admin\\/options\\.php$/i';
|
86 |
+
$this->whitelistedParams['request.body[blogdescription]'][] = '/\\/wp-admin\\/options\\.php$/i';
|
87 |
+
$this->whitelistedParams['request.body[siteurl]'][] = '/\\/wp-admin\\/options\\.php$/i';
|
88 |
+
$this->whitelistedParams['request.body[home]'][] = '/\\/wp-admin\\/options\\.php$/i';
|
89 |
+
$this->whitelistedParams['request.body[admin_email]'][] = '/\\/wp-admin\\/options\\.php$/i';
|
90 |
+
$this->whitelistedParams['request.body[moderation_keys]'][] = '/\\/wp-admin\\/options\\.php$/i';
|
91 |
+
$this->whitelistedParams['request.body[blacklist_keys]'][] = '/\\/wp-admin\\/options\\.php$/i';
|
92 |
+
$this->whitelistedParams['request.body[permalink_structure]'][] = '/\\/wp-admin\\/options\\.php$/i';
|
93 |
+
$this->whitelistedParams['request.body[category_base]'][] = '/\\/wp-admin\\/options\\.php$/i';
|
94 |
+
$this->whitelistedParams['request.body[tag_base]'][] = '/\\/wp-admin\\/options\\.php$/i';
|
95 |
+
$this->whitelistedParams['request.queryString[s]'][] = '/\\/wp-admin\\/edit-comments\\.php$/i';
|
96 |
+
$this->whitelistedParams['request.body[log]'][] = '/\\/wp-login\\.php$/i';
|
97 |
+
$this->whitelistedParams['request.body[pwd]'][] = '/\\/wp-login\\.php$/i';
|
98 |
+
$this->whitelistedParams['request.body[redirect_to]'][] = '/\\/wp-login\\.php$/i';
|
99 |
+
$this->whitelistedParams['request.queryString[s]'][] = '/\\/wp-admin\\/network\\/(?:user|site)s\\.php$/i';
|
100 |
+
$this->whitelistedParams['request.body[blog]'][] = '/\\/wp-admin\\/network\\/site-new\\.php$/i';
|
101 |
+
$this->whitelistedParams['request.body[deletedWhitelistedPath]'][] = '/\\/wp-admin\\/admin-ajax\\.php$/i';
|
102 |
+
$this->whitelistedParams['request.body[deletedWhitelistedParam]'][] = '/\\/wp-admin\\/admin-ajax\\.php$/i';
|
103 |
+
$this->whitelistedParams['request.body[itsec_global][log_location]'][] = '/\\/wp-admin\\/options\\.php$/i';
|
104 |
+
$this->whitelistedParams['request.body[itsec_backup][location]'][] = '/\\/wp-admin\\/options\\.php$/i';
|
105 |
+
$this->whitelistedParams['request.body[dir]'][] = '/\\/wp-admin\\/admin-ajax\\.php$/i';
|
106 |
+
$this->whitelistedParams['request.body[sql_query]'][] = '/(?:lint|import)\\.php$/i';
|
107 |
+
|
108 |
+
$this->rules[18] = wfWAFRule::create($this, 18, NULL, 'priv-esc', NULL, 'User Roles Manager Priviledge Escalation <= 4.24', 'block', new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'notEquals', '', array(wfWAFRuleComparisonSubject::create($this, array (
|
109 |
+
0 => 'request.body',
|
110 |
+
1 => 'ure_other_roles',
|
111 |
+
), array (
|
112 |
+
)))), new wfWAFRuleLogicalOperator('AND'), new wfWAFRuleComparison($this, 'match', '#/wp\\-admin/(network/)?(profile|user-new)\\.php#i', array(wfWAFRuleComparisonSubject::create($this, 'request.path', array (
|
113 |
+
)))), new wfWAFRuleLogicalOperator('AND'), new wfWAFRuleComparison($this, 'currentUserIsNot', 'administrator', array(wfWAFRuleComparisonSubject::create($this, 'server.empty', array (
|
114 |
+
))))));
|
115 |
+
$this->rules[1] = wfWAFRule::create($this, 1, NULL, 'whitelist', NULL, 'Whitelisted URL', 'allow', new wfWAFRuleComparisonGroup(new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'match', '#/wp\\-admin/(network/)?(post|profile|user-new|settings)\\.php$#i', array(wfWAFRuleComparisonSubject::create($this, 'server.script_filename', array (
|
116 |
+
))))), new wfWAFRuleLogicalOperator('OR'), new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'match', '#/wp\\-admin/admin\\-ajax\\.php$#i', array(wfWAFRuleComparisonSubject::create($this, 'server.script_filename', array (
|
117 |
+
)))), new wfWAFRuleLogicalOperator('AND'), new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'equals', 'wordfence_loadLiveTraffic', array(wfWAFRuleComparisonSubject::create($this, array (
|
118 |
+
0 => 'request.body',
|
119 |
+
1 => 'action',
|
120 |
+
), array (
|
121 |
+
)))), new wfWAFRuleLogicalOperator('OR'), new wfWAFRuleComparison($this, 'equals', 'wordfence_ticker', array(wfWAFRuleComparisonSubject::create($this, array (
|
122 |
+
0 => 'request.body',
|
123 |
+
1 => 'action',
|
124 |
+
), array (
|
125 |
+
))))))));
|
126 |
+
$this->rules[2] = wfWAFRule::create($this, 2, NULL, 'lfi', NULL, 'Slider Revolution: Local File Inclusion', 'block', new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'match', '/\\/wp\\-admin[\\/]+admin\\-ajax\\.php/', array(wfWAFRuleComparisonSubject::create($this, 'request.path', array (
|
127 |
+
)))), new wfWAFRuleLogicalOperator('AND'), new wfWAFRuleComparisonGroup(new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'equals', 'revslider_show_image', array(wfWAFRuleComparisonSubject::create($this, array (
|
128 |
+
0 => 'request.queryString',
|
129 |
+
1 => 'action',
|
130 |
+
), array (
|
131 |
+
)))), new wfWAFRuleLogicalOperator('AND'), new wfWAFRuleComparison($this, 'match', '/\\.php$/i', array(wfWAFRuleComparisonSubject::create($this, array (
|
132 |
+
0 => 'request.queryString',
|
133 |
+
1 => 'img',
|
134 |
+
), array (
|
135 |
+
))))), new wfWAFRuleLogicalOperator('OR'), new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'equals', 'revslider_show_image', array(wfWAFRuleComparisonSubject::create($this, array (
|
136 |
+
0 => 'request.body',
|
137 |
+
1 => 'action',
|
138 |
+
), array (
|
139 |
+
)))), new wfWAFRuleLogicalOperator('AND'), new wfWAFRuleComparison($this, 'match', '/\\.php$/i', array(wfWAFRuleComparisonSubject::create($this, array (
|
140 |
+
0 => 'request.body',
|
141 |
+
1 => 'img',
|
142 |
+
), array (
|
143 |
+
))))))));
|
144 |
+
$this->rules[15] = wfWAFRule::create($this, 15, NULL, 'xss', NULL, 'dzs-videogallery 8.80 XSS HTML injection in inline JavaScript', 'blockXSS', new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'match', '/dzs\\-videogallery[\\/]+admin[\\/]+(?:playlist|tag)seditor[\\/]+popup\\.php/', array(wfWAFRuleComparisonSubject::create($this, 'request.path', array (
|
145 |
+
)))), new wfWAFRuleLogicalOperator('AND'), new wfWAFRuleComparison($this, 'contains', '\'', array(wfWAFRuleComparisonSubject::create($this, array (
|
146 |
+
0 => 'request.queryString',
|
147 |
+
1 => 'initer',
|
148 |
+
), array (
|
149 |
+
))))));
|
150 |
+
$this->rules[16] = wfWAFRule::create($this, 16, NULL, 'sqli', NULL, 'Simple Ads Manager <= 2.9.4.116 - SQL Injection', 'block', new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'match', '/simple-ads-manager[\\/]+sam-ajax-loader\\.php/', array(wfWAFRuleComparisonSubject::create($this, 'request.path', array (
|
151 |
+
)))), new wfWAFRuleLogicalOperator('AND'), new wfWAFRuleComparison($this, 'match', new wfWAFRuleVariable($this, 'sqliRegex', NULL), array(wfWAFRuleComparisonSubject::create($this, array (
|
152 |
+
0 => 'request.body',
|
153 |
+
1 => 'wc',
|
154 |
+
), array (
|
155 |
+
0 => 'base64decode',
|
156 |
+
))))));
|
157 |
+
$this->rules[17] = wfWAFRule::create($this, 17, NULL, 'rfi', NULL, 'Gwolle Guestbook <= 1.5.3 - Remote File Inclusion', 'block', new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'match', '/gwolle\\-gb[\\/]+frontend[\\/]+captcha[\\/]+ajaxresponse\\.php/', array(wfWAFRuleComparisonSubject::create($this, 'request.path', array (
|
158 |
+
)))), new wfWAFRuleLogicalOperator('AND'), new wfWAFRuleComparison($this, 'match', '/.*/', array(wfWAFRuleComparisonSubject::create($this, array (
|
159 |
+
0 => 'request.queryString',
|
160 |
+
1 => 'abspath',
|
161 |
+
), array (
|
162 |
+
))))));
|
163 |
+
$this->rules[3] = wfWAFRule::create($this, 3, NULL, 'sqli', '40', 'SQL Injection', 'failSQLi', new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'matchCount', new wfWAFRuleVariable($this, 'sqliRegex', NULL), array(wfWAFRuleComparisonSubject::create($this, 'request.body', array (
|
164 |
+
)),
|
165 |
+
wfWAFRuleComparisonSubject::create($this, 'request.queryString', array (
|
166 |
+
))))));
|
167 |
+
$this->rules[9] = wfWAFRule::create($this, 9, NULL, 'xss', '100', 'XSS: Cross Site Scripting', 'failXSS', new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'matchCount', new wfWAFRuleVariable($this, 'xssRegex', NULL), array(wfWAFRuleComparisonSubject::create($this, 'request.body', array (
|
168 |
+
)),
|
169 |
+
wfWAFRuleComparisonSubject::create($this, 'request.queryString', array (
|
170 |
+
))))));
|
171 |
+
$this->rules[11] = wfWAFRule::create($this, 11, NULL, 'file_upload', NULL, 'Malicous File Upload', 'block', new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'match', '/\\.(p(h(p|tml)[0-9]?|l|y)|(j|a)sp|aspx|sh|shtml|html?|cgi|htaccess)($|\\.)/i', array(wfWAFRuleComparisonSubject::create($this, 'request.fileNames', array (
|
172 |
+
))))));
|
173 |
+
$this->rules[12] = wfWAFRule::create($this, 12, NULL, 'lfi', NULL, 'Directory Traversal', 'block', new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'match', '/(^|\\/|\\\\)\\.\\.(\\\\|\\/)/', array(wfWAFRuleComparisonSubject::create($this, 'request.body', array (
|
174 |
+
)),
|
175 |
+
wfWAFRuleComparisonSubject::create($this, 'request.queryString', array (
|
176 |
+
))))));
|
177 |
+
$this->rules[13] = wfWAFRule::create($this, 13, NULL, 'lfi', NULL, 'LFI: Local File Inclusion', 'block', new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'match', '/^\\/(?:\\.\\/)*(?:var|home|usr|mnt|media|etc|tmp|dev|proc)\\//i', array(wfWAFRuleComparisonSubject::create($this, 'request.body', array (
|
178 |
+
)),
|
179 |
+
wfWAFRuleComparisonSubject::create($this, 'request.queryString', array (
|
180 |
+
))))));
|
181 |
+
$this->rules[14] = wfWAFRule::create($this, 14, NULL, 'xxe', NULL, 'XXE: External Entity Expansion', 'block', new wfWAFRuleComparisonGroup(new wfWAFRuleComparison($this, 'match', '/<\\!(?:DOCTYPE|ENTITY)\\s+(?:%\\s*)?\\w+\\s+SYSTEM/i', array(wfWAFRuleComparisonSubject::create($this, 'request.body', array (
|
182 |
+
)),
|
183 |
+
wfWAFRuleComparisonSubject::create($this, 'request.queryString', array (
|
184 |
+
))))));
|
185 |
+
?>
|
vendor/wordfence/wf-waf/src/views/403-roadblock.php
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
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) {
|
10 |
+
foreach ($failedRules as $failedRule) {
|
11 |
+
/**
|
12 |
+
* @var wfWAFRule $rule
|
13 |
+
* @var wfWAFRuleComparisonFailure $failedComparison
|
14 |
+
*/
|
15 |
+
$rule = $failedRule['rule'];
|
16 |
+
$failedComparison = $failedRule['failedComparison'];
|
17 |
+
|
18 |
+
$urlParamsToWhitelist[] = array(
|
19 |
+
'path' => $waf->getRequest()->getPath(),
|
20 |
+
'paramKey' => $failedComparison->getParamKey(),
|
21 |
+
'ruleID' => $rule->getRuleID(),
|
22 |
+
);
|
23 |
+
}
|
24 |
+
}
|
25 |
+
}
|
26 |
+
|
27 |
+
|
28 |
+
?>
|
29 |
+
<!DOCTYPE html>
|
30 |
+
<html>
|
31 |
+
<head>
|
32 |
+
<meta charset="UTF-8">
|
33 |
+
<title>403 Forbidden</title>
|
34 |
+
</head>
|
35 |
+
<body>
|
36 |
+
|
37 |
+
<h1>403 Forbidden</h1>
|
38 |
+
|
39 |
+
<p>A potentially unsafe operation has been detected in your request to this site, and has been blocked by Wordfence.</p>
|
40 |
+
|
41 |
+
<?php if ($urlParamsToWhitelist): ?>
|
42 |
+
<p>If you are an administrator and you are certain this is a false positive, you can automatically whitelist this
|
43 |
+
request and repeat the same action.</p>
|
44 |
+
|
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 |
+
|
52 |
+
<div id="whitelist-actions">
|
53 |
+
<p>
|
54 |
+
<label>
|
55 |
+
<input id="verified-false-positive-checkbox" type="checkbox" name="wfwaf-false-positive-verified"
|
56 |
+
value="1">
|
57 |
+
<em>I am certain this is a false positive.</em>
|
58 |
+
</label>
|
59 |
+
</p>
|
60 |
+
|
61 |
+
<p>
|
62 |
+
<button id="whitelist-button" type="submit">Whitelist This Action</button>
|
63 |
+
</p>
|
64 |
+
</div>
|
65 |
+
|
66 |
+
<p id="success" style="color: #35b13a; font-weight: bold; display: none"><em>All set! You can refresh the page
|
67 |
+
to try this action again.</em></p>
|
68 |
+
|
69 |
+
<p id="error" style="color: #dd422c; font-weight: bold; display: none"><em>Something went wrong whitelisting
|
70 |
+
this request. You can try setting the Firewall Status to Learning Mode under Web App Firewall in the
|
71 |
+
Wordfence menu, and retry this same action.</em></p>
|
72 |
+
</form>
|
73 |
+
<script>
|
74 |
+
var whitelistButton = document.getElementById('whitelist-button');
|
75 |
+
var verified = document.getElementById('verified-false-positive-checkbox');
|
76 |
+
verified.checked = false;
|
77 |
+
verified.onclick = function() {
|
78 |
+
whitelistButton.disabled = !this.checked;
|
79 |
+
};
|
80 |
+
verified.onclick();
|
81 |
+
|
82 |
+
document.getElementById('whitelist-form').onsubmit = function(evt) {
|
83 |
+
evt.preventDefault();
|
84 |
+
var request = new XMLHttpRequest();
|
85 |
+
request.addEventListener("load", function() {
|
86 |
+
if (this.status === 200 && this.responseText.indexOf('Successfully whitelisted') > -1) {
|
87 |
+
document.getElementById('whitelist-actions').style.display = 'none';
|
88 |
+
document.getElementById('success').style.display = 'block';
|
89 |
+
} else {
|
90 |
+
document.getElementById('error').style.display = 'block';
|
91 |
+
}
|
92 |
+
});
|
93 |
+
request.open("POST", this.action, true);
|
94 |
+
request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
95 |
+
var inputs = this.querySelectorAll('input[name]');
|
96 |
+
var data = '';
|
97 |
+
for (var i = 0; i < inputs.length; i++) {
|
98 |
+
data += encodeURIComponent(inputs[i].name) + '=' + encodeURIComponent(inputs[i].value) + '&';
|
99 |
+
}
|
100 |
+
request.send(data);
|
101 |
+
return false;
|
102 |
+
};
|
103 |
+
|
104 |
+
|
105 |
+
</script>
|
106 |
+
|
107 |
+
<?php endif ?>
|
108 |
+
|
109 |
+
</body>
|
110 |
+
</html>
|
vendor/wordfence/wf-waf/src/views/403.php
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<title>403 Forbidden</title>
|
6 |
+
</head>
|
7 |
+
<body>
|
8 |
+
|
9 |
+
<h1>403 Forbidden</h1>
|
10 |
+
|
11 |
+
<p>A potentially unsafe operation has been detected in your request to this site.</p>
|
12 |
+
|
13 |
+
</body>
|
14 |
+
</html>
|
views/waf/debug.php
ADDED
@@ -0,0 +1,225 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/** @var wfRequestModel $hit */
|
4 |
+
/** @var stdClass $hitData */
|
5 |
+
|
6 |
+
$title = sprintf('Debugging #%d as False Positive', $hit->id);
|
7 |
+
|
8 |
+
$fields = array(
|
9 |
+
'URL' => $hit->URL,
|
10 |
+
'Timestamp' => date('r', $hit->ctime),
|
11 |
+
'IP' => wfUtils::inet_ntop($hit->IP),
|
12 |
+
'Status Code' => $hit->statusCode,
|
13 |
+
'User Agent' => $hit->UA,
|
14 |
+
'Referer' => $hit->referer,
|
15 |
+
);
|
16 |
+
|
17 |
+
if (isset($hitData->fullRequest)) {
|
18 |
+
$requestString = base64_decode($hitData->fullRequest);
|
19 |
+
$request = wfWAFRequest::parseString($requestString);
|
20 |
+
} else {
|
21 |
+
$request = new wfWAFRequest();
|
22 |
+
$request->setAuth(array());
|
23 |
+
$request->setBody(array());
|
24 |
+
$request->setCookies(array());
|
25 |
+
$request->setFileNames(array());
|
26 |
+
$request->setFiles(array());
|
27 |
+
$request->setHeaders(array());
|
28 |
+
$request->setHost('');
|
29 |
+
$request->setIp('');
|
30 |
+
$request->setMethod('GET');
|
31 |
+
$request->setPath('');
|
32 |
+
$request->setProtocol('http');
|
33 |
+
$request->setQueryString(array());
|
34 |
+
$request->setTimestamp('');
|
35 |
+
$request->setUri('');
|
36 |
+
|
37 |
+
$headers = array();
|
38 |
+
$urlPieces = parse_url($hit->URL);
|
39 |
+
if ($urlPieces) {
|
40 |
+
if (array_key_exists('scheme', $urlPieces)) {
|
41 |
+
$request->setProtocol($urlPieces['scheme']);
|
42 |
+
}
|
43 |
+
if (array_key_exists('host', $urlPieces)) {
|
44 |
+
$request->setHost($urlPieces['host']);
|
45 |
+
$headers['Host'] = $urlPieces['host'];
|
46 |
+
}
|
47 |
+
$uri = '/';
|
48 |
+
if (array_key_exists('path', $urlPieces)) {
|
49 |
+
$request->setPath($urlPieces['path']);
|
50 |
+
$uri = $urlPieces['path'];
|
51 |
+
}
|
52 |
+
if (array_key_exists('query', $urlPieces)) {
|
53 |
+
$uri .= '?' . $urlPieces['query'];
|
54 |
+
parse_str($urlPieces['query'], $query);
|
55 |
+
$request->setQueryString($query);
|
56 |
+
}
|
57 |
+
$request->setUri($uri);
|
58 |
+
}
|
59 |
+
$headers['User-Agent'] = $hit->UA;
|
60 |
+
$headers['Referer'] = $hit->referer;
|
61 |
+
$request->setHeaders($headers);
|
62 |
+
|
63 |
+
preg_match('/request\.([a-z]+)(?:\[(.*?)\](.*?))?/i', $hitData->paramKey, $matches);
|
64 |
+
if ($matches) {
|
65 |
+
switch ($matches[1]) {
|
66 |
+
case 'body':
|
67 |
+
$request->setMethod('POST');
|
68 |
+
parse_str("$matches[2]$matches[3]", $body);
|
69 |
+
$request->setBody($body);
|
70 |
+
break;
|
71 |
+
}
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
$request->setIP(wfUtils::inet_ntop($hit->IP));
|
76 |
+
$request->setTimestamp($hit->ctime);
|
77 |
+
|
78 |
+
|
79 |
+
$waf = wfWAF::getInstance();
|
80 |
+
$waf->setRequest($request);
|
81 |
+
|
82 |
+
$result = '<strong class="ok">Passed</strong>';
|
83 |
+
$failedRules = array();
|
84 |
+
try {
|
85 |
+
$waf->runRules();
|
86 |
+
} catch (wfWAFAllowException $e) {
|
87 |
+
$result = '<strong class="ok">Whitelisted</strong>';
|
88 |
+
} catch (wfWAFBlockException $e) {
|
89 |
+
$result = '<strong class="error">Blocked</strong>';
|
90 |
+
$failedRules = $waf->getFailedRules();
|
91 |
+
} catch (wfWAFBlockSQLiException $e) {
|
92 |
+
$result = '<strong class="error">Blocked For SQLi</strong>';
|
93 |
+
$failedRules = $waf->getFailedRules();
|
94 |
+
} catch (wfWAFBlockXSSException $e) {
|
95 |
+
$result = '<strong class="error">Blocked For XSS</strong>';
|
96 |
+
$failedRules = $waf->getFailedRules();
|
97 |
+
}
|
98 |
+
|
99 |
+
?>
|
100 |
+
<!doctype html>
|
101 |
+
<html lang="en">
|
102 |
+
<head>
|
103 |
+
<meta charset="UTF-8">
|
104 |
+
<title><?php echo esc_html($title) ?></title>
|
105 |
+
<link rel="stylesheet" href="<?php echo wfUtils::getBaseURL() . 'css/main.css' ?>">
|
106 |
+
<style>
|
107 |
+
html {
|
108 |
+
font-family: "Open Sans", Helvetica, Arial, sans-serif;
|
109 |
+
}
|
110 |
+
h1, h2, h3, h4, h5 {
|
111 |
+
margin: 20px 0px 8px;
|
112 |
+
}
|
113 |
+
pre, p {
|
114 |
+
8px 0px 20px;
|
115 |
+
}
|
116 |
+
pre.request-debug {
|
117 |
+
padding: 12px;
|
118 |
+
background: #fafafa;
|
119 |
+
border: 1px solid #999999;
|
120 |
+
overflow: auto;
|
121 |
+
}
|
122 |
+
pre.request-debug em {
|
123 |
+
font-style: normal;
|
124 |
+
padding: 1px;
|
125 |
+
border: 1px solid #ffb463;
|
126 |
+
background-color: #ffffe0;
|
127 |
+
border-radius: 2px;
|
128 |
+
}
|
129 |
+
pre.request-debug strong {
|
130 |
+
border: 1px solid #ff4a35;
|
131 |
+
background-color: #ffefe7;
|
132 |
+
margin: 1px;
|
133 |
+
}
|
134 |
+
.ok {
|
135 |
+
color: #00c000;
|
136 |
+
}
|
137 |
+
.error {
|
138 |
+
color: #ff4a35;
|
139 |
+
}
|
140 |
+
#wrapper {
|
141 |
+
max-width: 1060px;
|
142 |
+
margin: 0px auto;
|
143 |
+
}
|
144 |
+
</style>
|
145 |
+
</head>
|
146 |
+
<body>
|
147 |
+
<div id="wrapper">
|
148 |
+
<h1><?php echo esc_html($title) ?></h1>
|
149 |
+
|
150 |
+
<table class="wf-table">
|
151 |
+
<thead>
|
152 |
+
<tr>
|
153 |
+
<th colspan="2">Request Details</th>
|
154 |
+
</tr>
|
155 |
+
</thead>
|
156 |
+
<?php foreach ($fields as $label => $value): ?>
|
157 |
+
<tr>
|
158 |
+
<td><?php echo esc_html($label) ?>:</td>
|
159 |
+
<td><?php echo esc_html($value) ?></td>
|
160 |
+
</tr>
|
161 |
+
<?php endforeach ?>
|
162 |
+
</table>
|
163 |
+
|
164 |
+
<h4>HTTP Request: <?php echo $result ?></h4>
|
165 |
+
<?php if (!isset($hitData->fullRequest)): ?>
|
166 |
+
<em style="font-size: 14px;">This is a reconstruction of the request using what was flagged by the WAF.
|
167 |
+
Full requests are only stored when <code>WFWAF_DEBUG</code> is enabled.</em>
|
168 |
+
<?php endif ?>
|
169 |
+
<pre class="request-debug"><?php
|
170 |
+
$paramKey = wp_hash(uniqid('param', true));
|
171 |
+
$matchKey = wp_hash(uniqid('match', true));
|
172 |
+
|
173 |
+
$template = array(
|
174 |
+
"[$paramKey]" => '<em>',
|
175 |
+
"[/$paramKey]" => '</em>',
|
176 |
+
"[$matchKey]" => '<strong>',
|
177 |
+
"[/$matchKey]" => '</strong>',
|
178 |
+
);
|
179 |
+
$highlightParamFormat = "[$paramKey]%s[/$paramKey]";
|
180 |
+
$highlightMatchFormat = "[$matchKey]%s[/$matchKey]";
|
181 |
+
$requestOut = esc_html($request->highlightFailedParams($failedRules, $highlightParamFormat, $highlightMatchFormat));
|
182 |
+
|
183 |
+
echo str_replace(array_keys($template), $template, $requestOut) ?></pre>
|
184 |
+
|
185 |
+
<?php if ($failedRules): ?>
|
186 |
+
<h4>Failed Rules</h4>
|
187 |
+
<table class="wf-table">
|
188 |
+
<thead>
|
189 |
+
<tr>
|
190 |
+
<th>ID</th>
|
191 |
+
<th>Category</th>
|
192 |
+
</tr>
|
193 |
+
</thead>
|
194 |
+
<tbody>
|
195 |
+
<?php
|
196 |
+
foreach ($failedRules as $paramKey => $categories) {
|
197 |
+
foreach ($categories as $categoryKey => $failed) {
|
198 |
+
foreach ($failed as $failedRule) {
|
199 |
+
/** @var wfWAFRule $rule */
|
200 |
+
$rule = $failedRule['rule'];
|
201 |
+
printf("<tr><td>%d</td><td>%s</td></tr>", $rule->getRuleID(), $rule->getDescription());
|
202 |
+
}
|
203 |
+
}
|
204 |
+
}
|
205 |
+
?>
|
206 |
+
</tbody>
|
207 |
+
</table>
|
208 |
+
|
209 |
+
<?php endif ?>
|
210 |
+
|
211 |
+
<p>
|
212 |
+
<button type="button" id="run-waf-rules">Run Through WAF Rules</button>
|
213 |
+
</p>
|
214 |
+
|
215 |
+
<script>
|
216 |
+
document.getElementById('run-waf-rules').onclick = function() {
|
217 |
+
document.location.href = document.location.href;
|
218 |
+
}
|
219 |
+
</script>
|
220 |
+
|
221 |
+
|
222 |
+
</div>
|
223 |
+
|
224 |
+
</body>
|
225 |
+
</html>
|
waf/bootstrap.php
ADDED
@@ -0,0 +1,249 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
php_value auto_prepend_file ~/wp-content/plugins/wordfence/waf/bootstrap.php
|
5 |
+
*/
|
6 |
+
|
7 |
+
if (!defined('WFWAF_AUTO_PREPEND')) {
|
8 |
+
define('WFWAF_AUTO_PREPEND', true);
|
9 |
+
}
|
10 |
+
|
11 |
+
require_once dirname(__FILE__) . '/../vendor/wordfence/wf-waf/src/init.php';
|
12 |
+
|
13 |
+
class wfWAFWordPressRequest extends wfWAFRequest {
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @param wfWAFRequest|null $request
|
17 |
+
* @return wfWAFRequest
|
18 |
+
*/
|
19 |
+
public static function createFromGlobals($request = null) {
|
20 |
+
if (version_compare(phpversion(), '5.3.0') >= 0) {
|
21 |
+
$class = get_called_class();
|
22 |
+
$request = new $class();
|
23 |
+
} else {
|
24 |
+
$request = new self();
|
25 |
+
}
|
26 |
+
return parent::createFromGlobals($request);
|
27 |
+
}
|
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;
|
38 |
+
}
|
39 |
+
}
|
40 |
+
return $ip;
|
41 |
+
}
|
42 |
+
}
|
43 |
+
|
44 |
+
class wfWAFWordPressObserver extends wfWAFBaseObserver {
|
45 |
+
|
46 |
+
public function beforeRunRules() {
|
47 |
+
// Whitelisted URLs (in WAF config)
|
48 |
+
$whitelistedURLs = wfWAF::getInstance()->getStorageEngine()->getConfig('whitelistedURLs');
|
49 |
+
if ($whitelistedURLs) {
|
50 |
+
$whitelistPattern = "";
|
51 |
+
foreach ($whitelistedURLs as $whitelistedURL) {
|
52 |
+
$whitelistPattern .= preg_replace('/\\\\\*/', '.*?', preg_quote($whitelistedURL, '/')) . '|';
|
53 |
+
}
|
54 |
+
$whitelistPattern = '/^(?:' . substr($whitelistPattern, 0, -1) . ')$/i';
|
55 |
+
|
56 |
+
wfWAFRule::create(wfWAF::getInstance(), 0x8000000, 'rule', 'whitelist', 0, 'User Supplied Whitelisted URL', 'allow',
|
57 |
+
new wfWAFRuleComparisonGroup(
|
58 |
+
new wfWAFRuleComparison(wfWAF::getInstance(), 'match', $whitelistPattern, array(
|
59 |
+
'request.uri',
|
60 |
+
))
|
61 |
+
)
|
62 |
+
)->evaluate();
|
63 |
+
}
|
64 |
+
|
65 |
+
// Whitelisted IPs (Wordfence config)
|
66 |
+
$whitelistedIPs = wfWAF::getInstance()->getStorageEngine()->getConfig('whitelistedIPs');
|
67 |
+
if ($whitelistedIPs) {
|
68 |
+
require_once dirname(__FILE__) . '/wfWAFUserIPRange.php';
|
69 |
+
if (!is_array($whitelistedIPs)) {
|
70 |
+
$whitelistedIPs = explode(',', $whitelistedIPs);
|
71 |
+
}
|
72 |
+
foreach ($whitelistedIPs as $whitelistedIP) {
|
73 |
+
$ipRange = new wfWAFUserIPRange($whitelistedIP);
|
74 |
+
if ($ipRange->isIPInRange(wfWAF::getInstance()->getRequest()->getIP())) {
|
75 |
+
throw new wfWAFAllowException('Wordfence whitelisted IP.');
|
76 |
+
}
|
77 |
+
}
|
78 |
+
}
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
*
|
84 |
+
*/
|
85 |
+
class wfWAFWordPress extends wfWAF {
|
86 |
+
|
87 |
+
/** @var wfWAFRunException */
|
88 |
+
private $learningModeAttackException;
|
89 |
+
|
90 |
+
/**
|
91 |
+
* @param wfWAFBlockException $e
|
92 |
+
* @param int $httpCode
|
93 |
+
*/
|
94 |
+
public function blockAction($e, $httpCode = 403) {
|
95 |
+
if ($this->isInLearningMode()) {
|
96 |
+
register_shutdown_function(array(
|
97 |
+
$this, 'whitelistFailedRules',
|
98 |
+
));
|
99 |
+
$this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest());
|
100 |
+
$this->setLearningModeAttackException($e);
|
101 |
+
} else {
|
102 |
+
parent::blockAction($e, $httpCode);
|
103 |
+
}
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* @param wfWAFBlockXSSException $e
|
108 |
+
* @param int $httpCode
|
109 |
+
*/
|
110 |
+
public function blockXSSAction($e, $httpCode = 403) {
|
111 |
+
if ($this->isInLearningMode()) {
|
112 |
+
register_shutdown_function(array(
|
113 |
+
$this, 'whitelistFailedRules',
|
114 |
+
));
|
115 |
+
$this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest());
|
116 |
+
$this->setLearningModeAttackException($e);
|
117 |
+
} else {
|
118 |
+
parent::blockXSSAction($e, $httpCode);
|
119 |
+
}
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
*
|
124 |
+
*/
|
125 |
+
public function runCron() {
|
126 |
+
/**
|
127 |
+
* Removed sending attack data. Attack data is sent in @see wordfence::veryFirstAction
|
128 |
+
*/
|
129 |
+
$cron = $this->getStorageEngine()->getConfig('cron');
|
130 |
+
if (is_array($cron)) {
|
131 |
+
/** @var wfWAFCronEvent $event */
|
132 |
+
foreach ($cron as $index => $event) {
|
133 |
+
$event->setWaf($this);
|
134 |
+
if ($event->isInPast()) {
|
135 |
+
$event->fire();
|
136 |
+
$newEvent = $event->reschedule();
|
137 |
+
if ($newEvent instanceof wfWAFCronEvent && $newEvent !== $event) {
|
138 |
+
$cron[$index] = $newEvent;
|
139 |
+
} else {
|
140 |
+
unset($cron[$index]);
|
141 |
+
}
|
142 |
+
}
|
143 |
+
}
|
144 |
+
}
|
145 |
+
$this->getStorageEngine()->setConfig('cron', $cron);
|
146 |
+
}
|
147 |
+
|
148 |
+
|
149 |
+
/**
|
150 |
+
* @param $ip
|
151 |
+
* @return mixed
|
152 |
+
*/
|
153 |
+
public function isIPBlocked($ip) {
|
154 |
+
return false;
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* @return wfWAFRunException
|
159 |
+
*/
|
160 |
+
public function getLearningModeAttackException() {
|
161 |
+
return $this->learningModeAttackException;
|
162 |
+
}
|
163 |
+
|
164 |
+
/**
|
165 |
+
* @param wfWAFRunException $learningModeAttackException
|
166 |
+
*/
|
167 |
+
public function setLearningModeAttackException($learningModeAttackException) {
|
168 |
+
$this->learningModeAttackException = $learningModeAttackException;
|
169 |
+
}
|
170 |
+
}
|
171 |
+
|
172 |
+
if (!defined('WFWAF_LOG_PATH')) {
|
173 |
+
define('WFWAF_LOG_PATH', WP_CONTENT_DIR . '/wflogs/');
|
174 |
+
}
|
175 |
+
if (!is_dir(WFWAF_LOG_PATH)) {
|
176 |
+
@mkdir(WFWAF_LOG_PATH, 0755);
|
177 |
+
@file_put_contents(rtrim(WFWAF_LOG_PATH . '/') . '/.htaccess', <<<APACHE
|
178 |
+
<IfModule mod_authz_core.c>
|
179 |
+
Require all denied
|
180 |
+
</IfModule>
|
181 |
+
<IfModule !mod_authz_core.c>
|
182 |
+
Order deny,allow
|
183 |
+
Deny from all
|
184 |
+
</IfModule>
|
185 |
+
APACHE
|
186 |
+
);
|
187 |
+
}
|
188 |
+
|
189 |
+
|
190 |
+
wfWAF::setInstance(new wfWAFWordPress(
|
191 |
+
wfWAFWordPressRequest::createFromGlobals(),
|
192 |
+
new wfWAFStorageFile(WFWAF_LOG_PATH . 'attack-data.php', WFWAF_LOG_PATH . 'ips.php', WFWAF_LOG_PATH . 'config.php', WFWAF_LOG_PATH . 'wafRules.rules')
|
193 |
+
));
|
194 |
+
wfWAF::getInstance()->getEventBus()->attach(new wfWAFWordPressObserver);
|
195 |
+
|
196 |
+
try {
|
197 |
+
$rulesFiles = array(
|
198 |
+
WFWAF_LOG_PATH . 'rules.php',
|
199 |
+
// WFWAF_PATH . 'rules.php',
|
200 |
+
);
|
201 |
+
foreach ($rulesFiles as $rulesFile) {
|
202 |
+
if (!file_exists($rulesFile)) {
|
203 |
+
@touch($rulesFile);
|
204 |
+
}
|
205 |
+
if (is_writable($rulesFile)) {
|
206 |
+
wfWAF::getInstance()->setCompiledRulesFile($rulesFile);
|
207 |
+
break;
|
208 |
+
}
|
209 |
+
}
|
210 |
+
|
211 |
+
if (!file_exists(wfWAF::getInstance()->getCompiledRulesFile()) || !filesize(wfWAF::getInstance()->getCompiledRulesFile())) {
|
212 |
+
try {
|
213 |
+
wfWAF::getInstance()->updateRuleSet(file_get_contents(WFWAF_PATH . 'baseRules.rules'), false);
|
214 |
+
} catch (wfWAFBuildRulesException $e) {
|
215 |
+
// Log this somewhere
|
216 |
+
error_log($e->getMessage());
|
217 |
+
} catch (Exception $e) {
|
218 |
+
// Suppress this
|
219 |
+
error_log($e->getMessage());
|
220 |
+
}
|
221 |
+
}
|
222 |
+
|
223 |
+
if (WFWAF_DEBUG && file_exists(wfWAF::getInstance()->getStorageEngine()->getRulesDSLCacheFile())) {
|
224 |
+
try {
|
225 |
+
wfWAF::getInstance()->updateRuleSet(file_get_contents(wfWAF::getInstance()->getStorageEngine()->getRulesDSLCacheFile()), false);
|
226 |
+
} catch (wfWAFBuildRulesException $e) {
|
227 |
+
$GLOBALS['wfWAFDebugBuildException'] = $e;
|
228 |
+
} catch (Exception $e) {
|
229 |
+
$GLOBALS['wfWAFDebugBuildException'] = $e;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
try {
|
234 |
+
wfWAF::getInstance()->run();
|
235 |
+
} catch (wfWAFBuildRulesException $e) {
|
236 |
+
// Log this
|
237 |
+
error_log($e->getMessage());
|
238 |
+
} catch (Exception $e) {
|
239 |
+
// Suppress this
|
240 |
+
error_log($e->getMessage());
|
241 |
+
}
|
242 |
+
|
243 |
+
} catch (wfWAFStorageFileConfigException $e) {
|
244 |
+
// Let this request through for now
|
245 |
+
error_log($e->getMessage());
|
246 |
+
|
247 |
+
} catch (wfWAFStorageFileException $e) {
|
248 |
+
// We need to choose another storage engine here.
|
249 |
+
}
|
waf/wfWAFUserIPRange.php
ADDED
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
*
|
5 |
+
*/
|
6 |
+
class wfWAFUserIPRange {
|
7 |
+
|
8 |
+
/**
|
9 |
+
* @var string|null
|
10 |
+
*/
|
11 |
+
private $ip_string;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @param string|null $ip_string
|
15 |
+
*/
|
16 |
+
public function __construct($ip_string = null) {
|
17 |
+
$this->setIPString($ip_string);
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Check if the supplied IP address is within the user supplied range.
|
22 |
+
*
|
23 |
+
* @param string $ip
|
24 |
+
* @return bool
|
25 |
+
*/
|
26 |
+
public function isIPInRange($ip) {
|
27 |
+
$ip_string = $this->getIPString();
|
28 |
+
|
29 |
+
// IPv4 range
|
30 |
+
if (strpos($ip_string, '.') !== false && strpos($ip, '.') !== false) {
|
31 |
+
if (preg_match('/\[\d+\-\d+\]/', $ip_string)) {
|
32 |
+
$IPparts = explode('.', $ip);
|
33 |
+
$whiteParts = explode('.', $ip_string);
|
34 |
+
$mismatch = false;
|
35 |
+
for ($i = 0; $i <= 3; $i++) {
|
36 |
+
if (preg_match('/^\[(\d+)\-(\d+)\]$/', $whiteParts[$i], $m)) {
|
37 |
+
if ($IPparts[$i] < $m[1] || $IPparts[$i] > $m[2]) {
|
38 |
+
$mismatch = true;
|
39 |
+
}
|
40 |
+
} else if ($whiteParts[$i] != $IPparts[$i]) {
|
41 |
+
$mismatch = true;
|
42 |
+
}
|
43 |
+
}
|
44 |
+
if ($mismatch === false) {
|
45 |
+
return true; // Is whitelisted because we did not get a mismatch
|
46 |
+
}
|
47 |
+
} else if ($ip_string == $ip) {
|
48 |
+
return true;
|
49 |
+
}
|
50 |
+
|
51 |
+
// IPv6 range
|
52 |
+
} else if (strpos($ip_string, ':') !== false && strpos($ip, ':') !== false) {
|
53 |
+
if (preg_match('/\[[a-f0-9]+\-[a-f0-9]+\]/', $ip_string)) {
|
54 |
+
$IPparts = explode(':', strtolower(wfUtils::expandIPv6Address($ip)));
|
55 |
+
$whiteParts = explode(':', strtolower(self::expandIPv6Range($ip_string)));
|
56 |
+
$mismatch = false;
|
57 |
+
for ($i = 0; $i <= 7; $i++) {
|
58 |
+
if (preg_match('/^\[([a-f0-9]+)\-([a-f0-9]+)\]$/i', $whiteParts[$i], $m)) {
|
59 |
+
$ip_group = hexdec($IPparts[$i]);
|
60 |
+
$range_group_from = hexdec($m[1]);
|
61 |
+
$range_group_to = hexdec($m[2]);
|
62 |
+
if ($ip_group < $range_group_from || $ip_group > $range_group_to) {
|
63 |
+
$mismatch = true;
|
64 |
+
break;
|
65 |
+
}
|
66 |
+
} else if ($whiteParts[$i] != $IPparts[$i]) {
|
67 |
+
$mismatch = true;
|
68 |
+
break;
|
69 |
+
}
|
70 |
+
}
|
71 |
+
if ($mismatch === false) {
|
72 |
+
return true; // Is whitelisted because we did not get a mismatch
|
73 |
+
}
|
74 |
+
} else if ($ip_string == $ip) {
|
75 |
+
return true;
|
76 |
+
}
|
77 |
+
}
|
78 |
+
|
79 |
+
return false;
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Return a set of where clauses to use in MySQL.
|
84 |
+
*
|
85 |
+
* @param string $column
|
86 |
+
* @return false|null|string
|
87 |
+
*/
|
88 |
+
public function toSQL($column = 'ip') {
|
89 |
+
/** @var wpdb $wpdb */
|
90 |
+
global $wpdb;
|
91 |
+
$ip_string = $this->getIPString();
|
92 |
+
|
93 |
+
if (strpos($ip_string, '.') !== false && preg_match('/\[\d+\-\d+\]/', $ip_string)) {
|
94 |
+
$whiteParts = explode('.', $ip_string);
|
95 |
+
$sql = "(SUBSTR($column, 1, 12) = LPAD(CHAR(0xff, 0xff), 12, CHAR(0)) AND ";
|
96 |
+
|
97 |
+
for ($i = 0, $j = 24; $i <= 3; $i++, $j -= 8) {
|
98 |
+
// MySQL can only perform bitwise operations on integers
|
99 |
+
$conv = sprintf('CAST(CONV(HEX(SUBSTR(%s, 13, 8)), 16, 10) as UNSIGNED INTEGER)', $column);
|
100 |
+
if (preg_match('/^\[(\d+)\-(\d+)\]$/', $whiteParts[$i], $m)) {
|
101 |
+
$sql .= $wpdb->prepare("$conv >> $j & 0xFF BETWEEN %d AND %d", $m[1], $m[2]);
|
102 |
+
} else {
|
103 |
+
$sql .= $wpdb->prepare("$conv >> $j & 0xFF = %d", $whiteParts[$i]);
|
104 |
+
}
|
105 |
+
$sql .= ' AND ';
|
106 |
+
}
|
107 |
+
$sql = substr($sql, 0, -5) . ')';
|
108 |
+
return $sql;
|
109 |
+
|
110 |
+
} else if (strpos($ip_string, ':') !== false && preg_match('/\[[a-f0-9]+\-[a-f0-9]+\]/', $ip_string)) {
|
111 |
+
$whiteParts = explode(':', strtolower(self::expandIPv6Range($ip_string)));
|
112 |
+
$sql = '(';
|
113 |
+
|
114 |
+
for ($i = 0; $i <= 7; $i++) {
|
115 |
+
// MySQL can only perform bitwise operations on integers
|
116 |
+
$conv = sprintf('CAST(CONV(HEX(SUBSTR(%s, %d, 8)), 16, 10) as UNSIGNED INTEGER)', $column, $i < 4 ? 1 : 9);
|
117 |
+
$j = 16 * (3 - ($i % 4));
|
118 |
+
if (preg_match('/^\[([a-f0-9]+)\-([a-f0-9]+)\]$/', $whiteParts[$i], $m)) {
|
119 |
+
$sql .= $wpdb->prepare("$conv >> $j & 0xFFFF BETWEEN 0x%x AND 0x%x", hexdec($m[1]), hexdec($m[2]));
|
120 |
+
} else {
|
121 |
+
$sql .= $wpdb->prepare("$conv >> $j & 0xFFFF = 0x%x", hexdec($whiteParts[$i]));
|
122 |
+
}
|
123 |
+
$sql .= ' AND ';
|
124 |
+
}
|
125 |
+
$sql = substr($sql, 0, -5) . ')';
|
126 |
+
return $sql;
|
127 |
+
}
|
128 |
+
return $wpdb->prepare("($column = %s)", wfUtils::inet_pton($ip_string));
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Expand a compressed printable range representation of an IPv6 address.
|
133 |
+
*
|
134 |
+
* @todo Hook up exceptions for better error handling.
|
135 |
+
* @todo Allow IPv4 mapped IPv6 addresses (::ffff:192.168.1.1).
|
136 |
+
* @param string $ip_range
|
137 |
+
* @return string
|
138 |
+
*/
|
139 |
+
public static function expandIPv6Range($ip_range) {
|
140 |
+
$colon_count = substr_count($ip_range, ':');
|
141 |
+
$dbl_colon_count = substr_count($ip_range, '::');
|
142 |
+
if ($dbl_colon_count > 1) {
|
143 |
+
return false;
|
144 |
+
}
|
145 |
+
$dbl_colon_pos = strpos($ip_range, '::');
|
146 |
+
if ($dbl_colon_pos !== false) {
|
147 |
+
$ip_range = str_replace('::', str_repeat(':0000',
|
148 |
+
(($dbl_colon_pos === 0 || $dbl_colon_pos === strlen($ip_range) - 2) ? 9 : 8) - $colon_count) . ':', $ip_range);
|
149 |
+
$ip_range = trim($ip_range, ':');
|
150 |
+
}
|
151 |
+
$colon_count = substr_count($ip_range, ':');
|
152 |
+
if ($colon_count != 7) {
|
153 |
+
return false;
|
154 |
+
}
|
155 |
+
|
156 |
+
$groups = explode(':', $ip_range);
|
157 |
+
$expanded = '';
|
158 |
+
foreach ($groups as $group) {
|
159 |
+
if (preg_match('/\[([a-f0-9]{1,4})\-([a-f0-9]{1,4})\]/i', $group, $matches)) {
|
160 |
+
$expanded .= sprintf('[%s-%s]', str_pad(strtolower($matches[1]), 4, '0', STR_PAD_LEFT), str_pad(strtolower($matches[2]), 4, '0', STR_PAD_LEFT)) . ':';
|
161 |
+
} else if (preg_match('/[a-f0-9]{1,4}/i', $group)) {
|
162 |
+
$expanded .= str_pad(strtolower($group), 4, '0', STR_PAD_LEFT) . ':';
|
163 |
+
} else {
|
164 |
+
return false;
|
165 |
+
}
|
166 |
+
}
|
167 |
+
return trim($expanded, ':');
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* @return bool
|
172 |
+
*/
|
173 |
+
public function isValidRange() {
|
174 |
+
return $this->isValidIPv4Range() || $this->isValidIPv6Range();
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* @return bool
|
179 |
+
*/
|
180 |
+
public function isValidIPv4Range() {
|
181 |
+
$ip_string = $this->getIPString();
|
182 |
+
if (preg_match_all('/(\d+)/', $ip_string, $matches) > 0) {
|
183 |
+
foreach ($matches[1] as $match) {
|
184 |
+
$group = (int) $match;
|
185 |
+
if ($group > 255 || $group < 0) {
|
186 |
+
return false;
|
187 |
+
}
|
188 |
+
}
|
189 |
+
}
|
190 |
+
|
191 |
+
$group_regex = '([0-9]{1,3}|\[[0-9]{1,3}\-[0-9]{1,3}\])';
|
192 |
+
return preg_match('/^' . str_repeat("$group_regex.", 3) . $group_regex . '$/i', $ip_string) > 0;
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* @return bool
|
197 |
+
*/
|
198 |
+
public function isValidIPv6Range() {
|
199 |
+
$ip_string = $this->getIPString();
|
200 |
+
if (strpos($ip_string, '::') !== false) {
|
201 |
+
$ip_string = self::expandIPv6Range($ip_string);
|
202 |
+
}
|
203 |
+
if (!$ip_string) {
|
204 |
+
return false;
|
205 |
+
}
|
206 |
+
$group_regex = '([a-f0-9]{1,4}|\[[a-f0-9]{1,4}\-[a-f0-9]{1,4}\])';
|
207 |
+
return preg_match('/^' . str_repeat("$group_regex:", 7) . $group_regex . '$/i', $ip_string) > 0;
|
208 |
+
}
|
209 |
+
|
210 |
+
|
211 |
+
/**
|
212 |
+
* @return string|null
|
213 |
+
*/
|
214 |
+
public function getIPString() {
|
215 |
+
return $this->ip_string;
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* @param string|null $ip_string
|
220 |
+
*/
|
221 |
+
public function setIPString($ip_string) {
|
222 |
+
$this->ip_string = $ip_string;
|
223 |
+
}
|
224 |
+
}
|
wordfence.php
CHANGED
@@ -4,13 +4,33 @@ 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.
|
8 |
Author URI: http://www.wordfence.com/
|
|
|
9 |
*/
|
10 |
if(defined('WP_INSTALLING') && WP_INSTALLING){
|
11 |
return;
|
12 |
}
|
13 |
-
define('WORDFENCE_VERSION', '6.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
if(get_option('wordfenceActivated') != 1){
|
15 |
add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }
|
16 |
}
|
@@ -20,6 +40,21 @@ if(! defined('WORDFENCE_VERSIONONLY_MODE')){ //Used to get version from file.
|
|
20 |
@ini_set('memory_limit', '128M'); //Some hosts have ini set at as little as 32 megs. 64 is the min sane amount of memory.
|
21 |
}
|
22 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
require_once('lib/wordfenceConstants.php');
|
24 |
require_once('lib/wordfenceClass.php');
|
25 |
wordfence::install_actions();
|
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.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.1');
|
15 |
+
define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
|
16 |
+
basename(dirname(__FILE__)) . '/' . basename(__FILE__));
|
17 |
+
|
18 |
+
global $wp_plugin_paths;
|
19 |
+
foreach ($wp_plugin_paths as $dir => $realdir) {
|
20 |
+
if (strpos(__FILE__, $realdir) === 0) {
|
21 |
+
define('WORDFENCE_FCPATH', $dir . '/' . basename(__FILE__));
|
22 |
+
define('WORDFENCE_PATH', trailingslashit($dir));
|
23 |
+
break;
|
24 |
+
}
|
25 |
+
}
|
26 |
+
if (!defined('WORDFENCE_FCPATH')) {
|
27 |
+
/** @noinspection PhpConstantReassignmentInspection */
|
28 |
+
define('WORDFENCE_FCPATH', __FILE__);
|
29 |
+
/** @noinspection PhpConstantReassignmentInspection */
|
30 |
+
define('WORDFENCE_PATH', trailingslashit(dirname(WORDFENCE_FCPATH)));
|
31 |
+
}
|
32 |
+
|
33 |
+
|
34 |
if(get_option('wordfenceActivated') != 1){
|
35 |
add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }
|
36 |
}
|
40 |
@ini_set('memory_limit', '128M'); //Some hosts have ini set at as little as 32 megs. 64 is the min sane amount of memory.
|
41 |
}
|
42 |
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Constant to determine if Wordfence is installed on another WordPress site one or more directories up in
|
46 |
+
* auto_prepend_file mode.
|
47 |
+
*/
|
48 |
+
define('WFWAF_SUBDIRECTORY_INSTALL', class_exists('wfWAF') &&
|
49 |
+
!in_array(realpath(dirname(__FILE__) . '/vendor/wordfence/wf-waf/src/init.php'), get_included_files()));
|
50 |
+
if (!WFWAF_SUBDIRECTORY_INSTALL) {
|
51 |
+
require_once 'vendor/wordfence/wf-waf/src/init.php';
|
52 |
+
if (!wfWAF::getInstance()) {
|
53 |
+
define('WFWAF_AUTO_PREPEND', false);
|
54 |
+
require_once 'waf/bootstrap.php';
|
55 |
+
}
|
56 |
+
}
|
57 |
+
|
58 |
require_once('lib/wordfenceConstants.php');
|
59 |
require_once('lib/wordfenceClass.php');
|
60 |
wordfence::install_actions();
|