Sucuri Security – Auditing, Malware Scanner and Security Hardening - Version 1.8.7

Version Description

This version adds support for the latest version of WordPress. Introduces new features and fixes some bugs reported by the WordPress community as well as bugs found by our automated testing system.

=

Download this release

Release Info

Developer yorman
Plugin Icon 128x128 Sucuri Security – Auditing, Malware Scanner and Security Hardening
Version 1.8.7
Comparing to
See all releases

Code changes from version 1.8.6 to 1.8.7

inc/css/styles.css CHANGED
@@ -753,9 +753,6 @@ body.sucuri-security_page_sucuriscan_hardening {
753
.sucuriscan-tag-blue {
754
background-color: #3922f2;
755
}
756
- .sucuriscan-auditlog-response {
757
- margin-bottom: 30px;
758
- }
759
.sucuriscan-auditlog-date {
760
color: #808080;
761
padding: 30px 0;
@@ -816,6 +813,10 @@ body.sucuri-security_page_sucuriscan_hardening {
816
line-height: 32px;
817
margin-left: 10px;
818
}
819
.sucuriscan-hardening-option {
820
margin-bottom: 0;
821
}
753
.sucuriscan-tag-blue {
754
background-color: #3922f2;
755
}
756
.sucuriscan-auditlog-date {
757
color: #808080;
758
padding: 30px 0;
813
line-height: 32px;
814
margin-left: 10px;
815
}
816
+ .sucuriscan-pagination-panel,
817
+ .sucuriscan-auditlog-footer {
818
+ margin-top: 30px;
819
+ }
820
.sucuriscan-hardening-option {
821
margin-bottom: 0;
822
}
inc/tpl/auditlogs.html.tpl CHANGED
@@ -3,6 +3,18 @@
3
/* global jQuery */
4
/* jshint camelcase:false */
5
jQuery(function ($) {
6
var sucuriscanLoadAuditLogs = function (page, reset) {
7
var url = '%%SUCURI.AjaxURL.Dashboard%%';
8
@@ -14,7 +26,10 @@ jQuery(function ($) {
14
$('.sucuriscan-auditlog-response').html('<em>@@SUCURI.Loading@@</em>');
15
}
16
17
- $('.sucuriscan-pagination-loading').html('@@SUCURI.Loading@@');
18
19
$.post(url, {
20
action: 'sucuriscan_ajax',
@@ -23,14 +38,16 @@ jQuery(function ($) {
23
}, function (data) {
24
$('.sucuriscan-pagination-loading').html('');
25
26
if (data.content !== undefined) {
27
$('.sucuriscan-auditlog-response').html(data.content);
28
29
- if (data.selfhosting) {
30
- $('#sucuriscan-auditlog-selfhosting').removeClass('sucuriscan-hidden');
31
- }
32
-
33
if (data.pagination !== '') {
34
$('.sucuriscan-auditlog-table .sucuriscan-pagination').html(data.pagination);
35
}
36
} else if (typeof data === 'object') {
@@ -54,14 +71,14 @@ jQuery(function ($) {
54
sucuriscanLoadAuditLogs($(this).attr('data-page'));
55
});
56
57
- $('.sucuriscan-auditlog-table').on('click', '.sucuriscan-reset-auditlogs', function (event) {
58
event.preventDefault();
59
$.post('%%SUCURI.AjaxURL.Dashboard%%', {
60
action: 'sucuriscan_ajax',
61
sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
62
- form_action: 'reset_auditlogs_cache',
63
- }, function (data) {
64
- console.log(data);
65
sucuriscanLoadAuditLogs(0, true);
66
});
67
});
@@ -69,19 +86,11 @@ jQuery(function ($) {
69
</script>
70
71
<div class="sucuriscan-auditlog-table">
72
- <div id="sucuriscan-auditlog-selfhosting" class="sucuriscan-inline-alert-info sucuriscan-hidden">
73
- <p>@@SUCURI.SelfHostingFallback@@</p>
74
- </div>
75
-
76
<div class="sucuriscan-auditlog-response">
77
<em>@@SUCURI.Loading@@</em>
78
</div>
79
80
- <div>
81
- <small>@@SUCURI.AuditLogsCache@@ &mdash; <a href="#" class="sucuriscan-reset-auditlogs">@@SUCURI.Refresh@@</a></small>
82
- </div>
83
-
84
- <div class="sucuriscan-clearfix">
85
<ul class="sucuriscan-pull-left sucuriscan-pagination">
86
<!-- Populated via JavaScript -->
87
</ul>
@@ -90,4 +99,15 @@ jQuery(function ($) {
90
<!-- Populated via JavaScript -->
91
</div>
92
</div>
93
</div>
3
/* global jQuery */
4
/* jshint camelcase:false */
5
jQuery(function ($) {
6
+ var writeQueueSize = function (queueSize)
7
+ {
8
+ if (queueSize === 0) {
9
+ $('.sucuriscan-auditlogs-sendlogs-response').html('');
10
+ $('.sucuriscan-sendlogs-panel').addClass('sucuriscan-hidden');
11
+ } else {
12
+ var msg = '\x20@@SUCURI.AuditLogsQueue@@\x20&mdash;\x20';
13
+ $('.sucuriscan-auditlogs-sendlogs-response').html((queueSize).toString() + msg);
14
+ $('.sucuriscan-sendlogs-panel').removeClass('sucuriscan-hidden');
15
+ }
16
+ };
17
+
18
var sucuriscanLoadAuditLogs = function (page, reset) {
19
var url = '%%SUCURI.AjaxURL.Dashboard%%';
20
26
$('.sucuriscan-auditlog-response').html('<em>@@SUCURI.Loading@@</em>');
27
}
28
29
+ $('.sucuriscan-auditlog-status').html('');
30
+ $('.sucuriscan-pagination-loading').html('');
31
+ $('.sucuriscan-pagination-panel').addClass('sucuriscan-hidden');
32
+ $('.sucuriscan-auditlog-footer').addClass('sucuriscan-hidden');
33
34
$.post(url, {
35
action: 'sucuriscan_ajax',
38
}, function (data) {
39
$('.sucuriscan-pagination-loading').html('');
40
41
+ writeQueueSize(data.queueSize);
42
+
43
+ $('.sucuriscan-auditlog-status').html(data.status);
44
+ $('.sucuriscan-auditlog-footer').removeClass('sucuriscan-hidden');
45
+
46
if (data.content !== undefined) {
47
$('.sucuriscan-auditlog-response').html(data.content);
48
49
if (data.pagination !== '') {
50
+ $('.sucuriscan-pagination-panel').removeClass('sucuriscan-hidden');
51
$('.sucuriscan-auditlog-table .sucuriscan-pagination').html(data.pagination);
52
}
53
} else if (typeof data === 'object') {
71
sucuriscanLoadAuditLogs($(this).attr('data-page'));
72
});
73
74
+ $('.sucuriscan-auditlog-table').on('click', '.sucuriscan-auditlogs-sendlogs', function (event) {
75
event.preventDefault();
76
+ $('.sucuriscan-auditlogs-sendlogs-response').html('@@SUCURI.Loading@@');
77
$.post('%%SUCURI.AjaxURL.Dashboard%%', {
78
action: 'sucuriscan_ajax',
79
sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
80
+ form_action: 'auditlogs_send_logs',
81
+ }, function () {
82
sucuriscanLoadAuditLogs(0, true);
83
});
84
});
86
</script>
87
88
<div class="sucuriscan-auditlog-table">
89
<div class="sucuriscan-auditlog-response">
90
<em>@@SUCURI.Loading@@</em>
91
</div>
92
93
+ <div class="sucuriscan-clearfix sucuriscan-pagination-panel">
94
<ul class="sucuriscan-pull-left sucuriscan-pagination">
95
<!-- Populated via JavaScript -->
96
</ul>
99
<!-- Populated via JavaScript -->
100
</div>
101
</div>
102
+
103
+ <div class="sucuriscan-clearfix sucuriscan-auditlog-footer">
104
+ <div class="sucuriscan-pull-left sucuriscan-hidden sucuriscan-sendlogs-panel">
105
+ <small class="sucuriscan-auditlogs-sendlogs-response"></small>
106
+ <small><a href="#" class="sucuriscan-auditlogs-sendlogs">@@SUCURI.SendLogs@@</a></small>
107
+ </div>
108
+
109
+ <div class="sucuriscan-pull-right">
110
+ <small class="sucuriscan-auditlog-status"></small>
111
+ </div>
112
+ </div>
113
</div>
inc/tpl/lastlogins-loggedin.snippet.tpl CHANGED
@@ -8,7 +8,7 @@
8
9
<td class="sucuriscan-monospace">%%SUCURI.LoggedInUsers.Registered%%</td>
10
11
- <td class="sucuriscan-monospace">%%SUCURI.LoggedInUsers.RemoveAddr%%</td>
12
13
<td><a href="%%SUCURI.LoggedInUsers.UserURL%%" target="_blank">@@SUCURI.Edit@@</a></td>
14
</tr>
8
9
<td class="sucuriscan-monospace">%%SUCURI.LoggedInUsers.Registered%%</td>
10
11
+ <td class="sucuriscan-monospace">%%SUCURI.LoggedInUsers.RemoteAddr%%</td>
12
13
<td><a href="%%SUCURI.LoggedInUsers.UserURL%%" target="_blank">@@SUCURI.Edit@@</a></td>
14
</tr>
inc/tpl/notification-simple.html.tpl CHANGED
@@ -1,7 +1,7 @@
1
2
@@SUCURI.Event@@: %%SUCURI.Subject%%
3
@@SUCURI.Website@@: http://%%SUCURI.Website%%
4
- @@SUCURI.RemoveAddr@@: %%SUCURI.RemoteAddress%%
5
@@SUCURI.Datetime@@: %%SUCURI.Time%%
6
%%SUCURI.User%%
7
1
2
@@SUCURI.Event@@: %%SUCURI.Subject%%
3
@@SUCURI.Website@@: http://%%SUCURI.Website%%
4
+ @@SUCURI.RemoteAddr@@: %%SUCURI.RemoteAddress%%
5
@@SUCURI.Datetime@@: %%SUCURI.Time%%
6
%%SUCURI.User%%
7
inc/tpl/settings-apiservice-timeout.html.tpl DELETED
@@ -1,21 +0,0 @@
1
-
2
- <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.APITimeout@@</h3>
4
-
5
- <div class="inside">
6
- <p>@@SUCURI.APITimeoutInfo@@</p>
7
-
8
- <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
9
- <span>@@SUCURI.APITimeoutValue@@</span>
10
- </div>
11
-
12
- <form action="%%SUCURI.URL.Settings%%#apiservice" method="post">
13
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
14
- <fieldset class="sucuriscan-clearfix">
15
- <label>@@SUCURI.APITimeoutLabel@@</label>
16
- <input type="text" name="sucuriscan_request_timeout" />
17
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
18
- </fieldset>
19
- </form>
20
- </div>
21
- </div>
inc/tpl/settings-general-commentmonitor.html.tpl DELETED
@@ -1,18 +0,0 @@
1
-
2
- <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.CommentMonitor@@</h3>
4
-
5
- <div class="inside">
6
- <p>@@SUCURI.CommentMonitorInfo@@</p>
7
-
8
- <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
9
- <span>@@SUCURI.CommentMonitor@@ &mdash; %%SUCURI.CommentMonitorStatus%%</span>
10
-
11
- <form action="%%SUCURI.URL.Settings%%" method="post">
12
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
13
- <input type="hidden" name="sucuriscan_comment_monitor" value="%%SUCURI.CommentMonitorSwitchValue%%" />
14
- <button type="submit" class="button button-primary">%%SUCURI.CommentMonitorSwitchText%%</button>
15
- </form>
16
- </div>
17
- </div>
18
- </div>
inc/tpl/{settings-general-cronjobs.html.tpl → settings-scanner-cronjobs.html.tpl} RENAMED
@@ -3,9 +3,15 @@
3
<h3 class="sucuriscan-title">@@SUCURI.Cronjobs@@</h3>
4
5
<div class="inside">
6
<p>@@SUCURI.CronjobsInfo@@</p>
7
8
- <form action="%%SUCURI.URL.Settings%%#general" method="post">
9
<input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
11
<table class="wp-list-table widefat sucuriscan-table sucuriscan-wpcron-list">
3
<h3 class="sucuriscan-title">@@SUCURI.Cronjobs@@</h3>
4
5
<div class="inside">
6
+ <p>@@SUCURI.ScannerDescription@@</p>
7
+
8
+ <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.NoSPL.Visibility%%">
9
+ <p>@@SUCURI.ScannerWithoutSPL@@</p>
10
+ </div>
11
+
12
<p>@@SUCURI.CronjobsInfo@@</p>
13
14
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
15
<input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
16
17
<table class="wp-list-table widefat sucuriscan-table sucuriscan-wpcron-list">
inc/tpl/{settings-general-cronjobs.snippet.tpl → settings-scanner-cronjobs.snippet.tpl} RENAMED
@@ -8,7 +8,7 @@
8
9
<td>%%SUCURI.Cronjob.Schedule%%</td>
10
11
- <td>%%SUCURI.Cronjob.NextTime%%</td>
12
13
<td>%%SUCURI.Cronjob.Arguments%%</td>
14
</tr>
8
9
<td>%%SUCURI.Cronjob.Schedule%%</td>
10
11
+ <td>%%SUCURI.Cronjob.NextTime%% <em>(%%SUCURI.Cronjob.NextTimeHuman%%)</em></td>
12
13
<td>%%SUCURI.Cronjob.Arguments%%</td>
14
</tr>
inc/tpl/settings-scanner-options.html.tpl DELETED
@@ -1,24 +0,0 @@
1
-
2
- <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.ScannerTitle@@</h3>
4
-
5
- <div class="inside">
6
- <p>@@SUCURI.ScannerDescription@@</p>
7
-
8
- <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.NoSPL.Visibility%%">
9
- <p>@@SUCURI.ScannerWithoutSPL@@</p>
10
- </div>
11
-
12
- <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
13
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
14
-
15
- <fieldset class="sucuriscan-clearfix">
16
- <label>@@SUCURI.ScannerFrequency@@</label>
17
- <select name="sucuriscan_scan_frequency">
18
- %%%SUCURI.ScanningFrequencyOptions%%%
19
- </select>
20
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
21
- </fieldset>
22
- </form>
23
- </div>
24
- </div>
inc/tpl/settings.html.tpl CHANGED
@@ -18,14 +18,10 @@
18
19
%%%SUCURI.Settings.General.SelfHosting%%%
20
21
- %%%SUCURI.Settings.General.Cronjobs%%%
22
-
23
%%%SUCURI.Settings.General.ReverseProxy%%%
24
25
%%%SUCURI.Settings.General.IPDiscoverer%%%
26
27
- %%%SUCURI.Settings.General.CommentMonitor%%%
28
-
29
%%%SUCURI.Settings.General.AuditLogStats%%%
30
31
%%%SUCURI.Settings.General.ImportExport%%%
@@ -34,7 +30,7 @@
34
</div>
35
36
<div id="sucuriscan-tabs-scanner">
37
- %%%SUCURI.Settings.Scanner.Options%%%
38
39
%%%SUCURI.Settings.Scanner.IntegrityDiffUtility%%%
40
@@ -106,8 +102,6 @@
106
<div id="sucuriscan-tabs-apiservice">
107
%%%SUCURI.Settings.APIService.Status%%%
108
109
- %%%SUCURI.Settings.APIService.Timeout%%%
110
-
111
%%%SUCURI.Settings.APIService.Proxy%%%
112
</div>
113
18
19
%%%SUCURI.Settings.General.SelfHosting%%%
20
21
%%%SUCURI.Settings.General.ReverseProxy%%%
22
23
%%%SUCURI.Settings.General.IPDiscoverer%%%
24
25
%%%SUCURI.Settings.General.AuditLogStats%%%
26
27
%%%SUCURI.Settings.General.ImportExport%%%
30
</div>
31
32
<div id="sucuriscan-tabs-scanner">
33
+ %%%SUCURI.Settings.Scanner.Cronjobs%%%
34
35
%%%SUCURI.Settings.Scanner.IntegrityDiffUtility%%%
36
102
<div id="sucuriscan-tabs-apiservice">
103
%%%SUCURI.Settings.APIService.Status%%%
104
105
%%%SUCURI.Settings.APIService.Proxy%%%
106
</div>
107
languages/sucuri-scanner-en_US.mo CHANGED
Binary file
languages/sucuri-scanner-en_US.po CHANGED
@@ -194,6 +194,15 @@ msgstr "This data is cached for %%SUCURI.AuditLogs.Lifetime%% seconds"
194
msgid "Refresh"
195
msgstr "Refresh"
196
197
msgid "UnsupportedWordPress"
198
msgstr "WordPress version is not supported."
199
@@ -764,12 +773,6 @@ msgstr "The post-type is invalid or it may be already ignored."
764
msgid "APIServiceChanged"
765
msgstr "The status of the API service has been changed"
766
767
- msgid "HTTPTimeoutChange"
768
- msgstr "The timeout for the HTTP requests has been changed"
769
-
770
- msgid "HTTPTimeoutFailure"
771
- msgstr "API request timeout in seconds is too high."
772
-
773
msgid "PluginResetSuccess"
774
msgstr "Local security logs, hardening and settings were deleted"
775
@@ -792,7 +795,7 @@ msgid "Cronjobs"
792
msgstr "Scheduled Tasks"
793
794
msgid "CronjobsInfo"
795
- msgstr "<b>Scheduled Tasks</b> are rules registered in your database by a plugin, theme, or the base system itself; they are used to automatically execute actions defined in the code every certain amount of time. A good use of these rules is to generate backup files of your site, execute a security scanner, or remove unused elements like drafts."
796
797
msgid "CronjobRunNow"
798
msgstr "Execute Now (in +10 seconds)"
@@ -1016,9 +1019,6 @@ msgstr "Newest WordPress"
1016
msgid "NoUpdates"
1017
msgstr "There are no updates available."
1018
1019
- msgid "ScannerFreqStatus"
1020
- msgstr "The file system scanner frequency has been changed"
1021
-
1022
msgid "SiteClean"
1023
msgstr "Site is Clean"
1024
@@ -1346,14 +1346,8 @@ msgstr "Your website has no <code>.htaccess</code> file or it was not found in t
1346
msgid "HTAccessStandard"
1347
msgstr "The main <code>.htaccess</code> file in your site has the standard rules for a WordPress installation. You can customize it to improve the performance and change the behaviour of the redirections for pages and posts in your site. To get more information visit the official documentation at <a target=\"_blank\" rel=\"noopener\" href=\"https://codex.wordpress.org/Using_Permalinks#Creating_and_editing_.28.htaccess.29\"> Codex WordPress - Creating and editing (.htaccess)</a>"
1348
1349
- msgid "ScannerTitle"
1350
- msgstr "Scanner Settings"
1351
-
1352
- msgid "ScannerFrequency"
1353
- msgstr "Scanning Frequency"
1354
-
1355
msgid "ScannerDescription"
1356
- msgstr "The plugin scans your entire website looking for changes which are later reported via the API in the audit logs page. This scanner runs twice-daily by default but you can change the frequency to meet your own requirements. Notice that scanning your project files too frequently will affect the performance of your website. Be sure to have enough server resources before changing this option. The memory limit and maximum execution time are two of the PHP options that your server will set to stop your website from consuming too much resources."
1357
1358
msgid "ScannerWithoutSPL"
1359
msgstr "The scanner uses the <a href=\"http://php.net/manual/en/class.splfileobject.php\" target=\"_blank\" rel=\"noopener\">PHP SPL library</a> and the <a target=\"_blank\" href=\"http://php.net/manual/en/class.filesystemiterator.php\" rel=\"noopener\">Filesystem Iterator</a> class to scan the directory tree where your website is located in the server. This library is only available on PHP 5 >= 5.3.0 &mdash; OR &mdash; PHP 7; if you have an older version of PHP the plugin will not work as expected. Please ask your hosting provider to advice you on this matter."
@@ -1466,15 +1460,6 @@ msgstr "Data Storage"
1466
msgid "DataStorageInfo"
1467
msgstr "This is the directory where the plugin will store the security logs, the list of files marked as fixed in the core integrity tool, the cache for the malware scanner and 3rd-party plugin metadata. The plugin requires write permissions in this directory as well as the files contained in it. If you prefer to keep these files in a non-public directory <em>(one level up the document root) </em> please define a constant in the <em>\"wp-config.php\"</em> file named <em>\"SUCURI_DATA_STORAGE\"</em> with the absolute path to the new directory."
1468
1469
- msgid "CommentMonitor"
1470
- msgstr "Comment Monitor"
1471
-
1472
- msgid "CommentMonitorStatus"
1473
- msgstr "The status of the comment monitor has been changed"
1474
-
1475
- msgid "CommentMonitorInfo"
1476
- msgstr "User comments are the main source of spam in WordPress websites, this option enables the monitoring of data sent via the comment forms loaded in every page and post. We also use this information in an anonymous way to generate <a target=\"_blank\" href=\"https://sucuri.net/security-reports/brute-force/\">statistics</a> of usage that help us improve our service."
1477
-
1478
msgid "LogsReport"
1479
msgstr "Audit Log Statistics"
1480
194
msgid "Refresh"
195
msgstr "Refresh"
196
197
+ msgid "AuditLogsNoAPI"
198
+ msgstr "The API service is not available; logs are coming from the local server"
199
+
200
+ msgid "AuditLogsQueue"
201
+ msgstr "logs in the queue"
202
+
203
+ msgid "SendLogs"
204
+ msgstr "Send Logs"
205
+
206
msgid "UnsupportedWordPress"
207
msgstr "WordPress version is not supported."
208
773
msgid "APIServiceChanged"
774
msgstr "The status of the API service has been changed"
775
776
msgid "PluginResetSuccess"
777
msgstr "Local security logs, hardening and settings were deleted"
778
795
msgstr "Scheduled Tasks"
796
797
msgid "CronjobsInfo"
798
+ msgstr "Scheduled tasks are rules registered in your database by a plugin, theme, or the base system itself; they are used to automatically execute actions defined in the code every certain amount of time. A good use of these rules is to generate backup files of your site, execute a security scanner, or remove unused elements like drafts. <b>Note:</b> Scheduled tasks can be re-installed by any plugin/theme automatically, consider to deactivate the plugin entirely if you want to get rid of the scheduled tasks."
799
800
msgid "CronjobRunNow"
801
msgstr "Execute Now (in +10 seconds)"
1019
msgid "NoUpdates"
1020
msgstr "There are no updates available."
1021
1022
msgid "SiteClean"
1023
msgstr "Site is Clean"
1024
1346
msgid "HTAccessStandard"
1347
msgstr "The main <code>.htaccess</code> file in your site has the standard rules for a WordPress installation. You can customize it to improve the performance and change the behaviour of the redirections for pages and posts in your site. To get more information visit the official documentation at <a target=\"_blank\" rel=\"noopener\" href=\"https://codex.wordpress.org/Using_Permalinks#Creating_and_editing_.28.htaccess.29\"> Codex WordPress - Creating and editing (.htaccess)</a>"
1348
1349
msgid "ScannerDescription"
1350
+ msgstr "The plugin scans your entire website looking for changes which are later reported via the API in the audit logs page. This scanner runs daily but you can change the frequency to meet your own requirements. Notice that scanning your project files too frequently will affect the performance of your website. Be sure to have enough server resources before changing this option. The memory limit and maximum execution time are two of the PHP options that your server will set to stop your website from consuming too much resources."
1351
1352
msgid "ScannerWithoutSPL"
1353
msgstr "The scanner uses the <a href=\"http://php.net/manual/en/class.splfileobject.php\" target=\"_blank\" rel=\"noopener\">PHP SPL library</a> and the <a target=\"_blank\" href=\"http://php.net/manual/en/class.filesystemiterator.php\" rel=\"noopener\">Filesystem Iterator</a> class to scan the directory tree where your website is located in the server. This library is only available on PHP 5 >= 5.3.0 &mdash; OR &mdash; PHP 7; if you have an older version of PHP the plugin will not work as expected. Please ask your hosting provider to advice you on this matter."
1460
msgid "DataStorageInfo"
1461
msgstr "This is the directory where the plugin will store the security logs, the list of files marked as fixed in the core integrity tool, the cache for the malware scanner and 3rd-party plugin metadata. The plugin requires write permissions in this directory as well as the files contained in it. If you prefer to keep these files in a non-public directory <em>(one level up the document root) </em> please define a constant in the <em>\"wp-config.php\"</em> file named <em>\"SUCURI_DATA_STORAGE\"</em> with the absolute path to the new directory."
1462
1463
msgid "LogsReport"
1464
msgstr "Audit Log Statistics"
1465
languages/sucuri-scanner-es_ES.mo CHANGED
Binary file
languages/sucuri-scanner-es_ES.po CHANGED
@@ -194,6 +194,15 @@ msgstr "Los registros son guardados por %%SUCURI.AuditLogs.Lifetime%% segundos"
194
msgid "Refresh"
195
msgstr "Actualizar"
196
197
msgid "UnsupportedWordPress"
198
msgstr "esta versión de WordPress no tiene soporte."
199
@@ -764,12 +773,6 @@ msgstr "Este tipo de publicaciones no es válido o ya han sido ignoradas"
764
msgid "APIServiceChanged"
765
msgstr "El estado de comunicación con la API ha sido cambiado"
766
767
- msgid "HTTPTimeoutChange"
768
- msgstr "El tiempo de espera para las conexiones a través de HTTP ha sido cambiado"
769
-
770
- msgid "HTTPTimeoutFailure"
771
- msgstr "El tiempo de espera para las conexiones a través de HTTP es muy alto"
772
-
773
msgid "PluginResetSuccess"
774
msgstr "Los registros de seguridad, configuraciones y cambios adicionales hechos por el plugin han sido eliminados"
775
@@ -792,7 +795,7 @@ msgid "Cronjobs"
792
msgstr "Acciones Automatizadas"
793
794
msgid "CronjobsInfo"
795
- msgstr "<b>Acciones Automatizadas</b> son reglas registradas en su base de datos por un plugin, plantilla o WordPress; estas reglas son usadas para ejecutar automáticamente acciones definidas en el código con un intervalo de tiempo definido. Un buen uso de estas reglas es la generación de copias de seguridad, la ejecución de escáneres de seguridad o la remoción de elementos innecesarios de la base de datos como borradores de publicaciones."
796
797
msgid "CronjobRunNow"
798
msgstr "Ejecutar Ahora (en +10 segundos)"
@@ -1016,9 +1019,6 @@ msgstr "Última Versión"
1016
msgid "NoUpdates"
1017
msgstr "No existen actualizaciones."
1018
1019
- msgid "ScannerFreqStatus"
1020
- msgstr "La frecuencia para la ejecución del escáner de archivos ha sido cambiada"
1021
-
1022
msgid "SiteClean"
1023
msgstr "Sitio Limpio"
1024
@@ -1346,14 +1346,8 @@ msgstr "El sitio web no tiene un archivo <code>.htaccess</code> o no se encuentr
1346
msgid "HTAccessStandard"
1347
msgstr "El archivo <code>.htaccess</code> principal contiene las reglas estándares de WordPress generadas durante la instalación del sitio web. Usted puede modificar estas reglas para aumentar el rendimiento del servidor o para generar redirecciones a otras páginas. Para obtener más información al respecto visite la página oficial con la documentación en <a target=\"_blank\" rel=\"noopener\" href=\"https://codex.wordpress.org/Using_Permalinks#Creating_and_editing_.28.htaccess.29\"> Codex WordPress - Creating and editing (.htaccess)</a>"
1348
1349
- msgid "ScannerTitle"
1350
- msgstr "Ajustes para el Escáner"
1351
-
1352
- msgid "ScannerFrequency"
1353
- msgstr "Frecuencia de Ejecución"
1354
-
1355
msgid "ScannerDescription"
1356
- msgstr "El plugin escanea su sitio web en busca de cambios que son eventualmente reportados a la API y visualizados a través de los registros de seguridad. Este escáner es ejecutado automáticamente cada doce horas pero usted puede cambiar la frecuencia dependiendo de sus necesidades. Tenga en cuenta que si su sitio contiene una gran cantidad de archivos es posible que el escáner sea bloqueado por su proveedor de hosting si este excede el límite de ejecución que ellos hayan impuesto sobre su cuenta."
1357
1358
msgid "ScannerWithoutSPL"
1359
msgstr "El escáner hace uso de las librerías <a href=\"http://php.net/manual/en/class.splfileobject.php\" target=\"_blank\" rel=\"noopener\">SPL</a> y <a target=\"_blank\" href=\"http://php.net/manual/en/class.filesystemiterator.php\" rel=\"noopener\">Filesystem Iterator</a> de PHP para escanear el árbol de directorios de su sitio web. Esta librería solo está disponible en PHP 5 >= 5.3.0 &mdash; y &mdash; PHP 7; si su servidor hace uso de una versión antigua el plugin no funcionará como se espera. Por favor, contacte a su proveedor de hosting para obtener ayuda al respecto."
@@ -1466,15 +1460,6 @@ msgstr "Almacenamiento de Datos"
1466
msgid "DataStorageInfo"
1467
msgstr "Este es el directorio donde el plugin guardará los registros de seguridad, el archivo con las configuraciones, la cache y datos adicionales de otras herramientas. El plugin require de permisos de escritura en este directorio así como en los archivos contenidos allí. Si usted prefiere mantener estos archivos en un directorio diferente al especificado, por favor defina una constante llamada <em>\"SUCURI_DATA_STORAGE\"</em> en el archivo de configuración de WordPress con la ruta absoluta del nuevo directorio."
1468
1469
- msgid "CommentMonitor"
1470
- msgstr "Monitor de Comentarios"
1471
-
1472
- msgid "CommentMonitorStatus"
1473
- msgstr "El estado del monitor de comentarios ha sido cambiado"
1474
-
1475
- msgid "CommentMonitorInfo"
1476
- msgstr "Los comentarios de usuario son el origen principal de spam en sitios que usan WordPress, esta opción activa el monitoreo de los datos enviados a través de formularios de comentario disponibles en cada página. Nosotros también usamos esta información de forma anónima para generar <a target=\"_blank\" href=\"https://sucuri.net/security-reports/brute-force/\">estadísticas</a> de uso para ayudarnos a mejorar el servicio."
1477
-
1478
msgid "LogsReport"
1479
msgstr "Estadísticas De Seguridad"
1480
194
msgid "Refresh"
195
msgstr "Actualizar"
196
197
+ msgid "AuditLogsNoAPI"
198
+ msgstr "La API no está disponible; la información viene del servidor local"
199
+
200
+ msgid "AuditLogsQueue"
201
+ msgstr "registros en cola"
202
+
203
+ msgid "SendLogs"
204
+ msgstr "Enviar"
205
+
206
msgid "UnsupportedWordPress"
207
msgstr "esta versión de WordPress no tiene soporte."
208
773
msgid "APIServiceChanged"
774
msgstr "El estado de comunicación con la API ha sido cambiado"
775
776
msgid "PluginResetSuccess"
777
msgstr "Los registros de seguridad, configuraciones y cambios adicionales hechos por el plugin han sido eliminados"
778
795
msgstr "Acciones Automatizadas"
796
797
msgid "CronjobsInfo"
798
+ msgstr "Acciones automatizadas son reglas registradas en su base de datos por un plugin, plantilla o WordPress; estas reglas son usadas para ejecutar automáticamente acciones definidas en el código con un intervalo de tiempo definido. Un buen uso de estas reglas es la generación de copias de seguridad, la ejecución de escáneres de seguridad o la remoción de elementos innecesarios de la base de datos como borradores de publicaciones. <b>Nota:</b> Las acciones automatizadas pueden ser re-instaladas por cualquier plugin/plantilla automáticamente, considere desactivar el plugin completamente si quiere eliminar una de estas acciones."
799
800
msgid "CronjobRunNow"
801
msgstr "Ejecutar Ahora (en +10 segundos)"
1019
msgid "NoUpdates"
1020
msgstr "No existen actualizaciones."
1021
1022
msgid "SiteClean"
1023
msgstr "Sitio Limpio"
1024
1346
msgid "HTAccessStandard"
1347
msgstr "El archivo <code>.htaccess</code> principal contiene las reglas estándares de WordPress generadas durante la instalación del sitio web. Usted puede modificar estas reglas para aumentar el rendimiento del servidor o para generar redirecciones a otras páginas. Para obtener más información al respecto visite la página oficial con la documentación en <a target=\"_blank\" rel=\"noopener\" href=\"https://codex.wordpress.org/Using_Permalinks#Creating_and_editing_.28.htaccess.29\"> Codex WordPress - Creating and editing (.htaccess)</a>"
1348
1349
msgid "ScannerDescription"
1350
+ msgstr "El plugin escanea su sitio web en busca de cambios que son eventualmente reportados a la API y visualizados a través de los registros de seguridad. Este escáner es ejecutado automáticamente cada día pero usted puede cambiar la frecuencia dependiendo de sus necesidades. Tenga en cuenta que si su sitio contiene una gran cantidad de archivos es posible que el escáner sea bloqueado por su proveedor de hosting si este excede el límite de ejecución que ellos hayan impuesto sobre su cuenta."
1351
1352
msgid "ScannerWithoutSPL"
1353
msgstr "El escáner hace uso de las librerías <a href=\"http://php.net/manual/en/class.splfileobject.php\" target=\"_blank\" rel=\"noopener\">SPL</a> y <a target=\"_blank\" href=\"http://php.net/manual/en/class.filesystemiterator.php\" rel=\"noopener\">Filesystem Iterator</a> de PHP para escanear el árbol de directorios de su sitio web. Esta librería solo está disponible en PHP 5 >= 5.3.0 &mdash; y &mdash; PHP 7; si su servidor hace uso de una versión antigua el plugin no funcionará como se espera. Por favor, contacte a su proveedor de hosting para obtener ayuda al respecto."
1460
msgid "DataStorageInfo"
1461
msgstr "Este es el directorio donde el plugin guardará los registros de seguridad, el archivo con las configuraciones, la cache y datos adicionales de otras herramientas. El plugin require de permisos de escritura en este directorio así como en los archivos contenidos allí. Si usted prefiere mantener estos archivos en un directorio diferente al especificado, por favor defina una constante llamada <em>\"SUCURI_DATA_STORAGE\"</em> en el archivo de configuración de WordPress con la ruta absoluta del nuevo directorio."
1462
1463
msgid "LogsReport"
1464
msgstr "Estadísticas De Seguridad"
1465
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: dd@sucuri.net
3
Donate Link: https://sucuri.net/
4
Tags: malware, security, firewall, scan, spam, virus, sucuri, protection, WordPress Security, Login Security, Security Auditing, File Integrity, htaccess, phishing, backdoors, SQL Injection, RFI, LFI, XSS, CSRF, website firewall, Website Security, Performance Optimization, Zero Day, Software Vulnerability, Exploits, Hacks, Attackers, Bad Actors, Reverse Proxy, Two Factor Security, Two Factor Authentication, Security Logs, HeatBleed Vulnerability, Website Protection, Bash Vulnerability, RevSlider Vulnerability, MailPoet Vulnerability, Malware Prevention, Website Security, Website Firewall, Website AntiVirus, Security Response, Security Detection, Security Prevention
5
Requires at least: 3.6
6
- Stable tag: 1.8.6
7
Tested up to: 4.8.0
8
9
The Sucuri WordPress Security plugin is a security toolset for security integrity monitoring, malware detection and security hardening.
@@ -216,11 +216,23 @@ No, it is not required. The Website Firewall runs in the cloud without the need
216
217
== Upgrade Notice ==
218
219
- = 1.8.6 =
220
This version adds support for the latest version of WordPress. Introduces new features and fixes some bugs reported by the WordPress community as well as bugs found by our automated testing system.
221
222
== Changelog ==
223
224
= 1.8.6 =
225
* Add default language for internationalization fallback
226
3
Donate Link: https://sucuri.net/
4
Tags: malware, security, firewall, scan, spam, virus, sucuri, protection, WordPress Security, Login Security, Security Auditing, File Integrity, htaccess, phishing, backdoors, SQL Injection, RFI, LFI, XSS, CSRF, website firewall, Website Security, Performance Optimization, Zero Day, Software Vulnerability, Exploits, Hacks, Attackers, Bad Actors, Reverse Proxy, Two Factor Security, Two Factor Authentication, Security Logs, HeatBleed Vulnerability, Website Protection, Bash Vulnerability, RevSlider Vulnerability, MailPoet Vulnerability, Malware Prevention, Website Security, Website Firewall, Website AntiVirus, Security Response, Security Detection, Security Prevention
5
Requires at least: 3.6
6
+ Stable tag: 1.8.7
7
Tested up to: 4.8.0
8
9
The Sucuri WordPress Security plugin is a security toolset for security integrity monitoring, malware detection and security hardening.
216
217
== Upgrade Notice ==
218
219
+ = 1.8.7 =
220
This version adds support for the latest version of WordPress. Introduces new features and fixes some bugs reported by the WordPress community as well as bugs found by our automated testing system.
221
222
== Changelog ==
223
224
+ = 1.8.7 =
225
+ * Fix multiple issues with the API calls
226
+ * Add queue system to fix website performance
227
+ * Fix non-dismissable newsletter invitation message
228
+ * Fix performance of the audit log parser without regexp
229
+ * Add conditional to check for the availability of SPL
230
+ * Add cache for the audit logs to make dashboard responsive
231
+ * Modify frequency of the file system scans to run daily
232
+ * Remove option to configure the maximum API timeout
233
+ * Modify location of the scanner options and scheduled tasks
234
+ * Add button to send the logs from the queue to the API
235
+
236
= 1.8.6 =
237
* Add default language for internationalization fallback
238
src/api.lib.php CHANGED
@@ -37,31 +37,6 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
37
*/
38
class SucuriScanAPI extends SucuriScanOption
39
{
40
- /**
41
- * Seconds before consider a HTTP request as timeout.
42
- *
43
- * As for the 01/Jan/2016 if the number of seconds before a timeout is greater
44
- * than sixty (which is one minute) the method will reset the option to its
45
- * default value to keep the latency of the HTTP requests in a minimum to
46
- * minimize the interruptions in the admins workflow. The normal connection
47
- * timeout should be in the range of ten seconds, or fifteen if the DNS lookups
48
- * are slow.
49
- *
50
- * @return int Seconds to consider a HTTP request timeout.
51
- */
52
- public static function requestTimeout()
53
- {
54
- $timeout = (int) self::getOption(':request_timeout');
55
-
56
- if ($timeout > SUCURISCAN_MAX_REQUEST_TIMEOUT) {
57
- self::deleteOption(':request_timeout');
58
-
59
- return self::requestTimeout();
60
- }
61
-
62
- return $timeout;
63
- }
64
-
65
/**
66
* Alternative to the built-in PHP method http_build_query.
67
*
@@ -108,7 +83,7 @@ class SucuriScanAPI extends SucuriScanOption
108
}
109
110
$response = null;
111
- $timeout = self::requestTimeout();
112
$args = is_array($args) ? $args : array();
113
114
if (isset($args['timeout'])) {
@@ -259,10 +234,9 @@ class SucuriScanAPI extends SucuriScanOption
259
* for the first time.
260
*
261
* @param array $response HTTP response after API endpoint execution.
262
- * @param bool $enqueue Add the log to the local queue on a failure.
263
* @return bool False if the API call failed, true otherwise.
264
*/
265
- public static function handleResponse($response = array(), $enqueue = true)
266
{
267
if (!$response || getenv('SUCURISCAN_NO_API_HANDLE')) {
268
return false;
@@ -324,7 +298,7 @@ class SucuriScanAPI extends SucuriScanOption
324
$msg = __('ErrorInvalidEmail', SUCURISCAN_TEXTDOMAIN);
325
}
326
327
- return SucuriScanInterface::error($enqueue ? $msg : '');
328
}
329
330
/**
@@ -348,7 +322,7 @@ class SucuriScanAPI extends SucuriScanOption
348
if (self::handleResponse($response)) {
349
self::setPluginKey($response['output']['api_key']);
350
351
- SucuriScanEvent::scheduleTask(); /* install scheduled tasks */
352
SucuriScanEvent::notifyEvent('plugin_change', 'API key was generated and set');
353
return SucuriScanInterface::info(__('AlertAPIKeySet', SUCURISCAN_TEXTDOMAIN));
354
}
@@ -379,58 +353,6 @@ class SucuriScanAPI extends SucuriScanOption
379
return false;
380
}
381
382
- /**
383
- * Send a request to the API to store and analyze the events of the site. An
384
- * event can be anything from a simple request, an internal modification of the
385
- * settings or files in the administrator panel, or a notification generated by
386
- * this plugin.
387
- *
388
- * @param string $event Event triggered by the core system functions.
389
- * @param int $time Timestamp when the event was originally triggered.
390
- * @param bool $enqueue Add the log to the local queue on a failure.
391
- * @return bool True if the event was logged, false otherwise.
392
- */
393
- public static function sendLog($event = '', $time = 0, $enqueue = true)
394
- {
395
- if (empty($event)) {
396
- return self::throwException('Event identifier cannot be empty');
397
- }
398
-
399
- $params = array();
400
- $params['a'] = 'send_log';
401
- $params['m'] = $event;
402
-
403
- if (intval($time) > 0) {
404
- $params['time'] = (int) $time;
405
- }
406
-
407
- $response = self::apiCallWordpress('POST', $params, true);
408
-
409
- return self::handleResponse($response, $enqueue);
410
- }
411
-
412
- /**
413
- * Send all logs from the queue.
414
- *
415
- * Retry the HTTP calls for the logs that were not sent to the API service
416
- * because of a connection failure or misconfiguration. Each successful call
417
- * will remove the log from the queue and the failures will keep them until the
418
- * next method call is executed.
419
- */
420
- public static function sendLogsFromQueue()
421
- {
422
- $cache = new SucuriScanCache('auditqueue');
423
- $entries = $cache->getAll();
424
-
425
- if (is_array($entries) && !empty($entries)) {
426
- foreach ($entries as $key => $entry) {
427
- if (self::sendLog($entry->message, $entry->created_at, false)) {
428
- $cache->delete($key);
429
- }
430
- }
431
- }
432
- }
433
-
434
/**
435
* Retrieve the event logs registered by the API service.
436
*
@@ -452,36 +374,27 @@ class SucuriScanAPI extends SucuriScanOption
452
}
453
454
/**
455
- * Gets the security logs from the local server (if enabled).
456
*
457
- * @param int $limit Maximum number of logs to return.
458
- * @return array|bool The data structure with the logs.
459
*/
460
- public static function getSelfHostingLogs($limit = 0)
461
{
462
- if (SucuriScanOption::isDisabled(':selfhosting_monitor')) {
463
- return self::throwException('Self-hosting monitor is disabled');
464
- }
465
-
466
$auditlogs = array();
467
- $fpath = SucuriScanOption::getOption(':selfhosting_fpath');
468
- $category = "\x20WordPressAudit\x20" . SucuriScan::getDomain(true);
469
-
470
- if (class_exists('SplFileObject') && is_readable($fpath)) {
471
- $file = new SplFileObject($fpath);
472
- $file->seek(PHP_INT_MAX);
473
- $total = $file->key();
474
- $offset = $total - $limit;
475
- $file->seek($offset>0 ? $offset : 0);
476
-
477
- while (!$file->eof()) {
478
- $line = $file->current();
479
- $line = str_replace($category, '', $line);
480
- $line = trim($line); /* remove new line */
481
- if (!empty($line)) {
482
- $auditlogs[] = $line;
483
- }
484
- $file->next();
485
}
486
}
487
@@ -505,94 +418,116 @@ class SucuriScanAPI extends SucuriScanOption
505
{
506
$response = is_array($response) ? $response : array();
507
$response['output_data'] = array();
508
- $log_pattern = '/^([0-9\-]+) ([0-9:]+) (\S+) : (.+)/';
509
- $extra_pattern = '/(.+ \(multiple entries\):) (.+)/';
510
- $generic_pattern = '/^@?([A-Z][a-z]{3,7}): ([^;]+; )?(.+)/';
511
- $auth_pattern = '/^User authentication (succeeded|failed): ([^<;]+)/';
512
513
foreach ((array) @$response['output'] as $log) {
514
- if (@preg_match($log_pattern, $log, $log_match)) {
515
- $log_data = array(
516
- 'event' => 'notice',
517
- 'date' => '',
518
- 'time' => '',
519
- 'datetime' => '',
520
- 'timestamp' => 0,
521
- 'account' => $log_match[3],
522
- 'username' => 'system',
523
- 'remote_addr' => '127.0.0.1',
524
- 'message' => $log_match[4],
525
- 'file_list' => false,
526
- 'file_list_count' => 0,
527
- );
528
529
- /* extract and fix the date and time using the Eastern time zone */
530
- $datetime = sprintf('%s %s EDT', $log_match[1], $log_match[2]);
531
- $log_data['timestamp'] = strtotime($datetime);
532
- $log_data['datetime'] = date('Y-m-d H:i:s', $log_data['timestamp']);
533
- $log_data['date'] = date('Y-m-d', $log_data['timestamp']);
534
- $log_data['time'] = date('H:i:s', $log_data['timestamp']);
535
-
536
- /* extract more information from the generic audit logs */
537
- $log_data['message'] = str_replace('<br>', '; ', $log_data['message']);
538
-
539
- if (@preg_match($generic_pattern, $log_data['message'], $log_extra)) {
540
- $log_data['event'] = strtolower($log_extra[1]);
541
- $log_data['message'] = trim($log_extra[3]);
542
-
543
- /* extract the username and remote address from the log */
544
- if (!empty($log_extra[2])) {
545
- $username_address = rtrim($log_extra[2], ";\x20");
546
- $log_data['remote_addr'] = $username_address;
547
-
548
- /* separate the username from the remote address */
549
- if (strpos($username_address, ",\x20") !== false) {
550
- $usip_parts = explode(",\x20", $username_address, 2);
551
-
552
- if (count($usip_parts) == 2) {
553
- /* separate the username from the display name */
554
- $log_data['username'] = @preg_replace('/^.+ \((.+)\)#x2F;', '$1', $usip_parts[0]);
555
- $log_data['remote_addr'] = $usip_parts[1];
556
- }
557
- }
558
- }
559
560
- /* fix old user authentication logs for backward compatibility */
561
- $log_data['message'] = str_replace(
562
- 'logged in',
563
- 'authentication succeeded',
564
- $log_data['message']
565
- );
566
567
- if (@preg_match($auth_pattern, $log_data['message'], $user_match)) {
568
- $log_data['username'] = $user_match[2];
569
- }
570
- }
571
572
- /* extract more information from the special formatted logs */
573
- if (@preg_match($extra_pattern, $log_data['message'], $log_extra)) {
574
- $log_data['message'] = $log_extra[1];
575
- $log_extra[2] = str_replace(', new size', '; new size', $log_extra[2]);
576
- $log_extra[2] = str_replace(",\x20", ";\x20", $log_extra[2]);
577
- $log_data['file_list'] = explode(',', $log_extra[2]);
578
- $log_data['file_list_count'] = count($log_data['file_list']);
579
}
580
581
- /* extract additional details from the message */
582
- if (strpos($log_data['message'], '; details:')) {
583
- $idx = strpos($log_data['message'], '; details:');
584
- $message = substr($log_data['message'], 0, $idx);
585
- $details = substr($log_data['message'], $idx + 11);
586
587
- $log_data['message'] = $message . ' (details):';
588
- $log_data['file_list'] = explode(',', $details);
589
- $log_data['file_list_count'] = count($log_data['file_list']);
590
}
591
592
- if ($log_data = self::getLogsHotfix($log_data)) {
593
- $response['output_data'][] = $log_data;
594
}
595
}
596
}
597
598
return $response;
@@ -617,14 +552,18 @@ class SucuriScanAPI extends SucuriScanOption
617
*
618
* @see https://wordpress.org/plugins/php-compatibility-checker/
619
*/
620
- if (isset($data['message'])
621
- && strpos($data['message'], 'Wpephpcompat_jobs') === 0
622
- && preg_match('/ID: ([0-9]+); name: (.+)/', $data['message'], $match)
623
- ) {
624
$data['message'] = sprintf(
625
'WP Engine PHP Compatibility Checker: %s (created post #%d as cache)',
626
- $match[2], /* plugin or theme name */
627
- $match[1] /* unique post or page identifier */
628
);
629
}
630
@@ -725,16 +664,22 @@ class SucuriScanAPI extends SucuriScanOption
725
$report['end_timestamp'] = $event['timestamp'];
726
}
727
728
- // Detect successful and failed user authentications.
729
- if (@preg_match('/^User authentication (succeeded|failed):/', $event['message'], $match)) {
730
- if ($match[1] == 'succeeded') {
731
- $report['events_per_login']['successful']++;
732
- } else {
733
- $report['events_per_login']['failed']++;
734
- }
735
- } elseif (@preg_match('/^User logged in:/', $event['message'])) {
736
- // Backward compatibility for previous user login messages.
737
$report['events_per_login']['successful']++;
738
}
739
}
740
@@ -854,11 +799,18 @@ class SucuriScanAPI extends SucuriScanOption
854
* distributed by an independent developer.
855
*/
856
if (isset($plugin_data['PluginURI'])
857
- && preg_match($pattern, $plugin_data['PluginURI'], $match)
858
) {
859
- $repository = $match[0];
860
- $repository_name = $match[2];
861
$is_free_plugin = true;
862
} else {
863
$delimiter = strpos($path, '/') ? '/' : '.';
864
$parts = explode($delimiter, $path, 2);
37
*/
38
class SucuriScanAPI extends SucuriScanOption
39
{
40
/**
41
* Alternative to the built-in PHP method http_build_query.
42
*
83
}
84
85
$response = null;
86
+ $timeout = SUCURISCAN_MAX_REQUEST_TIMEOUT;
87
$args = is_array($args) ? $args : array();
88
89
if (isset($args['timeout'])) {
234
* for the first time.
235
*
236
* @param array $response HTTP response after API endpoint execution.
237
* @return bool False if the API call failed, true otherwise.
238
*/
239
+ public static function handleResponse($response = array())
240
{
241
if (!$response || getenv('SUCURISCAN_NO_API_HANDLE')) {
242
return false;
298
$msg = __('ErrorInvalidEmail', SUCURISCAN_TEXTDOMAIN);
299
}
300
301
+ return SucuriScanInterface::error($msg);
302
}
303
304
/**
322
if (self::handleResponse($response)) {
323
self::setPluginKey($response['output']['api_key']);
324
325
+ SucuriScanEvent::installScheduledTask();
326
SucuriScanEvent::notifyEvent('plugin_change', 'API key was generated and set');
327
return SucuriScanInterface::info(__('AlertAPIKeySet', SUCURISCAN_TEXTDOMAIN));
328
}
353
return false;
354
}
355
356
/**
357
* Retrieve the event logs registered by the API service.
358
*
374
}
375
376
/**
377
+ * Returns the security logs from the system queue.
378
*
379
+ * @return array The data structure with the logs.
380
*/
381
+ public static function getAuditLogsFromQueue()
382
{
383
$auditlogs = array();
384
+ $cache = new SucuriScanCache('auditqueue');
385
+
386
+ if ($events = $cache->getAll()) {
387
+ $events = array_reverse($events);
388
+
389
+ foreach ($events as $micro => $message) {
390
+ $offset = strpos($micro, '_');
391
+ $time = substr($micro, 0, $offset);
392
+ $auditlogs[] = sprintf(
393
+ '%s %s : %s',
394
+ date('Y-m-d H:i:s', intval($time)),
395
+ SucuriScan::getSiteEmail(),
396
+ $message
397
+ );
398
}
399
}
400
418
{
419
$response = is_array($response) ? $response : array();
420
$response['output_data'] = array();
421
422
foreach ((array) @$response['output'] as $log) {
423
+ /* YYYY-MM-dd HH:ii:ss EMAIL : MESSAGE: (multiple entries): a,b,c */
424
+ if (strpos($log, "\x20:\x20") === false) {
425
+ continue; /* ignore; invalid format */
426
+ }
427
428
+ $log_data = array(
429
+ 'event' => 'notice',
430
+ 'date' => '',
431
+ 'time' => '',
432
+ 'datetime' => '',
433
+ 'timestamp' => 0,
434
+ 'account' => '',
435
+ 'username' => 'system',
436
+ 'remote_addr' => '127.0.0.1',
437
+ 'message' => '',
438
+ 'file_list' => false,
439
+ 'file_list_count' => 0,
440
+ );
441
442
+ list($left, $right) = explode("\x20:\x20", $log, 2);
443
+ $dateAndEmail = explode("\x20", $left, 3);
444
445
+ /* set basic information */
446
+ $log_data['message'] = $right;
447
+ $log_data['account'] = $dateAndEmail[2];
448
+
449
+ /* extract and fix the date and time using the Eastern time zone */
450
+ $datetime = sprintf('%s %s EDT', $dateAndEmail[0], $dateAndEmail[1]);
451
+ $log_data['timestamp'] = strtotime($datetime);
452
+ $log_data['datetime'] = date('Y-m-d H:i:s', $log_data['timestamp']);
453
+ $log_data['date'] = date('Y-m-d', $log_data['timestamp']);
454
+ $log_data['time'] = date('H:i:s', $log_data['timestamp']);
455
+
456
+ /* extract more information from the generic audit logs */
457
+ $log_data['message'] = str_replace('<br>', ";\x20", $log_data['message']);
458
+
459
+ $eventTypes = self::getAuditEventTypes();
460
+ $eventTypes = array_keys($eventTypes);
461
+
462
+ /* LEVEL: USERNAME, IP; MESSAGE */
463
+ if (strpos($log_data['message'], ":\x20") && strpos($log_data['message'], ";\x20")) {
464
+ $offset = strpos($log_data['message'], ":\x20");
465
+ $level = substr($log_data['message'], 0, $offset);
466
+ $log_data['event'] = strtolower($level);
467
468
+ /* ignore; invalid event type */
469
+ if (!in_array($log_data['event'], $eventTypes)) {
470
+ continue;
471
}
472
473
+ /* extract the IP address */
474
+ $log_data['message'] = substr($log_data['message'], $offset+2);
475
+ $offset = strpos($log_data['message'], ";\x20");
476
+ $log_data['remote_addr'] = substr($log_data['message'], 0, $offset);
477
478
+ /* extract the username */
479
+ if (strpos($log_data['remote_addr'], ",\x20")) {
480
+ $index = strpos($log_data['remote_addr'], ",\x20");
481
+ $log_data['username'] = substr($log_data['remote_addr'], 0, $index);
482
+ $log_data['remote_addr'] = substr($log_data['remote_addr'], $index+2);
483
}
484
485
+ /* fix old user authentication logs for backward compatibility */
486
+ $log_data['message'] = substr($log_data['message'], $offset+2);
487
+ $log_data['message'] = str_replace(
488
+ 'logged in',
489
+ 'authentication succeeded',
490
+ $log_data['message']
491
+ );
492
+
493
+ /* extract the username of a successful/failed login */
494
+ if (strpos($log_data['message'], "User authentication\x20") === 0) {
495
+ $offset = strpos($log_data['message'], ":\x20");
496
+ $username = substr($log_data['message'], $offset+2);
497
+ if (strpos($username, ';') !== false) {
498
+ $username = substr($username, 0, strpos($username, ';'));
499
+ }
500
+ $log_data['username'] = $username;
501
}
502
}
503
+
504
+ /* extract more information from the special formatted logs */
505
+ if (strpos($log_data['message'], "(multiple entries):\x20")) {
506
+ $offset = strpos($log_data['message'], "(multiple entries):\x20");
507
+ $message = substr($log_data['message'], 0, $offset+19);
508
+ $entries = substr($log_data['message'], $offset+20);
509
+
510
+ $log_data['message'] = $message;
511
+ $entries = str_replace(', new size', '; new size', $entries);
512
+ $entries = str_replace(",\x20", ";\x20", $entries);
513
+ $log_data['file_list'] = explode(',', $entries);
514
+ $log_data['file_list_count'] = count($log_data['file_list']);
515
+ }
516
+
517
+ /* extract additional details from the message */
518
+ if (strpos($log_data['message'], '; details:')) {
519
+ $idx = strpos($log_data['message'], '; details:');
520
+ $message = substr($log_data['message'], 0, $idx);
521
+ $details = substr($log_data['message'], $idx + 11);
522
+
523
+ $log_data['message'] = $message . ' (details):';
524
+ $log_data['file_list'] = explode(',', $details);
525
+ $log_data['file_list_count'] = count($log_data['file_list']);
526
+ }
527
+
528
+ if ($log_data = self::getLogsHotfix($log_data)) {
529
+ $response['output_data'][] = $log_data;
530
+ }
531
}
532
533
return $response;
552
*
553
* @see https://wordpress.org/plugins/php-compatibility-checker/
554
*/
555
+ if (isset($data['message']) && strpos($data['message'], 'Wpephpcompat_jobs') === 0) {
556
+ $offset = strpos($data['message'], "ID:\x20");
557
+ $id = substr($data['message'], $offset+4);
558
+ $id = substr($id, 0, strpos($id, ';'));
559
+
560
+ $offset = strpos($data['message'], "name:\x20");
561
+ $name = substr($data['message'], $offset+6);
562
+
563
$data['message'] = sprintf(
564
'WP Engine PHP Compatibility Checker: %s (created post #%d as cache)',
565
+ $name, /* plugin or theme name */
566
+ $id /* unique post or page identifier */
567
);
568
}
569
664
$report['end_timestamp'] = $event['timestamp'];
665
}
666
667
+ /* backward compatibility for previous user login messages */
668
+ if (strpos($event['message'], 'User logged in:') === 0) {
669
+ $report['events_per_login']['successful']++;
670
+ continue;
671
+ }
672
+
673
+ /* detect successful user authentications */
674
+ if (strpos($event['message'], 'User authentication succeeded:') === 0) {
675
$report['events_per_login']['successful']++;
676
+ continue;
677
+ }
678
+
679
+ /* detect failed user authentications */
680
+ if (strpos($event['message'], 'User authentication failed:') === 0) {
681
+ $report['events_per_login']['failed']++;
682
+ continue;
683
}
684
}
685
799
* distributed by an independent developer.
800
*/
801
if (isset($plugin_data['PluginURI'])
802
+ && strpos($plugin_data['PluginURI'], '.org/plugins/')
803
+ && strpos($plugin_data['PluginURI'], '://wordpress.org/')
804
) {
805
$is_free_plugin = true;
806
+ $repository = $plugin_data['PluginURI'];
807
+ $offset = strpos($plugin_data['PluginURI'], '/plugins/');
808
+ $repository_name = substr($plugin_data['PluginURI'], $offset+9);
809
+
810
+ if (strpos($repository_name, '/') !== false) {
811
+ $offset = strpos($repository_name, '/');
812
+ $repository_name = substr($repository_name, 0, $offset);
813
+ }
814
} else {
815
$delimiter = strpos($path, '/') ? '/' : '.';
816
$parts = explode($delimiter, $path, 2);
src/auditlogs.lib.php CHANGED
@@ -64,10 +64,13 @@ class SucuriScanAuditLogs
64
65
$response = array();
66
$response['count'] = 0;
67
$response['content'] = '';
68
$response['selfhosting'] = false;
69
70
- /* Initialize the values for the pagination. */
71
$maxPerPage = SUCURISCAN_AUDITLOGS_PER_PAGE;
72
$pageNumber = SucuriScanTemplate::pageNumber();
73
$logsLimit = ($pageNumber * $maxPerPage);
@@ -81,17 +84,22 @@ class SucuriScanAuditLogs
81
/* API call if cache is invalid. */
82
if (!$auditlogs || $pageNumber !== 1) {
83
ob_start();
84
$cacheTheResponse = true;
85
$auditlogs = SucuriScanAPI::getAuditLogs($logsLimit);
86
- $errors = ob_get_contents();
87
ob_end_clean();
88
}
89
90
- /* Stop everything and report errors. */
91
if (!empty($errors)) {
92
- header('Content-Type: text/html; charset=UTF-8');
93
- print($errors);
94
- exit(0);
95
}
96
97
/* Cache the data for sometime. */
@@ -99,10 +107,21 @@ class SucuriScanAuditLogs
99
$cache->add('response', $auditlogs);
100
}
101
102
- /* Fallback; get the logs from the local server */
103
- if (!$auditlogs && !SucuriScanOption::getOption(':api_key')) {
104
- $auditlogs = SucuriScanAPI::getSelfHostingLogs(SUCURISCAN_AUDITLOGS_PER_PAGE);
105
- $response['selfhosting'] = (bool) ($auditlogs !== false);
106
}
107
108
if ($auditlogs) {
@@ -197,6 +216,11 @@ class SucuriScanAuditLogs
197
$response['content'] = __('NoLogs', SUCURISCAN_TEXTDOMAIN);
198
}
199
200
wp_send_json($response, 200);
201
}
202
@@ -290,7 +314,7 @@ class SucuriScanAuditLogs
290
}
291
292
/**
293
- * Deletes the cache file with the auditlogs data.
294
*
295
* @codeCoverageIgnore - Notice that there is a test case that covers this
296
* code, but since the WP-Send-JSON method uses die() to stop any further
@@ -298,27 +322,15 @@ class SucuriScanAuditLogs
298
* with a missing line in the coverage. Since the test case takes care of
299
* the functionality of this code we will assume that it is fully covered.
300
*/
301
- public static function ajaxAuditLogsResetCache()
302
{
303
- if (SucuriScanRequest::post('form_action') !== 'reset_auditlogs_cache') {
304
return;
305
}
306
307
- $response = array();
308
- $cache = new SucuriScanCache('auditlogs');
309
- $cacheInfo = $cache->getDatastoreInfo();
310
- $filename = $cacheInfo['fpath'];
311
- $response['path'] = basename($filename);
312
- $response['ok'] = false;
313
-
314
- if (!file_exists($filename)) {
315
- $response['error'] = 'cache does not exists';
316
- } elseif (!is_writable($filename)) {
317
- $response['error'] = 'cache is not resetable';
318
- } else {
319
- $response['ok'] = (bool) @unlink($filename);
320
- }
321
322
- wp_send_json($response, 200);
323
}
324
}
64
65
$response = array();
66
$response['count'] = 0;
67
+ $response['status'] = '';
68
$response['content'] = '';
69
+ $response['queueSize'] = 0;
70
+ $response['pagination'] = '';
71
$response['selfhosting'] = false;
72
73
+ /* initialize the values for the pagination */
74
$maxPerPage = SUCURISCAN_AUDITLOGS_PER_PAGE;
75
$pageNumber = SucuriScanTemplate::pageNumber();
76
$logsLimit = ($pageNumber * $maxPerPage);
84
/* API call if cache is invalid. */
85
if (!$auditlogs || $pageNumber !== 1) {
86
ob_start();
87
+ $start = microtime(true);
88
$cacheTheResponse = true;
89
$auditlogs = SucuriScanAPI::getAuditLogs($logsLimit);
90
+ $errors = ob_get_contents(); /* capture errors */
91
+ $duration = microtime(true) - $start;
92
ob_end_clean();
93
+
94
+ /* report latency in the API calls */
95
+ $response['status'] = !is_array($auditlogs)
96
+ ? __('AuditLogsNoAPI', SUCURISCAN_TEXTDOMAIN)
97
+ : sprintf('API %s secs', round($duration, 4));
98
}
99
100
+ /* stop everything and report errors */
101
if (!empty($errors)) {
102
+ $response['content'] .= $errors;
103
}
104
105
/* Cache the data for sometime. */
107
$cache->add('response', $auditlogs);
108
}
109
110
+ /* merge the logs from the queue system */
111
+ if ($queuelogs = SucuriScanAPI::getAuditLogsFromQueue()) {
112
+ if (!$auditlogs) {
113
+ $auditlogs = $queuelogs;
114
+ } else {
115
+ $auditlogs['total_entries'] += $queuelogs['total_entries'];
116
+ $auditlogs['output'] = array_merge(
117
+ $queuelogs['output'],
118
+ $auditlogs['output']
119
+ );
120
+ $auditlogs['output_data'] = array_merge(
121
+ $queuelogs['output_data'],
122
+ $auditlogs['output_data']
123
+ );
124
+ }
125
}
126
127
if ($auditlogs) {
216
$response['content'] = __('NoLogs', SUCURISCAN_TEXTDOMAIN);
217
}
218
219
+ $cache = new SucuriScanCache('auditqueue');
220
+ $finfo = $cache->getDatastoreInfo();
221
+ $events = $cache->getAll();
222
+ $response['queueSize'] = count($events);
223
+
224
wp_send_json($response, 200);
225
}
226
314
}
315
316
/**
317
+ * Send the logs from the queue to the API.
318
*
319
* @codeCoverageIgnore - Notice that there is a test case that covers this
320
* code, but since the WP-Send-JSON method uses die() to stop any further
322
* with a missing line in the coverage. Since the test case takes care of
323
* the functionality of this code we will assume that it is fully covered.
324
*/
325
+ public static function ajaxAuditLogsSendLogs()
326
{
327
+ if (SucuriScanRequest::post('form_action') !== 'auditlogs_send_logs') {
328
return;
329
}
330
331
+ /* blocking; might take a while */
332
+ SucuriScanEvent::sendLogsFromQueue();
333
334
+ wp_send_json(array('ok' => true), 200);
335
}
336
}
src/cache.lib.php CHANGED
@@ -81,7 +81,7 @@ class SucuriScanCache extends SucuriScan
81
*
82
* @return array Default attributes for every datastore file.
83
*/
84
- private function datastoreDefaultInfo()
85
{
86
$attrs = array(
87
'datastore' => $this->datastore,
@@ -135,32 +135,53 @@ class SucuriScanCache extends SucuriScan
135
return false;
136
}
137
138
- $filename = 'sucuri-' . $this->datastore . '.php';
139
- $file_path = $this->dataStorePath($filename);
140
- $folder_path = dirname($file_path);
141
142
- /* create the datastore parent directory. */
143
- if (!file_exists($folder_path)) {
144
- @mkdir($folder_path, 0755, true);
145
}
146
147
- /* create the cache file if necessary and the folder is writable. */
148
- if (!file_exists($file_path) && is_writable($folder_path) && $auto_create) {
149
- @file_put_contents($file_path, $this->datastoreInfo());
150
}
151
152
- return $file_path;
153
}
154
155
/**
156
* Check whether a key has a valid name or not.
157
*
158
* @param string $key Unique name for the data.
159
- * @return bool TRUE if the format of the key name is valid, FALSE otherwise.
160
*/
161
private function validKeyName($key = '')
162
{
163
- return (bool) @preg_match('/^([0-9a-zA-Z_]+)#x2F;', $key);
164
}
165
166
/**
@@ -171,18 +192,20 @@ class SucuriScanCache extends SucuriScan
171
*/
172
private function saveNewEntries($finfo = array())
173
{
174
- $data_string = $this->datastoreInfo($finfo);
175
176
- if (!empty($finfo)) {
177
foreach ($finfo['entries'] as $key => $data) {
178
- if ($this->validKeyName($key)) {
179
- $data = json_encode($data);
180
- $data_string .= sprintf("%s:%s\n", $key, $data);
181
- }
182
}
183
}
184
185
- return (bool) @file_put_contents($this->datastore_path, $data_string);
186
}
187
188
/**
@@ -191,28 +214,35 @@ class SucuriScanCache extends SucuriScan
191
* be merged automatically.
192
*
193
* @param bool $assoc Force object to array conversion.
194
* @return array Rainbow table with the key names and decoded values.
195
*/
196
- private function getDatastoreContent($assoc = false)
197
{
198
$object = array();
199
$object['info'] = array();
200
$object['entries'] = array();
201
- $header = '/^\/\/ ([a-z_]+)=(.*);#x2F;';
202
- $content = '/^([0-9a-zA-Z_]+):(.+)/';
203
204
if ($lines = SucuriScanFileInfo::fileLines($this->datastore_path)) {
205
foreach ($lines as $line) {
206
- if (preg_match($header, $line, $match)) {
207
- $object['info'][ $match[1] ] = $match[2];
208
continue;
209
}
210
211
- if (preg_match($content, $line, $match)) {
212
- if ($this->validKeyName($match[1])) {
213
- $object['entries'][$match[1]] =
214
- @json_decode($match[2], $assoc);
215
- }
216
}
217
}
218
}
@@ -234,7 +264,7 @@ class SucuriScanCache extends SucuriScan
234
*/
235
public function getDatastoreInfo()
236
{
237
- $finfo = $this->getDatastoreContent();
238
239
if (empty($finfo['info'])) {
240
return false;
@@ -273,7 +303,8 @@ class SucuriScanCache extends SucuriScan
273
public function dataHasExpired($lifetime = 0, $finfo = null)
274
{
275
if (is_null($finfo)) {
276
- $finfo = $this->getDatastoreContent();
277
}
278
279
if ($lifetime > 0 && !empty($finfo['info'])) {
@@ -315,11 +346,10 @@ class SucuriScanCache extends SucuriScan
315
return self::throwException('Invalid cache key name');
316
}
317
318
- $finfo = $this->getDatastoreContent(true);
319
-
320
- $finfo['entries'][$key] = $data;
321
322
- return $this->saveNewEntries($finfo);
323
}
324
325
/**
@@ -403,6 +433,20 @@ class SucuriScanCache extends SucuriScan
403
return $this->saveNewEntries($finfo);
404
}
405
406
/**
407
* Remove all the entries from the datastore file.
408
*
@@ -410,12 +454,8 @@ class SucuriScanCache extends SucuriScan
410
*/
411
public function flush()
412
{
413
- $finfo = $this->getDatastoreContent();
414
-
415
- if (array_key_exists('entries', $finfo)) {
416
- $finfo['entries'] = array();
417
- }
418
419
- return $this->saveNewEntries($finfo);
420
}
421
}
81
*
82
* @return array Default attributes for every datastore file.
83
*/
84
+ public function datastoreDefaultInfo()
85
{
86
$attrs = array(
87
'datastore' => $this->datastore,
135
return false;
136
}
137
138
+ $filename = $this->dataStorePath('sucuri-' . $this->datastore . '.php');
139
+ $directory = dirname($filename); /* create directory if necessary */
140
141
+ if (!file_exists($directory)) {
142
+ @mkdir($directory, 0755, true);
143
}
144
145
+ if (!file_exists($filename) && is_writable($directory) && $auto_create) {
146
+ @file_put_contents($filename, $this->datastoreInfo());
147
}
148
149
+ return $filename;
150
}
151
152
/**
153
* Check whether a key has a valid name or not.
154
*
155
+ * WARNING: Instead of using a regular expression to match the format of the
156
+ * key we will use a primitive string transformation technique to reduce the
157
+ * execution time, regular expressions are significantly slow.
158
+ *
159
* @param string $key Unique name for the data.
160
+ * @return bool True if the key is valid, false otherwise.
161
*/
162
private function validKeyName($key = '')
163
{
164
+ $result = true;
165
+ $length = strlen($key);
166
+ $allowed = array(
167
+ /* preg_match('/^([0-9a-zA-Z_]+)#x2F;', $key) */
168
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
169
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
170
+ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
171
+ 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
172
+ 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
173
+ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
174
+ 'Y', 'Z', '_',
175
+ );
176
+
177
+ for ($i = 0; $i < $length; $i++) {
178
+ if (!in_array($key[$i], $allowed)) {
179
+ $result = false;
180
+ break;
181
+ }
182
+ }
183
+
184
+ return $result;
185
}
186
187
/**
192
*/
193
private function saveNewEntries($finfo = array())
194
{
195
+ if (!$finfo) {
196
+ return false;
197
+ }
198
199
+ $metadata = $this->datastoreInfo($finfo);
200
+
201
+ if (@file_put_contents($this->datastore_path, $metadata)) {
202
foreach ($finfo['entries'] as $key => $data) {
203
+ $line = sprintf("%s:%s\n", $key, json_encode($data));
204
+ @file_put_contents($this->datastore_path, $line, FILE_APPEND);
205
}
206
}
207
208
+ return true;
209
}
210
211
/**
214
* be merged automatically.
215
*
216
* @param bool $assoc Force object to array conversion.
217
+ * @param bool $onlyInfo Returns the cache headers and no content.
218
* @return array Rainbow table with the key names and decoded values.
219
*/
220
+ private function getDatastoreContent($assoc = false, $onlyInfo = false)
221
{
222
$object = array();
223
$object['info'] = array();
224
$object['entries'] = array();
225
226
if ($lines = SucuriScanFileInfo::fileLines($this->datastore_path)) {
227
foreach ($lines as $line) {
228
+ if (strpos($line, "//\x20") === 0
229
+ && strpos($line, '=') !== false
230
+ && $line[strlen($line)-1] === ';'
231
+ ) {
232
+ $section = substr($line, 3, strlen($line)-4);
233
+ list($header, $value) = explode('=', $section, 2);
234
+ $object['info'][$header] = $value;
235
+ continue;
236
+ }
237
+
238
+ /* skip content */
239
+ if ($onlyInfo) {
240
continue;
241
}
242
243
+ if (strpos($line, ':') !== false) {
244
+ list($keyname, $value) = explode(':', $line, 2);
245
+ $object['entries'][$keyname] = @json_decode($value, $assoc);
246
}
247
}
248
}
264
*/
265
public function getDatastoreInfo()
266
{
267
+ $finfo = $this->getDatastoreContent(false, true);
268
269
if (empty($finfo['info'])) {
270
return false;
303
public function dataHasExpired($lifetime = 0, $finfo = null)
304
{
305
if (is_null($finfo)) {
306
+ $meta = $this->getDatastoreInfo();
307
+ $finfo = array('info' => $meta);
308
}
309
310
if ($lifetime > 0 && !empty($finfo['info'])) {
346
return self::throwException('Invalid cache key name');
347
}
348
349
+ $finfo = $this->getDatastoreInfo();
350
+ $line = sprintf("%s:%s\n", $key, json_encode($data));
351
352
+ return (bool) @file_put_contents($finfo['fpath'], $line, FILE_APPEND);
353
}
354
355
/**
433
return $this->saveNewEntries($finfo);
434
}
435
436
+ /**
437
+ * Replaces the entire content of the cache file.
438
+ *
439
+ * @param array $entries New data for the cache.
440
+ * @return bool True if the cache was replaced.
441
+ */
442
+ public function override($entries = array())
443
+ {
444
+ return $this->saveNewEntries(array(
445
+ 'info' => $this->getDatastoreInfo(),
446
+ 'entries' => $entries,
447
+ ));
448
+ }
449
+
450
/**
451
* Remove all the entries from the datastore file.
452
*
454
*/
455
public function flush()
456
{
457
+ $filename = 'sucuri-' . $this->datastore . '.php';
458
459
+ return @unlink($this->dataStorePath($filename));
460
}
461
}
src/event.lib.php CHANGED
@@ -34,7 +34,7 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
34
class SucuriScanEvent extends SucuriScan
35
{
36
/**
37
- * Creates a cronjob to run the file system scanner twice-daily.
38
*
39
* Right after a fresh installation of the plugin, it will create a cronjob
40
* that will execute the first scan in the next five minutes. This scan will
@@ -43,23 +43,13 @@ class SucuriScanEvent extends SucuriScan
43
* list with the checksum of the new file list, if there are differences we
44
* will assume that someone or something modified one or more files and send
45
* an email alsert about the incident.
46
- *
47
- * @param bool $run_now Forces the execute of the scanner right now.
48
*/
49
- public static function scheduleTask($run_now = true)
50
{
51
- /* stop if the user has disabled the cronjobs. */
52
- if (SucuriScanOption::getOption(':scan_frequency') !== '_oneoff') {
53
- $task_name = 'sucuriscan_scheduled_scan';
54
-
55
- if (!wp_next_scheduled($task_name)) {
56
- wp_schedule_event(time() + 10, 'twicedaily', $task_name);
57
- }
58
59
- if ($run_now === true) {
60
- // Execute scheduled task after five minutes.
61
- wp_schedule_single_event(time() + 300, $task_name);
62
- }
63
}
64
}
65
@@ -179,10 +169,38 @@ class SucuriScanEvent extends SucuriScan
179
* in a failure. However, this procedure depends on the ability of the plugin
180
* to write the log into the queue when the previous request failed.
181
*
182
- * @param string $event_message Information about the security event.
183
* @return bool True if the operation succeeded, false otherwise.
184
*/
185
- public static function sendEventLog($event_message = '')
186
{
187
/* create storage directory if necessary */
188
SucuriScanInterface::createStorageFolder();
@@ -191,9 +209,9 @@ class SucuriScanEvent extends SucuriScan
191
* Self-hosted Monitor.
192
*
193
* Send a copy of the event log to a local file, this will allow the
194
- * administrator of the server to integrate the events monitored by the plugin
195
- * with a 3rd-party service like OSSEC or similar. More information in the Self-
196
- * Hosting panel located in the plugin' settings page.
197
*/
198
if (function_exists('sucuriscan_selfhosting_fpath')) {
199
$monitor_fpath = sucuriscan_selfhosting_fpath();
@@ -204,7 +222,7 @@ class SucuriScanEvent extends SucuriScan
204
date('Y-m-d H:i:s'),
205
SucuriScan::getTopLevelDomain(),
206
SucuriScanOption::getOption(':account'),
207
- $event_message
208
);
209
@file_put_contents(
210
$monitor_fpath,
@@ -214,24 +232,69 @@ class SucuriScanEvent extends SucuriScan
214
}
215
}
216
217
if (SucuriScanOption::isEnabled(':api_service')) {
218
- SucuriScanAPI::sendLogsFromQueue();
219
-
220
- return SucuriScanAPI::sendLog($event_message);
221
}
222
223
return true;
224
}
225
226
/**
227
* Generates an audit event log (to be sent later).
228
*
229
* @param int $severity Importance of the event that will be reported, values from one to five.
230
* @param string $message The explanation of the event.
231
- * @param bool $internal Whether the event will be publicly visible or not.
232
* @return bool True if the event was logged in the monitoring service, false otherwise.
233
*/
234
- private static function reportEvent($severity = 0, $message = '', $internal = false)
235
{
236
$user = wp_get_current_user();
237
$remote_ip = self::getRemoteAddr();
@@ -260,18 +323,13 @@ class SucuriScanEvent extends SucuriScan
260
$severity_name = $severities[$severity];
261
}
262
263
- if ($internal === true) {
264
- /* mark the event as internal if necessary. */
265
- $severity_name = '@' . $severity_name;
266
- }
267
-
268
/* remove unnecessary characters */
269
$message = strip_tags($message);
270
$message = str_replace("\r", '', $message);
271
$message = str_replace("\n", '', $message);
272
$message = str_replace("\t", '', $message);
273
274
- return self::sendEventLog(sprintf(
275
'%s:%s %s; %s',
276
$severity_name,
277
$username,
@@ -284,72 +342,66 @@ class SucuriScanEvent extends SucuriScan
284
* Reports a debug event on the website.
285
*
286
* @param string $message Text witht the explanation of the event or action performed.
287
- * @param bool $internal Whether the event will be publicly visible or not.
288
* @return bool Either true or false depending on the success of the operation.
289
*/
290
- public static function reportDebugEvent($message = '', $internal = false)
291
{
292
- return self::reportEvent(0, $message, $internal);
293
}
294
295
/**
296
* Reports a notice event on the website.
297
*
298
* @param string $message Text witht the explanation of the event or action performed.
299
- * @param bool $internal Whether the event will be publicly visible or not.
300
* @return bool Either true or false depending on the success of the operation.
301
*/
302
- public static function reportNoticeEvent($message = '', $internal = false)
303
{
304
- return self::reportEvent(1, $message, $internal);
305
}
306
307
/**
308
* Reports a info event on the website.
309
*
310
* @param string $message Text witht the explanation of the event or action performed.
311
- * @param bool $internal Whether the event will be publicly visible or not.
312
* @return bool Either true or false depending on the success of the operation.
313
*/
314
- public static function reportInfoEvent($message = '', $internal = false)
315
{
316
- return self::reportEvent(2, $message, $internal);
317
}
318
319
/**
320
* Reports a warning event on the website.
321
*
322
* @param string $message Text witht the explanation of the event or action performed.
323
- * @param bool $internal Whether the event will be publicly visible or not.
324
* @return bool Either true or false depending on the success of the operation.
325
*/
326
- public static function reportWarningEvent($message = '', $internal = false)
327
{
328
- return self::reportEvent(3, $message, $internal);
329
}
330
331
/**
332
* Reports a error event on the website.
333
*
334
* @param string $message Text witht the explanation of the event or action performed.
335
- * @param bool $internal Whether the event will be publicly visible or not.
336
* @return bool Either true or false depending on the success of the operation.
337
*/
338
- public static function reportErrorEvent($message = '', $internal = false)
339
{
340
- return self::reportEvent(4, $message, $internal);
341
}
342
343
/**
344
* Reports a critical event on the website.
345
*
346
* @param string $message Text witht the explanation of the event or action performed.
347
- * @param bool $internal Whether the event will be publicly visible or not.
348
* @return bool Either true or false depending on the success of the operation.
349
*/
350
- public static function reportCriticalEvent($message = '', $internal = false)
351
{
352
- return self::reportEvent(5, $message, $internal);
353
}
354
355
/**
@@ -357,10 +409,9 @@ class SucuriScanEvent extends SucuriScan
357
*
358
* @param string $message Text witht the explanation of the event or action performed.
359
* @param string $action An optional text, hopefully either enabled or disabled.
360
- * @param bool $internal Whether the event will be publicly visible or not.
361
* @return bool Either true or false depending on the success of the operation.
362
*/
363
- public static function reportAutoEvent($message = '', $action = '', $internal = false)
364
{
365
$message = strip_tags($message);
366
@@ -370,14 +421,14 @@ class SucuriScanEvent extends SucuriScan
370
}
371
372
if ($action === 'enabled') {
373
- return self::reportNoticeEvent($message, $internal);
374
}
375
376
if ($action === 'disabled') {
377
- return self::reportErrorEvent($message, $internal);
378
}
379
380
- return self::reportInfoEvent($message, $internal);
381
}
382
383
/**
@@ -647,7 +698,7 @@ class SucuriScanEvent extends SucuriScan
647
}
648
}
649
650
- $response = array(
651
'updated' => is_writable($config_path),
652
'old_keys' => $old_keys,
653
'old_keys_string' => $old_keys_string,
@@ -656,10 +707,10 @@ class SucuriScanEvent extends SucuriScan
656
'new_wpconfig' => $new_wpconfig,
657
);
658
659
- if ($response['updated']) {
660
@file_put_contents($config_path, $new_wpconfig, LOCK_EX);
661
}
662
663
- return $response;
664
}
665
}
34
class SucuriScanEvent extends SucuriScan
35
{
36
/**
37
+ * Creates a cronjob to run the file system scanner.
38
*
39
* Right after a fresh installation of the plugin, it will create a cronjob
40
* that will execute the first scan in the next five minutes. This scan will
43
* list with the checksum of the new file list, if there are differences we
44
* will assume that someone or something modified one or more files and send
45
* an email alsert about the incident.
46
*/
47
+ public static function installScheduledTask()
48
{
49
+ $task_name = 'sucuriscan_scheduled_scan';
50
51
+ if (!wp_next_scheduled($task_name)) {
52
+ wp_schedule_event(time() + 10, 'daily', $task_name);
53
}
54
}
55
169
* in a failure. However, this procedure depends on the ability of the plugin
170
* to write the log into the queue when the previous request failed.
171
*
172
+ * @param string $message Information about the event.
173
+ * @param string $timestamp Time when the event was triggered.
174
+ * @return bool True if the event was logged, false otherwise.
175
+ */
176
+ private static function sendLogToAPI($message = '', $timestamp = '')
177
+ {
178
+ if (empty($message)) {
179
+ return self::throwException('Event identifier cannot be empty');
180
+ }
181
+
182
+ $params = array();
183
+ $params['a'] = 'send_log';
184
+ $params['m'] = $message;
185
+ $params['time'] = $timestamp;
186
+ $args = array('timeout' => 5 /* seconds */);
187
+
188
+ $resp = SucuriScanAPI::apiCallWordpress('POST', $params, true, $args);
189
+
190
+ return (bool) (
191
+ is_array($resp)
192
+ && array_key_exists('status', $resp)
193
+ && intval($resp['status']) === 1
194
+ );
195
+ }
196
+
197
+ /**
198
+ * Sends the event message to a local queue system.
199
+ *
200
+ * @param string $message Information about the security event.
201
* @return bool True if the operation succeeded, false otherwise.
202
*/
203
+ public static function sendLogToQueue($message = '')
204
{
205
/* create storage directory if necessary */
206
SucuriScanInterface::createStorageFolder();
209
* Self-hosted Monitor.
210
*
211
* Send a copy of the event log to a local file, this will allow the
212
+ * administrator of the server to integrate the events monitored by the
213
+ * plugin with a 3rd-party service like OSSEC or similar. More info in
214
+ * the Self-Hosting panel located in the plugin' settings page.
215
*/
216
if (function_exists('sucuriscan_selfhosting_fpath')) {
217
$monitor_fpath = sucuriscan_selfhosting_fpath();
222
date('Y-m-d H:i:s'),
223
SucuriScan::getTopLevelDomain(),
224
SucuriScanOption::getOption(':account'),
225
+ $message
226
);
227
@file_put_contents(
228
$monitor_fpath,
232
}
233
}
234
235
+ /* enqueue the event if the API is enabled */
236
if (SucuriScanOption::isEnabled(':api_service')) {
237
+ $cache = new SucuriScanCache('auditqueue');
238
+ $key = str_replace('.', '_', microtime(true));
239
+ $written = $cache->add($key, $message);
240
}
241
242
return true;
243
}
244
245
+ /**
246
+ * Sends all the events from the queue to the API.
247
+ */
248
+ public static function sendLogsFromQueue()
249
+ {
250
+ if (SucuriScanOption::isDisabled(':api_service')) {
251
+ return;
252
+ }
253
+
254
+ $cache = new SucuriScanCache('auditqueue');
255
+ $finfo = $cache->getDatastoreInfo();
256
+ $events = $cache->getAll();
257
+ $counter = 0;
258
+
259
+ if (!$events) {
260
+ return;
261
+ }
262
+
263
+ /* Send around 15,000 logs for maximum 30 seconds */
264
+ $maxtime = (int) SucuriScan::iniGet('max_execution_time');
265
+ $maxreqs = ($maxtime > 1) ? (500 * $maxtime) : 5000;
266
+
267
+ foreach ($events as $keyname => $message) {
268
+ $offset = strpos($keyname, '_');
269
+ $timestamp = substr($keyname, 0, $offset);
270
+ $status = self::sendLogToAPI($message, $timestamp);
271
+
272
+ if ($status !== true) {
273
+ /* API is down */
274
+ break;
275
+ }
276
+
277
+ /* dequeue event message */
278
+ unset($events[$keyname]);
279
+ $counter++;
280
+
281
+ /* avoid memory limit */
282
+ if ($counter >= $maxreqs) {
283
+ break;
284
+ }
285
+ }
286
+
287
+ $cache->override($events);
288
+ }
289
+
290
/**
291
* Generates an audit event log (to be sent later).
292
*
293
* @param int $severity Importance of the event that will be reported, values from one to five.
294
* @param string $message The explanation of the event.
295
* @return bool True if the event was logged in the monitoring service, false otherwise.
296
*/
297
+ private static function reportEvent($severity = 0, $message = '')
298
{
299
$user = wp_get_current_user();
300
$remote_ip = self::getRemoteAddr();
323
$severity_name = $severities[$severity];
324
}
325
326
/* remove unnecessary characters */
327
$message = strip_tags($message);
328
$message = str_replace("\r", '', $message);
329
$message = str_replace("\n", '', $message);
330
$message = str_replace("\t", '', $message);
331
332
+ return self::sendLogToQueue(sprintf(
333
'%s:%s %s; %s',
334
$severity_name,
335
$username,
342
* Reports a debug event on the website.
343
*
344
* @param string $message Text witht the explanation of the event or action performed.
345
* @return bool Either true or false depending on the success of the operation.
346
*/
347
+ public static function reportDebugEvent($message = '')
348
{
349
+ return self::reportEvent(0, $message);
350
}
351
352
/**
353
* Reports a notice event on the website.
354
*
355
* @param string $message Text witht the explanation of the event or action performed.
356
* @return bool Either true or false depending on the success of the operation.
357
*/
358
+ public static function reportNoticeEvent($message = '')
359
{
360
+ return self::reportEvent(1, $message);
361
}
362
363
/**
364
* Reports a info event on the website.
365
*
366
* @param string $message Text witht the explanation of the event or action performed.
367
* @return bool Either true or false depending on the success of the operation.
368
*/
369
+ public static function reportInfoEvent($message = '')
370
{
371
+ return self::reportEvent(2, $message);
372
}
373
374
/**
375
* Reports a warning event on the website.
376
*
377
* @param string $message Text witht the explanation of the event or action performed.
378
* @return bool Either true or false depending on the success of the operation.
379
*/
380
+ public static function reportWarningEvent($message = '')
381
{
382
+ return self::reportEvent(3, $message);
383
}
384
385
/**
386
* Reports a error event on the website.
387
*
388
* @param string $message Text witht the explanation of the event or action performed.
389
* @return bool Either true or false depending on the success of the operation.
390
*/
391
+ public static function reportErrorEvent($message = '')
392
{
393
+ return self::reportEvent(4, $message);
394
}
395
396
/**
397
* Reports a critical event on the website.
398
*
399
* @param string $message Text witht the explanation of the event or action performed.
400
* @return bool Either true or false depending on the success of the operation.
401
*/
402
+ public static function reportCriticalEvent($message = '')
403
{
404
+ return self::reportEvent(5, $message);
405
}
406
407
/**
409
*
410
* @param string $message Text witht the explanation of the event or action performed.
411
* @param string $action An optional text, hopefully either enabled or disabled.
412
* @return bool Either true or false depending on the success of the operation.
413
*/
414
+ public static function reportAutoEvent($message = '', $action = '')
415
{
416
$message = strip_tags($message);
417
421
}
422
423
if ($action === 'enabled') {
424
+ return self::reportNoticeEvent($message);
425
}
426
427
if ($action === 'disabled') {
428
+ return self::reportErrorEvent($message);
429
}
430
431
+ return self::reportInfoEvent($message);
432
}
433
434
/**
698
}
699
}
700
701
+ $resp = array(
702
'updated' => is_writable($config_path),
703
'old_keys' => $old_keys,
704
'old_keys_string' => $old_keys_string,
707
'new_wpconfig' => $new_wpconfig,
708
);
709
710
+ if ($resp['updated']) {
711
@file_put_contents($config_path, $new_wpconfig, LOCK_EX);
712
}
713
714
+ return $resp;
715
}
716
}
src/fileinfo.lib.php CHANGED
@@ -95,7 +95,12 @@ class SucuriScanFileInfo extends SucuriScan
95
*/
96
public static function isSplAvailable()
97
{
98
- return (bool) (class_exists('SplFileObject') && class_exists('FilesystemIterator'));
99
}
100
101
/**
@@ -180,7 +185,7 @@ class SucuriScanFileInfo extends SucuriScan
180
{
181
$files = array();
182
183
- if (is_dir($directory)) {
184
$objects = array();
185
186
$this->ignored_directories = SucuriScanFSScanner::getIgnoredDirectories();
95
*/
96
public static function isSplAvailable()
97
{
98
+ return (bool) (
99
+ class_exists('SplFileObject')
100
+ && class_exists('FilesystemIterator')
101
+ && class_exists('RecursiveIteratorIterator')
102
+ && class_exists('RecursiveDirectoryIterator')
103
+ );
104
}
105
106
/**
185
{
186
$files = array();
187
188
+ if (is_dir($directory) && self::isSplAvailable()) {
189
$objects = array();
190
191
$this->ignored_directories = SucuriScanFSScanner::getIgnoredDirectories();
src/firewall.lib.php CHANGED
@@ -297,9 +297,11 @@ class SucuriScanFirewall extends SucuriScanAPI
297
*/
298
public static function auditlogsPage()
299
{
300
- $date = date('Y-m-d');
301
$params = array();
302
303
$params['AuditLogs.DateYears'] = self::dates('years', $date);
304
$params['AuditLogs.DateMonths'] = self::dates('months', $date);
305
$params['AuditLogs.DateDays'] = self::dates('days', $date);
297
*/
298
public static function auditlogsPage()
299
{
300
$params = array();
301
302
+ /* logs are available after 24 hours */
303
+ $date = date('Y-m-d', strtotime('-1 day'));
304
+
305
$params['AuditLogs.DateYears'] = self::dates('years', $date);
306
$params['AuditLogs.DateMonths'] = self::dates('months', $date);
307
$params['AuditLogs.DateDays'] = self::dates('days', $date);
src/globals.php CHANGED
@@ -111,14 +111,38 @@ if (defined('SUCURISCAN')) {
111
{
112
global $locale;
113
114
- $filename = sprintf(
115
'%s/languages/%s-%s.po',
116
SUCURISCAN_PLUGIN_PATH,
117
SUCURISCAN_TEXTDOMAIN,
118
$locale
119
);
120
121
- if (!file_exists($filename)) {
122
$locale = 'en_US';
123
setlocale(LC_ALL, 'en_US');
124
}
@@ -203,7 +227,6 @@ if (defined('SUCURISCAN')) {
203
add_action('switch_theme', 'SucuriScanHook::hookThemeSwitch', 50, 5);
204
add_action('transition_post_status', 'SucuriScanHook::hookPostStatus', 50, 3);
205
add_action('user_register', 'SucuriScanHook::hookUserRegister', 50, 5);
206
- add_action('wp_insert_comment', 'SucuriScanHook::hookCommentInsert', 50, 5);
207
add_action('wp_login', 'SucuriScanHook::hookLoginSuccess', 50, 5);
208
add_action('wp_login_failed', 'SucuriScanHook::hookLoginFailure', 50, 5);
209
add_action('wp_trash_post', 'SucuriScanHook::hookPostTrash', 50, 5);
111
{
112
global $locale;
113
114
+ $pofile = sprintf(
115
'%s/languages/%s-%s.po',
116
SUCURISCAN_PLUGIN_PATH,
117
SUCURISCAN_TEXTDOMAIN,
118
$locale
119
);
120
+ $mofile = sprintf(
121
+ '%s/languages/%s-%s.mo',
122
+ SUCURISCAN_PLUGIN_PATH,
123
+ SUCURISCAN_TEXTDOMAIN,
124
+ $locale
125
+ );
126
+
127
+ /* attempt to import the English POT file into LOCALE */
128
+ if (!file_exists($pofile) || !file_exists($mofile)) {
129
+ $en_pofile = sprintf(
130
+ '%s/languages/%s-en_US.po',
131
+ SUCURISCAN_PLUGIN_PATH,
132
+ SUCURISCAN_TEXTDOMAIN
133
+ );
134
+ $en_mofile = sprintf(
135
+ '%s/languages/%s-en_US.mo',
136
+ SUCURISCAN_PLUGIN_PATH,
137
+ SUCURISCAN_TEXTDOMAIN
138
+ );
139
+
140
+ @copy($en_pofile, $pofile);
141
+ @copy($en_mofile, $mofile);
142
+ }
143
144
+ /* fallback to English on language import failure */
145
+ if (!file_exists($pofile) || !file_exists($mofile)) {
146
$locale = 'en_US';
147
setlocale(LC_ALL, 'en_US');
148
}
227
add_action('switch_theme', 'SucuriScanHook::hookThemeSwitch', 50, 5);
228
add_action('transition_post_status', 'SucuriScanHook::hookPostStatus', 50, 3);
229
add_action('user_register', 'SucuriScanHook::hookUserRegister', 50, 5);
230
add_action('wp_login', 'SucuriScanHook::hookLoginSuccess', 50, 5);
231
add_action('wp_login_failed', 'SucuriScanHook::hookLoginFailure', 50, 5);
232
add_action('wp_trash_post', 'SucuriScanHook::hookPostTrash', 50, 5);
src/hook.lib.php CHANGED
@@ -67,51 +67,6 @@ class SucuriScanHook extends SucuriScanEvent
67
self::notifyEvent('post_publication', $message);
68
}
69
70
- /**
71
- * Fires immediately after a comment is inserted into the database.
72
- *
73
- * The action comment-post can also be used to track the insertion of data in
74
- * the comments table, but this only returns the identifier of the new entry in
75
- * the database and the status (approved, not approved, spam). The WP-Insert-
76
- * Comment action returns the same identifier and additionally the full data set
77
- * with the comment information.
78
- *
79
- * @see https://codex.wordpress.org/Plugin_API/Action_Reference/wp_insert_comment
80
- * @see https://codex.wordpress.org/Plugin_API/Action_Reference/comment_post
81
- *
82
- * @param int $id The comment identifier.
83
- * @param object $comment The comment object.
84
- */
85
- public static function hookCommentInsert($id = 0, $comment = null)
86
- {
87
- if (is_object($comment)
88
- && property_exists($comment, 'comment_ID')
89
- && property_exists($comment, 'comment_agent')
90
- && property_exists($comment, 'comment_author_IP')
91
- && SucuriScanOption::isEnabled(':comment_monitor')
92
- ) {
93
- $data_set = array(
94
- 'id' => $comment->comment_ID,
95
- 'post_id' => $comment->comment_post_ID,
96
- 'user_id' => $comment->user_id,
97
- 'parent' => $comment->comment_parent,
98
- 'approved' => $comment->comment_approved,
99
- 'remote_addr' => $comment->comment_author_IP,
100
- 'author_email' => $comment->comment_author_email,
101
- 'date' => $comment->comment_date,
102
- 'content' => $comment->comment_content,
103
- 'user_agent' => $comment->comment_agent,
104
- );
105
- $message = base64_encode(json_encode($data_set));
106
- self::reportNoticeEvent('Base64:' . $message, true);
107
- }
108
- }
109
-
110
- // TODO: Log when the comment status is modified: wp_set_comment_status
111
- // TODO: Log when the comment data is modified: edit_comment
112
- // TODO: Log when the comment is going to be deleted: delete_comment, trash_comment
113
- // TODO: Log when the comment is finally deleted: deleted_comment, trashed_comment
114
- // TODO: Log when the comment is closed: comment_closed
115
// TODO: Detect auto updates in core, themes, and plugin files.
116
117
/**
@@ -620,6 +575,11 @@ class SucuriScanHook extends SucuriScanEvent
620
return self::throwException('Ignore corrupted post data');
621
}
622
623
$post_type = 'post'; /* either post or page */
624
625
if (property_exists($post, 'post_type')) {
67
self::notifyEvent('post_publication', $message);
68
}
69
70
// TODO: Detect auto updates in core, themes, and plugin files.
71
72
/**
575
return self::throwException('Ignore corrupted post data');
576
}
577
578
+ /* ignore; the same */
579
+ if ($old === $new) {
580
+ return;
581
+ }
582
+
583
$post_type = 'post'; /* either post or page */
584
585
if (property_exists($post, 'post_type')) {
src/integrity.lib.php CHANGED
@@ -119,45 +119,54 @@ class SucuriScanIntegrity
119
120
foreach ((array) $core_files as $file_meta) {
121
if (strpos($file_meta, '@')) {
122
- @list($status_type, $file_path) =
123
- explode('@', $file_meta, 2);
124
-
125
- if ($file_path && $status_type) {
126
- $full_path = ABSPATH . '/' . $file_path;
127
-
128
- switch ($action) {
129
- case 'restore':
130
- if ($content = SucuriScanAPI::getOriginalCoreFile($file_path)) {
131
- $basedir = dirname($full_path);
132
- @mkdir($basedir, 0755, true);
133
-
134
- if (file_exists($basedir)) {
135
- $restored = @file_put_contents($full_path, $content);
136
- $files_processed += ($restored ? 1 : 0);
137
- $files_affected[] = $full_path;
138
- }
139
- }
140
- break;
141
-
142
- case 'fixed':
143
- $cache_key = md5($file_path);
144
- $cache_value = array(
145
- 'file_path' => $file_path,
146
- 'file_status' => $status_type,
147
- 'ignored_at' => time(),
148
- );
149
- $cached = $cache->add($cache_key, $cache_value);
150
- $files_processed += ($cached ? 1 : 0);
151
$files_affected[] = $full_path;
152
- break;
153
154
- case 'delete':
155
- if (@unlink($full_path)) {
156
- $files_processed += 1;
157
- $files_affected[] = $full_path;
158
- }
159
- break;
160
}
161
}
162
}
163
}
119
120
foreach ((array) $core_files as $file_meta) {
121
if (strpos($file_meta, '@')) {
122
+ @list($status_type, $file_path) = explode('@', $file_meta, 2);
123
+
124
+ if (!$file_path || !$status_type) {
125
+ continue;
126
+ }
127
+
128
+ $full_path = ABSPATH . '/' . $file_path;
129
+
130
+ if ($action === 'fixed' && (
131
+ $status_type === 'added'
132
+ || $status_type === 'removed'
133
+ || $status_type === 'modified'
134
+ )) {
135
+ $cache_key = md5($file_path);
136
+ $cache_value = array(
137
+ 'file_path' => $file_path,
138
+ 'file_status' => $status_type,
139
+ 'ignored_at' => time(),
140
+ );
141
+ $cached = $cache->add($cache_key, $cache_value);
142
+ $files_processed += ($cached ? 1 : 0);
143
+ $files_affected[] = $full_path;
144
+ continue;
145
+ }
146
+
147
+ if ($action === 'restore' && (
148
+ $status_type === 'removed'
149
+ || $status_type === 'modified'
150
+ )) {
151
+ if ($content = SucuriScanAPI::getOriginalCoreFile($file_path)) {
152
+ $basedir = dirname($full_path);
153
+ @mkdir($basedir, 0755, true);
154
+
155
+ if (file_exists($basedir)) {
156
+ $restored = @file_put_contents($full_path, $content);
157
+ $files_processed += ($restored ? 1 : 0);
158
$files_affected[] = $full_path;
159
+ }
160
+ }
161
+ continue;
162
+ }
163
164
+ if ($action === 'delete' && $status_type === 'added') {
165
+ if (@unlink($full_path)) {
166
+ $files_processed += 1;
167
+ $files_affected[] = $full_path;
168
}
169
+ continue;
170
}
171
}
172
}
src/interface.lib.php CHANGED
<
@@ -37,14 +37,12 @@ class SucuriScanInterface
37
*/
38
public static function initialize()
39
{
40
- if (SucuriScan::supportReverseProxy()
41
- || SucuriScan::isBehindFirewall()
42
- ) {
43
$_SERVER['SUCURIREAL_REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
44
$_SERVER['REMOTE_ADDR'] = SucuriScan::getRemoteAddr();
45
}
46
-
47
- SucuriScanEvent::scheduleTask(false);
48
}
49
50
/**
@@ -189,11 +187,30 @@ class SucuriScanInterface
189
*/
190
public static function noticeAfterUpdate()
191
{
192
/* use simple comparison to force type cast. */
193
- if (SucuriScanOption::getOption(':plugin_version') == SUCURISCAN_VERSION) {
194
return;
195
}
196
197
/**
198
* Suggest re-activation of the API communication.
199
*
@@ -218,9 +235,6 @@ class SucuriScanInterface
218
* @date Featured added at - May 01, 2017
219
*/
220
self::info(__('NewsletterInvitation', SUCURISCAN_TEXTDOMAIN));
221
-
222
- /* update the version number in the plugin settings. */
223
- SucuriScanOption::updateOption(':plugin_version', SUCURISCAN_VERSION);
224
}
225