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

Version Description

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

=

Download this release

Release Info

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

Code changes from version 1.8.11 to 1.8.12

inc/css/styles.css CHANGED
@@ -46,7 +46,7 @@ body.sucuri-security_page_sucuriscan_hardening {
46
  height: 30px;
47
  line-height: normal;
48
  }
49
- .sucuriscan-container input[type=text] {
50
  margin: 0;
51
  padding: 0 7px;
52
  line-height: 28px;
@@ -62,6 +62,7 @@ body.sucuri-security_page_sucuriscan_hardening {
62
  text-transform: uppercase;
63
  line-height: 30px;
64
  font-weight: 700;
 
65
  }
66
  .sucuriscan-container fieldset span {
67
  line-height: 30px;
@@ -71,8 +72,8 @@ body.sucuri-security_page_sucuriscan_hardening {
71
  .sucuriscan-container fieldset label,
72
  .sucuriscan-container fieldset select,
73
  .sucuriscan-container fieldset button,
74
- .sucuriscan-container fieldset input[type=text],
75
- .sucuriscan-container fieldset input[type=checkbox],
76
  .wp-core-ui .sucuriscan-container fieldset .button,
77
  .wp-core-ui .sucuriscan-container fieldset .button-primary,
78
  .wp-core-ui .sucuriscan-container fieldset .button-secondary {
@@ -83,13 +84,13 @@ body.sucuri-security_page_sucuriscan_hardening {
83
  .sucuriscan-container fieldset label {
84
  margin-left: 0;
85
  }
86
- .sucuriscan-container fieldset input[type=checkbox] {
87
  margin-top: 7px;
88
  margin-bottom: 7px;
89
  }
90
  .sucuriscan-container .sucuriscan-full-textarea {
91
  width: 100%;
92
- min-height: 300px;
93
  background: #efefef;
94
  word-break: break-all;
95
  padding: 20px;
@@ -898,8 +899,8 @@ body.sucuri-security_page_sucuriscan_hardening {
898
  .rtl .sucuriscan-container fieldset label,
899
  .rtl .sucuriscan-container fieldset select,
900
  .rtl .sucuriscan-container fieldset button,
901
- .rtl .sucuriscan-container fieldset input[type=text],
902
- .rtl .sucuriscan-container fieldset input[type=checkbox],
903
  .rtl .wp-core-ui .sucuriscan-container fieldset .button,
904
  .rtl .wp-core-ui .sucuriscan-container fieldset .button-primary,
905
  .rtl .wp-core-ui .sucuriscan-container fieldset .button-secondary {
46
  height: 30px;
47
  line-height: normal;
48
  }
49
+ .sucuriscan-container input[type='text'] {
50
  margin: 0;
51
  padding: 0 7px;
52
  line-height: 28px;
62
  text-transform: uppercase;
63
  line-height: 30px;
64
  font-weight: 700;
65
+ cursor: initial;
66
  }
67
  .sucuriscan-container fieldset span {
68
  line-height: 30px;
72
  .sucuriscan-container fieldset label,
73
  .sucuriscan-container fieldset select,
74
  .sucuriscan-container fieldset button,
75
+ .sucuriscan-container fieldset input[type='text'],
76
+ .sucuriscan-container fieldset input[type='checkbox'],
77
  .wp-core-ui .sucuriscan-container fieldset .button,
78
  .wp-core-ui .sucuriscan-container fieldset .button-primary,
79
  .wp-core-ui .sucuriscan-container fieldset .button-secondary {
84
  .sucuriscan-container fieldset label {
85
  margin-left: 0;
86
  }
87
+ .sucuriscan-container fieldset input[type='checkbox'] {
88
  margin-top: 7px;
89
  margin-bottom: 7px;
90
  }
91
  .sucuriscan-container .sucuriscan-full-textarea {
92
  width: 100%;
93
+ min-height: 400px;
94
  background: #efefef;
95
  word-break: break-all;
96
  padding: 20px;
899
  .rtl .sucuriscan-container fieldset label,
900
  .rtl .sucuriscan-container fieldset select,
901
  .rtl .sucuriscan-container fieldset button,
902
+ .rtl .sucuriscan-container fieldset input[type='text'],
903
+ .rtl .sucuriscan-container fieldset input[type='checkbox'],
904
  .rtl .wp-core-ui .sucuriscan-container fieldset .button,
905
  .rtl .wp-core-ui .sucuriscan-container fieldset .button-primary,
906
  .rtl .wp-core-ui .sucuriscan-container fieldset .button-secondary {
inc/js/scripts.js CHANGED
@@ -1,29 +1,35 @@
1
  /* global jQuery */
2
- /* jshint unused:false */
3
 
4
- function sucuriscanAlertClose (id) {
 
5
  var element = document.getElementById('sucuriscan-alert-' + id);
6
  element.parentNode.removeChild(element);
7
  }
 
8
 
9
- jQuery(document).ready(function ($) {
10
- $('.sucuriscan-container').on('click', '.sucuriscan-modal-button', function (event) {
11
  event.preventDefault();
 
12
  var modalid = $(this).data('modalid');
 
13
  $('div.' + modalid + '-modal').removeClass('sucuriscan-hidden');
14
  });
15
 
16
- $('.sucuriscan-container').on('click', '.sucuriscan-overlay, .sucuriscan-modal-close', function (event) {
17
  event.preventDefault();
 
18
  $('.sucuriscan-overlay').addClass('sucuriscan-hidden');
19
  $('.sucuriscan-modal').addClass('sucuriscan-hidden');
20
  });
21
 
22
- $('.sucuriscan-container').on('click', '.sucuriscan-show-more', function (event) {
23
  event.preventDefault();
 
24
  var button = $(this);
25
  var target = button.attr('data-target');
26
  var status = button.attr('data-status');
 
27
  if (status === 'more') {
28
  button.attr('data-status', 'less');
29
  $(target).removeClass('sucuriscan-hidden');
@@ -41,36 +47,44 @@ jQuery(document).ready(function ($) {
41
  var activeState = 'sucuriscan-tab-active';
42
  var locationHash = location.href.split('#')[1];
43
 
44
- $('.sucuriscan-container').on('click', '.sucuriscan-tabs-buttons a', function (event) {
45
  event.preventDefault();
46
 
47
  var button = $(this);
48
  var uniqueid = button.attr('href').split('#')[1];
49
 
50
- if (uniqueid !== undefined) {
51
- var container = $('.sucuriscan-tabs-containers > #sucuriscan-tabs-' + uniqueid);
 
52
 
53
- if (container.length) {
54
- var rawurl = location.href.replace(location.hash, '');
55
- var newurl = rawurl + '#' + uniqueid;
56
 
57
- window.history.pushState({}, document.title, newurl);
 
 
58
 
59
- $('.sucuriscan-tabs-buttons a').removeClass(activeState);
60
- $('.sucuriscan-tabs-containers > div').addClass(hiddenState);
61
 
62
- button.addClass(activeState);
63
- container.addClass(visibleState);
64
- container.removeClass(hiddenState);
65
- }
66
- }
 
 
 
67
  });
68
 
69
  $('.sucuriscan-tabs-containers > div').addClass(hiddenState);
70
 
71
  if (locationHash !== undefined) {
72
- $('.sucuriscan-tabs-buttons a').each(function (e, button) {
73
- if ($(button).attr('href').split('#')[1] === locationHash) {
 
 
 
 
74
  $(button).trigger('click');
75
  }
76
  });
@@ -79,7 +93,7 @@ jQuery(document).ready(function ($) {
79
  }
80
  }
81
 
82
- $('.sucuriscan-container').on('mouseover', '.sucuriscan-tooltip', function (event) {
83
  var element = $(this);
84
  var content = element.attr('content');
85
 
@@ -88,7 +102,7 @@ jQuery(document).ready(function ($) {
88
  }
89
 
90
  /* create instance of tooltip container */
91
- var tooltip = $('<div>', { 'class': 'sucuriscan-tooltip-object' });
92
 
93
  if (element.attr('tooltip-width')) {
94
  var customWidth = element.attr('tooltip-width');
@@ -107,7 +121,6 @@ jQuery(document).ready(function ($) {
107
  var tooltipHeight = tooltip.outerHeight();
108
  tooltip.css('top', (tooltipHeight + arrowHeight) * -1);
109
 
110
- var positionLeft = 0;
111
  var elementWidth = element.outerWidth();
112
  var tooltipWidth = tooltip.outerWidth();
113
 
@@ -116,11 +129,31 @@ jQuery(document).ready(function ($) {
116
  } else if (elementWidth > tooltipWidth) {
117
  tooltip.css('left', (elementWidth - tooltipWidth) / 2);
118
  } else if (elementWidth < tooltipWidth) {
119
- tooltip.css('left', ((tooltipWidth - elementWidth) / 2) * -1);
120
  }
121
  });
122
 
123
- $('.sucuriscan-container').on('mouseout', '.sucuriscan-tooltip', function (event) {
124
- $(this).find('.sucuriscan-tooltip-object').remove();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  });
126
  });
1
  /* global jQuery */
 
2
 
3
+ /* jshint ignore:start */
4
+ function sucuriscanAlertClose(id) {
5
  var element = document.getElementById('sucuriscan-alert-' + id);
6
  element.parentNode.removeChild(element);
7
  }
8
+ /* jshint ignore:end */
9
 
10
+ jQuery(document).ready(function($) {
11
+ $('.sucuriscan-container').on('click', '.sucuriscan-modal-button', function(event) {
12
  event.preventDefault();
13
+
14
  var modalid = $(this).data('modalid');
15
+
16
  $('div.' + modalid + '-modal').removeClass('sucuriscan-hidden');
17
  });
18
 
19
+ $('.sucuriscan-container').on('click', '.sucuriscan-overlay, .sucuriscan-modal-close', function(event) {
20
  event.preventDefault();
21
+
22
  $('.sucuriscan-overlay').addClass('sucuriscan-hidden');
23
  $('.sucuriscan-modal').addClass('sucuriscan-hidden');
24
  });
25
 
26
+ $('.sucuriscan-container').on('click', '.sucuriscan-show-more', function(event) {
27
  event.preventDefault();
28
+
29
  var button = $(this);
30
  var target = button.attr('data-target');
31
  var status = button.attr('data-status');
32
+
33
  if (status === 'more') {
34
  button.attr('data-status', 'less');
35
  $(target).removeClass('sucuriscan-hidden');
47
  var activeState = 'sucuriscan-tab-active';
48
  var locationHash = location.href.split('#')[1];
49
 
50
+ $('.sucuriscan-container').on('click', '.sucuriscan-tabs-buttons a', function(event) {
51
  event.preventDefault();
52
 
53
  var button = $(this);
54
  var uniqueid = button.attr('href').split('#')[1];
55
 
56
+ if (!uniqueid) {
57
+ return;
58
+ }
59
 
60
+ var container = $('.sucuriscan-tabs-containers > #sucuriscan-tabs-' + uniqueid);
 
 
61
 
62
+ if (!container.length) {
63
+ return;
64
+ }
65
 
66
+ var rawurl = location.href.replace(location.hash, '');
67
+ var newurl = rawurl + '#' + uniqueid;
68
 
69
+ window.history.pushState({}, document.title, newurl);
70
+
71
+ $('.sucuriscan-tabs-buttons a').removeClass(activeState);
72
+ $('.sucuriscan-tabs-containers > div').addClass(hiddenState);
73
+
74
+ button.addClass(activeState);
75
+ container.addClass(visibleState);
76
+ container.removeClass(hiddenState);
77
  });
78
 
79
  $('.sucuriscan-tabs-containers > div').addClass(hiddenState);
80
 
81
  if (locationHash !== undefined) {
82
+ $('.sucuriscan-tabs-buttons a').each(function(e, button) {
83
+ var buttonHash = $(button)
84
+ .attr('href')
85
+ .split('#')[1];
86
+
87
+ if (buttonHash === locationHash) {
88
  $(button).trigger('click');
89
  }
90
  });
93
  }
94
  }
95
 
96
+ $('.sucuriscan-container').on('mouseover', '.sucuriscan-tooltip', function() {
97
  var element = $(this);
98
  var content = element.attr('content');
99
 
102
  }
103
 
104
  /* create instance of tooltip container */
105
+ var tooltip = $('<div>', { class: 'sucuriscan-tooltip-object' });
106
 
107
  if (element.attr('tooltip-width')) {
108
  var customWidth = element.attr('tooltip-width');
121
  var tooltipHeight = tooltip.outerHeight();
122
  tooltip.css('top', (tooltipHeight + arrowHeight) * -1);
123
 
 
124
  var elementWidth = element.outerWidth();
125
  var tooltipWidth = tooltip.outerWidth();
126
 
129
  } else if (elementWidth > tooltipWidth) {
130
  tooltip.css('left', (elementWidth - tooltipWidth) / 2);
131
  } else if (elementWidth < tooltipWidth) {
132
+ tooltip.css('left', (tooltipWidth - elementWidth) / 2 * -1);
133
  }
134
  });
135
 
136
+ $('.sucuriscan-container').on('mouseout', '.sucuriscan-tooltip', function() {
137
+ $(this)
138
+ .find('.sucuriscan-tooltip-object')
139
+ .remove();
140
+ });
141
+
142
+ $('.sucuriscan-container').on('click', 'button.sucuriscan-show-section', function(event) {
143
+ event.preventDefault();
144
+
145
+ var button = $(this);
146
+ var current = button.text();
147
+ var onText = button.attr('on');
148
+ var offText = button.attr('off');
149
+ var section = button.attr('section');
150
+
151
+ if (current === onText) {
152
+ $('#' + section).removeClass('sucuriscan-hidden');
153
+ button.html(offText);
154
+ } else {
155
+ $('#' + section).addClass('sucuriscan-hidden');
156
+ button.html(onText);
157
+ }
158
  });
159
  });
inc/tpl/lastlogins-failedlogins.html.tpl CHANGED
@@ -16,7 +16,6 @@
16
  <input id="cb-select-all-1" type="checkbox">
17
  </td>
18
  <th class="manage-column">Username</th>
19
- <th class="manage-column">Password</th>
20
  <th class="manage-column">IP Address</th>
21
  <th class="manage-column">Date/Time</th>
22
  <th class="manage-column" width="300">Web Browser</th>
@@ -27,13 +26,13 @@
27
  %%%SUCURI.FailedLogins.List%%%
28
 
29
  <tr class="sucuriscan-%%SUCURI.FailedLogins.NoItemsVisibility%%">
30
- <td colspan="6">
31
  <em>no data available</em>
32
  </td>
33
  </tr>
34
 
35
  <tr class="sucuriscan-%%SUCURI.FailedLogins.PaginationVisibility%%">
36
- <td colspan="6">
37
  <ul class="sucuriscan-pagination">
38
  %%%SUCURI.FailedLogins.PaginationLinks%%%
39
  </ul>
16
  <input id="cb-select-all-1" type="checkbox">
17
  </td>
18
  <th class="manage-column">Username</th>
 
19
  <th class="manage-column">IP Address</th>
20
  <th class="manage-column">Date/Time</th>
21
  <th class="manage-column" width="300">Web Browser</th>
26
  %%%SUCURI.FailedLogins.List%%%
27
 
28
  <tr class="sucuriscan-%%SUCURI.FailedLogins.NoItemsVisibility%%">
29
+ <td colspan="5">
30
  <em>no data available</em>
31
  </td>
32
  </tr>
33
 
34
  <tr class="sucuriscan-%%SUCURI.FailedLogins.PaginationVisibility%%">
35
+ <td colspan="5">
36
  <ul class="sucuriscan-pagination">
37
  %%%SUCURI.FailedLogins.PaginationLinks%%%
38
  </ul>
inc/tpl/lastlogins-failedlogins.snippet.tpl CHANGED
@@ -6,8 +6,6 @@
6
 
7
  <td><span class="sucuriscan-monospace">%%SUCURI.FailedLogins.Username%%</span></td>
8
 
9
- <td><span class="sucuriscan-monospace">%%SUCURI.FailedLogins.Password%%</span></td>
10
-
11
  <td><span class="sucuriscan-monospace">%%SUCURI.FailedLogins.RemoteAddr%%</span></td>
12
 
13
  <td><em>%%SUCURI.FailedLogins.Datetime%%</em></td>
6
 
7
  <td><span class="sucuriscan-monospace">%%SUCURI.FailedLogins.Username%%</span></td>
8
 
 
 
9
  <td><span class="sucuriscan-monospace">%%SUCURI.FailedLogins.RemoteAddr%%</span></td>
10
 
11
  <td><em>%%SUCURI.FailedLogins.Datetime%%</em></td>
inc/tpl/notification-pretty.html.tpl CHANGED
@@ -18,6 +18,7 @@
18
  <p style="margin:0 0 10px 0">
19
  Website: <a href="http://%%SUCURI.Website%%">%%SUCURI.Website%%</a><br>
20
  IP Address: %%SUCURI.RemoteAddress%%<br>
 
21
  Date/Time: %%SUCURI.Time%%<br>
22
  %%SUCURI.User%%
23
  </p>
18
  <p style="margin:0 0 10px 0">
19
  Website: <a href="http://%%SUCURI.Website%%">%%SUCURI.Website%%</a><br>
20
  IP Address: %%SUCURI.RemoteAddress%%<br>
21
+ Reverse IP: %%SUCURI.ReverseAddress%%<br>
22
  Date/Time: %%SUCURI.Time%%<br>
23
  %%SUCURI.User%%
24
  </p>
inc/tpl/notification-simple.html.tpl CHANGED
@@ -2,6 +2,7 @@
2
  Event: %%SUCURI.Subject%%
3
  Website: http://%%SUCURI.Website%%
4
  IP Address: %%SUCURI.RemoteAddress%%
 
5
  Date/Time: %%SUCURI.Time%%
6
  %%SUCURI.User%%
7
 
2
  Event: %%SUCURI.Subject%%
3
  Website: http://%%SUCURI.Website%%
4
  IP Address: %%SUCURI.RemoteAddress%%
5
+ Reverse IP: %%SUCURI.ReverseAddress%%
6
  Date/Time: %%SUCURI.Time%%
7
  %%SUCURI.User%%
8
 
inc/tpl/settings-alerts-ignore-posts.html.tpl CHANGED
@@ -22,29 +22,35 @@
22
 
23
  <hr>
24
 
25
- <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
26
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
27
- <input type="hidden" name="sucuriscan_ignorerule_action" value="batch" />
28
-
29
- <table class="wp-list-table widefat sucuriscan-table sucuriscan-settings-ignorerules">
30
- <thead>
31
- <tr>
32
- <td id="cb" class="manage-column column-cb check-column">
33
- <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
34
- <input id="cb-select-all-1" type="checkbox">
35
- </td>
36
- <th class="manage-column">Post Type</th>
37
- <th class="manage-column">Post Type ID</th>
38
- <th class="manage-column">Ignored At (optional)</th>
39
- </tr>
40
- </thead>
41
-
42
- <tbody>
43
- %%%SUCURI.PostTypes.List%%%
44
- </tbody>
45
- </table>
46
-
47
- <button type="submit" class="button button-primary">Submit</button>
48
- </form>
 
 
 
 
 
 
49
  </div>
50
  </div>
22
 
23
  <hr>
24
 
25
+ <button class="button button-primary sucuriscan-show-section" section="sucuriscan-ignorerules" on="Show Post-Types Table" off="Hide Post-Types Table">Show Post-Types Table</button>
26
+
27
+ <div class="sucuriscan-hidden" id="sucuriscan-ignorerules">
28
+ <hr>
29
+
30
+ <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
31
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
32
+ <input type="hidden" name="sucuriscan_ignorerule_action" value="batch" />
33
+
34
+ <table class="wp-list-table widefat sucuriscan-table sucuriscan-settings-ignorerules">
35
+ <thead>
36
+ <tr>
37
+ <td id="cb" class="manage-column column-cb check-column">
38
+ <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
39
+ <input id="cb-select-all-1" type="checkbox">
40
+ </td>
41
+ <th class="manage-column">Post Type</th>
42
+ <th class="manage-column">Post Type ID</th>
43
+ <th class="manage-column">Ignored At (optional)</th>
44
+ </tr>
45
+ </thead>
46
+
47
+ <tbody>
48
+ %%%SUCURI.PostTypes.List%%%
49
+ </tbody>
50
+ </table>
51
+
52
+ <button type="submit" class="button button-primary">Submit</button>
53
+ </form>
54
+ </div>
55
  </div>
56
  </div>
inc/tpl/settings-apiservice-status.html.tpl CHANGED
@@ -18,5 +18,13 @@
18
  <button type="submit" class="button button-primary">%%SUCURI.ApiStatus.SwitchText%%</button>
19
  </form>
20
  </div>
 
 
 
 
 
 
 
 
21
  </div>
22
  </div>
18
  <button type="submit" class="button button-primary">%%SUCURI.ApiStatus.SwitchText%%</button>
19
  </form>
20
  </div>
21
+
22
+ <p>
23
+ <strong>Are you a developer?</strong> You may be interested in our API. Feel free to use the URL shown below to access the latest 50 entries in your security log, change the value for the parameter <code>l=N</code> if you need more. Be aware that the API doesn't provides an offset parameter, so if you have the intension to query specific sections of the log you will need to wrap the HTTP request around your own cache mechanism. We <strong>DO NOT</strong> take feature requests for the API, this is a semi-private service tailored for the specific needs of the plugin and not intended to be used by 3rd-party apps, we may change the behavior of each API endpoint without previous notice, use it at your own risk.
24
+ </p>
25
+
26
+ <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-monospace">
27
+ <span>curl -s "https://wordpress.sucuri.net/api/?k=%%SUCURI.ApiStatus.ApiKey%%&a=get_logs&l=50" | python -m json.tool</span>
28
+ </div>
29
  </div>
30
  </div>
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: dd@sucuri.net
3
  Donate Link: https://sucuri.net/
4
  Tags: malware, security, firewall, scan, spam, virus, sucuri, protection, WordPress Security, Login Security, Security Auditing, File Integrity, htaccess, phishing, backdoors, SQL Injection, RFI, LFI, XSS, CSRF, website firewall, Website Security, Performance Optimization, Zero Day, Software Vulnerability, Exploits, Hacks, Attackers, Bad Actors, Reverse Proxy, Two Factor Security, Two Factor Authentication, Security Logs, HeatBleed Vulnerability, Website Protection, Bash Vulnerability, RevSlider Vulnerability, MailPoet Vulnerability, Malware Prevention, Website Security, Website Firewall, Website AntiVirus, Security Response, Security Detection, Security Prevention
5
  Requires at least: 3.6
6
- Tested up to: 4.8.0
7
- Stable tag: 1.8.11
8
 
9
  The Sucuri WordPress Security plugin is a security toolset for security integrity monitoring, malware detection and security hardening.
10
 
@@ -181,11 +181,31 @@ No, it is not required. The Website Firewall runs in the cloud without the need
181
 
182
  == Upgrade Notice ==
183
 
184
- = 1.8.11 =
185
  This version adds support for the latest version of WordPress. Introduces new features and fixes some bugs reported by the WordPress community as well as bugs found by our automated testing system.
186
 
187
  == Changelog ==
188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  = 1.8.11 =
190
  * Modify Sucuri firewall detection with regular expressions
191
  * Modify option to force scanner to ignore directories
3
  Donate Link: https://sucuri.net/
4
  Tags: malware, security, firewall, scan, spam, virus, sucuri, protection, WordPress Security, Login Security, Security Auditing, File Integrity, htaccess, phishing, backdoors, SQL Injection, RFI, LFI, XSS, CSRF, website firewall, Website Security, Performance Optimization, Zero Day, Software Vulnerability, Exploits, Hacks, Attackers, Bad Actors, Reverse Proxy, Two Factor Security, Two Factor Authentication, Security Logs, HeatBleed Vulnerability, Website Protection, Bash Vulnerability, RevSlider Vulnerability, MailPoet Vulnerability, Malware Prevention, Website Security, Website Firewall, Website AntiVirus, Security Response, Security Detection, Security Prevention
5
  Requires at least: 3.6
6
+ Tested up to: 4.9.4
7
+ Stable tag: 1.8.12
8
 
9
  The Sucuri WordPress Security plugin is a security toolset for security integrity monitoring, malware detection and security hardening.
10
 
181
 
182
  == Upgrade Notice ==
183
 
184
+ = 1.8.12 =
185
  This version adds support for the latest version of WordPress. Introduces new features and fixes some bugs reported by the WordPress community as well as bugs found by our automated testing system.
186
 
187
  == Changelog ==
188
 
189
+ = 1.8.12 =
190
+ * Fix invalid array when deselecting all security alerts
191
+ * Add language files to the list of ignored changes
192
+ * Modify internal response to the log file not found error
193
+ * Add option to force the firewall cache flush
194
+ * Fix unexpected exception when open_basedir is in place
195
+ * Add support to export and import trusted IP addresses
196
+ * Add link to the audit logs API endpoint for developers
197
+ * Add reverse ip address in all email alerts from visitor
198
+ * Remove API key from the settings that can be exported
199
+ * Modify code to make default plugin options filterable
200
+ * Add ability to store the settings in the object cache
201
+ * Add support for wp-cli and command to generate an API key
202
+ * Fix missing documentation tags in the command line library
203
+ * Fix format and coding standard in CSS and JavaScript files
204
+ * Add button to toggle the visibility of the post-types table
205
+ * Modify order of the added, modified, removed core files
206
+ * Fix relative file path when ABSPATH is point to root
207
+ * Add additional notifications for changes on users
208
+
209
  = 1.8.11 =
210
  * Modify Sucuri firewall detection with regular expressions
211
  * Modify option to force scanner to ignore directories
src/api.lib.php CHANGED
@@ -284,12 +284,9 @@ class SucuriScanAPI extends SucuriScanOption
284
  if (stripos($raw, 'log file not found') !== false) {
285
  $key = SucuriScanOption::getOption(':api_key');
286
  $msg .= '; this generally happens when you use an invalid API key,'
287
- . ' the key will be deleted to hide these warnings, if you want to'
288
- . ' recover it go to the settings page and follow the instructions'
289
- . ' in the "API Key" section: <code>' . SucuriScan::escape($key)
290
- . '</code>';
291
 
292
- SucuriScanOption::deleteOption(':api_key');
293
  }
294
 
295
  // Special response for invalid firewall API keys.
284
  if (stripos($raw, 'log file not found') !== false) {
285
  $key = SucuriScanOption::getOption(':api_key');
286
  $msg .= '; this generally happens when you use an invalid API key,'
287
+ . ' or when the connection with the API service suddently closes.';
 
 
 
288
 
289
+ SucuriScanEvent::reportCriticalEvent($msg);
290
  }
291
 
292
  // Special response for invalid firewall API keys.
src/auditlogs.lib.php CHANGED
@@ -170,7 +170,6 @@ class SucuriScanAuditLogs
170
  $outdata = (array) $auditlogs['output_data'];
171
  $todaysDate = SucuriScan::datetime(null, 'M d, Y');
172
  $iterator_start = ($pageNumber - 1) * $maxPerPage;
173
- $show_password = SucuriScanOption::isEnabled(':notify_failed_password');
174
  $total_items = count($outdata);
175
 
176
  usort($outdata, array('SucuriScanAuditLogs', 'sortByDate'));
@@ -186,7 +185,7 @@ class SucuriScanAuditLogs
186
 
187
  $audit_log = (array) $outdata[$i];
188
 
189
- if (!$show_password && strpos($audit_log['message'], ";\x20password:")) {
190
  $idx = strpos($audit_log['message'], ";\x20password:");
191
  $audit_log['message'] = substr($audit_log['message'], 0, $idx);
192
  }
170
  $outdata = (array) $auditlogs['output_data'];
171
  $todaysDate = SucuriScan::datetime(null, 'M d, Y');
172
  $iterator_start = ($pageNumber - 1) * $maxPerPage;
 
173
  $total_items = count($outdata);
174
 
175
  usort($outdata, array('SucuriScanAuditLogs', 'sortByDate'));
185
 
186
  $audit_log = (array) $outdata[$i];
187
 
188
+ if (strpos($audit_log['message'], ";\x20password:")) {
189
  $idx = strpos($audit_log['message'], ";\x20password:");
190
  $audit_log['message'] = substr($audit_log['message'], 0, $idx);
191
  }
src/cli.lib.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Code related to the cli.lib.php interface.
5
+ *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanCLI
11
+ * @author Daniel Cid <dcid@sucuri.net>
12
+ * @copyright 2010-2017 Sucuri Inc.
13
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
14
+ * @link https://wordpress.org/plugins/sucuri-scanner
15
+ */
16
+
17
+ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
18
+ if (!headers_sent()) {
19
+ /* Report invalid access if possible. */
20
+ header('HTTP/1.1 403 Forbidden');
21
+ }
22
+ exit(1);
23
+ }
24
+
25
+ /**
26
+ * Manage Sucuri API registration.
27
+ *
28
+ * @category Library
29
+ * @package Sucuri
30
+ * @subpackage SucuriScanner
31
+ * @author Daniel Cid <dcid@sucuri.net>
32
+ * @copyright 2010-2017 Sucuri Inc.
33
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
34
+ * @link https://wordpress.org/plugins/sucuri-scanner
35
+ */
36
+ class SucuriScanCLI extends WP_CLI_Command
37
+ {
38
+ /**
39
+ * Register and connect to the Sucuri API.
40
+ *
41
+ * ## OPTIONS
42
+ *
43
+ * [<api_key>]
44
+ * : Sucuri API key to register with.
45
+ *
46
+ * ## EXAMPLES
47
+ *
48
+ * # New registration
49
+ * wp sucuri register
50
+ * API key: 99e656abef7a123d1cffe73f91ba63702
51
+ * Success: The API key for your site was successfully generated and saved.
52
+ *
53
+ * # Existing key registration
54
+ * wp sucuri register 99e656abef7a123d1cffe73f91ba63702
55
+ * Success: The API key for your site was successfully saved.
56
+ *
57
+ * # Registration recovery
58
+ * wp sucuri register
59
+ * Warning: We already have an API key created for this site. It has been sent to the email admin@example.com for recovery.
60
+ *
61
+ * @param array $args Arguments from the command line interface.
62
+ * @return void
63
+ */
64
+ public function register($args)
65
+ {
66
+ list($api_key) = $args;
67
+
68
+ ob_start();
69
+ $registered = $api_key ? SucuriScanAPI::setPluginKey($api_key, true) : SucuriScanAPI::registerSite();
70
+ $output = ob_get_clean();
71
+
72
+ preg_match_all('/<p><b>SUCURI:<\/b>(.+)<\/p>/', $output, $matches);
73
+
74
+ $message = isset($matches[1][0]) ? trim(strip_tags($matches[1][0])) : 'An unknown error occurred during registration.';
75
+
76
+ if (! $registered) {
77
+ WP_CLI::error($message);
78
+ }
79
+
80
+ if ($registered && $api_key) {
81
+ WP_CLI::success('The API key for your site was successfully saved.');
82
+ return;
83
+ }
84
+
85
+ $api_key = SucuriScanAPI::getPluginKey();
86
+
87
+ WP_CLI::line("API key: $api_key");
88
+
89
+ WP_CLI::success($message);
90
+ }
91
+ }
92
+
93
+ WP_CLI::add_command('sucuri', 'SucuriScanCLI');
src/fileinfo.lib.php CHANGED
@@ -235,23 +235,27 @@ class SucuriScanFileInfo extends SucuriScan
235
  continue;
236
  }
237
 
238
- /* check only files */
239
- if ($fifo->isFile()
240
- && $filterby === 'file'
241
- && !$this->ignoreFile($filepath)
242
- && !$this->ignoreFolder($filepath)
243
- ) {
244
- $files[] = $filepath;
245
- continue;
246
- }
247
-
248
- /* check only directories */
249
- if ($fifo->isDir()
250
- && $filterby === 'directory'
251
- && !$this->ignoreFolder($filepath)
252
- ) {
253
- $files[] = $filepath;
254
- continue;
 
 
 
 
255
  }
256
  }
257
 
@@ -293,7 +297,13 @@ class SucuriScanFileInfo extends SucuriScan
293
  $filesize = @filesize($filepath);
294
 
295
  if ($as_array) {
296
- $basename = str_replace($abspath . '/', '', $filepath);
 
 
 
 
 
 
297
  $signatures[$basename] = array(
298
  'filepath' => $filepath,
299
  'checksum' => $file_checksum,
235
  continue;
236
  }
237
 
238
+ try {
239
+ /* check only files */
240
+ if ($fifo->isFile()
241
+ && $filterby === 'file'
242
+ && !$this->ignoreFile($filepath)
243
+ && !$this->ignoreFolder($filepath)
244
+ ) {
245
+ $files[] = $filepath;
246
+ continue;
247
+ }
248
+
249
+ /* check only directories */
250
+ if ($fifo->isDir()
251
+ && $filterby === 'directory'
252
+ && !$this->ignoreFolder($filepath)
253
+ ) {
254
+ $files[] = $filepath;
255
+ continue;
256
+ }
257
+ } catch (RuntimeException $e) {
258
+ SucuriScanEvent::reportCriticalEvent($e->getMessage());
259
  }
260
  }
261
 
297
  $filesize = @filesize($filepath);
298
 
299
  if ($as_array) {
300
+ $basename = $filepath;
301
+
302
+ if (strlen($abspath . '/') > 1) {
303
+ /* convert absolute path into relative path */
304
+ $basename = str_replace($abspath . '/', '', $filepath);
305
+ }
306
+
307
  $signatures[$basename] = array(
308
  'filepath' => $filepath,
309
  'checksum' => $file_checksum,
src/firewall.lib.php CHANGED
@@ -691,7 +691,7 @@ class SucuriScanFirewall extends SucuriScanAPI
691
 
692
  $params['FirewallAutoClearCache'] = 'data-status="disabled"';
693
 
694
- if (SucuriScanOption::isEnabled(':auto_clear_cache')) {
695
  $params['FirewallAutoClearCache'] = 'checked="checked"';
696
  }
697
 
@@ -711,7 +711,7 @@ class SucuriScanFirewall extends SucuriScanAPI
711
  */
712
  public static function clearCacheHook()
713
  {
714
- if (SucuriScanOption::isEnabled(':auto_clear_cache')) {
715
  ob_start();
716
  self::clearCache();
717
  $error = ob_get_clean();
@@ -776,4 +776,17 @@ class SucuriScanFirewall extends SucuriScanAPI
776
 
777
  wp_send_json($response, 200);
778
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
779
  }
691
 
692
  $params['FirewallAutoClearCache'] = 'data-status="disabled"';
693
 
694
+ if (self::shouldAutoClearCache()) {
695
  $params['FirewallAutoClearCache'] = 'checked="checked"';
696
  }
697
 
711
  */
712
  public static function clearCacheHook()
713
  {
714
+ if (self::shouldAutoClearCache()) {
715
  ob_start();
716
  self::clearCache();
717
  $error = ob_get_clean();
776
 
777
  wp_send_json($response, 200);
778
  }
779
+
780
+ /**
781
+ * Returns true if the plugin should flush the firewall cache.
782
+ *
783
+ * @return bool True if the plugin should flush the firewall cache.
784
+ */
785
+ private static function shouldAutoClearCache()
786
+ {
787
+ return (bool) (
788
+ defined('SUCURI_CLEAR_CACHE_ON_PUBLISH')
789
+ || SucuriScanOption::isEnabled(':auto_clear_cache')
790
+ );
791
+ }
792
  }
src/hook.lib.php CHANGED
@@ -176,13 +176,7 @@ class SucuriScanHook extends SucuriScanEvent
176
  $title = empty($title) ? 'Unknown' : sanitize_user($title, true);
177
  $message = 'User authentication failed: ' . $title;
178
 
179
- /* send the submitted password along with the alert */
180
- if (SucuriScanOption::isEnabled(':notify_failed_password')) {
181
- $message .= '; password: ' . $password;
182
- sucuriscan_log_failed_login($title, $password);
183
- } else {
184
- sucuriscan_log_failed_login($title, '[hidden]');
185
- }
186
 
187
  self::reportErrorEvent($message);
188
 
176
  $title = empty($title) ? 'Unknown' : sanitize_user($title, true);
177
  $message = 'User authentication failed: ' . $title;
178
 
179
+ sucuriscan_log_failed_login($title);
 
 
 
 
 
 
180
 
181
  self::reportErrorEvent($message);
182
 
src/integrity.lib.php CHANGED
@@ -586,6 +586,10 @@ class SucuriScanIntegrity
586
  }
587
  }
588
 
 
 
 
 
589
  return $output;
590
  }
591
 
@@ -662,6 +666,8 @@ class SucuriScanIntegrity
662
  '^wp-content\/(themes|plugins)\/.+',
663
  '^google[0-9a-z]{16}\.html$',
664
  '^pinterest-[0-9a-z]{5}\.html$',
 
 
665
  '\.ico$',
666
  );
667
 
586
  }
587
  }
588
 
589
+ sort($output['added']);
590
+ sort($output['removed']);
591
+ sort($output['modified']);
592
+
593
  return $output;
594
  }
595
 
666
  '^wp-content\/(themes|plugins)\/.+',
667
  '^google[0-9a-z]{16}\.html$',
668
  '^pinterest-[0-9a-z]{5}\.html$',
669
+ '^wp-content\/languages\/.+\.mo$',
670
+ '^wp-content\/languages\/.+\.po$',
671
  '\.ico$',
672
  );
673
 
src/interface.lib.php CHANGED
@@ -67,7 +67,7 @@ class SucuriScanInterface
67
  'sucuriscan1',
68
  SUCURISCAN_URL . '/inc/css/styles.css',
69
  array(/* empty */),
70
- '3eeb7af'
71
  );
72
  wp_enqueue_style('sucuriscan1');
73
 
@@ -75,7 +75,7 @@ class SucuriScanInterface
75
  'sucuriscan1',
76
  SUCURISCAN_URL . '/inc/js/scripts.js',
77
  array(/* empty */),
78
- '81f6bb4'
79
  );
80
  wp_enqueue_script('sucuriscan1');
81
 
@@ -84,7 +84,7 @@ class SucuriScanInterface
84
  'sucuriscan3',
85
  SUCURISCAN_URL . '/inc/css/flags.min.css',
86
  array(/* empty */),
87
- substr(md5(time()), 0, 7)
88
  );
89
  wp_enqueue_style('sucuriscan3');
90
  }
67
  'sucuriscan1',
68
  SUCURISCAN_URL . '/inc/css/styles.css',
69
  array(/* empty */),
70
+ SucuriScan::fileVersion('inc/css/styles.css')
71
  );
72
  wp_enqueue_style('sucuriscan1');
73
 
75
  'sucuriscan1',
76
  SUCURISCAN_URL . '/inc/js/scripts.js',
77
  array(/* empty */),
78
+ SucuriScan::fileVersion('inc/js/scripts.js')
79
  );
80
  wp_enqueue_script('sucuriscan1');
81
 
84
  'sucuriscan3',
85
  SUCURISCAN_URL . '/inc/css/flags.min.css',
86
  array(/* empty */),
87
+ SucuriScan::fileVersion('inc/css/flags.min.css')
88
  );
89
  wp_enqueue_style('sucuriscan3');
90
  }
src/lastlogins-failed.php CHANGED
@@ -57,7 +57,6 @@ function sucuriscan_failed_logins_panel()
57
  $max_failed_logins = SucuriScanOption::getOption(':maximum_failed_logins');
58
  $notify_bruteforce_attack = SucuriScanOption::getOption(':notify_bruteforce_attack');
59
  $failed_logins = sucuriscan_get_all_failed_logins($page_offset, $max_per_page);
60
- $show_password = SucuriScanOption::isEnabled(':notify_failed_password');
61
 
62
  if ($failed_logins) {
63
  $counter = 0;
@@ -70,21 +69,6 @@ function sucuriscan_failed_logins_panel()
70
  continue;
71
  }
72
 
73
- $wrong_user_password = 'hidden';
74
- $wrong_user_password_color = 'default';
75
-
76
- if (isset($login_data['user_password']) && !empty($login_data['user_password'])) {
77
- $wrong_user_password = $login_data['user_password'];
78
- $wrong_user_password_color = 'danger';
79
- } else {
80
- $wrong_user_password = 'empty';
81
- $wrong_user_password_color = 'info';
82
- }
83
-
84
- if (!$show_password) {
85
- $wrong_user_password = 'hidden';
86
- }
87
-
88
  $template_variables['FailedLogins.List'] .= SucuriScanTemplate::getSnippet(
89
  'lastlogins-failedlogins',
90
  array(
@@ -92,8 +76,6 @@ function sucuriscan_failed_logins_panel()
92
  'FailedLogins.Username' => $login_data['user_login'],
93
  'FailedLogins.RemoteAddr' => $login_data['remote_addr'],
94
  'FailedLogins.UserAgent' => $login_data['user_agent'],
95
- 'FailedLogins.Password' => $wrong_user_password,
96
- 'FailedLogins.PasswordColor' => $wrong_user_password_color,
97
  'FailedLogins.Datetime' => SucuriScan::datetime($login_data['attempt_time']),
98
  )
99
  );
@@ -314,11 +296,10 @@ function sucuriscan_get_failed_logins($get_old_logs = false, $offset = 0, $limit
314
  * this entry will contain the username, timestamp of the login attempt, remote
315
  * address of the computer sending the request, and the user-agent.
316
  *
317
- * @param string $user_login Information from the current failed login event.
318
- * @param string $wrong_password Wrong password used during the supposed attack.
319
- * @return bool Whether the information of the current failed login event was stored or not.
320
  */
321
- function sucuriscan_log_failed_login($user_login = '', $wrong_password = '')
322
  {
323
  $storage = sucuriscan_failed_logins_datastore_path();
324
 
@@ -329,7 +310,6 @@ function sucuriscan_log_failed_login($user_login = '', $wrong_password = '')
329
  $login_data = json_encode(
330
  array(
331
  'user_login' => $user_login,
332
- 'user_password' => $wrong_password,
333
  'attempt_time' => time(),
334
  'remote_addr' => SucuriScan::getRemoteAddr(),
335
  'user_agent' => SucuriScan::getUserAgent(),
57
  $max_failed_logins = SucuriScanOption::getOption(':maximum_failed_logins');
58
  $notify_bruteforce_attack = SucuriScanOption::getOption(':notify_bruteforce_attack');
59
  $failed_logins = sucuriscan_get_all_failed_logins($page_offset, $max_per_page);
 
60
 
61
  if ($failed_logins) {
62
  $counter = 0;
69
  continue;
70
  }
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  $template_variables['FailedLogins.List'] .= SucuriScanTemplate::getSnippet(
73
  'lastlogins-failedlogins',
74
  array(
76
  'FailedLogins.Username' => $login_data['user_login'],
77
  'FailedLogins.RemoteAddr' => $login_data['remote_addr'],
78
  'FailedLogins.UserAgent' => $login_data['user_agent'],
 
 
79
  'FailedLogins.Datetime' => SucuriScan::datetime($login_data['attempt_time']),
80
  )
81
  );
296
  * this entry will contain the username, timestamp of the login attempt, remote
297
  * address of the computer sending the request, and the user-agent.
298
  *
299
+ * @param string $user_login Information from the current failed login event.
300
+ * @return bool True if the information was saved, false otherwise.
 
301
  */
302
+ function sucuriscan_log_failed_login($user_login = '')
303
  {
304
  $storage = sucuriscan_failed_logins_datastore_path();
305
 
310
  $login_data = json_encode(
311
  array(
312
  'user_login' => $user_login,
 
313
  'attempt_time' => time(),
314
  'remote_addr' => SucuriScan::getRemoteAddr(),
315
  'user_agent' => SucuriScan::getUserAgent(),
src/mail.lib.php CHANGED
@@ -204,6 +204,7 @@ class SucuriScanMail extends SucuriScanOption
204
  $params['Subject'] = $subject;
205
  $params['Website'] = $website;
206
  $params['RemoteAddress'] = self::getRemoteAddr();
 
207
  $params['Message'] = $message;
208
  $params['User'] = $display_name;
209
  $params['Time'] = SucuriScan::datetime();
204
  $params['Subject'] = $subject;
205
  $params['Website'] = $website;
206
  $params['RemoteAddress'] = self::getRemoteAddr();
207
+ $params['ReverseAddress'] = gethostbyaddr($params['RemoteAddress']);
208
  $params['Message'] = $message;
209
  $params['User'] = $display_name;
210
  $params['Time'] = SucuriScan::datetime();
src/option.lib.php CHANGED
@@ -82,7 +82,6 @@ class SucuriScanOption extends SucuriScanRequest
82
  'sucuriscan_notify_available_updates' => 'disabled',
83
  'sucuriscan_notify_bruteforce_attack' => 'disabled',
84
  'sucuriscan_notify_failed_login' => 'enabled',
85
- 'sucuriscan_notify_failed_password' => 'disabled',
86
  'sucuriscan_notify_plugin_activated' => 'enabled',
87
  'sucuriscan_notify_plugin_change' => 'enabled',
88
  'sucuriscan_notify_plugin_deactivated' => 'disabled',
@@ -115,7 +114,7 @@ class SucuriScanOption extends SucuriScanRequest
115
  'sucuriscan_use_wpmail' => 'enabled',
116
  );
117
 
118
- return $defaults;
119
  }
120
 
121
  /**
@@ -179,6 +178,12 @@ class SucuriScanOption extends SucuriScanRequest
179
  */
180
  public static function getAllOptions()
181
  {
 
 
 
 
 
 
182
  $options = array();
183
  $fpath = self::optionsFilePath();
184
 
@@ -198,6 +203,8 @@ class SucuriScanOption extends SucuriScanRequest
198
  }
199
  }
200
 
 
 
201
  return $options;
202
  }
203
 
@@ -209,6 +216,8 @@ class SucuriScanOption extends SucuriScanRequest
209
  */
210
  public static function writeNewOptions($options = array())
211
  {
 
 
212
  $fpath = self::optionsFilePath();
213
  $content = "<?php exit(0); ?>\n";
214
  $content .= @json_encode($options) . "\n";
82
  'sucuriscan_notify_available_updates' => 'disabled',
83
  'sucuriscan_notify_bruteforce_attack' => 'disabled',
84
  'sucuriscan_notify_failed_login' => 'enabled',
 
85
  'sucuriscan_notify_plugin_activated' => 'enabled',
86
  'sucuriscan_notify_plugin_change' => 'enabled',
87
  'sucuriscan_notify_plugin_deactivated' => 'disabled',
114
  'sucuriscan_use_wpmail' => 'enabled',
115
  );
116
 
117
+ return (array) apply_filters('sucuriscan_option_defaults', $defaults);
118
  }
119
 
120
  /**
178
  */
179
  public static function getAllOptions()
180
  {
181
+ $options = wp_cache_get('alloptions', SUCURISCAN, WP_DEBUG);
182
+
183
+ if ($options && is_array($options)) {
184
+ return $options;
185
+ }
186
+
187
  $options = array();
188
  $fpath = self::optionsFilePath();
189
 
203
  }
204
  }
205
 
206
+ wp_cache_set('alloptions', $options, SUCURISCAN);
207
+
208
  return $options;
209
  }
210
 
216
  */
217
  public static function writeNewOptions($options = array())
218
  {
219
+ wp_cache_delete('alloptions', SUCURISCAN);
220
+
221
  $fpath = self::optionsFilePath();
222
  $content = "<?php exit(0); ?>\n";
223
  $content .= @json_encode($options) . "\n";
src/settings-alerts.php CHANGED
@@ -139,8 +139,8 @@ function sucuriscan_settings_alerts_trustedips()
139
  if ($trust_ip) {
140
  if (SucuriScan::isValidIP($trust_ip) || SucuriScan::isValidCIDR($trust_ip)) {
141
  $ip_info = SucuriScan::getIPInfo($trust_ip);
142
- $ip_info['added_at'] = time();
143
  $cache_key = md5($ip_info['remote_addr']);
 
144
 
145
  if ($cache->exists($cache_key)) {
146
  SucuriScanInterface::error('The IP specified address was already added.');
@@ -405,7 +405,6 @@ function sucuriscan_settings_alerts_events($nonce)
405
  'sucuriscan_notify_user_registration' => 'user:' . 'Receive email alerts for new user registration',
406
  'sucuriscan_notify_success_login' => 'user:' . 'Receive email alerts for successful login attempts',
407
  'sucuriscan_notify_failed_login' => 'user:' . 'Receive email alerts for failed login attempts <em>(you may receive tons of emails)</em>',
408
- 'sucuriscan_notify_failed_password' => 'user:' . 'Receive email alerts for failed login attempts including the submitted password',
409
  'sucuriscan_notify_bruteforce_attack' => 'user:' . 'Receive email alerts for password guessing attacks <em>(summary of failed logins per hour)</em>',
410
  'sucuriscan_notify_post_publication' => 'setting:' . 'Receive email alerts for changes in the post status <em>(configure from Ignore Posts Changes)</em>',
411
  'sucuriscan_notify_website_updated' => 'setting:' . 'Receive email alerts when the WordPress version is updated',
@@ -441,7 +440,6 @@ function sucuriscan_settings_alerts_events($nonce)
441
  $params['Alerts.NoAlertsVisibility'] = 'visible';
442
  unset($notify_options['sucuriscan_notify_success_login']);
443
  unset($notify_options['sucuriscan_notify_failed_login']);
444
- unset($notify_options['sucuriscan_notify_failed_password']);
445
  }
446
 
447
  // Process form submission to change the alert settings.
@@ -450,11 +448,6 @@ function sucuriscan_settings_alerts_events($nonce)
450
  if (SucuriScanRequest::post(':save_alert_events') !== false) {
451
  $ucounter = 0;
452
 
453
- /* disable password tracker for failed logins as well */
454
- if (SucuriScanRequest::post(':notify_failed_login') === '0') {
455
- $_POST['sucuriscan_notify_failed_password'] = '0';
456
- }
457
-
458
  foreach ($notify_options as $alert_type => $alert_label) {
459
  $option_value = SucuriScanRequest::post($alert_type, '(1|0)');
460
 
@@ -545,7 +538,7 @@ function sucuriscan_settings_alerts_ignore_posts()
545
  // Ignore a new event for email alerts.
546
  $action = SucuriScanRequest::post(':ignorerule_action');
547
  $ignore_rule = SucuriScanRequest::post(':ignorerule');
548
- $selected_types = SucuriScanRequest::post(':posttypes', '_array');
549
 
550
  if ($action === 'add') {
551
  if (!preg_match('/^[a-z_\-]+$/', $ignore_rule)) {
@@ -567,7 +560,7 @@ function sucuriscan_settings_alerts_ignore_posts()
567
  $timestamp = time();
568
 
569
  foreach ($post_types as $post_type) {
570
- if (!in_array($post_type, $selected_types)) {
571
  $ignored_events[$post_type] = $timestamp;
572
  }
573
  }
139
  if ($trust_ip) {
140
  if (SucuriScan::isValidIP($trust_ip) || SucuriScan::isValidCIDR($trust_ip)) {
141
  $ip_info = SucuriScan::getIPInfo($trust_ip);
 
142
  $cache_key = md5($ip_info['remote_addr']);
143
+ $ip_info['added_at'] = time();
144
 
145
  if ($cache->exists($cache_key)) {
146
  SucuriScanInterface::error('The IP specified address was already added.');
405
  'sucuriscan_notify_user_registration' => 'user:' . 'Receive email alerts for new user registration',
406
  'sucuriscan_notify_success_login' => 'user:' . 'Receive email alerts for successful login attempts',
407
  'sucuriscan_notify_failed_login' => 'user:' . 'Receive email alerts for failed login attempts <em>(you may receive tons of emails)</em>',
 
408
  'sucuriscan_notify_bruteforce_attack' => 'user:' . 'Receive email alerts for password guessing attacks <em>(summary of failed logins per hour)</em>',
409
  'sucuriscan_notify_post_publication' => 'setting:' . 'Receive email alerts for changes in the post status <em>(configure from Ignore Posts Changes)</em>',
410
  'sucuriscan_notify_website_updated' => 'setting:' . 'Receive email alerts when the WordPress version is updated',
440
  $params['Alerts.NoAlertsVisibility'] = 'visible';
441
  unset($notify_options['sucuriscan_notify_success_login']);
442
  unset($notify_options['sucuriscan_notify_failed_login']);
 
443
  }
444
 
445
  // Process form submission to change the alert settings.
448
  if (SucuriScanRequest::post(':save_alert_events') !== false) {
449
  $ucounter = 0;
450
 
 
 
 
 
 
451
  foreach ($notify_options as $alert_type => $alert_label) {
452
  $option_value = SucuriScanRequest::post($alert_type, '(1|0)');
453
 
538
  // Ignore a new event for email alerts.
539
  $action = SucuriScanRequest::post(':ignorerule_action');
540
  $ignore_rule = SucuriScanRequest::post(':ignorerule');
541
+ $selected = SucuriScanRequest::post(':posttypes', '_array');
542
 
543
  if ($action === 'add') {
544
  if (!preg_match('/^[a-z_\-]+$/', $ignore_rule)) {
560
  $timestamp = time();
561
 
562
  foreach ($post_types as $post_type) {
563
+ if (is_array($selected) && !in_array($post_type, $selected)) {
564
  $ignored_events[$post_type] = $timestamp;
565
  }
566
  }
src/settings-apiservice.php CHANGED
@@ -39,6 +39,7 @@ function sucuriscan_settings_apiservice_status($nonce)
39
  $params['ApiStatus.WarningVisibility'] = 'visible';
40
  $params['ApiStatus.ErrorVisibility'] = 'hidden';
41
  $params['ApiStatus.ServiceURL'] = SUCURISCAN_API_URL;
 
42
 
43
  if ($nonce) {
44
  // Enable or disable the API service communication.
@@ -66,6 +67,9 @@ function sucuriscan_settings_apiservice_status($nonce)
66
  $params['ApiStatus.ErrorVisibility'] = 'visible';
67
  }
68
 
 
 
 
69
  return SucuriScanTemplate::getSection('settings-apiservice-status', $params);
70
  }
71
 
39
  $params['ApiStatus.WarningVisibility'] = 'visible';
40
  $params['ApiStatus.ErrorVisibility'] = 'hidden';
41
  $params['ApiStatus.ServiceURL'] = SUCURISCAN_API_URL;
42
+ $params['ApiStatus.ApiKey'] = '';
43
 
44
  if ($nonce) {
45
  // Enable or disable the API service communication.
67
  $params['ApiStatus.ErrorVisibility'] = 'visible';
68
  }
69
 
70
+ $api_key = SucuriScanAPI::getPluginKey();
71
+ $params['ApiStatus.ApiKey'] = $api_key ? $api_key : 'NONE';
72
+
73
  return SucuriScanTemplate::getSection('settings-apiservice-status', $params);
74
  }
75
 
src/settings-general.php CHANGED
@@ -476,7 +476,6 @@ function sucuriscan_settings_general_importexport($nonce)
476
  $params = array();
477
  $allowed = array(
478
  ':addr_header',
479
- ':api_key',
480
  ':api_protocol',
481
  ':api_service',
482
  ':cloudproxy_apikey',
@@ -549,6 +548,26 @@ function sucuriscan_settings_general_importexport($nonce)
549
  $count++;
550
  }
551
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
552
  SucuriScanInterface::info(
553
  sprintf(
554
  '%d out of %d option have been successfully imported',
@@ -569,6 +588,14 @@ function sucuriscan_settings_general_importexport($nonce)
569
  $settings[$option_name] = SucuriScanOption::getOption($option);
570
  }
571
 
 
 
 
 
 
 
 
 
572
  $params['Export'] = @json_encode($settings);
573
 
574
  return SucuriScanTemplate::getSection('settings-general-importexport', $params);
476
  $params = array();
477
  $allowed = array(
478
  ':addr_header',
 
479
  ':api_protocol',
480
  ':api_service',
481
  ':cloudproxy_apikey',
548
  $count++;
549
  }
550
 
551
+ /* import trusted ip addresses */
552
+ if (array_key_exists('trusted_ips', $data) && is_array($data)) {
553
+ $cache = new SucuriScanCache('trustip');
554
+
555
+ foreach ($data['trusted_ips'] as $trustedIP) {
556
+ $trustedIP = str_replace('\/', '/', $trustedIP);
557
+ $trustedIP = str_replace('/32', '', $trustedIP);
558
+
559
+ if (SucuriScan::isValidIP($trustedIP) || SucuriScan::isValidCIDR($trustedIP)) {
560
+ $ipInfo = SucuriScan::getIPInfo($trustedIP);
561
+ $cacheKey = md5($ipInfo['remote_addr']);
562
+ $ipInfo['added_at'] = time();
563
+
564
+ if (!$cache->exists($cacheKey)) {
565
+ $cache->add($cacheKey, $ipInfo);
566
+ }
567
+ }
568
+ }
569
+ }
570
+
571
  SucuriScanInterface::info(
572
  sprintf(
573
  '%d out of %d option have been successfully imported',
588
  $settings[$option_name] = SucuriScanOption::getOption($option);
589
  }
590
 
591
+ /* include the trusted IP address list */
592
+ $settings['trusted_ips'] = array();
593
+ $cache = new SucuriScanCache('trustip');
594
+ $trusted = $cache->getAll();
595
+ foreach ($trusted as $trustedIP) {
596
+ $settings['trusted_ips'][] = $trustedIP->cidr_format;
597
+ }
598
+
599
  $params['Export'] = @json_encode($settings);
600
 
601
  return SucuriScanTemplate::getSection('settings-general-importexport', $params);
src/sucuriscan.lib.php CHANGED
@@ -259,7 +259,10 @@ class SucuriScan
259
  */
260
  public static function fixPath($path = '')
261
  {
262
- return rtrim(str_replace(DIRECTORY_SEPARATOR, '/', $path), '/');
 
 
 
263
  }
264
 
265
  /**
@@ -770,15 +773,15 @@ class SucuriScan
770
  */
771
  public static function isValidCIDR($remote_addr = '')
772
  {
773
- $status = false;
 
 
774
 
775
  if (preg_match('/^([0-9\.]{7,15})\/(8|16|24)$/', $remote_addr, $match)) {
776
- if (self::isValidIP($match[1])) {
777
- $status = true;
778
- }
779
  }
780
 
781
- return $status;
782
  }
783
 
784
  /**
@@ -906,4 +909,15 @@ class SucuriScan
906
  {
907
  return (bool) (stripos(@$_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false);
908
  }
 
 
 
 
 
 
 
 
 
 
 
909
  }
259
  */
260
  public static function fixPath($path = '')
261
  {
262
+ $new = str_replace(DIRECTORY_SEPARATOR, '/', $path);
263
+ $new = rtrim($new, '/'); /* purge right side. */
264
+
265
+ return empty($new) ? '/' : $new;
266
  }
267
 
268
  /**
773
  */
774
  public static function isValidCIDR($remote_addr = '')
775
  {
776
+ if (!is_string($remote_addr)) {
777
+ return false;
778
+ }
779
 
780
  if (preg_match('/^([0-9\.]{7,15})\/(8|16|24)$/', $remote_addr, $match)) {
781
+ return self::isValidIP($match[1]);
 
 
782
  }
783
 
784
+ return false;
785
  }
786
 
787
  /**
909
  {
910
  return (bool) (stripos(@$_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false);
911
  }
912
+
913
+ /**
914
+ * Returns the md5 hash representing the content of a file.
915
+ *
916
+ * @param string $file Relative path to the file.
917
+ * @return string Seven first characters in the hash of the file.
918
+ */
919
+ public static function fileVersion($file = '')
920
+ {
921
+ return substr(md5_file(SUCURISCAN_PLUGIN_PATH . '/' . $file), 0, 7);
922
+ }
923
  }
sucuri.php CHANGED
@@ -6,7 +6,7 @@
6
  * Plugin URI: https://wordpress.sucuri.net/
7
  * Author URI: https://sucuri.net/
8
  * Author: Sucuri Inc.
9
- * Version: 1.8.11
10
  *
11
  * PHP version 5
12
  *
@@ -83,7 +83,7 @@ define('SUCURISCAN', 'sucuriscan');
83
  /**
84
  * Current version of the plugin's code.
85
  */
86
- define('SUCURISCAN_VERSION', '1.8.11');
87
 
88
  /**
89
  * The name of the folder where the plugin's files will be located.
@@ -232,6 +232,11 @@ require_once 'src/settings-webinfo.php';
232
  /* Load global variables and triggers */
233
  require_once 'src/globals.php';
234
 
 
 
 
 
 
235
  /**
236
  * Uninstalls the plugin, its settings and reverts the hardening.
237
  *
6
  * Plugin URI: https://wordpress.sucuri.net/
7
  * Author URI: https://sucuri.net/
8
  * Author: Sucuri Inc.
9
+ * Version: 1.8.12
10
  *
11
  * PHP version 5
12
  *
83
  /**
84
  * Current version of the plugin's code.
85
  */
86
+ define('SUCURISCAN_VERSION', '1.8.12');
87
 
88
  /**
89
  * The name of the folder where the plugin's files will be located.
232
  /* Load global variables and triggers */
233
  require_once 'src/globals.php';
234
 
235
+ /* Load WP-CLI command */
236
+ if (defined('WP_CLI') && WP_CLI) {
237
+ include_once 'src/cli.lib.php';
238
+ }
239
+
240
  /**
241
  * Uninstalls the plugin, its settings and reverts the hardening.
242
  *