Wordfence Security – Firewall & Malware Scan - Version 2.0.1

Version Description

  • Improved scanning for specific attacks being used in the PHP-CGI vulnerability ( CVE-2012-1823)
  • API keys no longer required. WF fetches a temporary anonymous API key for you on activation.
  • Added real-time activity log on scan page.
  • Added real-time summary updates on scan page.
  • Fixed ability to view files that have symlinks in path.
  • Added message to configure alert email address for multi-site and single site installs on activation.
  • Disabled firewall rules by default because most sites don't need them.
  • Disabled blocking of fake googlebots except for high security levels to prevent users who like to pretend they're googlebot from blocking themselves.
  • Geshi the syntax highlighter now asks for more memory before running.
  • Fixed bug that caused scan to hang on very large files.
  • Added an index to wfStatus to make it faster for summary statuses
  • Removed multisite pre-activation check to make activation more reliable on multisite installs.
  • Better problem reporting if you trashed your Wordfence schema but the plugin is still installed.
Download this release

Release Info

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

Code changes from version 1.5.6 to 2.0.1

css/main.css CHANGED
@@ -1,5 +1,5 @@
1
.wordfenceWrap {
2
- margin: 0 0 0 20px;
3
}
4
div.wordfenceLive {
5
height: 29px;
@@ -89,7 +89,7 @@ div.wordfenceScanButton input.button-wf-grey {
89
.wfTabsContainer {
90
overflow: hidden;
91
border: 1px solid #CCC;
92
- max-width: 970px;
93
padding: 15px;
94
min-height: 200px;
95
-webkit-font-smoothing: antialiased;
@@ -187,24 +187,18 @@ table th.wfConfigEnable { font-weight: bold; }
187
table th.wfSubheading { font-weight: bold; padding-top: 10px; }
188
.wfALogTime { color: #999; }
189
.wfALogEntry { }
190
- .wfALogMailLink, .wfALogReloadLink {
191
display: block;
192
position: absolute;
193
- width: 16px;
194
- height: 16px;
195
- padding: 0;
196
margin: 0;
197
- }
198
- .wfALogMailLink {
199
- right: 20px;
200
- top: 2px;
201
background-image: url(../images/icons/email_go.png);
202
}
203
- .wfALogReloadLink {
204
- right: 42px;
205
- top: 2px;
206
- background-image: url(../images/icons/arrow_refresh.png);
207
- }
208
#wfActivity { position: relative; }
209
h3.wfConfigHeading {
210
font-size: 22px;
@@ -218,3 +212,53 @@ h3.wfConfigHeading {
218
font-family: Georgia;
219
font-style: italic;
220
}
1
.wordfenceWrap {
2
+ margin: 20px 0 0 20px;
3
}
4
div.wordfenceLive {
5
height: 29px;
89
.wfTabsContainer {
90
overflow: hidden;
91
border: 1px solid #CCC;
92
+ max-width: 870px;
93
padding: 15px;
94
min-height: 200px;
95
-webkit-font-smoothing: antialiased;
187
table th.wfSubheading { font-weight: bold; padding-top: 10px; }
188
.wfALogTime { color: #999; }
189
.wfALogEntry { }
190
+ .wfALogMailLink {
191
display: block;
192
position: absolute;
193
+ padding: 0 0 0 18px;
194
margin: 0;
195
+ right: 10px;
196
+ top: 0px;
197
background-image: url(../images/icons/email_go.png);
198
+ background-repeat: no-repeat;
199
+ font-weight: normal;
200
}
201
+
202
#wfActivity { position: relative; }
203
h3.wfConfigHeading {
204
font-size: 22px;
212
font-family: Georgia;
213
font-style: italic;
214
}
215
+
216
+ .consoleHead {
217
+ position: relative;
218
+ padding: 0 0 0 3px;
219
+ font-weight: bold;
220
+ width: 800px;
221
+ }
222
+ .consoleHeadText {
223
+ font-size: 18px;
224
+ font-family: Georgia, serif;
225
+ font-style: italic;
226
+ color: #555;
227
+ font-weight: bold;
228
+ -webkit-font-smoothing: antialiased;
229
+
230
+ }
231
+ .consoleOuter { width: 800px; margin-bottom: 20px; }
232
+ .consoleInner { height: 116px; overflow: auto; z-index: 1; }
233
+ .bevelDiv1 { border: 1px solid #EFEFEF; }
234
+ .bevelDiv2 { border: 1px solid #AAA; }
235
+ .bevelDiv3 { border: 1px solid #555;
236
+ background-color: #FFFFE0; /* #FFFFF0; /* #FFEBCD; #FFFACD; */
237
+ color: #000; padding: 5px; font-family: Arial; -webkit-font-smoothing: none; }
238
+
239
+ .wfBlackCursor{ color: #FFF; }
240
+ .wfSecure { color: #0A0; font-weight: bold; }
241
+ .wfActivityLine {
242
+ }
243
+ .wfSummaryDate { float: left; margin-left: 3px; }
244
+ .wfSummaryMsg { float: left; margin-left: 3px; }
245
+ .wfSummaryResult { float: right; text-align: left; width: 280px; }
246
+ .wfSummaryLoading { width: 16px; height: 11px; background-image: url('../images/icons/ajaxScan.gif'); }
247
+ .wfSummaryBad, .wfSummaryErr { color: #A00; }
248
+ .wfSummaryOK { color: #0A0; }
249
+ .wfClear {
250
+ content: ".";
251
+ display: block;
252
+ height: 0;
253
+ width: 0;
254
+ line-height: 0;
255
+ clear: both;
256
+ visibility: hidden;
257
+ }
258
+ .wfSummaryFinal {
259
+ -webkit-font-smoothing: antialiased;
260
+ font-weight: bold;
261
+ color: #555;
262
+ }
263
+ input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
264
+
images/icons/ajaxScan.gif ADDED
Binary file
js/admin.js CHANGED
@@ -1,6 +1,7 @@
1
if(! window['wordfenceAdmin']){
2
window['wordfenceAdmin'] = {
3
loading16: '<div class="wfLoading16"></div>',
4
dbCheckTables: [],
5
dbCheckCount_ok: 0,
6
dbCheckCount_skipped: 0,
@@ -11,7 +12,6 @@ window['wordfenceAdmin'] = {
11
scanIDLoaded: 0,
12
colorboxQueue: [],
13
colorboxOpen: false,
14
- scanPending: false,
15
mode: '',
16
visibleIssuesPanel: 'new',
17
preFirstScanMsgsLoaded: false,
@@ -19,33 +19,179 @@ window['wordfenceAdmin'] = {
19
elementGeneratorIter: 1,
20
reloadConfigPage: false,
21
nonce: false,
22
init: function(){
23
this.nonce = WordfenceAdminVars.firstNonce;
24
if(jQuery('#wordfenceMode_scan').length > 0){
25
this.mode = 'scan';
26
this.noScanHTML = jQuery('#wfNoScanYetTmpl').tmpl().html();
27
} else if(jQuery('#wordfenceMode_activity').length > 0){
28
this.mode = 'activity';
29
this.activityMode = 'hit';
30
- this.updateTicker(true);
31
} else if(jQuery('#wordfenceMode_options').length > 0){
32
this.mode = 'options';
33
jQuery('.wfConfigElem').change(function(){ jQuery('#securityLevel').val('CUSTOM'); });
34
this.updateTicker(true);
35
} else if(jQuery('#wordfenceMode_blockedIPs').length > 0){
36
this.mode = 'blocked';
37
this.staticTabChanged();
38
this.updateTicker(true);
39
} else {
40
this.mode = false;
41
}
42
if(this.mode){ //We are in a Wordfence page
43
var self = this;
44
- this.liveInt = setInterval(function(){ self.updateTicker(); }, 2000);
45
jQuery(document).bind('cbox_closed', function(){ self.colorboxIsOpen = false; self.colorboxServiceQueue(); });
46
}
47
48
},
49
updateTicker: function(forceUpdate){
50
if( (! forceUpdate) && this.tickerUpdatePending){
51
return;
@@ -80,27 +226,7 @@ window['wordfenceAdmin'] = {
80
jQuery('#wfLiveStatus').hide().html(newMsg).fadeIn(200);
81
}
82
83
- if(this.mode == 'scan'){
84
- if(res.running){
85
- jQuery('.wfStartScanButton').addClass('button-wf-grey').val("A scan is in progress...").unbind('click').click(function(){ wordfenceAdmin.scanRunningMsg(); }).show();
86
- } else {
87
- if(! this.scanPending){
88
- jQuery('.wfStartScanButton').removeClass('button-wf-grey').val("Start a Wordfence Scan").unbind('click').click(function(){ wordfenceAdmin.startScan(); }).show();
89
- }
90
- }
91
- if(res.currentScanID && res.currentScanID != this.scanIDLoaded){
92
- this.scanIDLoaded = res.currentScanID;
93
- this.loadIssues();
94
- } else if( (! res.currentScanID) && (! this.scanIDLoaded)){
95
- //We haven't done our first scan yet.
96
- if(! this.preFirstScanMsgsLoaded){
97
- this.preFirstScanMsgsLoaded = true;
98
- jQuery('#wfSummaryTables').html(this.noScanHTML);
99
- this.switchIssuesTab(jQuery('#wfNewIssuesTab'), 'new');
100
- jQuery('#wfActivity').html('<p>No events to report yet. Please complete your first scan.</p>');
101
- }
102
- }
103
- } else if(this.mode == 'activity'){
104
if(res.alsoGet != 'logList_' + this.activityMode){ return; } //user switched panels since ajax request started
105
if(/^(?:topScanners|topLeechers)#x2F;.test(this.activityMode)){
106
if(statusMsgChanged){ this.updateTicker(true); } return;
@@ -202,12 +328,20 @@ window['wordfenceAdmin'] = {
202
});
203
},
204
startScan: function(){
205
- var self = this;
206
- jQuery('.wfStartScanButton').addClass('button-wf-grey').val("A scan is in progress...").unbind('click').click(function(){ wordfenceAdmin.scanRunningMsg(); }).show();
207
- //scanPending prevents the button from switching to grey when clicked and then quickly to blue and grey again as the ticker us updated.
208
- this.scanPending = true;
209
- var self = this;
210
- setTimeout(function(){ self.scanPending = false; }, 10000);
211
this.ajax('wordfence_scan', {}, function(res){ } );
212
},
213
loadIssues: function(callback){
@@ -231,7 +365,6 @@ window['wordfenceAdmin'] = {
231
displayIssues: function(res, callback){
232
var self = this;
233
res.summary['lastScanCompleted'] = res['lastScanCompleted'];
234
- jQuery('#wfSummaryTables').html( jQuery('#wfScanSummaryTmpl').tmpl(res.summary).html() );
235
jQuery('.wfIssuesContainer').hide();
236
for(issueStatus in res.issuesLists){
237
var containerID = 'wfIssues_dataTable_' + issueStatus;
1
if(! window['wordfenceAdmin']){
2
window['wordfenceAdmin'] = {
3
loading16: '<div class="wfLoading16"></div>',
4
+ actUpdateInterval: 2000,
5
dbCheckTables: [],
6
dbCheckCount_ok: 0,
7
dbCheckCount_skipped: 0,
12
scanIDLoaded: 0,
13
colorboxQueue: [],
14
colorboxOpen: false,
15
mode: '',
16
visibleIssuesPanel: 'new',
17
preFirstScanMsgsLoaded: false,
19
elementGeneratorIter: 1,
20
reloadConfigPage: false,
21
nonce: false,
22
+ tickerUpdatePending: false,
23
+ activityLogUpdatePending: false,
24
+ lastALogCtime: 0,
25
+ activityQueue: [],
26
+ totalActAdded: 0,
27
+ maxActivityLogItems: 1000,
28
+ scanReqAnimation: false,
29
init: function(){
30
this.nonce = WordfenceAdminVars.firstNonce;
31
+ var startTicker = false;
32
if(jQuery('#wordfenceMode_scan').length > 0){
33
this.mode = 'scan';
34
+ jQuery('#consoleActivity').scrollTop(jQuery('#consoleActivity').prop('scrollHeight'));
35
+ jQuery('#consoleScan').scrollTop(jQuery('#consoleScan').prop('scrollHeight'));
36
this.noScanHTML = jQuery('#wfNoScanYetTmpl').tmpl().html();
37
+ this.loadIssues();
38
+ this.startActivityLogUpdates();
39
} else if(jQuery('#wordfenceMode_activity').length > 0){
40
this.mode = 'activity';
41
this.activityMode = 'hit';
42
+ startTicker = true;
43
} else if(jQuery('#wordfenceMode_options').length > 0){
44
this.mode = 'options';
45
jQuery('.wfConfigElem').change(function(){ jQuery('#securityLevel').val('CUSTOM'); });
46
this.updateTicker(true);
47
+ startTicker = true;
48
} else if(jQuery('#wordfenceMode_blockedIPs').length > 0){
49
this.mode = 'blocked';
50
this.staticTabChanged();
51
this.updateTicker(true);
52
+ startTicker = true;
53
} else {
54
this.mode = false;
55
}
56
if(this.mode){ //We are in a Wordfence page
57
var self = this;
58
+ if(startTicker){
59
+ this.liveInt = setInterval(function(){ self.updateTicker(); }, 2000);
60
+ }
61
jQuery(document).bind('cbox_closed', function(){ self.colorboxIsOpen = false; self.colorboxServiceQueue(); });
62
}
63
64
},
65
+ startActivityLogUpdates: function(){
66
+ var self = this;
67
+ setInterval(function(){
68
+ self.updateActivityLog();
69
+ }, this.actUpdateInterval);
70
+ },
71
+ updateActivityLog: function(){
72
+ if(this.activityLogUpdatePending){
73
+ return;
74
+ }
75
+ this.activityLogUpdatePending = true;
76
+ var self = this;
77
+ this.ajax('wordfence_activityLogUpdate', {
78
+ lastctime: this.lastALogCtime
79
+ }, function(res){ self.doneUpdateActivityLog(res); }, function(){ self.activityLogUpdatePending = false; });
80
+
81
+ },
82
+ doneUpdateActivityLog: function(res){
83
+ this.actNextUpdateAt = (new Date()).getTime() + this.actUpdateInterval;
84
+ if(res.ok){
85
+ if(res.items.length > 0){
86
+ this.activityQueue.push.apply(this.activityQueue, res.items);
87
+ this.lastALogCtime = res.items[res.items.length - 1].ctime;
88
+ this.processActQueue(res.currentScanID);
89
+ }
90
+ }
91
+ this.activityLogUpdatePending = false;
92
+ },
93
+ processActQueue: function(currentScanID){
94
+ if(this.activityQueue.length > 0){
95
+
96
+ this.addActItem(this.activityQueue.shift());
97
+ this.totalActAdded++;
98
+ if(this.totalActAdded > this.maxActivityLogItems){
99
+ jQuery('#consoleActivity div:first').remove();
100
+ this.totalActAdded--;
101
+ }
102
+
103
+ var timeTillNextUpdate = this.actNextUpdateAt - (new Date()).getTime();
104
+ var maxRate = 50 / 1000; //Rate per millisecond
105
+ var bulkTotal = 0;
106
+ while(this.activityQueue.length > 0 && this.activityQueue.length / timeTillNextUpdate > maxRate ){
107
+ var item = this.activityQueue.shift();
108
+ if(item){
109
+ bulkTotal++;
110
+ this.addActItem(item);
111
+ }
112
+ }
113
+ this.totalActAdded += bulkTotal;
114
+ if(this.totalActAdded > this.maxActivityLogItems){
115
+ jQuery('#consoleActivity div:lt(' + bulkTotal + ')').remove();
116
+ this.totalActAdded -= bulkTotal;
117
+ }
118
+ var minDelay = 100;
119
+ var delay = minDelay;
120
+ if(timeTillNextUpdate < 1){
121
+ delay = minDelay;
122
+ } else {
123
+ delay = Math.round(timeTillNextUpdate / this.activityQueue.length);
124
+ if(delay < minDelay){ delay = minDelay; }
125
+ }
126
+ var self = this;
127
+ setTimeout(function(){ self.processActQueue(); }, delay);
128
+ }
129
+ jQuery('#consoleActivity').scrollTop(jQuery('#consoleActivity').prop('scrollHeight'));
130
+ },
131
+ processActArray: function(arr){
132
+ for(var i = 0; i < arr.length; i++){
133
+ this.addActItem(arr[i]);
134
+ }
135
+ },
136
+ addActItem: function(item){
137
+ if(item.msg.indexOf('SUM_') == 0){
138
+ this.processSummaryLine(item);
139
+ jQuery('#consoleSummary').scrollTop(jQuery('#consoleSummary').prop('scrollHeight'));
140
+ jQuery('#wfStartingScan').addClass('wfSummaryOK').html('Done.');
141
+ } else {
142
+ jQuery('#consoleActivity').append('<div class="wfActivityLine wf' + item.type + '">[' + item.date + ']&nbsp;' + item.msg + '</div>');
143
+ if(/Scan complete\./i.test(item.msg)){
144
+ this.loadIssues();
145
+ }
146
+ }
147
+ },
148
+ processSummaryLine: function(item){
149
+ if(item.msg.indexOf('SUM_START:') != -1){
150
+ var msg = item.msg.replace('SUM_START:', '');
151
+ jQuery('#consoleSummary').append('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult"><div class="wfSummaryLoading"></div></div><div class="wfClear"></div>');
152
+ summaryUpdated = true;
153
+ } else if(item.msg.indexOf('SUM_ENDBAD') != -1){
154
+ var msg = item.msg.replace('SUM_ENDBAD:', '');
155
+ jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryBad').html('Problems found.');
156
+ summaryUpdated = true;
157
+ } else if(item.msg.indexOf('SUM_ENDOK') != -1){
158
+ var msg = item.msg.replace('SUM_ENDOK:', '');
159
+ jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryOK').html('Secure.');
160
+ summaryUpdated = true;
161
+ } else if(item.msg.indexOf('SUM_ENDERR') != -1){
162
+ var msg = item.msg.replace('SUM_ENDERR:', '');
163
+ jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryErr').html('An error occured.');
164
+ summaryUpdated = true;
165
+ } else if(item.msg.indexOf('SUM_DISABLED:') != -1){
166
+ var msg = item.msg.replace('SUM_DISABLED:', '');
167
+ jQuery('#consoleSummary').append('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult">Disabled</div><div class="wfClear"></div>');
168
+ summaryUpdated = true;
169
+ } else if(item.msg.indexOf('SUM_PAIDONLY:') != -1){
170
+ var msg = item.msg.replace('SUM_PAIDONLY:', '');
171
+ jQuery('#consoleSummary').append('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult"><a href="http://www.wordfence.com/" target="_blank">Paid Members Only</a></div><div class="wfClear"></div>');
172
+ summaryUpdated = true;
173
+ } else if(item.msg.indexOf('SUM_FINAL:') != -1){
174
+ var msg = item.msg.replace('SUM_FINAL:', '');
175
+ jQuery('#consoleSummary').append('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg wfSummaryFinal">' + msg + '</div><div class="wfSummaryResult wfSummaryOK">Scan Complete.</div><div class="wfClear"></div>');
176
+ } else if(item.msg.indexOf('SUM_PREP:') != -1){
177
+ var msg = item.msg.replace('SUM_PREP:', '');
178
+ jQuery('#consoleSummary').empty().html('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult" id="wfStartingScan"><div class="wfSummaryLoading"></div></div><div class="wfClear"></div>');
179
+ }
180
+ },
181
+ processActQueueItem: function(){
182
+ var item = this.activityQueue.shift();
183
+ if(item){
184
+ jQuery('#consoleActivity').append('<div class="wfActivityLine wf' + item.type + '">[' + item.date + ']&nbsp;' + item.msg + '</div>');
185
+ this.totalActAdded++;
186
+ if(this.totalActAdded > this.maxActivityLogItems){
187
+ jQuery('#consoleActivity div:first').remove();
188
+ this.totalActAdded--;
189
+ }
190
+ if(item.msg == 'Scan complete.'){
191
+ this.loadIssues();
192
+ }
193
+ }
194
+ },
195
updateTicker: function(forceUpdate){
196
if( (! forceUpdate) && this.tickerUpdatePending){
197
return;
226
jQuery('#wfLiveStatus').hide().html(newMsg).fadeIn(200);
227
}
228
229
+ if(this.mode == 'activity'){
230
if(res.alsoGet != 'logList_' + this.activityMode){ return; } //user switched panels since ajax request started
231
if(/^(?:topScanners|topLeechers)#x2F;.test(this.activityMode)){
232
if(statusMsgChanged){ this.updateTicker(true); } return;
328
});
329
},
330
startScan: function(){
331
+ var scanReqAnimation = setInterval(function(){
332
+ var str = jQuery('#wfStartScanButton1').prop('value');
333
+ ch = str.charAt(str.length - 1);
334
+ if(ch == '/'){ ch = '-'; }
335
+ else if(ch == '-'){ ch = '\\'; }
336
+ else if(ch == '\\'){ ch = '|'; }
337
+ else if(ch == '|'){ ch = '/'; }
338
+ else {ch = '/'; }
339
+ jQuery('#wfStartScanButton1,#wfStartScanButton2').prop('value', "Requesting a New Scan " + ch);
340
+ }, 100);
341
+ setTimeout(function(res){
342
+ clearInterval(scanReqAnimation);
343
+ jQuery('#wfStartScanButton1,#wfStartScanButton2').prop('value', "Start a Wordfence Scan");
344
+ }, 2000);
345
this.ajax('wordfence_scan', {}, function(res){ } );
346
},
347
loadIssues: function(callback){
365
displayIssues: function(res, callback){
366
var self = this;
367
res.summary['lastScanCompleted'] = res['lastScanCompleted'];
368
jQuery('.wfIssuesContainer').hide();
369
for(issueStatus in res.issuesLists){
370
var containerID = 'wfIssues_dataTable_' + issueStatus;
lib/menu_scan.php CHANGED
@@ -1,27 +1,49 @@
1
<div class="wordfenceModeElem" id="wordfenceMode_scan"></div>
2
<div class="wrap wordfence">
3
<div class="wordfence-lock-icon wordfence-icon32"><br /></div><h2>Wordfence Scan</h2>
4
- <div class="wordfenceLive">
5
- <table border="0" cellpadding="0" cellspacing="0">
6
- <tr><td><h2>Wordfence Activity Log:</h2></td><td id="wfLiveStatus"></td></tr>
7
- </table>
8
- </div>
9
<div class="wordfenceWrap">
10
<div>
11
- <div id="wfTabs">
12
- <a href="#" class="wfTab1 wfTabSwitch selected" onclick="wordfenceAdmin.switchToSummaryTab(this); return false;">Summary</a>
13
- <a href="#" class="wfTab1 wfTabSwitch" onclick="wordfenceAdmin.switchToLiveTab(this); return false;">Activity Log</a>
14
</div>
15
- <div class="wfTabsContainer">
16
- <div id="wfSummaryTables" class="wfDataPanel">
17
- <div class="wfLoadingWhite32"></div>
18
- &nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />
19
- </div>
20
- <div id="wfActivity" class="wfDataPanel" style="display: none; overflow: scroll; height: 400px; border: 1px solid #CCC; padding: 2px;">
21
- <div class="wfLoadingWhite32"></div>
22
- &nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />
23
</div>
24
</div>
25
</div>
26
<div style="margin-top: 20px;">
27
<div id="wfTabs">
@@ -36,7 +58,8 @@
36
If you have fixed all the issues below, you can <a href="#" onclick="WFAD.updateAllIssues('deleteNew'); return false;">click here to mark all new issues as fixed</a>.
37
You can also <a href="#" onclick="WFAD.updateAllIssues('ignoreAllNew'); return false;">ignore all new issues</a> which will exclude all issues listed below from future scans.
38
</p>
39
- <div id="wfIssues_dataTable_new"></div>
40
</div>
41
<div id="wfIssues_ignored" class="wfIssuesContainer">
42
<h2>Ignored Issues</h2>
@@ -395,7 +418,7 @@
395
where you can post your comments or questions. We would love to hear from you.
396
</td></tr>
397
<tr><td>
398
- <div class="wordfenceScanButton"><input type="button" value="Start a Wordfence Scan" class="wfStartScanButton button-primary" /></div>
399
</td></tr>
400
</table>
401
</td>
@@ -403,74 +426,4 @@
403
</div>
404
</script>
405
406
- <script type="text/x-jquery-template" id="wfScanSummaryTmpl">
407
- <div>
408
- <table class="wfSummaryParent" cellpadding="0" cellspacing="0">
409
- <tr><th class="wfHead">Activity Summary:</th><th class="wfHead" colspan="3">Wordfence is Protecting:</th></tr>
410
- <tr><td>
411
- <table class="wfSC1" cellpadding="0" cellspacing="0">
412
- <tr><td>
413
- The most recent scan completed ${scanTimeAgo} ago.
414
- </td></tr>
415
- <tr><td>
416
- {{if scanRunning == '1'}}
417
- There is currently a scan running
418
- {{else}}
419
- A scan is not running at this time
420
- {{/if}}
421
- {{if scheduledScansEnabled}}
422
- and the next scan is scheduled to run approximately ${nextRun}.
423
- {{else}}
424
- and scheduled scans are disabled.
425
- {{/if}}
426
- </td></tr>
427
- <tr><td>
428
- {{if totalCritical > 0 || totalWarning > 0}}
429
- There are currently
430
- {{if totalCritical > 0 && totalWarning > 0}}
431
- ${totalCritical} critical issues and ${totalWarning} warning issues
432
- {{else totalCritical > 0}}
433
- ${totalCritical} critical issues
434
- {{else totalWarning > 0}}
435
- ${totalWarning} warning issues
436
- {{/if}}
437
- you need to investigate. See below for full details.
438
- {{else lastScanCompleted == 'ok'}}
439
- Congratulations, you have no security issues that need investigating.
440
- {{else lastScanCompleted}}
441
- <span style="color: #A00;">Latest scan failed: ${lastScanCompleted}</span>
442
- {{/if}}
443
- </td></tr>
444
- <tr><td>
445
- <div class="wordfenceScanButton"><input type="button" value="Start a Wordfence Scan" class="wfStartScanButton button-primary" onclick="wordfenceAdmin.startScan();" /></div>
446
- <a href="http://www.wordfence.com/forums/" target="_blank">Visit the Wordfence forums for help.</a>
447
- </td></tr>
448
- </table>
449
- </td>
450
- <td>
451
- <table class="wfSummaryChild wfSC2" cellpadding="0" cellspacing="0">
452
- <tr><th>${wordfenceAdmin.commify(totalFiles)}</th><td>Files</td></tr>
453
- <tr><th>${wordfenceAdmin.commify(totalDirs)}</th><td>Directories</td></tr>
454
- <tr><th>${wordfenceAdmin.commify(totalUsers)}</th><td>Users</td></tr>
455
- <tr><th>${wordfenceAdmin.commify(totalPlugins)}</th><td>Plugins</td></tr>
456
- <tr><th>${wordfenceAdmin.commify(totalThemes)}</th><td>Themes</td></tr>
457
- <tr><th>${wordfenceAdmin.commify(totalPages)}</th><td>Pages</td></tr>
458
- <tr><th>${wordfenceAdmin.commify(totalPosts)}</th><td>Posts</td></tr>
459
- </table>
460
- </td>
461
- <td>&nbsp;&nbsp;</td>
462
- <td>
463
- <table class="wfSummaryChild wfSC3" cellpadding="0" cellspacing="0">
464
- <tr><th>${wordfenceAdmin.commify(totalComments)}</th><td>Comments</td></tr>
465
- <tr><th>${wordfenceAdmin.commify(totalCategories)}</th><td>Categories</td></tr>
466
- <tr><th>${wordfenceAdmin.commify(linesOfPHP)}</th><td>Lines of PHP code</td></tr>
467
- <tr><th>${wordfenceAdmin.commify(linesOfJCH)}</th><td>Lines of JS, HTML and CSS code</td></tr>
468
- <tr><th>${wordfenceAdmin.commify(totalData)}</th><td>of data in ${wordfenceAdmin.commify(totalFiles)} files</td></tr>
469
- <tr><th>${wordfenceAdmin.commify(totalTables)}</th><td>Database Tables</td><tr>
470
- <tr><th>${wordfenceAdmin.commify(totalRows)}</th><td>Database Rows</td></tr>
471
- </table>
472
- </td>
473
- </tr></table>
474
- </div>
475
- </script>
476
1
<div class="wordfenceModeElem" id="wordfenceMode_scan"></div>
2
<div class="wrap wordfence">
3
<div class="wordfence-lock-icon wordfence-icon32"><br /></div><h2>Wordfence Scan</h2>
4
<div class="wordfenceWrap">
5
+ <div class="wordfenceScanButton"><input type="button" value="Start a Wordfence Scan" id="wfStartScanButton1" class="wfStartScanButton button-primary" onclick="wordfenceAdmin.startScan();" />
6
+ <a target="_blank" href="http://www.wordfence.com/forums/">You can always get help on our support forum.</a>
7
+ </div>
8
<div>
9
+ <div class="consoleHead">
10
+ <span class="consoleHeadText">Scan Summary</span>
11
</div>
12
+ <?php
13
+ $events = wordfence::getLog()->getStatusEvents(0);
14
+ ?>
15
+ <div class="bevelDiv1 consoleOuter"><div class="bevelDiv2"><div class="bevelDiv3 consoleInner" id="consoleSummary">
16
+ <?php if(sizeof($events) < 1){ ?>
17
+ <div style="width: 500px;">
18
+ Welcome to Wordfence!<br /><br />
19
+ To get started, simply click the blue button at the top of this page to start your first scan.
20
</div>
21
+ <?php } ?>
22
+ </div></div></div>
23
+ <div class="consoleHead">
24
+ <span class="consoleHeadText">Scan Detailed Activity</span>
25
+ <a href="#" class="wfALogMailLink" onclick="WFAD.emailActivityLog(); return false;">Email Activity Log</a>
26
</div>
27
+ <div class="bevelDiv1 consoleOuter"><div class="bevelDiv2"><div class="bevelDiv3 consoleInner" id="consoleActivity">
28
+ <?php
29
+ if(sizeof($events) > 0){
30
+ $newestItem = 0;
31
+ $sumEvents = array();
32
+ foreach($events as $e){
33
+ if(strpos($e['msg'], 'SUM_') !== 0){
34
+ echo '<div class="wfActivityLine wf' . $e['type'] . '">[' . date('M d H:i:s', $e['ctime']) . ']&nbsp;' . $e['msg'] . '</div>';
35
+ }
36
+ $newestItem = $e['ctime'];
37
+ }
38
+
39
+ echo '<script type="text/javascript">WFAD.lastALogCtime = ' . $newestItem . '; WFAD.processActArray(' . json_encode(wordfence::getLog()->getSummaryEvents()) . ');</script>';
40
+ } else { ?>
41
+ A live stream of what Wordfence is busy with right now will appear in this box.
42
+
43
+ <?php
44
+ }
45
+ ?>
46
+ </div></div></div>
47
</div>
48
<div style="margin-top: 20px;">
49
<div id="wfTabs">
58
If you have fixed all the issues below, you can <a href="#" onclick="WFAD.updateAllIssues('deleteNew'); return false;">click here to mark all new issues as fixed</a>.
59
You can also <a href="#" onclick="WFAD.updateAllIssues('ignoreAllNew'); return false;">ignore all new issues</a> which will exclude all issues listed below from future scans.
60
</p>
61
+ <div id="wfIssues_dataTable_new">
62
+ </div>
63
</div>
64
<div id="wfIssues_ignored" class="wfIssuesContainer">
65
<h2>Ignored Issues</h2>
418
where you can post your comments or questions. We would love to hear from you.
419
</td></tr>
420
<tr><td>
421
+ <div class="wordfenceScanButton"><input type="button" value="Start a Wordfence Scan" id="wfStartScanButton2" class="wfStartScanButton button-primary" /></div>
422
</td></tr>
423
</table>
424
</td>
426
</div>
427
</script>
428
429
lib/wfAPI.php CHANGED
@@ -15,7 +15,6 @@ class wfAPI {
15
$this->wordpressVersion = $wordpressVersion;
16
}
17
public function call($action, $getParams = array(), $postParams = array()){
18
- wordfence::status(3, 'info', "Starting API call: $action");
19
$this->errorMsg = false;
20
$json = $this->getURL(WORDFENCE_API_URL . '/v' . WORDFENCE_VERSION . '/?' . $this->makeAPIQueryString() . '&' . http_build_query(
21
array_merge(
@@ -42,7 +41,7 @@ class wfAPI {
42
if($this->errorMsg){
43
wordfence::status(3, 'error', "API Error: " . $this->errorMsg);
44
} else {
45
- wordfence::status(3, 'info', "Completed API call: $action");
46
}
47
return $dat;
48
}
@@ -94,7 +93,6 @@ class wfAPI {
94
95
}
96
public function binCall($func, $postData){
97
- wordfence::status(3, 'info', "Starting binary API call: $func");
98
$this->errorMsg = false;
99
$url = WORDFENCE_API_URL . '/v' . WORDFENCE_VERSION . '/?' . $this->makeAPIQueryString() . '&action=' . $func;
100
$curl = curl_init($url);
@@ -120,7 +118,6 @@ class wfAPI {
120
return false;
121
}
122
}
123
- wordfence::status(3, 'info', "Completed binary API call $func with code: $httpStatus");
124
return array('code' => $httpStatus, 'data' => $data);
125
}
126
public function makeAPIQueryString(){
15
$this->wordpressVersion = $wordpressVersion;
16
}
17
public function call($action, $getParams = array(), $postParams = array()){
18
$this->errorMsg = false;
19
$json = $this->getURL(WORDFENCE_API_URL . '/v' . WORDFENCE_VERSION . '/?' . $this->makeAPIQueryString() . '&' . http_build_query(
20
array_merge(
41
if($this->errorMsg){
42
wordfence::status(3, 'error', "API Error: " . $this->errorMsg);
43
} else {
44
+ //wordfence::status(3, 'info', "Completed API call: $action");
45
}
46
return $dat;
47
}
93
94
}
95
public function binCall($func, $postData){
96
$this->errorMsg = false;
97
$url = WORDFENCE_API_URL . '/v' . WORDFENCE_VERSION . '/?' . $this->makeAPIQueryString() . '&action=' . $func;
98
$curl = curl_init($url);
118
return false;
119
}
120
}
121
return array('code' => $httpStatus, 'data' => $data);
122
}
123
public function makeAPIQueryString(){
lib/wfConfig.php CHANGED
@@ -89,7 +89,7 @@ class wfConfig {
89
"scansEnabled_diskSpace" => true,
90
"scansEnabled_dns" => true,
91
"scansEnabled_oldVersions" => true,
92
- "firewallEnabled" => true,
93
"blockFakeBots" => false,
94
"autoBlockScanners" => true,
95
"loginSecurityEnabled" => true,
@@ -150,8 +150,8 @@ class wfConfig {
150
"scansEnabled_diskSpace" => true,
151
"scansEnabled_dns" => true,
152
"scansEnabled_oldVersions" => true,
153
- "firewallEnabled" => true,
154
- "blockFakeBots" => true,
155
"autoBlockScanners" => true,
156
"loginSecurityEnabled" => true,
157
"loginSec_lockInvalidUsers" => false,
@@ -212,7 +212,7 @@ class wfConfig {
212
"scansEnabled_dns" => true,
213
"scansEnabled_oldVersions" => true,
214
"firewallEnabled" => true,
215
- "blockFakeBots" => true,
216
"autoBlockScanners" => true,
217
"loginSecurityEnabled" => true,
218
"loginSec_lockInvalidUsers" => false,
89
"scansEnabled_diskSpace" => true,
90
"scansEnabled_dns" => true,
91
"scansEnabled_oldVersions" => true,
92
+ "firewallEnabled" => false,
93
"blockFakeBots" => false,
94
"autoBlockScanners" => true,
95
"loginSecurityEnabled" => true,
150
"scansEnabled_diskSpace" => true,
151
"scansEnabled_dns" => true,
152
"scansEnabled_oldVersions" => true,
153
+ "firewallEnabled" => false,
154
+ "blockFakeBots" => false,
155
"autoBlockScanners" => true,
156
"loginSecurityEnabled" => true,
157
"loginSec_lockInvalidUsers" => false,
212
"scansEnabled_dns" => true,
213
"scansEnabled_oldVersions" => true,
214
"firewallEnabled" => true,
215
+ "blockFakeBots" => false,
216
"autoBlockScanners" => true,
217
"loginSecurityEnabled" => true,
218
"loginSec_lockInvalidUsers" => false,
lib/wfDB.php CHANGED
@@ -117,6 +117,25 @@ class wfDB {
117
error_log($msg);
118
exit(1);
119
}
120
}
121
122
?>
117
error_log($msg);
118
exit(1);
119
}
120
+ public function createKeyIfNotExists($table, $col, $keyName){
121
+ global $wpdb; $prefix = $wpdb->prefix;
122
+ $table = $prefix . $table;
123
+ $exists = $this->querySingle("show tables like '$table'");
124
+ $keyFound = false;
125
+ if($exists){
126
+ $q = $this->query("show keys from $table");
127
+ if($q){
128
+ while($row = mysql_fetch_assoc($q)){
129
+ if($row['Key_name'] == $keyName){
130
+ $keyFound = true;
131
+ }
132
+ }
133
+ }
134
+ }
135
+ if(! $keyFound){
136
+ $this->query("alter table $table add KEY $keyName($col)");
137
+ }
138
+ }
139
}
140
141
?>
lib/wfIssues.php CHANGED
@@ -25,9 +25,9 @@ class wfIssues {
25
$ignoreC = md5($ignoreC);
26
$rec = $this->getDB()->querySingleRec("select status, ignoreP, ignoreC from " . $this->issuesTable . " where (ignoreP='%s' OR ignoreC='%s')", $ignoreP, $ignoreC);
27
if($rec){
28
- if($rec['status'] == 'new' && ($rec['ignoreC'] == $ignoreC || $rec['ignoreP'] == $ignoreP)){ return; }
29
- if($rec['status'] == 'ignoreC' && $rec['ignoreC'] == $ignoreC){ return; }
30
- if($rec['status'] == 'ignoreP' && $rec['ignoreP'] == $ignoreP){ return; }
31
}
32
33
$this->totalIssues++;
@@ -56,6 +56,7 @@ class wfIssues {
56
$longMsg,
57
serialize($templateData)
58
);
59
}
60
public function deleteIgnored(){
61
$this->getDB()->query("delete from " . $this->issuesTable . " where status='ignoreP' or status='ignoreC'");
@@ -190,7 +191,7 @@ class wfIssues {
190
$this->updateSummaryItems();
191
}
192
$arr = wfConfig::get_ser('wf_summaryItems', array());
193
- $arr['scanTimeAgo'] = wfUtils::makeTimeAgo(sprintf('%.0f', time() - $arr['scanTime']));
194
$arr['scanRunning'] = wfConfig::get('wf_scanRunning') ? '1' : '0';
195
$arr['scheduledScansEnabled'] = wfConfig::get('scheduledScansEnabled');
196
$secsToGo = wp_next_scheduled('wordfence_scheduled_scan') - time();
25
$ignoreC = md5($ignoreC);
26
$rec = $this->getDB()->querySingleRec("select status, ignoreP, ignoreC from " . $this->issuesTable . " where (ignoreP='%s' OR ignoreC='%s')", $ignoreP, $ignoreC);
27
if($rec){
28
+ if($rec['status'] == 'new' && ($rec['ignoreC'] == $ignoreC || $rec['ignoreP'] == $ignoreP)){ return false; }
29
+ if($rec['status'] == 'ignoreC' && $rec['ignoreC'] == $ignoreC){ return false; }
30
+ if($rec['status'] == 'ignoreP' && $rec['ignoreP'] == $ignoreP){ return false; }
31
}
32
33
$this->totalIssues++;
56
$longMsg,
57
serialize($templateData)
58
);
59
+ return true;
60
}
61
public function deleteIgnored(){
62
$this->getDB()->query("delete from " . $this->issuesTable . " where status='ignoreP' or status='ignoreC'");
191
$this->updateSummaryItems();
192
}
193
$arr = wfConfig::get_ser('wf_summaryItems', array());
194
+ //$arr['scanTimeAgo'] = wfUtils::makeTimeAgo(sprintf('%.0f', time() - $arr['scanTime']));
195
$arr['scanRunning'] = wfConfig::get('wf_scanRunning') ? '1' : '0';
196
$arr['scheduledScansEnabled'] = wfConfig::get('scheduledScansEnabled');
197
$secsToGo = wp_next_scheduled('wordfence_scheduled_scan') - time();
lib/wfLog.php CHANGED
@@ -575,16 +575,37 @@ class wfLog {
575
//$msg = '[' . sprintf('%.2f', memory_get_usage(true) / (1024 * 1024)) . '] ' . $msg;
576
$this->getDB()->query("insert into " . $this->statusTable . " (ctime, level, type, msg) values (%s, %d, '%s', '%s')", microtime(true), $level, $type, $msg);
577
}
578
- public function getStatusEvents(){
579
- $res = $this->getDB()->query("select ctime, level, type, msg from " . $this->statusTable . " order by ctime desc limit 2000");
580
$results = array();
581
$lastTime = false;
582
while($rec = mysql_fetch_assoc($res)){
583
- $rec['timeAgo'] = wfUtils::makeTimeAgo(time() - $rec['ctime']);
584
array_push($results, $rec);
585
}
586
return $results;
587
}
588
}
589
590
?>
575
//$msg = '[' . sprintf('%.2f', memory_get_usage(true) / (1024 * 1024)) . '] ' . $msg;
576
$this->getDB()->query("insert into " . $this->statusTable . " (ctime, level, type, msg) values (%s, %d, '%s', '%s')", microtime(true), $level, $type, $msg);
577
}
578
+ public function getStatusEvents($lastCtime){
579
+ if($lastCtime < 1){
580
+ $lastCtime = $this->getDB()->querySingle("select ctime from " . $this->statusTable . " order by ctime desc limit 1000,1");
581
+ if(! $lastCtime){
582
+ $lastCtime = 0;
583
+ }
584
+ }
585
+ $res = $this->getDB()->query("select ctime, level, type, msg from " . $this->statusTable . " where ctime > %f order by ctime asc", $lastCtime);
586
$results = array();
587
$lastTime = false;
588
while($rec = mysql_fetch_assoc($res)){
589
+ //$rec['timeAgo'] = wfUtils::makeTimeAgo(time() - $rec['ctime']);
590
+ $rec['date'] = date('M d H:i:s', $rec['ctime']);
591
array_push($results, $rec);
592
}
593
return $results;
594
}
595
+ public function getSummaryEvents(){
596
+ $res = $this->getDB()->query("select ctime, level, type, msg from " . $this->statusTable . " where level = 10 order by ctime desc limit 100", $lastCtime);
597
+ $results = array();
598
+ $lastTime = false;
599
+ while($rec = mysql_fetch_assoc($res)){
600
+ $rec['date'] = date('M d H:i:s', $rec['ctime']);
601
+ array_push($results, $rec);
602
+ if(strpos($rec['msg'], 'SUM_PREP:') === 0){
603
+ break;
604
+ }
605
+ }
606
+ return array_reverse($results);
607
+ }
608
+
609
}
610
611
?>
lib/wfScanEngine.php CHANGED
@@ -14,7 +14,9 @@ class wfScanEngine {
14
private $apiKey = false;
15
private $errorStopped = false;
16
private $dictWords = array();
17
public function __construct(){
18
$this->i = new wfIssues();
19
$this->wp_version = wfUtils::getWPVersion();
20
$this->apiKey = wfConfig::get('apiKey');
@@ -86,40 +88,58 @@ class wfScanEngine {
86
$this->scanOldVersions();
87
if($this->errorStopped){ return; }
88
}
89
- $this->status(1, 'info', "Scan complete.");
90
return;
91
}
92
private function scanKnownFiles(){
93
$malwareScanEnabled = $coreScanEnabled = $pluginScanEnabled = $themeScanEnabled = false;
94
if(wfConfig::get('scansEnabled_core')){
95
$coreScanEnabled = true;
96
} else {
97
- $this->status(2, 'info', "Skipping core scan because it's disabled.");
98
}
99
if(wfConfig::get('scansEnabled_plugins')){
100
$pluginScanEnabled = true;
101
} else {
102
$this->status(2, 'info', "Skipping plugin scan because it's disabled.");
103
}
104
if(wfConfig::get('scansEnabled_themes')){
105
$themeScanEnabled = true;
106
} else {
107
$this->status(2, 'info', "Skipping themes scan because it's disabled.");
108
}
109
110
if(wfConfig::get('scansEnabled_malware')){
111
$malwareScanEnabled = true;
112
} else {
113
$this->status(2, 'info', "Skipping malware scan because it's disabled.");
114
}
115
$summaryUpdateRequired = $this->i->summaryUpdateRequired();
116
if((! $summaryUpdateRequired) && (! ($coreScanEnabled || $pluginScanEnabled || $themeScanEnabled || $malwareScanEnabled))){
117
- $this->status(2, 'info', "Finishign this stage because we don't have to do a summary update and we don't need to do a core, plugin, theme or malware scan.");
118
return array();
119
}
120
121
//CORE SCAN
122
- $this->status(2, 'info', "Examining files in WordPress base directory.");
123
$hasher = new wordfenceHash(strlen(ABSPATH));
124
$baseWPStuff = array( '.htaccess', 'index.php', 'license.txt', 'readme.html', 'wp-activate.php', 'wp-admin', 'wp-app.php', 'wp-blog-header.php', 'wp-comments-post.php', 'wp-config-sample.php', 'wp-content', 'wp-cron.php', 'wp-includes', 'wp-links-opml.php', 'wp-load.php', 'wp-login.php', 'wp-mail.php', 'wp-pass.php', 'wp-register.php', 'wp-settings.php', 'wp-signup.php', 'wp-trackback.php', 'xmlrpc.php');
125
$baseContents = scandir(ABSPATH);
@@ -186,28 +206,44 @@ class wfScanEngine {
186
$result1 = $this->api->call('main_scan', array(), array(
187
'data' => json_encode($scanData)
188
));
189
- $this->status(2, 'info', "Done API call to Wordfence servers and got a result.");
190
if($this->api->errorMsg){
191
$this->errorStop($this->api->errorMsg);
192
return;
193
}
194
if(empty($result1['errorMsg']) === false){
195
$this->errorStop($result['errorMsg']);
196
return;
197
}
198
if(! $result1){
199
$this->errorStop("We received an empty response from the Wordfence server when scanning core, plugin and theme files.");
200
return;
201
}
202
$this->status(2, 'info', "Processing scan results");
203
foreach($result1['results'] as $issue){
204
$this->status(2, 'info', "Adding issue: " . $issue['shortMsg']);
205
- $this->addIssue($issue['type'], $issue['severity'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data']);
206
}
207
return $result1['unknownFiles'];
208
}
209
private function scanFileContents($unknownFiles){
210
- $this->status(1, 'info', "Scanning file contents.");
211
if(! is_array($unknownFiles)){
212
$unknownFiles = array();
213
}
@@ -219,13 +255,23 @@ class wfScanEngine {
219
if($scanner->errorMsg){
220
$this->errorStop($scanner->errorMsg);
221
}
222
foreach($result2 as $issue){
223
$this->status(2, 'info', "Adding issue: " . $issue['shortMsg']);
224
- $this->addIssue($issue['type'], $issue['severity'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data']);
225
}
226
}
227
private function scanPosts(){
228
- $this->status(1, 'info', "Starting posts scan");
229
global $wpdb;
230
$wfdb = new wfDB();
231
//NOTE: There must be no other DB activity by wfDB between here and free_result below because we're doing an unbuffered query. THAT INCLUDES calls to status() which updates the DB
@@ -248,8 +294,11 @@ class wfScanEngine {
248
$this->status(2, 'info', "Done examining URls");
249
if($h->errorMsg){
250
$this->errorStop($h->errorMsg);
251
return;
252
}
253
foreach($hooverResults as $id => $hresults){
254
$uctype = ucfirst($postDat[$id]['type']);
255
$type = $postDat[$id]['type'];
@@ -265,7 +314,7 @@ class wfScanEngine {
265
continue;
266
}
267
$this->status(2, 'info', "Adding issue: $shortMsg");
268
- $this->addIssue('postBadURL', 1, $id, $id . $postDat[$id]['contentMD5'], $shortMsg, $longMsg, array(
269
'postID' => $id,
270
'badURL' => $result['URL'],
271
'postTitle' => $postDat[$id]['title'],
@@ -274,9 +323,12 @@ class wfScanEngine {
274
'permalink' => get_permalink($id),
275
'editPostLink' => get_edit_post_link($id),
276
'postDate' => $postDat[$id]['postDate']
277
- ));
278
}
279
}
280
}
281
public function isBadComment($author, $email, $url, $IP, $content){
282
$content = $author . ' ' . $email . ' ' . $url . ' ' . $IP . ' ' . $content;
@@ -315,11 +367,13 @@ class wfScanEngine {
315
return false;
316
}
317
private function scanComments(){
318
global $wpdb;
319
$wfdb = new wfDB();
320
//NOTE: There must be no other DB activity by wfDB between here and free_result below because we're doing an unbuffered query. THAT INCLUDES calls to status() which updates the DB
321
$q1 = $wfdb->uQuery("select comment_ID, comment_date, comment_type, comment_author, comment_author_url, comment_content from $wpdb->comments where comment_approved=1");
322
if( ! $q1){
323
return;
324
}
325
$h = new wordfenceURLHoover($this->apiKey, $this->wp_version);
@@ -336,12 +390,17 @@ class wfScanEngine {
336
);
337
}
338
mysql_free_result($q1);
339
- if(! $gotRow){ return; }
340
$hooverResults = $h->getBaddies();
341
if($h->errorMsg){
342
$this->errorStop($h->errorMsg);
343
return;
344
}
345
foreach($hooverResults as $id => $hresults){
346
$uctype = ucfirst($commentDat[$id]['type']);
347
$type = $commentDat[$id]['type'];
@@ -356,7 +415,7 @@ class wfScanEngine {
356
//A list type that may be new and the plugin has not been upgraded yet.
357
continue;
358
}
359
- $this->addIssue('commentBadURL', 1, $id, $id . $commentDat[$id]['contentMD5'], $shortMsg, $longMsg, array(
360
'commentID' => $id,
361
'badURL' => $result['URL'],
362
'author' => $commentDat[$id]['author'],
@@ -364,9 +423,12 @@ class wfScanEngine {
364
'uctype' => $uctype,
365
'editCommentLink' => get_edit_comment_link($id),
366
'commentDate' => $commentDat[$id]['date']
367
- ));
368
}
369
}
370
}
371
private function highestCap($caps){
372
foreach(array('administrator', 'editor', 'author', 'contributor', 'subscriber') as $cap){
@@ -385,12 +447,16 @@ class wfScanEngine {
385
return false;
386
}
387
private function scanAllPasswords(){
388
global $wpdb;
389
$ws = $wpdb->get_results("SELECT ID, user_login FROM $wpdb->users");
390
foreach($ws as $user){
391
$this->status(2, 'info', "Checking password strength for: " . $user->user_login);
392
- $this->scanUserPassword($user->ID);
393
}
394
}
395
public function scanUserPassword($userID){
396
require_once( ABSPATH . 'wp-includes/class-phpass.php');
@@ -412,28 +478,33 @@ class wfScanEngine {
412
$level = 2;
413
$words = array($userDat->user_login);
414
}
415
for($i = 0; $i < sizeof($words); $i++){
416
if($hasher->CheckPassword($words[$i], $userDat->user_pass)){
417
$this->status(2, 'info', "Adding issue " . $shortMsg);
418
- $this->addIssue('easyPassword', $level, $userDat->ID, $userDat->ID . '-' . $userDat->user_pass, $shortMsg, $longMsg, array(
419
'ID' => $userDat->ID,
420
'user_login' => $userDat->user_login,
421
'user_email' => $userDat->user_email,
422
'first_name' => $userDat->first_name,
423
'last_name' => $userDat->last_name,
424
'editUserLink' => wfUtils::editUserLink($userDat->ID)
425
- ));
426
break;
427
}
428
}
429
$this->status(2, 'info', "Completed checking password strength of user '" . $userDat->user_login . "'");
430
}
431
private function scanDiskSpace(){
432
- $this->status(2, 'info', "Starting disk space check.");
433
$total = disk_total_space('.');
434
$free = disk_free_space('.');
435
$this->status(2, 'info', "Total space: $total Free space: $free");
436
if( (! $total) || (! $free )){ //If we get zeros it's probably not reading right. If free is zero then we're out of space and already in trouble.
437
return;
438
}
439
$level = false;
@@ -444,20 +515,23 @@ class wfScanEngine {
444
} else if($spaceLeft < 5){
445
$level = 2;
446
} else {
447
return;
448
}
449
- $this->status(2, 'info', "Adding issue due to low disk space.");
450
- $this->addIssue('diskSpace', $level, 'diskSpace' . $level, 'diskSpace' . $level, "You have $spaceLeft" . "% disk space remaining", "You only have $spaceLeft" . "% of your disk space remaining. Please free up disk space or your website may stop serving requests.", array(
451
- 'spaceLeft' => $spaceLeft ));
452
-
453
-
454
}
455
private function scanDNSChanges(){
456
if(! function_exists('dns_get_record')){
457
$this->status(1, 'info', "Skipping DNS scan because this system does not support dns_get_record()");
458
return;
459
}
460
- $this->status(2, 'info', "Starting DNS checks");
461
$home = get_home_url();
462
if(preg_match('/https?:\/\/([^\/]+)/i', $home, $matches)){
463
$host = strtolower($matches[1]);
@@ -480,12 +554,14 @@ class wfScanEngine {
480
$loggedCNAME = wfConfig::get('wf_dnsCNAME');
481
$dnsLogged = wfConfig::get('wf_dnsLogged', false);
482
if($dnsLogged && $loggedCNAME != $currentCNAME){
483
- $this->addIssue('dnsChange', 2, 'dnsChanges', 'dnsChanges', "Your DNS records have changed", "We have detected a change in the CNAME records of your DNS configuration for the domain $host. A CNAME record is an alias that is used to point a domain name to another domain name. For example foo.example.com can point to bar.example.com which then points to an IP address of 10.1.1.1. $msg", array(
484
'type' => 'CNAME',
485
'host' => $host,
486
'oldDNS' => $loggedCNAME,
487
'newDNS' => $currentCNAME
488
- ));
489
}
490
wfConfig::set('wf_dnsCNAME', $currentCNAME);
491
@@ -505,12 +581,14 @@ class wfScanEngine {
505
$dnsLogged = wfConfig::get('wf_dnsLogged', false);
506
$msg = "A change in your DNS records may indicate that a hacker has hacked into your DNS administration system and has pointed your email or website to their own server for malicious purposes. It could also indicate that your domain has expired. If you made this change yourself you can mark it 'resolved' and safely ignore it.";
507
if($dnsLogged && $loggedA != $currentA){
508
- $this->addIssue('dnsChange', 2, 'dnsChanges', 'dnsChanges', "Your DNS records have changed", "We have detected a change in the A records of your DNS configuration that may affect the domain $host. An A record is a record in DNS that points a domain name to an IP address. $msg", array(
509
'type' => 'A',
510
'host' => $host,
511
'oldDNS' => $loggedA,
512
'newDNS' => $currentA
513
- ));
514
}
515
wfConfig::set('wf_dnsA', $currentA);
516
@@ -530,29 +608,36 @@ class wfScanEngine {
530
$currentMX = implode(', ', $mxArr);
531
$loggedMX = wfConfig::get('wf_dnsMX');
532
if($dnsLogged && $loggedMX != $currentMX){
533
- $this->addIssue('dnsChange', 2, 'dnsChanges', 'dnsChanges', "Your DNS records have changed", "We have detected a change in the email server (MX) records of your DNS configuration for the domain $host. $msg", array(
534
'type' => 'MX',
535
'host' => $host,
536
'oldDNS' => $loggedMX,
537
'newDNS' => $currentMX
538
- ));
539
540
}
541
wfConfig::set('wf_dnsMX', $currentMX);
542
543
wfConfig::set('wf_dnsLogged', 1);
544
}
545
}
546
private function scanOldVersions(){
547
if(! function_exists( 'get_preferred_from_update_core')){
548
require_once(ABSPATH . 'wp-admin/includes/update.php');
549
}
550
$cur = get_preferred_from_update_core();
551
if(isset( $cur->response ) && $cur->response == 'upgrade'){
552
- $this->addIssue('wfUpgrade', 1, 'wfUpgrade' . $cur->current, 'wfUpgrade' . $cur->current, "Your WordPress version is out of date", "WordPress version " . $cur->current . " is now available. Please upgrade immediately to get the latest security updates from WordPress.", array(
553
'currentVersion' => $this->wp_version,
554
'newVersion' => $cur->current
555
- ));
556
}
557
$update_plugins = get_site_transient( 'update_plugins' );
558
if(isset($update_plugins) && (! empty($update_plugins->response))){
@@ -565,7 +650,9 @@ class wfScanEngine {
565
$data = get_plugin_data($pluginFile);
566
$data['newVersion'] = $vals->new_version;
567
$key = 'wfPluginUpgrade' . ' ' . $plugin . ' ' . $data['newVersion'] . ' ' . $data['Version'];
568
- $this->addIssue('wfPluginUpgrade', 1, $key, $key, "The Plugin \"" . $data['Name'] . "\" needs an upgrade.", "You need to upgrade \"" . $data['Name'] . "\" to the newest version to ensure you have any security fixes the developer has released.", $data);
569
}
570
}
571
}
@@ -586,13 +673,15 @@ class wfScanEngine {
586
'version' => $themeData['Version']
587
);
588
$key = 'wfThemeUpgrade' . ' ' . $theme . ' ' . $tData['version'] . ' ' . $tData['newVersion'];
589
- $this->addIssue('wfThemeUpgrade', 1, $key, $key, "The Theme \"" . $themeData['Name'] . "\" needs an upgrade.", "You need to upgrade \"" . $themeData['Name'] . "\" to the newest version to ensure you have any security fixes the developer has released.", $tData);
590
}
591
}
592
593
}
594
}
595
-
596
}
597
private function errorStop($msg){
598
$this->errorStopped = true;
@@ -603,7 +692,7 @@ class wfScanEngine {
603
wordfence::status($level, $type, $msg);
604
}
605
private function addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData){
606
- $this->i->addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData);
607
}
608
}
609
14
private $apiKey = false;
15
private $errorStopped = false;
16
private $dictWords = array();
17
+ private $startTime = 0;
18
public function __construct(){
19
+ $this->startTime = time();
20
$this->i = new wfIssues();
21
$this->wp_version = wfUtils::getWPVersion();
22
$this->apiKey = wfConfig::get('apiKey');
88
$this->scanOldVersions();
89
if($this->errorStopped){ return; }
90
}
91
+ $summary = $this->i->getSummaryItems();
92
+ $this->status(1, 'info', "Scan Complete. Scanned " . $summary['totalFiles'] . " files, " . $summary['totalPlugins'] . " plugins, " . $summary['totalThemes'] . " themes, " . ($summary['totalPages'] + $summary['totalPosts']) . " pages, " . $summary['totalComments'] . " comments and " . $summary['totalRows'] . " records in " . (time() - $this->startTime) . " seconds.");
93
+ if($this->i->totalIssues > 0){
94
+ $this->status(10, 'info', "SUM_FINAL:Scan complete. You have " . $this->i->totalIssues . " new issues to fix. See below for details.");
95
+ } else {
96
+ $this->status(10, 'info', "SUM_FINAL:Scan complete. Congratulations, there were no problems found.");
97
+ }
98
return;
99
}
100
private function scanKnownFiles(){
101
$malwareScanEnabled = $coreScanEnabled = $pluginScanEnabled = $themeScanEnabled = false;
102
+ $statusIDX = array(
103
+ 'core' => false,
104
+ 'plugin' => false,
105
+ 'theme' => false,
106
+ 'unknown' => false
107
+ );
108
if(wfConfig::get('scansEnabled_core')){
109
$coreScanEnabled = true;
110
+ $statusIDX['core'] = wordfence::statusStart("Comparing core WordPress files against originals in repository");
111
} else {
112
+ wordfence::statusDisabled("Skipping core scan");
113
}
114
if(wfConfig::get('scansEnabled_plugins')){
115
$pluginScanEnabled = true;
116
+ $statusIDX['plugin'] = wordfence::statusStart("Comparing plugin files against originals in repository");
117
} else {
118
+ wordfence::statusDisabled("Skipping plugin scan");
119
$this->status(2, 'info', "Skipping plugin scan because it's disabled.");
120
}
121
if(wfConfig::get('scansEnabled_themes')){
122
$themeScanEnabled = true;
123
+ $statusIDX['theme'] = wordfence::statusStart("Comparing theme files against originals in repository");
124
} else {
125
+ wordfence::statusDisabled("Skipping theme scan");
126
$this->status(2, 'info', "Skipping themes scan because it's disabled.");
127
}
128
129
if(wfConfig::get('scansEnabled_malware')){
130
+ $statusIDX['unknown'] = wordfence::statusStart("Scanning for known malware files");
131
$malwareScanEnabled = true;
132
} else {
133
+ wordfence::statusDisabled("Skipping malware scan");
134
$this->status(2, 'info', "Skipping malware scan because it's disabled.");
135
}
136
$summaryUpdateRequired = $this->i->summaryUpdateRequired();
137
if((! $summaryUpdateRequired) && (! ($coreScanEnabled || $pluginScanEnabled || $themeScanEnabled || $malwareScanEnabled))){
138
+ $this->status(2, 'info', "Finishing this stage because we don't have to do a summary update and we don't need to do a core, plugin, theme or malware scan.");
139
return array();
140
}
141
142
//CORE SCAN
143
$hasher = new wordfenceHash(strlen(ABSPATH));
144
$baseWPStuff = array( '.htaccess', 'index.php', 'license.txt', 'readme.html', 'wp-activate.php', 'wp-admin', 'wp-app.php', 'wp-blog-header.php', 'wp-comments-post.php', 'wp-config-sample.php', 'wp-content', 'wp-cron.php', 'wp-includes', 'wp-links-opml.php', 'wp-load.php', 'wp-login.php', 'wp-mail.php', 'wp-pass.php', 'wp-register.php', 'wp-settings.php', 'wp-signup.php', 'wp-trackback.php', 'xmlrpc.php');
145
$baseContents = scandir(ABSPATH);
206
$result1 = $this->api->call('main_scan', array(), array(
207
'data' => json_encode($scanData)
208
));
209
if($this->api->errorMsg){
210
$this->errorStop($this->api->errorMsg);
211
+ wordfence::statusEndErr();
212
return;
213
}
214
if(empty($result1['errorMsg']) === false){
215
$this->errorStop($result['errorMsg']);
216
+ wordfence::statusEndErr();
217
return;
218
}
219
if(! $result1){
220
$this->errorStop("We received an empty response from the Wordfence server when scanning core, plugin and theme files.");
221
+ wordfence::statusEndErr();
222
return;
223
}
224
$this->status(2, 'info', "Processing scan results");
225
+ $haveIssues = array(
226
+ 'core' => false,
227
+ 'plugin' => false,
228
+ 'theme' => false,
229
+ 'unknown' => false
230
+ );
231
foreach($result1['results'] as $issue){
232
$this->status(2, 'info', "Adding issue: " . $issue['shortMsg']);
233
+ if($this->addIssue($issue['type'], $issue['severity'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data'])){
234
+ $haveIssues[$issue['data']['cType']] = true;
235
+ }
236
+ }
237
+ foreach($haveIssues as $type => $have){
238
+ if($statusIDX[$type] !== false){
239
+ wordfence::statusEnd($statusIDX[$type], $have);
240
+ }
241
}
242
return $result1['unknownFiles'];
243
}
244
private function scanFileContents($unknownFiles){
245
+ $statusIDX = wordfence::statusStart('Scanning file contents for infections and vulnerabilities');
246
+ $statusIDX2 = wordfence::statusStart('Scanning files for URLs in Google\'s Safe Browsing List');
247
if(! is_array($unknownFiles)){
248
$unknownFiles = array();
249
}
255
if($scanner->errorMsg){
256
$this->errorStop($scanner->errorMsg);
257
}
258
+ $haveIssues = false;
259
+ $haveIssuesGSB = false;
260
foreach($result2 as $issue){
261
$this->status(2, 'info', "Adding issue: " . $issue['shortMsg']);
262
+ if($this->addIssue($issue['type'], $issue['severity'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data'])){
263
+ if(empty($issue['data']['gsb']) === false){
264
+ $haveIssuesGSB = true;
265
+ } else {
266
+ $haveIssues = true;
267
+ }
268
+ }
269
}
270
+ wordfence::statusEnd($statusIDX, $haveIssues);
271
+ wordfence::statusEnd($statusIDX2, $haveIssuesGSB);
272
}
273
private function scanPosts(){
274
+ $statusIDX = wordfence::statusStart('Scanning posts for URL\'s in Google\'s Safe Browsing List');
275
global $wpdb;
276
$wfdb = new wfDB();
277
//NOTE: There must be no other DB activity by wfDB between here and free_result below because we're doing an unbuffered query. THAT INCLUDES calls to status() which updates the DB
294
$this->status(2, 'info', "Done examining URls");
295
if($h->errorMsg){
296
$this->errorStop($h->errorMsg);
297
+ wordfence::statusEndErr();
298
return;
299
+
300
}
301
+ $haveIssues = false;
302
foreach($hooverResults as $id => $hresults){
303
$uctype = ucfirst($postDat[$id]['type']);
304
$type = $postDat[$id]['type'];
314
continue;
315
}
316
$this->status(2, 'info', "Adding issue: $shortMsg");
317
+ if($this->addIssue('postBadURL', 1, $id, $id . $postDat[$id]['contentMD5'], $shortMsg, $longMsg, array(
318
'postID' => $id,
319
'badURL' => $result['URL'],
320
'postTitle' => $postDat[$id]['title'],
323
'permalink' => get_permalink($id),
324
'editPostLink' => get_edit_post_link($id),
325
'postDate' => $postDat[$id]['postDate']
326
+ ))){
327
+ $haveIssues = true;
328
+ }
329
}
330
}
331
+ wordfence::statusEnd($statusIDX, $haveIssues);
332
}
333
public function isBadComment($author, $email, $url, $IP, $content){
334
$content = $author . ' ' . $email . ' ' . $url . ' ' . $IP . ' ' . $content;
367
return false;
368
}
369
private function scanComments(){
370
+ $statusIDX = wordfence::statusStart('Scanning comments for URL\'s in Google\'s Safe Browsing List');
371
global $wpdb;
372
$wfdb = new wfDB();
373
//NOTE: There must be no other DB activity by wfDB between here and free_result below because we're doing an unbuffered query. THAT INCLUDES calls to status() which updates the DB
374
$q1 = $wfdb->uQuery("select comment_ID, comment_date, comment_type, comment_author, comment_author_url, comment_content from $wpdb->comments where comment_approved=1");
375
if( ! $q1){
376
+ wordfence::statusEndErr();
377
return;
378
}
379
$h = new wordfenceURLHoover($this->apiKey, $this->wp_version);
390
);
391
}
392
mysql_free_result($q1);
393
+ if(! $gotRow){
394
+ wordfence::statusEnd($statusIDX, false);
395
+ return;
396
+ }
397
$hooverResults = $h->getBaddies();
398
if($h->errorMsg){
399
$this->errorStop($h->errorMsg);
400
+ wordfence::statusEndErr();
401
return;
402
}
403
+ $haveIssues = false;
404
foreach($hooverResults as $id => $hresults){
405
$uctype = ucfirst($commentDat[$id]['type']);
406
$type = $commentDat[$id]['type'];
415
//A list type that may be new and the plugin has not been upgraded yet.
416
continue;
417
}
418
+ if($this->addIssue('commentBadURL', 1, $id, $id . $commentDat[$id]['contentMD5'], $shortMsg, $longMsg, array(
419
'commentID' => $id,
420
'badURL' => $result['URL'],
421
'author' => $commentDat[$id]['author'],
423
'uctype' => $uctype,
424
'editCommentLink' => get_edit_comment_link($id),
425
'commentDate' => $commentDat[$id]['date']
426
+ ))){
427
+ $haveIssues = true;
428
+ }
429
}
430
}
431
+ wordfence::statusEnd($statusIDX, $haveIssues);
432
}
433
private function highestCap($caps){
434
foreach(array('administrator', 'editor', 'author', 'contributor', 'subscriber') as $cap){
447
return false;
448
}
449
private function scanAllPasswords(){
450
+ $statusIDX = wordfence::statusStart('Scanning for weak passwords');
451
global $wpdb;
452
$ws = $wpdb->get_results("SELECT ID, user_login FROM $wpdb->users");
453
+ $haveIssues = false;
454
foreach($ws as $user){
455
$this->status(2, 'info', "Checking password strength for: " . $user->user_login);
456
+ $isWeak = $this->scanUserPassword($user->ID);
457
+ if($isWeak){ $haveIssues = true; }
458
}
459
+ wordfence::statusEnd($statusIDX, $haveIssues);
460
}
461
public function scanUserPassword($userID){
462
require_once( ABSPATH . 'wp-includes/class-phpass.php');
478
$level = 2;
479
$words = array($userDat->user_login);
480
}
481
+ $haveIssue = false;
482
for($i = 0; $i < sizeof($words); $i++){
483
if($hasher->CheckPassword($words[$i], $userDat->user_pass)){
484
$this->status(2, 'info', "Adding issue " . $shortMsg);
485
+ if($this->addIssue('easyPassword', $level, $userDat->ID, $userDat->ID . '-' . $userDat->user_pass, $shortMsg, $longMsg, array(
486
'ID' => $userDat->ID,
487
'user_login' => $userDat->user_login,
488
'user_email' => $userDat->user_email,
489
'first_name' => $userDat->first_name,
490
'last_name' => $userDat->last_name,
491
'editUserLink' => wfUtils::editUserLink($userDat->ID)
492
+ ))){
493
+ $haveIssue = true;
494
+ }
495
break;
496
}
497
}
498
$this->status(2, 'info', "Completed checking password strength of user '" . $userDat->user_login . "'");
499
+ return $haveIssue;
500
}
501
private function scanDiskSpace(){
502
+ $statusIDX = wordfence::statusStart("Scanning to check available disk space");
503
$total = disk_total_space('.');
504
$free = disk_free_space('.');
505
$this->status(2, 'info', "Total space: $total Free space: $free");
506
if( (! $total) || (! $free )){ //If we get zeros it's probably not reading right. If free is zero then we're out of space and already in trouble.
507
+ wordfence::statusEnd($statusIDX, false);
508
return;
509
}
510
$level = false;
515
} else if($spaceLeft < 5){
516
$level = 2;
517
} else {
518
+ wordfence::statusEnd($statusIDX, false);
519
return;
520
}
521
+ if($this->addIssue('diskSpace', $level, 'diskSpace' . $level, 'diskSpace' . $level, "You have $spaceLeft" . "% disk space remaining", "You only have $spaceLeft" . "% of your disk space remaining. Please free up disk space or your website may stop serving requests.", array(
522
+ 'spaceLeft' => $spaceLeft ))){
523
+ wordfence::statusEnd($statusIDX, true);
524
+ } else {
525
+ wordfence::statusEnd($statusIDX, true);
526
+ }
527
}
528
private function scanDNSChanges(){
529
if(! function_exists('dns_get_record')){
530
$this->status(1, 'info', "Skipping DNS scan because this system does not support dns_get_record()");
531
return;
532
}
533
+ $statusIDX = wordfence::statusStart("Scanning DNS for unauthorized changes");
534
+ $haveIssues = false;
535
$home = get_home_url();
536
if(preg_match('/https?:\/\/([^\/]+)/i', $home, $matches)){
537
$host = strtolower($matches[1]);
554
$loggedCNAME = wfConfig::get('wf_dnsCNAME');
555
$dnsLogged = wfConfig::get('wf_dnsLogged', false);
556
if($dnsLogged && $loggedCNAME != $currentCNAME){
557
+ if($this->addIssue('dnsChange', 2, 'dnsChanges', 'dnsChanges', "Your DNS records have changed", "We have detected a change in the CNAME records of your DNS configuration for the domain $host. A CNAME record is an alias that is used to point a domain name to another domain name. For example foo.example.com can point to bar.example.com which then points to an IP address of 10.1.1.1. $msg", array(
558
'type' => 'CNAME',
559
'host' => $host,
560
'oldDNS' => $loggedCNAME,
561
'newDNS' => $currentCNAME
562
+ ))){
563
+ $haveIssues = true;
564
+ }
565
}
566
wfConfig::set('wf_dnsCNAME', $currentCNAME);
567
581
$dnsLogged = wfConfig::get('wf_dnsLogged', false);
582
$msg = "A change in your DNS records may indicate that a hacker has hacked into your DNS administration system and has pointed your email or website to their own server for malicious purposes. It could also indicate that your domain has expired. If you made this change yourself you can mark it 'resolved' and safely ignore it.";
583
if($dnsLogged && $loggedA != $currentA){
584
+ if($this->addIssue('dnsChange', 2, 'dnsChanges', 'dnsChanges', "Your DNS records have changed", "We have detected a change in the A records of your DNS configuration that may affect the domain $host. An A record is a record in DNS that points a domain name to an IP address. $msg", array(
585
'type' => 'A',
586
'host' => $host,
587
'oldDNS' => $loggedA,
588
'newDNS' => $currentA
589
+ ))){
590
+ $haveIssues = true;
591
+ }
592
}
593
wfConfig::set('wf_dnsA', $currentA);
594
608
$currentMX = implode(', ', $mxArr);
609
$loggedMX = wfConfig::get('wf_dnsMX');
610
if($dnsLogged && $loggedMX != $currentMX){
611
+ if($this->addIssue('dnsChange', 2, 'dnsChanges', 'dnsChanges', "Your DNS records have changed", "We have detected a change in the email server (MX) records of your DNS configuration for the domain $host. $msg", array(
612
'type' => 'MX',
613
'host' => $host,
614
'oldDNS' => $loggedMX,
615
'newDNS' => $currentMX
616
+ ))){
617
+ $haveIssues = true;
618
+ }
619
620
}
621
wfConfig::set('wf_dnsMX', $currentMX);
622
623
wfConfig::set('wf_dnsLogged', 1);
624
}
625
+ wordfence::statusEnd($statusIDX, $haveIssues);
626
}
627
private function scanOldVersions(){
628
+ $statusIDX = wordfence::statusStart("Scanning for old themes, plugins and core files");
629
if(! function_exists( 'get_preferred_from_update_core')){
630
require_once(ABSPATH . 'wp-admin/includes/update.php');
631
}
632
$cur = get_preferred_from_update_core();
633
+ $haveIssues = false;
634
if(isset( $cur->response ) && $cur->response == 'upgrade'){
635
+ if($this->addIssue('wfUpgrade', 1, 'wfUpgrade' . $cur->current, 'wfUpgrade' . $cur->current, "Your WordPress version is out of date", "WordPress version " . $cur->current . " is now available. Please upgrade immediately to get the latest security updates from WordPress.", array(
636
'currentVersion' => $this->wp_version,
637
'newVersion' => $cur->current
638
+ ))){
639
+ $haveIssues = true;
640
+ }
641
}
642
$update_plugins = get_site_transient( 'update_plugins' );
643
if(isset($update_plugins) && (! empty($update_plugins->response))){
650
$data = get_plugin_data($pluginFile);
651
$data['newVersion'] = $vals->new_version;
652
$key = 'wfPluginUpgrade' . ' ' . $plugin . ' ' . $data['newVersion'] . ' ' . $data['Version'];
653
+ if($this->addIssue('wfPluginUpgrade', 1, $key, $key, "The Plugin \"" . $data['Name'] . "\" needs an upgrade.", "You need to upgrade \"" . $data['Name'] . "\" to the newest version to ensure you have any security fixes the developer has released.", $data)){
654
+ $haveIssues = true;
655
+ }
656
}
657
}
658
}
673
'version' => $themeData['Version']
674
);
675
$key = 'wfThemeUpgrade' . ' ' . $theme . ' ' . $tData['version'] . ' ' . $tData['newVersion'];
676
+ if($this->addIssue('wfThemeUpgrade', 1, $key, $key, "The Theme \"" . $themeData['Name'] . "\" needs an upgrade.", "You need to upgrade \"" . $themeData['Name'] . "\" to the newest version to ensure you have any security fixes the developer has released.", $tData)){
677
+ $haveIssues = true;
678
+ }
679
}
680
}
681
682
}
683
}
684
+ wordfence::statusEnd($statusIDX, $haveIssues);
685
}
686
private function errorStop($msg){
687
$this->errorStopped = true;
692
wordfence::status($level, $type, $msg);
693
}
694
private function addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData){
695
+ return $this->i->addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData);
696
}
697
}
698
lib/wfSchema.php CHANGED
@@ -126,7 +126,8 @@ class wfSchema {
126
level tinyint UNSIGNED NOT NULL,
127
type char(5) NOT NULL,
128
msg varchar(255) NOT NULL,
129
- KEY k1(ctime)
130
) default charset=utf8",
131
'wfNet404s' => "(
132
sig binary(16) NOT NULL PRIMARY KEY,
126
level tinyint UNSIGNED NOT NULL,
127
type char(5) NOT NULL,
128
msg varchar(255) NOT NULL,
129
+ KEY k1(ctime),
130
+ KEY k2(type)
131
) default charset=utf8",
132
'wfNet404s' => "(
133
sig binary(16) NOT NULL PRIMARY KEY,
lib/wfUtils.php CHANGED
@@ -165,6 +165,38 @@ class wfUtils {
165
}
166
return 'unknown';
167
}
168
}
169
170
165
}
166
return 'unknown';
167
}
168
+ public static function longestLine($data){
169
+ $lines = preg_split('/[\r\n]+/', $data);
170
+ $max = 0;
171
+ foreach($lines as $line){
172
+ $len = strlen($line);
173
+ if($len > $max){
174
+ $max = $len;
175
+ }
176
+ }
177
+ return $max;
178
+ }
179
+ public static function longestNospace($data){
180
+ $lines = preg_split('/[\r\n\s\t]+/', $data);
181
+ $max = 0;
182
+ foreach($lines as $line){
183
+ $len = strlen($line);
184
+ if($len > $max){
185
+ $max = $len;
186
+ }
187
+ }
188
+ return $max;
189
+ }
190
+ public static function requestMaxMemory(){
191
+ if(wfConfig::get('maxMem', false) && (int) wfConfig::get('maxMem') > 0){
192
+ $maxMem = (int) wfConfig::get('maxMem');
193
+ } else {
194
+ $maxMem = 256;
195
+ }
196
+ if( function_exists('memory_get_usage') && ( (int) @ini_get('memory_limit') < $maxMem ) ){
197
+ @ini_set('memory_limit', $maxMem . 'M');
198
+ }
199
+ }
200
}
201
202
lib/wfViewResult.php CHANGED
@@ -15,6 +15,7 @@
15
if($isEmpty){
16
echo "File is empty.";
17
} else {
18
echo $geshi->parse_code();
19
}
20
?>
15
if($isEmpty){
16
echo "File is empty.";
17
} else {
18
+ wfUtils::requestMaxMemory();
19
echo $geshi->parse_code();
20
}
21
?>
lib/wordfenceClass.php CHANGED
@@ -22,13 +22,24 @@ class wordfence {
22
public static $newVisit = false;
23
private static $wfLog = false;
24
private static $hitID = 0;
25
public static function installPlugin(){
26
- if(is_multisite() && @$_GET['networkwide'] != 1){
27
- die("Sorry but you can't activate Wordfence on an individual site when WordPress MultiSite is enabled. Only the Network Admin can enable Wordfence and only they have access to administer Wordfence.");
28
- }
29
$schema = new wfSchema();
30
$schema->createAll(); //if not exists
31
wfConfig::setDefaults(); //If not set
32
if( !wp_next_scheduled( 'wordfence_daily_cron' )){
33
wp_schedule_event(time(), 'daily', 'wordfence_daily_cron');
34
}
@@ -36,6 +47,10 @@ class wordfence {
36
wp_schedule_event(time(), 'hourly', 'wordfence_daily_cron');
37
}
38
update_option('wordfenceActivated', 1);
39
}
40
public static function uninstallPlugin(){
41
update_option('wordfenceActivated', 0);
@@ -136,8 +151,12 @@ class wordfence {
136
$wfdb->query("delete from $p"."wfThrottleLog order by endTime asc limit %d", ($count3 - $maxRows));
137
}
138
$count4 = $wfdb->querySingle("select count(*) as cnt from $p"."wfStatus");
139
- if($count4 > 10000){ //max status events we keep. This determines how much gets emailed to us when users sends us a debug report.
140
- $wfdb->query("delete from $p"."wfStatus order by ctime asc limit %d", ($count4 - 10000));
141
}
142
143
}
@@ -651,8 +670,7 @@ class wordfence {
651
$issues = new wfIssues();
652
$jsonData = array(
653
'serverTime' => $serverTime,
654
- 'msg' => $wfdb->querySingle("select msg from $p"."wfStatus where level < 3 order by ctime desc limit 1"),
655
- 'currentScanID' => $issues->getScanTime()
656
);
657
$events = array();
658
$alsoGet = $_POST['alsoGet'];
@@ -667,16 +685,14 @@ class wordfence {
667
}
668
$jsonData['events'] = $events;
669
$jsonData['alsoGet'] = $alsoGet; //send it back so we don't load data if panel has changed
670
- if(wfConfig::get('wf_scanRunning') && time() - wfConfig::get('wf_scanRunning') < WORDFENCE_MAX_SCAN_TIME){
671
- $jsonData['running'] = '1';
672
- } else {
673
- $jsonData['running'] = '';
674
- }
675
return $jsonData;
676
}
677
- public static function ajax_loadActivityLog_callback(){
678
return array(
679
- 'events' => self::getLog()->getStatusEvents()
680
);
681
}
682
public static function ajax_deleteFile_callback(){
@@ -943,10 +959,10 @@ class wordfence {
943
return ($a['ctime'] < $b['ctime']) ? -1 : 1;
944
}
945
public static function wfFunc_view(){
946
- $localFile = realpath(ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $_GET['file']));
947
- if(strpos($localFile, ABSPATH) !== 0){
948
- echo "An invalid file was requested for viewing.";
949
- exit(0);
950
}
951
$lang = false;
952
$cont = @file_get_contents($localFile);
@@ -1048,7 +1064,7 @@ class wordfence {
1048
public static function admin_init(){
1049
if(! self::isAdmin()){ return; }
1050
1051
- foreach(array('activate', 'scan', 'sendActivityLog', 'restoreFile', 'deleteFile', 'removeExclusion', 'loadActivityLog', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues', 'reverseLookup', 'unlockOutIP', 'unblockIP', 'blockIP', 'loadStaticPanel', 'saveConfig', 'clearAllBlocked') as $func){
1052
add_action('wp_ajax_wordfence_' . $func, 'wordfence::ajaxReceiver');
1053
}
1054
wp_enqueue_style('wordfence-main-style', WP_PLUGIN_URL . '/wordfence/css/main.css');
@@ -1069,23 +1085,26 @@ class wordfence {
1069
1070
}
1071
public static function configure_warning(){
1072
- echo '<div id="wordfenceConfigWarning" class="updated fade"><p><strong>Wordfence is almost ready.</strong> You must <a href="admin.php?page=Wordfence">configure Wordfence to activate it</a>.</p></div>';
1073
}
1074
public static function admin_menus(){
1075
if(! self::isAdmin()){ return; }
1076
- if(wfConfig::get('apiKey')){
1077
- add_submenu_page("Wordfence", "Scan", "Scan", "activate_plugins", "Wordfence", 'wordfence::menu_scan');
1078
- add_menu_page('Wordfence', 'Wordfence', 'activate_plugins', 'Wordfence', 'wordfence::menu_scan', WP_PLUGIN_URL . '/wordfence/images/wordfence-logo-16x16.png', 'div');
1079
- if(wfConfig::get('liveTrafficEnabled')){
1080
- add_submenu_page("Wordfence", "Live Traffic", "Live Traffic", "activate_plugins", "WordfenceActivity", 'wordfence::menu_activity');
1081
}
1082
- add_submenu_page('Wordfence', 'Blocked IPs', 'Blocked IPs', 'activate_plugins', 'WordfenceBlockedIPs', 'wordfence::menu_blockedIPs');
1083
- add_submenu_page("Wordfence", "Options", "Options", "activate_plugins", "WordfenceSecOpt", 'wordfence::menu_options');
1084
- } else {
1085
- add_action('admin_notices', 'wordfence::configure_warning');
1086
- add_submenu_page("Wordfence", "Configure", "Configure", "activate_plugins", "Wordfence", 'wordfence::menu_config');
1087
- add_menu_page('Wordfence', 'Wordfence', 'activate_plugins', 'Wordfence', 'wordfence::menu_config', WP_PLUGIN_URL . '/wordfence/images/wordfence-logo-16x16.png', 'div');
1088
- }
1089
}
1090
public static function menu_options(){
1091
require 'menu_options.php';
@@ -1201,5 +1220,39 @@ class wordfence {
1201
}
1202
return self::$wfLog;
1203
}
1204
}
1205
?>
22
public static $newVisit = false;
23
private static $wfLog = false;
24
private static $hitID = 0;
25
+ private static $statusStartMsgs = array();
26
public static function installPlugin(){
27
$schema = new wfSchema();
28
$schema->createAll(); //if not exists
29
wfConfig::setDefaults(); //If not set
30
+
31
+ $api = new wfAPI('', wfUtils::getWPVersion());
32
+ $keyData = $api->call('get_anon_api_key');
33
+ if($api->errorMsg){
34
+ die("Error fetching free API key from Wordfence: " . $api->errorMsg);
35
+ }
36
+ if($keyData['ok'] && $keyData['apiKey']){
37
+ wfConfig::set('apiKey', $keyData['apiKey']);
38
+ } else {
39
+ die("Could not understand the response we received from the Wordfence servers when applying for a free API key.");
40
+ }
41
+
42
+
43
if( !wp_next_scheduled( 'wordfence_daily_cron' )){
44
wp_schedule_event(time(), 'daily', 'wordfence_daily_cron');
45
}
47
wp_schedule_event(time(), 'hourly', 'wordfence_daily_cron');
48
}
49
update_option('wordfenceActivated', 1);
50
+ $db = new wfDB();
51
+
52
+ //Upgrading from 1.5.6 or earlier needs:
53
+ $db->createKeyIfNotExists($prefix . 'wfStatus', 'level', 'k2');
54
}
55
public static function uninstallPlugin(){
56
update_option('wordfenceActivated', 0);
151
$wfdb->query("delete from $p"."wfThrottleLog order by endTime asc limit %d", ($count3 - $maxRows));
152
}
153
$count4 = $wfdb->querySingle("select count(*) as cnt from $p"."wfStatus");
154
+ if($count4 > 100000){ //max status events we keep. This determines how much gets emailed to us when users sends us a debug report.
155
+ $wfdb->query("delete from $p"."wfStatus where level != 10 order by ctime asc limit %d", ($count4 - 100000));
156
+ $count5 = $wfdb->querySingle("select count(*) as cnt from $p"."wfStatus where level=10");
157
+ if($count5 > 100){
158
+ $wfdb->query("delete from $p"."wfStatus where level = 10 order by ctime asc limit %d", ($count5 - 100) );
159
+ }
160
}
161
162
}
670
$issues = new wfIssues();
671
$jsonData = array(
672
'serverTime' => $serverTime,
673
+ 'msg' => $wfdb->querySingle("select msg from $p"."wfStatus where level < 3 order by ctime desc limit 1")
674
);
675
$events = array();
676
$alsoGet = $_POST['alsoGet'];
685
}
686
$jsonData['events'] = $events;
687
$jsonData['alsoGet'] = $alsoGet; //send it back so we don't load data if panel has changed
688
return $jsonData;
689
}
690
+ public static function ajax_activityLogUpdate_callback(){
691
+ $issues = new wfIssues();
692
return array(
693
+ 'ok' => 1,
694
+ 'items' => self::getLog()->getStatusEvents($_POST['lastctime']),
695
+ 'currentScanID' => $issues->getScanTime()
696
);
697
}
698
public static function ajax_deleteFile_callback(){
959
return ($a['ctime'] < $b['ctime']) ? -1 : 1;
960
}
961
public static function wfFunc_view(){
962
+ $localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $_GET['file']);
963
+ if(strpos($localFile, '..') !== false){
964
+ echo "Invalid file requested. (Relative paths not allowed)";
965
+ exit();
966
}
967
$lang = false;
968
$cont = @file_get_contents($localFile);
1064
public static function admin_init(){
1065
if(! self::isAdmin()){ return; }
1066
1067
+ foreach(array('activate', 'scan', 'sendActivityLog', 'restoreFile', 'deleteFile', 'removeExclusion', 'activityLogUpdate', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues', 'reverseLookup', 'unlockOutIP', 'unblockIP', 'blockIP', 'loadStaticPanel', 'saveConfig', 'clearAllBlocked') as $func){
1068
add_action('wp_ajax_wordfence_' . $func, 'wordfence::ajaxReceiver');
1069
}
1070
wp_enqueue_style('wordfence-main-style', WP_PLUGIN_URL . '/wordfence/css/main.css');
1085
1086
}
1087
public static function configure_warning(){
1088
+ if(! preg_match('/WordfenceSecOpt/', $_SERVER['REQUEST_URI'])){
1089
+ echo '<div id="wordfenceConfigWarning" class="updated fade"><p><strong>Please set up an email address to receive Wordfence security alerts. </strong> You can do this on the <a href="admin.php?page=WordfenceSecOpt">Wordfence Options Page</a>.</p></div>';
1090
+ }
1091
}
1092
public static function admin_menus(){
1093
if(! self::isAdmin()){ return; }
1094
+ if(! wfConfig::get('alertEmails')){
1095
+ if(wfUtils::isAdminPageMU()){
1096
+ add_action('network_admin_notices', 'wordfence::configure_warning');
1097
+ } else {
1098
+ add_action('admin_notices', 'wordfence::configure_warning');
1099
}
1100
+ }
1101
+ add_submenu_page("Wordfence", "Scan", "Scan", "activate_plugins", "Wordfence", 'wordfence::menu_scan');
1102
+ add_menu_page('Wordfence', 'Wordfence', 'activate_plugins', 'Wordfence', 'wordfence::menu_scan', WP_PLUGIN_URL . '/wordfence/images/wordfence-logo-16x16.png', 'div');
1103
+ if(wfConfig::get('liveTrafficEnabled')){
1104
+ add_submenu_page("Wordfence", "Live Traffic", "Live Traffic", "activate_plugins", "WordfenceActivity", 'wordfence::menu_activity');
1105
+ }
1106
+ add_submenu_page('Wordfence', 'Blocked IPs', 'Blocked IPs', 'activate_plugins', 'WordfenceBlockedIPs', 'wordfence::menu_blockedIPs');
1107
+ add_submenu_page("Wordfence", "Options", "Options", "activate_plugins", "WordfenceSecOpt", 'wordfence::menu_options');
1108
}
1109
public static function menu_options(){
1110
require 'menu_options.php';
1220
}
1221
return self::$wfLog;
1222
}
1223
+ public static function statusStart($msg){
1224
+ self::$statusStartMsgs[] = $msg;
1225
+ self::status(10, 'info', 'SUM_START:' . $msg);
1226
+ return sizeof(self::$statusStartMsgs) - 1;
1227
+ }
1228
+ public static function statusEnd($idx, $haveIssues){
1229
+ if($haveIssues){
1230
+ self::status(10, 'info', 'SUM_ENDBAD:' . self::$statusStartMsgs[$idx]);
1231
+ } else {
1232
+ self::status(10, 'info', 'SUM_ENDOK:' . self::$statusStartMsgs[$idx]);
1233
+ }
1234
+ self::$statusStartMsgs[$idx] = '';
1235
+ }
1236
+ public static function statusEndErr(){
1237
+ for($i = 0; $i < sizeof(self::$statusStartMsgs); $i++){
1238
+ if(empty(self::$statusStartMsgs[$i]) === false){
1239
+ self::status(10, 'info', 'SUM_ENDERR:' . self::$statusStartMsgs[$i]);
1240
+ self::$statusStartMsgs[$i] = '';
1241
+ }
1242
+ }
1243
+ }
1244
+ public static function statusDisabled($msg){
1245
+ if(wfConfig::get('isPaid') == 'free'){
1246
+ self::status(10, 'info', "SUM_PAIDONLY:" . $msg);
1247
+ } else {
1248
+ self::status(10, 'info', "SUM_DISABLED:" . $msg);
1249
+ }
1250
+ }
1251
+ public static function wfSchemaExists(){
1252
+ $db = new wfDB();
1253
+ global $wpdb; $prefix = $wpdb->prefix;
1254
+ $exists = $db->querySingle("show tables like '$prefix"."wfConfig'");
1255
+ return $exists ? true : false;
1256
+ }
1257
}
1258
?>
lib/wordfenceConstants.php CHANGED
@@ -1,9 +1,10 @@
1
<?php
2
- define('WORDFENCE_VERSION', 1.2);
3
define('WORDFENCE_API_URL', 'https://noc1.wordfence.com/');
4
define('WORDFENCE_MAX_SCAN_TIME', 600);
5
define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
6
define('WORDFENCE_MAX_IPLOC_AGE', 604800); //1 week
7
define('WORDFENCE_CRAWLER_VERIFY_CACHE_TIME', 604800);
8
define('WORDFENCE_REVERSE_LOOKUP_CACHE_TIME', 86400);
9
?>
1
<?php
2
+ define('WORDFENCE_VERSION', 1.3);
3
define('WORDFENCE_API_URL', 'https://noc1.wordfence.com/');
4
define('WORDFENCE_MAX_SCAN_TIME', 600);
5
define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
6
define('WORDFENCE_MAX_IPLOC_AGE', 604800); //1 week
7
define('WORDFENCE_CRAWLER_VERIFY_CACHE_TIME', 604800);
8
define('WORDFENCE_REVERSE_LOOKUP_CACHE_TIME', 86400);
9
+ define('WORDFENCE_MAX_FILE_SIZE_TO_PROCESS', 52428800); //50 megs
10
?>
lib/wordfenceHash.php CHANGED
@@ -58,13 +58,17 @@ class wordfenceHash {
58
}
59
}
60
private function processFile($file){
61
$wfHash = $this->wfHash($file, true);
62
if($wfHash){
63
- if(function_exists('memory_get_usage')){
64
- wordfence::status(2, 'info', "Examined file: $file (Mem:" . sprintf('%.1f', memory_get_usage(true) / (1024 * 1024)) . "M)");
65
- } else {
66
- wordfence::status(2, 'info', "Examined file: $file");
67
- }
68
$this->hashes[substr($file, $this->striplen)] = $wfHash;
69
//Now that we know we can open the file, lets update stats
70
if(preg_match('/\.(?:js|html|htm|css)#x2F;i', $file)){
58
}
59
}
60
private function processFile($file){
61
+ if(@filesize($file) > WORDFENCE_MAX_FILE_SIZE_TO_PROCESS){
62
+ wordfence::status(2, 'info', "Skipping file larger than 50 megs: $file");
63
+ return;
64
+ }
65
+ if(function_exists('memory_get_usage')){
66
+ wordfence::status(2, 'info', "Scanning: $file (Mem:" . sprintf('%.1f', memory_get_usage(true) / (1024 * 1024)) . "M)");
67
+ } else {
68
+ wordfence::status(2, 'info', "Scanning: $file");
69
+ }
70
$wfHash = $this->wfHash($file, true);
71
if($wfHash){
72
$this->hashes[substr($file, $this->striplen)] = $wfHash;
73
//Now that we know we can open the file, lets update stats
74
if(preg_match('/\.(?:js|html|htm|css)#x2F;i', $file)){
lib/wordfenceScanner.php CHANGED
@@ -49,9 +49,9 @@ class wordfenceScanner {
49
$fsize = $fsize . "B";
50
}
51
if(function_exists('memory_get_usage')){
52
- wordfence::status(2, 'info', "Currently scanning: $file (Size:$fsize Mem:" . sprintf('%.1f', memory_get_usage(true) / (1024 * 1024)) . "M)");
53
} else {
54
- wordfence::status(2, 'info', "Currently scanning: $file (Size: $fsize)");
55
}
56
$stime = microtime(true);
57
$fileSum = @md5_file($this->path . $file);
@@ -88,14 +88,15 @@ class wordfenceScanner {
88
));
89
break;
90
}
91
- if(strpos($data, 'eval') !== false && strpos($data, 'base64_decode') !== false && preg_match('/[a-zA-Z0-9\+\/\=]{200,}/', $data) ){
92
$this->addResult(array(
93
'type' => 'file',
94
'severity' => 1,
95
'ignoreP' => $this->path . $file,
96
'ignoreC' => $fileSum,
97
- 'shortMsg' => "This file may contain hidden executable code",
98
- 'longMsg' => "This file is a PHP executable file and contains the 'eval' and 'base64_decode' functions which are very common in backdoor programs and other malicious files. If you know about this file you can choose to ignore it to exclude it from future scans.",
99
'data' => array(
100
'file' => $file,
101
'canDiff' => false,
@@ -143,7 +144,8 @@ class wordfenceScanner {
143
'file' => $file,
144
'canDiff' => false,
145
'canFix' => false,
146
- 'canDelete' => true
147
)
148
));
149
} else if($result['badList'] == 'googpub-phish-shavar'){
@@ -158,7 +160,8 @@ class wordfenceScanner {
158
'file' => $file,
159
'canDiff' => false,
160
'canFix' => false,
161
- 'canDelete' => true
162
)
163
));
164
}
49
$fsize = $fsize . "B";
50
}
51
if(function_exists('memory_get_usage')){
52
+ wordfence::status(2, 'info', "Scanning contents: $file (Size:$fsize Mem:" . sprintf('%.1f', memory_get_usage(true) / (1024 * 1024)) . "M)");
53
} else {
54
+ wordfence::status(2, 'info', "Scanning contents: $file (Size: $fsize)");
55
}
56
$stime = microtime(true);
57
$fileSum = @md5_file($this->path . $file);
88
));
89
break;
90
}
91
+ $longestNospace = wfUtils::longestNospace($data);
92
+ if($longestNospace > 1000 && (strpos($data, 'eval') !== false || preg_match('/preg_replace\([^\(]+\/[a-z]*e/', $data)) ){
93
$this->addResult(array(
94
'type' => 'file',
95
'severity' => 1,
96
'ignoreP' => $this->path . $file,
97
'ignoreC' => $fileSum,
98
+ 'shortMsg' => "This file may contain malicious executable code",
99
+ 'longMsg' => "This file is a PHP executable file and contains a line $longestNospace characters long without spaces that may be encoded data along with functions that may be used to execute that code. If you know about this file you can choose to ignore it to exclude it from future scans.",
100
'data' => array(
101
'file' => $file,
102
'canDiff' => false,
144
'file' => $file,
145
'canDiff' => false,
146
'canFix' => false,
147
+ 'canDelete' => true,
148
+ 'gsb' => 'goog-malware-shavar'
149
)
150
));
151
} else if($result['badList'] == 'googpub-phish-shavar'){
160
'file' => $file,
161
'canDiff' => false,
162
'canFix' => false,
163
+ 'canDelete' => true,
164
+ 'gsb' => 'googpub-phish-shavar'
165
)
166
));
167
}
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: mmaunder
3
Tags: wordpress, security, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure
4
Requires at least: 3.3.1
5
Tested up to: 3.3.2
6
- Stable tag: 1.5.6
7
8
Wordfence Security is a free enterprise class security plugin that includes a firewall, virus scanning, real-time traffic with geolocation and more.
9
@@ -152,6 +152,21 @@ or a theme, because often these have been updated to fix a security hole.
152
5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
153
154
== Changelog ==
155
= 1.5.6 =
156
* Removed use of nonces and purely using 30 minute key for unlocking emails.
157
* Fixed bug that caused admin emails to not get emailed when requesting unlocking email.
3
Tags: wordpress, security, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure
4
Requires at least: 3.3.1
5
Tested up to: 3.3.2
6
+ Stable tag: 2.0.1
7
8
Wordfence Security is a free enterprise class security plugin that includes a firewall, virus scanning, real-time traffic with geolocation and more.
9
152
5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
153
154
== Changelog ==
155
+ = 2.0.1 =
156
+ * Improved scanning for specific attacks being used in the PHP-CGI vulnerability ( CVE-2012-1823)
157
+ * API keys no longer required. WF fetches a temporary anonymous API key for you on activation.
158
+ * Added real-time activity log on scan page.
159
+ * Added real-time summary updates on scan page.
160
+ * Fixed ability to view files that have symlinks in path.
161
+ * Added message to configure alert email address for multi-site and single site installs on activation.
162
+ * Disabled firewall rules by default because most sites don't need them.
163
+ * Disabled blocking of fake googlebots except for high security levels to prevent users who like to pretend they're googlebot from blocking themselves.
164
+ * Geshi the syntax highlighter now asks for more memory before running.
165
+ * Fixed bug that caused scan to hang on very large files.
166
+ * Added an index to wfStatus to make it faster for summary statuses
167
+ * Removed multisite pre-activation check to make activation more reliable on multisite installs.
168
+ * Better problem reporting if you trashed your Wordfence schema but the plugin is still installed.
169
+
170
= 1.5.6 =
171
* Removed use of nonces and purely using 30 minute key for unlocking emails.
172
* Fixed bug that caused admin emails to not get emailed when requesting unlocking email.
wfscan.php CHANGED
@@ -20,6 +20,9 @@ require_once('lib/wfScanEngine.php');
20
21
class wfScan {
22
public static function wfScanMain(){
23
if(! $_SERVER['HTTP_X_WORDFENCE_CRONKEY']){
24
self::errorExit("The Wordfence scanner did not receive the x_wordfence_cronkey secure header.");
25
}
@@ -43,15 +46,8 @@ class wfScan {
43
if($scanRunning && time() - $scanRunning < WORDFENCE_MAX_SCAN_TIME){
44
self::errorExit("There is already a scan running.");
45
}
46
- if(wfConfig::get('maxMem', false) && (int) wfConfig::get('maxMem') > 0){
47
- $maxMem = (int) wfConfig::get('maxMem');
48
- } else {
49
- $maxMem = 256;
50
- }
51
- if( function_exists('memory_get_usage') && ( (int) @ini_get('memory_limit') < $maxMem ) ){
52
- wordfence::status(1, 'info', "Requesting a maximum memory limit of $maxMem megabytes from PHP.");
53
- @ini_set('memory_limit', $maxMem . 'M');
54
- }
55
56
set_error_handler('wfScan::error_handler', E_ALL);
57
register_shutdown_function('wfScan::shutdown');
@@ -62,6 +58,7 @@ class wfScan {
62
wfConfig::set('wf_scanRunning', time());
63
$scan = new wfScanEngine();
64
$scan->go();
65
wfConfig::set('wf_scanRunning', '');
66
}
67
public static function obHandler($buf){
20
21
class wfScan {
22
public static function wfScanMain(){
23
+ if(! wordfence::wfSchemaExists()){
24
+ self::errorExit("Looks like the Wordfence database tables have been deleted. You can fix this by de-activating and re-activating the Wordfence plugin from your Plugins menu.");
25
+ }
26
if(! $_SERVER['HTTP_X_WORDFENCE_CRONKEY']){
27
self::errorExit("The Wordfence scanner did not receive the x_wordfence_cronkey secure header.");
28
}
46
if($scanRunning && time() - $scanRunning < WORDFENCE_MAX_SCAN_TIME){
47
self::errorExit("There is already a scan running.");
48
}
49
+ wordfence::status(10, 'info', "SUM_PREP:Preparing a new scan.");
50
+ wfUtils::requestMaxMemory();
51
52
set_error_handler('wfScan::error_handler', E_ALL);
53
register_shutdown_function('wfScan::shutdown');
58
wfConfig::set('wf_scanRunning', time());
59
$scan = new wfScanEngine();
60
$scan->go();
61
+
62
wfConfig::set('wf_scanRunning', '');
63
}
64
public static function obHandler($buf){
wordfence.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: Wordfence Security
4
Plugin URI: http://wordfence.com/
5
Description: Wordfence Security - Anti-virus and Firewall security plugin for WordPress
6
Author: Mark Maunder
7
- Version: 1.5.6
8
Author URI: http://wordfence.com/
9
*/
10
require_once('lib/wordfenceConstants.php');
4
Plugin URI: http://wordfence.com/
5
Description: Wordfence Security - Anti-virus and Firewall security plugin for WordPress
6
Author: Mark Maunder
7
+ Version: 2.0.1
8
Author URI: http://wordfence.com/
9
*/
10
require_once('lib/wordfenceConstants.php');