PluginProbe ʕ •ᴥ•ʔ
Wordfence Security – Firewall, Malware Scan, and Login Security / 7.3.2
Wordfence Security – Firewall, Malware Scan, and Login Security v7.3.2
8.2.2 8.2.1 8.2.0 3.7.1 3.7.2 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.8.8 3.8.9 3.9.1 4.0.1 4.0.2 4.0.3 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.0.9 5.1.1 5.1.2 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.2.1 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.1 5.3.10 5.3.11 5.3.12 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.3.8 5.3.9 6.0.1 6.0.10 6.0.11 6.0.12 6.0.14 6.0.15 6.0.16 6.0.17 6.0.18 6.0.19 6.0.2 6.0.20 6.0.21 6.0.22 6.0.23 6.0.24 6.0.25 6.0.3 6.0.4 6.0.5 6.0.6 6.0.7 6.0.8 6.0.9 6.1.1 6.1.10 6.1.11 6.1.12 6.1.14 6.1.15 6.1.16 6.1.17 6.1.2 6.1.3 6.1.4 6.1.5 6.1.6 6.1.7 6.1.8 6.1.9 6.2.0 6.2.1 6.2.10 6.2.2 6.2.3 6.2.4 6.2.5 6.2.6 6.2.7 6.2.8 6.2.9 6.3.0 6.3.1 6.3.10 6.3.11 6.3.12 6.3.14 6.3.15 6.3.16 6.3.17 6.3.18 6.3.19 6.3.2 6.3.20 6.3.21 6.3.22 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.3.8 6.3.9 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1.0 7.1.1 7.1.10 7.1.11 7.1.12 7.1.14 7.1.15 7.1.16 7.1.17 7.1.18 7.1.19 7.1.2 7.1.20 7.1.3 7.1.4 7.1.5 7.1.6 7.1.7 7.1.8 7.1.9 7.10.0 7.10.1 7.10.2 7.10.3 7.10.4 7.10.5 7.10.6 7.10.7 7.11.0 7.11.1 7.11.2 7.11.3 7.11.4 7.11.5 7.11.6 7.11.7 7.2.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3.1 7.3.2 7.3.3 7.3.4 7.3.5 7.3.6 7.4.0 7.4.1 7.4.10 7.4.11 7.4.12 7.4.14 7.4.2 7.4.3 trunk 7.4.4 1.1 7.4.5 1.2 7.4.6 1.3 7.4.7 1.3.1 7.4.8 1.3.2 7.4.9 1.3.3 7.5.0 1.4.2 7.5.1 1.4.3 7.5.10 1.4.4 7.5.11 1.4.5 7.5.2 1.4.6 7.5.3 1.4.7 7.5.4 1.4.8 7.5.5 1.5.1 7.5.6 1.5.2 7.5.7 1.5.3 7.5.8 1.5.4 7.5.9 1.5.5 7.6.0 1.5.6 7.6.1 2.0.1 7.6.2 2.0.2 7.7.0 2.0.3 7.7.1 2.0.5 7.8.0 2.0.6 7.8.1 2.0.7 7.8.2 2.1.0 7.9.0 2.1.1 7.9.1 2.1.2 7.9.2 2.1.3 7.9.3 2.1.4 8.0.0 2.1.5 8.0.1 3.0.2 8.0.2 3.0.3 8.0.3 3.0.4 8.0.4 3.0.5 8.0.5 3.0.6 8.1.0 3.0.7 8.1.1 3.0.8 8.1.2 3.0.9 8.1.3 3.1.0 8.1.4 3.1.1 v1.4.1 3.1.2 3.1.4 3.1.6 3.2.1 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.1 3.4.4 3.4.5 3.5.1 3.5.2 3.6.1 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.6.9
wordfence / lib / wfActivityReport.php
wordfence / lib Last commit date
Diff 8 years ago dashboard 7 years ago rest-api 7 years ago .htaccess 7 years ago Diff.php 14 years ago GeoLite2-Country.mmdb 7 years ago IPTraf.php 8 years ago IPTrafList.php 7 years ago WFLSPHP52Compatability.php 7 years ago compat.php 8 years ago conntest.php 7 years ago cronview.php 8 years ago dbview.php 8 years ago diffResult.php 8 years ago email_genericAlert.php 7 years ago email_newIssues.php 7 years ago email_unlockRequest.php 8 years ago email_unsubscribeRequest.php 7 years ago flags.php 7 years ago live_activity.php 8 years ago menu_dashboard.php 7 years ago menu_dashboard_options.php 7 years ago menu_firewall.php 7 years ago menu_firewall_blocking.php 7 years ago menu_firewall_blocking_options.php 8 years ago menu_firewall_waf.php 7 years ago menu_firewall_waf_options.php 7 years ago menu_options.php 7 years ago menu_scanner.php 7 years ago menu_scanner_credentials.php 8 years ago menu_scanner_options.php 8 years ago menu_support.php 7 years ago menu_tools.php 7 years ago menu_tools_diagnostic.php 7 years ago menu_tools_importExport.php 7 years ago menu_tools_livetraffic.php 7 years ago menu_tools_twoFactor.php 7 years ago menu_tools_whois.php 8 years ago menu_wordfence_central.php 7 years ago noc1.key 7 years ago sysinfo.php 8 years ago unknownFiles.php 8 years ago viewFullActivityLog.php 8 years ago wf503.php 7 years ago wfAPI.php 7 years ago wfActivityReport.php 7 years ago wfAdminNoticeQueue.php 8 years ago wfArray.php 7 years ago wfBrowscap.php 8 years ago wfBrowscapCache.php 7 years ago wfBulkCountries.php 7 years ago wfCache.php 9 years ago wfCentralAPI.php 7 years ago wfConfig.php 7 years ago wfCrawl.php 8 years ago wfCredentialsController.php 7 years ago wfCrypt.php 7 years ago wfDB.php 7 years ago wfDashboard.php 7 years ago wfDateLocalization.php 8 years ago wfDiagnostic.php 7 years ago wfDict.php 8 years ago wfDirectoryIterator.php 7 years ago wfHelperBin.php 11 years ago wfHelperString.php 11 years ago wfIPWhitelist.php 7 years ago wfImportExportController.php 7 years ago wfIssues.php 7 years ago wfJWT.php 7 years ago wfLockedOut.php 7 years ago wfLog.php 7 years ago wfMD5BloomFilter.php 8 years ago wfModuleController.php 7 years ago wfNotification.php 8 years ago wfOnboardingController.php 7 years ago wfPersistenceController.php 8 years ago wfRESTAPI.php 7 years ago wfScan.php 7 years ago wfScanEngine.php 7 years ago wfSchema.php 7 years ago wfStyle.php 7 years ago wfSupportController.php 7 years ago wfUnlockMsg.php 7 years ago wfUpdateCheck.php 8 years ago wfUtils.php 7 years ago wfVersionCheckController.php 8 years ago wfView.php 10 years ago wfViewResult.php 8 years ago wordfenceClass.php 7 years ago wordfenceConstants.php 7 years ago wordfenceHash.php 7 years ago wordfenceScanner.php 7 years ago wordfenceURLHoover.php 7 years ago
wfActivityReport.php
755 lines
1 <?php
2 if (defined('WORDFENCE_VERSION')) {
3
4 class wfActivityReport {
5 const BLOCK_TYPE_COMPLEX = 'complex';
6 const BLOCK_TYPE_BRUTE_FORCE = 'bruteforce';
7 const BLOCK_TYPE_BLACKLIST = 'blacklist';
8
9 /**
10 * @var int
11 */
12 private $limit = 10;
13
14 /**
15 * @var wpdb
16 */
17 private $db;
18
19 /**
20 * @param int $limit
21 */
22 public function __construct($limit = 10) {
23 global $wpdb;
24 $this->db = $wpdb;
25 $this->limit = $limit;
26 }
27
28 /**
29 * Schedule the activity report cron job.
30 */
31 public static function scheduleCronJob() {
32 self::clearCronJobs();
33
34 if (!wfConfig::get('email_summary_enabled', 1)) {
35 return;
36 }
37
38 if (is_main_site()) {
39 list(, $end_time) = wfActivityReport::getReportDateRange();
40 wp_schedule_single_event($end_time, 'wordfence_email_activity_report');
41 }
42 }
43
44 /**
45 * Remove the activity report cron job.
46 */
47 public static function disableCronJob() {
48 self::clearCronJobs();
49 }
50
51 public static function clearCronJobs() {
52 wp_clear_scheduled_hook('wordfence_email_activity_report');
53 }
54
55 /**
56 * Send out the report and reschedule the next report's cron job.
57 */
58 public static function executeCronJob() {
59 if (!wfConfig::get('email_summary_enabled', 1)) {
60 return;
61 }
62
63 $emails = wfConfig::getAlertEmails();
64 if (count($emails)) {
65 $report = new self();
66 $report->sendReportViaEmail($emails);
67 }
68 self::scheduleCronJob();
69 }
70
71 /**
72 * Output a compact version of the email for the WP dashboard.
73 */
74 public static function outputDashboardWidget() {
75 $report = new self(5);
76 echo $report->toWidgetView();
77 }
78
79 /**
80 * @return array
81 */
82 public static function getReportDateRange() {
83 $interval = wfConfig::get('email_summary_interval', 'weekly');
84 $offset = get_option('gmt_offset');
85 return self::_getReportDateRange($interval, $offset);
86 }
87
88 /**
89 * Testable code.
90 *
91 * @param string $interval
92 * @param int $offset
93 * @param null $time
94 * @return array
95 */
96 public static function _getReportDateRange($interval = 'weekly', $offset = 0, $time = null) {
97 if ($time === null) {
98 $time = time();
99 }
100
101 $day = (int) gmdate('w', $time);
102 $month = (int) gmdate("n", $time);
103 $day_of_month = (int) gmdate("j", $time);
104 $year = (int) gmdate("Y", $time);
105
106 $start_time = 0;
107 $end_time = 0;
108
109 switch ($interval) {
110 // Send a report 4pm every day
111 case 'daily':
112 $start_time = gmmktime(16, 0, 0, $month, $day_of_month, $year) + (-$offset * 60 * 60);
113 $end_time = $start_time + 86400;
114 break;
115
116 // Send a report 4pm every Monday
117 case 'weekly':
118 $start_time = gmmktime(16, 0, 0, $month, $day_of_month - $day + 1, $year) + (-$offset * 60 * 60);
119 $end_time = $start_time + (86400 * 7);
120 break;
121
122 // Send a report at 4pm the first of every month
123 case 'monthly':
124 $start_time = gmmktime(16, 0, 0, $month, 1, $year) + (-$offset * 60 * 60);
125 $end_time = gmmktime(16, 0, 0, $month + 1, 1, $year) + (-$offset * 60 * 60);
126 break;
127 }
128
129 return array($start_time, $end_time);
130 }
131
132 /**
133 * @return int
134 */
135 public static function getReportDateFrom() {
136 $interval = wfConfig::get('email_summary_interval', 'weekly');
137 return self::_getReportDateFrom($interval);
138 }
139
140 /**
141 * @param string $interval
142 * @param null $time
143 * @return int
144 */
145 public static function _getReportDateFrom($interval = 'weekly', $time = null) {
146 if ($time === null) {
147 $time = time();
148 }
149
150 // weekly
151 $from = $time - (86400 * 7);
152 switch ($interval) {
153 case 'daily':
154 $from = $time - 86400;
155 break;
156
157 // Send a report at 4pm the first of every month
158 case 'monthly':
159 $from = strtotime('-1 month', $time);
160 break;
161 }
162
163 return $from;
164 }
165
166 /**
167 * @return array
168 */
169 public function getFullReport() {
170 $start_time = microtime(true);
171 $remainder = 0;
172 $recent_firewall_activity = $this->getRecentFirewallActivity($this->limit, $remainder);
173 return array(
174 'top_ips_blocked' => $this->getTopIPsBlocked($this->limit),
175 'top_countries_blocked' => $this->getTopCountriesBlocked($this->limit),
176 'top_failed_logins' => $this->getTopFailedLogins($this->limit),
177 'recent_firewall_activity' => $recent_firewall_activity,
178 'omitted_firewall_activity'=> $remainder,
179 'recently_modified_files' => $this->getRecentFilesModified($this->limit),
180 'updates_needed' => $this->getUpdatesNeeded(),
181 'microseconds' => microtime(true) - $start_time,
182 );
183 }
184
185 /**
186 * @return array
187 */
188 public function getWidgetReport() {
189 $start_time = microtime(true);
190 return array(
191 'top_ips_blocked' => $this->getTopIPsBlocked($this->limit),
192 'top_countries_blocked' => $this->getTopCountriesBlocked($this->limit),
193 'top_failed_logins' => $this->getTopFailedLogins($this->limit),
194 'updates_needed' => $this->getUpdatesNeeded(),
195 'microseconds' => microtime(true) - $start_time,
196 );
197 }
198
199 public function getBlockedCount($maxAgeDays = null, $grouping = null) {
200 $maxAgeDays = (int) $maxAgeDays;
201 if ($maxAgeDays <= 0) {
202 $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 7 day)) / 86400)';
203 switch (wfConfig::get('email_summary_interval', 'weekly')) {
204 case 'daily':
205 $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 day)) / 86400)';
206 break;
207 case 'monthly':
208 $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 month)) / 86400)';
209 break;
210 }
211 }
212 else {
213 $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval ' . $maxAgeDays . ' day)) / 86400)';
214 }
215
216 //Possible values for blockType: throttle, manual, brute, fakegoogle, badpost, country, advanced, blacklist, waf
217 $groupingWHERE = '';
218 switch ($grouping) {
219 case self::BLOCK_TYPE_COMPLEX:
220 $groupingWHERE = ' AND blockType IN ("fakegoogle", "badpost", "country", "advanced", "waf")';
221 break;
222 case self::BLOCK_TYPE_BRUTE_FORCE:
223 $groupingWHERE = ' AND blockType IN ("throttle", "brute")';
224 break;
225 case self::BLOCK_TYPE_BLACKLIST:
226 $groupingWHERE = ' AND blockType IN ("blacklist", "manual")';
227 break;
228 }
229
230 $table_wfBlockedIPLog = wfDB::networkTable('wfBlockedIPLog');
231 $count = $this->db->get_var(<<<SQL
232 SELECT SUM(blockCount) as blockCount
233 FROM {$table_wfBlockedIPLog}
234 WHERE unixday >= {$interval}{$groupingWHERE}
235 SQL
236 );
237 return $count;
238 }
239
240 /**
241 * @param int $limit
242 * @return mixed
243 */
244 public function getTopIPsBlocked($limit = 10, $maxAgeDays = null) {
245 $maxAgeDays = (int) $maxAgeDays;
246 if ($maxAgeDays <= 0) {
247 $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 7 day)) / 86400)';
248 switch (wfConfig::get('email_summary_interval', 'weekly')) {
249 case 'daily':
250 $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 day)) / 86400)';
251 break;
252 case 'monthly':
253 $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 month)) / 86400)';
254 break;
255 }
256 }
257 else {
258 $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval ' . $maxAgeDays . ' day)) / 86400)';
259 }
260
261 $table_wfBlockedIPLog = wfDB::networkTable('wfBlockedIPLog');
262 $results = $this->db->get_results($this->db->prepare(<<<SQL
263 SELECT *,
264 SUM(blockCount) as blockCount
265 FROM {$table_wfBlockedIPLog}
266 WHERE unixday >= {$interval}
267 GROUP BY IP
268 ORDER BY blockCount DESC
269 LIMIT %d
270 SQL
271 , $limit));
272 if ($results) {
273 foreach ($results as &$row) {
274 $row->countryName = $this->getCountryNameByCode($row->countryCode);
275 }
276 }
277 return $results;
278 }
279
280 /**
281 * @param int $limit
282 * @return array
283 */
284 public function getTopCountriesBlocked($limit = 10, $maxAgeDays = null) {
285 $maxAgeDays = (int) $maxAgeDays;
286 if ($maxAgeDays <= 0) {
287 $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 7 day)) / 86400)';
288 switch (wfConfig::get('email_summary_interval', 'weekly')) {
289 case 'daily':
290 $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 day)) / 86400)';
291 break;
292 case 'monthly':
293 $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 month)) / 86400)';
294 break;
295 }
296 }
297 else {
298 $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval ' . $maxAgeDays . ' day)) / 86400)';
299 }
300
301 $table_wfBlockedIPLog = wfDB::networkTable('wfBlockedIPLog');
302 $results = $this->db->get_results($this->db->prepare(<<<SQL
303 SELECT *, COUNT(IP) as totalIPs, SUM(blockCount) as totalBlockCount
304 FROM (SELECT * FROM {$table_wfBlockedIPLog} WHERE unixday >= {$interval} GROUP BY IP) t
305 GROUP BY countryCode
306 ORDER BY totalBlockCount DESC
307 LIMIT %d
308 SQL
309 , $limit));
310 if ($results) {
311 foreach ($results as &$row) {
312 $row->countryName = $this->getCountryNameByCode($row->countryCode);
313 }
314 }
315 return $results;
316 }
317
318 /**
319 * @param int $limit
320 * @return mixed
321 */
322 public function getTopFailedLogins($limit = 10) {
323 $interval = 'UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 7 day))';
324 switch (wfConfig::get('email_summary_interval', 'weekly')) {
325 case 'daily':
326 $interval = 'UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 day))';
327 break;
328 case 'monthly':
329 $interval = 'UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 month))';
330 break;
331 }
332
333 $table_wfLogins = wfDB::networkTable('wfLogins');
334 $failedLogins = $this->db->get_results($this->db->prepare(<<<SQL
335 SELECT wfl.*,
336 sum(wfl.fail) as fail_count
337 FROM {$table_wfLogins} wfl
338 WHERE wfl.fail = 1
339 AND wfl.ctime > $interval
340 GROUP BY wfl.username
341 ORDER BY fail_count DESC
342 LIMIT %d
343 SQL
344 , $limit));
345
346 foreach ($failedLogins as &$login) {
347 $exists = $this->db->get_var($this->db->prepare(<<<SQL
348 SELECT !ISNULL(ID) FROM {$this->db->users} WHERE user_login = '%s' OR user_email = '%s'
349 SQL
350 , $login->username, $login->username));
351 $login->is_valid_user = $exists;
352 }
353
354 return $failedLogins;
355 }
356
357 /**
358 * Returns any updates needs or false if everything is up to date.
359 *
360 * @return array|bool
361 */
362 public function getUpdatesNeeded($useCachedValued = true) {
363 $update_check = new wfUpdateCheck();
364 $needs_update = $update_check->checkAllUpdates($useCachedValued)
365 ->needsAnyUpdates();
366 if ($needs_update) {
367 return array(
368 'core' => $update_check->getCoreUpdateVersion(),
369 'plugins' => $update_check->getPluginUpdates(),
370 'themes' => $update_check->getThemeUpdates(),
371 );
372 }
373 return false;
374 }
375
376 /**
377 * Returns list of firewall activity up to $limit number of entries.
378 *
379 * @param int $limit Max events to return in results
380 * @return array
381 */
382 public function getRecentFirewallActivity($limit = 300, &$remainder) {
383 $dateRange = wfActivityReport::getReportDateRange();
384 $recent_firewall_activity = new wfRecentFirewallActivity(null, max(604800, $dateRange[1] - $dateRange[0]));
385 $recent_firewall_activity->run();
386 return $recent_firewall_activity->mostRecentActivity($limit, $remainder);
387 }
388
389 /**
390 * Returns list of files modified within given timeframe.
391 *
392 * @todo Add option to configure the regex used to filter files allowed in this list.
393 * @todo Add option to exclude directories (such as cache directories).
394 *
395 * @param string $directory Search for files within this directory
396 * @param int $time_range One week
397 * @param int $limit Max files to return in results
398 * @param int $directory_limit Hard limit for number of files to search within a directory.
399 * @return array
400 */
401 public function getRecentFilesModified($limit = 300, $directory = ABSPATH, $time_range = 604800, $directory_limit = 20000) {
402 $recently_modified = new wfRecentlyModifiedFiles($directory);
403 $recently_modified->run();
404 return $recently_modified->mostRecentFiles($limit);
405 }
406
407 /**
408 * Remove entries older than a month in the IP log.
409 */
410 public function rotateIPLog() {
411 $table_wfBlockedIPLog = wfDB::networkTable('wfBlockedIPLog');
412 $this->db->query(<<<SQL
413 DELETE FROM {$table_wfBlockedIPLog}
414 WHERE unixday < FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 month)) / 86400)
415 SQL
416 );
417 }
418
419 /**
420 * @param mixed $ip_address
421 * @param int|null $unixday
422 */
423 public static function logBlockedIP($ip_address, $unixday = null, $type = null) {
424 /** @var wpdb $wpdb */
425 global $wpdb;
426
427 //Possible values for $type: throttle, manual, brute, fakegoogle, badpost, country, advanced, blacklist, waf
428
429 if (wfUtils::isValidIP($ip_address)) {
430 $ip_bin = wfUtils::inet_pton($ip_address);
431 } else {
432 $ip_bin = $ip_address;
433 $ip_address = wfUtils::inet_ntop($ip_bin);
434 }
435
436 $blocked_table = wfDB::networkTable('wfBlockedIPLog');
437
438 $unixday_insert = 'FLOOR(UNIX_TIMESTAMP() / 86400)';
439 if (is_int($unixday)) {
440 $unixday_insert = absint($unixday);
441 }
442
443 if ($type === null) {
444 $type = 'generic';
445 }
446
447 $country = wfUtils::IP2Country($ip_address);
448
449 $wpdb->query($wpdb->prepare(<<<SQL
450 INSERT INTO $blocked_table (IP, countryCode, blockCount, unixday, blockType)
451 VALUES (%s, %s, 1, $unixday_insert, %s)
452 ON DUPLICATE KEY UPDATE blockCount = blockCount + 1
453 SQL
454 , $ip_bin, $country, $type));
455 }
456
457 /**
458 * @param $code
459 * @return string
460 */
461 public function getCountryNameByCode($code) {
462 static $wfBulkCountries;
463 if (!isset($wfBulkCountries)) {
464 include 'wfBulkCountries.php';
465 }
466 return array_key_exists($code, $wfBulkCountries) ? $wfBulkCountries[$code] : "";
467 }
468
469 /**
470 * @return wfActivityReportView
471 */
472 public function toView() {
473 return new wfActivityReportView('reports/activity-report', $this->getFullReport() + array(
474 'limit' => $this->getLimit(),
475 ));
476 }
477
478 /**
479 * @return wfActivityReportView
480 */
481 public function toWidgetView() {
482 return new wfActivityReportView('reports/activity-report', $this->getWidgetReport() + array(
483 'limit' => $this->getLimit(),
484 ));
485 }
486
487 /**
488 * @return wfActivityReportView
489 */
490 public function toEmailView() {
491 return new wfActivityReportView('reports/activity-report-email-inline', $this->getFullReport());
492 }
493
494 /**
495 * @param $email_addresses string|array
496 * @return bool
497 */
498 public function sendReportViaEmail($email_addresses) {
499 $shortSiteURL = preg_replace('/^https?:\/\//i', '', site_url());
500
501 $content = $this->toEmailView()->__toString();
502
503 $success = true;
504 if (is_string($email_addresses)) { $email_addresses = explode(',', $email_addresses); }
505 foreach ($email_addresses as $email) {
506 $uniqueContent = str_replace('<!-- ##UNSUBSCRIBE## -->', sprintf(__('No longer an administrator for this site? <a href="%s" target="_blank">Click here</a> to stop receiving security alerts.', 'wordfence'), wfUtils::getSiteBaseURL() . '?_wfsf=removeAlertEmail&jwt=' . wfUtils::generateJWT(array('email' => $email))), $content);
507 if (!wp_mail($email, 'Wordfence activity for ' . date_i18n(get_option('date_format')) . ' on ' . $shortSiteURL, $uniqueContent, 'Content-Type: text/html')) {
508 $success = false;
509 }
510 }
511
512 return $success;
513 }
514
515 /**
516 * @return string
517 * @throws wfViewNotFoundException
518 */
519 public function render() {
520 return $this->toView()
521 ->render();
522 }
523
524 /**
525 * @return string
526 */
527 public function __toString() {
528 return $this->toView()
529 ->__toString();
530 }
531
532 /**
533 * @return int
534 */
535 public function getLimit() {
536 return $this->limit;
537 }
538
539 /**
540 * @param int $limit
541 */
542 public function setLimit($limit) {
543 $this->limit = $limit;
544 }
545 }
546
547 class wfRecentFirewallActivity {
548 private $activity = array();
549
550 private $max_fetch = 2000;
551 private $time_range = 604800;
552
553 public function __construct($max_fetch = null, $time_range = null) {
554 if ($max_fetch !== null) {
555 $this->max_fetch = $max_fetch;
556 }
557
558 if ($time_range !== null) {
559 $this->time_range = $time_range;
560 }
561 }
562
563 public function run() {
564 global $wpdb;
565
566 $table_wfHits = wfDB::networkTable('wfHits');
567 $results = $wpdb->get_results($wpdb->prepare(<<<SQL
568 SELECT attackLogTime, IP, URL, UA, actionDescription, actionData
569 FROM {$table_wfHits}
570 WHERE action = 'blocked:waf' AND attackLogTime > (UNIX_TIMESTAMP() - %d)
571 ORDER BY attackLogTime DESC
572 LIMIT %d
573 SQL
574 , $this->time_range, $this->max_fetch));
575 if ($results) {
576 foreach ($results as &$row) {
577 $actionData = json_decode($row->actionData, true);
578 if (!is_array($actionData) || !isset($actionData['paramKey']) || !isset($actionData['paramValue'])) {
579 continue;
580 }
581
582 if (isset($actionData['failedRules']) && $actionData['failedRules'] == 'blocked') {
583 $row->longDescription = "Blocked because the IP is blacklisted";
584 }
585 else {
586 $row->longDescription = "Blocked for " . $row->actionDescription;
587 }
588
589 $paramKey = base64_decode($actionData['paramKey']);
590 $paramValue = base64_decode($actionData['paramValue']);
591 if (strlen($paramValue) > 100) {
592 $paramValue = substr($paramValue, 0, 100) . chr(2026);
593 }
594
595 if (preg_match('/([a-z0-9_]+\.[a-z0-9_]+)(?:\[(.+?)\](.*))?/i', $paramKey, $matches)) {
596 switch ($matches[1]) {
597 case 'request.queryString':
598 $row->longDescription = "Blocked for " . $row->actionDescription . ' in query string: ' . $matches[2] . '=' . $paramValue;
599 break;
600 case 'request.body':
601 $row->longDescription = "Blocked for " . $row->actionDescription . ' in POST body: ' . $matches[2] . '=' . $paramValue;
602 break;
603 case 'request.cookie':
604 $row->longDescription = "Blocked for " . $row->actionDescription . ' in cookie: ' . $matches[2] . '=' . $paramValue;
605 break;
606 case 'request.fileNames':
607 $row->longDescription = "Blocked for a " . $row->actionDescription . ' in file: ' . $matches[2] . '=' . $paramValue;
608 break;
609 }
610 }
611 }
612 }
613
614 $this->activity = $results;
615 }
616
617 public function mostRecentActivity($limit, &$remainder = null) {
618 if ($remainder !== null) {
619 $remainder = count($this->activity) - $limit;
620 }
621 return array_slice($this->activity, 0, $limit);
622 }
623 }
624
625 class wfRecentlyModifiedFiles extends wfDirectoryIterator {
626
627 /**
628 * @var int
629 */
630 private $time_range = 604800;
631
632 /**
633 * @var array
634 */
635 private $files = array();
636 private $excluded_directories;
637
638 /**
639 * @param string $directory
640 * @param int $max_files_per_directory
641 * @param int $max_iterations
642 * @param int $time_range
643 */
644 public function __construct($directory = ABSPATH, $max_files_per_directory = 20000, $max_iterations = 250000, $time_range = 604800) {
645 parent::__construct($directory, $max_files_per_directory, $max_iterations);
646 $this->time_range = $time_range;
647 $excluded_directories = explode("\n", wfUtils::cleanupOneEntryPerLine(wfConfig::get('email_summary_excluded_directories', '')));
648 $this->excluded_directories = array();
649 foreach ($excluded_directories as $index => $path) {
650 if (($dir = realpath(ABSPATH . $path)) !== false) {
651 $this->excluded_directories[$dir] = 1;
652 }
653 }
654 }
655
656 /**
657 * @param $dir
658 * @return bool
659 */
660 protected function scan($dir) {
661 if (!array_key_exists(realpath($dir), $this->excluded_directories)) {
662 return parent::scan($dir);
663 }
664 return true;
665 }
666
667
668 /**
669 * @param string $file
670 */
671 public function file($file) {
672 $mtime = filemtime($file);
673 if (time() - $mtime < $this->time_range) {
674 $this->files[] = array($file, $mtime);
675 }
676 }
677
678 /**
679 * @param int $limit
680 * @return array
681 */
682 public function mostRecentFiles($limit = 300) {
683 usort($this->files, array(
684 $this,
685 '_sortMostRecentFiles',
686 ));
687 return array_slice($this->files, 0, $limit);
688 }
689
690 /**
691 * Sort in descending order.
692 *
693 * @param $a
694 * @param $b
695 * @return int
696 */
697 private function _sortMostRecentFiles($a, $b) {
698 if ($a[1] > $b[1]) {
699 return -1;
700 }
701 if ($a[1] < $b[1]) {
702 return 1;
703 }
704 return 0;
705 }
706
707 /**
708 * @return mixed
709 */
710 public function getFiles() {
711 return $this->files;
712 }
713 }
714
715
716 class wfActivityReportView extends wfView {
717
718 /**
719 * @param $file
720 * @return string
721 */
722 public function displayFile($file) {
723 $realPath = realpath($file);
724 if (stripos($realPath, ABSPATH) === 0) {
725 return substr($realPath, strlen(ABSPATH));
726 }
727 return $realPath;
728 }
729
730 /**
731 * @param null $unix_time
732 * @return string
733 */
734 public function modTime($unix_time = null) {
735 if ($unix_time === null) {
736 $unix_time = time();
737 }
738 return wfUtils::formatLocalTime('F j, Y g:ia', $unix_time);
739 }
740
741 public function attackTime($unix_time = null) {
742 if ($unix_time === null) {
743 $unix_time = time();
744 }
745 return wfUtils::formatLocalTime('F j, Y', $unix_time) . "<br>" . wfUtils::formatLocalTime('g:ia', $unix_time);
746 }
747
748 public function displayIP($binaryIP) {
749 $readableIP = wfUtils::inet_ntop($binaryIP);
750 $country = wfUtils::countryCode2Name(wfUtils::IP2Country($readableIP));
751 return "{$readableIP} (" . ($country ? $country : 'Unknown') . ")";
752 }
753 }
754 }
755