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

Version Description

  • Fixed remote scanning that was not loading automatically on some installs.
Download this release

Release Info

Developer dd@sucuri.net
Plugin Icon 128x128 Sucuri Security – Auditing, Malware Scanner and Security Hardening
Version 1.7.1
Comparing to
See all releases

Code changes from version 1.7.0 to 1.7.1

inc/css/sucuriscan-default-css.css CHANGED
@@ -103,11 +103,12 @@
103
  /* WordPress Alerts */
104
  div.sucuriscan-alert{position:relative;margin:0 0 20px 0}
105
  div.sucuriscan-alert > a.close{position:absolute;top:10px;right:10px;font-size:18px;font-weight:bold;text-decoration:none}
106
- .sucuriscan-inline-alert, .sucuriscan-inline-alert-updated, .sucuriscan-inline-alert-error, .sucuriscan-inline-alert-warning{background:#fff;box-shadow:0 1px 1px 0 rgba(0,0,0,.1);padding:0;border-left:4px solid #ddd}
107
- .sucuriscan-inline-alert > p, .sucuriscan-inline-alert-updated > p, .sucuriscan-inline-alert-error > p, .sucuriscan-inline-alert-warning > p{margin:0;padding:8px 12px;border:1px solid #ddd;border-left:0}
108
  .sucuriscan-inline-alert-updated{border-left-color:#7ad03a}
109
  .sucuriscan-inline-alert-warning{border-left-color:#ffba00}
110
  .sucuriscan-inline-alert-error{border-left-color:#dd3d36}
 
111
  /* Tabulation Panels */
112
  .sucuriscan-tabs{}
113
  .sucuriscan-tabs > ul{margin:0}
@@ -222,6 +223,7 @@ div.sucuriscan-alert > a.close{position:absolute;top:10px;right:10px;font-size:1
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 */
@@ -247,7 +249,6 @@ div.sucuriscan-alert > a.close{position:absolute;top:10px;right:10px;font-size:1
247
  .sucuriscan-maincontent #poststuff{min-width:initial;padding-top:0}
248
  .sucuriscan-maincontent .widefat tbody th.check-column{padding:6px 0 3px 0}
249
  .sucuriscan-maincontent .hardening-box .primary-secondary{margin:0 0 0 10px}
250
- .sucuri-inline-error{font-weight:bold;color:red}
251
  .sucuriscan-maincontent .alternate{background:#f5f5f5}
252
  .sucuriscan-maincontent hr{border:none;border-top:1px solid #999}
253
  .sucuriscan-maincontent table td > table{background:#fff}
103
  /* WordPress Alerts */
104
  div.sucuriscan-alert{position:relative;margin:0 0 20px 0}
105
  div.sucuriscan-alert > a.close{position:absolute;top:10px;right:10px;font-size:18px;font-weight:bold;text-decoration:none}
106
+ .sucuriscan-inline-alert, .sucuriscan-inline-alert-updated, .sucuriscan-inline-alert-error, .sucuriscan-inline-alert-warning, .sucuriscan-inline-alert-info{background:#fff;box-shadow:0 1px 1px 0 rgba(0,0,0,.1);padding:0;border-left:4px solid #ddd}
107
+ .sucuriscan-inline-alert > p, .sucuriscan-inline-alert-updated > p, .sucuriscan-inline-alert-error > p, .sucuriscan-inline-alert-warning > p, .sucuriscan-inline-alert-info > p{margin:0;padding:8px 12px;border:1px solid #ddd;border-left:0}
108
  .sucuriscan-inline-alert-updated{border-left-color:#7ad03a}
109
  .sucuriscan-inline-alert-warning{border-left-color:#ffba00}
110
  .sucuriscan-inline-alert-error{border-left-color:#dd3d36}
111
+ .sucuriscan-inline-alert-info{border-left-color:#2ea2cc}
112
  /* Tabulation Panels */
113
  .sucuriscan-tabs{}
114
  .sucuriscan-tabs > ul{margin:0}
223
  .sucuriscan-maincontent .sucuriscan-settings form{display:inline-block}
224
  .sucuriscan-maincontent .sucuriscan-settings select, .sucuriscan-maincontent .sucuriscan-settings .input-text{width:220px}
225
  .sucuriscan-maincontent .sucuriscan-settings-notifications{margin-top:0}
226
+ .sucuriscan-maincontent .sucuriscan-settings-ignorescanning{margin-top:0}
227
  .sucuriscan-maincontent .sucuriscan-settings-heartbeat{}
228
  .sucuriscan-maincontent .sucuriscan-wpcron-list{margin-top:0}
229
  /* Responsive Styles */
249
  .sucuriscan-maincontent #poststuff{min-width:initial;padding-top:0}
250
  .sucuriscan-maincontent .widefat tbody th.check-column{padding:6px 0 3px 0}
251
  .sucuriscan-maincontent .hardening-box .primary-secondary{margin:0 0 0 10px}
 
252
  .sucuriscan-maincontent .alternate{background:#f5f5f5}
253
  .sucuriscan-maincontent hr{border:none;border-top:1px solid #999}
254
  .sucuriscan-maincontent table td > table{background:#fff}
inc/tpl/posthack-resetpassword.html.tpl CHANGED
@@ -15,7 +15,7 @@
15
  <div class="sucuriscan-inline-alert-warning">
16
  <p>
17
  If you choose to change the password of your own user, then your current session
18
- will expire inmediately. You will need to log into the admin panel with the new
19
  password that will be sent to your email. If you are unsure of this, do not
20
  select your account from the list.
21
  </p>
15
  <div class="sucuriscan-inline-alert-warning">
16
  <p>
17
  If you choose to change the password of your own user, then your current session
18
+ will expire immediately. You will need to log into the admin panel with the new
19
  password that will be sent to your email. If you are unsure of this, do not
20
  select your account from the list.
21
  </p>
inc/tpl/settings-general.html.tpl CHANGED
@@ -127,92 +127,5 @@
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">
134
- <form action="%%SUCURI.URL.Settings%%" method="post">
135
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
136
- <input type="hidden" name="sucuriscan_fs_scanner" value="%%SUCURI.FsScannerSwitchValue%%" />
137
- <button type="submit" class="button-primary %%SUCURI.FsScannerSwitchCssClass%%">%%SUCURI.FsScannerSwitchText%%</button>
138
- </form>
139
- </td>
140
- </tr>
141
-
142
- <tr>
143
- <td>Scan modified files</td>
144
- <td>%%SUCURI.ScanModfilesStatus%%</td>
145
- <td class="td-with-button">
146
- <form action="%%SUCURI.URL.Settings%%" method="post">
147
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
148
- <input type="hidden" name="sucuriscan_scan_modfiles" value="%%SUCURI.ScanModfilesSwitchValue%%" />
149
- <button type="submit" class="button-primary %%SUCURI.ScanModfilesSwitchCssClass%%">%%SUCURI.ScanModfilesSwitchText%%</button>
150
- </form>
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">
158
- <form action="%%SUCURI.URL.Settings%%" method="post">
159
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
160
- <input type="hidden" name="sucuriscan_scan_checksums" value="%%SUCURI.ScanChecksumsSwitchValue%%" />
161
- <button type="submit" class="button-primary %%SUCURI.ScanChecksumsSwitchCssClass%%">%%SUCURI.ScanChecksumsSwitchText%%</button>
162
- </form>
163
- </td>
164
- </tr>
165
-
166
- <tr>
167
- <td>Scan error log files</td>
168
- <td>%%SUCURI.ScanErrorlogsStatus%%</td>
169
- <td class="td-with-button">
170
- <form action="%%SUCURI.URL.Settings%%" method="post">
171
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
172
- <input type="hidden" name="sucuriscan_scan_errorlogs" value="%%SUCURI.ScanErrorlogsSwitchValue%%" />
173
- <button type="submit" class="button-primary %%SUCURI.ScanErrorlogsSwitchCssClass%%">%%SUCURI.ScanErrorlogsSwitchText%%</button>
174
- </form>
175
- </td>
176
- </tr>
177
-
178
- <tr class="alternate">
179
- <td>Last Scanning</td>
180
- <td><span class="sucuriscan-monospace">%%SUCURI.ScanningRuntimeHuman%%</span></td>
181
- <td class="td-with-button">
182
- <form action="%%SUCURI.URL.Home%%" method="post">
183
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
184
- <button type="submit" name="sucuriscan_force_scan" class="button-primary">Force Scan</button>
185
- </form>
186
- </td>
187
- </tr>
188
-
189
- <tr>
190
- <td>Scanning frequency</td>
191
- <td>%%SUCURI.ScanningFrequency%%</td>
192
- <td class="td-with-button">
193
- <form action="%%SUCURI.URL.Settings%%" method="post">
194
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
195
- <select name="sucuriscan_scan_frequency">
196
- %%SUCURI.ScanningFrequencyOptions%%
197
- </select>
198
- <button type="submit" class="button-primary">Change</button>
199
- </form>
200
- </td>
201
- </tr>
202
-
203
- <tr class="alternate sucuriscan-%%SUCURI.ScanningInterfaceVisibility%%">
204
- <td>Scanning interface</td>
205
- <td>%%SUCURI.ScanningInterface%%</td>
206
- <td class="td-with-button">
207
- <form action="%%SUCURI.URL.Settings%%" method="post">
208
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
209
- <select name="sucuriscan_scan_interface">
210
- %%SUCURI.ScanningInterfaceOptions%%
211
- </select>
212
- <button type="submit" class="button-primary">Change</button>
213
- </form>
214
- </td>
215
- </tr>
216
-
217
  </tbody>
218
  </table>
127
  </td>
128
  </tr>
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  </tbody>
131
  </table>
inc/tpl/settings-ignorescanning.html.tpl ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div id="poststuff">
3
+ <div class="postbox sucuriscan-border sucuriscan-table-description">
4
+ <h3>Ignore Scanning</h3>
5
+
6
+ <div class="inside">
7
+ <p>
8
+ If your project has too many directories and/or files it may cause the file
9
+ system scanners to fail, you may want to increase the maximum execution time of
10
+ the PHP scripts and the memory limit to allow the functions executed during the
11
+ file system scans to finish successfully. If you do not want or do not have
12
+ sufficient privileges to increase these values then you may want to skip some
13
+ directories, this will force the plugin to ignore the files inside these
14
+ folders.
15
+ </p>
16
+ </div>
17
+ </div>
18
+ </div>
19
+
20
+ <form action="%%SUCURI.URL.Settings%%#settings-ignorescanning" method="post">
21
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
22
+
23
+ <table class="wp-list-table widefat sucuriscan-table sucuriscan-settings-ignorescanning">
24
+ <thead>
25
+ <th class="manage-column column-cb check-column">
26
+ <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
27
+ <input id="cb-select-all-1" type="checkbox">
28
+ </th>
29
+ <th class="manage-column" width="80">Ignored</th>
30
+ <th class="manage-column">Directory</th>
31
+ <th class="manage-column" width="180">Ignored At</th>
32
+ </thead>
33
+
34
+ <tbody>
35
+ %%SUCURI.IgnoreScanning.ResourceList%%
36
+ </tbody>
37
+
38
+ <tfoot>
39
+ <tr>
40
+ <td colspan="4">
41
+ <p>
42
+ Selecting one or more directories from the list will force the plugin to ignore
43
+ the monitoring of the sub-folders and files inside these directories during the
44
+ execution of any of the file system scanners. This will applies to all the
45
+ scanners <em>(general scanner, modified files, integrity checks, error
46
+ logs)</em>.
47
+ </p>
48
+
49
+ <label>
50
+ <select name="sucuriscan_ignorescanning_action">
51
+ <option value="">Choose action</option>
52
+ <option value="ignore">Ignore items</option>
53
+ <option value="unignore">Un-ignore items</option>
54
+ </select>
55
+ </label>
56
+
57
+ <button type="submit" class="button button-primary">Send action</button>
58
+ </td>
59
+ </tr>
60
+ </tfoot>
61
+ </table>
62
+ </form>
inc/tpl/settings-ignorescanning.snippet.tpl ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+
2
+ <tr class="%%SUCURI.IgnoreScanning.CssClass%%">
3
+ <td class="check-column">
4
+ <input type="checkbox" name="sucuriscan_ignorescanning_dirs[]" value="%%SUCURI.IgnoreScanning.DirectoryPath%%" />
5
+ </td>
6
+ <td><span class="sucuriscan-label-%%SUCURI.IgnoreScanning.IgnoredCssClass%%">%%SUCURI.IgnoreScanning.IgnoredAtText%%</span></td>
7
+ <td><span class="sucuriscan-monospace sucuriscan-wraptext">%%SUCURI.IgnoreScanning.DirectoryPath%%</span></td>
8
+ <td>%%SUCURI.IgnoreScanning.IgnoredAt%%</td>
9
+ </tr>
inc/tpl/settings-scanner.html.tpl ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div id="poststuff">
3
+ <div class="postbox sucuriscan-border sucuriscan-table-description">
4
+ <h3>Scanner Settings</h3>
5
+
6
+ <div class="inside">
7
+ <p>
8
+ There are multiple scanners implemented in the code of the plugin, all of them
9
+ are enabled by default and you can deactivate them separately without affect the
10
+ others. You may want to disable a scanner because your site has too many
11
+ directories and/or files to scan, or because the maximum quantity of memory
12
+ allowed for your project is not enough to execute one these functions. You can
13
+ enable and disable any of the scanners anything you want.
14
+ </p>
15
+
16
+ <div class="sucuriscan-inline-alert-info">
17
+ <p>
18
+ The <em>Scanning Interface</em> is the method that will be used internally to
19
+ retrieve the diretories and files inside the project when the file system
20
+ scanners are executed. In the best case <strong>SPL</strong> will be enough
21
+ <em>(and it is the default option)</em>, but with older versions of PHP you may
22
+ need to choose a different method like <strong>OpenDir</strong> or
23
+ <strong>Glob</strong> which provide the same results.
24
+ </p>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ </div>
29
+
30
+ <table class="wp-list-table widefat sucuriscan-table sucuriscan-settings sucuriscan-settings-scanner">
31
+ <thead>
32
+ <tr>
33
+ <th>Option</th>
34
+ <th>Value</th>
35
+ <th>&nbsp;</th>
36
+ </tr>
37
+ </thead>
38
+
39
+ <tbody>
40
+ <tr class="alternate">
41
+ <td>Filesystem scanner</td>
42
+ <td>%%SUCURI.FsScannerStatus%%</td>
43
+ <td class="td-with-button">
44
+ <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
45
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
46
+ <input type="hidden" name="sucuriscan_fs_scanner" value="%%SUCURI.FsScannerSwitchValue%%" />
47
+ <button type="submit" class="button-primary %%SUCURI.FsScannerSwitchCssClass%%">%%SUCURI.FsScannerSwitchText%%</button>
48
+ </form>
49
+ </td>
50
+ </tr>
51
+
52
+ <tr>
53
+ <td>Scan modified files</td>
54
+ <td>%%SUCURI.ScanModfilesStatus%%</td>
55
+ <td class="td-with-button">
56
+ <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
57
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
58
+ <input type="hidden" name="sucuriscan_scan_modfiles" value="%%SUCURI.ScanModfilesSwitchValue%%" />
59
+ <button type="submit" class="button-primary %%SUCURI.ScanModfilesSwitchCssClass%%">%%SUCURI.ScanModfilesSwitchText%%</button>
60
+ </form>
61
+ </td>
62
+ </tr>
63
+
64
+ <tr class="alternate">
65
+ <td>Integrity checking</td>
66
+ <td>%%SUCURI.ScanChecksumsStatus%%</td>
67
+ <td class="td-with-button">
68
+ <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
69
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
70
+ <input type="hidden" name="sucuriscan_scan_checksums" value="%%SUCURI.ScanChecksumsSwitchValue%%" />
71
+ <button type="submit" class="button-primary %%SUCURI.ScanChecksumsSwitchCssClass%%">%%SUCURI.ScanChecksumsSwitchText%%</button>
72
+ </form>
73
+ </td>
74
+ </tr>
75
+
76
+ <tr>
77
+ <td>Scan error log files</td>
78
+ <td>%%SUCURI.ScanErrorlogsStatus%%</td>
79
+ <td class="td-with-button">
80
+ <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
81
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
82
+ <input type="hidden" name="sucuriscan_scan_errorlogs" value="%%SUCURI.ScanErrorlogsSwitchValue%%" />
83
+ <button type="submit" class="button-primary %%SUCURI.ScanErrorlogsSwitchCssClass%%">%%SUCURI.ScanErrorlogsSwitchText%%</button>
84
+ </form>
85
+ </td>
86
+ </tr>
87
+
88
+ <tr class="alternate">
89
+ <td>Last Scanning</td>
90
+ <td><span class="sucuriscan-monospace">%%SUCURI.ScanningRuntimeHuman%%</span></td>
91
+ <td class="td-with-button">
92
+ <form action="%%SUCURI.URL.Home%%" method="post">
93
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
94
+ <button type="submit" name="sucuriscan_force_scan" class="button-primary">Force Scan</button>
95
+ </form>
96
+ </td>
97
+ </tr>
98
+
99
+ <tr>
100
+ <td>Scanning frequency</td>
101
+ <td>%%SUCURI.ScanningFrequency%%</td>
102
+ <td class="td-with-button">
103
+ <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
104
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
105
+ <select name="sucuriscan_scan_frequency">
106
+ %%SUCURI.ScanningFrequencyOptions%%
107
+ </select>
108
+ <button type="submit" class="button-primary">Change</button>
109
+ </form>
110
+ </td>
111
+ </tr>
112
+
113
+ <tr class="alternate">
114
+ <td>Scanning interface</td>
115
+ <td>%%SUCURI.ScanningInterface%%</td>
116
+ <td class="td-with-button">
117
+ <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
118
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
119
+ <select name="sucuriscan_scan_interface">
120
+ %%SUCURI.ScanningInterfaceOptions%%
121
+ </select>
122
+ <button type="submit" class="button-primary">Change</button>
123
+ </form>
124
+ </td>
125
+ </tr>
126
+
127
+ </tbody>
128
+ </table>
inc/tpl/settings.html.tpl CHANGED
@@ -5,10 +5,16 @@
5
  <a href="#" data-tabname="settings-general">General Settings</a>
6
  </li>
7
  <li>
8
- <a href="#" data-tabname="settings-notifications">Notification Settings</a>
9
  </li>
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>
@@ -20,6 +26,14 @@
20
  %%SUCURI.Settings.General%%
21
  </div>
22
 
 
 
 
 
 
 
 
 
23
  <div id="sucuriscan-settings-notifications">
24
  %%SUCURI.Settings.Notifications%%
25
  </div>
5
  <a href="#" data-tabname="settings-general">General Settings</a>
6
  </li>
7
  <li>
8
+ <a href="#" data-tabname="settings-scanner">Scanner Settings</a>
9
  </li>
10
  <li>
11
+ <a href="#" data-tabname="settings-ignorescanning">Ignore Scanning</a>
12
+ </li>
13
+ <li>
14
+ <a href="#" data-tabname="settings-notifications">Alert Settings</a>
15
+ </li>
16
+ <li>
17
+ <a href="#" data-tabname="settings-ignorerules">Ignore Alerts</a>
18
  </li>
19
  <li>
20
  <a href="#" data-tabname="settings-heartbeat">Heartbeat</a>
26
  %%SUCURI.Settings.General%%
27
  </div>
28
 
29
+ <div id="sucuriscan-settings-scanner">
30
+ %%SUCURI.Settings.Scanner%%
31
+ </div>
32
+
33
+ <div id="sucuriscan-settings-ignorescanning">
34
+ %%SUCURI.Settings.IgnoreScanning%%
35
+ </div>
36
+
37
  <div id="sucuriscan-settings-notifications">
38
  %%SUCURI.Settings.Notifications%%
39
  </div>
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: dd@sucuri.net
3
  Donate Link: http://sitecheck.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,
5
  Requires at least:3.2
6
- Stable tag:1.7.0
7
  Tested up to: 4.0
8
 
9
  The Sucuri WordPress Security plugin is the best security toolset for security integrity monitoring, activity monitoring and malware detection. It’s a complementary toolset to your existing security posture.
@@ -220,9 +220,90 @@ integrity checking. We encourage you to visit this section and tune your
220
  security needs as you see fit.
221
 
222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
  == Changelog ==
225
 
 
 
 
226
  = 1.7.0 =
227
  * Added Hardening option to remove error log files
228
  * Bug fixes on some new registrations.
3
  Donate Link: http://sitecheck.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,
5
  Requires at least:3.2
6
+ Stable tag:1.7.1
7
  Tested up to: 4.0
8
 
9
  The Sucuri WordPress Security plugin is the best security toolset for security integrity monitoring, activity monitoring and malware detection. It’s a complementary toolset to your existing security posture.
220
  security needs as you see fit.
221
 
222
 
223
+ == FAQ ==
224
+
225
+ = If I install the Sucuri Security plugin do I get a Sucuri account? =
226
+
227
+ No, this is a free plugin that we offer at no charge. It does not mean you get a free account.
228
+
229
+
230
+ = If I have the premium plugin, do I need the Free plugin? =
231
+
232
+ The free plugin will be replacing the Premium plugin in the coming weeks.
233
+ Eventually, there will only be one plugin. If you have the premium plugin, and
234
+ install the free version, the free version will overwrite the premium version.
235
+
236
+ = Do I still need Sucuri’s products if I have this plugin? =
237
+
238
+ Yes. This plugin compliments your existing security toolsets. It is not
239
+ designed to replace the Sucuri AntiVirus or Firewall products.
240
+
241
+ = Where do I get support for this plugin? =
242
+
243
+ The best place is to engage us via the <a
244
+ href=“https://wordpress.org/support/plugin/sucuri-scanner”>Support Forum. If
245
+ you are a client, <a href=“https://support.sucuri.net/support/?new”you can
246
+ submit a ticket here</a>.
247
+
248
+ = Does your plugin conflict with WordFence? =
249
+
250
+ The plugin does not, but there might be issues with our scanners. If you get
251
+ an “Unable to Properly Scan Your Site” It’s likely because the WordFence
252
+ plugin is blocking our scanner as an invalid crawler.
253
+
254
+ You would have to white list our IP address on the WordFence dashboard.
255
+
256
+
257
+ = What are the Remote Security Malware Scanning Limitations? =
258
+
259
+ Because the security malware scanner is remote, it is unable to see things
260
+ that are on the server but that are not displaying on the browser. If you are
261
+ interested in this, we encourage you to subscribe to our Website AntiVirus
262
+ product.
263
+
264
+ This issues includes things like Phishing pages, Backdoors, Mailer Scripts,
265
+ etc…
266
+
267
+ = Your plugin didn’t detect this malware? =
268
+
269
+ This happens, reference the Remote scanner limitations above. This should not
270
+ be confused with our Website AntiVirus product. If you have malware, and you
271
+ are a client, submit a ticket so that <a
272
+ href=“https://support.sucuri.net/support/?new&mremoval”>we can help you get
273
+ clean.</a>
274
+
275
+ If you are not a client, and you want to share what you have found please send
276
+ it to <a href=“mailto:labs@sucuri.net”>labs@sucuri.net</a>.
277
+
278
+ The plugin is not performing application level malware / security scanning so
279
+ this is not uncommon.
280
+
281
+ = Is it free to enable the Website Firewall option? =
282
+
283
+ No, it is not. To enable you must subscribe to the <a
284
+ href=“https://sucuri.net/website-firewall-signup”>Website Firewall
285
+ service</a>.
286
+
287
+ = Will this plugin impact the performance of my website? =
288
+
289
+ No, it will not.
290
+
291
+ = Do the logs get stored to my database? =
292
+
293
+ No, it does not.
294
+
295
+ = Are there any issues installing your plugin with any hosts? =
296
+
297
+ Not that we are aware of.
298
+
299
+
300
+
301
 
302
  == Changelog ==
303
 
304
+ = 1.7.1 =
305
+ * Fixed remote scanning that was not loading automatically on some installs.
306
+
307
  = 1.7.0 =
308
  * Added Hardening option to remove error log files
309
  * Bug fixes on some new registrations.
sucuri.php CHANGED
@@ -4,7 +4,7 @@ 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.7.0
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.7.0');
70
 
71
  /**
72
  * The name of the Sucuri plugin main file.
@@ -560,7 +560,8 @@ class SucuriScan {
560
  /**
561
  * Retrieve the real ip address of the user in the current request.
562
  *
563
- * @return string The real ip address of the user in the current request.
 
564
  */
565
  public static function get_remote_addr( $return_header=FALSE ){
566
  $remote_addr = '';
@@ -692,6 +693,19 @@ class SucuriScan {
692
  return FALSE;
693
  }
694
 
 
 
 
 
 
 
 
 
 
 
 
 
 
695
  /**
696
  * Retrieve the date in localized format, based on timestamp.
697
  *
@@ -720,7 +734,7 @@ class SucuriScan {
720
  * @return string The date, translated if locale specifies it.
721
  */
722
  public static function current_datetime(){
723
- $local_time = current_time('timestamp');
724
 
725
  return self::datetime($local_time);
726
  }
@@ -736,7 +750,7 @@ class SucuriScan {
736
  $timestamp = strtotime($timestamp);
737
  }
738
 
739
- $local_time = current_time('timestamp');
740
  $diff = abs( $local_time - intval($timestamp) );
741
 
742
  if( $diff == 0 ){ return 'just now'; }
@@ -891,6 +905,21 @@ class SucuriScan {
891
  return $text;
892
  }
893
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
894
  /**
895
  * Check whether an list is a multidimensional array or not.
896
  *
@@ -984,7 +1013,7 @@ class SucuriScanRequest extends SucuriScan {
984
  }
985
 
986
  // Check the format of the request data with a regex defined above.
987
- if( preg_match($pattern, $key_value) ){
988
  return self::escape($key_value);
989
  }
990
  }
@@ -1058,6 +1087,16 @@ class SucuriScanFileInfo extends SucuriScan {
1058
  */
1059
  public $ignore_directories = TRUE;
1060
 
 
 
 
 
 
 
 
 
 
 
1061
  /**
1062
  * Whether the filesystem scanner should run recursively or not.
1063
  *
@@ -1086,34 +1125,37 @@ class SucuriScanFileInfo extends SucuriScan {
1086
  $project_signatures = '';
1087
  $abs_path = rtrim( ABSPATH, '/' );
1088
  $files = $this->get_directory_tree($directory, $scan_with);
1089
- sort($files);
1090
 
1091
  if( $as_array ){
1092
  $project_signatures = array();
1093
  }
1094
 
1095
- foreach( $files as $filepath){
1096
- $file_checksum = @md5_file($filepath);
1097
- $filesize = @filesize($filepath);
1098
 
1099
- if( $as_array ){
1100
- $basename = str_replace( $abs_path . '/', '', $filepath );
1101
- $project_signatures[$basename] = array(
1102
- 'filepath' => $filepath,
1103
- 'checksum' => $file_checksum,
1104
- 'filesize' => $filesize,
1105
- 'created_at' => filectime($filepath),
1106
- 'modified_at' => filemtime($filepath),
1107
- );
1108
- } else {
1109
- $filepath = str_replace( $abs_path, $abs_path . '/', $filepath );
1110
- $project_signatures .= sprintf(
1111
- "%s%s%s%s\n",
1112
- $file_checksum,
1113
- $filesize,
1114
- chr(32),
1115
- $filepath
1116
- );
 
 
 
 
 
1117
  }
1118
  }
1119
 
@@ -1132,6 +1174,7 @@ class SucuriScanFileInfo extends SucuriScan {
1132
  public function get_directory_tree($directory='', $scan_with='spl'){
1133
  if( file_exists($directory) && is_dir($directory) ){
1134
  $tree = array();
 
1135
 
1136
  switch( $scan_with ){
1137
  case 'spl':
@@ -1333,21 +1376,31 @@ class SucuriScanFileInfo extends SucuriScan {
1333
  /**
1334
  * Skip some specific directories and file paths from the filesystem scan.
1335
  *
1336
- * @param string $directory Directory where the scanner is located at the moment.
1337
- * @param string $filename Name of the folder or file being scanned at the moment.
1338
- * @return boolean Either TRUE or FALSE representing that the scan should ignore this folder or not.
1339
  */
1340
  private function ignore_folderpath( $directory='', $filename='' ){
1341
  // Ignoring current and parent folders.
1342
  if( $filename == '.' || $filename == '..' ){ return TRUE; }
1343
 
1344
  if( $this->ignore_directories ){
 
1345
  $filepath = realpath( $directory . '/' . $filename );
1346
  $pattern = '/\/wp-content\/(uploads|cache|backup|w3tc)/';
1347
 
1348
  if( preg_match($pattern, $filepath) ){
1349
  return TRUE;
1350
  }
 
 
 
 
 
 
 
 
 
1351
  }
1352
 
1353
  return FALSE;
@@ -2381,30 +2434,6 @@ class SucuriScanOption extends SucuriScanRequest {
2381
  return $response;
2382
  }
2383
 
2384
- /**
2385
- * Retrieve the last time when the filesystem scan was ran.
2386
- *
2387
- * @param boolean $format Whether the timestamp must be formatted as date/time or not.
2388
- * @return string The timestamp of the runtime, or an string with the date/time.
2389
- */
2390
- public static function get_filesystem_runtime( $format=FALSE ){
2391
- $runtime = self::get_option(':runtime');
2392
-
2393
- if( $runtime > 0 ){
2394
- if( $format ){
2395
- return SucuriScan::datetime($runtime);
2396
- }
2397
-
2398
- return $runtime;
2399
- }
2400
-
2401
- if( $format ){
2402
- return '<em>Unknown</em>';
2403
- }
2404
-
2405
- return FALSE;
2406
- }
2407
-
2408
  }
2409
 
2410
  /**
@@ -3455,7 +3484,8 @@ class SucuriScanAPI extends SucuriScanOption {
3455
  isset($response['headers']['content-type'])
3456
  && $response['headers']['content-type'] == 'application/json'
3457
  ){
3458
- $response['body'] = @json_decode($response['body_raw']);
 
3459
  }
3460
 
3461
  // Check if the response data is serialized (which we will consider as insecure).
@@ -3899,6 +3929,8 @@ class SucuriScanAPI extends SucuriScanOption {
3899
  'fromwp' => 2,
3900
  'clear' => 1,
3901
  'json' => 1,
 
 
3902
  ));
3903
 
3904
  if( $response ){
@@ -4625,6 +4657,161 @@ class SucuriScanTemplate extends SucuriScanRequest {
4625
 
4626
  }
4627
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4628
  /**
4629
  * Heartbeat library.
4630
  *
@@ -6960,7 +7147,13 @@ function sucuriscan_core_files(){
6960
 
6961
  foreach( $file_list as $file_path ){
6962
  // Skip files that were marked as fixed.
6963
- if( array_key_exists(md5($file_path), $ignored_files) ){ continue; }
 
 
 
 
 
 
6964
 
6965
  // Generate the HTML code from the snippet template for this file.
6966
  $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
@@ -8360,6 +8553,8 @@ function sucuriscan_settings_page(){
8360
  $template_variables = array(
8361
  'PageTitle' => 'Settings',
8362
  'Settings.General' => sucuriscan_settings_general(),
 
 
8363
  'Settings.Notifications' => sucuriscan_settings_notifications(),
8364
  'Settings.IgnoreRules' => sucuriscan_settings_ignore_rules(),
8365
  'Settings.Heartbeat' => sucuriscan_settings_heartbeat(),
@@ -8443,9 +8638,7 @@ function sucuriscan_settings_form_submissions( $page_nonce=NULL ){
8443
 
8444
  // Modify the schedule of the filesystem scanner.
8445
  if( $frequency = SucuriScanRequest::post(':scan_frequency') ){
8446
- $allowed_frequency = array_keys($sucuriscan_schedule_allowed);
8447
-
8448
- if( in_array($frequency, $allowed_frequency) ){
8449
  SucuriScanOption::update_option(':scan_frequency', $frequency);
8450
  wp_clear_scheduled_hook('sucuriscan_scheduled_scan');
8451
 
@@ -8453,8 +8646,9 @@ function sucuriscan_settings_form_submissions( $page_nonce=NULL ){
8453
  wp_schedule_event( time()+10, $frequency, 'sucuriscan_scheduled_scan' );
8454
  }
8455
 
8456
- SucuriScanEvent::notify_event( 'plugin_change', 'Filesystem scanning frequency changed to: ' . $frequency );
8457
- SucuriScanInterface::info( 'Filesystem scan scheduled to run <code>'.$frequency.'</code>' );
 
8458
  }
8459
  }
8460
 
@@ -8587,6 +8781,31 @@ function sucuriscan_settings_form_submissions( $page_nonce=NULL ){
8587
  }
8588
  }
8589
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8590
  // Update the settings for the heartbeat API.
8591
  if( $heartbeat_status = SucuriScanRequest::post(':heartbeat_status') ){
8592
  $statuses_allowed = SucuriScanHeartbeat::statuses_allowed();
@@ -8639,9 +8858,7 @@ function sucuriscan_settings_form_submissions( $page_nonce=NULL ){
8639
  */
8640
  function sucuriscan_settings_general(){
8641
 
8642
- global $sucuriscan_schedule_allowed,
8643
- $sucuriscan_interface_allowed,
8644
- $sucuriscan_emails_per_hour,
8645
  $sucuriscan_maximum_failed_logins,
8646
  $sucuriscan_verify_ssl_cert;
8647
 
@@ -8672,16 +8889,9 @@ function sucuriscan_settings_general(){
8672
 
8673
  // Get initial variables to decide some things bellow.
8674
  $api_key = SucuriScanAPI::get_plugin_key();
8675
- $fs_scanner = SucuriScanOption::get_option(':fs_scanner');
8676
- $scan_freq = SucuriScanOption::get_option(':scan_frequency');
8677
- $scan_interface = SucuriScanOption::get_option(':scan_interface');
8678
- $scan_modfiles = SucuriScanOption::get_option(':scan_modfiles');
8679
- $scan_checksums = SucuriScanOption::get_option(':scan_checksums');
8680
- $scan_errorlogs = SucuriScanOption::get_option(':scan_errorlogs');
8681
  $emails_per_hour = SucuriScanOption::get_option(':emails_per_hour');
8682
  $maximum_failed_logins = SucuriScanOption::get_option(':maximum_failed_logins');
8683
  $verify_ssl_cert = SucuriScanOption::get_option(':verify_ssl_cert');
8684
- $runtime_scan_human = SucuriScanOption::get_filesystem_runtime(TRUE);
8685
 
8686
  // Check whether the domain name is valid or not.
8687
  if( !$api_key ){
@@ -8691,18 +8901,66 @@ function sucuriscan_settings_general(){
8691
  }
8692
 
8693
  // Generate the HTML code for the option list in the form select fields.
8694
- $scan_freq_options = SucuriScanTemplate::get_select_options( $sucuriscan_schedule_allowed, $scan_freq );
8695
- $scan_interface_options = SucuriScanTemplate::get_select_options( $sucuriscan_interface_allowed, $scan_interface );
8696
  $emails_per_hour_options = SucuriScanTemplate::get_select_options( $sucuriscan_emails_per_hour, $emails_per_hour );
8697
  $maximum_failed_logins_options = SucuriScanTemplate::get_select_options( $sucuriscan_maximum_failed_logins, $maximum_failed_logins );
8698
  $verify_ssl_cert_options = SucuriScanTemplate::get_select_options( $sucuriscan_verify_ssl_cert, $verify_ssl_cert );
8699
 
8700
  $template_variables = array(
8701
- 'APIKey' => $api_key,
8702
  'APIKey.RecoverVisibility' => SucuriScanTemplate::visibility( !$api_key && !$display_manual_key_form ),
8703
  'APIKey.ManualKeyFormVisibility' => SucuriScanTemplate::visibility($display_manual_key_form),
8704
  'APIKey.RemoveVisibility' => SucuriScanTemplate::visibility($api_key),
8705
  'InvalidDomainVisibility' => SucuriScanTemplate::visibility($invalid_domain),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8706
  /* Filesystem scanner */
8707
  'FsScannerStatus' => 'Enabled',
8708
  'FsScannerSwitchText' => 'Disable',
@@ -8728,19 +8986,8 @@ function sucuriscan_settings_general(){
8728
  'ScanningFrequencyOptions' => $scan_freq_options,
8729
  'ScanningInterface' => ( $scan_interface ? $sucuriscan_interface_allowed[$scan_interface] : 'Undefined' ),
8730
  'ScanningInterfaceOptions' => $scan_interface_options,
8731
- 'ScanningInterfaceVisibility' => SucuriScanTemplate::visibility( !SucuriScanFileInfo::is_spl_available() ),
8732
  /* Filesystem scanning runtime. */
8733
  'ScanningRuntimeHuman' => $runtime_scan_human,
8734
- 'ModalWhenAPIRegistered' => $api_registered_modal,
8735
- 'NotifyTo' => SucuriScanOption::get_option(':notify_to'),
8736
- 'EmailsPerHour' => 'Undefined',
8737
- 'EmailsPerHourOptions' => $emails_per_hour_options,
8738
- 'MaximumFailedLogins' => 'Undefined',
8739
- 'MaximumFailedLoginsOptions' => $maximum_failed_logins_options,
8740
- 'VerifySSLCert' => 'Undefined',
8741
- 'VerifySSLCertOptions' => $verify_ssl_cert_options,
8742
- 'RequestTimeout' => SucuriScanOption::get_option(':request_timeout') . ' seconds',
8743
- 'ModalWhenAPIRegistered' => $api_registered_modal,
8744
  );
8745
 
8746
  if( $fs_scanner == 'disabled' ){
@@ -8775,19 +9022,7 @@ function sucuriscan_settings_general(){
8775
  $template_variables['ScanningFrequency'] = $sucuriscan_schedule_allowed[$scan_freq];
8776
  }
8777
 
8778
- if( array_key_exists($emails_per_hour, $sucuriscan_emails_per_hour) ){
8779
- $template_variables['EmailsPerHour'] = $sucuriscan_emails_per_hour[$emails_per_hour];
8780
- }
8781
-
8782
- if( array_key_exists($maximum_failed_logins, $sucuriscan_maximum_failed_logins) ){
8783
- $template_variables['MaximumFailedLogins'] = $sucuriscan_maximum_failed_logins[$maximum_failed_logins];
8784
- }
8785
-
8786
- if( array_key_exists($verify_ssl_cert, $sucuriscan_verify_ssl_cert) ){
8787
- $template_variables['VerifySSLCert'] = $sucuriscan_verify_ssl_cert[$verify_ssl_cert];
8788
- }
8789
-
8790
- return SucuriScanTemplate::get_section('settings-general', $template_variables);
8791
  }
8792
 
8793
  /**
@@ -8883,10 +9118,62 @@ function sucuriscan_settings_ignore_rules(){
8883
  return SucuriScanTemplate::get_section('settings-ignorerules', $template_variables);
8884
  }
8885
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8886
  /**
8887
  * Read and parse the content of the heartbeat settings template.
8888
  *
8889
- * @return string Parsed HTML code for the ignored-rules settings panel.
8890
  */
8891
  function sucuriscan_settings_heartbeat(){
8892
  // Current values set in the options table.
@@ -9269,7 +9556,7 @@ function sucuriscan_server_info(){
9269
  $info_vars = array(
9270
  'Plugin_version' => SUCURISCAN_VERSION,
9271
  'Plugin_checksum' => SUCURISCAN_PLUGIN_CHECKSUM,
9272
- 'Last_filesystem_scan' => SucuriScanOption::get_filesystem_runtime(TRUE),
9273
  'Using_CloudProxy' => 'Unknown',
9274
  'HTTP_Host' => 'Unknown',
9275
  'Host_Name' => 'Unknown',
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.7.1
8
  Author URI: http://sucuri.net
9
  */
10
 
66
  /**
67
  * Current version of the plugin's code.
68
  */
69
+ define('SUCURISCAN_VERSION', '1.7.1');
70
 
71
  /**
72
  * The name of the Sucuri plugin main file.
560
  /**
561
  * Retrieve the real ip address of the user in the current request.
562
  *
563
+ * @param boolean $return_header Whether the header name where the address was found must be returned.
564
+ * @return string The real ip address of the user in the current request.
565
  */
566
  public static function get_remote_addr( $return_header=FALSE ){
567
  $remote_addr = '';
693
  return FALSE;
694
  }
695
 
696
+ /**
697
+ * Returns the current time measured in the number of seconds since the Unix Epoch.
698
+ *
699
+ * @return integer Return current Unix timestamp.
700
+ */
701
+ public static function local_time(){
702
+ if( function_exists('current_time') ){
703
+ return current_time('timestamp');
704
+ } else {
705
+ return time();
706
+ }
707
+ }
708
+
709
  /**
710
  * Retrieve the date in localized format, based on timestamp.
711
  *
734
  * @return string The date, translated if locale specifies it.
735
  */
736
  public static function current_datetime(){
737
+ $local_time = self::local_time();
738
 
739
  return self::datetime($local_time);
740
  }
750
  $timestamp = strtotime($timestamp);
751
  }
752
 
753
+ $local_time = self::local_time();
754
  $diff = abs( $local_time - intval($timestamp) );
755
 
756
  if( $diff == 0 ){ return 'just now'; }
905
  return $text;
906
  }
907
 
908
+ /**
909
+ * Same as the excerpt method but with the string reversed.
910
+ *
911
+ * @param string $text String of characters that will be cut.
912
+ * @param integer $length Maximum length of the returned string, default is 10.
913
+ * @return string Short version of the text specified.
914
+ */
915
+ public static function excerpt_rev( $text='', $length=10 ){
916
+ $str_reversed = strrev($text);
917
+ $str_excerpt = self::excerpt( $str_reversed, $length );
918
+ $text_transformed = strrev($str_excerpt);
919
+
920
+ return $text_transformed;
921
+ }
922
+
923
  /**
924
  * Check whether an list is a multidimensional array or not.
925
  *
1013
  }
1014
 
1015
  // Check the format of the request data with a regex defined above.
1016
+ if( @preg_match($pattern, $key_value) ){
1017
  return self::escape($key_value);
1018
  }
1019
  }
1087
  */
1088
  public $ignore_directories = TRUE;
1089
 
1090
+ /**
1091
+ * A list of ignored directory paths, these folders will be skipped during the
1092
+ * execution of the file system scans, and any sub-directory or files inside
1093
+ * these paths will be ignored too.
1094
+ *
1095
+ * @see SucuriScanFSScanner.get_ignored_directories()
1096
+ * @var array
1097
+ */
1098
+ private $ignored_directories = array();
1099
+
1100
  /**
1101
  * Whether the filesystem scanner should run recursively or not.
1102
  *
1125
  $project_signatures = '';
1126
  $abs_path = rtrim( ABSPATH, '/' );
1127
  $files = $this->get_directory_tree($directory, $scan_with);
 
1128
 
1129
  if( $as_array ){
1130
  $project_signatures = array();
1131
  }
1132
 
1133
+ if( $files ){
1134
+ sort($files);
 
1135
 
1136
+ foreach( $files as $filepath){
1137
+ $file_checksum = @md5_file($filepath);
1138
+ $filesize = @filesize($filepath);
1139
+
1140
+ if( $as_array ){
1141
+ $basename = str_replace( $abs_path . '/', '', $filepath );
1142
+ $project_signatures[$basename] = array(
1143
+ 'filepath' => $filepath,
1144
+ 'checksum' => $file_checksum,
1145
+ 'filesize' => $filesize,
1146
+ 'created_at' => filectime($filepath),
1147
+ 'modified_at' => filemtime($filepath),
1148
+ );
1149
+ } else {
1150
+ $filepath = str_replace( $abs_path, $abs_path . '/', $filepath );
1151
+ $project_signatures .= sprintf(
1152
+ "%s%s%s%s\n",
1153
+ $file_checksum,
1154
+ $filesize,
1155
+ chr(32),
1156
+ $filepath
1157
+ );
1158
+ }
1159
  }
1160
  }
1161
 
1174
  public function get_directory_tree($directory='', $scan_with='spl'){
1175
  if( file_exists($directory) && is_dir($directory) ){
1176
  $tree = array();
1177
+ $this->ignored_directories = SucuriScanFSScanner::get_ignored_directories();
1178
 
1179
  switch( $scan_with ){
1180
  case 'spl':
1376
  /**
1377
  * Skip some specific directories and file paths from the filesystem scan.
1378
  *
1379
+ * @param string $directory Directory where the scanner is located at the moment.
1380
+ * @param string $filename Name of the folder or file being scanned at the moment.
1381
+ * @return boolean Either TRUE or FALSE representing that the scan should ignore this folder or not.
1382
  */
1383
  private function ignore_folderpath( $directory='', $filename='' ){
1384
  // Ignoring current and parent folders.
1385
  if( $filename == '.' || $filename == '..' ){ return TRUE; }
1386
 
1387
  if( $this->ignore_directories ){
1388
+ // Ignore directories based on a common regular expression.
1389
  $filepath = realpath( $directory . '/' . $filename );
1390
  $pattern = '/\/wp-content\/(uploads|cache|backup|w3tc)/';
1391
 
1392
  if( preg_match($pattern, $filepath) ){
1393
  return TRUE;
1394
  }
1395
+
1396
+ // Ignore directories specified by the administrator.
1397
+ if( !empty($this->ignored_directories) ){
1398
+ foreach( $this->ignored_directories['directories'] as $ignored_dir ){
1399
+ if( strpos($directory, $ignored_dir) !== FALSE ){
1400
+ return TRUE;
1401
+ }
1402
+ }
1403
+ }
1404
  }
1405
 
1406
  return FALSE;
2434
  return $response;
2435
  }
2436
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2437
  }
2438
 
2439
  /**
3484
  isset($response['headers']['content-type'])
3485
  && $response['headers']['content-type'] == 'application/json'
3486
  ){
3487
+ $assoc = ( isset($args['assoc']) && $args['assoc'] === TRUE ) ? TRUE : FALSE;
3488
+ $response['body'] = @json_decode($response['body_raw'], $assoc);
3489
  }
3490
 
3491
  // Check if the response data is serialized (which we will consider as insecure).
3929
  'fromwp' => 2,
3930
  'clear' => 1,
3931
  'json' => 1,
3932
+ ), array(
3933
+ 'assoc' => TRUE
3934
  ));
3935
 
3936
  if( $response ){
4657
 
4658
  }
4659
 
4660
+ /**
4661
+ * File System Scanner
4662
+ *
4663
+ * The File System Scanner component performs full and incremental scans over a
4664
+ * file system folder, maintaining a snapshot of the filesystem and comparing it
4665
+ * with the current content to establish what content has been updated. Updated
4666
+ * content is then submitted to the remote server and it is stored for future
4667
+ * analysis.
4668
+ */
4669
+ class SucuriScanFSScanner extends SucuriScan {
4670
+
4671
+ /**
4672
+ * Retrieve the last time when the filesystem scan was ran.
4673
+ *
4674
+ * @param boolean $format Whether the timestamp must be formatted as date/time or not.
4675
+ * @return string The timestamp of the runtime, or an string with the date/time.
4676
+ */
4677
+ public static function get_filesystem_runtime( $format=FALSE ){
4678
+ $runtime = SucuriScanOption::get_option(':runtime');
4679
+
4680
+ if( $runtime > 0 ){
4681
+ if( $format ){
4682
+ return SucuriScan::datetime($runtime);
4683
+ }
4684
+
4685
+ return $runtime;
4686
+ }
4687
+
4688
+ if( $format ){
4689
+ return '<em>Unknown</em>';
4690
+ }
4691
+
4692
+ return FALSE;
4693
+ }
4694
+
4695
+ /**
4696
+ * Add a new directory path to the list of ignored paths.
4697
+ *
4698
+ * @param string $directory_path The (full) absolute path of a directory.
4699
+ * @return boolean TRUE if the directory path was added to the list, FALSE otherwise.
4700
+ */
4701
+ public static function ignore_directory( $directory_path='' ){
4702
+ $cache = new SucuriScanCache('ignorescanning');
4703
+
4704
+ // Use the checksum of the directory path as the cache key.
4705
+ $cache_key = md5($directory_path);
4706
+ $cache_value = array(
4707
+ 'directory_path' => $directory_path,
4708
+ 'ignored_at' => self::local_time(),
4709
+ );
4710
+ $cached = $cache->add( $cache_key, $cache_value );
4711
+
4712
+ return $cached;
4713
+ }
4714
+
4715
+ /**
4716
+ * Remove a directory path from the list of ignored paths.
4717
+ *
4718
+ * @param string $directory_path The (full) absolute path of a directory.
4719
+ * @return boolean TRUE if the directory path was removed to the list, FALSE otherwise.
4720
+ */
4721
+ public static function unignore_directory( $directory_path='' ){
4722
+ $cache = new SucuriScanCache('ignorescanning');
4723
+
4724
+ // Use the checksum of the directory path as the cache key.
4725
+ $cache_key = md5($directory_path);
4726
+ $removed = $cache->delete($cache_key);
4727
+
4728
+ return $removed;
4729
+ }
4730
+
4731
+ /**
4732
+ * Retrieve a list of directories ignored.
4733
+ *
4734
+ * Retrieve a list of directory paths that will be ignored during the file
4735
+ * system scans, any sub-directory and files inside these folders will be
4736
+ * skipped automatically and will not be used to detect malware or modifications
4737
+ * in the site.
4738
+ *
4739
+ * The structure of the array returned by the function will always be composed
4740
+ * by four (4) indexes which will facilitate the execution of common conditions
4741
+ * in the implementation code.
4742
+ *
4743
+ * <ul>
4744
+ * <li>raw: Will contains the raw data retrieved from the built-in cache system.</li>
4745
+ * <li>checksums: Will contains the md5 of all the directory paths.</li>
4746
+ * <li>directories: Will contains a list of directory paths.</li>
4747
+ * <li>ignored_at_list: Will contains a list of timestamps for when the directories were ignored.</li>
4748
+ * </ul>
4749
+ *
4750
+ * @return array List of ignored directory paths.
4751
+ */
4752
+ public static function get_ignored_directories(){
4753
+ $response = array(
4754
+ 'raw' => array(),
4755
+ 'checksums' => array(),
4756
+ 'directories' => array(),
4757
+ 'ignored_at_list' => array(),
4758
+ );
4759
+
4760
+ $cache = new SucuriScanCache('ignorescanning');
4761
+ $cache_lifetime = 0; // It is not necessary to expire this cache.
4762
+ $ignored_directories = $cache->get_all( $cache_lifetime, 'array' );
4763
+
4764
+ if( $ignored_directories ){
4765
+ $response['raw'] = $ignored_directories;
4766
+
4767
+ foreach( $ignored_directories as $checksum => $data ){
4768
+ $response['checksums'][] = $checksum;
4769
+ $response['directories'][] = $data['directory_path'];
4770
+ $response['ignored_at_list'][] = $data['ignored_at'];
4771
+ }
4772
+ }
4773
+
4774
+ return $response;
4775
+ }
4776
+
4777
+ /**
4778
+ * Run file system scan and retrieve ignored folders.
4779
+ *
4780
+ * Run a file system scan and retrieve an array with two indexes, the first
4781
+ * containing a list of ignored directory paths and their respective timestamps
4782
+ * of when they were added by an administrator user, and the second containing a
4783
+ * list of directories that are not being ignored.
4784
+ *
4785
+ * @return array List of ignored and not ignored directories.
4786
+ */
4787
+ public static function get_ignored_directories_live(){
4788
+ $response = array(
4789
+ 'is_ignored' => array(),
4790
+ 'is_not_ignored' => array(),
4791
+ );
4792
+
4793
+ // Get the ignored directories from the cache.
4794
+ $ignored_directories = self::get_ignored_directories();
4795
+
4796
+ if( $ignored_directories ){
4797
+ $response['is_ignored'] = $ignored_directories['raw'];
4798
+ }
4799
+
4800
+ // Scan the project and file all directories.
4801
+ $sucuri_fileinfo = new SucuriScanFileInfo();
4802
+ $sucuri_fileinfo->ignore_files = TRUE;
4803
+ $sucuri_fileinfo->ignore_directories = TRUE;
4804
+ $directory_list = $sucuri_fileinfo->get_diretories_only(ABSPATH);
4805
+
4806
+ if( $directory_list ){
4807
+ $response['is_not_ignored'] = $directory_list;
4808
+ }
4809
+
4810
+ return $response;
4811
+ }
4812
+
4813
+ }
4814
+
4815
  /**
4816
  * Heartbeat library.
4817
  *
7147
 
7148
  foreach( $file_list as $file_path ){
7149
  // Skip files that were marked as fixed.
7150
+ if( $ignored_files ){
7151
+ $file_path_checksum = md5($file_path);
7152
+
7153
+ if( array_key_exists($file_path_checksum, $ignored_files) ){
7154
+ continue;
7155
+ }
7156
+ }
7157
 
7158
  // Generate the HTML code from the snippet template for this file.
7159
  $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
8553
  $template_variables = array(
8554
  'PageTitle' => 'Settings',
8555
  'Settings.General' => sucuriscan_settings_general(),
8556
+ 'Settings.Scanner' => sucuriscan_settings_scanner(),
8557
+ 'Settings.IgnoreScanning' => sucuriscan_settings_ignorescanning(),
8558
  'Settings.Notifications' => sucuriscan_settings_notifications(),
8559
  'Settings.IgnoreRules' => sucuriscan_settings_ignore_rules(),
8560
  'Settings.Heartbeat' => sucuriscan_settings_heartbeat(),
8638
 
8639
  // Modify the schedule of the filesystem scanner.
8640
  if( $frequency = SucuriScanRequest::post(':scan_frequency') ){
8641
+ if( array_key_exists($frequency, $sucuriscan_schedule_allowed) ){
 
 
8642
  SucuriScanOption::update_option(':scan_frequency', $frequency);
8643
  wp_clear_scheduled_hook('sucuriscan_scheduled_scan');
8644
 
8646
  wp_schedule_event( time()+10, $frequency, 'sucuriscan_scheduled_scan' );
8647
  }
8648
 
8649
+ $frequency_title = $sucuriscan_schedule_allowed[$frequency];
8650
+ SucuriScanEvent::notify_event( 'plugin_change', 'Filesystem scanning frequency changed to: ' . $frequency_title );
8651
+ SucuriScanInterface::info( 'Filesystem scan scheduled to run <code>'.$frequency_title.'</code>' );
8652
  }
8653
  }
8654
 
8781
  }
8782
  }
8783
 
8784
+ // Ignore a new directory path for the file system scans.
8785
+ if( $action = SucuriScanRequest::post(':ignorescanning_action', '(ignore|unignore)') ){
8786
+ $ignore_directories = SucuriScanRequest::post(':ignorescanning_dirs', '_array');
8787
+
8788
+ if( empty($ignore_directories) ){
8789
+ SucuriScanInterface::error( 'You did not choose a directory path from the list.' );
8790
+ }
8791
+
8792
+ elseif( $action == 'ignore' ){
8793
+ foreach( $ignore_directories as $directory_path ){
8794
+ SucuriScanFSScanner::ignore_directory($directory_path);
8795
+ }
8796
+
8797
+ SucuriScanInterface::info( 'Directories selected from the list will be ignored in future scans.' );
8798
+ }
8799
+
8800
+ elseif( $action == 'unignore' ) {
8801
+ foreach( $ignore_directories as $directory_path ){
8802
+ SucuriScanFSScanner::unignore_directory($directory_path);
8803
+ }
8804
+
8805
+ SucuriScanInterface::info( 'Directories selected from the list will not be ignored anymore.' );
8806
+ }
8807
+ }
8808
+
8809
  // Update the settings for the heartbeat API.
8810
  if( $heartbeat_status = SucuriScanRequest::post(':heartbeat_status') ){
8811
  $statuses_allowed = SucuriScanHeartbeat::statuses_allowed();
8858
  */
8859
  function sucuriscan_settings_general(){
8860
 
8861
+ global $sucuriscan_emails_per_hour,
 
 
8862
  $sucuriscan_maximum_failed_logins,
8863
  $sucuriscan_verify_ssl_cert;
8864
 
8889
 
8890
  // Get initial variables to decide some things bellow.
8891
  $api_key = SucuriScanAPI::get_plugin_key();
 
 
 
 
 
 
8892
  $emails_per_hour = SucuriScanOption::get_option(':emails_per_hour');
8893
  $maximum_failed_logins = SucuriScanOption::get_option(':maximum_failed_logins');
8894
  $verify_ssl_cert = SucuriScanOption::get_option(':verify_ssl_cert');
 
8895
 
8896
  // Check whether the domain name is valid or not.
8897
  if( !$api_key ){
8901
  }
8902
 
8903
  // Generate the HTML code for the option list in the form select fields.
 
 
8904
  $emails_per_hour_options = SucuriScanTemplate::get_select_options( $sucuriscan_emails_per_hour, $emails_per_hour );
8905
  $maximum_failed_logins_options = SucuriScanTemplate::get_select_options( $sucuriscan_maximum_failed_logins, $maximum_failed_logins );
8906
  $verify_ssl_cert_options = SucuriScanTemplate::get_select_options( $sucuriscan_verify_ssl_cert, $verify_ssl_cert );
8907
 
8908
  $template_variables = array(
8909
+ 'APIKey' => ( !$api_key ? '<em>(not set)</em>' : $api_key ),
8910
  'APIKey.RecoverVisibility' => SucuriScanTemplate::visibility( !$api_key && !$display_manual_key_form ),
8911
  'APIKey.ManualKeyFormVisibility' => SucuriScanTemplate::visibility($display_manual_key_form),
8912
  'APIKey.RemoveVisibility' => SucuriScanTemplate::visibility($api_key),
8913
  'InvalidDomainVisibility' => SucuriScanTemplate::visibility($invalid_domain),
8914
+ 'NotifyTo' => SucuriScanOption::get_option(':notify_to'),
8915
+ 'EmailsPerHour' => 'Undefined',
8916
+ 'EmailsPerHourOptions' => $emails_per_hour_options,
8917
+ 'MaximumFailedLogins' => 'Undefined',
8918
+ 'MaximumFailedLoginsOptions' => $maximum_failed_logins_options,
8919
+ 'VerifySSLCert' => 'Undefined',
8920
+ 'VerifySSLCertOptions' => $verify_ssl_cert_options,
8921
+ 'RequestTimeout' => SucuriScanOption::get_option(':request_timeout') . ' seconds',
8922
+ 'ModalWhenAPIRegistered' => $api_registered_modal,
8923
+ );
8924
+
8925
+ if( array_key_exists($emails_per_hour, $sucuriscan_emails_per_hour) ){
8926
+ $template_variables['EmailsPerHour'] = $sucuriscan_emails_per_hour[$emails_per_hour];
8927
+ }
8928
+
8929
+ if( array_key_exists($maximum_failed_logins, $sucuriscan_maximum_failed_logins) ){
8930
+ $template_variables['MaximumFailedLogins'] = $sucuriscan_maximum_failed_logins[$maximum_failed_logins];
8931
+ }
8932
+
8933
+ if( array_key_exists($verify_ssl_cert, $sucuriscan_verify_ssl_cert) ){
8934
+ $template_variables['VerifySSLCert'] = $sucuriscan_verify_ssl_cert[$verify_ssl_cert];
8935
+ }
8936
+
8937
+ return SucuriScanTemplate::get_section('settings-general', $template_variables);
8938
+ }
8939
+
8940
+ /**
8941
+ * Read and parse the content of the scanner settings template.
8942
+ *
8943
+ * @return string Parsed HTML code for the scanner settings panel.
8944
+ */
8945
+ function sucuriscan_settings_scanner(){
8946
+
8947
+ global $sucuriscan_schedule_allowed,
8948
+ $sucuriscan_interface_allowed;
8949
+
8950
+ // Get initial variables to decide some things bellow.
8951
+ $fs_scanner = SucuriScanOption::get_option(':fs_scanner');
8952
+ $scan_freq = SucuriScanOption::get_option(':scan_frequency');
8953
+ $scan_interface = SucuriScanOption::get_option(':scan_interface');
8954
+ $scan_modfiles = SucuriScanOption::get_option(':scan_modfiles');
8955
+ $scan_checksums = SucuriScanOption::get_option(':scan_checksums');
8956
+ $scan_errorlogs = SucuriScanOption::get_option(':scan_errorlogs');
8957
+ $runtime_scan_human = SucuriScanFSScanner::get_filesystem_runtime(TRUE);
8958
+
8959
+ // Generate the HTML code for the option list in the form select fields.
8960
+ $scan_freq_options = SucuriScanTemplate::get_select_options( $sucuriscan_schedule_allowed, $scan_freq );
8961
+ $scan_interface_options = SucuriScanTemplate::get_select_options( $sucuriscan_interface_allowed, $scan_interface );
8962
+
8963
+ $template_variables = array(
8964
  /* Filesystem scanner */
8965
  'FsScannerStatus' => 'Enabled',
8966
  'FsScannerSwitchText' => 'Disable',
8986
  'ScanningFrequencyOptions' => $scan_freq_options,
8987
  'ScanningInterface' => ( $scan_interface ? $sucuriscan_interface_allowed[$scan_interface] : 'Undefined' ),
8988
  'ScanningInterfaceOptions' => $scan_interface_options,
 
8989
  /* Filesystem scanning runtime. */
8990
  'ScanningRuntimeHuman' => $runtime_scan_human,
 
 
 
 
 
 
 
 
 
 
8991
  );
8992
 
8993
  if( $fs_scanner == 'disabled' ){
9022
  $template_variables['ScanningFrequency'] = $sucuriscan_schedule_allowed[$scan_freq];
9023
  }
9024
 
9025
+ return SucuriScanTemplate::get_section('settings-scanner', $template_variables);
 
 
 
 
 
 
 
 
 
 
 
 
9026
  }
9027
 
9028
  /**
9118
  return SucuriScanTemplate::get_section('settings-ignorerules', $template_variables);
9119
  }
9120
 
9121
+ /**
9122
+ * Read and parse the content of the ignore-scanning settings template.
9123
+ *
9124
+ * @return string Parsed HTML code for the ignore-scanning settings panel.
9125
+ */
9126
+ function sucuriscan_settings_ignorescanning(){
9127
+ $template_variables = array(
9128
+ 'IgnoreScanning.ResourceList' => '',
9129
+ );
9130
+
9131
+ $dir_list_list = SucuriScanFSScanner::get_ignored_directories_live();
9132
+ $counter = 0;
9133
+
9134
+ foreach( $dir_list_list as $group => $dir_list ){
9135
+ foreach( $dir_list as $dir_data ){
9136
+ $valid_entry = FALSE;
9137
+ $snippet_data = array(
9138
+ 'IgnoreScanning.CssClass' => '',
9139
+ 'IgnoreScanning.Directory' => '',
9140
+ 'IgnoreScanning.DirectoryPath' => '',
9141
+ 'IgnoreScanning.IgnoredAt' => '',
9142
+ 'IgnoreScanning.IgnoredAtText' => 'ok',
9143
+ 'IgnoreScanning.IgnoredCssClass' => 'success',
9144
+ );
9145
+
9146
+ if( $group == 'is_ignored' ){
9147
+ $valid_entry = TRUE;
9148
+ $snippet_data['IgnoreScanning.Directory'] = urlencode($dir_data['directory_path']);
9149
+ $snippet_data['IgnoreScanning.DirectoryPath'] = SucuriScan::escape($dir_data['directory_path']);
9150
+ $snippet_data['IgnoreScanning.IgnoredAt'] = SucuriScan::datetime($dir_data['ignored_at']);
9151
+ $snippet_data['IgnoreScanning.IgnoredAtText'] = 'ignored';
9152
+ $snippet_data['IgnoreScanning.IgnoredCssClass'] = 'warning';
9153
+ }
9154
+
9155
+ elseif( $group == 'is_not_ignored' ){
9156
+ $valid_entry = TRUE;
9157
+ $snippet_data['IgnoreScanning.Directory'] = urlencode($dir_data);
9158
+ $snippet_data['IgnoreScanning.DirectoryPath'] = SucuriScan::escape($dir_data);
9159
+ }
9160
+
9161
+ if( $valid_entry ){
9162
+ $css_class = ( $counter %2 == 0 ) ? '' : 'alternate';
9163
+ $snippet_data['IgnoreScanning.CssClass'] = $css_class;
9164
+ $template_variables['IgnoreScanning.ResourceList'] .= SucuriScanTemplate::get_snippet('settings-ignorescanning', $snippet_data);
9165
+ $counter += 1;
9166
+ }
9167
+ }
9168
+ }
9169
+
9170
+ return SucuriScanTemplate::get_section('settings-ignorescanning', $template_variables);
9171
+ }
9172
+
9173
  /**
9174
  * Read and parse the content of the heartbeat settings template.
9175
  *
9176
+ * @return string Parsed HTML code for the heartbeat settings panel.
9177
  */
9178
  function sucuriscan_settings_heartbeat(){
9179
  // Current values set in the options table.
9556
  $info_vars = array(
9557
  'Plugin_version' => SUCURISCAN_VERSION,
9558
  'Plugin_checksum' => SUCURISCAN_PLUGIN_CHECKSUM,
9559
+ 'Last_filesystem_scan' => SucuriScanFSScanner::get_filesystem_runtime(TRUE),
9560
  'Using_CloudProxy' => 'Unknown',
9561
  'HTTP_Host' => 'Unknown',
9562
  'Host_Name' => 'Unknown',