Version Description
- Multiple bug fixes (as reported on the support forums).
- Added heartbeat for the file scans.
- Code cleanup.
Download this release
Release Info
Developer | dd@sucuri.net |
Plugin | Sucuri Security – Auditing, Malware Scanner and Security Hardening |
Version | 1.6.9 |
Comparing to | |
See all releases |
Code changes from version 1.6.8 to 1.6.9
- inc/css/sucuriscan-default-css.css +7 -5
- inc/images/menu-icon.png +0 -0
- inc/images/ok.png +0 -0
- inc/images/warn.png +0 -0
- inc/tpl/integrity-modifiedfiles.html.tpl +3 -3
- inc/tpl/lastlogins-all.html.tpl +6 -3
- inc/tpl/lastlogins-loggedin.html.tpl +2 -2
- inc/tpl/monitoring-logs.html.tpl +2 -3
- inc/tpl/monitoring-logs.snippet.tpl +53 -46
- inc/tpl/settings-general.html.tpl +27 -6
- inc/tpl/settings-heartbeat.html.tpl +84 -0
- inc/tpl/settings.html.tpl +7 -0
- readme.txt +8 -3
- sucuri.php +572 -138
inc/css/sucuriscan-default-css.css
CHANGED
@@ -181,12 +181,13 @@ div.sucuriscan-alert > a.close{position:absolute;top:10px;right:10px;font-size:1
|
|
181 |
.sucuriscan-monitoring-logs .sucuriscan-monitoring-date-form select + select{width:112px}
|
182 |
.sucuriscan-monitoring-logs .sucuriscan-monitoring-date-form select + select + select{width:60px}
|
183 |
.sucuriscan-monitoring-logs .sucuriscan-target-date{font-size:12px;color:#999;margin-right:5px}
|
|
|
|
|
184 |
/* Monitoring AccessLog Styles */
|
185 |
.sucuriscan-request-summary{margin:-15px;margin-top:-3px}
|
186 |
-
.sucuriscan-request-summary
|
187 |
-
.sucuriscan-request-summary
|
188 |
-
.sucuriscan-request-summary
|
189 |
-
.sucuriscan-request-summary span{max-width:395px;font-family:monospace;vertical-align:top;word-break:break-all}
|
190 |
/* Hardening Status */
|
191 |
.sucuriscan-hstatus{position:relative;margin:0 -12px;padding:10px 12px;border:1px solid transparent}
|
192 |
.sucuriscan-hstatus-1{background-color:#dff0d8;color:#3c763d;border-color:#d6e9c6}
|
@@ -219,8 +220,9 @@ div.sucuriscan-alert > a.close{position:absolute;top:10px;right:10px;font-size:1
|
|
219 |
.sucuriscan-maincontent .sucuriscan-full-textarea{width:100%;height:400px;line-height:normal;resize:vertical;padding:10px}
|
220 |
.sucuriscan-maincontent .sucuriscan-settings{margin-top:0}
|
221 |
.sucuriscan-maincontent .sucuriscan-settings form{display:inline-block}
|
222 |
-
.sucuriscan-maincontent .sucuriscan-settings select, .sucuriscan-maincontent .sucuriscan-settings .input-text{
|
223 |
.sucuriscan-maincontent .sucuriscan-settings-notifications{margin-top:0}
|
|
|
224 |
.sucuriscan-maincontent .sucuriscan-wpcron-list{margin-top:0}
|
225 |
/* Responsive Styles */
|
226 |
@media (max-width: 620px) {
|
181 |
.sucuriscan-monitoring-logs .sucuriscan-monitoring-date-form select + select{width:112px}
|
182 |
.sucuriscan-monitoring-logs .sucuriscan-monitoring-date-form select + select + select{width:60px}
|
183 |
.sucuriscan-monitoring-logs .sucuriscan-target-date{font-size:12px;color:#999;margin-right:5px}
|
184 |
+
.sucuriscan-monitoring-logs .sucuriscan-denial-type{font-size:14px}
|
185 |
+
.sucuriscan-monitoring-logs .sucuriscan-denial-type-date{font-style:italic;color:#999}
|
186 |
/* Monitoring AccessLog Styles */
|
187 |
.sucuriscan-request-summary{margin:-15px;margin-top:-3px}
|
188 |
+
.sucuriscan-request-summary td{font-size:14px}
|
189 |
+
.sucuriscan-request-summary tr td:first-child{font-weight:bold}
|
190 |
+
.sucuriscan-request-summary td+td{font-family:monospace;word-break:break-all}
|
|
|
191 |
/* Hardening Status */
|
192 |
.sucuriscan-hstatus{position:relative;margin:0 -12px;padding:10px 12px;border:1px solid transparent}
|
193 |
.sucuriscan-hstatus-1{background-color:#dff0d8;color:#3c763d;border-color:#d6e9c6}
|
220 |
.sucuriscan-maincontent .sucuriscan-full-textarea{width:100%;height:400px;line-height:normal;resize:vertical;padding:10px}
|
221 |
.sucuriscan-maincontent .sucuriscan-settings{margin-top:0}
|
222 |
.sucuriscan-maincontent .sucuriscan-settings form{display:inline-block}
|
223 |
+
.sucuriscan-maincontent .sucuriscan-settings select, .sucuriscan-maincontent .sucuriscan-settings .input-text{width:220px}
|
224 |
.sucuriscan-maincontent .sucuriscan-settings-notifications{margin-top:0}
|
225 |
+
.sucuriscan-maincontent .sucuriscan-settings-heartbeat{}
|
226 |
.sucuriscan-maincontent .sucuriscan-wpcron-list{margin-top:0}
|
227 |
/* Responsive Styles */
|
228 |
@media (max-width: 620px) {
|
inc/images/menu-icon.png
CHANGED
Binary file
|
inc/images/ok.png
DELETED
Binary file
|
inc/images/warn.png
DELETED
Binary file
|
inc/tpl/integrity-modifiedfiles.html.tpl
CHANGED
@@ -22,9 +22,9 @@
|
|
22 |
</tr>
|
23 |
|
24 |
<tr>
|
25 |
-
<th
|
26 |
-
<th>CheckSum</th>
|
27 |
-
<th width="
|
28 |
</tr>
|
29 |
</thead>
|
30 |
|
22 |
</tr>
|
23 |
|
24 |
<tr>
|
25 |
+
<th>Filepath</th>
|
26 |
+
<th width="130">CheckSum</th>
|
27 |
+
<th width="200">Modification</th>
|
28 |
</tr>
|
29 |
</thead>
|
30 |
|
inc/tpl/lastlogins-all.html.tpl
CHANGED
@@ -3,9 +3,12 @@
|
|
3 |
<thead>
|
4 |
<tr>
|
5 |
<th colspan="6" class="thead-with-button">
|
6 |
-
<span>User last logins</span>
|
7 |
-
<span class="thead-topright-action
|
8 |
-
%%SUCURI.
|
|
|
|
|
|
|
9 |
</span>
|
10 |
</th>
|
11 |
</tr>
|
3 |
<thead>
|
4 |
<tr>
|
5 |
<th colspan="6" class="thead-with-button">
|
6 |
+
<span>User last logins (%%SUCURI.UserList.Total%%)</span>
|
7 |
+
<span class="thead-topright-action">
|
8 |
+
<form action="%%SUCURI.URL.Lastlogins%%" method="post">
|
9 |
+
<input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
|
10 |
+
<button type="submit" name="sucuriscan_reset_lastlogins" class="button button-primary">Reset logs</button>
|
11 |
+
</form>
|
12 |
</span>
|
13 |
</th>
|
14 |
</tr>
|
inc/tpl/lastlogins-loggedin.html.tpl
CHANGED
@@ -7,8 +7,8 @@
|
|
7 |
<tr>
|
8 |
<th>ID</th>
|
9 |
<th>Username</th>
|
10 |
-
<th>Last Activity
|
11 |
-
<th>Registered
|
12 |
<th>IP Address</th>
|
13 |
<th> </th>
|
14 |
</tr>
|
7 |
<tr>
|
8 |
<th>ID</th>
|
9 |
<th>Username</th>
|
10 |
+
<th>Last Activity</th>
|
11 |
+
<th>Registered</th>
|
12 |
<th>IP Address</th>
|
13 |
<th> </th>
|
14 |
</tr>
|
inc/tpl/monitoring-logs.html.tpl
CHANGED
@@ -45,9 +45,8 @@
|
|
45 |
</tr>
|
46 |
|
47 |
<tr>
|
48 |
-
<th>Denial Type</th>
|
49 |
-
<th>
|
50 |
-
<th>Remote Address</th>
|
51 |
<th>Request Path</th>
|
52 |
</tr>
|
53 |
</thead>
|
45 |
</tr>
|
46 |
|
47 |
<tr>
|
48 |
+
<th width="250">Denial Type</th>
|
49 |
+
<th width="120">Remote Address</th>
|
|
|
50 |
<th>Request Path</th>
|
51 |
</tr>
|
52 |
</thead>
|
inc/tpl/monitoring-logs.snippet.tpl
CHANGED
@@ -1,10 +1,8 @@
|
|
1 |
|
2 |
<tr class="%%SUCURI.AuditLog.CssClass%%">
|
3 |
-
<td>%%SUCURI.AuditLog.SucuriBlockReason%%</td>
|
4 |
<td>
|
5 |
-
<span class="sucuriscan-
|
6 |
-
|
7 |
-
</span>
|
8 |
</td>
|
9 |
<td><span class="sucuriscan-monospace">%%SUCURI.AuditLog.RemoteAddr%%</span></td>
|
10 |
<td>
|
@@ -16,48 +14,57 @@
|
|
16 |
|
17 |
<div id="sucuriscan-reqsummary-%%SUCURI.AuditLog.Id%%" style="display:none">
|
18 |
<div class="sucuriscan-request-summary">
|
19 |
-
<
|
20 |
-
<
|
21 |
-
<
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
</div>
|
62 |
</div>
|
63 |
</td>
|
1 |
|
2 |
<tr class="%%SUCURI.AuditLog.CssClass%%">
|
|
|
3 |
<td>
|
4 |
+
<span class="sucuriscan-denial-type">%%SUCURI.AuditLog.SucuriBlockReason%%</span><br>
|
5 |
+
<span class="sucuriscan-denial-type-date">Date/Time: %%SUCURI.AuditLog.LocalRequestTime%%</span>
|
|
|
6 |
</td>
|
7 |
<td><span class="sucuriscan-monospace">%%SUCURI.AuditLog.RemoteAddr%%</span></td>
|
8 |
<td>
|
14 |
|
15 |
<div id="sucuriscan-reqsummary-%%SUCURI.AuditLog.Id%%" style="display:none">
|
16 |
<div class="sucuriscan-request-summary">
|
17 |
+
<table class="wp-list-table widefat">
|
18 |
+
<thead>
|
19 |
+
<tr>
|
20 |
+
<th width="200">Information</th>
|
21 |
+
<th> </th>
|
22 |
+
</tr>
|
23 |
+
</thead>
|
24 |
+
|
25 |
+
<tbody>
|
26 |
+
<tr class="alternate">
|
27 |
+
<td>Blocked Reason</td>
|
28 |
+
<td>%%SUCURI.AuditLog.SucuriBlockReason%%</td>
|
29 |
+
</tr>
|
30 |
+
<tr>
|
31 |
+
<td>Remote Address</td>
|
32 |
+
<td>%%SUCURI.AuditLog.RemoteAddr%%</td>
|
33 |
+
</tr>
|
34 |
+
<tr class="alternate">
|
35 |
+
<td>Date & Time (Local Time)</td>
|
36 |
+
<td>%%SUCURI.AuditLog.LocalRequestTime%%</td>
|
37 |
+
</tr>
|
38 |
+
<tr>
|
39 |
+
<td>Resource Path</td>
|
40 |
+
<td>%%SUCURI.AuditLog.ResourcePath%%</td>
|
41 |
+
</tr>
|
42 |
+
<tr class="alternate">
|
43 |
+
<td>Request Method</td>
|
44 |
+
<td>%%SUCURI.AuditLog.RequestMethod%%</td>
|
45 |
+
</tr>
|
46 |
+
<tr>
|
47 |
+
<td>HTTP Protocol</td>
|
48 |
+
<td>%%SUCURI.AuditLog.HttpProtocol%%</td>
|
49 |
+
</tr>
|
50 |
+
<tr class="alternate">
|
51 |
+
<td>HTTP Status</td>
|
52 |
+
<td>%%SUCURI.AuditLog.HttpStatus%% %%SUCURI.AuditLog.HttpStatusTitle%%</td>
|
53 |
+
</tr>
|
54 |
+
<tr>
|
55 |
+
<td>HTTP Bytes Sent</td>
|
56 |
+
<td>%%SUCURI.AuditLog.HttpBytesSent%%</td>
|
57 |
+
</tr>
|
58 |
+
<tr class="alternate">
|
59 |
+
<td>HTTP Referer</td>
|
60 |
+
<td>%%SUCURI.AuditLog.HttpReferer%%</td>
|
61 |
+
</tr>
|
62 |
+
<tr>
|
63 |
+
<td>HTTP User Agent</td>
|
64 |
+
<td>%%SUCURI.AuditLog.HttpUserAgent%%</td>
|
65 |
+
</tr>
|
66 |
+
</tbody>
|
67 |
+
</table>
|
68 |
</div>
|
69 |
</div>
|
70 |
</td>
|
inc/tpl/settings-general.html.tpl
CHANGED
@@ -5,7 +5,7 @@
|
|
5 |
<thead>
|
6 |
<tr>
|
7 |
<th colspan="3" class="thead-with-button">
|
8 |
-
<span>
|
9 |
<form action="%%SUCURI.URL.Settings%%" method="post" class="thead-topright-action">
|
10 |
<input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
|
11 |
<button type="submit" name="sucuriscan_reset_options" class="button-primary">Reset plugin options</button>
|
@@ -25,6 +25,15 @@
|
|
25 |
the domain of this site, this will allow you to have access to our free
|
26 |
monitoring tool forever even if you remove the API key and generate it again.
|
27 |
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
</td>
|
29 |
</tr>
|
30 |
|
@@ -107,6 +116,18 @@
|
|
107 |
</tr>
|
108 |
|
109 |
<tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
110 |
<td>Filesystem scanner</td>
|
111 |
<td>%%SUCURI.FsScannerStatus%%</td>
|
112 |
<td class="td-with-button">
|
@@ -118,7 +139,7 @@
|
|
118 |
</td>
|
119 |
</tr>
|
120 |
|
121 |
-
<tr
|
122 |
<td>Scan modified files</td>
|
123 |
<td>%%SUCURI.ScanModfilesStatus%%</td>
|
124 |
<td class="td-with-button">
|
@@ -130,7 +151,7 @@
|
|
130 |
</td>
|
131 |
</tr>
|
132 |
|
133 |
-
<tr>
|
134 |
<td>Integrity checking</td>
|
135 |
<td>%%SUCURI.ScanChecksumsStatus%%</td>
|
136 |
<td class="td-with-button">
|
@@ -142,7 +163,7 @@
|
|
142 |
</td>
|
143 |
</tr>
|
144 |
|
145 |
-
<tr
|
146 |
<td>Last Scanning</td>
|
147 |
<td><span class="sucuriscan-monospace">%%SUCURI.ScanningRuntimeHuman%%</span></td>
|
148 |
<td class="td-with-button">
|
@@ -153,7 +174,7 @@
|
|
153 |
</td>
|
154 |
</tr>
|
155 |
|
156 |
-
<tr>
|
157 |
<td>Scanning frequency</td>
|
158 |
<td>%%SUCURI.ScanningFrequency%%</td>
|
159 |
<td class="td-with-button">
|
@@ -167,7 +188,7 @@
|
|
167 |
</td>
|
168 |
</tr>
|
169 |
|
170 |
-
<tr class="
|
171 |
<td>Scanning interface</td>
|
172 |
<td>%%SUCURI.ScanningInterface%%</td>
|
173 |
<td class="td-with-button">
|
5 |
<thead>
|
6 |
<tr>
|
7 |
<th colspan="3" class="thead-with-button">
|
8 |
+
<span>General Settings</span>
|
9 |
<form action="%%SUCURI.URL.Settings%%" method="post" class="thead-topright-action">
|
10 |
<input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
|
11 |
<button type="submit" name="sucuriscan_reset_options" class="button-primary">Reset plugin options</button>
|
25 |
the domain of this site, this will allow you to have access to our free
|
26 |
monitoring tool forever even if you remove the API key and generate it again.
|
27 |
</p>
|
28 |
+
|
29 |
+
<div class="sucuriscan-inline-alert-warning sucuriscan-%%SUCURI.InvalidDomainVisibility%%">
|
30 |
+
<p>
|
31 |
+
Your domain <code>%%SUCURI.CleanDomain%%</code> does not seems to have a DNS
|
32 |
+
<code>A</code> record so it will be considered as <em>invalid</em> by the API
|
33 |
+
interface when you request the generation of a new key. Adding <code>www</code>
|
34 |
+
at the beginning of the domain name may fix this issue.
|
35 |
+
</p>
|
36 |
+
</div>
|
37 |
</td>
|
38 |
</tr>
|
39 |
|
116 |
</tr>
|
117 |
|
118 |
<tr>
|
119 |
+
<td>API request timeout</td>
|
120 |
+
<td>%%SUCURI.RequestTimeout%%</td>
|
121 |
+
<td class="td-with-button">
|
122 |
+
<form action="%%SUCURI.URL.Settings%%" method="post">
|
123 |
+
<input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
|
124 |
+
<input type="text" name="sucuriscan_request_timeout" class="input-text" placeholder="Timeout in seconds..." />
|
125 |
+
<button type="submit" class="button-primary">Change</button>
|
126 |
+
</form>
|
127 |
+
</td>
|
128 |
+
</tr>
|
129 |
+
|
130 |
+
<tr class="alternate">
|
131 |
<td>Filesystem scanner</td>
|
132 |
<td>%%SUCURI.FsScannerStatus%%</td>
|
133 |
<td class="td-with-button">
|
139 |
</td>
|
140 |
</tr>
|
141 |
|
142 |
+
<tr>
|
143 |
<td>Scan modified files</td>
|
144 |
<td>%%SUCURI.ScanModfilesStatus%%</td>
|
145 |
<td class="td-with-button">
|
151 |
</td>
|
152 |
</tr>
|
153 |
|
154 |
+
<tr class="alternate">
|
155 |
<td>Integrity checking</td>
|
156 |
<td>%%SUCURI.ScanChecksumsStatus%%</td>
|
157 |
<td class="td-with-button">
|
163 |
</td>
|
164 |
</tr>
|
165 |
|
166 |
+
<tr>
|
167 |
<td>Last Scanning</td>
|
168 |
<td><span class="sucuriscan-monospace">%%SUCURI.ScanningRuntimeHuman%%</span></td>
|
169 |
<td class="td-with-button">
|
174 |
</td>
|
175 |
</tr>
|
176 |
|
177 |
+
<tr class="alternate">
|
178 |
<td>Scanning frequency</td>
|
179 |
<td>%%SUCURI.ScanningFrequency%%</td>
|
180 |
<td class="td-with-button">
|
188 |
</td>
|
189 |
</tr>
|
190 |
|
191 |
+
<tr class="sucuriscan-%%SUCURI.ScanningInterfaceVisibility%%">
|
192 |
<td>Scanning interface</td>
|
193 |
<td>%%SUCURI.ScanningInterface%%</td>
|
194 |
<td class="td-with-button">
|
inc/tpl/settings-heartbeat.html.tpl
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
<div id="poststuff">
|
3 |
+
<div class="postbox sucuriscan-border sucuriscan-table-description">
|
4 |
+
<h3>Heartbeat</h3>
|
5 |
+
|
6 |
+
<div class="inside">
|
7 |
+
<p>
|
8 |
+
The purpose of the <a href="https://core.trac.wordpress.org/ticket/23216"
|
9 |
+
target="_blank">Heartbeat API</a> is to simulate bidirectional connection
|
10 |
+
between the browser and the server. Initially it was used for autosave, post
|
11 |
+
locking and log-in expiration warning while a user is writing or editing. The
|
12 |
+
idea was to have an API that sends XHR <em>(XML HTTP Request)</em> requests to
|
13 |
+
the server every fifteen seconds and triggers events <em>(or callbacks)</em> on
|
14 |
+
receiving data.
|
15 |
+
</p>
|
16 |
+
</div>
|
17 |
+
</div>
|
18 |
+
</div>
|
19 |
+
|
20 |
+
<table class="wp-list-table widefat sucuriscan-table sucuriscan-settings sucuriscan-settings-heartbeat">
|
21 |
+
<thead>
|
22 |
+
<tr>
|
23 |
+
<th>Option</th>
|
24 |
+
<th>Value</th>
|
25 |
+
<th> </th>
|
26 |
+
</tr>
|
27 |
+
</thead>
|
28 |
+
|
29 |
+
<tbody>
|
30 |
+
<tr>
|
31 |
+
<td>Heartbeat status</td>
|
32 |
+
<td>%%SUCURI.HeartbeatStatus%%</td>
|
33 |
+
<td class="td-with-button">
|
34 |
+
<form action="%%SUCURI.URL.Settings%%#settings-heartbeat" method="post">
|
35 |
+
<input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
|
36 |
+
<select name="sucuriscan_heartbeat_status">
|
37 |
+
%%SUCURI.HeartbeatStatusOptions%%
|
38 |
+
</select>
|
39 |
+
<button type="submit" class="button-primary">Change</button>
|
40 |
+
</form>
|
41 |
+
</td>
|
42 |
+
</tr>
|
43 |
+
|
44 |
+
<tr class="alternate">
|
45 |
+
<td>Pulse interval</td>
|
46 |
+
<td>%%SUCURI.HeartbeatPulse%%</td>
|
47 |
+
<td class="td-with-button">
|
48 |
+
<form action="%%SUCURI.URL.Settings%%#settings-heartbeat" method="post">
|
49 |
+
<input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
|
50 |
+
<select name="sucuriscan_heartbeat_pulse">
|
51 |
+
%%SUCURI.HeartbeatPulseOptions%%
|
52 |
+
</select>
|
53 |
+
<button type="submit" class="button-primary">Change</button>
|
54 |
+
</form>
|
55 |
+
</td>
|
56 |
+
</tr>
|
57 |
+
|
58 |
+
<tr>
|
59 |
+
<td>Interval speed</td>
|
60 |
+
<td>%%SUCURI.HeartbeatInterval%%</td>
|
61 |
+
<td class="td-with-button">
|
62 |
+
<form action="%%SUCURI.URL.Settings%%#settings-heartbeat" method="post">
|
63 |
+
<input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
|
64 |
+
<select name="sucuriscan_heartbeat_interval">
|
65 |
+
%%SUCURI.HeartbeatIntervalOptions%%
|
66 |
+
</select>
|
67 |
+
<button type="submit" class="button-primary">Change</button>
|
68 |
+
</form>
|
69 |
+
</td>
|
70 |
+
</tr>
|
71 |
+
|
72 |
+
<tr class="alternate">
|
73 |
+
<td>Auto-start</td>
|
74 |
+
<td>%%SUCURI.HeartbeatAutostart%%</td>
|
75 |
+
<td class="td-with-button">
|
76 |
+
<form action="%%SUCURI.URL.Settings%%#settings-heartbeat" method="post">
|
77 |
+
<input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
|
78 |
+
<input type="hidden" name="sucuriscan_heartbeat_autostart" value="%%SUCURI.HeartbeatAutostartSwitchValue%%" />
|
79 |
+
<button type="submit" class="button-primary %%SUCURI.HeartbeatAutostartSwitchCssClass%%">%%SUCURI.HeartbeatAutostartSwitchText%%</button>
|
80 |
+
</form>
|
81 |
+
</td>
|
82 |
+
</tr>
|
83 |
+
</tbody>
|
84 |
+
</table>
|
inc/tpl/settings.html.tpl
CHANGED
@@ -10,6 +10,9 @@
|
|
10 |
<li>
|
11 |
<a href="#" data-tabname="settings-ignorerules">Ignore Notifications</a>
|
12 |
</li>
|
|
|
|
|
|
|
13 |
</ul>
|
14 |
|
15 |
<div class="sucuriscan-tab-containers">
|
@@ -24,5 +27,9 @@
|
|
24 |
<div id="sucuriscan-settings-ignorerules">
|
25 |
%%SUCURI.Settings.IgnoreRules%%
|
26 |
</div>
|
|
|
|
|
|
|
|
|
27 |
</div>
|
28 |
</div>
|
10 |
<li>
|
11 |
<a href="#" data-tabname="settings-ignorerules">Ignore Notifications</a>
|
12 |
</li>
|
13 |
+
<li>
|
14 |
+
<a href="#" data-tabname="settings-heartbeat">Heartbeat</a>
|
15 |
+
</li>
|
16 |
</ul>
|
17 |
|
18 |
<div class="sucuriscan-tab-containers">
|
27 |
<div id="sucuriscan-settings-ignorerules">
|
28 |
%%SUCURI.Settings.IgnoreRules%%
|
29 |
</div>
|
30 |
+
|
31 |
+
<div id="sucuriscan-settings-heartbeat">
|
32 |
+
%%SUCURI.Settings.Heartbeat%%
|
33 |
+
</div>
|
34 |
</div>
|
35 |
</div>
|
readme.txt
CHANGED
@@ -3,10 +3,10 @@ Contributors: dd@sucuri.net
|
|
3 |
Donate Link: http://sitecheck.sucuri.net
|
4 |
Tags: malware, security, firewall, scan, spam, virus, sucuri, protection
|
5 |
Requires at least:3.2
|
6 |
-
Stable tag:1.6.
|
7 |
-
Tested up to:
|
8 |
|
9 |
-
The Sucuri Security
|
10 |
|
11 |
== Description ==
|
12 |
|
@@ -66,6 +66,11 @@ the compromise on your site).
|
|
66 |
|
67 |
== Changelog ==
|
68 |
|
|
|
|
|
|
|
|
|
|
|
69 |
= 1.6.8 =
|
70 |
* Fixing interface.
|
71 |
|
3 |
Donate Link: http://sitecheck.sucuri.net
|
4 |
Tags: malware, security, firewall, scan, spam, virus, sucuri, protection
|
5 |
Requires at least:3.2
|
6 |
+
Stable tag:1.6.9
|
7 |
+
Tested up to: 4.0
|
8 |
|
9 |
+
The Sucuri WordPress Security plugin provides the website owner the best Activity Auditing, SiteCheck Remote Malware Scanning, Effective Security Hardening and Post-Hack features.
|
10 |
|
11 |
== Description ==
|
12 |
|
66 |
|
67 |
== Changelog ==
|
68 |
|
69 |
+
= 1.6.9 =
|
70 |
+
* Multiple bug fixes (as reported on the support forums).
|
71 |
+
* Added heartbeat for the file scans.
|
72 |
+
* Code cleanup.
|
73 |
+
|
74 |
= 1.6.8 =
|
75 |
* Fixing interface.
|
76 |
|
sucuri.php
CHANGED
@@ -2,9 +2,9 @@
|
|
2 |
/*
|
3 |
Plugin Name: Sucuri Security - Auditing, Malware Scanner and Hardening
|
4 |
Plugin URI: http://wordpress.sucuri.net/
|
5 |
-
Description: The <a href="http://sucuri.net/" target="_blank">Sucuri
|
6 |
Author: Sucuri, INC
|
7 |
-
Version: 1.6.
|
8 |
Author URI: http://sucuri.net
|
9 |
*/
|
10 |
|
@@ -66,7 +66,7 @@ define('SUCURISCAN', 'sucuriscan');
|
|
66 |
/**
|
67 |
* Current version of the plugin's code.
|
68 |
*/
|
69 |
-
define('SUCURISCAN_VERSION', '1.6.
|
70 |
|
71 |
/**
|
72 |
* The name of the Sucuri plugin main file.
|
@@ -319,6 +319,22 @@ if( defined('SUCURISCAN') ){
|
|
319 |
$sucuriscan_admin_notice_name = SucuriScan::is_multisite() ? 'network_admin_notices' : 'admin_notices';
|
320 |
add_action( $sucuriscan_admin_notice_name, 'SucuriScanInterface::setup_notice' );
|
321 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
322 |
}
|
323 |
|
324 |
/**
|
@@ -571,6 +587,28 @@ class SucuriScan {
|
|
571 |
return FALSE;
|
572 |
}
|
573 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
574 |
/**
|
575 |
* Check whether the site is behing the Sucuri CloudProxy network.
|
576 |
*
|
@@ -578,14 +616,9 @@ class SucuriScan {
|
|
578 |
* @return boolean Either TRUE or FALSE if the site is behind CloudProxy.
|
579 |
*/
|
580 |
public static function is_behind_cloudproxy( $verbose=FALSE ){
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
$http_host = 'localhost';
|
585 |
-
}
|
586 |
-
|
587 |
-
$host_by_name = gethostbyname($http_host);
|
588 |
-
$host_by_addr = gethostbyaddr($host_by_name);
|
589 |
$status = (bool) preg_match('/^cloudproxy[0-9]+\.sucuri\.net$/', $host_by_addr);
|
590 |
|
591 |
if( $verbose ){
|
@@ -600,6 +633,45 @@ class SucuriScan {
|
|
600 |
return $status;
|
601 |
}
|
602 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
603 |
/**
|
604 |
* Return the time passed since the specified timestamp until now.
|
605 |
*
|
@@ -718,6 +790,47 @@ class SucuriScan {
|
|
718 |
return $text;
|
719 |
}
|
720 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
721 |
}
|
722 |
|
723 |
/**
|
@@ -1676,6 +1789,79 @@ class SucuriScanCache extends SucuriScan {
|
|
1676 |
*/
|
1677 |
class SucuriScanOption extends SucuriScanRequest {
|
1678 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1679 |
/**
|
1680 |
* Retrieve specific options from the database.
|
1681 |
*
|
@@ -1831,63 +2017,6 @@ class SucuriScanOption extends SucuriScanRequest {
|
|
1831 |
return $option_value;
|
1832 |
}
|
1833 |
|
1834 |
-
/**
|
1835 |
-
* Retrieve the default values for some specific options.
|
1836 |
-
*
|
1837 |
-
* @param string|array $settings Either an array that will be complemented or a string with the name of the option.
|
1838 |
-
* @return string|array The default values for the specified options.
|
1839 |
-
*/
|
1840 |
-
private static function get_default_options( $settings='' ){
|
1841 |
-
$admin_email = '';
|
1842 |
-
|
1843 |
-
// Use framework built-in function.
|
1844 |
-
if( function_exists('get_option') ){
|
1845 |
-
$admin_email = get_option('admin_email');
|
1846 |
-
}
|
1847 |
-
|
1848 |
-
$default_options = array(
|
1849 |
-
'sucuriscan_api_key' => FALSE,
|
1850 |
-
'sucuriscan_account' => $admin_email,
|
1851 |
-
'sucuriscan_fs_scanner' => 'enabled',
|
1852 |
-
'sucuriscan_scan_frequency' => 'hourly',
|
1853 |
-
'sucuriscan_scan_interface' => 'spl',
|
1854 |
-
'sucuriscan_scan_modfiles' => 'enabled',
|
1855 |
-
'sucuriscan_scan_checksums' => 'enabled',
|
1856 |
-
'sucuriscan_runtime' => 0,
|
1857 |
-
'sucuriscan_lastlogin_redirection' => 'enabled',
|
1858 |
-
'sucuriscan_notify_to' => $admin_email,
|
1859 |
-
'sucuriscan_emails_sent' => 0,
|
1860 |
-
'sucuriscan_emails_per_hour' => 5,
|
1861 |
-
'sucuriscan_last_email_at' => time(),
|
1862 |
-
'sucuriscan_prettify_mails' => 'enabled',
|
1863 |
-
'sucuriscan_notify_success_login' => 'enabled',
|
1864 |
-
'sucuriscan_notify_failed_login' => 'enabled',
|
1865 |
-
'sucuriscan_notify_post_publication' => 'enabled',
|
1866 |
-
'sucuriscan_notify_theme_editor' => 'enabled',
|
1867 |
-
'sucuriscan_maximum_failed_logins' => 30,
|
1868 |
-
'sucuriscan_ignored_events' => '',
|
1869 |
-
'sucuriscan_verify_ssl_cert' => 'true',
|
1870 |
-
);
|
1871 |
-
|
1872 |
-
if( is_array($settings) ){
|
1873 |
-
foreach( $default_options as $option_name => $option_value ){
|
1874 |
-
if( !isset($settings[$option_name]) ){
|
1875 |
-
$settings[$option_name] = $option_value;
|
1876 |
-
}
|
1877 |
-
}
|
1878 |
-
|
1879 |
-
return $settings;
|
1880 |
-
}
|
1881 |
-
|
1882 |
-
if( is_string($settings) ){
|
1883 |
-
if( isset($default_options[$settings]) ){
|
1884 |
-
return $default_options[$settings];
|
1885 |
-
}
|
1886 |
-
}
|
1887 |
-
|
1888 |
-
return FALSE;
|
1889 |
-
}
|
1890 |
-
|
1891 |
/**
|
1892 |
* Retrieve all the options stored by Wordpress in the database. The options
|
1893 |
* containing the word "transient" are excluded from the results, this function
|
@@ -1980,35 +2109,6 @@ class SucuriScanOption extends SucuriScanRequest {
|
|
1980 |
return FALSE;
|
1981 |
}
|
1982 |
|
1983 |
-
/**
|
1984 |
-
* Get the email address set by the administrator to receive the notifications
|
1985 |
-
* sent by the plugin, if the email is missing the WordPress email address is
|
1986 |
-
* chosen by default.
|
1987 |
-
*
|
1988 |
-
* @return string The administrator email address.
|
1989 |
-
*/
|
1990 |
-
public static function get_site_email(){
|
1991 |
-
$email = self::get_option('admin_email');
|
1992 |
-
|
1993 |
-
if( self::is_valid_email($email) ){
|
1994 |
-
return $email;
|
1995 |
-
}
|
1996 |
-
|
1997 |
-
return FALSE;
|
1998 |
-
}
|
1999 |
-
|
2000 |
-
/**
|
2001 |
-
* Get the clean version of the current domain.
|
2002 |
-
*
|
2003 |
-
* @return string The domain of the current site.
|
2004 |
-
*/
|
2005 |
-
public static function get_domain(){
|
2006 |
-
$http_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
|
2007 |
-
$domain_name = preg_replace( '/^www\./', '', $http_host );
|
2008 |
-
|
2009 |
-
return $domain_name;
|
2010 |
-
}
|
2011 |
-
|
2012 |
/**
|
2013 |
* Get a list of the post types ignored to receive email notifications when the
|
2014 |
* "new site content" hook is triggered.
|
@@ -2140,7 +2240,7 @@ class SucuriScanOption extends SucuriScanRequest {
|
|
2140 |
|
2141 |
if( $runtime > 0 ){
|
2142 |
if( $format ){
|
2143 |
-
return
|
2144 |
}
|
2145 |
|
2146 |
return $runtime;
|
@@ -2369,7 +2469,7 @@ class SucuriScanEvent extends SucuriScan {
|
|
2369 |
$email_params['Force'] = TRUE;
|
2370 |
}
|
2371 |
|
2372 |
-
$title =
|
2373 |
$mail_sent = SucuriScanMail::send_mail( $email, $title, $content, $email_params );
|
2374 |
|
2375 |
return $mail_sent;
|
@@ -3112,6 +3212,15 @@ class SucuriScanAPI extends SucuriScanOption {
|
|
3112 |
return ( self::get_option(':verify_ssl_cert') === 'true' );
|
3113 |
}
|
3114 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3115 |
/**
|
3116 |
* Generate an user-agent for the HTTP requests.
|
3117 |
*
|
@@ -3145,7 +3254,7 @@ class SucuriScanAPI extends SucuriScanOption {
|
|
3145 |
|
3146 |
$req_args = array(
|
3147 |
'method' => $method,
|
3148 |
-
'timeout' =>
|
3149 |
'redirection' => 2,
|
3150 |
'httpversion' => '1.0',
|
3151 |
'user-agent' => self::user_agent(),
|
@@ -3218,7 +3327,7 @@ class SucuriScanAPI extends SucuriScanOption {
|
|
3218 |
*/
|
3219 |
public static function set_plugin_key( $api_key='', $validate=FALSE ){
|
3220 |
if( $validate ){
|
3221 |
-
if( !preg_match('/^
|
3222 |
SucuriScanInterface::error( 'Invalid API key format' );
|
3223 |
return FALSE;
|
3224 |
}
|
@@ -3601,7 +3710,7 @@ class SucuriScanAPI extends SucuriScanOption {
|
|
3601 |
'date' => date('Y-m-d'),
|
3602 |
);
|
3603 |
|
3604 |
-
if( preg_match('/^
|
3605 |
$params['date'] = $date;
|
3606 |
}
|
3607 |
|
@@ -3895,7 +4004,7 @@ class SucuriScanMail extends SucuriScanOption {
|
|
3895 |
public static function send_mail( $email='', $subject='', $message='', $data_set=array() ){
|
3896 |
$headers = array();
|
3897 |
$subject = ucwords(strtolower($subject));
|
3898 |
-
$wp_domain =
|
3899 |
$force = FALSE;
|
3900 |
$debug = FALSE;
|
3901 |
|
@@ -3930,9 +4039,11 @@ class SucuriScanMail extends SucuriScanOption {
|
|
3930 |
|
3931 |
if( $debug ){ die($message); }
|
3932 |
|
|
|
|
|
3933 |
$email_sent = wp_mail(
|
3934 |
$email,
|
3935 |
-
|
3936 |
$message,
|
3937 |
$headers
|
3938 |
);
|
@@ -4073,8 +4184,8 @@ class SucuriScanTemplate extends SucuriScanRequest {
|
|
4073 |
$params['PageTitle'] = isset($params['PageTitle']) ? '('.$params['PageTitle'].')' : '';
|
4074 |
$params['PageNonce'] = wp_create_nonce('sucuriscan_page_nonce');
|
4075 |
$params['PageStyleClass'] = isset($params['PageStyleClass']) ? $params['PageStyleClass'] : 'base';
|
4076 |
-
$params['CleanDomain'] =
|
4077 |
-
$params['AdminEmail'] =
|
4078 |
|
4079 |
return $params;
|
4080 |
}
|
@@ -4365,6 +4476,160 @@ class SucuriScanTemplate extends SucuriScanRequest {
|
|
4365 |
|
4366 |
}
|
4367 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4368 |
/**
|
4369 |
* Plugin initializer.
|
4370 |
*
|
@@ -4664,7 +4929,7 @@ function sucuriscan_scanner_page(){
|
|
4664 |
function sucuriscan_sitecheck_info( $res=array() ){
|
4665 |
// Will be TRUE only if the scanning results were retrieved from the cache.
|
4666 |
$display_results = (bool) $res;
|
4667 |
-
$clean_domain =
|
4668 |
|
4669 |
// If the results are not cached, then request a new scanning.
|
4670 |
if( $res === FALSE ){
|
@@ -5140,6 +5405,9 @@ function sucuriscan_monitoring_form_submissions(){
|
|
5140 |
|
5141 |
if( $clear_cache_resp ){
|
5142 |
if( isset($clear_cache_resp->messages[0]) ){
|
|
|
|
|
|
|
5143 |
SucuriScanInterface::info($clear_cache_resp->messages[0]);
|
5144 |
} else {
|
5145 |
SucuriScanInterface::error('Could not clear the cache of your site, try later again.');
|
@@ -5314,7 +5582,7 @@ function sucuriscan_monitoring_logs( $api_key='' ){
|
|
5314 |
}
|
5315 |
}
|
5316 |
|
5317 |
-
$template_variables['AuditLogs.TargetDate'] =
|
5318 |
$template_variables['AuditLogs.DateYears'] = sucuriscan_monitoring_dates('years', $date);
|
5319 |
$template_variables['AuditLogs.DateMonths'] = sucuriscan_monitoring_dates('months', $date);
|
5320 |
$template_variables['AuditLogs.DateDays'] = sucuriscan_monitoring_dates('days', $date);
|
@@ -5337,6 +5605,8 @@ function sucuriscan_monitoring_access_logs( $access_logs=array() ){
|
|
5337 |
'request_date',
|
5338 |
'request_time',
|
5339 |
'request_timezone',
|
|
|
|
|
5340 |
'remote_addr',
|
5341 |
'sucuri_block_reason',
|
5342 |
'resource_path',
|
@@ -5398,9 +5668,20 @@ function sucuriscan_monitoring_access_logs( $access_logs=array() ){
|
|
5398 |
|
5399 |
if( isset($access_log->{$attr_name}) ){
|
5400 |
$attr_value = $access_log->{$attr_name};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5401 |
}
|
5402 |
|
5403 |
-
$audit_log_snippet[$attr_title] = $attr_value;
|
5404 |
}
|
5405 |
|
5406 |
$logs_html .= SucuriScanTemplate::get_snippet('monitoring-logs', $audit_log_snippet);
|
@@ -5427,7 +5708,9 @@ function sucuriscan_monitoring_denial_types( $access_logs=array(), $in_html=TRUE
|
|
5427 |
foreach( $access_logs as $access_log ){
|
5428 |
if( !array_key_exists($access_log->sucuri_block_reason, $types) ){
|
5429 |
$denial_type_k = SucuriScan::human2var($access_log->sucuri_block_reason);
|
5430 |
-
$
|
|
|
|
|
5431 |
}
|
5432 |
}
|
5433 |
}
|
@@ -5734,7 +6017,7 @@ function sucuriscan_harden_upload(){
|
|
5734 |
@file_put_contents($htaccess_upload, $htaccess_content, LOCK_EX);
|
5735 |
}
|
5736 |
|
5737 |
-
SucuriScanInterface::info('Hardening reverted for upload directory
|
5738 |
} else {
|
5739 |
SucuriScanInterface::error(
|
5740 |
'File <code>/wp-content/uploads/.htaccess</code> does not exists or
|
@@ -5768,7 +6051,7 @@ function sucuriscan_harden_upload(){
|
|
5768 |
function sucuriscan_harden_wpcontent(){
|
5769 |
$cp = 1;
|
5770 |
$upmsg = NULL;
|
5771 |
-
$htaccess_upload =
|
5772 |
|
5773 |
if( !is_readable($htaccess_upload) ){
|
5774 |
$cp = 0;
|
@@ -5789,7 +6072,7 @@ function sucuriscan_harden_wpcontent(){
|
|
5789 |
if( @file_put_contents($htaccess_upload, "\n<Files *.php>\ndeny from all\n</Files>") === FALSE ){
|
5790 |
$upmsg = SucuriScanInterface::error('Unable to create <code>.htaccess</code> file, folder destination is not writable.');
|
5791 |
} else {
|
5792 |
-
$upmsg = SucuriScanInterface::info('Hardening applied successfully to content directory
|
5793 |
$cp = 1;
|
5794 |
}
|
5795 |
}
|
@@ -5806,11 +6089,11 @@ function sucuriscan_harden_wpcontent(){
|
|
5806 |
@file_put_contents($htaccess_upload, $htaccess_content, LOCK_EX);
|
5807 |
}
|
5808 |
|
5809 |
-
SucuriScanInterface::info('Hardening reverted for content directory
|
5810 |
} else {
|
5811 |
SucuriScanInterface::info(
|
5812 |
-
'File <code
|
5813 |
-
you will need to remove the following code manually from there:
|
5814 |
<code><Files *.php>deny from all</Files></code>'
|
5815 |
);
|
5816 |
}
|
@@ -5819,7 +6102,7 @@ function sucuriscan_harden_wpcontent(){
|
|
5819 |
|
5820 |
$description = 'This option blocks direct PHP access to any file inside wp-content. If you experience '
|
5821 |
. 'any issue after this with a theme or plugin in your site, like for example images not displaying, '
|
5822 |
-
. 'remove the <code>.htaccess</code> file located
|
5823 |
. '</p><p><b>Note:</b> Many <em>(insecure)</em> themes and plugins use a PHP file in this directory '
|
5824 |
. 'to generate images like thumbnails and captcha codes, this is intentional so it is recommended '
|
5825 |
. 'to check your site once this option is enabled.';
|
@@ -5869,7 +6152,7 @@ function sucuriscan_harden_wpincludes(){
|
|
5869 |
if( @file_put_contents($htaccess_upload, "\n<Files *.php>\ndeny from all\n</Files>\n<Files wp-tinymce.php>\nallow from all\n</Files>\n")===FALSE ){
|
5870 |
$upmsg = SucuriScanInterface::error('Unable to create <code>.htaccess</code> file, folder destination is not writable.');
|
5871 |
} else {
|
5872 |
-
$upmsg = SucuriScanInterface::info('Hardening applied successfully to library\'s directory
|
5873 |
$cp = 1;
|
5874 |
}
|
5875 |
}
|
@@ -5887,7 +6170,7 @@ function sucuriscan_harden_wpincludes(){
|
|
5887 |
|
5888 |
@file_put_contents($htaccess_upload, $htaccess_content, LOCK_EX);
|
5889 |
}
|
5890 |
-
SucuriScanInterface::info('Hardening reverted for library\'s directory
|
5891 |
} else {
|
5892 |
SucuriScanInterface::error(
|
5893 |
'File <code>wp-includes/.htaccess</code> does not exists or is not
|
@@ -6337,9 +6620,9 @@ function sucuriscan_auditlogs(){
|
|
6337 |
$css_class = ( $counter_i % 2 == 0 ) ? '' : 'alternate';
|
6338 |
$snippet_data = array(
|
6339 |
'AuditLog.CssClass' => $css_class,
|
6340 |
-
'AuditLog.DateTime' =>
|
6341 |
-
'AuditLog.Account' => $audit_log['account'],
|
6342 |
-
'AuditLog.Message' => $audit_log['message'],
|
6343 |
'AuditLog.Extra' => '',
|
6344 |
);
|
6345 |
|
@@ -6348,7 +6631,7 @@ function sucuriscan_auditlogs(){
|
|
6348 |
$css_scrollable = $audit_log['extra_total'] > 10 ? 'sucuriscan-list-as-table-scrollable' : '';
|
6349 |
$snippet_data['AuditLog.Extra'] .= '<ul class="sucuriscan-list-as-table ' . $css_scrollable . '">';
|
6350 |
foreach( $audit_log['extra'] as $log_extra ){
|
6351 |
-
$snippet_data['AuditLog.Extra'] .= '<li>' . $log_extra . '</li>';
|
6352 |
}
|
6353 |
$snippet_data['AuditLog.Extra'] .= '</ul>';
|
6354 |
$snippet_data['AuditLog.Extra'] .= '<small>For Mac users, this is a scrollable container</small>';
|
@@ -6652,13 +6935,13 @@ function sucuriscan_modified_files(){
|
|
6652 |
&& $file_info['modified_at'] >= $back_days
|
6653 |
){
|
6654 |
$css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
|
6655 |
-
$mod_date =
|
6656 |
|
6657 |
$template_variables['ModifiedFiles.List'] .= SucuriScanTemplate::get_snippet('integrity-modifiedfiles', array(
|
6658 |
'ModifiedFiles.CssClass' => $css_class,
|
6659 |
'ModifiedFiles.CheckSum' => $file_info['checksum'],
|
6660 |
'ModifiedFiles.FilePath' => $file_path,
|
6661 |
-
'ModifiedFiles.DateTime' => $mod_date
|
6662 |
));
|
6663 |
$counter += 1;
|
6664 |
}
|
@@ -6819,7 +7102,7 @@ function sucuriscan_posthack_users( $process_form=FALSE ){
|
|
6819 |
|
6820 |
foreach( $user_list as $user ){
|
6821 |
$user->user_registered_timestamp = strtotime($user->user_registered);
|
6822 |
-
$user->user_registered_formatted =
|
6823 |
$css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
|
6824 |
|
6825 |
$template_variables['ResetPassword.UserList'] .= SucuriScanTemplate::get_snippet('posthack-resetpassword', array(
|
@@ -6978,6 +7261,21 @@ function sucuriscan_posthack_reinstall_plugins( $process_form=FALSE ){
|
|
6978 |
function sucuriscan_lastlogins_page(){
|
6979 |
SucuriScanInterface::check_permissions();
|
6980 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6981 |
// Page pseudo-variables initialization.
|
6982 |
$template_variables = array(
|
6983 |
'PageTitle' => 'Last Logins',
|
@@ -7023,14 +7321,18 @@ function sucuriscan_lastlogins_admins(){
|
|
7023 |
if( !empty($admin->lastlogins) ){
|
7024 |
$user_snippet['AdminUsers.NoLastLogins'] = 'hidden';
|
7025 |
$user_snippet['AdminUsers.NoLastLoginsTable'] = 'visible';
|
7026 |
-
$user_snippet['AdminUsers.RegisteredAt'] =
|
7027 |
$counter = 0;
|
7028 |
|
7029 |
-
foreach( $admin->lastlogins as $lastlogin ){
|
|
|
|
|
|
|
|
|
7030 |
$css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
|
7031 |
$user_snippet['AdminUsers.LastLogins'] .= SucuriScanTemplate::get_snippet('lastlogins-admins-lastlogin', array(
|
7032 |
'AdminUsers.RemoteAddr' => SucuriScan::escape($lastlogin->user_remoteaddr),
|
7033 |
-
'AdminUsers.Datetime' => SucuriScan::
|
7034 |
'AdminUsers.CssClass' => $css_class,
|
7035 |
));
|
7036 |
$counter += 1;
|
@@ -7263,6 +7565,8 @@ function sucuriscan_get_logins( $limit=10, $offset=0, $user_id=0 ){
|
|
7263 |
|
7264 |
if( preg_match('/^a:[0-9]+:.+/', $line) ){
|
7265 |
$last_login = @unserialize($line);
|
|
|
|
|
7266 |
|
7267 |
// Only administrators can see all login stats.
|
7268 |
if( !$is_admin_user && $current_user->user_login != $last_login['user_login'] ){
|
@@ -7283,6 +7587,10 @@ function sucuriscan_get_logins( $limit=10, $offset=0, $user_id=0 ){
|
|
7283 |
|
7284 |
foreach( $user_account->data as $var_name=>$var_value ){
|
7285 |
$last_login[$var_name] = $var_value;
|
|
|
|
|
|
|
|
|
7286 |
}
|
7287 |
}
|
7288 |
|
@@ -7291,6 +7599,10 @@ function sucuriscan_get_logins( $limit=10, $offset=0, $user_id=0 ){
|
|
7291 |
$parsed_lines += 1;
|
7292 |
}
|
7293 |
|
|
|
|
|
|
|
|
|
7294 |
if( preg_match('/^[0-9]+$/', $limit) && $limit>0 ){
|
7295 |
if( $parsed_lines >= $limit ){ break; }
|
7296 |
}
|
@@ -7345,8 +7657,10 @@ if( !function_exists('sucuri_get_user_lastlogin') ){
|
|
7345 |
if( isset($last_logins['entries'][1]) ){
|
7346 |
$row = $last_logins['entries'][1];
|
7347 |
|
7348 |
-
$
|
7349 |
-
|
|
|
|
|
7350 |
$lastlogin_message .= chr(32).'(<a href="'.SucuriScanTemplate::get_url('lastlogins').'">view all logs</a>)';
|
7351 |
SucuriScanInterface::info( $lastlogin_message );
|
7352 |
}
|
@@ -7376,8 +7690,8 @@ function sucuriscan_loggedin_users_panel(){
|
|
7376 |
|
7377 |
foreach( (array) $logged_in_users as $logged_in_user ){
|
7378 |
$counter += 1;
|
7379 |
-
$logged_in_user['last_activity_datetime'] =
|
7380 |
-
$logged_in_user['user_registered_datetime'] =
|
7381 |
|
7382 |
$template_variables['LoggedInUsers.List'] .= SucuriScanTemplate::get_snippet('lastlogins-loggedin', array(
|
7383 |
'LoggedInUsers.Id' => SucuriScan::escape($logged_in_user['user_id']),
|
@@ -7584,7 +7898,7 @@ function sucuriscan_failed_logins_panel(){
|
|
7584 |
'FailedLogins.Num' => ($counter + 1),
|
7585 |
'FailedLogins.Username' => SucuriScan::escape($login_data['user_login']),
|
7586 |
'FailedLogins.RemoteAddr' => SucuriScan::escape($login_data['remote_addr']),
|
7587 |
-
'FailedLogins.Datetime' =>
|
7588 |
'FailedLogins.UserAgent' => SucuriScan::escape($login_data['user_agent']),
|
7589 |
));
|
7590 |
|
@@ -7820,6 +8134,7 @@ function sucuriscan_settings_page(){
|
|
7820 |
'Settings.General' => sucuriscan_settings_general(),
|
7821 |
'Settings.Notifications' => sucuriscan_settings_notifications(),
|
7822 |
'Settings.IgnoreRules' => sucuriscan_settings_ignore_rules(),
|
|
|
7823 |
);
|
7824 |
|
7825 |
echo SucuriScanTemplate::get_template('settings', $template_variables);
|
@@ -7970,6 +8285,12 @@ function sucuriscan_settings_form_submissions( $page_nonce=NULL ){
|
|
7970 |
}
|
7971 |
}
|
7972 |
|
|
|
|
|
|
|
|
|
|
|
|
|
7973 |
// Update the notification settings.
|
7974 |
if( SucuriScanRequest::post(':save_notification_settings') !== FALSE ){
|
7975 |
$options_updated_counter = 0;
|
@@ -8028,6 +8349,48 @@ function sucuriscan_settings_form_submissions( $page_nonce=NULL ){
|
|
8028 |
}
|
8029 |
}
|
8030 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8031 |
}
|
8032 |
}
|
8033 |
|
@@ -8081,6 +8444,13 @@ function sucuriscan_settings_general(){
|
|
8081 |
$verify_ssl_cert = SucuriScanOption::get_option(':verify_ssl_cert');
|
8082 |
$runtime_scan_human = SucuriScanOption::get_filesystem_runtime(TRUE);
|
8083 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8084 |
// Generate the HTML code for the option list in the form select fields.
|
8085 |
$scan_freq_options = SucuriScanTemplate::get_select_options( $sucuriscan_schedule_allowed, $scan_freq );
|
8086 |
$scan_interface_options = SucuriScanTemplate::get_select_options( $sucuriscan_interface_allowed, $scan_interface );
|
@@ -8093,6 +8463,7 @@ function sucuriscan_settings_general(){
|
|
8093 |
'APIKey.RecoverVisibility' => SucuriScanTemplate::visibility( !$api_key && !$display_manual_key_form ),
|
8094 |
'APIKey.ManualKeyFormVisibility' => SucuriScanTemplate::visibility($display_manual_key_form),
|
8095 |
'APIKey.RemoveVisibility' => SucuriScanTemplate::visibility($api_key),
|
|
|
8096 |
/* Filesystem scanner */
|
8097 |
'FsScannerStatus' => 'Enabled',
|
8098 |
'FsScannerSwitchText' => 'Disable',
|
@@ -8124,6 +8495,7 @@ function sucuriscan_settings_general(){
|
|
8124 |
'MaximumFailedLoginsOptions' => $maximum_failed_logins_options,
|
8125 |
'VerifySSLCert' => 'Undefined',
|
8126 |
'VerifySSLCertOptions' => $verify_ssl_cert_options,
|
|
|
8127 |
'ModalWhenAPIRegistered' => $api_registered_modal,
|
8128 |
);
|
8129 |
|
@@ -8227,7 +8599,7 @@ function sucuriscan_settings_ignore_rules(){
|
|
8227 |
|
8228 |
if( array_key_exists($post_type, $ignored_events) ){
|
8229 |
$is_ignored_text = 'YES';
|
8230 |
-
$was_ignored_at =
|
8231 |
$is_ignored_class = 'danger';
|
8232 |
$button_action = 'remove';
|
8233 |
$button_class = 'button-primary';
|
@@ -8259,6 +8631,65 @@ function sucuriscan_settings_ignore_rules(){
|
|
8259 |
return SucuriScanTemplate::get_section('settings-ignorerules', $template_variables);
|
8260 |
}
|
8261 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8262 |
/**
|
8263 |
* Generate and print the HTML code for the InfoSys page.
|
8264 |
*
|
@@ -8496,18 +8927,21 @@ function sucuriscan_show_cronjobs(){
|
|
8496 |
|
8497 |
$cronjobs = _get_cron_array();
|
8498 |
$schedules = wp_get_schedules();
|
8499 |
-
$date_format = _x('M j, Y - H:i', 'Publish box date format', 'cron-view' );
|
8500 |
$counter = 0;
|
8501 |
|
8502 |
foreach( $cronjobs as $timestamp => $cronhooks ){
|
8503 |
foreach( (array) $cronhooks as $hook => $events ){
|
8504 |
foreach( (array) $events as $key => $event ){
|
|
|
|
|
|
|
|
|
8505 |
$template_variables['Cronjobs.Total'] += 1;
|
8506 |
$template_variables['Cronjobs.List'] .= SucuriScanTemplate::get_snippet('infosys-cronjobs', array(
|
8507 |
'Cronjob.Hook' => $hook,
|
8508 |
'Cronjob.Schedule' => $event['schedule'],
|
8509 |
-
'Cronjob.NextTime' =>
|
8510 |
-
'Cronjob.Arguments' =>
|
8511 |
'Cronjob.CssClass' => ( $counter % 2 == 0 ) ? '' : 'alternate',
|
8512 |
));
|
8513 |
$counter += 1;
|
2 |
/*
|
3 |
Plugin Name: Sucuri Security - Auditing, Malware Scanner and Hardening
|
4 |
Plugin URI: http://wordpress.sucuri.net/
|
5 |
+
Description: The <a href="http://sucuri.net/" target="_blank">Sucuri</a> plugin provides the website owner the best Activity Auditing, SiteCheck Remote Malware Scanning, Effective Security Hardening and Post-Hack features. SiteCheck will check for malware, spam, blacklisting and other security issues like .htaccess redirects, hidden eval code, etc. The best thing about it is it's completely free.
|
6 |
Author: Sucuri, INC
|
7 |
+
Version: 1.6.9
|
8 |
Author URI: http://sucuri.net
|
9 |
*/
|
10 |
|
66 |
/**
|
67 |
* Current version of the plugin's code.
|
68 |
*/
|
69 |
+
define('SUCURISCAN_VERSION', '1.6.9');
|
70 |
|
71 |
/**
|
72 |
* The name of the Sucuri plugin main file.
|
319 |
$sucuriscan_admin_notice_name = SucuriScan::is_multisite() ? 'network_admin_notices' : 'admin_notices';
|
320 |
add_action( $sucuriscan_admin_notice_name, 'SucuriScanInterface::setup_notice' );
|
321 |
|
322 |
+
/**
|
323 |
+
* Heartbeat API
|
324 |
+
*
|
325 |
+
* Update the settings of the Heartbeat API according to the values set by an
|
326 |
+
* administrator. This tool may cause an increase in the CPU usage, a bad
|
327 |
+
* configuration may cause low account to run out of resources, but in better
|
328 |
+
* cases it may improve the performance of the site by reducing the quantity of
|
329 |
+
* requests sent to the server per session.
|
330 |
+
*/
|
331 |
+
add_filter( 'init', 'SucuriScanHeartbeat::register_script', 1 );
|
332 |
+
add_filter( 'heartbeat_settings', 'SucuriScanHeartbeat::update_settings' );
|
333 |
+
add_filter( 'heartbeat_send', 'SucuriScanHeartbeat::respond_to_send', 10, 3 );
|
334 |
+
add_filter( 'heartbeat_received', 'SucuriScanHeartbeat::respond_to_received', 10, 3 );
|
335 |
+
add_filter( 'heartbeat_nopriv_send', 'SucuriScanHeartbeat::respond_to_send', 10, 3 );
|
336 |
+
add_filter( 'heartbeat_nopriv_received', 'SucuriScanHeartbeat::respond_to_received', 10, 3 );
|
337 |
+
|
338 |
}
|
339 |
|
340 |
/**
|
587 |
return FALSE;
|
588 |
}
|
589 |
|
590 |
+
/**
|
591 |
+
* Get the clean version of the current domain.
|
592 |
+
*
|
593 |
+
* @return string The domain of the current site.
|
594 |
+
*/
|
595 |
+
public static function get_domain(){
|
596 |
+
if( function_exists('get_site_url') ){
|
597 |
+
$site_url = get_site_url();
|
598 |
+
} else {
|
599 |
+
if( !isset($_SERVER['HTTP_HOST']) ){
|
600 |
+
$_SERVER['HTTP_HOST'] = 'localhost';
|
601 |
+
}
|
602 |
+
|
603 |
+
$site_url = $_SERVER['HTTP_HOST'];
|
604 |
+
}
|
605 |
+
|
606 |
+
$pattern = '/([fhtps]+:\/\/)?([^:\/]+)(:[0-9:]+)?(\/.*)?/';
|
607 |
+
$domain_name = preg_replace( $pattern, '$2', $site_url );
|
608 |
+
|
609 |
+
return $domain_name;
|
610 |
+
}
|
611 |
+
|
612 |
/**
|
613 |
* Check whether the site is behing the Sucuri CloudProxy network.
|
614 |
*
|
616 |
* @return boolean Either TRUE or FALSE if the site is behind CloudProxy.
|
617 |
*/
|
618 |
public static function is_behind_cloudproxy( $verbose=FALSE ){
|
619 |
+
$http_host = self::get_domain();
|
620 |
+
$host_by_name = @gethostbyname($http_host);
|
621 |
+
$host_by_addr = @gethostbyaddr($host_by_name);
|
|
|
|
|
|
|
|
|
|
|
622 |
$status = (bool) preg_match('/^cloudproxy[0-9]+\.sucuri\.net$/', $host_by_addr);
|
623 |
|
624 |
if( $verbose ){
|
633 |
return $status;
|
634 |
}
|
635 |
|
636 |
+
/**
|
637 |
+
* Get the email address set by the administrator to receive the notifications
|
638 |
+
* sent by the plugin, if the email is missing the WordPress email address is
|
639 |
+
* chosen by default.
|
640 |
+
*
|
641 |
+
* @return string The administrator email address.
|
642 |
+
*/
|
643 |
+
public static function get_site_email(){
|
644 |
+
$email = get_option('admin_email');
|
645 |
+
|
646 |
+
if( self::is_valid_email($email) ){
|
647 |
+
return $email;
|
648 |
+
}
|
649 |
+
|
650 |
+
return FALSE;
|
651 |
+
}
|
652 |
+
|
653 |
+
/**
|
654 |
+
* Retrieve the date in localized format, based on timestamp.
|
655 |
+
*
|
656 |
+
* If the locale specifies the locale month and weekday, then the locale will
|
657 |
+
* take over the format for the date. If it isn't, then the date format string
|
658 |
+
* will be used instead.
|
659 |
+
*
|
660 |
+
* @param integer $timestamp Unix timestamp.
|
661 |
+
* @return string The date, translated if locale specifies it.
|
662 |
+
*/
|
663 |
+
public static function datetime( $timestamp=0 ){
|
664 |
+
if( is_numeric($timestamp) && $timestamp > 0 ){
|
665 |
+
$date_format = get_option('date_format');
|
666 |
+
$time_format = get_option('time_format');
|
667 |
+
$timezone_format = sprintf( '%s %s', $date_format, $time_format );
|
668 |
+
|
669 |
+
return date_i18n( $timezone_format, $timestamp );
|
670 |
+
}
|
671 |
+
|
672 |
+
return NULL;
|
673 |
+
}
|
674 |
+
|
675 |
/**
|
676 |
* Return the time passed since the specified timestamp until now.
|
677 |
*
|
790 |
return $text;
|
791 |
}
|
792 |
|
793 |
+
/**
|
794 |
+
* Check whether an list is a multidimensional array or not.
|
795 |
+
*
|
796 |
+
* @param array $list An array or multidimensional array of different values.
|
797 |
+
* @return boolean TRUE if the list is multidimensional, FALSE otherwise.
|
798 |
+
*/
|
799 |
+
public static function is_multi_list( $list=array() ){
|
800 |
+
if( !empty($list) ){
|
801 |
+
foreach( $list as $item ){
|
802 |
+
if( is_array($item) ){
|
803 |
+
return TRUE;
|
804 |
+
}
|
805 |
+
}
|
806 |
+
}
|
807 |
+
|
808 |
+
return FALSE;
|
809 |
+
}
|
810 |
+
|
811 |
+
/**
|
812 |
+
* Join array elements with a string no matter if it is multidimensional.
|
813 |
+
*
|
814 |
+
* @param string $separator Character that will act as a separator, default to an empty string.
|
815 |
+
* @param array $list The array of strings to implode.
|
816 |
+
* @return string String of all the items in the list, with the separator between them.
|
817 |
+
*/
|
818 |
+
public static function implode( $separator='', $list=array() ){
|
819 |
+
if( self::is_multi_list($list) ){
|
820 |
+
$pieces = array();
|
821 |
+
|
822 |
+
foreach( $list as $items ){
|
823 |
+
$pieces[] = @implode( $separator, $items );
|
824 |
+
}
|
825 |
+
|
826 |
+
$joined_pieces = '(' . implode( '), (', $pieces ) . ')';
|
827 |
+
|
828 |
+
return $joined_pieces;
|
829 |
+
} else {
|
830 |
+
return implode( $separator, $list );
|
831 |
+
}
|
832 |
+
}
|
833 |
+
|
834 |
}
|
835 |
|
836 |
/**
|
1789 |
*/
|
1790 |
class SucuriScanOption extends SucuriScanRequest {
|
1791 |
|
1792 |
+
/**
|
1793 |
+
* Default values for the plugin options.
|
1794 |
+
*
|
1795 |
+
* @return array Default plugin option values.
|
1796 |
+
*/
|
1797 |
+
private static function get_default_option_values(){
|
1798 |
+
$defaults = array(
|
1799 |
+
'sucuriscan_api_key' => FALSE,
|
1800 |
+
'sucuriscan_account' => '',
|
1801 |
+
'sucuriscan_fs_scanner' => 'enabled',
|
1802 |
+
'sucuriscan_scan_frequency' => 'hourly',
|
1803 |
+
'sucuriscan_scan_interface' => 'spl',
|
1804 |
+
'sucuriscan_scan_modfiles' => 'enabled',
|
1805 |
+
'sucuriscan_scan_checksums' => 'enabled',
|
1806 |
+
'sucuriscan_runtime' => 0,
|
1807 |
+
'sucuriscan_lastlogin_redirection' => 'enabled',
|
1808 |
+
'sucuriscan_notify_to' => '',
|
1809 |
+
'sucuriscan_emails_sent' => 0,
|
1810 |
+
'sucuriscan_emails_per_hour' => 5,
|
1811 |
+
'sucuriscan_last_email_at' => time(),
|
1812 |
+
'sucuriscan_prettify_mails' => 'enabled',
|
1813 |
+
'sucuriscan_notify_success_login' => 'enabled',
|
1814 |
+
'sucuriscan_notify_failed_login' => 'enabled',
|
1815 |
+
'sucuriscan_notify_post_publication' => 'enabled',
|
1816 |
+
'sucuriscan_notify_theme_editor' => 'enabled',
|
1817 |
+
'sucuriscan_maximum_failed_logins' => 30,
|
1818 |
+
'sucuriscan_ignored_events' => '',
|
1819 |
+
'sucuriscan_verify_ssl_cert' => 'true',
|
1820 |
+
'sucuriscan_request_timeout' => 90,
|
1821 |
+
'sucuriscan_heartbeat' => 'enabled',
|
1822 |
+
'sucuriscan_heartbeat_pulse' => 15,
|
1823 |
+
'sucuriscan_heartbeat_interval' => 'standard',
|
1824 |
+
'sucuriscan_heartbeat_autostart' => 'enabled',
|
1825 |
+
);
|
1826 |
+
|
1827 |
+
return $defaults;
|
1828 |
+
}
|
1829 |
+
|
1830 |
+
/**
|
1831 |
+
* Retrieve the default values for some specific options.
|
1832 |
+
*
|
1833 |
+
* @param string|array $settings Either an array that will be complemented or a string with the name of the option.
|
1834 |
+
* @return string|array The default values for the specified options.
|
1835 |
+
*/
|
1836 |
+
private static function get_default_options( $settings='' ){
|
1837 |
+
$default_options = self::get_default_option_values();
|
1838 |
+
|
1839 |
+
// Use framework built-in function.
|
1840 |
+
if( function_exists('get_option') ){
|
1841 |
+
$admin_email = get_option('admin_email');
|
1842 |
+
$default_options['sucuriscan_account'] = $admin_email;
|
1843 |
+
$default_options['sucuriscan_notify_to'] = $admin_email;
|
1844 |
+
}
|
1845 |
+
|
1846 |
+
if( is_array($settings) ){
|
1847 |
+
foreach( $default_options as $option_name => $option_value ){
|
1848 |
+
if( !isset($settings[$option_name]) ){
|
1849 |
+
$settings[$option_name] = $option_value;
|
1850 |
+
}
|
1851 |
+
}
|
1852 |
+
|
1853 |
+
return $settings;
|
1854 |
+
}
|
1855 |
+
|
1856 |
+
if( is_string($settings) ){
|
1857 |
+
if( isset($default_options[$settings]) ){
|
1858 |
+
return $default_options[$settings];
|
1859 |
+
}
|
1860 |
+
}
|
1861 |
+
|
1862 |
+
return FALSE;
|
1863 |
+
}
|
1864 |
+
|
1865 |
/**
|
1866 |
* Retrieve specific options from the database.
|
1867 |
*
|
2017 |
return $option_value;
|
2018 |
}
|
2019 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020 |
/**
|
2021 |
* Retrieve all the options stored by Wordpress in the database. The options
|
2022 |
* containing the word "transient" are excluded from the results, this function
|
2109 |
return FALSE;
|
2110 |
}
|
2111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2112 |
/**
|
2113 |
* Get a list of the post types ignored to receive email notifications when the
|
2114 |
* "new site content" hook is triggered.
|
2240 |
|
2241 |
if( $runtime > 0 ){
|
2242 |
if( $format ){
|
2243 |
+
return SucuriScan::datetime($runtime);
|
2244 |
}
|
2245 |
|
2246 |
return $runtime;
|
2469 |
$email_params['Force'] = TRUE;
|
2470 |
}
|
2471 |
|
2472 |
+
$title = str_replace('_', chr(32), $event);
|
2473 |
$mail_sent = SucuriScanMail::send_mail( $email, $title, $content, $email_params );
|
2474 |
|
2475 |
return $mail_sent;
|
3212 |
return ( self::get_option(':verify_ssl_cert') === 'true' );
|
3213 |
}
|
3214 |
|
3215 |
+
/**
|
3216 |
+
* Seconds before consider a HTTP request as timeout.
|
3217 |
+
*
|
3218 |
+
* @return integer Seconds to consider a HTTP request timeout.
|
3219 |
+
*/
|
3220 |
+
public static function request_timeout(){
|
3221 |
+
return intval( self::get_option(':request_timeout') );
|
3222 |
+
}
|
3223 |
+
|
3224 |
/**
|
3225 |
* Generate an user-agent for the HTTP requests.
|
3226 |
*
|
3254 |
|
3255 |
$req_args = array(
|
3256 |
'method' => $method,
|
3257 |
+
'timeout' => self::request_timeout(),
|
3258 |
'redirection' => 2,
|
3259 |
'httpversion' => '1.0',
|
3260 |
'user-agent' => self::user_agent(),
|
3327 |
*/
|
3328 |
public static function set_plugin_key( $api_key='', $validate=FALSE ){
|
3329 |
if( $validate ){
|
3330 |
+
if( !preg_match('/^[a-z0-9]{32}$/', $api_key) ){
|
3331 |
SucuriScanInterface::error( 'Invalid API key format' );
|
3332 |
return FALSE;
|
3333 |
}
|
3710 |
'date' => date('Y-m-d'),
|
3711 |
);
|
3712 |
|
3713 |
+
if( preg_match('/^[0-9]{4}(\-[0-9]{2}){2}$/', $date) ){
|
3714 |
$params['date'] = $date;
|
3715 |
}
|
3716 |
|
4004 |
public static function send_mail( $email='', $subject='', $message='', $data_set=array() ){
|
4005 |
$headers = array();
|
4006 |
$subject = ucwords(strtolower($subject));
|
4007 |
+
$wp_domain = self::get_domain();
|
4008 |
$force = FALSE;
|
4009 |
$debug = FALSE;
|
4010 |
|
4039 |
|
4040 |
if( $debug ){ die($message); }
|
4041 |
|
4042 |
+
$subject = sprintf( 'Sucuri Alert, %s, %s', $wp_domain, $subject );
|
4043 |
+
|
4044 |
$email_sent = wp_mail(
|
4045 |
$email,
|
4046 |
+
$subject,
|
4047 |
$message,
|
4048 |
$headers
|
4049 |
);
|
4184 |
$params['PageTitle'] = isset($params['PageTitle']) ? '('.$params['PageTitle'].')' : '';
|
4185 |
$params['PageNonce'] = wp_create_nonce('sucuriscan_page_nonce');
|
4186 |
$params['PageStyleClass'] = isset($params['PageStyleClass']) ? $params['PageStyleClass'] : 'base';
|
4187 |
+
$params['CleanDomain'] = self::get_domain();
|
4188 |
+
$params['AdminEmail'] = self::get_site_email();
|
4189 |
|
4190 |
return $params;
|
4191 |
}
|
4476 |
|
4477 |
}
|
4478 |
|
4479 |
+
/**
|
4480 |
+
* Heartbeat library.
|
4481 |
+
*
|
4482 |
+
* The purpose of the Heartbeat API is to simulate bidirectional connection
|
4483 |
+
* between the browser and the server. Initially it was used for autosave, post
|
4484 |
+
* locking and log-in expiration warning while a user is writing or editing. The
|
4485 |
+
* idea was to have an API that sends XHR (XML HTTP Request) requests to the
|
4486 |
+
* server every fifteen seconds and triggers events (or callbacks) on receiving
|
4487 |
+
* data.
|
4488 |
+
*
|
4489 |
+
* @see https://core.trac.wordpress.org/ticket/23216
|
4490 |
+
*/
|
4491 |
+
class SucuriScanHeartbeat extends SucuriScanOption {
|
4492 |
+
|
4493 |
+
/**
|
4494 |
+
* Stop execution of the heartbeat API in certain parts of the site.
|
4495 |
+
*
|
4496 |
+
* @return void
|
4497 |
+
*/
|
4498 |
+
public static function register_script(){
|
4499 |
+
global $pagenow;
|
4500 |
+
|
4501 |
+
$status = SucuriScanOption::get_option(':heartbeat');
|
4502 |
+
|
4503 |
+
// Enable heartbeat everywhere.
|
4504 |
+
if( $status == 'enabled' ){ /* do_nothing */ }
|
4505 |
+
|
4506 |
+
// Disable heartbeat everywhere.
|
4507 |
+
elseif( $status == 'disabled' ){
|
4508 |
+
wp_deregister_script('heartbeat');
|
4509 |
+
}
|
4510 |
+
|
4511 |
+
// Disable heartbeat only on the dashboard and home pages.
|
4512 |
+
elseif(
|
4513 |
+
$status == 'dashboard'
|
4514 |
+
&& $pagenow == 'index.php'
|
4515 |
+
){
|
4516 |
+
wp_deregister_script('heartbeat');
|
4517 |
+
}
|
4518 |
+
|
4519 |
+
// Disable heartbeat everywhere except in post edition.
|
4520 |
+
elseif(
|
4521 |
+
$status == 'addpost'
|
4522 |
+
&& $pagenow != 'post.php'
|
4523 |
+
&& $pagenow != 'post-new.php'
|
4524 |
+
){
|
4525 |
+
wp_deregister_script('heartbeat');
|
4526 |
+
}
|
4527 |
+
}
|
4528 |
+
|
4529 |
+
/**
|
4530 |
+
* Update the settings of the Heartbeat API according to the values set by an
|
4531 |
+
* administrator. This tool may cause an increase in the CPU usage, a bad
|
4532 |
+
* configuration may cause low account to run out of resources, but in better
|
4533 |
+
* cases it may improve the performance of the site by reducing the quantity of
|
4534 |
+
* requests sent to the server per session.
|
4535 |
+
*
|
4536 |
+
* @param array $settings Heartbeat settings.
|
4537 |
+
* @return array Updated version of the heartbeat settings.
|
4538 |
+
*/
|
4539 |
+
public static function update_settings( $settings=array() ){
|
4540 |
+
$pulse = SucuriScanOption::get_option(':heartbeat_pulse');
|
4541 |
+
$autostart = SucuriScanOption::get_option(':heartbeat_autostart');
|
4542 |
+
|
4543 |
+
if( $pulse < 15 || $pulse > 60 ){
|
4544 |
+
SucuriScanOption::delete_option(':heartbeat_pulse');
|
4545 |
+
$pulse = 15;
|
4546 |
+
}
|
4547 |
+
|
4548 |
+
$settings['interval'] = $pulse;
|
4549 |
+
$settings['autostart'] = ( $autostart == 'disabled' ? FALSE : TRUE );
|
4550 |
+
|
4551 |
+
return $settings;
|
4552 |
+
}
|
4553 |
+
|
4554 |
+
/**
|
4555 |
+
* Respond to the browser according to the data received.
|
4556 |
+
*
|
4557 |
+
* @param array $response Response received.
|
4558 |
+
* @param array $data Data received from the beat.
|
4559 |
+
* @param string $screen_id Identifier of the screen the heartbeat occurred on.
|
4560 |
+
* @return array Response with new data.
|
4561 |
+
*/
|
4562 |
+
public static function respond_to_received( $response=array(), $data=array(), $screen_id='' ) {
|
4563 |
+
$interval = SucuriScanOption::get_option(':heartbeat_interval');
|
4564 |
+
|
4565 |
+
if(
|
4566 |
+
$interval == 'slow'
|
4567 |
+
|| $interval == 'fast'
|
4568 |
+
|| $interval == 'standard'
|
4569 |
+
){
|
4570 |
+
$response['heartbeat_interval'] = $interval;
|
4571 |
+
} else {
|
4572 |
+
SucuriScanOption::delete_option(':heartbeat_interval');
|
4573 |
+
}
|
4574 |
+
|
4575 |
+
return $response;
|
4576 |
+
}
|
4577 |
+
|
4578 |
+
/**
|
4579 |
+
* Respond to the browser according to the data sent.
|
4580 |
+
*
|
4581 |
+
* @param array $response Response sent.
|
4582 |
+
* @param string $screen_id Identifier of the screen the heartbeat occurred on.
|
4583 |
+
* @return array Response with new data.
|
4584 |
+
*/
|
4585 |
+
public static function respond_to_send( $response=array(), $screen_id='' ) {
|
4586 |
+
return $response;
|
4587 |
+
}
|
4588 |
+
|
4589 |
+
/**
|
4590 |
+
* Allowed values for the heartbeat status.
|
4591 |
+
*
|
4592 |
+
* @return array Allowed values for the heartbeat status.
|
4593 |
+
*/
|
4594 |
+
public static function statuses_allowed(){
|
4595 |
+
return array(
|
4596 |
+
'enabled' => 'Enable everywhere',
|
4597 |
+
'disabled' => 'Disable everywhere',
|
4598 |
+
'dashboard' => 'Disable on dashboard page',
|
4599 |
+
'addpost' => 'Everywhere except post addition',
|
4600 |
+
);
|
4601 |
+
}
|
4602 |
+
|
4603 |
+
/**
|
4604 |
+
* Allowed values for the heartbeat intervals.
|
4605 |
+
*
|
4606 |
+
* @return array Allowed values for the heartbeat intervals.
|
4607 |
+
*/
|
4608 |
+
public static function intervals_allowed(){
|
4609 |
+
return array(
|
4610 |
+
'slow' => 'Slow interval',
|
4611 |
+
'fast' => 'Fast interval',
|
4612 |
+
'standard' => 'Standard interval',
|
4613 |
+
);
|
4614 |
+
}
|
4615 |
+
|
4616 |
+
/**
|
4617 |
+
* Allowed values for the heartbeat pulses.
|
4618 |
+
*
|
4619 |
+
* @return array Allowed values for the heartbeat pulses.
|
4620 |
+
*/
|
4621 |
+
public static function pulses_allowed(){
|
4622 |
+
$pulses = array();
|
4623 |
+
|
4624 |
+
for( $i=15; $i<=60; $i++ ){
|
4625 |
+
$pulses[$i] = sprintf( 'Run every %d seconds', $i );
|
4626 |
+
}
|
4627 |
+
|
4628 |
+
return $pulses;
|
4629 |
+
}
|
4630 |
+
|
4631 |
+
}
|
4632 |
+
|
4633 |
/**
|
4634 |
* Plugin initializer.
|
4635 |
*
|
4929 |
function sucuriscan_sitecheck_info( $res=array() ){
|
4930 |
// Will be TRUE only if the scanning results were retrieved from the cache.
|
4931 |
$display_results = (bool) $res;
|
4932 |
+
$clean_domain = SucuriScan::get_domain();
|
4933 |
|
4934 |
// If the results are not cached, then request a new scanning.
|
4935 |
if( $res === FALSE ){
|
5405 |
|
5406 |
if( $clear_cache_resp ){
|
5407 |
if( isset($clear_cache_resp->messages[0]) ){
|
5408 |
+
// Clear W3 Total Cache if it is installed.
|
5409 |
+
if( function_exists('w3tc_flush_all') ){ w3tc_flush_all(); }
|
5410 |
+
|
5411 |
SucuriScanInterface::info($clear_cache_resp->messages[0]);
|
5412 |
} else {
|
5413 |
SucuriScanInterface::error('Could not clear the cache of your site, try later again.');
|
5582 |
}
|
5583 |
}
|
5584 |
|
5585 |
+
$template_variables['AuditLogs.TargetDate'] = SucuriScan::escape($date);
|
5586 |
$template_variables['AuditLogs.DateYears'] = sucuriscan_monitoring_dates('years', $date);
|
5587 |
$template_variables['AuditLogs.DateMonths'] = sucuriscan_monitoring_dates('months', $date);
|
5588 |
$template_variables['AuditLogs.DateDays'] = sucuriscan_monitoring_dates('days', $date);
|
5605 |
'request_date',
|
5606 |
'request_time',
|
5607 |
'request_timezone',
|
5608 |
+
'request_timestamp',
|
5609 |
+
'local_request_time',
|
5610 |
'remote_addr',
|
5611 |
'sucuri_block_reason',
|
5612 |
'resource_path',
|
5668 |
|
5669 |
if( isset($access_log->{$attr_name}) ){
|
5670 |
$attr_value = $access_log->{$attr_name};
|
5671 |
+
|
5672 |
+
if(
|
5673 |
+
empty($attr_value)
|
5674 |
+
&& $attr_name == 'sucuri_block_reason'
|
5675 |
+
){
|
5676 |
+
$attr_value = 'Unknown';
|
5677 |
+
}
|
5678 |
+
}
|
5679 |
+
|
5680 |
+
elseif( $attr_name == 'local_request_time' ){
|
5681 |
+
$attr_value = SucuriScan::datetime($access_log->request_timestamp);
|
5682 |
}
|
5683 |
|
5684 |
+
$audit_log_snippet[$attr_title] = SucuriScan::escape($attr_value);
|
5685 |
}
|
5686 |
|
5687 |
$logs_html .= SucuriScanTemplate::get_snippet('monitoring-logs', $audit_log_snippet);
|
5708 |
foreach( $access_logs as $access_log ){
|
5709 |
if( !array_key_exists($access_log->sucuri_block_reason, $types) ){
|
5710 |
$denial_type_k = SucuriScan::human2var($access_log->sucuri_block_reason);
|
5711 |
+
$denial_type_v = $access_log->sucuri_block_reason;
|
5712 |
+
if( empty($denial_type_v) ){ $denial_type_v = 'Unknown'; }
|
5713 |
+
$types[$denial_type_k] = $denial_type_v;
|
5714 |
}
|
5715 |
}
|
5716 |
}
|
6017 |
@file_put_contents($htaccess_upload, $htaccess_content, LOCK_EX);
|
6018 |
}
|
6019 |
|
6020 |
+
SucuriScanInterface::info('Hardening reverted for upload directory.');
|
6021 |
} else {
|
6022 |
SucuriScanInterface::error(
|
6023 |
'File <code>/wp-content/uploads/.htaccess</code> does not exists or
|
6051 |
function sucuriscan_harden_wpcontent(){
|
6052 |
$cp = 1;
|
6053 |
$upmsg = NULL;
|
6054 |
+
$htaccess_upload = WP_CONTENT_DIR . '/.htaccess';
|
6055 |
|
6056 |
if( !is_readable($htaccess_upload) ){
|
6057 |
$cp = 0;
|
6072 |
if( @file_put_contents($htaccess_upload, "\n<Files *.php>\ndeny from all\n</Files>") === FALSE ){
|
6073 |
$upmsg = SucuriScanInterface::error('Unable to create <code>.htaccess</code> file, folder destination is not writable.');
|
6074 |
} else {
|
6075 |
+
$upmsg = SucuriScanInterface::info('Hardening applied successfully to content directory.');
|
6076 |
$cp = 1;
|
6077 |
}
|
6078 |
}
|
6089 |
@file_put_contents($htaccess_upload, $htaccess_content, LOCK_EX);
|
6090 |
}
|
6091 |
|
6092 |
+
SucuriScanInterface::info('Hardening reverted for content directory.');
|
6093 |
} else {
|
6094 |
SucuriScanInterface::info(
|
6095 |
+
'File <code>' . WP_CONTENT_DIR . '/.htaccess</code> does not exists or is not
|
6096 |
+
writable, you will need to remove the following code manually from there:
|
6097 |
<code><Files *.php>deny from all</Files></code>'
|
6098 |
);
|
6099 |
}
|
6102 |
|
6103 |
$description = 'This option blocks direct PHP access to any file inside wp-content. If you experience '
|
6104 |
. 'any issue after this with a theme or plugin in your site, like for example images not displaying, '
|
6105 |
+
. 'remove the <code>.htaccess</code> file located in the content directory.'
|
6106 |
. '</p><p><b>Note:</b> Many <em>(insecure)</em> themes and plugins use a PHP file in this directory '
|
6107 |
. 'to generate images like thumbnails and captcha codes, this is intentional so it is recommended '
|
6108 |
. 'to check your site once this option is enabled.';
|
6152 |
if( @file_put_contents($htaccess_upload, "\n<Files *.php>\ndeny from all\n</Files>\n<Files wp-tinymce.php>\nallow from all\n</Files>\n")===FALSE ){
|
6153 |
$upmsg = SucuriScanInterface::error('Unable to create <code>.htaccess</code> file, folder destination is not writable.');
|
6154 |
} else {
|
6155 |
+
$upmsg = SucuriScanInterface::info('Hardening applied successfully to library\'s directory.');
|
6156 |
$cp = 1;
|
6157 |
}
|
6158 |
}
|
6170 |
|
6171 |
@file_put_contents($htaccess_upload, $htaccess_content, LOCK_EX);
|
6172 |
}
|
6173 |
+
SucuriScanInterface::info('Hardening reverted for library\'s directory.');
|
6174 |
} else {
|
6175 |
SucuriScanInterface::error(
|
6176 |
'File <code>wp-includes/.htaccess</code> does not exists or is not
|
6620 |
$css_class = ( $counter_i % 2 == 0 ) ? '' : 'alternate';
|
6621 |
$snippet_data = array(
|
6622 |
'AuditLog.CssClass' => $css_class,
|
6623 |
+
'AuditLog.DateTime' => SucuriScan::datetime($audit_log['timestamp']),
|
6624 |
+
'AuditLog.Account' => SucuriScan::escape($audit_log['account']),
|
6625 |
+
'AuditLog.Message' => SucuriScan::escape($audit_log['message']),
|
6626 |
'AuditLog.Extra' => '',
|
6627 |
);
|
6628 |
|
6631 |
$css_scrollable = $audit_log['extra_total'] > 10 ? 'sucuriscan-list-as-table-scrollable' : '';
|
6632 |
$snippet_data['AuditLog.Extra'] .= '<ul class="sucuriscan-list-as-table ' . $css_scrollable . '">';
|
6633 |
foreach( $audit_log['extra'] as $log_extra ){
|
6634 |
+
$snippet_data['AuditLog.Extra'] .= '<li>' . SucuriScan::escape($log_extra) . '</li>';
|
6635 |
}
|
6636 |
$snippet_data['AuditLog.Extra'] .= '</ul>';
|
6637 |
$snippet_data['AuditLog.Extra'] .= '<small>For Mac users, this is a scrollable container</small>';
|
6935 |
&& $file_info['modified_at'] >= $back_days
|
6936 |
){
|
6937 |
$css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
|
6938 |
+
$mod_date = SucuriScan::datetime($file_info['modified_at']);
|
6939 |
|
6940 |
$template_variables['ModifiedFiles.List'] .= SucuriScanTemplate::get_snippet('integrity-modifiedfiles', array(
|
6941 |
'ModifiedFiles.CssClass' => $css_class,
|
6942 |
'ModifiedFiles.CheckSum' => $file_info['checksum'],
|
6943 |
'ModifiedFiles.FilePath' => $file_path,
|
6944 |
+
'ModifiedFiles.DateTime' => $mod_date,
|
6945 |
));
|
6946 |
$counter += 1;
|
6947 |
}
|
7102 |
|
7103 |
foreach( $user_list as $user ){
|
7104 |
$user->user_registered_timestamp = strtotime($user->user_registered);
|
7105 |
+
$user->user_registered_formatted = SucuriScan::datetime($user->user_registered_timestamp);
|
7106 |
$css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
|
7107 |
|
7108 |
$template_variables['ResetPassword.UserList'] .= SucuriScanTemplate::get_snippet('posthack-resetpassword', array(
|
7261 |
function sucuriscan_lastlogins_page(){
|
7262 |
SucuriScanInterface::check_permissions();
|
7263 |
|
7264 |
+
// Reset the file with the last-logins logs.
|
7265 |
+
if(
|
7266 |
+
SucuriScanInterface::check_nonce()
|
7267 |
+
&& SucuriScanRequest::post(':reset_lastlogins') !== FALSE
|
7268 |
+
){
|
7269 |
+
$file_path = sucuriscan_lastlogins_datastore_filepath();
|
7270 |
+
|
7271 |
+
if( unlink($file_path) ){
|
7272 |
+
sucuriscan_lastlogins_datastore_exists();
|
7273 |
+
SucuriScanInterface::info( 'Last-Logins logs were reset successfully.' );
|
7274 |
+
} else {
|
7275 |
+
SucuriScanInterface::error( 'Could not reset the last-logins logs.' );
|
7276 |
+
}
|
7277 |
+
}
|
7278 |
+
|
7279 |
// Page pseudo-variables initialization.
|
7280 |
$template_variables = array(
|
7281 |
'PageTitle' => 'Last Logins',
|
7321 |
if( !empty($admin->lastlogins) ){
|
7322 |
$user_snippet['AdminUsers.NoLastLogins'] = 'hidden';
|
7323 |
$user_snippet['AdminUsers.NoLastLoginsTable'] = 'visible';
|
7324 |
+
$user_snippet['AdminUsers.RegisteredAt'] = 'Unknown';
|
7325 |
$counter = 0;
|
7326 |
|
7327 |
+
foreach( $admin->lastlogins as $i => $lastlogin ){
|
7328 |
+
if( $i == 0 ){
|
7329 |
+
$user_snippet['AdminUsers.RegisteredAt'] = SucuriScan::datetime($lastlogin->user_registered_timestamp);
|
7330 |
+
}
|
7331 |
+
|
7332 |
$css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
|
7333 |
$user_snippet['AdminUsers.LastLogins'] .= SucuriScanTemplate::get_snippet('lastlogins-admins-lastlogin', array(
|
7334 |
'AdminUsers.RemoteAddr' => SucuriScan::escape($lastlogin->user_remoteaddr),
|
7335 |
+
'AdminUsers.Datetime' => SucuriScan::datetime($lastlogin->user_lastlogin_timestamp),
|
7336 |
'AdminUsers.CssClass' => $css_class,
|
7337 |
));
|
7338 |
$counter += 1;
|
7565 |
|
7566 |
if( preg_match('/^a:[0-9]+:.+/', $line) ){
|
7567 |
$last_login = @unserialize($line);
|
7568 |
+
$last_login['user_lastlogin_timestamp'] = strtotime($last_login['user_lastlogin']);
|
7569 |
+
$last_login['user_registered_timestamp'] = 0;
|
7570 |
|
7571 |
// Only administrators can see all login stats.
|
7572 |
if( !$is_admin_user && $current_user->user_login != $last_login['user_login'] ){
|
7587 |
|
7588 |
foreach( $user_account->data as $var_name=>$var_value ){
|
7589 |
$last_login[$var_name] = $var_value;
|
7590 |
+
|
7591 |
+
if( $var_name == 'user_registered' ){
|
7592 |
+
$last_login['user_registered_timestamp'] = strtotime($var_value);
|
7593 |
+
}
|
7594 |
}
|
7595 |
}
|
7596 |
|
7599 |
$parsed_lines += 1;
|
7600 |
}
|
7601 |
|
7602 |
+
else {
|
7603 |
+
$last_logins['total'] -= 1;
|
7604 |
+
}
|
7605 |
+
|
7606 |
if( preg_match('/^[0-9]+$/', $limit) && $limit>0 ){
|
7607 |
if( $parsed_lines >= $limit ){ break; }
|
7608 |
}
|
7657 |
if( isset($last_logins['entries'][1]) ){
|
7658 |
$row = $last_logins['entries'][1];
|
7659 |
|
7660 |
+
$lastlogin_message = sprintf(
|
7661 |
+
'Last time you logged in was at <code>%s</code> from <code>%s</code> - <code>%s</code>',
|
7662 |
+
date('d/M/Y H:i'), $row->user_remoteaddr, $row->user_hostname
|
7663 |
+
);
|
7664 |
$lastlogin_message .= chr(32).'(<a href="'.SucuriScanTemplate::get_url('lastlogins').'">view all logs</a>)';
|
7665 |
SucuriScanInterface::info( $lastlogin_message );
|
7666 |
}
|
7690 |
|
7691 |
foreach( (array) $logged_in_users as $logged_in_user ){
|
7692 |
$counter += 1;
|
7693 |
+
$logged_in_user['last_activity_datetime'] = SucuriScan::datetime($logged_in_user['last_activity']);
|
7694 |
+
$logged_in_user['user_registered_datetime'] = SucuriScan::datetime( strtotime($logged_in_user['user_registered']) );
|
7695 |
|
7696 |
$template_variables['LoggedInUsers.List'] .= SucuriScanTemplate::get_snippet('lastlogins-loggedin', array(
|
7697 |
'LoggedInUsers.Id' => SucuriScan::escape($logged_in_user['user_id']),
|
7898 |
'FailedLogins.Num' => ($counter + 1),
|
7899 |
'FailedLogins.Username' => SucuriScan::escape($login_data['user_login']),
|
7900 |
'FailedLogins.RemoteAddr' => SucuriScan::escape($login_data['remote_addr']),
|
7901 |
+
'FailedLogins.Datetime' => SucuriScan::datetime($login_data['attempt_time']),
|
7902 |
'FailedLogins.UserAgent' => SucuriScan::escape($login_data['user_agent']),
|
7903 |
));
|
7904 |
|
8134 |
'Settings.General' => sucuriscan_settings_general(),
|
8135 |
'Settings.Notifications' => sucuriscan_settings_notifications(),
|
8136 |
'Settings.IgnoreRules' => sucuriscan_settings_ignore_rules(),
|
8137 |
+
'Settings.Heartbeat' => sucuriscan_settings_heartbeat(),
|
8138 |
);
|
8139 |
|
8140 |
echo SucuriScanTemplate::get_template('settings', $template_variables);
|
8285 |
}
|
8286 |
}
|
8287 |
|
8288 |
+
// Update the API request timeout.
|
8289 |
+
if( $request_timeout = SucuriScanRequest::post(':request_timeout', '[0-9]+') ){
|
8290 |
+
SucuriScanOption::update_option(':request_timeout', $request_timeout);
|
8291 |
+
SucuriScanInterface::info( 'API request timeout set to <code>' . $request_timeout . '</code> seconds.' );
|
8292 |
+
}
|
8293 |
+
|
8294 |
// Update the notification settings.
|
8295 |
if( SucuriScanRequest::post(':save_notification_settings') !== FALSE ){
|
8296 |
$options_updated_counter = 0;
|
8349 |
}
|
8350 |
}
|
8351 |
|
8352 |
+
// Update the settings for the heartbeat API.
|
8353 |
+
if( $heartbeat_status = SucuriScanRequest::post(':heartbeat_status') ){
|
8354 |
+
$statuses_allowed = SucuriScanHeartbeat::statuses_allowed();
|
8355 |
+
|
8356 |
+
if( array_key_exists($heartbeat_status, $statuses_allowed) ){
|
8357 |
+
SucuriScanOption::update_option(':heartbeat', $heartbeat_status);
|
8358 |
+
SucuriScanInterface::info( 'Heartbeat status set to <code>' . $heartbeat_status . '</code>' );
|
8359 |
+
} else {
|
8360 |
+
SucuriScanInterface::error( 'Heartbeat status not allowed.' );
|
8361 |
+
}
|
8362 |
+
}
|
8363 |
+
|
8364 |
+
// Update the value of the heartbeat pulse.
|
8365 |
+
if( $heartbeat_pulse = SucuriScanRequest::post(':heartbeat_pulse') ){
|
8366 |
+
$pulses_allowed = SucuriScanHeartbeat::pulses_allowed();
|
8367 |
+
|
8368 |
+
if( array_key_exists($heartbeat_pulse, $pulses_allowed) ){
|
8369 |
+
SucuriScanOption::update_option(':heartbeat_pulse', $heartbeat_pulse);
|
8370 |
+
SucuriScanInterface::info( 'Heartbeat pulse set to <code>' . $heartbeat_pulse . '</code> seconds.' );
|
8371 |
+
} else {
|
8372 |
+
SucuriScanInterface::error( 'Heartbeat pulse not allowed.' );
|
8373 |
+
}
|
8374 |
+
}
|
8375 |
+
|
8376 |
+
// Update the value of the heartbeat interval.
|
8377 |
+
if( $heartbeat_interval = SucuriScanRequest::post(':heartbeat_interval') ){
|
8378 |
+
$intervals_allowed = SucuriScanHeartbeat::intervals_allowed();
|
8379 |
+
|
8380 |
+
if( array_key_exists($heartbeat_interval, $intervals_allowed) ){
|
8381 |
+
SucuriScanOption::update_option(':heartbeat_interval', $heartbeat_interval);
|
8382 |
+
SucuriScanInterface::info( 'Heartbeat interval set to <code>' . $heartbeat_interval . '</code>' );
|
8383 |
+
} else {
|
8384 |
+
SucuriScanInterface::error( 'Heartbeat interval not allowed.' );
|
8385 |
+
}
|
8386 |
+
}
|
8387 |
+
|
8388 |
+
// Enable or disable the auto-start execution of heartbeat.
|
8389 |
+
if( $heartbeat_autostart = SucuriScanRequest::post(':heartbeat_autostart', '(en|dis)able') ){
|
8390 |
+
$action_d = $heartbeat_autostart . 'd';
|
8391 |
+
SucuriScanOption::update_option(':heartbeat_autostart', $action_d);
|
8392 |
+
SucuriScanInterface::info( 'Heartbeat auto-start was <code>' . $action_d . '</code>' );
|
8393 |
+
}
|
8394 |
}
|
8395 |
}
|
8396 |
|
8444 |
$verify_ssl_cert = SucuriScanOption::get_option(':verify_ssl_cert');
|
8445 |
$runtime_scan_human = SucuriScanOption::get_filesystem_runtime(TRUE);
|
8446 |
|
8447 |
+
// Check whether the domain name is valid or not.
|
8448 |
+
if( !$api_key ){
|
8449 |
+
$clean_domain = SucuriScan::get_domain();
|
8450 |
+
$domain_address = @gethostbyname($clean_domain);
|
8451 |
+
$invalid_domain = ( $domain_address == $clean_domain ) ? TRUE : FALSE;
|
8452 |
+
}
|
8453 |
+
|
8454 |
// Generate the HTML code for the option list in the form select fields.
|
8455 |
$scan_freq_options = SucuriScanTemplate::get_select_options( $sucuriscan_schedule_allowed, $scan_freq );
|
8456 |
$scan_interface_options = SucuriScanTemplate::get_select_options( $sucuriscan_interface_allowed, $scan_interface );
|
8463 |
'APIKey.RecoverVisibility' => SucuriScanTemplate::visibility( !$api_key && !$display_manual_key_form ),
|
8464 |
'APIKey.ManualKeyFormVisibility' => SucuriScanTemplate::visibility($display_manual_key_form),
|
8465 |
'APIKey.RemoveVisibility' => SucuriScanTemplate::visibility($api_key),
|
8466 |
+
'InvalidDomainVisibility' => SucuriScanTemplate::visibility($invalid_domain),
|
8467 |
/* Filesystem scanner */
|
8468 |
'FsScannerStatus' => 'Enabled',
|
8469 |
'FsScannerSwitchText' => 'Disable',
|
8495 |
'MaximumFailedLoginsOptions' => $maximum_failed_logins_options,
|
8496 |
'VerifySSLCert' => 'Undefined',
|
8497 |
'VerifySSLCertOptions' => $verify_ssl_cert_options,
|
8498 |
+
'RequestTimeout' => SucuriScanOption::get_option(':request_timeout') . ' seconds',
|
8499 |
'ModalWhenAPIRegistered' => $api_registered_modal,
|
8500 |
);
|
8501 |
|
8599 |
|
8600 |
if( array_key_exists($post_type, $ignored_events) ){
|
8601 |
$is_ignored_text = 'YES';
|
8602 |
+
$was_ignored_at = SucuriScan::datetime($ignored_events[$post_type]);
|
8603 |
$is_ignored_class = 'danger';
|
8604 |
$button_action = 'remove';
|
8605 |
$button_class = 'button-primary';
|
8631 |
return SucuriScanTemplate::get_section('settings-ignorerules', $template_variables);
|
8632 |
}
|
8633 |
|
8634 |
+
/**
|
8635 |
+
* Read and parse the content of the heartbeat settings template.
|
8636 |
+
*
|
8637 |
+
* @return string Parsed HTML code for the ignored-rules settings panel.
|
8638 |
+
*/
|
8639 |
+
function sucuriscan_settings_heartbeat(){
|
8640 |
+
// Current values set in the options table.
|
8641 |
+
$heartbeat_status = SucuriScanOption::get_option(':heartbeat');
|
8642 |
+
$heartbeat_pulse = SucuriScanOption::get_option(':heartbeat_pulse');
|
8643 |
+
$heartbeat_interval = SucuriScanOption::get_option(':heartbeat_interval');
|
8644 |
+
$heartbeat_autostart = SucuriScanOption::get_option(':heartbeat_autostart');
|
8645 |
+
|
8646 |
+
// Allowed values for each setting.
|
8647 |
+
$statuses_allowed = SucuriScanHeartbeat::statuses_allowed();
|
8648 |
+
$pulses_allowed = SucuriScanHeartbeat::pulses_allowed();
|
8649 |
+
$intervals_allowed = SucuriScanHeartbeat::intervals_allowed();
|
8650 |
+
|
8651 |
+
// HTML select form fields.
|
8652 |
+
$heartbeat_options = SucuriScanTemplate::get_select_options( $statuses_allowed, $heartbeat_status );
|
8653 |
+
$heartbeat_pulse_options = SucuriScanTemplate::get_select_options( $pulses_allowed, $heartbeat_pulse );
|
8654 |
+
$heartbeat_interval_options = SucuriScanTemplate::get_select_options( $intervals_allowed, $heartbeat_interval );
|
8655 |
+
|
8656 |
+
$template_variables = array(
|
8657 |
+
'HeartbeatStatus' => 'Undefined',
|
8658 |
+
'HeartbeatPulse' => 'Undefined',
|
8659 |
+
'HeartbeatInterval' => 'Undefined',
|
8660 |
+
/* Heartbeat Options. */
|
8661 |
+
'HeartbeatStatusOptions' => $heartbeat_options,
|
8662 |
+
'HeartbeatPulseOptions' => $heartbeat_pulse_options,
|
8663 |
+
'HeartbeatIntervalOptions' => $heartbeat_interval_options,
|
8664 |
+
/* Heartbeat Auto-Start. */
|
8665 |
+
'HeartbeatAutostart' => 'Enabled',
|
8666 |
+
'HeartbeatAutostartSwitchText' => 'Disable',
|
8667 |
+
'HeartbeatAutostartSwitchValue' => 'disable',
|
8668 |
+
'HeartbeatAutostartSwitchCssClass' => 'button-danger',
|
8669 |
+
);
|
8670 |
+
|
8671 |
+
if( array_key_exists($heartbeat_status, $statuses_allowed) ){
|
8672 |
+
$template_variables['HeartbeatStatus'] = $statuses_allowed[$heartbeat_status];
|
8673 |
+
}
|
8674 |
+
|
8675 |
+
if( array_key_exists($heartbeat_pulse, $pulses_allowed) ){
|
8676 |
+
$template_variables['HeartbeatPulse'] = $pulses_allowed[$heartbeat_pulse];
|
8677 |
+
}
|
8678 |
+
|
8679 |
+
if( array_key_exists($heartbeat_interval, $intervals_allowed) ){
|
8680 |
+
$template_variables['HeartbeatInterval'] = $intervals_allowed[$heartbeat_interval];
|
8681 |
+
}
|
8682 |
+
|
8683 |
+
if( $heartbeat_autostart == 'disabled' ){
|
8684 |
+
$template_variables['HeartbeatAutostart'] = 'Disabled';
|
8685 |
+
$template_variables['HeartbeatAutostartSwitchText'] = 'Enable';
|
8686 |
+
$template_variables['HeartbeatAutostartSwitchValue'] = 'enable';
|
8687 |
+
$template_variables['HeartbeatAutostartSwitchCssClass'] = 'button-success';
|
8688 |
+
}
|
8689 |
+
|
8690 |
+
return SucuriScanTemplate::get_section('settings-heartbeat', $template_variables);
|
8691 |
+
}
|
8692 |
+
|
8693 |
/**
|
8694 |
* Generate and print the HTML code for the InfoSys page.
|
8695 |
*
|
8927 |
|
8928 |
$cronjobs = _get_cron_array();
|
8929 |
$schedules = wp_get_schedules();
|
|
|
8930 |
$counter = 0;
|
8931 |
|
8932 |
foreach( $cronjobs as $timestamp => $cronhooks ){
|
8933 |
foreach( (array) $cronhooks as $hook => $events ){
|
8934 |
foreach( (array) $events as $key => $event ){
|
8935 |
+
if( empty($event['args']) ){
|
8936 |
+
$event['args'] = array( '<em>empty</em>' );
|
8937 |
+
}
|
8938 |
+
|
8939 |
$template_variables['Cronjobs.Total'] += 1;
|
8940 |
$template_variables['Cronjobs.List'] .= SucuriScanTemplate::get_snippet('infosys-cronjobs', array(
|
8941 |
'Cronjob.Hook' => $hook,
|
8942 |
'Cronjob.Schedule' => $event['schedule'],
|
8943 |
+
'Cronjob.NextTime' => SucuriScan::datetime($timestamp),
|
8944 |
+
'Cronjob.Arguments' => SucuriScan::implode(', ', $event['args']),
|
8945 |
'Cronjob.CssClass' => ( $counter % 2 == 0 ) ? '' : 'alternate',
|
8946 |
));
|
8947 |
$counter += 1;
|