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

Version Description

= 1.8.8 = 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.9
Comparing to
See all releases

Code changes from version 1.8.8 to 1.8.9

Files changed (113) hide show
  1. inc/css/styles.css +48 -0
  2. inc/tpl/auditlogs.html.tpl +8 -8
  3. inc/tpl/base.html.tpl +6 -6
  4. inc/tpl/dashboard.html.tpl +3 -3
  5. inc/tpl/firewall-auditlogs.html.tpl +9 -9
  6. inc/tpl/firewall-clearcache.html.tpl +10 -12
  7. inc/tpl/firewall-ipaccess.html.tpl +11 -11
  8. inc/tpl/firewall-settings.html.tpl +11 -11
  9. inc/tpl/firewall.html.tpl +4 -4
  10. inc/tpl/integrity-correct.html.tpl +5 -5
  11. inc/tpl/integrity-diff-utility.html.tpl +10 -2
  12. inc/tpl/integrity-incorrect.html.tpl +17 -17
  13. inc/tpl/integrity-notification.html.tpl +7 -7
  14. inc/tpl/integrity.html.tpl +4 -4
  15. inc/tpl/lastlogins-admins.html.tpl +5 -5
  16. inc/tpl/lastlogins-admins.snippet.tpl +4 -4
  17. inc/tpl/lastlogins-all.html.tpl +8 -8
  18. inc/tpl/lastlogins-all.snippet.tpl +1 -1
  19. inc/tpl/lastlogins-blockedusers.html.tpl +10 -10
  20. inc/tpl/lastlogins-failedlogins.html.tpl +9 -9
  21. inc/tpl/lastlogins-loggedin.html.tpl +7 -7
  22. inc/tpl/lastlogins-loggedin.snippet.tpl +1 -1
  23. inc/tpl/lastlogins.html.tpl +5 -5
  24. inc/tpl/notification-pretty.html.tpl +5 -5
  25. inc/tpl/notification-simple.html.tpl +5 -5
  26. inc/tpl/register-site.html.tpl +7 -7
  27. inc/tpl/settings-alerts-bruteforce.html.tpl +4 -4
  28. inc/tpl/settings-alerts-events.html.tpl +4 -4
  29. inc/tpl/settings-alerts-ignore-posts.html.tpl +30 -22
  30. inc/tpl/settings-alerts-ignore-posts.snippet.tpl +9 -10
  31. inc/tpl/settings-alerts-perhour.html.tpl +4 -4
  32. inc/tpl/settings-alerts-recipients.html.tpl +7 -7
  33. inc/tpl/settings-alerts-subject.html.tpl +4 -4
  34. inc/tpl/settings-alerts-trustedips.html.tpl +9 -9
  35. inc/tpl/settings-apirecovery.html.tpl +4 -4
  36. inc/tpl/settings-apiregistered.html.tpl +4 -4
  37. inc/tpl/settings-apiservice-checksums.html.tpl +5 -5
  38. inc/tpl/settings-apiservice-proxy.html.tpl +6 -6
  39. inc/tpl/settings-apiservice-status.html.tpl +4 -4
  40. inc/tpl/settings-general-apikey.html.tpl +9 -9
  41. inc/tpl/settings-general-datastorage.html.tpl +7 -7
  42. inc/tpl/settings-general-importexport.html.tpl +4 -4
  43. inc/tpl/settings-general-ipdiscoverer.html.tpl +8 -8
  44. inc/tpl/settings-general-resetoptions.html.tpl +4 -4
  45. inc/tpl/settings-general-reverseproxy.html.tpl +3 -3
  46. inc/tpl/settings-general-selfhosting.html.tpl +5 -5
  47. inc/tpl/settings-general-timezone.html.tpl +4 -4
  48. inc/tpl/settings-hardening-whitelist-phpfiles.html.tpl +9 -9
  49. inc/tpl/settings-posthack-available-updates-alert.html.tpl +5 -5
  50. inc/tpl/settings-posthack-available-updates.html.tpl +8 -8
  51. inc/tpl/settings-posthack-available-updates.snippet.tpl +2 -2
  52. inc/tpl/settings-posthack-reset-password-alert.html.tpl +1 -1
  53. inc/tpl/settings-posthack-reset-password.html.tpl +10 -10
  54. inc/tpl/settings-posthack-reset-plugins.html.tpl +12 -12
  55. inc/tpl/settings-posthack-reset-plugins.snippet.tpl +1 -1
  56. inc/tpl/settings-posthack-security-keys.html.tpl +8 -8
  57. inc/tpl/settings-scanner-cronjobs.html.tpl +10 -10
  58. inc/tpl/settings-scanner-ignore-folders.html.tpl +9 -36
  59. inc/tpl/settings-scanner-ignore-folders.snippet.tpl +3 -3
  60. inc/tpl/settings-scanner-integrity-cache.html.tpl +7 -7
  61. inc/tpl/settings-scanner-integrity-diff-utility.html.tpl +3 -3
  62. inc/tpl/settings-scanner-integrity-language.html.tpl +0 -24
  63. inc/tpl/settings-webinfo-details.html.tpl +1 -1
  64. inc/tpl/settings-webinfo-htaccess.html.tpl +5 -5
  65. inc/tpl/settings.html.tpl +7 -9
  66. inc/tpl/sitecheck-blacklist.html.tpl +1 -3
  67. inc/tpl/sitecheck-blacklist.snippet.tpl +1 -3
  68. inc/tpl/sitecheck-malware.html.tpl +1 -1
  69. inc/tpl/sitecheck-malware.snippet.tpl +3 -3
  70. inc/tpl/sitecheck-recommendations.html.tpl +1 -1
  71. inc/tpl/sitecheck-recommendations.snippet.tpl +1 -1
  72. inc/tpl/sitecheck-target.html.tpl +5 -8
  73. languages/index.html +0 -1
  74. languages/sucuri-scanner-en_US.mo +0 -0
  75. languages/sucuri-scanner-en_US.po +0 -1512
  76. languages/sucuri-scanner-es_ES.mo +0 -0
  77. languages/sucuri-scanner-es_ES.po +0 -1513
  78. readme.txt +1 -1
  79. src/api.lib.php +149 -205
  80. src/auditlogs.lib.php +120 -89
  81. src/cache.lib.php +70 -53
  82. src/command.lib.php +39 -20
  83. src/event.lib.php +108 -100
  84. src/fileinfo.lib.php +53 -37
  85. src/firewall.lib.php +110 -77
  86. src/fsscanner.lib.php +35 -69
  87. src/globals.php +19 -97
  88. src/hardening.lib.php +40 -26
  89. src/hook.lib.php +173 -98
  90. src/installer-skin.lib.php +21 -8
  91. src/integrity.lib.php +100 -78
  92. src/interface.lib.php +72 -34
  93. src/lastlogins-blocked.php +64 -42
  94. src/lastlogins-failed.php +105 -84
  95. src/lastlogins-loggedin.php +30 -19
  96. src/lastlogins.php +117 -77
  97. src/mail.lib.php +32 -18
  98. src/option.lib.php +82 -102
  99. src/pagehandler.php +27 -13
  100. src/request.lib.php +29 -15
  101. src/settings-alerts.php +179 -155
  102. src/settings-apiservice.php +19 -11
  103. src/settings-general.php +78 -64
  104. src/settings-hardening.php +210 -100
  105. src/settings-integrity.php +47 -58
  106. src/settings-posthack.php +110 -77
  107. src/settings-scanner.php +108 -130
  108. src/settings-webinfo.php +26 -31
  109. src/settings.php +17 -3
  110. src/sitecheck.lib.php +71 -55
  111. src/sucuriscan.lib.php +148 -309
  112. src/template.lib.php +81 -93
  113. sucuri.php +56 -58
inc/css/styles.css CHANGED
@@ -863,3 +863,51 @@ body.sucuri-security_page_sucuriscan_hardening {
863
  .sucuriscan-firewall-accesslog {
864
  word-break: break-all;
865
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
863
  .sucuriscan-firewall-accesslog {
864
  word-break: break-all;
865
  }
866
+ .rtl .sucuriscan-header .sucuriscan-pull-left {
867
+ float: right;
868
+ }
869
+ .rtl .sucuriscan-header .sucuriscan-pull-right {
870
+ float: left;
871
+ }
872
+ .rtl .sucuriscan-header .sucuriscan-navbar li {
873
+ float: right;
874
+ }
875
+ .rtl .sucuriscan-tabs .sucuriscan-tabs-buttons li {
876
+ float: right;
877
+ }
878
+ .rtl .sucuriscan-tabs .sucuriscan-tabs-buttons li:first-child {
879
+ margin-left: 0;
880
+ margin-right: 12px;
881
+ }
882
+ .rtl .sucuriscan-tabs .sucuriscan-tabs-buttons a {
883
+ border-right: 1px solid #dfdfdf;
884
+ border-left: 0;
885
+ }
886
+ .rtl .sucuriscan-tabs .sucuriscan-tabs-buttons li:first-child a {
887
+ border-top-right-radius: 6px;
888
+ }
889
+ .rtl .sucuriscan-tabs .sucuriscan-tabs-buttons li:last-child a {
890
+ border-left: 1px solid #ccc;
891
+ border-top-left-radius: 6px;
892
+ }
893
+ .rtl .sucuriscan-hstatus form {
894
+ left: 20px;
895
+ right: initial;
896
+ }
897
+ .rtl .sucuriscan-container fieldset span,
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 {
906
+ float: right !important;
907
+ margin-left: 0;
908
+ margin-right: 5px;
909
+ }
910
+ .rtl .wrap .sucuriscan-alert > .close {
911
+ right: initial;
912
+ left: 20px;
913
+ }
inc/tpl/auditlogs.html.tpl CHANGED
@@ -2,13 +2,13 @@
2
  <script type="text/javascript">
3
  /* global jQuery */
4
  /* jshint camelcase:false */
5
- jQuery(function ($) {
6
  var writeQueueSize = function (queueSize) {
7
  if (queueSize === 0) {
8
  $('.sucuriscan-auditlogs-sendlogs-response').html('');
9
  $('.sucuriscan-sendlogs-panel').addClass('sucuriscan-hidden');
10
  } else {
11
- var msg = '\x20@@SUCURI.AuditLogsQueue@@\x20&mdash;\x20';
12
  $('.sucuriscan-auditlogs-sendlogs-response').html((queueSize).toString() + msg);
13
  $('.sucuriscan-sendlogs-panel').removeClass('sucuriscan-hidden');
14
  }
@@ -21,9 +21,9 @@ jQuery(function ($) {
21
  url += '&paged=' + page;
22
  }
23
 
24
- $('.sucuriscan-auditlog-response').html('<em>@@SUCURI.Loading@@</em>');
25
- $('.sucuriscan-auditlog-status').html('@@SUCURI.Loading@@');
26
- $('.sucuriscan-pagination-loading').html('@@SUCURI.Loading@@');
27
  $('.sucuriscan-pagination-panel').addClass('sucuriscan-hidden');
28
  $('.sucuriscan-auditlog-footer').addClass('sucuriscan-hidden');
29
 
@@ -72,7 +72,7 @@ jQuery(function ($) {
72
  event.preventDefault();
73
 
74
  $('.sucuriscan-sendlogs-panel').attr('content', '');
75
- $('.sucuriscan-auditlogs-sendlogs-response').html('@@SUCURI.Loading@@');
76
 
77
  $.post('%%SUCURI.AjaxURL.Dashboard%%', {
78
  action: 'sucuriscan_ajax',
@@ -104,7 +104,7 @@ jQuery(function ($) {
104
 
105
  <div class="sucuriscan-auditlog-table">
106
  <div class="sucuriscan-auditlog-response">
107
- <em>@@SUCURI.Loading@@</em>
108
  </div>
109
 
110
  <div class="sucuriscan-clearfix sucuriscan-pagination-panel">
@@ -121,7 +121,7 @@ jQuery(function ($) {
121
  <div class="sucuriscan-pull-left sucuriscan-hidden sucuriscan-tooltip
122
  sucuriscan-sendlogs-panel" tooltip-width="250" tooltip-html="true">
123
  <small class="sucuriscan-auditlogs-sendlogs-response"></small>
124
- <small><a href="#" class="sucuriscan-auditlogs-sendlogs">@@SUCURI.SendLogs@@</a></small>
125
  </div>
126
 
127
  <div class="sucuriscan-pull-right">
2
  <script type="text/javascript">
3
  /* global jQuery */
4
  /* jshint camelcase:false */
5
+ jQuery(document).ready(function ($) {
6
  var writeQueueSize = function (queueSize) {
7
  if (queueSize === 0) {
8
  $('.sucuriscan-auditlogs-sendlogs-response').html('');
9
  $('.sucuriscan-sendlogs-panel').addClass('sucuriscan-hidden');
10
  } else {
11
+ var msg = '\x20logs in the queue\x20&mdash;\x20';
12
  $('.sucuriscan-auditlogs-sendlogs-response').html((queueSize).toString() + msg);
13
  $('.sucuriscan-sendlogs-panel').removeClass('sucuriscan-hidden');
14
  }
21
  url += '&paged=' + page;
22
  }
23
 
24
+ $('.sucuriscan-auditlog-response').html('<em>Loading...</em>');
25
+ $('.sucuriscan-auditlog-status').html('Loading...');
26
+ $('.sucuriscan-pagination-loading').html('Loading...');
27
  $('.sucuriscan-pagination-panel').addClass('sucuriscan-hidden');
28
  $('.sucuriscan-auditlog-footer').addClass('sucuriscan-hidden');
29
 
72
  event.preventDefault();
73
 
74
  $('.sucuriscan-sendlogs-panel').attr('content', '');
75
+ $('.sucuriscan-auditlogs-sendlogs-response').html('Loading...');
76
 
77
  $.post('%%SUCURI.AjaxURL.Dashboard%%', {
78
  action: 'sucuriscan_ajax',
104
 
105
  <div class="sucuriscan-auditlog-table">
106
  <div class="sucuriscan-auditlog-response">
107
+ <em>Loading...</em>
108
  </div>
109
 
110
  <div class="sucuriscan-clearfix sucuriscan-pagination-panel">
121
  <div class="sucuriscan-pull-left sucuriscan-hidden sucuriscan-tooltip
122
  sucuriscan-sendlogs-panel" tooltip-width="250" tooltip-html="true">
123
  <small class="sucuriscan-auditlogs-sendlogs-response"></small>
124
+ <small><a href="#" class="sucuriscan-auditlogs-sendlogs">Send Logs</a></small>
125
  </div>
126
 
127
  <div class="sucuriscan-pull-right">
inc/tpl/base.html.tpl CHANGED
@@ -17,18 +17,18 @@
17
 
18
  <div class="sucuriscan-pull-right sucuriscan-navbar">
19
  <ul>
20
- <li><a href="https://goo.gl/aByqP5" target="_blank" rel="noopener" class="button button-secondary">@@SUCURI.Review@@</a></li>
21
 
22
  <li class="sucuriscan-%%SUCURI.GenerateAPIKey.Visibility%%">
23
  <a href="#" class="button button-primary sucuriscan-modal-button sucuriscan-register-site-button"
24
- data-modalid="sucuriscan-register-site">@@SUCURI.GenerateAPIKey@@</a>
25
  </li>
26
 
27
- <li><a href="%%SUCURI.URL.Dashboard%%" class="button button-primary">@@SUCURI.Dashboard@@</a></li>
28
 
29
- <li><a href="%%SUCURI.URL.Firewall%%" class="button button-primary">@@SUCURI.Firewall@@</a></li>
30
 
31
- <li><a href="%%SUCURI.URL.Settings%%" class="button button-primary">@@SUCURI.Settings@@</a></li>
32
  </ul>
33
  </div>
34
  </div>
@@ -38,6 +38,6 @@
38
  </div>
39
 
40
  <div class="sucuriscan-clearfix sucuriscan-footer">
41
- <div>@@SUCURI.Copyright@@</div>
42
  </div>
43
  </div>
17
 
18
  <div class="sucuriscan-pull-right sucuriscan-navbar">
19
  <ul>
20
+ <li><a href="https://goo.gl/aByqP5" target="_blank" rel="noopener" class="button button-secondary">Review</a></li>
21
 
22
  <li class="sucuriscan-%%SUCURI.GenerateAPIKey.Visibility%%">
23
  <a href="#" class="button button-primary sucuriscan-modal-button sucuriscan-register-site-button"
24
+ data-modalid="sucuriscan-register-site">Generate API Key</a>
25
  </li>
26
 
27
+ <li><a href="%%SUCURI.URL.Dashboard%%" class="button button-primary">Dashboard</a></li>
28
 
29
+ <li><a href="%%SUCURI.URL.Firewall%%" class="button button-primary">Firewall (WAF)</a></li>
30
 
31
+ <li><a href="%%SUCURI.URL.Settings%%" class="button button-primary">Settings</a></li>
32
  </ul>
33
  </div>
34
  </div>
38
  </div>
39
 
40
  <div class="sucuriscan-clearfix sucuriscan-footer">
41
+ <div>Copyright &copy; %%SUCURI.Year%% Sucuri Inc. All Rights Reserved.</div>
42
  </div>
43
  </div>
inc/tpl/dashboard.html.tpl CHANGED
@@ -4,10 +4,10 @@
4
  <script type="text/javascript">
5
  /* global jQuery */
6
  /* jshint camelcase: false */
7
- jQuery(function ($) {
8
  var sucuriscanSiteCheckLinks = function (target, links) {
9
  if (links.length === 0) {
10
- $(target).html('<div><em>@@SUCURI.NoData@@</em></div>');
11
  return;
12
  }
13
 
@@ -50,7 +50,7 @@ jQuery(function ($) {
50
  <div class="sucuriscan-panel">
51
  <div class="sucuriscan-tabs">
52
  <ul class="sucuriscan-clearfix sucuriscan-tabs-buttons">
53
- <li><a href="%%SUCURI.URL.Dashboard%%#auditlogs">@@SUCURI.AuditLogs@@</a></li>
54
  <li><a href="%%SUCURI.URL.Dashboard%%#iframes" id="sucuriscan-title-iframes">%%SUCURI.SiteCheck.iFramesTitle%%</a></li>
55
  <li><a href="%%SUCURI.URL.Dashboard%%#links" id="sucuriscan-title-links">%%SUCURI.SiteCheck.LinksTitle%%</a></li>
56
  <li><a href="%%SUCURI.URL.Dashboard%%#scripts" id="sucuriscan-title-scripts">%%SUCURI.SiteCheck.ScriptsTitle%%</a></li>
4
  <script type="text/javascript">
5
  /* global jQuery */
6
  /* jshint camelcase: false */
7
+ jQuery(document).ready(function ($) {
8
  var sucuriscanSiteCheckLinks = function (target, links) {
9
  if (links.length === 0) {
10
+ $(target).html('<div><em>no data available</em></div>');
11
  return;
12
  }
13
 
50
  <div class="sucuriscan-panel">
51
  <div class="sucuriscan-tabs">
52
  <ul class="sucuriscan-clearfix sucuriscan-tabs-buttons">
53
+ <li><a href="%%SUCURI.URL.Dashboard%%#auditlogs">Audit Logs</a></li>
54
  <li><a href="%%SUCURI.URL.Dashboard%%#iframes" id="sucuriscan-title-iframes">%%SUCURI.SiteCheck.iFramesTitle%%</a></li>
55
  <li><a href="%%SUCURI.URL.Dashboard%%#links" id="sucuriscan-title-links">%%SUCURI.SiteCheck.LinksTitle%%</a></li>
56
  <li><a href="%%SUCURI.URL.Dashboard%%#scripts" id="sucuriscan-title-scripts">%%SUCURI.SiteCheck.ScriptsTitle%%</a></li>
inc/tpl/firewall-auditlogs.html.tpl CHANGED
@@ -1,25 +1,25 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.FirewallLogsTitle@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.FirewallLogsInfo@@</p>
7
 
8
  <div class="sucuriscan-inline-alert-info">
9
- <p>@@SUCURI.FirewallLogsNote@@</p>
10
  </div>
11
 
12
  <script type="text/javascript">
13
  /* global jQuery */
14
  /* jshint camelcase: false */
15
- jQuery(function ($) {
16
  $('#sucuriscan-firewall-auditlogs-button').on('click', function (event) {
17
  event.preventDefault();
18
 
19
  var params = {};
20
 
21
  $('.sucuriscan-firewall-auditlogs tbody')
22
- .html('<tr><td><em>@@SUCURI.Loading@@</em></td></tr>');
23
 
24
  params.action = 'sucuriscan_ajax';
25
  params.form_action = 'get_firewall_logs';
@@ -44,24 +44,24 @@
44
 
45
  <form action="%%SUCURI.URL.Firewall%%#auditlogs" method="post">
46
  <fieldset class="sucuriscan-clearfix">
47
- <label>@@SUCURI.Search@@:</label>
48
  <input type="text" id="sucuriscan_firewall_query" />
49
  <select id="sucuriscan_firewall_day">%%%SUCURI.AuditLogs.DateDays%%%</select>
50
  <select id="sucuriscan_firewall_month">%%%SUCURI.AuditLogs.DateMonths%%%</select>
51
  <select id="sucuriscan_firewall_year">%%%SUCURI.AuditLogs.DateYears%%%</select>
52
- <button id="sucuriscan-firewall-auditlogs-button" class="button button-primary">@@SUCURI.Submit@@</button>
53
  </fieldset>
54
 
55
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-firewall-auditlogs">
56
  <thead>
57
  <tr>
58
- <th>@@SUCURI.FirewallLogsTitle@@</th>
59
  </tr>
60
  </thead>
61
 
62
  <tbody>
63
  <tr>
64
- <td><em>@@SUCURI.Loading@@</em></td>
65
  </tr>
66
  </tbody>
67
  </table>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Firewall Audit Logs</h3>
4
 
5
  <div class="inside">
6
+ <p>The firewall logs every request involved in an attack and separates them from the legitimate requests. You can analyze the data from the latest entries in the logs using this tool and take action either enabling the advanced features of the IDS <em>(Intrusion Detection System)</em> from the <a href="https://waf.sucuri.net/?settings" target="_blank" rel="noopener">Firewall Dashboard</a> and/or blocking IP addresses and URL paths directly from the <a href="https://waf.sucuri.net/?audit" target="_blank" rel="noopener">Firewall Audit Trails</a> page.</p>
7
 
8
  <div class="sucuriscan-inline-alert-info">
9
+ <p>Non-blocked requests are hidden from the logs, this is intentional.</p>
10
  </div>
11
 
12
  <script type="text/javascript">
13
  /* global jQuery */
14
  /* jshint camelcase: false */
15
+ jQuery(document).ready(function ($) {
16
  $('#sucuriscan-firewall-auditlogs-button').on('click', function (event) {
17
  event.preventDefault();
18
 
19
  var params = {};
20
 
21
  $('.sucuriscan-firewall-auditlogs tbody')
22
+ .html('<tr><td><em>Loading...</em></td></tr>');
23
 
24
  params.action = 'sucuriscan_ajax';
25
  params.form_action = 'get_firewall_logs';
44
 
45
  <form action="%%SUCURI.URL.Firewall%%#auditlogs" method="post">
46
  <fieldset class="sucuriscan-clearfix">
47
+ <label>Search:</label>
48
  <input type="text" id="sucuriscan_firewall_query" />
49
  <select id="sucuriscan_firewall_day">%%%SUCURI.AuditLogs.DateDays%%%</select>
50
  <select id="sucuriscan_firewall_month">%%%SUCURI.AuditLogs.DateMonths%%%</select>
51
  <select id="sucuriscan_firewall_year">%%%SUCURI.AuditLogs.DateYears%%%</select>
52
+ <button id="sucuriscan-firewall-auditlogs-button" class="button button-primary">Submit</button>
53
  </fieldset>
54
 
55
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-firewall-auditlogs">
56
  <thead>
57
  <tr>
58
+ <th>Firewall Audit Logs</th>
59
  </tr>
60
  </thead>
61
 
62
  <tbody>
63
  <tr>
64
+ <td><em>Loading...</em></td>
65
  </tr>
66
  </tbody>
67
  </table>
inc/tpl/firewall-clearcache.html.tpl CHANGED
@@ -2,13 +2,13 @@
2
  <script type="text/javascript">
3
  /* global jQuery */
4
  /* jshint camelcase: false */
5
- jQuery(function ($) {
6
  $('#firewall-clear-cache-button').on('click', function (event) {
7
  event.preventDefault();
8
 
9
  var button = $(this);
10
  button.attr('disabled', true);
11
- button.html('@@SUCURI.Loading@@');
12
  $('#firewall-clear-cache-response').html('');
13
 
14
  $.post('%%SUCURI.AjaxURL.Firewall%%', {
@@ -24,8 +24,7 @@ jQuery(function ($) {
24
  $('#firewall-clear-cache-auto').on('change', 'input:checkbox', function () {
25
  var checked = $(this).is(':checked');
26
 
27
- $('#firewall-clear-cache-auto span').html(
28
- '@@SUCURI.FirewallAutoClearCache@@ (@@SUCURI.Loading@@)');
29
 
30
  $.post('%%SUCURI.AjaxURL.Firewall%%', {
31
  action: 'sucuriscan_ajax',
@@ -33,33 +32,32 @@ jQuery(function ($) {
33
  form_action: 'firewall_auto_clear_cache',
34
  auto_clear_cache: (checked?'enable':'disable'),
35
  }, function () {
36
- $('#firewall-clear-cache-auto span')
37
- .html('@@SUCURI.FirewallAutoClearCache@@');
38
  });
39
  });
40
  });
41
  </script>
42
 
43
  <div class="sucuriscan-panel">
44
- <h3 class="sucuriscan-title">@@SUCURI.FirewallCacheTitle@@</h3>
45
 
46
  <div class="inside">
47
- <p>@@SUCURI.FirewallCacheInfo@@</p>
48
 
49
  <div class="sucuriscan-inline-alert-info">
50
- <p>@@SUCURI.FirewallCacheNote@@</p>
51
  </div>
52
 
53
- <p>@@SUCURI.FirewallCacheWiki@@</p>
54
 
55
  <div id="firewall-clear-cache-auto">
56
  <label>
57
  <input type="checkbox" name="sucuriscan_auto_clear_cache" value="true" %%SUCURI.FirewallAutoClearCache%% />
58
- <span>@@SUCURI.FirewallAutoClearCache@@</span>
59
  </label>
60
  </div>
61
 
62
  <div id="firewall-clear-cache-response"></div>
63
- <button id="firewall-clear-cache-button" class="button button-primary">@@SUCURI.FirewallCacheButton@@</button>
64
  </div>
65
  </div>
2
  <script type="text/javascript">
3
  /* global jQuery */
4
  /* jshint camelcase: false */
5
+ jQuery(document).ready(function ($) {
6
  $('#firewall-clear-cache-button').on('click', function (event) {
7
  event.preventDefault();
8
 
9
  var button = $(this);
10
  button.attr('disabled', true);
11
+ button.html('Loading...');
12
  $('#firewall-clear-cache-response').html('');
13
 
14
  $.post('%%SUCURI.AjaxURL.Firewall%%', {
24
  $('#firewall-clear-cache-auto').on('change', 'input:checkbox', function () {
25
  var checked = $(this).is(':checked');
26
 
27
+ $('#firewall-clear-cache-auto span').html('Clear cache when a post or page is updated (Loading...)');
 
28
 
29
  $.post('%%SUCURI.AjaxURL.Firewall%%', {
30
  action: 'sucuriscan_ajax',
32
  form_action: 'firewall_auto_clear_cache',
33
  auto_clear_cache: (checked?'enable':'disable'),
34
  }, function () {
35
+ $('#firewall-clear-cache-auto span').html('Clear cache when a post or page is updated');
 
36
  });
37
  });
38
  });
39
  </script>
40
 
41
  <div class="sucuriscan-panel">
42
+ <h3 class="sucuriscan-title">Clear Cache</h3>
43
 
44
  <div class="inside">
45
+ <p>The firewall offers multiple options to configure the cache level applied to your website. You can either enable the full cache which is the recommended setting, or you can set the cache level to minimal which will keep the pages static for a couple of minutes, or force the usage of the website headers <em>(only for advanced users)</em>, or in extreme cases where you do not need the cache you can simply disable it. Find more information about it in the <a href="https://kb.sucuri.net/firewall/Performance/caching-options" target="_blank" rel="noopener">Sucuri Knowledge Base</a> website.</p>
46
 
47
  <div class="sucuriscan-inline-alert-info">
48
+ <p>Note that the firewall has <a href="https://kb.sucuri.net/firewall/Performance/cache-exceptions" target="_blank" rel="noopener">special caching rules</a> for Images, CSS, PDF, TXT, JavaScript, media files and a few more extensions that are stored on our <a href="https://en.wikipedia.org/wiki/Edge_device" target="_blank" rel="noopener">edge</a>. The only way to flush the cache for these files is by clearing the firewall's cache completely <em>(for the whole website)</em>. Due to our caching of JavaScript and CSS files, often, as is best practice, the use of versioning during development will ensure updates going live as expected. This is done by adding a query string such as <code>?ver=1.2.3</code> and incrementing on each update.</p>
49
  </div>
50
 
51
+ <p>A web cache (or HTTP cache) is an information technology for the temporary storage (caching) of web documents, such as HTML pages and images, to reduce bandwidth usage, server load, and perceived lag. A web cache system stores copies of documents passing through it; subsequent requests may be satisfied from the cache if certain conditions are met. A web cache system can refer either to an appliance, or to a computer program. &mdash; <a href="https://en.wikipedia.org/wiki/Web_cache" target="_blank" rel="noopener">WikiPedia - Web Cache</a></p>
52
 
53
  <div id="firewall-clear-cache-auto">
54
  <label>
55
  <input type="checkbox" name="sucuriscan_auto_clear_cache" value="true" %%SUCURI.FirewallAutoClearCache%% />
56
+ <span>Clear cache when a post or page is updated</span>
57
  </label>
58
  </div>
59
 
60
  <div id="firewall-clear-cache-response"></div>
61
+ <button id="firewall-clear-cache-button" class="button button-primary">Clear Cache</button>
62
  </div>
63
  </div>
inc/tpl/firewall-ipaccess.html.tpl CHANGED
@@ -6,7 +6,7 @@
6
  jQuery(document).ready(function ($) {
7
  var sucuriscanLoadIPAccess = function () {
8
  // $('.sucuriscan-ipaccess-table tbody').html('<tr>' +
9
- // '<td colspan="2">@@SUCURI.Loading@@</td></tr>');
10
 
11
  $.post('%%SUCURI.AjaxURL.Firewall%%', {
12
  action: 'sucuriscan_ajax',
@@ -19,7 +19,7 @@ jQuery(document).ready(function ($) {
19
  $('.sucuriscan-ipaccess-table tbody').append('<tr>' +
20
  '<td><span class="sucuriscan-monospace">' + data.blacklist[i] + '</span></td>' +
21
  '<td><button class="button button-primary sucuriscan-deblacklist" ' +
22
- 'ip="' + data.blacklist[i] + '">@@SUCURI.Delete@@</button></td>' +
23
  '</tr>');
24
  }
25
  });
@@ -27,7 +27,7 @@ jQuery(document).ready(function ($) {
27
 
28
  var sucuriscanPrintStatus = function (button, data) {
29
  button.attr('disabled', false);
30
- button.html('@@SUCURI.Submit@@');
31
 
32
  if (data.ok) {
33
  sucuriscanLoadIPAccess();
@@ -49,7 +49,7 @@ jQuery(document).ready(function ($) {
49
  var ip = $('.sucuriscan-ipaccess-form input[name=sucuriscan_ip]').val();
50
 
51
  button.attr('disabled', true);
52
- button.html('@@SUCURI.Loading@@');
53
  $('#sucuriscan-ipaccess-response').html('');
54
 
55
  $.post('%%SUCURI.AjaxURL.Firewall%%', {
@@ -68,7 +68,7 @@ jQuery(document).ready(function ($) {
68
  var button = $(this);
69
 
70
  button.attr('disabled', true);
71
- button.html('@@SUCURI.Loading@@');
72
  $('#sucuriscan-ipaccess-response').html('');
73
 
74
  $.post('%%SUCURI.AjaxURL.Firewall%%', {
@@ -86,32 +86,32 @@ jQuery(document).ready(function ($) {
86
  </script>
87
 
88
  <div class="sucuriscan-panel">
89
- <h3 class="sucuriscan-title">@@SUCURI.FirewallIPAccessTitle@@</h3>
90
 
91
  <div class="inside">
92
- <p>@@SUCURI.FirewallIPAccessInfo@@</p>
93
 
94
  <div id="sucuriscan-ipaccess-response"></div>
95
 
96
  <form action="%%SUCURI.URL.Firewall%%#ipaccess" method="post" class="sucuriscan-ipaccess-form">
97
  <input type="hidden" name="sucuriscan_blacklist_ip" value="true" />
98
  <fieldset class="sucuriscan-clearfix">
99
- <label>@@SUCURI.BlacklistIP@@:</label>
100
  <input type="text" name="sucuriscan_ip" placeholder="e.g. 192.168.1.54" />
101
- <button class="button button-primary sucuriscan-ipaccess-button">@@SUCURI.Submit@@</button>
102
  </fieldset>
103
  </form>
104
 
105
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-ipaccess-table">
106
  <thead>
107
  <tr>
108
- <th>@@SUCURI.RemoteAddr@@</th>
109
  <th>&nbsp;</th>
110
  </tr>
111
  </thead>
112
 
113
  <tbody>
114
- <tr><td colspan="2">@@SUCURI.Loading@@</td></tr>
115
  </tbody>
116
  </table>
117
  </div>
6
  jQuery(document).ready(function ($) {
7
  var sucuriscanLoadIPAccess = function () {
8
  // $('.sucuriscan-ipaccess-table tbody').html('<tr>' +
9
+ // '<td colspan="2">Loading...</td></tr>');
10
 
11
  $.post('%%SUCURI.AjaxURL.Firewall%%', {
12
  action: 'sucuriscan_ajax',
19
  $('.sucuriscan-ipaccess-table tbody').append('<tr>' +
20
  '<td><span class="sucuriscan-monospace">' + data.blacklist[i] + '</span></td>' +
21
  '<td><button class="button button-primary sucuriscan-deblacklist" ' +
22
+ 'ip="' + data.blacklist[i] + '">Delete</button></td>' +
23
  '</tr>');
24
  }
25
  });
27
 
28
  var sucuriscanPrintStatus = function (button, data) {
29
  button.attr('disabled', false);
30
+ button.html('Submit');
31
 
32
  if (data.ok) {
33
  sucuriscanLoadIPAccess();
49
  var ip = $('.sucuriscan-ipaccess-form input[name=sucuriscan_ip]').val();
50
 
51
  button.attr('disabled', true);
52
+ button.html('Loading...');
53
  $('#sucuriscan-ipaccess-response').html('');
54
 
55
  $.post('%%SUCURI.AjaxURL.Firewall%%', {
68
  var button = $(this);
69
 
70
  button.attr('disabled', true);
71
+ button.html('Loading...');
72
  $('#sucuriscan-ipaccess-response').html('');
73
 
74
  $.post('%%SUCURI.AjaxURL.Firewall%%', {
86
  </script>
87
 
88
  <div class="sucuriscan-panel">
89
+ <h3 class="sucuriscan-title">IP Address Access</h3>
90
 
91
  <div class="inside">
92
+ <p>This tool allows you to whitleist and blacklist one or more IP addresses from accessing your website. You can also configure the plugin to automatically blacklist any IP address involved in a password guessing brute-force attack. If a legitimate user fails to submit the correct credentials of their account they will have to log into the Firewall dashboard in order to delete their IP address from the blacklist, or try to login once again through a VPN.</p>
93
 
94
  <div id="sucuriscan-ipaccess-response"></div>
95
 
96
  <form action="%%SUCURI.URL.Firewall%%#ipaccess" method="post" class="sucuriscan-ipaccess-form">
97
  <input type="hidden" name="sucuriscan_blacklist_ip" value="true" />
98
  <fieldset class="sucuriscan-clearfix">
99
+ <label>Blacklist IP:</label>
100
  <input type="text" name="sucuriscan_ip" placeholder="e.g. 192.168.1.54" />
101
+ <button class="button button-primary sucuriscan-ipaccess-button">Submit</button>
102
  </fieldset>
103
  </form>
104
 
105
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-ipaccess-table">
106
  <thead>
107
  <tr>
108
+ <th>IP Address</th>
109
  <th>&nbsp;</th>
110
  </tr>
111
  </thead>
112
 
113
  <tbody>
114
+ <tr><td colspan="2">Loading...</td></tr>
115
  </tbody>
116
  </table>
117
  </div>
inc/tpl/firewall-settings.html.tpl CHANGED
@@ -31,30 +31,30 @@ jQuery(document).ready(function ($) {
31
  </script>
32
 
33
  <div class="sucuriscan-panel">
34
- <h3 class="sucuriscan-title">@@SUCURI.FirewallSettingsTitle@@</h3>
35
 
36
  <div class="inside">
37
- <p>@@SUCURI.FirewallSettingsInfo@@</p>
38
 
39
  <div class="sucuriscan-inline-alert-info sucuriscan-%%SUCURI.Firewall.APIKeyFormVisibility%%">
40
- <p>@@SUCURI.FirewallAddKey@@</p>
41
  </div>
42
 
43
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-firewall-apikey sucuriscan-%%SUCURI.Firewall.APIKeyVisibility%%">
44
- <strong>@@SUCURI.FirewallKey@@:</strong>
45
  <span class="sucuriscan-monospace">%%SUCURI.Firewall.APIKey%%</span>
46
  <form action="%%SUCURI.URL.Firewall%%" method="post">
47
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
48
- <button type="submit" name="sucuriscan_delete_wafkey" class="button button-primary">@@SUCURI.Delete@@</button>
49
  </form>
50
  </div>
51
 
52
  <form action="%%SUCURI.URL.Firewall%%" method="post" class="sucuriscan-%%SUCURI.Firewall.APIKeyFormVisibility%%">
53
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
54
  <fieldset class="sucuriscan-clearfix">
55
- <label>@@SUCURI.FirewallKey@@:</label>
56
  <input type="text" name="sucuriscan_cloudproxy_apikey" />
57
- <button type="submit" class="button button-primary">@@SUCURI.Save@@</button>
58
  </fieldset>
59
  <br>
60
  </form>
@@ -62,16 +62,16 @@ jQuery(document).ready(function ($) {
62
  <table class="wp-list-table widefat sucuriscan-table" id="firewall-settings-table">
63
  <thead>
64
  <tr>
65
- <th>@@SUCURI.Name@@</th>
66
- <th>@@SUCURI.Value@@</th>
67
  </tr>
68
  </thead>
69
 
70
  <tbody>
71
- <tr><td colspan="2">@@SUCURI.Loading@@</td></tr>
72
  </tbody>
73
  </table>
74
 
75
- <p>@@SUCURI.FirewallFootNote@@</p>
76
  </div>
77
  </div>
31
  </script>
32
 
33
  <div class="sucuriscan-panel">
34
+ <h3 class="sucuriscan-title">Firewall Settings</h3>
35
 
36
  <div class="inside">
37
+ <p>A powerful Web Application Firewall and <b>Intrusion Detection System</b> for any WordPress user and many other platforms. This page will help you to configure and monitor your site through the <b>Sucuri Firewall</b>. Once enabled, our firewall will act as a shield, protecting your site from attacks and preventing malware infections and reinfections. It will block SQL injection attempts, brute force attacks, XSS, RFI, backdoors and many other threats against your site.</p>
38
 
39
  <div class="sucuriscan-inline-alert-info sucuriscan-%%SUCURI.Firewall.APIKeyFormVisibility%%">
40
+ <p>Add your <a href="https://waf.sucuri.net/?settings&panel=api" target="_blank" rel="noopener">Firewall API key</a> in the form below to start communicating with the firewall API service.</p>
41
  </div>
42
 
43
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-firewall-apikey sucuriscan-%%SUCURI.Firewall.APIKeyVisibility%%">
44
+ <strong>Firewall API Key:</strong>
45
  <span class="sucuriscan-monospace">%%SUCURI.Firewall.APIKey%%</span>
46
  <form action="%%SUCURI.URL.Firewall%%" method="post">
47
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
48
+ <button type="submit" name="sucuriscan_delete_wafkey" class="button button-primary">Delete</button>
49
  </form>
50
  </div>
51
 
52
  <form action="%%SUCURI.URL.Firewall%%" method="post" class="sucuriscan-%%SUCURI.Firewall.APIKeyFormVisibility%%">
53
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
54
  <fieldset class="sucuriscan-clearfix">
55
+ <label>Firewall API Key:</label>
56
  <input type="text" name="sucuriscan_cloudproxy_apikey" />
57
+ <button type="submit" class="button button-primary">Save</button>
58
  </fieldset>
59
  <br>
60
  </form>
62
  <table class="wp-list-table widefat sucuriscan-table" id="firewall-settings-table">
63
  <thead>
64
  <tr>
65
+ <th>Name</th>
66
+ <th>Value</th>
67
  </tr>
68
  </thead>
69
 
70
  <tbody>
71
+ <tr><td colspan="2">Loading...</td></tr>
72
  </tbody>
73
  </table>
74
 
75
+ <p><em>[1]</em> More information about the <a href="https://sucuri.net/website-firewall/" target="_blank" rel="noopener">Sucuri Firewall</a>, features and pricing.<br><em>[2]</em> Instructions and videos in the official <a href="https://kb.sucuri.net/firewall" target="_blank" rel="noopener">Knowledge Base</a> site.<br><em>[3]</em> <a href="https://login.sucuri.net/signup2/create?CloudProxy" target="_blank" rel="noopener">Sign up</a> for a new account and start protecting your site.</p>
76
  </div>
77
  </div>
inc/tpl/firewall.html.tpl CHANGED
@@ -1,10 +1,10 @@
1
 
2
  <div class="sucuriscan-tabs">
3
  <ul class="sucuriscan-clearfix sucuriscan-tabs-buttons">
4
- <li><a href="%%SUCURI.URL.Firewall%%#settings">@@SUCURI.Settings@@</a></li>
5
- <li><a href="%%SUCURI.URL.Firewall%%#auditlogs">@@SUCURI.AuditLogs@@</a></li>
6
- <li><a href="%%SUCURI.URL.Firewall%%#ipaccess">@@SUCURI.IPAccess@@</a></li>
7
- <li><a href="%%SUCURI.URL.Firewall%%#clearcache">@@SUCURI.ClearCache@@</a></li>
8
  </ul>
9
 
10
  <div class="sucuriscan-tabs-containers">
1
 
2
  <div class="sucuriscan-tabs">
3
  <ul class="sucuriscan-clearfix sucuriscan-tabs-buttons">
4
+ <li><a href="%%SUCURI.URL.Firewall%%#settings">Settings</a></li>
5
+ <li><a href="%%SUCURI.URL.Firewall%%#auditlogs">Audit Logs</a></li>
6
+ <li><a href="%%SUCURI.URL.Firewall%%#ipaccess">IP Access</a></li>
7
+ <li><a href="%%SUCURI.URL.Firewall%%#clearcache">Clear Cache</a></li>
8
  </ul>
9
 
10
  <div class="sucuriscan-tabs-containers">
inc/tpl/integrity-correct.html.tpl CHANGED
@@ -2,17 +2,17 @@
2
  <div class="sucuriscan-panel sucuriscan-integrity sucuriscan-integrity-correct">
3
  <div class="sucuriscan-clearfix">
4
  <div class="sucuriscan-pull-left sucuriscan-integrity-left">
5
- <h2 class="sucuriscan-title">@@SUCURI.IntegrityTitle@@</h2>
6
 
7
- <p>@@SUCURI.IntegrityDescription@@</p>
8
  </div>
9
 
10
  <div class="sucuriscan-pull-right sucuriscan-integrity-right">
11
- <h2 class="sucuriscan-subtitle">@@SUCURI.IntegrityGoodTitle@@</h2>
12
 
13
- <p>@@SUCURI.IntegrityGoodDescription@@</p>
14
 
15
- <p><a href="%%SUCURI.URL.Settings%%#scanner">@@SUCURI.ReviewFalsePositives@@</a></p>
16
  </div>
17
  </div>
18
 
2
  <div class="sucuriscan-panel sucuriscan-integrity sucuriscan-integrity-correct">
3
  <div class="sucuriscan-clearfix">
4
  <div class="sucuriscan-pull-left sucuriscan-integrity-left">
5
+ <h2 class="sucuriscan-title">WordPress Integrity</h2>
6
 
7
+ <p>We inspect your WordPress installation and look for modifications on the core files as provided by WordPress.org. Files located in the root directory, wp-admin and wp-includes will be compared against the files distributed with v%%SUCURI.WordPressVersion%%; all files with inconsistencies will be listed here. Any changes might indicate a hack.</p>
8
  </div>
9
 
10
  <div class="sucuriscan-pull-right sucuriscan-integrity-right">
11
+ <h2 class="sucuriscan-subtitle">All Core WordPress Files Are Correct</h2>
12
 
13
+ <p>We have not identified additional files, deleted files, or relevant changes to the core files in your WordPress installation. If you are experiencing other malware issues, please use a <a href="https://sucuri.net/website-security/malware-removal" target="_blank" rel="noopener">Server Side Scanner</a>.</p>
14
 
15
+ <p><a href="%%SUCURI.URL.Settings%%#scanner">Review False Positives</a></p>
16
  </div>
17
  </div>
18
 
inc/tpl/integrity-diff-utility.html.tpl CHANGED
@@ -2,10 +2,12 @@
2
  <div class="sucuriscan-integrity-diff-utility">
3
  %%%SUCURI.DiffUtility.Modal%%%
4
 
 
 
5
  <script type="text/javascript">
6
  /* global jQuery */
7
  /* jshint camelcase: false */
8
- jQuery(function ($) {
9
  $('.sucuriscan-integrity-table th .sucuriscan-tooltip').removeClass('sucuriscan-hidden');
10
 
11
  $('.sucuriscan-integrity-table .sucuriscan-integrity-filepath').on('click', function (event) {
@@ -23,7 +25,13 @@
23
  filepath: filepath,
24
  }, function (data) {
25
  $('.sucuriscan-diff-utility-modal .sucuriscan-modal-inside').html(data);
26
- $('.sucuriscan-diff-content').before('<p>@@SUCURI.DiffUtilityInstructions@@</p>');
 
 
 
 
 
 
27
  });
28
  });
29
  });
2
  <div class="sucuriscan-integrity-diff-utility">
3
  %%%SUCURI.DiffUtility.Modal%%%
4
 
5
+ <style type="text/css">.sucuriscan-integrity-filepath {cursor: pointer}</style>
6
+
7
  <script type="text/javascript">
8
  /* global jQuery */
9
  /* jshint camelcase: false */
10
+ jQuery(document).ready(function ($) {
11
  $('.sucuriscan-integrity-table th .sucuriscan-tooltip').removeClass('sucuriscan-hidden');
12
 
13
  $('.sucuriscan-integrity-table .sucuriscan-integrity-filepath').on('click', function (event) {
25
  filepath: filepath,
26
  }, function (data) {
27
  $('.sucuriscan-diff-utility-modal .sucuriscan-modal-inside').html(data);
28
+ $('.sucuriscan-diff-content').before('<p>Lines with a <b>minus' +
29
+ '</b> sign as the prefix <em>(here in red)</em> show the origi' +
30
+ 'nal code. Lines with a <b>plus</b> sign as the prefix <em>(he' +
31
+ 're in green)</em> show the modified code. You can read more a' +
32
+ 'bout the DIFF format from the WikiPedia article about the <a ' +
33
+ 'target="_blank" href="https://en.wikipedia.org/wiki/Diff_util' +
34
+ 'ity" rel="noopener">Unix Diff Utility</a>.</p>');
35
  });
36
  });
37
  });
inc/tpl/integrity-incorrect.html.tpl CHANGED
@@ -2,17 +2,17 @@
2
  <div class="sucuriscan-panel sucuriscan-integrity sucuriscan-integrity-incorrect">
3
  <div class="sucuriscan-clearfix">
4
  <div class="sucuriscan-pull-left sucuriscan-integrity-left">
5
- <h2 class="sucuriscan-title">@@SUCURI.IntegrityTitle@@</h2>
6
 
7
- <p>@@SUCURI.IntegrityDescription@@</p>
8
  </div>
9
 
10
  <div class="sucuriscan-pull-right sucuriscan-integrity-right">
11
- <h2 class="sucuriscan-subtitle">@@SUCURI.IntegrityBadTitle@@</h2>
12
 
13
- <p>@@SUCURI.IntegrityBadDescription@@</p>
14
 
15
- <p><a href="%%SUCURI.URL.Settings%%#scanner">@@SUCURI.ReviewFalsePositives@@</a></p>
16
  </div>
17
  </div>
18
 
@@ -27,9 +27,9 @@
27
  <thead>
28
  <tr>
29
  <th colspan="5">
30
- <span>@@SUCURI.IntegrityTitle@@ (%%SUCURI.Integrity.ListCount%%)</span>
31
 
32
- <span class="sucuriscan-tooltip sucuriscan-hidden" content="@@SUCURI.DiffUtilityInfo@@">
33
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14">
34
  <path fill="#000000" d="m6.998315,0.033333c-3.846307,0 -6.964982,
35
  3.118675 -6.964982,6.964982s3.118675,6.965574 6.964982,6.965574s6.965574,
@@ -71,9 +71,9 @@
71
  <input id="cb-select-all-1" type="checkbox">
72
  </td>
73
  <th width="20" class="manage-column">&nbsp;</th>
74
- <th width="100" class="manage-column">@@SUCURI.FileSize@@</th>
75
- <th width="200" class="manage-column">@@SUCURI.ModifiedAt@@</th>
76
- <th class="manage-column">@@SUCURI.FilePath@@</th>
77
  </tr>
78
  </thead>
79
 
@@ -86,22 +86,22 @@
86
  <label>
87
  <input type="hidden" name="sucuriscan_process_form" value="0" />
88
  <input type="checkbox" name="sucuriscan_process_form" value="1" />
89
- <span>@@SUCURI.UnderstandTheRisk@@</span>
90
  </label>
91
  </p>
92
 
93
  <fieldset class="sucuriscan-clearfix">
94
- <label>@@SUCURI.Action@@:</label>
95
 
96
  <select name="sucuriscan_integrity_action">
97
- <option value="fixed">@@SUCURI.MarkFixed@@</option>
98
- <option value="restore">@@SUCURI.RestoreFile@@</option>
99
- <option value="delete">@@SUCURI.DeleteFile@@</option>
100
  </select>
101
 
102
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
103
 
104
- <span class="sucuriscan-tooltip" content="@@SUCURI.MarkFixedDescription@@">
105
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14">
106
  <path fill="#000000" d="m6.998315,0.033333c-3.846307,0 -6.964982,
107
  3.118675 -6.964982,6.964982s3.118675,6.965574 6.964982,6.965574s6.965574,
2
  <div class="sucuriscan-panel sucuriscan-integrity sucuriscan-integrity-incorrect">
3
  <div class="sucuriscan-clearfix">
4
  <div class="sucuriscan-pull-left sucuriscan-integrity-left">
5
+ <h2 class="sucuriscan-title">WordPress Integrity</h2>
6
 
7
+ <p>We inspect your WordPress installation and look for modifications on the core files as provided by WordPress.org. Files located in the root directory, wp-admin and wp-includes will be compared against the files distributed with v%%SUCURI.WordPressVersion%%; all files with inconsistencies will be listed here. Any changes might indicate a hack.</p>
8
  </div>
9
 
10
  <div class="sucuriscan-pull-right sucuriscan-integrity-right">
11
+ <h2 class="sucuriscan-subtitle">Core WordPress Files Were Modified</h2>
12
 
13
+ <p>We identified that some of your WordPress core files were modified. That might indicate a hack or a broken file on your installation. If you are experiencing other malware issues, please use a <a href="https://sucuri.net/website-security/malware-removal" target="_blank" rel="noopener">Server Side Scanner</a>.</p>
14
 
15
+ <p><a href="%%SUCURI.URL.Settings%%#scanner">Review False Positives</a></p>
16
  </div>
17
  </div>
18
 
27
  <thead>
28
  <tr>
29
  <th colspan="5">
30
+ <span>WordPress Integrity (%%SUCURI.Integrity.ListCount%%)</span>
31
 
32
+ <span class="sucuriscan-tooltip sucuriscan-hidden" content="The Unix Diff Utility is enabled. You can click the files in the table to see the differences detected by the scanner. If you consider the differences to be harmless you can mark the file as fixed, otherwise it is adviced to restore the original content immediately.">
33
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14">
34
  <path fill="#000000" d="m6.998315,0.033333c-3.846307,0 -6.964982,
35
  3.118675 -6.964982,6.964982s3.118675,6.965574 6.964982,6.965574s6.965574,
71
  <input id="cb-select-all-1" type="checkbox">
72
  </td>
73
  <th width="20" class="manage-column">&nbsp;</th>
74
+ <th width="100" class="manage-column">File Size</th>
75
+ <th width="200" class="manage-column">Modified At</th>
76
+ <th class="manage-column">File Path</th>
77
  </tr>
78
  </thead>
79
 
86
  <label>
87
  <input type="hidden" name="sucuriscan_process_form" value="0" />
88
  <input type="checkbox" name="sucuriscan_process_form" value="1" />
89
+ <span>I understand that this operation can not be reverted.</span>
90
  </label>
91
  </p>
92
 
93
  <fieldset class="sucuriscan-clearfix">
94
+ <label>Action:</label>
95
 
96
  <select name="sucuriscan_integrity_action">
97
+ <option value="fixed">Mark as Fixed</option>
98
+ <option value="restore">Restore File</option>
99
+ <option value="delete">Delete File</option>
100
  </select>
101
 
102
+ <button type="submit" class="button button-primary">Submit</button>
103
 
104
+ <span class="sucuriscan-tooltip" content="Marking one or more files as fixed will force the plugin to ignore them during the next scan, very useful when you find false positives. Additionally you can restore the original content of the core files that appear as modified or deleted, this will tell the plugin to download a copy of the original files from the official WordPress repository. Deleting a file is an irreversible action, be careful.">
105
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14">
106
  <path fill="#000000" d="m6.998315,0.033333c-3.846307,0 -6.964982,
107
  3.118675 -6.964982,6.964982s3.118675,6.965574 6.964982,6.965574s6.965574,
inc/tpl/integrity-notification.html.tpl CHANGED
@@ -1,18 +1,18 @@
1
 
2
- <p>@@SUCURI.IntegrityBadDescription@@</p>
3
 
4
  <table border="1" cellspacing="1" cellpadding="5">
5
  <thead>
6
  <tr>
7
- <th colspan="5">@@SUCURI.IntegrityTitle@@ (%%SUCURI.Integrity.ListCount%%)</th>
8
  </tr>
9
 
10
  <tr>
11
  <th>&nbsp;</th>
12
- <th width="80">@@SUCURI.Status@@</th>
13
- <th width="100">@@SUCURI.FileSize@@</th>
14
- <th width="170">@@SUCURI.ModifiedAt@@</th>
15
- <th>@@SUCURI.FilePath@@</th>
16
  </tr>
17
  </thead>
18
 
@@ -23,7 +23,7 @@
23
  <tfoot>
24
  <tr>
25
  <td colspan="5">
26
- <p>@@SUCURI.MarkFixedDescription@@</p>
27
  </td>
28
  </tr>
29
  </tfoot>
1
 
2
+ <p>We identified that some of your WordPress core files were modified. That might indicate a hack or a broken file on your installation. If you are experiencing other malware issues, please use a <a href="https://sucuri.net/website-security/malware-removal" target="_blank" rel="noopener">Server Side Scanner</a>.</p>
3
 
4
  <table border="1" cellspacing="1" cellpadding="5">
5
  <thead>
6
  <tr>
7
+ <th colspan="5">WordPress Integrity (%%SUCURI.Integrity.ListCount%%)</th>
8
  </tr>
9
 
10
  <tr>
11
  <th>&nbsp;</th>
12
+ <th width="80">Status</th>
13
+ <th width="100">File Size</th>
14
+ <th width="170">Modified At</th>
15
+ <th>File Path</th>
16
  </tr>
17
  </thead>
18
 
23
  <tfoot>
24
  <tr>
25
  <td colspan="5">
26
+ <p>Marking one or more files as fixed will force the plugin to ignore them during the next scan, very useful when you find false positives. Additionally you can restore the original content of the core files that appear as modified or deleted, this will tell the plugin to download a copy of the original files from the official WordPress repository. Deleting a file is an irreversible action, be careful.</p>
27
  </td>
28
  </tr>
29
  </tfoot>
inc/tpl/integrity.html.tpl CHANGED
@@ -2,7 +2,7 @@
2
  <script type="text/javascript">
3
  /* global jQuery */
4
  /* jshint camelcase: false */
5
- jQuery(function ($) {
6
  $.post('%%SUCURI.AjaxURL.Dashboard%%', {
7
  action: 'sucuriscan_ajax',
8
  sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
@@ -19,9 +19,9 @@ jQuery(function ($) {
19
  <div class="sucuriscan-panel sucuriscan-integrity sucuriscan-integrity-loading">
20
  <div class="sucuriscan-clearfix">
21
  <div class="sucuriscan-pull-left sucuriscan-integrity-left">
22
- <h2 class="sucuriscan-title">@@SUCURI.IntegrityTitle@@</h2>
23
 
24
- <p>@@SUCURI.IntegrityDescription@@</p>
25
  </div>
26
 
27
  <div class="sucuriscan-pull-right sucuriscan-integrity-right">
@@ -31,6 +31,6 @@ jQuery(function ($) {
31
  </div>
32
  </div>
33
 
34
- <p>@@SUCURI.Loading@@</p>
35
  </div>
36
  </div>
2
  <script type="text/javascript">
3
  /* global jQuery */
4
  /* jshint camelcase: false */
5
+ jQuery(document).ready(function ($) {
6
  $.post('%%SUCURI.AjaxURL.Dashboard%%', {
7
  action: 'sucuriscan_ajax',
8
  sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
19
  <div class="sucuriscan-panel sucuriscan-integrity sucuriscan-integrity-loading">
20
  <div class="sucuriscan-clearfix">
21
  <div class="sucuriscan-pull-left sucuriscan-integrity-left">
22
+ <h2 class="sucuriscan-title">WordPress Integrity</h2>
23
 
24
+ <p>We inspect your WordPress installation and look for modifications on the core files as provided by WordPress.org. Files located in the root directory, wp-admin and wp-includes will be compared against the files distributed with v%%SUCURI.WordPressVersion%%; all files with inconsistencies will be listed here. Any changes might indicate a hack.</p>
25
  </div>
26
 
27
  <div class="sucuriscan-pull-right sucuriscan-integrity-right">
31
  </div>
32
  </div>
33
 
34
+ <p>Loading...</p>
35
  </div>
36
  </div>
inc/tpl/lastlogins-admins.html.tpl CHANGED
@@ -1,16 +1,16 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.LoginsAdmins@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.LoginsAdminsInfo@@</p>
7
 
8
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-table-double-title sucuriscan-adminusers">
9
  <thead>
10
  <tr>
11
- <th class="manage-column">@@SUCURI.Username@@</th>
12
- <th class="manage-column">@@SUCURI.Registration@@</th>
13
- <th class="manage-column">@@SUCURI.NewestLogins@@</th>
14
  <th class="manage-column">&nbsp;</th>
15
  </tr>
16
  </thead>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Successful Logins (admins)</h3>
4
 
5
  <div class="inside">
6
+ <p>Here you can see a list of all the successful logins of accounts with admin privileges.</p>
7
 
8
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-table-double-title sucuriscan-adminusers">
9
  <thead>
10
  <tr>
11
+ <th class="manage-column">Username</th>
12
+ <th class="manage-column">Registration</th>
13
+ <th class="manage-column">Newest To Oldest</th>
14
  <th class="manage-column">&nbsp;</th>
15
  </tr>
16
  </thead>
inc/tpl/lastlogins-admins.snippet.tpl CHANGED
@@ -6,14 +6,14 @@
6
 
7
  <td class="adminusers-lastlogin">
8
  <div class="sucuriscan-%%SUCURI.AdminUsers.NoLastLogins%%">
9
- <em>@@SUCURI.NoData@@</em>
10
  </div>
11
 
12
  <table class="widefat sucuriscan-admins-lastlogins sucuriscan-%%SUCURI.AdminUsers.NoLastLoginsTable%%">
13
  <thead>
14
  <tr>
15
- <th>@@SUCURI.RemoteAddr@@</th>
16
- <th>@@SUCURI.Datetime@@</th>
17
  </tr>
18
  </thead>
19
 
@@ -24,6 +24,6 @@
24
  </td>
25
 
26
  <td>
27
- <a href="%%SUCURI.AdminUsers.UserURL%%" target="_blank" class="button button-primary">@@SUCURI.Edit@@</a>
28
  </td>
29
  </tr>
6
 
7
  <td class="adminusers-lastlogin">
8
  <div class="sucuriscan-%%SUCURI.AdminUsers.NoLastLogins%%">
9
+ <em>no data available</em>
10
  </div>
11
 
12
  <table class="widefat sucuriscan-admins-lastlogins sucuriscan-%%SUCURI.AdminUsers.NoLastLoginsTable%%">
13
  <thead>
14
  <tr>
15
+ <th>IP Address</th>
16
+ <th>Date/Time</th>
17
  </tr>
18
  </thead>
19
 
24
  </td>
25
 
26
  <td>
27
+ <a href="%%SUCURI.AdminUsers.UserURL%%" target="_blank" class="button button-primary" rel="noopener">Edit</a>
28
  </td>
29
  </tr>
inc/tpl/lastlogins-all.html.tpl CHANGED
@@ -1,21 +1,21 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.LoginsAll@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.LoginsAllInfo@@</p>
7
 
8
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-table-double-title sucuriscan-last-logins">
9
  <thead>
10
  <tr>
11
- <th colspan="5">@@SUCURI.LoginsAll@@</th>
12
  </tr>
13
 
14
  <tr>
15
- <th class="manage-column">@@SUCURI.Username@@</th>
16
- <th class="manage-column">@@SUCURI.RemoteAddr@@</th>
17
- <th class="manage-column">@@SUCURI.Hostname@@</th>
18
- <th class="manage-column">@@SUCURI.Datetime@@</th>
19
  <th class="manage-column">&nbsp;</th>
20
  </tr>
21
  </thead>
@@ -25,7 +25,7 @@
25
 
26
  <tr class="sucuriscan-%%SUCURI.UserList.NoItemsVisibility%%">
27
  <td colspan="5">
28
- <em>@@SUCURI.NoData@@</em>
29
  </td>
30
  </tr>
31
 
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Successful Logins (all)</h3>
4
 
5
  <div class="inside">
6
+ <p>Here you can see a list of all the successful user logins.</p>
7
 
8
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-table-double-title sucuriscan-last-logins">
9
  <thead>
10
  <tr>
11
+ <th colspan="5">Successful Logins (all)</th>
12
  </tr>
13
 
14
  <tr>
15
+ <th class="manage-column">Username</th>
16
+ <th class="manage-column">IP Address</th>
17
+ <th class="manage-column">Hostname</th>
18
+ <th class="manage-column">Date/Time</th>
19
  <th class="manage-column">&nbsp;</th>
20
  </tr>
21
  </thead>
25
 
26
  <tr class="sucuriscan-%%SUCURI.UserList.NoItemsVisibility%%">
27
  <td colspan="5">
28
+ <em>no data available</em>
29
  </td>
30
  </tr>
31
 
inc/tpl/lastlogins-all.snippet.tpl CHANGED
@@ -8,5 +8,5 @@
8
 
9
  <td><span title="%%SUCURI.UserList.Datetime%%">%%SUCURI.UserList.TimeAgo%%</span></td>
10
 
11
- <td><a href="%%SUCURI.UserList.UserURL%%" target="_blank">@@SUCURI.Edit@@</a></td>
12
  </tr>
8
 
9
  <td><span title="%%SUCURI.UserList.Datetime%%">%%SUCURI.UserList.TimeAgo%%</span></td>
10
 
11
+ <td><a href="%%SUCURI.UserList.UserURL%%" target="_blank" rel="noopener">Edit</a></td>
12
  </tr>
inc/tpl/lastlogins-blockedusers.html.tpl CHANGED
@@ -1,16 +1,16 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.BlockedUsers@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.BlockedUsersInfo@@</p>
7
 
8
  <div class="sucuriscan-inline-alert-info">
9
- <p>@@SUCURI.BlockedUsersNote@@</p>
10
  </div>
11
 
12
  <div class="sucuriscan-inline-alert-error">
13
- <p>@@SUCURI.BlockedUsersByIP@@</p>
14
  </div>
15
 
16
  <form action="%%SUCURI.URL.Lastlogins%%#blocked" method="post">
@@ -23,10 +23,10 @@
23
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
24
  <input id="cb-select-all-1" type="checkbox">
25
  </td>
26
- <th class="manage-column">@@SUCURI.Username@@</th>
27
- <th class="manage-column">@@SUCURI.BlockedAt@@</th>
28
- <th class="manage-column">@@SUCURI.FirstAttempt@@</th>
29
- <th class="manage-column">@@SUCURI.LastAttempt@@</th>
30
  </tr>
31
  </thead>
32
 
@@ -35,13 +35,13 @@
35
 
36
  <tr class="sucuriscan-%%SUCURI.BlockedUsers.NoItemsVisibility%%">
37
  <td colspan="5">
38
- <em>@@SUCURI.NoData@@</em>
39
  </td>
40
  </tr>
41
  </tbody>
42
  </table>
43
 
44
- <button type="submit" class="button button-primary">@@SUCURI.Unblock@@</button>
45
  </form>
46
  </div>
47
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Blocked Users</h3>
4
 
5
  <div class="inside">
6
+ <p>Any attempt to authenticate an user account using the functions provided by WordPress will be intercepted and analyzed by the plugin, if the username coincides with any of the users in this list, the authentication process will be immediately stopped. These attemps will not be logged and no email alerts will be sent.</p>
7
 
8
  <div class="sucuriscan-inline-alert-info">
9
+ <p>Take in consideration that this is not a 100% bulletproof mechanism to block unwanted user authentications from malicious users. Depending on the configuration of your website, installed plugins, installed themes, and even the version of WordPress there might still be weak points that automated tools can take advantage of to brute force the user accounts registered in your website. <a target="_blank" href="https://sucuri.net/website-firewall/?wp=bu" rel="noopener">Install a firewall</a> to have full protection and mitigate this and a myriad of other attacks.</p>
10
  </div>
11
 
12
  <div class="sucuriscan-inline-alert-error">
13
+ <p>Blocking users per IP address is a feature provided by the <a href="https://sucuri.net/website-firewall/" target="_blank" rel="noopener">Sucuri Firewall</a>; to avoid the duplication of code and reduce the amount of false positives this feature will never be implemented in this plugin.</p>
14
  </div>
15
 
16
  <form action="%%SUCURI.URL.Lastlogins%%#blocked" method="post">
23
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
24
  <input id="cb-select-all-1" type="checkbox">
25
  </td>
26
+ <th class="manage-column">Username</th>
27
+ <th class="manage-column">Blocked At</th>
28
+ <th class="manage-column">First Attempt</th>
29
+ <th class="manage-column">Last Attempt</th>
30
  </tr>
31
  </thead>
32
 
35
 
36
  <tr class="sucuriscan-%%SUCURI.BlockedUsers.NoItemsVisibility%%">
37
  <td colspan="5">
38
+ <em>no data available</em>
39
  </td>
40
  </tr>
41
  </tbody>
42
  </table>
43
 
44
+ <button type="submit" class="button button-primary">Unblock</button>
45
  </form>
46
  </div>
47
  </div>
inc/tpl/lastlogins-failedlogins.html.tpl CHANGED
@@ -1,9 +1,9 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.FailedLogins@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.FailedLoginsInfo@@</p>
7
 
8
  <form action="%%SUCURI.URL.Lastlogins%%#blocked" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
@@ -15,11 +15,11 @@
15
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
16
  <input id="cb-select-all-1" type="checkbox">
17
  </td>
18
- <th class="manage-column">@@SUCURI.Username@@</th>
19
- <th class="manage-column">@@SUCURI.Password@@</th>
20
- <th class="manage-column">@@SUCURI.RemoteAddr@@</th>
21
- <th class="manage-column">@@SUCURI.Datetime@@</th>
22
- <th class="manage-column" width="300">@@SUCURI.Browser@@</th>
23
  </tr>
24
  </thead>
25
 
@@ -28,7 +28,7 @@
28
 
29
  <tr class="sucuriscan-%%SUCURI.FailedLogins.NoItemsVisibility%%">
30
  <td colspan="6">
31
- <em>@@SUCURI.NoData@@</em>
32
  </td>
33
  </tr>
34
 
@@ -42,7 +42,7 @@
42
  </tbody>
43
  </table>
44
 
45
- <button type="submit" class="button button-primary">@@SUCURI.Block@@</button>
46
  </form>
47
  </div>
48
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Failed logins</h3>
4
 
5
  <div class="inside">
6
+ <p>This information will be used to determine if your site is being victim of <a href="https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing" target="_blank" rel="noopener">Password Guessing Brute Force Attacks</a>. These logs will be accumulated and the plugin will send a report via email if there are more than <code>%%SUCURI.FailedLogins.MaxFailedLogins%%</code> failed login attempts during the same hour, you can change this number from <a href="%%SUCURI.URL.Settings%%#alerts">here</a>. <b>NOTE:</b> Some <em>"Two-Factor Authentication"</em> plugins do not follow the same rules that WordPress have to report failed login attempts, so you may not see all the attempts in this panel if you have one of these plugins installed.</p>
7
 
8
  <form action="%%SUCURI.URL.Lastlogins%%#blocked" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
15
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
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>
23
  </tr>
24
  </thead>
25
 
28
 
29
  <tr class="sucuriscan-%%SUCURI.FailedLogins.NoItemsVisibility%%">
30
  <td colspan="6">
31
+ <em>no data available</em>
32
  </td>
33
  </tr>
34
 
42
  </tbody>
43
  </table>
44
 
45
+ <button type="submit" class="button button-primary">Block</button>
46
  </form>
47
  </div>
48
  </div>
inc/tpl/lastlogins-loggedin.html.tpl CHANGED
@@ -1,22 +1,22 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.LoggedInUsers@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.LoggedInUsersInfo@@</p>
7
 
8
  <table class="wp-list-table widefat sucuriscan-loggedin-users">
9
  <thead>
10
  <tr>
11
- <th colspan="6">@@SUCURI.LoggedInUsers@@</th>
12
  </tr>
13
 
14
  <tr>
15
  <th>ID</th>
16
- <th>@@SUCURI.Username@@</th>
17
- <th>@@SUCURI.LastActivity@@</th>
18
- <th>@@SUCURI.Registered@@</th>
19
- <th>@@SUCURI.RemoteAddr@@</th>
20
  <th>&nbsp;</th>
21
  </tr>
22
  </thead>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Logged-in Users</h3>
4
 
5
  <div class="inside">
6
+ <p>Here you can see a list of the users that are currently logged-in.</p>
7
 
8
  <table class="wp-list-table widefat sucuriscan-loggedin-users">
9
  <thead>
10
  <tr>
11
+ <th colspan="6">Logged-in Users</th>
12
  </tr>
13
 
14
  <tr>
15
  <th>ID</th>
16
+ <th>Username</th>
17
+ <th>Last Activity</th>
18
+ <th>Registered</th>
19
+ <th>IP Address</th>
20
  <th>&nbsp;</th>
21
  </tr>
22
  </thead>
inc/tpl/lastlogins-loggedin.snippet.tpl CHANGED
@@ -10,5 +10,5 @@
10
 
11
  <td class="sucuriscan-monospace">%%SUCURI.LoggedInUsers.RemoteAddr%%</td>
12
 
13
- <td><a href="%%SUCURI.LoggedInUsers.UserURL%%" target="_blank">@@SUCURI.Edit@@</a></td>
14
  </tr>
10
 
11
  <td class="sucuriscan-monospace">%%SUCURI.LoggedInUsers.RemoteAddr%%</td>
12
 
13
+ <td><a href="%%SUCURI.LoggedInUsers.UserURL%%" target="_blank">Edit</a></td>
14
  </tr>
inc/tpl/lastlogins.html.tpl CHANGED
@@ -1,11 +1,11 @@
1
 
2
  <div class="sucuriscan-tabs">
3
  <ul class="sucuriscan-clearfix sucuriscan-tabs-buttons">
4
- <li><a href="%%SUCURI.URL.Lastlogins%%#allusers">@@SUCURI.AllUsers@@</a></li>
5
- <li><a href="%%SUCURI.URL.Lastlogins%%#admins">@@SUCURI.Admins@@</a></li>
6
- <li><a href="%%SUCURI.URL.Lastlogins%%#loggedin">@@SUCURI.LoggedInUsers@@</a></li>
7
- <li><a href="%%SUCURI.URL.Lastlogins%%#failed">@@SUCURI.FailedLogins@@</a></li>
8
- <li><a href="%%SUCURI.URL.Lastlogins%%#blocked">@@SUCURI.BlockedUsers@@</a></li>
9
  </ul>
10
 
11
  <div class="sucuriscan-tabs-containers">
1
 
2
  <div class="sucuriscan-tabs">
3
  <ul class="sucuriscan-clearfix sucuriscan-tabs-buttons">
4
+ <li><a href="%%SUCURI.URL.Lastlogins%%#allusers">All Users</a></li>
5
+ <li><a href="%%SUCURI.URL.Lastlogins%%#admins">Admins</a></li>
6
+ <li><a href="%%SUCURI.URL.Lastlogins%%#loggedin">Logged-in Users</a></li>
7
+ <li><a href="%%SUCURI.URL.Lastlogins%%#failed">Failed logins</a></li>
8
+ <li><a href="%%SUCURI.URL.Lastlogins%%#blocked">Blocked Users</a></li>
9
  </ul>
10
 
11
  <div class="sucuriscan-tabs-containers">
inc/tpl/notification-pretty.html.tpl CHANGED
@@ -14,14 +14,14 @@
14
  <tbody>
15
  <tr>
16
  <td style="padding:20px 20px 10px 20px;border:1px solid #ccc;border-top:none">
17
- <h4 style="text-transform:uppercase;margin:0">@@SUCURI.Information@@:</h4>
18
  <p style="margin:0 0 10px 0">
19
- @@SUCURI.Website@@: <a href="http://%%SUCURI.Website%%">%%SUCURI.Website%%</a><br>
20
- @@SUCURI.RemoteAddr@@: %%SUCURI.RemoteAddress%%<br>
21
- @@SUCURI.Datetime@@: %%SUCURI.Time%%<br>
22
  %%SUCURI.User%%
23
  </p>
24
- <h4 style="text-transform:uppercase;margin:0">@@SUCURI.Message@@:</h4>
25
  <p style="margin:0 0 10px 0">%%%SUCURI.Message%%%</p>
26
  </td>
27
  </tr>
14
  <tbody>
15
  <tr>
16
  <td style="padding:20px 20px 10px 20px;border:1px solid #ccc;border-top:none">
17
+ <h4 style="text-transform:uppercase;margin:0">Information:</h4>
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>
24
+ <h4 style="text-transform:uppercase;margin:0">Message:</h4>
25
  <p style="margin:0 0 10px 0">%%%SUCURI.Message%%%</p>
26
  </td>
27
  </tr>
inc/tpl/notification-simple.html.tpl CHANGED
@@ -1,8 +1,8 @@
1
 
2
- @@SUCURI.Event@@: %%SUCURI.Subject%%
3
- @@SUCURI.Website@@: http://%%SUCURI.Website%%
4
- @@SUCURI.RemoteAddr@@: %%SUCURI.RemoteAddress%%
5
- @@SUCURI.Datetime@@: %%SUCURI.Time%%
6
  %%SUCURI.User%%
7
 
8
- @@SUCURI.Message@@: %%SUCURI.Message%%
1
 
2
+ Event: %%SUCURI.Subject%%
3
+ Website: http://%%SUCURI.Website%%
4
+ IP Address: %%SUCURI.RemoteAddress%%
5
+ Date/Time: %%SUCURI.Time%%
6
  %%SUCURI.User%%
7
 
8
+ Message: %%SUCURI.Message%%
inc/tpl/register-site.html.tpl CHANGED
@@ -1,8 +1,8 @@
1
 
2
- <p>@@SUCURI.APIKeyExplanation@@</p>
3
 
4
  <div class="sucuriscan-inline-alert-info">
5
- <p>@@SUCURI.APIKeyHelp@@</p>
6
  </div>
7
 
8
  <form action="%%SUCURI.URL.Settings%%" method="post">
@@ -10,27 +10,27 @@
10
  <input type="hidden" name="sucuriscan_plugin_api_key" value="1" />
11
 
12
  <fieldset class="sucuriscan-clearfix">
13
- <label>@@SUCURI.Website@@:</label>
14
  <input type="text" value="%%SUCURI.CleanDomain%%" readonly="readonly">
15
  </fieldset>
16
 
17
  <fieldset class="sucuriscan-clearfix">
18
- <label>@@SUCURI.Email@@:</label>
19
  <select name="sucuriscan_setup_user">
20
  %%%SUCURI.AdminEmails%%%
21
  </select>
22
  </fieldset>
23
 
24
  <fieldset class="sucuriscan-clearfix">
25
- <label>@@SUCURI.DNSLookups@@</label>
26
  <input type="hidden" name="sucuriscan_dns_lookups" value="disable" />
27
  <input type="checkbox" name="sucuriscan_dns_lookups" value="enable" checked="checked" />
28
- <span class="sucuriscan-tooltip" content="@@SUCURI.DNSLookupsText@@">@@SUCURI.DNSLookupsLabel@@</span>
29
  </fieldset>
30
 
31
  <div class="sucuriscan-clearfix">
32
  <div class="sucuriscan-pull-left">
33
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
34
  </div>
35
  </div>
36
  </form>
1
 
2
+ <p>An API key is required to activate some additional tools available in this plugin. The keys are free and you can virtually generate an unlimited number of them as long as the domain name and email address are unique. The key is used to authenticate the HTTP requests sent by the plugin to a public API service managed by Sucuri Inc. Do not generate the key if you disagree with this.</p>
3
 
4
  <div class="sucuriscan-inline-alert-info">
5
+ <p>If you experience issues generating the API key you can request one sending the domain name and email address that you want to use to <a href="mailto:info@sucuri.net">info@sucuri.net</a>. Note generating a key for a website that is not facing the Internet is not possible because the API service needs to validate that the domain name exists, however, if you want to test the plugin in a development environment please contact us so we can generate the key manually.</p>
6
  </div>
7
 
8
  <form action="%%SUCURI.URL.Settings%%" method="post">
10
  <input type="hidden" name="sucuriscan_plugin_api_key" value="1" />
11
 
12
  <fieldset class="sucuriscan-clearfix">
13
+ <label>Website:</label>
14
  <input type="text" value="%%SUCURI.CleanDomain%%" readonly="readonly">
15
  </fieldset>
16
 
17
  <fieldset class="sucuriscan-clearfix">
18
+ <label>E-mail:</label>
19
  <select name="sucuriscan_setup_user">
20
  %%%SUCURI.AdminEmails%%%
21
  </select>
22
  </fieldset>
23
 
24
  <fieldset class="sucuriscan-clearfix">
25
+ <label>DNS Lookups</label>
26
  <input type="hidden" name="sucuriscan_dns_lookups" value="disable" />
27
  <input type="checkbox" name="sucuriscan_dns_lookups" value="enable" checked="checked" />
28
+ <span class="sucuriscan-tooltip" content="Check the box if your website is behind a known firewall service, this guarantees that the IP address of your visitors will be detected correctly for the security logs. You can change this later from the settings.">Enable DNS Lookups On Startup</span>
29
  </fieldset>
30
 
31
  <div class="sucuriscan-clearfix">
32
  <div class="sucuriscan-pull-left">
33
+ <button type="submit" class="button button-primary">Submit</button>
34
  </div>
35
  </div>
36
  </form>
inc/tpl/settings-alerts-bruteforce.html.tpl CHANGED
@@ -1,18 +1,18 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.PasswordAttack@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.PasswordAttackInfo@@</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
  <fieldset class="sucuriscan-clearfix">
11
- <label>@@SUCURI.PasswordAttackAfter@@:</label>
12
  <select name="sucuriscan_maximum_failed_logins">
13
  %%%SUCURI.Alerts.BruteForce%%%
14
  </select>
15
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
16
  </fieldset>
17
  </form>
18
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Password Guessing Brute Force Attacks</h3>
4
 
5
  <div class="inside">
6
+ <p><a href="https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing" target="_blank" rel="noopener">Password guessing brute force attacks</a> are very common against web sites and web servers. They are one of the most common vectors used to compromise web sites. The process is very simple and the attackers basically try multiple combinations of usernames and passwords until they find one that works. Once they get in, they can compromise the web site with malware, spam , phishing or anything else they want.</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
  <fieldset class="sucuriscan-clearfix">
11
+ <label>Consider Brute-Force Attack After:</label>
12
  <select name="sucuriscan_maximum_failed_logins">
13
  %%%SUCURI.Alerts.BruteForce%%%
14
  </select>
15
+ <button type="submit" class="button button-primary">Submit</button>
16
  </fieldset>
17
  </form>
18
  </div>
inc/tpl/settings-alerts-events.html.tpl CHANGED
@@ -1,10 +1,10 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.SecurityAlerts@@</h3>
4
 
5
  <div class="inside">
6
  <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.Alerts.NoAlertsVisibility%%">
7
- <p>@@SUCURI.NoAlertsError@@</p>
8
  </div>
9
 
10
  <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
@@ -17,7 +17,7 @@
17
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
18
  <input id="cb-select-all-1" type="checkbox">
19
  </td>
20
- <th class="manage-column">@@SUCURI.Event@@</th>
21
  </tr>
22
  </thead>
23
 
@@ -27,7 +27,7 @@
27
  </table>
28
 
29
  <div class="sucuriscan-recipient-form">
30
- <button type="submit" name="sucuriscan_save_alert_events" class="button button-primary">@@SUCURI.Submit@@</button>
31
  </div>
32
  </form>
33
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Security Alerts</h3>
4
 
5
  <div class="inside">
6
  <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.Alerts.NoAlertsVisibility%%">
7
+ <p>You have installed a plugin or theme that is not fully compatible with our plugin, some of the security alerts (like the successful and failed logins) will not be sent to you. To prevent an infinite loop while detecting these changes in the website and sending the email alerts via a custom SMTP plugin, we have decided to stop any attempt to send the emails to prevent fatal errors.</p>
8
  </div>
9
 
10
  <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
17
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
18
  <input id="cb-select-all-1" type="checkbox">
19
  </td>
20
+ <th class="manage-column">Event</th>
21
  </tr>
22
  </thead>
23
 
27
  </table>
28
 
29
  <div class="sucuriscan-recipient-form">
30
+ <button type="submit" name="sucuriscan_save_alert_events" class="button button-primary">Submit</button>
31
  </div>
32
  </form>
33
  </div>
inc/tpl/settings-alerts-ignore-posts.html.tpl CHANGED
@@ -1,42 +1,50 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.PostTypeAlerts@@</h3>
4
 
5
  <div class="inside">
6
- <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.IgnoreRules.ErrorVisibility%%">
7
- <p>@@SUCURI.PostTypeAlertsDisabled@@</p>
8
  </div>
9
 
10
- <p>@@SUCURI.PostTypeAlertsInfo@@</p>
11
-
12
- <p>@@SUCURI.PostTypeAlertsInvisible@@</p>
13
 
14
  <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
15
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
16
  <input type="hidden" name="sucuriscan_ignorerule_action" value="add">
17
 
18
  <fieldset class="sucuriscan-clearfix">
19
- <label>@@SUCURI.PostTypeAlertsStop@@:</label>
20
  <input type="text" name="sucuriscan_ignorerule" placeholder="e.g. unique_post_type_id" />
21
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
22
  </fieldset>
23
  </form>
24
 
25
  <hr>
26
 
27
- <table class="wp-list-table widefat sucuriscan-table sucuriscan-settings-ignorerules">
28
- <thead>
29
- <tr>
30
- <th>@@SUCURI.IgnoredAt@@</th>
31
- <th>@@SUCURI.Ignored@@</th>
32
- <th>@@SUCURI.PostType@@</th>
33
- <th>&nbsp;</th>
34
- </tr>
35
- </thead>
36
-
37
- <tbody>
38
- %%%SUCURI.IgnoreRules.PostTypes%%%
39
- </tbody>
40
- </table>
 
 
 
 
 
 
 
 
 
 
41
  </div>
42
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Post-Type Alerts</h3>
4
 
5
  <div class="inside">
6
+ <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.PostTypes.ErrorVisibility%%">
7
+ <p>It seems that you disabled the email alerts for <b>new site content</b>, this panel is intended to provide a way to ignore specific events in your site and with that the alerts reported to your email. Since you have deactivated the <b>new site content</b> alerts, this panel will be disabled too.</p>
8
  </div>
9
 
10
+ <p>This is a list of registered <a href="https://codex.wordpress.org/Post_Types" target="_blank" rel="noopener">Post Types</a>. You will receive an email alert when a custom page or post associated to any of these types is created or updated. If you don't want to receive one or more of these alerts, feel free to uncheck the boxes in the table below. If you are receiving alerts for post types that are not listed in this table, it may be because there is an add-on that that is generating a custom post-type on runtime, you will have to find out by yourself what is the unique ID of that post-type and type it in the form below. The plugin will do its best to ignore these alerts as long as the unique ID is valid.</p>
 
 
11
 
12
  <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
13
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
14
  <input type="hidden" name="sucuriscan_ignorerule_action" value="add">
15
 
16
  <fieldset class="sucuriscan-clearfix">
17
+ <label>Stop Alerts For This Post-Type:</label>
18
  <input type="text" name="sucuriscan_ignorerule" placeholder="e.g. unique_post_type_id" />
19
+ <button type="submit" class="button button-primary">Submit</button>
20
  </fieldset>
21
  </form>
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>
inc/tpl/settings-alerts-ignore-posts.snippet.tpl CHANGED
@@ -1,17 +1,16 @@
1
 
2
  <tr>
3
- <td><em class="sucuriscan-monospace">%%SUCURI.IgnoreRules.WasIgnoredAt%%</em></td>
 
 
4
 
5
- <td><span class="sucuriscan-label-%%SUCURI.IgnoreRules.IsIgnoredClass%%">%%SUCURI.IgnoreRules.IsIgnored%%</span></td>
6
 
7
- <td>%%SUCURI.IgnoreRules.PostTypeTitle%%</td>
 
 
8
 
9
- <td class="td-with-button">
10
- <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
11
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
12
- <input type="hidden" name="sucuriscan_ignorerule" value="%%SUCURI.IgnoreRules.PostType%%" />
13
- <input type="hidden" name="sucuriscan_ignorerule_action" value="%%SUCURI.IgnoreRules.Action%%" />
14
- <button type="submit" class="button button-secondary">%%SUCURI.IgnoreRules.ButtonText%%</button>
15
- </form>
16
  </td>
17
  </tr>
1
 
2
  <tr>
3
+ <th class="check-column">
4
+ <input type="checkbox" name="sucuriscan_posttypes[]" %%SUCURI.PostTypes.Selected%% value="%%SUCURI.PostTypes.UniqueID%%" />
5
+ </th>
6
 
7
+ <td>%%SUCURI.PostTypes.Title%%</td>
8
 
9
+ <td>
10
+ <span class="sucuriscan-monospace">%%SUCURI.PostTypes.UniqueID%%</span>
11
+ </td>
12
 
13
+ <td>
14
+ <em class="sucuriscan-monospace">%%SUCURI.PostTypes.IgnoredAt%%</em>
 
 
 
 
 
15
  </td>
16
  </tr>
inc/tpl/settings-alerts-perhour.html.tpl CHANGED
@@ -1,18 +1,18 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.AlertsPerHour@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.AlertsPerHourInfo@@</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
  <fieldset class="sucuriscan-clearfix">
11
- <label>@@SUCURI.AlertsPerHourMaximum@@:</label>
12
  <select name="sucuriscan_emails_per_hour">
13
  %%%SUCURI.Alerts.PerHour%%%
14
  </select>
15
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
16
  </fieldset>
17
  </form>
18
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Alerts Per Hour</h3>
4
 
5
  <div class="inside">
6
+ <p>Configure the maximum number of email alerts per hour. If the number is exceeded and the plugin detects more events during the same hour, it will still log the events into the audit logs but will not send the email alerts. Be careful with this as you will miss important information.</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
  <fieldset class="sucuriscan-clearfix">
11
+ <label>Maximum Alerts Per Hour:</label>
12
  <select name="sucuriscan_emails_per_hour">
13
  %%%SUCURI.Alerts.PerHour%%%
14
  </select>
15
+ <button type="submit" class="button button-primary">Submit</button>
16
  </fieldset>
17
  </form>
18
  </div>
inc/tpl/settings-alerts-recipients.html.tpl CHANGED
@@ -1,17 +1,17 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.AlertsRecipient@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.AlertsRecipientInfo@@</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
 
11
  <fieldset class="sucuriscan-clearfix">
12
- <label>@@SUCURI.Email@@:</label>
13
  <input type="text" name="sucuriscan_recipient" placeholder="e.g. user@example.com" />
14
- <button type="submit" name="sucuriscan_save_recipient" class="button button-primary">@@SUCURI.Submit@@</button>
15
  </fieldset>
16
 
17
  <table class="wp-list-table widefat sucuriscan-table">
@@ -21,7 +21,7 @@
21
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
22
  <input id="cb-select-all-1" type="checkbox">
23
  </td>
24
- <th class="manage-column">@@SUCURI.Email@@</th>
25
  </tr>
26
  </thead>
27
 
@@ -30,8 +30,8 @@
30
  </tbody>
31
  </table>
32
 
33
- <button type="submit" name="sucuriscan_delete_recipients" class="button button-primary">@@SUCURI.Delete@@</button>
34
- <button type="submit" name="sucuriscan_debug_email" value="1" class="button button-primary">@@SUCURI.TestAlerts@@</button>
35
  </form>
36
  </div>
37
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Alerts Recipient</h3>
4
 
5
  <div class="inside">
6
+ <p>By default, the plugin will send the email alerts to the primary admin account, the same account created during the installation of WordPress in your web server. You can add more people to the list, they will receive a copy of the same security alerts.</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
 
11
  <fieldset class="sucuriscan-clearfix">
12
+ <label>E-mail:</label>
13
  <input type="text" name="sucuriscan_recipient" placeholder="e.g. user@example.com" />
14
+ <button type="submit" name="sucuriscan_save_recipient" class="button button-primary">Submit</button>
15
  </fieldset>
16
 
17
  <table class="wp-list-table widefat sucuriscan-table">
21
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
22
  <input id="cb-select-all-1" type="checkbox">
23
  </td>
24
+ <th class="manage-column">E-mail</th>
25
  </tr>
26
  </thead>
27
 
30
  </tbody>
31
  </table>
32
 
33
+ <button type="submit" name="sucuriscan_delete_recipients" class="button button-primary">Delete</button>
34
+ <button type="submit" name="sucuriscan_debug_email" value="1" class="button button-primary">Test Alerts</button>
35
  </form>
36
  </div>
37
  </div>
inc/tpl/settings-alerts-subject.html.tpl CHANGED
@@ -1,9 +1,9 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.AlertsSubject@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.AlertsSubjectInfo@@</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
@@ -14,14 +14,14 @@
14
  <li>
15
  <label>
16
  <input type="radio" name="sucuriscan_email_subject" value="custom" %%SUCURI.Alerts.CustomChecked%% />
17
- <span>@@SUCURI.CustomFormat@@</span>
18
  <input type="text" name="sucuriscan_custom_email_subject" value="%%SUCURI.Alerts.CustomValue%%" />
19
  </label>
20
  </li>
21
  </ul>
22
 
23
  <div class="sucuriscan-recipient-form">
24
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
25
  </div>
26
  </form>
27
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Alert Subject</h3>
4
 
5
  <div class="inside">
6
+ <p>Format of the subject for the email alerts, by default the plugin will use the website name and the event identifier that is being reported, you can use this panel to include the IP address of the user that triggered the event and some additional data. You can create filters in your email client creating a custom email subject using the pseudo-tags shown below.</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#alerts" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
14
  <li>
15
  <label>
16
  <input type="radio" name="sucuriscan_email_subject" value="custom" %%SUCURI.Alerts.CustomChecked%% />
17
+ <span>Custom Format</span>
18
  <input type="text" name="sucuriscan_custom_email_subject" value="%%SUCURI.Alerts.CustomValue%%" />
19
  </label>
20
  </li>
21
  </ul>
22
 
23
  <div class="sucuriscan-recipient-form">
24
+ <button type="submit" class="button button-primary">Submit</button>
25
  </div>
26
  </form>
27
  </div>
inc/tpl/settings-alerts-trustedips.html.tpl CHANGED
@@ -1,17 +1,17 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.TrustedIPs@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.TrustedIPsInfo@@</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#alerts" method="POST">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
 
11
  <fieldset class="sucuriscan-clearfix">
12
- <label>@@SUCURI.RemoteAddr@@:</label>
13
  <input type="text" name="sucuriscan_trust_ip" placeholder="e.g. 182.120.56.0/24" />
14
- <input type="submit" value="@@SUCURI.Submit@@" class="button button-primary" />
15
  </fieldset>
16
  </form>
17
 
@@ -27,9 +27,9 @@
27
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
28
  <input id="cb-select-all-1" type="checkbox">
29
  </td>
30
- <th class="manage-column">@@SUCURI.RemoteAddr@@</th>
31
- <th class="manage-column">@@SUCURI.CIDRFormat@@</th>
32
- <th class="manage-column">@@SUCURI.IPAddedAt@@</th>
33
  </tr>
34
  </thead>
35
 
@@ -38,13 +38,13 @@
38
 
39
  <tr class="sucuriscan-%%SUCURI.TrustedIPs.NoItems.Visibility%%">
40
  <td colspan="4">
41
- <em>@@SUCURI.NoData@@</em>
42
  </td>
43
  </tr>
44
  </tbody>
45
  </table>
46
 
47
- <button type="submit" class="button button-primary">@@SUCURI.Delete@@</button>
48
  </form>
49
  </div>
50
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Trusted IP Addresses</h3>
4
 
5
  <div class="inside">
6
+ <p>If you are working in a LAN <em>(Local Area Network)</em> you may want to include the IP addresses of all the nodes in the subnet, this will force the plugin to stop sending email alerts about actions executed from trusted IP addresses. Use the CIDR <em>(Classless Inter Domain Routing)</em> format to specify ranges of IP addresses <em>(only 8, 16, and 24)</em>.</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#alerts" method="POST">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
 
11
  <fieldset class="sucuriscan-clearfix">
12
+ <label>IP Address:</label>
13
  <input type="text" name="sucuriscan_trust_ip" placeholder="e.g. 182.120.56.0/24" />
14
+ <input type="submit" value="Submit" class="button button-primary" />
15
  </fieldset>
16
  </form>
17
 
27
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
28
  <input id="cb-select-all-1" type="checkbox">
29
  </td>
30
+ <th class="manage-column">IP Address</th>
31
+ <th class="manage-column">CIDR Format</th>
32
+ <th class="manage-column">IP Added At</th>
33
  </tr>
34
  </thead>
35
 
38
 
39
  <tr class="sucuriscan-%%SUCURI.TrustedIPs.NoItems.Visibility%%">
40
  <td colspan="4">
41
+ <em>no data available</em>
42
  </td>
43
  </tr>
44
  </tbody>
45
  </table>
46
 
47
+ <button type="submit" class="button button-primary">Delete</button>
48
  </form>
49
  </div>
50
  </div>
inc/tpl/settings-apirecovery.html.tpl CHANGED
@@ -1,15 +1,15 @@
1
 
2
  <div class="sucuriscan-clearfix">
3
- <p>@@SUCURI.APIKeyRecoveryExplanation@@</p>
4
 
5
- <p>@@SUCURI.APIKeyRecoveryPossibleFailures@@</p>
6
 
7
  <form action="%%SUCURI.URL.Settings%%" method="post">
8
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
9
  <fieldset class="sucuriscan-clearfix">
10
- <label>@@SUCURI.APIKey@@:</label>
11
  <input type="text" name="sucuriscan_manual_api_key" />
12
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
13
  </fieldset>
14
  </form>
15
  </div>
1
 
2
  <div class="sucuriscan-clearfix">
3
+ <p>If this operation was successful you will receive a message in the email used during the registration of the API key <em>(usually the email of the main admin user)</em>. This message contains the key in plain text, copy and paste the key in the form field below. The plugin will verify the authenticity of the key sending an initial HTTP request to the API service, if this fails the key will be removed automatically and you will have to start the process all over again.</p>
4
 
5
+ <p>There are cases where this operation may fail, an example would be when the email address is not associated with the domain anymore, this happens when the base URL changes <em>(from www to none or viceversa)</em>. If you are having issues recovering the key please send an email explaining the situation to <a href="mailto:info@sucuri.net">info@sucuri.net</a></p>
6
 
7
  <form action="%%SUCURI.URL.Settings%%" method="post">
8
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
9
  <fieldset class="sucuriscan-clearfix">
10
+ <label>API Key:</label>
11
  <input type="text" name="sucuriscan_manual_api_key" />
12
+ <button type="submit" class="button button-primary">Submit</button>
13
  </fieldset>
14
  </form>
15
  </div>
inc/tpl/settings-apiregistered.html.tpl CHANGED
@@ -3,13 +3,13 @@
3
  <div class="sucuriscan-pull-left sucuriscan-sitelogo">&nbsp;</div>
4
 
5
  <div class="sucuriscan-pull-right">
6
- <p>@@SUCURI.APIKeyGenerated@@</p>
7
 
8
  <div class="sucuriscan-inline-alert-success">
9
- <p>@@SUCURI.APIKeyContinueSetup@@</p>
10
  </div>
11
 
12
- <a href="%%SUCURI.URL.Dashboard%%" class="button button-primary">@@SUCURI.Dashboard@@</a>
13
- <a href="%%SUCURI.URL.Settings%%" class="button button-primary">@@SUCURI.Settings@@</a>
14
  </div>
15
  </div>
3
  <div class="sucuriscan-pull-left sucuriscan-sitelogo">&nbsp;</div>
4
 
5
  <div class="sucuriscan-pull-right">
6
+ <p>Congratulations! The rest of the features available in the plugin have been enabled. This product is designed to supplement existing security products. It's not a silver bullet for your security needs, but it'll give you greater security awareness and better posture, all with the intent of reducing risk.</p>
7
 
8
  <div class="sucuriscan-inline-alert-success">
9
+ <p>Your website has been granted a new API key and it was associated to the email address that you chose during the registration process. You can use the same email to recover the key if you happen to lose it sometime. We encourage you to check the rest of the settings page and configure the plugin to your own needs.</p>
10
  </div>
11
 
12
+ <a href="%%SUCURI.URL.Dashboard%%" class="button button-primary">Dashboard</a>
13
+ <a href="%%SUCURI.URL.Settings%%" class="button button-primary">Settings</a>
14
  </div>
15
  </div>
inc/tpl/settings-apiservice-checksums.html.tpl CHANGED
@@ -1,12 +1,12 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.ChecksumsAPI@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.ChecksumsAPIInfo@@</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
9
- <span>@@SUCURI.ChecksumsAPI@@ &mdash; <a target="_blank"
10
  href="%%SUCURI.ChecksumsAPI%%">%%SUCURI.ChecksumsAPI%%</a>
11
  </span>
12
  </div>
@@ -14,9 +14,9 @@
14
  <form action="%%SUCURI.URL.Settings%%#apiservice" method="post">
15
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
16
  <fieldset class="sucuriscan-clearfix">
17
- <label>@@SUCURI.ChecksumsAPI@@:</label>
18
  <input type="text" name="sucuriscan_checksum_api" placeholder="e.g. URL — or — user/repo" size="30" />
19
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
20
  </fieldset>
21
  </form>
22
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">WordPress Checksums API</h3>
4
 
5
  <div class="inside">
6
+ <p>The WordPress integrity tool uses a remote API service maintained by the WordPress organization to determine which files in the installation were added, removed or modified. The API returns a list of files with their respective checksums, this information guarantees that the installation is not corrupt. You can, however, point the integrity tool to a GitHub repository in case that you are using a custom version of WordPress like the <a href="https://github.com/WordPress/WordPress" target="_blank" rel="noopener">development version of the code</a>.</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
9
+ <span>WordPress Checksums API &mdash; <a target="_blank"
10
  href="%%SUCURI.ChecksumsAPI%%">%%SUCURI.ChecksumsAPI%%</a>
11
  </span>
12
  </div>
14
  <form action="%%SUCURI.URL.Settings%%#apiservice" method="post">
15
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
16
  <fieldset class="sucuriscan-clearfix">
17
+ <label>WordPress Checksums API:</label>
18
  <input type="text" name="sucuriscan_checksum_api" placeholder="e.g. URL — or — user/repo" size="30" />
19
+ <button type="submit" class="button button-primary">Submit</button>
20
  </fieldset>
21
  </form>
22
  </div>
inc/tpl/settings-apiservice-proxy.html.tpl CHANGED
@@ -1,15 +1,15 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.APIViaProxy@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.APIViaProxyInfo@@</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-monospace">
9
- <div>@@SUCURI.ProxyHostname@@: %%SUCURI.APIProxy.Host%%</div>
10
- <div>@@SUCURI.ProxyPort@@: %%SUCURI.APIProxy.Port%%</div>
11
- <div>@@SUCURI.ProxyUsername@@: %%SUCURI.APIProxy.Username%%</div>
12
- <div>@@SUCURI.ProxyPassword@@: <span class="sucuriscan-label-%%SUCURI.APIProxy.PasswordType%%">%%SUCURI.APIProxy.PasswordText%%</span></div>
13
  </div>
14
  </div>
15
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">API Communication via Proxy</h3>
4
 
5
  <div class="inside">
6
+ <p>All the HTTP requests used to communicate with the API service are being sent using the WordPress built-in functions, so (almost) all its official features are inherited, this is useful if you need to pass these HTTP requests through a proxy. According to the <a href="https://developer.wordpress.org/reference/classes/wp_http_proxy/" target="_blank" rel="noopener">official documentation</a> you have to add some constants to the main configuration file: <em>WP_PROXY_HOST, WP_PROXY_PORT, WP_PROXY_USERNAME, WP_PROXY_PASSWORD</em>.</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-monospace">
9
+ <div>HTTP Proxy Hostname: %%SUCURI.APIProxy.Host%%</div>
10
+ <div>HTTP Proxy Port num: %%SUCURI.APIProxy.Port%%</div>
11
+ <div>HTTP Proxy Username: %%SUCURI.APIProxy.Username%%</div>
12
+ <div>HTTP Proxy Password: <span class="sucuriscan-label-%%SUCURI.APIProxy.PasswordType%%">%%SUCURI.APIProxy.PasswordText%%</span></div>
13
  </div>
14
  </div>
15
  </div>
inc/tpl/settings-apiservice-status.html.tpl CHANGED
@@ -1,16 +1,16 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.APICommunication@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.APICommunicationInfo@@</p>
7
 
8
  <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.ApiStatus.ErrorVisibility%%">
9
- <p>@@SUCURI.APICommunicationDisabled@@</p>
10
  </div>
11
 
12
  <div class="sucuriscan-hstatus sucuriscan-hstatus-%%SUCURI.ApiStatus.StatusNum%%">
13
- <span>@@SUCURI.APICommunication@@ &mdash; %%SUCURI.ApiStatus.Status%% &mdash;</span>
14
  <span class="sucuriscan-monospace">%%SUCURI.ApiStatus.ServiceURL%%</span>
15
  <form action="%%SUCURI.URL.Settings%%#apiservice" method="post">
16
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">API Service Communication</h3>
4
 
5
  <div class="inside">
6
+ <p>Once the API key is generate the plugin will communicate with a remote API service that will act as a safe data storage for the audit logs generated when the website triggers certain events that the plugin monitors. If the website is hacked the attacker will not have access to these logs and that way you can investigate what was modified <em>(for malware infaction)</em> and/or how the malicious person was able to gain access to the website.</p>
7
 
8
  <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.ApiStatus.ErrorVisibility%%">
9
+ <p>Disabling the API service communication will stop the event monitoring, consider to enable the <a href="%%SUCURI.URL.Settings%%#general">Log Exporter</a> to keep the monitoring working while the HTTP requests are ignored, otherwise an attacker may execute an action that will not be registered in the security logs and you will not have a way to investigate the attack in the future.</p>
10
  </div>
11
 
12
  <div class="sucuriscan-hstatus sucuriscan-hstatus-%%SUCURI.ApiStatus.StatusNum%%">
13
+ <span>API Service Communication &mdash; %%SUCURI.ApiStatus.Status%% &mdash;</span>
14
  <span class="sucuriscan-monospace">%%SUCURI.ApiStatus.ServiceURL%%</span>
15
  <form action="%%SUCURI.URL.Settings%%#apiservice" method="post">
16
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
inc/tpl/settings-general-apikey.html.tpl CHANGED
@@ -4,36 +4,36 @@
4
  %%%SUCURI.ModalForApiKeyRecovery%%%
5
 
6
  <div class="sucuriscan-panel">
7
- <h3 class="sucuriscan-title">@@SUCURI.APIKey@@</h3>
8
 
9
  <div class="inside">
10
- <p>@@SUCURI.APIKeyInfo@@</p>
11
 
12
  <div class="sucuriscan-inline-alert-info">
13
- <p>@@SUCURI.APIKeyTerms@@</p>
14
  </div>
15
 
16
  <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.InvalidDomainVisibility%%">
17
- <p>@@SUCURI.APIKeyInvalidDomain@@</p>
18
  </div>
19
 
20
  <div class="sucuriscan-%%SUCURI.APIKey.RecoverVisibility%%">
21
  <div class="sucuriscan-hstatus sucuriscan-hstatus-0">
22
- <div class="sucuriscan-monospace">@@SUCURI.APIKey@@: %%SUCURI.APIKey%%</div>
23
  <form action="%%SUCURI.URL.Settings%%" method="post">
24
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
25
- <button type="submit" name="sucuriscan_recover_key" class="button button-primary">@@SUCURI.APIKeyRecoverButton@@</button>
26
  </form>
27
  </div>
28
 
29
- <p>@@SUCURI.APIKeyRecoveryCondition@@</p>
30
  </div>
31
 
32
  <div class="sucuriscan-hstatus sucuriscan-hstatus-1 sucuriscan-%%SUCURI.APIKey.RemoveVisibility%%">
33
- <div class="sucuriscan-monospace">@@SUCURI.APIKey@@: %%SUCURI.APIKey%%</div>
34
  <form action="%%SUCURI.URL.Settings%%" method="post">
35
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
36
- <button type="submit" name="sucuriscan_remove_api_key" class="button button-primary">@@SUCURI.Delete@@</button>
37
  </form>
38
  </div>
39
  </div>
4
  %%%SUCURI.ModalForApiKeyRecovery%%%
5
 
6
  <div class="sucuriscan-panel">
7
+ <h3 class="sucuriscan-title">API Key</h3>
8
 
9
  <div class="inside">
10
+ <p>Most of the tools in this plugin can be used without a specific configuration, but the core features <b>require an API key</b> to communicate with the Sucuri services. The key is generated using your administrator e-mail and the domain of this site, this will allow you to have access to our free monitoring tool and other extra features.</p>
11
 
12
  <div class="sucuriscan-inline-alert-info">
13
+ <p>Generating an API key implies that you agree to send the information collected by the plugin to the Sucuri API service which is a remote server where the information for the audit logs is stored, this is to prevent malicious users to delete the logs during an attack which may affect an investigation if you suspect that your website was hacked. We also use this information to display <a href="https://sucuri.net/security-reports/brute-force/" target="_blank" rel="noopener">statistics</a> and try to use the data in an anonymous way as we are concerned about your privacy too. Please do not generate an API key if you do not agree with this, you can keep using the plugin without it anyway.</p>
14
  </div>
15
 
16
  <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.InvalidDomainVisibility%%">
17
+ <p>Your domain <code>%%SUCURI.CleanDomain%%</code> does not seems to have a DNS <code>A</code> record so it will be considered as <em>invalid</em> by the API interface when you request the generation of a new key. Adding <code>www</code> at the beginning of the domain name may fix this issue. If you do not understand what is this then send an email to our support team requesting the key.</p>
18
  </div>
19
 
20
  <div class="sucuriscan-%%SUCURI.APIKey.RecoverVisibility%%">
21
  <div class="sucuriscan-hstatus sucuriscan-hstatus-0">
22
+ <div class="sucuriscan-monospace">API Key: %%SUCURI.APIKey%%</div>
23
  <form action="%%SUCURI.URL.Settings%%" method="post">
24
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
25
+ <button type="submit" name="sucuriscan_recover_key" class="button button-primary">Recover Via E-mail</button>
26
  </form>
27
  </div>
28
 
29
+ <p>If you don't have access to the e-mail address used to generate the API key, but have a copy of the key at hand you can <a target="_self" href="%%SUCURI.URL.Settings%%&recover">click this link</a> to activate the plugin manually. Be aware that if the key is invalid the plugin will delete it afterwards.</p>
30
  </div>
31
 
32
  <div class="sucuriscan-hstatus sucuriscan-hstatus-1 sucuriscan-%%SUCURI.APIKey.RemoveVisibility%%">
33
+ <div class="sucuriscan-monospace">API Key: %%SUCURI.APIKey%%</div>
34
  <form action="%%SUCURI.URL.Settings%%" method="post">
35
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
36
+ <button type="submit" name="sucuriscan_remove_api_key" class="button button-primary">Delete</button>
37
  </form>
38
  </div>
39
  </div>
inc/tpl/settings-general-datastorage.html.tpl CHANGED
@@ -1,9 +1,9 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.DataStorage@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.DataStorageInfo@@</p>
7
  </div>
8
 
9
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
@@ -21,10 +21,10 @@
21
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
22
  <input id="cb-select-all-1" type="checkbox">
23
  </td>
24
- <th class="manage-column">@@SUCURI.FilePath@@</th>
25
- <th class="manage-column">@@SUCURI.FileSize@@</th>
26
- <th class="manage-column">@@SUCURI.Status@@</th>
27
- <th class="manage-column">@@SUCURI.Writable@@</th>
28
  </tr>
29
  </thead>
30
 
@@ -34,7 +34,7 @@
34
  </table>
35
 
36
  <p>
37
- <button type="submit" class="button button-primary">@@SUCURI.Delete@@</button>
38
  </p>
39
  </form>
40
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Data Storage</h3>
4
 
5
  <div class="inside">
6
+ <p>This is the directory where the plugin will store the security logs, the list of files marked as fixed in the core integrity tool, the cache for the malware scanner and 3rd-party plugin metadata. The plugin requires write permissions in this directory as well as the files contained in it. If you prefer to keep these files in a non-public directory <em>(one level up the document root)</em> please define a constant in the <em>"wp-config.php"</em> file named <em>"SUCURI_DATA_STORAGE"</em> with the absolute path to the new directory.</p>
7
  </div>
8
 
9
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
21
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
22
  <input id="cb-select-all-1" type="checkbox">
23
  </td>
24
+ <th class="manage-column">File Path</th>
25
+ <th class="manage-column">File Size</th>
26
+ <th class="manage-column">Status</th>
27
+ <th class="manage-column">Writable</th>
28
  </tr>
29
  </thead>
30
 
34
  </table>
35
 
36
  <p>
37
+ <button type="submit" class="button button-primary">Delete</button>
38
  </p>
39
  </form>
40
  </div>
inc/tpl/settings-general-importexport.html.tpl CHANGED
@@ -1,12 +1,12 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.ImportExport@@</h3>
4
 
5
  <div class="inside">
6
  <form action="%%SUCURI.URL.Settings%%" method="post">
7
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
8
 
9
- <p>@@SUCURI.ImportExportInfo@@</p>
10
 
11
  <textarea name="sucuriscan_settings" class="sucuriscan-full-textarea sucuriscan-monospace">%%SUCURI.Export%%</textarea>
12
 
@@ -14,11 +14,11 @@
14
  <label>
15
  <input type="hidden" name="sucuriscan_process_form" value="0" />
16
  <input type="checkbox" name="sucuriscan_process_form" value="1" />
17
- <span>@@SUCURI.UnderstandTheRisk@@</span>
18
  </label>
19
  </p>
20
 
21
- <button type="submit" name="sucuriscan_import" class="button button-primary">@@SUCURI.Submit@@</button>
22
  </form>
23
  </div>
24
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Import &amp; Export Settings</h3>
4
 
5
  <div class="inside">
6
  <form action="%%SUCURI.URL.Settings%%" method="post">
7
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
8
 
9
+ <p>Copy the JSON-encoded data from the box below, go to your other websites and click the <em>"Import"</em> button in the settings page. The plugin will start using the same settings from this website. Notice that some options are omitted as they contain values specific to this website. To import the settings from another website into this one, replace the JSON-encoded data in the box below with the JSON-encoded data exported from the other website, then click the button <em>"Import"</em>. Notice that some options will not be imported to reduce the security risk of writing arbitrary data into the disk.</p>
10
 
11
  <textarea name="sucuriscan_settings" class="sucuriscan-full-textarea sucuriscan-monospace">%%SUCURI.Export%%</textarea>
12
 
14
  <label>
15
  <input type="hidden" name="sucuriscan_process_form" value="0" />
16
  <input type="checkbox" name="sucuriscan_process_form" value="1" />
17
+ <span>I understand that this operation can not be reverted.</span>
18
  </label>
19
  </p>
20
 
21
+ <button type="submit" name="sucuriscan_import" class="button button-primary">Submit</button>
22
  </form>
23
  </div>
24
  </div>
inc/tpl/settings-general-ipdiscoverer.html.tpl CHANGED
@@ -1,12 +1,12 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.IPDiscoverer@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.IPDiscovererInfo@@</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
9
- <span>@@SUCURI.IPDiscoverer@@ &mdash; %%SUCURI.DnsLookupsStatus%%</span>
10
 
11
  <form action="%%SUCURI.URL.Settings%%" method="post">
12
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
@@ -19,7 +19,7 @@
19
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
20
 
21
  <fieldset class="sucuriscan-clearfix">
22
- <label>@@SUCURI.HTTPHeader@@:</label>
23
  <select name="sucuriscan_addr_header">
24
  %%%SUCURI.AddrHeaderOptions%%%
25
  </select>
@@ -28,11 +28,11 @@
28
 
29
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-monospace">
30
  <div>Sucuri Firewall &mdash; %%SUCURI.IsUsingFirewall%%</div>
31
- <div>@@SUCURI.Website@@: %%SUCURI.WebsiteURL%%</div>
32
  <div>Top Level Domain: %%SUCURI.TopLevelDomain%%</div>
33
- <div>@@SUCURI.Hostname@@: %%SUCURI.WebsiteHostName%%</div>
34
- <div>@@SUCURI.RemoteAddr@@ (@@SUCURI.Hostname@@): %%SUCURI.WebsiteHostAddress%%</div>
35
- <div>@@SUCURI.RemoteAddr@@ (@@SUCURI.Username@@): %%SUCURI.RemoteAddress%% (%%SUCURI.RemoteAddressHeader%%)</div>
36
  </div>
37
  </form>
38
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">IP Address Discoverer</h3>
4
 
5
  <div class="inside">
6
+ <p>IP address discoverer will use DNS lookups to automatically detect if the website is behind the <a href="https://sucuri.net/website-firewall/" target="_blank" rel="noopener">Sucuri Firewall</a> in which case will modify the global server variable <em>Remote-Addr</em> to set the real IP of the website's visitors. This check runs on every WordPress init action and that is why it may slow down your website as some hosting providers rely on slow DNS servers which makes the operation take more time than it should.</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
9
+ <span>IP Address Discoverer &mdash; %%SUCURI.DnsLookupsStatus%%</span>
10
 
11
  <form action="%%SUCURI.URL.Settings%%" method="post">
12
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
19
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
20
 
21
  <fieldset class="sucuriscan-clearfix">
22
+ <label>HTTP Header:</label>
23
  <select name="sucuriscan_addr_header">
24
  %%%SUCURI.AddrHeaderOptions%%%
25
  </select>
28
 
29
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-monospace">
30
  <div>Sucuri Firewall &mdash; %%SUCURI.IsUsingFirewall%%</div>
31
+ <div>Website: %%SUCURI.WebsiteURL%%</div>
32
  <div>Top Level Domain: %%SUCURI.TopLevelDomain%%</div>
33
+ <div>Hostname: %%SUCURI.WebsiteHostName%%</div>
34
+ <div>IP Address (Hostname): %%SUCURI.WebsiteHostAddress%%</div>
35
+ <div>IP Address (Username): %%SUCURI.RemoteAddress%% (%%SUCURI.RemoteAddressHeader%%)</div>
36
  </div>
37
  </form>
38
  </div>
inc/tpl/settings-general-resetoptions.html.tpl CHANGED
@@ -1,9 +1,9 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.Uninstall@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.UninstallInfo@@</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
@@ -11,10 +11,10 @@
11
  <label>
12
  <input type="hidden" name="sucuriscan_process_form" value="0" />
13
  <input type="checkbox" name="sucuriscan_process_form" value="1" />
14
- <span>@@SUCURI.UnderstandTheRisk@@</span>
15
  </label>
16
  </p>
17
- <button type="submit" name="sucuriscan_reset_options" class="button button-primary">@@SUCURI.Submit@@</button>
18
  </form>
19
  </div>
20
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Reset Security Logs, Hardening and Settings</h3>
4
 
5
  <div class="inside">
6
+ <p>This action will trigger the deactivation / uninstallation process of the plugin. All local security logs, hardening and settings will be deleted. Notice that the security logs stored in the API service will not be deleted, this is to prevent tampering from a malicious user. You can request a new API key if you want to start from scratch.</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
11
  <label>
12
  <input type="hidden" name="sucuriscan_process_form" value="0" />
13
  <input type="checkbox" name="sucuriscan_process_form" value="1" />
14
+ <span>I understand that this operation can not be reverted.</span>
15
  </label>
16
  </p>
17
+ <button type="submit" name="sucuriscan_reset_options" class="button button-primary">Submit</button>
18
  </form>
19
  </div>
20
  </div>
inc/tpl/settings-general-reverseproxy.html.tpl CHANGED
@@ -1,12 +1,12 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.ReverseProxy@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.ReverseProxyInfo@@</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
9
- <span>@@SUCURI.ReverseProxy@@ &mdash; %%SUCURI.ReverseProxyStatus%%</span>
10
 
11
  <form action="%%SUCURI.URL.Settings%%" method="post">
12
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Reverse Proxy</h3>
4
 
5
  <div class="inside">
6
+ <p>The event monitor uses the API address of the origin of the request to track the actions, the plugin uses two methods to retrieve this: the main method uses the global server variable <em>Remote-Addr</em> available in most modern web servers, an alternative method uses custom HTTP headers <em>(which are unsafe by default)</em>. You should not worry about this option unless you know what a reverse proxy is. Services like the <a href="https://sucuri.net/website-firewall/" target="_blank" rel="noopener">Sucuri Firewall</a> &mdash; once active &mdash; forces the network traffic to pass through them to filter any security threat that may affect the original server. A side effect of this is that the real IP address is no longer available in the global server variable <em>REMOTE-ADDR</em> but in a custom HTTP header with a name provided by the service.</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
9
+ <span>Reverse Proxy &mdash; %%SUCURI.ReverseProxyStatus%%</span>
10
 
11
  <form action="%%SUCURI.URL.Settings%%" method="post">
12
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
inc/tpl/settings-general-selfhosting.html.tpl CHANGED
@@ -1,12 +1,12 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.SelfHosting@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.SelfHostingInfo@@</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-%%SUCURI.SelfHosting.DisabledVisibility%%">
9
- <span>@@SUCURI.SelfHosting@@ &mdash; %%SUCURI.SelfHosting.Status%%</span>
10
  </div>
11
 
12
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-monitor-fpath sucuriscan-%%SUCURI.SelfHosting.FpathVisibility%%">
@@ -21,9 +21,9 @@
21
  <form action="%%SUCURI.URL.Settings%%#general" method="post">
22
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
23
  <fieldset class="sucuriscan-clearfix">
24
- <label>@@SUCURI.FilePath@@:</label>
25
  <input type="text" name="sucuriscan_selfhosting_fpath" />
26
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
27
  </fieldset>
28
  </form>
29
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Log Exporter</h3>
4
 
5
  <div class="inside">
6
+ <p>This option allows you to export the WordPress audit logs to a local log file that can be read by a SIEM or any log analysis software <em>(we recommend OSSEC)</em>. That will give visibility from within WordPress to complement your log monitoring infrastructure. <b>NOTE:</b> Do not use a publicly accessible file, you must use a file at least one level up the document root to prevent leaks of information.</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-%%SUCURI.SelfHosting.DisabledVisibility%%">
9
+ <span>Log Exporter &mdash; %%SUCURI.SelfHosting.Status%%</span>
10
  </div>
11
 
12
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-monitor-fpath sucuriscan-%%SUCURI.SelfHosting.FpathVisibility%%">
21
  <form action="%%SUCURI.URL.Settings%%#general" method="post">
22
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
23
  <fieldset class="sucuriscan-clearfix">
24
+ <label>File Path:</label>
25
  <input type="text" name="sucuriscan_selfhosting_fpath" />
26
+ <button type="submit" class="button button-primary">Submit</button>
27
  </fieldset>
28
  </form>
29
  </div>
inc/tpl/settings-general-timezone.html.tpl CHANGED
@@ -1,18 +1,18 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.TimezoneTitle@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.TimezoneInfo@@</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
  <fieldset class="sucuriscan-clearfix">
11
- <label>@@SUCURI.TimezoneTitle@@:</label>
12
  <select name="sucuriscan_timezone">
13
  %%%SUCURI.Timezone.Dropdown%%%
14
  </select>
15
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
16
  <span><em>(%%SUCURI.Timezone.Example%%)</em></span>
17
  </fieldset>
18
  </form>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Timezone</h3>
4
 
5
  <div class="inside">
6
+ <p>This option defines the timezone that will be used through out the entire plugin to print the dates and times whenever is necessary. This option also affects the date and time of the logs visible in the audit logs panel which is data that comes from a remote server configured to use Eastern Daylight Time (EDT). WordPress offers an option in the general settings page to allow you to configure the timezone for the entire website, however, if you are experiencing problems with the time in the audit logs, this option will help you fix them.</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
  <fieldset class="sucuriscan-clearfix">
11
+ <label>Timezone:</label>
12
  <select name="sucuriscan_timezone">
13
  %%%SUCURI.Timezone.Dropdown%%%
14
  </select>
15
+ <button type="submit" class="button button-primary">Submit</button>
16
  <span><em>(%%SUCURI.Timezone.Example%%)</em></span>
17
  </fieldset>
18
  </form>
inc/tpl/settings-hardening-whitelist-phpfiles.html.tpl CHANGED
@@ -1,21 +1,21 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.WhitelistScript@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.WhitelistScriptInfo@@</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#hardening" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
  <fieldset class="sucuriscan-clearfix">
11
- <label>@@SUCURI.FilePath@@:</label>
12
  <input type="text" name="sucuriscan_hardening_whitelist" placeholder="e.g. wp-tinymce.php" />
13
  <select name="sucuriscan_hardening_folder">
14
  <option value="wp-includes">wp-includes</option>
15
  <option value="wp-content">wp-content</option>
16
  <option value="wp-content/uploads">wp-content/uploads</option>
17
  </select>
18
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
19
  </fieldset>
20
  </form>
21
 
@@ -30,9 +30,9 @@
30
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
31
  <input id="cb-select-all-1" type="checkbox">
32
  </td>
33
- <th class="manage-column">@@SUCURI.FilePath@@</th>
34
- <th class="manage-column">@@SUCURI.Directory@@</th>
35
- <th class="manage-column">@@SUCURI.Pattern@@</th>
36
  </thead>
37
 
38
  <tbody>
@@ -40,13 +40,13 @@
40
 
41
  <tr class="sucuriscan-%%SUCURI.HardeningWhitelist.NoItemsVisibility%%">
42
  <td colspan="4">
43
- <em>@@SUCURI.NoData@@</em>
44
  </td>
45
  </tr>
46
  </tbody>
47
  </table>
48
 
49
- <button type="submit" class="button button-primary">@@SUCURI.Delete@@</button>
50
  </form>
51
  </div>
52
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Whitelist Blocked PHP Files</h3>
4
 
5
  <div class="inside">
6
+ <p>After you apply the hardening in either the includes, content, and/or upload directories the plugin will add a rule in the access control file to deny access to any PHP file located in these folders, this is a good precaution in case that an attacker is able to upload a shell script; with a few exceptions the <em>"index.php"</em> is the only one that should be publicly accessible, however many theme/plugin developers decide to use these folders to process some operations, in this case applying the hardening <strong>may break</strong> their functionality.</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#hardening" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
  <fieldset class="sucuriscan-clearfix">
11
+ <label>File Path:</label>
12
  <input type="text" name="sucuriscan_hardening_whitelist" placeholder="e.g. wp-tinymce.php" />
13
  <select name="sucuriscan_hardening_folder">
14
  <option value="wp-includes">wp-includes</option>
15
  <option value="wp-content">wp-content</option>
16
  <option value="wp-content/uploads">wp-content/uploads</option>
17
  </select>
18
+ <button type="submit" class="button button-primary">Submit</button>
19
  </fieldset>
20
  </form>
21
 
30
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
31
  <input id="cb-select-all-1" type="checkbox">
32
  </td>
33
+ <th class="manage-column">File Path</th>
34
+ <th class="manage-column">Directory</th>
35
+ <th class="manage-column">Pattern</th>
36
  </thead>
37
 
38
  <tbody>
40
 
41
  <tr class="sucuriscan-%%SUCURI.HardeningWhitelist.NoItemsVisibility%%">
42
  <td colspan="4">
43
+ <em>no data available</em>
44
  </td>
45
  </tr>
46
  </tbody>
47
  </table>
48
 
49
+ <button type="submit" class="button button-primary">Delete</button>
50
  </form>
51
  </div>
52
  </div>
inc/tpl/settings-posthack-available-updates-alert.html.tpl CHANGED
@@ -1,13 +1,13 @@
1
 
2
- <p>@@SUCURI.AvailableUpdatesInfo@@</p>
3
 
4
  <table border="1" cellspacing="1" cellpadding="5">
5
  <thead>
6
  <tr>
7
- <th>@@SUCURI.Name@@</th>
8
- <th>@@SUCURI.Version@@</th>
9
- <th>@@SUCURI.Update@@</th>
10
- <th>@@SUCURI.TestedWith@@</th>
11
  <th>&nbsp;</th>
12
  </tr>
13
  </thead>
1
 
2
+ <p>WordPress has a big user base in the public Internet, this brings interest to malicious people to find vulnerabilities in the code, 3rd-party extensions, and themes that other companies develop. You should keep every piece of code installed in your website update to prevent attacks as soon as disclosed vulnerabilities are patched.</p>
3
 
4
  <table border="1" cellspacing="1" cellpadding="5">
5
  <thead>
6
  <tr>
7
+ <th>Name</th>
8
+ <th>Version</th>
9
+ <th>Update</th>
10
+ <th>Tested With</th>
11
  <th>&nbsp;</th>
12
  </tr>
13
  </thead>
inc/tpl/settings-posthack-available-updates.html.tpl CHANGED
@@ -1,11 +1,11 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.AvailableUpdates@@</h3>
4
 
5
  <script type="text/javascript">
6
  /* global jQuery */
7
  /* jshint camelcase: false */
8
- jQuery(function ($) {
9
  $.post('%%SUCURI.AjaxURL.Dashboard%%', {
10
  action: 'sucuriscan_ajax',
11
  sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
@@ -17,15 +17,15 @@
17
  </script>
18
 
19
  <div class="inside">
20
- <p>@@SUCURI.AvailableUpdatesInfo@@</p>
21
 
22
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-available-updates-table">
23
  <thead>
24
  <tr>
25
- <th class="manage-column">@@SUCURI.Name@@</th>
26
- <th class="manage-column">@@SUCURI.Version@@</th>
27
- <th class="manage-column">@@SUCURI.Update@@</th>
28
- <th class="manage-column">@@SUCURI.TestedWith@@</th>
29
  <th class="manage-column">&nbsp;</th>
30
  </tr>
31
  </thead>
@@ -33,7 +33,7 @@
33
  <tbody>
34
  <tr>
35
  <td colspan="5">
36
- <span>@@SUCURI.Loading@@</span>
37
  </td>
38
  </tr>
39
  </tbody>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Available Plugin and Theme Updates</h3>
4
 
5
  <script type="text/javascript">
6
  /* global jQuery */
7
  /* jshint camelcase: false */
8
+ jQuery(document).ready(function ($) {
9
  $.post('%%SUCURI.AjaxURL.Dashboard%%', {
10
  action: 'sucuriscan_ajax',
11
  sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
17
  </script>
18
 
19
  <div class="inside">
20
+ <p>WordPress has a big user base in the public Internet, this brings interest to malicious people to find vulnerabilities in the code, 3rd-party extensions, and themes that other companies develop. You should keep every piece of code installed in your website update to prevent attacks as soon as disclosed vulnerabilities are patched.</p>
21
 
22
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-available-updates-table">
23
  <thead>
24
  <tr>
25
+ <th class="manage-column">Name</th>
26
+ <th class="manage-column">Version</th>
27
+ <th class="manage-column">Update</th>
28
+ <th class="manage-column">Tested With</th>
29
  <th class="manage-column">&nbsp;</th>
30
  </tr>
31
  </thead>
33
  <tbody>
34
  <tr>
35
  <td colspan="5">
36
+ <span>Loading...</span>
37
  </td>
38
  </tr>
39
  </tbody>
inc/tpl/settings-posthack-available-updates.snippet.tpl CHANGED
@@ -2,7 +2,7 @@
2
  <tr>
3
  <td>
4
  <span class="dashicons-before dashicons-admin-%%SUCURI.Update.IconType%%">
5
- <a href="%%SUCURI.Update.MarketUrl%%" target="_blank">%%SUCURI.Update.Extension%%</a>
6
  </span>
7
  </td>
8
 
@@ -12,5 +12,5 @@
12
 
13
  <td>%%SUCURI.Update.TestedWith%%</td>
14
 
15
- <td><a href="%%SUCURI.Update.ArchiveUrl%%" target="_blank">@@SUCURI.Download@@</a></td>
16
  </tr>
2
  <tr>
3
  <td>
4
  <span class="dashicons-before dashicons-admin-%%SUCURI.Update.IconType%%">
5
+ <a href="%%SUCURI.Update.MarketUrl%%" target="_blank" rel="noopener">%%SUCURI.Update.Extension%%</a>
6
  </span>
7
  </td>
8
 
12
 
13
  <td>%%SUCURI.Update.TestedWith%%</td>
14
 
15
+ <td><a href="%%SUCURI.Update.ArchiveUrl%%" target="_blank" rel="noopener">Download</a></td>
16
  </tr>
inc/tpl/settings-posthack-reset-password-alert.html.tpl CHANGED
@@ -1,2 +1,2 @@
1
 
2
- @@SUCURI.PasswordChangeAlert@@
1
 
2
+ WordPress has generated a new (random) password for your account <b>%%SUCURI.ResetPassword.UserName%%</b> at <a target="_blank" href="http://%%SUCURI.ResetPassword.Website%%" rel="noopener">%%SUCURI.ResetPassword.Website%%</a>. The change has been requested by one of the admins in this website for security reasons. Your new password is &mdash; <span style="font-family:Menlo, Monaco, monospace, serif;font-weight:700">%%%SUCURI.ResetPassword.Password%%%</span> &mdash; please change it as soon as possible.
inc/tpl/settings-posthack-reset-password.html.tpl CHANGED
@@ -1,11 +1,11 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.PasswordChange@@</h3>
4
 
5
  <script type="text/javascript">
6
  /* global jQuery */
7
  /* jshint camelcase: false */
8
- jQuery(function ($) {
9
  $('#sucuriscan-reset-password-button').on('click', function (event) {
10
  event.preventDefault();
11
  $('.sucuriscan-reset-password-table :checkbox:checked').each(function (key, el) {
@@ -13,7 +13,7 @@
13
 
14
  $('#sucuriscan-userid-' + user_id)
15
  .find('.sucuriscan-response')
16
- .html('(@@SUCURI.Loading@@)');
17
 
18
  $.post('%%SUCURI.AjaxURL.Dashboard%%', {
19
  action: 'sucuriscan_ajax',
@@ -31,7 +31,7 @@
31
  </script>
32
 
33
  <div class="inside">
34
- <p>@@SUCURI.PasswordChangeInfo@@</p>
35
 
36
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-reset-password-table">
37
  <thead>
@@ -40,10 +40,10 @@
40
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
41
  <input id="cb-select-all-1" type="checkbox">
42
  </td>
43
- <th class="manage-column">@@SUCURI.Username@@</th>
44
- <th class="manage-column">@@SUCURI.Email@@</th>
45
- <th class="manage-column">@@SUCURI.Registered@@</th>
46
- <th class="manage-column">@@SUCURI.Roles@@</th>
47
  </tr>
48
  </thead>
49
 
@@ -51,7 +51,7 @@
51
  %%%SUCURI.ResetPassword.UserList%%%
52
 
53
  <tr class="sucuriscan-%%SUCURI.ResetPassword.PaginationVisibility%%">
54
- <td colspan="4">
55
  <ul class="sucuriscan-pagination">
56
  %%%SUCURI.ResetPassword.PaginationLinks%%%
57
  </ul>
@@ -61,6 +61,6 @@
61
  </table>
62
 
63
  <button type="button" id="sucuriscan-reset-password-button"
64
- class="button button-primary">@@SUCURI.Submit@@</button>
65
  </div>
66
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Reset User Password</h3>
4
 
5
  <script type="text/javascript">
6
  /* global jQuery */
7
  /* jshint camelcase: false */
8
+ jQuery(document).ready(function ($) {
9
  $('#sucuriscan-reset-password-button').on('click', function (event) {
10
  event.preventDefault();
11
  $('.sucuriscan-reset-password-table :checkbox:checked').each(function (key, el) {
13
 
14
  $('#sucuriscan-userid-' + user_id)
15
  .find('.sucuriscan-response')
16
+ .html('(Loading...)');
17
 
18
  $.post('%%SUCURI.AjaxURL.Dashboard%%', {
19
  action: 'sucuriscan_ajax',
31
  </script>
32
 
33
  <div class="inside">
34
+ <p>You can generate a new random password for the user accounts that you select from the list. An email with the new password will be sent to the email address of each chosen users. If you choose to change the password of your own user, then your current session will expire immediately. You will need to log into the admin panel with the new password that will be sent to your email.</p>
35
 
36
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-reset-password-table">
37
  <thead>
40
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
41
  <input id="cb-select-all-1" type="checkbox">
42
  </td>
43
+ <th class="manage-column">Username</th>
44
+ <th class="manage-column">E-mail</th>
45
+ <th class="manage-column">Registered</th>
46
+ <th class="manage-column">Roles</th>
47
  </tr>
48
  </thead>
49
 
51
  %%%SUCURI.ResetPassword.UserList%%%
52
 
53
  <tr class="sucuriscan-%%SUCURI.ResetPassword.PaginationVisibility%%">
54
+ <td colspan="5">
55
  <ul class="sucuriscan-pagination">
56
  %%%SUCURI.ResetPassword.PaginationLinks%%%
57
  </ul>
61
  </table>
62
 
63
  <button type="button" id="sucuriscan-reset-password-button"
64
+ class="button button-primary">Submit</button>
65
  </div>
66
  </div>
inc/tpl/settings-posthack-reset-plugins.html.tpl CHANGED
@@ -1,11 +1,11 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.PluginReinstall@@</h3>
4
 
5
  <script type="text/javascript">
6
  /* global jQuery */
7
  /* jshint camelcase:false */
8
- jQuery(function ($) {
9
  $.post('%%SUCURI.AjaxURL.Dashboard%%', {
10
  action: 'sucuriscan_ajax',
11
  sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
@@ -22,7 +22,7 @@
22
 
23
  $('#sucuriscan-plugin-' + unique)
24
  .find('.sucuriscan-reset-plugin-response')
25
- .html('@@SUCURI.Loading@@');
26
 
27
  $.post('%%SUCURI.AjaxURL.Dashboard%%', {
28
  action: 'sucuriscan_ajax',
@@ -40,14 +40,14 @@
40
  </script>
41
 
42
  <div class="inside">
43
- <p>@@SUCURI.PluginReinstallInfo@@</p>
44
 
45
  <div class="sucuriscan-inline-alert-info">
46
- <p>@@SUCURI.PluginReinstallCache@@</p>
47
  </div>
48
 
49
  <div class="sucuriscan-inline-alert-error">
50
- <p>@@SUCURI.PluginReinstallWarning@@</p>
51
  </div>
52
 
53
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-reset-plugins-table">
@@ -57,22 +57,22 @@
57
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
58
  <input id="cb-select-all-1" type="checkbox">
59
  </td>
60
- <th class="manage-column">@@SUCURI.Name@@</th>
61
- <th class="manage-column">@@SUCURI.Version@@</th>
62
- <th class="manage-column">@@SUCURI.Type@@</th>
63
- <th class="manage-column">@@SUCURI.Status@@</th>
64
  </tr>
65
  </thead>
66
 
67
  <tbody>
68
  <tr>
69
  <td colspan="5">
70
- <span>@@SUCURI.Loading@@</span>
71
  </td>
72
  </tr>
73
  </tbody>
74
  </table>
75
 
76
- <button type="button" id="sucuriscan_reset_plugins" class="button button-primary">@@SUCURI.Submit@@</button>
77
  </div>
78
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Reset Installed Plugins</h3>
4
 
5
  <script type="text/javascript">
6
  /* global jQuery */
7
  /* jshint camelcase:false */
8
+ jQuery(document).ready(function ($) {
9
  $.post('%%SUCURI.AjaxURL.Dashboard%%', {
10
  action: 'sucuriscan_ajax',
11
  sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
22
 
23
  $('#sucuriscan-plugin-' + unique)
24
  .find('.sucuriscan-reset-plugin-response')
25
+ .html('Loading...');
26
 
27
  $.post('%%SUCURI.AjaxURL.Dashboard%%', {
28
  action: 'sucuriscan_ajax',
40
  </script>
41
 
42
  <div class="inside">
43
+ <p>In case that you suspect of an infection in your site, or even after you got rid of a malicious code, it would be better if you reinstall all the plugins installed in your site, including the ones you are not using. Notice that premium plugins will not be reinstalled to prevent backward compatibility issues and problems with licenses.</p>
44
 
45
  <div class="sucuriscan-inline-alert-info">
46
+ <p>The information shown here is cache for %%SUCURI.ResetPlugin.CacheLifeTime%% seconds, this is necessary to reduce the quantity of HTTP requests sent to the WordPress servers and the bandwidth of your site. Currently there is no option to recreate this cache so you have to wait until it resets itself.</p>
47
  </div>
48
 
49
  <div class="sucuriscan-inline-alert-error">
50
+ <p><b>WARNING!</b> This procedure can break your website. The reset will not affect the database nor the settings of each plugin but depending on how they were written the reset action might break them. Be sure to create a backup of the plugins directory before the execution of this tool.</p>
51
  </div>
52
 
53
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-reset-plugins-table">
57
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
58
  <input id="cb-select-all-1" type="checkbox">
59
  </td>
60
+ <th class="manage-column">Name</th>
61
+ <th class="manage-column">Version</th>
62
+ <th class="manage-column">Type</th>
63
+ <th class="manage-column">Status</th>
64
  </tr>
65
  </thead>
66
 
67
  <tbody>
68
  <tr>
69
  <td colspan="5">
70
+ <span>Loading...</span>
71
  </td>
72
  </tr>
73
  </tbody>
74
  </table>
75
 
76
+ <button type="button" id="sucuriscan_reset_plugins" class="button button-primary">Submit</button>
77
  </div>
78
  </div>
inc/tpl/settings-posthack-reset-plugins.snippet.tpl CHANGED
@@ -5,7 +5,7 @@
5
  </th>
6
 
7
  <td>
8
- <a href="%%SUCURI.ResetPlugin.Repository%%" target="_blank">%%SUCURI.ResetPlugin.Plugin%%</a>
9
  </td>
10
 
11
  <td class="sucuriscan-reset-plugin-response">
5
  </th>
6
 
7
  <td>
8
+ <a href="%%SUCURI.ResetPlugin.Repository%%" target="_blank" rel="noopener">%%SUCURI.ResetPlugin.Plugin%%</a>
9
  </td>
10
 
11
  <td class="sucuriscan-reset-plugin-response">
inc/tpl/settings-posthack-security-keys.html.tpl CHANGED
@@ -1,12 +1,12 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.SecretKeys@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.SecretKeysInfo@@</p>
7
 
8
  <div class="sucuriscan-inline-alert-error">
9
- <p>@@SUCURI.SecretKeysExpiration@@</p>
10
  </div>
11
 
12
  <div class="sucuriscan_wpconfig_keys_updated sucuriscan-monospace sucuriscan-%%SUCURI.WPConfigUpdate.Visibility%%">
@@ -16,9 +16,9 @@
16
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-security-keys-table">
17
  <thead>
18
  <tr>
19
- <th>@@SUCURI.Status@@</th>
20
- <th>@@SUCURI.Name@@</th>
21
- <th>@@SUCURI.Value@@</th>
22
  </tr>
23
  </thead>
24
 
@@ -35,11 +35,11 @@
35
  <label>
36
  <input type="hidden" name="sucuriscan_process_form" value="0" />
37
  <input type="checkbox" name="sucuriscan_process_form" value="1" />
38
- <span>@@SUCURI.UnderstandTheRisk@@</span>
39
  </label>
40
  </p>
41
 
42
- <input type="submit" value="@@SUCURI.SecretKeysGenerate@@" class="button button-primary" />
43
  </form>
44
  </div>
45
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Update Secret Keys</h3>
4
 
5
  <div class="inside">
6
+ <p>The secret or security keys are a list of constants added to your site to ensure better encryption of information stored in the user's cookies. A secret key makes your site harder to hack and access by adding random elements to the password. You do not have to remember the keys, just write a random, complicated, and long string in the <code>wp-config.php</code> file. You can change these keys at any point in time to invalidate all existing cookies, forcing all users to login again.</p>
7
 
8
  <div class="sucuriscan-inline-alert-error">
9
+ <p>Your current session will expire once the form is submitted.</p>
10
  </div>
11
 
12
  <div class="sucuriscan_wpconfig_keys_updated sucuriscan-monospace sucuriscan-%%SUCURI.WPConfigUpdate.Visibility%%">
16
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-security-keys-table">
17
  <thead>
18
  <tr>
19
+ <th>Status</th>
20
+ <th>Name</th>
21
+ <th>Value</th>
22
  </tr>
23
  </thead>
24
 
35
  <label>
36
  <input type="hidden" name="sucuriscan_process_form" value="0" />
37
  <input type="checkbox" name="sucuriscan_process_form" value="1" />
38
+ <span>I understand that this operation can not be reverted.</span>
39
  </label>
40
  </p>
41
 
42
+ <input type="submit" value="Generate New Security Keys" class="button button-primary" />
43
  </form>
44
  </div>
45
  </div>
inc/tpl/settings-scanner-cronjobs.html.tpl CHANGED
@@ -1,15 +1,15 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.Cronjobs@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.ScannerDescription@@</p>
7
 
8
  <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.NoSPL.Visibility%%">
9
- <p>@@SUCURI.ScannerWithoutSPL@@</p>
10
  </div>
11
 
12
- <p>@@SUCURI.CronjobsInfo@@</p>
13
 
14
  <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
15
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
@@ -21,10 +21,10 @@
21
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
22
  <input id="cb-select-all-1" type="checkbox">
23
  </td>
24
- <th>@@SUCURI.Name@@</th>
25
- <th>@@SUCURI.Schedule@@</th>
26
- <th>@@SUCURI.NextDue@@</th>
27
- <th>@@SUCURI.Arguments@@</th>
28
  </tr>
29
  </thead>
30
 
@@ -34,11 +34,11 @@
34
  </table>
35
 
36
  <fieldset class="sucuriscan-clearfix">
37
- <label>@@SUCURI.Action@@:</label>
38
  <select name="sucuriscan_cronjob_action">
39
  %%%SUCURI.Cronjob.Schedules%%%
40
  </select>
41
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
42
  </fieldset>
43
  </form>
44
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Scheduled Tasks</h3>
4
 
5
  <div class="inside">
6
+ <p>The plugin scans your entire website looking for changes which are later reported via the API in the audit logs page. This scanner runs daily but you can change the frequency to meet your own requirements. Notice that scanning your project files too frequently will affect the performance of your website. Be sure to have enough server resources before changing this option. The memory limit and maximum execution time are two of the PHP options that your server will set to stop your website from consuming too much resources.</p>
7
 
8
  <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.NoSPL.Visibility%%">
9
+ <p>The scanner uses the <a href="http://php.net/manual/en/class.splfileobject.php" target="_blank" rel="noopener">PHP SPL library</a> and the <a target="_blank" href="http://php.net/manual/en/class.filesystemiterator.php" rel="noopener">Filesystem Iterator</a> class to scan the directory tree where your website is located in the server. This library is only available on PHP 5 >= 5.3.0 &mdash; OR &mdash; PHP 7; if you have an older version of PHP the plugin will not work as expected. Please ask your hosting provider to advice you on this matter.</p>
10
  </div>
11
 
12
+ <p>Scheduled tasks are rules registered in your database by a plugin, theme, or the base system itself; they are used to automatically execute actions defined in the code every certain amount of time. A good use of these rules is to generate backup files of your site, execute a security scanner, or remove unused elements like drafts. <b>Note:</b> Scheduled tasks can be re-installed by any plugin/theme automatically, consider to deactivate the plugin entirely if you want to get rid of the scheduled tasks.</p>
13
 
14
  <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
15
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
21
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
22
  <input id="cb-select-all-1" type="checkbox">
23
  </td>
24
+ <th>Name</th>
25
+ <th>Schedule</th>
26
+ <th>Next Due</th>
27
+ <th>Arguments</th>
28
  </tr>
29
  </thead>
30
 
34
  </table>
35
 
36
  <fieldset class="sucuriscan-clearfix">
37
+ <label>Action:</label>
38
  <select name="sucuriscan_cronjob_action">
39
  %%%SUCURI.Cronjob.Schedules%%%
40
  </select>
41
+ <button type="submit" class="button button-primary">Submit</button>
42
  </fieldset>
43
  </form>
44
  </div>
inc/tpl/settings-scanner-ignore-folders.html.tpl CHANGED
@@ -1,35 +1,17 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.IgnoreFiles@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.IgnoreFilesInfo@@</p>
7
-
8
- <script type="text/javascript">
9
- /* global jQuery */
10
- /* jshint camelcase: false */
11
- jQuery(function ($) {
12
- $('.sucuriscan-ignorescanning tbody').html(
13
- '<tr><td colspan="3"><span>@@SUCURI.Loading@@</span></td></tr>'
14
- );
15
- $.post('%%SUCURI.AjaxURL.Dashboard%%', {
16
- action: 'sucuriscan_ajax',
17
- sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
18
- form_action: 'get_ignored_files',
19
- }, function (data) {
20
- $('.sucuriscan-ignorescanning tbody').html(data);
21
- });
22
- });
23
- </script>
24
 
25
  <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
26
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
27
- <input type="hidden" name="sucuriscan_ignorescanning_action" value="ignore" />
28
 
29
  <fieldset class="sucuriscan-clearfix">
30
- <label>@@SUCURI.IgnoreFilesSingle@@:</label>
31
- <input type="text" name="sucuriscan_ignorescanning_file" placeholder="e.g. /private/cert.crt" />
32
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
33
  </fieldset>
34
  </form>
35
 
@@ -44,25 +26,16 @@
44
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
45
  <input id="cb-select-all-1" type="checkbox">
46
  </td>
47
- <th class="manage-column">@@SUCURI.FilePath@@</th>
48
- <th class="manage-column">@@SUCURI.Status@@</th>
49
  </thead>
50
 
51
  <tbody>
 
52
  </tbody>
53
  </table>
54
 
55
- <div class="sucuriscan-recipient-form">
56
- <label>
57
- <select name="sucuriscan_ignorescanning_action">
58
- <option value="">@@SUCURI.Action@@</option>
59
- <option value="ignore">@@SUCURI.Ignore@@</option>
60
- <option value="unignore">@@SUCURI.Unignore@@</option>
61
- </select>
62
- </label>
63
-
64
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
65
- </div>
66
  </form>
67
  </div>
68
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Ignore Files And Folders During The Scans</h3>
4
 
5
  <div class="inside">
6
+ <p>Use this tool to select the files and/or folders that are too heavy for the scanner to process. These are usually folders with images, media files like videos and audios, backups and &mdash; in general &mdash; anything that is not code-related. Ignoring these files or folders will reduce the memory consumption of the PHP script.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
 
10
 
11
  <fieldset class="sucuriscan-clearfix">
12
+ <label>Ignore a file or directory:</label>
13
+ <input type="text" name="sucuriscan_ignorefolder" placeholder="e.g. /private/directory/" />
14
+ <button type="submit" class="button button-primary">Submit</button>
15
  </fieldset>
16
  </form>
17
 
26
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
27
  <input id="cb-select-all-1" type="checkbox">
28
  </td>
29
+ <th class="manage-column">File Path</th>
30
+ <th class="manage-column">Status</th>
31
  </thead>
32
 
33
  <tbody>
34
+ %%%SUCURI.IgnoreScan.List%%%
35
  </tbody>
36
  </table>
37
 
38
+ <button type="submit" class="button button-primary">Unignore Selected Directories</button>
 
 
 
 
 
 
 
 
 
 
39
  </form>
40
  </div>
41
  </div>
inc/tpl/settings-scanner-ignore-folders.snippet.tpl CHANGED
@@ -1,10 +1,10 @@
1
 
2
  <tr>
3
  <th class="check-column">
4
- <input type="checkbox" name="sucuriscan_ignorescanning_dirs[]" value="%%SUCURI.IgnoreScan.DirectoryPath%%" />
5
  </th>
6
 
7
- <td><span class="sucuriscan-monospace sucuriscan-wraptext">%%SUCURI.IgnoreScan.DirectoryPath%%</span></td>
8
 
9
- <td>%%SUCURI.IgnoreScan.IgnoredAtText%%</td>
10
  </tr>
1
 
2
  <tr>
3
  <th class="check-column">
4
+ <input type="checkbox" name="sucuriscan_unignorefolders[]" value="%%SUCURI.IgnoreScan.Directory%%" />
5
  </th>
6
 
7
+ <td><span class="sucuriscan-monospace sucuriscan-wraptext">%%SUCURI.IgnoreScan.Directory%%</span></td>
8
 
9
+ <td>%%SUCURI.IgnoreScan.IgnoredAt%%</td>
10
  </tr>
inc/tpl/settings-scanner-integrity-cache.html.tpl CHANGED
@@ -1,9 +1,9 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.FalsePositives@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.FalsePositivesInfo@@</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
@@ -16,9 +16,9 @@
16
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
17
  <input id="cb-select-all-1" type="checkbox">
18
  </td>
19
- <th>@@SUCURI.Reason@@</th>
20
- <th>@@SUCURI.IgnoredAt@@</th>
21
- <th>@@SUCURI.FilePath@@</th>
22
  </tr>
23
  </thead>
24
 
@@ -27,14 +27,14 @@
27
 
28
  <tr class="sucuriscan-%%SUCURI.NoFilesVisibility%%">
29
  <td colspan="4">
30
- <em>@@SUCURI.NoData@@</em>
31
  </td>
32
  </tr>
33
  </tbody>
34
  </table>
35
 
36
  <p>
37
- <button type="submit" class="button button-primary">@@SUCURI.FalsePositivesUnignore@@</button>
38
  </p>
39
  </form>
40
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">WordPress Integrity (False Positives)</h3>
4
 
5
  <div class="inside">
6
+ <p>Since the scanner doesn't reads the files during the execution of the integrity check, it is possible to find false positives. Files listed here have been marked as false positives and will be ignored by the scanner in subsequent scans.</p>
7
 
8
  <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
16
  <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
17
  <input id="cb-select-all-1" type="checkbox">
18
  </td>
19
+ <th>Reason</th>
20
+ <th>Ignored At</th>
21
+ <th>File Path</th>
22
  </tr>
23
  </thead>
24
 
27
 
28
  <tr class="sucuriscan-%%SUCURI.NoFilesVisibility%%">
29
  <td colspan="4">
30
+ <em>no data available</em>
31
  </td>
32
  </tr>
33
  </tbody>
34
  </table>
35
 
36
  <p>
37
+ <button type="submit" class="button button-primary">Stop Ignoring the Selected Files</button>
38
  </p>
39
  </form>
40
  </div>
inc/tpl/settings-scanner-integrity-diff-utility.html.tpl CHANGED
@@ -1,12 +1,12 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.DiffUtility@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.DiffUtilityDescription@@</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-%%SUCURI.DiffUtility.StatusNum%%">
9
- <span>@@SUCURI.DiffUtility@@ &mdash; %%SUCURI.DiffUtility.Status%%</span>
10
 
11
  <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
12
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">WordPress Integrity Diff Utility</h3>
4
 
5
  <div class="inside">
6
+ <p>If your server allows the execution of system commands, you can configure the plugin to use the <a href="https://en.wikipedia.org/wiki/Diff_utility" target="_blank" rel="noopener">Unix Diff Utility</a> to compare the actual content of the file installed in the website and the original file provided by WordPress. This will show the differences between both files and then you can act upon the information provided.</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-%%SUCURI.DiffUtility.StatusNum%%">
9
+ <span>WordPress Integrity Diff Utility &mdash; %%SUCURI.DiffUtility.Status%%</span>
10
 
11
  <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
12
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
inc/tpl/settings-scanner-integrity-language.html.tpl DELETED
@@ -1,24 +0,0 @@
1
-
2
- <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.IntegrityLanguageTitle@@</h3>
4
-
5
- <div class="inside">
6
- <p>@@SUCURI.IntegrityInfo@@</p>
7
-
8
- <div class="sucuriscan-inline-alert-info">
9
- <p>@@SUCURI.IntegrityNote@@</p>
10
- </div>
11
-
12
- <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
13
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
14
- <fieldset class="sucuriscan-clearfix">
15
- <label>WordPress Locale:</label>
16
- <select name="sucuriscan_set_language">
17
- %%%SUCURI.Integrity.LanguageDropdown%%%
18
- </select>
19
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
20
- <span><em>(WordPress Locale %%SUCURI.Integrity.WordPressLocale%%)</em></span>
21
- </fieldset>
22
- </form>
23
- </div>
24
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/tpl/settings-webinfo-details.html.tpl CHANGED
@@ -1,6 +1,6 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.EnvVariables@@</h3>
4
 
5
  <div class="inside">
6
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-server-info">
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Environment Variables</h3>
4
 
5
  <div class="inside">
6
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-server-info">
inc/tpl/settings-webinfo-htaccess.html.tpl CHANGED
@@ -1,20 +1,20 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.HTAccessTitle@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.HTAccessInfo@@</p>
7
 
8
  <div class="sucuriscan-inline-alert-success sucuriscan-%%SUCURI.HTAccess.FoundVisible%%">
9
- <p>@@SUCURI.HTAccessFound@@</p>
10
  </div>
11
 
12
  <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.HTAccess.NotFoundVisible%%">
13
- <p>@@SUCURI.HTAccessNotFound@@</p>
14
  </div>
15
 
16
  <div class="sucuriscan-inline-alert-info sucuriscan-%%SUCURI.HTAccess.StandardVisible%%">
17
- <p>@@SUCURI.HTAccessStandard@@</p>
18
  </div>
19
 
20
  <textarea readonly class="sucuriscan-full-textarea sucuriscan-monospace %%SUCURI.HTAccess.TextareaVisible%%">%%SUCURI.HTAccess.Content%%</textarea>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Access File Integrity</h3>
4
 
5
  <div class="inside">
6
+ <p>The <code>.htaccess</code> is a distributed configuration file, and is how the Apache web server handles configuration changes on a per-directory basis. WordPress uses this file to manipulate how Apache serves files from its root directory and subdirectories thereof; most notably, it modifies this file to be able to handle pretty permalinks.</p>
7
 
8
  <div class="sucuriscan-inline-alert-success sucuriscan-%%SUCURI.HTAccess.FoundVisible%%">
9
+ <p>Htaccess file found in <code>%%SUCURI.HTAccess.Fpath%%</code></p>
10
  </div>
11
 
12
  <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.HTAccess.NotFoundVisible%%">
13
+ <p>Your website has no <code>.htaccess</code> file or it was not found in the default location.</p>
14
  </div>
15
 
16
  <div class="sucuriscan-inline-alert-info sucuriscan-%%SUCURI.HTAccess.StandardVisible%%">
17
+ <p>The main <code>.htaccess</code> file in your site has the standard rules for a WordPress installation. You can customize it to improve the performance and change the behaviour of the redirections for pages and posts in your site. To get more information visit the official documentation at <a target="_blank" rel="noopener" href="https://codex.wordpress.org/Using_Permalinks#Creating_and_editing_.28.htaccess.29"> Codex WordPress - Creating and editing (.htaccess)</a></p>
18
  </div>
19
 
20
  <textarea readonly class="sucuriscan-full-textarea sucuriscan-monospace %%SUCURI.HTAccess.TextareaVisible%%">%%SUCURI.HTAccess.Content%%</textarea>
inc/tpl/settings.html.tpl CHANGED
@@ -1,13 +1,13 @@
1
 
2
  <div class="sucuriscan-tabs">
3
  <ul class="sucuriscan-clearfix sucuriscan-tabs-buttons">
4
- <li><a href="%%SUCURI.URL.Settings%%#general">@@SUCURI.General@@</a></li>
5
- <li><a href="%%SUCURI.URL.Settings%%#scanner">@@SUCURI.Scanner@@</a></li>
6
- <li><a href="%%SUCURI.URL.Settings%%#hardening">@@SUCURI.Hardening@@</a></li>
7
- <li><a href="%%SUCURI.URL.Settings%%#posthack">@@SUCURI.PostHack@@</a></li>
8
- <li><a href="%%SUCURI.URL.Settings%%#alerts">@@SUCURI.Alerts@@</a></li>
9
- <li><a href="%%SUCURI.URL.Settings%%#apiservice">@@SUCURI.APICommunication@@</a></li>
10
- <li><a href="%%SUCURI.URL.Settings%%#webinfo">@@SUCURI.WebsiteInfo@@</a></li>
11
  </ul>
12
 
13
  <div class="sucuriscan-tabs-containers">
@@ -34,8 +34,6 @@
34
 
35
  %%%SUCURI.Settings.Scanner.IntegrityDiffUtility%%%
36
 
37
- %%%SUCURI.Settings.Scanner.IntegrityLanguage%%%
38
-
39
  %%%SUCURI.Settings.Scanner.IntegrityCache%%%
40
 
41
  %%%SUCURI.Settings.Scanner.IgnoreFolders%%%
1
 
2
  <div class="sucuriscan-tabs">
3
  <ul class="sucuriscan-clearfix sucuriscan-tabs-buttons">
4
+ <li><a href="%%SUCURI.URL.Settings%%#general">General</a></li>
5
+ <li><a href="%%SUCURI.URL.Settings%%#scanner">Scanner</a></li>
6
+ <li><a href="%%SUCURI.URL.Settings%%#hardening">Hardening</a></li>
7
+ <li><a href="%%SUCURI.URL.Settings%%#posthack">Post-Hack</a></li>
8
+ <li><a href="%%SUCURI.URL.Settings%%#alerts">Alerts</a></li>
9
+ <li><a href="%%SUCURI.URL.Settings%%#apiservice">API Service Communication</a></li>
10
+ <li><a href="%%SUCURI.URL.Settings%%#webinfo">Website Info</a></li>
11
  </ul>
12
 
13
  <div class="sucuriscan-tabs-containers">
34
 
35
  %%%SUCURI.Settings.Scanner.IntegrityDiffUtility%%%
36
 
 
 
37
  %%%SUCURI.Settings.Scanner.IntegrityCache%%%
38
 
39
  %%%SUCURI.Settings.Scanner.IgnoreFolders%%%
inc/tpl/sitecheck-blacklist.html.tpl CHANGED
@@ -1,8 +1,6 @@
1
 
2
  <div class="sucuriscan-panel sucuriscan-sitecheck-list sucuriscan-sitecheck-blacklist">
3
- <h3 class="sucuriscan-tag-title sucuriscan-tag-%%SUCURI.Blacklist.Color%%">
4
- %%SUCURI.Blacklist.Title%%
5
- </h3>
6
 
7
  <ul>
8
  %%%SUCURI.Blacklist.Content%%%
1
 
2
  <div class="sucuriscan-panel sucuriscan-sitecheck-list sucuriscan-sitecheck-blacklist">
3
+ <h3 class="sucuriscan-tag-title sucuriscan-tag-%%SUCURI.Blacklist.Color%%">%%SUCURI.Blacklist.Title%%</h3>
 
 
4
 
5
  <ul>
6
  %%%SUCURI.Blacklist.Content%%%
inc/tpl/sitecheck-blacklist.snippet.tpl CHANGED
@@ -1,6 +1,4 @@
1
 
2
  <li class="sucuriscan-sitecheck-list-%%SUCURI.Blacklist.Status%%">
3
- <a href="%%SUCURI.Blacklist.URL%%" target="_blank">
4
- %%SUCURI.Blacklist.Service%%
5
- </a>
6
  </li>
1
 
2
  <li class="sucuriscan-sitecheck-list-%%SUCURI.Blacklist.Status%%">
3
+ <a href="%%SUCURI.Blacklist.URL%%" target="_blank" rel="noopener">%%SUCURI.Blacklist.Service%%</a>
 
 
4
  </li>
inc/tpl/sitecheck-malware.html.tpl CHANGED
@@ -17,6 +17,6 @@
17
  </ul>
18
 
19
  <div class="sucuriscan-sitecheck-footnote">
20
- <p>@@SUCURI.SiteCheckNoResults@@</p>
21
  </div>
22
  </div>
17
  </ul>
18
 
19
  <div class="sucuriscan-sitecheck-footnote">
20
+ <p>If our free scanner did not detect any issue, you may have a more complicated and hidden problem. You can <a href="https://sucuri.net/website-security-platform/signup" target="_blank" rel="noopener">sign up with Sucuri</a> for a complete and in-depth scan + cleanup (not included in the free checks).</p>
21
  </div>
22
  </div>
inc/tpl/sitecheck-malware.snippet.tpl CHANGED
@@ -1,8 +1,8 @@
1
 
2
  <li class="sucuriscan-sitecheck-list-WARN">
3
- <a href="%%SUCURI.Malware.MalwareDocs%%" target="_blank">%%SUCURI.Malware.MalwareType%%</a>
4
 
5
- <p>%%SUCURI.Malware.AlertMessage%% <a href="%%SUCURI.Malware.InfectedURL%%" target="_blank">%%SUCURI.Malware.InfectedURL%%</a></p>
6
 
7
- <p><em class="sucuriscan-tooltip" content="%%SUCURI.Malware.MalwarePayload%%">(@@SUCURI.HoverForPayload@@)</em></p>
8
  </li>
1
 
2
  <li class="sucuriscan-sitecheck-list-WARN">
3
+ <a href="%%SUCURI.Malware.MalwareDocs%%" target="_blank" rel="noopener">%%SUCURI.Malware.MalwareType%%</a>
4
 
5
+ <p>%%SUCURI.Malware.AlertMessage%% <a href="%%SUCURI.Malware.InfectedURL%%" target="_blank" rel="noopener">%%SUCURI.Malware.InfectedURL%%</a></p>
6
 
7
+ <p><em class="sucuriscan-tooltip" content="%%SUCURI.Malware.MalwarePayload%%">(Hover to see the Payload)</em></p>
8
  </li>
inc/tpl/sitecheck-recommendations.html.tpl CHANGED
@@ -1,6 +1,6 @@
1
 
2
  <div class="sucuriscan-panel sucuriscan-sitecheck-list sucuriscan-sitecheck-recommendations sucuriscan-%%SUCURI.Recommendations.Visibility%%">
3
- <h3 class="sucuriscan-tag-title sucuriscan-tag-blue">@@SUCURI.Recomendations@@</h3>
4
 
5
  <ul>
6
  %%%SUCURI.Recommendations.Content%%%
1
 
2
  <div class="sucuriscan-panel sucuriscan-sitecheck-list sucuriscan-sitecheck-recommendations sucuriscan-%%SUCURI.Recommendations.Visibility%%">
3
+ <h3 class="sucuriscan-tag-title sucuriscan-tag-blue">Recomendations</h3>
4
 
5
  <ul>
6
  %%%SUCURI.Recommendations.Content%%%
inc/tpl/sitecheck-recommendations.snippet.tpl CHANGED
@@ -1,6 +1,6 @@
1
 
2
  <li class="sucuriscan-sitecheck-list-NOTICE">
3
- <a href="%%SUCURI.Recommendations.URL%%" target="_blank">
4
  <b>%%SUCURI.Recommendations.Title%%</b><br>
5
  <span>%%SUCURI.Recommendations.Value%%</span>
6
  </a>
1
 
2
  <li class="sucuriscan-sitecheck-list-NOTICE">
3
+ <a href="%%SUCURI.Recommendations.URL%%" target="_blank" rel="noopener">
4
  <b>%%SUCURI.Recommendations.Title%%</b><br>
5
  <span>%%SUCURI.Recommendations.Value%%</span>
6
  </a>
inc/tpl/sitecheck-target.html.tpl CHANGED
@@ -1,23 +1,20 @@
1
 
2
  <div class="sucuriscan-panel">
3
- <h3 class="sucuriscan-title">@@SUCURI.SiteCheckTarget@@</h3>
4
 
5
  <div class="inside">
6
- <p>@@SUCURI.SiteCheckTargetInfo@@</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
9
- <span>@@SUCURI.SiteCheckTarget@@ &mdash; <a target="_blank"
10
- href="https://sitecheck.sucuri.net/results/%%SUCURI.SiteCheck.Target%%">
11
- https://sitecheck.sucuri.net/results/%%SUCURI.SiteCheck.Target%%</a>
12
- </span>
13
  </div>
14
 
15
  <form action="%%SUCURI.URL.Settings%%#apiservice" method="post">
16
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
17
  <fieldset class="sucuriscan-clearfix">
18
- <label>@@SUCURI.SiteCheckTarget@@:</label>
19
  <input type="text" name="sucuriscan_sitecheck_target" />
20
- <button type="submit" class="button button-primary">@@SUCURI.Submit@@</button>
21
  </fieldset>
22
  </form>
23
  </div>
1
 
2
  <div class="sucuriscan-panel">
3
+ <h3 class="sucuriscan-title">Malware Scan Target</h3>
4
 
5
  <div class="inside">
6
+ <p>The remote malware scanner provided by the plugin is powered by <a href="https://sitecheck.sucuri.net/" target="_blank" rel="noopener">Sucuri SiteCheck</a>, a service that takes a publicly accessible URL and scans it for malicious code. If your website is not visible to the Internet, for example, if it is hosted in a local development environment or a restricted network, the scanner will not be able to work on it. Additionally, if the website was installed in a non-standard directory the scanner will report a "404 Not Found" error. You can use this option to change the URL that will be scanned.</p>
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
9
+ <span>Malware Scan Target &mdash; <a href="https://sitecheck.sucuri.net/results/%%SUCURI.SiteCheck.Target%%" target="_blank" rel="noopener">https://sitecheck.sucuri.net/results/%%SUCURI.SiteCheck.Target%%</a></span>
 
 
 
10
  </div>
11
 
12
  <form action="%%SUCURI.URL.Settings%%#apiservice" method="post">
13
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
14
  <fieldset class="sucuriscan-clearfix">
15
+ <label>Malware Scan Target:</label>
16
  <input type="text" name="sucuriscan_sitecheck_target" />
17
+ <button type="submit" class="button button-primary">Submit</button>
18
  </fieldset>
19
  </form>
20
  </div>
languages/index.html DELETED
@@ -1 +0,0 @@
1
- <!-- Avoid the directory listing. -->
 
languages/sucuri-scanner-en_US.mo DELETED
Binary file
languages/sucuri-scanner-en_US.po DELETED
@@ -1,1512 +0,0 @@
1
- msgid ""
2
- msgstr ""
3
- "Content-Transfer-Encoding: 8bit\n"
4
- "Content-Type: text/plain; charset=UTF-8\n"
5
- "Language-Team: https://sucuri.net/\n"
6
- "Language: en_US\n"
7
- "Last-Translator: Sucuri Inc. <info@sucuri.net>\n"
8
- "MIME-Version: 1.0\n"
9
- "Plural-Forms: nplurals=2; plural=(n != 1);\n"
10
- "PO-Revision-Date: \n"
11
- "POT-Creation-Date: 2017-06-01 09:00-0700\n"
12
- "Project-Id-Version: Sucuri Scanner\n"
13
-
14
- msgid "Review"
15
- msgstr "Review"
16
-
17
- msgid "Dashboard"
18
- msgstr "Dashboard"
19
-
20
- msgid "Firewall"
21
- msgstr "Firewall (WAF)"
22
-
23
- msgid "LastLogins"
24
- msgstr "Last Logins"
25
-
26
- msgid "Settings"
27
- msgstr "Settings"
28
-
29
- msgid "AuditLogs"
30
- msgstr "Audit Logs"
31
-
32
- msgid "ClearCache"
33
- msgstr "Clear Cache"
34
-
35
- msgid "IPAccess"
36
- msgstr "IP Access"
37
-
38
- msgid "GenerateAPIKey"
39
- msgstr "Generate API Key"
40
-
41
- msgid "APIKeyRecovery"
42
- msgstr "Plugin API Key Recovery"
43
-
44
- msgid "Copyright"
45
- msgstr "Copyright &copy; %%SUCURI.Year%% Sucuri Inc. All Rights Reserved."
46
-
47
- msgid "AccessDenied"
48
- msgstr "Access denied by Sucuri Inc."
49
-
50
- msgid "NonceFailure"
51
- msgstr "WordPress Nonce verification failed, try again going back and checking the form."
52
-
53
- msgid "NewsletterInvitation"
54
- msgstr "Do you want to get vulnerability disclosures? Subscribe to our newsletter <a href=\"http://sucuri.hs-sites.com/subscribe-to-security\" target=\"_blank\" rel=\"noopener\">here</a>"
55
-
56
- msgid "EnableAPIServiceAgain"
57
- msgstr "API service communication is disabled, if you just updated the plugin this might be a good opportunity to test this feature once again with the new code. Enable it again from the \"API Service\" panel located in the settings page."
58
-
59
- msgid "Save"
60
- msgstr "Save"
61
-
62
- msgid "Delete"
63
- msgstr "Delete"
64
-
65
- msgid "InvalidAPIKey"
66
- msgstr "Invalid API key format"
67
-
68
- msgid "ErrorNoInfo"
69
- msgstr "Unknown error, there is no information."
70
-
71
- msgid "ErrorLogFileNotFound"
72
- msgstr "; this generally happens when you add an invalid API key, the key will be deleted automatically to hide these warnings, if you want to recover it go to the settings page and use the recover button to send the key to your email address: %1$s"
73
-
74
- msgid "ErrorWrongAPIKey"
75
- msgstr "; invalid firewall API key: %1$s"
76
-
77
- msgid "ErrorSSLCertificate"
78
- msgstr ". Your website seems to use an old version of the OpenSSL library or the CURL extension was compiled without support for the algorithm used on the API service. Contact your hosting provider to fix this issue."
79
-
80
- msgid "ErrorInvalidEmail"
81
- msgstr "Email has an invalid format, or the host associated to the email has no MX records."
82
-
83
- msgid "AlertAPIKeySet"
84
- msgstr "The API key for your site was successfully generated and saved."
85
-
86
- msgid "Yes"
87
- msgstr "Yes"
88
-
89
- msgid "No"
90
- msgstr "No"
91
-
92
- msgid "Okay"
93
- msgstr "OK"
94
-
95
- msgid "Enable"
96
- msgstr "Enable"
97
-
98
- msgid "Enabled"
99
- msgstr "Enabled"
100
-
101
- msgid "Disable"
102
- msgstr "Disable"
103
-
104
- msgid "Disabled"
105
- msgstr "Disabled"
106
-
107
- msgid "Ignore"
108
- msgstr "Ignore"
109
-
110
- msgid "Unignore"
111
- msgstr "Unignore"
112
-
113
- msgid "Ignored"
114
- msgstr "Ignored"
115
-
116
- msgid "Submit"
117
- msgstr "Submit"
118
-
119
- msgid "Done"
120
- msgstr "Done"
121
-
122
- msgid "Error"
123
- msgstr "Error"
124
-
125
- msgid "Good"
126
- msgstr "good"
127
-
128
- msgid "Edit"
129
- msgstr "Edit"
130
-
131
- msgid "NotSet"
132
- msgstr "(not set)"
133
-
134
- msgid "Download"
135
- msgstr "Download"
136
-
137
- msgid "NotRandomized"
138
- msgstr "not randomized"
139
-
140
- msgid "NotWritable"
141
- msgstr "Not Writable"
142
-
143
- msgid "ParentNotWritable"
144
- msgstr "File parent directory is not writable."
145
-
146
- msgid "DoesNotExist"
147
- msgstr "Does Not Exist"
148
-
149
- msgid "Email"
150
- msgstr "E-mail"
151
-
152
- msgid "Website"
153
- msgstr "Website"
154
-
155
- msgid "Message"
156
- msgstr "Message"
157
-
158
- msgid "Information"
159
- msgstr "Information"
160
-
161
- msgid "Event"
162
- msgstr "Event"
163
-
164
- msgid "Exists"
165
- msgstr "Exists"
166
-
167
- msgid "Writable"
168
- msgstr "Writable"
169
-
170
- msgid "Today"
171
- msgstr "Today"
172
-
173
- msgid "NoLogs"
174
- msgstr "There are no logs."
175
-
176
- msgid "SelfHosting"
177
- msgstr "Log Exporter"
178
-
179
- msgid "SelfHostingEnabled"
180
- msgstr "The log exporter feature has been enabled and the data file was successfully set."
181
-
182
- msgid "SelfHostingDisabled"
183
- msgstr "The log exporter feature has been disabled"
184
-
185
- msgid "SelfHostingInfo"
186
- msgstr "This option allows you to export the WordPress audit logs to a local log file that can be read by a SIEM or any log analysis software <em>(we recommend OSSEC)</em>. That will give visibility from within WordPress to complement your log monitoring infrastructure. <b>NOTE:</b> Do not use a publicly accessible file, you must use a file at least one level up the document root to prevent leaks of information."
187
-
188
- msgid "SelfHostingFallback"
189
- msgstr "You don't have a valid API key to communicate with the remote API service. However, the self-hosting monitor is enabled, the plugin will read the logs from that file and display the data here. Notice that only the latest logs will be processed to keep a low memory footprint. Consider to generate a free API key to get a better coverage of the activity in your website."
190
-
191
- msgid "AuditLogsCache"
192
- msgstr "This data is cached for %%SUCURI.AuditLogs.Lifetime%% seconds"
193
-
194
- msgid "Refresh"
195
- msgstr "Refresh"
196
-
197
- msgid "AuditLogsNoAPI"
198
- msgstr "The API service is not available; logs are coming from the local server"
199
-
200
- msgid "AuditLogsQueue"
201
- msgstr "logs in the queue"
202
-
203
- msgid "SendLogs"
204
- msgstr "Send Logs"
205
-
206
- msgid "UnsupportedWordPress"
207
- msgstr "WordPress version is not supported."
208
-
209
- msgid "NoWordPressFile"
210
- msgstr "File is not part of the official WordPress installation."
211
-
212
- msgid "ThereAreNoDifferences"
213
- msgstr "There are no differences between the installed and original files"
214
-
215
- msgid "ScheduledTask"
216
- msgstr "%1$s (every %2$d seconds)"
217
-
218
- msgid "ScheduledTaskNever"
219
- msgstr "Never (no execution)"
220
-
221
- msgid "SucuriAlert"
222
- msgstr "Sucuri Alert"
223
-
224
- msgid "UpdatedEmailSubject"
225
- msgstr "The email subject has been successfully updated"
226
-
227
- msgid "InvalidEmailSubject"
228
- msgstr "Invalid characters in the email subject."
229
-
230
- msgid "EmailSubject.available_updates"
231
- msgstr "Available Updates"
232
-
233
- msgid "EmailSubject.bruteforce_attack"
234
- msgstr "Bruteforce Attack"
235
-
236
- msgid "EmailSubject.failed_login"
237
- msgstr "Failed Login"
238
-
239
- msgid "EmailSubject.plugin_activated"
240
- msgstr "Plugin Activated"
241
-
242
- msgid "EmailSubject.plugin_change"
243
- msgstr "Plugin Change"
244
-
245
- msgid "EmailSubject.plugin_deactivated"
246
- msgstr "Plugin Deactivated"
247
-
248
- msgid "EmailSubject.plugin_deleted"
249
- msgstr "Plugin Deleted"
250
-
251
- msgid "EmailSubject.plugin_installed"
252
- msgstr "Plugin Installed"
253
-
254
- msgid "EmailSubject.plugin_updated"
255
- msgstr "Plugin Updated"
256
-
257
- msgid "EmailSubject.post_publication"
258
- msgstr "Post Publication"
259
-
260
- msgid "EmailSubject.scan_checksums"
261
- msgstr "Scan Checksums"
262
-
263
- msgid "EmailSubject.settings_updated"
264
- msgstr "Settings Updated"
265
-
266
- msgid "EmailSubject.success_login"
267
- msgstr "Success Login"
268
-
269
- msgid "EmailSubject.theme_activated"
270
- msgstr "Theme Activated"
271
-
272
- msgid "EmailSubject.theme_deleted"
273
- msgstr "Theme Deleted"
274
-
275
- msgid "EmailSubject.theme_editor"
276
- msgstr "Theme Editor"
277
-
278
- msgid "EmailSubject.theme_installed"
279
- msgstr "Theme Installed"
280
-
281
- msgid "EmailSubject.theme_updated"
282
- msgstr "Theme Updated"
283
-
284
- msgid "EmailSubject.user_registration"
285
- msgstr "User Registration"
286
-
287
- msgid "EmailSubject.website_updated"
288
- msgstr "Website Updated"
289
-
290
- msgid "EmailSubject.widget_added"
291
- msgstr "Widget Added"
292
-
293
- msgid "EmailSubject.widget_deleted"
294
- msgstr "Widget Deleted"
295
-
296
- msgid "EmailSubject.post_update"
297
- msgstr "Post Update"
298
-
299
- msgid "EmailSubject.core_integrity_checks"
300
- msgstr "Core Integrity Checks"
301
-
302
- msgid "EmailSubject.password_change"
303
- msgstr "Password Change"
304
-
305
- msgid "FailedLoginFooter"
306
- msgstr "<br><br><em>Explanation: Someone failed to login to your site. If you are getting too many of these messages, it is likely your site is under a password guessing brute-force attack [1]. You can disable the failed login alerts from here [2]. Alternatively, you can consider to install a firewall between your website and your visitors to filter out these and other attacks, take a look at Sucuri Firewall [3].</em><br><br>[1] <a href='https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing'>https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing</a><br>[2] <a href='%1$s'>%2$s</a> <br>[3] <a href='https://sucuri.net/website-firewall/?wpalert'>https://sucuri.net/website-firewall/</a><br>"
307
-
308
- msgid "FirewallAPIKeySet"
309
- msgstr "Firewall API key was successfully saved"
310
-
311
- msgid "FirewallAPIKeyUnset"
312
- msgstr "Firewall API key was successfully removed"
313
-
314
- msgid "FirewallAPIKeyInvalid"
315
- msgstr "Invalid firewall API key"
316
-
317
- msgid "FirewallAPIKeyMissing"
318
- msgstr "Firewall API key was not found."
319
-
320
- msgid "NoData"
321
- msgstr "no data available"
322
-
323
- msgid "FirewallDoCache"
324
- msgstr "enabled (recommended)"
325
-
326
- msgid "FirewallSiteCache"
327
- msgstr "site caching (using your site headers)"
328
-
329
- msgid "FirewallNoCache"
330
- msgstr "minimal (only for a few minutes)"
331
-
332
- msgid "FirewallNoCacheAtAll"
333
- msgstr "caching disabled (use with caution)"
334
-
335
- msgid "FirewallNotEnabled"
336
- msgstr "Firewall is not enabled on your site, or your API key is invalid."
337
-
338
- msgid "FirewallClearCacheFailure"
339
- msgstr "Could not clear the cache of your site, try later again."
340
-
341
- msgid "Active"
342
- msgstr "active"
343
-
344
- msgid "NotActive"
345
- msgstr "not active"
346
-
347
- msgid "Unknown"
348
- msgstr "unknown"
349
-
350
- msgid "Undefined"
351
- msgstr "Undefined"
352
-
353
- msgid "January"
354
- msgstr "January"
355
-
356
- msgid "February"
357
- msgstr "February"
358
-
359
- msgid "March"
360
- msgstr "March"
361
-
362
- msgid "April"
363
- msgstr "April"
364
-
365
- msgid "May"
366
- msgstr "May"
367
-
368
- msgid "June"
369
- msgstr "June"
370
-
371
- msgid "July"
372
- msgstr "July"
373
-
374
- msgid "August"
375
- msgstr "August"
376
-
377
- msgid "September"
378
- msgstr "September"
379
-
380
- msgid "October"
381
- msgstr "October"
382
-
383
- msgid "November"
384
- msgstr "November"
385
-
386
- msgid "December"
387
- msgstr "December"
388
-
389
- msgid "Username"
390
- msgstr "Username"
391
-
392
- msgid "Password"
393
- msgstr "Password"
394
-
395
- msgid "RemoteAddr"
396
- msgstr "IP Address"
397
-
398
- msgid "Hostname"
399
- msgstr "Hostname"
400
-
401
- msgid "Browser"
402
- msgstr "Web Browser"
403
-
404
- msgid "Datetime"
405
- msgstr "Date/Time"
406
-
407
- msgid "Block"
408
- msgstr "Block"
409
-
410
- msgid "Unblock"
411
- msgstr "Unblock"
412
-
413
- msgid "BlockedAt"
414
- msgstr "Blocked At"
415
-
416
- msgid "FirstAttempt"
417
- msgstr "First Attempt"
418
-
419
- msgid "LastAttempt"
420
- msgstr "Last Attempt"
421
-
422
- msgid "LastActivity"
423
- msgstr "Last Activity"
424
-
425
- msgid "AttemptTimestamp"
426
- msgstr "Attempt Timestamp"
427
-
428
- msgid "AttemptDatetime"
429
- msgstr "Attempt Date/Time"
430
-
431
- msgid "ConfirmOperation"
432
- msgstr "You need to confirm that you understand the risk of this operation."
433
-
434
- msgid "UnderstandTheRisk"
435
- msgstr "I understand that this operation can not be reverted."
436
-
437
- msgid "NonSupportedAction"
438
- msgstr "Requested action is not supported."
439
-
440
- msgid "NonSupportedLanguage"
441
- msgstr "Selected language is not supported."
442
-
443
- msgid "NothingSelected"
444
- msgstr "Nothing was selected from the list."
445
-
446
- msgid "ItemsProcessed"
447
- msgstr "The selected files have been successfully processed."
448
-
449
- msgid "AllItemsProcessed"
450
- msgstr "<b>%1$d</b> out of <b>%2$d</b> files were successfully processed."
451
-
452
- msgid "SomeItemsProcessed"
453
- msgstr "Only <b>%1$d</b> out of <b>%2$d</b> files were processed."
454
-
455
- msgid "ErrorIntegrityAdded"
456
- msgstr "The plugin has no permission to delete this file because it was created by a different system user who has more privileges than your account. Please use FTP to delete it."
457
-
458
- msgid "ErrorIntegrityModified"
459
- msgstr "The plugin has no permission to restore this file because it was modified by a different system user who has more privileges than your account. Please use FTP to restore it."
460
-
461
- msgid "ErrorIntegrityRemoved"
462
- msgstr "The plugin has no permission to restore this file because its directory is owned by a different system user who has more privileges than your account. Please use FTP to restore it."
463
-
464
- msgid "SecurityAlerts"
465
- msgstr "Security Alerts"
466
-
467
- msgid "NoAlertsError"
468
- msgstr "You have installed a plugin or theme that is not fully compatible with our plugin, some of the security alerts (like the successful and failed logins) will not be sent to you. To prevent an infinite loop while detecting these changes in the website and sending the email alerts via a custom SMTP plugin, we have decided to stop any attempt to send the emails to prevent fatal errors."
469
-
470
- msgid "AlertSettingsUpdated"
471
- msgstr "The alert settings have been updated"
472
-
473
- msgid "OptionNotifyPluginChange"
474
- msgstr "Receive email alerts for changes in the settings of the Sucuri plugin"
475
-
476
- msgid "OptionPrettifyMails"
477
- msgstr "Receive email alerts in HTML <em>(there may be issues with some mail services)</em>"
478
-
479
- msgid "OptionUseWordPressMail"
480
- msgstr "Use WordPress functions to send mails <em>(uncheck to use native PHP functions)</em>"
481
-
482
- msgid "OptionLastLoginRedirection"
483
- msgstr "Allow redirection after login to report the last-login information"
484
-
485
- msgid "OptionNotifyScanChecksums"
486
- msgstr "Receive email alerts for core integrity checks"
487
-
488
- msgid "OptionNotifyAvailableUpdates"
489
- msgstr "Receive email alerts for available updates"
490
-
491
- msgid "OptionNotifyUserRegistration"
492
- msgstr "Receive email alerts for new user registration"
493
-
494
- msgid "OptionNotifySuccessLogin"
495
- msgstr "Receive email alerts for successful login attempts"
496
-
497
- msgid "OptionNotifyFailedLogin"
498
- msgstr "Receive email alerts for failed login attempts <em>(you may receive tons of emails)</em>"
499
-
500
- msgid "OptionNotifyFailedPassword"
501
- msgstr "Receive email alerts for failed login attempts including the submitted password"
502
-
503
- msgid "OptionNotifyBruteforceAttack"
504
- msgstr "Receive email alerts for password guessing attacks <em>(summary of failed logins per hour)</em>"
505
-
506
- msgid "OptionNotifyPostPublication"
507
- msgstr "Receive email alerts for changes in the post status <em>(configure from Ignore Posts Changes)</em>"
508
-
509
- msgid "OptionNotifyWebsiteUpdated"
510
- msgstr "Receive email alerts when the WordPress version is updated"
511
-
512
- msgid "OptionNotifySettingsUpdated"
513
- msgstr "Receive email alerts when your website settings are updated"
514
-
515
- msgid "OptionNotifyThemeEditor"
516
- msgstr "Receive email alerts when a file is modified with theme/plugin editor"
517
-
518
- msgid "OptionNotifyPluginInstalled"
519
- msgstr "Receive email alerts when a <b>plugin is installed</b>"
520
-
521
- msgid "OptionNotifyPluginActivated"
522
- msgstr "Receive email alerts when a <b>plugin is activated</b>"
523
-
524
- msgid "OptionNotifyPluginDeactivated"
525
- msgstr "Receive email alerts when a <b>plugin is deactivated</b>"
526
-
527
- msgid "OptionNotifyPluginUpdated"
528
- msgstr "Receive email alerts when a <b>plugin is updated</b>"
529
-
530
- msgid "OptionNotifyPluginDeleted"
531
- msgstr "Receive email alerts when a <b>plugin is deleted</b>"
532
-
533
- msgid "OptionNotifyWidgetAdded"
534
- msgstr "Receive email alerts when a <b>widget is added</b> to a sidebar"
535
-
536
- msgid "OptionNotifyWidgetDeleted"
537
- msgstr "Receive email alerts when a <b>widget is deleted</b> from a sidebar"
538
-
539
- msgid "OptionNotifyThemeInstalled"
540
- msgstr "Receive email alerts when a <b>theme is installed</b>"
541
-
542
- msgid "OptionNotifyThemeActivated"
543
- msgstr "Receive email alerts when a <b>theme is activated</b>"
544
-
545
- msgid "OptionNotifyThemeUpdated"
546
- msgstr "Receive email alerts when a <b>theme is updated</b>"
547
-
548
- msgid "OptionNotifyThemeDeleted"
549
- msgstr "Receive email alerts when a <b>theme is deleted</b>"
550
-
551
- msgid "MaximumAlertsSuccess"
552
- msgstr "The maximum number of alerts per hour has been updated"
553
-
554
- msgid "MaximumAlertsFailure"
555
- msgstr "Error updating the maximum number of alerts per hour"
556
-
557
- msgid "OptionPerHour5"
558
- msgstr "Maximum 5 per hour"
559
-
560
- msgid "OptionPerHour10"
561
- msgstr "Maximum 10 per hour"
562
-
563
- msgid "OptionPerHour20"
564
- msgstr "Maximum 20 per hour"
565
-
566
- msgid "OptionPerHour40"
567
- msgstr "Maximum 40 per hour"
568
-
569
- msgid "OptionPerHour80"
570
- msgstr "Maximum 80 per hour"
571
-
572
- msgid "OptionPerHour160"
573
- msgstr "Maximum 160 per hour"
574
-
575
- msgid "BruteForceAlertSuccess"
576
- msgstr "The plugin will assume that your website is under a brute-force attack after %1$s failed logins are detected during the same hour"
577
-
578
- msgid "BruteForceAlertFailure"
579
- msgstr "Invalid number of failed logins per hour to consider your website under a brute-force attack, try another number"
580
-
581
- msgid "OptionPerHourUnlimited"
582
- msgstr "Unlimited alerts per hour"
583
-
584
- msgid "OptionFailedLogins30"
585
- msgstr "30 failed logins per hour"
586
-
587
- msgid "OptionFailedLogins60"
588
- msgstr "60 failed logins per hour"
589
-
590
- msgid "OptionFailedLogins120"
591
- msgstr "120 failed logins per hour"
592
-
593
- msgid "OptionFailedLogins240"
594
- msgstr "240 failed logins per hour"
595
-
596
- msgid "OptionFailedLogins480"
597
- msgstr "480 failed logins per hour"
598
-
599
- msgid "HTAccessIsMissing"
600
- msgstr "Access control file does not exists"
601
-
602
- msgid "HTAccessNotWritable"
603
- msgstr "Access control file is not writable"
604
-
605
- msgid "DiffUtility"
606
- msgstr "WordPress Integrity Diff Utility"
607
-
608
- msgid "DiffUtilityDescription"
609
- msgstr "If your server allows the execution of system commands, you can configure the plugin to use the <a href=\"https://en.wikipedia.org/wiki/Diff_utility\" target=\"_blank\" rel=\"noopener\">Unix Diff Utility</a> to compare the actual content of the file installed in the website and the original file provided by WordPress. This will show the differences between both files and then you can act upon the information provided."
610
-
611
- msgid "DiffUtilityInfo"
612
- msgstr "The Unix Diff Utility is enabled. You can click the files in the table to see the differences detected by the scanner. If you consider the differences to be harmless you can mark the file as fixed, otherwise it is adviced to restore the original content immediately."
613
-
614
- msgid "DiffUtilityInstructions"
615
- msgstr "Lines with a <b>minus</b> sign as the prefix <em>(here in red)</em> show the original code. Lines with a <b>plus</b> sign as the prefix <em>(here in green)</em> show the modified code. You can read more about the DIFF format from the WikiPedia article about the <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/Diff_utility\" rel=\"noopener\">Unix Diff Utility</a>."
616
-
617
- msgid "AccountsWereBlocked"
618
- msgstr "Selected user accounts were blocked"
619
-
620
- msgid "AccountsWereUnblocked"
621
- msgstr "Selected user accounts were unblocked"
622
-
623
- msgid "LastLoginMessage"
624
- msgstr "Last login was at <b>%1$s</b> from <b>%2$s</b> <em>(%3$s)</em> <a href='%4$s'>view all logs</a>"
625
-
626
- msgid "LastLoginsNotWritable"
627
- msgstr "Last-logins data file is not writable: <code>%1$s</code>"
628
-
629
- msgid "UserInfo"
630
- msgstr "User: %1$s (%2$s)"
631
-
632
- msgid "ReverseProxy"
633
- msgstr "Reverse Proxy"
634
-
635
- msgid "ReverseProxyStatus"
636
- msgstr "Reverse proxy support was set to <b>%1$s</b>"
637
-
638
- msgid "ReverseProxyInfo"
639
- msgstr "The event monitor uses the API address of the origin of the request to track the actions, the plugin uses two methods to retrieve this: the main method uses the global server variable <em>Remote-Addr</em> available in most modern web servers, an alternative method uses custom HTTP headers <em>(which are unsafe by default)</em>. You should not worry about this option unless you know what a reverse proxy is. Services like the <a href=\"https://sucuri.net/website-firewall/\" target=\"_blank\">Sucuri Firewall</a> &mdash; once active &mdash; forces the network traffic to pass through them to filter any security threat that may affect the original server. A side effect of this is that the real IP address is no longer available in the global server variable <em>REMOTE-ADDR</em> but in a custom HTTP header with a name provided by the service."
640
-
641
- msgid "IPDiscoverer"
642
- msgstr "IP Address Discoverer"
643
-
644
- msgid "IPDiscovererInfo"
645
- msgstr "IP address discoverer will use DNS lookups to automatically detect if the website is behind the <a href=\"https://sucuri.net/website-firewall/\" target=\"_blank\">Sucuri Firewall</a> in which case will modify the global server variable <em>Remote-Addr</em> to set the real IP of the website's visitors. This check runs on every WordPress init action and that is why it may slow down your website as some hosting providers rely on slow DNS servers which makes the operation take more time than it should."
646
-
647
- msgid "HTTPHeader"
648
- msgstr "HTTP Header"
649
-
650
- msgid "HTTPHeaderStatus"
651
- msgstr "HTTP header was set to <code>%1$s</code>"
652
-
653
- msgid "DisallowedHTTPHeader"
654
- msgstr "HTTP header is not allowed"
655
-
656
- msgid "RequiresModernPHP"
657
- msgstr "The plugin requires PHP 5 >= 5.3.0 - OR - PHP 7"
658
-
659
- msgid "General"
660
- msgstr "General"
661
-
662
- msgid "Scanner"
663
- msgstr "Scanner"
664
-
665
- msgid "Hardening"
666
- msgstr "Hardening"
667
-
668
- msgid "PostHack"
669
- msgstr "Post-Hack"
670
-
671
- msgid "Alerts"
672
- msgstr "Alerts"
673
-
674
- msgid "WebsiteInfo"
675
- msgstr "Website Info"
676
-
677
- msgid "Loading"
678
- msgstr "Loading..."
679
-
680
- msgid "iFrames"
681
- msgstr "iFrames"
682
-
683
- msgid "Links"
684
- msgstr "Links"
685
-
686
- msgid "Scripts"
687
- msgstr "Scripts"
688
-
689
- msgid "iFramesNum"
690
- msgstr "iFrames: %1$d"
691
-
692
- msgid "LinksNum"
693
- msgstr "Links: %1$d"
694
-
695
- msgid "ScriptsNum"
696
- msgstr "Scripts: %1$d"
697
-
698
- msgid "Type"
699
- msgstr "Type"
700
-
701
- msgid "Name"
702
- msgstr "Name"
703
-
704
- msgid "Value"
705
- msgstr "Value"
706
-
707
- msgid "Version"
708
- msgstr "Version"
709
-
710
- msgid "NextDue"
711
- msgstr "Next Due"
712
-
713
- msgid "Schedule"
714
- msgstr "Schedule"
715
-
716
- msgid "Arguments"
717
- msgstr "Arguments"
718
-
719
- msgid "EnvVariables"
720
- msgstr "Environment Variables"
721
-
722
- msgid "ConfigVariables"
723
- msgstr "Configuration Variables"
724
-
725
- msgid "ConfigNotFound"
726
- msgstr "WordPress configuration file was not found."
727
-
728
- msgid "ConfigNotWritable"
729
- msgstr "WordPress configuration file is not writable."
730
-
731
- msgid "LastLoginsResetSuccess"
732
- msgstr "Last-Logins logs were successfully reset."
733
-
734
- msgid "LastLoginsResetFailure"
735
- msgstr "Could not reset the last-logins data file."
736
-
737
- msgid "WillReceiveAlerts"
738
- msgstr "The email alerts will be sent to: <code>%1$s</code>"
739
-
740
- msgid "WillNotReceiveAlerts"
741
- msgstr "These emails will stop receiving alerts: <code>%1$s</code>"
742
-
743
- msgid "TestAlertSent"
744
- msgstr "A test alert was sent to your email, check your inbox"
745
-
746
- msgid "InvalidEmail"
747
- msgstr "Email format not supported."
748
-
749
- msgid "TrustedIPDuplicate"
750
- msgstr "The IP specified address was already added."
751
-
752
- msgid "TrustedIPAdded"
753
- msgstr "Events generated from this IP will be ignored: <code>%1$s</code>"
754
-
755
- msgid "TrustedIPFailure"
756
- msgstr "The IP address could not be added to the trusted list"
757
-
758
- msgid "TrustedIPDeleted"
759
- msgstr "The selected IP addresses were successfully deleted."
760
-
761
- msgid "OnlyLowerUppercase"
762
- msgstr "Only lowercase letters and underscores are allowed."
763
-
764
- msgid "PostTypeIgnore"
765
- msgstr "Receive These Alerts"
766
-
767
- msgid "PostTypeUnignore"
768
- msgstr "Stop These Alerts"
769
-
770
- msgid "PostTypeIgnored"
771
- msgstr "Post-type has been successfully ignored."
772
-
773
- msgid "PostTypeUnignored"
774
- msgstr "The selected post-type will not be ignored anymore."
775
-
776
- msgid "PostTypeFailure"
777
- msgstr "The post-type is invalid or it may be already ignored."
778
-
779
- msgid "APIServiceChanged"
780
- msgstr "The status of the API service has been changed"
781
-
782
- msgid "PluginResetSuccess"
783
- msgstr "Local security logs, hardening and settings were deleted"
784
-
785
- msgid "StorageNotWritable"
786
- msgstr "Storage is not writable: <code>%1$s</code>"
787
-
788
- msgid "SiteWasRegistered"
789
- msgstr "Site registered successfully"
790
-
791
- msgid "NFilesWereDeleted"
792
- msgstr "%1$d out of %2$d files has been deleted"
793
-
794
- msgid "AvoidDocumentRoot"
795
- msgstr "File should not be publicly accessible."
796
-
797
- msgid "AvoidFileOverride"
798
- msgstr "File already exists and will not be overwritten."
799
-
800
- msgid "Cronjobs"
801
- msgstr "Scheduled Tasks"
802
-
803
- msgid "CronjobsInfo"
804
- msgstr "Scheduled tasks are rules registered in your database by a plugin, theme, or the base system itself; they are used to automatically execute actions defined in the code every certain amount of time. A good use of these rules is to generate backup files of your site, execute a security scanner, or remove unused elements like drafts. <b>Note:</b> Scheduled tasks can be re-installed by any plugin/theme automatically, consider to deactivate the plugin entirely if you want to get rid of the scheduled tasks."
805
-
806
- msgid "CronjobRunNow"
807
- msgstr "Execute Now (in +10 seconds)"
808
-
809
- msgid "CronjobsWillRunSoon"
810
- msgstr "%1$d tasks has been scheduled to run in the next ten seconds."
811
-
812
- msgid "CronjobsWereDeleted"
813
- msgstr "%1$d scheduled tasks have been removed."
814
-
815
- msgid "CronjobsWereReinstalled"
816
- msgstr "%1$d tasks has been re-scheduled to run <code>%2$s</code>.'"
817
-
818
- msgid "CronjobsWereNotSelected"
819
- msgstr "No scheduled tasks were selected from the list."
820
-
821
- msgid "DNSLookupStatus"
822
- msgstr "The status of the DNS lookups for the reverse proxy detection has been changed"
823
-
824
- msgid "IncorrectEncoding"
825
- msgstr "Data is incorrectly encoded"
826
-
827
- msgid "ImportCount"
828
- msgstr "%1$d out of %2$d option have been successfully imported"
829
-
830
- msgid "APIKey"
831
- msgstr "API Key"
832
-
833
- msgid "APIKeyInfo"
834
- msgstr "Most of the tools in this plugin can be used without a specific configuration, but the core features <b>require an API key</b> to communicate with the Sucuri services. The key is generated using your administrator e-mail and the domain of this site, this will allow you to have access to our free monitoring tool and other extra features."
835
-
836
- msgid "APIKeyTerms"
837
- msgstr "Generating an API key implies that you agree to send the information collected by the plugin to the Sucuri API service which is a remote server where the information for the audit logs is stored, this is to prevent malicious users to delete the logs during an attack which may affect an investigation if you suspect that your website was hacked. We also use this information to display <a href=\"https://sucuri.net/security-reports/brute-force/\" target=\"_blank\">statistics</a> and try to use the data in an anonymous way as we are concerned about your privacy too. Please do not generate an API key if you do not agree with this, you can keep using the plugin without it anyway."
838
-
839
- msgid "APIKeyMissing"
840
- msgstr "API key is missing"
841
-
842
- msgid "APIKeyInvalidDomain"
843
- msgstr "Your domain <code>%%SUCURI.CleanDomain%%</code> does not seems to have a DNS <code>A</code> record so it will be considered as <em>invalid</em> by the API interface when you request the generation of a new key. Adding <code>www</code> at the beginning of the domain name may fix this issue. If you do not understand what is this then send an email to our support team requesting the key."
844
-
845
- msgid "APIKeyRecoverButton"
846
- msgstr "Recover Via E-mail"
847
-
848
- msgid "APIKeyRecoveryCondition"
849
- msgstr "If you don't have access to the e-mail address used to generate the API key, but have a copy of the key at hand you can <a target=\"_self\" href=\"%%SUCURI.URL.Settings%%&recover\">click this link</a> to activate the plugin manually. Be aware that if the key is invalid the plugin will delete it afterwards."
850
-
851
- msgid "ApplyHardening"
852
- msgstr "Apply Hardening"
853
-
854
- msgid "RevertHardening"
855
- msgstr "Revert Hardening"
856
-
857
- msgid "HardeningFirewallTitle"
858
- msgstr "Website Firewall Protection"
859
-
860
- msgid "HardeningFirewallDescription"
861
- msgstr "A WAF is a protection layer for your web site, blocking all sort of attacks (brute force attempts, DDoS, SQL injections, etc) and helping it remain malware and blacklist free. This test checks if your site is using Sucuri Firewall to protect your site."
862
-
863
- msgid "HardeningFirewallPurchase"
864
- msgstr "The firewall is a premium service that you need purchase at - <a href='https://goo.gl/qfNkMq' target='_blank'>Sucuri Firewall</a>"
865
-
866
- msgid "HardeningVersionTitle"
867
- msgstr "Verify WordPress Version"
868
-
869
- msgid "HardeningVersionDescription"
870
- msgstr "Why keep your site updated? WordPress is an open-source project which means that with every update the details of the changes made to the source code are made public, if there were security fixes then someone with malicious intent can use this information to attack any site that has not been upgraded."
871
-
872
- msgid "HardeningPHPVersionTitle"
873
- msgstr "Verify PHP Version"
874
-
875
- msgid "HardeningPHPVersionDescription"
876
- msgstr "PHP %1$s is installed."
877
-
878
- msgid "HardeningPHPVersionLifetime"
879
- msgstr "Ask your hosting provider to install an updated version of PHP - <a href='http://php.net/supported-versions.php' target='_blank' rel='noopener'>List of PHP Supported Versions</a>"
880
-
881
- msgid "HardeningGeneratorTitle"
882
- msgstr "Remove WordPress Version"
883
-
884
- msgid "HardeningGeneratorDescription"
885
- msgstr "It checks if your WordPress version is being leaked to the public via a HTML meta-tag. Many web vulnerability scanners use this to determine which version of the code is running in your website. They use this to find disclosed vulnerabilities associated to this version number. A vulnerability scanner can still guess which version of WordPress is installed by comparing the checksum of some static files."
886
-
887
- msgid "HardeningNginxTitle"
888
- msgstr "Block of Certain PHP Files"
889
-
890
- msgid "HardeningNginxDescription"
891
- msgstr "Block the execution of PHP files in sensitive directories. Be careful while applying this hardening option as there are many plugins and theme which rely on the ability to execute PHP files in the content directory to generate images or save temporary data. Use the 'Whitelist PHP Files' tool to add exceptions to individual files."
892
-
893
- msgid "HardeningNginxField"
894
- msgstr "Check Hardening"
895
-
896
- msgid "HardeningNginxSuggestion"
897
- msgstr "Read the official WordPress guidelines to learn how to restrict access to PHP files in sensitive directories - <a href='https://codex.wordpress.org/Nginx#Global_restrictions_file' target='_blank' rel='noopener'>Nginx Global Restrictions For WordPress</a>"
898
-
899
- msgid "HardeningUploadsTitle"
900
- msgstr "Block PHP Files in Uploads Directory"
901
-
902
- msgid "HardeningUploadsDescription"
903
- msgstr "Block the execution of PHP files in sensitive directories. Be careful while applying this hardening option as there are many plugins and theme which rely on the ability to execute PHP files in the content directory to generate images or save temporary data. Use the 'Whitelist PHP Files' tool to add exceptions to individual files."
904
-
905
- msgid "HardeningUploadsApplySuccess"
906
- msgstr "Hardening applied to the uploads directory"
907
-
908
- msgid "HardeningUploadsApplyFailure"
909
- msgstr "Error hardening directory, check the permissions."
910
-
911
- msgid "HardeningUploadsRevertSuccess"
912
- msgstr "Hardening reverted in the uploads directory"
913
-
914
- msgid "HardeningUploadsRevertFailure"
915
- msgstr "Access file is not writable, check the permissions."
916
-
917
- msgid "HardeningContentTitle"
918
- msgstr "Block PHP Files in WP-CONTENT Directory"
919
-
920
- msgid "HardeningContentDescription"
921
- msgstr "Block the execution of PHP files in sensitive directories. Be careful while applying this hardening option as there are many plugins and theme which rely on the ability to execute PHP files in the content directory to generate images or save temporary data. Use the 'Whitelist PHP Files' tool to add exceptions to individual files."
922
-
923
- msgid "HardeningContentApplySuccess"
924
- msgstr "Hardening applied to the content directory"
925
-
926
- msgid "HardeningContentApplyFailure"
927
- msgstr "Error hardening directory, check the permissions."
928
-
929
- msgid "HardeningContentRevertSuccess"
930
- msgstr "Hardening reverted in the content directory"
931
-
932
- msgid "HardeningContentRevertFailure"
933
- msgstr "Access file is not writable, check the permissions."
934
-
935
- msgid "HardeningIncludesTitle"
936
- msgstr "Block PHP Files in WP-INCLUDES Directory"
937
-
938
- msgid "HardeningIncludesDescription"
939
- msgstr "Block the execution of PHP files in sensitive directories. Be careful while applying this hardening option as there are many plugins and theme which rely on the ability to execute PHP files in the content directory to generate images or save temporary data. Use the 'Whitelist PHP Files' tool to add exceptions to individual files."
940
-
941
- msgid "HardeningIncludesApplySuccess"
942
- msgstr "Hardening applied to the library directory"
943
-
944
- msgid "HardeningIncludesApplyFailure"
945
- msgstr "Error hardening directory, check the permissions."
946
-
947
- msgid "HardeningIncludesRevertSuccess"
948
- msgstr "Hardening reverted in the library directory"
949
-
950
- msgid "HardeningIncludesRevertFailure"
951
- msgstr "Access file is not writable, check the permissions."
952
-
953
- msgid "HardeningReadmeTitle"
954
- msgstr "Information Leakage"
955
-
956
- msgid "HardeningReadmeDescription"
957
- msgstr "Checks if the WordPress README file still exists in the website. The information in this file can be used by malicious users to pin-point which disclosed vulnerabilities are associated to the website. Be aware that WordPress recreates this file automatically with every update."
958
-
959
- msgid "HardeningReadmeApplySuccess"
960
- msgstr "Hardening applied to the <code>readme.html</code> file"
961
-
962
- msgid "HardeningReadmeApplyFailure"
963
- msgstr "Cannot delete <code>%1$s/readme.html</code>"
964
-
965
- msgid "HardeningAdminUserTitle"
966
- msgstr "Default Admin Account"
967
-
968
- msgid "HardeningAdminUserDescription"
969
- msgstr "Check if the primary user account still uses the name 'admin'. This allows malicious users to easily identify which account has the highest privileges to target an attack."
970
-
971
- msgid "HardeningFileEditorTitle"
972
- msgstr "Plugin and Theme Editor"
973
-
974
- msgid "HardeningFileEditorDescription"
975
- msgstr "Disables the theme and plugin editors to prevent unwanted modifications to the code. If you are having problems reverting this hardening option please open the wp-config.php file and delete the line with the constant DISALLOW_FILE_EDIT."
976
-
977
- msgid "HardeningFileEditorApplySuccess"
978
- msgstr "Hardening applied to the plugin and theme editor"
979
-
980
- msgid "HardeningFileEditorRevertSuccess"
981
- msgstr "Hardening reverted in the plugin and theme editor"
982
-
983
- msgid "HardeningFileEditorRevertFailure"
984
- msgstr "File Editor was not disabled using this tool. You must scan your project for a constant defined as DISALLOW_FILE_EDIT, then either delete it or set its value to False. Any plugin/theme can disable the file editor, so it is impossible to determine the origin of the constant."
985
-
986
- msgid "PHPWhitelistSuccess"
987
- msgstr "The file has been whitelisted from the hardening"
988
-
989
- msgid "PHPWhitelistFailure"
990
- msgstr "Specified folder is not hardened by this plugin"
991
-
992
- msgid "PHPDewhitelistSuccess"
993
- msgstr "The selected files have been removed from the hardening exception list"
994
-
995
- msgid "DiffUtilityStatus"
996
- msgstr "The status of the integrity diff utility has been changed"
997
-
998
- msgid "DiffUtilityMissing"
999
- msgstr "Your hosting provider has blocked the execution of external commands."
1000
-
1001
- msgid "IntegrityLanguage"
1002
- msgstr "The language for the core integrity checks has been changed"
1003
-
1004
- msgid "NotInstalled"
1005
- msgstr "not installed"
1006
-
1007
- msgid "PremiumPlugin"
1008
- msgstr "Plugin is Premium"
1009
-
1010
- msgid "MissingLibrary"
1011
- msgstr "Missing Library"
1012
-
1013
- msgid "CannotDownload"
1014
- msgstr "Cannot Download"
1015
-
1016
- msgid "CannotBackup"
1017
- msgstr "Cannot Backup"
1018
-
1019
- msgid "CannotInstall"
1020
- msgstr "Cannot Install"
1021
-
1022
- msgid "VersionInstalled"
1023
- msgstr "Installed v%1$s"
1024
-
1025
- msgid "NewestWordPress"
1026
- msgstr "Newest WordPress"
1027
-
1028
- msgid "NoUpdates"
1029
- msgstr "There are no updates available."
1030
-
1031
- msgid "SiteClean"
1032
- msgstr "Site is Clean"
1033
-
1034
- msgid "SiteNotClean"
1035
- msgstr "Site is not Clean"
1036
-
1037
- msgid "Blacklisted"
1038
- msgstr "Blacklisted"
1039
-
1040
- msgid "NotBlacklisted"
1041
- msgstr "Not Blacklisted"
1042
-
1043
- msgid "APIKeyExplanation"
1044
- msgstr "An API key is required to activate some additional tools available in this plugin. The keys are free and you can virtually generate an unlimited number of them as long as the domain name and email address are unique. The key is used to authenticate the HTTP requests sent by the plugin to a public API service managed by Sucuri Inc. Do not generate the key if you disagree with this."
1045
-
1046
- msgid "APIKeyHelp"
1047
- msgstr "If you experience issues generating the API key you can request one sending the domain name and email address that you want to use to <a href=\"mailto:info@sucuri.net\">info@sucuri.net</a>. Note generating a key for a website that is not facing the Internet is not possible because the API service needs to validate that the domain name exists, however, if you want to test the plugin in a development environment please contact us so we can generate the key manually."
1048
-
1049
- msgid "APIKeyGenerated"
1050
- msgstr "Congratulations! The rest of the features available in the plugin have been enabled. This product is designed to supplement existing security products. It's not a silver bullet for your security needs, but it'll give you greater security awareness and better posture, all with the intent of reducing risk."
1051
-
1052
- msgid "APIKeyContinueSetup"
1053
- msgstr "Your website has been granted a new API key and it was associated to the email address that you chose during the registration process. You can use the same email to recover the key if you happen to lose it sometime. We encourage you to check the rest of the settings page and configure the plugin to your own needs."
1054
-
1055
- msgid "APIKeyRecoveryExplanation"
1056
- msgstr "If this operation was successful you will receive a message in the email used during the registration of the API key <em>(usually the email of the main admin user)</em>. This message contains the key in plain text, copy and paste the key in the form field below. The plugin will verify the authenticity of the key sending an initial HTTP request to the API service, if this fails the key will be removed automatically and you will have to start the process all over again."
1057
-
1058
- msgid "APIKeyRecoveryPossibleFailures"
1059
- msgstr "There are cases where this operation may fail, an example would be when the email address is not associated with the domain anymore, this happens when the base URL changes <em>(from www to none or viceversa)</em>. If you are having issues recovering the key please send an email explaining the situation to <a href=\"mailto:info@sucuri.net\">info@sucuri.net</a>"
1060
-
1061
- msgid "DNSLookups"
1062
- msgstr "DNS Lookups"
1063
-
1064
- msgid "DNSLookupsLabel"
1065
- msgstr "Enable DNS Lookups On Startup"
1066
-
1067
- msgid "DNSLookupsText"
1068
- msgstr "Check the box if your website is behind a known firewall service, this guarantees that the IP address of your visitors will be detected correctly for the security logs. You can change this later from the settings."
1069
-
1070
- msgid "IntegrityTitle"
1071
- msgstr "WordPress Integrity"
1072
-
1073
- msgid "IntegrityDescription"
1074
- msgstr "We inspect your WordPress installation and look for modifications on the core files as provided by WordPress.org. Files located in the root directory, wp-admin and wp-includes will be compared against the files distributed with v%%SUCURI.WordPressVersion%%; all files with inconsistencies will be listed here. Any changes might indicate a hack."
1075
-
1076
- msgid "ReviewFalsePositives"
1077
- msgstr "Review False/Positives"
1078
-
1079
- msgid "IntegrityGoodTitle"
1080
- msgstr "All Core WordPress Files Are Correct"
1081
-
1082
- msgid "IntegrityGoodDescription"
1083
- msgstr "We have not identified additional files, deleted files, or relevant changes to the core files in your WordPress installation. If you are experiencing other malware issues, please use a <a target=\"_blank\" href=\"https://sucuri.net/website-security/malware-removal\">Server Side Scanner</a>."
1084
-
1085
- msgid "IntegrityBadTitle"
1086
- msgstr "Core WordPress Files Were Modified"
1087
-
1088
- msgid "IntegrityBadDescription"
1089
- msgstr "We identified that some of your WordPress core files were modified. That might indicate a hack or a broken file on your installation. If you are experiencing other malware issues, please use a <a href=\"https://sucuri.net/website-security/malware-removal\" target=\"_blank\">Server Side Scanner</a>."
1090
-
1091
- msgid "MarkFixedDescription"
1092
- msgstr "Marking one or more files as fixed will force the plugin to ignore them during the next scan, very useful when you find false positives. Additionally you can restore the original content of the core files that appear as modified or deleted, this will tell the plugin to download a copy of the original files from the official WordPress repository. Deleting a file is an irreversible action, be careful."
1093
-
1094
- msgid "Action"
1095
- msgstr "Action"
1096
-
1097
- msgid "Status"
1098
- msgstr "Status"
1099
-
1100
- msgid "FileSize"
1101
- msgstr "File Size"
1102
-
1103
- msgid "ModifiedAt"
1104
- msgstr "Modified At"
1105
-
1106
- msgid "Pattern"
1107
- msgstr "Pattern"
1108
-
1109
- msgid "FilePath"
1110
- msgstr "File Path"
1111
-
1112
- msgid "Directory"
1113
- msgstr "Directory"
1114
-
1115
- msgid "MarkFixed"
1116
- msgstr "Mark as Fixed"
1117
-
1118
- msgid "RestoreFile"
1119
- msgstr "Restore File"
1120
-
1121
- msgid "DeleteFile"
1122
- msgstr "Delete File"
1123
-
1124
- msgid "HoverForPayload"
1125
- msgstr "Hover to see the Payload"
1126
-
1127
- msgid "LogsPerEvent"
1128
- msgstr "Audit Logs per Event"
1129
-
1130
- msgid "LogsForLogins"
1131
- msgstr "Successful/Failed Logins"
1132
-
1133
- msgid "LogsPerUser"
1134
- msgstr "Audit Logs per User"
1135
-
1136
- msgid "LogsPerIP"
1137
- msgstr "Audit Logs per IP Address"
1138
-
1139
- msgid "Search"
1140
- msgstr "Search"
1141
-
1142
- msgid "FirewallSettingsTitle"
1143
- msgstr "Firewall Settings"
1144
-
1145
- msgid "FirewallSettingsInfo"
1146
- msgstr "A powerful Web Application Firewall and <b>Intrusion Detection System</b> for any WordPress user and many other platforms. This page will help you to configure and monitor your site through the <b>Sucuri Firewall</b>. Once enabled, our firewall will act as a shield, protecting your site from attacks and preventing malware infections and reinfections. It will block SQL injection attempts, brute force attacks, XSS, RFI, backdoors and many other threats against your site."
1147
-
1148
- msgid "FirewallKey"
1149
- msgstr "Firewall API Key"
1150
-
1151
- msgid "FirewallAddKey"
1152
- msgstr "Add your <a href=\"https://waf.sucuri.net/?settings&panel=api\" target=\"_blank\">Firewall API key</a> in the form below to start communicating with the firewall API service."
1153
-
1154
- msgid "FirewallFootNote"
1155
- msgstr "<em>[1]</em> More information about the <a href=\"https://sucuri.net/website-firewall/\" target=\"_blank\">Sucuri Firewall</a>, features and pricing.<br><em>[2]</em> Instructions and videos in the official <a href=\"https://kb.sucuri.net/firewall\" target=\"_blank\">Knowledge Base</a> site.<br><em>[3]</em> <a href=\"https://login.sucuri.net/signup2/create?CloudProxy\" target=\"_blank\">Sign up</a> for a new account and start protecting your site."
1156
-
1157
- msgid "FirewallLogsTitle"
1158
- msgstr "Firewall Audit Logs"
1159
-
1160
- msgid "FirewallLogsInfo"
1161
- msgstr "The firewall logs every request involved in an attack and separates them from the legitimate requests. You can analyze the data from the latest entries in the logs using this tool and take action either enabling the advanced features of the IDS <em>(Intrusion Detection System)</em> from the <a target=\"_blank\" href=\"https://waf.sucuri.net/?settings\">Firewall Dashboard</a> and/or blocking IP addresses and URL paths directly from the <a href=\"https://waf.sucuri.net/?audit\" target=\"_blank\">Firewall Audit Trails</a> page."
1162
-
1163
- msgid "FirewallLogsNote"
1164
- msgstr "Note that non-blocked requests are hidden from the logs, this is intentional."
1165
-
1166
- msgid "FirewallIPAccessTitle"
1167
- msgstr "IP Address Access"
1168
-
1169
- msgid "FirewallIPAccessInfo"
1170
- msgstr "This tool allows you to whitleist and blacklist one or more IP addresses from accessing your website. You can also configure the plugin to automatically blacklist any IP address involved in a password guessing brute-force attack. If a legitimate user fails to submit the correct credentials of their account they will have to log into the Firewall dashboard in order to delete their IP address from the blacklist, or try to login once again through a VPN."
1171
-
1172
- msgid "WhitelistIP"
1173
- msgstr "Whitelist IP"
1174
-
1175
- msgid "BlacklistIP"
1176
- msgstr "Blacklist IP"
1177
-
1178
- msgid "FirewallCacheTitle"
1179
- msgstr "Clear Cache"
1180
-
1181
- msgid "FirewallCacheButton"
1182
- msgstr "Clear Cache"
1183
-
1184
- msgid "FirewallCacheInfo"
1185
- msgstr "The firewall offers multiple options to configure the cache level applied to your website. You can either enable the full cache which is the recommended setting, or you can set the cache level to minimal which will keep the pages static for a couple of minutes, or force the usage of the website headers <em>(only for advanced users)</em>, or in extreme cases where you do not need the cache you can simply disable it. Find more information about it in the <a target=\"_blank\" href=\"https://kb.sucuri.net/firewall/Performance/caching-options\">Sucuri Knowledge Base</a> website."
1186
-
1187
- msgid "FirewallCacheNote"
1188
- msgstr "Note that the firewall has <a href=\"https://kb.sucuri.net/firewall/Performance/cache-exceptions\" target=\"_blank\">special caching rules</a> for Images, CSS, PDF, TXT, JavaScript, media files and a few more extensions that are stored on our <a href=\"https://en.wikipedia.org/wiki/Edge_device\" target=\"_blank\" rel=\"noopener\">edge</a>. The only way to flush the cache for these files is by clearing the firewall's cache completely <em>(for the whole website)</em>. Due to our caching of JavaScript and CSS files, often, as is best practice, the use of versioning during development will ensure updates going live as expected. This is done by adding a query string such as <code>?ver=1.2.3</code> and incrementing on each update."
1189
-
1190
- msgid "FirewallCacheWiki"
1191
- msgstr "A web cache (or HTTP cache) is an information technology for the temporary storage (caching) of web documents, such as HTML pages and images, to reduce bandwidth usage, server load, and perceived lag. A web cache system stores copies of documents passing through it; subsequent requests may be satisfied from the cache if certain conditions are met. A web cache system can refer either to an appliance, or to a computer program. &mdash; <a href=\"https://en.wikipedia.org/wiki/Web_cache\" target=\"_blank\" rel=\"noopener\">WikiPedia - Web Cache</a>"
1192
-
1193
- msgid "FirewallAutoClearCache"
1194
- msgstr "Clear cache automatically when a post or page is updated"
1195
-
1196
- msgid "LoginsAdmins"
1197
- msgstr "Successful Logins (admins)"
1198
-
1199
- msgid "LoginsAdminsInfo"
1200
- msgstr "Here you can see a list of all the successful logins of accounts with admin privileges."
1201
-
1202
- msgid "Registration"
1203
- msgstr "Registration"
1204
-
1205
- msgid "NewestLogins"
1206
- msgstr "Newest To Oldest"
1207
-
1208
- msgid "Admins"
1209
- msgstr "Admins"
1210
-
1211
- msgid "AllUsers"
1212
- msgstr "All Users"
1213
-
1214
- msgid "LoginsAll"
1215
- msgstr "Successful Logins (all)"
1216
-
1217
- msgid "LoginsAllInfo"
1218
- msgstr "Here you can see a list of all the successful user logins."
1219
-
1220
- msgid "BlockedUsers"
1221
- msgstr "Blocked Users"
1222
-
1223
- msgid "BlockedUsersInfo"
1224
- msgstr "Any attempt to authenticate an user account using the functions provided by WordPress will be intercepted and analyzed by the plugin, if the username coincides with any of the users in this list, the authentication process will be immediately stopped. These attemps will not be logged and no email alerts will be sent."
1225
-
1226
- msgid "BlockedUsersNote"
1227
- msgstr "Take in consideration that this is not a 100% bulletproof mechanism to block unwanted user authentications from malicious users. Depending on the configuration of your website, installed plugins, installed themes, and even the version of WordPress there might still be weak points that automated tools can take advantage of to brute force the user accounts registered in your website. <a target=\"_blank\" href=\"https://sucuri.net/website-firewall/?wp=bu\">Install a firewall</a> to have full protection and mitigate this and a myriad of other attacks."
1228
-
1229
- msgid "BlockedUsersByIP"
1230
- msgstr "Blocking users per IP address is a feature provided by the <a href=\"https://sucuri.net/website-firewall/\" target=\"_blank\">Sucuri Firewall</a>; to avoid the duplication of code and reduce the amount of false/positives this feature will never be implemented in this plugin."
1231
-
1232
- msgid "FailedLogins"
1233
- msgstr "Failed logins"
1234
-
1235
- msgid "FailedLoginsInfo"
1236
- msgstr "This information will be used to determine if your site is being victim of <a href=\"https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing\" target=\"_blank\">Password Guessing Brute Force Attacks</a>. These logs will be accumulated and the plugin will send a report via email if there are more than <code>%%SUCURI.FailedLogins.MaxFailedLogins%%</code> failed login attempts during the same hour, you can change this number from <a href=\"%%SUCURI.URL.Settings%%#alerts\">here</a>. <b>NOTE:</b> Some <em>\"Two-Factor Authentication\"</em> plugins do not follow the same rules that WordPress have to report failed login attempts, so you may not see all the attempts in this panel if you have one of these plugins installed."
1237
-
1238
- msgid "LoggedInUsers"
1239
- msgstr "Logged-in Users"
1240
-
1241
- msgid "LoggedInUsersInfo"
1242
- msgstr "Here you can see a list of the users that are currently logged-in."
1243
-
1244
- msgid "Recomendations"
1245
- msgstr "Recomendations"
1246
-
1247
- msgid "SiteCheckNoResults"
1248
- msgstr "If our free scanner did not detect any issue, you may have a more complicated and hidden problem. You can <a target=\"_blank\" href=\"https://sucuri.net/website-security-platform/signup\">sign up with Sucuri</a> for a complete and in-depth scan + cleanup (not included in the free checks)."
1249
-
1250
- msgid "SiteCheckTarget"
1251
- msgstr "Malware Scan Target"
1252
-
1253
- msgid "SiteCheckTargetInfo"
1254
- msgstr "The remote malware scanner provided by the plugin is powered by <a href=\"https://sitecheck.sucuri.net/\" target=\"_blank\">Sucuri SiteCheck</a>, a service that takes a publicly accessible URL and scans it for malicious code. If your website is not visible to the Internet, for example, if it is hosted in a local development environment or a restricted network, the scanner will not be able to work on it. Additionally, if the website was installed in a non-standard directory the scanner will report a \"404 Not Found\" error. You can use this option to change the URL that will be scanned."
1255
-
1256
- msgid "PasswordAttack"
1257
- msgstr "Password Guessing Brute Force Attacks"
1258
-
1259
- msgid "PasswordAttackAfter"
1260
- msgstr "Consider Brute-Force Attack After"
1261
-
1262
- msgid "PasswordAttackInfo"
1263
- msgstr "<a href=\"https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing\" target=\"_blank\">Password guessing brute force attacks</a> are very common against web sites and web servers. They are one of the most common vectors used to compromise web sites. The process is very simple and the attackers basically try multiple combinations of usernames and passwords until they find one that works. Once they get in, they can compromise the web site with malware, spam , phishing or anything else they want."
1264
-
1265
- msgid "PostTypeAlerts"
1266
- msgstr "Post-Type Alerts"
1267
-
1268
- msgid "IgnoredAt"
1269
- msgstr "Ignored At"
1270
-
1271
- msgid "PostType"
1272
- msgstr "Post-Type"
1273
-
1274
- msgid "PostTypeAlertsDisabled"
1275
- msgstr "It seems that you disabled the email alerts for <b>new site content</b>, this panel is intended to provide a way to ignore specific events in your site and with that the alerts reported to your email. Since you have deactivated the <b>new site content</b> alerts, this panel will be disabled too."
1276
-
1277
- msgid "PostTypeAlertsInfo"
1278
- msgstr "This is a list of registered <a href=\"https://codex.wordpress.org/Post_Types\" target=\"_blank\" rel=\"noopener\">Post Types</a>. You will receive an email alert when a custom page or post associated to any of these types is created or updated. Some of these are created by WordPress but the majority are created by 3rd-party plugins and themes to extend functionality from WordPress. If you don't want to receive alerts for certain posts you can stop them from here."
1279
-
1280
- msgid "PostTypeAlertsInvisible"
1281
- msgstr "If you are receiving alerts for post types that are not listed here it may be because the theme or plugin that is making these changes is registering the custom post-type on runtime, in this case our plugin will not be able to detect these changes and consequently you will not be able to ignore those alerts. However, if you know the unique identifier of the post-type you can type it in the form bellow and our plugin will do its best to skip the alerts associated to that."
1282
-
1283
- msgid "PostTypeAlertsStop"
1284
- msgstr "Stop Alerts For This Post-Type"
1285
-
1286
- msgid "AlertsPerHour"
1287
- msgstr "Alerts Per Hour"
1288
-
1289
- msgid "AlertsPerHourMaximum"
1290
- msgstr "Maximum Alerts Per Hour"
1291
-
1292
- msgid "AlertsPerHourInfo"
1293
- msgstr "Configure the maximum number of email alerts per hour. If the number is exceeded and the plugin detects more events during the same hour, it will still log the events into the audit logs but will not send the email alerts. Be careful with this as you will miss important information."
1294
-
1295
- msgid "TestAlerts"
1296
- msgstr "Test Alerts"
1297
-
1298
- msgid "AlertsRecipient"
1299
- msgstr "Alerts Recipient"
1300
-
1301
- msgid "AlertsRecipientInfo"
1302
- msgstr "By default, the plugin will send the email alerts to the primary admin account, the same account created during the installation of WordPress in your web server. You can add more people to the list, they will receive a copy of the same security alerts."
1303
-
1304
- msgid "CustomFormat"
1305
- msgstr "Custom Format"
1306
-
1307
- msgid "AlertsSubject"
1308
- msgstr "Alert Subject"
1309
-
1310
- msgid "AlertsSubjectInfo"
1311
- msgstr "Format of the subject for the email alerts, by default the plugin will use the website name and the event identifier that is being reported, you can use this panel to include the IP address of the user that triggered the event and some additional data. You can create filters in your email client creating a custom email subject using the pseudo-tags shown below."
1312
-
1313
- msgid "TrustedIPs"
1314
- msgstr "Trusted IP Addresses"
1315
-
1316
- msgid "CIDRFormat"
1317
- msgstr "CIDR Format"
1318
-
1319
- msgid "IPAddedAt"
1320
- msgstr "IP Added At"
1321
-
1322
- msgid "TrustedIPsInfo"
1323
- msgstr "If you are working in a LAN <em>(Local Area Network)</em> you may want to include the IP addresses of all the nodes in the subnet, this will force the plugin to stop sending email alerts about actions executed from trusted IP addresses. Use the CIDR <em>(Classless Inter Domain Routing)</em> format to specify ranges of IP addresses <em>(only 8, 16, and 24)</em>."
1324
-
1325
- msgid "APIViaProxy"
1326
- msgstr "API Communication via Proxy"
1327
-
1328
- msgid "APIViaProxyInfo"
1329
- msgstr "All the HTTP requests used to communicate with the API service are being sent using the WordPress built-in functions, so (almost) all its official features are inherited, this is useful if you need to pass these HTTP requests through a proxy. According to the <a href=\"https://developer.wordpress.org/reference/classes/wp_http_proxy/\" target=\"_blank\" rel=\"noopener\">official documentation</a> you have to add some constants to the main configuration file: <em>WP_PROXY_HOST, WP_PROXY_PORT, WP_PROXY_USERNAME, WP_PROXY_PASSWORD</em>."
1330
-
1331
- msgid "ProxyHostname"
1332
- msgstr "HTTP Proxy Hostname"
1333
-
1334
- msgid "ProxyPort"
1335
- msgstr "HTTP Proxy Port num"
1336
-
1337
- msgid "ProxyUsername"
1338
- msgstr "HTTP Proxy Username"
1339
-
1340
- msgid "ProxyPassword"
1341
- msgstr "HTTP Proxy Password"
1342
-
1343
- msgid "APICommunication"
1344
- msgstr "API Service Communication"
1345
-
1346
- msgid "APICommunicationInfo"
1347
- msgstr "Once the API key is generate the plugin will communicate with a remote API service that will act as a safe data storage for the audit logs generated when the website triggers certain events that the plugin monitors. If the website is hacked the attacker will not have access to these logs and that way you can investigate what was modified <em>(for malware infaction)</em> and/or how the malicious person was able to gain access to the website."
1348
-
1349
- msgid "APICommunicationDisabled"
1350
- msgstr "Disabling the API service communication will stop the event monitoring, consider to enable the <a href=\"%%SUCURI.URL.Settings%%#general\">Log Exporter</a> to keep the monitoring working while the HTTP requests are ignored, otherwise an attacker may execute an action that will not be registered in the security logs and you will not have a way to investigate the attack in the future."
1351
-
1352
- msgid "APITimeout"
1353
- msgstr "API Request Timeout"
1354
-
1355
- msgid "APITimeoutLabel"
1356
- msgstr "API Request Timeout (in seconds)"
1357
-
1358
- msgid "APITimeoutValue"
1359
- msgstr "Wait <b>%%SUCURI.RequestTimeout%%</b> before timeout"
1360
-
1361
- msgid "APITimeoutInfo"
1362
- msgstr "The plugin sends the data associated to the events triggered by WordPress when it considers the action is suspicious, it sends this information via HTTP requests using the HTTP transport protocol available in the system and the <a target=\"_blank\" href=\"https://developer.wordpress.org/reference/functions/wp_remote_post/\" rel=\"noopener\">built-in functions</a> provided by WordPress, then it waits for the response. If you start experiencing issues related with the timeout of the requests you may consider to increase the number of seconds to wait for the response. You may also want to check with your hosting provider to see if there is something in the server blocking the connection."
1363
-
1364
- msgid "HTAccessTitle"
1365
- msgstr "Access File Integrity"
1366
-
1367
- msgid "HTAccessInfo"
1368
- msgstr "The <code>.htaccess</code> is a distributed configuration file, and is how the Apache web server handles configuration changes on a per-directory basis. WordPress uses this file to manipulate how Apache serves files from its root directory and subdirectories thereof; most notably, it modifies this file to be able to handle pretty permalinks."
1369
-
1370
- msgid "HTAccessFound"
1371
- msgstr "HTAccess file found in this path <code>%%SUCURI.HTAccess.Fpath%%</code>"
1372
-
1373
- msgid "HTAccessNotFound"
1374
- msgstr "Your website has no <code>.htaccess</code> file or it was not found in the default location."
1375
-
1376
- msgid "HTAccessStandard"
1377
- msgstr "The main <code>.htaccess</code> file in your site has the standard rules for a WordPress installation. You can customize it to improve the performance and change the behaviour of the redirections for pages and posts in your site. To get more information visit the official documentation at <a target=\"_blank\" rel=\"noopener\" href=\"https://codex.wordpress.org/Using_Permalinks#Creating_and_editing_.28.htaccess.29\"> Codex WordPress - Creating and editing (.htaccess)</a>"
1378
-
1379
- msgid "ScannerDescription"
1380
- msgstr "The plugin scans your entire website looking for changes which are later reported via the API in the audit logs page. This scanner runs daily but you can change the frequency to meet your own requirements. Notice that scanning your project files too frequently will affect the performance of your website. Be sure to have enough server resources before changing this option. The memory limit and maximum execution time are two of the PHP options that your server will set to stop your website from consuming too much resources."
1381
-
1382
- msgid "ScannerWithoutSPL"
1383
- msgstr "The scanner uses the <a href=\"http://php.net/manual/en/class.splfileobject.php\" target=\"_blank\" rel=\"noopener\">PHP SPL library</a> and the <a target=\"_blank\" href=\"http://php.net/manual/en/class.filesystemiterator.php\" rel=\"noopener\">Filesystem Iterator</a> class to scan the directory tree where your website is located in the server. This library is only available on PHP 5 >= 5.3.0 &mdash; OR &mdash; PHP 7; if you have an older version of PHP the plugin will not work as expected. Please ask your hosting provider to advice you on this matter."
1384
-
1385
- msgid "IntegrityLanguageTitle"
1386
- msgstr "WordPress Integrity (Language)"
1387
-
1388
- msgid "IntegrityInfo"
1389
- msgstr "The information necessary to check the WordPress integrity uses data obtained from the <a href=\"http://codex.wordpress.org/WordPress.org_API\" target=\"_blank\" rel=\"noopener\">WordPress API</a>. It compares this data with the content of the files installed in your website. By default the API returns this data for the English version of WordPress. If your website is using a non-English version of the code you will have to specify the language to reduce the amount of false/positives."
1390
-
1391
- msgid "IntegrityNote"
1392
- msgstr "<b>NOTE:</b> Not all the languages are supported. If you notice a high amount of false/positives please consider to switch the option back to English and then mark the files that you consider are clean as such, they will be ignored by the scanner the next time it runs."
1393
-
1394
- msgid "Reason"
1395
- msgstr "Reason"
1396
-
1397
- msgid "FalsePositives"
1398
- msgstr "WordPress Integrity (False/Positives)"
1399
-
1400
- msgid "FalsePositivesUnignore"
1401
- msgstr "Stop Ignoring the Selected Files"
1402
-
1403
- msgid "FalsePositivesInfo"
1404
- msgstr "Since the scanner doesn't reads the files during the execution of the integrity check, it is possible to find false/positives. Files listed here have been marked as false/positives and will be ignored by the scanner in subsequent scans."
1405
-
1406
- msgid "IgnoreFiles"
1407
- msgstr "Ignore Files And Folders During The Scans"
1408
-
1409
- msgid "IgnoreFilesSingle"
1410
- msgstr "Ignore One Single File"
1411
-
1412
- msgid "IgnoreFilesInfo"
1413
- msgstr "Use this tool to select the files and/or folders that are too heavy for the scanner to process. These are usually folders with images, media files like videos and audios, backups and &mdash; in general &mdash; anything that is not code-related. Ignoring these files or folders will reduce the memory consumption of the PHP script."
1414
-
1415
- msgid "SecretKeys"
1416
- msgstr "Update Secret Keys"
1417
-
1418
- msgid "SecretKeysUpdated"
1419
- msgstr "Secret keys updated successfully (summary of the operation bellow)."
1420
-
1421
- msgid "SecretKeysGenerate"
1422
- msgstr "Generate New Security Keys"
1423
-
1424
- msgid "SecretKeysExpiration"
1425
- msgstr "Your session will expire immediately after the security keys are changed."
1426
-
1427
- msgid "SecretKeysInfo"
1428
- msgstr "The secret or security keys are a list of constants added to your site to ensure better encryption of information stored in the user's cookies. A secret key makes your site harder to hack and access by adding random elements to the password. You do not have to remember the keys, just write a random, complicated, and long string in the <code>wp-config.php</code> file. You can change these keys at any point in time to invalidate all existing cookies, forcing all users to login again."
1429
-
1430
- msgid "PluginReinstall"
1431
- msgstr "Reset Installed Plugins"
1432
-
1433
- msgid "PluginReinstallInfo"
1434
- msgstr "In case that you suspect of an infection in your site, or even after you got rid of a malicious code, it would be better if you reinstall all the plugins installed in your site, including the ones you are not using. Notice that premium plugins will not be reinstalled to prevent backward compatibility issues and problems with licenses."
1435
-
1436
- msgid "PluginReinstallCache"
1437
- msgstr "The information shown here is cache for %%SUCURI.ResetPlugin.CacheLifeTime%% seconds, this is necessary to reduce the quantity of HTTP requests sent to the WordPress servers and the bandwidth of your site. Currently there is no option to recreate this cache so you have to wait until it resets itself."
1438
-
1439
- msgid "PluginReinstallWarning"
1440
- msgstr "<b>WARNING!</b> This procedure can break your website. The reset will not affect the database nor the settings of each plugin but depending on how they were written the reset action might break them. Be sure to create a backup of the plugins directory before the execution of this tool."
1441
-
1442
- msgid "Roles"
1443
- msgstr "Roles"
1444
-
1445
- msgid "Registered"
1446
- msgstr "Registered"
1447
-
1448
- msgid "PasswordChange"
1449
- msgstr "Reset User Password"
1450
-
1451
- msgid "PasswordChangeInfo"
1452
- msgstr "You can generate a new random password for the user accounts that you select from the list. An email with the new password will be sent to the email address of each chosen users. If you choose to change the password of your own user, then your current session will expire immediately. You will need to log into the admin panel with the new password that will be sent to your email."
1453
-
1454
- msgid "PasswordChangeAlert"
1455
- msgstr "WordPress has generated a new (random) password for your account <b>%%SUCURI.ResetPassword.UserName%%</b> at <a target=\"_blank\" href=\"http://%%SUCURI.ResetPassword.Website%%\">%%SUCURI.ResetPassword.Website%%</a>. The change has been requested by one of the admins in this website for security reasons. Your new password is &mdash; <span style=\"font-family:Menlo, Monaco, monospace, serif;font-weight:700\">%%%SUCURI.ResetPassword.Password%%%</span> &mdash; please change it as soon as possible."
1456
-
1457
- msgid "Update"
1458
- msgstr "Update"
1459
-
1460
- msgid "TestedWith"
1461
- msgstr "Tested With"
1462
-
1463
- msgid "AvailableUpdates"
1464
- msgstr "Available Plugin and Theme Updates"
1465
-
1466
- msgid "AvailableUpdatesInfo"
1467
- msgstr "WordPress has a big user base in the public Internet, this brings interest to malicious people to find vulnerabilities in the code, 3rd-party extensions, and themes that other companies develop. You should keep every piece of code installed in your website update to prevent attacks as soon as disclosed vulnerabilities are patched."
1468
-
1469
- msgid "WhitelistScript"
1470
- msgstr "Whitelist Blocked PHP Files"
1471
-
1472
- msgid "WhitelistScriptInfo"
1473
- msgstr "After you apply the hardening in either the includes, content, and/or upload directories the plugin will add a rule in the access control file to deny access to any PHP file located in these folders, this is a good precaution in case that an attacker is able to upload a shell script; with a few exceptions the <em>\"index.php\"</em> is the only one that should be publicly accessible, however many theme/plugin developers decide to use these folders to process some operations, in this case applying the hardening <strong>may break</strong> their functionality."
1474
-
1475
- msgid "Uninstall"
1476
- msgstr "Reset Security Logs, Hardening and Settings"
1477
-
1478
- msgid "UninstallInfo"
1479
- msgstr "This action will trigger the deactivation / uninstallation process of the plugin. All local security logs, hardening and settings will be deleted. Notice that the security logs stored in the API service will not be deleted, this is to prevent tampering from a malicious user. You can request a new API key if you want to start from scratch."
1480
-
1481
- msgid "ImportExport"
1482
- msgstr "Import &amp; Export Settings"
1483
-
1484
- msgid "ImportExportInfo"
1485
- msgstr "Copy the JSON-encoded data from the box below, go to your other websites and click the <em>\"Import\"</em> button in the settings page. The plugin will start using the same settings from this website. Notice that some options are omitted as they contain values specific to this website. To import the settings from another website into this one, replace the JSON-encoded data in the box below with the JSON-encoded data exported from the other website, then click the button <em>\"Import\"</em>. Notice that some options will not be imported to reduce the security risk of writing arbitrary data into the disk."
1486
-
1487
- msgid "DataStorage"
1488
- msgstr "Data Storage"
1489
-
1490
- msgid "DataStorageInfo"
1491
- msgstr "This is the directory where the plugin will store the security logs, the list of files marked as fixed in the core integrity tool, the cache for the malware scanner and 3rd-party plugin metadata. The plugin requires write permissions in this directory as well as the files contained in it. If you prefer to keep these files in a non-public directory <em>(one level up the document root) </em> please define a constant in the <em>\"wp-config.php\"</em> file named <em>\"SUCURI_DATA_STORAGE\"</em> with the absolute path to the new directory."
1492
-
1493
- msgid "TimezoneTitle"
1494
- msgstr "Timezone"
1495
-
1496
- msgid "TimezoneInfo"
1497
- msgstr "This option defines the timezone that will be used through out the entire plugin to print the dates and times whenever is necessary. This option also affects the date and time of the logs visible in the audit logs panel which is data that comes from a remote server configured to use Eastern Daylight Time (EDT). WordPress offers an option in the general settings page to allow you to configure the timezone for the entire website, however, if you are experiencing problems with the time in the audit logs, this option will help you fix them."
1498
-
1499
- msgid "TimezoneStatus"
1500
- msgstr "The timezone for the date and time in the audit logs has been changed"
1501
-
1502
- msgid "ChecksumsAPI"
1503
- msgstr "WordPress Checksums API"
1504
-
1505
- msgid "ChecksumsAPIInfo"
1506
- msgstr "The WordPress integrity tool uses a remote API service maintained by the WordPress organization to determine which files in the installation were added, removed or modified. The API returns a list of files with their respective checksums, this information guarantees that the installation is not corrupt. You can, however, point the integrity tool to a GitHub repository in case that you are using a custom version of WordPress like the <a href=\"https://github.com/WordPress/WordPress\" target=\"_blank\">development version of the code</a>."
1507
-
1508
- msgid "ChecksumsAPIChanged"
1509
- msgstr "The URL to retrieve the WordPress checksums has been changed"
1510
-
1511
- msgid "MaxExecutionTimeAlert"
1512
- msgstr "Server is not fast enough to process this action; maximum execution time reached"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
languages/sucuri-scanner-es_ES.mo DELETED
Binary file
languages/sucuri-scanner-es_ES.po DELETED
@@ -1,1513 +0,0 @@
1
- msgid ""
2
- msgstr ""
3
- "Content-Transfer-Encoding: 8bit\n"
4
- "Content-Type: text/plain; charset=UTF-8\n"
5
- "Language-Team: https://sucuri.net/\n"
6
- "Language: es_ES\n"
7
- "Last-Translator: Sucuri Inc. <info@sucuri.net>\n"
8
- "MIME-Version: 1.0\n"
9
- "Plural-Forms: nplurals=2; plural=(n != 1);\n"
10
- "PO-Revision-Date: \n"
11
- "POT-Creation-Date: 2017-06-01 09:00-0700\n"
12
- "Project-Id-Version: Sucuri Scanner\n"
13
- "X-Generator: Poedit 1.8.12\n"
14
-
15
- msgid "Review"
16
- msgstr "Revisión"
17
-
18
- msgid "Dashboard"
19
- msgstr "Inicio"
20
-
21
- msgid "Firewall"
22
- msgstr "Firewall (WAF)"
23
-
24
- msgid "LastLogins"
25
- msgstr "Autenticaciones"
26
-
27
- msgid "Settings"
28
- msgstr "Ajustes"
29
-
30
- msgid "AuditLogs"
31
- msgstr "Registros"
32
-
33
- msgid "ClearCache"
34
- msgstr "Limpiar Cache"
35
-
36
- msgid "IPAccess"
37
- msgstr "Acceso Por IP"
38
-
39
- msgid "GenerateAPIKey"
40
- msgstr "Generar Llave"
41
-
42
- msgid "APIKeyRecovery"
43
- msgstr "Recuperacion de Llave"
44
-
45
- msgid "Copyright"
46
- msgstr "&copy; %%SUCURI.Year%% Sucuri Inc. Todos los derechos reservados."
47
-
48
- msgid "AccessDenied"
49
- msgstr "Accesso denegado por Sucuri Inc."
50
-
51
- msgid "NonceFailure"
52
- msgstr "Error al verificar el código Nonce del formulario, verifica los datos e inténtalo de nuevo."
53
-
54
- msgid "NewsletterInvitation"
55
- msgstr "¿Te gustaría recibir información sobre vulnerabilidades descubiertas? Subscríbete a nuestro newsletter <a href=\"http://sucuri.hs-sites.com/subscribe-to-security\" target=\"_blank\" rel=\"noopener\">aquí</a>"
56
-
57
- msgid "EnableAPIServiceAgain"
58
- msgstr "La comunicación con la API de Sucuri está deshabilitada, si acabas de actualizar el plugin esta podría ser una buena oportunidad para probar esta herramienta una vez más con el nuevo código. Habilite la comunicación con la API desde la página de ajustes."
59
-
60
- msgid "Save"
61
- msgstr "Guardar"
62
-
63
- msgid "Delete"
64
- msgstr "Eliminar"
65
-
66
- msgid "InvalidAPIKey"
67
- msgstr "El formato de la llave es inválido"
68
-
69
- msgid "ErrorNoInfo"
70
- msgstr "Error desconocido, no hay más información."
71
-
72
- msgid "ErrorLogFileNotFound"
73
- msgstr "; esto generalmente sucede cuando una llave inválida es agregada al plugin, la llave será eliminada automáticamente para reducir la cantidad de errores, si quieres recuperarla, utiliza el botón de recuperación en la página de ajustes, y recibirás un correo con las instrucciones de activación: %1$s"
74
-
75
- msgid "ErrorWrongAPIKey"
76
- msgstr "; la llave del Firewall es inválida: %1$s"
77
-
78
- msgid "ErrorSSLCertificate"
79
- msgstr ". Tu sitio web parece estar usando una versión desactualizada de la librería OpenSSL o el módulo de CURL ha sido compilado sin soporte para el algoritmo usado para establecer un enlace de comunicación segura con la API. Contácta a tu proveedor de hosting para solucionar el problema."
80
-
81
- msgid "ErrorInvalidEmail"
82
- msgstr "El correo electrónico tiene un formato inválido o el dominio asociado no tiene asociado un servidor MX."
83
-
84
- msgid "AlertAPIKeySet"
85
- msgstr "La llave para la comunicación con la API ha sido generada y registrada."
86
-
87
- msgid "Yes"
88
- msgstr "Sí"
89
-
90
- msgid "No"
91
- msgstr "No"
92
-
93
- msgid "Okay"
94
- msgstr "OK"
95
-
96
- msgid "Enable"
97
- msgstr "Activar"
98
-
99
- msgid "Enabled"
100
- msgstr "Activado"
101
-
102
- msgid "Disable"
103
- msgstr "Desactivar"
104
-
105
- msgid "Disabled"
106
- msgstr "Desactivado"
107
-
108
- msgid "Ignore"
109
- msgstr "Ignorar"
110
-
111
- msgid "Unignore"
112
- msgstr "No Ignorar"
113
-
114
- msgid "Ignored"
115
- msgstr "Ignorado"
116
-
117
- msgid "Done"
118
- msgstr "Hecho"
119
-
120
- msgid "Submit"
121
- msgstr "Proceder"
122
-
123
- msgid "Error"
124
- msgstr "Error"
125
-
126
- msgid "Good"
127
- msgstr "bien"
128
-
129
- msgid "Edit"
130
- msgstr "Editar"
131
-
132
- msgid "NotSet"
133
- msgstr "(no existe)"
134
-
135
- msgid "Download"
136
- msgstr "Descargar"
137
-
138
- msgid "NotRandomized"
139
- msgstr "no es aleatorio"
140
-
141
- msgid "NotWritable"
142
- msgstr "No es Modificable"
143
-
144
- msgid "ParentNotWritable"
145
- msgstr "El directorio donde el archivo está ubicado no tiene permisos de escritura."
146
-
147
- msgid "DoesNotExist"
148
- msgstr "No Existe"
149
-
150
- msgid "Email"
151
- msgstr "Correo Electrónico"
152
-
153
- msgid "Website"
154
- msgstr "Sitio Web"
155
-
156
- msgid "Message"
157
- msgstr "Mensaje"
158
-
159
- msgid "Information"
160
- msgstr "Información"
161
-
162
- msgid "Event"
163
- msgstr "Evento"
164
-
165
- msgid "Exists"
166
- msgstr "Existe"
167
-
168
- msgid "Writable"
169
- msgstr "Modificable"
170
-
171
- msgid "Today"
172
- msgstr "Hoy"
173
-
174
- msgid "NoLogs"
175
- msgstr "No existen registros."
176
-
177
- msgid "SelfHosting"
178
- msgstr "Exportador de Registros"
179
-
180
- msgid "SelfHostingEnabled"
181
- msgstr "La herramienta para exportar los registros de seguridad ha sido activada"
182
-
183
- msgid "SelfHostingDisabled"
184
- msgstr "La herramienta para exportar los registros de seguridad ha sido desactivada"
185
-
186
- msgid "SelfHostingInfo"
187
- msgstr "Esta opción te permite guardar una copia de los registros de seguridad en un archivo ubicado en tu servidor que puede ser leído por un SIEM o cualquier otro sistema de análisis (le recomendamos OSSEC). Esto te dará visibilidad de lo que sucede en tu sitio web para complementar tu infraestructura de monitoreo. <b>NOTA:</b> No use archivos que sean accesibles de forma pública, use un archivo que esté por lo menos un nivel arriva de la raíz del sitio web para evitar fugas de información."
188
-
189
- msgid "SelfHostingFallback"
190
- msgstr "No tienes una llave para comunicarte con la API. Sin embargo, la herramienta para exportar los registros de seguridad está activada, el plugin leerá el contenido de este archivo y mostrará esa información aquí. Ten en cuenta que solo los últimos registros serán procesados para mantener un consumo de memoria bajo. Considera generar una llave para obtener una mayor cobertura en la actividad de tu sitio web."
191
-
192
- msgid "AuditLogsCache"
193
- msgstr "Los registros son guardados por %%SUCURI.AuditLogs.Lifetime%% segundos"
194
-
195
- msgid "Refresh"
196
- msgstr "Actualizar"
197
-
198
- msgid "AuditLogsNoAPI"
199
- msgstr "La API no está disponible; la información viene del servidor local"
200
-
201
- msgid "AuditLogsQueue"
202
- msgstr "registros en cola"
203
-
204
- msgid "SendLogs"
205
- msgstr "Enviar"
206
-
207
- msgid "UnsupportedWordPress"
208
- msgstr "esta versión de WordPress no tiene soporte."
209
-
210
- msgid "NoWordPressFile"
211
- msgstr "El archivo no hace parte de la instalación oficial de WordPress."
212
-
213
- msgid "ThereAreNoDifferences"
214
- msgstr "No existen diferencias entre el archivo instalado y el original"
215
-
216
- msgid "ScheduledTask"
217
- msgstr "%1$s (cada %2$d segundos)"
218
-
219
- msgid "ScheduledTaskNever"
220
- msgstr "Nunca (no ejecutar)"
221
-
222
- msgid "SucuriAlert"
223
- msgstr "Alerta de Sucuri"
224
-
225
- msgid "UpdatedEmailSubject"
226
- msgstr "El asunto para las alertas de seguridad ha sido actualizado"
227
-
228
- msgid "InvalidEmailSubject"
229
- msgstr "El asunto para las alertas de seguridad contiene letras inválidas."
230
-
231
- msgid "EmailSubject.available_updates"
232
- msgstr "Actualizaciones Disponibles"
233
-
234
- msgid "EmailSubject.bruteforce_attack"
235
- msgstr "Ataque de Fuerza Bruta"
236
-
237
- msgid "EmailSubject.failed_login"
238
- msgstr "Autenticación Fallida"
239
-
240
- msgid "EmailSubject.plugin_activated"
241
- msgstr "Plugin Activado"
242
-
243
- msgid "EmailSubject.plugin_change"
244
- msgstr "Plugin Modificado"
245
-
246
- msgid "EmailSubject.plugin_deactivated"
247
- msgstr "Plugin Desactivado"
248
-
249
- msgid "EmailSubject.plugin_deleted"
250
- msgstr "Plugin Eliminado"
251
-
252
- msgid "EmailSubject.plugin_installed"
253
- msgstr "Plugin Instalado"
254
-
255
- msgid "EmailSubject.plugin_updated"
256
- msgstr "Plugin Actualizado"
257
-
258
- msgid "EmailSubject.post_publication"
259
- msgstr "Publicación"
260
-
261
- msgid "EmailSubject.scan_checksums"
262
- msgstr "Escaneo de Checksums"
263
-
264
- msgid "EmailSubject.settings_updated"
265
- msgstr "Ajustes Actualizados"
266
-
267
- msgid "EmailSubject.success_login"
268
- msgstr "Autenticación Exitosa"
269
-
270
- msgid "EmailSubject.theme_activated"
271
- msgstr "Plantilla Activada"
272
-
273
- msgid "EmailSubject.theme_deleted"
274
- msgstr "Plantilla Eliminada"
275
-
276
- msgid "EmailSubject.theme_editor"
277
- msgstr "Editor de Plantilla"
278
-
279
- msgid "EmailSubject.theme_installed"
280
- msgstr "Plantilla Instalada"
281
-
282
- msgid "EmailSubject.theme_updated"
283
- msgstr "Plantilla Actualizada"
284
-
285
- msgid "EmailSubject.user_registration"
286
- msgstr "Registro de Usuario"
287
-
288
- msgid "EmailSubject.website_updated"
289
- msgstr "Sitio Web Actualizado"
290
-
291
- msgid "EmailSubject.widget_added"
292
- msgstr "Widget Agregado"
293
-
294
- msgid "EmailSubject.widget_deleted"
295
- msgstr "Widget Eliminado"
296
-
297
- msgid "EmailSubject.post_update"
298
- msgstr "Publicación Actualizada"
299
-
300
- msgid "EmailSubject.core_integrity_checks"
301
- msgstr "Integridad de Archivos de WordPress"
302
-
303
- msgid "EmailSubject.password_change"
304
- msgstr "Cambio de Contraseña"
305
-
306
- msgid "FailedLoginFooter"
307
- msgstr "<br><br><em>Explicación: Alguien intentó ingresar al sistema de administración de tu sitio web. Si estás recibiendo muchas de estas alertas, es posible que alguien esté utilizando un ataque de fuerza bruta para vulnerar tu sitio [1]. Puedes desactivar estas alertas desde aquí [2]. También puedes considerar la instalación de un Firewall para filtrar el tráfico malicioso y ataques como este [3].</em><br><br>[1] <a href='https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing'>https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing</a><br>[2] <a href='%1$s'>%2$s</a> <br>[3] <a href='https://sucuri.net/es/firewall-de-sitios-web/?wpalert'>https://sucuri.net/es/firewall-de-sitios-web/</a><br>"
308
-
309
- msgid "FirewallAPIKeySet"
310
- msgstr "La llave del Firewall ha sido guardada"
311
-
312
- msgid "FirewallAPIKeyUnset"
313
- msgstr "La llave del Firewall ha sido eliminada"
314
-
315
- msgid "FirewallAPIKeyInvalid"
316
- msgstr "La llave del Firewall es inválida"
317
-
318
- msgid "FirewallAPIKeyMissing"
319
- msgstr "La llave del Firewall no fué encontrada."
320
-
321
- msgid "NoData"
322
- msgstr "No existen datos"
323
-
324
- msgid "FirewallDoCache"
325
- msgstr "Activada (recomendada)"
326
-
327
- msgid "FirewallSiteCache"
328
- msgstr "Cache usando cabeceras de HTTP"
329
-
330
- msgid "FirewallNoCache"
331
- msgstr "Mínima (solo por algunos minutos)"
332
-
333
- msgid "FirewallNoCacheAtAll"
334
- msgstr "Desactivada (use con precaución)"
335
-
336
- msgid "FirewallNotEnabled"
337
- msgstr "El Firewall no ha sido activado en tu sitio o la llave de autenticación es inválida."
338
-
339
- msgid "FirewallClearCacheFailure"
340
- msgstr "No se pudo actualizar la cache de tu sitio, inténtalo más tarde."
341
-
342
- msgid "Active"
343
- msgstr "activo"
344
-
345
- msgid "NotActive"
346
- msgstr "inactivo"
347
-
348
- msgid "Unknown"
349
- msgstr "desconocido"
350
-
351
- msgid "Undefined"
352
- msgstr "Indefinido"
353
-
354
- msgid "January"
355
- msgstr "Enero"
356
-
357
- msgid "February"
358
- msgstr "Febrero"
359
-
360
- msgid "March"
361
- msgstr "Marzo"
362
-
363
- msgid "April"
364
- msgstr "Abril"
365
-
366
- msgid "May"
367
- msgstr "Mayo"
368
-
369
- msgid "June"
370
- msgstr "Junio"
371
-
372
- msgid "July"
373
- msgstr "Julio"
374
-
375
- msgid "August"
376
- msgstr "Agosto"
377
-
378
- msgid "September"
379
- msgstr "Septiembre"
380
-
381
- msgid "October"
382
- msgstr "Octubre"
383
-
384
- msgid "November"
385
- msgstr "Noviembre"
386
-
387
- msgid "December"
388
- msgstr "Deciembre"
389
-
390
- msgid "Username"
391
- msgstr "Usuario"
392
-
393
- msgid "Password"
394
- msgstr "Contraseña"
395
-
396
- msgid "RemoteAddr"
397
- msgstr "Dirección IP"
398
-
399
- msgid "Hostname"
400
- msgstr "Servidor"
401
-
402
- msgid "Browser"
403
- msgstr "Navegador Web"
404
-
405
- msgid "Datetime"
406
- msgstr "Fecha/Hora"
407
-
408
- msgid "Block"
409
- msgstr "Bloquear"
410
-
411
- msgid "Unblock"
412
- msgstr "Desbloquear"
413
-
414
- msgid "BlockedAt"
415
- msgstr "Bloqueado en"
416
-
417
- msgid "FirstAttempt"
418
- msgstr "Primer Intento"
419
-
420
- msgid "LastAttempt"
421
- msgstr "Último Intento"
422
-
423
- msgid "LastActivity"
424
- msgstr "Último Ingreso"
425
-
426
- msgid "AttemptTimestamp"
427
- msgstr "Timestamp"
428
-
429
- msgid "AttemptDatetime"
430
- msgstr "Fecha/Hora"
431
-
432
- msgid "ConfirmOperation"
433
- msgstr "Debes confirmar que entiendes el riesgo de esta operación."
434
-
435
- msgid "UnderstandTheRisk"
436
- msgstr "Entiendo que esta operación no puede ser revertida."
437
-
438
- msgid "NonSupportedAction"
439
- msgstr "La acción ejecutada no está soportada."
440
-
441
- msgid "NonSupportedLanguage"
442
- msgstr "El idioma seleccionado no está soportado."
443
-
444
- msgid "NothingSelected"
445
- msgstr "Nada ha sido seleccionado de la lista."
446
-
447
- msgid "ItemsProcessed"
448
- msgstr "Los items seleccionados han sido procesados."
449
-
450
- msgid "AllItemsProcessed"
451
- msgstr "<b>%1$d</b> de <b>%2$d</b> archivos fueron procesados."
452
-
453
- msgid "SomeItemsProcessed"
454
- msgstr "Solo <b>%1$d</b> de <b>%2$d</b> archivos fueron procesados."
455
-
456
- msgid "ErrorIntegrityAdded"
457
- msgstr "El plugin no tiene permiso para eliminar este archivo porque fué creado por otro usuario del sistema con más privilegios que tu cuenta. Conéctate a tu sitio a través de FTP para eliminarlo."
458
-
459
- msgid "ErrorIntegrityModified"
460
- msgstr "El plugin no tiene permiso para restablecer el contenido de este archivo porque fué modificado por otro usuario del sistema con más privilegios que tu cuenta. Conéctate a tu sitio a través de FTP para restablecerlo."
461
-
462
- msgid "ErrorIntegrityRemoved"
463
- msgstr "El plugin no tiene permiso para restablecer el contenido de este archivo porque el directorio donde está ubicado pertenece a otro usuario del sistema con más privilegios que tu cuenta. Conéctate a tu sitio a través de FTP para restablecerlo."
464
-
465
- msgid "SecurityAlerts"
466
- msgstr "Alertas de Seguridad"
467
-
468
- msgid "NoAlertsError"
469
- msgstr "Usted ha instalado un plugin o plantilla que no es completamente compatible con nuestro plugin, algunas de las alertas de seguridad (como las autenticaciones exitosas y fallidas de usuario) no serán enviadas. Para prevenir un ciclo infinito mientras el plugin detecta cambios en el sitio y envía las alertas de seguridad a través de un plugin de SMTP personalizado, hemos decidido detener cualquier intento de enviar los correos para prevenir errores fatales."
470
-
471
- msgid "AlertSettingsUpdated"
472
- msgstr "La configuración para las alertas de seguridad ha sido actualizada"
473
-
474
- msgid "OptionNotifyPluginChange"
475
- msgstr "Recibir alertas por cambios en la configuración del plugin de Sucuri"
476
-
477
- msgid "OptionPrettifyMails"
478
- msgstr "Recibir alertas en HTML <em>(podrías experimentar problemas con tu servicio de correo electrónico)</em>"
479
-
480
- msgid "OptionUseWordPressMail"
481
- msgstr "Usar funciones nativas de WordPress para enviar las alertas <em>(deselecciona para usar funciones nativas de PHP)</em>"
482
-
483
- msgid "OptionLastLoginRedirection"
484
- msgstr "Permitir redirección durante el login para reportar información acerca de la última autenticación exitosa de tu cuenta de usuario"
485
-
486
- msgid "OptionNotifyScanChecksums"
487
- msgstr "Recibir alertas por diferencias en los archivos originales de WordPress"
488
-
489
- msgid "OptionNotifyAvailableUpdates"
490
- msgstr "Recibir alertas con actualizaciones disponibles"
491
-
492
- msgid "OptionNotifyUserRegistration"
493
- msgstr "Recibir alertas por registros de nuevas cuentas de usuario"
494
-
495
- msgid "OptionNotifySuccessLogin"
496
- msgstr "Recibir alertas por autenticaciones exitosas de usuario"
497
-
498
- msgid "OptionNotifyFailedLogin"
499
- msgstr "Recibir alertas por autenticaciones de usuario fallidas <em>(si tu sitio está siendo atacado recibirás multiples alertas por segundo, deselecciona para reducir el Spam)</em>"
500
-
501
- msgid "OptionNotifyFailedPassword"
502
- msgstr "Recibir alertas por autenticaciones de usuario fallidas incluyendo la contraseña enviada"
503
-
504
- msgid "OptionNotifyBruteforceAttack"
505
- msgstr "Recibir un resumen con todos los intentos de autenticación de usuario fallidos durante la misma hora. El reporte será eliminado del servidor una vez la información sea enviada a su correo"
506
-
507
- msgid "OptionNotifyPostPublication"
508
- msgstr "Recibir alertas por cambios en el estado de las publicaciones y páginas <em>(puedes configurar qué alertas serán monitoreadas desde el panel \"Ignorar Cambios en Publicaciones\")</em>"
509
-
510
- msgid "OptionNotifyWebsiteUpdated"
511
- msgstr "Recibir alertas cuando tu sitio web sea actualizado"
512
-
513
- msgid "OptionNotifySettingsUpdated"
514
- msgstr "Recibir alertas por cambios en la configuración general de tu sitio web"
515
-
516
- msgid "OptionNotifyThemeEditor"
517
- msgstr "Recibir alertas cuando un archivo sea modificado a través del editor de Plugins/Plantillas"
518
-
519
- msgid "OptionNotifyPluginInstalled"
520
- msgstr "Recibir alertas cuando un <b>plugin sea instalado</b>"
521
-
522
- msgid "OptionNotifyPluginActivated"
523
- msgstr "Recibir alertas cuando un <b>plugin sea activado</b>"
524
-
525
- msgid "OptionNotifyPluginDeactivated"
526
- msgstr "Recibir alertas cuando un <b>plugin sea desactivado</b>"
527
-
528
- msgid "OptionNotifyPluginUpdated"
529
- msgstr "Recibir alertas cuando un <b>plugin sea actualizado</b>"
530
-
531
- msgid "OptionNotifyPluginDeleted"
532
- msgstr "Recibir alertas cuando un <b>plugin sea eliminado</b>"
533
-
534
- msgid "OptionNotifyWidgetAdded"
535
- msgstr "Recibir alertas cuando un <b>widget sea agregado</b> a tu sitio web"
536
-
537
- msgid "OptionNotifyWidgetDeleted"
538
- msgstr "Recibir alertas cuando un <b>widget sea eliminado</b> de tu sitio web"
539
-
540
- msgid "OptionNotifyThemeInstalled"
541
- msgstr "Recibir alertas cuando una <b>plantilla sea instalada</b>"
542
-
543
- msgid "OptionNotifyThemeActivated"
544
- msgstr "Recibir alertas cuando una <b>plantilla sea activada</b>"
545
-
546
- msgid "OptionNotifyThemeUpdated"
547
- msgstr "Recibir alertas cuando una <b>plantilla sea actualizada</b>"
548
-
549
- msgid "OptionNotifyThemeDeleted"
550
- msgstr "Recibir alertas cuando una <b>plantilla sea eliminada</b>"
551
-
552
- msgid "MaximumAlertsSuccess"
553
- msgstr "El número máximo de alertas por hora ha sido actualizado"
554
-
555
- msgid "MaximumAlertsFailure"
556
- msgstr "Error al actualizar el número máximo de alertas por hora"
557
-
558
- msgid "OptionPerHour5"
559
- msgstr "Máximo 5 por hora"
560
-
561
- msgid "OptionPerHour10"
562
- msgstr "Máximo 10 por hora"
563
-
564
- msgid "OptionPerHour20"
565
- msgstr "Máximo 20 por hora"
566
-
567
- msgid "OptionPerHour40"
568
- msgstr "Máximo 40 por hora"
569
-
570
- msgid "OptionPerHour80"
571
- msgstr "Máximo 80 por hora"
572
-
573
- msgid "OptionPerHour160"
574
- msgstr "Máximo 160 por hora"
575
-
576
- msgid "BruteForceAlertSuccess"
577
- msgstr "El plugin asumirá que tu sitio web está siendo atacado después de %1$s autenticaciones de usuario fallidas durante la misma hora"
578
-
579
- msgid "BruteForceAlertFailure"
580
- msgstr "Ese número no está soportado por el plugin, use uno de la lista"
581
-
582
- msgid "OptionPerHourUnlimited"
583
- msgstr "Alertas ilimitadas por hora"
584
-
585
- msgid "OptionFailedLogins30"
586
- msgstr "30 autenticaciones fallidas por hora"
587
-
588
- msgid "OptionFailedLogins60"
589
- msgstr "60 autenticaciones fallidas por hora"
590
-
591
- msgid "OptionFailedLogins120"
592
- msgstr "120 autenticaciones fallidas por hora"
593
-
594
- msgid "OptionFailedLogins240"
595
- msgstr "240 autenticaciones fallidas por hora"
596
-
597
- msgid "OptionFailedLogins480"
598
- msgstr "480 autenticaciones fallidas por hora"
599
-
600
- msgid "HTAccessIsMissing"
601
- msgstr "Archivo .htaccess no existe"
602
-
603
- msgid "HTAccessNotWritable"
604
- msgstr "Archivo .htaccess no es modificable"
605
-
606
- msgid "DiffUtility"
607
- msgstr "Herramienta de Integridad de Archivos"
608
-
609
- msgid "DiffUtilityDescription"
610
- msgstr "Si tu proveedor de hosting permite la ejecución de comandos del sistema a través de PHP, puedes configurar el plugin para usar el comando <a href=\"https://en.wikipedia.org/wiki/Diff_utility\" target=\"_blank\" rel=\"noopener\">Diff de UNIX</a> para comparar el contenido actual de tus archivos con los archivos originales de WordPress. Esta herramienta te permitirá ver las diferencias entre ambos repositorios y actuar dependiendo de la información encontrada."
611
-
612
- msgid "DiffUtilityInfo"
613
- msgstr "El comando diff de Unix está activado. Puedes hacer clic en los archivos en la tabla para ver las diferencias detectadas por el escáner. Si consideras que las diferencias no causan daño a tu sitio, puedes marcar el archivo como revisado, de otra forma se te sugiere restaurar el contenido original inmediatamente."
614
-
615
- msgid "DiffUtilityInstructions"
616
- msgstr "Las lineas con un signo <b>negativo</b> como prefijo <em>(marcadas en rojo)</em> muestran el código original. Las lineas con un signo <b>positivo</b> como prefijo <em>(marcadas en verde)</em> muestran el código actual. Puedes leer más acerca del formato DIFF en el artículo de WikiPedia <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/Diff_utility\" rel=\"noopener\">Unix Diff Utility</a>."
617
-
618
- msgid "AccountsWereBlocked"
619
- msgstr "Las cuentas de usuario seleccionadas han sido bloqueadas"
620
-
621
- msgid "AccountsWereUnblocked"
622
- msgstr "Las cuentas de usuario seleccionadas han sido desbloqueadas"
623
-
624
- msgid "LastLoginMessage"
625
- msgstr "El último acceso de tu cuenta fué en <b>%1$s</b> desde <b>%2$s</b> <em>(%3$s)</em> <a href='%4$s'>ver todos los registros</a>"
626
-
627
- msgid "LastLoginsNotWritable"
628
- msgstr "El archivo para los registros de autenticaciones de usuario no es modificable: <code>%1$s</code>"
629
-
630
- msgid "UserInfo"
631
- msgstr "Usuario: %1$s (%2$s)"
632
-
633
- msgid "ReverseProxy"
634
- msgstr "Túneles Proxy"
635
-
636
- msgid "ReverseProxyStatus"
637
- msgstr "El soporte para túneles proxy ha sido cambiado a <b>%1$s</b>"
638
-
639
- msgid "ReverseProxyInfo"
640
- msgstr "El plugin utiliza la dirección IP de cada usuario para registrar los eventos ejecutados, el plugin utiliza dos métodos para obtener esta dirección: el método principal usa una variable global del servidor llamada <em>Remote-Addr</em> disponible en la mayoría de servidores web, un método alternativo usa cabeceras de HTTP personalizadas <em>(que son consideradas inseguras por defecto)</em>. No es necesario que te preocupes por esta opción a menos que tu sitio web esté detrás de un túnel proxy. Servicios como el <a href=\"https://sucuri.net/es/firewall-de-sitios-web/\" target=\"_blank\">Firewall de Sucuri</a> &mdash; una vez activo &mdash; fuerzan el paso del tráfico de red a través de ellos para filtrar amenazas de seguridad que podrían afectar el servidor original. Un efecto secundario del uso de estos servicios es que la dirección IP de los usuarios ya no está disponible entre las variables globales del servidor sino que es enviada a través de una cabecera HTTP personalizada."
641
-
642
- msgid "IPDiscoverer"
643
- msgstr "Detección de Direcciones IP"
644
-
645
- msgid "IPDiscovererInfo"
646
- msgstr "La detección de direcciones IP hace uso de DNS para detectar si el sitio web está detrás del <a href=\"https://sucuri.net/es/firewall-de-sitios-web/\" target=\"_blank\">Firewall de Sucuri</a> en cuyo caso modificará las variables globales del servidor para configurar la dirección IP real de cada visitante del sitio. Este proceso es ejecutado con cada proceso de WordPress, y es posible que la velocidad de tu sitio sea afectada ya que algunos proveedores de hosting utilizan servidores DNS lentos, lo que hace que esta operación tome más tiempo de lo que se espera."
647
-
648
- msgid "HTTPHeader"
649
- msgstr "Cabecera de HTTP"
650
-
651
- msgid "HTTPHeaderStatus"
652
- msgstr "La cabecera de HTTP ha sido cambiada a <code>%1$s</code>"
653
-
654
- msgid "DisallowedHTTPHeader"
655
- msgstr "La cabecera de HTTP no es permitida"
656
-
657
- msgid "RequiresModernPHP"
658
- msgstr "El plugin requiere de PHP 5 >= 5.3.0 - o - PHP 7"
659
-
660
- msgid "General"
661
- msgstr "Generales"
662
-
663
- msgid "Scanner"
664
- msgstr "Escáner"
665
-
666
- msgid "Hardening"
667
- msgstr "Hardening"
668
-
669
- msgid "PostHack"
670
- msgstr "Pos-Ataque"
671
-
672
- msgid "Alerts"
673
- msgstr "Alertas"
674
-
675
- msgid "WebsiteInfo"
676
- msgstr "Información"
677
-
678
- msgid "Loading"
679
- msgstr "Cargando..."
680
-
681
- msgid "iFrames"
682
- msgstr "iFrames"
683
-
684
- msgid "Links"
685
- msgstr "Vínculos"
686
-
687
- msgid "Scripts"
688
- msgstr "Scripts"
689
-
690
- msgid "iFramesNum"
691
- msgstr "iFrames: %1$d"
692
-
693
- msgid "LinksNum"
694
- msgstr "Vínculos: %1$d"
695
-
696
- msgid "ScriptsNum"
697
- msgstr "Scripts: %1$d"
698
-
699
- msgid "Type"
700
- msgstr "Tipo"
701
-
702
- msgid "Name"
703
- msgstr "Nombre"
704
-
705
- msgid "Value"
706
- msgstr "Valor"
707
-
708
- msgid "Version"
709
- msgstr "Versión"
710
-
711
- msgid "NextDue"
712
- msgstr "Próxima Ejecución"
713
-
714
- msgid "Schedule"
715
- msgstr "Frecuencia"
716
-
717
- msgid "Arguments"
718
- msgstr "Argumentos"
719
-
720
- msgid "EnvVariables"
721
- msgstr "Variables del Servidor"
722
-
723
- msgid "ConfigVariables"
724
- msgstr "Variables de Configuración"
725
-
726
- msgid "ConfigNotFound"
727
- msgstr "El archivo de configuración de WordPress no fué encontrado."
728
-
729
- msgid "ConfigNotWritable"
730
- msgstr "El archivo de configuraciòn de WordPress no es modificable."
731
-
732
- msgid "LastLoginsResetSuccess"
733
- msgstr "El archivo para los registros de autenticaciones de usuario ha sido reseteado."
734
-
735
- msgid "LastLoginsResetFailure"
736
- msgstr "No se pudo resetear el archivo para los registros de autenticaciones de usuario."
737
-
738
- msgid "WillReceiveAlerts"
739
- msgstr "Las alertas de seguridad serán enviadas a: <code>%1$s</code>"
740
-
741
- msgid "WillNotReceiveAlerts"
742
- msgstr "Estos correos no recibirán más alertas de seguridad: <code>%1$s</code>"
743
-
744
- msgid "TestAlertSent"
745
- msgstr "Un correo de prueba ha sido enviado a tu dirección, revisa tu bandeja de entrada"
746
-
747
- msgid "InvalidEmail"
748
- msgstr "Formato de correo electrónico inválido."
749
-
750
- msgid "TrustedIPDuplicate"
751
- msgstr "La dirección IP especificada ya ha sido agregada."
752
-
753
- msgid "TrustedIPAdded"
754
- msgstr "Eventos sospechosos generados desde esta dirección IP serán ignorados: <code>%1$s</code>"
755
-
756
- msgid "TrustedIPFailure"
757
- msgstr "La dirección IP no pudo ser agregada a la lista"
758
-
759
- msgid "TrustedIPDeleted"
760
- msgstr "La dirección IP ha sido eliminada de la lista"
761
-
762
- msgid "OnlyLowerUppercase"
763
- msgstr "Solo se permiten letras en minúscula y guiones-bajo"
764
-
765
- msgid "PostTypeIgnore"
766
- msgstr "Recibir Estas Alertas"
767
-
768
- msgid "PostTypeUnignore"
769
- msgstr "No Recibir Estas Alertas"
770
-
771
- msgid "PostTypeIgnored"
772
- msgstr "Este tipo de publicaciones serán ignoradas"
773
-
774
- msgid "PostTypeUnignored"
775
- msgstr "Este tipo de publicaciones no serán ignoradas"
776
-
777
- msgid "PostTypeFailure"
778
- msgstr "Este tipo de publicaciones no es válido o ya han sido ignoradas"
779
-
780
- msgid "APIServiceChanged"
781
- msgstr "El estado de comunicación con la API ha sido cambiado"
782
-
783
- msgid "PluginResetSuccess"
784
- msgstr "Los registros de seguridad, configuraciones y cambios adicionales hechos por el plugin han sido eliminados"
785
-
786
- msgid "StorageNotWritable"
787
- msgstr "El plugin no tiene acceso a este directorio: <code>%1$s</code>"
788
-
789
- msgid "SiteWasRegistered"
790
- msgstr "Sitio web registrado exitosamente"
791
-
792
- msgid "NFilesWereDeleted"
793
- msgstr "%1$d de %2$d archivos han sido eliminados"
794
-
795
- msgid "AvoidDocumentRoot"
796
- msgstr "El archivo no puede ser accesible públicamente"
797
-
798
- msgid "AvoidFileOverride"
799
- msgstr "El archivo ya existe; no será sobre-escrito"
800
-
801
- msgid "Cronjobs"
802
- msgstr "Acciones Automatizadas"
803
-
804
- msgid "CronjobsInfo"
805
- msgstr "Acciones automatizadas son reglas registradas en tu base de datos por un plugin, plantilla o WordPress; estas reglas son usadas para ejecutar automáticamente acciones definidas en el código con un intervalo de tiempo definido. Un buen uso de estas reglas es la generación de copias de seguridad, la ejecución de escáneres de seguridad o la remoción de elementos innecesarios de la base de datos como borradores de publicaciones. <b>Nota:</b> Las acciones automatizadas pueden ser re-instaladas por cualquier plugin/plantilla automáticamente, considera desactivar el plugin completamente si quieres eliminar una de estas acciones."
806
-
807
- msgid "CronjobRunNow"
808
- msgstr "Ejecutar Ahora (en +10 segundos)"
809
-
810
- msgid "CronjobsWillRunSoon"
811
- msgstr "%1$d acciones automatizadas serán ejecutadas en los próximos segundos"
812
-
813
- msgid "CronjobsWereDeleted"
814
- msgstr "%1$d acciones automatizadas han sido eliminadas"
815
-
816
- msgid "CronjobsWereReinstalled"
817
- msgstr "%1$d acciones automatizadas han sido reinstaladas para ser ejecutadas con este intervalo de tiempo: <code>%2$s</code>.'"
818
-
819
- msgid "CronjobsWereNotSelected"
820
- msgstr "Ninguna acción automatizada ha sido seleccionada de la lista"
821
-
822
- msgid "DNSLookupStatus"
823
- msgstr "El estado de las conexiones vía DNS para detectar el uso de túneles proxy ha sido cambiado"
824
-
825
- msgid "IncorrectEncoding"
826
- msgstr "Los datos han sido codificados incorrectamente"
827
-
828
- msgid "ImportCount"
829
- msgstr "%1$d de %2$d opciones han sido importadas exitosamente"
830
-
831
- msgid "APIKey"
832
- msgstr "Llave de Autenticación"
833
-
834
- msgid "APIKeyInfo"
835
- msgstr "La mayoría de herramientas en este plugin pueden ser usadas sin una configuración específica, pero las herramientas más importantes requieren de una <b>llave de autenticación</b> para establecer un túnel de comunicación segura con los servidores de Sucuri. La llave es generada usando tu correo electrónico y el nombre de dominio de tu sitio web, esto te permitirá tener acceso a la herramienta de monitoreo y opciones extra."
836
-
837
- msgid "APIKeyTerms"
838
- msgstr "Ten en cuenta que generar una llave implica que estás de acuerdo con enviar la información recolectada por el plugin a los servidores de Sucuri, esto te permite mantener los Tem de seguridad fuera del alcance de usuarios maliciosos que podrían intentar eliminarlos para limpiar sus huellas durante un ataque. Nosotros además usamos esta información de forma anónima para generar <a href=\"https://sucuri.net/security-reports/brute-force/\" target=\"_blank\">estadísticas</a>. Por favor, no generes la llave de autenticación si estás en desacuerdo con esto, puedes seguir usando el plugin sin la llave."
839
-
840
- msgid "APIKeyMissing"
841
- msgstr "La llave no existe"
842
-
843
- msgid "APIKeyInvalidDomain"
844
- msgstr "Tu sitio web <code>%%SUCURI.CleanDomain%%</code> parece no tener un registro de DNS tipo <code>A</code> así que la API lo considerará inválido durante el proceso de generación de la llave. Agrega <code>www</code> al inicio del dominio para intentar solucionar el problema. Envía un mensaje a nuestro servicio de soporte si no entiendes qué significa esto."
845
-
846
- msgid "APIKeyRecoverButton"
847
- msgstr "Recuperar Por Correo Electrónico"
848
-
849
- msgid "APIKeyRecoveryCondition"
850
- msgstr "Si no tienes acceso al correo electrónico usado para generar la llave pero tienes una copia de la misma a la mano, haz <a target=\"_self\" href=\"%%SUCURI.URL.Settings%%&recover\">clic aquí</a> para activar el plugin manualmente. Ten en cuenta que si la llave es inválida el plugin la eliminará inmediatamente."
851
-
852
- msgid "ApplyHardening"
853
- msgstr "Aplicar Hardening"
854
-
855
- msgid "RevertHardening"
856
- msgstr "Revertir Hardening"
857
-
858
- msgid "HardeningFirewallTitle"
859
- msgstr "Firewall Para Aplicaciones Web"
860
-
861
- msgid "HardeningFirewallDescription"
862
- msgstr "Un WAF (Web Application Firewall) es una capa de protección para tu sitio web que bloquea todo tipo de ataques (fuerza bruta, denegación de servicios, inyecciones a base de datos, etc) y te ayuda a permanecer limpio de código malicioso y fuera de listas negras. Esta opción verifica si tu sitio web está usando el Firewall de Sucuri como tu capa de protección primaria."
863
-
864
- msgid "HardeningFirewallPurchase"
865
- msgstr "El Firewall de Sucuri es un servicio premium que puedes comprar aquí - <a href='https://goo.gl/qfNkMq' target='_blank'>Sucuri Firewall</a>"
866
-
867
- msgid "HardeningVersionTitle"
868
- msgstr "Verificar la Versión de WordPress"
869
-
870
- msgid "HardeningVersionDescription"
871
- msgstr "¿Por qué mantener tu sitio web actualizado? WordPress es un proyecto de código libre lo que significa que todos los cambios hechos al código son hechos de forma pública, si alguno de esos cambios hace parte de una solución para un problema de seguridad, cualquier persona puede utilizar esta información para atacar tu sitio web si este aún no ha aplicado el parche de seguridad."
872
-
873
- msgid "HardeningPHPVersionTitle"
874
- msgstr "Verificar la Versión de PHP"
875
-
876
- msgid "HardeningPHPVersionDescription"
877
- msgstr "PHP %1$s está instalado."
878
-
879
- msgid "HardeningPHPVersionLifetime"
880
- msgstr "Comunícate con tu proveedor de hosting para instalar una versión actualizada de PHP - <a href='http://php.net/supported-versions.php' target='_blank' rel='noopener'>Lista de Versiones de PHP Soportadas</a>"
881
-
882
- msgid "HardeningGeneratorTitle"
883
- msgstr "Eliminar Versión de WordPress"
884
-
885
- msgid "HardeningGeneratorDescription"
886
- msgstr "Esta opción verifica si la versión de WordPress usada en tu sitio web está siendo expuesta de forma pública a través del código HTML. Muchos escáneres de vulnerabilidades web utilizan esta información para determinar si tu página web tiene vulnerabilidades para luego ser explotadas. Ten en cuenta que aún sin esta información un escáner inteligente puede detectar la versión de WordPress en uso comparando los Checksums de algunos archivos estáticos distribuidos con cada instalación."
887
-
888
- msgid "HardeningNginxTitle"
889
- msgstr "Bloquear Algunos Archivos PHP"
890
-
891
- msgid "HardeningNginxDescription"
892
- msgstr "Esta opción bloquea la ejecución de archivos PHP en directorios sensitivos. Se cuidadoso cuando apliques esta opción, ya que algunos plugins y plantillas requieren de acceso público a este directorio para algunas de sus herramientas, por ejemplo, para generar imágenes o guardar información temporalmente. Agrega los archivos que están siendo bloqueados a la lista blanca de archivos PHP."
893
-
894
- msgid "HardeningNginxField"
895
- msgstr "Verificar Hardening"
896
-
897
- msgid "HardeningNginxSuggestion"
898
- msgstr "Lee las instrucciones oficiales de WordPress para aprender como restringir el acceso a archivos PHP en directorios sensitivos - <a href='https://codex.wordpress.org/Nginx#Global_restrictions_file' target='_blank' rel='noopener'>Restricciones Globales para Nginx y WordPress</a>"
899
-
900
- msgid "HardeningUploadsTitle"
901
- msgstr "Bloquear Archivos PHP en el Directorio de Subidas"
902
-
903
- msgid "HardeningUploadsDescription"
904
- msgstr "Esta opción bloquea la ejecución de archivos PHP en directorios sensitivos. Se cuidadoso cuando aplique esta opción ya que algunos plugins y plantillas requieren de acceso público a este directorio para algunas de sus herramientas, por ejemplo, para generar imágenes o guardar información temporalmente. Agrega los archivos que están siendo bloqueados a la lista blanca de archivos PHP."
905
-
906
- msgid "HardeningUploadsApplySuccess"
907
- msgstr "Hardening aplicado al directorio de subidas"
908
-
909
- msgid "HardeningUploadsApplyFailure"
910
- msgstr "Error aplicando hardening en el directorio seleccionado"
911
-
912
- msgid "HardeningUploadsRevertSuccess"
913
- msgstr "Hardening revertido en el directorio de subidas"
914
-
915
- msgid "HardeningUploadsRevertFailure"
916
- msgstr "El archivo .htaccess no es modificable"
917
-
918
- msgid "HardeningContentTitle"
919
- msgstr "Bloquear Archivos PHP en el Directorio de Contenido"
920
-
921
- msgid "HardeningContentDescription"
922
- msgstr "Esta opción bloquea la ejecución de archivos PHP en directorios sensitivos. Se cuidadoso cuando aplique esta opción ya que algunos plugins y plantillas requieren de acceso público a este directorio para algunas de sus herramientas, por ejemplo, para generar imágenes o guardar información temporalmente. Agrega los archivos que están siendo bloqueados a la lista blanca de archivos PHP."
923
-
924
- msgid "HardeningContentApplySuccess"
925
- msgstr "Hardening aplicado al directorio de contenido"
926
-
927
- msgid "HardeningContentApplyFailure"
928
- msgstr "Error aplicando hardening en el directorio seleccionado"
929
-
930
- msgid "HardeningContentRevertSuccess"
931
- msgstr "Hardening revertido en el directorio de contenido"
932
-
933
- msgid "HardeningContentRevertFailure"
934
- msgstr "El archivo .htaccess no es modificable"
935
-
936
- msgid "HardeningIncludesTitle"
937
- msgstr "Bloquear Archivos PHP en el Directorio de Inclusiones"
938
-
939
- msgid "HardeningIncludesDescription"
940
- msgstr "Esta opción bloquea la ejecución de archivos PHP en directorios sensitivos. Se cuidadoso cuando aplique esta opción ya que algunos plugins y plantillas requieren de acceso público a este directorio para algunas de sus herramientas, por ejemplo, para generar imágenes o guardar información temporalmente. Agrega los archivos que están siendo bloqueados a la lista blanca de archivos PHP."
941
-
942
- msgid "HardeningIncludesApplySuccess"
943
- msgstr "Hardening aplicado al directory de inclusiones"
944
-
945
- msgid "HardeningIncludesApplyFailure"
946
- msgstr "Error aplicando hardening en el directorio de inclusiones"
947
-
948
- msgid "HardeningIncludesRevertSuccess"
949
- msgstr "Hardening revertido en el directorio de inclusiones"
950
-
951
- msgid "HardeningIncludesRevertFailure"
952
- msgstr "El archivo .htaccess no es modificable"
953
-
954
- msgid "HardeningReadmeTitle"
955
- msgstr "Exposición de Información"
956
-
957
- msgid "HardeningReadmeDescription"
958
- msgstr "Esta opción verifica si el archivo README todavía existe. La información en este archivo puede ser usada por personas maliciosas para encontrar vulnerabilidades asociadas con la versión de WordPress que has instalado. Ten en cuenta que este archivo is re-creado cada vez que una actualización es ejecutada."
959
-
960
- msgid "HardeningReadmeApplySuccess"
961
- msgstr "Hardening aplicado sobre el archivo README"
962
-
963
- msgid "HardeningReadmeApplyFailure"
964
- msgstr "No se puede eliminar el archivo README"
965
-
966
- msgid "HardeningAdminUserTitle"
967
- msgstr "Cuenta de Administración Primaria"
968
-
969
- msgid "HardeningAdminUserDescription"
970
- msgstr "Esta opción verifica si la cuenta de administración primaria está usando un nombre fácil de adivinar como \"admin\". Esto permite a personas maliciosas identificar fácilmente qué cuenta tiene los privilegios más altos entre todos los usuarios registrados."
971
-
972
- msgid "HardeningFileEditorTitle"
973
- msgstr "Editor de Plugins y Plantillas"
974
-
975
- msgid "HardeningFileEditorDescription"
976
- msgstr "Esta opción desactiva el editor de plugins y plantillas para prevenir modificaciones innecesarias en el código."
977
-
978
- msgid "HardeningFileEditorApplySuccess"
979
- msgstr "Hardening aplicado al editor de plugins y plantillas"
980
-
981
- msgid "HardeningFileEditorRevertSuccess"
982
- msgstr "Hardening revertido en el editor de plugins y plantillas"
983
-
984
- msgid "HardeningFileEditorRevertFailure"
985
- msgstr "El editor de plugins y plantillas no fue desactivado usando el plugin de Sucuri. Debes escanear tu sitio web por una constante definida como \"DISALLOW_FILE_EDIT\" cuando la encuentres, debes eliminarla o cambiar su valor a \"False\". Ten en cuenta que cualquier otro plugin o plantilla puede desactivar el editor, así que es imposible determinar el origen de la constante."
986
-
987
- msgid "PHPWhitelistSuccess"
988
- msgstr "El archivo ha sido agregado a la lista blanca de archivos PHP"
989
-
990
- msgid "PHPWhitelistFailure"
991
- msgstr "El directorio especificado no fue fortalecido por el plugin de Sucuri"
992
-
993
- msgid "PHPDewhitelistSuccess"
994
- msgstr "Los archivos seleccionados han sido eliminados de la lista blanca de archivos PHP"
995
-
996
- msgid "DiffUtilityStatus"
997
- msgstr "El estado de la herramienta de integridad ha sido cambiado"
998
-
999
- msgid "DiffUtilityMissing"
1000
- msgstr "Tu proveedor de hosting no permite la ejecución de comandos del sistema a través de archivos PHP."
1001
-
1002
- msgid "IntegrityLanguage"
1003
- msgstr "El idioma para la herramienta de integridad de archivos de WordPress ha sido cambiado"
1004
-
1005
- msgid "NotInstalled"
1006
- msgstr "no instalado"
1007
-
1008
- msgid "PremiumPlugin"
1009
- msgstr "Plugin Premium"
1010
-
1011
- msgid "MissingLibrary"
1012
- msgstr "NO_INSTALLER"
1013
-
1014
- msgid "CannotDownload"
1015
- msgstr "Imposible Descargar"
1016
-
1017
- msgid "CannotBackup"
1018
- msgstr "Imposible Hacer Copia de Seguridad"
1019
-
1020
- msgid "CannotInstall"
1021
- msgstr "Imposible Instalar"
1022
-
1023
- msgid "VersionInstalled"
1024
- msgstr "Instalado v%1$s"
1025
-
1026
- msgid "NewestWordPress"
1027
- msgstr "Última Versión"
1028
-
1029
- msgid "NoUpdates"
1030
- msgstr "No existen actualizaciones."
1031
-
1032
- msgid "SiteClean"
1033
- msgstr "Sitio Limpio"
1034
-
1035
- msgid "SiteNotClean"
1036
- msgstr "Sitio Infectado"
1037
-
1038
- msgid "Blacklisted"
1039
- msgstr "Lista Negra"
1040
-
1041
- msgid "NotBlacklisted"
1042
- msgstr "Sin Lista Negra"
1043
-
1044
- msgid "APIKeyExplanation"
1045
- msgstr "Una llave de autenticación es necesaria para activar herramientas adicionales disponibles en el plugin. Las llaves son gratuitas y puedes generar un número ilimitado de ellas siempre y cuando el nombre del sitio web y el correo electrónico sean únicos. La llave es usada para autenticar las conexiones a través de HTTP hechas a la API administrada por Sucuri Inc. No generes la llave si estás en desacuerdo con el envío de información a nuestros servidores."
1046
-
1047
- msgid "APIKeyHelp"
1048
- msgstr "Si experimentas problemas con la generación de la llave, puedes solicitar una enviando la dirección de tu sitio web y el correo electrónico que deseas usar para la asociación a <a href=\"mailto:info@sucuri.net\">info@sucuri.net</a>. Ten en cuenta que generar una llave para un sitio web que no es público en Internet no es posible, ya que la API necesita validar que el nombre del host existe, sin embargo, si deseas probar el plugin en un ambiente de desarrollo, por favor contáctanos para poder generar una llave manualmente."
1049
-
1050
- msgid "APIKeyGenerated"
1051
- msgstr "¡Felicitaciones! Todas las herramientas disponibles en el plugin han sido habilitadas. Este producto está diseñado para suplementar productos de seguridad existentes en tu sitio web. El plugin no provee una solución completa a tus necesidades de seguridad, pero te ofrece más herramientas para controlar el funcionamiento de tu sitio y tener una mejor postura con respecto a ataques que puedas sufrir en el futuro, todo esto con la intensión de reducir riesgos."
1052
-
1053
- msgid "APIKeyContinueSetup"
1054
- msgstr "Sucuri Inc. ha generado una llave única para tu sitio web, esta llave fue asociada al correo electrónico que seleccionaste anteriormente, correo que puedes utilizar para recuperar dicha llave si llegaras a perderla en el futuro. Te invitamos a revisar el resto de opciones disponibles en la página de ajustes para que configures el plugin a la medida de tu sitio."
1055
-
1056
- msgid "APIKeyRecoveryExplanation"
1057
- msgstr "Si esta operación fue exitosa, recibirás un mensaje en el correo electrónico que usaste durante la generación de la llave <em>(usualmente este correo es el mismo asociado a la cuenta de administración primaria)</em>. Este mensaje contiene una copia de la llave, por favor copia y pega la llave en el formulario de abajo. El plugin verificará la autenticidad de la misma, si esta verificación falla, la llave será eliminada automáticamente y deberás iniciar el proceso de recuperación nuevamente."
1058
-
1059
- msgid "APIKeyRecoveryPossibleFailures"
1060
- msgstr "Existen casos en los que esta operación puede fallar, un ejemplo sería cuando el correo electrónico ya no está asociado al sitio web, esto sucede cuando la base de la URL cambia <em>(de www a nada y viceversa)</em>. Si estás teniendo problemas con el proceso de recuperación de la llave, por favor envía un mensaje explicando la situación a <a href=\"mailto:info@sucuri.net\">info@sucuri.net</a>"
1061
-
1062
- msgid "DNSLookups"
1063
- msgstr "Conexiones DNS"
1064
-
1065
- msgid "DNSLookupsLabel"
1066
- msgstr "Activar conexiones DNS al inicio de cada proceso"
1067
-
1068
- msgid "DNSLookupsText"
1069
- msgstr "Activa esta opción si tu sitio web está detrás de un Firewall, esto garantizará que la dirección IP de tus visitantes será detectada correctamente para los registros de seguridad. Puedes cambiar esto después desde la página de configuraciones del plugin."
1070
-
1071
- msgid "IntegrityTitle"
1072
- msgstr "Integridad de la Instalación"
1073
-
1074
- msgid "IntegrityDescription"
1075
- msgstr "El plugin inspecciona la instalación de WordPress en busca de modificaciones en los archivos originales disponibles en WordPress.org. Archivos ubicados en la raíz del proyecto, el directorio wp-admin y el directorio wp-includes serán comparados con los archivos distribuidos en la versión v%%SUCURI.WordPressVersion%%; todos los archivos con inconsistencias aparecerán en esta lista. Cualquiera de estos cambios podría indicar un problema de seguridad."
1076
-
1077
- msgid "ReviewFalsePositives"
1078
- msgstr "Revisar Falsos/Positivos"
1079
-
1080
- msgid "IntegrityGoodTitle"
1081
- msgstr "Todos los Archivos Están Intáctos"
1082
-
1083
- msgid "IntegrityGoodDescription"
1084
- msgstr "No hemos detectado archivos adicionales, eliminados o cambios relevantes en los archivos originales de WordPress. Si estás experimentando otros problemas, por favor usa un <a target=\"_blank\" href=\"https://sucuri.net/es/seguridad-de-sitios-web/remocion-de-malware\">servicio de limpieza de malware</a>."
1085
-
1086
- msgid "IntegrityBadTitle"
1087
- msgstr "Archivos de WordPress Modificados"
1088
-
1089
- msgid "IntegrityBadDescription"
1090
- msgstr "Hemos identificado que algunos de los archivos de WordPress han sido modificados. Esto podría indicar un ataque o la existencia de un archivo corrupto en tu instalación. Si estás experimentando otros problemas, por favor usa un <a target=\"_blank\" href=\"https://sucuri.net/es/seguridad-de-sitios-web/remocion-de-malware\">servicio de limpieza de malware</a>."
1091
-
1092
- msgid "MarkFixedDescription"
1093
- msgstr "Marcar uno o más archivos como revisado forzará al plugin a ignorar dichos archivos durante futuros escaneos, esto es útil cuando un archivo contiene falsos/positivos. Adicionalmente, puedes restaurar el contenido original si estos han sido modificados o eliminados, el plugin descargará una copia del código desde el repositorio oficial de WordPress. La eliminación de archivos es una operación irreversible, ten cuidado."
1094
-
1095
- msgid "Action"
1096
- msgstr "Acción"
1097
-
1098
- msgid "Status"
1099
- msgstr "Estado"
1100
-
1101
- msgid "FileSize"
1102
- msgstr "Tamaño"
1103
-
1104
- msgid "ModifiedAt"
1105
- msgstr "Modificado en"
1106
-
1107
- msgid "Pattern"
1108
- msgstr "Patrón"
1109
-
1110
- msgid "FilePath"
1111
- msgstr "Ubicación del Archivo"
1112
-
1113
- msgid "Directory"
1114
- msgstr "Directorio"
1115
-
1116
- msgid "MarkFixed"
1117
- msgstr "Marcar como Revisado"
1118
-
1119
- msgid "RestoreFile"
1120
- msgstr "Restaurar Archivo"
1121
-
1122
- msgid "DeleteFile"
1123
- msgstr "Eliminar Archivo"
1124
-
1125
- msgid "HoverForPayload"
1126
- msgstr "Ver Código Malicioso"
1127
-
1128
- msgid "LogsPerEvent"
1129
- msgstr "Tipos de Eventos"
1130
-
1131
- msgid "LogsForLogins"
1132
- msgstr "Autenticaciones Exitosas/Fallidas"
1133
-
1134
- msgid "LogsPerUser"
1135
- msgstr "Registros Por Usuario"
1136
-
1137
- msgid "LogsPerIP"
1138
- msgstr "Registros Por Dirección IP"
1139
-
1140
- msgid "Search"
1141
- msgstr "Buscar"
1142
-
1143
- msgid "FirewallSettingsTitle"
1144
- msgstr "Ajustes del Firewall"
1145
-
1146
- msgid "FirewallSettingsInfo"
1147
- msgstr "Un poderoso Firewall para aplicaciones web y <b>Sistema para la Detección de Intrusiones</b> para cualquier usuario de WordPress y cualquier otra plataforma. Esta página te ayudará a configurar y monitorear tu sitio web a través del <b>Firewall de Sucuri</b>. Una vez activado, nuestro Firewall actuará como un escudo, protegiendo tu sitio de ataques y previniendo infecciones de malware y reinfecciones. El Firewall también bloqueará inyecciones a bases de datos, ataques de fuerza bruta, XSS, RFI, puertas traseras y muchas otras amenazas en contra de tu sitio web."
1148
-
1149
- msgid "FirewallKey"
1150
- msgstr "Llave del Firewall"
1151
-
1152
- msgid "FirewallAddKey"
1153
- msgstr "Agrega la <a href=\"https://waf.sucuri.net/?settings&panel=api\" target=\"_blank\">llave del Firewall</a> en el formulario para iniciar una conexión con la API."
1154
-
1155
- msgid "FirewallFootNote"
1156
- msgstr "<em>[1]</em> Más información acerca del <a href=\"https://sucuri.net/es/firewall-de-sitios-web/\" target=\"_blank\">Firewall de Sucuri</a>, herramientas disponibles y precios.<br><em>[2]</em> Instrucciones y videos en la <a href=\"https://kb.sucuri.net/firewall\" target=\"_blank\">Wiki de Sucuri</a>.<br><em>[3]</em> <a href=\"https://login.sucuri.net/signup2/create?CloudProxy\" target=\"_blank\">Registra</a> una cuenta nueva y empieza a proteger tu sitio web."
1157
-
1158
- msgid "FirewallLogsTitle"
1159
- msgstr "Registros de Seguridad del Firewall"
1160
-
1161
- msgid "FirewallLogsInfo"
1162
- msgstr "El Firewall de Sucuri registra todas las conexiones HTTP involucradas en un ataque y las separa de las conexiones legítimas. Puedes usar esta herramienta para analizar la información de las últimas conexiones y tomar acción. O sea, activando las herramientas avanzadas del IDS <em>(Intrusion Detection System)</em> desde el <a target=\"_blank\" href=\"https://waf.sucuri.net/?settings\">panel de administración del Firewall</a> y/o bloquear las direcciones IP y URL directamente desde la <a href=\"https://waf.sucuri.net/?audit\" target=\"_blank\">página de registros de seguridad del Firewall</a>."
1163
-
1164
- msgid "FirewallLogsNote"
1165
- msgstr "Las conexiones que no fueron bloqueadas son omitidas de la lista, esto es intencional."
1166
-
1167
- msgid "FirewallIPAccessTitle"
1168
- msgstr "Dirección IP (Acceso)"
1169
-
1170
- msgid "FirewallIPAccessInfo"
1171
- msgstr "Esta herramienta le permite bloquear y desbloquear el acceso a su sitio web de una o mas direcciones IP. Además, puede bloquear direcciones IP automáticamente cuando estas están involucradas en un ataque de fuerza bruta para adivinar la contraseña de uno o más usuarios registrados. Si un usuario legítimo es bloqueado después de enviar las credenciales incorrectas de su cuenta más de tres veces, ellos deberán ingresar al panel de control del Firewall para eliminar su dirección IP de la lista negra, o intentar acceder al sitio a través de una VPN."
1172
-
1173
- msgid "WhitelistIP"
1174
- msgstr "Desbloquear Dirección IP"
1175
-
1176
- msgid "BlacklistIP"
1177
- msgstr "Bloquear Dirección IP"
1178
-
1179
- msgid "FirewallCacheTitle"
1180
- msgstr "Caché del Firewall"
1181
-
1182
- msgid "FirewallCacheButton"
1183
- msgstr "Limpiar Caché"
1184
-
1185
- msgid "FirewallCacheInfo"
1186
- msgstr "El Firewall ofrece multiples opciones para configurar el nivel de caché aplicado a tu sitio web. Puedes considerar activar la cobertura total de la caché <em>(que es lo recomendado)</em>, o puedes configurar la caché a un nivel mínimo, o forzar el uso de las cabeceras HTTP <em>(solo para usuarios avanzados)</em>, o en casos extremos donde no necesitas la caché, puedes desactivarla completamente. Puedes encontrar más información acerca de estas opciones <a target=\"_blank\" href=\"https://kb.sucuri.net/firewall/Performance/caching-options\">aquí</a>."
1187
-
1188
- msgid "FirewallCacheNote"
1189
- msgstr "Tenga en cuenta que el Firewall tiene unas <a href=\"https://kb.sucuri.net/firewall/Performance/cache-exceptions\" target=\"_blank\">reglas especiales para la caché</a> de imágenes, archivos CSS, archivos PDF, archivos de texto, código JavaScript, archivos audio visuales y algunas otros formatos de archivo que son guardados en nuestros <a href=\"https://en.wikipedia.org/wiki/Edge_device\" target=\"_blank\" rel=\"noopener\">Edge Devices</a>. La única forma de limpiar la caché de estos archivos es haciendo uso de parámetros aleatorios en la URL, puede agregar un parametro como este <code>?ver=1.2.3</code> e incrementar el número con cada actualización."
1190
-
1191
- msgid "FirewallCacheWiki"
1192
- msgstr "La caché web es una tecnología para el almacenamiento temporal (caching) de documentos web, como páginas en HTML e imágenes, para reducir el uso de ancho de banda, la carga del servidor, y retrasos en las descargas. Un sistema de caché web guarda copias de los documentos que pasan a través de él; conexiones subsecuentes pueden devolver el contenido desde la caché si las condiciones lo permiten. &mdash; <a href=\"https://en.wikipedia.org/wiki/Web_cache\" target=\"_blank\" rel=\"noopener\">WikiPedia - Web Cache</a>"
1193
-
1194
- msgid "FirewallAutoClearCache"
1195
- msgstr "Limpiar cache automáticamente cuando una publicación o página sea modificada"
1196
-
1197
- msgid "LoginsAdmins"
1198
- msgstr "Autenticaciones Exitosas (admins)"
1199
-
1200
- msgid "LoginsAdminsInfo"
1201
- msgstr "Aquí puede encontrar una lista con todas las autenticaciones exitosas de cuentas de usuario con privilegios de administración."
1202
-
1203
- msgid "Registration"
1204
- msgstr "Registro"
1205
-
1206
- msgid "NewestLogins"
1207
- msgstr "Ordenados de Nuevos a Viejos"
1208
-
1209
- msgid "Admins"
1210
- msgstr "Administradores"
1211
-
1212
- msgid "AllUsers"
1213
- msgstr "Todos Los Usuarios"
1214
-
1215
- msgid "LoginsAll"
1216
- msgstr "Autenticaciones Exitosas (todos)"
1217
-
1218
- msgid "LoginsAllInfo"
1219
- msgstr "Aquí puede encontrar una lista con todas las autenticaciones exitosas de todas las cuentas de usuario."
1220
-
1221
- msgid "BlockedUsers"
1222
- msgstr "Usuarios Bloqueados"
1223
-
1224
- msgid "BlockedUsersInfo"
1225
- msgstr "Todo intento de autenticación que haga uso de las funciones nativas de WordPress será interceptado y analizado por el plugin, si el nombre de usuario coincide con alguno en esta lista la conexión al servidor será inmediatamente detenida. Estos intentos no serán registrados y ninguna alerta de seguridad será enviada."
1226
-
1227
- msgid "BlockedUsersNote"
1228
- msgstr "Ten en cuenta que esta herramienta no intenta proveer una protección completa para bloquear autenticaciones indeseadas de usuarios maliciosos. Dependiendo de la configuración de tu sitio web, los plugins o plantillas que sean instalados, o inclusive la versión de WordPress que estés usando, habrán huecos en el sistema que usuarios maliciosos pueden utilizar para evadir estos bloqueos e iniciar un ataque por fuerza bruta. <a target=\"_blank\" href=\"https://sucuri.net/es/firewall-de-sitios-web/?wp=bu\">Instala un Firewall</a> para obtener protección completa y mitigar este y otros ataques en contra tu sitio web."
1229
-
1230
- msgid "BlockedUsersByIP"
1231
- msgstr "El bloqueo de usuarios por dirección IP es una opción disponible en el <a href=\"https://sucuri.net/es/firewall-de-sitios-web/\" target=\"_blank\">Firewall de Sucuri</a>; para evitar la duplicación de código y reducir la cantidad de falsos/positivos esta opción nunca será implementada en este plugin."
1232
-
1233
- msgid "FailedLogins"
1234
- msgstr "Autenticaciones Fallidas"
1235
-
1236
- msgid "FailedLoginsInfo"
1237
- msgstr "Esta información será usada para determinar si tu sitio es víctima de un <a href=\"https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing\" target=\"_blank\">Password Guessing Brute Force Attacks</a>. Estos registros serán acumulados, si más de <code>%%SUCURI.FailedLogins.MaxFailedLogins%%</code> autenticaciones fallidas son registradas durante la misma hora, el plugin enviará un correo electrónico con un resumen del ataque <em>(debe activar esta opción desde la página de ajustes, sección alertas)</em>. <b>NOTA:</b> Algunos plugins que ofrecen la opción de <em>\"Autenticación por Dos-Pasos\"</em> no usan las funciones nativas de WordPress, motivo por el cual algunos intentos de autenticación no podrán ser interceptados ni analizados."
1238
-
1239
- msgid "LoggedInUsers"
1240
- msgstr "Usuarios Activos"
1241
-
1242
- msgid "LoggedInUsersInfo"
1243
- msgstr "Aquí puedes encontrar una lista de usuarios que han iniciado una sesión en los últimos minutos."
1244
-
1245
- msgid "Recomendations"
1246
- msgstr "Recomendaciones"
1247
-
1248
- msgid "SiteCheckNoResults"
1249
- msgstr "Si nuestro escáner gratuito no ha detectado ningún problema, es posible que tu sitio tenga un problema más complicado y la infección esté escondida. Puede <a target=\"_blank\" href=\"https://sucuri.net/es/plataforma-de-seguridad-de-sitios-web/signup\">registrar una cuenta con Sucuri</a> para obtener un escaneo más detallado y eventualmente una limpieza absoluta de tu sitio web (no incluido con el plan gratuito)."
1250
-
1251
- msgid "SiteCheckTarget"
1252
- msgstr "URL Para el Escáner de Malware"
1253
-
1254
- msgid "SiteCheckTargetInfo"
1255
- msgstr "El escáner remoto de malware funciona gracias a <a href=\"https://sitecheck.sucuri.net/\" target=\"_blank\">Sucuri SiteCheck</a>, un servicio que toma la dirección de un sitio web con acceso público y la escanea en busca de código malicioso. Si su sitio web no es visible a través de Internet, por ejemplo, si el sitio está en un ambiente de desarrollo local or una red restringida, el escáner no podrá trabajar en él. Adicionalmente, si el sitio web fué instalado en un directorio diferente al sugerido el escáner reportará un error \"404 Not Found\". Puedes usar esta opción para cambiar la dirección del sitio web que será escaneado."
1256
-
1257
- msgid "PasswordAttack"
1258
- msgstr "Ataque De Fuerza Bruta En Contra De Contraseñas"
1259
-
1260
- msgid "PasswordAttackAfter"
1261
- msgstr "Considerar Ataque Después De"
1262
-
1263
- msgid "PasswordAttackInfo"
1264
- msgstr "Los <a href=\"https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing\" target=\"_blank\">ataques de fuerza bruta en contra de contraseñas</a> son muy comunes en sitios web y páneles de administración. Estos son uno de los vectores más comunes usado para comprometer sitios web. El proceso es muy simple, los atacantes básicamente intentan múltiple combinaciones de usuario y contraseña hasta que encuentran una que funciona. Una vez el acceso al sitio es obtenido, ellos inyectan código malicioso para generar spam, crear páginas fraudulentas, etc."
1265
-
1266
- msgid "PostTypeAlerts"
1267
- msgstr "Alerts Para Diferentes Tipos de Publicaciones"
1268
-
1269
- msgid "IgnoredAt"
1270
- msgstr "Ignorado en"
1271
-
1272
- msgid "PostType"
1273
- msgstr "Tipo de Publicación"
1274
-
1275
- msgid "PostTypeAlertsDisabled"
1276
- msgstr "Desactivaste las alertas de seguridad, cuando una publicación es creada o modificada, esta herramienta espera que esa opción esté activa para poder filtrar los tipos de publicaciones que no son relevantes para el administrador del sitio."
1277
-
1278
- msgid "PostTypeAlertsInfo"
1279
- msgstr "Esta es una lista de <a href=\"https://codex.wordpress.org/Post_Types\" target=\"_blank\" rel=\"noopener\">tipos de publicaciones</a> registradas. Recibirás alertas de seguridad cuando una página o publicación sea creada o modificada. Algunos de estos tipos son nativos de WordPress pero la mayoría son creados por plugins y plantillas que tengas instalados. Si no quieres recibir alertas por cambios en uno o más de estos tipos, puedes usar este panel para desactivarlas."
1280
-
1281
- msgid "PostTypeAlertsInvisible"
1282
- msgstr "Si estás recibiendo alertas por cambios en tipos de publicaciones que no aparecen en la lista, es posible que estas hayan sido creadas dinámicamente por un plugin o plantilla sin usar las funciones nativas de WordPress. Si conoce el identificador único del tipo de publicación, puedes ingresarlo en el formulario y el plugin de Sucuri hará lo mejor posible para ignorar estas alertas."
1283
-
1284
- msgid "PostTypeAlertsStop"
1285
- msgstr "Ignorar Cambios En Este Tipo De Publicaciones"
1286
-
1287
- msgid "AlertsPerHour"
1288
- msgstr "Alertas Por Hora"
1289
-
1290
- msgid "AlertsPerHourMaximum"
1291
- msgstr "Número Máximo De Alertas Por Hora"
1292
-
1293
- msgid "AlertsPerHourInfo"
1294
- msgstr "Utiliza esta opción para configurar el número máximo de alertas de seguridad por hora. Si el número es excedido y el plugin detecta más eventos durante la misma hora, estos serán agregados al registro de seguridad pero no serán enviados por correo electrónico. Ten cuidado con esta opción, ya que puedes perder información importante."
1295
-
1296
- msgid "TestAlerts"
1297
- msgstr "Probar Alertas"
1298
-
1299
- msgid "AlertsRecipient"
1300
- msgstr "Quién Recibirá Alertas"
1301
-
1302
- msgid "AlertsRecipientInfo"
1303
- msgstr "Por defecto, el plugin enviará las alertas de seguridad a la cuenta de administración primaria, la misma cuenta que fue creada durante el proceso de instalación de WordPress en tu servidor web. Puedes agregar más direcciones de correo a la lista, ellos recibirán una copia de las mismas alertas de seguridad."
1304
-
1305
- msgid "CustomFormat"
1306
- msgstr "Formato Personalizado"
1307
-
1308
- msgid "AlertsSubject"
1309
- msgstr "Asunto de la Alerta"
1310
-
1311
- msgid "AlertsSubjectInfo"
1312
- msgstr "El formato para el asunto de las alertas de seguridad, por defecto las alertas incluirán el nombre del sitio web y el identificador del evento, puedes usar este panel para agregar la dirección IP del usuario o cualquier otra información adicional. También puedes crear filtros en tu cliente de correo electrónico, agregando etiquetas personalizadas en el formulario."
1313
-
1314
- msgid "TrustedIPs"
1315
- msgstr "Direcciones IP Seguras"
1316
-
1317
- msgid "CIDRFormat"
1318
- msgstr "Formato CIDR"
1319
-
1320
- msgid "IPAddedAt"
1321
- msgstr "Dirección Agregada en"
1322
-
1323
- msgid "TrustedIPsInfo"
1324
- msgstr "Si estás trabajando en una red de área local, puedes querer incluir las direcciones IP de todos los nodos de la red en esta lista, esto hará que el plugin ignore todos los eventos originados desde estas direcciones para reducir la cantidad de falsos/positivos. Puede usar el formato CIDR <em>(Classless Inter Domain Routing)</em> para especificar un rango de direcciones IP <em>(solo 8, 16, y 24)</em>."
1325
-
1326
- msgid "APIViaProxy"
1327
- msgstr "API a Través de un Proxy"
1328
-
1329
- msgid "APIViaProxyInfo"
1330
- msgstr "Todas las conexiones a través de HTTP usadas para comunicarse con la API utilizan las funciones nativas de WordPress, esto significa que puedes usar el mismo mecanismo sugerido por WordPress para establecer estas conexiones a través de un proxy. De acuerdo a la <a href=\"https://developer.wordpress.org/reference/classes/wp_http_proxy/\" target=\"_blank\" rel=\"noopener\">documentación oficial</a>, debes agregar estas constantes en el archivo de configuración: <em>WP_PROXY_HOST, WP_PROXY_PORT, WP_PROXY_USERNAME, WP_PROXY_PASSWORD</em>."
1331
-
1332
- msgid "ProxyHostname"
1333
- msgstr "Dirección del Servidor Proxy"
1334
-
1335
- msgid "ProxyPort"
1336
- msgstr "Número del Puerto del Proxy"
1337
-
1338
- msgid "ProxyUsername"
1339
- msgstr "Nombre de Usuario del Proxy"
1340
-
1341
- msgid "ProxyPassword"
1342
- msgstr "Contraseña del Proxy"
1343
-
1344
- msgid "APICommunication"
1345
- msgstr "Conexión con la API"
1346
-
1347
- msgid "APICommunicationInfo"
1348
- msgstr "Una vez la llave para la API haya sido generada, el plugin establecerá una comunicación con un servicio remoto que actuará como una caja fuerte para almacenar los registros de seguridad generados cuando algo sospechoso suceda en tu sitio web. Si el sitio es comprometido, el hacker no podrá eliminar los registros de seguridad para encubrir sus huellas, de esta forma podrás investigar el ataque y prevenir futuros problemas."
1349
-
1350
- msgid "APICommunicationDisabled"
1351
- msgstr "Desactivar la comunicación entre el plugin y la API detendrá el monitor de eventos de seguridad. Considera activar el exportador de registros desde la página general de ajustes para mantener el monitor de eventos funcionando sin enviar datos a la API, de lo contrario, un usuario malicioso podrá ejecutar acciones sin que un administrador se de cuenta de lo que está sucediendo."
1352
-
1353
- msgid "APITimeout"
1354
- msgstr "Tiempo de Espera para la API"
1355
-
1356
- msgid "APITimeoutLabel"
1357
- msgstr "Tiempo de Espera (en segundos)"
1358
-
1359
- msgid "APITimeoutValue"
1360
- msgstr "Esperar <b>%%SUCURI.RequestTimeout%%</b> antes de terminar la conexión"
1361
-
1362
- msgid "APITimeoutInfo"
1363
- msgstr "El plugin envía a la API información asociada a los eventos generados por WordPress que sean considerados sospechosos. El envío de estos datos se hace a través de HTTP usando uno de los protocolos de comunicación disponibles en el sistema y <a target=\"_blank\" href=\"https://developer.wordpress.org/reference/functions/wp_remote_post/\" rel=\"noopener\">funciones nativas de WordPress</a>. Si la conexión o el servicio remoto excede la cantidad de segundos configurados en esta opción, la operación es cancelada. Si experimentas problemas de conexión, por favor comunícate con tu proveedor de hosting para investigar los bloqueos."
1364
-
1365
- msgid "HTAccessTitle"
1366
- msgstr "Integridad del Archivo .htaccess"
1367
-
1368
- msgid "HTAccessInfo"
1369
- msgstr "El archivo <code>.htaccess</code> es un archivo de configuración usado por el servidor web Apache para aplicar cambios generales por directorio. WordPress utiliza este archivo para manipular las direcciones de las páginas creadas por los usuarios; más notable, WordPress utiliza este archivo para generar URL amigables."
1370
-
1371
- msgid "HTAccessFound"
1372
- msgstr "Archivo encontrado en esta ubicación <code>%%SUCURI.HTAccess.Fpath%%</code>"
1373
-
1374
- msgid "HTAccessNotFound"
1375
- msgstr "El sitio web no tiene un archivo <code>.htaccess</code> o no se encuentra en la ubicación esperada."
1376
-
1377
- msgid "HTAccessStandard"
1378
- msgstr "El archivo <code>.htaccess</code> principal contiene las reglas estándares de WordPress generadas durante la instalación del sitio web. Puedes modificar estas reglas para aumentar el rendimiento del servidor o para generar redirecciones a otras páginas. Para obtener más información al respecto visita la página oficial con la documentación en <a target=\"_blank\" rel=\"noopener\" href=\"https://codex.wordpress.org/Using_Permalinks#Creating_and_editing_.28.htaccess.29\"> Codex WordPress - Creating and editing (.htaccess)</a>"
1379
-
1380
- msgid "ScannerDescription"
1381
- msgstr "El plugin escanea tu sitio web en busca de cambios que son eventualmente reportados a la API y visualizados a través de los registros de seguridad. Este escáner es ejecutado automáticamente cada día, pero puedes cambiar la frecuencia dependiendo de tus necesidades. Ten en cuenta que si tu sitio contiene una gran cantidad de archivos, es posible que el escáner sea bloqueado por tu proveedor de hosting si este excede el límite de ejecución que ellos hayan impuesto sobre su cuenta."
1382
-
1383
- msgid "ScannerWithoutSPL"
1384
- msgstr "El escáner hace uso de las librerías <a href=\"http://php.net/manual/en/class.splfileobject.php\" target=\"_blank\" rel=\"noopener\">SPL</a> y <a target=\"_blank\" href=\"http://php.net/manual/en/class.filesystemiterator.php\" rel=\"noopener\">Filesystem Iterator</a> de PHP para escanear el árbol de directorios de tu sitio web. Esta librería solo está disponible en PHP 5 >= 5.3.0 &mdash; y &mdash; PHP 7; si tu servidor hace uso de una versión antigua, el plugin no funcionará como se espera. Por favor, contacta a tu proveedor de hosting para obtener ayuda al respecto."
1385
-
1386
- msgid "IntegrityLanguageTitle"
1387
- msgstr "Integridad de WordPress (Idioma)"
1388
-
1389
- msgid "IntegrityInfo"
1390
- msgstr "La información necesaria para verificar la integridad de tu instalación de WordPress proviene de la <a href=\"http://codex.wordpress.org/WordPress.org_API\" target=\"_blank\" rel=\"noopener\">API Oficial de WordPress</a>. El plugin compara esta información con los archivos instalados en tu sitio web. Por defecto, la API provee una copia de los archivos en Inglés, si instalaste un idioma diferente, por favor selecciónalo en esta opción para reducir la cantidad de falsos/positivos que el escáner pueda encontrar."
1391
-
1392
- msgid "IntegrityNote"
1393
- msgstr "<b>Nota:</b> No todos los idiomas están soportados por la API de WordPress. Es posible que tengas que usar la versión en Inglés para reducir la cantidad de falsos/positivos arrojados por el escáner durante su ejecución. Como alternativa, puede seleccionar dichos archivos y marcarlos como revisados para que el escáner los ignore."
1394
-
1395
- msgid "Reason"
1396
- msgstr "Motivo"
1397
-
1398
- msgid "FalsePositives"
1399
- msgstr "Integridad de WordPress (Falsos/Positivos)"
1400
-
1401
- msgid "FalsePositivesUnignore"
1402
- msgstr "No Ignorar Estos Archivos"
1403
-
1404
- msgid "FalsePositivesInfo"
1405
- msgstr "El escáner de archivos no lee el contenido de estos, sino que compara el Checksum para saber si han sido modificados. Un falso/positivo ocurre cuando la modificación no hace parte de una inyección de código malicioso, estas modificaciones pueden ser consideradas inofencivas. Los archivos en esta lista han sido marcados como falsos/positivos y serán ignorados durante los próximos escaneos del sistema."
1406
-
1407
- msgid "IgnoreFiles"
1408
- msgstr "Ignorar Archivos Durante Los Escaneos"
1409
-
1410
- msgid "IgnoreFilesSingle"
1411
- msgstr "Ignorar Un Solo Archivo"
1412
-
1413
- msgid "IgnoreFilesInfo"
1414
- msgstr "Utiliza esta herramienta para seleccionar directorios que contienen muchos archivos o archivos que son muy pesados para ser procesados por el escáner de seguridad. Esto usualmente incluye directorios con imágenes, archivos multimedia, y en general cualquier archivo que no tenga código. Ignorar estos archivos mejorará el rendimiento del plugin y mantendrá el consumo de memoria a niveles mínimos."
1415
-
1416
- msgid "SecretKeys"
1417
- msgstr "Llaves de Seguridad"
1418
-
1419
- msgid "SecretKeysUpdated"
1420
- msgstr "Las llaves de seguridad han sido cambiadas (abajo puedes encontrar un resumen de la operación)."
1421
-
1422
- msgid "SecretKeysGenerate"
1423
- msgstr "Generar Nuevas Llaves de Seguridad"
1424
-
1425
- msgid "SecretKeysExpiration"
1426
- msgstr "Tu sesión de usuario expirará inmediatamente cuando las nuevas llaves de seguridad sean generadas."
1427
-
1428
- msgid "SecretKeysInfo"
1429
- msgstr "Las llaves secretas o de seguridad son una lista de constantes aleatorias agregadas a tu sitio web, para mejorar el proceso de encriptación de las cookies para cada usuario. Las llaves hacen que tu sitio sea más difícil de vulnerar, al agregar elementos aleatorios en las contraseñas. No necesitas memorizar estas llaves, ellas serán guardadas en el archivo de configuración de WordPress. Puede cambiarlas en cualquier momento para invalidar sesiones de usuario existentes, forzando a todos los usuarios a autenticar sus cuentas nuevamente."
1430
-
1431
- msgid "PluginReinstall"
1432
- msgstr "Re-Instalar Plugins"
1433
-
1434
- msgid "PluginReinstallInfo"
1435
- msgstr "En el caso de que sospeches que tu sitio web está infectado con código maliciosos, o incluso después de limpiar tu sitio, es una buena idea reinstalar todos los plugins y plantillas, incluso aquellos que no están activados, para asegurar que el código es el original. Ten en cuenta que plugins y plantillas premium no pueden ser reinstaladas por motivos de compatibilidad con sus licencias."
1436
-
1437
- msgid "PluginReinstallCache"
1438
- msgstr "La información mostrada aquí es guardada en cache por %%SUCURI.ResetPlugin.CacheLifeTime%% segundos, esto es necesario para reducir el número de conexiones HTTP y subsecuentemente el consumo de ancho de banda de tu servidor. Actualmente no existe una opción para limpiar esta caché, debes esperar a que el plugin la regenere automáticamente."
1439
-
1440
- msgid "PluginReinstallWarning"
1441
- msgstr "<b>¡ADVERTENCIA!</b> La reinstalación de uno o más plugin puede ocasionar daños irreversibles a tu sitio web, procede con cuidado y asegúrate de mantener una copia de seguridad actualizada del sitio a la mano. Esta herramienta no tendrá ningún efecto sobre la base de datos."
1442
-
1443
- msgid "Roles"
1444
- msgstr "Privilegios"
1445
-
1446
- msgid "Registered"
1447
- msgstr "Registrado"
1448
-
1449
- msgid "PasswordChange"
1450
- msgstr "Cambiar la Contraseña de Usuario"
1451
-
1452
- msgid "PasswordChangeInfo"
1453
- msgstr "Puedes generar una nueva contraseña aleatoria para las cuentas de usuario que selecciones de la lista. Un correo será enviado a cada usuario. Si decides cambiar tu propia contraseña, tu sesión de usuario expirará inmediatamente. Necesitarás autenticar tu cuenta nuevamente con la nueva contraseña que recibirás en tu correo electrónico."
1454
-
1455
- msgid "PasswordChangeAlert"
1456
- msgstr "WordPress ha generado una nueva contraseña de forma aleatoria para tu cuenta de usuario <b>%%SUCURI.ResetPassword.UserName%%</b> en este sitio web <a target=\"_blank\" href=\"http://%%SUCURI.ResetPassword.Website%%\">%%SUCURI.ResetPassword.Website%%</a>. El cambio ha sido solicitado por uno de los administradores del sitio por motivos de seguridad. Tu nueva contraseña es &mdash; <span style=\"font-family:Menlo, Monaco, monospace, serif;font-weight:700\">%%%SUCURI.ResetPassword.Password%%%</span> &mdash; por favor cámbiala lo antes posible."
1457
-
1458
- msgid "Update"
1459
- msgstr "Actualización"
1460
-
1461
- msgid "TestedWith"
1462
- msgstr "Probado Con"
1463
-
1464
- msgid "AvailableUpdates"
1465
- msgstr "Actualizaciones Disponibles"
1466
-
1467
- msgid "AvailableUpdatesInfo"
1468
- msgstr "WordPress tiene una acogida muy grande en Internet, esto atrae la atención de personas maliciosas para encontrar vulnerabilidades en el código, al igual que en el código de plugins y plantillas que otras empresas desarrollan. Es recomendable mantener plugins y plantillas actualizadas para prevenir ataques tan pronto como vulnerabilidades sean descubiertas."
1469
-
1470
- msgid "WhitelistScript"
1471
- msgstr "Desbloquear Archivos PHP"
1472
-
1473
- msgid "WhitelistScriptInfo"
1474
- msgstr "Después de aplicar el hardening en los directorios soportados por el plugin, un archivo .htaccess será creado con reglas para bloquear el acceso directo a todos los archivos PHP contenidos en dichos directorios, esta es una buena precaución para evitar la ejecución de código malicioso; con algunas excepciones, el archivo <em>\"index.php\"</em> es el único que debería tener acceso público, sin embargo muchos plugins y plantillas requieren de este acceso también, utilice esta herramienta para desbloquearlos."
1475
-
1476
- msgid "Uninstall"
1477
- msgstr "Eliminar Registros de Seguridad, Hardening y Configuraciones"
1478
-
1479
- msgid "UninstallInfo"
1480
- msgstr "Esta acción iniciará el proceso de desactivación / desinstalación del plugin. Todos los registro de seguridad guardados en el disco local, las reglas de hardening y las configuraciones serán eliminadas. Ten en cuenta que los registros de seguridad guardados en el servidor de Sucuri no serán eliminados, esto es para prevenir que usuarios maliciosos intenten borrar sus huellas después de ejecutar un ataque en contra del sitio web."
1481
-
1482
- msgid "ImportExport"
1483
- msgstr "Importar Y Exportar Ajustes"
1484
-
1485
- msgid "ImportExportInfo"
1486
- msgstr "Copie la información codificada en JSON que aparece en la caja de abajo, pegue esta información en la misma caja en el sitio web donde desea importar esta configuración. Ten en cuenta que algunas opciones serán omitidas, ya que estas contienen información irrelevante para otros sitios. Para importar la configuración de otro sitio web a este, reemplace el contenido de la caja con los datos codificados en JSON de otro sitio, luego presione el botón para continuar. Ten en cuenta que algunas opciones no serán importadas para reducir el riesgo de insertar datos arbitrarios en el disco."
1487
-
1488
- msgid "DataStorage"
1489
- msgstr "Almacenamiento de Datos"
1490
-
1491
- msgid "DataStorageInfo"
1492
- msgstr "Este es el directorio donde el plugin guardará los registros de seguridad, el archivo con las configuraciones, la caché y datos adicionales de otras herramientas. El plugin requiere de permisos de escritura en este directorio, así como en los archivos contenidos allí. Si prefieres mantener estos archivos en un directorio diferente al especificado, por favor define una constante llamada <em>\"SUCURI_DATA_STORAGE\"</em> en el archivo de configuración de WordPress, con la ruta absoluta del nuevo directorio."
1493
-
1494
- msgid "TimezoneTitle"
1495
- msgstr "Zona Horaria"
1496
-
1497
- msgid "TimezoneInfo"
1498
- msgstr "Esta opción define la zona horaria que será usada a través de todo el plugin para mostrar las fechas y horas cuando sea necesario. Esta opción también afecta la fecha y hora de la información visible en el panel de registros de seguridad que viene de un servidor remoto configurado para usar Horario del este de Norteamérica (EDT por sus siglas en Inglés). WordPress ofrece una opción en la página de ajustes generales que te permite configurar la zona horaria para todo el sitio web, sin embargo, si estás experimentando problemas con la hora en los registros de seguridad, esta opción te ayudará a solucionarlos."
1499
-
1500
- msgid "TimezoneStatus"
1501
- msgstr "La zona horaria para la fecha y hora en los registros de seguridad ha sido cambiada"
1502
-
1503
- msgid "ChecksumsAPI"
1504
- msgstr "WordPress Checksums API"
1505
-
1506
- msgid "ChecksumsAPIInfo"
1507
- msgstr "La herramienta que verifica la integridad de la instalación usa un servicio remoto mantenido por WordPress.org para determinar qué archivos fueron agregados, eliminados o modificados. La API devuelve una lista de archivos con sus respectivos checksums, esta información garantiza que la instalación no está corrupta. Tu puedes, sin embargo, apuntar el plugin a un repositorio en GitHub en caso de que estés usando una versión personalizada de WordPress como la <a href=\"https://github.com/WordPress/WordPress\" target=\"_blank\">versión alpha del código</a>."
1508
-
1509
- msgid "ChecksumsAPIChanged"
1510
- msgstr "La dirección web para obtener los checksums de WordPress ha sido cambiada"
1511
-
1512
- msgid "MaxExecutionTimeAlert"
1513
- msgstr "El servidor no pudo procesar esta acción rápido; tiempo máximo de ejecución alcanzado"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -265,7 +265,7 @@ This version adds support for the latest version of WordPress. Introduces new fe
265
  * Fix lazy load of the CSS and Scripts on the correct pages
266
  * Add audit log message fixer for the wpephpcompat_jobs event
267
  * Fix website URL in the template for the email alerts
268
- * Add message in the core integrity tool for false/positives
269
  * Add option to reset the content of some storage files
270
  * Add mechanism to display self-hosting logs as fallback
271
  * Fix incoherent failed login processor on pagination
265
  * Fix lazy load of the CSS and Scripts on the correct pages
266
  * Add audit log message fixer for the wpephpcompat_jobs event
267
  * Fix website URL in the template for the email alerts
268
+ * Add message in the core integrity tool for false positives
269
  * Add option to reset the content of some storage files
270
  * Add mechanism to display self-hosting logs as fallback
271
  * Fix incoherent failed login processor on pagination
src/api.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the api.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage api.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -33,7 +39,13 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
33
  * APIs allow the combination of multiple APIs into new applications known as
34
  * mashups.
35
  *
36
- * @see https://en.wikipedia.org/wiki/Application_programming_interface#Web_APIs
 
 
 
 
 
 
37
  */
38
  class SucuriScanAPI extends SucuriScanOption
39
  {
@@ -45,8 +57,8 @@ class SucuriScanAPI extends SucuriScanOption
45
  * URL, because of this we decided to write our own URL query builder to
46
  * keep control of the output.
47
  *
48
- * @param array $params May be an array or object containing properties.
49
- * @return string Returns a URL-encoded string.
50
  */
51
  private static function buildQuery($params = array())
52
  {
@@ -63,14 +75,15 @@ class SucuriScanAPI extends SucuriScanOption
63
  /**
64
  * Sends a HTTP request via WordPress WP_HTTP class.
65
  *
 
66
  * @see https://secure.php.net/manual/en/book.curl.php
67
  * @see https://developer.wordpress.org/reference/classes/wp_http/request/
68
  *
69
- * @param string $url The target URL where the request will be sent.
70
- * @param string $method HTTP method that will be used to send the request.
71
- * @param array $params Parameters for the request defined in an associative array.
72
- * @param array $args Request arguments like the timeout, headers, cookies, etc.
73
- * @return array|string|bool HTTP response, JSON-decoded array, or false on failure.
74
  */
75
  public static function apiCall($url = '', $method = 'GET', $params = array(), $args = array())
76
  {
@@ -141,18 +154,15 @@ class SucuriScanAPI extends SucuriScanOption
141
  }
142
 
143
  /* try to return a JSON-encode object */
144
- if ($data = @json_decode($res['body'], true)) {
145
- return $data; /* associative array */
146
- }
147
-
148
- return $res['body'];
149
  }
150
 
151
  /**
152
  * Check whether the plugin API key is valid or not.
153
  *
154
- * @param string $api_key An unique string to identify this installation.
155
- * @return bool True if the API key is valid, false otherwise.
156
  */
157
  private static function isValidKey($api_key = '')
158
  {
@@ -162,14 +172,14 @@ class SucuriScanAPI extends SucuriScanOption
162
  /**
163
  * Store the API key locally.
164
  *
165
- * @param string $api_key An unique string of characters to identify this installation.
166
- * @param bool $validate Whether the format of the key should be validated before store it.
167
- * @return bool Either true or false if the key was saved successfully or not respectively.
168
  */
169
  public static function setPluginKey($api_key = '', $validate = false)
170
  {
171
  if ($validate && !self::isValidKey($api_key)) {
172
- return SucuriScanInterface::error(__('InvalidAPIKey', SUCURISCAN_TEXTDOMAIN));
173
  }
174
 
175
  if (!empty($api_key)) {
@@ -198,11 +208,11 @@ class SucuriScanAPI extends SucuriScanOption
198
  /**
199
  * Call an action from the remote API interface of our WordPress service.
200
  *
201
- * @param string $method HTTP method that will be used to send the request.
202
- * @param array $params Parameters for the request defined in an associative array of key-value.
203
- * @param bool $send_api_key Whether the API key should be added to the request parameters or not.
204
- * @param array $args Request arguments like the timeout, redirections, headers, cookies, etc.
205
- * @return array|bool Response object after the HTTP request is executed.
206
  */
207
  public static function apiCallWordpress($method = 'GET', $params = array(), $send_api_key = true, $args = array())
208
  {
@@ -223,9 +233,10 @@ class SucuriScanAPI extends SucuriScanOption
223
  }
224
 
225
  /**
226
- * Determine whether an API response was successful or not checking the expected
227
- * generic variables and types, in case of an error a notification will appears
228
- * in the administrator panel explaining the result of the operation.
 
229
  *
230
  * For failures in the HTTP response:
231
  *
@@ -239,22 +250,8 @@ class SucuriScanAPI extends SucuriScanOption
239
  * address of the site administrator was changed so the data is not valid
240
  * anymore.
241
  *
242
- * Connection timeout: means that the API service is down either because the
243
- * hosting provider has connectivity issues or because the code is being
244
- * deployed. There is an option in the settings page that allows to temporarily
245
- * disable the communication with the API service while the server is down, this
246
- * allows the admins to keep the latency at zero and continue working in their
247
- * websites without interruptions.
248
- *
249
- * SSL issues: depending on the options used to compile the OpenSSL library
250
- * built by each hosting provider, the connection with the HTTPs version of the
251
- * API service may be rejected because of a failure in the SSL algorithm check.
252
- * There is an option in the settings page that allows to disable the SSL pair
253
- * verification, this option it disable automatically when the error is detected
254
- * for the first time.
255
- *
256
- * @param array $res HTTP response after API endpoint execution.
257
- * @return bool False if the API call failed, true otherwise.
258
  */
259
  public static function handleResponse($res = array())
260
  {
@@ -277,7 +274,7 @@ class SucuriScanAPI extends SucuriScanOption
277
  || !isset($res['messages'])
278
  || empty($res['messages'])
279
  ) {
280
- return SucuriScanInterface::error(__('ErrorNoInfo', SUCURISCAN_TEXTDOMAIN));
281
  }
282
 
283
  $msg = implode(".\x20", $res['messages']);
@@ -286,8 +283,11 @@ class SucuriScanAPI extends SucuriScanOption
286
  // Special response for invalid API keys.
287
  if (stripos($raw, 'log file not found') !== false) {
288
  $key = SucuriScanOption::getOption(':api_key');
289
- $key = SucuriScan::escape($key);
290
- $msg .= sprintf(__('ErrorLogFileNotFound', SUCURISCAN_TEXTDOMAIN), $key);
 
 
 
291
 
292
  SucuriScanOption::deleteOption(':api_key');
293
  }
@@ -296,7 +296,7 @@ class SucuriScanAPI extends SucuriScanOption
296
  if (stripos($raw, 'wrong api key') !== false) {
297
  $key = SucuriScanOption::getOption(':cloudproxy_apikey');
298
  $key = SucuriScan::escape($key);
299
- $msg .= sprintf(__('ErrorWrongAPIKey', SUCURISCAN_TEXTDOMAIN), $key);
300
 
301
  SucuriScanOption::setRevProxy('disable', true);
302
  SucuriScanOption::setAddrHeader('REMOTE_ADDR', true);
@@ -310,12 +310,15 @@ class SucuriScanAPI extends SucuriScanOption
310
  || stripos($raw, 'error setting certificate')
311
  || stripos($raw, 'SSL connect error')
312
  ) {
313
- $msg .= __('ErrorSSLCertificate', SUCURISCAN_TEXTDOMAIN);
 
 
 
314
  }
315
 
316
  // Check if the MX records as missing for API registration.
317
  if (strpos($raw, 'Invalid email') !== false) {
318
- $msg = __('ErrorInvalidEmail', SUCURISCAN_TEXTDOMAIN);
319
  }
320
 
321
  return SucuriScanInterface::error($msg);
@@ -324,8 +327,8 @@ class SucuriScanAPI extends SucuriScanOption
324
  /**
325
  * Send a request to the API to register this site.
326
  *
327
- * @param string $email Optional email address for the registration.
328
- * @return bool True if the API key was generated, false otherwise.
329
  */
330
  public static function registerSite($email = '')
331
  {
@@ -333,21 +336,26 @@ class SucuriScanAPI extends SucuriScanOption
333
  $email = self::getSiteEmail();
334
  }
335
 
336
- $res = self::apiCallWordpress('POST', array(
337
- 'e' => $email,
338
- 's' => self::getDomain(),
339
- 'a' => 'register_site',
340
- ), false);
341
-
342
- if (self::handleResponse($res)) {
343
- self::setPluginKey($res['output']['api_key']);
 
344
 
345
- SucuriScanEvent::installScheduledTask();
346
- SucuriScanEvent::notifyEvent('plugin_change', 'API key was generated and set');
347
- return SucuriScanInterface::info(__('AlertAPIKeySet', SUCURISCAN_TEXTDOMAIN));
348
  }
349
 
350
- return false;
 
 
 
 
 
351
  }
352
 
353
  /**
@@ -359,32 +367,40 @@ class SucuriScanAPI extends SucuriScanOption
359
  {
360
  $domain = self::getDomain();
361
 
362
- $res = self::apiCallWordpress('GET', array(
363
- 'e' => self::getSiteEmail(),
364
- 's' => $domain,
365
- 'a' => 'recover_key',
366
- ), false);
 
 
 
 
367
 
368
- if (self::handleResponse($res)) {
369
- SucuriScanEvent::notifyEvent('plugin_change', 'API key recovery for domain: ' . $domain);
370
- return SucuriScanInterface::info($res['output']['message']);
371
  }
372
 
373
- return false;
 
 
374
  }
375
 
376
  /**
377
  * Retrieve the event logs registered by the API service.
378
  *
379
- * @param int $lines Maximum number of logs to return.
380
  * @return array|bool The data structure with the logs.
381
  */
382
  public static function getAuditLogs($lines = 50)
383
  {
384
- $res = self::apiCallWordpress('GET', array(
385
- 'a' => 'get_logs',
386
- 'l' => $lines,
387
- ));
 
 
 
388
 
389
  if (!self::handleResponse($res)) {
390
  return false;
@@ -402,11 +418,17 @@ class SucuriScanAPI extends SucuriScanOption
402
  {
403
  $auditlogs = array();
404
  $cache = new SucuriScanCache('auditqueue');
 
405
 
406
- if ($events = $cache->getAll()) {
407
  $events = array_reverse($events);
408
 
409
  foreach ($events as $micro => $message) {
 
 
 
 
 
410
  $offset = strpos($micro, '_');
411
  $time = substr($micro, 0, $offset);
412
  $auditlogs[] = sprintf(
@@ -418,21 +440,23 @@ class SucuriScanAPI extends SucuriScanOption
418
  }
419
  }
420
 
421
- return self::parseAuditLogs(array(
422
  'status' => 1,
423
  'action' => 'get_logs',
424
  'request_time' => time(),
425
  'verbose' => 0,
426
  'output' => array_reverse($auditlogs),
427
  'total_entries' => count($auditlogs),
428
- ));
 
 
429
  }
430
 
431
  /**
432
  * Reads, parses and extracts relevant data from the security logs.
433
  *
434
- * @param array $res JSON-decoded logs.
435
- * @return array Full data extracted from the logs.
436
  */
437
  private static function parseAuditLogs($res)
438
  {
@@ -494,7 +518,7 @@ class SucuriScanAPI extends SucuriScanOption
494
  }
495
 
496
  /* extract the IP address */
497
- $log_data['message'] = substr($log_data['message'], $offset+2);
498
  $offset = strpos($log_data['message'], ";\x20");
499
  $log_data['remote_addr'] = substr($log_data['message'], 0, $offset);
500
 
@@ -502,11 +526,11 @@ class SucuriScanAPI extends SucuriScanOption
502
  if (strpos($log_data['remote_addr'], ",\x20")) {
503
  $index = strpos($log_data['remote_addr'], ",\x20");
504
  $log_data['username'] = substr($log_data['remote_addr'], 0, $index);
505
- $log_data['remote_addr'] = substr($log_data['remote_addr'], $index+2);
506
  }
507
 
508
  /* fix old user authentication logs for backward compatibility */
509
- $log_data['message'] = substr($log_data['message'], $offset+2);
510
  $log_data['message'] = str_replace(
511
  'logged in',
512
  'authentication succeeded',
@@ -516,7 +540,7 @@ class SucuriScanAPI extends SucuriScanOption
516
  /* extract the username of a successful/failed login */
517
  if (strpos($log_data['message'], "User authentication\x20") === 0) {
518
  $offset = strpos($log_data['message'], ":\x20");
519
- $username = substr($log_data['message'], $offset+2);
520
  if (strpos($username, ';') !== false) {
521
  $username = substr($username, 0, strpos($username, ';'));
522
  }
@@ -527,8 +551,8 @@ class SucuriScanAPI extends SucuriScanOption
527
  /* extract more information from the special formatted logs */
528
  if (strpos($log_data['message'], "(multiple entries):\x20")) {
529
  $offset = strpos($log_data['message'], "(multiple entries):\x20");
530
- $message = substr($log_data['message'], 0, $offset+19);
531
- $entries = substr($log_data['message'], $offset+20);
532
 
533
  $log_data['message'] = $message;
534
  $entries = str_replace(', new size', '; new size', $entries);
@@ -548,7 +572,9 @@ class SucuriScanAPI extends SucuriScanOption
548
  $log_data['file_list_count'] = count($log_data['file_list']);
549
  }
550
 
551
- if ($log_data = self::getLogsHotfix($log_data)) {
 
 
552
  $res['output_data'][] = $log_data;
553
  }
554
  }
@@ -559,8 +585,8 @@ class SucuriScanAPI extends SucuriScanOption
559
  /**
560
  * Modifies some of the security logs to detail the information.
561
  *
562
- * @param array $data Valid security log data structure.
563
- * @return array|bool Modified security log.
564
  */
565
  private static function getLogsHotfix($data)
566
  {
@@ -577,11 +603,11 @@ class SucuriScanAPI extends SucuriScanOption
577
  */
578
  if (isset($data['message']) && strpos($data['message'], 'Wpephpcompat_jobs') === 0) {
579
  $offset = strpos($data['message'], "ID:\x20");
580
- $id = substr($data['message'], $offset+4);
581
  $id = substr($id, 0, strpos($id, ';'));
582
 
583
  $offset = strpos($data['message'], "name:\x20");
584
- $name = substr($data['message'], $offset+6);
585
 
586
  $data['message'] = sprintf(
587
  'WP Engine PHP Compatibility Checker: %s (created post #%d as cache)',
@@ -613,8 +639,8 @@ class SucuriScanAPI extends SucuriScanOption
613
  /**
614
  * Parse the event logs with multiple entries.
615
  *
616
- * @param string $event_log Event log that will be processed.
617
- * @return string|array List of parts of the event log.
618
  */
619
  public static function parseMultipleEntries($event_log = '')
620
  {
@@ -627,98 +653,14 @@ class SucuriScanAPI extends SucuriScanOption
627
  return $event_log;
628
  }
629
 
630
- /**
631
- * Collect the information for the audit log report.
632
- *
633
- * @param int $lines How many lines from the log file will be retrieved.
634
- * @return array|bool All the information necessary to display the audit logs report.
635
- */
636
- public static function getAuditReport($lines = 50)
637
- {
638
- $audit_logs = self::getAuditLogs($lines);
639
-
640
- if (is_array($audit_logs)
641
- && array_key_exists('total_entries', $audit_logs)
642
- && array_key_exists('output_data', $audit_logs)
643
- && !empty($audit_logs['output_data'])
644
- ) {
645
- // Data structure that will be returned.
646
- $report = array(
647
- 'total_events' => 0,
648
- 'start_timestamp' => 0,
649
- 'end_timestamp' => 0,
650
- 'event_colors' => array(),
651
- 'events_per_type' => array(),
652
- 'events_per_user' => array(),
653
- 'events_per_ipaddress' => array(),
654
- 'events_per_login' => array(
655
- 'successful' => 0,
656
- 'failed' => 0,
657
- ),
658
- );
659
-
660
- // Get a list of valid audit event types.
661
- $event_types = self::getAuditEventTypes();
662
- foreach ($event_types as $event => $event_color) {
663
- $report['events_per_type'][$event] = 0;
664
- $report['event_colors'][] = $event_color;
665
- }
666
-
667
- // Collect information for each report chart.
668
- foreach ($audit_logs['output_data'] as $event) {
669
- $_username = SucuriScan::escape($event['username']);
670
- $_remote_addr = SucuriScan::escape($event['remote_addr']);
671
-
672
- @$report['total_events']++;
673
- @$report['events_per_user'][$_username]++;
674
- @$report['events_per_type'][$event['event']]++;
675
- @$report['events_per_ipaddress'][$_remote_addr]++;
676
-
677
- // Find the lowest datetime among the filtered events.
678
- if ($event['timestamp'] <= $report['start_timestamp']
679
- || $report['start_timestamp'] === 0
680
- ) {
681
- $report['start_timestamp'] = $event['timestamp'];
682
- }
683
-
684
- // Find the highest datetime among the filtered events.
685
- if ($event['timestamp'] >= $report['end_timestamp']) {
686
- $report['end_timestamp'] = $event['timestamp'];
687
- }
688
-
689
- /* backward compatibility for previous user login messages */
690
- if (strpos($event['message'], 'User logged in:') === 0) {
691
- $report['events_per_login']['successful']++;
692
- continue;
693
- }
694
-
695
- /* detect successful user authentications */
696
- if (strpos($event['message'], 'User authentication succeeded:') === 0) {
697
- $report['events_per_login']['successful']++;
698
- continue;
699
- }
700
-
701
- /* detect failed user authentications */
702
- if (strpos($event['message'], 'User authentication failed:') === 0) {
703
- $report['events_per_login']['failed']++;
704
- continue;
705
- }
706
- }
707
-
708
- return $report['total_events'] ? $report : false;
709
- }
710
-
711
- return false;
712
- }
713
-
714
  /**
715
  * Send a request to the API to store and analyze the file's hashes of the site.
716
  * This will be the core of the monitoring tools and will enhance the
717
  * information of the audit logs alerting the administrator of suspicious
718
  * changes in the system.
719
  *
720
- * @param string $hashes The information gathered after the scanning of the site's files.
721
- * @return bool True if the hashes were stored, false otherwise.
722
  */
723
  public static function sendHashes($hashes = '')
724
  {
@@ -726,10 +668,8 @@ class SucuriScanAPI extends SucuriScanOption
726
  return false;
727
  }
728
 
729
- $res = self::apiCallWordpress('POST', array(
730
- 'a' => 'send_hashes',
731
- 'h' => $hashes,
732
- ));
733
 
734
  return self::handleResponse($res);
735
  }
@@ -762,8 +702,9 @@ class SucuriScanAPI extends SucuriScanOption
762
  public static function checksumAPI()
763
  {
764
  $url = 'https://api.wordpress.org/core/checksums/1.0/?version={version}&locale={locale}';
 
765
 
766
- if ($custom = SucuriScanOption::getOption(':checksum_api')) {
767
  $url = sprintf(
768
  'https://api.github.com/repos/%s/git/trees/master?recursive=1',
769
  $custom /* expect: username/repository */
@@ -771,7 +712,7 @@ class SucuriScanAPI extends SucuriScanOption
771
  }
772
 
773
  $url = str_replace('{version}', SucuriScan::siteVersion(), $url);
774
- $url = str_replace('{locale}', SucuriScanOption::getOption(':language'), $url);
775
 
776
  return $url;
777
  }
@@ -809,9 +750,9 @@ class SucuriScanAPI extends SucuriScanOption
809
  *
810
  * @see https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#_object_storage
811
  *
812
- * @param string $algorithm Either md5 or sha1.
813
- * @param string $filename Absolute path to the given file.
814
- * @return string Hash of the given file.
815
  */
816
  public static function checksum($algorithm, $filename)
817
  {
@@ -850,7 +791,6 @@ class SucuriScanAPI extends SucuriScanOption
850
  */
851
  public static function getOfficialChecksums()
852
  {
853
- $result = false;
854
  $url = self::checksumAPI();
855
  $version = SucuriScan::siteVersion();
856
  $res = self::apiCall($url, 'GET', array());
@@ -868,13 +808,16 @@ class SucuriScanAPI extends SucuriScanOption
868
  $res = array('checksums' => array($version => $checksums));
869
  }
870
 
871
- if (is_array($res) && isset($res['checksums'])) {
872
- $result = isset($res['checksums'][$version])
873
- ? $res['checksums'][$version]
874
- : $res['checksums'];
 
 
 
875
  }
876
 
877
- return $result;
878
  }
879
 
880
  /**
@@ -920,7 +863,7 @@ class SucuriScanAPI extends SucuriScanOption
920
  $is_free_plugin = true;
921
  $repository = $plugin_data['PluginURI'];
922
  $offset = strpos($plugin_data['PluginURI'], '/plugins/');
923
- $repository_name = substr($plugin_data['PluginURI'], $offset+9);
924
 
925
  if (strpos($repository_name, '/') !== false) {
926
  $offset = strpos($repository_name, '/');
@@ -969,8 +912,8 @@ class SucuriScanAPI extends SucuriScanOption
969
  *
970
  * The second filter, 'plugins_api', is the result that would be returned.
971
  *
972
- * @param string $plugin Frienly name of the plugin.
973
- * @return array|bool Object on success, WP_Error on failure.
974
  */
975
  public static function getRemotePluginData($plugin = '')
976
  {
@@ -987,15 +930,16 @@ class SucuriScanAPI extends SucuriScanOption
987
  * @see https://i18n.svn.wordpress.org/
988
  * @see https://core.svn.wordpress.org/tags/VERSION_NUMBER/
989
  *
990
- * @param string $filename Relative path of a core file.
991
- * @return string|bool Original code for the core file, false otherwise.
992
  */
993
  public static function getOriginalCoreFile($filename)
994
  {
995
  $version = self::siteVersion();
996
  $url = 'https://core.svn.wordpress.org/tags/{version}/{filename}';
 
997
 
998
- if ($custom = SucuriScanOption::getOption(':checksum_api')) {
999
  $url = sprintf(
1000
  'https://raw.githubusercontent.com/%s/master/{filename}',
1001
  $custom /* expect: username/repository */
3
  /**
4
  * Code related to the api.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
39
  * APIs allow the combination of multiple APIs into new applications known as
40
  * mashups.
41
  *
42
+ * @category Library
43
+ * @package Sucuri
44
+ * @subpackage SucuriScanner
45
+ * @author Daniel Cid <dcid@sucuri.net>
46
+ * @copyright 2010-2017 Sucuri Inc.
47
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
48
+ * @link https://wordpress.org/plugins/sucuri-scanner
49
  */
50
  class SucuriScanAPI extends SucuriScanOption
51
  {
57
  * URL, because of this we decided to write our own URL query builder to
58
  * keep control of the output.
59
  *
60
+ * @param array $params May be an array or object containing properties.
61
+ * @return string Returns a URL-encoded string.
62
  */
63
  private static function buildQuery($params = array())
64
  {
75
  /**
76
  * Sends a HTTP request via WordPress WP_HTTP class.
77
  *
78
+ * @suppress PhanNonClassMethodCall
79
  * @see https://secure.php.net/manual/en/book.curl.php
80
  * @see https://developer.wordpress.org/reference/classes/wp_http/request/
81
  *
82
+ * @param string $url The target URL where the request will be sent.
83
+ * @param string $method HTTP method that will be used to send the request.
84
+ * @param array $params Parameters for the request defined in an associative array.
85
+ * @param array $args Request arguments like the timeout, headers, cookies, etc.
86
+ * @return mixed HTTP response, JSON-decoded array, or false on failure.
87
  */
88
  public static function apiCall($url = '', $method = 'GET', $params = array(), $args = array())
89
  {
154
  }
155
 
156
  /* try to return a JSON-encode object */
157
+ $data = @json_decode($res['body'], true);
158
+ return $data ? $data : $res['body'];
 
 
 
159
  }
160
 
161
  /**
162
  * Check whether the plugin API key is valid or not.
163
  *
164
+ * @param string $api_key An unique string to identify this installation.
165
+ * @return bool True if the API key is valid, false otherwise.
166
  */
167
  private static function isValidKey($api_key = '')
168
  {
172
  /**
173
  * Store the API key locally.
174
  *
175
+ * @param string $api_key An unique string of characters to identify this installation.
176
+ * @param bool $validate Whether the format of the key should be validated before store it.
177
+ * @return bool Either true or false if the key was saved successfully or not respectively.
178
  */
179
  public static function setPluginKey($api_key = '', $validate = false)
180
  {
181
  if ($validate && !self::isValidKey($api_key)) {
182
+ return SucuriScanInterface::error('Invalid API key format');
183
  }
184
 
185
  if (!empty($api_key)) {
208
  /**
209
  * Call an action from the remote API interface of our WordPress service.
210
  *
211
+ * @param string $method HTTP method that will be used to send the request.
212
+ * @param array $params Parameters for the request defined in an associative array of key-value.
213
+ * @param bool $send_api_key Whether the API key should be added to the request parameters or not.
214
+ * @param array $args Request arguments like the timeout, redirections, headers, cookies, etc.
215
+ * @return array|bool Response object after the HTTP request is executed.
216
  */
217
  public static function apiCallWordpress($method = 'GET', $params = array(), $send_api_key = true, $args = array())
218
  {
233
  }
234
 
235
  /**
236
+ * Determine whether an API response was successful or not by checking the
237
+ * expected generic variables and types, in case of an error a notification
238
+ * will appears in the administrator panel explaining the result of the
239
+ * operation.
240
  *
241
  * For failures in the HTTP response:
242
  *
250
  * address of the site administrator was changed so the data is not valid
251
  * anymore.
252
  *
253
+ * @param array $res HTTP response after API endpoint execution.
254
+ * @return bool False if the API call failed, true otherwise.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  */
256
  public static function handleResponse($res = array())
257
  {
274
  || !isset($res['messages'])
275
  || empty($res['messages'])
276
  ) {
277
+ return SucuriScanInterface::error('Unknown error, there is no information');
278
  }
279
 
280
  $msg = implode(".\x20", $res['messages']);
283
  // Special response for invalid 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
+ . ' 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
  }
296
  if (stripos($raw, 'wrong api key') !== false) {
297
  $key = SucuriScanOption::getOption(':cloudproxy_apikey');
298
  $key = SucuriScan::escape($key);
299
+ $msg .= sprintf('; invalid firewall API key: %s', $key);
300
 
301
  SucuriScanOption::setRevProxy('disable', true);
302
  SucuriScanOption::setAddrHeader('REMOTE_ADDR', true);
310
  || stripos($raw, 'error setting certificate')
311
  || stripos($raw, 'SSL connect error')
312
  ) {
313
+ $msg .= '. The website seems to be using an old version of the Ope'
314
+ . 'nSSL library or the CURL extension was compiled without support'
315
+ . ' for the algorithm used in the certificate installed in the API'
316
+ . ' service. Contact your hosting provider to fix this issue.';
317
  }
318
 
319
  // Check if the MX records as missing for API registration.
320
  if (strpos($raw, 'Invalid email') !== false) {
321
+ $msg = 'Invalid email format or the host is missing MX records.';
322
  }
323
 
324
  return SucuriScanInterface::error($msg);
327
  /**
328
  * Send a request to the API to register this site.
329
  *
330
+ * @param string $email Optional email address for the registration.
331
+ * @return bool True if the API key was generated, false otherwise.
332
  */
333
  public static function registerSite($email = '')
334
  {
336
  $email = self::getSiteEmail();
337
  }
338
 
339
+ $res = self::apiCallWordpress(
340
+ 'POST',
341
+ array(
342
+ 'e' => $email,
343
+ 's' => self::getDomain(),
344
+ 'a' => 'register_site',
345
+ ),
346
+ false
347
+ );
348
 
349
+ if (!self::handleResponse($res)) {
350
+ return false;
 
351
  }
352
 
353
+ self::setPluginKey($res['output']['api_key']);
354
+
355
+ SucuriScanEvent::installScheduledTask();
356
+ SucuriScanEvent::notifyEvent('plugin_change', 'API key was generated and set');
357
+
358
+ return SucuriScanInterface::info('API key successfully generated and saved.');
359
  }
360
 
361
  /**
367
  {
368
  $domain = self::getDomain();
369
 
370
+ $res = self::apiCallWordpress(
371
+ 'GET',
372
+ array(
373
+ 'e' => self::getSiteEmail(),
374
+ 's' => $domain,
375
+ 'a' => 'recover_key',
376
+ ),
377
+ false
378
+ );
379
 
380
+ if (!self::handleResponse($res)) {
381
+ return false;
 
382
  }
383
 
384
+ SucuriScanEvent::notifyEvent('plugin_change', 'API key recovery for domain: ' . $domain);
385
+
386
+ return SucuriScanInterface::info($res['output']['message']);
387
  }
388
 
389
  /**
390
  * Retrieve the event logs registered by the API service.
391
  *
392
+ * @param int $lines Maximum number of logs to return.
393
  * @return array|bool The data structure with the logs.
394
  */
395
  public static function getAuditLogs($lines = 50)
396
  {
397
+ $res = self::apiCallWordpress(
398
+ 'GET',
399
+ array(
400
+ 'a' => 'get_logs',
401
+ 'l' => $lines,
402
+ )
403
+ );
404
 
405
  if (!self::handleResponse($res)) {
406
  return false;
418
  {
419
  $auditlogs = array();
420
  $cache = new SucuriScanCache('auditqueue');
421
+ $events = $cache->getAll();
422
 
423
+ if (is_array($events) && !empty($events)) {
424
  $events = array_reverse($events);
425
 
426
  foreach ($events as $micro => $message) {
427
+ if (!is_string($message)) {
428
+ /* incompatible JSON data */
429
+ continue;
430
+ }
431
+
432
  $offset = strpos($micro, '_');
433
  $time = substr($micro, 0, $offset);
434
  $auditlogs[] = sprintf(
440
  }
441
  }
442
 
443
+ $res = array(
444
  'status' => 1,
445
  'action' => 'get_logs',
446
  'request_time' => time(),
447
  'verbose' => 0,
448
  'output' => array_reverse($auditlogs),
449
  'total_entries' => count($auditlogs),
450
+ );
451
+
452
+ return self::parseAuditLogs($res);
453
  }
454
 
455
  /**
456
  * Reads, parses and extracts relevant data from the security logs.
457
  *
458
+ * @param array $res JSON-decoded logs.
459
+ * @return array Full data extracted from the logs.
460
  */
461
  private static function parseAuditLogs($res)
462
  {
518
  }
519
 
520
  /* extract the IP address */
521
+ $log_data['message'] = substr($log_data['message'], $offset + 2);
522
  $offset = strpos($log_data['message'], ";\x20");
523
  $log_data['remote_addr'] = substr($log_data['message'], 0, $offset);
524
 
526
  if (strpos($log_data['remote_addr'], ",\x20")) {
527
  $index = strpos($log_data['remote_addr'], ",\x20");
528
  $log_data['username'] = substr($log_data['remote_addr'], 0, $index);
529
+ $log_data['remote_addr'] = substr($log_data['remote_addr'], $index + 2);
530
  }
531
 
532
  /* fix old user authentication logs for backward compatibility */
533
+ $log_data['message'] = substr($log_data['message'], $offset + 2);
534
  $log_data['message'] = str_replace(
535
  'logged in',
536
  'authentication succeeded',
540
  /* extract the username of a successful/failed login */
541
  if (strpos($log_data['message'], "User authentication\x20") === 0) {
542
  $offset = strpos($log_data['message'], ":\x20");
543
+ $username = substr($log_data['message'], $offset + 2);
544
  if (strpos($username, ';') !== false) {
545
  $username = substr($username, 0, strpos($username, ';'));
546
  }
551
  /* extract more information from the special formatted logs */
552
  if (strpos($log_data['message'], "(multiple entries):\x20")) {
553
  $offset = strpos($log_data['message'], "(multiple entries):\x20");
554
+ $message = substr($log_data['message'], 0, $offset + 19);
555
+ $entries = substr($log_data['message'], $offset + 20);
556
 
557
  $log_data['message'] = $message;
558
  $entries = str_replace(', new size', '; new size', $entries);
572
  $log_data['file_list_count'] = count($log_data['file_list']);
573
  }
574
 
575
+ $log_data = self::getLogsHotfix($log_data);
576
+
577
+ if ($log_data) {
578
  $res['output_data'][] = $log_data;
579
  }
580
  }
585
  /**
586
  * Modifies some of the security logs to detail the information.
587
  *
588
+ * @param array $data Valid security log data structure.
589
+ * @return array|bool Modified security log.
590
  */
591
  private static function getLogsHotfix($data)
592
  {
603
  */
604
  if (isset($data['message']) && strpos($data['message'], 'Wpephpcompat_jobs') === 0) {
605
  $offset = strpos($data['message'], "ID:\x20");
606
+ $id = substr($data['message'], $offset + 4);
607
  $id = substr($id, 0, strpos($id, ';'));
608
 
609
  $offset = strpos($data['message'], "name:\x20");
610
+ $name = substr($data['message'], $offset + 6);
611
 
612
  $data['message'] = sprintf(
613
  'WP Engine PHP Compatibility Checker: %s (created post #%d as cache)',
639
  /**
640
  * Parse the event logs with multiple entries.
641
  *
642
+ * @param string $event_log Event log that will be processed.
643
+ * @return string|array List of parts of the event log.
644
  */
645
  public static function parseMultipleEntries($event_log = '')
646
  {
653
  return $event_log;
654
  }
655
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
656
  /**
657
  * Send a request to the API to store and analyze the file's hashes of the site.
658
  * This will be the core of the monitoring tools and will enhance the
659
  * information of the audit logs alerting the administrator of suspicious
660
  * changes in the system.
661
  *
662
+ * @param string $hashes The information gathered after the scanning of the site's files.
663
+ * @return bool True if the hashes were stored, false otherwise.
664
  */
665
  public static function sendHashes($hashes = '')
666
  {
668
  return false;
669
  }
670
 
671
+ $params = array('a' => 'send_hashes', 'h' => $hashes);
672
+ $res = self::apiCallWordpress('POST', $params);
 
 
673
 
674
  return self::handleResponse($res);
675
  }
702
  public static function checksumAPI()
703
  {
704
  $url = 'https://api.wordpress.org/core/checksums/1.0/?version={version}&locale={locale}';
705
+ $custom = SucuriScanOption::getOption(':checksum_api');
706
 
707
+ if ($custom) {
708
  $url = sprintf(
709
  'https://api.github.com/repos/%s/git/trees/master?recursive=1',
710
  $custom /* expect: username/repository */
712
  }
713
 
714
  $url = str_replace('{version}', SucuriScan::siteVersion(), $url);
715
+ $url = str_replace('{locale}', get_locale(), $url);
716
 
717
  return $url;
718
  }
750
  *
751
  * @see https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#_object_storage
752
  *
753
+ * @param string $algorithm Either md5 or sha1.
754
+ * @param string $filename Absolute path to the given file.
755
+ * @return string Hash of the given file.
756
  */
757
  public static function checksum($algorithm, $filename)
758
  {
791
  */
792
  public static function getOfficialChecksums()
793
  {
 
794
  $url = self::checksumAPI();
795
  $version = SucuriScan::siteVersion();
796
  $res = self::apiCall($url, 'GET', array());
808
  $res = array('checksums' => array($version => $checksums));
809
  }
810
 
811
+ if (!isset($res['checksums'])) {
812
+ return false;
813
+ }
814
+
815
+ /* checksums for a specific version */
816
+ if (isset($res['checksums'][$version])) {
817
+ return $res['checksums'][$version];
818
  }
819
 
820
+ return $res['checksums'];
821
  }
822
 
823
  /**
863
  $is_free_plugin = true;
864
  $repository = $plugin_data['PluginURI'];
865
  $offset = strpos($plugin_data['PluginURI'], '/plugins/');
866
+ $repository_name = substr($plugin_data['PluginURI'], $offset + 9);
867
 
868
  if (strpos($repository_name, '/') !== false) {
869
  $offset = strpos($repository_name, '/');
912
  *
913
  * The second filter, 'plugins_api', is the result that would be returned.
914
  *
915
+ * @param string $plugin Frienly name of the plugin.
916
+ * @return array|bool Object on success, WP_Error on failure.
917
  */
918
  public static function getRemotePluginData($plugin = '')
919
  {
930
  * @see https://i18n.svn.wordpress.org/
931
  * @see https://core.svn.wordpress.org/tags/VERSION_NUMBER/
932
  *
933
+ * @param string $filename Relative path of a core file.
934
+ * @return string|bool Original code for the core file, false otherwise.
935
  */
936
  public static function getOriginalCoreFile($filename)
937
  {
938
  $version = self::siteVersion();
939
  $url = 'https://core.svn.wordpress.org/tags/{version}/{filename}';
940
+ $custom = SucuriScanOption::getOption(':checksum_api');
941
 
942
+ if ($custom) {
943
  $url = sprintf(
944
  'https://raw.githubusercontent.com/%s/master/{filename}',
945
  $custom /* expect: username/repository */
src/auditlogs.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the auditlogs.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage auditlogs.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -18,6 +24,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
18
 
19
  /**
20
  * Lists the logs collected by the API service.
 
 
 
 
 
 
 
 
21
  */
22
  class SucuriScanAuditLogs
23
  {
@@ -55,6 +69,8 @@ class SucuriScanAuditLogs
55
  * output it means that XDebug cannot cover the next line, leaving a report
56
  * with a missing line in the coverage. Since the test case takes care of
57
  * the functionality of this code we will assume that it is fully covered.
 
 
58
  */
59
  public static function ajaxAuditLogs()
60
  {
@@ -92,14 +108,16 @@ class SucuriScanAuditLogs
92
  ob_end_clean();
93
 
94
  /* report latency in the API calls */
95
- $response['status'] = !is_array($auditlogs)
96
- ? __('AuditLogsNoAPI', SUCURISCAN_TEXTDOMAIN)
97
- : sprintf('API %s secs', round($duration, 4));
 
 
98
  }
99
 
100
  /* explain missing API key */
101
  if (!SucuriScanAPI::getPluginKey()) {
102
- $response['status'] = __('APIKeyMissing', SUCURISCAN_TEXTDOMAIN);
103
  }
104
 
105
  /* stop everything and report errors */
@@ -113,13 +131,12 @@ class SucuriScanAuditLogs
113
  }
114
 
115
  /* merge the logs from the queue system */
116
- if ($queuelogs = SucuriScanAPI::getAuditLogsFromQueue()) {
 
 
117
  if (!$auditlogs) {
118
  $auditlogs = $queuelogs;
119
  } else {
120
- $auditlogs['total_entries'] = isset($auditlogs['total_entries'])
121
- ? ($auditlogs['total_entries'] + $queuelogs['total_entries'])
122
- : $queuelogs['total_entries'];
123
  $auditlogs['output'] = array_merge(
124
  $queuelogs['output'],
125
  @$auditlogs['output']
@@ -128,102 +145,114 @@ class SucuriScanAuditLogs
128
  $queuelogs['output_data'],
129
  @$auditlogs['output_data']
130
  );
 
 
 
 
 
 
131
  }
132
  }
133
 
134
- if (is_array($auditlogs)
135
- && isset($auditlogs['output_data'])
136
- && isset($auditlogs['total_entries'])
137
- && is_array($auditlogs['output_data'])
138
- && is_numeric($auditlogs['total_entries'])
139
  ) {
140
- $counter_i = 0;
141
- $total_items = 0;
142
- $previousDate = '';
143
- $todaysDate = SucuriScan::datetime(null, 'M d, Y');
144
- $iterator_start = ($pageNumber - 1) * $maxPerPage;
145
- $total_items = count($auditlogs['output_data']);
146
-
147
- usort($auditlogs['output_data'], array('SucuriScanAuditLogs', 'sortByDate'));
148
-
149
- for ($i = $iterator_start; $i < $total_items; $i++) {
150
- if ($counter_i > $maxPerPage) {
151
- break;
152
- }
153
 
154
- if (!isset($auditlogs['output_data'][$i])) {
155
- continue;
156
- }
 
 
 
 
157
 
158
- $audit_log = $auditlogs['output_data'][$i];
159
 
160
- $snippet_data = array(
161
- 'AuditLog.Event' => $audit_log['event'],
162
- 'AuditLog.Time' => SucuriScan::datetime($audit_log['timestamp'], 'H:i'),
163
- 'AuditLog.Date' => SucuriScan::datetime($audit_log['timestamp'], 'M d, Y'),
164
- 'AuditLog.Username' => $audit_log['username'],
165
- 'AuditLog.Address' => $audit_log['remote_addr'],
166
- 'AuditLog.Message' => $audit_log['message'],
167
- 'AuditLog.Extra' => '',
168
- );
169
 
170
- /* determine if we need to print the date */
171
- if ($snippet_data['AuditLog.Date'] === $previousDate) {
172
- $snippet_data['AuditLog.Date'] = '';
173
- } elseif ($snippet_data['AuditLog.Date'] === $todaysDate) {
174
- $previousDate = $snippet_data['AuditLog.Date'];
175
- $snippet_data['AuditLog.Date'] = __('Today', SUCURISCAN_TEXTDOMAIN);
176
- } else {
177
- $previousDate = $snippet_data['AuditLog.Date'];
178
- }
179
 
180
- /* decorate date if necessary */
181
- if (!empty($snippet_data['AuditLog.Date'])) {
182
- $snippet_data['AuditLog.Date'] =
183
- '<div class="sucuriscan-auditlog-date">'
184
- . $snippet_data['AuditLog.Date']
185
- . '</div>';
186
- }
187
 
188
- /* print every file_list information item in a separate table */
189
- if ($audit_log['file_list']) {
190
- $snippet_data['AuditLog.Extra'] .= '<ul class="sucuriscan-list-as-table">';
 
191
 
192
- foreach ($audit_log['file_list'] as $log_extra) {
193
- $snippet_data['AuditLog.Extra'] .= '<li>' . SucuriScan::escape($log_extra) . '</li>';
194
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
- $snippet_data['AuditLog.Extra'] .= '</ul>';
197
- }
 
 
 
198
 
199
- /* simplify the details of events with low metadata */
200
- if (strpos($audit_log['message'], 'status has been changed')) {
201
- $snippet_data['AuditLog.Extra'] = implode(",\x20", $audit_log['file_list']);
 
 
 
202
  }
203
 
204
- $response['content'] .= SucuriScanTemplate::getSnippet('auditlogs', $snippet_data);
205
- $counter_i += 1;
206
  }
207
 
208
- $response['count'] = $counter_i;
 
 
 
209
 
210
- if ($total_items > 1) {
211
- $maxpages = ceil($auditlogs['total_entries'] / $maxPerPage);
 
212
 
213
- if ($maxpages > SUCURISCAN_MAX_PAGINATION_BUTTONS) {
214
- $maxpages = SUCURISCAN_MAX_PAGINATION_BUTTONS;
215
- }
216
 
217
- if ($maxpages > 1) {
218
- $response['pagination'] = SucuriScanTemplate::pagination(
219
- SucuriScanTemplate::getUrl(),
220
- ($maxPerPage * $maxpages),
221
- $maxPerPage
222
- );
223
- }
 
 
 
 
 
 
224
  }
225
- } else {
226
- $response['content'] = __('NoLogs', SUCURISCAN_TEXTDOMAIN);
227
  }
228
 
229
  $cache = new SucuriScanCache('auditqueue');
@@ -242,6 +271,8 @@ class SucuriScanAuditLogs
242
  * output it means that XDebug cannot cover the next line, leaving a report
243
  * with a missing line in the coverage. Since the test case takes care of
244
  * the functionality of this code we will assume that it is fully covered.
 
 
245
  */
246
  public static function ajaxAuditLogsSendLogs()
247
  {
@@ -261,9 +292,9 @@ class SucuriScanAuditLogs
261
  * queue is emptied, we will have to sort the entries in the list to keep
262
  * the dates in sync.
263
  *
264
- * @param array $a Data associated to a single log.
265
- * @param array $b Data associated to another log.
266
- * @return int Comparison between the dates of both logs.
267
  */
268
  public static function sortByDate($a, $b)
269
  {
3
  /**
4
  * Code related to the auditlogs.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
24
 
25
  /**
26
  * Lists the logs collected by the API service.
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 SucuriScanAuditLogs
37
  {
69
  * output it means that XDebug cannot cover the next line, leaving a report
70
  * with a missing line in the coverage. Since the test case takes care of
71
  * the functionality of this code we will assume that it is fully covered.
72
+ *
73
+ * @return void
74
  */
75
  public static function ajaxAuditLogs()
76
  {
108
  ob_end_clean();
109
 
110
  /* report latency in the API calls */
111
+ if (!is_array($auditlogs)) {
112
+ $response['status'] = 'API is not available; using local queue';
113
+ } else {
114
+ $response['status'] = sprintf('API %s secs', round($duration, 4));
115
+ }
116
  }
117
 
118
  /* explain missing API key */
119
  if (!SucuriScanAPI::getPluginKey()) {
120
+ $response['status'] = 'API key is missing';
121
  }
122
 
123
  /* stop everything and report errors */
131
  }
132
 
133
  /* merge the logs from the queue system */
134
+ $queuelogs = SucuriScanAPI::getAuditLogsFromQueue();
135
+
136
+ if (is_array($queuelogs) && !empty($queuelogs)) {
137
  if (!$auditlogs) {
138
  $auditlogs = $queuelogs;
139
  } else {
 
 
 
140
  $auditlogs['output'] = array_merge(
141
  $queuelogs['output'],
142
  @$auditlogs['output']
145
  $queuelogs['output_data'],
146
  @$auditlogs['output_data']
147
  );
148
+
149
+ if (isset($auditlogs['total_entries'])) {
150
+ $auditlogs['total_entries'] = $auditlogs['total_entries'] + $queuelogs['total_entries'];
151
+ } else {
152
+ $auditlogs['total_entries'] = $queuelogs['total_entries'];
153
+ }
154
  }
155
  }
156
 
157
+ if (!is_array($auditlogs)
158
+ || !isset($auditlogs['output_data'])
159
+ || !isset($auditlogs['total_entries'])
160
+ || !is_array($auditlogs['output_data'])
161
+ || !is_numeric($auditlogs['total_entries'])
162
  ) {
163
+ $response['content'] = 'There are no logs.';
164
+ wp_send_json($response, 200);
165
+ return;
166
+ }
 
 
 
 
 
 
 
 
 
167
 
168
+ $counter_i = 0;
169
+ $previousDate = '';
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'));
177
 
178
+ for ($i = $iterator_start; $i < $total_items; $i++) {
179
+ if ($counter_i > $maxPerPage) {
180
+ break;
181
+ }
 
 
 
 
 
182
 
183
+ if (!isset($outdata[$i])) {
184
+ continue;
185
+ }
 
 
 
 
 
 
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
+ }
193
 
194
+ $snippet_data = array(
195
+ 'AuditLog.Event' => $audit_log['event'],
196
+ 'AuditLog.Time' => SucuriScan::datetime($audit_log['timestamp'], 'H:i'),
197
+ 'AuditLog.Date' => SucuriScan::datetime($audit_log['timestamp'], 'M d, Y'),
198
+ 'AuditLog.Username' => $audit_log['username'],
199
+ 'AuditLog.Address' => $audit_log['remote_addr'],
200
+ 'AuditLog.Message' => $audit_log['message'],
201
+ 'AuditLog.Extra' => '',
202
+ );
203
+
204
+ /* determine if we need to print the date */
205
+ if ($snippet_data['AuditLog.Date'] === $previousDate) {
206
+ $snippet_data['AuditLog.Date'] = '';
207
+ } elseif ($snippet_data['AuditLog.Date'] === $todaysDate) {
208
+ $previousDate = $snippet_data['AuditLog.Date'];
209
+ $snippet_data['AuditLog.Date'] = 'Today';
210
+ } else {
211
+ $previousDate = $snippet_data['AuditLog.Date'];
212
+ }
213
 
214
+ /* decorate date if necessary */
215
+ if (!empty($snippet_data['AuditLog.Date'])) {
216
+ $snippet_data['AuditLog.Date'] = '<div class="sucuriscan-aud'
217
+ . 'itlog-date">' . $snippet_data['AuditLog.Date'] . '</div>';
218
+ }
219
 
220
+ /* print every file_list information item in a separate table */
221
+ if ($audit_log['file_list']) {
222
+ $snippet_data['AuditLog.Extra'] .= '<ul class="sucuriscan-list-as-table">';
223
+
224
+ foreach ($audit_log['file_list'] as $log_extra) {
225
+ $snippet_data['AuditLog.Extra'] .= '<li>' . SucuriScan::escape($log_extra) . '</li>';
226
  }
227
 
228
+ $snippet_data['AuditLog.Extra'] .= '</ul>';
 
229
  }
230
 
231
+ /* simplify the details of events with low metadata */
232
+ if (strpos($audit_log['message'], 'status has been changed')) {
233
+ $snippet_data['AuditLog.Extra'] = implode(",\x20", $audit_log['file_list']);
234
+ }
235
 
236
+ $response['content'] .= SucuriScanTemplate::getSnippet('auditlogs', $snippet_data);
237
+ $counter_i += 1;
238
+ }
239
 
240
+ $response['count'] = $counter_i;
 
 
241
 
242
+ if ($total_items > 1) {
243
+ $maxpages = ceil($auditlogs['total_entries'] / $maxPerPage);
244
+
245
+ if ($maxpages > SUCURISCAN_MAX_PAGINATION_BUTTONS) {
246
+ $maxpages = SUCURISCAN_MAX_PAGINATION_BUTTONS;
247
+ }
248
+
249
+ if ($maxpages > 1) {
250
+ $response['pagination'] = SucuriScanTemplate::pagination(
251
+ SucuriScanTemplate::getUrl(),
252
+ ($maxPerPage * $maxpages),
253
+ $maxPerPage
254
+ );
255
  }
 
 
256
  }
257
 
258
  $cache = new SucuriScanCache('auditqueue');
271
  * output it means that XDebug cannot cover the next line, leaving a report
272
  * with a missing line in the coverage. Since the test case takes care of
273
  * the functionality of this code we will assume that it is fully covered.
274
+ *
275
+ * @return void
276
  */
277
  public static function ajaxAuditLogsSendLogs()
278
  {
292
  * queue is emptied, we will have to sort the entries in the list to keep
293
  * the dates in sync.
294
  *
295
+ * @param array $a Data associated to a single log.
296
+ * @param array $b Data associated to another log.
297
+ * @return int Comparison between the dates of both logs.
298
  */
299
  public static function sortByDate($a, $b)
300
  {
src/cache.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the cache.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage cache.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -28,6 +34,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
28
  *
29
  * [1] https://codex.wordpress.org/Class_Reference/WP_Object_Cache
30
  * [2] https://codex.wordpress.org/Class_Reference/WP_Object_Cache#Persistent_Caching
 
 
 
 
 
 
 
 
31
  */
32
  class SucuriScanCache extends SucuriScan
33
  {
@@ -42,16 +56,16 @@ class SucuriScanCache extends SucuriScan
42
  *
43
  * [1] /public/data/sucuri-DATASTORE.php
44
  *
45
- * @var null|string
46
  */
47
- private $datastore = null;
48
 
49
  /**
50
  * The full path of the datastore file.
51
  *
52
  * @var string
53
  */
54
- private $datastore_path = '';
55
 
56
  /**
57
  * Whether the datastore file is usable or not.
@@ -59,15 +73,15 @@ class SucuriScanCache extends SucuriScan
59
  * This variable will only be TRUE if the datastore file specified exists, is
60
  * writable and readable, in any other case it will always be FALSE.
61
  *
62
- * @var boolean
63
  */
64
- private $usable_datastore = false;
65
 
66
  /**
67
  * Initializes the cache library.
68
  *
69
- * @param string $datastore Name of the storage file.
70
- * @param bool $auto_create Forces the creation of the storage file.
71
  */
72
  public function __construct($datastore = '', $auto_create = true)
73
  {
@@ -95,8 +109,8 @@ class SucuriScanCache extends SucuriScan
95
  /**
96
  * Default content of every datastore file.
97
  *
98
- * @param array $finfo Rainbow table with the key names and decoded values.
99
- * @return string Default content of every datastore file.
100
  */
101
  private function datastoreInfo($finfo = array())
102
  {
@@ -126,8 +140,8 @@ class SucuriScanCache extends SucuriScan
126
  * user running the server, in case that it does not exists the method will
127
  * tries to create it by itself with the right permissions to use it.
128
  *
129
- * @param bool $auto_create Create the file is it does not exists.
130
- * @return string|bool Absolute path to the storage file, false otherwise.
131
  */
132
  private function datastoreFilePath($auto_create = false)
133
  {
@@ -156,8 +170,8 @@ class SucuriScanCache extends SucuriScan
156
  * key we will use a primitive string transformation technique to reduce the
157
  * execution time, regular expressions are significantly slow.
158
  *
159
- * @param string $key Unique name for the data.
160
- * @return bool True if the key is valid, false otherwise.
161
  */
162
  private function validKeyName($key = '')
163
  {
@@ -187,8 +201,8 @@ class SucuriScanCache extends SucuriScan
187
  /**
188
  * Update the content of the datastore file with the new entries.
189
  *
190
- * @param array $finfo Rainbow table with the key names and decoded values.
191
- * @return bool TRUE if the operation finished successfully, FALSE otherwise.
192
  */
193
  private function saveNewEntries($finfo = array())
194
  {
@@ -213,23 +227,24 @@ class SucuriScanCache extends SucuriScan
213
  * and decoded data as the values of each entry. Duplicated key names will
214
  * be merged automatically.
215
  *
216
- * @param bool $assoc Force object to array conversion.
217
- * @param bool $onlyInfo Returns the cache headers and no content.
218
- * @return array Rainbow table with the key names and decoded values.
219
  */
220
  private function getDatastoreContent($assoc = false, $onlyInfo = false)
221
  {
222
  $object = array();
223
  $object['info'] = array();
224
  $object['entries'] = array();
 
225
 
226
- if ($lines = SucuriScanFileInfo::fileLines($this->datastore_path)) {
227
  foreach ($lines as $line) {
228
  if (strpos($line, "//\x20") === 0
229
  && strpos($line, '=') !== false
230
- && $line[strlen($line)-1] === ';'
231
  ) {
232
- $section = substr($line, 3, strlen($line)-4);
233
  list($header, $value) = explode('=', $section, 2);
234
  $object['info'][$header] = $value;
235
  continue;
@@ -278,8 +293,8 @@ class SucuriScanCache extends SucuriScan
278
  /**
279
  * Get the total number of unique entries in the datastore file.
280
  *
281
- * @param array $finfo Rainbow table with the key names and decoded values.
282
- * @return int Total number of unique entries found in the datastore file.
283
  */
284
  public function getCount($finfo = null)
285
  {
@@ -296,13 +311,13 @@ class SucuriScanCache extends SucuriScan
296
  * the caching process, any others besides this are just methods used to handle
297
  * the data inside those files.
298
  *
299
- * @param int $lifetime Life time of the key in the datastore file.
300
- * @param array $finfo Rainbow table with the key names and decoded values.
301
- * @return bool TRUE if the life time of the data has expired, FALSE otherwise.
302
  */
303
  public function dataHasExpired($lifetime = 0, $finfo = null)
304
  {
305
- if (is_null($finfo)) {
306
  $meta = $this->getDatastoreInfo();
307
  $finfo = array('info' => $meta);
308
  }
@@ -324,9 +339,9 @@ class SucuriScanCache extends SucuriScan
324
  * duplicated, but when getting the value of the same key later again it will
325
  * return only the value of the first occurrence found in the file.
326
  *
327
- * @param string $key Unique name for the data.
328
- * @param mixed $data Data to associate to the key.
329
- * @return bool True if the data was cached, false otherwise.
330
  */
331
  public function add($key = '', $data = '')
332
  {
@@ -336,9 +351,9 @@ class SucuriScanCache extends SucuriScan
336
  /**
337
  * Update the data of all the key names matching the one specified.
338
  *
339
- * @param string $key Unique name for the data.
340
- * @param mixed $data Data to associate to the key.
341
- * @return bool True if the cache data was updated, false otherwise.
342
  */
343
  public function set($key = '', $data = '')
344
  {
@@ -355,10 +370,10 @@ class SucuriScanCache extends SucuriScan
355
  /**
356
  * Retrieve the first occurrence of the key found in the datastore file.
357
  *
358
- * @param string $key Unique name for the data.
359
- * @param int $lifetime Seconds before the data expires.
360
- * @param string $assoc Force data to be converted to an array.
361
- * @return mixed Data associated to the key.
362
  */
363
  public function get($key = '', $lifetime = 0, $assoc = '')
364
  {
@@ -380,9 +395,9 @@ class SucuriScanCache extends SucuriScan
380
  /**
381
  * Retrieve all the entries found in the datastore file.
382
  *
383
- * @param int $lifetime Life time of the key in the datastore file.
384
- * @param string $assoc Force data to be converted to an array.
385
- * @return mixed All the entries stored in the cache file.
386
  */
387
  public function getAll($lifetime = 0, $assoc = '')
388
  {
@@ -398,8 +413,8 @@ class SucuriScanCache extends SucuriScan
398
  /**
399
  * Check whether a specific key exists in the datastore file.
400
  *
401
- * @param string $key Unique name for the data.
402
- * @return bool True if the data exists, false otherwise.
403
  */
404
  public function exists($key = '')
405
  {
@@ -415,8 +430,8 @@ class SucuriScanCache extends SucuriScan
415
  /**
416
  * Delete any entry from the datastore file matching the key name specified.
417
  *
418
- * @param string $key Unique name for the data.
419
- * @return bool True if the data was deleted, false otherwise.
420
  */
421
  public function delete($key = '')
422
  {
@@ -438,15 +453,17 @@ class SucuriScanCache extends SucuriScan
438
  /**
439
  * Replaces the entire content of the cache file.
440
  *
441
- * @param array $entries New data for the cache.
442
- * @return bool True if the cache was replaced.
443
  */
444
  public function override($entries = array())
445
  {
446
- return $this->saveNewEntries(array(
447
- 'info' => $this->getDatastoreInfo(),
448
- 'entries' => $entries,
449
- ));
 
 
450
  }
451
 
452
  /**
3
  /**
4
  * Code related to the cache.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
34
  *
35
  * [1] https://codex.wordpress.org/Class_Reference/WP_Object_Cache
36
  * [2] https://codex.wordpress.org/Class_Reference/WP_Object_Cache#Persistent_Caching
37
+ *
38
+ * @category Library
39
+ * @package Sucuri
40
+ * @subpackage SucuriScanner
41
+ * @author Daniel Cid <dcid@sucuri.net>
42
+ * @copyright 2010-2017 Sucuri Inc.
43
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
44
+ * @link https://wordpress.org/plugins/sucuri-scanner
45
  */
46
  class SucuriScanCache extends SucuriScan
47
  {
56
  *
57
  * [1] /public/data/sucuri-DATASTORE.php
58
  *
59
+ * @var string
60
  */
61
+ private $datastore;
62
 
63
  /**
64
  * The full path of the datastore file.
65
  *
66
  * @var string
67
  */
68
+ private $datastore_path;
69
 
70
  /**
71
  * Whether the datastore file is usable or not.
73
  * This variable will only be TRUE if the datastore file specified exists, is
74
  * writable and readable, in any other case it will always be FALSE.
75
  *
76
+ * @var bool
77
  */
78
+ private $usable_datastore;
79
 
80
  /**
81
  * Initializes the cache library.
82
  *
83
+ * @param string $datastore Name of the storage file.
84
+ * @param bool $auto_create Forces the creation of the storage file.
85
  */
86
  public function __construct($datastore = '', $auto_create = true)
87
  {
109
  /**
110
  * Default content of every datastore file.
111
  *
112
+ * @param array $finfo Rainbow table with the key names and decoded values.
113
+ * @return string Default content of every datastore file.
114
  */
115
  private function datastoreInfo($finfo = array())
116
  {
140
  * user running the server, in case that it does not exists the method will
141
  * tries to create it by itself with the right permissions to use it.
142
  *
143
+ * @param bool $auto_create Create the file is it does not exists.
144
+ * @return string|bool Absolute path to the storage file, false otherwise.
145
  */
146
  private function datastoreFilePath($auto_create = false)
147
  {
170
  * key we will use a primitive string transformation technique to reduce the
171
  * execution time, regular expressions are significantly slow.
172
  *
173
+ * @param string $key Unique name for the data.
174
+ * @return bool True if the key is valid, false otherwise.
175
  */
176
  private function validKeyName($key = '')
177
  {
201
  /**
202
  * Update the content of the datastore file with the new entries.
203
  *
204
+ * @param array $finfo Rainbow table with the key names and decoded values.
205
+ * @return bool TRUE if the operation finished successfully, FALSE otherwise.
206
  */
207
  private function saveNewEntries($finfo = array())
208
  {
227
  * and decoded data as the values of each entry. Duplicated key names will
228
  * be merged automatically.
229
  *
230
+ * @param bool $assoc Force object to array conversion.
231
+ * @param bool $onlyInfo Returns the cache headers and no content.
232
+ * @return array Rainbow table with the key names and decoded values.
233
  */
234
  private function getDatastoreContent($assoc = false, $onlyInfo = false)
235
  {
236
  $object = array();
237
  $object['info'] = array();
238
  $object['entries'] = array();
239
+ $lines = SucuriScanFileInfo::fileLines($this->datastore_path);
240
 
241
+ if (is_array($lines) && !empty($lines)) {
242
  foreach ($lines as $line) {
243
  if (strpos($line, "//\x20") === 0
244
  && strpos($line, '=') !== false
245
+ && $line[strlen($line) - 1] === ';'
246
  ) {
247
+ $section = substr($line, 3, strlen($line) - 4);
248
  list($header, $value) = explode('=', $section, 2);
249
  $object['info'][$header] = $value;
250
  continue;
293
  /**
294
  * Get the total number of unique entries in the datastore file.
295
  *
296
+ * @param array $finfo Rainbow table with the key names and decoded values.
297
+ * @return int Total number of unique entries found in the datastore file.
298
  */
299
  public function getCount($finfo = null)
300
  {
311
  * the caching process, any others besides this are just methods used to handle
312
  * the data inside those files.
313
  *
314
+ * @param int $lifetime Life time of the key in the datastore file.
315
+ * @param array $finfo Rainbow table with the key names and decoded values.
316
+ * @return bool TRUE if the life time of the data has expired, FALSE otherwise.
317
  */
318
  public function dataHasExpired($lifetime = 0, $finfo = null)
319
  {
320
+ if (!is_array($finfo)) {
321
  $meta = $this->getDatastoreInfo();
322
  $finfo = array('info' => $meta);
323
  }
339
  * duplicated, but when getting the value of the same key later again it will
340
  * return only the value of the first occurrence found in the file.
341
  *
342
+ * @param string $key Unique name for the data.
343
+ * @param mixed $data Data to associate to the key.
344
+ * @return bool True if the data was cached, false otherwise.
345
  */
346
  public function add($key = '', $data = '')
347
  {
351
  /**
352
  * Update the data of all the key names matching the one specified.
353
  *
354
+ * @param string $key Unique name for the data.
355
+ * @param mixed $data Data to associate to the key.
356
+ * @return bool True if the cache data was updated, false otherwise.
357
  */
358
  public function set($key = '', $data = '')
359
  {
370
  /**
371
  * Retrieve the first occurrence of the key found in the datastore file.
372
  *
373
+ * @param string $key Unique name for the data.
374
+ * @param int $lifetime Seconds before the data expires.
375
+ * @param string $assoc Force data to be converted to an array.
376
+ * @return mixed Data associated to the key.
377
  */
378
  public function get($key = '', $lifetime = 0, $assoc = '')
379
  {
395
  /**
396
  * Retrieve all the entries found in the datastore file.
397
  *
398
+ * @param int $lifetime Life time of the key in the datastore file.
399
+ * @param string $assoc Force data to be converted to an array.
400
+ * @return mixed All the entries stored in the cache file.
401
  */
402
  public function getAll($lifetime = 0, $assoc = '')
403
  {
413
  /**
414
  * Check whether a specific key exists in the datastore file.
415
  *
416
+ * @param string $key Unique name for the data.
417
+ * @return bool True if the data exists, false otherwise.
418
  */
419
  public function exists($key = '')
420
  {
430
  /**
431
  * Delete any entry from the datastore file matching the key name specified.
432
  *
433
+ * @param string $key Unique name for the data.
434
+ * @return bool True if the data was deleted, false otherwise.
435
  */
436
  public function delete($key = '')
437
  {
453
  /**
454
  * Replaces the entire content of the cache file.
455
  *
456
+ * @param array $entries New data for the cache.
457
+ * @return bool True if the cache was replaced.
458
  */
459
  public function override($entries = array())
460
  {
461
+ return $this->saveNewEntries(
462
+ array(
463
+ 'info' => $this->getDatastoreInfo(),
464
+ 'entries' => $entries,
465
+ )
466
+ );
467
  }
468
 
469
  /**
src/command.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the command.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage command.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -23,6 +29,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
23
  * and folders using the built-in PHP class SplFileInfo. The SplFileInfo class
24
  * offers a high-level object oriented interface to information for an individual
25
  * file.
 
 
 
 
 
 
 
 
26
  */
27
  class SucuriScanCommand extends SucuriScan
28
  {
@@ -44,8 +58,8 @@ class SucuriScanCommand extends SucuriScan
44
  /**
45
  * Checks if an external command exists or not.
46
  *
47
- * @param string $cmd Name of the external command.
48
- * @return bool True if the command exists, false otherwise.
49
  */
50
  public static function exists($cmd)
51
  {
@@ -67,20 +81,24 @@ class SucuriScanCommand extends SucuriScan
67
  /**
68
  * Compares two files line by line.
69
  *
70
- * @param string $a File path of the original file.
71
- * @param string $b File path of the modified file.
72
- * @return array Line-by-line changes (if any).
73
  */
74
  public static function diff($a, $b)
75
  {
76
- $out = array(); /* default empty */
77
 
78
  if (self::exists('diff')) {
79
- @exec(sprintf(
80
- 'diff -u -- %s %s 2> /dev/null',
81
- escapeshellarg($a),
82
- escapeshellarg($b)
83
- ), $out, $err);
 
 
 
 
84
  }
85
 
86
  return $out;
@@ -99,26 +117,27 @@ class SucuriScanCommand extends SucuriScan
99
  * them in the dashboard. Some basic CSS classes will be attached to some of
100
  * the elements in the code to facilitate the styling of the diff report.
101
  *
102
- * @param string $filepath Relative path to the core WordPress file.
103
- * @return string|bool HTML code with the diff report, false on failure.
104
  */
105
  public static function diffHTML($filepath)
106
  {
107
  $checksums = SucuriScanAPI::getOfficialChecksums();
108
 
109
  if (!$checksums) {
110
- return SucuriScanInterface::error(__('UnsupportedWordPress', SUCURISCAN_TEXTDOMAIN));
111
  }
112
 
113
  if (!array_key_exists($filepath, $checksums)) {
114
- return SucuriScanInterface::error(__('NoWordPressFile', SUCURISCAN_TEXTDOMAIN));
115
  }
116
 
117
  $output = ''; /* initialize empty with no differences */
118
  $a = tempnam(sys_get_temp_dir(), SUCURISCAN . '-integrity-');
119
  $b = tempnam(sys_get_temp_dir(), SUCURISCAN . '-integrity-');
 
120
 
121
- if ($handle = @fopen($a, 'w')) {
122
  @fwrite($handle, SucuriScanAPI::getOriginalCoreFile($filepath));
123
  @copy(ABSPATH . '/' . $filepath, $b);
124
  $output = self::diff($a, $b);
@@ -129,7 +148,7 @@ class SucuriScanCommand extends SucuriScan
129
  @unlink($b); /* delete modified file */
130
 
131
  if (!is_array($output) || empty($output)) {
132
- return SucuriScanInterface::error(__('ThereAreNoDifferences', SUCURISCAN_TEXTDOMAIN));
133
  }
134
 
135
  $response = "<ul class='" . SUCURISCAN . "-diff-content'>\n";
3
  /**
4
  * Code related to the command.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
29
  * and folders using the built-in PHP class SplFileInfo. The SplFileInfo class
30
  * offers a high-level object oriented interface to information for an individual
31
  * file.
32
+ *
33
+ * @category Library
34
+ * @package Sucuri
35
+ * @subpackage SucuriScanner
36
+ * @author Daniel Cid <dcid@sucuri.net>
37
+ * @copyright 2010-2017 Sucuri Inc.
38
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
39
+ * @link https://wordpress.org/plugins/sucuri-scanner
40
  */
41
  class SucuriScanCommand extends SucuriScan
42
  {
58
  /**
59
  * Checks if an external command exists or not.
60
  *
61
+ * @param string $cmd Name of the external command.
62
+ * @return bool True if the command exists, false otherwise.
63
  */
64
  public static function exists($cmd)
65
  {
81
  /**
82
  * Compares two files line by line.
83
  *
84
+ * @param string $a File path of the original file.
85
+ * @param string $b File path of the modified file.
86
+ * @return array Line-by-line changes (if any).
87
  */
88
  public static function diff($a, $b)
89
  {
90
+ $out = array();
91
 
92
  if (self::exists('diff')) {
93
+ @exec(
94
+ sprintf(
95
+ 'diff -u -- %s %s 2> /dev/null',
96
+ escapeshellarg($a),
97
+ escapeshellarg($b)
98
+ ),
99
+ $out,
100
+ $err
101
+ );
102
  }
103
 
104
  return $out;
117
  * them in the dashboard. Some basic CSS classes will be attached to some of
118
  * the elements in the code to facilitate the styling of the diff report.
119
  *
120
+ * @param string $filepath Relative path to the core WordPress file.
121
+ * @return string|bool HTML code with the diff report, false on failure.
122
  */
123
  public static function diffHTML($filepath)
124
  {
125
  $checksums = SucuriScanAPI::getOfficialChecksums();
126
 
127
  if (!$checksums) {
128
+ return SucuriScanInterface::error('Unsupported WordPress version.');
129
  }
130
 
131
  if (!array_key_exists($filepath, $checksums)) {
132
+ return SucuriScanInterface::error('Not an official WordPress file.');
133
  }
134
 
135
  $output = ''; /* initialize empty with no differences */
136
  $a = tempnam(sys_get_temp_dir(), SUCURISCAN . '-integrity-');
137
  $b = tempnam(sys_get_temp_dir(), SUCURISCAN . '-integrity-');
138
+ $handle = @fopen($a, 'w');
139
 
140
+ if ($handle) {
141
  @fwrite($handle, SucuriScanAPI::getOriginalCoreFile($filepath));
142
  @copy(ABSPATH . '/' . $filepath, $b);
143
  $output = self::diff($a, $b);
148
  @unlink($b); /* delete modified file */
149
 
150
  if (!is_array($output) || empty($output)) {
151
+ return SucuriScanInterface::error('There are no differences.');
152
  }
153
 
154
  $response = "<ul class='" . SUCURISCAN . "-diff-content'>\n";
src/event.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the event.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage event.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -29,7 +35,13 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
29
  * response to events is said to be event-driven, often with the goal of being
30
  * interactive.
31
  *
32
- * @see https://en.wikipedia.org/wiki/Event_%28computing%29
 
 
 
 
 
 
33
  */
34
  class SucuriScanEvent extends SucuriScan
35
  {
@@ -43,6 +55,8 @@ class SucuriScanEvent extends SucuriScan
43
  * list with the checksum of the new file list, if there are differences we
44
  * will assume that someone or something modified one or more files and send
45
  * an email alsert about the incident.
 
 
46
  */
47
  public static function installScheduledTask()
48
  {
@@ -72,13 +86,13 @@ class SucuriScanEvent extends SucuriScan
72
 
73
  foreach ($jobs as $unique => $info) {
74
  $schedules[$unique] = sprintf(
75
- __('ScheduledTask', SUCURISCAN_TEXTDOMAIN),
76
  $info['display'],
77
  $info['interval']
78
  );
79
  }
80
 
81
- $schedules['_oneoff'] = __('ScheduledTaskNever', SUCURISCAN_TEXTDOMAIN);
82
 
83
  return $schedules;
84
  }
@@ -110,8 +124,8 @@ class SucuriScanEvent extends SucuriScan
110
  /**
111
  * Decides if the file system scanner can run or not.
112
  *
113
- * @param bool $force_scan Force the execution of the scanner.
114
- * @return bool True if the scanner can run, false otherwise.
115
  */
116
  private static function runFileScanner($force_scan = false)
117
  {
@@ -135,8 +149,8 @@ class SucuriScanEvent extends SucuriScan
135
  * analyze them using the Sucuri Monitoring service, this will generate the
136
  * audit logs for this site and be part of the integrity checks.
137
  *
138
- * @param bool $force_scan Whether the filesystem scan was forced by an administrator user or not.
139
- * @return bool True if the filesystem scan was successful, false otherwise.
140
  */
141
  public static function filesystemScan($force_scan = false)
142
  {
@@ -169,10 +183,10 @@ class SucuriScanEvent extends SucuriScan
169
  * in a failure. However, this procedure depends on the ability of the plugin
170
  * to write the log into the queue when the previous request failed.
171
  *
172
- * @param string $message Information about the event.
173
- * @param string|int $timestamp Time when the event was triggered.
174
- * @param int $timeout Maximum time in seconds to connect to the API.
175
- * @return bool True if the event was logged, false otherwise.
176
  */
177
  private static function sendLogToAPI($message = '', $timestamp = '', $timeout = 1)
178
  {
@@ -202,8 +216,8 @@ class SucuriScanEvent extends SucuriScan
202
  * but it could be private and be mocked by the test bootstrap script. Take
203
  * this in consideration during the static analysis of the code.
204
  *
205
- * @param string $message Information about the security event.
206
- * @return bool True if the operation succeeded, false otherwise.
207
  */
208
  public static function sendLogToQueue($message = '')
209
  {
@@ -335,9 +349,9 @@ class SucuriScanEvent extends SucuriScan
335
  /**
336
  * Generates an audit event log (to be sent later).
337
  *
338
- * @param int $severity Importance of the event that will be reported, values from one to five.
339
- * @param string $message The explanation of the event.
340
- * @return bool True if the event was logged in the monitoring service, false otherwise.
341
  */
342
  private static function reportEvent($severity = 0, $message = '')
343
  {
@@ -354,7 +368,7 @@ class SucuriScanEvent extends SucuriScan
354
  }
355
 
356
  $severity = intval($severity);
357
- $severity_name = 'Info'; /* default */
358
  $severities = array(
359
  /* 0 */ 'Debug',
360
  /* 1 */ 'Notice',
@@ -374,20 +388,22 @@ class SucuriScanEvent extends SucuriScan
374
  $message = str_replace("\n", '', $message);
375
  $message = str_replace("\t", '', $message);
376
 
377
- return self::sendLogToQueue(sprintf(
378
- '%s:%s %s; %s',
379
- $severity_name,
380
- $username,
381
- $remote_ip,
382
- $message
383
- ));
 
 
384
  }
385
 
386
  /**
387
  * Reports a debug event on the website.
388
  *
389
- * @param string $message Text witht the explanation of the event or action performed.
390
- * @return bool Either true or false depending on the success of the operation.
391
  */
392
  public static function reportDebugEvent($message = '')
393
  {
@@ -397,8 +413,8 @@ class SucuriScanEvent extends SucuriScan
397
  /**
398
  * Reports a notice event on the website.
399
  *
400
- * @param string $message Text witht the explanation of the event or action performed.
401
- * @return bool Either true or false depending on the success of the operation.
402
  */
403
  public static function reportNoticeEvent($message = '')
404
  {
@@ -408,8 +424,8 @@ class SucuriScanEvent extends SucuriScan
408
  /**
409
  * Reports a info event on the website.
410
  *
411
- * @param string $message Text witht the explanation of the event or action performed.
412
- * @return bool Either true or false depending on the success of the operation.
413
  */
414
  public static function reportInfoEvent($message = '')
415
  {
@@ -419,8 +435,8 @@ class SucuriScanEvent extends SucuriScan
419
  /**
420
  * Reports a warning event on the website.
421
  *
422
- * @param string $message Text witht the explanation of the event or action performed.
423
- * @return bool Either true or false depending on the success of the operation.
424
  */
425
  public static function reportWarningEvent($message = '')
426
  {
@@ -430,8 +446,8 @@ class SucuriScanEvent extends SucuriScan
430
  /**
431
  * Reports a error event on the website.
432
  *
433
- * @param string $message Text witht the explanation of the event or action performed.
434
- * @return bool Either true or false depending on the success of the operation.
435
  */
436
  public static function reportErrorEvent($message = '')
437
  {
@@ -441,45 +457,21 @@ class SucuriScanEvent extends SucuriScan
441
  /**
442
  * Reports a critical event on the website.
443
  *
444
- * @param string $message Text witht the explanation of the event or action performed.
445
- * @return bool Either true or false depending on the success of the operation.
446
  */
447
  public static function reportCriticalEvent($message = '')
448
  {
449
  return self::reportEvent(5, $message);
450
  }
451
 
452
- /**
453
- * Reports a notice or error event for enable and disable actions.
454
- *
455
- * @param string $message Text witht the explanation of the event or action performed.
456
- * @param string $action An optional text, hopefully either enabled or disabled.
457
- * @return bool Either true or false depending on the success of the operation.
458
- */
459
- public static function reportAutoEvent($message = '', $action = '')
460
- {
461
- $message = strip_tags($message);
462
-
463
- /* auto-detect the action performed: enabled */
464
- if (strpos($message, 'enabled') !== false) {
465
- return self::reportNoticeEvent($message);
466
- }
467
-
468
- /* auto-detect the action performed: disabled */
469
- if (strpos($message, 'disabled') !== false) {
470
- return self::reportErrorEvent($message);
471
- }
472
-
473
- return self::reportInfoEvent($message);
474
- }
475
-
476
  /**
477
  * Send a notification to the administrator of the specified events, only if
478
  * the administrator accepted to receive alerts for this type of events.
479
  *
480
- * @param string $event The name of the event that was triggered.
481
- * @param string $content Body of the email that will be sent to the administrator.
482
- * @return bool True if the email was apparently sent, false otherwise.
483
  */
484
  public static function notifyEvent($event = '', $content = '')
485
  {
@@ -513,7 +505,20 @@ class SucuriScanEvent extends SucuriScan
513
  case 'failed_login':
514
  $settings_url = SucuriScanTemplate::getUrl('settings');
515
  $content .= "\n" . sprintf(
516
- __('FailedLoginFooter', SUCURISCAN_TEXTDOMAIN),
 
 
 
 
 
 
 
 
 
 
 
 
 
517
  $settings_url,
518
  $settings_url
519
  );
@@ -534,7 +539,7 @@ class SucuriScanEvent extends SucuriScan
534
  $email_params['ForceHTML'] = true;
535
  }
536
 
537
- $title = __('EmailSubject.' . $event, SUCURISCAN_TEXTDOMAIN);
538
 
539
  return SucuriScanMail::sendMail(
540
  $email,
@@ -547,13 +552,13 @@ class SucuriScanEvent extends SucuriScan
547
  /**
548
  * Check whether an IP address is being trusted or not.
549
  *
550
- * @param string $remote_addr The supposed ip address that will be checked.
551
- * @return bool True if the IP address of the user is trusted, false otherwise.
552
  */
553
- public static function isTrustedIP($remote_addr = '')
554
  {
555
- if (!$remote_addr) {
556
- $remote_addr = SucuriScan::getRemoteAddr();
557
  }
558
 
559
  $cache = new SucuriScanCache('trustip', false);
@@ -564,7 +569,7 @@ class SucuriScanEvent extends SucuriScan
564
  }
565
 
566
  /* check if exact IP address match is whitelisted */
567
- if (array_key_exists(md5($remote_addr), $trusted_ips)) {
568
  return true;
569
  }
570
 
@@ -578,31 +583,31 @@ class SucuriScanEvent extends SucuriScan
578
  // Generate the regular expression for a specific CIDR range.
579
  switch ($ip_info->cidr_range) {
580
  case 24:
581
- $ip_pattern =
582
- '/^' . $ip_parts[0]
583
- . '.' . $ip_parts[1]
584
- . '.' . $ip_parts[2]
585
- . '\.[0-9]{1,3}$/';
 
586
  break;
587
 
588
  case 16:
589
- $ip_pattern =
590
- '/^' . $ip_parts[0]
591
- . '.' . $ip_parts[1]
592
- . '\.[0-9]{1,3}'
593
- . '\.[0-9]{1,3}$/';
594
  break;
595
 
596
  case 8:
597
- $ip_pattern =
598
- '/^' . $ip_parts[0]
599
- . '\.[0-9]{1,3}'
600
- . '\.[0-9]{1,3}'
601
- . '\.[0-9]{1,3}$/';
602
  break;
603
  }
604
 
605
- if ($ip_pattern && preg_match($ip_pattern, $remote_addr)) {
606
  $is_trusted_ip = true;
607
  break;
608
  }
@@ -614,8 +619,8 @@ class SucuriScanEvent extends SucuriScan
614
  /**
615
  * Generate and set a new password for a specific user not in session.
616
  *
617
- * @param int $user_id User account identifier.
618
- * @return bool True if the process exit clean, false otherwise.
619
  */
620
  public static function setNewPassword($user_id = 0)
621
  {
@@ -630,19 +635,22 @@ class SucuriScanEvent extends SucuriScan
630
  $display_name = $user->display_name;
631
  $new_password = wp_generate_password(15, true, false);
632
 
633
- $message = SucuriScanTemplate::getSection('settings-posthack-reset-password-alert', array(
634
- 'ResetPassword.UserName' => $user_login,
635
- 'ResetPassword.DisplayName' => $display_name,
636
- 'ResetPassword.Password' => $new_password,
637
- 'ResetPassword.Website' => $website,
638
- ));
 
 
 
639
 
640
  /* Skip per hour alert limit and force text/html content-type */
641
  $data_set = array('Force' => true, 'ForceHTML' => true);
642
 
643
  $sent = SucuriScanMail::sendMail(
644
  $user->user_email,
645
- __('EmailSubject.password_change', SUCURISCAN_TEXTDOMAIN),
646
  $message,
647
  $data_set
648
  );
@@ -667,7 +675,7 @@ class SucuriScanEvent extends SucuriScan
667
  public static function setNewConfigKeys()
668
  {
669
  $new_wpconfig = '';
670
- $config_path = self::getWPConfigPath();
671
 
672
  if (!$config_path) {
673
  return false;
3
  /**
4
  * Code related to the event.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
35
  * response to events is said to be event-driven, often with the goal of being
36
  * interactive.
37
  *
38
+ * @category Library
39
+ * @package Sucuri
40
+ * @subpackage SucuriScanner
41
+ * @author Daniel Cid <dcid@sucuri.net>
42
+ * @copyright 2010-2017 Sucuri Inc.
43
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
44
+ * @link https://wordpress.org/plugins/sucuri-scanner
45
  */
46
  class SucuriScanEvent extends SucuriScan
47
  {
55
  * list with the checksum of the new file list, if there are differences we
56
  * will assume that someone or something modified one or more files and send
57
  * an email alsert about the incident.
58
+ *
59
+ * @return void
60
  */
61
  public static function installScheduledTask()
62
  {
86
 
87
  foreach ($jobs as $unique => $info) {
88
  $schedules[$unique] = sprintf(
89
+ '%s (every %d seconds)',
90
  $info['display'],
91
  $info['interval']
92
  );
93
  }
94
 
95
+ $schedules['_oneoff'] = 'Never (no execution)';
96
 
97
  return $schedules;
98
  }
124
  /**
125
  * Decides if the file system scanner can run or not.
126
  *
127
+ * @param bool $force_scan Force the execution of the scanner.
128
+ * @return bool True if the scanner can run, false otherwise.
129
  */
130
  private static function runFileScanner($force_scan = false)
131
  {
149
  * analyze them using the Sucuri Monitoring service, this will generate the
150
  * audit logs for this site and be part of the integrity checks.
151
  *
152
+ * @param bool $force_scan Whether the filesystem scan was forced by an administrator user or not.
153
+ * @return bool True if the filesystem scan was successful, false otherwise.
154
  */
155
  public static function filesystemScan($force_scan = false)
156
  {
183
  * in a failure. However, this procedure depends on the ability of the plugin
184
  * to write the log into the queue when the previous request failed.
185
  *
186
+ * @param string $message Information about the event.
187
+ * @param string|int $timestamp Time when the event was triggered.
188
+ * @param int $timeout Maximum time in seconds to connect to the API.
189
+ * @return bool True if the event was logged, false otherwise.
190
  */
191
  private static function sendLogToAPI($message = '', $timestamp = '', $timeout = 1)
192
  {
216
  * but it could be private and be mocked by the test bootstrap script. Take
217
  * this in consideration during the static analysis of the code.
218
  *
219
+ * @param string $message Information about the security event.
220
+ * @return bool True if the operation succeeded, false otherwise.
221
  */
222
  public static function sendLogToQueue($message = '')
223
  {
349
  /**
350
  * Generates an audit event log (to be sent later).
351
  *
352
+ * @param int $severity Importance of the event that will be reported.
353
+ * @param string $message The explanation of the event.
354
+ * @return bool True if the event was logged, false otherwise.
355
  */
356
  private static function reportEvent($severity = 0, $message = '')
357
  {
368
  }
369
 
370
  $severity = intval($severity);
371
+ $severity_name = 'Info';
372
  $severities = array(
373
  /* 0 */ 'Debug',
374
  /* 1 */ 'Notice',
388
  $message = str_replace("\n", '', $message);
389
  $message = str_replace("\t", '', $message);
390
 
391
+ return self::sendLogToQueue(
392
+ sprintf(
393
+ '%s:%s %s; %s',
394
+ $severity_name,
395
+ $username,
396
+ $remote_ip,
397
+ $message
398
+ )
399
+ );
400
  }
401
 
402
  /**
403
  * Reports a debug event on the website.
404
  *
405
+ * @param string $message Text witht the explanation of the event or action performed.
406
+ * @return bool Either true or false depending on the success of the operation.
407
  */
408
  public static function reportDebugEvent($message = '')
409
  {
413
  /**
414
  * Reports a notice event on the website.
415
  *
416
+ * @param string $message Text witht the explanation of the event or action performed.
417
+ * @return bool Either true or false depending on the success of the operation.
418
  */
419
  public static function reportNoticeEvent($message = '')
420
  {
424
  /**
425
  * Reports a info event on the website.
426
  *
427
+ * @param string $message Text witht the explanation of the event or action performed.
428
+ * @return bool Either true or false depending on the success of the operation.
429
  */
430
  public static function reportInfoEvent($message = '')
431
  {
435
  /**
436
  * Reports a warning event on the website.
437
  *
438
+ * @param string $message Text witht the explanation of the event or action performed.
439
+ * @return bool Either true or false depending on the success of the operation.
440
  */
441
  public static function reportWarningEvent($message = '')
442
  {
446
  /**
447
  * Reports a error event on the website.
448
  *
449
+ * @param string $message Text witht the explanation of the event or action performed.
450
+ * @return bool Either true or false depending on the success of the operation.
451
  */
452
  public static function reportErrorEvent($message = '')
453
  {
457
  /**
458
  * Reports a critical event on the website.
459
  *
460
+ * @param string $message Text witht the explanation of the event or action performed.
461
+ * @return bool Either true or false depending on the success of the operation.
462
  */
463
  public static function reportCriticalEvent($message = '')
464
  {
465
  return self::reportEvent(5, $message);
466
  }
467
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
  /**
469
  * Send a notification to the administrator of the specified events, only if
470
  * the administrator accepted to receive alerts for this type of events.
471
  *
472
+ * @param string $event The name of the event that was triggered.
473
+ * @param string $content Body of the email that will be sent to the administrator.
474
+ * @return bool True if the email was apparently sent, false otherwise.
475
  */
476
  public static function notifyEvent($event = '', $content = '')
477
  {
505
  case 'failed_login':
506
  $settings_url = SucuriScanTemplate::getUrl('settings');
507
  $content .= "\n" . sprintf(
508
+ "<br><br>\n\n<em>Explanation: Someone failed to login to y"
509
+ . "our site. If you are getting too many of these messages"
510
+ . ", it is likely your site is under a password guessing b"
511
+ . "rute-force attack [1]. You can disable the failed login"
512
+ . " alerts from here [2]. Alternatively, you can consider "
513
+ . "to install a firewall between your website and your vis"
514
+ . "itors to filter out these and other attacks, take a loo"
515
+ . "k at Sucuri Firewall [3].</em><br><br>\n\n[1] <a href='"
516
+ . "https://kb.sucuri.net/definitions/attacks/brute-force/p"
517
+ . "assword-guessing'>https://kb.sucuri.net/definitions/att"
518
+ . "acks/brute-force/password-guessing</a><br>\n[2] <a href"
519
+ . "='%s'>%s</a> <br>\n[3] <a href='https://sucuri.net/webs"
520
+ . "ite-firewall/?wpalert'>https://sucuri.net/website-firew"
521
+ . "all/</a><br>\n",
522
  $settings_url,
523
  $settings_url
524
  );
539
  $email_params['ForceHTML'] = true;
540
  }
541
 
542
+ $title = ucwords(str_replace('_', "\x20", $event));
543
 
544
  return SucuriScanMail::sendMail(
545
  $email,
552
  /**
553
  * Check whether an IP address is being trusted or not.
554
  *
555
+ * @param string $addr The supposed ip address that will be checked.
556
+ * @return bool True if the user IP is trusted, false otherwise.
557
  */
558
+ public static function isTrustedIP($addr = '')
559
  {
560
+ if (!$addr) {
561
+ $addr = SucuriScan::getRemoteAddr();
562
  }
563
 
564
  $cache = new SucuriScanCache('trustip', false);
569
  }
570
 
571
  /* check if exact IP address match is whitelisted */
572
+ if (array_key_exists(md5($addr), $trusted_ips)) {
573
  return true;
574
  }
575
 
583
  // Generate the regular expression for a specific CIDR range.
584
  switch ($ip_info->cidr_range) {
585
  case 24:
586
+ $ip_pattern = sprintf(
587
+ '/^%d\.%d\.%d\.[0-9]{1,3}$/',
588
+ intval($ip_parts[0]),
589
+ intval($ip_parts[1]),
590
+ intval($ip_parts[2])
591
+ );
592
  break;
593
 
594
  case 16:
595
+ $ip_pattern = sprintf(
596
+ '/^%d\.%d\.[0-9]{1,3}\.[0-9]{1,3}$/',
597
+ intval($ip_parts[0]),
598
+ intval($ip_parts[1])
599
+ );
600
  break;
601
 
602
  case 8:
603
+ $ip_pattern = sprintf(
604
+ '/^%d\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/',
605
+ intval($ip_parts[0])
606
+ );
 
607
  break;
608
  }
609
 
610
+ if ($ip_pattern && preg_match($ip_pattern, $addr)) {
611
  $is_trusted_ip = true;
612
  break;
613
  }
619
  /**
620
  * Generate and set a new password for a specific user not in session.
621
  *
622
+ * @param int $user_id User account identifier.
623
+ * @return bool True if the process exit clean, false otherwise.
624
  */
625
  public static function setNewPassword($user_id = 0)
626
  {
635
  $display_name = $user->display_name;
636
  $new_password = wp_generate_password(15, true, false);
637
 
638
+ $message = SucuriScanTemplate::getSection(
639
+ 'settings-posthack-reset-password-alert',
640
+ array(
641
+ 'ResetPassword.UserName' => $user_login,
642
+ 'ResetPassword.DisplayName' => $display_name,
643
+ 'ResetPassword.Password' => $new_password,
644
+ 'ResetPassword.Website' => $website,
645
+ )
646
+ );
647
 
648
  /* Skip per hour alert limit and force text/html content-type */
649
  $data_set = array('Force' => true, 'ForceHTML' => true);
650
 
651
  $sent = SucuriScanMail::sendMail(
652
  $user->user_email,
653
+ 'Password Change',
654
  $message,
655
  $data_set
656
  );
675
  public static function setNewConfigKeys()
676
  {
677
  $new_wpconfig = '';
678
+ $config_path = self::getConfigPath();
679
 
680
  if (!$config_path) {
681
  return false;
src/fileinfo.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the fileinfo.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage fileinfo.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -23,6 +29,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
23
  * and folders using the built-in PHP class SplFileInfo. The SplFileInfo class
24
  * offers a high-level object oriented interface to information for an individual
25
  * file.
 
 
 
 
 
 
 
 
26
  */
27
  class SucuriScanFileInfo extends SucuriScan
28
  {
@@ -31,7 +45,7 @@ class SucuriScanFileInfo extends SucuriScan
31
  * be used to return the directory tree, this should be disabled when scanning a
32
  * directory without the need to filter the items in the list.
33
  *
34
- * @var boolean
35
  */
36
  public $ignore_files;
37
 
@@ -40,7 +54,7 @@ class SucuriScanFileInfo extends SucuriScan
40
  * be used to return the directory tree, this should be disabled when scanning a
41
  * path without the need to filter the items in the list.
42
  *
43
- * @var boolean
44
  */
45
  public $ignore_directories;
46
 
@@ -57,7 +71,7 @@ class SucuriScanFileInfo extends SucuriScan
57
  /**
58
  * Whether the filesystem scanner should run recursively or not.
59
  *
60
- * @var boolean
61
  */
62
  public $run_recursively;
63
 
@@ -70,7 +84,7 @@ class SucuriScanFileInfo extends SucuriScan
70
  * are ignored and that a folder may be empty some times there could be issues
71
  * because the deletion will not reach these resources.
72
  *
73
- * @var boolean
74
  */
75
  public $skip_directories;
76
 
@@ -108,8 +122,8 @@ class SucuriScanFileInfo extends SucuriScan
108
  *
109
  * Note: This is an approach that is intentionally naive.
110
  *
111
- * @param string $path Path to the file.
112
- * @return True if the file must be ignored.
113
  */
114
  private function ignoreFile($path)
115
  {
@@ -130,8 +144,8 @@ class SucuriScanFileInfo extends SucuriScan
130
  *
131
  * Note: This is an approach that is intentionally naive.
132
  *
133
- * @param string $path Path to the folder.
134
- * @return True if the folder must be ignored.
135
  */
136
  private function ignoreFolder($path)
137
  {
@@ -149,8 +163,8 @@ class SucuriScanFileInfo extends SucuriScan
149
  /**
150
  * Ignores files specified by the admins.
151
  *
152
- * @param string $path Path to the file or directory.
153
- * @return boolean True if the path has to be ignored.
154
  */
155
  private function isIgnoredPath($path)
156
  {
@@ -180,9 +194,9 @@ class SucuriScanFileInfo extends SucuriScan
180
  * @see http://php.net/manual/en/class.directoryiterator.php
181
  * @see http://php.net/manual/en/class.splfileinfo.php
182
  *
183
- * @param string $directory Where to execute the scanner.
184
- * @param string $filterby Either "file" or "directory".
185
- * @return array List of files in the specified directory.
186
  */
187
  public function getDirectoryTree($directory = '', $filterby = 'file')
188
  {
@@ -196,10 +210,10 @@ class SucuriScanFileInfo extends SucuriScan
196
  // @codeCoverageIgnoreStart
197
  try {
198
  if ($this->run_recursively) {
199
- $flags = FilesystemIterator::KEY_AS_PATHNAME
200
- | FilesystemIterator::CURRENT_AS_FILEINFO
201
- | FilesystemIterator::SKIP_DOTS
202
- | FilesystemIterator::UNIX_PATHS;
203
  $objects = new RecursiveIteratorIterator(
204
  new RecursiveDirectoryIterator($directory, $flags),
205
  RecursiveIteratorIterator::SELF_FIRST,
@@ -253,8 +267,8 @@ class SucuriScanFileInfo extends SucuriScan
253
  * and md5sum of that file. Some folders and files will be ignored depending
254
  * on some rules defined by the developer.
255
  *
256
- * @param string $directory Where to execute the scanner.
257
- * @param bool $as_array Return the file list as an array.
258
  * @return array|string|bool List of files in this project.
259
  */
260
  public function getDirectoryTreeMd5($directory = '', $as_array = false)
@@ -299,8 +313,8 @@ class SucuriScanFileInfo extends SucuriScan
299
  /**
300
  * Retrieves a list of unique directory paths.
301
  *
302
- * @param string $directory Directory path to scan.
303
- * @return array A list of unique directory paths.
304
  */
305
  public function getDirectoriesOnly($directory = '')
306
  {
@@ -312,8 +326,8 @@ class SucuriScanFileInfo extends SucuriScan
312
  /**
313
  * Deletes a directory recursively.
314
  *
315
- * @param string $directory Path of the existing directory that will be removed.
316
- * @return bool TRUE if all the files and folder inside the directory were removed.
317
  */
318
  public function removeDirectoryTree($directory = '')
319
  {
@@ -337,7 +351,8 @@ class SucuriScanFileInfo extends SucuriScan
337
  $this->ignore_directories = false;
338
 
339
  /* delete all the regular files and symbolic links */
340
- if ($dir_tree = $this->getDirectoryTree($directory, 'file')) {
 
341
  foreach ($dir_tree as $filename) {
342
  if (is_file($filename) || is_link($filename)) {
343
  @unlink($filename);
@@ -346,7 +361,8 @@ class SucuriScanFileInfo extends SucuriScan
346
  }
347
 
348
  /* delete directories starting from the deepest level */
349
- if ($dir_tree = $this->getDirectoryTree($directory, 'directory')) {
 
350
  $dir_tree = array_unique($dir_tree);
351
  usort($dir_tree, array('SucuriScanFileInfo', 'sortByLength'));
352
  foreach ($dir_tree as $dir_path) {
@@ -363,9 +379,9 @@ class SucuriScanFileInfo extends SucuriScan
363
  /**
364
  * Evaluates the difference between the length of two strings.
365
  *
366
- * @param string $a First string of characters that will be measured.
367
- * @param string $b Second string of characters that will be measured.
368
- * @return int The difference in length between the two strings.
369
  */
370
  public static function sortByLength($a, $b)
371
  {
@@ -380,8 +396,8 @@ class SucuriScanFileInfo extends SucuriScan
380
  * equals in order to avoid ambiguous results when the file exists, is
381
  * readable, but is empty.
382
  *
383
- * @param string $path Relative or absolute path of the file.
384
- * @return string Content of the file, false if not accessible.
385
  */
386
  public static function fileContent($path = '')
387
  {
@@ -393,8 +409,8 @@ class SucuriScanFileInfo extends SucuriScan
393
  * new line characters from the end of each line, and skip empty lines from
394
  * the list.
395
  *
396
- * @param string $filepath Path to the file.
397
- * @return array An array where each element is a line in the file.
398
  */
399
  public static function fileLines($filepath = '')
400
  {
@@ -404,8 +420,8 @@ class SucuriScanFileInfo extends SucuriScan
404
  /**
405
  * Tells whether the filename is a directory, symbolic link, or file.
406
  *
407
- * @param string $path Path to the file.
408
- * @return string Type of resource: dir, link, file.
409
  */
410
  public static function getResourceType($path = '')
411
  {
3
  /**
4
  * Code related to the fileinfo.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
29
  * and folders using the built-in PHP class SplFileInfo. The SplFileInfo class
30
  * offers a high-level object oriented interface to information for an individual
31
  * file.
32
+ *
33
+ * @category Library
34
+ * @package Sucuri
35
+ * @subpackage SucuriScanner
36
+ * @author Daniel Cid <dcid@sucuri.net>
37
+ * @copyright 2010-2017 Sucuri Inc.
38
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
39
+ * @link https://wordpress.org/plugins/sucuri-scanner
40
  */
41
  class SucuriScanFileInfo extends SucuriScan
42
  {
45
  * be used to return the directory tree, this should be disabled when scanning a
46
  * directory without the need to filter the items in the list.
47
  *
48
+ * @var bool
49
  */
50
  public $ignore_files;
51
 
54
  * be used to return the directory tree, this should be disabled when scanning a
55
  * path without the need to filter the items in the list.
56
  *
57
+ * @var bool
58
  */
59
  public $ignore_directories;
60
 
71
  /**
72
  * Whether the filesystem scanner should run recursively or not.
73
  *
74
+ * @var bool
75
  */
76
  public $run_recursively;
77
 
84
  * are ignored and that a folder may be empty some times there could be issues
85
  * because the deletion will not reach these resources.
86
  *
87
+ * @var bool
88
  */
89
  public $skip_directories;
90
 
122
  *
123
  * Note: This is an approach that is intentionally naive.
124
  *
125
+ * @param string $path Path to the file.
126
+ * @return True if the file must be ignored.
127
  */
128
  private function ignoreFile($path)
129
  {
144
  *
145
  * Note: This is an approach that is intentionally naive.
146
  *
147
+ * @param string $path Path to the folder.
148
+ * @return True if the folder must be ignored.
149
  */
150
  private function ignoreFolder($path)
151
  {
163
  /**
164
  * Ignores files specified by the admins.
165
  *
166
+ * @param string $path Path to the file or directory.
167
+ * @return bool True if the path has to be ignored.
168
  */
169
  private function isIgnoredPath($path)
170
  {
194
  * @see http://php.net/manual/en/class.directoryiterator.php
195
  * @see http://php.net/manual/en/class.splfileinfo.php
196
  *
197
+ * @param string $directory Where to execute the scanner.
198
+ * @param string $filterby Either "file" or "directory".
199
+ * @return array List of files in the specified directory.
200
  */
201
  public function getDirectoryTree($directory = '', $filterby = 'file')
202
  {
210
  // @codeCoverageIgnoreStart
211
  try {
212
  if ($this->run_recursively) {
213
+ $flags = FilesystemIterator::KEY_AS_PATHNAME;
214
+ $flags |= FilesystemIterator::CURRENT_AS_FILEINFO;
215
+ $flags |= FilesystemIterator::SKIP_DOTS;
216
+ $flags |= FilesystemIterator::UNIX_PATHS;
217
  $objects = new RecursiveIteratorIterator(
218
  new RecursiveDirectoryIterator($directory, $flags),
219
  RecursiveIteratorIterator::SELF_FIRST,
267
  * and md5sum of that file. Some folders and files will be ignored depending
268
  * on some rules defined by the developer.
269
  *
270
+ * @param string $directory Where to execute the scanner.
271
+ * @param bool $as_array Return the file list as an array.
272
  * @return array|string|bool List of files in this project.
273
  */
274
  public function getDirectoryTreeMd5($directory = '', $as_array = false)
313
  /**
314
  * Retrieves a list of unique directory paths.
315
  *
316
+ * @param string $directory Directory path to scan.
317
+ * @return array A list of unique directory paths.
318
  */
319
  public function getDirectoriesOnly($directory = '')
320
  {
326
  /**
327
  * Deletes a directory recursively.
328
  *
329
+ * @param string $directory Path of the existing directory that will be removed.
330
+ * @return bool TRUE if all the files and folder inside the directory were removed.
331
  */
332
  public function removeDirectoryTree($directory = '')
333
  {
351
  $this->ignore_directories = false;
352
 
353
  /* delete all the regular files and symbolic links */
354
+ $dir_tree = $this->getDirectoryTree($directory, 'file');
355
+ if (is_array($dir_tree) && !empty($dir_tree)) {
356
  foreach ($dir_tree as $filename) {
357
  if (is_file($filename) || is_link($filename)) {
358
  @unlink($filename);
361
  }
362
 
363
  /* delete directories starting from the deepest level */
364
+ $dir_tree = $this->getDirectoryTree($directory, 'directory');
365
+ if (is_array($dir_tree) && !empty($dir_tree)) {
366
  $dir_tree = array_unique($dir_tree);
367
  usort($dir_tree, array('SucuriScanFileInfo', 'sortByLength'));
368
  foreach ($dir_tree as $dir_path) {
379
  /**
380
  * Evaluates the difference between the length of two strings.
381
  *
382
+ * @param string $a First string of characters that will be measured.
383
+ * @param string $b Second string of characters that will be measured.
384
+ * @return int The difference in length between the two strings.
385
  */
386
  public static function sortByLength($a, $b)
387
  {
396
  * equals in order to avoid ambiguous results when the file exists, is
397
  * readable, but is empty.
398
  *
399
+ * @param string $path Relative or absolute path of the file.
400
+ * @return string Content of the file, false if not accessible.
401
  */
402
  public static function fileContent($path = '')
403
  {
409
  * new line characters from the end of each line, and skip empty lines from
410
  * the list.
411
  *
412
+ * @param string $filepath Path to the file.
413
+ * @return array An array where each element is a line in the file.
414
  */
415
  public static function fileLines($filepath = '')
416
  {
420
  /**
421
  * Tells whether the filename is a directory, symbolic link, or file.
422
  *
423
+ * @param string $path Path to the file.
424
+ * @return string Type of resource: dir, link, file.
425
  */
426
  public static function getResourceType($path = '')
427
  {
src/firewall.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the firewall.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage firewall.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -18,15 +24,23 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
18
 
19
  /**
20
  * Defines methods to interact with Sucuri Firewall's API service.
 
 
 
 
 
 
 
 
21
  */
22
  class SucuriScanFirewall extends SucuriScanAPI
23
  {
24
  /**
25
  * Check whether the firewall API key is valid or not.
26
  *
27
- * @param string $api_key The firewall API key.
28
- * @param bool $return_match Whether the parts of the API key must be returned or not.
29
- * @return array|bool True if the API key specified is valid, false otherwise.
30
  */
31
  public static function isValidKey($api_key = '', $return_match = false)
32
  {
@@ -70,20 +84,22 @@ class SucuriScanFirewall extends SucuriScanAPI
70
  /**
71
  * Call an action from the remote API interface of our firewall service.
72
  *
73
- * @param string $method HTTP method that will be used to send the request.
74
- * @param array $params Parameters for the request defined in an associative array of key-value.
75
- * @return array|bool Response object after the HTTP request is executed.
76
  */
77
  public static function apiCallFirewall($method = 'GET', $params = array())
78
  {
79
- $send_request = false;
 
 
 
80
 
81
- if (isset($params['k']) && isset($params['s'])) {
82
- $send_request = true;
83
- } elseif ($api_key = self::getKey()) {
84
- $send_request = true;
85
- $params['k'] = $api_key['k'];
86
- $params['s'] = $api_key['s'];
87
  }
88
 
89
  if ($send_request) {
@@ -101,8 +117,8 @@ class SucuriScanFirewall extends SucuriScanAPI
101
  * request to the remote API service and process its response, when successful
102
  * it will return an array/object containing the public attributes of the site.
103
  *
104
- * @param array|bool $api_key The firewall API key.
105
- * @return array|bool A hash with the settings of a firewall account.
106
  */
107
  public static function settings($api_key = false)
108
  {
@@ -142,18 +158,18 @@ class SucuriScanFirewall extends SucuriScanAPI
142
 
143
  if (self::isValidKey($api_key)) {
144
  SucuriScanOption::updateOption($option_name, $api_key);
145
- SucuriScanInterface::info(__('FirewallAPIKeySet', SUCURISCAN_TEXTDOMAIN));
146
  SucuriScanOption::setRevProxy('enable');
147
  SucuriScanOption::setAddrHeader('HTTP_X_SUCURI_CLIENTIP');
148
  } else {
149
- SucuriScanInterface::error(__('FirewallAPIKeyInvalid', SUCURISCAN_TEXTDOMAIN));
150
  }
151
  }
152
 
153
  // Delete the firewall API key from the plugin.
154
  if (SucuriScanRequest::post(':delete_wafkey') !== false) {
155
  SucuriScanOption::deleteOption($option_name);
156
- SucuriScanInterface::info(__('FirewallAPIKeyUnset', SUCURISCAN_TEXTDOMAIN));
157
  SucuriScanOption::setRevProxy('disable');
158
  SucuriScanOption::setAddrHeader('REMOTE_ADDR');
159
  }
@@ -175,33 +191,35 @@ class SucuriScanFirewall extends SucuriScanAPI
175
  * text, for example changing numbers or variable names into a more explicit
176
  * text so the administrator can understand the meaning of these settings.
177
  *
178
- * @param array $settings A hash with the settings of a firewall account.
179
- * @return array The explained version of the firewall settings.
180
  */
181
  public static function settingsExplanation($settings = array())
182
  {
183
  if (!is_array($settings)) {
184
- return array(/* empty */);
185
  }
186
 
187
  $cache_modes = array(
188
- 'docache' => __('FirewallDoCache', SUCURISCAN_TEXTDOMAIN),
189
- 'sitecache' => __('FirewallSiteCache', SUCURISCAN_TEXTDOMAIN),
190
- 'nocache' => __('FirewallNoCache', SUCURISCAN_TEXTDOMAIN),
191
- 'nocacheatall' => __('FirewallNoCacheAtAll', SUCURISCAN_TEXTDOMAIN),
192
  );
193
 
194
  foreach ($settings as $keyname => $value) {
195
  if ($keyname == 'proxy_active') {
196
- $settings[$keyname] = ($value === 1)
197
- ? __('Active', SUCURISCAN_TEXTDOMAIN)
198
- : __('NotActive', SUCURISCAN_TEXTDOMAIN);
199
- } elseif ($keyname == 'cache_mode') {
 
200
  if (array_key_exists($value, $cache_modes)) {
201
  $settings[$keyname] = $cache_modes[$value];
202
  } else {
203
- $settings[$keyname] = __('Unknown', SUCURISCAN_TEXTDOMAIN);
204
  }
 
205
  }
206
  }
207
 
@@ -212,6 +230,8 @@ class SucuriScanFirewall extends SucuriScanAPI
212
  * Returns the public firewall settings.
213
  *
214
  * @codeCoverageIgnore
 
 
215
  */
216
  public static function getSettingsAjax()
217
  {
@@ -230,7 +250,7 @@ class SucuriScanFirewall extends SucuriScanAPI
230
  if (!$settings) {
231
  if (empty($error)) {
232
  ob_start();
233
- SucuriScanInterface::error(__('FirewallAPIKeyMissing', SUCURISCAN_TEXTDOMAIN));
234
  $response['error'] = ob_get_clean();
235
  } else {
236
  $response['error'] = $error;
@@ -258,12 +278,12 @@ class SucuriScanFirewall extends SucuriScanAPI
258
  * the logs of previous days you will need to add a new parameter to the request
259
  * URL named "date" with format yyyy-mm-dd.
260
  *
261
- * @param array|string $api_key The firewall API key.
262
- * @param string $date Retrieve the data from this date.
263
- * @param string $query Filter the data to match this query.
264
- * @param int $limit Retrieve this maximum of data.
265
- * @param int $offset Retrieve the data from this point.
266
- * @return array|bool Objects with details of each blocked request.
267
  */
268
  public static function auditlogs($api_key, $date = '', $query = '', $limit = 10, $offset = 0)
269
  {
@@ -312,6 +332,8 @@ class SucuriScanFirewall extends SucuriScanAPI
312
  * feature. The plugin will display a warning in this case.
313
  *
314
  * @codeCoverageIgnore
 
 
315
  */
316
  public static function auditlogsAjax()
317
  {
@@ -324,7 +346,7 @@ class SucuriScanFirewall extends SucuriScanAPI
324
 
325
  if (!$api_key) {
326
  ob_start();
327
- SucuriScanInterface::error(__('FirewallAPIKeyMissing', SUCURISCAN_TEXTDOMAIN));
328
  $response = ob_get_clean();
329
  wp_send_json($response, 200);
330
  }
@@ -360,7 +382,7 @@ class SucuriScanFirewall extends SucuriScanAPI
360
  $response = self::auditlogsEntries($auditlogs['access_logs']);
361
 
362
  if (empty($response)) {
363
- $response = '<tr><td>' . __('NoData', SUCURISCAN_TEXTDOMAIN) . '.</td></tr>';
364
  }
365
  }
366
 
@@ -370,8 +392,8 @@ class SucuriScanFirewall extends SucuriScanAPI
370
  /**
371
  * Returns the security logs from the firewall in HTML.
372
  *
373
- * @param array $entries Security logs retrieved from the Firewall API.
374
- * @return string HTML with the information from the logs.
375
  */
376
  public static function auditlogsEntries($entries = array())
377
  {
@@ -432,10 +454,10 @@ class SucuriScanFirewall extends SucuriScanAPI
432
  /**
433
  * Get a list of years, months or days depending of the type specified.
434
  *
435
- * @param string $type Either years, months or days.
436
- * @param string $date Year, month and day selected from the request.
437
- * @param bool $in_html Whether the list should be converted to a HTML select options or not.
438
- * @return array|string Either an array with the expected values, or a HTML code.
439
  */
440
  public static function dates($type = '', $date = '', $in_html = true)
441
  {
@@ -463,18 +485,18 @@ class SucuriScanFirewall extends SucuriScanAPI
463
  case 'months':
464
  $selected = $s_month;
465
  $options = array(
466
- '01' => __('January', SUCURISCAN_TEXTDOMAIN),
467
- '02' => __('February', SUCURISCAN_TEXTDOMAIN),
468
- '03' => __('March', SUCURISCAN_TEXTDOMAIN),
469
- '04' => __('April', SUCURISCAN_TEXTDOMAIN),
470
- '05' => __('May', SUCURISCAN_TEXTDOMAIN),
471
- '06' => __('June', SUCURISCAN_TEXTDOMAIN),
472
- '07' => __('July', SUCURISCAN_TEXTDOMAIN),
473
- '08' => __('August', SUCURISCAN_TEXTDOMAIN),
474
- '09' => __('September', SUCURISCAN_TEXTDOMAIN),
475
- '10' => __('October', SUCURISCAN_TEXTDOMAIN),
476
- '11' => __('November', SUCURISCAN_TEXTDOMAIN),
477
- '12' => __('December', SUCURISCAN_TEXTDOMAIN),
478
  );
479
  break;
480
 
@@ -522,6 +544,8 @@ class SucuriScanFirewall extends SucuriScanAPI
522
  * Returns the whitelisted and blacklisted IP addresses.
523
  *
524
  * @codeCoverageIgnore
 
 
525
  */
526
  public static function ipAccessAjax()
527
  {
@@ -540,7 +564,7 @@ class SucuriScanFirewall extends SucuriScanAPI
540
  if (!$settings) {
541
  if (empty($error)) {
542
  ob_start();
543
- SucuriScanInterface::error(__('FirewallAPIKeyMissing', SUCURISCAN_TEXTDOMAIN));
544
  $response['error'] = ob_get_clean();
545
  } else {
546
  $response['error'] = $error;
@@ -560,6 +584,8 @@ class SucuriScanFirewall extends SucuriScanAPI
560
  * Blacklists an IP address.
561
  *
562
  * @codeCoverageIgnore
 
 
563
  */
564
  public static function blacklistAjax()
565
  {
@@ -573,7 +599,7 @@ class SucuriScanFirewall extends SucuriScanAPI
573
 
574
  if (!$params) {
575
  ob_start();
576
- SucuriScanInterface::error(__('FirewallAPIKeyMissing', SUCURISCAN_TEXTDOMAIN));
577
  $response['msg'] = ob_get_clean();
578
  wp_send_json($response, 200);
579
  }
@@ -588,8 +614,7 @@ class SucuriScanFirewall extends SucuriScanAPI
588
  $response['msg'] = implode(";\x20", $out['messages']);
589
 
590
  if ($out['status'] == 1) {
591
- SucuriScanEvent::reportInfoEvent(
592
- 'IP has been blacklisted: ' . $params['ip']);
593
  }
594
  }
595
 
@@ -600,6 +625,8 @@ class SucuriScanFirewall extends SucuriScanAPI
600
  * Deletes an IP address from the blacklist.
601
  *
602
  * @codeCoverageIgnore
 
 
603
  */
604
  public static function deblacklistAjax()
605
  {
@@ -613,7 +640,7 @@ class SucuriScanFirewall extends SucuriScanAPI
613
  if (!$params) {
614
  ob_start();
615
  $response['ok'] = false;
616
- SucuriScanInterface::error(__('FirewallAPIKeyMissing', SUCURISCAN_TEXTDOMAIN));
617
  $response['error'] = ob_get_clean();
618
  wp_send_json($response, 200);
619
  }
@@ -626,8 +653,7 @@ class SucuriScanFirewall extends SucuriScanAPI
626
  $response['msg'] = implode(";\x20", $out['messages']);
627
 
628
  if ($out['status'] == 1) {
629
- SucuriScanEvent::reportInfoEvent(
630
- 'IP has been unblacklisted: ' . $params['ip']);
631
  }
632
 
633
  wp_send_json($response, 200);
@@ -636,12 +662,12 @@ class SucuriScanFirewall extends SucuriScanAPI
636
  /**
637
  * Flush the cache of the site(s) associated with the API key.
638
  *
639
- * @param array|bool $api_key The firewall API key.
640
- * @return string|bool Message explaining the result of the operation.
641
  */
642
  public static function clearCache($api_key = false)
643
  {
644
- $params = array( 'a' => 'clear_cache' );
645
 
646
  if (is_array($api_key)) {
647
  $params = array_merge($params, $api_key);
@@ -663,9 +689,11 @@ class SucuriScanFirewall extends SucuriScanAPI
663
  {
664
  $params = array();
665
 
666
- $params['FirewallAutoClearCache'] =
667
- SucuriScanOption::isEnabled(':auto_clear_cache')
668
- ? 'checked="checked"' : 'data-status="disabled"';
 
 
669
 
670
  return SucuriScanTemplate::getSection('firewall-clearcache', $params);
671
  }
@@ -679,9 +707,9 @@ class SucuriScanFirewall extends SucuriScanAPI
679
  * of certain files is going to stay as it is due to the configuration on the
680
  * edge of the servers.
681
  *
682
- * @param array $data The post metadata.
683
  */
684
- public static function clearCacheHook($data)
685
  {
686
  if (SucuriScanOption::isEnabled(':auto_clear_cache')) {
687
  ob_start();
@@ -694,6 +722,8 @@ class SucuriScanFirewall extends SucuriScanAPI
694
  * Requests a cache flush to the firewall service.
695
  *
696
  * @codeCoverageIgnore
 
 
697
  */
698
  public static function clearCacheAjax()
699
  {
@@ -702,10 +732,11 @@ class SucuriScanFirewall extends SucuriScanAPI
702
  }
703
 
704
  ob_start();
705
- SucuriScanInterface::error(__('FirewallAPIKeyMissing', SUCURISCAN_TEXTDOMAIN));
706
  $response = ob_get_clean();
 
707
 
708
- if ($api_key = self::getKey()) {
709
  $res = self::clearCache($api_key);
710
 
711
  if (is_array($res) && isset($res['messages'])) {
@@ -724,6 +755,8 @@ class SucuriScanFirewall extends SucuriScanAPI
724
  * Configures the status of the automatic cache flush.
725
  *
726
  * @codeCoverageIgnore
 
 
727
  */
728
  public static function clearAutoCacheAjax()
729
  {
3
  /**
4
  * Code related to the firewall.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
24
 
25
  /**
26
  * Defines methods to interact with Sucuri Firewall's API service.
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 SucuriScanFirewall extends SucuriScanAPI
37
  {
38
  /**
39
  * Check whether the firewall API key is valid or not.
40
  *
41
+ * @param string $api_key The firewall API key.
42
+ * @param bool $return_match Whether the parts of the API key must be returned or not.
43
+ * @return array|bool True if the API key specified is valid, false otherwise.
44
  */
45
  public static function isValidKey($api_key = '', $return_match = false)
46
  {
84
  /**
85
  * Call an action from the remote API interface of our firewall service.
86
  *
87
+ * @param string $method HTTP method that will be used to send the request.
88
+ * @param array $params HTTP request parameters (key-value array).
89
+ * @return array|bool HTTP response object.
90
  */
91
  public static function apiCallFirewall($method = 'GET', $params = array())
92
  {
93
+ $send_request = (bool) (isset($params['k']) && isset($params['s']));
94
+
95
+ if (!$send_request) {
96
+ $api_key = self::getKey();
97
 
98
+ if ($api_key) {
99
+ $send_request = true;
100
+ $params['k'] = $api_key['k'];
101
+ $params['s'] = $api_key['s'];
102
+ }
 
103
  }
104
 
105
  if ($send_request) {
117
  * request to the remote API service and process its response, when successful
118
  * it will return an array/object containing the public attributes of the site.
119
  *
120
+ * @param array|bool $api_key The firewall API key.
121
+ * @return array|bool A hash with the settings of a firewall account.
122
  */
123
  public static function settings($api_key = false)
124
  {
158
 
159
  if (self::isValidKey($api_key)) {
160
  SucuriScanOption::updateOption($option_name, $api_key);
161
+ SucuriScanInterface::info('Firewall API key was successfully saved');
162
  SucuriScanOption::setRevProxy('enable');
163
  SucuriScanOption::setAddrHeader('HTTP_X_SUCURI_CLIENTIP');
164
  } else {
165
+ SucuriScanInterface::error('Invalid firewall API key');
166
  }
167
  }
168
 
169
  // Delete the firewall API key from the plugin.
170
  if (SucuriScanRequest::post(':delete_wafkey') !== false) {
171
  SucuriScanOption::deleteOption($option_name);
172
+ SucuriScanInterface::info('Firewall API key was successfully removed');
173
  SucuriScanOption::setRevProxy('disable');
174
  SucuriScanOption::setAddrHeader('REMOTE_ADDR');
175
  }
191
  * text, for example changing numbers or variable names into a more explicit
192
  * text so the administrator can understand the meaning of these settings.
193
  *
194
+ * @param array $settings A hash with the settings of a firewall account.
195
+ * @return array The explained version of the firewall settings.
196
  */
197
  public static function settingsExplanation($settings = array())
198
  {
199
  if (!is_array($settings)) {
200
+ return array();
201
  }
202
 
203
  $cache_modes = array(
204
+ 'docache' => 'enabled (recommended)',
205
+ 'sitecache' => 'site caching (using your site headers)',
206
+ 'nocache' => 'minimal (only for a few minutes)',
207
+ 'nocacheatall' => 'caching disabled (use with caution)',
208
  );
209
 
210
  foreach ($settings as $keyname => $value) {
211
  if ($keyname == 'proxy_active') {
212
+ $settings[$keyname] = ($value === 1) ? 'active' : 'not active';
213
+ continue;
214
+ }
215
+
216
+ if ($keyname == 'cache_mode') {
217
  if (array_key_exists($value, $cache_modes)) {
218
  $settings[$keyname] = $cache_modes[$value];
219
  } else {
220
+ $settings[$keyname] = 'unknown';
221
  }
222
+ continue;
223
  }
224
  }
225
 
230
  * Returns the public firewall settings.
231
  *
232
  * @codeCoverageIgnore
233
+ *
234
+ * @return void
235
  */
236
  public static function getSettingsAjax()
237
  {
250
  if (!$settings) {
251
  if (empty($error)) {
252
  ob_start();
253
+ SucuriScanInterface::error('Firewall API key was not found.');
254
  $response['error'] = ob_get_clean();
255
  } else {
256
  $response['error'] = $error;
278
  * the logs of previous days you will need to add a new parameter to the request
279
  * URL named "date" with format yyyy-mm-dd.
280
  *
281
+ * @param array|string $api_key The firewall API key.
282
+ * @param string $date Retrieve the data from this date.
283
+ * @param string $query Filter the data to match this query.
284
+ * @param int $limit Retrieve this maximum of data.
285
+ * @param int $offset Retrieve the data from this point.
286
+ * @return array|bool Objects with details of each blocked request.
287
  */
288
  public static function auditlogs($api_key, $date = '', $query = '', $limit = 10, $offset = 0)
289
  {
332
  * feature. The plugin will display a warning in this case.
333
  *
334
  * @codeCoverageIgnore
335
+ *
336
+ * @return void
337
  */
338
  public static function auditlogsAjax()
339
  {
346
 
347
  if (!$api_key) {
348
  ob_start();
349
+ SucuriScanInterface::error('Firewall API key was not found.');
350
  $response = ob_get_clean();
351
  wp_send_json($response, 200);
352
  }
382
  $response = self::auditlogsEntries($auditlogs['access_logs']);
383
 
384
  if (empty($response)) {
385
+ $response = '<tr><td>no data available.</td></tr>';
386
  }
387
  }
388
 
392
  /**
393
  * Returns the security logs from the firewall in HTML.
394
  *
395
+ * @param array $entries Security logs retrieved from the Firewall API.
396
+ * @return string HTML with the information from the logs.
397
  */
398
  public static function auditlogsEntries($entries = array())
399
  {
454
  /**
455
  * Get a list of years, months or days depending of the type specified.
456
  *
457
+ * @param string $type Either years, months or days.
458
+ * @param string $date Year, month and day selected from the request.
459
+ * @param bool $in_html Whether the list should be converted to a HTML select options or not.
460
+ * @return array|string Either an array with the expected values, or a HTML code.
461
  */
462
  public static function dates($type = '', $date = '', $in_html = true)
463
  {
485
  case 'months':
486
  $selected = $s_month;
487
  $options = array(
488
+ '01' => 'January',
489
+ '02' => 'February',
490
+ '03' => 'March',
491
+ '04' => 'April',
492
+ '05' => 'May',
493
+ '06' => 'June',
494
+ '07' => 'July',
495
+ '08' => 'August',
496
+ '09' => 'September',
497
+ '10' => 'October',
498
+ '11' => 'November',
499
+ '12' => 'December',
500
  );
501
  break;
502
 
544
  * Returns the whitelisted and blacklisted IP addresses.
545
  *
546
  * @codeCoverageIgnore
547
+ *
548
+ * @return void
549
  */
550
  public static function ipAccessAjax()
551
  {
564
  if (!$settings) {
565
  if (empty($error)) {
566
  ob_start();
567
+ SucuriScanInterface::error('Firewall API key was not found.');
568
  $response['error'] = ob_get_clean();
569
  } else {
570
  $response['error'] = $error;
584
  * Blacklists an IP address.
585
  *
586
  * @codeCoverageIgnore
587
+ *
588
+ * @return void
589
  */
590
  public static function blacklistAjax()
591
  {
599
 
600
  if (!$params) {
601
  ob_start();
602
+ SucuriScanInterface::error('Firewall API key was not found.');
603
  $response['msg'] = ob_get_clean();
604
  wp_send_json($response, 200);
605
  }
614
  $response['msg'] = implode(";\x20", $out['messages']);
615
 
616
  if ($out['status'] == 1) {
617
+ SucuriScanEvent::reportInfoEvent('IP has been blacklisted: ' . $params['ip']);
 
618
  }
619
  }
620
 
625
  * Deletes an IP address from the blacklist.
626
  *
627
  * @codeCoverageIgnore
628
+ *
629
+ * @return void
630
  */
631
  public static function deblacklistAjax()
632
  {
640
  if (!$params) {
641
  ob_start();
642
  $response['ok'] = false;
643
+ SucuriScanInterface::error('Firewall API key was not found.');
644
  $response['error'] = ob_get_clean();
645
  wp_send_json($response, 200);
646
  }
653
  $response['msg'] = implode(";\x20", $out['messages']);
654
 
655
  if ($out['status'] == 1) {
656
+ SucuriScanEvent::reportInfoEvent('IP has been unblacklisted: ' . $params['ip']);
 
657
  }
658
 
659
  wp_send_json($response, 200);
662
  /**
663
  * Flush the cache of the site(s) associated with the API key.
664
  *
665
+ * @param array|bool $api_key The firewall API key.
666
+ * @return string|bool Message explaining the result of the operation.
667
  */
668
  public static function clearCache($api_key = false)
669
  {
670
+ $params = array('a' => 'clear_cache');
671
 
672
  if (is_array($api_key)) {
673
  $params = array_merge($params, $api_key);
689
  {
690
  $params = array();
691
 
692
+ $params['FirewallAutoClearCache'] = 'data-status="disabled"';
693
+
694
+ if (SucuriScanOption::isEnabled(':auto_clear_cache')) {
695
+ $params['FirewallAutoClearCache'] = 'checked="checked"';
696
+ }
697
 
698
  return SucuriScanTemplate::getSection('firewall-clearcache', $params);
699
  }
707
  * of certain files is going to stay as it is due to the configuration on the
708
  * edge of the servers.
709
  *
710
+ * @return void
711
  */
712
+ public static function clearCacheHook()
713
  {
714
  if (SucuriScanOption::isEnabled(':auto_clear_cache')) {
715
  ob_start();
722
  * Requests a cache flush to the firewall service.
723
  *
724
  * @codeCoverageIgnore
725
+ *
726
+ * @return void
727
  */
728
  public static function clearCacheAjax()
729
  {
732
  }
733
 
734
  ob_start();
735
+ SucuriScanInterface::error('Firewall API key was not found.');
736
  $response = ob_get_clean();
737
+ $api_key = self::getKey();
738
 
739
+ if ($api_key) {
740
  $res = self::clearCache($api_key);
741
 
742
  if (is_array($res) && isset($res['messages'])) {
755
  * Configures the status of the automatic cache flush.
756
  *
757
  * @codeCoverageIgnore
758
+ *
759
+ * @return void
760
  */
761
  public static function clearAutoCacheAjax()
762
  {
src/fsscanner.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the fsscanner.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage fsscanner.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -24,14 +30,22 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
24
  * with the current content to establish what content has been updated. Updated
25
  * content is then submitted to the remote server and it is stored for future
26
  * analysis.
 
 
 
 
 
 
 
 
27
  */
28
  class SucuriScanFSScanner extends SucuriScan
29
  {
30
  /**
31
  * Retrieve the last time when the filesystem scan was ran.
32
  *
33
- * @param bool $format Whether the timestamp must be formatted as date/time or not.
34
- * @return string The timestamp of the runtime, or an string with the date/time.
35
  */
36
  public static function getFilesystemRuntime($format = false)
37
  {
@@ -51,8 +65,8 @@ class SucuriScanFSScanner extends SucuriScan
51
  /**
52
  * Add a new directory path to the list of ignored paths.
53
  *
54
- * @param string $path The (full) absolute path of a directory.
55
- * @return bool TRUE if the directory path was added to the list, FALSE otherwise.
56
  */
57
  public static function ignoreDirectory($path = '')
58
  {
@@ -70,8 +84,8 @@ class SucuriScanFSScanner extends SucuriScan
70
  /**
71
  * Remove a directory path from the list of ignored paths.
72
  *
73
- * @param string $path The (full) absolute path of a directory.
74
- * @return bool TRUE if the directory path was removed to the list, FALSE otherwise.
75
  */
76
  public static function unignoreDirectory($path = '')
77
  {
@@ -81,25 +95,16 @@ class SucuriScanFSScanner extends SucuriScan
81
  }
82
 
83
  /**
84
- * Retrieve a list of directories ignored.
85
- *
86
- * Retrieve a list of directory paths that will be ignored during the file
87
- * system scans, any sub-directory and files inside these folders will be
88
- * skipped automatically and will not be used to detect malware or modifications
89
- * in the site.
90
  *
91
- * The structure of the array returned by the method will always be composed
92
- * by four (4) indexes which will facilitate the execution of common conditions
93
- * in the implementation code.
94
  *
95
- * <ul>
96
- * <li>raw: Will contains the raw data retrieved from the built-in cache system.</li>
97
- * <li>checksums: Will contains the md5 of all the directory paths.</li>
98
- * <li>directories: Will contains a list of directory paths.</li>
99
- * <li>ignored_at_list: Will contains a list of timestamps for when the directories were ignored.</li>
100
- * </ul>
101
  *
102
- * @return array List of ignored directory paths.
103
  */
104
  public static function getIgnoredDirectories()
105
  {
@@ -112,15 +117,13 @@ class SucuriScanFSScanner extends SucuriScan
112
 
113
  $cache = new SucuriScanCache('ignorescanning');
114
  $cache_lifetime = 0; // It is not necessary to expire this cache.
115
- $ignored_directories = $cache->getAll($cache_lifetime, 'array');
116
 
117
- if ($ignored_directories) {
118
- $response['raw'] = $ignored_directories;
119
 
120
- foreach ($ignored_directories as $checksum => $data) {
121
- if (array_key_exists('directory_path', $data)
122
- && array_key_exists('ignored_at', $data)
123
- ) {
124
  $response['checksums'][] = $checksum;
125
  $response['directories'][] = $data['directory_path'];
126
  $response['ignored_at_list'][] = $data['ignored_at'];
@@ -130,41 +133,4 @@ class SucuriScanFSScanner extends SucuriScan
130
 
131
  return $response;
132
  }
133
-
134
- /**
135
- * Run file system scan and retrieve ignored folders.
136
- *
137
- * Run a file system scan and retrieve an array with two indexes, the first
138
- * containing a list of ignored directory paths and their respective timestamps
139
- * of when they were added by an administrator user, and the second containing a
140
- * list of directories that are not being ignored.
141
- *
142
- * @return array List of ignored and not ignored directories.
143
- */
144
- public static function getIgnoredDirectoriesLive()
145
- {
146
- $response = array(
147
- 'is_ignored' => array(),
148
- 'is_not_ignored' => array(),
149
- );
150
-
151
- // Get the ignored directories from the cache.
152
- $ignored_directories = self::getIgnoredDirectories();
153
-
154
- if ($ignored_directories) {
155
- $response['is_ignored'] = $ignored_directories['raw'];
156
- }
157
-
158
- // Scan the project and file all directories.
159
- $file_info = new SucuriScanFileInfo();
160
- $file_info->ignore_files = true;
161
- $file_info->ignore_directories = true;
162
- $directory_list = $file_info->getDirectoriesOnly(ABSPATH);
163
-
164
- if ($directory_list) {
165
- $response['is_not_ignored'] = $directory_list;
166
- }
167
-
168
- return $response;
169
- }
170
  }
3
  /**
4
  * Code related to the fsscanner.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
30
  * with the current content to establish what content has been updated. Updated
31
  * content is then submitted to the remote server and it is stored for future
32
  * analysis.
33
+ *
34
+ * @category Library
35
+ * @package Sucuri
36
+ * @subpackage SucuriScanner
37
+ * @author Daniel Cid <dcid@sucuri.net>
38
+ * @copyright 2010-2017 Sucuri Inc.
39
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
40
+ * @link https://wordpress.org/plugins/sucuri-scanner
41
  */
42
  class SucuriScanFSScanner extends SucuriScan
43
  {
44
  /**
45
  * Retrieve the last time when the filesystem scan was ran.
46
  *
47
+ * @param bool $format Whether the timestamp must be formatted as date/time or not.
48
+ * @return string The timestamp of the runtime, or an string with the date/time.
49
  */
50
  public static function getFilesystemRuntime($format = false)
51
  {
65
  /**
66
  * Add a new directory path to the list of ignored paths.
67
  *
68
+ * @param string $path The (full) absolute path of a directory.
69
+ * @return bool TRUE if the directory path was added to the list, FALSE otherwise.
70
  */
71
  public static function ignoreDirectory($path = '')
72
  {
84
  /**
85
  * Remove a directory path from the list of ignored paths.
86
  *
87
+ * @param string $path The (full) absolute path of a directory.
88
+ * @return bool TRUE if the directory path was removed to the list, FALSE otherwise.
89
  */
90
  public static function unignoreDirectory($path = '')
91
  {
95
  }
96
 
97
  /**
98
+ * Returns a list of ignored directories.
 
 
 
 
 
99
  *
100
+ * The method returns an array with the following keys:
 
 
101
  *
102
+ * - raw: Contains the raw data from the local cache.
103
+ * - checksums: Contains the md5 of all the directories.
104
+ * - directories: Contains a list of directories.
105
+ * - ignored_at_list: Contains a list of timestamps.
 
 
106
  *
107
+ * @return array List of ignored directories.
108
  */
109
  public static function getIgnoredDirectories()
110
  {
117
 
118
  $cache = new SucuriScanCache('ignorescanning');
119
  $cache_lifetime = 0; // It is not necessary to expire this cache.
120
+ $entries = $cache->getAll($cache_lifetime, 'array');
121
 
122
+ if ($entries) {
123
+ $response['raw'] = $entries;
124
 
125
+ foreach ($entries as $checksum => $data) {
126
+ if (isset($data['directory_path']) && isset($data['ignored_at'])) {
 
 
127
  $response['checksums'][] = $checksum;
128
  $response['directories'][] = $data['directory_path'];
129
  $response['ignored_at_list'][] = $data['ignored_at'];
133
 
134
  return $response;
135
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  }
src/globals.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the globals.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage globals.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -69,102 +75,16 @@ if (defined('SUCURISCAN')) {
69
  *
70
  * @return array List of sub-pages of this plugin.
71
  */
72
- function sucuriscan_pages()
73
  {
74
  return array(
75
- 'sucuriscan' => __('Dashboard', SUCURISCAN_TEXTDOMAIN),
76
- 'sucuriscan_firewall' => __('Firewall', SUCURISCAN_TEXTDOMAIN),
77
- 'sucuriscan_lastlogins' => __('LastLogins', SUCURISCAN_TEXTDOMAIN),
78
- 'sucuriscan_settings' => __('Settings', SUCURISCAN_TEXTDOMAIN),
79
  );
80
  }
81
 
82
- if (function_exists('load_plugin_textdomain')) {
83
- /**
84
- * Loads the language files for the entire interface.
85
- *
86
- * Internationalization is the process of developing your plugin so it
87
- * can be translated into other languages. Localization describes the
88
- * process of translating an internationalized plugin. Internationaliza-
89
- * tion is often abbreviated as i18n (there are 18 letters between the
90
- * i and the n) and localization is abbreviated as l10n (there are 10
91
- * letters between the l and the n).
92
- *
93
- * @see https://codex.wordpress.org/I18n_for_WordPress_Developers
94
- */
95
- function sucuriscan_load_plugin_textdomain()
96
- {
97
- global $locale;
98
-
99
- $default_locale = 'en_US';
100
- $pofile = sprintf(
101
- '%s/languages/%s-%s.po',
102
- SUCURISCAN_PLUGIN_PATH,
103
- SUCURISCAN_TEXTDOMAIN,
104
- $locale
105
- );
106
- $mofile = sprintf(
107
- '%s/languages/%s-%s.mo',
108
- SUCURISCAN_PLUGIN_PATH,
109
- SUCURISCAN_TEXTDOMAIN,
110
- $locale
111
- );
112
-
113
- /* attempt to import the English POT file into LOCALE */
114
- if (!file_exists($pofile) || !file_exists($mofile)) {
115
- $fallback = array(
116
- 'en_NZ' => 'en_US', /* English (New Zealand) */
117
- 'en_CA' => 'en_US', /* English (Canada) */
118
- 'en_ZA' => 'en_US', /* English (South Africa) */
119
- 'en_GB' => 'en_US', /* English (UK) */
120
- 'en_AU' => 'en_US', /* English (Australia) */
121
- 'es_AR' => 'es_ES', /* Español de Argentina */
122
- 'es_MX' => 'es_ES', /* Español de México */
123
- 'es_CO' => 'es_ES', /* Español de Colombia */
124
- 'es_GT' => 'es_ES', /* Español de Guatemala */
125
- 'es_VE' => 'es_ES', /* Español de Venezuela */
126
- 'es_CL' => 'es_ES', /* Español de Chile */
127
- 'es_PE' => 'es_ES', /* Español de Perú */
128
- );
129
-
130
- /* try to find a similar translation */
131
- if (array_key_exists($locale, $fallback)) {
132
- $default_locale = $fallback[$locale];
133
- }
134
-
135
- $en_pofile = sprintf(
136
- '%s/languages/%s-%s.po',
137
- SUCURISCAN_PLUGIN_PATH,
138
- SUCURISCAN_TEXTDOMAIN,
139
- $default_locale
140
- );
141
- $en_mofile = sprintf(
142
- '%s/languages/%s-%s.mo',
143
- SUCURISCAN_PLUGIN_PATH,
144
- SUCURISCAN_TEXTDOMAIN,
145
- $default_locale
146
- );
147
-
148
- @copy($en_pofile, $pofile);
149
- @copy($en_mofile, $mofile);
150
- }
151
-
152
- /* fallback to English on language import failure */
153
- if (!file_exists($pofile) || !file_exists($mofile)) {
154
- $locale = $default_locale;
155
- setlocale(LC_ALL, $default_locale);
156
- }
157
-
158
- load_plugin_textdomain(
159
- SUCURISCAN_TEXTDOMAIN,
160
- false, /* deprecated */
161
- SUCURISCAN_PLUGIN_FOLDER . '/languages/'
162
- );
163
- }
164
-
165
- add_action('init', 'sucuriscan_load_plugin_textdomain');
166
- }
167
-
168
  if (function_exists('add_action')) {
169
  /**
170
  * Display extension menu and submenu items in the correct interface.
@@ -174,10 +94,12 @@ if (defined('SUCURISCAN')) {
174
  * administration panel of the subsites.
175
  *
176
  * @codeCoverageIgnore
 
 
177
  */
178
- function sucuriscan_add_menu_page()
179
  {
180
- $pages = sucuriscan_pages();
181
 
182
  add_menu_page(
183
  'Sucuri Security',
@@ -201,7 +123,7 @@ if (defined('SUCURISCAN')) {
201
  }
202
 
203
  /* Attach HTTP request handlers for the internal plugin pages */
204
- add_action($sucuriscan_action_prefix . 'admin_menu', 'sucuriscan_add_menu_page');
205
 
206
  /* Attach HTTP request handlers for the AJAX requests */
207
  add_action('wp_ajax_sucuriscan_ajax', 'sucuriscan_ajax');
3
  /**
4
  * Code related to the globals.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
75
  *
76
  * @return array List of sub-pages of this plugin.
77
  */
78
+ function sucuriscanMainPages()
79
  {
80
  return array(
81
+ 'sucuriscan' => 'Dashboard',
82
+ 'sucuriscan_firewall' => 'Firewall (WAF)',
83
+ 'sucuriscan_lastlogins' => 'Last Logins',
84
+ 'sucuriscan_settings' => 'Settings',
85
  );
86
  }
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  if (function_exists('add_action')) {
89
  /**
90
  * Display extension menu and submenu items in the correct interface.
94
  * administration panel of the subsites.
95
  *
96
  * @codeCoverageIgnore
97
+ *
98
+ * @return void
99
  */
100
+ function sucuriscanAddMenuPage()
101
  {
102
+ $pages = sucuriscanMainPages();
103
 
104
  add_menu_page(
105
  'Sucuri Security',
123
  }
124
 
125
  /* Attach HTTP request handlers for the internal plugin pages */
126
+ add_action($sucuriscan_action_prefix . 'admin_menu', 'sucuriscanAddMenuPage');
127
 
128
  /* Attach HTTP request handlers for the AJAX requests */
129
  add_action('wp_ajax_sucuriscan_ajax', 'sucuriscan_ajax');
src/hardening.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the hardening.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage hardening.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -33,6 +39,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
33
  * scripts and tools like Bastille Linux, JASS for Solaris systems and
34
  * Apache/PHP Hardener that can, for example, deactivate unneeded features in
35
  * configuration files or perform various other protective measures.
 
 
 
 
 
 
 
 
36
  */
37
  class SucuriScanHardening extends SucuriScan
38
  {
@@ -65,8 +79,8 @@ class SucuriScanHardening extends SucuriScan
65
  * The permissions to modify the file are checked before anything else, this
66
  * method is self-contained.
67
  *
68
- * @param string $directory Valid directory path where to place the access rules.
69
- * @return bool True if the rules are successfully added, false otherwise.
70
  */
71
  public static function hardenDirectory($directory = '')
72
  {
@@ -97,8 +111,8 @@ class SucuriScanHardening extends SucuriScan
97
  * all files with certain extension in any mixed case. The file is truncated if
98
  * after the operation its size is equals to zero.
99
  *
100
- * @param string $directory Valid directory path where to access rules are.
101
- * @return bool True if the rules are successfully deleted, false otherwise.
102
  */
103
  public static function unhardenDirectory($directory = '')
104
  {
@@ -124,8 +138,8 @@ class SucuriScanHardening extends SucuriScan
124
  /**
125
  * Remove the hardening applied in previous versions.
126
  *
127
- * @param string $directory Valid directory path.
128
- * @return bool True if the access control file was fixed.
129
  */
130
  private static function fixPreviousHardening($directory = '')
131
  {
@@ -147,8 +161,8 @@ class SucuriScanHardening extends SucuriScan
147
  /**
148
  * Check whether a directory is hardened or not.
149
  *
150
- * @param string $directory Valid directory path.
151
- * @return bool True if the directory is hardened, false otherwise.
152
  */
153
  public static function isHardened($directory = '')
154
  {
@@ -167,8 +181,8 @@ class SucuriScanHardening extends SucuriScan
167
  /**
168
  * Returns the path to the Apache access control file.
169
  *
170
- * @param string $folder Folder where the htaccess file is supposed to be.
171
- * @return string Path to the htaccess file in the specified folder.
172
  */
173
  private static function htaccess($folder = '')
174
  {
@@ -186,8 +200,8 @@ class SucuriScanHardening extends SucuriScan
186
  * can send a direct request to it. The method will generate both the rules
187
  * for Apache 2.4 and a compatibility conditional for older versions.
188
  *
189
- * @param string $file File to be ignored by the hardening.
190
- * @return string Access control rules to whitelist the file.
191
  */
192
  private static function whitelistRule($file = '')
193
  {
@@ -217,20 +231,20 @@ class SucuriScanHardening extends SucuriScan
217
  * admin can ignore this hardening in one or more files if direct access to
218
  * it is required, as is the case with some 3rd-party plugins and themes.
219
  *
220
- * @param string $file File to be ignored by the hardening.
221
- * @param string $folder Folder hosting the specified file.
222
- * @return bool True if the file has been whitelisted, false otherwise.
223
  */
224
  public static function whitelist($file = '', $folder = '')
225
  {
226
  $htaccess = self::htaccess($folder);
227
 
228
  if (!file_exists($htaccess)) {
229
- throw new Exception(__('HTAccessIsMissing', SUCURISCAN_TEXTDOMAIN));
230
  }
231
 
232
  if (!is_writable($htaccess)) {
233
- throw new Exception(__('HTAccessNotWritable', SUCURISCAN_TEXTDOMAIN));
234
  }
235
 
236
  return (bool) @file_put_contents(
@@ -250,9 +264,9 @@ class SucuriScanHardening extends SucuriScan
250
  * theme required it, they can decide to revert the whitelisting using this
251
  * method which is executed by one of the tools in the settings page.
252
  *
253
- * @param string $file File to stop ignoring from the hardening.
254
- * @param string $folder Folder hosting the specified file.
255
- * @return bool True if the file has been dewhitelisted, false otherwise.
256
  */
257
  public static function dewhitelist($file = '', $folder = '')
258
  {
@@ -273,8 +287,8 @@ class SucuriScanHardening extends SucuriScan
273
  /**
274
  * Returns a list of whitelisted files in folder.
275
  *
276
- * @param string $folder Directory to scan for whitelisted files.
277
- * @return array List of whitelisted files, false on failure.
278
  */
279
  public static function getWhitelisted($folder = '')
280
  {
@@ -282,6 +296,6 @@ class SucuriScanHardening extends SucuriScan
282
  $content = SucuriScanFileInfo::fileContent($htaccess);
283
  @preg_match_all('/<Files (\S+)>/', $content, $matches);
284
 
285
- return $matches[1] /* empty for no matches */;
286
  }
287
  }
3
  /**
4
  * Code related to the hardening.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
39
  * scripts and tools like Bastille Linux, JASS for Solaris systems and
40
  * Apache/PHP Hardener that can, for example, deactivate unneeded features in
41
  * configuration files or perform various other protective measures.
42
+ *
43
+ * @category Library
44
+ * @package Sucuri
45
+ * @subpackage SucuriScanner
46
+ * @author Daniel Cid <dcid@sucuri.net>
47
+ * @copyright 2010-2017 Sucuri Inc.
48
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
49
+ * @link https://wordpress.org/plugins/sucuri-scanner
50
  */
51
  class SucuriScanHardening extends SucuriScan
52
  {
79
  * The permissions to modify the file are checked before anything else, this
80
  * method is self-contained.
81
  *
82
+ * @param string $directory Valid directory path where to place the access rules.
83
+ * @return bool True if the rules are successfully added, false otherwise.
84
  */
85
  public static function hardenDirectory($directory = '')
86
  {
111
  * all files with certain extension in any mixed case. The file is truncated if
112
  * after the operation its size is equals to zero.
113
  *
114
+ * @param string $directory Valid directory path where to access rules are.
115
+ * @return bool True if the rules are successfully deleted, false otherwise.
116
  */
117
  public static function unhardenDirectory($directory = '')
118
  {
138
  /**
139
  * Remove the hardening applied in previous versions.
140
  *
141
+ * @param string $directory Valid directory path.
142
+ * @return bool True if the access control file was fixed.
143
  */
144
  private static function fixPreviousHardening($directory = '')
145
  {
161
  /**
162
  * Check whether a directory is hardened or not.
163
  *
164
+ * @param string $directory Valid directory path.
165
+ * @return bool True if the directory is hardened, false otherwise.
166
  */
167
  public static function isHardened($directory = '')
168
  {
181
  /**
182
  * Returns the path to the Apache access control file.
183
  *
184
+ * @param string $folder Folder where the htaccess file is supposed to be.
185
+ * @return string Path to the htaccess file in the specified folder.
186
  */
187
  private static function htaccess($folder = '')
188
  {
200
  * can send a direct request to it. The method will generate both the rules
201
  * for Apache 2.4 and a compatibility conditional for older versions.
202
  *
203
+ * @param string $file File to be ignored by the hardening.
204
+ * @return string Access control rules to whitelist the file.
205
  */
206
  private static function whitelistRule($file = '')
207
  {
231
  * admin can ignore this hardening in one or more files if direct access to
232
  * it is required, as is the case with some 3rd-party plugins and themes.
233
  *
234
+ * @param string $file File to be ignored by the hardening.
235
+ * @param string $folder Folder hosting the specified file.
236
+ * @return bool True if the file has been whitelisted, false otherwise.
237
  */
238
  public static function whitelist($file = '', $folder = '')
239
  {
240
  $htaccess = self::htaccess($folder);
241
 
242
  if (!file_exists($htaccess)) {
243
+ throw new Exception('Access control file does not exists');
244
  }
245
 
246
  if (!is_writable($htaccess)) {
247
+ throw new Exception('Access control file is not writable');
248
  }
249
 
250
  return (bool) @file_put_contents(
264
  * theme required it, they can decide to revert the whitelisting using this
265
  * method which is executed by one of the tools in the settings page.
266
  *
267
+ * @param string $file File to stop ignoring from the hardening.
268
+ * @param string $folder Folder hosting the specified file.
269
+ * @return bool True if the file has been dewhitelisted, false otherwise.
270
  */
271
  public static function dewhitelist($file = '', $folder = '')
272
  {
287
  /**
288
  * Returns a list of whitelisted files in folder.
289
  *
290
+ * @param string $folder Directory to scan for whitelisted files.
291
+ * @return array List of whitelisted files, false on failure.
292
  */
293
  public static function getWhitelisted($folder = '')
294
  {
296
  $content = SucuriScanFileInfo::fileContent($htaccess);
297
  @preg_match_all('/<Files (\S+)>/', $content, $matches);
298
 
299
+ return $matches[1];
300
  }
301
  }
src/hook.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the hook.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage hook.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -22,27 +28,38 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
22
  * The term hooking covers a range of techniques used to alter or augment the
23
  * behavior of an operating system, of applications, or of other software
24
  * components by intercepting method calls or messages or events passed
25
- * between software components. Code that handles such intercepted functionmethods, events or messages is called a "hook".
 
26
  *
27
  * Hooking is used for many purposes, including debugging and extending
28
  * functionality. Examples might include intercepting keyboard or mouse event
29
  * messages before they reach an application, or intercepting operating system
30
  * calls in order to monitor behavior or modify the method of an application
31
  * or other component; it is also widely used in benchmarking programs.
 
 
 
 
 
 
 
 
32
  */
33
  class SucuriScanHook extends SucuriScanEvent
34
  {
35
  /**
36
  * Send to Sucuri servers an alert notifying that an attachment was added to a post.
37
  *
38
- * @param int $id The post identifier.
 
39
  */
40
  public static function hookAttachmentAdd($id = 0)
41
  {
42
  $title = 'unknown';
43
  $mime_type = 'unknown';
 
44
 
45
- if ($data = get_post($id)) {
46
  $id = $data->ID;
47
  $title = $data->post_title;
48
  $mime_type = $data->post_mime_type;
@@ -56,7 +73,8 @@ class SucuriScanHook extends SucuriScanEvent
56
  /**
57
  * Send an alert notifying that a category was created.
58
  *
59
- * @param int $id The identifier of the category created.
 
60
  */
61
  public static function hookCategoryCreate($id = 0)
62
  {
@@ -67,10 +85,10 @@ class SucuriScanHook extends SucuriScanEvent
67
  self::notifyEvent('post_publication', $message);
68
  }
69
 
70
- // TODO: Detect auto updates in core, themes, and plugin files.
71
-
72
  /**
73
  * Detects when the core files are updated.
 
 
74
  */
75
  public static function hookCoreUpdate()
76
  {
@@ -88,15 +106,17 @@ class SucuriScanHook extends SucuriScanEvent
88
  /**
89
  * Send an alert notifying that a new link was added to the bookmarks.
90
  *
91
- * @param int $id Identifier of the new link created;
 
92
  */
93
  public static function hookLinkAdd($id = 0)
94
  {
95
  $title = 'unknown';
96
  $target = '_none';
97
  $url = 'undefined/url';
 
98
 
99
- if ($data = get_bookmark($id)) {
100
  $title = $data->link_name;
101
  $target = $data->link_target;
102
  $url = $data->link_url;
@@ -116,15 +136,17 @@ class SucuriScanHook extends SucuriScanEvent
116
  /**
117
  * Send an alert notifying that a new link was added to the bookmarks.
118
  *
119
- * @param int $id Identifier of the new link created;
 
120
  */
121
  public static function hookLinkEdit($id = 0)
122
  {
123
  $title = 'unknown';
124
  $target = '_none';
125
  $url = 'undefined/url';
 
126
 
127
- if ($data = get_bookmark($id)) {
128
  $title = $data->link_name;
129
  $target = $data->link_target;
130
  $url = $data->link_url;
@@ -145,7 +167,8 @@ class SucuriScanHook extends SucuriScanEvent
145
  * Send an alert notifying that an attempt to login into the
146
  * administration panel failed.
147
  *
148
- * @param string $title The name of the user account involved in the transaction.
 
149
  */
150
  public static function hookLoginFailure($title = '')
151
  {
@@ -166,7 +189,9 @@ class SucuriScanHook extends SucuriScanEvent
166
  self::notifyEvent('failed_login', $message);
167
 
168
  /* report brute-force attack if necessary */
169
- if ($logins = sucuriscan_get_failed_logins()) {
 
 
170
  $max_time = 3600; /* report logins in the last hour */
171
  $maximum = SucuriScanOption::getOption(':maximum_failed_logins');
172
 
@@ -193,19 +218,16 @@ class SucuriScanHook extends SucuriScanEvent
193
  * a brute-force attack (if it exists) because the time passed
194
  * between the first and last login attempt is big enough to
195
  * mitigate the attack.
196
- *
197
- * We will consider the current failed login event as the first
198
- * entry of that file in case of future attempts during the next
199
- * sixty minutes.
200
  */
201
  sucuriscan_reset_failed_logins();
202
- sucuriscan_log_failed_login($title, $password);
203
  }
204
  }
205
  }
206
 
207
  /**
208
  * Detects usage of the password reset form.
 
 
209
  */
210
  public static function hookLoginFormResetpass()
211
  {
@@ -219,7 +241,8 @@ class SucuriScanHook extends SucuriScanEvent
219
  * Send an alert notifying that an attempt to login into the
220
  * administration panel was successful.
221
  *
222
- * @param string $title The name of the user account involved in the transaction.
 
223
  */
224
  public static function hookLoginSuccess($title = '')
225
  {
@@ -236,6 +259,8 @@ class SucuriScanHook extends SucuriScanEvent
236
  * will compare the value sent with the form with the value in the database
237
  * and if there are differences will send an email alert notifying the admin
238
  * about the changes.
 
 
239
  */
240
  public static function hookOptionsManagement()
241
  {
@@ -265,8 +290,7 @@ class SucuriScanHook extends SucuriScanEvent
265
  }
266
 
267
  /* identify the origin of the request */
268
- $option_page = isset($_POST['option_page'])
269
- ? $_POST['option_page'] : 'options';
270
  $page_referer = 'Common';
271
 
272
  switch ($option_page) {
@@ -286,11 +310,13 @@ class SucuriScanHook extends SucuriScanEvent
286
 
287
  if ($options_changed_count) {
288
  $message = $page_referer . ' settings changed';
289
- self::reportErrorEvent(sprintf(
290
- '%s: (multiple entries): %s',
291
- $message,
292
- rtrim($options_changed_simple, ',')
293
- ));
 
 
294
  self::notifyEvent('settings_updated', $message . "<br>\n" . $options_changed_str);
295
  }
296
  }
@@ -299,8 +325,9 @@ class SucuriScanHook extends SucuriScanEvent
299
  /**
300
  * Sends an alert with information about a plugin that has been activated.
301
  *
302
- * @param string $plugin Name of the plugin.
303
- * @param string $network_activation Whether the activation was global or not.
 
304
  */
305
  public static function hookPluginActivate($plugin = '', $network_activation = '')
306
  {
@@ -316,11 +343,12 @@ class SucuriScanHook extends SucuriScanEvent
316
  * able to detect a deactivation if the plugin has been deleted via FTP or
317
  * SSH or any file manager available in the hosting panel.
318
  *
319
- * @param string $action Activated or deactivated.
320
- * @param string $plugin Short name of the plugin file.
321
- * @param string $network_activation Whether the action is global or not.
 
322
  */
323
- private static function hookPluginChanges($action, $plugin = '', $network_activation = '')
324
  {
325
  $filename = WP_PLUGIN_DIR . '/' . $plugin;
326
 
@@ -342,11 +370,12 @@ class SucuriScanHook extends SucuriScanEvent
342
  }
343
 
344
  $message = sprintf(
345
- 'Plugin %s: %s (v%s; %s)',
346
  $action, /* activated or deactivated */
347
  self::escape($info['Name']),
348
  self::escape($info['Version']),
349
- self::escape($plugin)
 
350
  );
351
  self::reportWarningEvent($message);
352
  self::notifyEvent('plugin_' . $action, $message);
@@ -355,8 +384,9 @@ class SucuriScanHook extends SucuriScanEvent
355
  /**
356
  * Sends an alert with information about a plugin that has been deactivated.
357
  *
358
- * @param string $plugin Name of the plugin.
359
- * @param string $network_activation Whether the deactivation was global or not.
 
360
  */
361
  public static function hookPluginDeactivate($plugin = '', $network_activation = '')
362
  {
@@ -365,6 +395,8 @@ class SucuriScanHook extends SucuriScanEvent
365
 
366
  /**
367
  * Detects when a plugin is deleted.
 
 
368
  */
369
  public static function hookPluginDelete()
370
  {
@@ -399,13 +431,14 @@ class SucuriScanHook extends SucuriScanEvent
399
 
400
  // Report deleted plugins at once.
401
  if (!empty($items_affected)) {
402
- $message_tpl = ( count($items_affected) > 1 )
403
- ? 'Plugins deleted: (multiple entries): %s'
404
- : 'Plugin deleted: %s';
405
- $message = sprintf(
406
- $message_tpl,
407
- @implode(',', $items_affected)
408
- );
 
409
  self::reportWarningEvent($message);
410
  self::notifyEvent('plugin_deleted', $message);
411
  }
@@ -414,6 +447,8 @@ class SucuriScanHook extends SucuriScanEvent
414
 
415
  /**
416
  * Detects when the plugin editor is used.
 
 
417
  */
418
  public static function hookPluginEditor()
419
  {
@@ -433,6 +468,8 @@ class SucuriScanHook extends SucuriScanEvent
433
 
434
  /**
435
  * Detects when a plugin is uploaded or installed.
 
 
436
  */
437
  public static function hookPluginInstall()
438
  {
@@ -455,17 +492,20 @@ class SucuriScanHook extends SucuriScanEvent
455
 
456
  /**
457
  * Detects when a plugin is updated or upgraded.
 
 
458
  */
459
  public static function hookPluginUpdate()
460
  {
461
  // Plugin update request.
462
  $plugin_update_actions = '(upgrade-plugin|do-plugin-upgrade|update-selected)';
463
 
464
- if (current_user_can('update_plugins')
465
- && (
466
- SucuriScanRequest::getOrPost('action', $plugin_update_actions)
467
- || SucuriScanRequest::getOrPost('action2', $plugin_update_actions)
468
- )
 
469
  ) {
470
  $plugin_list = array();
471
  $items_affected = array();
@@ -498,13 +538,14 @@ class SucuriScanHook extends SucuriScanEvent
498
 
499
  // Report updated plugins at once.
500
  if (!empty($items_affected)) {
501
- $message_tpl = ( count($items_affected) > 1 )
502
- ? 'Plugins updated: (multiple entries): %s'
503
- : 'Plugin updated: %s';
504
- $message = sprintf(
505
- $message_tpl,
506
- @implode(',', $items_affected)
507
- );
 
508
  self::reportWarningEvent($message);
509
  self::notifyEvent('plugin_updated', $message);
510
  }
@@ -524,32 +565,37 @@ class SucuriScanHook extends SucuriScanEvent
524
  * this informaiton to send it to the API. We will delete the temporary data
525
  * after the operation has succeeded.
526
  *
527
- * @param int $id The identifier of the post deleted.
 
528
  */
529
  public static function hookPostBeforeDelete($id = 0)
530
  {
531
- if ($data = get_post($id)) {
532
- $out = array(); /* data to cache */
533
- $cache = new SucuriScanCache('hookdata');
534
 
535
- $out['id'] = $data->ID;
536
- $out['author'] = $data->post_author;
537
- $out['type'] = $data->post_type;
538
- $out['status'] = $data->post_status;
539
- $out['inserted'] = $data->post_date;
540
- $out['modified'] = $data->post_modified;
541
- $out['guid'] = $data->guid;
542
- $out['title'] = empty($data->post_title)
543
- ? '(empty)' : $data->post_title;
544
-
545
- $cache->add('post_' . $id, $out);
546
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
547
  }
548
 
549
  /**
550
  * Send an alert notifying that a post was deleted.
551
  *
552
- * @param int $id The identifier of the post deleted.
 
553
  */
554
  public static function hookPostDelete($id = 0)
555
  {
@@ -570,9 +616,10 @@ class SucuriScanHook extends SucuriScanEvent
570
  /**
571
  * Sends an alert for transitions between post statuses.
572
  *
573
- * @param string $new New post status.
574
- * @param string $old Old post status.
575
- * @param object $post Post data.
 
576
  */
577
  public static function hookPostStatus($new = '', $old = '', $post = null)
578
  {
@@ -642,14 +689,16 @@ class SucuriScanHook extends SucuriScanEvent
642
  /**
643
  * Send an alert notifying that a post was moved to the trash.
644
  *
645
- * @param int $id The identifier of the trashed post.
 
646
  */
647
  public static function hookPostTrash($id = 0)
648
  {
649
  $title = 'Unknown';
650
  $status = 'none';
 
651
 
652
- if ($data = get_post($id)) {
653
  $title = $data->post_title;
654
  $status = $data->post_status;
655
  }
@@ -666,15 +715,17 @@ class SucuriScanHook extends SucuriScanEvent
666
  /**
667
  * Send an alert notifying that a post or page is created or updated.
668
  *
669
- * @param int $id The identifier of the post or page published.
 
670
  */
671
  private static function hookPublish($id = 0)
672
  {
673
  $title = 'Unknown';
674
  $p_type = 'Publication';
675
  $action = 'published';
 
676
 
677
- if ($data = get_post($id)) {
678
  $title = $data->post_title;
679
  $p_type = ucwords($data->post_type);
680
  $action = 'updated';
@@ -684,7 +735,7 @@ class SucuriScanHook extends SucuriScanEvent
684
  $action = 'created';
685
  }
686
 
687
- SucuriScanFirewall::clearCacheHook($data);
688
  }
689
 
690
  $message = sprintf(
@@ -701,7 +752,8 @@ class SucuriScanHook extends SucuriScanEvent
701
  /**
702
  * Detects when a page is created or updated.
703
  *
704
- * @param int $id The identifier of the post or page published.
 
705
  */
706
  public static function hookPublishPage($id = 0)
707
  {
@@ -711,7 +763,8 @@ class SucuriScanHook extends SucuriScanEvent
711
  /**
712
  * Detects when a post is created or updated via email.
713
  *
714
- * @param int $id The identifier of the post or page published.
 
715
  */
716
  public static function hookPublishPhone($id = 0)
717
  {
@@ -721,7 +774,8 @@ class SucuriScanHook extends SucuriScanEvent
721
  /**
722
  * Detects when a post is created or updated.
723
  *
724
- * @param int $id The identifier of the post or page published.
 
725
  */
726
  public static function hookPublishPost($id = 0)
727
  {
@@ -731,7 +785,8 @@ class SucuriScanHook extends SucuriScanEvent
731
  /**
732
  * Detects when a post is created or updated via XML-RPC.
733
  *
734
- * @param int $id The identifier of the post or page published.
 
735
  */
736
  public static function hookPublishPostXMLRPC($id = 0)
737
  {
@@ -742,7 +797,8 @@ class SucuriScanHook extends SucuriScanEvent
742
  * Send an alert notifying that an attempt to retrieve the password
743
  * of an user account was tried.
744
  *
745
- * @param string $title The name of the user account involved in the trasaction.
 
746
  */
747
  public static function hookRetrievePassword($title = '')
748
  {
@@ -753,6 +809,8 @@ class SucuriScanHook extends SucuriScanEvent
753
 
754
  /**
755
  * Detects when a theme is deleted.
 
 
756
  */
757
  public static function hookThemeDelete()
758
  {
@@ -772,6 +830,8 @@ class SucuriScanHook extends SucuriScanEvent
772
 
773
  /**
774
  * Detects when the theme editor is used.
 
 
775
  */
776
  public static function hookThemeEditor()
777
  {
@@ -792,6 +852,8 @@ class SucuriScanHook extends SucuriScanEvent
792
 
793
  /**
794
  * Detects when a theme is installed.
 
 
795
  */
796
  public static function hookThemeInstall()
797
  {
@@ -811,7 +873,8 @@ class SucuriScanHook extends SucuriScanEvent
811
  /**
812
  * Send an alert notifying that the theme of the site was changed.
813
  *
814
- * @param string $title The name of the new theme selected to used through out the site.
 
815
  */
816
  public static function hookThemeSwitch($title = '')
817
  {
@@ -823,6 +886,8 @@ class SucuriScanHook extends SucuriScanEvent
823
 
824
  /**
825
  * Detects when a theme is automatically or manually updated.
 
 
826
  */
827
  public static function hookThemeUpdate()
828
  {
@@ -853,14 +918,15 @@ class SucuriScanHook extends SucuriScanEvent
853
  }
854
 
855
  // Report updated themes at once.
856
- if (!empty($items_affected)) {
857
- $message_tpl = ( count($items_affected) > 1 )
858
- ? 'Themes updated: (multiple entries): %s'
859
- : 'Theme updated: %s';
860
- $message = sprintf(
861
- $message_tpl,
862
- @implode(',', $items_affected)
863
- );
 
864
  self::reportWarningEvent($message);
865
  self::notifyEvent('theme_updated', $message);
866
  }
@@ -870,7 +936,8 @@ class SucuriScanHook extends SucuriScanEvent
870
  /**
871
  * Send an alert notifying that a user account was deleted.
872
  *
873
- * @param int $id The identifier of the user account deleted.
 
874
  */
875
  public static function hookUserDelete($id = 0)
876
  {
@@ -880,15 +947,17 @@ class SucuriScanHook extends SucuriScanEvent
880
  /**
881
  * Send an alert notifying that a new user account was created.
882
  *
883
- * @param int $id The identifier of the new user account created.
 
884
  */
885
  public static function hookUserRegister($id = 0)
886
  {
887
  $title = 'unknown';
888
  $email = 'user@domain.com';
889
  $roles = 'none';
 
890
 
891
- if ($data = get_userdata($id)) {
892
  $title = $data->user_login;
893
  $email = $data->user_email;
894
  $roles = @implode(', ', $data->roles);
@@ -907,6 +976,8 @@ class SucuriScanHook extends SucuriScanEvent
907
 
908
  /**
909
  * Detects when a widget is added.
 
 
910
  */
911
  public static function hookWidgetAdd()
912
  {
@@ -915,6 +986,8 @@ class SucuriScanHook extends SucuriScanEvent
915
 
916
  /**
917
  * Detects when a widget is added.
 
 
918
  */
919
  private static function hookWidgetChanges()
920
  {
@@ -950,6 +1023,8 @@ class SucuriScanHook extends SucuriScanEvent
950
 
951
  /**
952
  * Detects when a widget is deleted.
 
 
953
  */
954
  public static function hookWidgetDelete()
955
  {
3
  /**
4
  * Code related to the hook.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
28
  * The term hooking covers a range of techniques used to alter or augment the
29
  * behavior of an operating system, of applications, or of other software
30
  * components by intercepting method calls or messages or events passed
31
+ * between software components. Code that handles such intercepted function
32
+ * methods, events or messages is called a "hook".
33
  *
34
  * Hooking is used for many purposes, including debugging and extending
35
  * functionality. Examples might include intercepting keyboard or mouse event
36
  * messages before they reach an application, or intercepting operating system
37
  * calls in order to monitor behavior or modify the method of an application
38
  * or other component; it is also widely used in benchmarking programs.
39
+ *
40
+ * @category Library
41
+ * @package Sucuri
42
+ * @subpackage SucuriScanner
43
+ * @author Daniel Cid <dcid@sucuri.net>
44
+ * @copyright 2010-2017 Sucuri Inc.
45
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
46
+ * @link https://wordpress.org/plugins/sucuri-scanner
47
  */
48
  class SucuriScanHook extends SucuriScanEvent
49
  {
50
  /**
51
  * Send to Sucuri servers an alert notifying that an attachment was added to a post.
52
  *
53
+ * @param int $id The post identifier.
54
+ * @return void
55
  */
56
  public static function hookAttachmentAdd($id = 0)
57
  {
58
  $title = 'unknown';
59
  $mime_type = 'unknown';
60
+ $data = get_post($id);
61
 
62
+ if ($data) {
63
  $id = $data->ID;
64
  $title = $data->post_title;
65
  $mime_type = $data->post_mime_type;
73
  /**
74
  * Send an alert notifying that a category was created.
75
  *
76
+ * @param int $id The identifier of the category created.
77
+ * @return void
78
  */
79
  public static function hookCategoryCreate($id = 0)
80
  {
85
  self::notifyEvent('post_publication', $message);
86
  }
87
 
 
 
88
  /**
89
  * Detects when the core files are updated.
90
+ *
91
+ * @return void
92
  */
93
  public static function hookCoreUpdate()
94
  {
106
  /**
107
  * Send an alert notifying that a new link was added to the bookmarks.
108
  *
109
+ * @param int $id Identifier of the new link created;
110
+ * @return void
111
  */
112
  public static function hookLinkAdd($id = 0)
113
  {
114
  $title = 'unknown';
115
  $target = '_none';
116
  $url = 'undefined/url';
117
+ $data = get_bookmark($id);
118
 
119
+ if ($data) {
120
  $title = $data->link_name;
121
  $target = $data->link_target;
122
  $url = $data->link_url;
136
  /**
137
  * Send an alert notifying that a new link was added to the bookmarks.
138
  *
139
+ * @param int $id Identifier of the new link created;
140
+ * @return void
141
  */
142
  public static function hookLinkEdit($id = 0)
143
  {
144
  $title = 'unknown';
145
  $target = '_none';
146
  $url = 'undefined/url';
147
+ $data = get_bookmark($id);
148
 
149
+ if ($data) {
150
  $title = $data->link_name;
151
  $target = $data->link_target;
152
  $url = $data->link_url;
167
  * Send an alert notifying that an attempt to login into the
168
  * administration panel failed.
169
  *
170
+ * @param string $title The name of the user account involved in the transaction.
171
+ * @return void
172
  */
173
  public static function hookLoginFailure($title = '')
174
  {
189
  self::notifyEvent('failed_login', $message);
190
 
191
  /* report brute-force attack if necessary */
192
+ $logins = sucuriscan_get_failed_logins();
193
+
194
+ if (is_array($logins) && !empty($logins)) {
195
  $max_time = 3600; /* report logins in the last hour */
196
  $maximum = SucuriScanOption::getOption(':maximum_failed_logins');
197
 
218
  * a brute-force attack (if it exists) because the time passed
219
  * between the first and last login attempt is big enough to
220
  * mitigate the attack.
 
 
 
 
221
  */
222
  sucuriscan_reset_failed_logins();
 
223
  }
224
  }
225
  }
226
 
227
  /**
228
  * Detects usage of the password reset form.
229
+ *
230
+ * @return void
231
  */
232
  public static function hookLoginFormResetpass()
233
  {
241
  * Send an alert notifying that an attempt to login into the
242
  * administration panel was successful.
243
  *
244
+ * @param string $title User account involved in the transaction.
245
+ * @return void
246
  */
247
  public static function hookLoginSuccess($title = '')
248
  {
259
  * will compare the value sent with the form with the value in the database
260
  * and if there are differences will send an email alert notifying the admin
261
  * about the changes.
262
+ *
263
+ * @return void
264
  */
265
  public static function hookOptionsManagement()
266
  {
290
  }
291
 
292
  /* identify the origin of the request */
293
+ $option_page = isset($_POST['option_page']) ? $_POST['option_page'] : 'options';
 
294
  $page_referer = 'Common';
295
 
296
  switch ($option_page) {
310
 
311
  if ($options_changed_count) {
312
  $message = $page_referer . ' settings changed';
313
+ self::reportErrorEvent(
314
+ sprintf(
315
+ '%s: (multiple entries): %s',
316
+ $message,
317
+ rtrim($options_changed_simple, ',')
318
+ )
319
+ );
320
  self::notifyEvent('settings_updated', $message . "<br>\n" . $options_changed_str);
321
  }
322
  }
325
  /**
326
  * Sends an alert with information about a plugin that has been activated.
327
  *
328
+ * @param string $plugin Name of the plugin.
329
+ * @param string $network_activation Whether the activation was global or not.
330
+ * @return void
331
  */
332
  public static function hookPluginActivate($plugin = '', $network_activation = '')
333
  {
343
  * able to detect a deactivation if the plugin has been deleted via FTP or
344
  * SSH or any file manager available in the hosting panel.
345
  *
346
+ * @param string $action Activated or deactivated.
347
+ * @param string $plugin Short name of the plugin file.
348
+ * @param string $network Whether the action is global or not.
349
+ * @return void
350
  */
351
+ private static function hookPluginChanges($action, $plugin = '', $network = '')
352
  {
353
  $filename = WP_PLUGIN_DIR . '/' . $plugin;
354
 
370
  }
371
 
372
  $message = sprintf(
373
+ 'Plugin %s: %s (v%s; %s%s)',
374
  $action, /* activated or deactivated */
375
  self::escape($info['Name']),
376
  self::escape($info['Version']),
377
+ self::escape($plugin),
378
+ ($network ? '; network' : '')
379
  );
380
  self::reportWarningEvent($message);
381
  self::notifyEvent('plugin_' . $action, $message);
384
  /**
385
  * Sends an alert with information about a plugin that has been deactivated.
386
  *
387
+ * @param string $plugin Name of the plugin.
388
+ * @param string $network_activation Whether the deactivation was global or not.
389
+ * @return void
390
  */
391
  public static function hookPluginDeactivate($plugin = '', $network_activation = '')
392
  {
395
 
396
  /**
397
  * Detects when a plugin is deleted.
398
+ *
399
+ * @return void
400
  */
401
  public static function hookPluginDelete()
402
  {
431
 
432
  // Report deleted plugins at once.
433
  if (!empty($items_affected)) {
434
+ if (count($items_affected) > 1) {
435
+ $message = 'Plugins deleted: (multiple entries):';
436
+ } else {
437
+ $message = 'Plugin deleted:';
438
+ }
439
+
440
+ $message .= "\x20" . @implode(',', $items_affected);
441
+
442
  self::reportWarningEvent($message);
443
  self::notifyEvent('plugin_deleted', $message);
444
  }
447
 
448
  /**
449
  * Detects when the plugin editor is used.
450
+ *
451
+ * @return void
452
  */
453
  public static function hookPluginEditor()
454
  {
468
 
469
  /**
470
  * Detects when a plugin is uploaded or installed.
471
+ *
472
+ * @return void
473
  */
474
  public static function hookPluginInstall()
475
  {
492
 
493
  /**
494
  * Detects when a plugin is updated or upgraded.
495
+ *
496
+ * @return void
497
  */
498
  public static function hookPluginUpdate()
499
  {
500
  // Plugin update request.
501
  $plugin_update_actions = '(upgrade-plugin|do-plugin-upgrade|update-selected)';
502
 
503
+ if (!current_user_can('update_plugins')) {
504
+ return;
505
+ }
506
+
507
+ if (SucuriScanRequest::getOrPost('action', $plugin_update_actions)
508
+ || SucuriScanRequest::getOrPost('action2', $plugin_update_actions)
509
  ) {
510
  $plugin_list = array();
511
  $items_affected = array();
538
 
539
  // Report updated plugins at once.
540
  if (!empty($items_affected)) {
541
+ if (count($items_affected) > 1) {
542
+ $message = 'Plugins updated: (multiple entries):';
543
+ } else {
544
+ $message = 'Plugin updated:';
545
+ }
546
+
547
+ $message .= "\x20" . @implode(',', $items_affected);
548
+
549
  self::reportWarningEvent($message);
550
  self::notifyEvent('plugin_updated', $message);
551
  }
565
  * this informaiton to send it to the API. We will delete the temporary data
566
  * after the operation has succeeded.
567
  *
568
+ * @param int $id The identifier of the post deleted.
569
+ * @return void
570
  */
571
  public static function hookPostBeforeDelete($id = 0)
572
  {
573
+ $data = get_post($id);
 
 
574
 
575
+ if (!$data) {
576
+ return;
 
 
 
 
 
 
 
 
 
577
  }
578
+
579
+ $out = array(); /* data to cache */
580
+ $cache = new SucuriScanCache('hookdata');
581
+
582
+ $out['id'] = $data->ID;
583
+ $out['author'] = $data->post_author;
584
+ $out['type'] = $data->post_type;
585
+ $out['status'] = $data->post_status;
586
+ $out['inserted'] = $data->post_date;
587
+ $out['modified'] = $data->post_modified;
588
+ $out['guid'] = $data->guid;
589
+ $out['title'] = empty($data->post_title) ? '(empty)' : $data->post_title;
590
+
591
+ $cache->add('post_' . $id, $out);
592
  }
593
 
594
  /**
595
  * Send an alert notifying that a post was deleted.
596
  *
597
+ * @param int $id The identifier of the post deleted.
598
+ * @return void
599
  */
600
  public static function hookPostDelete($id = 0)
601
  {
616
  /**
617
  * Sends an alert for transitions between post statuses.
618
  *
619
+ * @param string $new New post status.
620
+ * @param string $old Old post status.
621
+ * @param mixed $post Post data.
622
+ * @return void
623
  */
624
  public static function hookPostStatus($new = '', $old = '', $post = null)
625
  {
689
  /**
690
  * Send an alert notifying that a post was moved to the trash.
691
  *
692
+ * @param int $id The identifier of the trashed post.
693
+ * @return void
694
  */
695
  public static function hookPostTrash($id = 0)
696
  {
697
  $title = 'Unknown';
698
  $status = 'none';
699
+ $data = get_post($id);
700
 
701
+ if ($data) {
702
  $title = $data->post_title;
703
  $status = $data->post_status;
704
  }
715
  /**
716
  * Send an alert notifying that a post or page is created or updated.
717
  *
718
+ * @param int $id The identifier of the post or page published.
719
+ * @return void
720
  */
721
  private static function hookPublish($id = 0)
722
  {
723
  $title = 'Unknown';
724
  $p_type = 'Publication';
725
  $action = 'published';
726
+ $data = get_post($id);
727
 
728
+ if ($data) {
729
  $title = $data->post_title;
730
  $p_type = ucwords($data->post_type);
731
  $action = 'updated';
735
  $action = 'created';
736
  }
737
 
738
+ SucuriScanFirewall::clearCacheHook();
739
  }
740
 
741
  $message = sprintf(
752
  /**
753
  * Detects when a page is created or updated.
754
  *
755
+ * @param int $id The identifier of the post or page published.
756
+ * @return void
757
  */
758
  public static function hookPublishPage($id = 0)
759
  {
763
  /**
764
  * Detects when a post is created or updated via email.
765
  *
766
+ * @param int $id The identifier of the post or page published.
767
+ * @return void
768
  */
769
  public static function hookPublishPhone($id = 0)
770
  {
774
  /**
775
  * Detects when a post is created or updated.
776
  *
777
+ * @param int $id The identifier of the post or page published.
778
+ * @return void
779
  */
780
  public static function hookPublishPost($id = 0)
781
  {
785
  /**
786
  * Detects when a post is created or updated via XML-RPC.
787
  *
788
+ * @param int $id The identifier of the post or page published.
789
+ * @return void
790
  */
791
  public static function hookPublishPostXMLRPC($id = 0)
792
  {
797
  * Send an alert notifying that an attempt to retrieve the password
798
  * of an user account was tried.
799
  *
800
+ * @param string $title The name of the user account involved in the trasaction.
801
+ * @return void
802
  */
803
  public static function hookRetrievePassword($title = '')
804
  {
809
 
810
  /**
811
  * Detects when a theme is deleted.
812
+ *
813
+ * @return void
814
  */
815
  public static function hookThemeDelete()
816
  {
830
 
831
  /**
832
  * Detects when the theme editor is used.
833
+ *
834
+ * @return void
835
  */
836
  public static function hookThemeEditor()
837
  {
852
 
853
  /**
854
  * Detects when a theme is installed.
855
+ *
856
+ * @return void
857
  */
858
  public static function hookThemeInstall()
859
  {
873
  /**
874
  * Send an alert notifying that the theme of the site was changed.
875
  *
876
+ * @param string $title The name of the new theme selected to used through out the site.
877
+ * @return void
878
  */
879
  public static function hookThemeSwitch($title = '')
880
  {
886
 
887
  /**
888
  * Detects when a theme is automatically or manually updated.
889
+ *
890
+ * @return void
891
  */
892
  public static function hookThemeUpdate()
893
  {
918
  }
919
 
920
  // Report updated themes at once.
921
+ if (is_array($items_affected) && !empty($items_affected)) {
922
+ if (count($items_affected) > 1) {
923
+ $message = 'Themes updated: (multiple entries):';
924
+ } else {
925
+ $message = 'Theme updated:';
926
+ }
927
+
928
+ $message .= "\x20" . implode(',', $items_affected);
929
+
930
  self::reportWarningEvent($message);
931
  self::notifyEvent('theme_updated', $message);
932
  }
936
  /**
937
  * Send an alert notifying that a user account was deleted.
938
  *
939
+ * @param int $id The identifier of the user account deleted.
940
+ * @return void
941
  */
942
  public static function hookUserDelete($id = 0)
943
  {
947
  /**
948
  * Send an alert notifying that a new user account was created.
949
  *
950
+ * @param int $id The identifier of the new user account created.
951
+ * @return void
952
  */
953
  public static function hookUserRegister($id = 0)
954
  {
955
  $title = 'unknown';
956
  $email = 'user@domain.com';
957
  $roles = 'none';
958
+ $data = get_userdata($id);
959
 
960
+ if ($data) {
961
  $title = $data->user_login;
962
  $email = $data->user_email;
963
  $roles = @implode(', ', $data->roles);
976
 
977
  /**
978
  * Detects when a widget is added.
979
+ *
980
+ * @return void
981
  */
982
  public static function hookWidgetAdd()
983
  {
986
 
987
  /**
988
  * Detects when a widget is added.
989
+ *
990
+ * @return void
991
  */
992
  private static function hookWidgetChanges()
993
  {
1023
 
1024
  /**
1025
  * Detects when a widget is deleted.
1026
+ *
1027
+ * @return void
1028
  */
1029
  public static function hookWidgetDelete()
1030
  {
src/installer-skin.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the installer-skin.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage installer-skin.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -18,8 +24,8 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
18
 
19
  if (class_exists('SucuriScanInterface') && class_exists('SucuriScanRequest')) {
20
  if (SucuriScanRequest::post('form_action') == 'reset_plugin') {
21
- include_once(ABSPATH . '/wp-admin/includes/class-wp-upgrader.php');
22
- include_once(ABSPATH . '/wp-admin/includes/plugin-install.php');
23
 
24
  /**
25
  * Plugin Installer Skin for WordPress Plugin Installer.
@@ -30,16 +36,23 @@ if (class_exists('SucuriScanInterface') && class_exists('SucuriScanRequest')) {
30
  * process immediately and we will not be able to disregard these logs
31
  * after the operation has finished.
32
  *
33
- * @see WP_Upgrader_Skin
34
- *
35
  * @codeCoverageIgnore
 
 
 
 
 
 
 
 
36
  */
37
  class SucuriScanPluginInstallerSkin extends Plugin_Installer_Skin
38
  {
39
  /**
40
  * Reports the progress of the plugin installation.
41
  *
42
- * @param string $string Message to send to the buffer.
 
43
  */
44
  public function feedback($string = '')
45
  {
3
  /**
4
  * Code related to the installer-skin.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
24
 
25
  if (class_exists('SucuriScanInterface') && class_exists('SucuriScanRequest')) {
26
  if (SucuriScanRequest::post('form_action') == 'reset_plugin') {
27
+ include_once ABSPATH . '/wp-admin/includes/class-wp-upgrader.php';
28
+ include_once ABSPATH . '/wp-admin/includes/plugin-install.php';
29
 
30
  /**
31
  * Plugin Installer Skin for WordPress Plugin Installer.
36
  * process immediately and we will not be able to disregard these logs
37
  * after the operation has finished.
38
  *
 
 
39
  * @codeCoverageIgnore
40
+ *
41
+ * @category Library
42
+ * @package Sucuri
43
+ * @subpackage SucuriScanner
44
+ * @author Daniel Cid <dcid@sucuri.net>
45
+ * @copyright 2010-2017 Sucuri Inc.
46
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
47
+ * @link https://wordpress.org/plugins/sucuri-scanner
48
  */
49
  class SucuriScanPluginInstallerSkin extends Plugin_Installer_Skin
50
  {
51
  /**
52
  * Reports the progress of the plugin installation.
53
  *
54
+ * @param string $string Message to send to the buffer.
55
+ * @return void
56
  */
57
  public function feedback($string = '')
58
  {
src/integrity.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the integrity.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage integrity.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -23,6 +29,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
23
  * in the root directory, wp-admin and wp-includes will be compared against the
24
  * files distributed with the current WordPress version; all files with
25
  * inconsistencies will be listed here.
 
 
 
 
 
 
 
 
26
  */
27
  class SucuriScanIntegrity
28
  {
@@ -64,6 +78,8 @@ class SucuriScanIntegrity
64
  * output it means that XDebug cannot cover the next line, leaving a report
65
  * with a missing line in the coverage. Since the test case takes care of
66
  * the functionality of this code we will assume that it is fully covered.
 
 
67
  */
68
  public static function ajaxIntegrity()
69
  {
@@ -80,6 +96,8 @@ class SucuriScanIntegrity
80
  * Process the HTTP requests sent by the form submissions originated in the
81
  * integrity page, all forms must have a nonce field that will be checked
82
  * against the one generated in the template render function.
 
 
83
  */
84
  private static function pageIntegritySubmission()
85
  {
@@ -92,12 +110,12 @@ class SucuriScanIntegrity
92
 
93
  /* skip if the user didn't confirm the operation */
94
  if (SucuriScanRequest::post(':process_form') != 1) {
95
- return SucuriScanInterface::error(__('ConfirmOperation', SUCURISCAN_TEXTDOMAIN));
96
  }
97
 
98
  /* skip if the requested action is not currently supported */
99
  if ($action !== 'fixed' && $action !== 'delete' && $action !== 'restore') {
100
- return SucuriScanInterface::error(__('NonSupportedAction', SUCURISCAN_TEXTDOMAIN));
101
  }
102
 
103
  /* process the HTTP request */
@@ -114,7 +132,7 @@ class SucuriScanIntegrity
114
 
115
  /* skip if no files were selected */
116
  if (!$core_files) {
117
- return SucuriScanInterface::error(__('NothingSelected', SUCURISCAN_TEXTDOMAIN));
118
  }
119
 
120
  /* process files until the maximum execution time is reached */
@@ -143,11 +161,7 @@ class SucuriScanIntegrity
143
 
144
  $full_path = ABSPATH . '/' . $file_path;
145
 
146
- if ($action === 'fixed' && (
147
- $status_type === 'added'
148
- || $status_type === 'removed'
149
- || $status_type === 'modified'
150
- )) {
151
  $cache_key = md5($file_path);
152
  $cache_value = array(
153
  'file_path' => $file_path,
@@ -162,11 +176,10 @@ class SucuriScanIntegrity
162
  continue;
163
  }
164
 
165
- if ($action === 'restore' && (
166
- $status_type === 'removed'
167
- || $status_type === 'modified'
168
- )) {
169
- if ($content = SucuriScanAPI::getOriginalCoreFile($file_path)) {
170
  $basedir = dirname($full_path);
171
 
172
  if (!file_exists($basedir)) {
@@ -192,14 +205,9 @@ class SucuriScanIntegrity
192
 
193
  /* report files affected as a single event */
194
  if (!empty($files_affected)) {
195
- $message_tpl = (count($files_affected) > 1)
196
- ? '%s: (multiple entries): %s'
197
- : '%s: %s';
198
- $message = sprintf(
199
- $message_tpl,
200
- $action_titles[$action],
201
- @implode(',', $files_affected)
202
- );
203
 
204
  switch ($action) {
205
  case 'restore':
@@ -217,22 +225,26 @@ class SucuriScanIntegrity
217
  }
218
 
219
  if ($displayTimeoutAlert) {
220
- SucuriScanInterface::error(__('MaxExecutionTimeAlert', SUCURISCAN_TEXTDOMAIN));
221
  }
222
 
223
  if ($files_processed != $files_selected) {
224
- return SucuriScanInterface::error(sprintf(
225
- __('SomeItemsProcessed', SUCURISCAN_TEXTDOMAIN),
226
- $files_processed,
227
- $files_selected
228
- ));
 
 
229
  }
230
 
231
- return SucuriScanInterface::info(sprintf(
232
- __('AllItemsProcessed', SUCURISCAN_TEXTDOMAIN),
233
- $files_processed,
234
- $files_selected
235
- ));
 
 
236
  }
237
 
238
  /**
@@ -247,8 +259,8 @@ class SucuriScanIntegrity
247
  *
248
  * The website owner will receive an email alert with this information.
249
  *
250
- * @param bool $send_email Send an email alert to the admins.
251
- * @return string|bool HTML with information about the integrity.
252
  */
253
  public static function getIntegrityStatus($send_email = false)
254
  {
@@ -306,11 +318,11 @@ class SucuriScanIntegrity
306
  $visibility = 'visible';
307
 
308
  if ($list_type === 'added') {
309
- $error = __('ErrorIntegrityAdded', SUCURISCAN_TEXTDOMAIN);
310
  } elseif ($list_type === 'modified') {
311
- $error = __('ErrorIntegrityModified', SUCURISCAN_TEXTDOMAIN);
312
  } elseif ($list_type === 'removed') {
313
- $error = __('ErrorIntegrityRemoved', SUCURISCAN_TEXTDOMAIN);
314
  }
315
  }
316
 
@@ -319,18 +331,22 @@ class SucuriScanIntegrity
319
  $file_size_human = SucuriScan::humanFileSize($file_size);
320
  }
321
 
 
 
322
  // Generate the HTML code from the snippet template for this file.
323
- $params['Integrity.List'] .=
324
- SucuriScanTemplate::getSnippet('integrity-incorrect', array(
325
- 'Integrity.StatusType' => $list_type,
326
- 'Integrity.FilePath' => $file_path,
327
- 'Integrity.FileSize' => $file_size,
328
- 'Integrity.FileSizeHuman' => $file_size_human,
329
- 'Integrity.FileSizeNumber' => number_format($file_size),
330
- 'Integrity.ModifiedAt' => SucuriScan::datetime($file_info['modified_at']),
331
- 'Integrity.ErrorVisibility' => $visibility,
332
- 'Integrity.ErrorMessage' => $error,
333
- ));
 
 
334
  $affected_files++;
335
  $counter++;
336
  }
@@ -344,12 +360,14 @@ class SucuriScanIntegrity
344
  }
345
 
346
  if ($send_email === true) {
347
- return ($affected_files > 0)
348
- ? SucuriScanEvent::notifyEvent(
349
- 'scan_checksums', /* send alert with a list of affected files */
350
- SucuriScanTemplate::getSection('integrity-notification', $params)
351
- )
352
- : false;
 
 
353
  }
354
 
355
  ob_start();
@@ -359,9 +377,8 @@ class SucuriScanIntegrity
359
 
360
  $params['Integrity.DiffUtility'] = SucuriScanIntegrity::diffUtility();
361
 
362
- return ($affected_files === 0)
363
- ? SucuriScanTemplate::getSection('integrity-correct', $params)
364
- : SucuriScanTemplate::getSection('integrity-incorrect', $params);
365
  }
366
 
367
  /**
@@ -382,12 +399,15 @@ class SucuriScanIntegrity
382
 
383
  $params = array();
384
 
385
- $params['DiffUtility.Modal'] = SucuriScanTemplate::getModal('none', array(
386
- 'Title' => __('DiffUtility', SUCURISCAN_TEXTDOMAIN),
387
- 'Content' => '' /* empty */,
388
- 'Identifier' => 'diff-utility',
389
- 'Visibility' => 'hidden',
390
- ));
 
 
 
391
 
392
  return SucuriScanTemplate::getSection('integrity-diff-utility', $params);
393
  }
@@ -406,6 +426,8 @@ class SucuriScanIntegrity
406
  * output it means that XDebug cannot cover the next line, leaving a report
407
  * with a missing line in the coverage. Since the test case takes care of
408
  * the functionality of this code we will assume that it is fully covered.
 
 
409
  */
410
  public static function ajaxIntegrityDiffUtility()
411
  {
@@ -415,7 +437,7 @@ class SucuriScanIntegrity
415
 
416
  ob_start();
417
  $filename = SucuriScanRequest::post('filepath');
418
- print(SucuriScanCommand::diffHTML($filename));
419
  $response = ob_get_clean();
420
 
421
  wp_send_json($response, 200);
@@ -425,9 +447,9 @@ class SucuriScanIntegrity
425
  * Retrieve a list of md5sum and last modification time of all the files in the
426
  * folder specified. This is a recursive function.
427
  *
428
- * @param string $dir The base path where the scanning will start.
429
- * @param bool $recursive Either TRUE or FALSE if the scan should be performed recursively.
430
- * @return array List of arrays containing the md5sum and last modification time of the files found.
431
  */
432
  private static function integrityTree($dir = './', $recursive = false)
433
  {
@@ -457,10 +479,12 @@ class SucuriScanIntegrity
457
  */
458
  private static function checkIntegrityIntegrity()
459
  {
 
460
  $latest_hashes = SucuriScanAPI::getOfficialChecksums();
461
- $base_content_dir = defined('WP_CONTENT_DIR')
462
- ? basename(rtrim(WP_CONTENT_DIR, '/'))
463
- : '';
 
464
 
465
  // @codeCoverageIgnoreStart
466
  if (!$latest_hashes) {
@@ -568,13 +592,11 @@ class SucuriScanIntegrity
568
  /**
569
  * Ignore irrelevant files and directories from the integrity checking.
570
  *
571
- * @param string $path File path that will be compared.
572
- * @return bool True if the file should be ignored, false otherwise.
573
  */
574
  private static function ignoreIntegrityFilepath($path = '')
575
  {
576
- global $wp_local_package;
577
-
578
  $irrelevant = array(
579
  'php.ini',
580
  '.htaccess',
@@ -621,7 +643,7 @@ class SucuriScanIntegrity
621
  * specifying the language that will be used in the admin panel, site
622
  * options, and emails.
623
  */
624
- if (isset($wp_local_package) && $wp_local_package != 'en_US') {
625
  $irrelevant[] = 'wp-includes/version.php';
626
  $irrelevant[] = 'wp-config-sample.php';
627
  }
3
  /**
4
  * Code related to the integrity.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
29
  * in the root directory, wp-admin and wp-includes will be compared against the
30
  * files distributed with the current WordPress version; all files with
31
  * inconsistencies will be listed here.
32
+ *
33
+ * @category Library
34
+ * @package Sucuri
35
+ * @subpackage SucuriScanner
36
+ * @author Daniel Cid <dcid@sucuri.net>
37
+ * @copyright 2010-2017 Sucuri Inc.
38
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
39
+ * @link https://wordpress.org/plugins/sucuri-scanner
40
  */
41
  class SucuriScanIntegrity
42
  {
78
  * output it means that XDebug cannot cover the next line, leaving a report
79
  * with a missing line in the coverage. Since the test case takes care of
80
  * the functionality of this code we will assume that it is fully covered.
81
+ *
82
+ * @return void
83
  */
84
  public static function ajaxIntegrity()
85
  {
96
  * Process the HTTP requests sent by the form submissions originated in the
97
  * integrity page, all forms must have a nonce field that will be checked
98
  * against the one generated in the template render function.
99
+ *
100
+ * @return void
101
  */
102
  private static function pageIntegritySubmission()
103
  {
110
 
111
  /* skip if the user didn't confirm the operation */
112
  if (SucuriScanRequest::post(':process_form') != 1) {
113
+ return SucuriScanInterface::error('You need to confirm that you understand the risk of this operation.');
114
  }
115
 
116
  /* skip if the requested action is not currently supported */
117
  if ($action !== 'fixed' && $action !== 'delete' && $action !== 'restore') {
118
+ return SucuriScanInterface::error('Requested action is not supported.');
119
  }
120
 
121
  /* process the HTTP request */
132
 
133
  /* skip if no files were selected */
134
  if (!$core_files) {
135
+ return SucuriScanInterface::error('Nothing was selected from the list.');
136
  }
137
 
138
  /* process files until the maximum execution time is reached */
161
 
162
  $full_path = ABSPATH . '/' . $file_path;
163
 
164
+ if ($action === 'fixed' && ($status_type === 'added' || $status_type === 'removed' || $status_type === 'modified')) {
 
 
 
 
165
  $cache_key = md5($file_path);
166
  $cache_value = array(
167
  'file_path' => $file_path,
176
  continue;
177
  }
178
 
179
+ if ($action === 'restore' && ($status_type === 'removed' || $status_type === 'modified')) {
180
+ $content = SucuriScanAPI::getOriginalCoreFile($file_path);
181
+
182
+ if ($content) {
 
183
  $basedir = dirname($full_path);
184
 
185
  if (!file_exists($basedir)) {
205
 
206
  /* report files affected as a single event */
207
  if (!empty($files_affected)) {
208
+ $message = $action_titles[$action] . ':';
209
+ $message .= count($files_affected) > 1 ? "\x20(multiple entries):\x20" : '';
210
+ $message .= @implode(',', $files_affected);
 
 
 
 
 
211
 
212
  switch ($action) {
213
  case 'restore':
225
  }
226
 
227
  if ($displayTimeoutAlert) {
228
+ SucuriScanInterface::error('Server is not fast enough to process this action; maximum execution time reached');
229
  }
230
 
231
  if ($files_processed != $files_selected) {
232
+ return SucuriScanInterface::error(
233
+ sprintf(
234
+ 'Only <b>%d</b> out of <b>%d</b> files were processed.',
235
+ $files_processed,
236
+ $files_selected
237
+ )
238
+ );
239
  }
240
 
241
+ return SucuriScanInterface::info(
242
+ sprintf(
243
+ '<b>%d</b> out of <b>%d</b> files were successfully processed.',
244
+ $files_processed,
245
+ $files_selected
246
+ )
247
+ );
248
  }
249
 
250
  /**
259
  *
260
  * The website owner will receive an email alert with this information.
261
  *
262
+ * @param bool $send_email Send an email alert to the admins.
263
+ * @return string|bool HTML with information about the integrity.
264
  */
265
  public static function getIntegrityStatus($send_email = false)
266
  {
318
  $visibility = 'visible';
319
 
320
  if ($list_type === 'added') {
321
+ $error = 'The plugin has no permission to delete this file because it was created by a different system user who has more privileges than your account. Please use FTP to delete it.';
322
  } elseif ($list_type === 'modified') {
323
+ $error = 'The plugin has no permission to restore this file because it was modified by a different system user who has more privileges than your account. Please use FTP to restore it.';
324
  } elseif ($list_type === 'removed') {
325
+ $error = 'The plugin has no permission to restore this file because its directory is owned by a different system user who has more privileges than your account. Please use FTP to restore it.';
326
  }
327
  }
328
 
331
  $file_size_human = SucuriScan::humanFileSize($file_size);
332
  }
333
 
334
+ $modified_at = $file_info['modified_at'] ? SucuriScan::datetime($file_info['modified_at']) : '';
335
+
336
  // Generate the HTML code from the snippet template for this file.
337
+ $params['Integrity.List'] .= SucuriScanTemplate::getSnippet(
338
+ 'integrity-incorrect',
339
+ array(
340
+ 'Integrity.StatusType' => $list_type,
341
+ 'Integrity.FilePath' => $file_path,
342
+ 'Integrity.FileSize' => $file_size,
343
+ 'Integrity.FileSizeHuman' => $file_size_human,
344
+ 'Integrity.FileSizeNumber' => number_format($file_size),
345
+ 'Integrity.ModifiedAt' => $modified_at,
346
+ 'Integrity.ErrorVisibility' => $visibility,
347
+ 'Integrity.ErrorMessage' => $error,
348
+ )
349
+ );
350
  $affected_files++;
351
  $counter++;
352
  }
360
  }
361
 
362
  if ($send_email === true) {
363
+ if ($affected_files > 0) {
364
+ return SucuriScanEvent::notifyEvent(
365
+ 'scan_checksums', /* send alert with a list of affected files */
366
+ SucuriScanTemplate::getSection('integrity-notification', $params)
367
+ );
368
+ }
369
+
370
+ return false;
371
  }
372
 
373
  ob_start();
377
 
378
  $params['Integrity.DiffUtility'] = SucuriScanIntegrity::diffUtility();
379
 
380
+ $template = ($affected_files === 0) ? 'correct' : 'incorrect';
381
+ return SucuriScanTemplate::getSection('integrity-' . $template, $params);
 
382
  }
383
 
384
  /**
399
 
400
  $params = array();
401
 
402
+ $params['DiffUtility.Modal'] = SucuriScanTemplate::getModal(
403
+ 'none',
404
+ array(
405
+ 'Title' => 'WordPress Integrity Diff Utility',
406
+ 'Content' => '' /* empty */,
407
+ 'Identifier' => 'diff-utility',
408
+ 'Visibility' => 'hidden',
409
+ )
410
+ );
411
 
412
  return SucuriScanTemplate::getSection('integrity-diff-utility', $params);
413
  }
426
  * output it means that XDebug cannot cover the next line, leaving a report
427
  * with a missing line in the coverage. Since the test case takes care of
428
  * the functionality of this code we will assume that it is fully covered.
429
+ *
430
+ * @return void
431
  */
432
  public static function ajaxIntegrityDiffUtility()
433
  {
437
 
438
  ob_start();
439
  $filename = SucuriScanRequest::post('filepath');
440
+ echo SucuriScanCommand::diffHTML($filename);
441
  $response = ob_get_clean();
442
 
443
  wp_send_json($response, 200);
447
  * Retrieve a list of md5sum and last modification time of all the files in the
448
  * folder specified. This is a recursive function.
449
  *
450
+ * @param string $dir The base path where the scanning will start.
451
+ * @param bool $recursive Either TRUE or FALSE if the scan should be performed recursively.
452
+ * @return array List of arrays containing the md5sum and last modification time of the files found.
453
  */
454
  private static function integrityTree($dir = './', $recursive = false)
455
  {
479
  */
480
  private static function checkIntegrityIntegrity()
481
  {
482
+ $base_content_dir = '';
483
  $latest_hashes = SucuriScanAPI::getOfficialChecksums();
484
+
485
+ if (defined('WP_CONTENT_DIR')) {
486
+ $base_content_dir = basename(rtrim(WP_CONTENT_DIR, '/'));
487
+ }
488
 
489
  // @codeCoverageIgnoreStart
490
  if (!$latest_hashes) {
592
  /**
593
  * Ignore irrelevant files and directories from the integrity checking.
594
  *
595
+ * @param string $path File path that will be compared.
596
+ * @return bool True if the file should be ignored, false otherwise.
597
  */
598
  private static function ignoreIntegrityFilepath($path = '')
599
  {
 
 
600
  $irrelevant = array(
601
  'php.ini',
602
  '.htaccess',
643
  * specifying the language that will be used in the admin panel, site
644
  * options, and emails.
645
  */
646
+ if (@$GLOBALS['wp_local_package'] != 'en_US') {
647
  $irrelevant[] = 'wp-includes/version.php';
648
  $irrelevant[] = 'wp-config-sample.php';
649
  }
src/interface.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the interface.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage interface.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -22,18 +28,22 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
22
  * Define all the required variables, script, styles, and basic functions needed
23
  * when the site is loaded, not even the administrator panel but also the front
24
  * page, some bug-fixes will/are applied here for sites behind a proxy, and
25
- * sites with old versions of the premium plugin (that was deprecated at
26
- * July/2014).
 
 
 
 
 
 
 
27
  */
28
  class SucuriScanInterface
29
  {
30
  /**
31
  * Initialization code for the plugin.
32
  *
33
- * The initial variables and information needed by the plugin during the
34
- * execution of other functions will be generated. Things like the real IP
35
- * address of the client when it has been forwarded or it's behind an external
36
- * service like a Proxy.
37
  */
38
  public static function initialize()
39
  {
@@ -48,16 +58,16 @@ class SucuriScanInterface
48
  /**
49
  * Define which javascript and css files will be loaded in the header of the
50
  * plugin pages, only when the administrator panel is accessed.
 
 
51
  */
52
  public static function enqueueScripts()
53
  {
54
- $asset = substr(md5(microtime(true)), 0, 7);
55
-
56
  wp_register_style(
57
  'sucuriscan1',
58
  SUCURISCAN_URL . '/inc/css/styles.css',
59
  array(/* empty */),
60
- $asset
61
  );
62
  wp_enqueue_style('sucuriscan1');
63
 
@@ -65,7 +75,7 @@ class SucuriScanInterface
65
  'sucuriscan1',
66
  SUCURISCAN_URL . '/inc/js/scripts.js',
67
  array(/* empty */),
68
- $asset
69
  );
70
  wp_enqueue_script('sucuriscan1');
71
 
@@ -74,7 +84,7 @@ class SucuriScanInterface
74
  'sucuriscan3',
75
  SUCURISCAN_URL . '/inc/css/flags.min.css',
76
  array(/* empty */),
77
- $asset
78
  );
79
  wp_enqueue_style('sucuriscan3');
80
  }
@@ -85,6 +95,8 @@ class SucuriScanInterface
85
  * 1.6.0) all the functionality of the others will be merged here, this will
86
  * remove duplicated functionality, duplicated bugs and/or duplicated
87
  * maintenance reports allowing us to focus in one unique project.
 
 
88
  */
89
  public static function handleOldPlugins()
90
  {
@@ -121,6 +133,8 @@ class SucuriScanInterface
121
  /**
122
  * Create a folder in the WordPress upload directory where the plugin will
123
  * store all the temporal or dynamic information.
 
 
124
  */
125
  public static function createStorageFolder()
126
  {
@@ -156,6 +170,8 @@ class SucuriScanInterface
156
  * for the current user in session which usually needs to be granted admin
157
  * privileges to access the plugin's tools. It also checks if the required
158
  * SPL library is available and if the settings file is writable.
 
 
159
  */
160
  public static function startupChecks()
161
  {
@@ -165,16 +181,18 @@ class SucuriScanInterface
165
 
166
  if (!SucuriScanFileInfo::isSplAvailable()) {
167
  /* display a warning when system dependencies are not met */
168
- self::error(__('RequiresModernPHP', SUCURISCAN_TEXTDOMAIN));
169
  }
170
 
171
- if ($filename = SucuriScanOption::optionsFilePath()) {
172
- if (!is_writable($filename)) {
173
- self::error(sprintf(
174
- __('StorageNotWritable', SUCURISCAN_TEXTDOMAIN),
 
 
175
  $filename /* absolute path of the settings file */
176
- ));
177
- }
178
  }
179
  }
180
 
@@ -187,6 +205,8 @@ class SucuriScanInterface
187
  * will execute certain actions and/or display some messages.
188
  *
189
  * @codeCoverageIgnore
 
 
190
  */
191
  public static function noticeAfterUpdate()
192
  {
@@ -211,7 +231,7 @@ class SucuriScanInterface
211
  * the new code.
212
  */
213
  if (SucuriScanOption::isDisabled(':api_service')) {
214
- self::info(__('EnableAPIServiceAgain', SUCURISCAN_TEXTDOMAIN));
215
  }
216
 
217
  /**
@@ -224,19 +244,21 @@ class SucuriScanInterface
224
  *
225
  * @date Featured added at - May 01, 2017
226
  */
227
- self::info(__('NewsletterInvitation', SUCURISCAN_TEXTDOMAIN));
228
  }
229
 
230
  /**
231
  * Check whether a user has the permissions to see a page from the plugin.
232
  *
233
  * @codeCoverageIgnore
 
 
234
  */
235
  public static function checkPageVisibility()
236
  {
237
  if (!function_exists('current_user_can') || !current_user_can('manage_options')) {
238
  SucuriScan::throwException('Access denied; cannot manage options');
239
- wp_die(__('AccessDenied', SUCURISCAN_TEXTDOMAIN));
240
  }
241
  }
242
 
@@ -257,7 +279,17 @@ class SucuriScanInterface
257
 
258
  if (!$nonce_value || !wp_verify_nonce($nonce_value, $nonce_name)) {
259
  SucuriScan::throwException('Nonce is invalid');
260
- wp_die(__('NonceFailure', SUCURISCAN_TEXTDOMAIN));
 
 
 
 
 
 
 
 
 
 
261
  return false;
262
  }
263
  }
@@ -270,8 +302,9 @@ class SucuriScanInterface
270
  *
271
  * @codeCoverageIgnore
272
  *
273
- * @param string $type The type of alert, it can be either Updated or Error.
274
- * @param string $message The message that will be printed in the alert.
 
275
  */
276
  private static function adminNotice($type = 'updated', $message = '')
277
  {
@@ -300,18 +333,22 @@ class SucuriScanInterface
300
 
301
  SucuriScan::throwException($message, $type);
302
 
303
- echo SucuriScanTemplate::getSection('notification-admin', array(
304
- 'AlertType' => $type,
305
- 'AlertUnique' => rand(100, 999),
306
- 'AlertMessage' => $message,
307
- ));
 
 
 
308
  }
309
  }
310
 
311
  /**
312
  * Prints a HTML alert of type ERROR in the WordPress admin interface.
313
  *
314
- * @param string $msg The message that will be printed in the alert.
 
315
  */
316
  public static function error($msg = '')
317
  {
@@ -322,7 +359,8 @@ class SucuriScanInterface
322
  /**
323
  * Prints a HTML alert of type INFO in the WordPress admin interface.
324
  *
325
- * @param string $msg The message that will be printed in the alert.
 
326
  */
327
  public static function info($msg = '')
328
  {
3
  /**
4
  * Code related to the interface.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
28
  * Define all the required variables, script, styles, and basic functions needed
29
  * when the site is loaded, not even the administrator panel but also the front
30
  * page, some bug-fixes will/are applied here for sites behind a proxy, and
31
+ * sites with old versions of the premium plugin (deprecated on July, 2014).
32
+ *
33
+ * @category Library
34
+ * @package Sucuri
35
+ * @subpackage SucuriScanner
36
+ * @author Daniel Cid <dcid@sucuri.net>
37
+ * @copyright 2010-2017 Sucuri Inc.
38
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
39
+ * @link https://wordpress.org/plugins/sucuri-scanner
40
  */
41
  class SucuriScanInterface
42
  {
43
  /**
44
  * Initialization code for the plugin.
45
  *
46
+ * @return void
 
 
 
47
  */
48
  public static function initialize()
49
  {
58
  /**
59
  * Define which javascript and css files will be loaded in the header of the
60
  * plugin pages, only when the administrator panel is accessed.
61
+ *
62
+ * @return void
63
  */
64
  public static function enqueueScripts()
65
  {
 
 
66
  wp_register_style(
67
  'sucuriscan1',
68
  SUCURISCAN_URL . '/inc/css/styles.css',
69
  array(/* empty */),
70
+ '3eeb7af'
71
  );
72
  wp_enqueue_style('sucuriscan1');
73
 
75
  'sucuriscan1',
76
  SUCURISCAN_URL . '/inc/js/scripts.js',
77
  array(/* empty */),
78
+ '81f6bb4'
79
  );
80
  wp_enqueue_script('sucuriscan1');
81
 
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
  }
95
  * 1.6.0) all the functionality of the others will be merged here, this will
96
  * remove duplicated functionality, duplicated bugs and/or duplicated
97
  * maintenance reports allowing us to focus in one unique project.
98
+ *
99
+ * @return void
100
  */
101
  public static function handleOldPlugins()
102
  {
133
  /**
134
  * Create a folder in the WordPress upload directory where the plugin will
135
  * store all the temporal or dynamic information.
136
+ *
137
+ * @return void
138
  */
139
  public static function createStorageFolder()
140
  {
170
  * for the current user in session which usually needs to be granted admin
171
  * privileges to access the plugin's tools. It also checks if the required
172
  * SPL library is available and if the settings file is writable.
173
+ *
174
+ * @return void
175
  */
176
  public static function startupChecks()
177
  {
181
 
182
  if (!SucuriScanFileInfo::isSplAvailable()) {
183
  /* display a warning when system dependencies are not met */
184
+ self::error('The plugin requires PHP 5 >= 5.3.0 - OR - PHP 7');
185
  }
186
 
187
+ $filename = SucuriScanOption::optionsFilePath();
188
+
189
+ if (!is_writable($filename)) {
190
+ self::error(
191
+ sprintf(
192
+ 'Storage is not writable: <code>%s</code>',
193
  $filename /* absolute path of the settings file */
194
+ )
195
+ );
196
  }
197
  }
198
 
205
  * will execute certain actions and/or display some messages.
206
  *
207
  * @codeCoverageIgnore
208
+ *
209
+ * @return void
210
  */
211
  public static function noticeAfterUpdate()
212
  {
231
  * the new code.
232
  */
233
  if (SucuriScanOption::isDisabled(':api_service')) {
234
+ self::info('API service communication is disabled, if you just updated the plugin this might be a good opportunity to test this feature once again with the new code. Enable it again from the "API Service" panel located in the settings page.');
235
  }
236
 
237
  /**
244
  *
245
  * @date Featured added at - May 01, 2017
246
  */
247
+ self::info('Do you want to get vulnerability disclosures? Subscribe to our newsletter <a href="http://sucuri.hs-sites.com/subscribe-to-security" target="_blank" rel="noopener">here</a>');
248
  }
249
 
250
  /**
251
  * Check whether a user has the permissions to see a page from the plugin.
252
  *
253
  * @codeCoverageIgnore
254
+ *
255
+ * @return void
256
  */
257
  public static function checkPageVisibility()
258
  {
259
  if (!function_exists('current_user_can') || !current_user_can('manage_options')) {
260
  SucuriScan::throwException('Access denied; cannot manage options');
261
+ wp_die('Access denied by Sucuri Inc.');
262
  }
263
  }
264
 
279
 
280
  if (!$nonce_value || !wp_verify_nonce($nonce_value, $nonce_name)) {
281
  SucuriScan::throwException('Nonce is invalid');
282
+ self::error(
283
+ 'WordPress CSRF verification failed. The submitted form is'
284
+ . ' missing an important unique code that prevents automat'
285
+ . 'ed unwated access, go back and try again. If you did no'
286
+ . 't submit a form, this error message could be an indicat'
287
+ . 'ion of an incompatibility between this plugin and anoth'
288
+ . 'er add-on; one of them is inserting data into the globa'
289
+ . 'l POST variable when the HTTP request is coming via GET'
290
+ . '. Disable them one by one (while reloading this page) t'
291
+ . 'o find the culprit.'
292
+ );
293
  return false;
294
  }
295
  }
302
  *
303
  * @codeCoverageIgnore
304
  *
305
+ * @param string $type The type of alert, it can be either Updated or Error.
306
+ * @param string $message The message that will be printed in the alert.
307
+ * @return void
308
  */
309
  private static function adminNotice($type = 'updated', $message = '')
310
  {
333
 
334
  SucuriScan::throwException($message, $type);
335
 
336
+ echo SucuriScanTemplate::getSection(
337
+ 'notification-admin',
338
+ array(
339
+ 'AlertType' => $type,
340
+ 'AlertUnique' => rand(100, 999),
341
+ 'AlertMessage' => $message,
342
+ )
343
+ );
344
  }
345
  }
346
 
347
  /**
348
  * Prints a HTML alert of type ERROR in the WordPress admin interface.
349
  *
350
+ * @param string $msg The message that will be printed in the alert.
351
+ * @return void
352
  */
353
  public static function error($msg = '')
354
  {
359
  /**
360
  * Prints a HTML alert of type INFO in the WordPress admin interface.
361
  *
362
+ * @param string $msg The message that will be printed in the alert.
363
+ * @return void
364
  */
365
  public static function info($msg = '')
366
  {
src/lastlogins-blocked.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the lastlogins-blocked.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage lastlogins-blocked.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -28,6 +34,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
28
  * in the Firewall service and it also provides a better filtering mechanism for
29
  * any other suspicious login attempt. We will encourage people to leverage the
30
  * power of the Firewall.
 
 
 
 
 
 
 
 
31
  */
32
  class SucuriScanBlockedUsers extends SucuriScanLastLogins
33
  {
@@ -47,7 +61,7 @@ class SucuriScanBlockedUsers extends SucuriScanLastLogins
47
 
48
  if (is_array($unblockUsers) && !empty($unblockUsers)) {
49
  self::unblock($unblockUsers);
50
- SucuriScanInterface::info(__('AccountsWereUnblocked', SUCURISCAN_TEXTDOMAIN));
51
  }
52
  }
53
 
@@ -56,13 +70,15 @@ class SucuriScanBlockedUsers extends SucuriScanLastLogins
56
 
57
  if (is_array($blocked) && !empty($blocked)) {
58
  foreach ($blocked as $data) {
59
- $output['BlockedUsers.List'] .=
60
- SucuriScanTemplate::getSnippet('lastlogins-blockedusers', array(
61
- 'BlockedUsers.Username' => $data->username,
62
- 'BlockedUsers.BlockedAt' => self::datetime($data->blocked_at),
63
- 'BlockedUsers.FirstAttempt' => self::datetime($data->first_attempt),
64
- 'BlockedUsers.LastAttempt' => self::datetime($data->last_attempt),
65
- ));
 
 
66
  }
67
 
68
  $output['BlockedUsers.NoItemsVisibility'] = 'hidden';
@@ -74,7 +90,8 @@ class SucuriScanBlockedUsers extends SucuriScanLastLogins
74
  /**
75
  * Blocks one or more usernames.
76
  *
77
- * @param array $users List of usernames.
 
78
  */
79
  public static function block($users = array())
80
  {
@@ -104,7 +121,8 @@ class SucuriScanBlockedUsers extends SucuriScanLastLogins
104
  /**
105
  * Unblocks one or more usernames.
106
  *
107
- * @param array $users List of usernames.
 
108
  */
109
  public static function unblock($users = array())
110
  {
@@ -129,43 +147,47 @@ class SucuriScanBlockedUsers extends SucuriScanLastLogins
129
  * check to see if the username has been blocked by an admin and proceed
130
  * according to the expected behavior. Either we will stop the request right
131
  * here or let it propagate to the authentication checker.
 
 
132
  */
133
  public static function blockUserLogin()
134
  {
135
- if (class_exists('SucuriScanRequest')
136
- && class_exists('SucuriScanCache')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  ) {
138
- $username = SucuriScanRequest::post('log');
139
- $password = SucuriScanRequest::post('pwd');
140
-
141
- if ($username !== false && $password !== false) {
142
- $cache = new SucuriScanCache('blockedusers');
143
- $blocked = $cache->getAll();
144
- $cache_key = md5($username);
145
-
146
- if (is_array($blocked)
147
- && is_string($cache_key)
148
- && array_key_exists($cache_key, $blocked)
149
- ) {
150
- $blocked[$cache_key]->last_attempt = time();
151
- $cache->set($cache_key, $blocked[$cache_key]);
152
-
153
- if (!headers_sent()) {
154
- header('HTTP/1.1 403 Forbidden');
155
- }
156
-
157
- exit(0);
158
- }
159
  }
 
 
160
  }
161
  }
162
 
163
  /**
164
  * Finds the first login attempt of a specific username.
165
  *
166
- * @param array $logs List of failed login attempts.
167
- * @param string $user Username to be inspected.
168
- * @return int Timestamp of the first login attempt.
169
  */
170
  private static function firstAttempt($logs, $user)
171
  {
@@ -187,9 +209,9 @@ class SucuriScanBlockedUsers extends SucuriScanLastLogins
187
  /**
188
  * Finds the last login attempt of a specific username.
189
  *
190
- * @param array $logs List of failed login attempts.
191
- * @param string $user Username to be inspected.
192
- * @return int Timestamp of the last login attempt.
193
  */
194
  private static function lastAttempt($logs, $user)
195
  {
3
  /**
4
  * Code related to the lastlogins-blocked.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
34
  * in the Firewall service and it also provides a better filtering mechanism for
35
  * any other suspicious login attempt. We will encourage people to leverage the
36
  * power of the Firewall.
37
+ *
38
+ * @category Library
39
+ * @package Sucuri
40
+ * @subpackage SucuriScanner
41
+ * @author Daniel Cid <dcid@sucuri.net>
42
+ * @copyright 2010-2017 Sucuri Inc.
43
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
44
+ * @link https://wordpress.org/plugins/sucuri-scanner
45
  */
46
  class SucuriScanBlockedUsers extends SucuriScanLastLogins
47
  {
61
 
62
  if (is_array($unblockUsers) && !empty($unblockUsers)) {
63
  self::unblock($unblockUsers);
64
+ SucuriScanInterface::info('Selected user accounts were unblocked');
65
  }
66
  }
67
 
70
 
71
  if (is_array($blocked) && !empty($blocked)) {
72
  foreach ($blocked as $data) {
73
+ $output['BlockedUsers.List'] .= SucuriScanTemplate::getSnippet(
74
+ 'lastlogins-blockedusers',
75
+ array(
76
+ 'BlockedUsers.Username' => $data->username,
77
+ 'BlockedUsers.BlockedAt' => self::datetime($data->blocked_at),
78
+ 'BlockedUsers.FirstAttempt' => self::datetime($data->first_attempt),
79
+ 'BlockedUsers.LastAttempt' => self::datetime($data->last_attempt),
80
+ )
81
+ );
82
  }
83
 
84
  $output['BlockedUsers.NoItemsVisibility'] = 'hidden';
90
  /**
91
  * Blocks one or more usernames.
92
  *
93
+ * @param array $users List of usernames.
94
+ * @return void
95
  */
96
  public static function block($users = array())
97
  {
121
  /**
122
  * Unblocks one or more usernames.
123
  *
124
+ * @param array $users List of usernames.
125
+ * @return void
126
  */
127
  public static function unblock($users = array())
128
  {
147
  * check to see if the username has been blocked by an admin and proceed
148
  * according to the expected behavior. Either we will stop the request right
149
  * here or let it propagate to the authentication checker.
150
+ *
151
+ * @return void
152
  */
153
  public static function blockUserLogin()
154
  {
155
+ if (!class_exists('SucuriScanRequest') || !class_exists('SucuriScanCache')) {
156
+ return;
157
+ }
158
+
159
+ $username = SucuriScanRequest::post('log');
160
+ $password = SucuriScanRequest::post('pwd');
161
+
162
+ if ($username === false || $password === false) {
163
+ return;
164
+ }
165
+
166
+ $cache = new SucuriScanCache('blockedusers');
167
+ $blocked = $cache->getAll();
168
+ $cache_key = md5($username);
169
+
170
+ if (is_array($blocked)
171
+ && is_string($cache_key)
172
+ && array_key_exists($cache_key, $blocked)
173
  ) {
174
+ $blocked[$cache_key]->last_attempt = time();
175
+ $cache->set($cache_key, $blocked[$cache_key]);
176
+
177
+ if (!headers_sent()) {
178
+ header('HTTP/1.1 403 Forbidden');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  }
180
+
181
+ exit(0);
182
  }
183
  }
184
 
185
  /**
186
  * Finds the first login attempt of a specific username.
187
  *
188
+ * @param array $logs List of failed login attempts.
189
+ * @param string $user Username to be inspected.
190
+ * @return int Timestamp of the first login attempt.
191
  */
192
  private static function firstAttempt($logs, $user)
193
  {
209
  /**
210
  * Finds the last login attempt of a specific username.
211
  *
212
+ * @param array $logs List of failed login attempts.
213
+ * @param string $user Username to be inspected.
214
+ * @return int Timestamp of the last login attempt.
215
  */
216
  private static function lastAttempt($logs, $user)
217
  {
src/lastlogins-failed.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the lastlogins-failed.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage lastlogins-failed.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -38,7 +44,7 @@ function sucuriscan_failed_logins_panel()
38
 
39
  if (is_array($blockUsers) && !empty($blockUsers)) {
40
  SucuriScanBlockedUsers::block($blockUsers);
41
- SucuriScanInterface::info(__('AccountsWereBlocked', SUCURISCAN_TEXTDOMAIN));
42
  }
43
  }
44
 
@@ -51,6 +57,7 @@ function sucuriscan_failed_logins_panel()
51
  $max_failed_logins = SucuriScanOption::getOption(':maximum_failed_logins');
52
  $notify_bruteforce_attack = SucuriScanOption::getOption(':notify_bruteforce_attack');
53
  $failed_logins = sucuriscan_get_all_failed_logins($page_offset, $max_per_page);
 
54
 
55
  if ($failed_logins) {
56
  $counter = 0;
@@ -74,16 +81,22 @@ function sucuriscan_failed_logins_panel()
74
  $wrong_user_password_color = 'info';
75
  }
76
 
77
- $template_variables['FailedLogins.List'] .=
78
- SucuriScanTemplate::getSnippet('lastlogins-failedlogins', array(
79
- 'FailedLogins.Num' => $login_data['attempt_count'],
80
- 'FailedLogins.Username' => $login_data['user_login'],
81
- 'FailedLogins.RemoteAddr' => $login_data['remote_addr'],
82
- 'FailedLogins.UserAgent' => $login_data['user_agent'],
83
- 'FailedLogins.Password' => $wrong_user_password,
84
- 'FailedLogins.PasswordColor' => $wrong_user_password_color,
85
- 'FailedLogins.Datetime' => SucuriScan::datetime($login_data['attempt_time']),
86
- ));
 
 
 
 
 
 
87
 
88
  $counter++;
89
  }
@@ -121,9 +134,9 @@ function sucuriscan_failed_logins_panel()
121
  *
122
  * @see sucuriscan_reset_failed_logins()
123
  *
124
- * @param bool $get_old_logs Whether the old logs will be retrieved or not.
125
- * @param bool $reset Whether the file will be resetted or not.
126
- * @return string|false Absolute path to the file.
127
  */
128
  function sucuriscan_failed_logins_datastore_path($get_old_logs = false, $reset = false)
129
  {
@@ -157,8 +170,8 @@ function sucuriscan_failed_logins_default_content()
157
  /**
158
  * Returns failed logins data including old entries.
159
  *
160
- * @param int $offset Initial index to start the array.
161
- * @param int $limit Number of items in the returned array.
162
  * @return array|false Failed logins data.
163
  */
164
  function sucuriscan_get_all_failed_logins($offset = 0, $limit = -1)
@@ -195,10 +208,10 @@ function sucuriscan_get_all_failed_logins($offset = 0, $limit = -1)
195
  * with the report) or reset the file after considering it a normal behavior of
196
  * the site.
197
  *
198
- * @param bool $get_old_logs Whether the old logs will be retrieved or not.
199
- * @param int $offset Array index from where to start collecting the data.
200
- * @param int $limit Number of items to insert into the returned array.
201
- * @return array|false Information and entries gathered from the failed logins datastore file.
202
  */
203
  function sucuriscan_get_failed_logins($get_old_logs = false, $offset = 0, $limit = -1)
204
  {
@@ -286,7 +299,7 @@ function sucuriscan_get_failed_logins($get_old_logs = false, $offset = 0, $limit
286
 
287
  if (!is_array($first)) {
288
  /* In case the JSON is not decoded yet */
289
- $first= @json_decode($first, true);
290
  }
291
 
292
  $failed_logins['last_attempt'] = $last['attempt_time'];
@@ -301,29 +314,33 @@ function sucuriscan_get_failed_logins($get_old_logs = false, $offset = 0, $limit
301
  * this entry will contain the username, timestamp of the login attempt, remote
302
  * address of the computer sending the request, and the user-agent.
303
  *
304
- * @param string $user_login Information from the current failed login event.
305
- * @param string $wrong_password Wrong password used during the supposed attack.
306
- * @return bool Whether the information of the current failed login event was stored or not.
307
  */
308
  function sucuriscan_log_failed_login($user_login = '', $wrong_password = '')
309
  {
310
- if ($storage = sucuriscan_failed_logins_datastore_path()) {
311
- $login_data = json_encode(array(
 
 
 
 
 
 
312
  'user_login' => $user_login,
313
  'user_password' => $wrong_password,
314
  'attempt_time' => time(),
315
  'remote_addr' => SucuriScan::getRemoteAddr(),
316
  'user_agent' => SucuriScan::getUserAgent(),
317
- ));
318
-
319
- return (bool) @file_put_contents(
320
- $storage,
321
- $login_data . "\n",
322
- FILE_APPEND
323
- );
324
- }
325
 
326
- return false;
 
 
 
 
327
  }
328
 
329
  /**
@@ -332,64 +349,68 @@ function sucuriscan_log_failed_login($user_login = '', $wrong_password = '')
332
  * in HTML code to send as a report via email according to the plugin settings
333
  * for the email alerts.
334
  *
335
- * @param array $failed_logins Information and entries gathered from the failed logins datastore file.
336
- * @return bool Whether the report was sent via email or not.
337
  */
338
  function sucuriscan_report_failed_logins($failed_logins = array())
339
  {
340
- if ($failed_logins && $failed_logins['count'] > 0) {
341
- $prettify_mails = SucuriScanMail::prettifyMails();
342
- $mail_content = '';
 
 
 
343
 
344
- if ($prettify_mails) {
345
- $table_html = '<table border="1" cellspacing="0" cellpadding="0">';
346
 
347
- // Add the table headers.
348
- $table_html .= '<thead>';
349
- $table_html .= '<tr>';
350
- $table_html .= '<th>' . __('Username', SUCURISCAN_TEXTDOMAIN) . '</th>';
351
- $table_html .= '<th>' . __('Password', SUCURISCAN_TEXTDOMAIN) . '</th>';
352
- $table_html .= '<th>' . __('RemoteAddr', SUCURISCAN_TEXTDOMAIN) . '</th>';
353
- $table_html .= '<th>' . __('AttemptTimestamp', SUCURISCAN_TEXTDOMAIN) . '</th>';
354
- $table_html .= '<th>' . __('AttemptDatetime', SUCURISCAN_TEXTDOMAIN) . '</th>';
355
- $table_html .= '</tr>';
356
- $table_html .= '</thead>';
357
 
358
- $table_html .= '<tbody>';
359
- }
 
 
 
 
 
 
 
 
360
 
361
- foreach ($failed_logins['entries'] as $login_data) {
362
- $login_data['attempt_date'] = SucuriScan::datetime($login_data['attempt_time']);
363
-
364
- if ($prettify_mails) {
365
- $table_html .= '<tr>';
366
- $table_html .= '<td>' . esc_attr($login_data['user_login']) . '</td>';
367
- $table_html .= '<td>' . esc_attr($login_data['user_password']) . '</td>';
368
- $table_html .= '<td>' . esc_attr($login_data['remote_addr']) . '</td>';
369
- $table_html .= '<td>' . esc_attr($login_data['attempt_time']) . '</td>';
370
- $table_html .= '<td>' . esc_attr($login_data['attempt_date']) . '</td>';
371
- $table_html .= '</tr>';
372
- } else {
373
- $mail_content .= "\n";
374
- $mail_content .= __('Username', SUCURISCAN_TEXTDOMAIN) . ":\x20" . $login_data['user_login'] . "\n";
375
- $mail_content .= __('Password', SUCURISCAN_TEXTDOMAIN) . ":\x20" . $login_data['user_password'] . "\n";
376
- $mail_content .= __('RemoteAddr', SUCURISCAN_TEXTDOMAIN) . ":\x20" . $login_data['remote_addr'] . "\n";
377
- $mail_content .= __('AttemptTimestamp', SUCURISCAN_TEXTDOMAIN) . ":\x20" . $login_data['attempt_time'] . "\n";
378
- $mail_content .= __('AttemptDatetime', SUCURISCAN_TEXTDOMAIN) . ":\x20" . $login_data['attempt_date'] . "\n";
379
- }
380
- }
381
 
382
  if ($prettify_mails) {
383
- $table_html .= '</tbody>';
384
- $table_html .= '</table>';
385
- $mail_content = $table_html;
 
 
 
 
 
 
 
 
 
 
 
386
  }
 
387
 
388
- if (SucuriScanEvent::notifyEvent('bruteforce_attack', $mail_content)) {
389
- sucuriscan_reset_failed_logins();
 
 
 
390
 
391
- return true;
392
- }
 
393
  }
394
 
395
  return false;
3
  /**
4
  * Code related to the lastlogins-failed.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
44
 
45
  if (is_array($blockUsers) && !empty($blockUsers)) {
46
  SucuriScanBlockedUsers::block($blockUsers);
47
+ SucuriScanInterface::info('Selected user accounts were blocked');
48
  }
49
  }
50
 
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;
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(
91
+ 'FailedLogins.Num' => $login_data['attempt_count'],
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
+ );
100
 
101
  $counter++;
102
  }
134
  *
135
  * @see sucuriscan_reset_failed_logins()
136
  *
137
+ * @param bool $get_old_logs Whether the old logs will be retrieved or not.
138
+ * @param bool $reset Whether the file will be resetted or not.
139
+ * @return string|false Absolute path to the file.
140
  */
141
  function sucuriscan_failed_logins_datastore_path($get_old_logs = false, $reset = false)
142
  {
170
  /**
171
  * Returns failed logins data including old entries.
172
  *
173
+ * @param int $offset Initial index to start the array.
174
+ * @param int $limit Number of items in the returned array.
175
  * @return array|false Failed logins data.
176
  */
177
  function sucuriscan_get_all_failed_logins($offset = 0, $limit = -1)
208
  * with the report) or reset the file after considering it a normal behavior of
209
  * the site.
210
  *
211
+ * @param bool $get_old_logs Whether the old logs will be retrieved or not.
212
+ * @param int $offset Array index from where to start collecting the data.
213
+ * @param int $limit Number of items to insert into the returned array.
214
+ * @return array|false Information and entries gathered from the failed logins datastore file.
215
  */
216
  function sucuriscan_get_failed_logins($get_old_logs = false, $offset = 0, $limit = -1)
217
  {
299
 
300
  if (!is_array($first)) {
301
  /* In case the JSON is not decoded yet */
302
+ $first = @json_decode($first, true);
303
  }
304
 
305
  $failed_logins['last_attempt'] = $last['attempt_time'];
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
+
325
+ if (!$storage) {
326
+ return false;
327
+ }
328
+
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(),
336
+ )
337
+ );
 
 
 
 
 
 
338
 
339
+ return (bool) @file_put_contents(
340
+ $storage,
341
+ $login_data . "\n",
342
+ FILE_APPEND
343
+ );
344
  }
345
 
346
  /**
349
  * in HTML code to send as a report via email according to the plugin settings
350
  * for the email alerts.
351
  *
352
+ * @param array $failed_logins Information gathered from the failed logins.
353
+ * @return bool Whether the report was sent via email or not.
354
  */
355
  function sucuriscan_report_failed_logins($failed_logins = array())
356
  {
357
+ if (!$failed_logins
358
+ || !isset($failed_logins['count'])
359
+ || $failed_logins['count'] < 1
360
+ ) {
361
+ return false;
362
+ }
363
 
364
+ $mail_content = '';
365
+ $prettify_mails = SucuriScanMail::prettifyMails();
366
 
367
+ if ($prettify_mails) {
368
+ $table_html = '<table border="1" cellspacing="0" cellpadding="0">';
 
 
 
 
 
 
 
 
369
 
370
+ // Add the table headers.
371
+ $table_html .= '<thead>';
372
+ $table_html .= '<tr>';
373
+ $table_html .= '<th>' . 'Username' . '</th>';
374
+ $table_html .= '<th>' . 'Password' . '</th>';
375
+ $table_html .= '<th>' . 'IP Address' . '</th>';
376
+ $table_html .= '<th>' . 'Attempt Timestamp' . '</th>';
377
+ $table_html .= '<th>' . 'Attempt Date/Time' . '</th>';
378
+ $table_html .= '</tr>';
379
+ $table_html .= '</thead>';
380
 
381
+ $table_html .= '<tbody>';
382
+ }
383
+
384
+ foreach ($failed_logins['entries'] as $login_data) {
385
+ $login_data['attempt_date'] = SucuriScan::datetime($login_data['attempt_time']);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
 
387
  if ($prettify_mails) {
388
+ $table_html .= '<tr>';
389
+ $table_html .= '<td>' . esc_attr($login_data['user_login']) . '</td>';
390
+ $table_html .= '<td>' . esc_attr($login_data['user_password']) . '</td>';
391
+ $table_html .= '<td>' . esc_attr($login_data['remote_addr']) . '</td>';
392
+ $table_html .= '<td>' . esc_attr($login_data['attempt_time']) . '</td>';
393
+ $table_html .= '<td>' . esc_attr($login_data['attempt_date']) . '</td>';
394
+ $table_html .= '</tr>';
395
+ } else {
396
+ $mail_content .= "\n";
397
+ $mail_content .= 'Username' . ":\x20" . $login_data['user_login'] . "\n";
398
+ $mail_content .= 'Password' . ":\x20" . $login_data['user_password'] . "\n";
399
+ $mail_content .= 'IP Address' . ":\x20" . $login_data['remote_addr'] . "\n";
400
+ $mail_content .= 'Attempt Timestamp' . ":\x20" . $login_data['attempt_time'] . "\n";
401
+ $mail_content .= 'Attempt Date/Time' . ":\x20" . $login_data['attempt_date'] . "\n";
402
  }
403
+ }
404
 
405
+ if ($prettify_mails) {
406
+ $table_html .= '</tbody>';
407
+ $table_html .= '</table>';
408
+ $mail_content = $table_html;
409
+ }
410
 
411
+ if (SucuriScanEvent::notifyEvent('bruteforce_attack', $mail_content)) {
412
+ sucuriscan_reset_failed_logins();
413
+ return true;
414
  }
415
 
416
  return false;
src/lastlogins-loggedin.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the lastlogins-loggedin.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage lastlogins-loggedin.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -38,15 +44,18 @@ function sucuriscan_loggedin_users_panel()
38
  $logged_in_user['last_activity_datetime'] = SucuriScan::datetime($logged_in_user['last_activity']);
39
  $logged_in_user['user_registered_datetime'] = SucuriScan::datetime(strtotime($logged_in_user['user_registered']));
40
 
41
- $params['LoggedInUsers.List'] .= SucuriScanTemplate::getSnippet('lastlogins-loggedin', array(
42
- 'LoggedInUsers.Id' => $logged_in_user['user_id'],
43
- 'LoggedInUsers.UserURL' => SucuriScan::adminURL('user-edit.php?user_id=' . $logged_in_user['user_id']),
44
- 'LoggedInUsers.UserLogin' => $logged_in_user['user_login'],
45
- 'LoggedInUsers.UserEmail' => $logged_in_user['user_email'],
46
- 'LoggedInUsers.LastActivity' => $logged_in_user['last_activity_datetime'],
47
- 'LoggedInUsers.Registered' => $logged_in_user['user_registered_datetime'],
48
- 'LoggedInUsers.RemoteAddr' => $logged_in_user['remote_addr'],
49
- ));
 
 
 
50
  }
51
  }
52
 
@@ -104,8 +113,9 @@ function sucuriscan_save_online_users($logged_in_users = array())
104
 
105
  if (!function_exists('sucuriscan_unset_online_user_on_logout')) {
106
  /**
107
- * Remove a logged in user from the list of registered users in session when
108
- * the logout page is requested.
 
109
  */
110
  function sucuriscan_unset_online_user_on_logout()
111
  {
@@ -123,9 +133,9 @@ if (!function_exists('sucuriscan_unset_online_user_on_logout')) {
123
  * Remove a logged in user from the list of registered users in session using
124
  * the user identifier and the ip address of the last computer used to login.
125
  *
126
- * @param int $user_id User ID of the account that will be logged out.
127
- * @param string $remote_addr IP address of the computer where the user logged in.
128
- * @return bool Either TRUE or FALSE representing the success or fail of the operation.
129
  */
130
  function sucuriscan_unset_online_user($user_id = 0, $remote_addr = '')
131
  {
@@ -150,8 +160,9 @@ if (!function_exists('sucuriscan_set_online_user')) {
150
  /**
151
  * Add an user account to the list of registered users in session.
152
  *
153
- * @param string $user_login The name of the user account that just logged in the site.
154
- * @param bool $user The WordPress object containing all the information associated to the user.
 
155
  */
156
  function sucuriscan_set_online_user($user_login = '', $user = false)
157
  {
3
  /**
4
  * Code related to the lastlogins-loggedin.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
44
  $logged_in_user['last_activity_datetime'] = SucuriScan::datetime($logged_in_user['last_activity']);
45
  $logged_in_user['user_registered_datetime'] = SucuriScan::datetime(strtotime($logged_in_user['user_registered']));
46
 
47
+ $params['LoggedInUsers.List'] .= SucuriScanTemplate::getSnippet(
48
+ 'lastlogins-loggedin',
49
+ array(
50
+ 'LoggedInUsers.Id' => $logged_in_user['user_id'],
51
+ 'LoggedInUsers.UserURL' => SucuriScan::adminURL('user-edit.php?user_id=' . $logged_in_user['user_id']),
52
+ 'LoggedInUsers.UserLogin' => $logged_in_user['user_login'],
53
+ 'LoggedInUsers.UserEmail' => $logged_in_user['user_email'],
54
+ 'LoggedInUsers.LastActivity' => $logged_in_user['last_activity_datetime'],
55
+ 'LoggedInUsers.Registered' => $logged_in_user['user_registered_datetime'],
56
+ 'LoggedInUsers.RemoteAddr' => $logged_in_user['remote_addr'],
57
+ )
58
+ );
59
  }
60
  }
61
 
113
 
114
  if (!function_exists('sucuriscan_unset_online_user_on_logout')) {
115
  /**
116
+ * Remove a logged in user from the list.
117
+ *
118
+ * @return void
119
  */
120
  function sucuriscan_unset_online_user_on_logout()
121
  {
133
  * Remove a logged in user from the list of registered users in session using
134
  * the user identifier and the ip address of the last computer used to login.
135
  *
136
+ * @param int $user_id User ID of the account that will be logged out.
137
+ * @param string $remote_addr IP address of the computer where the user logged in.
138
+ * @return bool True on success, false otherwise.
139
  */
140
  function sucuriscan_unset_online_user($user_id = 0, $remote_addr = '')
141
  {
160
  /**
161
  * Add an user account to the list of registered users in session.
162
  *
163
+ * @param string $user_login The name of the user account that just logged in the site.
164
+ * @param bool $user The WordPress object containing all the information associated to the user.
165
+ * @return void
166
  */
167
  function sucuriscan_set_online_user($user_login = '', $user = false)
168
  {
src/lastlogins.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the lastlogins.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage lastlogins.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -18,6 +24,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
18
 
19
  /**
20
  * Placeholder for the last logins interface.
 
 
 
 
 
 
 
 
21
  */
22
  class SucuriScanLastLogins extends SucuriScan
23
  {
@@ -27,6 +41,8 @@ class SucuriScanLastLogins extends SucuriScan
27
  * List all the user administrator accounts.
28
  *
29
  * @see https://codex.wordpress.org/Class_Reference/WP_User_Query
 
 
30
  */
31
  function sucuriscan_lastlogins_admins()
32
  {
@@ -44,7 +60,7 @@ function sucuriscan_lastlogins_admins()
44
  'AdminUsers.Username' => $admin->user_login,
45
  'AdminUsers.Email' => $admin->user_email,
46
  'AdminUsers.LastLogins' => '',
47
- 'AdminUsers.RegisteredAt' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
48
  'AdminUsers.UserURL' => SucuriScan::adminURL('user-edit.php?user_id=' . $admin->ID),
49
  'AdminUsers.NoLastLogins' => 'visible',
50
  'AdminUsers.NoLastLoginsTable' => 'hidden',
@@ -56,20 +72,22 @@ function sucuriscan_lastlogins_admins()
56
  ) {
57
  $user_snippet['AdminUsers.NoLastLogins'] = 'hidden';
58
  $user_snippet['AdminUsers.NoLastLoginsTable'] = 'visible';
59
- $user_snippet['AdminUsers.RegisteredAt'] = __('Unknown', SUCURISCAN_TEXTDOMAIN);
60
 
61
  foreach ($last_logins['entries'] as $i => $lastlogin) {
62
  if ($i === 0) {
63
  $user_snippet['AdminUsers.RegisteredAt'] = SucuriScan::datetime(
64
- $lastlogin->user_registered_timestamp
65
  );
66
  }
67
 
68
- $user_snippet['AdminUsers.LastLogins'] .=
69
- SucuriScanTemplate::getSnippet('lastlogins-admins-lastlogin', array(
70
- 'AdminUsers.RemoteAddr' => $lastlogin->user_remoteaddr,
71
- 'AdminUsers.Datetime' => SucuriScan::datetime($lastlogin->user_lastlogin_timestamp),
72
- ));
 
 
73
  }
74
  }
75
 
@@ -103,10 +121,12 @@ function sucuriscan_lastlogins_all()
103
 
104
  if (!sucuriscan_lastlogins_datastore_is_writable()) {
105
  $fpath = SucuriScan::escape(sucuriscan_lastlogins_datastore_filepath());
106
- SucuriScanInterface::error(sprintf(__('LastLoginsNotWritable', SUCURISCAN_TEXTDOMAIN), $fpath));
107
  }
108
 
109
- if ($last_logins = sucuriscan_get_logins($max_per_page, $offset)) {
 
 
110
  $params['UserList.Total'] = $last_logins['total'];
111
 
112
  if ($last_logins['total'] > $max_per_page) {
@@ -119,24 +139,24 @@ function sucuriscan_lastlogins_all()
119
 
120
  foreach ($last_logins['entries'] as $user) {
121
  $user_dataset = array(
122
- 'UserList.Number' => $user->line_num,
123
- 'UserList.UserId' => $user->user_id,
124
- 'UserList.Username' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
125
  'UserList.Displayname' => '',
126
  'UserList.Email' => '',
127
  'UserList.Registered' => '',
128
- 'UserList.RemoteAddr' => $user->user_remoteaddr,
129
- 'UserList.Hostname' => $user->user_hostname,
130
- 'UserList.Datetime' => $user->user_lastlogin,
131
- 'UserList.TimeAgo' => SucuriScan::humanTime($user->user_lastlogin_timestamp),
132
- 'UserList.UserURL' => SucuriScan::adminURL('user-edit.php?user_id=' . $user->user_id),
133
  );
134
 
135
- if ($user->user_exists) {
136
- $user_dataset['UserList.Username'] = $user->user_login;
137
- $user_dataset['UserList.Displayname'] = $user->display_name;
138
- $user_dataset['UserList.Email'] = $user->user_email;
139
- $user_dataset['UserList.Registered'] = $user->user_registered;
140
  }
141
 
142
  $params['UserList'] .= SucuriScanTemplate::getSnippet('lastlogins-all', $user_dataset);
@@ -177,11 +197,7 @@ function sucuriscan_lastlogins_datastore_exists()
177
  @file_put_contents($fpath, "<?php exit(0); ?>\n", LOCK_EX);
178
  }
179
 
180
- if (file_exists($fpath)) {
181
- return $fpath;
182
- }
183
-
184
- return false;
185
  }
186
 
187
  /**
@@ -228,29 +244,35 @@ if (!function_exists('sucuri_set_lastlogin')) {
228
  /**
229
  * Add a new user session to the list of last user logins.
230
  *
231
- * @param string $user_login The name of the user account involved in the operation.
 
232
  */
233
  function sucuriscan_set_lastlogin($user_login = '')
234
  {
235
- if ($filename = sucuriscan_lastlogins_datastore_is_writable()) {
236
- $current_user = get_user_by('login', $user_login);
237
- $remote_addr = SucuriScan::getRemoteAddr();
238
-
239
- $login_info = array(
240
- 'user_id' => $current_user->ID,
241
- 'user_login' => $current_user->user_login,
242
- 'user_remoteaddr' => $remote_addr,
243
- 'user_hostname' => @gethostbyaddr($remote_addr),
244
- 'user_lastlogin' => current_time('mysql')
245
- );
246
 
247
- @file_put_contents(
248
- $filename,
249
- json_encode($login_info) . "\n",
250
- FILE_APPEND
251
- );
252
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  }
 
254
  add_action('wp_login', 'sucuriscan_set_lastlogin', 50);
255
  }
256
 
@@ -260,10 +282,10 @@ if (!function_exists('sucuri_set_lastlogin')) {
260
  * The results of this operation can be filtered by specific user identifiers,
261
  * or limiting the quantity of entries.
262
  *
263
- * @param int $limit How many entries will be returned from the operation.
264
- * @param int $offset Initial point where the logs will be start counting.
265
- * @param int $user_id Optional user identifier to filter the results.
266
- * @return array|bool All user logins, false on failure.
267
  */
268
  function sucuriscan_get_logins($limit = 10, $offset = 0, $user_id = 0)
269
  {
@@ -305,6 +327,7 @@ function sucuriscan_get_logins($limit = 10, $offset = 0, $user_id = 0)
305
  *
306
  * @var object
307
  */
 
308
  $current_user = wp_get_current_user();
309
  $is_admin_user = (bool) current_user_can('manage_options');
310
 
@@ -332,24 +355,11 @@ function sucuriscan_get_logins($limit = 10, $offset = 0, $user_id = 0)
332
  continue;
333
  }
334
 
335
- // Get the WP_User object and add extra information from the last-login data.
336
  $last_login['user_exists'] = false;
337
- $user_account = get_userdata($last_login['user_id']);
338
-
339
- if ($user_account) {
340
- $last_login['user_exists'] = true;
341
-
342
- foreach ($user_account->data as $var_name => $var_value) {
343
- $last_login[ $var_name ] = $var_value;
344
-
345
- if ($var_name == 'user_registered') {
346
- $last_login['user_registered_timestamp'] = strtotime($var_value);
347
- }
348
- }
349
- }
350
 
351
  $last_login['line_num'] = $i + 1;
352
- $last_logins['entries'][] = (object) $last_login;
353
  $parsed_lines += 1;
354
 
355
  if ($limit > 0 && $parsed_lines >= $limit) {
@@ -357,6 +367,34 @@ function sucuriscan_get_logins($limit = 10, $offset = 0, $user_id = 0)
357
  }
358
  }
359
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
  return $last_logins;
361
  }
362
 
@@ -365,10 +403,10 @@ if (!function_exists('sucuri_login_redirect')) {
365
  * Hook for the wp-login action to redirect the user to a specific URL after
366
  * his successfully login to the administrator interface.
367
  *
368
- * @param string $redirect_to The redirect destination URL.
369
- * @param object $request The requested redirect destination URL passed as a parameter.
370
- * @param bool $user WP_User object if login was successful, WP_Error object otherwise.
371
- * @return string URL where the browser must be redirected to.
372
  */
373
  function sucuriscan_login_redirect($redirect_to = '', $request = null, $user = false)
374
  {
@@ -392,6 +430,8 @@ if (!function_exists('sucuri_login_redirect')) {
392
  if (!function_exists('sucuri_get_user_lastlogin')) {
393
  /**
394
  * Display the last user login at the top of the admin interface.
 
 
395
  */
396
  function sucuriscan_get_user_lastlogin()
397
  {
@@ -399,8 +439,6 @@ if (!function_exists('sucuri_get_user_lastlogin')) {
399
  && SucuriScanRequest::get(':lastlogin', '1')
400
  ) {
401
  $current_user = wp_get_current_user();
402
-
403
- // Select the penultimate entry, not the last one.
404
  $last_logins = sucuriscan_get_logins(2, 0, $current_user->ID);
405
 
406
  if ($last_logins
@@ -410,10 +448,11 @@ if (!function_exists('sucuri_get_user_lastlogin')) {
410
  $row = $last_logins['entries'][1];
411
  $page_url = SucuriScanTemplate::getUrl('lastlogins');
412
  $message = sprintf(
413
- __('LastLoginMessage', SUCURISCAN_TEXTDOMAIN),
414
- SucuriScan::datetime($row->user_lastlogin_timestamp),
415
- SucuriScan::escape($row->user_remoteaddr),
416
- SucuriScan::escape($row->user_hostname),
 
417
  SucuriScan::escape($page_url)
418
  );
419
  SucuriScanInterface::info($message);
@@ -421,5 +460,6 @@ if (!function_exists('sucuri_get_user_lastlogin')) {
421
  }
422
  }
423
 
 
424
  add_action('admin_notices', 'sucuriscan_get_user_lastlogin');
425
  }
3
  /**
4
  * Code related to the lastlogins.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
24
 
25
  /**
26
  * Placeholder for the last logins interface.
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 SucuriScanLastLogins extends SucuriScan
37
  {
41
  * List all the user administrator accounts.
42
  *
43
  * @see https://codex.wordpress.org/Class_Reference/WP_User_Query
44
+ *
45
+ * @return void
46
  */
47
  function sucuriscan_lastlogins_admins()
48
  {
60
  'AdminUsers.Username' => $admin->user_login,
61
  'AdminUsers.Email' => $admin->user_email,
62
  'AdminUsers.LastLogins' => '',
63
+ 'AdminUsers.RegisteredAt' => 'unknown',
64
  'AdminUsers.UserURL' => SucuriScan::adminURL('user-edit.php?user_id=' . $admin->ID),
65
  'AdminUsers.NoLastLogins' => 'visible',
66
  'AdminUsers.NoLastLoginsTable' => 'hidden',
72
  ) {
73
  $user_snippet['AdminUsers.NoLastLogins'] = 'hidden';
74
  $user_snippet['AdminUsers.NoLastLoginsTable'] = 'visible';
75
+ $user_snippet['AdminUsers.RegisteredAt'] = 'unknown';
76
 
77
  foreach ($last_logins['entries'] as $i => $lastlogin) {
78
  if ($i === 0) {
79
  $user_snippet['AdminUsers.RegisteredAt'] = SucuriScan::datetime(
80
+ $lastlogin['user_registered_timestamp']
81
  );
82
  }
83
 
84
+ $user_snippet['AdminUsers.LastLogins'] .= SucuriScanTemplate::getSnippet(
85
+ 'lastlogins-admins-lastlogin',
86
+ array(
87
+ 'AdminUsers.RemoteAddr' => $lastlogin['user_remoteaddr'],
88
+ 'AdminUsers.Datetime' => SucuriScan::datetime($lastlogin['user_lastlogin_timestamp']),
89
+ )
90
+ );
91
  }
92
  }
93
 
121
 
122
  if (!sucuriscan_lastlogins_datastore_is_writable()) {
123
  $fpath = SucuriScan::escape(sucuriscan_lastlogins_datastore_filepath());
124
+ SucuriScanInterface::error(sprintf('Last-logins data file is not writable: <code>%s</code>', $fpath));
125
  }
126
 
127
+ $last_logins = sucuriscan_get_logins($max_per_page, $offset);
128
+
129
+ if (is_array($last_logins) && !empty($last_logins)) {
130
  $params['UserList.Total'] = $last_logins['total'];
131
 
132
  if ($last_logins['total'] > $max_per_page) {
139
 
140
  foreach ($last_logins['entries'] as $user) {
141
  $user_dataset = array(
142
+ 'UserList.Number' => $user['line_num'],
143
+ 'UserList.UserId' => $user['user_id'],
144
+ 'UserList.Username' => 'unknown',
145
  'UserList.Displayname' => '',
146
  'UserList.Email' => '',
147
  'UserList.Registered' => '',
148
+ 'UserList.RemoteAddr' => $user['user_remoteaddr'],
149
+ 'UserList.Hostname' => $user['user_hostname'],
150
+ 'UserList.Datetime' => $user['user_lastlogin'],
151
+ 'UserList.TimeAgo' => SucuriScan::humanTime($user['user_lastlogin_timestamp']),
152
+ 'UserList.UserURL' => SucuriScan::adminURL('user-edit.php?user_id=' . $user['user_id']),
153
  );
154
 
155
+ if ($user['user_exists']) {
156
+ $user_dataset['UserList.Username'] = $user['user_login'];
157
+ $user_dataset['UserList.Displayname'] = $user['display_name'];
158
+ $user_dataset['UserList.Email'] = $user['user_email'];
159
+ $user_dataset['UserList.Registered'] = $user['user_registered'];
160
  }
161
 
162
  $params['UserList'] .= SucuriScanTemplate::getSnippet('lastlogins-all', $user_dataset);
197
  @file_put_contents($fpath, "<?php exit(0); ?>\n", LOCK_EX);
198
  }
199
 
200
+ return file_exists($fpath) ? $fpath : false;
 
 
 
 
201
  }
202
 
203
  /**
244
  /**
245
  * Add a new user session to the list of last user logins.
246
  *
247
+ * @param string $user_login User account involved in the operation.
248
+ * @return void
249
  */
250
  function sucuriscan_set_lastlogin($user_login = '')
251
  {
252
+ $filename = sucuriscan_lastlogins_datastore_is_writable();
 
 
 
 
 
 
 
 
 
 
253
 
254
+ if (!is_string($filename)) {
255
+ return false;
 
 
 
256
  }
257
+
258
+ $current_user = get_user_by('login', $user_login);
259
+ $remote_addr = SucuriScan::getRemoteAddr();
260
+
261
+ $login_info = array(
262
+ 'user_id' => $current_user->ID,
263
+ 'user_login' => $current_user->user_login,
264
+ 'user_remoteaddr' => $remote_addr,
265
+ 'user_hostname' => @gethostbyaddr($remote_addr),
266
+ 'user_lastlogin' => current_time('mysql')
267
+ );
268
+
269
+ @file_put_contents(
270
+ $filename,
271
+ json_encode($login_info) . "\n",
272
+ FILE_APPEND
273
+ );
274
  }
275
+
276
  add_action('wp_login', 'sucuriscan_set_lastlogin', 50);
277
  }
278
 
282
  * The results of this operation can be filtered by specific user identifiers,
283
  * or limiting the quantity of entries.
284
  *
285
+ * @param int $limit How many entries will be returned from the operation.
286
+ * @param int $offset Initial point where the logs will be start counting.
287
+ * @param int $user_id Optional user identifier to filter the results.
288
+ * @return array|bool All user logins, false on failure.
289
  */
290
  function sucuriscan_get_logins($limit = 10, $offset = 0, $user_id = 0)
291
  {
327
  *
328
  * @var object
329
  */
330
+ $user_ids = array();
331
  $current_user = wp_get_current_user();
332
  $is_admin_user = (bool) current_user_can('manage_options');
333
 
355
  continue;
356
  }
357
 
 
358
  $last_login['user_exists'] = false;
359
+ $user_ids[] = $last_login['user_id'];
 
 
 
 
 
 
 
 
 
 
 
 
360
 
361
  $last_login['line_num'] = $i + 1;
362
+ $last_logins['entries'][] = $last_login;
363
  $parsed_lines += 1;
364
 
365
  if ($limit > 0 && $parsed_lines >= $limit) {
367
  }
368
  }
369
 
370
+ /* no need for database query */
371
+ if (empty($user_ids)) {
372
+ return $last_logins;
373
+ }
374
+
375
+ /* extra information for the unique user accounts */
376
+ $user_ids = array_unique($user_ids);
377
+ $dbquery = new WP_User_Query(array('include' => $user_ids));
378
+
379
+ if ($dbquery) {
380
+ $results = $dbquery->get_results();
381
+
382
+ foreach ($last_logins['entries'] as $key => $entry) {
383
+ foreach ($results as $dbentry) {
384
+ if ($entry['user_id'] === $dbentry->ID) {
385
+ $last_logins['entries'][$key]['user_exists'] = true;
386
+ $last_logins['entries'][$key]['user_nicename'] = $dbentry->user_nicename;
387
+ $last_logins['entries'][$key]['user_email'] = $dbentry->user_email;
388
+ $last_logins['entries'][$key]['user_url'] = $dbentry->user_url;
389
+ $last_logins['entries'][$key]['user_status'] = $dbentry->user_status;
390
+ $last_logins['entries'][$key]['display_name'] = $dbentry->display_name;
391
+ $last_logins['entries'][$key]['user_registered'] = $dbentry->user_registered;
392
+ $last_logins['entries'][$key]['user_registered_timestamp'] = strtotime($dbentry->user_registered);
393
+ }
394
+ }
395
+ }
396
+ }
397
+
398
  return $last_logins;
399
  }
400
 
403
  * Hook for the wp-login action to redirect the user to a specific URL after
404
  * his successfully login to the administrator interface.
405
  *
406
+ * @param string $redirect_to The redirect destination URL.
407
+ * @param mixed $request The requested redirect destination URL passed as a parameter.
408
+ * @param bool $user WP_User object if login was successful, WP_Error object otherwise.
409
+ * @return string URL where the browser must be redirected to.
410
  */
411
  function sucuriscan_login_redirect($redirect_to = '', $request = null, $user = false)
412
  {
430
  if (!function_exists('sucuri_get_user_lastlogin')) {
431
  /**
432
  * Display the last user login at the top of the admin interface.
433
+ *
434
+ * @return void
435
  */
436
  function sucuriscan_get_user_lastlogin()
437
  {
439
  && SucuriScanRequest::get(':lastlogin', '1')
440
  ) {
441
  $current_user = wp_get_current_user();
 
 
442
  $last_logins = sucuriscan_get_logins(2, 0, $current_user->ID);
443
 
444
  if ($last_logins
448
  $row = $last_logins['entries'][1];
449
  $page_url = SucuriScanTemplate::getUrl('lastlogins');
450
  $message = sprintf(
451
+ 'Last login was at <b>%s</b> from <b>%s</b> <em>(%s)</em> '
452
+ . '<a href="%s" target="_self">view all logs</a>',
453
+ SucuriScan::datetime($row['user_lastlogin_timestamp']),
454
+ SucuriScan::escape($row['user_remoteaddr']),
455
+ SucuriScan::escape($row['user_hostname']),
456
  SucuriScan::escape($page_url)
457
  );
458
  SucuriScanInterface::info($message);
460
  }
461
  }
462
 
463
+ add_action('network_admin_notices', 'sucuriscan_get_user_lastlogin');
464
  add_action('admin_notices', 'sucuriscan_get_user_lastlogin');
465
  }
src/mail.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the mail.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage mail.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -23,6 +29,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
23
  * will check if the site is being compromised, in which case a notification
24
  * will be sent to the site email address (an address that can be configured in
25
  * the settings page).
 
 
 
 
 
 
 
 
26
  */
27
  class SucuriScanMail extends SucuriScanOption
28
  {
@@ -39,11 +53,11 @@ class SucuriScanMail extends SucuriScanOption
39
  /**
40
  * Send a message to a specific email address.
41
  *
42
- * @param string $email The email address of the recipient that will receive the message.
43
- * @param string $subject The reason of the message that will be sent.
44
- * @param string $message Body of the message that will be sent.
45
- * @param array $data_set Optional parameter to add more information to the notification.
46
- * @return bool Whether the email contents were sent successfully.
47
  */
48
  public static function sendMail($email = '', $subject = '', $message = '', $data_set = array())
49
  {
@@ -101,8 +115,8 @@ class SucuriScanMail extends SucuriScanOption
101
  /**
102
  * Generate a subject for the email alerts.
103
  *
104
- * @param string $event The reason of the message that will be sent.
105
- * @return string A text with the subject for the email alert.
106
  */
107
  private static function getEmailSubject($event = '')
108
  {
@@ -117,8 +131,8 @@ class SucuriScanMail extends SucuriScanOption
117
  || strpos($subject, ':email') !== false
118
  ) {
119
  $user = wp_get_current_user();
120
- $username = __('Unknown', SUCURISCAN_TEXTDOMAIN);
121
- $eaddress = __('Unknown', SUCURISCAN_TEXTDOMAIN);
122
 
123
  if ($user instanceof WP_User
124
  && isset($user->user_login)
@@ -138,10 +152,10 @@ class SucuriScanMail extends SucuriScanOption
138
  /**
139
  * Generate a HTML version of the message that will be sent through an email.
140
  *
141
- * @param string $subject The reason of the message that will be sent.
142
- * @param string $message Body of the message that will be sent.
143
- * @param array $data_set Optional parameter to add more information to the notification.
144
- * @return string The message formatted in a HTML template.
145
  */
146
  private static function prettifyMail($subject = '', $message = '', $data_set = array())
147
  {
@@ -160,7 +174,7 @@ class SucuriScanMail extends SucuriScanOption
160
  && !empty($user->user_login)
161
  ) {
162
  $display_name = sprintf(
163
- __('UserInfo', SUCURISCAN_TEXTDOMAIN),
164
  $user->display_name,
165
  $user->user_login
166
  );
@@ -186,7 +200,7 @@ class SucuriScanMail extends SucuriScanOption
186
  }
187
  }
188
 
189
- $params['TemplateTitle'] = __('SucuriAlert', SUCURISCAN_TEXTDOMAIN);
190
  $params['Subject'] = $subject;
191
  $params['Website'] = $website;
192
  $params['RemoteAddress'] = self::getRemoteAddr();
3
  /**
4
  * Code related to the mail.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
29
  * will check if the site is being compromised, in which case a notification
30
  * will be sent to the site email address (an address that can be configured in
31
  * the settings page).
32
+ *
33
+ * @category Library
34
+ * @package Sucuri
35
+ * @subpackage SucuriScanner
36
+ * @author Daniel Cid <dcid@sucuri.net>
37
+ * @copyright 2010-2017 Sucuri Inc.
38
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
39
+ * @link https://wordpress.org/plugins/sucuri-scanner
40
  */
41
  class SucuriScanMail extends SucuriScanOption
42
  {
53
  /**
54
  * Send a message to a specific email address.
55
  *
56
+ * @param string $email The email address of the recipient that will receive the message.
57
+ * @param string $subject The reason of the message that will be sent.
58
+ * @param string $message Body of the message that will be sent.
59
+ * @param array $data_set Optional parameter to add more information to the notification.
60
+ * @return bool Whether the email contents were sent successfully.
61
  */
62
  public static function sendMail($email = '', $subject = '', $message = '', $data_set = array())
63
  {
115
  /**
116
  * Generate a subject for the email alerts.
117
  *
118
+ * @param string $event The reason of the message that will be sent.
119
+ * @return string A text with the subject for the email alert.
120
  */
121
  private static function getEmailSubject($event = '')
122
  {
131
  || strpos($subject, ':email') !== false
132
  ) {
133
  $user = wp_get_current_user();
134
+ $username = 'unknown';
135
+ $eaddress = 'unknown';
136
 
137
  if ($user instanceof WP_User
138
  && isset($user->user_login)
152
  /**
153
  * Generate a HTML version of the message that will be sent through an email.
154
  *
155
+ * @param string $subject The reason of the message that will be sent.
156
+ * @param string $message Body of the message that will be sent.
157
+ * @param array $data_set Optional parameter to add more information to the notification.
158
+ * @return string The message formatted in a HTML template.
159
  */
160
  private static function prettifyMail($subject = '', $message = '', $data_set = array())
161
  {
174
  && !empty($user->user_login)
175
  ) {
176
  $display_name = sprintf(
177
+ 'User: %s (%s)',
178
  $user->display_name,
179
  $user->user_login
180
  );
200
  }
201
  }
202
 
203
+ $params['TemplateTitle'] = 'Sucuri Alert';
204
  $params['Subject'] = $subject;
205
  $params['Website'] = $website;
206
  $params['RemoteAddress'] = self::getRemoteAddr();
src/option.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the option.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage option.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -36,8 +42,15 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
36
  * apply network-wide and the data is stored in the wp_sitemeta table under the
37
  * given custom name.
38
  *
39
- * @see https://codex.wordpress.org/Option_Reference
40
- * @see https://codex.wordpress.org/Options_API
 
 
 
 
 
 
 
41
  */
42
  class SucuriScanOption extends SucuriScanRequest
43
  {
@@ -63,8 +76,6 @@ class SucuriScanOption extends SucuriScanRequest
63
  'sucuriscan_emails_per_hour' => 5,
64
  'sucuriscan_emails_sent' => 0,
65
  'sucuriscan_ignored_events' => '',
66
- 'sucuriscan_integrity_startup' => '',
67
- 'sucuriscan_language' => 'en_US',
68
  'sucuriscan_last_email_at' => time(),
69
  'sucuriscan_lastlogin_redirection' => 'enabled',
70
  'sucuriscan_maximum_failed_logins' => 30,
@@ -123,8 +134,8 @@ class SucuriScanOption extends SucuriScanRequest
123
  /**
124
  * Retrieve the default values for some specific options.
125
  *
126
- * @param string $option List of options, or single option name.
127
- * @return mixed The default values for the specified options.
128
  */
129
  private static function getDefaultOptions($option = '')
130
  {
@@ -135,10 +146,7 @@ class SucuriScanOption extends SucuriScanRequest
135
  $admin_email = get_option('admin_email');
136
  $default['sucuriscan_account'] = $admin_email;
137
  $default['sucuriscan_notify_to'] = $admin_email;
138
-
139
- $default['sucuriscan_email_subject'] =
140
- __('SucuriAlert', SUCURISCAN_TEXTDOMAIN)
141
- . ', :domain, :event, :remoteaddr';
142
  }
143
 
144
  return @$default[$option];
@@ -196,8 +204,8 @@ class SucuriScanOption extends SucuriScanRequest
196
  /**
197
  * Write new options into the external options file.
198
  *
199
- * @param array $options Array with plugins options.
200
- * @return bool True if the new options were saved, false otherwise.
201
  */
202
  public static function writeNewOptions($options = array())
203
  {
@@ -212,9 +220,8 @@ class SucuriScanOption extends SucuriScanRequest
212
  * Returns data from the settings file or the database.
213
  *
214
  * To facilitate the development, you can prefix the name of the key in the
215
- * request (when accessing it) with a single colon, this method will
216
- * automatically replace that character with the unique identifier of the
217
- * plugin.
218
  *
219
  * NOTE: The SucuriScanCache library is a better interface to read the
220
  * content of a configuration file following the same standard in the other
@@ -225,8 +232,8 @@ class SucuriScanOption extends SucuriScanRequest
225
  *
226
  * @see https://developer.wordpress.org/reference/functions/get_option/
227
  *
228
- * @param string $option Name of the option.
229
- * @return mixed Value associated to the option.
230
  */
231
  public static function getOption($option = '')
232
  {
@@ -270,8 +277,18 @@ class SucuriScanOption extends SucuriScanRequest
270
  }
271
  }
272
 
 
 
 
 
 
 
 
 
273
  if (strpos($option, SUCURISCAN . '_') === 0) {
274
- return self::getDefaultOptions($option);
 
 
275
  }
276
 
277
  return false;
@@ -287,9 +304,9 @@ class SucuriScanOption extends SucuriScanRequest
287
  *
288
  * @see https://developer.wordpress.org/reference/functions/update_option/
289
  *
290
- * @param string $option Name of the option.
291
- * @param mixed $value New value for the option.
292
- * @return bool True if option has been updated, false otherwise.
293
  */
294
  public static function updateOption($option = '', $value = '')
295
  {
@@ -311,8 +328,8 @@ class SucuriScanOption extends SucuriScanRequest
311
  *
312
  * @see https://developer.wordpress.org/reference/functions/delete_option/
313
  *
314
- * @param string $option Name of the option to be deleted.
315
- * @return bool True if option is successfully deleted, false otherwise.
316
  */
317
  public static function deleteOption($option = '')
318
  {
@@ -334,8 +351,8 @@ class SucuriScanOption extends SucuriScanRequest
334
  /**
335
  * Check whether a setting is enabled or not.
336
  *
337
- * @param string $option Name of the option to be deleted.
338
- * @return bool True if the option is enabled, false otherwise.
339
  */
340
  public static function isEnabled($option = '')
341
  {
@@ -345,8 +362,8 @@ class SucuriScanOption extends SucuriScanRequest
345
  /**
346
  * Check whether a setting is disabled or not.
347
  *
348
- * @param string $option Name of the option to be deleted.
349
- * @return bool True if the option is disabled, false otherwise.
350
  */
351
  public static function isDisabled($option = '')
352
  {
@@ -358,21 +375,21 @@ class SucuriScanOption extends SucuriScanRequest
358
  * containing the word "transient" are excluded from the results, this method
359
  * compatible with multisite instances.
360
  *
361
- * @return array All the options stored by Wordpress in the database, except the transient options.
362
  */
363
  private static function getSiteOptions()
364
  {
365
- global $wpdb;
366
-
367
  $settings = array();
368
- $results = $wpdb->get_results(
369
- "SELECT * FROM {$wpdb->options}
370
- WHERE option_name NOT LIKE '%_transient_%'
371
- ORDER BY option_id ASC"
372
- );
373
 
374
- foreach ($results as $row) {
375
- $settings[ $row->option_name ] = $row->option_value;
 
 
 
 
 
 
 
376
  }
377
 
378
  $external = self::getAllOptions();
@@ -388,8 +405,8 @@ class SucuriScanOption extends SucuriScanRequest
388
  * Check what Wordpress options were changed comparing the values in the database
389
  * with the values sent through a simple request using a GET or POST method.
390
  *
391
- * @param array $request The content of the global variable GET or POST considering SERVER[REQUEST_METHOD].
392
- * @return array A list of all the options that were changes through this request.
393
  */
394
  public static function whatOptionsWereChanged($request = array())
395
  {
@@ -510,8 +527,8 @@ class SucuriScanOption extends SucuriScanRequest
510
  /**
511
  * Check whether an event is being ignored to send alerts or not.
512
  *
513
- * @param string $event Unique post-type name.
514
- * @return bool Whether an event is being ignored or not.
515
  */
516
  public static function isIgnoredEvent($event = '')
517
  {
@@ -521,25 +538,6 @@ class SucuriScanOption extends SucuriScanRequest
521
  return array_key_exists($event, $ignored);
522
  }
523
 
524
- /**
525
- * Add a new post type to the list of ignored events to send alerts.
526
- *
527
- * @param string $event_name Unique post-type name.
528
- * @return bool Whether the event was ignored or not.
529
- */
530
- public static function addIgnoredEvent($event_name = '')
531
- {
532
- $ignored = self::getIgnoredEvents();
533
-
534
- if (array_key_exists($event_name, $ignored)) {
535
- return false; /* skip if the post-type was already ignored */
536
- }
537
-
538
- $ignored[$event_name] = time(); /* add post-type to the list */
539
-
540
- return self::updateOption(':ignored_events', @json_encode($ignored));
541
- }
542
-
543
  /**
544
  * Get a list of the post types ignored to receive email alerts when the
545
  * "new site content" hook is triggered.
@@ -549,36 +547,12 @@ class SucuriScanOption extends SucuriScanRequest
549
  public static function getIgnoredEvents()
550
  {
551
  $post_types = self::getOption(':ignored_events');
552
- $post_types_arr = false;
553
 
554
  if (is_string($post_types)) {
555
- $post_types_arr = @json_decode($post_types, true);
556
- }
557
-
558
- if (!is_array($post_types_arr)) {
559
- $post_types_arr = array();
560
- }
561
-
562
- return $post_types_arr;
563
- }
564
-
565
- /**
566
- * Remove a post type from the list of ignored events to send alerts.
567
- *
568
- * @param string $event Unique post-type name.
569
- * @return bool Whether the event was removed from the list or not.
570
- */
571
- public static function removeIgnoredEvent($event = '')
572
- {
573
- $ignored = self::getIgnoredEvents();
574
-
575
- if (!array_key_exists($event, $ignored)) {
576
- return false;
577
  }
578
 
579
- unset($ignored[$event]); /* remove post-type from the list */
580
-
581
- return self::updateOption(':ignored_events', @json_encode($ignored));
582
  }
583
 
584
  /**
@@ -631,8 +605,9 @@ class SucuriScanOption extends SucuriScanRequest
631
  * firewall page is activated as it assumes that the proxy is creating a
632
  * custom HTTP header for the real IP.
633
  *
634
- * @param string $action Enable or disable the reverse proxy.
635
- * @param bool $silent Hide admin notices on success.
 
636
  */
637
  public static function setRevProxy($action = 'disable', $silent = false)
638
  {
@@ -652,17 +627,20 @@ class SucuriScanOption extends SucuriScanRequest
652
  return true;
653
  }
654
 
655
- return SucuriScanInterface::info(sprintf(
656
- __('ReverseProxyStatus', SUCURISCAN_TEXTDOMAIN),
657
- $action_d /* either enabled or disabled */
658
- ));
 
 
659
  }
660
 
661
  /**
662
  * Change the HTTP header to retrieve the real IP address.
663
  *
664
- * @param string $header Valid HTTP header name.
665
- * @param bool $silent Hide admin notices on success.
 
666
  */
667
  public static function setAddrHeader($header = 'REMOTE_ADDR', $silent = false)
668
  {
@@ -670,7 +648,7 @@ class SucuriScanOption extends SucuriScanRequest
670
  $allowed = SucuriScan::allowedHttpHeaders(true);
671
 
672
  if (!array_key_exists($header, $allowed)) {
673
- return SucuriScanInterface::error(__('DisallowedHTTPHeader', SUCURISCAN_TEXTDOMAIN));
674
  }
675
 
676
  $message = sprintf('HTTP header was set to %s', $header);
@@ -684,9 +662,11 @@ class SucuriScanOption extends SucuriScanRequest
684
  return true;
685
  }
686
 
687
- return SucuriScanInterface::info(sprintf(
688
- __('HTTPHeaderStatus', SUCURISCAN_TEXTDOMAIN),
689
- $header /* one of the allowed HTTP headers */
690
- ));
 
 
691
  }
692
  }
3
  /**
4
  * Code related to the option.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
42
  * apply network-wide and the data is stored in the wp_sitemeta table under the
43
  * given custom name.
44
  *
45
+ * @category Library
46
+ * @package Sucuri
47
+ * @subpackage SucuriScanner
48
+ * @author Daniel Cid <dcid@sucuri.net>
49
+ * @copyright 2010-2017 Sucuri Inc.
50
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
51
+ * @link https://wordpress.org/plugins/sucuri-scanner
52
+ * @see https://codex.wordpress.org/Option_Reference
53
+ * @see https://codex.wordpress.org/Options_API
54
  */
55
  class SucuriScanOption extends SucuriScanRequest
56
  {
76
  'sucuriscan_emails_per_hour' => 5,
77
  'sucuriscan_emails_sent' => 0,
78
  'sucuriscan_ignored_events' => '',
 
 
79
  'sucuriscan_last_email_at' => time(),
80
  'sucuriscan_lastlogin_redirection' => 'enabled',
81
  'sucuriscan_maximum_failed_logins' => 30,
134
  /**
135
  * Retrieve the default values for some specific options.
136
  *
137
+ * @param string $option List of options, or single option name.
138
+ * @return mixed The default values for the specified options.
139
  */
140
  private static function getDefaultOptions($option = '')
141
  {
146
  $admin_email = get_option('admin_email');
147
  $default['sucuriscan_account'] = $admin_email;
148
  $default['sucuriscan_notify_to'] = $admin_email;
149
+ $default['sucuriscan_email_subject'] = 'Sucuri Alert, :domain, :event, :remoteaddr';
 
 
 
150
  }
151
 
152
  return @$default[$option];
204
  /**
205
  * Write new options into the external options file.
206
  *
207
+ * @param array $options Array with plugins options.
208
+ * @return bool True if the new options were saved, false otherwise.
209
  */
210
  public static function writeNewOptions($options = array())
211
  {
220
  * Returns data from the settings file or the database.
221
  *
222
  * To facilitate the development, you can prefix the name of the key in the
223
+ * request (when accessing it) with a single colon, this method will automa-
224
+ * tically replace that character with the unique identifier of the plugin.
 
225
  *
226
  * NOTE: The SucuriScanCache library is a better interface to read the
227
  * content of a configuration file following the same standard in the other
232
  *
233
  * @see https://developer.wordpress.org/reference/functions/get_option/
234
  *
235
+ * @param string $option Name of the option.
236
+ * @return mixed Value associated to the option.
237
  */
238
  public static function getOption($option = '')
239
  {
277
  }
278
  }
279
 
280
+ /**
281
+ * Cache default value to stop querying the database.
282
+ *
283
+ * The option was not found in the database either, we will return the
284
+ * data from the array of default values hardcoded in the source code,
285
+ * then will attempt to write the default value into the flat settings
286
+ * file to stop querying the database in subsequent requests.
287
+ */
288
  if (strpos($option, SUCURISCAN . '_') === 0) {
289
+ $value = self::getDefaultOptions($option);
290
+ self::updateOption($option, $value);
291
+ return $value;
292
  }
293
 
294
  return false;
304
  *
305
  * @see https://developer.wordpress.org/reference/functions/update_option/
306
  *
307
+ * @param string $option Name of the option.
308
+ * @param mixed $value New value for the option.
309
+ * @return bool True if option has been updated, false otherwise.
310
  */
311
  public static function updateOption($option = '', $value = '')
312
  {
328
  *
329
  * @see https://developer.wordpress.org/reference/functions/delete_option/
330
  *
331
+ * @param string $option Name of the option to be deleted.
332
+ * @return bool True if option is successfully deleted, false otherwise.
333
  */
334
  public static function deleteOption($option = '')
335
  {
351
  /**
352
  * Check whether a setting is enabled or not.
353
  *
354
+ * @param string $option Name of the option to be deleted.
355
+ * @return bool True if the option is enabled, false otherwise.
356
  */
357
  public static function isEnabled($option = '')
358
  {
362
  /**
363
  * Check whether a setting is disabled or not.
364
  *
365
+ * @param string $option Name of the option to be deleted.
366
+ * @return bool True if the option is disabled, false otherwise.
367
  */
368
  public static function isDisabled($option = '')
369
  {
375
  * containing the word "transient" are excluded from the results, this method
376
  * compatible with multisite instances.
377
  *
378
+ * @return array All the options stored by Wordpress in the database.
379
  */
380
  private static function getSiteOptions()
381
  {
 
 
382
  $settings = array();
 
 
 
 
 
383
 
384
+ if (array_key_exists('wpdb', $GLOBALS)) {
385
+ $results = $GLOBALS['wpdb']->get_results(
386
+ 'SELECT * FROM ' . $GLOBALS['wpdb']->options . ' WHERE opti'
387
+ . 'on_name NOT LIKE "%_transient_%" ORDER BY option_id ASC'
388
+ );
389
+
390
+ foreach ($results as $row) {
391
+ $settings[$row->option_name] = $row->option_value;
392
+ }
393
  }
394
 
395
  $external = self::getAllOptions();
405
  * Check what Wordpress options were changed comparing the values in the database
406
  * with the values sent through a simple request using a GET or POST method.
407
  *
408
+ * @param array $request The content of the global variable GET or POST considering SERVER[REQUEST_METHOD].
409
+ * @return array A list of all the options that were changes through this request.
410
  */
411
  public static function whatOptionsWereChanged($request = array())
412
  {
527
  /**
528
  * Check whether an event is being ignored to send alerts or not.
529
  *
530
+ * @param string $event Unique post-type name.
531
+ * @return bool Whether an event is being ignored or not.
532
  */
533
  public static function isIgnoredEvent($event = '')
534
  {
538
  return array_key_exists($event, $ignored);
539
  }
540
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
  /**
542
  * Get a list of the post types ignored to receive email alerts when the
543
  * "new site content" hook is triggered.
547
  public static function getIgnoredEvents()
548
  {
549
  $post_types = self::getOption(':ignored_events');
 
550
 
551
  if (is_string($post_types)) {
552
+ $post_types = @json_decode($post_types, true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
553
  }
554
 
555
+ return (array) $post_types;
 
 
556
  }
557
 
558
  /**
605
  * firewall page is activated as it assumes that the proxy is creating a
606
  * custom HTTP header for the real IP.
607
  *
608
+ * @param string $action Enable or disable the reverse proxy.
609
+ * @param bool $silent Hide admin notices on success.
610
+ * @return void
611
  */
612
  public static function setRevProxy($action = 'disable', $silent = false)
613
  {
627
  return true;
628
  }
629
 
630
+ return SucuriScanInterface::info(
631
+ sprintf(
632
+ 'Reverse proxy support was set to <b>%s</b>',
633
+ $action_d /* either enabled or disabled */
634
+ )
635
+ );
636
  }
637
 
638
  /**
639
  * Change the HTTP header to retrieve the real IP address.
640
  *
641
+ * @param string $header Valid HTTP header name.
642
+ * @param bool $silent Hide admin notices on success.
643
+ * @return void
644
  */
645
  public static function setAddrHeader($header = 'REMOTE_ADDR', $silent = false)
646
  {
648
  $allowed = SucuriScan::allowedHttpHeaders(true);
649
 
650
  if (!array_key_exists($header, $allowed)) {
651
+ return SucuriScanInterface::error('HTTP header is not allowed');
652
  }
653
 
654
  $message = sprintf('HTTP header was set to %s', $header);
662
  return true;
663
  }
664
 
665
+ return SucuriScanInterface::info(
666
+ sprintf(
667
+ 'HTTP header was set to <code>%s</code>',
668
+ $header /* one of the allowed HTTP headers */
669
+ )
670
+ );
671
  }
672
  }
src/pagehandler.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the pagehandler.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage pagehandler.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -18,6 +24,8 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
18
 
19
  /**
20
  * Renders the content of the plugin's dashboard page.
 
 
21
  */
22
  function sucuriscan_page()
23
  {
@@ -32,12 +40,12 @@ function sucuriscan_page()
32
  $params['AuditLogs'] = SucuriScanAuditLogs::pageAuditLogs();
33
 
34
  /* load data for the SiteCheck section */
35
- $params['SiteCheck.iFramesTitle'] = __('iFrames', SUCURISCAN_TEXTDOMAIN);
36
- $params['SiteCheck.LinksTitle'] = __('Links', SUCURISCAN_TEXTDOMAIN);
37
- $params['SiteCheck.ScriptsTitle'] = __('Scripts', SUCURISCAN_TEXTDOMAIN);
38
- $params['SiteCheck.iFramesContent'] = __('Loading', SUCURISCAN_TEXTDOMAIN);
39
- $params['SiteCheck.LinksContent'] = __('Loading', SUCURISCAN_TEXTDOMAIN);
40
- $params['SiteCheck.ScriptsContent'] = __('Loading', SUCURISCAN_TEXTDOMAIN);
41
  $params['SiteCheck.Malware'] = '<div id="sucuriscan-malware"></div>';
42
  $params['SiteCheck.Blacklist'] = '<div id="sucuriscan-blacklist"></div>';
43
  $params['SiteCheck.Recommendations'] = '<div id="sucuriscan-recommendations"></div>';
@@ -47,6 +55,8 @@ function sucuriscan_page()
47
 
48
  /**
49
  * Renders the content of the plugin's firewall page.
 
 
50
  */
51
  function sucuriscan_firewall_page()
52
  {
@@ -64,6 +74,8 @@ function sucuriscan_firewall_page()
64
 
65
  /**
66
  * Renders the content of the plugin's last logins page.
 
 
67
  */
68
  function sucuriscan_lastlogins_page()
69
  {
@@ -77,9 +89,9 @@ function sucuriscan_lastlogins_page()
77
 
78
  if (@unlink($file_path)) {
79
  sucuriscan_lastlogins_datastore_exists();
80
- SucuriScanInterface::info(__('LastLoginsResetSuccess', SUCURISCAN_TEXTDOMAIN));
81
  } else {
82
- SucuriScanInterface::error(__('LastLoginsResetFailure', SUCURISCAN_TEXTDOMAIN));
83
  }
84
  }
85
 
@@ -97,6 +109,8 @@ function sucuriscan_lastlogins_page()
97
 
98
  /**
99
  * Renders the content of the plugin's settings page.
 
 
100
  */
101
  function sucuriscan_settings_page()
102
  {
@@ -120,7 +134,6 @@ function sucuriscan_settings_page()
120
  /* settings - scanner */
121
  $params['Settings.Scanner.Cronjobs'] = SucuriScanSettingsScanner::cronjobs($nonce);
122
  $params['Settings.Scanner.IntegrityDiffUtility'] = SucuriScanSettingsIntegrity::diffUtility($nonce);
123
- $params['Settings.Scanner.IntegrityLanguage'] = SucuriScanSettingsIntegrity::language($nonce);
124
  $params['Settings.Scanner.IntegrityCache'] = SucuriScanSettingsIntegrity::cache($nonce);
125
  $params['Settings.Scanner.IgnoreFolders'] = SucuriScanSettingsScanner::ignoreFolders($nonce);
126
 
@@ -168,6 +181,8 @@ function sucuriscan_settings_page()
168
 
169
  /**
170
  * Handles all the AJAX plugin's requests.
 
 
171
  */
172
  function sucuriscan_ajax()
173
  {
@@ -190,7 +205,6 @@ function sucuriscan_ajax()
190
  SucuriScanSettingsPosthack::getPluginsAjax();
191
  SucuriScanSettingsPosthack::resetPasswordAjax();
192
  SucuriScanSettingsPosthack::resetPluginAjax();
193
- SucuriScanSettingsScanner::ignoreFoldersAjax();
194
  }
195
 
196
  wp_send_json(array('ok' => false, 'error' => 'invalid ajax action'), 200);
3
  /**
4
  * Code related to the pagehandler.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
24
 
25
  /**
26
  * Renders the content of the plugin's dashboard page.
27
+ *
28
+ * @return void
29
  */
30
  function sucuriscan_page()
31
  {
40
  $params['AuditLogs'] = SucuriScanAuditLogs::pageAuditLogs();
41
 
42
  /* load data for the SiteCheck section */
43
+ $params['SiteCheck.iFramesTitle'] = 'iFrames';
44
+ $params['SiteCheck.LinksTitle'] = 'Links';
45
+ $params['SiteCheck.ScriptsTitle'] = 'Scripts';
46
+ $params['SiteCheck.iFramesContent'] = 'Loading...';
47
+ $params['SiteCheck.LinksContent'] = 'Loading...';
48
+ $params['SiteCheck.ScriptsContent'] = 'Loading...';
49
  $params['SiteCheck.Malware'] = '<div id="sucuriscan-malware"></div>';
50
  $params['SiteCheck.Blacklist'] = '<div id="sucuriscan-blacklist"></div>';
51
  $params['SiteCheck.Recommendations'] = '<div id="sucuriscan-recommendations"></div>';
55
 
56
  /**
57
  * Renders the content of the plugin's firewall page.
58
+ *
59
+ * @return void
60
  */
61
  function sucuriscan_firewall_page()
62
  {
74
 
75
  /**
76
  * Renders the content of the plugin's last logins page.
77
+ *
78
+ * @return void
79
  */
80
  function sucuriscan_lastlogins_page()
81
  {
89
 
90
  if (@unlink($file_path)) {
91
  sucuriscan_lastlogins_datastore_exists();
92
+ SucuriScanInterface::info('Last-Logins logs were successfully reset.');
93
  } else {
94
+ SucuriScanInterface::error('Could not reset the last-logins data file.');
95
  }
96
  }
97
 
109
 
110
  /**
111
  * Renders the content of the plugin's settings page.
112
+ *
113
+ * @return void
114
  */
115
  function sucuriscan_settings_page()
116
  {
134
  /* settings - scanner */
135
  $params['Settings.Scanner.Cronjobs'] = SucuriScanSettingsScanner::cronjobs($nonce);
136
  $params['Settings.Scanner.IntegrityDiffUtility'] = SucuriScanSettingsIntegrity::diffUtility($nonce);
 
137
  $params['Settings.Scanner.IntegrityCache'] = SucuriScanSettingsIntegrity::cache($nonce);
138
  $params['Settings.Scanner.IgnoreFolders'] = SucuriScanSettingsScanner::ignoreFolders($nonce);
139
 
181
 
182
  /**
183
  * Handles all the AJAX plugin's requests.
184
+ *
185
+ * @return void
186
  */
187
  function sucuriscan_ajax()
188
  {
205
  SucuriScanSettingsPosthack::getPluginsAjax();
206
  SucuriScanSettingsPosthack::resetPasswordAjax();
207
  SucuriScanSettingsPosthack::resetPluginAjax();
 
208
  }
209
 
210
  wp_send_json(array('ok' => false, 'error' => 'invalid ajax action'), 200);
src/request.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the request.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage request.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -23,6 +29,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
23
  * HTTP request, generally after a form submission or while loading a URL. Use
24
  * these methods at most instead of accessing an index in the global PHP
25
  * variables _POST, _GET, _REQUEST since they may come with insecure data.
 
 
 
 
 
 
 
 
26
  */
27
  class SucuriScanRequest extends SucuriScan
28
  {
@@ -33,9 +47,9 @@ class SucuriScanRequest extends SucuriScan
33
  * will return False if the value doesn't matches what the RegExp defined.
34
  * Very useful to whitelist user input besides form validations.
35
  *
36
- * @param array $list The array where the specified key will be searched.
37
- * @param string $key Name of the variable contained in _POST.
38
- * @param string $pattern Optional pattern to match allowed values.
39
  * @return array|string|bool Value from the global _GET or _POST variable.
40
  */
41
  private static function request($list = array(), $key = '', $pattern = '')
@@ -80,9 +94,9 @@ class SucuriScanRequest extends SucuriScan
80
  * Returns the value stored in a specific index in the global _GET variable,
81
  * you can specify a pattern as the second argument to match allowed values.
82
  *
83
- * @param string $key Name of the variable contained in _GET.
84
- * @param string $pattern Optional pattern to match allowed values.
85
- * @return array|string Value from the global _GET variable.
86
  */
87
  public static function get($key = '', $pattern = '')
88
  {
@@ -93,9 +107,9 @@ class SucuriScanRequest extends SucuriScan
93
  * Returns the value stored in a specific index in the global _POST variable,
94
  * you can specify a pattern as the second argument to match allowed values.
95
  *
96
- * @param string $key Name of the variable contained in _POST.
97
- * @param string $pattern Optional pattern to match allowed values.
98
- * @return array|string Value from the global _POST variable.
99
  */
100
  public static function post($key = '', $pattern = '')
101
  {
@@ -106,9 +120,9 @@ class SucuriScanRequest extends SucuriScan
106
  * Returns the value stored in a specific index in the global _REQUEST variable,
107
  * you can specify a pattern as the second argument to match allowed values.
108
  *
109
- * @param string $key Name of the variable contained in _REQUEST.
110
- * @param string $pattern Optional pattern to match allowed values.
111
- * @return array|string Value from the global _REQUEST variable.
112
  */
113
  public static function getOrPost($key = '', $pattern = '')
114
  {
3
  /**
4
  * Code related to the request.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
29
  * HTTP request, generally after a form submission or while loading a URL. Use
30
  * these methods at most instead of accessing an index in the global PHP
31
  * variables _POST, _GET, _REQUEST since they may come with insecure data.
32
+ *
33
+ * @category Library
34
+ * @package Sucuri
35
+ * @subpackage SucuriScanner
36
+ * @author Daniel Cid <dcid@sucuri.net>
37
+ * @copyright 2010-2017 Sucuri Inc.
38
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
39
+ * @link https://wordpress.org/plugins/sucuri-scanner
40
  */
41
  class SucuriScanRequest extends SucuriScan
42
  {
47
  * will return False if the value doesn't matches what the RegExp defined.
48
  * Very useful to whitelist user input besides form validations.
49
  *
50
+ * @param array $list The array where the specified key will be searched.
51
+ * @param string $key Name of the variable contained in _POST.
52
+ * @param string $pattern Optional pattern to match allowed values.
53
  * @return array|string|bool Value from the global _GET or _POST variable.
54
  */
55
  private static function request($list = array(), $key = '', $pattern = '')
94
  * Returns the value stored in a specific index in the global _GET variable,
95
  * you can specify a pattern as the second argument to match allowed values.
96
  *
97
+ * @param string $key Name of the variable contained in _GET.
98
+ * @param string $pattern Optional pattern to match allowed values.
99
+ * @return array|string Value from the global _GET variable.
100
  */
101
  public static function get($key = '', $pattern = '')
102
  {
107
  * Returns the value stored in a specific index in the global _POST variable,
108
  * you can specify a pattern as the second argument to match allowed values.
109
  *
110
+ * @param string $key Name of the variable contained in _POST.
111
+ * @param string $pattern Optional pattern to match allowed values.
112
+ * @return array|string Value from the global _POST variable.
113
  */
114
  public static function post($key = '', $pattern = '')
115
  {
120
  * Returns the value stored in a specific index in the global _REQUEST variable,
121
  * you can specify a pattern as the second argument to match allowed values.
122
  *
123
+ * @param string $key Name of the variable contained in _REQUEST.
124
+ * @param string $pattern Optional pattern to match allowed values.
125
+ * @return array|string Value from the global _REQUEST variable.
126
  */
127
  public static function getOrPost($key = '', $pattern = '')
128
  {
src/settings-alerts.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the settings-alerts.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage settings-alerts.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -24,8 +30,8 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
24
  * is usually the email of the website owner. The plugin allows to add more
25
  * emails to the list so the alerts are sent to other people.
26
  *
27
- * @param bool $nonce True if the CSRF protection worked, false otherwise.
28
- * @return string HTML for the email alert recipients.
29
  */
30
  function sucuriscan_settings_alerts_recipients($nonce)
31
  {
@@ -47,14 +53,14 @@ function sucuriscan_settings_alerts_recipients($nonce)
47
 
48
  if (SucuriScan::isValidEmail($new_email)) {
49
  $emails[] = $new_email;
50
- $message = sprintf(__('WillReceiveAlerts', SUCURISCAN_TEXTDOMAIN), $new_email);
51
 
52
  SucuriScanOption::updateOption(':notify_to', implode(',', $emails));
53
  SucuriScanEvent::reportInfoEvent('The email alerts will be sent to: ' . $new_email);
54
  SucuriScanEvent::notifyEvent('plugin_change', $message);
55
  SucuriScanInterface::info($message);
56
  } else {
57
- SucuriScanInterface::error(__('InvalidEmail', SUCURISCAN_TEXTDOMAIN));
58
  }
59
  }
60
 
@@ -73,7 +79,7 @@ function sucuriscan_settings_alerts_recipients($nonce)
73
 
74
  if (!empty($deleted_emails)) {
75
  $deleted_emails_str = implode(",\x20", $deleted_emails);
76
- $message = sprintf(__('WillNotReceiveAlerts', SUCURISCAN_TEXTDOMAIN), $deleted_emails_str);
77
 
78
  SucuriScanOption::updateOption(':notify_to', implode(',', $emails));
79
  SucuriScanEvent::reportInfoEvent('These emails will stop receiving alerts: ' . $deleted_emails_str);
@@ -91,17 +97,18 @@ function sucuriscan_settings_alerts_recipients($nonce)
91
  sprintf('Test email alert sent at %s', SucuriScan::datetime()),
92
  array('Force' => true)
93
  );
94
- SucuriScanInterface::info(__('TestAlertSent', SUCURISCAN_TEXTDOMAIN));
 
95
  }
96
  }
97
 
98
 
99
  foreach ($emails as $email) {
100
  if (!empty($email)) {
101
- $params['Alerts.Recipients'] .=
102
- SucuriScanTemplate::getSnippet('settings-alerts-recipients', array(
103
- 'Recipient.Email' => $email,
104
- ));
105
  }
106
  }
107
 
@@ -127,30 +134,34 @@ function sucuriscan_settings_alerts_trustedips()
127
 
128
  if (SucuriScanInterface::checkNonce()) {
129
  // Trust and IP address to ignore alerts for a subnet.
130
- if ($trust_ip = SucuriScanRequest::post(':trust_ip')) {
 
 
131
  if (SucuriScan::isValidIP($trust_ip) || SucuriScan::isValidCIDR($trust_ip)) {
132
  $ip_info = SucuriScan::getIPInfo($trust_ip);
133
  $ip_info['added_at'] = time();
134
  $cache_key = md5($ip_info['remote_addr']);
135
 
136
  if ($cache->exists($cache_key)) {
137
- SucuriScanInterface::error(__('TrustedIPDuplicate', SUCURISCAN_TEXTDOMAIN));
138
  } elseif ($cache->add($cache_key, $ip_info)) {
139
  SucuriScanEvent::reportWarningEvent('IP has been trusted: ' . $trust_ip);
140
- SucuriScanInterface::info(sprintf(__('TrustedIPAdded', SUCURISCAN_TEXTDOMAIN), $trust_ip));
141
  } else {
142
- SucuriScanInterface::error(__('TrustedIPFailure', SUCURISCAN_TEXTDOMAIN));
143
  }
144
  }
145
  }
146
 
147
  // Trust and IP address to ignore alerts for a subnet.
148
- if ($del_trust_ip = SucuriScanRequest::post(':del_trust_ip', '_array')) {
 
 
149
  foreach ($del_trust_ip as $cache_key) {
150
  $cache->delete($cache_key);
151
  }
152
 
153
- SucuriScanInterface::info(__('TrustedIPDeleted', SUCURISCAN_TEXTDOMAIN));
154
  }
155
  }
156
 
@@ -162,13 +173,15 @@ function sucuriscan_settings_alerts_trustedips()
162
  $ip_info->cidr_format = 'n/a';
163
  }
164
 
165
- $params['TrustedIPs.List'] .=
166
- SucuriScanTemplate::getSnippet('settings-alerts-trustedips', array(
167
- 'TrustIP.CacheKey' => $cache_key,
168
- 'TrustIP.RemoteAddr' => SucuriScan::escape($ip_info->remote_addr),
169
- 'TrustIP.CIDRFormat' => SucuriScan::escape($ip_info->cidr_format),
170
- 'TrustIP.AddedAt' => SucuriScan::datetime($ip_info->added_at),
171
- ));
 
 
172
  }
173
 
174
  $params['TrustedIPs.NoItems.Visibility'] = 'hidden';
@@ -191,20 +204,20 @@ function sucuriscan_settings_alerts_subject($nonce)
191
  'Alerts.CustomValue' => '',
192
  );
193
 
194
- $header = __('SucuriAlert', SUCURISCAN_TEXTDOMAIN);
195
-
196
  $subjects = array(
197
- $header . ', :domain, :event',
198
- $header . ', :domain, :event, :remoteaddr',
199
- $header . ', :domain, :event, :username',
200
- $header . ', :domain, :event, :email',
201
- $header . ', :event, :remoteaddr',
202
- $header . ', :event',
203
  );
204
 
205
  // Process form submission to change the alert settings.
206
  if ($nonce) {
207
- if ($email_subject = SucuriScanRequest::post(':email_subject')) {
 
 
208
  $current_value = SucuriScanOption::getOption(':email_subject');
209
  $new_subject = false;
210
 
@@ -225,7 +238,7 @@ function sucuriscan_settings_alerts_subject($nonce)
225
  ) {
226
  $new_subject = trim($custom_subject);
227
  } else {
228
- SucuriScanInterface::error(__('InvalidEmailSubject', SUCURISCAN_TEXTDOMAIN));
229
  }
230
  } elseif (is_array($subjects) && in_array($email_subject, $subjects)) {
231
  $new_subject = trim($email_subject);
@@ -238,7 +251,7 @@ function sucuriscan_settings_alerts_subject($nonce)
238
  SucuriScanOption::updateOption(':email_subject', $new_subject);
239
  SucuriScanEvent::reportInfoEvent($message);
240
  SucuriScanEvent::notifyEvent('plugin_change', $message);
241
- SucuriScanInterface::info(__('UpdatedEmailSubject', SUCURISCAN_TEXTDOMAIN));
242
  }
243
  }
244
  }
@@ -256,12 +269,14 @@ function sucuriscan_settings_alerts_subject($nonce)
256
  $checked = '';
257
  }
258
 
259
- $params['Alerts.Subject'] .=
260
- SucuriScanTemplate::getSnippet('settings-alerts-subject', array(
261
- 'EmailSubject.Name' => $subject_format,
262
- 'EmailSubject.Value' => $subject_format,
263
- 'EmailSubject.Checked' => $checked,
264
- ));
 
 
265
  }
266
 
267
  if ($is_official_subject === false) {
@@ -285,18 +300,20 @@ function sucuriscan_settings_alerts_perhour($nonce)
285
  $params['Alerts.PerHour'] = '';
286
 
287
  $emails_per_hour = array(
288
- '5' => __('OptionPerHour5', SUCURISCAN_TEXTDOMAIN),
289
- '10' => __('OptionPerHour10', SUCURISCAN_TEXTDOMAIN),
290
- '20' => __('OptionPerHour20', SUCURISCAN_TEXTDOMAIN),
291
- '40' => __('OptionPerHour40', SUCURISCAN_TEXTDOMAIN),
292
- '80' => __('OptionPerHour80', SUCURISCAN_TEXTDOMAIN),
293
- '160' => __('OptionPerHour160', SUCURISCAN_TEXTDOMAIN),
294
- 'unlimited' => __('OptionPerHourUnlimited', SUCURISCAN_TEXTDOMAIN),
295
  );
296
 
297
  if ($nonce) {
298
  // Update the value for the maximum emails per hour.
299
- if ($per_hour = SucuriScanRequest::post(':emails_per_hour')) {
 
 
300
  if (array_key_exists($per_hour, $emails_per_hour)) {
301
  $per_hour_label = strtolower($emails_per_hour[$per_hour]);
302
  $message = 'Maximum alerts per hour set to <code>' . $per_hour_label . '</code>';
@@ -304,9 +321,9 @@ function sucuriscan_settings_alerts_perhour($nonce)
304
  SucuriScanOption::updateOption(':emails_per_hour', $per_hour);
305
  SucuriScanEvent::reportInfoEvent($message);
306
  SucuriScanEvent::notifyEvent('plugin_change', $message);
307
- SucuriScanInterface::info(__('MaximumAlertsSuccess', SUCURISCAN_TEXTDOMAIN));
308
  } else {
309
- SucuriScanInterface::error(__('MaximumAlertsFailure', SUCURISCAN_TEXTDOMAIN));
310
  }
311
  }
312
  }
@@ -330,28 +347,31 @@ function sucuriscan_settings_alerts_bruteforce($nonce)
330
  $params['Alerts.BruteForce'] = '';
331
 
332
  $max_failed_logins = array(
333
- '30' => __('OptionFailedLogins30', SUCURISCAN_TEXTDOMAIN),
334
- '60' => __('OptionFailedLogins60', SUCURISCAN_TEXTDOMAIN),
335
- '120' => __('OptionFailedLogins120', SUCURISCAN_TEXTDOMAIN),
336
- '240' => __('OptionFailedLogins240', SUCURISCAN_TEXTDOMAIN),
337
- '480' => __('OptionFailedLogins480', SUCURISCAN_TEXTDOMAIN),
338
  );
339
 
340
  if ($nonce) {
341
  // Update the maximum failed logins per hour before consider it a brute-force attack.
342
- if ($maximum = SucuriScanRequest::post(':maximum_failed_logins')) {
 
 
343
  if (array_key_exists($maximum, $max_failed_logins)) {
344
  $message = 'Consider brute-force attack after <code>' . $maximum . '</code> failed logins per hour';
345
 
346
  SucuriScanOption::updateOption(':maximum_failed_logins', $maximum);
347
  SucuriScanEvent::reportInfoEvent($message);
348
  SucuriScanEvent::notifyEvent('plugin_change', $message);
349
- SucuriScanInterface::info(sprintf(
350
- __('BruteForceAlertSuccess', SUCURISCAN_TEXTDOMAIN),
351
- $maximum /* one of the allowed maximum numbers */
352
- ));
 
353
  } else {
354
- SucuriScanInterface::error(__('BruteForceAlertFailure', SUCURISCAN_TEXTDOMAIN));
355
  }
356
  }
357
  }
@@ -376,32 +396,32 @@ function sucuriscan_settings_alerts_events($nonce)
376
  $params['Alerts.NoAlertsVisibility'] = 'hidden';
377
 
378
  $notify_options = array(
379
- 'sucuriscan_notify_plugin_change' => 'setting:' . __('OptionNotifyPluginChange', SUCURISCAN_TEXTDOMAIN),
380
- 'sucuriscan_prettify_mails' => 'setting:' . __('OptionPrettifyMails', SUCURISCAN_TEXTDOMAIN),
381
- 'sucuriscan_use_wpmail' => 'setting:' . __('OptionUseWordPressMail', SUCURISCAN_TEXTDOMAIN),
382
- 'sucuriscan_lastlogin_redirection' => 'setting:' . __('OptionLastLoginRedirection', SUCURISCAN_TEXTDOMAIN),
383
- 'sucuriscan_notify_scan_checksums' => 'setting:' . __('OptionNotifyScanChecksums', SUCURISCAN_TEXTDOMAIN),
384
- 'sucuriscan_notify_available_updates' => 'setting:' . __('OptionNotifyAvailableUpdates', SUCURISCAN_TEXTDOMAIN),
385
- 'sucuriscan_notify_user_registration' => 'user:' . __('OptionNotifyUserRegistration', SUCURISCAN_TEXTDOMAIN),
386
- 'sucuriscan_notify_success_login' => 'user:' . __('OptionNotifySuccessLogin', SUCURISCAN_TEXTDOMAIN),
387
- 'sucuriscan_notify_failed_login' => 'user:' . __('OptionNotifyFailedLogin', SUCURISCAN_TEXTDOMAIN),
388
- 'sucuriscan_notify_failed_password' => 'user:' . __('OptionNotifyFailedPassword', SUCURISCAN_TEXTDOMAIN),
389
- 'sucuriscan_notify_bruteforce_attack' => 'user:' . __('OptionNotifyBruteforceAttack', SUCURISCAN_TEXTDOMAIN),
390
- 'sucuriscan_notify_post_publication' => 'setting:' . __('OptionNotifyPostPublication', SUCURISCAN_TEXTDOMAIN),
391
- 'sucuriscan_notify_website_updated' => 'setting:' . __('OptionNotifyWebsiteUpdated', SUCURISCAN_TEXTDOMAIN),
392
- 'sucuriscan_notify_settings_updated' => 'setting:' . __('OptionNotifySettingsUpdated', SUCURISCAN_TEXTDOMAIN),
393
- 'sucuriscan_notify_theme_editor' => 'setting:' . __('OptionNotifyThemeEditor', SUCURISCAN_TEXTDOMAIN),
394
- 'sucuriscan_notify_plugin_installed' => 'plugin:' . __('OptionNotifyPluginInstalled', SUCURISCAN_TEXTDOMAIN),
395
- 'sucuriscan_notify_plugin_activated' => 'plugin:' . __('OptionNotifyPluginActivated', SUCURISCAN_TEXTDOMAIN),
396
- 'sucuriscan_notify_plugin_deactivated' => 'plugin:' . __('OptionNotifyPluginDeactivated', SUCURISCAN_TEXTDOMAIN),
397
- 'sucuriscan_notify_plugin_updated' => 'plugin:' . __('OptionNotifyPluginUpdated', SUCURISCAN_TEXTDOMAIN),
398
- 'sucuriscan_notify_plugin_deleted' => 'plugin:' . __('OptionNotifyPluginDeleted', SUCURISCAN_TEXTDOMAIN),
399
- 'sucuriscan_notify_widget_added' => 'widget:' . __('OptionNotifyWidgetAdded', SUCURISCAN_TEXTDOMAIN),
400
- 'sucuriscan_notify_widget_deleted' => 'widget:' . __('OptionNotifyWidgetDeleted', SUCURISCAN_TEXTDOMAIN),
401
- 'sucuriscan_notify_theme_installed' => 'theme:' . __('OptionNotifyThemeInstalled', SUCURISCAN_TEXTDOMAIN),
402
- 'sucuriscan_notify_theme_activated' => 'theme:' . __('OptionNotifyThemeActivated', SUCURISCAN_TEXTDOMAIN),
403
- 'sucuriscan_notify_theme_updated' => 'theme:' . __('OptionNotifyThemeUpdated', SUCURISCAN_TEXTDOMAIN),
404
- 'sucuriscan_notify_theme_deleted' => 'theme:' . __('OptionNotifyThemeDeleted', SUCURISCAN_TEXTDOMAIN),
405
  );
406
 
407
  /**
@@ -421,6 +441,7 @@ function sucuriscan_settings_alerts_events($nonce)
421
  $params['Alerts.NoAlertsVisibility'] = 'visible';
422
  unset($notify_options['sucuriscan_notify_success_login']);
423
  unset($notify_options['sucuriscan_notify_failed_login']);
 
424
  }
425
 
426
  // Process form submission to change the alert settings.
@@ -429,6 +450,11 @@ function sucuriscan_settings_alerts_events($nonce)
429
  if (SucuriScanRequest::post(':save_alert_events') !== false) {
430
  $ucounter = 0;
431
 
 
 
 
 
 
432
  foreach ($notify_options as $alert_type => $alert_label) {
433
  $option_value = SucuriScanRequest::post($alert_type, '(1|0)');
434
 
@@ -449,7 +475,7 @@ function sucuriscan_settings_alerts_events($nonce)
449
 
450
  SucuriScanEvent::reportInfoEvent($message);
451
  SucuriScanEvent::notifyEvent('plugin_change', $message);
452
- SucuriScanInterface::info(__('AlertSettingsUpdated', SUCURISCAN_TEXTDOMAIN));
453
  }
454
  }
455
  }
@@ -463,7 +489,7 @@ function sucuriscan_settings_alerts_events($nonce)
463
  /* identify the optional icon */
464
  $offset = strpos($alert_label, ':');
465
  $alert_group = substr($alert_label, 0, $offset);
466
- $alert_label = substr($alert_label, $offset+1);
467
 
468
  switch ($alert_group) {
469
  case 'user':
@@ -487,13 +513,15 @@ function sucuriscan_settings_alerts_events($nonce)
487
  break;
488
  }
489
 
490
- $params['Alerts.Events'] .=
491
- SucuriScanTemplate::getSnippet('settings-alerts-events', array(
492
- 'Event.Name' => $alert_type,
493
- 'Event.Checked' => $checked,
494
- 'Event.Label' => $alert_label,
495
- 'Event.LabelIcon' => $alert_icon,
496
- ));
 
 
497
  }
498
 
499
  return SucuriScanTemplate::getSection('settings-alerts-events', $params);
@@ -506,82 +534,78 @@ function sucuriscan_settings_alerts_events($nonce)
506
  */
507
  function sucuriscan_settings_alerts_ignore_posts()
508
  {
509
- $params = array(
510
- 'IgnoreRules.PostTypes' => '',
511
- 'IgnoreRules.ErrorVisibility' => 'hidden',
512
- );
 
 
513
 
514
  if (SucuriScanInterface::checkNonce()) {
515
  // Ignore a new event for email alerts.
516
- if ($action = SucuriScanRequest::post(':ignorerule_action', '(add|remove)')) {
517
- $ignore_rule = SucuriScanRequest::post(':ignorerule');
518
-
519
- if ($action == 'add') {
520
- if (!preg_match('/^[a-z_]+$/', $ignore_rule)) {
521
- SucuriScanInterface::error(__('OnlyLowerUppercase', SUCURISCAN_TEXTDOMAIN));
522
- } elseif (SucuriScanOption::addIgnoredEvent($ignore_rule)) {
523
- SucuriScanInterface::info(__('PostTypeIgnored', SUCURISCAN_TEXTDOMAIN));
524
- SucuriScanEvent::reportWarningEvent('Changes in <code>' . $ignore_rule . '</code> post-type will be ignored');
525
- } else {
526
- SucuriScanInterface::error(__('PostTypeFailure', SUCURISCAN_TEXTDOMAIN));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
527
  }
528
- } elseif ($action == 'remove') {
529
- SucuriScanOption::removeIgnoredEvent($ignore_rule);
530
- SucuriScanInterface::info(__('PostTypeUnignored', SUCURISCAN_TEXTDOMAIN));
531
- SucuriScanEvent::reportNoticeEvent('Changes in <code>' . $ignore_rule . '</code> post-type will not be ignored');
532
  }
 
 
 
 
533
  }
534
  }
535
 
536
  /* notifications are post updates are disabled; print error */
537
  if (SucuriScanOption::isDisabled(':notify_post_publication')) {
538
- $params['IgnoreRules.ErrorVisibility'] = 'visible';
539
- $params['IgnoreRules.PostTypes'] = sprintf(
540
- '<tr><td colspan="4">%s</td></tr>',
541
- __('NoData', SUCURISCAN_TEXTDOMAIN)
542
- );
543
 
544
  return SucuriScanTemplate::getSection('settings-alerts-ignore-posts', $params);
545
  }
546
 
547
- $post_types = SucuriScanOption::getPostTypes();
548
- $ignored_events = SucuriScanOption::getIgnoredEvents();
549
-
550
- /* include custom non-registered post-types */
551
- foreach ($ignored_events as $event => $time) {
552
- if (!array_key_exists($event, $post_types)) {
553
- $post_types[$event] = $event;
554
- }
555
- }
556
-
557
  /* Check which post-types are being ignored */
558
  foreach ($post_types as $post_type) {
559
- $post_type_title = ucwords(str_replace('_', chr(32), $post_type));
 
 
560
 
561
  if (array_key_exists($post_type, $ignored_events)) {
562
- $is_ignored_text = __('Yes', SUCURISCAN_TEXTDOMAIN);
563
- $was_ignored_at = SucuriScan::datetime($ignored_events[ $post_type ]);
564
- $is_ignored_class = 'danger';
565
- $button_action = 'remove';
566
- $button_text = __('PostTypeIgnore', SUCURISCAN_TEXTDOMAIN);
567
- } else {
568
- $is_ignored_text = __('No', SUCURISCAN_TEXTDOMAIN);
569
- $was_ignored_at = '--';
570
- $is_ignored_class = 'success';
571
- $button_action = 'add';
572
- $button_text = __('PostTypeUnignore', SUCURISCAN_TEXTDOMAIN);
573
  }
574
 
575
- $params['IgnoreRules.PostTypes'] .=
576
- SucuriScanTemplate::getSnippet('settings-alerts-ignore-posts', array(
577
- 'IgnoreRules.PostTypeTitle' => $post_type_title,
578
- 'IgnoreRules.IsIgnored' => $is_ignored_text,
579
- 'IgnoreRules.WasIgnoredAt' => $was_ignored_at,
580
- 'IgnoreRules.IsIgnoredClass' => $is_ignored_class,
581
- 'IgnoreRules.PostType' => $post_type,
582
- 'IgnoreRules.Action' => $button_action,
583
- 'IgnoreRules.ButtonText' => $button_text,
584
- ));
585
  }
586
 
587
  return SucuriScanTemplate::getSection('settings-alerts-ignore-posts', $params);
3
  /**
4
  * Code related to the settings-alerts.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
30
  * is usually the email of the website owner. The plugin allows to add more
31
  * emails to the list so the alerts are sent to other people.
32
  *
33
+ * @param bool $nonce True if the CSRF protection worked, false otherwise.
34
+ * @return string HTML for the email alert recipients.
35
  */
36
  function sucuriscan_settings_alerts_recipients($nonce)
37
  {
53
 
54
  if (SucuriScan::isValidEmail($new_email)) {
55
  $emails[] = $new_email;
56
+ $message = sprintf('The email alerts will be sent to: <code>%s</code>', $new_email);
57
 
58
  SucuriScanOption::updateOption(':notify_to', implode(',', $emails));
59
  SucuriScanEvent::reportInfoEvent('The email alerts will be sent to: ' . $new_email);
60
  SucuriScanEvent::notifyEvent('plugin_change', $message);
61
  SucuriScanInterface::info($message);
62
  } else {
63
+ SucuriScanInterface::error('Email format not supported.');
64
  }
65
  }
66
 
79
 
80
  if (!empty($deleted_emails)) {
81
  $deleted_emails_str = implode(",\x20", $deleted_emails);
82
+ $message = sprintf('These emails will stop receiving alerts: <code>%s</code>', $deleted_emails_str);
83
 
84
  SucuriScanOption::updateOption(':notify_to', implode(',', $emails));
85
  SucuriScanEvent::reportInfoEvent('These emails will stop receiving alerts: ' . $deleted_emails_str);
97
  sprintf('Test email alert sent at %s', SucuriScan::datetime()),
98
  array('Force' => true)
99
  );
100
+
101
+ SucuriScanInterface::info('A test alert was sent to your email, check your inbox');
102
  }
103
  }
104
 
105
 
106
  foreach ($emails as $email) {
107
  if (!empty($email)) {
108
+ $params['Alerts.Recipients'] .= SucuriScanTemplate::getSnippet(
109
+ 'settings-alerts-recipients',
110
+ array('Recipient.Email' => $email)
111
+ );
112
  }
113
  }
114
 
134
 
135
  if (SucuriScanInterface::checkNonce()) {
136
  // Trust and IP address to ignore alerts for a subnet.
137
+ $trust_ip = SucuriScanRequest::post(':trust_ip');
138
+
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.');
147
  } elseif ($cache->add($cache_key, $ip_info)) {
148
  SucuriScanEvent::reportWarningEvent('IP has been trusted: ' . $trust_ip);
149
+ SucuriScanInterface::info(sprintf('Events generated from this IP will be ignored: <code>%s</code>', $trust_ip));
150
  } else {
151
+ SucuriScanInterface::error('The IP address could not be added to the trusted list');
152
  }
153
  }
154
  }
155
 
156
  // Trust and IP address to ignore alerts for a subnet.
157
+ $del_trust_ip = SucuriScanRequest::post(':del_trust_ip', '_array');
158
+
159
+ if ($del_trust_ip) {
160
  foreach ($del_trust_ip as $cache_key) {
161
  $cache->delete($cache_key);
162
  }
163
 
164
+ SucuriScanInterface::info('The selected IP addresses were successfully deleted.');
165
  }
166
  }
167
 
173
  $ip_info->cidr_format = 'n/a';
174
  }
175
 
176
+ $params['TrustedIPs.List'] .= SucuriScanTemplate::getSnippet(
177
+ 'settings-alerts-trustedips',
178
+ array(
179
+ 'TrustIP.CacheKey' => $cache_key,
180
+ 'TrustIP.RemoteAddr' => SucuriScan::escape($ip_info->remote_addr),
181
+ 'TrustIP.CIDRFormat' => SucuriScan::escape($ip_info->cidr_format),
182
+ 'TrustIP.AddedAt' => SucuriScan::datetime($ip_info->added_at),
183
+ )
184
+ );
185
  }
186
 
187
  $params['TrustedIPs.NoItems.Visibility'] = 'hidden';
204
  'Alerts.CustomValue' => '',
205
  );
206
 
 
 
207
  $subjects = array(
208
+ 'Sucuri Alert, :domain, :event',
209
+ 'Sucuri Alert, :domain, :event, :remoteaddr',
210
+ 'Sucuri Alert, :domain, :event, :username',
211
+ 'Sucuri Alert, :domain, :event, :email',
212
+ 'Sucuri Alert, :event, :remoteaddr',
213
+ 'Sucuri Alert, :event',
214
  );
215
 
216
  // Process form submission to change the alert settings.
217
  if ($nonce) {
218
+ $email_subject = SucuriScanRequest::post(':email_subject');
219
+
220
+ if ($email_subject) {
221
  $current_value = SucuriScanOption::getOption(':email_subject');
222
  $new_subject = false;
223
 
238
  ) {
239
  $new_subject = trim($custom_subject);
240
  } else {
241
+ SucuriScanInterface::error('Invalid characters in the email subject.');
242
  }
243
  } elseif (is_array($subjects) && in_array($email_subject, $subjects)) {
244
  $new_subject = trim($email_subject);
251
  SucuriScanOption::updateOption(':email_subject', $new_subject);
252
  SucuriScanEvent::reportInfoEvent($message);
253
  SucuriScanEvent::notifyEvent('plugin_change', $message);
254
+ SucuriScanInterface::info('The email subject has been successfully updated');
255
  }
256
  }
257
  }
269
  $checked = '';
270
  }
271
 
272
+ $params['Alerts.Subject'] .= SucuriScanTemplate::getSnippet(
273
+ 'settings-alerts-subject',
274
+ array(
275
+ 'EmailSubject.Name' => $subject_format,
276
+ 'EmailSubject.Value' => $subject_format,
277
+ 'EmailSubject.Checked' => $checked,
278
+ )
279
+ );
280
  }
281
 
282
  if ($is_official_subject === false) {
300
  $params['Alerts.PerHour'] = '';
301
 
302
  $emails_per_hour = array(
303
+ '5' => 'Maximum 5 per hour',
304
+ '10' => 'Maximum 10 per hour',
305
+ '20' => 'Maximum 20 per hour',
306
+ '40' => 'Maximum 40 per hour',
307
+ '80' => 'Maximum 80 per hour',
308
+ '160' => 'Maximum 160 per hour',
309
+ 'unlimited' => 'Unlimited alerts per hour',
310
  );
311
 
312
  if ($nonce) {
313
  // Update the value for the maximum emails per hour.
314
+ $per_hour = SucuriScanRequest::post(':emails_per_hour');
315
+
316
+ if ($per_hour) {
317
  if (array_key_exists($per_hour, $emails_per_hour)) {
318
  $per_hour_label = strtolower($emails_per_hour[$per_hour]);
319
  $message = 'Maximum alerts per hour set to <code>' . $per_hour_label . '</code>';
321
  SucuriScanOption::updateOption(':emails_per_hour', $per_hour);
322
  SucuriScanEvent::reportInfoEvent($message);
323
  SucuriScanEvent::notifyEvent('plugin_change', $message);
324
+ SucuriScanInterface::info('The maximum number of alerts per hour has been updated');
325
  } else {
326
+ SucuriScanInterface::error('Error updating the maximum number of alerts per hour');
327
  }
328
  }
329
  }
347
  $params['Alerts.BruteForce'] = '';
348
 
349
  $max_failed_logins = array(
350
+ '30' => '30 failed logins per hour',
351
+ '60' => '60 failed logins per hour',
352
+ '120' => '120 failed logins per hour',
353
+ '240' => '240 failed logins per hour',
354
+ '480' => '480 failed logins per hour',
355
  );
356
 
357
  if ($nonce) {
358
  // Update the maximum failed logins per hour before consider it a brute-force attack.
359
+ $maximum = SucuriScanRequest::post(':maximum_failed_logins');
360
+
361
+ if ($maximum) {
362
  if (array_key_exists($maximum, $max_failed_logins)) {
363
  $message = 'Consider brute-force attack after <code>' . $maximum . '</code> failed logins per hour';
364
 
365
  SucuriScanOption::updateOption(':maximum_failed_logins', $maximum);
366
  SucuriScanEvent::reportInfoEvent($message);
367
  SucuriScanEvent::notifyEvent('plugin_change', $message);
368
+ SucuriScanInterface::info(
369
+ 'The plugin will assume that your website is under a brute'
370
+ . '-force attack after ' . $maximum . ' failed logins are '
371
+ . 'detected during the same hour'
372
+ );
373
  } else {
374
+ SucuriScanInterface::error('Invalid number of failed logins per hour');
375
  }
376
  }
377
  }
396
  $params['Alerts.NoAlertsVisibility'] = 'hidden';
397
 
398
  $notify_options = array(
399
+ 'sucuriscan_notify_plugin_change' => 'setting:' . 'Receive email alerts for changes in the settings of the Sucuri plugin',
400
+ 'sucuriscan_prettify_mails' => 'setting:' . 'Receive email alerts in HTML <em>(there may be issues with some mail services)</em>',
401
+ 'sucuriscan_use_wpmail' => 'setting:' . 'Use WordPress functions to send mails <em>(uncheck to use native PHP functions)</em>',
402
+ 'sucuriscan_lastlogin_redirection' => 'setting:' . 'Allow redirection after login to report the last-login information',
403
+ 'sucuriscan_notify_scan_checksums' => 'setting:' . 'Receive email alerts for core integrity checks',
404
+ 'sucuriscan_notify_available_updates' => 'setting:' . 'Receive email alerts for available updates',
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',
412
+ 'sucuriscan_notify_settings_updated' => 'setting:' . 'Receive email alerts when your website settings are updated',
413
+ 'sucuriscan_notify_theme_editor' => 'setting:' . 'Receive email alerts when a file is modified with theme/plugin editor',
414
+ 'sucuriscan_notify_plugin_installed' => 'plugin:' . 'Receive email alerts when a <b>plugin is installed</b>',
415
+ 'sucuriscan_notify_plugin_activated' => 'plugin:' . 'Receive email alerts when a <b>plugin is activated</b>',
416
+ 'sucuriscan_notify_plugin_deactivated' => 'plugin:' . 'Receive email alerts when a <b>plugin is deactivated</b>',
417
+ 'sucuriscan_notify_plugin_updated' => 'plugin:' . 'Receive email alerts when a <b>plugin is updated</b>',
418
+ 'sucuriscan_notify_plugin_deleted' => 'plugin:' . 'Receive email alerts when a <b>plugin is deleted</b>',
419
+ 'sucuriscan_notify_widget_added' => 'widget:' . 'Receive email alerts when a <b>widget is added</b> to a sidebar',
420
+ 'sucuriscan_notify_widget_deleted' => 'widget:' . 'Receive email alerts when a <b>widget is deleted</b> from a sidebar',
421
+ 'sucuriscan_notify_theme_installed' => 'theme:' . 'Receive email alerts when a <b>theme is installed</b>',
422
+ 'sucuriscan_notify_theme_activated' => 'theme:' . 'Receive email alerts when a <b>theme is activated</b>',
423
+ 'sucuriscan_notify_theme_updated' => 'theme:' . 'Receive email alerts when a <b>theme is updated</b>',
424
+ 'sucuriscan_notify_theme_deleted' => 'theme:' . 'Receive email alerts when a <b>theme is deleted</b>',
425
  );
426
 
427
  /**
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
  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
 
475
 
476
  SucuriScanEvent::reportInfoEvent($message);
477
  SucuriScanEvent::notifyEvent('plugin_change', $message);
478
+ SucuriScanInterface::info('The alert settings have been updated');
479
  }
480
  }
481
  }
489
  /* identify the optional icon */
490
  $offset = strpos($alert_label, ':');
491
  $alert_group = substr($alert_label, 0, $offset);
492
+ $alert_label = substr($alert_label, $offset + 1);
493
 
494
  switch ($alert_group) {
495
  case 'user':
513
  break;
514
  }
515
 
516
+ $params['Alerts.Events'] .= SucuriScanTemplate::getSnippet(
517
+ 'settings-alerts-events',
518
+ array(
519
+ 'Event.Name' => $alert_type,
520
+ 'Event.Checked' => $checked,
521
+ 'Event.Label' => $alert_label,
522
+ 'Event.LabelIcon' => $alert_icon,
523
+ )
524
+ );
525
  }
526
 
527
  return SucuriScanTemplate::getSection('settings-alerts-events', $params);
534
  */
535
  function sucuriscan_settings_alerts_ignore_posts()
536
  {
537
+ $params = array();
538
+ $post_types = SucuriScanOption::getPostTypes();
539
+ $ignored_events = SucuriScanOption::getIgnoredEvents();
540
+
541
+ $params['PostTypes.List'] = '';
542
+ $params['PostTypes.ErrorVisibility'] = 'hidden';
543
 
544
  if (SucuriScanInterface::checkNonce()) {
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)) {
552
+ SucuriScanInterface::error('Only lowercase letters, underscores and hyphens are allowed.');
553
+ } elseif (array_key_exists($ignore_rule, $ignored_events)) {
554
+ SucuriScanInterface::error('The post-type is already being ignored (duplicate).');
555
+ } else {
556
+ $ignored_events[$ignore_rule] = time();
557
+
558
+ SucuriScanInterface::info('Post-type has been successfully ignored.');
559
+ SucuriScanOption::updateOption(':ignored_events', $ignored_events);
560
+ SucuriScanEvent::reportWarningEvent('Changes in <code>' . $ignore_rule . '</code> post-type will be ignored');
561
+ }
562
+ }
563
+
564
+ if ($action === 'batch') {
565
+ /* reset current data to start all over again */
566
+ $ignored_events = array();
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
  }
574
+
575
+ SucuriScanInterface::info('List of monitored post-types has been updated.');
576
+ SucuriScanOption::updateOption(':ignored_events', $ignored_events);
577
+ SucuriScanEvent::reportWarningEvent('List of monitored post-types has been updated');
578
  }
579
  }
580
 
581
  /* notifications are post updates are disabled; print error */
582
  if (SucuriScanOption::isDisabled(':notify_post_publication')) {
583
+ $params['PostTypes.ErrorVisibility'] = 'visible';
584
+ $params['PostTypes.List'] = '<tr><td colspan="4">no data available</td></tr>';
 
 
 
585
 
586
  return SucuriScanTemplate::getSection('settings-alerts-ignore-posts', $params);
587
  }
588
 
 
 
 
 
 
 
 
 
 
 
589
  /* Check which post-types are being ignored */
590
  foreach ($post_types as $post_type) {
591
+ $was_ignored_at = '--';
592
+ $selected = 'checked="checked"';
593
+ $post_type_title = ucwords(str_replace('_', "\x20", $post_type));
594
 
595
  if (array_key_exists($post_type, $ignored_events)) {
596
+ $was_ignored_at = SucuriScan::datetime($ignored_events[$post_type]);
597
+ $selected = ''; /* uncheck the HTML checkbox */
 
 
 
 
 
 
 
 
 
598
  }
599
 
600
+ $params['PostTypes.List'] .= SucuriScanTemplate::getSnippet(
601
+ 'settings-alerts-ignore-posts',
602
+ array(
603
+ 'PostTypes.Selected' => $selected,
604
+ 'PostTypes.UniqueID' => $post_type,
605
+ 'PostTypes.Title' => $post_type_title,
606
+ 'PostTypes.IgnoredAt' => $was_ignored_at,
607
+ )
608
+ );
 
609
  }
610
 
611
  return SucuriScanTemplate::getSection('settings-alerts-ignore-posts', $params);
src/settings-apiservice.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the settings-apiservice.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage settings-apiservice.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -27,8 +33,8 @@ function sucuriscan_settings_apiservice_status($nonce)
27
  $params = array();
28
 
29
  $params['ApiStatus.StatusNum'] = '1';
30
- $params['ApiStatus.Status'] = __('Enabled', SUCURISCAN_TEXTDOMAIN);
31
- $params['ApiStatus.SwitchText'] = __('Disable', SUCURISCAN_TEXTDOMAIN);
32
  $params['ApiStatus.SwitchValue'] = 'disable';
33
  $params['ApiStatus.WarningVisibility'] = 'visible';
34
  $params['ApiStatus.ErrorVisibility'] = 'hidden';
@@ -36,14 +42,16 @@ function sucuriscan_settings_apiservice_status($nonce)
36
 
37
  if ($nonce) {
38
  // Enable or disable the API service communication.
39
- if ($api_service = SucuriScanRequest::post(':api_service', '(en|dis)able')) {
 
 
40
  $action_d = $api_service . 'd';
41
  $message = 'API service communication was <code>' . $action_d . '</code>';
42
 
43
  SucuriScanEvent::reportInfoEvent($message);
44
  SucuriScanEvent::notifyEvent('plugin_change', $message);
45
  SucuriScanOption::updateOption(':api_service', $action_d);
46
- SucuriScanInterface::info(__('APIServiceChanged', SUCURISCAN_TEXTDOMAIN));
47
  }
48
  }
49
 
@@ -51,8 +59,8 @@ function sucuriscan_settings_apiservice_status($nonce)
51
 
52
  if ($api_service === 'disabled') {
53
  $params['ApiStatus.StatusNum'] = '0';
54
- $params['ApiStatus.Status'] = __('Disabled', SUCURISCAN_TEXTDOMAIN);
55
- $params['ApiStatus.SwitchText'] = __('Enable', SUCURISCAN_TEXTDOMAIN);
56
  $params['ApiStatus.SwitchValue'] = 'enable';
57
  $params['ApiStatus.WarningVisibility'] = 'hidden';
58
  $params['ApiStatus.ErrorVisibility'] = 'visible';
@@ -119,14 +127,14 @@ function sucuriscan_settings_apiservice_checksums($nonce)
119
  $message = 'Core integrity API changed: ' . SucuriScanAPI::checksumAPI();
120
  SucuriScanEvent::reportInfoEvent($message);
121
  SucuriScanEvent::notifyEvent('plugin_change', $message);
122
- SucuriScanInterface::info(__('ChecksumsAPIChanged', SUCURISCAN_TEXTDOMAIN));
123
  } else {
124
  SucuriScanOption::deleteOption(':checksum_api');
125
 
126
  $message = 'Core integrity API changed: ' . SucuriScanAPI::checksumAPI();
127
  SucuriScanEvent::reportInfoEvent($message);
128
  SucuriScanEvent::notifyEvent('plugin_change', $message);
129
- SucuriScanInterface::info(__('ChecksumsAPIChanged', SUCURISCAN_TEXTDOMAIN));
130
  }
131
  }
132
 
3
  /**
4
  * Code related to the settings-apiservice.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
33
  $params = array();
34
 
35
  $params['ApiStatus.StatusNum'] = '1';
36
+ $params['ApiStatus.Status'] = 'Enabled';
37
+ $params['ApiStatus.SwitchText'] = 'Disable';
38
  $params['ApiStatus.SwitchValue'] = 'disable';
39
  $params['ApiStatus.WarningVisibility'] = 'visible';
40
  $params['ApiStatus.ErrorVisibility'] = 'hidden';
42
 
43
  if ($nonce) {
44
  // Enable or disable the API service communication.
45
+ $api_service = SucuriScanRequest::post(':api_service', '(en|dis)able');
46
+
47
+ if ($api_service) {
48
  $action_d = $api_service . 'd';
49
  $message = 'API service communication was <code>' . $action_d . '</code>';
50
 
51
  SucuriScanEvent::reportInfoEvent($message);
52
  SucuriScanEvent::notifyEvent('plugin_change', $message);
53
  SucuriScanOption::updateOption(':api_service', $action_d);
54
+ SucuriScanInterface::info('The status of the API service has been changed');
55
  }
56
  }
57
 
59
 
60
  if ($api_service === 'disabled') {
61
  $params['ApiStatus.StatusNum'] = '0';
62
+ $params['ApiStatus.Status'] = 'Disabled';
63
+ $params['ApiStatus.SwitchText'] = 'Enable';
64
  $params['ApiStatus.SwitchValue'] = 'enable';
65
  $params['ApiStatus.WarningVisibility'] = 'hidden';
66
  $params['ApiStatus.ErrorVisibility'] = 'visible';
127
  $message = 'Core integrity API changed: ' . SucuriScanAPI::checksumAPI();
128
  SucuriScanEvent::reportInfoEvent($message);
129
  SucuriScanEvent::notifyEvent('plugin_change', $message);
130
+ SucuriScanInterface::info('The URL to retrieve the WordPress checksums has been changed');
131
  } else {
132
  SucuriScanOption::deleteOption(':checksum_api');
133
 
134
  $message = 'Core integrity API changed: ' . SucuriScanAPI::checksumAPI();
135
  SucuriScanEvent::reportInfoEvent($message);
136
  SucuriScanEvent::notifyEvent('plugin_change', $message);
137
+ SucuriScanInterface::info('The URL to retrieve the WordPress checksums has been changed');
138
  }
139
  }
140
 
src/settings-general.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the settings-general.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage settings-general.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -31,13 +37,13 @@ function sucuriscan_settings_general_resetoptions($nonce)
31
  if (intval($process) === 1) {
32
  $message = 'Local security logs, hardening and settings were deleted';
33
 
34
- sucuriscan_deactivate(); /* simulate plugin deactivation */
35
 
36
  SucuriScanEvent::reportCriticalEvent($message);
37
  SucuriScanEvent::notifyEvent('plugin_change', $message);
38
- SucuriScanInterface::info(__('PluginResetSuccess', SUCURISCAN_TEXTDOMAIN));
39
  } else {
40
- SucuriScanInterface::error(__('ConfirmOperation', SUCURISCAN_TEXTDOMAIN));
41
  }
42
  }
43
 
@@ -62,19 +68,22 @@ function sucuriscan_settings_general_apikey($nonce)
62
 
63
  if ($nonce) {
64
  // Remove API key from the local storage.
 
65
  if (SucuriScanRequest::post(':remove_api_key') !== false
66
  && SucuriScanAPI::setPluginKey('') !== false
67
  ) {
68
  wp_clear_scheduled_hook('sucuriscan_scheduled_scan');
69
 
 
70
  SucuriScanEvent::reportCriticalEvent('Sucuri API key has been deleted.');
71
  SucuriScanEvent::notifyEvent('plugin_change', 'Sucuri API key removed');
72
- SucuriScanInterface::info('Sucuri API key has been deleted <code>'
73
- . SucuriScan::escape(SucuriScanAPI::getPluginKey()) . '</code>');
74
  }
75
 
76
  // Save API key after it was recovered by the administrator.
77
- if ($api_key = SucuriScanRequest::post(':manual_api_key')) {
 
 
78
  SucuriScanAPI::setPluginKey($api_key, true);
79
  SucuriScanEvent::installScheduledTask();
80
  SucuriScanEvent::reportInfoEvent('Sucuri API key was added manually.');
@@ -88,9 +97,10 @@ function sucuriscan_settings_general_apikey($nonce)
88
  if ($user_obj && user_can($user_obj, 'administrator')) {
89
  // Send request to generate new API key or display form to set manually.
90
  if (SucuriScanAPI::registerSite($user_obj->user_email)) {
91
- $api_registered_modal = SucuriScanTemplate::getModal('settings-apiregistered', array(
92
- 'Title' => __('SiteWasRegistered', SUCURISCAN_TEXTDOMAIN),
93
- ));
 
94
  } else {
95
  $display_manual_key_form = true;
96
  }
@@ -111,9 +121,10 @@ function sucuriscan_settings_general_apikey($nonce)
111
  $api_key = SucuriScanAPI::getPluginKey();
112
 
113
  if (SucuriScanRequest::get('recover') !== false) {
114
- $api_recovery_modal = SucuriScanTemplate::getModal('settings-apirecovery', array(
115
- 'Title' => __('APIKeyRecovery', SUCURISCAN_TEXTDOMAIN),
116
- ));
 
117
  }
118
 
119
  // Check whether the domain name is valid or not.
@@ -123,7 +134,7 @@ function sucuriscan_settings_general_apikey($nonce)
123
  $invalid_domain = (bool) ($domain_address === $clean_domain);
124
  }
125
 
126
- $params['APIKey'] = (!$api_key ? __('NotSet', SUCURISCAN_TEXTDOMAIN) : $api_key);
127
  $params['APIKey.RecoverVisibility'] = SucuriScanTemplate::visibility(!$api_key);
128
  $params['APIKey.ManualKeyFormVisibility'] = SucuriScanTemplate::visibility($display_manual_key_form);
129
  $params['APIKey.RemoveVisibility'] = SucuriScanTemplate::visibility((bool) $api_key);
@@ -164,7 +175,9 @@ function sucuriscan_settings_general_datastorage($nonce)
164
  $params['Storage.Path'] = SucuriScan::dataStorePath();
165
 
166
  if ($nonce) {
167
- if ($filenames = SucuriScanRequest::post(':filename', '_array')) {
 
 
168
  $deleted = 0;
169
 
170
  foreach ($filenames as $filename) {
@@ -187,11 +200,13 @@ function sucuriscan_settings_general_datastorage($nonce)
187
  }
188
  }
189
 
190
- SucuriScanInterface::info(sprintf(
191
- __('NFilesWereDeleted', SUCURISCAN_TEXTDOMAIN),
192
- $deleted,
193
- count($filenames)
194
- ));
 
 
195
  }
196
  }
197
 
@@ -200,20 +215,20 @@ function sucuriscan_settings_general_datastorage($nonce)
200
  $fname = ($name ? sprintf('sucuri-%s.php', $name) : '');
201
  $fpath = SucuriScan::dataStorePath($fname);
202
  $disabled = 'disabled="disabled"';
203
- $iswritable = __('NotWritable', SUCURISCAN_TEXTDOMAIN);
204
- $exists = __('DoesNotExist', SUCURISCAN_TEXTDOMAIN);
205
  $labelExistence = 'danger';
206
  $labelWritability = 'default';
207
 
208
  if (file_exists($fpath)) {
209
  $fsize = @filesize($fpath);
210
- $exists = __('Exists', SUCURISCAN_TEXTDOMAIN);
211
  $labelExistence = 'success';
212
  $labelWritability = 'danger';
213
 
214
  if (is_writable($fpath)) {
215
  $disabled = ''; /* Allow file deletion */
216
- $iswritable = __('Writable', SUCURISCAN_TEXTDOMAIN);
217
  $labelWritability = 'success';
218
  }
219
  }
@@ -228,8 +243,8 @@ function sucuriscan_settings_general_datastorage($nonce)
228
  $params['Storage.Writability'] = $labelWritability;
229
 
230
  if (is_dir($fpath)) {
 
231
  $params['Storage.DisabledInput'] = 'disabled="disabled"';
232
- $params['Storage.Filesize'] = '' /* empty */;
233
  }
234
 
235
  $params['Storage.Files'] .= SucuriScanTemplate::getSnippet('settings-general-datastorage', $params);
@@ -274,8 +289,8 @@ function sucuriscan_settings_general_selfhosting($nonce)
274
  $params = array();
275
 
276
  $params['SelfHosting.DisabledVisibility'] = 'visible';
277
- $params['SelfHosting.Status'] = __('Enabled', SUCURISCAN_TEXTDOMAIN);
278
- $params['SelfHosting.SwitchText'] = __('Disable', SUCURISCAN_TEXTDOMAIN);
279
  $params['SelfHosting.SwitchValue'] = 'disable';
280
  $params['SelfHosting.FpathVisibility'] = 'hidden';
281
  $params['SelfHosting.Fpath'] = '';
@@ -286,29 +301,29 @@ function sucuriscan_settings_general_selfhosting($nonce)
286
 
287
  if ($monitor_fpath !== false) {
288
  if (empty($monitor_fpath)) {
289
- $message = 'Log exporter was disabled.';
290
 
291
  SucuriScanEvent::reportInfoEvent($message);
292
  SucuriScanOption::deleteOption(':selfhosting_fpath');
293
  SucuriScanOption::updateOption(':selfhosting_monitor', 'disabled');
294
  SucuriScanEvent::notifyEvent('plugin_change', $message);
295
- SucuriScanInterface::info(__('SelfHostingDisabled', SUCURISCAN_TEXTDOMAIN));
296
  } elseif (strpos($monitor_fpath, $_SERVER['DOCUMENT_ROOT']) !== false) {
297
- SucuriScanInterface::error(__('AvoidDocumentRoot', SUCURISCAN_TEXTDOMAIN));
298
  } elseif (file_exists($monitor_fpath)) {
299
- SucuriScanInterface::error(__('AvoidFileOverride', SUCURISCAN_TEXTDOMAIN));
300
  } elseif (!is_writable(dirname($monitor_fpath))) {
301
- SucuriScanInterface::error(__('ParentNotWritable', SUCURISCAN_TEXTDOMAIN));
302
  } else {
303
  @file_put_contents($monitor_fpath, '', LOCK_EX);
304
 
305
- $message = 'Log exporter file path was set correctly.';
306
 
307
  SucuriScanEvent::reportInfoEvent($message);
308
  SucuriScanOption::updateOption(':selfhosting_monitor', 'enabled');
309
  SucuriScanOption::updateOption(':selfhosting_fpath', $monitor_fpath);
310
  SucuriScanEvent::notifyEvent('plugin_change', $message);
311
- SucuriScanInterface::info(__('SelfHostingEnabled', SUCURISCAN_TEXTDOMAIN));
312
  }
313
  }
314
  }
@@ -317,8 +332,8 @@ function sucuriscan_settings_general_selfhosting($nonce)
317
  $monitor_fpath = SucuriScanOption::getOption(':selfhosting_fpath');
318
 
319
  if ($monitor === 'disabled') {
320
- $params['SelfHosting.Status'] = __('Disabled', SUCURISCAN_TEXTDOMAIN);
321
- $params['SelfHosting.SwitchText'] = __('Enable', SUCURISCAN_TEXTDOMAIN);
322
  $params['SelfHosting.SwitchValue'] = 'enable';
323
  }
324
 
@@ -340,8 +355,8 @@ function sucuriscan_settings_general_selfhosting($nonce)
340
  function sucuriscan_settings_general_reverseproxy($nonce)
341
  {
342
  $params = array(
343
- 'ReverseProxyStatus' => __('Enabled', SUCURISCAN_TEXTDOMAIN),
344
- 'ReverseProxySwitchText' => __('Disable', SUCURISCAN_TEXTDOMAIN),
345
  'ReverseProxySwitchValue' => 'disable',
346
  );
347
 
@@ -361,8 +376,8 @@ function sucuriscan_settings_general_reverseproxy($nonce)
361
  }
362
 
363
  if (SucuriScanOption::isDisabled(':revproxy')) {
364
- $params['ReverseProxyStatus'] = __('Disabled', SUCURISCAN_TEXTDOMAIN);
365
- $params['ReverseProxySwitchText'] = __('Enable', SUCURISCAN_TEXTDOMAIN);
366
  $params['ReverseProxySwitchValue'] = 'enable';
367
  }
368
 
@@ -378,17 +393,17 @@ function sucuriscan_settings_general_reverseproxy($nonce)
378
  function sucuriscan_settings_general_ipdiscoverer($nonce)
379
  {
380
  $params = array(
381
- 'TopLevelDomain' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
382
- 'WebsiteHostName' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
383
- 'WebsiteHostAddress' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
384
- 'IsUsingFirewall' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
385
- 'WebsiteURL' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
386
  'RemoteAddress' => '127.0.0.1',
387
  'RemoteAddressHeader' => 'INVALID',
388
  'AddrHeaderOptions' => '',
389
  /* Switch form information. */
390
- 'DnsLookupsStatus' => __('Enabled', SUCURISCAN_TEXTDOMAIN),
391
- 'DnsLookupsSwitchText' => __('Disable', SUCURISCAN_TEXTDOMAIN),
392
  'DnsLookupsSwitchValue' => 'disable',
393
  );
394
 
@@ -407,7 +422,7 @@ function sucuriscan_settings_general_ipdiscoverer($nonce)
407
  SucuriScanOption::updateOption(':dns_lookups', $action_d);
408
  SucuriScanEvent::reportInfoEvent($message);
409
  SucuriScanEvent::notifyEvent('plugin_change', $message);
410
- SucuriScanInterface::info(__('DNSLookupStatus', SUCURISCAN_TEXTDOMAIN));
411
  }
412
 
413
  if ($addr_header) {
@@ -422,8 +437,8 @@ function sucuriscan_settings_general_ipdiscoverer($nonce)
422
  }
423
 
424
  if (SucuriScanOption::isDisabled(':dns_lookups')) {
425
- $params['DnsLookupsStatus'] = __('Disabled', SUCURISCAN_TEXTDOMAIN);
426
- $params['DnsLookupsSwitchText'] = __('Enable', SUCURISCAN_TEXTDOMAIN);
427
  $params['DnsLookupsSwitchValue'] = 'enable';
428
  }
429
 
@@ -440,9 +455,7 @@ function sucuriscan_settings_general_ipdiscoverer($nonce)
440
  $allowed_headers, /* list is limited to a few options */
441
  SucuriScanOption::getOption(':addr_header')
442
  );
443
- $params['IsUsingFirewall'] = ($proxy_info['status']
444
- ? __('Active', SUCURISCAN_TEXTDOMAIN)
445
- : __('NotActive', SUCURISCAN_TEXTDOMAIN));
446
 
447
  if ($base_domain !== $proxy_info['http_host']) {
448
  $params['TopLevelDomain'] = sprintf('%s (%s)', $params['TopLevelDomain'], $base_domain);
@@ -472,7 +485,6 @@ function sucuriscan_settings_general_importexport($nonce)
472
  ':email_subject',
473
  ':emails_per_hour',
474
  ':ignored_events',
475
- ':language',
476
  ':lastlogin_redirection',
477
  ':maximum_failed_logins',
478
  ':notify_available_updates',
@@ -537,16 +549,18 @@ function sucuriscan_settings_general_importexport($nonce)
537
  $count++;
538
  }
539
 
540
- SucuriScanInterface::info(sprintf(
541
- __('ImportCount', SUCURISCAN_TEXTDOMAIN),
542
- $count,
543
- $total
544
- ));
 
 
545
  } else {
546
- SucuriScanInterface::error(__('IncorrectEncoding', SUCURISCAN_TEXTDOMAIN));
547
  }
548
  } else {
549
- SucuriScanInterface::error(__('ConfirmOperation', SUCURISCAN_TEXTDOMAIN));
550
  }
551
  }
552
 
@@ -598,7 +612,7 @@ function sucuriscan_settings_general_timezone($nonce)
598
  SucuriScanOption::updateOption(':timezone', $timezone);
599
  SucuriScanEvent::reportInfoEvent($message);
600
  SucuriScanEvent::notifyEvent('plugin_change', $message);
601
- SucuriScanInterface::info(__('TimezoneStatus', SUCURISCAN_TEXTDOMAIN));
602
  }
603
  }
604
 
3
  /**
4
  * Code related to the settings-general.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
37
  if (intval($process) === 1) {
38
  $message = 'Local security logs, hardening and settings were deleted';
39
 
40
+ sucuriscanResetAndDeactivate(); /* simulate plugin deactivation */
41
 
42
  SucuriScanEvent::reportCriticalEvent($message);
43
  SucuriScanEvent::notifyEvent('plugin_change', $message);
44
+ SucuriScanInterface::info('Local security logs, hardening and settings were deleted');
45
  } else {
46
+ SucuriScanInterface::error('You need to confirm that you understand the risk of this operation.');
47
  }
48
  }
49
 
68
 
69
  if ($nonce) {
70
  // Remove API key from the local storage.
71
+ $api_key = SucuriScanAPI::getPluginKey();
72
  if (SucuriScanRequest::post(':remove_api_key') !== false
73
  && SucuriScanAPI::setPluginKey('') !== false
74
  ) {
75
  wp_clear_scheduled_hook('sucuriscan_scheduled_scan');
76
 
77
+ $api_key = SucuriScan::escape($api_key);
78
  SucuriScanEvent::reportCriticalEvent('Sucuri API key has been deleted.');
79
  SucuriScanEvent::notifyEvent('plugin_change', 'Sucuri API key removed');
80
+ SucuriScanInterface::info('Sucuri API key has been deleted <code>' . $api_key . '</code>');
 
81
  }
82
 
83
  // Save API key after it was recovered by the administrator.
84
+ $api_key = SucuriScanRequest::post(':manual_api_key');
85
+
86
+ if ($api_key) {
87
  SucuriScanAPI::setPluginKey($api_key, true);
88
  SucuriScanEvent::installScheduledTask();
89
  SucuriScanEvent::reportInfoEvent('Sucuri API key was added manually.');
97
  if ($user_obj && user_can($user_obj, 'administrator')) {
98
  // Send request to generate new API key or display form to set manually.
99
  if (SucuriScanAPI::registerSite($user_obj->user_email)) {
100
+ $api_registered_modal = SucuriScanTemplate::getModal(
101
+ 'settings-apiregistered',
102
+ array('Title' => 'Site registered successfully')
103
+ );
104
  } else {
105
  $display_manual_key_form = true;
106
  }
121
  $api_key = SucuriScanAPI::getPluginKey();
122
 
123
  if (SucuriScanRequest::get('recover') !== false) {
124
+ $api_recovery_modal = SucuriScanTemplate::getModal(
125
+ 'settings-apirecovery',
126
+ array('Title' => 'Plugin API Key Recovery')
127
+ );
128
  }
129
 
130
  // Check whether the domain name is valid or not.
134
  $invalid_domain = (bool) ($domain_address === $clean_domain);
135
  }
136
 
137
+ $params['APIKey'] = (!$api_key ? '(not set)' : $api_key);
138
  $params['APIKey.RecoverVisibility'] = SucuriScanTemplate::visibility(!$api_key);
139
  $params['APIKey.ManualKeyFormVisibility'] = SucuriScanTemplate::visibility($display_manual_key_form);
140
  $params['APIKey.RemoveVisibility'] = SucuriScanTemplate::visibility((bool) $api_key);
175
  $params['Storage.Path'] = SucuriScan::dataStorePath();
176
 
177
  if ($nonce) {
178
+ $filenames = SucuriScanRequest::post(':filename', '_array');
179
+
180
+ if ($filenames) {
181
  $deleted = 0;
182
 
183
  foreach ($filenames as $filename) {
200
  }
201
  }
202
 
203
+ SucuriScanInterface::info(
204
+ sprintf(
205
+ '%d out of %d files has been deleted',
206
+ $deleted,
207
+ count($filenames)
208
+ )
209
+ );
210
  }
211
  }
212
 
215
  $fname = ($name ? sprintf('sucuri-%s.php', $name) : '');
216
  $fpath = SucuriScan::dataStorePath($fname);
217
  $disabled = 'disabled="disabled"';
218
+ $iswritable = 'Not Writable';
219
+ $exists = 'Does Not Exist';
220
  $labelExistence = 'danger';
221
  $labelWritability = 'default';
222
 
223
  if (file_exists($fpath)) {
224
  $fsize = @filesize($fpath);
225
+ $exists = 'Exists';
226
  $labelExistence = 'success';
227
  $labelWritability = 'danger';
228
 
229
  if (is_writable($fpath)) {
230
  $disabled = ''; /* Allow file deletion */
231
+ $iswritable = 'Writable';
232
  $labelWritability = 'success';
233
  }
234
  }
243
  $params['Storage.Writability'] = $labelWritability;
244
 
245
  if (is_dir($fpath)) {
246
+ $params['Storage.Filesize'] = '';
247
  $params['Storage.DisabledInput'] = 'disabled="disabled"';
 
248
  }
249
 
250
  $params['Storage.Files'] .= SucuriScanTemplate::getSnippet('settings-general-datastorage', $params);
289
  $params = array();
290
 
291
  $params['SelfHosting.DisabledVisibility'] = 'visible';
292
+ $params['SelfHosting.Status'] = 'Enabled';
293
+ $params['SelfHosting.SwitchText'] = 'Disable';
294
  $params['SelfHosting.SwitchValue'] = 'disable';
295
  $params['SelfHosting.FpathVisibility'] = 'hidden';
296
  $params['SelfHosting.Fpath'] = '';
301
 
302
  if ($monitor_fpath !== false) {
303
  if (empty($monitor_fpath)) {
304
+ $message = 'Log exporter was disabled';
305
 
306
  SucuriScanEvent::reportInfoEvent($message);
307
  SucuriScanOption::deleteOption(':selfhosting_fpath');
308
  SucuriScanOption::updateOption(':selfhosting_monitor', 'disabled');
309
  SucuriScanEvent::notifyEvent('plugin_change', $message);
310
+ SucuriScanInterface::info('The log exporter feature has been disabled');
311
  } elseif (strpos($monitor_fpath, $_SERVER['DOCUMENT_ROOT']) !== false) {
312
+ SucuriScanInterface::error('File should not be publicly accessible.');
313
  } elseif (file_exists($monitor_fpath)) {
314
+ SucuriScanInterface::error('File already exists and will not be overwritten.');
315
  } elseif (!is_writable(dirname($monitor_fpath))) {
316
+ SucuriScanInterface::error('File parent directory is not writable.');
317
  } else {
318
  @file_put_contents($monitor_fpath, '', LOCK_EX);
319
 
320
+ $message = 'Log exporter file path was correctly set';
321
 
322
  SucuriScanEvent::reportInfoEvent($message);
323
  SucuriScanOption::updateOption(':selfhosting_monitor', 'enabled');
324
  SucuriScanOption::updateOption(':selfhosting_fpath', $monitor_fpath);
325
  SucuriScanEvent::notifyEvent('plugin_change', $message);
326
+ SucuriScanInterface::info('The log exporter feature has been enabled and the data file was successfully set.');
327
  }
328
  }
329
  }
332
  $monitor_fpath = SucuriScanOption::getOption(':selfhosting_fpath');
333
 
334
  if ($monitor === 'disabled') {
335
+ $params['SelfHosting.Status'] = 'Disabled';
336
+ $params['SelfHosting.SwitchText'] = 'Enable';
337
  $params['SelfHosting.SwitchValue'] = 'enable';
338
  }
339
 
355
  function sucuriscan_settings_general_reverseproxy($nonce)
356
  {
357
  $params = array(
358
+ 'ReverseProxyStatus' => 'Enabled',
359
+ 'ReverseProxySwitchText' => 'Disable',
360
  'ReverseProxySwitchValue' => 'disable',
361
  );
362
 
376
  }
377
 
378
  if (SucuriScanOption::isDisabled(':revproxy')) {
379
+ $params['ReverseProxyStatus'] = 'Disabled';
380
+ $params['ReverseProxySwitchText'] = 'Enable';
381
  $params['ReverseProxySwitchValue'] = 'enable';
382
  }
383
 
393
  function sucuriscan_settings_general_ipdiscoverer($nonce)
394
  {
395
  $params = array(
396
+ 'TopLevelDomain' => 'unknown',
397
+ 'WebsiteHostName' => 'unknown',
398
+ 'WebsiteHostAddress' => 'unknown',
399
+ 'IsUsingFirewall' => 'unknown',
400
+ 'WebsiteURL' => 'unknown',
401
  'RemoteAddress' => '127.0.0.1',
402
  'RemoteAddressHeader' => 'INVALID',
403
  'AddrHeaderOptions' => '',
404
  /* Switch form information. */
405
+ 'DnsLookupsStatus' => 'Enabled',
406
+ 'DnsLookupsSwitchText' => 'Disable',
407
  'DnsLookupsSwitchValue' => 'disable',
408
  );
409
 
422
  SucuriScanOption::updateOption(':dns_lookups', $action_d);
423
  SucuriScanEvent::reportInfoEvent($message);
424
  SucuriScanEvent::notifyEvent('plugin_change', $message);
425
+ SucuriScanInterface::info('The status of the DNS lookups for the reverse proxy detection has been changed');
426
  }
427
 
428
  if ($addr_header) {
437
  }
438
 
439
  if (SucuriScanOption::isDisabled(':dns_lookups')) {
440
+ $params['DnsLookupsStatus'] = 'Disabled';
441
+ $params['DnsLookupsSwitchText'] = 'Enable';
442
  $params['DnsLookupsSwitchValue'] = 'enable';
443
  }
444
 
455
  $allowed_headers, /* list is limited to a few options */
456
  SucuriScanOption::getOption(':addr_header')
457
  );
458
+ $params['IsUsingFirewall'] = $proxy_info['status'] ? 'active' : 'not active';
 
 
459
 
460
  if ($base_domain !== $proxy_info['http_host']) {
461
  $params['TopLevelDomain'] = sprintf('%s (%s)', $params['TopLevelDomain'], $base_domain);
485
  ':email_subject',
486
  ':emails_per_hour',
487
  ':ignored_events',
 
488
  ':lastlogin_redirection',
489
  ':maximum_failed_logins',
490
  ':notify_available_updates',
549
  $count++;
550
  }
551
 
552
+ SucuriScanInterface::info(
553
+ sprintf(
554
+ '%d out of %d option have been successfully imported',
555
+ $count,
556
+ $total
557
+ )
558
+ );
559
  } else {
560
+ SucuriScanInterface::error('Data is incorrectly encoded');
561
  }
562
  } else {
563
+ SucuriScanInterface::error('You need to confirm that you understand the risk of this operation.');
564
  }
565
  }
566
 
612
  SucuriScanOption::updateOption(':timezone', $timezone);
613
  SucuriScanEvent::reportInfoEvent($message);
614
  SucuriScanEvent::notifyEvent('plugin_change', $message);
615
+ SucuriScanInterface::info('The timezone for the date and time in the audit logs has been changed');
616
  }
617
  }
618
 
src/settings-hardening.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the settings-hardening.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage settings-hardening.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -18,6 +24,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
18
 
19
  /**
20
  * Renders the content of the plugin's hardening page.
 
 
 
 
 
 
 
 
21
  */
22
  class SucuriScanHardeningPage extends SucuriScan
23
  {
@@ -26,8 +40,8 @@ class SucuriScanHardeningPage extends SucuriScan
26
  * a specific part of the WordPress installation, if the Status variable is
27
  * set as a positive integer the button is shown as "unharden".
28
  *
29
- * @param array $args Array with template variables to replace.
30
- * @return string HTML code with the replaced template variables.
31
  */
32
  private static function drawSection($args = array())
33
  {
@@ -58,8 +72,8 @@ class SucuriScanHardeningPage extends SucuriScan
58
  /**
59
  * Checks if the request has a valid nonce to prevent a CSRF.
60
  *
61
- * @param string $function Name of the action that was executed.
62
- * @return bool True if the request has a valid CSRF protection.
63
  */
64
  private static function processRequest($function)
65
  {
@@ -76,26 +90,36 @@ class SucuriScanHardeningPage extends SucuriScan
76
  * (brute force attempts, DDoS, SQL injections, etc) and helping it remain
77
  * malware and blacklist free. This test checks if your site is using Sucuri
78
  * Firewall to protect your site.
 
 
79
  */
80
  public static function firewall()
81
  {
82
  $params = array();
83
 
84
  if (self::processRequest(__FUNCTION__)) {
85
- SucuriScanInterface::error(__('HardeningFirewallPurchase', SUCURISCAN_TEXTDOMAIN));
 
 
 
 
86
  }
87
 
88
  $params['Hardening.FieldName'] = __FUNCTION__;
89
- $params['Hardening.Title'] = __('HardeningFirewallTitle', SUCURISCAN_TEXTDOMAIN);
90
- $params['Hardening.Description'] = __('HardeningFirewallDescription', SUCURISCAN_TEXTDOMAIN);
 
 
 
 
91
 
92
  if (!SucuriScan::isBehindFirewall()) {
93
  $params['Hardening.Status'] = 0;
94
- $params['Hardening.FieldText'] = __('ApplyHardening', SUCURISCAN_TEXTDOMAIN);
95
  } else {
96
  $params['Hardening.Status'] = 1;
97
  $params['Hardening.FieldAttrs'] = 'disabled';
98
- $params['Hardening.FieldText'] = __('RevertHardening', SUCURISCAN_TEXTDOMAIN);
99
  }
100
 
101
  return self::drawSection($params);
@@ -109,6 +133,8 @@ class SucuriScanHardeningPage extends SucuriScan
109
  * source code are made public, if there were security fixes then someone
110
  * with malicious intent can use this information to attack any site that
111
  * has not been upgraded.
 
 
112
  */
113
  public static function wpversion()
114
  {
@@ -118,13 +144,17 @@ class SucuriScanHardeningPage extends SucuriScan
118
 
119
  $params['URL.Settings'] = admin_url('update-core.php');
120
  $params['Hardening.Status'] = 0;
121
- $params['Hardening.FieldText'] = __('ApplyHardening', SUCURISCAN_TEXTDOMAIN);
122
- $params['Hardening.Title'] = __('HardeningVersionTitle', SUCURISCAN_TEXTDOMAIN);
123
- $params['Hardening.Description'] = __('HardeningVersionDescription', SUCURISCAN_TEXTDOMAIN);
 
 
 
 
124
 
125
  if (isset($updates[0]) && $updates[0] instanceof stdClass) {
126
  if ($updates[0]->response == 'latest' || $updates[0]->response == 'development') {
127
- $params['Hardening.FieldText'] = __('RevertHardening', SUCURISCAN_TEXTDOMAIN);
128
  $params['Hardening.FieldAttrs'] = 'disabled';
129
  $params['Hardening.Status'] = 1;
130
  }
@@ -147,26 +177,32 @@ class SucuriScanHardeningPage extends SucuriScan
147
  * of life and is no longer supported.
148
  *
149
  * @see http://php.net/supported-versions.php
 
 
150
  */
151
  public static function phpversion()
152
  {
153
  $params = array();
154
 
155
  if (self::processRequest(__FUNCTION__)) {
156
- SucuriScanInterface::error(__('HardeningPHPVersionLifetime', SUCURISCAN_TEXTDOMAIN));
 
 
 
 
157
  }
158
 
159
  $params['Hardening.FieldName'] = __FUNCTION__;
160
- $params['Hardening.Title'] = __('HardeningPHPVersionTitle', SUCURISCAN_TEXTDOMAIN);
161
- $params['Hardening.Description'] = sprintf(__('HardeningPHPVersionDescription', SUCURISCAN_TEXTDOMAIN), PHP_VERSION);
162
 
163
  if (intval(version_compare(PHP_VERSION, '5.6.0') >= 0)) {
164
  $params['Hardening.Status'] = 1;
165
  $params['Hardening.FieldAttrs'] = 'disabled';
166
- $params['Hardening.FieldText'] = __('RevertHardening', SUCURISCAN_TEXTDOMAIN);
167
  } else {
168
  $params['Hardening.Status'] = 0;
169
- $params['Hardening.FieldText'] = __('ApplyHardening', SUCURISCAN_TEXTDOMAIN);
170
  }
171
 
172
  return self::drawSection($params);
@@ -176,22 +212,32 @@ class SucuriScanHardeningPage extends SucuriScan
176
  * Notify the state of the hardening for the removal of the Generator tag in
177
  * HTML code printed by WordPress to show the current version number of the
178
  * installation.
 
 
179
  */
180
  public static function wpgenerator()
181
  {
182
  $params = array();
183
 
184
- $params['Hardening.Title'] = __('HardeningGeneratorTitle', SUCURISCAN_TEXTDOMAIN);
185
  $params['Hardening.Status'] = 1;
186
- $params['Hardening.FieldText'] = __('RevertHardening', SUCURISCAN_TEXTDOMAIN);
187
  $params['Hardening.FieldAttrs'] = 'disabled';
188
- $params['Hardening.Description'] = __('HardeningGeneratorDescription', SUCURISCAN_TEXTDOMAIN);
 
 
 
 
 
 
189
 
190
  return self::drawSection($params);
191
  }
192
 
193
  /**
194
  * Offers information to apply a hardening to an Nginx installation.
 
 
195
  */
196
  public static function nginxphp()
197
  {
@@ -202,14 +248,25 @@ class SucuriScanHardeningPage extends SucuriScan
202
  $params = array();
203
 
204
  if (self::processRequest(__FUNCTION__)) {
205
- SucuriScanInterface::error(__('HardeningNginxSuggestion', SUCURISCAN_TEXTDOMAIN));
 
 
 
 
 
 
206
  }
207
 
208
- $params['Hardening.Title'] = __('HardeningNginxTitle', SUCURISCAN_TEXTDOMAIN);
209
  $params['Hardening.Status'] = 2;
210
  $params['Hardening.FieldName'] = __FUNCTION__;
211
- $params['Hardening.FieldText'] = __('HardeningNginxField', SUCURISCAN_TEXTDOMAIN);
212
- $params['Hardening.Description'] = __('HardeningNginxDescription', SUCURISCAN_TEXTDOMAIN);
 
 
 
 
 
213
 
214
  return self::drawSection($params);
215
  }
@@ -220,6 +277,8 @@ class SucuriScanHardeningPage extends SucuriScan
220
  * A htaccess file is placed in the upload folder denying the access to any php
221
  * file that could be uploaded through a vulnerability in a Plugin, Theme or
222
  * WordPress itself.
 
 
223
  */
224
  public static function wpuploads()
225
  {
@@ -235,9 +294,9 @@ class SucuriScanHardeningPage extends SucuriScan
235
 
236
  if ($result === true) {
237
  SucuriScanEvent::reportNoticeEvent('Hardening applied to the uploads directory');
238
- SucuriScanInterface::info(__('HardeningUploadsApplySuccess', SUCURISCAN_TEXTDOMAIN));
239
  } else {
240
- SucuriScanInterface::error(__('HardeningUploadsApplyFailure', SUCURISCAN_TEXTDOMAIN));
241
  }
242
  }
243
 
@@ -246,27 +305,32 @@ class SucuriScanHardeningPage extends SucuriScan
246
 
247
  if ($result === true) {
248
  SucuriScanEvent::reportErrorEvent('Hardening reverted in the uploads directory');
249
- SucuriScanInterface::info(__('HardeningUploadsRevertSuccess', SUCURISCAN_TEXTDOMAIN));
250
  } else {
251
- SucuriScanInterface::error(__('HardeningUploadsRevertFailure', SUCURISCAN_TEXTDOMAIN));
252
  }
253
  }
254
 
255
- $params['Hardening.Title'] = __('HardeningUploadsTitle', SUCURISCAN_TEXTDOMAIN);
256
- $params['Hardening.Description'] = __('HardeningUploadsDescription', SUCURISCAN_TEXTDOMAIN);
 
 
 
 
 
257
 
258
  if (SucuriScan::isBehindFirewall()) {
259
  $params['Hardening.Status'] = 1;
260
  $params['Hardening.FieldAttrs'] = 'disabled';
261
- $params['Hardening.FieldText'] = __('RevertHardening', SUCURISCAN_TEXTDOMAIN);
262
  } elseif (SucuriScanHardening::isHardened($folder)) {
263
  $params['Hardening.Status'] = 1;
264
  $params['Hardening.FieldName'] = __FUNCTION__ . '_revert';
265
- $params['Hardening.FieldText'] = __('RevertHardening', SUCURISCAN_TEXTDOMAIN);
266
  } else {
267
  $params['Hardening.Status'] = 0;
268
  $params['Hardening.FieldName'] = __FUNCTION__;
269
- $params['Hardening.FieldText'] = __('ApplyHardening', SUCURISCAN_TEXTDOMAIN);
270
  }
271
 
272
  return self::drawSection($params);
@@ -278,6 +342,8 @@ class SucuriScanHardeningPage extends SucuriScan
278
  * A htaccess file is placed in the content folder denying the access to any php
279
  * file that could be uploaded through a vulnerability in a Plugin, Theme or
280
  * WordPress itself.
 
 
281
  */
282
  public static function wpcontent()
283
  {
@@ -292,9 +358,9 @@ class SucuriScanHardeningPage extends SucuriScan
292
 
293
  if ($result === true) {
294
  SucuriScanEvent::reportNoticeEvent('Hardening applied to the content directory');
295
- SucuriScanInterface::info(__('HardeningContentApplySuccess', SUCURISCAN_TEXTDOMAIN));
296
  } else {
297
- SucuriScanInterface::error(__('HardeningContentApplyFailure', SUCURISCAN_TEXTDOMAIN));
298
  }
299
  }
300
 
@@ -303,27 +369,32 @@ class SucuriScanHardeningPage extends SucuriScan
303
 
304
  if ($result === true) {
305
  SucuriScanEvent::reportErrorEvent('Hardening reverted in the content directory');
306
- SucuriScanInterface::info(__('HardeningContentRevertSuccess', SUCURISCAN_TEXTDOMAIN));
307
  } else {
308
- SucuriScanInterface::error(__('HardeningContentRevertFailure', SUCURISCAN_TEXTDOMAIN));
309
  }
310
  }
311
 
312
- $params['Hardening.Title'] = __('HardeningContentTitle', SUCURISCAN_TEXTDOMAIN);
313
- $params['Hardening.Description'] = __('HardeningContentDescription', SUCURISCAN_TEXTDOMAIN);
 
 
 
 
 
314
 
315
  if (SucuriScan::isBehindFirewall()) {
316
  $params['Hardening.Status'] = 1;
317
  $params['Hardening.FieldAttrs'] = 'disabled';
318
- $params['Hardening.FieldText'] = __('RevertHardening', SUCURISCAN_TEXTDOMAIN);
319
  } elseif (SucuriScanHardening::isHardened(WP_CONTENT_DIR)) {
320
  $params['Hardening.Status'] = 1;
321
  $params['Hardening.FieldName'] = __FUNCTION__ . '_revert';
322
- $params['Hardening.FieldText'] = __('RevertHardening', SUCURISCAN_TEXTDOMAIN);
323
  } else {
324
  $params['Hardening.Status'] = 0;
325
  $params['Hardening.FieldName'] = __FUNCTION__;
326
- $params['Hardening.FieldText'] = __('ApplyHardening', SUCURISCAN_TEXTDOMAIN);
327
  }
328
 
329
  return self::drawSection($params);
@@ -336,6 +407,8 @@ class SucuriScanHardeningPage extends SucuriScan
336
  * file that could be uploaded through a vulnerability in a Plugin, Theme or
337
  * WordPress itself, there are some exceptions for some specific files that must
338
  * be available publicly.
 
 
339
  */
340
  public static function wpincludes()
341
  {
@@ -354,12 +427,12 @@ class SucuriScanHardeningPage extends SucuriScan
354
  SucuriScanHardening::whitelist('wp-tinymce.php', 'wp-includes');
355
  SucuriScanHardening::whitelist('ms-files.php', 'wp-includes');
356
  SucuriScanEvent::reportNoticeEvent('Hardening applied to the library directory');
357
- SucuriScanInterface::info(__('HardeningIncludesApplySuccess', SUCURISCAN_TEXTDOMAIN));
358
  } catch (Exception $e) {
359
  SucuriScanInterface::error($e->getMessage());
360
  }
361
  } else {
362
- SucuriScanInterface::error(__('HardeningIncludesApplyFailure', SUCURISCAN_TEXTDOMAIN));
363
  }
364
  }
365
 
@@ -370,27 +443,32 @@ class SucuriScanHardeningPage extends SucuriScan
370
  SucuriScanHardening::dewhitelist('wp-tinymce.php', 'wp-includes');
371
  SucuriScanHardening::dewhitelist('ms-files.php', 'wp-includes');
372
  SucuriScanEvent::reportErrorEvent('Hardening reverted in the library directory');
373
- SucuriScanInterface::info(__('HardeningIncludesRevertSuccess', SUCURISCAN_TEXTDOMAIN));
374
  } else {
375
- SucuriScanInterface::error(__('HardeningIncludesRevertFailure', SUCURISCAN_TEXTDOMAIN));
376
  }
377
  }
378
 
379
- $params['Hardening.Title'] = __('HardeningIncludesTitle', SUCURISCAN_TEXTDOMAIN);
380
- $params['Hardening.Description'] = __('HardeningIncludesDescription', SUCURISCAN_TEXTDOMAIN);
 
 
 
 
 
381
 
382
  if (SucuriScan::isBehindFirewall()) {
383
  $params['Hardening.Status'] = 1;
384
  $params['Hardening.FieldAttrs'] = 'disabled';
385
- $params['Hardening.FieldText'] = __('RevertHardening', SUCURISCAN_TEXTDOMAIN);
386
  } elseif (SucuriScanHardening::isHardened($folder)) {
387
  $params['Hardening.Status'] = 1;
388
  $params['Hardening.FieldName'] = __FUNCTION__ . '_revert';
389
- $params['Hardening.FieldText'] = __('RevertHardening', SUCURISCAN_TEXTDOMAIN);
390
  } else {
391
  $params['Hardening.Status'] = 0;
392
  $params['Hardening.FieldName'] = __FUNCTION__;
393
- $params['Hardening.FieldText'] = __('ApplyHardening', SUCURISCAN_TEXTDOMAIN);
394
  }
395
 
396
  return self::drawSection($params);
@@ -400,34 +478,36 @@ class SucuriScanHardeningPage extends SucuriScan
400
  * Check whether the "readme.html" file is still available in the root of the
401
  * site or not, which can lead to an attacker to know which version number of
402
  * Wordpress is being used and search for possible vulnerabilities.
 
 
403
  */
404
  public static function readme()
405
  {
406
  $params = array();
407
 
408
- // TODO: Create an option to automatically delete this after WP upgrade.
409
  if (self::processRequest(__FUNCTION__)) {
410
  if (@unlink(ABSPATH . '/readme.html') === false) {
411
- SucuriScanInterface::error(sprintf(
412
- __('HardeningReadmeApplyFailure', SUCURISCAN_TEXTDOMAIN),
413
- ABSPATH /* root of the WordPress installation */
414
- ));
415
  } else {
416
  SucuriScanEvent::reportNoticeEvent('Hardening applied to the <code>readme.html</code> file');
417
- SucuriScanInterface::info(__('HardeningReadmeApplySuccess', SUCURISCAN_TEXTDOMAIN));
418
  }
419
  }
420
 
421
- $params['Hardening.Title'] = __('HardeningReadmeTitle', SUCURISCAN_TEXTDOMAIN);
422
- $params['Hardening.Description'] = __('HardeningReadmeDescription', SUCURISCAN_TEXTDOMAIN);
 
 
 
 
423
 
424
  if (file_exists(ABSPATH . '/readme.html')) {
425
  $params['Hardening.Status'] = 0;
426
  $params['Hardening.FieldName'] = __FUNCTION__;
427
- $params['Hardening.FieldText'] = __('ApplyHardening', SUCURISCAN_TEXTDOMAIN);
428
  } else {
429
  $params['Hardening.Status'] = 1;
430
- $params['Hardening.FieldText'] = __('RevertHardening', SUCURISCAN_TEXTDOMAIN);
431
  $params['Hardening.FieldAttrs'] = 'disabled';
432
  }
433
 
@@ -435,32 +515,39 @@ class SucuriScanHardeningPage extends SucuriScan
435
  }
436
 
437
  /**
438
- * Check whether the main administrator user still has the default name "admin"
439
- * or not, which can lead to an attacker to perform a brute force attack.
 
 
440
  */
441
  public static function adminuser()
442
  {
443
  $params = array();
444
 
445
- $user_query = new WP_User_Query(array(
446
- 'search' => 'admin',
447
- 'fields' => array('ID', 'user_login'),
448
- 'search_columns' => array('user_login'),
449
- ));
 
 
450
  $results = $user_query->get_results();
451
 
452
  $params['URL.Settings'] = admin_url('users.php?role=administrator');
453
- $params['Hardening.Title'] = __('HardeningAdminUserTitle', SUCURISCAN_TEXTDOMAIN);
454
- $params['Hardening.Description'] = __('HardeningAdminUserDescription', SUCURISCAN_TEXTDOMAIN);
 
 
 
455
 
456
  if (count($results) === 0) {
457
  $params['Hardening.Status'] = 1;
458
  $params['Hardening.FieldAttrs'] = 'disabled';
459
- $params['Hardening.FieldText'] = __('RevertHardening', SUCURISCAN_TEXTDOMAIN);
460
  } else {
461
  $params['Hardening.Status'] = 0;
462
  $params['Hardening.FieldName'] = __FUNCTION__;
463
- $params['Hardening.FieldText'] = __('ApplyHardening', SUCURISCAN_TEXTDOMAIN);
464
  }
465
 
466
  return self::drawSection($params);
@@ -468,6 +555,8 @@ class SucuriScanHardeningPage extends SucuriScan
468
 
469
  /**
470
  * Enable or disable the user of the built-in Wordpress file editor.
 
 
471
  */
472
  public static function fileeditor()
473
  {
@@ -475,12 +564,12 @@ class SucuriScanHardeningPage extends SucuriScan
475
  $fileEditorWasDisabled = (bool) (defined('DISALLOW_FILE_EDIT') && DISALLOW_FILE_EDIT);
476
 
477
  if (self::processRequest(__FUNCTION__)) {
478
- $config = SucuriScan::getWPConfigPath();
479
 
480
  if (!$config) {
481
- SucuriScanInterface::error(__('ConfigNotFound', SUCURISCAN_TEXTDOMAIN));
482
  } elseif (!is_writable($config)) {
483
- SucuriScanInterface::error(__('ConfigNotWritable', SUCURISCAN_TEXTDOMAIN));
484
  } else {
485
  $content = SucuriScanFileInfo::fileContent($config);
486
  $lines = explode("\n", $content);
@@ -501,17 +590,17 @@ class SucuriScanHardeningPage extends SucuriScan
501
  $content = implode("\n", $newlines);
502
  @file_put_contents($config, $content, LOCK_EX);
503
  SucuriScanEvent::reportNoticeEvent('Hardening applied to the plugin and theme editor');
504
- SucuriScanInterface::info(__('HardeningFileEditorApplySuccess', SUCURISCAN_TEXTDOMAIN));
505
  }
506
  }
507
 
508
  if (self::processRequest(__FUNCTION__ . '_revert')) {
509
- $config = SucuriScan::getWPConfigPath();
510
 
511
  if (!$config) {
512
- SucuriScanInterface::error(__('ConfigNotFound', SUCURISCAN_TEXTDOMAIN));
513
  } elseif (!is_writable($config)) {
514
- SucuriScanInterface::error(__('ConfigNotWritable', SUCURISCAN_TEXTDOMAIN));
515
  } else {
516
  $content = SucuriScanFileInfo::fileContent($config);
517
  $lines = explode("\n", $content);
@@ -528,28 +617,38 @@ class SucuriScanHardeningPage extends SucuriScan
528
  }
529
 
530
  if (!$hardeningWasReverted) {
531
- SucuriScanInterface::error(__('HardeningFileEditorRevertFailure', SUCURISCAN_TEXTDOMAIN));
 
 
 
 
 
 
 
532
  } else {
533
  $fileEditorWasDisabled = false;
534
  $content = implode("\n", $newlines);
535
  @file_put_contents($config, $content, LOCK_EX);
536
  SucuriScanEvent::reportErrorEvent('Hardening reverted in the plugin and theme editor');
537
- SucuriScanInterface::info(__('HardeningFileEditorRevertSuccess', SUCURISCAN_TEXTDOMAIN));
538
  }
539
  }
540
  }
541
 
542
- $params['Hardening.Title'] = __('HardeningFileEditorTitle', SUCURISCAN_TEXTDOMAIN);
543
- $params['Hardening.Description'] = __('HardeningFileEditorDescription', SUCURISCAN_TEXTDOMAIN);
 
 
 
544
 
545
  if ($fileEditorWasDisabled) {
546
  $params['Hardening.Status'] = 1;
547
  $params['Hardening.FieldName'] = __FUNCTION__ . '_revert';
548
- $params['Hardening.FieldText'] = __('RevertHardening', SUCURISCAN_TEXTDOMAIN);
549
  } else {
550
  $params['Hardening.Status'] = 0;
551
  $params['Hardening.FieldName'] = __FUNCTION__;
552
- $params['Hardening.FieldText'] = __('ApplyHardening', SUCURISCAN_TEXTDOMAIN);
553
  }
554
 
555
  return self::drawSection($params);
@@ -563,6 +662,8 @@ class SucuriScanHardeningPage extends SucuriScan
563
  * contained in such directory, 3rd-party plugins and themes that makes use
564
  * of these direct requests will stop working. The admins will want to allow
565
  * direct access to certain PHP files.
 
 
566
  */
567
  public static function whitelistPHPFiles()
568
  {
@@ -578,45 +679,54 @@ class SucuriScanHardeningPage extends SucuriScan
578
 
579
  if (SucuriScanInterface::checkNonce()) {
580
  // Add a new file to the hardening whitelist.
581
- if ($fwhite = SucuriScanRequest::post(':hardening_whitelist')) {
 
 
582
  $folder = SucuriScanRequest::post(':hardening_folder');
583
 
584
  if (in_array($folder, $allowed_folders)) {
585
  try {
586
  SucuriScanHardening::whitelist($fwhite, $folder);
587
- SucuriScanInterface::info(__('PHPWhitelistSuccess', SUCURISCAN_TEXTDOMAIN));
588
  } catch (Exception $e) {
589
  SucuriScanInterface::error($e->getMessage());
590
  }
591
  } else {
592
- SucuriScanInterface::error(__('PHPWhitelistFailure', SUCURISCAN_TEXTDOMAIN));
593
  }
594
  }
595
 
596
  // Remove a file from the hardening whitelist.
597
- if ($rmfwhite = SucuriScanRequest::post(':hardening_rmfwhite', '_array')) {
 
 
598
  foreach ($rmfwhite as $fpath) {
599
  $fpath = str_replace('/.*/', '|', $fpath);
600
  $parts = explode('|', $fpath, 2);
601
  SucuriScanHardening::dewhitelist($parts[1], $parts[0]);
602
  }
603
 
604
- SucuriScanInterface::info(__('PHPDewhitelistSuccess', SUCURISCAN_TEXTDOMAIN));
605
  }
606
  }
607
 
608
  // Read the access control file and retrieve the whitelisted files.
609
  foreach ($allowed_folders as $folder) {
610
- if ($files = SucuriScanHardening::getWhitelisted($folder)) {
 
 
611
  $params['HardeningWhitelist.NoItemsVisibility'] = 'hidden';
612
 
613
  foreach ($files as $file) {
614
  $fregexp = sprintf('%s/.*/%s', $folder, $file);
615
- $html = SucuriScanTemplate::getSnippet('settings-hardening-whitelist-phpfiles', array(
616
- 'HardeningWhitelist.Regexp' => $fregexp,
617
- 'HardeningWhitelist.Folder' => $folder,
618
- 'HardeningWhitelist.File' => $file,
619
- ));
 
 
 
620
  $params['HardeningWhitelist.List'] .= $html;
621
  }
622
  }
3
  /**
4
  * Code related to the settings-hardening.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
24
 
25
  /**
26
  * Renders the content of the plugin's hardening page.
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 SucuriScanHardeningPage extends SucuriScan
37
  {
40
  * a specific part of the WordPress installation, if the Status variable is
41
  * set as a positive integer the button is shown as "unharden".
42
  *
43
+ * @param array $args Array with template variables to replace.
44
+ * @return string HTML code with the replaced template variables.
45
  */
46
  private static function drawSection($args = array())
47
  {
72
  /**
73
  * Checks if the request has a valid nonce to prevent a CSRF.
74
  *
75
+ * @param string $function Name of the action that was executed.
76
+ * @return bool True if the request has a valid CSRF protection.
77
  */
78
  private static function processRequest($function)
79
  {
90
  * (brute force attempts, DDoS, SQL injections, etc) and helping it remain
91
  * malware and blacklist free. This test checks if your site is using Sucuri
92
  * Firewall to protect your site.
93
+ *
94
+ * @return string HTML code with the replaced template variables.
95
  */
96
  public static function firewall()
97
  {
98
  $params = array();
99
 
100
  if (self::processRequest(__FUNCTION__)) {
101
+ SucuriScanInterface::error(
102
+ 'The firewall is a premium service that you need purchase at -'
103
+ . ' <a href="https://goo.gl/qfNkMq" target="_blank">Sucuri Fir'
104
+ . 'ewall</a>'
105
+ );
106
  }
107
 
108
  $params['Hardening.FieldName'] = __FUNCTION__;
109
+ $params['Hardening.Title'] = 'Website Firewall Protection';
110
+ $params['Hardening.Description'] = 'A WAF is a protection layer for yo'
111
+ . 'ur web site, blocking all sort of attacks (brute force attempts, DD'
112
+ . 'oS, SQL injections, etc) and helping it remain malware and blacklis'
113
+ . 't free. This test checks if your site is using Sucuri Firewall to p'
114
+ . 'rotect your site.';
115
 
116
  if (!SucuriScan::isBehindFirewall()) {
117
  $params['Hardening.Status'] = 0;
118
+ $params['Hardening.FieldText'] = 'Apply Hardening';
119
  } else {
120
  $params['Hardening.Status'] = 1;
121
  $params['Hardening.FieldAttrs'] = 'disabled';
122
+ $params['Hardening.FieldText'] = 'Revert Hardening';
123
  }
124
 
125
  return self::drawSection($params);
133
  * source code are made public, if there were security fixes then someone
134
  * with malicious intent can use this information to attack any site that
135
  * has not been upgraded.
136
+ *
137
+ * @return HTML with the information about this hardening option.
138
  */
139
  public static function wpversion()
140
  {
144
 
145
  $params['URL.Settings'] = admin_url('update-core.php');
146
  $params['Hardening.Status'] = 0;
147
+ $params['Hardening.FieldText'] = 'Apply Hardening';
148
+ $params['Hardening.Title'] = 'Verify WordPress Version';
149
+ $params['Hardening.Description'] = 'Why keep your site updated? WordPr'
150
+ . 'ess is an open-source project which means that with every update th'
151
+ . 'e details of the changes made to the source code are made public, i'
152
+ . 'f there were security fixes then someone with malicious intent can '
153
+ . 'use this information to attack any site that has not been upgraded.';
154
 
155
  if (isset($updates[0]) && $updates[0] instanceof stdClass) {
156
  if ($updates[0]->response == 'latest' || $updates[0]->response == 'development') {
157
+ $params['Hardening.FieldText'] = 'Revert Hardening';
158
  $params['Hardening.FieldAttrs'] = 'disabled';
159
  $params['Hardening.Status'] = 1;
160
  }
177
  * of life and is no longer supported.
178
  *
179
  * @see http://php.net/supported-versions.php
180
+ *
181
+ * @return HTML with the information about this hardening option.
182
  */
183
  public static function phpversion()
184
  {
185
  $params = array();
186
 
187
  if (self::processRequest(__FUNCTION__)) {
188
+ SucuriScanInterface::error(
189
+ 'Ask your hosting provider to install an updated version of PH'
190
+ . 'P - <a href="http://php.net/supported-versions.php" target='
191
+ . '"_blank" rel="noopener">List of PHP Supported Versions</a>'
192
+ );
193
  }
194
 
195
  $params['Hardening.FieldName'] = __FUNCTION__;
196
+ $params['Hardening.Title'] = 'Verify PHP Version';
197
+ $params['Hardening.Description'] = sprintf('PHP %s is installed.', PHP_VERSION);
198
 
199
  if (intval(version_compare(PHP_VERSION, '5.6.0') >= 0)) {
200
  $params['Hardening.Status'] = 1;
201
  $params['Hardening.FieldAttrs'] = 'disabled';
202
+ $params['Hardening.FieldText'] = 'Revert Hardening';
203
  } else {
204
  $params['Hardening.Status'] = 0;
205
+ $params['Hardening.FieldText'] = 'Apply Hardening';
206
  }
207
 
208
  return self::drawSection($params);
212
  * Notify the state of the hardening for the removal of the Generator tag in
213
  * HTML code printed by WordPress to show the current version number of the
214
  * installation.
215
+ *
216
+ * @return HTML with the information about this hardening option.
217
  */
218
  public static function wpgenerator()
219
  {
220
  $params = array();
221
 
222
+ $params['Hardening.Title'] = 'Remove WordPress Version';
223
  $params['Hardening.Status'] = 1;
224
+ $params['Hardening.FieldText'] = 'Revert Hardening';
225
  $params['Hardening.FieldAttrs'] = 'disabled';
226
+ $params['Hardening.Description'] = 'It checks if your WordPress versio'
227
+ . 'n is being leaked to the public via a HTML meta-tag. Many web vulne'
228
+ . 'rability scanners use this to determine which version of the code i'
229
+ . 's running in your website. They use this to find disclosed vulnerab'
230
+ . 'ilities associated to this version number. A vulnerability scanner '
231
+ . 'can still guess which version of WordPress is installed by comparin'
232
+ . 'g the checksum of some static files.';
233
 
234
  return self::drawSection($params);
235
  }
236
 
237
  /**
238
  * Offers information to apply a hardening to an Nginx installation.
239
+ *
240
+ * @return HTML with the information about this hardening option.
241
  */
242
  public static function nginxphp()
243
  {
248
  $params = array();
249
 
250
  if (self::processRequest(__FUNCTION__)) {
251
+ SucuriScanInterface::error(
252
+ 'Read the official WordPress guidelines to learn how to restri'
253
+ . 'ct access to PHP files in sensitive directories - <a href="'
254
+ . 'https://codex.wordpress.org/Nginx#Global_restrictions_file"'
255
+ . ' target="_blank" rel="noopener">Nginx Global Restrictions F'
256
+ . 'or WordPress</a>'
257
+ );
258
  }
259
 
260
+ $params['Hardening.Title'] = 'Block of Certain PHP Files';
261
  $params['Hardening.Status'] = 2;
262
  $params['Hardening.FieldName'] = __FUNCTION__;
263
+ $params['Hardening.FieldText'] = 'Check Hardening';
264
+ $params['Hardening.Description'] = 'Block the execution of PHP files i'
265
+ . 'n sensitive directories. Be careful while applying this hardening o'
266
+ . 'ption as there are many plugins and theme which rely on the ability'
267
+ . ' to execute PHP files in the content directory to generate images o'
268
+ . 'r save temporary data. Use the "Whitelist PHP Files" tool to add ex'
269
+ . 'ceptions to individual files.';
270
 
271
  return self::drawSection($params);
272
  }
277
  * A htaccess file is placed in the upload folder denying the access to any php
278
  * file that could be uploaded through a vulnerability in a Plugin, Theme or
279
  * WordPress itself.
280
+ *
281
+ * @return HTML with the information about this hardening option.
282
  */
283
  public static function wpuploads()
284
  {
294
 
295
  if ($result === true) {
296
  SucuriScanEvent::reportNoticeEvent('Hardening applied to the uploads directory');
297
+ SucuriScanInterface::info('Hardening applied to the uploads directory');
298
  } else {
299
+ SucuriScanInterface::error('Error hardening directory, check the permissions.');
300
  }
301
  }
302
 
305
 
306
  if ($result === true) {
307
  SucuriScanEvent::reportErrorEvent('Hardening reverted in the uploads directory');
308
+ SucuriScanInterface::info('Hardening reverted in the uploads directory');
309
  } else {
310
+ SucuriScanInterface::error('Access file is not writable, check the permissions.');
311
  }
312
  }
313
 
314
+ $params['Hardening.Title'] = 'Block PHP Files in Uploads Directory';
315
+ $params['Hardening.Description'] = 'Block the execution of PHP files i'
316
+ . 'n sensitive directories. Be careful while applying this hardening o'
317
+ . 'ption as there are many plugins and theme which rely on the ability'
318
+ . ' to execute PHP files in the content directory to generate images o'
319
+ . 'r save temporary data. Use the "Whitelist PHP Files" tool to add ex'
320
+ . 'ceptions to individual files.';
321
 
322
  if (SucuriScan::isBehindFirewall()) {
323
  $params['Hardening.Status'] = 1;
324
  $params['Hardening.FieldAttrs'] = 'disabled';
325
+ $params['Hardening.FieldText'] = 'Revert Hardening';
326
  } elseif (SucuriScanHardening::isHardened($folder)) {
327
  $params['Hardening.Status'] = 1;
328
  $params['Hardening.FieldName'] = __FUNCTION__ . '_revert';
329
+ $params['Hardening.FieldText'] = 'Revert Hardening';
330
  } else {
331
  $params['Hardening.Status'] = 0;
332
  $params['Hardening.FieldName'] = __FUNCTION__;
333
+ $params['Hardening.FieldText'] = 'Apply Hardening';
334
  }
335
 
336
  return self::drawSection($params);
342
  * A htaccess file is placed in the content folder denying the access to any php
343
  * file that could be uploaded through a vulnerability in a Plugin, Theme or
344
  * WordPress itself.
345
+ *
346
+ * @return HTML with the information about this hardening option.
347
  */
348
  public static function wpcontent()
349
  {
358
 
359
  if ($result === true) {
360
  SucuriScanEvent::reportNoticeEvent('Hardening applied to the content directory');
361
+ SucuriScanInterface::info('Hardening applied to the content directory');
362
  } else {
363
+ SucuriScanInterface::error('Error hardening directory, check the permissions.');
364
  }
365
  }
366
 
369
 
370
  if ($result === true) {
371
  SucuriScanEvent::reportErrorEvent('Hardening reverted in the content directory');
372
+ SucuriScanInterface::info('Hardening reverted in the content directory');
373
  } else {
374
+ SucuriScanInterface::error('Access file is not writable, check the permissions.');
375
  }
376
  }
377
 
378
+ $params['Hardening.Title'] = 'Block PHP Files in WP-CONTENT Directory';
379
+ $params['Hardening.Description'] = 'Block the execution of PHP files i'
380
+ . 'n sensitive directories. Be careful while applying this hardening o'
381
+ . 'ption as there are many plugins and theme which rely on the ability'
382
+ . ' to execute PHP files in the content directory to generate images o'
383
+ . 'r save temporary data. Use the "Whitelist PHP Files" tool to add ex'
384
+ . 'ceptions to individual files.';
385
 
386
  if (SucuriScan::isBehindFirewall()) {
387
  $params['Hardening.Status'] = 1;
388
  $params['Hardening.FieldAttrs'] = 'disabled';
389
+ $params['Hardening.FieldText'] = 'Revert Hardening';
390
  } elseif (SucuriScanHardening::isHardened(WP_CONTENT_DIR)) {
391
  $params['Hardening.Status'] = 1;
392
  $params['Hardening.FieldName'] = __FUNCTION__ . '_revert';
393
+ $params['Hardening.FieldText'] = 'Revert Hardening';
394
  } else {
395
  $params['Hardening.Status'] = 0;
396
  $params['Hardening.FieldName'] = __FUNCTION__;
397
+ $params['Hardening.FieldText'] = 'Apply Hardening';
398
  }
399
 
400
  return self::drawSection($params);
407
  * file that could be uploaded through a vulnerability in a Plugin, Theme or
408
  * WordPress itself, there are some exceptions for some specific files that must
409
  * be available publicly.
410
+ *
411
+ * @return HTML with the information about this hardening option.
412
  */
413
  public static function wpincludes()
414
  {
427
  SucuriScanHardening::whitelist('wp-tinymce.php', 'wp-includes');
428
  SucuriScanHardening::whitelist('ms-files.php', 'wp-includes');
429
  SucuriScanEvent::reportNoticeEvent('Hardening applied to the library directory');
430
+ SucuriScanInterface::info('Hardening applied to the library directory');
431
  } catch (Exception $e) {
432
  SucuriScanInterface::error($e->getMessage());
433
  }
434
  } else {
435
+ SucuriScanInterface::error('Error hardening directory, check the permissions.');
436
  }
437
  }
438
 
443
  SucuriScanHardening::dewhitelist('wp-tinymce.php', 'wp-includes');
444
  SucuriScanHardening::dewhitelist('ms-files.php', 'wp-includes');
445
  SucuriScanEvent::reportErrorEvent('Hardening reverted in the library directory');
446
+ SucuriScanInterface::info('Hardening reverted in the library directory');
447
  } else {
448
+ SucuriScanInterface::error('Access file is not writable, check the permissions.');
449
  }
450
  }
451
 
452
+ $params['Hardening.Title'] = 'Block PHP Files in WP-INCLUDES Directory';
453
+ $params['Hardening.Description'] = 'Block the execution of PHP files i'
454
+ . 'n sensitive directories. Be careful while applying this hardening o'
455
+ . 'ption as there are many plugins and theme which rely on the ability'
456
+ . ' to execute PHP files in the content directory to generate images o'
457
+ . 'r save temporary data. Use the "Whitelist PHP Files" tool to add ex'
458
+ . 'ceptions to individual files.';
459
 
460
  if (SucuriScan::isBehindFirewall()) {
461
  $params['Hardening.Status'] = 1;
462
  $params['Hardening.FieldAttrs'] = 'disabled';
463
+ $params['Hardening.FieldText'] = 'Revert Hardening';
464
  } elseif (SucuriScanHardening::isHardened($folder)) {
465
  $params['Hardening.Status'] = 1;
466
  $params['Hardening.FieldName'] = __FUNCTION__ . '_revert';
467
+ $params['Hardening.FieldText'] = 'Revert Hardening';
468
  } else {
469
  $params['Hardening.Status'] = 0;
470
  $params['Hardening.FieldName'] = __FUNCTION__;
471
+ $params['Hardening.FieldText'] = 'Apply Hardening';
472
  }
473
 
474
  return self::drawSection($params);
478
  * Check whether the "readme.html" file is still available in the root of the
479
  * site or not, which can lead to an attacker to know which version number of
480
  * Wordpress is being used and search for possible vulnerabilities.
481
+ *
482
+ * @return HTML with the information about this hardening option.
483
  */
484
  public static function readme()
485
  {
486
  $params = array();
487
 
 
488
  if (self::processRequest(__FUNCTION__)) {
489
  if (@unlink(ABSPATH . '/readme.html') === false) {
490
+ SucuriScanInterface::error('Cannot delete <code>' . ABSPATH . '/readme.html</code>');
 
 
 
491
  } else {
492
  SucuriScanEvent::reportNoticeEvent('Hardening applied to the <code>readme.html</code> file');
493
+ SucuriScanInterface::info('Hardening applied to the <code>readme.html</code> file');
494
  }
495
  }
496
 
497
+ $params['Hardening.Title'] = 'Information Leakage';
498
+ $params['Hardening.Description'] = 'Checks if the WordPress README fil'
499
+ . 'e still exists in the website. The information in this file can be '
500
+ . 'used by malicious users to pin-point which disclosed vulnerabilitie'
501
+ . 's are associated to the website. Be aware that WordPress recreates '
502
+ . 'this file automatically with every update.';
503
 
504
  if (file_exists(ABSPATH . '/readme.html')) {
505
  $params['Hardening.Status'] = 0;
506
  $params['Hardening.FieldName'] = __FUNCTION__;
507
+ $params['Hardening.FieldText'] = 'Apply Hardening';
508
  } else {
509
  $params['Hardening.Status'] = 1;
510
+ $params['Hardening.FieldText'] = 'Revert Hardening';
511
  $params['Hardening.FieldAttrs'] = 'disabled';
512
  }
513
 
515
  }
516
 
517
  /**
518
+ * Check whether the main admin user still has the default name "admin" or
519
+ * not, which can lead to an attacker to perform a brute force attack.
520
+ *
521
+ * @return HTML with the information about this hardening option.
522
  */
523
  public static function adminuser()
524
  {
525
  $params = array();
526
 
527
+ $user_query = new WP_User_Query(
528
+ array(
529
+ 'search' => 'admin',
530
+ 'fields' => array('ID', 'user_login'),
531
+ 'search_columns' => array('user_login'),
532
+ )
533
+ );
534
  $results = $user_query->get_results();
535
 
536
  $params['URL.Settings'] = admin_url('users.php?role=administrator');
537
+ $params['Hardening.Title'] = 'Default Admin Account';
538
+ $params['Hardening.Description'] = 'Check if the primary user account '
539
+ . 'still uses the name "admin". This allows malicious users to easily '
540
+ . 'identify which account has the highest privileges to target an atta'
541
+ . 'ck.';
542
 
543
  if (count($results) === 0) {
544
  $params['Hardening.Status'] = 1;
545
  $params['Hardening.FieldAttrs'] = 'disabled';
546
+ $params['Hardening.FieldText'] = 'Revert Hardening';
547
  } else {
548
  $params['Hardening.Status'] = 0;
549
  $params['Hardening.FieldName'] = __FUNCTION__;
550
+ $params['Hardening.FieldText'] = 'Apply Hardening';
551
  }
552
 
553
  return self::drawSection($params);
555
 
556
  /**
557
  * Enable or disable the user of the built-in Wordpress file editor.
558
+ *
559
+ * @return HTML with the information about this hardening option.
560
  */
561
  public static function fileeditor()
562
  {
564
  $fileEditorWasDisabled = (bool) (defined('DISALLOW_FILE_EDIT') && DISALLOW_FILE_EDIT);
565
 
566
  if (self::processRequest(__FUNCTION__)) {
567
+ $config = SucuriScan::getConfigPath();
568
 
569
  if (!$config) {
570
+ SucuriScanInterface::error('WordPress configuration file was not found.');
571
  } elseif (!is_writable($config)) {
572
+ SucuriScanInterface::error('WordPress configuration file is not writable.');
573
  } else {
574
  $content = SucuriScanFileInfo::fileContent($config);
575
  $lines = explode("\n", $content);
590
  $content = implode("\n", $newlines);
591
  @file_put_contents($config, $content, LOCK_EX);
592
  SucuriScanEvent::reportNoticeEvent('Hardening applied to the plugin and theme editor');
593
+ SucuriScanInterface::info('Hardening applied to the plugin and theme editor');
594
  }
595
  }
596
 
597
  if (self::processRequest(__FUNCTION__ . '_revert')) {
598
+ $config = SucuriScan::getConfigPath();
599
 
600
  if (!$config) {
601
+ SucuriScanInterface::error('WordPress configuration file was not found.');
602
  } elseif (!is_writable($config)) {
603
+ SucuriScanInterface::error('WordPress configuration file is not writable.');
604
  } else {
605
  $content = SucuriScanFileInfo::fileContent($config);
606
  $lines = explode("\n", $content);
617
  }
618
 
619
  if (!$hardeningWasReverted) {
620
+ SucuriScanInterface::error(
621
+ 'File Editor was not disabled using this tool. You mus'
622
+ . 't scan your project for a constant defined as DISAL'
623
+ . 'LOW_FILE_EDIT, then either delete it or set its val'
624
+ . 'ue to False. Any plugin/theme can disable the file '
625
+ . 'editor, so it is impossible to determine the origin'
626
+ . ' of the constant.'
627
+ );
628
  } else {
629
  $fileEditorWasDisabled = false;
630
  $content = implode("\n", $newlines);
631
  @file_put_contents($config, $content, LOCK_EX);
632
  SucuriScanEvent::reportErrorEvent('Hardening reverted in the plugin and theme editor');
633
+ SucuriScanInterface::info('Hardening reverted in the plugin and theme editor');
634
  }
635
  }
636
  }
637
 
638
+ $params['Hardening.Title'] = 'Plugin and Theme Editor';
639
+ $params['Hardening.Description'] = 'Disables the theme and plugin edit'
640
+ . 'ors to prevent unwanted modifications to the code. If you are havin'
641
+ . 'g problems reverting this please open the wp-config.php file and de'
642
+ . 'lete the line with the constant DISALLOW_FILE_EDIT.';
643
 
644
  if ($fileEditorWasDisabled) {
645
  $params['Hardening.Status'] = 1;
646
  $params['Hardening.FieldName'] = __FUNCTION__ . '_revert';
647
+ $params['Hardening.FieldText'] = 'Revert Hardening';
648
  } else {
649
  $params['Hardening.Status'] = 0;
650
  $params['Hardening.FieldName'] = __FUNCTION__;
651
+ $params['Hardening.FieldText'] = 'Apply Hardening';
652
  }
653
 
654
  return self::drawSection($params);
662
  * contained in such directory, 3rd-party plugins and themes that makes use
663
  * of these direct requests will stop working. The admins will want to allow
664
  * direct access to certain PHP files.
665
+ *
666
+ * @return HTML with the information about this hardening option.
667
  */
668
  public static function whitelistPHPFiles()
669
  {
679
 
680
  if (SucuriScanInterface::checkNonce()) {
681
  // Add a new file to the hardening whitelist.
682
+ $fwhite = SucuriScanRequest::post(':hardening_whitelist');
683
+
684
+ if ($fwhite) {
685
  $folder = SucuriScanRequest::post(':hardening_folder');
686
 
687
  if (in_array($folder, $allowed_folders)) {
688
  try {
689
  SucuriScanHardening::whitelist($fwhite, $folder);
690
+ SucuriScanInterface::info('The file has been whitelisted from the hardening');
691
  } catch (Exception $e) {
692
  SucuriScanInterface::error($e->getMessage());
693
  }
694
  } else {
695
+ SucuriScanInterface::error('Specified folder is not hardened by this plugin');
696
  }
697
  }
698
 
699
  // Remove a file from the hardening whitelist.
700
+ $rmfwhite = SucuriScanRequest::post(':hardening_rmfwhite', '_array');
701
+
702
+ if ($rmfwhite) {
703
  foreach ($rmfwhite as $fpath) {
704
  $fpath = str_replace('/.*/', '|', $fpath);
705
  $parts = explode('|', $fpath, 2);
706
  SucuriScanHardening::dewhitelist($parts[1], $parts[0]);
707
  }
708
 
709
+ SucuriScanInterface::info('Selected files have been removed');
710
  }
711
  }
712
 
713
  // Read the access control file and retrieve the whitelisted files.
714
  foreach ($allowed_folders as $folder) {
715
+ $files = SucuriScanHardening::getWhitelisted($folder);
716
+
717
+ if (is_array($files) && !empty($files)) {
718
  $params['HardeningWhitelist.NoItemsVisibility'] = 'hidden';
719
 
720
  foreach ($files as $file) {
721
  $fregexp = sprintf('%s/.*/%s', $folder, $file);
722
+ $html = SucuriScanTemplate::getSnippet(
723
+ 'settings-hardening-whitelist-phpfiles',
724
+ array(
725
+ 'HardeningWhitelist.Regexp' => $fregexp,
726
+ 'HardeningWhitelist.Folder' => $folder,
727
+ 'HardeningWhitelist.File' => $file,
728
+ )
729
+ );
730
  $params['HardeningWhitelist.List'] .= $html;
731
  }
732
  }
src/settings-integrity.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the settings-integrity.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage settings-integrity.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -24,29 +30,39 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
24
  * integrity scanner and the optional Unix diff utility. This also includes some
25
  * options to configure the website installation language and the false/positive
26
  * cache file.
 
 
 
 
 
 
 
 
27
  */
28
  class SucuriScanSettingsIntegrity extends SucuriScanSettings
29
  {
30
  /**
31
  * Configures the diffUtility for the integrity scanner.
32
  *
33
- * @param bool $nonce True if the CSRF protection worked, false otherwise.
34
- * @return string HTML code to render the configuration panel.
35
  */
36
  public static function diffUtility($nonce)
37
  {
38
  $params = array();
39
 
40
  $params['DiffUtility.StatusNum'] = 0;
41
- $params['DiffUtility.Status'] = __('Disabled', SUCURISCAN_TEXTDOMAIN);
42
- $params['DiffUtility.SwitchText'] = __('Enable', SUCURISCAN_TEXTDOMAIN);
43
  $params['DiffUtility.SwitchValue'] = 'enable';
44
 
45
  if ($nonce) {
46
  // Enable or disable the Unix diff utility.
47
- if ($status = SucuriScanRequest::post(':diff_utility', '(en|dis)able')) {
 
 
48
  if (!SucuriScanCommand::exists('diff')) {
49
- SucuriScanInterface::error(__('DiffUtilityMissing', SUCURISCAN_TEXTDOMAIN));
50
  } else {
51
  $status = $status . 'd'; /* add past tense */
52
  $message = 'Integrity diff utility has been <code>' . $status . '</code>';
@@ -54,60 +70,26 @@ class SucuriScanSettingsIntegrity extends SucuriScanSettings
54
  SucuriScanOption::updateOption(':diff_utility', $status);
55
  SucuriScanEvent::reportInfoEvent($message);
56
  SucuriScanEvent::notifyEvent('plugin_change', $message);
57
- SucuriScanInterface::info(__('DiffUtilityStatus', SUCURISCAN_TEXTDOMAIN));
58
  }
59
  }
60
  }
61
 
62
  if (SucuriScanOption::isEnabled(':diff_utility')) {
63
  $params['DiffUtility.StatusNum'] = 1;
64
- $params['DiffUtility.Status'] = __('Enabled', SUCURISCAN_TEXTDOMAIN);
65
- $params['DiffUtility.SwitchText'] = __('Disable', SUCURISCAN_TEXTDOMAIN);
66
  $params['DiffUtility.SwitchValue'] = 'disable';
67
  }
68
 
69
  return SucuriScanTemplate::getSection('settings-scanner-integrity-diff-utility', $params);
70
  }
71
 
72
- /**
73
- * Configures the language for the integrity scanner.
74
- *
75
- * @param bool $nonce True if the CSRF protection worked, false otherwise.
76
- * @return string HTML code to render the configuration panel.
77
- */
78
- public static function language($nonce)
79
- {
80
- $params = array();
81
- $languages = SucuriScan::languages();
82
-
83
- if ($nonce) {
84
- // Configure the language for the core integrity checks.
85
- if ($language = SucuriScanRequest::post(':set_language')) {
86
- if (array_key_exists($language, $languages)) {
87
- $message = 'Core integrity language set to <code>' . $language . '</code>';
88
-
89
- SucuriScanOption::updateOption(':language', $language);
90
- SucuriScanEvent::reportInfoEvent($message);
91
- SucuriScanEvent::notifyEvent('plugin_change', $message);
92
- SucuriScanInterface::info(__('IntegrityLanguage', SUCURISCAN_TEXTDOMAIN));
93
- } else {
94
- SucuriScanInterface::error(__('NonSupportedLanguage', SUCURISCAN_TEXTDOMAIN));
95
- }
96
- }
97
- }
98
-
99
- $language = SucuriScanOption::getOption(':language');
100
- $params['Integrity.LanguageDropdown'] = SucuriScanTemplate::selectOptions($languages, $language);
101
- $params['Integrity.WordPressLocale'] = get_locale();
102
-
103
- return SucuriScanTemplate::getSection('settings-scanner-integrity-language', $params);
104
- }
105
-
106
  /**
107
  * Configures the cache for the integrity scanner.
108
  *
109
- * @param bool $nonce True if the CSRF protection worked, false otherwise.
110
- * @return string HTML code to render the configuration panel.
111
  */
112
  public static function cache($nonce)
113
  {
@@ -126,9 +108,11 @@ class SucuriScanSettingsIntegrity extends SucuriScanSettings
126
  }
127
 
128
  if (!empty($deletedFiles)) {
129
- SucuriScanEvent::reportDebugEvent('Core files that will not be '
130
- . 'ignored anymore: (multiple entries): ' . implode(',', $deletedFiles));
131
- SucuriScanInterface::info(__('ItemsProcessed', SUCURISCAN_TEXTDOMAIN));
 
 
132
  }
133
  }
134
 
@@ -137,16 +121,21 @@ class SucuriScanSettingsIntegrity extends SucuriScanSettings
137
  $params['CacheLifeTime'] = SUCURISCAN_SITECHECK_LIFETIME;
138
  $params['NoFilesVisibility'] = 'visible';
139
 
140
- if ($ignored_files = $cache->getAll()) {
 
 
141
  $params['NoFilesVisibility'] = 'hidden';
142
 
143
  foreach ($ignored_files as $hash => $data) {
144
- $params['IgnoredFiles'] .= SucuriScanTemplate::getSnippet('settings-scanner-integrity-cache', array(
145
- 'UniqueId' => substr($hash, 0, 8),
146
- 'FilePath' => $data->file_path,
147
- 'StatusType' => $data->file_status,
148
- 'IgnoredAt' => SucuriScan::datetime($data->ignored_at),
149
- ));
 
 
 
150
  }
151
  }
152
 
3
  /**
4
  * Code related to the settings-integrity.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
30
  * integrity scanner and the optional Unix diff utility. This also includes some
31
  * options to configure the website installation language and the false/positive
32
  * cache file.
33
+ *
34
+ * @category Library
35
+ * @package Sucuri
36
+ * @subpackage SucuriScanner
37
+ * @author Daniel Cid <dcid@sucuri.net>
38
+ * @copyright 2010-2017 Sucuri Inc.
39
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
40
+ * @link https://wordpress.org/plugins/sucuri-scanner
41
  */
42
  class SucuriScanSettingsIntegrity extends SucuriScanSettings
43
  {
44
  /**
45
  * Configures the diffUtility for the integrity scanner.
46
  *
47
+ * @param bool $nonce True if the CSRF protection worked, false otherwise.
48
+ * @return string HTML code to render the configuration panel.
49
  */
50
  public static function diffUtility($nonce)
51
  {
52
  $params = array();
53
 
54
  $params['DiffUtility.StatusNum'] = 0;
55
+ $params['DiffUtility.Status'] = 'Disabled';
56
+ $params['DiffUtility.SwitchText'] = 'Enable';
57
  $params['DiffUtility.SwitchValue'] = 'enable';
58
 
59
  if ($nonce) {
60
  // Enable or disable the Unix diff utility.
61
+ $status = SucuriScanRequest::post(':diff_utility', '(en|dis)able');
62
+
63
+ if ($status) {
64
  if (!SucuriScanCommand::exists('diff')) {
65
+ SucuriScanInterface::error('Your hosting provider has blocked the execution of external commands.');
66
  } else {
67
  $status = $status . 'd'; /* add past tense */
68
  $message = 'Integrity diff utility has been <code>' . $status . '</code>';
70
  SucuriScanOption::updateOption(':diff_utility', $status);
71
  SucuriScanEvent::reportInfoEvent($message);
72
  SucuriScanEvent::notifyEvent('plugin_change', $message);
73
+ SucuriScanInterface::info('The status of the integrity diff utility has been changed');
74
  }
75
  }
76
  }
77
 
78
  if (SucuriScanOption::isEnabled(':diff_utility')) {
79
  $params['DiffUtility.StatusNum'] = 1;
80
+ $params['DiffUtility.Status'] = 'Enabled';
81
+ $params['DiffUtility.SwitchText'] = 'Disable';
82
  $params['DiffUtility.SwitchValue'] = 'disable';
83
  }
84
 
85
  return SucuriScanTemplate::getSection('settings-scanner-integrity-diff-utility', $params);
86
  }
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  /**
89
  * Configures the cache for the integrity scanner.
90
  *
91
+ * @param bool $nonce True if the CSRF protection worked, false otherwise.
92
+ * @return string HTML code to render the configuration panel.
93
  */
94
  public static function cache($nonce)
95
  {
108
  }
109
 
110
  if (!empty($deletedFiles)) {
111
+ SucuriScanEvent::reportDebugEvent(
112
+ 'Core files that will not be ignored anymore: (mul'
113
+ . 'tiple entries): ' . implode(',', $deletedFiles)
114
+ );
115
+ SucuriScanInterface::info('The selected files have been successfully processed.');
116
  }
117
  }
118
 
121
  $params['CacheLifeTime'] = SUCURISCAN_SITECHECK_LIFETIME;
122
  $params['NoFilesVisibility'] = 'visible';
123
 
124
+ $ignored_files = $cache->getAll();
125
+
126
+ if (is_array($ignored_files) && !empty($ignored_files)) {
127
  $params['NoFilesVisibility'] = 'hidden';
128
 
129
  foreach ($ignored_files as $hash => $data) {
130
+ $params['IgnoredFiles'] .= SucuriScanTemplate::getSnippet(
131
+ 'settings-scanner-integrity-cache',
132
+ array(
133
+ 'UniqueId' => substr($hash, 0, 8),
134
+ 'FilePath' => $data->file_path,
135
+ 'StatusType' => $data->file_status,
136
+ 'IgnoredAt' => SucuriScan::datetime($data->ignored_at),
137
+ )
138
+ );
139
  }
140
  }
141
 
src/settings-posthack.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the settings-posthack.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage settings-posthack.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -22,6 +28,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
22
  * The plugin allows to execute some tools that will clear up the site after a
23
  * suspicious activity. This includes the ability to reset the secret security
24
  * keys, the password for each user account, and the installed plugins.
 
 
 
 
 
 
 
 
25
  */
26
  class SucuriScanSettingsPosthack extends SucuriScanSettings
27
  {
@@ -41,15 +55,15 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
41
  // Update all WordPress secret keys.
42
  if (SucuriScanInterface::checkNonce() && SucuriScanRequest::post(':update_wpconfig')) {
43
  if (SucuriScanRequest::post(':process_form') != 1) {
44
- SucuriScanInterface::error(__('ConfirmOperation', SUCURISCAN_TEXTDOMAIN));
45
  } else {
46
  $wpconfig_process = SucuriScanEvent::setNewConfigKeys();
47
 
48
  if (!$wpconfig_process) {
49
- SucuriScanInterface::error(__('ConfigNotFound', SUCURISCAN_TEXTDOMAIN));
50
  } elseif ($wpconfig_process['updated']) {
51
  SucuriScanEvent::reportNoticeEvent('Generate new security keys (success)');
52
- SucuriScanInterface::info(__('SecretKeysUpdated', SUCURISCAN_TEXTDOMAIN));
53
 
54
  $params['WPConfigUpdate.Visibility'] = 'visible';
55
  $params['WPConfigUpdate.NewConfig'] .= "/* Old Security Keys */\n";
@@ -59,7 +73,7 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
59
  $params['WPConfigUpdate.NewConfig'] .= $wpconfig_process['new_keys_string'];
60
  } else {
61
  SucuriScanEvent::reportNoticeEvent('Generate new security keys (failure)');
62
- SucuriScanInterface::error(__('ConfigNotWritable', SUCURISCAN_TEXTDOMAIN));
63
 
64
  $params['WPConfigUpdate.Visibility'] = 'visible';
65
  $params['WPConfigUpdate.NewConfig'] = $wpconfig_process['new_wpconfig'];
@@ -74,26 +88,28 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
74
  foreach ($key_list as $key_name => $key_value) {
75
  switch ($key_status) {
76
  case 'good':
77
- $key_status_text = __('Good', SUCURISCAN_TEXTDOMAIN);
78
  break;
79
 
80
  case 'bad':
81
- $key_status_text = __('NotRandomized', SUCURISCAN_TEXTDOMAIN);
82
  break;
83
 
84
  case 'missing':
85
  $key_value = '';
86
- $key_status_text = __('NotSet', SUCURISCAN_TEXTDOMAIN);
87
  break;
88
  }
89
 
90
  if (isset($key_status_text)) {
91
- $params['SecurityKeys.List'] .=
92
- SucuriScanTemplate::getSnippet('settings-posthack-security-keys', array(
93
- 'SecurityKey.KeyName' => $key_name,
94
- 'SecurityKey.KeyValue' => $key_value,
95
- 'SecurityKey.KeyStatusText' => $key_status_text,
96
- ));
 
 
97
  }
98
  }
99
  }
@@ -120,12 +136,14 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
120
  $user_list = array();
121
  $page_number = SucuriScanTemplate::pageNumber();
122
  $max_per_page = SUCURISCAN_MAX_PAGINATION_BUTTONS;
123
- $dbquery = new WP_User_Query(array(
124
- 'number' => $max_per_page,
125
- 'offset' => ($page_number - 1) * $max_per_page,
126
- 'fields' => 'all_with_meta',
127
- 'orderby' => 'ID',
128
- ));
 
 
129
 
130
  // Retrieve the results and build the pagination links.
131
  if ($dbquery) {
@@ -149,15 +167,17 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
149
  $user->user_registered_formatted = SucuriScan::datetime($user->user_registered_timestamp);
150
  $disabled = ($user->user_login == $session->user_login) ? 'disabled' : '';
151
 
152
- $params['ResetPassword.UserList'] .=
153
- SucuriScanTemplate::getSnippet('settings-posthack-reset-password', array(
154
- 'ResetPassword.UserID' => $user->ID,
155
- 'ResetPassword.Username' => $user->user_login,
156
- 'ResetPassword.Email' => $user->user_email,
157
- 'ResetPassword.Registered' => $user->user_registered_formatted,
158
- 'ResetPassword.Roles' => @implode(', ', $user->roles),
159
- 'ResetPassword.Disabled' => $disabled,
160
- ));
 
 
161
  }
162
  }
163
 
@@ -166,6 +186,8 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
166
 
167
  /**
168
  * Sets a new password for the specified user account.
 
 
169
  */
170
  public static function resetPasswordAjax()
171
  {
@@ -173,11 +195,11 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
173
  return;
174
  }
175
 
176
- $response = __('Error', SUCURISCAN_TEXTDOMAIN);
177
  $user_id = intval(SucuriScanRequest::post('user_id'));
178
 
179
  if (SucuriScanEvent::setNewPassword($user_id)) {
180
- $response = __('Done', SUCURISCAN_TEXTDOMAIN);
181
  SucuriScanEvent::reportNoticeEvent('Password changed for user #' . $user_id);
182
  }
183
 
@@ -186,12 +208,14 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
186
 
187
  /**
188
  * Reset all the FREE plugins, even if they are not activated.
 
 
189
  */
190
  public static function resetPlugins()
191
  {
192
  $params = array(
193
  'ResetPlugin.PluginList' => '',
194
- 'ResetPlugin.CacheLifeTime' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
195
  );
196
 
197
  if (defined('SUCURISCAN_GET_PLUGINS_LIFETIME')) {
@@ -203,6 +227,8 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
203
 
204
  /**
205
  * Find and list available updates for plugins and themes.
 
 
206
  */
207
  public static function availableUpdates()
208
  {
@@ -213,6 +239,8 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
213
 
214
  /**
215
  * Process the Ajax request to retrieve the plugins metadata.
 
 
216
  */
217
  public static function getPluginsAjax()
218
  {
@@ -227,22 +255,23 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
227
  $plugin_type_class = ( $plugin_data['PluginType'] == 'free' ) ? 'primary' : 'warning';
228
  $input_disabled = ( $plugin_data['PluginType'] == 'free' ) ? '' : 'disabled="disabled"';
229
  $plugin_status_class = $plugin_data['IsPluginActive'] ? 'success' : 'default';
230
- $plugin_status = $plugin_data['IsPluginActive']
231
- ? __('Active', SUCURISCAN_TEXTDOMAIN)
232
- : __('NotActive', SUCURISCAN_TEXTDOMAIN);
233
-
234
- $response .= SucuriScanTemplate::getSnippet('settings-posthack-reset-plugins', array(
235
- 'ResetPlugin.Disabled' => $input_disabled,
236
- 'ResetPlugin.Path' => $plugin_path,
237
- 'ResetPlugin.Unique' => crc32($plugin_path),
238
- 'ResetPlugin.Repository' => $plugin_data['Repository'],
239
- 'ResetPlugin.Plugin' => SucuriScan::excerpt($plugin_data['Name'], 60),
240
- 'ResetPlugin.Version' => $plugin_data['Version'],
241
- 'ResetPlugin.Type' => $plugin_data['PluginType'],
242
- 'ResetPlugin.TypeClass' => $plugin_type_class,
243
- 'ResetPlugin.Status' => $plugin_status,
244
- 'ResetPlugin.StatusClass' => $plugin_status_class,
245
- ));
 
246
  }
247
 
248
  wp_send_json($response, true);
@@ -250,6 +279,8 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
250
 
251
  /**
252
  * Process the Ajax request to reset one free plugin.
 
 
253
  */
254
  public static function resetPluginAjax()
255
  {
@@ -264,20 +295,20 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
264
  /* Check if the plugin actually exists */
265
  if (!array_key_exists($plugin, $allPlugins)) {
266
  $response = '<span class="sucuriscan-label-default">'
267
- . __('NotInstalled', SUCURISCAN_TEXTDOMAIN) . '</span>';
268
  } elseif ($allPlugins[$plugin]['IsFreePlugin'] !== true) {
269
  // Ignore plugins not listed in the WordPress repository.
270
  // This usually applies to premium plugins. They cannot be downloaded from
271
  // a reliable source because we can't check the checksum of the files nor
272
  // we can verify if the installation of the new code will work or not.
273
  $response = '<span class="sucuriscan-label-danger">'
274
- . __('PremiumPlugin', SUCURISCAN_TEXTDOMAIN) . '</span>';
275
  } elseif (!is_writable($allPlugins[$plugin]['InstallationPath'])) {
276
  $response = '<span class="sucuriscan-label-danger">'
277
- . __('NotWritable', SUCURISCAN_TEXTDOMAIN) . '</span>';
278
  } elseif (!class_exists('SucuriScanPluginInstallerSkin')) {
279
  $response = '<span class="sucuriscan-label-danger">'
280
- . __('MissingLibrary', SUCURISCAN_TEXTDOMAIN) . '</span>';
281
  } else {
282
  // Get data associated to the plugin.
283
  $data = $allPlugins[$plugin];
@@ -287,10 +318,10 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
287
 
288
  if (!$info) {
289
  $response = '<span class="sucuriscan-label-danger">'
290
- . __('CannotDownload', SUCURISCAN_TEXTDOMAIN) . '</span>';
291
  } elseif (!rename($data['InstallationPath'], $newpath)) {
292
  $response = '<span class="sucuriscan-label-danger">'
293
- . __('CannotBackup', SUCURISCAN_TEXTDOMAIN) . '</span>';
294
  } else {
295
  ob_start();
296
  $upgrader_skin = new SucuriScanPluginInstallerSkin();
@@ -303,7 +334,7 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
303
  /* Revert backup to its original location */
304
  @rename($newpath, $data['InstallationPath']);
305
  $response = '<span class="sucuriscan-label-danger">'
306
- . __('CannotInstall', SUCURISCAN_TEXTDOMAIN) . '</span>';
307
  } else {
308
  /* Destroy the backup of the plugin */
309
  $fifo = new SucuriScanFileInfo();
@@ -312,10 +343,7 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
312
  $fifo->skip_directories = false;
313
  $fifo->removeDirectoryTree($newpath);
314
 
315
- $installed = SucuriScan::escape(sprintf(
316
- __('VersionInstalled', SUCURISCAN_TEXTDOMAIN),
317
- $info['version'] /* mixed version number */
318
- ));
319
  $response = '<span class="sucuriscan-label-success">' . $installed . '</span>';
320
  }
321
  }
@@ -327,8 +355,8 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
327
  /**
328
  * Retrieve the information for the available updates.
329
  *
330
- * @param bool $send_email Sends the available updates via email.
331
- * @return string|bool HTML code for a table with the updates information.
332
  */
333
  public static function availableUpdatesContent($send_email = false)
334
  {
@@ -350,10 +378,10 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
350
  'Update.IconType' => 'plugins',
351
  'Update.Extension' => SucuriScan::excerpt($data->Name, 35),
352
  'Update.Version' => $data->Version,
353
- 'Update.NewVersion' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
354
- 'Update.TestedWith' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
355
- 'Update.ArchiveUrl' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
356
- 'Update.MarketUrl' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
357
  );
358
 
359
  if (property_exists($data->update, 'new_version')) {
@@ -382,15 +410,18 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
382
 
383
  if (is_array($updates) && !empty($updates)) {
384
  foreach ($updates as $data) {
385
- $response .= SucuriScanTemplate::getSnippet('settings-posthack-available-updates', array(
386
- 'Update.IconType' => 'appearance',
387
- 'Update.Extension' => SucuriScan::excerpt($data->Name, 35),
388
- 'Update.Version' => $data->Version,
389
- 'Update.NewVersion' => $data->update['new_version'],
390
- 'Update.TestedWith' => __('NewestWordPress', SUCURISCAN_TEXTDOMAIN),
391
- 'Update.ArchiveUrl' => $data->update['package'],
392
- 'Update.MarketUrl' => $data->update['url'],
393
- ));
 
 
 
394
  }
395
  }
396
 
@@ -412,6 +443,8 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
412
 
413
  /**
414
  * Process the Ajax request to retrieve the available updates.
 
 
415
  */
416
  public static function availableUpdatesAjax()
417
  {
@@ -422,7 +455,7 @@ class SucuriScanSettingsPosthack extends SucuriScanSettings
422
  $response = SucuriScanSettingsPosthack::availableUpdatesContent();
423
 
424
  if (!$response) {
425
- $response = '<tr><td colspan="5">' . __('NoUpdates', SUCURISCAN_TEXTDOMAIN) . '</td></tr>';
426
  }
427
 
428
  wp_send_json($response, 200);
3
  /**
4
  * Code related to the settings-posthack.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
28
  * The plugin allows to execute some tools that will clear up the site after a
29
  * suspicious activity. This includes the ability to reset the secret security
30
  * keys, the password for each user account, and the installed plugins.
31
+ *
32
+ * @category Library
33
+ * @package Sucuri
34
+ * @subpackage SucuriScanner
35
+ * @author Daniel Cid <dcid@sucuri.net>
36
+ * @copyright 2010-2017 Sucuri Inc.
37
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
38
+ * @link https://wordpress.org/plugins/sucuri-scanner
39
  */
40
  class SucuriScanSettingsPosthack extends SucuriScanSettings
41
  {
55
  // Update all WordPress secret keys.
56
  if (SucuriScanInterface::checkNonce() && SucuriScanRequest::post(':update_wpconfig')) {
57
  if (SucuriScanRequest::post(':process_form') != 1) {
58
+ SucuriScanInterface::error('You need to confirm that you understand the risk of this operation.');
59
  } else {
60
  $wpconfig_process = SucuriScanEvent::setNewConfigKeys();
61
 
62
  if (!$wpconfig_process) {
63
+ SucuriScanInterface::error('WordPress configuration file was not found.');
64
  } elseif ($wpconfig_process['updated']) {
65
  SucuriScanEvent::reportNoticeEvent('Generate new security keys (success)');
66
+ SucuriScanInterface::info('Secret keys updated successfully (summary of the operation bellow).');
67
 
68
  $params['WPConfigUpdate.Visibility'] = 'visible';
69
  $params['WPConfigUpdate.NewConfig'] .= "/* Old Security Keys */\n";
73
  $params['WPConfigUpdate.NewConfig'] .= $wpconfig_process['new_keys_string'];
74
  } else {
75
  SucuriScanEvent::reportNoticeEvent('Generate new security keys (failure)');
76
+ SucuriScanInterface::error('WordPress configuration file is not writable.');
77
 
78
  $params['WPConfigUpdate.Visibility'] = 'visible';
79
  $params['WPConfigUpdate.NewConfig'] = $wpconfig_process['new_wpconfig'];
88
  foreach ($key_list as $key_name => $key_value) {
89
  switch ($key_status) {
90
  case 'good':
91
+ $key_status_text = 'good';
92
  break;
93
 
94
  case 'bad':
95
+ $key_status_text = 'not randomized';
96
  break;
97
 
98
  case 'missing':
99
  $key_value = '';
100
+ $key_status_text = '(not set)';
101
  break;
102
  }
103
 
104
  if (isset($key_status_text)) {
105
+ $params['SecurityKeys.List'] .= SucuriScanTemplate::getSnippet(
106
+ 'settings-posthack-security-keys',
107
+ array(
108
+ 'SecurityKey.KeyName' => $key_name,
109
+ 'SecurityKey.KeyValue' => $key_value,
110
+ 'SecurityKey.KeyStatusText' => $key_status_text,
111
+ )
112
+ );
113
  }
114
  }
115
  }
136
  $user_list = array();
137
  $page_number = SucuriScanTemplate::pageNumber();
138
  $max_per_page = SUCURISCAN_MAX_PAGINATION_BUTTONS;
139
+ $dbquery = new WP_User_Query(
140
+ array(
141
+ 'number' => $max_per_page,
142
+ 'offset' => ($page_number - 1) * $max_per_page,
143
+ 'fields' => 'all_with_meta',
144
+ 'orderby' => 'ID',
145
+ )
146
+ );
147
 
148
  // Retrieve the results and build the pagination links.
149
  if ($dbquery) {
167
  $user->user_registered_formatted = SucuriScan::datetime($user->user_registered_timestamp);
168
  $disabled = ($user->user_login == $session->user_login) ? 'disabled' : '';
169
 
170
+ $params['ResetPassword.UserList'] .= SucuriScanTemplate::getSnippet(
171
+ 'settings-posthack-reset-password',
172
+ array(
173
+ 'ResetPassword.UserID' => $user->ID,
174
+ 'ResetPassword.Username' => $user->user_login,
175
+ 'ResetPassword.Email' => $user->user_email,
176
+ 'ResetPassword.Registered' => $user->user_registered_formatted,
177
+ 'ResetPassword.Roles' => @implode(', ', $user->roles),
178
+ 'ResetPassword.Disabled' => $disabled,
179
+ )
180
+ );
181
  }
182
  }
183
 
186
 
187
  /**
188
  * Sets a new password for the specified user account.
189
+ *
190
+ * @return void
191
  */
192
  public static function resetPasswordAjax()
193
  {
195
  return;
196
  }
197
 
198
+ $response = 'Error';
199
  $user_id = intval(SucuriScanRequest::post('user_id'));
200
 
201
  if (SucuriScanEvent::setNewPassword($user_id)) {
202
+ $response = 'Done';
203
  SucuriScanEvent::reportNoticeEvent('Password changed for user #' . $user_id);
204
  }
205
 
208
 
209
  /**
210
  * Reset all the FREE plugins, even if they are not activated.
211
+ *
212
+ * @return void
213
  */
214
  public static function resetPlugins()
215
  {
216
  $params = array(
217
  'ResetPlugin.PluginList' => '',
218
+ 'ResetPlugin.CacheLifeTime' => 'unknown',
219
  );
220
 
221
  if (defined('SUCURISCAN_GET_PLUGINS_LIFETIME')) {
227
 
228
  /**
229
  * Find and list available updates for plugins and themes.
230
+ *
231
+ * @return void
232
  */
233
  public static function availableUpdates()
234
  {
239
 
240
  /**
241
  * Process the Ajax request to retrieve the plugins metadata.
242
+ *
243
+ * @return void
244
  */
245
  public static function getPluginsAjax()
246
  {
255
  $plugin_type_class = ( $plugin_data['PluginType'] == 'free' ) ? 'primary' : 'warning';
256
  $input_disabled = ( $plugin_data['PluginType'] == 'free' ) ? '' : 'disabled="disabled"';
257
  $plugin_status_class = $plugin_data['IsPluginActive'] ? 'success' : 'default';
258
+ $plugin_status = $plugin_data['IsPluginActive'] ? 'active' : 'not active';
259
+
260
+ $response .= SucuriScanTemplate::getSnippet(
261
+ 'settings-posthack-reset-plugins',
262
+ array(
263
+ 'ResetPlugin.Disabled' => $input_disabled,
264
+ 'ResetPlugin.Path' => $plugin_path,
265
+ 'ResetPlugin.Unique' => crc32($plugin_path),
266
+ 'ResetPlugin.Repository' => $plugin_data['Repository'],
267
+ 'ResetPlugin.Plugin' => SucuriScan::excerpt($plugin_data['Name'], 60),
268
+ 'ResetPlugin.Version' => $plugin_data['Version'],
269
+ 'ResetPlugin.Type' => $plugin_data['PluginType'],
270
+ 'ResetPlugin.TypeClass' => $plugin_type_class,
271
+ 'ResetPlugin.Status' => $plugin_status,
272
+ 'ResetPlugin.StatusClass' => $plugin_status_class,
273
+ )
274
+ );
275
  }
276
 
277
  wp_send_json($response, true);
279
 
280
  /**
281
  * Process the Ajax request to reset one free plugin.
282
+ *
283
+ * @return void
284
  */
285
  public static function resetPluginAjax()
286
  {
295
  /* Check if the plugin actually exists */
296
  if (!array_key_exists($plugin, $allPlugins)) {
297
  $response = '<span class="sucuriscan-label-default">'
298
+ . 'not installed' . '</span>';
299
  } elseif ($allPlugins[$plugin]['IsFreePlugin'] !== true) {
300
  // Ignore plugins not listed in the WordPress repository.
301
  // This usually applies to premium plugins. They cannot be downloaded from
302
  // a reliable source because we can't check the checksum of the files nor
303
  // we can verify if the installation of the new code will work or not.
304
  $response = '<span class="sucuriscan-label-danger">'
305
+ . 'Plugin is Premium' . '</span>';
306
  } elseif (!is_writable($allPlugins[$plugin]['InstallationPath'])) {
307
  $response = '<span class="sucuriscan-label-danger">'
308
+ . 'Not Writable' . '</span>';
309
  } elseif (!class_exists('SucuriScanPluginInstallerSkin')) {
310
  $response = '<span class="sucuriscan-label-danger">'
311
+ . 'Missing Library' . '</span>';
312
  } else {
313
  // Get data associated to the plugin.
314
  $data = $allPlugins[$plugin];
318
 
319
  if (!$info) {
320
  $response = '<span class="sucuriscan-label-danger">'
321
+ . 'Cannot Download' . '</span>';
322
  } elseif (!rename($data['InstallationPath'], $newpath)) {
323
  $response = '<span class="sucuriscan-label-danger">'
324
+ . 'Cannot Backup' . '</span>';
325
  } else {
326
  ob_start();
327
  $upgrader_skin = new SucuriScanPluginInstallerSkin();
334
  /* Revert backup to its original location */
335
  @rename($newpath, $data['InstallationPath']);
336
  $response = '<span class="sucuriscan-label-danger">'
337
+ . 'Cannot Install' . '</span>';
338
  } else {
339
  /* Destroy the backup of the plugin */
340
  $fifo = new SucuriScanFileInfo();
343
  $fifo->skip_directories = false;
344
  $fifo->removeDirectoryTree($newpath);
345
 
346
+ $installed = 'Installed v' . SucuriScan::escape($info['version']);
 
 
 
347
  $response = '<span class="sucuriscan-label-success">' . $installed . '</span>';
348
  }
349
  }
355
  /**
356
  * Retrieve the information for the available updates.
357
  *
358
+ * @param bool $send_email Sends the available updates via email.
359
+ * @return string|bool HTML code for a table with the updates information.
360
  */
361
  public static function availableUpdatesContent($send_email = false)
362
  {
378
  'Update.IconType' => 'plugins',
379
  'Update.Extension' => SucuriScan::excerpt($data->Name, 35),
380
  'Update.Version' => $data->Version,
381
+ 'Update.NewVersion' => 'unknown',
382
+ 'Update.TestedWith' => 'unknown',
383
+ 'Update.ArchiveUrl' => 'unknown',
384
+ 'Update.MarketUrl' => 'unknown',
385
  );
386
 
387
  if (property_exists($data->update, 'new_version')) {
410
 
411
  if (is_array($updates) && !empty($updates)) {
412
  foreach ($updates as $data) {
413
+ $response .= SucuriScanTemplate::getSnippet(
414
+ 'settings-posthack-available-updates',
415
+ array(
416
+ 'Update.IconType' => 'appearance',
417
+ 'Update.Extension' => SucuriScan::excerpt($data->Name, 35),
418
+ 'Update.Version' => $data->Version,
419
+ 'Update.NewVersion' => $data->update['new_version'],
420
+ 'Update.TestedWith' => 'Newest WordPress',
421
+ 'Update.ArchiveUrl' => $data->update['package'],
422
+ 'Update.MarketUrl' => $data->update['url'],
423
+ )
424
+ );
425
  }
426
  }
427
 
443
 
444
  /**
445
  * Process the Ajax request to retrieve the available updates.
446
+ *
447
+ * @return void
448
  */
449
  public static function availableUpdatesAjax()
450
  {
455
  $response = SucuriScanSettingsPosthack::availableUpdatesContent();
456
 
457
  if (!$response) {
458
+ $response = '<tr><td colspan="5">' . 'There are no updates available.' . '</td></tr>';
459
  }
460
 
461
  wp_send_json($response, 200);
src/settings-scanner.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the settings-scanner.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage settings-scanner.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -18,14 +24,22 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
18
 
19
  /**
20
  * Returns the HTML to configure the scanner.
 
 
 
 
 
 
 
 
21
  */
22
  class SucuriScanSettingsScanner extends SucuriScanSettings
23
  {
24
  /**
25
  * Renders a page with information about the cronjobs feature.
26
  *
27
- * @param bool $nonce True if the CSRF protection worked.
28
- * @return string Page with information about the cronjobs.
29
  */
30
  public static function cronjobs($nonce)
31
  {
@@ -41,8 +55,9 @@ class SucuriScanSettingsScanner extends SucuriScanSettings
41
  $allowed_actions[] = 'runnow'; /* execute in the next 10 seconds */
42
  $allowed_actions[] = 'remove'; /* can be reinstalled automatically */
43
  $allowed_actions = sprintf('(%s)', implode('|', $allowed_actions));
 
44
 
45
- if ($cronjob_action = SucuriScanRequest::post(':cronjob_action', $allowed_actions)) {
46
  $cronjobs = SucuriScanRequest::post(':cronjobs', '_array');
47
 
48
  if (!empty($cronjobs)) {
@@ -50,43 +65,55 @@ class SucuriScanSettingsScanner extends SucuriScanSettings
50
 
51
  if ($cronjob_action == 'runnow') {
52
  /* Force execution of the selected scheduled tasks. */
53
- SucuriScanInterface::info(sprintf(
54
- __('CronjobsWillRunSoon', SUCURISCAN_TEXTDOMAIN),
55
- $total_tasks /* some cronjobs will be ignored */
56
- ));
57
- SucuriScanEvent::reportNoticeEvent(sprintf(
58
- 'Force execution of scheduled tasks: (multiple entries): %s',
59
- @implode(',', $cronjobs)
60
- ));
 
 
 
 
61
 
62
  foreach ($cronjobs as $task_name) {
63
  wp_schedule_single_event(time() + 10, $task_name);
64
  }
65
  } elseif ($cronjob_action == 'remove' || $cronjob_action == '_oneoff') {
66
  /* Force deletion of the selected scheduled tasks. */
67
- SucuriScanInterface::info(sprintf(
68
- __('CronjobsWereDeleted', SUCURISCAN_TEXTDOMAIN),
69
- $total_tasks /* some cronjobs will be ignored */
70
- ));
71
- SucuriScanEvent::reportNoticeEvent(sprintf(
72
- 'Delete scheduled tasks: (multiple entries): %s',
73
- @implode(',', $cronjobs)
74
- ));
 
 
 
 
75
 
76
  foreach ($cronjobs as $task_name) {
77
  wp_clear_scheduled_hook($task_name);
78
  }
79
  } else {
80
- SucuriScanInterface::info(sprintf(
81
- __('CronjobsWereReinstalled', SUCURISCAN_TEXTDOMAIN),
82
- $total_tasks, /* some cronjobs will be ignored */
83
- $cronjob_action /* frequency to run cronjob */
84
- ));
85
- SucuriScanEvent::reportNoticeEvent(sprintf(
86
- 'Re-configure scheduled tasks %s: (multiple entries): %s',
87
- $cronjob_action,
88
- @implode(',', $cronjobs)
89
- ));
 
 
 
 
90
 
91
  foreach ($cronjobs as $task_name) {
92
  $next_due = wp_next_scheduled($task_name);
@@ -94,7 +121,7 @@ class SucuriScanSettingsScanner extends SucuriScanSettings
94
  }
95
  }
96
  } else {
97
- SucuriScanInterface::error(__('CronjobsWereNotSelected', SUCURISCAN_TEXTDOMAIN));
98
  }
99
  }
100
  }
@@ -104,7 +131,7 @@ class SucuriScanSettingsScanner extends SucuriScanSettings
104
 
105
  /* Hardcode the first one to allow the immediate execution of the cronjob(s) */
106
  $params['Cronjob.Schedules'] .= '<option value="runnow">'
107
- . __('CronjobRunNow', SUCURISCAN_TEXTDOMAIN) . '</option>';
108
 
109
  foreach ($available as $freq => $name) {
110
  $params['Cronjob.Schedules'] .= sprintf('<option value="%s">%s</option>', $freq, $name);
@@ -118,14 +145,16 @@ class SucuriScanSettingsScanner extends SucuriScanSettings
118
  }
119
 
120
  $params['Cronjobs.Total'] += 1;
121
- $params['Cronjobs.List'] .=
122
- SucuriScanTemplate::getSnippet('settings-scanner-cronjobs', array(
123
- 'Cronjob.Hook' => $hook,
124
- 'Cronjob.Schedule' => $event['schedule'],
125
- 'Cronjob.NextTime' => SucuriScan::datetime($timestamp),
126
- 'Cronjob.NextTimeHuman' => SucuriScan::humanTime($timestamp),
127
- 'Cronjob.Arguments' => SucuriScan::implode(', ', $event['args']),
128
- ));
 
 
129
  }
130
  }
131
  }
@@ -136,49 +165,6 @@ class SucuriScanSettingsScanner extends SucuriScanSettings
136
  return SucuriScanTemplate::getSection('settings-scanner-cronjobs', $params);
137
  }
138
 
139
- /**
140
- * Returns a list of directories in the website.
141
- */
142
- public static function ignoreFoldersAjax()
143
- {
144
- if (SucuriScanRequest::post('form_action') !== 'get_ignored_files') {
145
- return;
146
- }
147
-
148
- $response = ''; /* request response */
149
- $ignored_dirs = SucuriScanFSScanner::getIgnoredDirectoriesLive();
150
-
151
- foreach ($ignored_dirs as $group => $dir_list) {
152
- foreach ($dir_list as $dir_data) {
153
- $valid_entry = false;
154
- $snippet = array(
155
- 'IgnoreScan.Directory' => '',
156
- 'IgnoreScan.DirectoryPath' => '',
157
- 'IgnoreScan.IgnoredAt' => '',
158
- 'IgnoreScan.IgnoredAtText' => __('Okay', SUCURISCAN_TEXTDOMAIN),
159
- );
160
-
161
- if ($group == 'is_ignored') {
162
- $valid_entry = true;
163
- $snippet['IgnoreScan.Directory'] = urlencode($dir_data['directory_path']);
164
- $snippet['IgnoreScan.DirectoryPath'] = $dir_data['directory_path'];
165
- $snippet['IgnoreScan.IgnoredAt'] = SucuriScan::datetime($dir_data['ignored_at']);
166
- $snippet['IgnoreScan.IgnoredAtText'] = __('Ignored', SUCURISCAN_TEXTDOMAIN);
167
- } elseif ($group == 'is_not_ignored') {
168
- $valid_entry = true;
169
- $snippet['IgnoreScan.Directory'] = urlencode($dir_data);
170
- $snippet['IgnoreScan.DirectoryPath'] = $dir_data;
171
- }
172
-
173
- if ($valid_entry) {
174
- $response .= SucuriScanTemplate::getSnippet('settings-scanner-ignore-folders', $snippet);
175
- }
176
- }
177
- }
178
-
179
- wp_send_json($response, true);
180
- }
181
-
182
  /**
183
  * Returns the HTML for the folder scanner skipper.
184
  *
@@ -187,63 +173,55 @@ class SucuriScanSettingsScanner extends SucuriScanSettings
187
  * directories with media files like images, audio, videos, etc and directories
188
  * used to store cache data.
189
  *
190
- * @param bool $nonce True if the CSRF protection worked, false otherwise.
191
- * @return string HTML for the folder scanner skipper.
192
  */
193
  public static function ignoreFolders($nonce)
194
  {
195
  $params = array();
196
 
197
- if ($nonce) {
198
- // Ignore a new directory path for the file system scans.
199
- if ($action = SucuriScanRequest::post(':ignorescanning_action', '(ignore|unignore)')) {
200
- $ign_dirs = SucuriScanRequest::post(':ignorescanning_dirs', '_array');
201
- $ign_file = SucuriScanRequest::post(':ignorescanning_file');
202
-
203
- if ($action == 'ignore') {
204
- // Target a single file path to be ignored.
205
- if ($ign_file !== false) {
206
- $ign_dirs = array($ign_file);
207
- unset($_POST['sucuriscan_ignorescanning_file']);
208
- }
209
-
210
- // Target a list of directories to be ignored.
211
- if (is_array($ign_dirs) && !empty($ign_dirs)) {
212
- $were_ignored = array();
213
 
214
- foreach ($ign_dirs as $resource_path) {
215
- if (file_exists($resource_path)
216
- && SucuriScanFSScanner::ignoreDirectory($resource_path)
217
- ) {
218
- $were_ignored[] = $resource_path;
219
- }
220
- }
221
 
222
- if (!empty($were_ignored)) {
223
- SucuriScanInterface::info(__('ItemsProcessed', SUCURISCAN_TEXTDOMAIN));
224
- SucuriScanEvent::reportWarningEvent(sprintf(
225
- 'Resources will not be scanned: (multiple entries): %s',
226
- @implode(',', $ign_dirs)
227
- ));
228
- }
229
- }
230
- } elseif ($action == 'unignore'
231
- && is_array($ign_dirs)
232
- && !empty($ign_dirs)
233
- ) {
234
- foreach ($ign_dirs as $directory_path) {
235
- SucuriScanFSScanner::unignoreDirectory($directory_path);
236
- }
237
 
238
- SucuriScanInterface::info(__('ItemsProcessed', SUCURISCAN_TEXTDOMAIN));
239
- SucuriScanEvent::reportNoticeEvent(sprintf(
240
- 'Resources will be scanned: (multiple entries): %s',
241
- @implode(',', $ign_dirs)
242
- ));
243
  }
 
 
 
 
 
 
244
  }
245
  }
246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  return SucuriScanTemplate::getSection('settings-scanner-ignore-folders', $params);
248
  }
249
  }
3
  /**
4
  * Code related to the settings-scanner.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
24
 
25
  /**
26
  * Returns the HTML to configure the scanner.
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 SucuriScanSettingsScanner extends SucuriScanSettings
37
  {
38
  /**
39
  * Renders a page with information about the cronjobs feature.
40
  *
41
+ * @param bool $nonce True if the CSRF protection worked.
42
+ * @return string Page with information about the cronjobs.
43
  */
44
  public static function cronjobs($nonce)
45
  {
55
  $allowed_actions[] = 'runnow'; /* execute in the next 10 seconds */
56
  $allowed_actions[] = 'remove'; /* can be reinstalled automatically */
57
  $allowed_actions = sprintf('(%s)', implode('|', $allowed_actions));
58
+ $cronjob_action = SucuriScanRequest::post(':cronjob_action', $allowed_actions);
59
 
60
+ if ($cronjob_action) {
61
  $cronjobs = SucuriScanRequest::post(':cronjobs', '_array');
62
 
63
  if (!empty($cronjobs)) {
65
 
66
  if ($cronjob_action == 'runnow') {
67
  /* Force execution of the selected scheduled tasks. */
68
+ SucuriScanInterface::info(
69
+ sprintf(
70
+ '%d tasks has been scheduled to run in the next ten seconds.',
71
+ $total_tasks /* some cronjobs will be ignored */
72
+ )
73
+ );
74
+ SucuriScanEvent::reportNoticeEvent(
75
+ sprintf(
76
+ 'Force execution of scheduled tasks: (multiple entries): %s',
77
+ @implode(',', $cronjobs)
78
+ )
79
+ );
80
 
81
  foreach ($cronjobs as $task_name) {
82
  wp_schedule_single_event(time() + 10, $task_name);
83
  }
84
  } elseif ($cronjob_action == 'remove' || $cronjob_action == '_oneoff') {
85
  /* Force deletion of the selected scheduled tasks. */
86
+ SucuriScanInterface::info(
87
+ sprintf(
88
+ '%d scheduled tasks have been removed.',
89
+ $total_tasks /* some cronjobs will be ignored */
90
+ )
91
+ );
92
+ SucuriScanEvent::reportNoticeEvent(
93
+ sprintf(
94
+ 'Delete scheduled tasks: (multiple entries): %s',
95
+ @implode(',', $cronjobs)
96
+ )
97
+ );
98
 
99
  foreach ($cronjobs as $task_name) {
100
  wp_clear_scheduled_hook($task_name);
101
  }
102
  } else {
103
+ SucuriScanInterface::info(
104
+ sprintf(
105
+ '%d tasks has been re-scheduled to run <code>%s</code>.',
106
+ $total_tasks, /* some cronjobs will be ignored */
107
+ $cronjob_action /* frequency to run cronjob */
108
+ )
109
+ );
110
+ SucuriScanEvent::reportNoticeEvent(
111
+ sprintf(
112
+ 'Re-configure scheduled tasks %s: (multiple entries): %s',
113
+ $cronjob_action,
114
+ @implode(',', $cronjobs)
115
+ )
116
+ );
117
 
118
  foreach ($cronjobs as $task_name) {
119
  $next_due = wp_next_scheduled($task_name);
121
  }
122
  }
123
  } else {
124
+ SucuriScanInterface::error('No scheduled tasks were selected from the list.');
125
  }
126
  }
127
  }
131
 
132
  /* Hardcode the first one to allow the immediate execution of the cronjob(s) */
133
  $params['Cronjob.Schedules'] .= '<option value="runnow">'
134
+ . 'Execute Now (in +10 seconds)' . '</option>';
135
 
136
  foreach ($available as $freq => $name) {
137
  $params['Cronjob.Schedules'] .= sprintf('<option value="%s">%s</option>', $freq, $name);
145
  }
146
 
147
  $params['Cronjobs.Total'] += 1;
148
+ $params['Cronjobs.List'] .= SucuriScanTemplate::getSnippet(
149
+ 'settings-scanner-cronjobs',
150
+ array(
151
+ 'Cronjob.Hook' => $hook,
152
+ 'Cronjob.Schedule' => $event['schedule'],
153
+ 'Cronjob.NextTime' => SucuriScan::datetime($timestamp),
154
+ 'Cronjob.NextTimeHuman' => SucuriScan::humanTime($timestamp),
155
+ 'Cronjob.Arguments' => SucuriScan::implode(', ', $event['args']),
156
+ )
157
+ );
158
  }
159
  }
160
  }
165
  return SucuriScanTemplate::getSection('settings-scanner-cronjobs', $params);
166
  }
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  /**
169
  * Returns the HTML for the folder scanner skipper.
170
  *
173
  * directories with media files like images, audio, videos, etc and directories
174
  * used to store cache data.
175
  *
176
+ * @param bool $nonce True if the CSRF protection worked, false otherwise.
177
+ * @return string HTML for the folder scanner skipper.
178
  */
179
  public static function ignoreFolders($nonce)
180
  {
181
  $params = array();
182
 
183
+ $params['IgnoreScan.List'] = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
+ if ($nonce) {
186
+ $ign_ress = SucuriScanRequest::post(':ignorefolder');
187
+ $ign_dirs = SucuriScanRequest::post(':unignorefolders', '_array');
 
 
 
 
188
 
189
+ if ($ign_ress !== false && SucuriScanFSScanner::ignoreDirectory($ign_ress)) {
190
+ SucuriScanInterface::info('Selected files have been successfully processed.');
191
+ SucuriScanEvent::reportWarningEvent('This directory will not be scanned: ' . $ign_ress);
192
+ }
 
 
 
 
 
 
 
 
 
 
 
193
 
194
+ if ($ign_dirs !== false && is_array($ign_dirs) && !empty($ign_dirs)) {
195
+ foreach ($ign_dirs as $dir) {
196
+ SucuriScanFSScanner::unignoreDirectory($dir);
 
 
197
  }
198
+
199
+ SucuriScanInterface::info('Selected files have been successfully processed.');
200
+ SucuriScanEvent::reportNoticeEvent(
201
+ 'Directories will be scanned: (multiple entries): '
202
+ . @implode(',', $ign_dirs) /* all directories */
203
+ );
204
  }
205
  }
206
 
207
+ $ignored_dirs = SucuriScanFSScanner::getIgnoredDirectories();
208
+
209
+ foreach ($ignored_dirs['directories'] as $index => $folder) {
210
+ $ts = $ignored_dirs['ignored_at_list'][$index];
211
+
212
+ $params['IgnoreScan.List'] .= SucuriScanTemplate::getSnippet(
213
+ 'settings-scanner-ignore-folders',
214
+ array(
215
+ 'IgnoreScan.Directory' => $folder,
216
+ 'IgnoreScan.IgnoredAt' => SucuriScan::datetime($ts),
217
+ )
218
+ );
219
+ }
220
+
221
+ if (empty($ignored_dirs['directories'])) {
222
+ $params['IgnoreScan.List'] .= '<tr><td colspan="3">no data available</td></tr>';
223
+ }
224
+
225
  return SucuriScanTemplate::getSection('settings-scanner-ignore-folders', $params);
226
  }
227
  }
src/settings-webinfo.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the settings-webinfo.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage settings-webinfo.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -23,25 +29,19 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
23
  */
24
  function sucuriscan_settings_webinfo_details()
25
  {
26
- global $wpdb;
27
-
28
- $params = array(
29
- 'ServerInfo.Variables' => '',
30
- );
31
-
32
  $info_vars = array(
33
  'Plugin_version' => SUCURISCAN_VERSION,
34
  'Last_filesystem_scan' => SucuriScanFSScanner::getFilesystemRuntime(true),
35
  'Datetime_and_Timezone' => '',
36
  'Operating_system' => sprintf('%s (%d Bit)', PHP_OS, PHP_INT_SIZE * 8),
37
- 'Server' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
38
- 'WordPress_debug' => __('NotActive', SUCURISCAN_TEXTDOMAIN),
39
- 'Memory_usage' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
40
- 'MySQL_version' => '0.0',
41
- 'SQL_mode' => __('NotSet', SUCURISCAN_TEXTDOMAIN),
42
  'PHP_version' => PHP_VERSION,
43
  );
44
 
 
45
  $info_vars['Datetime_and_Timezone'] = sprintf(
46
  '%s (%s)',
47
  SucuriScan::datetime(),
@@ -49,7 +49,7 @@ function sucuriscan_settings_webinfo_details()
49
  );
50
 
51
  if (defined('WP_DEBUG') && WP_DEBUG) {
52
- $info_vars['WordPress_debug'] = __('Active', SUCURISCAN_TEXTDOMAIN);
53
  }
54
 
55
  if (function_exists('memory_get_usage')) {
@@ -60,15 +60,6 @@ function sucuriscan_settings_webinfo_details()
60
  $info_vars['Server'] = $_SERVER['SERVER_SOFTWARE'];
61
  }
62
 
63
- if ($wpdb) {
64
- $info_vars['MySQL_version'] = $wpdb->get_var('SELECT VERSION() AS version');
65
-
66
- $mysql_info = $wpdb->get_results('SHOW VARIABLES LIKE "sql_mode"');
67
- if (is_array($mysql_info) && !empty($mysql_info[0]->Value)) {
68
- $info_vars['SQL_mode'] = $mysql_info[0]->Value;
69
- }
70
- }
71
-
72
  /* PHP INI Settings */
73
  $field_names = array(
74
  'safe_mode',
@@ -106,11 +97,13 @@ function sucuriscan_settings_webinfo_details()
106
  foreach ($info_vars as $var_name => $var_value) {
107
  $var_name = str_replace('_', "\x20", $var_name);
108
 
109
- $params['ServerInfo.Variables'] .=
110
- SucuriScanTemplate::getSnippet('settings-webinfo-details', array(
111
- 'ServerInfo.Title' => $var_name,
112
- 'ServerInfo.Value' => $var_value,
113
- ));
 
 
114
  }
115
 
116
  return SucuriScanTemplate::getSection('settings-webinfo-details', $params);
@@ -131,7 +124,7 @@ function sucuriscan_settings_webinfo_htaccess()
131
  'HTAccess.StandardVisible' => 'hidden',
132
  'HTAccess.NotFoundVisible' => 'hidden',
133
  'HTAccess.FoundVisible' => 'hidden',
134
- 'HTAccess.Fpath' => __('Unknown', SUCURISCAN_TEXTDOMAIN),
135
  );
136
 
137
  if ($htaccess) {
@@ -165,7 +158,9 @@ function sucuriscan_settings_webinfo_htaccess()
165
  function sucuriscan_htaccess_is_standard($rules = '')
166
  {
167
  if (!$rules) {
168
- if ($htaccess = SucuriScan::getHtaccessPath()) {
 
 
169
  $rules = SucuriScanFileInfo::fileContent($htaccess);
170
  }
171
  }
3
  /**
4
  * Code related to the settings-webinfo.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
29
  */
30
  function sucuriscan_settings_webinfo_details()
31
  {
32
+ $params = array();
 
 
 
 
 
33
  $info_vars = array(
34
  'Plugin_version' => SUCURISCAN_VERSION,
35
  'Last_filesystem_scan' => SucuriScanFSScanner::getFilesystemRuntime(true),
36
  'Datetime_and_Timezone' => '',
37
  'Operating_system' => sprintf('%s (%d Bit)', PHP_OS, PHP_INT_SIZE * 8),
38
+ 'Server' => 'unknown',
39
+ 'WordPress_debug' => 'not active',
40
+ 'Memory_usage' => 'unknown',
 
 
41
  'PHP_version' => PHP_VERSION,
42
  );
43
 
44
+ $params['ServerInfo.Variables'] = '';
45
  $info_vars['Datetime_and_Timezone'] = sprintf(
46
  '%s (%s)',
47
  SucuriScan::datetime(),
49
  );
50
 
51
  if (defined('WP_DEBUG') && WP_DEBUG) {
52
+ $info_vars['WordPress_debug'] = 'active';
53
  }
54
 
55
  if (function_exists('memory_get_usage')) {
60
  $info_vars['Server'] = $_SERVER['SERVER_SOFTWARE'];
61
  }
62
 
 
 
 
 
 
 
 
 
 
63
  /* PHP INI Settings */
64
  $field_names = array(
65
  'safe_mode',
97
  foreach ($info_vars as $var_name => $var_value) {
98
  $var_name = str_replace('_', "\x20", $var_name);
99
 
100
+ $params['ServerInfo.Variables'] .= SucuriScanTemplate::getSnippet(
101
+ 'settings-webinfo-details',
102
+ array(
103
+ 'ServerInfo.Title' => $var_name,
104
+ 'ServerInfo.Value' => $var_value,
105
+ )
106
+ );
107
  }
108
 
109
  return SucuriScanTemplate::getSection('settings-webinfo-details', $params);
124
  'HTAccess.StandardVisible' => 'hidden',
125
  'HTAccess.NotFoundVisible' => 'hidden',
126
  'HTAccess.FoundVisible' => 'hidden',
127
+ 'HTAccess.Fpath' => 'unknown',
128
  );
129
 
130
  if ($htaccess) {
158
  function sucuriscan_htaccess_is_standard($rules = '')
159
  {
160
  if (!$rules) {
161
+ $htaccess = SucuriScan::getHtaccessPath();
162
+
163
+ if ($htaccess) {
164
  $rules = SucuriScanFileInfo::fileContent($htaccess);
165
  }
166
  }
src/settings.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the settings.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage settings.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -18,6 +24,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
18
 
19
  /**
20
  * Abstract class for the settings page.
 
 
 
 
 
 
 
 
21
  */
22
  class SucuriScanSettings extends SucuriScanOption
23
  {
3
  /**
4
  * Code related to the settings.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
24
 
25
  /**
26
  * Abstract class for the settings page.
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 SucuriScanSettings extends SucuriScanOption
37
  {
src/sitecheck.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the sitecheck.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage sitecheck.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -28,7 +34,14 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
28
  * operation to finish, otherwise the scanner will return innacurate
29
  * information.
30
  *
31
- * @see https://sitecheck.sucuri.net/
 
 
 
 
 
 
 
32
  */
33
  class SucuriScanSiteCheck extends SucuriScanAPI
34
  {
@@ -39,14 +52,14 @@ class SucuriScanSiteCheck extends SucuriScanAPI
39
  */
40
  private static function targetURL()
41
  {
42
- $url = SucuriScan::getDomain();
43
-
44
  /* allow to set a custom URL for the scans */
45
- if ($custom = SucuriScanOption::getOption(':sitecheck_target')) {
 
 
46
  return $custom;
47
  }
48
 
49
- return $url;
50
  }
51
 
52
  /**
@@ -54,8 +67,8 @@ class SucuriScanSiteCheck extends SucuriScanAPI
54
  *
55
  * @see https://sitecheck.sucuri.net/
56
  *
57
- * @param bool $clear Request the results from a fresh scan or not.
58
- * @return array|bool JSON encoded website scan results.
59
  */
60
  public static function runMalwareScan($clear = false)
61
  {
@@ -208,11 +221,13 @@ class SucuriScanSiteCheck extends SucuriScanAPI
208
  continue;
209
  }
210
 
211
- $params['SiteCheck.Metadata'] .=
212
- SucuriScanTemplate::getSnippet('sitecheck-details', array(
213
- 'SiteCheck.Title' => trim($parts[0]),
214
- 'SiteCheck.Value' => trim($parts[1]),
215
- ));
 
 
216
  }
217
  }
218
 
@@ -231,26 +246,30 @@ class SucuriScanSiteCheck extends SucuriScanAPI
231
 
232
  $params['Malware.Content'] = '';
233
  $params['Malware.Color'] = 'green';
234
- $params['Malware.Title'] = __('SiteClean', SUCURISCAN_TEXTDOMAIN);
235
  $params['Malware.CleanVisibility'] = 'visible';
236
  $params['Malware.InfectedVisibility'] = 'hidden';
237
 
238
  if (isset($data['MALWARE']['WARN']) && !empty($data['MALWARE']['WARN'])) {
239
  $params['Malware.Color'] = 'red';
240
- $params['Malware.Title'] = __('SiteNotClean', SUCURISCAN_TEXTDOMAIN);
241
  $params['Malware.CleanVisibility'] = 'hidden';
242
  $params['Malware.InfectedVisibility'] = 'visible';
243
 
244
  foreach ($data['MALWARE']['WARN'] as $mal) {
245
- if ($info = self::malwareDetails($mal)) {
246
- $params['Malware.Content'] .=
247
- SucuriScanTemplate::getSnippet('sitecheck-malware', array(
248
- 'Malware.InfectedURL' => $info['infected_url'],
249
- 'Malware.MalwareType' => $info['malware_type'],
250
- 'Malware.MalwareDocs' => $info['malware_docs'],
251
- 'Malware.AlertMessage' => $info['alert_message'],
252
- 'Malware.MalwarePayload' => $info['malware_payload'],
253
- ));
 
 
 
 
254
  }
255
  }
256
  }
@@ -272,7 +291,7 @@ class SucuriScanSiteCheck extends SucuriScanAPI
272
  return ''; /* there is not enough information to render */
273
  }
274
 
275
- $params['Blacklist.Title'] = __('NotBlacklisted', SUCURISCAN_TEXTDOMAIN);
276
  $params['Blacklist.Color'] = 'green';
277
  $params['Blacklist.Content'] = '';
278
 
@@ -285,17 +304,19 @@ class SucuriScanSiteCheck extends SucuriScanAPI
285
  substr($info[0], 0, strrpos($info[0], ':'))
286
  );
287
 
288
- $params['Blacklist.Content'] .=
289
- SucuriScanTemplate::getSnippet('sitecheck-blacklist', array(
290
- 'Blacklist.URL' => $url,
291
- 'Blacklist.Status' => $type,
292
- 'Blacklist.Service' => $title,
293
- ));
 
 
294
  }
295
  }
296
 
297
  if (isset($data['BLACKLIST']['WARN'])) {
298
- $params['Blacklist.Title'] = __('Blacklisted', SUCURISCAN_TEXTDOMAIN);
299
  $params['Blacklist.Color'] = 'red';
300
  }
301
 
@@ -322,12 +343,14 @@ class SucuriScanSiteCheck extends SucuriScanAPI
322
  }
323
 
324
  $params['Recommendations.Visibility'] = 'visible';
325
- $params['Recommendations.Content'] .=
326
- SucuriScanTemplate::getSnippet('sitecheck-recommendations', array(
327
- 'Recommendations.Title' => $recommendation[0],
328
- 'Recommendations.Value' => $recommendation[1],
329
- 'Recommendations.URL' => $recommendation[2],
330
- ));
 
 
331
  }
332
  }
333
 
@@ -343,10 +366,7 @@ class SucuriScanSiteCheck extends SucuriScanAPI
343
  {
344
  $data = self::scanAndCollectData();
345
 
346
- return sprintf(
347
- __('iFramesNum', SUCURISCAN_TEXTDOMAIN),
348
- @count($data['LINKS']['IFRAME'])
349
- );
350
  }
351
 
352
  /**
@@ -358,10 +378,7 @@ class SucuriScanSiteCheck extends SucuriScanAPI
358
  {
359
  $data = self::scanAndCollectData();
360
 
361
- return sprintf(
362
- __('LinksNum', SUCURISCAN_TEXTDOMAIN),
363
- @count($data['LINKS']['URL'])
364
- );
365
  }
366
 
367
  /**
@@ -382,10 +399,7 @@ class SucuriScanSiteCheck extends SucuriScanAPI
382
  $total += count($data['LINKS']['JSEXTERNAL']);
383
  }
384
 
385
- return sprintf(
386
- __('ScriptsNum', SUCURISCAN_TEXTDOMAIN),
387
- $total
388
- );
389
  }
390
 
391
  /**
@@ -434,8 +448,8 @@ class SucuriScanSiteCheck extends SucuriScanAPI
434
  /**
435
  * Extract detailed information from a SiteCheck malware payload.
436
  *
437
- * @param array $malware Array with two entries with basic malware information.
438
- * @return array Detailed information of the malware found by SiteCheck.
439
  */
440
  public static function malwareDetails($malware = array())
441
  {
@@ -467,7 +481,7 @@ class SucuriScanSiteCheck extends SucuriScanAPI
467
  if (strpos($malware_parts[0], $pattern) !== false) {
468
  $offset = strpos($malware_parts[0], $pattern);
469
  $data['malware_type'] = substr($malware_parts[0], 0, $offset);
470
- $data['malware_docs'] = substr($malware_parts[0], $offset+11);
471
  }
472
 
473
  $data['malware_payload'] = trim($malware_parts[1]);
@@ -484,6 +498,8 @@ class SucuriScanSiteCheck extends SucuriScanAPI
484
  * output it means that XDebug cannot cover the next line, leaving a report
485
  * with a missing line in the coverage. Since the test case takes care of
486
  * the functionality of this code we will assume that it is fully covered.
 
 
487
  */
488
  public static function ajaxMalwareScan()
489
  {
3
  /**
4
  * Code related to the sitecheck.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
34
  * operation to finish, otherwise the scanner will return innacurate
35
  * information.
36
  *
37
+ * @category Library
38
+ * @package Sucuri
39
+ * @subpackage SucuriScanner
40
+ * @author Daniel Cid <dcid@sucuri.net>
41
+ * @copyright 2010-2017 Sucuri Inc.
42
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
43
+ * @link https://wordpress.org/plugins/sucuri-scanner
44
+ * @see https://sitecheck.sucuri.net/
45
  */
46
  class SucuriScanSiteCheck extends SucuriScanAPI
47
  {
52
  */
53
  private static function targetURL()
54
  {
 
 
55
  /* allow to set a custom URL for the scans */
56
+ $custom = SucuriScanOption::getOption(':sitecheck_target');
57
+
58
+ if ($custom) {
59
  return $custom;
60
  }
61
 
62
+ return SucuriScan::getDomain();
63
  }
64
 
65
  /**
67
  *
68
  * @see https://sitecheck.sucuri.net/
69
  *
70
+ * @param bool $clear Request the results from a fresh scan or not.
71
+ * @return array|bool JSON encoded website scan results.
72
  */
73
  public static function runMalwareScan($clear = false)
74
  {
221
  continue;
222
  }
223
 
224
+ $params['SiteCheck.Metadata'] .= SucuriScanTemplate::getSnippet(
225
+ 'sitecheck-details',
226
+ array(
227
+ 'SiteCheck.Title' => trim($parts[0]),
228
+ 'SiteCheck.Value' => trim($parts[1]),
229
+ )
230
+ );
231
  }
232
  }
233
 
246
 
247
  $params['Malware.Content'] = '';
248
  $params['Malware.Color'] = 'green';
249
+ $params['Malware.Title'] = 'Site is Clean';
250
  $params['Malware.CleanVisibility'] = 'visible';
251
  $params['Malware.InfectedVisibility'] = 'hidden';
252
 
253
  if (isset($data['MALWARE']['WARN']) && !empty($data['MALWARE']['WARN'])) {
254
  $params['Malware.Color'] = 'red';
255
+ $params['Malware.Title'] = 'Site is not Clean';
256
  $params['Malware.CleanVisibility'] = 'hidden';
257
  $params['Malware.InfectedVisibility'] = 'visible';
258
 
259
  foreach ($data['MALWARE']['WARN'] as $mal) {
260
+ $info = self::malwareDetails($mal);
261
+
262
+ if ($info) {
263
+ $params['Malware.Content'] .= SucuriScanTemplate::getSnippet(
264
+ 'sitecheck-malware',
265
+ array(
266
+ 'Malware.InfectedURL' => $info['infected_url'],
267
+ 'Malware.MalwareType' => $info['malware_type'],
268
+ 'Malware.MalwareDocs' => $info['malware_docs'],
269
+ 'Malware.AlertMessage' => $info['alert_message'],
270
+ 'Malware.MalwarePayload' => $info['malware_payload'],
271
+ )
272
+ );
273
  }
274
  }
275
  }
291
  return ''; /* there is not enough information to render */
292
  }
293
 
294
+ $params['Blacklist.Title'] = 'Not Blacklisted';
295
  $params['Blacklist.Color'] = 'green';
296
  $params['Blacklist.Content'] = '';
297
 
304
  substr($info[0], 0, strrpos($info[0], ':'))
305
  );
306
 
307
+ $params['Blacklist.Content'] .= SucuriScanTemplate::getSnippet(
308
+ 'sitecheck-blacklist',
309
+ array(
310
+ 'Blacklist.URL' => $url,
311
+ 'Blacklist.Status' => $type,
312
+ 'Blacklist.Service' => $title,
313
+ )
314
+ );
315
  }
316
  }
317
 
318
  if (isset($data['BLACKLIST']['WARN'])) {
319
+ $params['Blacklist.Title'] = 'Blacklisted';
320
  $params['Blacklist.Color'] = 'red';
321
  }
322
 
343
  }
344
 
345
  $params['Recommendations.Visibility'] = 'visible';
346
+ $params['Recommendations.Content'] .= SucuriScanTemplate::getSnippet(
347
+ 'sitecheck-recommendations',
348
+ array(
349
+ 'Recommendations.Title' => $recommendation[0],
350
+ 'Recommendations.Value' => $recommendation[1],
351
+ 'Recommendations.URL' => $recommendation[2],
352
+ )
353
+ );
354
  }
355
  }
356
 
366
  {
367
  $data = self::scanAndCollectData();
368
 
369
+ return sprintf('iFrames: %d', @count($data['LINKS']['IFRAME']));
 
 
 
370
  }
371
 
372
  /**
378
  {
379
  $data = self::scanAndCollectData();
380
 
381
+ return sprintf('Links: %d', @count($data['LINKS']['URL']));
 
 
 
382
  }
383
 
384
  /**
399
  $total += count($data['LINKS']['JSEXTERNAL']);
400
  }
401
 
402
+ return sprintf('Scripts: %d', $total);
 
 
 
403
  }
404
 
405
  /**
448
  /**
449
  * Extract detailed information from a SiteCheck malware payload.
450
  *
451
+ * @param array $malware Array with two entries with basic malware information.
452
+ * @return array Detailed information of the malware found by SiteCheck.
453
  */
454
  public static function malwareDetails($malware = array())
455
  {
481
  if (strpos($malware_parts[0], $pattern) !== false) {
482
  $offset = strpos($malware_parts[0], $pattern);
483
  $data['malware_type'] = substr($malware_parts[0], 0, $offset);
484
+ $data['malware_docs'] = substr($malware_parts[0], $offset + 11);
485
  }
486
 
487
  $data['malware_payload'] = trim($malware_parts[1]);
498
  * output it means that XDebug cannot cover the next line, leaving a report
499
  * with a missing line in the coverage. Since the test case takes care of
500
  * the functionality of this code we will assume that it is fully covered.
501
+ *
502
+ * @return void
503
  */
504
  public static function ajaxMalwareScan()
505
  {
src/sucuriscan.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the sucuriscan.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage sucuriscan.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -19,18 +25,28 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
19
  /**
20
  * Miscellaneous library.
21
  *
22
- * Multiple and generic methods that will be used through out the code of
23
- * other libraries extending from this and methods defined in other files, be
24
- * aware of the hierarchy and check the other libraries for duplicated methods.
 
 
 
 
 
 
 
 
25
  */
26
  class SucuriScan
27
  {
28
  /**
29
  * Throw generic exception instead of silent failure for unit-tests.
30
  *
31
- * @param string $message Error or information message.
32
- * @param string $type Either info or error.
33
  * @throws Exception
 
 
 
 
34
  */
35
  public static function throwException($message, $type = 'error')
36
  {
@@ -56,32 +72,31 @@ class SucuriScan
56
  * Return name of a variable with the plugin's prefix (if needed).
57
  *
58
  * To facilitate the development, you can prefix the name of the key in the
59
- * request (when accessing it) with a single colon, this method will
60
- * automatically replace that character with the unique identifier of the
61
- * plugin.
62
  *
63
- * @param string $var_name Name of a variable with an optional colon at the beginning.
64
- * @return string Full name of the variable with the extra characters (if needed).
65
  */
66
- public static function varPrefix($var_name = '')
67
  {
68
- if (!empty($var_name) && $var_name[0] === ':') {
69
- $var_name = sprintf(
70
  '%s_%s',
71
  SUCURISCAN,
72
- substr($var_name, 1)
73
  );
74
  }
75
 
76
- return $var_name;
77
  }
78
 
79
  /**
80
  * Gets the value of a configuration option.
81
  *
82
- * @param string $property The configuration option name.
83
- * @param bool $raw Return the original value from the php.ini file.
84
- * @return string Value of the configuration option as a string on success.
85
  */
86
  public static function iniGet($property = '', $raw = false)
87
  {
@@ -93,7 +108,7 @@ class SucuriScan
93
 
94
  $default = array(
95
  'error_log' => 'error_log',
96
- 'safe_mode' => __('NotActive', SUCURISCAN_TEXTDOMAIN),
97
  'memory_limit' => '128M',
98
  'upload_max_filesize' => '2M',
99
  'post_max_size' => '8M',
@@ -102,12 +117,12 @@ class SucuriScan
102
  );
103
 
104
  if ($ini_value === false) {
105
- $ini_value = __('Unknown', SUCURISCAN_TEXTDOMAIN);
106
  } elseif (empty($ini_value) || $ini_value === null) {
107
  if (array_key_exists($property, $default)) {
108
  $ini_value = $default[$property];
109
  } else {
110
- $ini_value = __('NotActive', SUCURISCAN_TEXTDOMAIN);
111
  }
112
  }
113
 
@@ -123,8 +138,9 @@ class SucuriScan
123
  * quote characters, will never double encode entities.
124
  *
125
  * @see https://developer.wordpress.org/reference/functions/esc_attr/
126
- * @param string $text The text which is to be encoded.
127
- * @return string The encoded text with HTML entities.
 
128
  */
129
  public static function escape($text = '')
130
  {
@@ -135,19 +151,20 @@ class SucuriScan
135
  * Translate a given number in bytes to a human readable file size using the
136
  * a approximate value in Kylo, Mega, Giga, etc.
137
  *
138
- * @link https://www.php.net/manual/en/function.filesize.php#106569
139
- * @param int $bytes Integer representing a file size in bytes.
140
- * @param int $decimals How many decimals should be returned.
141
- * @return string Human readable representation of the given number.
 
142
  */
143
  public static function humanFileSize($bytes = 0, $decimals = 2)
144
  {
145
  $sz = 'BKMGTP';
146
- $factor = floor((strlen((string) $bytes) - 1) / 3);
147
  $number = $bytes / pow(1024, $factor);
148
  $result = sprintf("%.{$decimals}f", $number) . @$sz[$factor];
149
  $zeroes = '.' . str_repeat('0', $decimals);
150
- $result = str_replace($zeroes, '', $result); /* remove unused zeroes */
151
 
152
  return $result;
153
  }
@@ -161,8 +178,8 @@ class SucuriScan
161
  * in the form of "in X time". If the timestamp is the same as the current
162
  * time it will return "right now".
163
  *
164
- * @param integer $time Unix timestamp.
165
- * @return string Different between timestamp and current time.
166
  */
167
  public static function humanTime($time = 0)
168
  {
@@ -237,30 +254,28 @@ class SucuriScan
237
  * differs from what other file systems use. To keep consistency during the
238
  * unit-tests we have decided to replace any non forward slash with it.
239
  *
240
- * @param string $path Directory path to fix.
241
- * @return string Fixed file path.
242
  */
243
  public static function fixPath($path = '')
244
  {
245
- $delimiter = '/' /* Forward slash */;
246
- $path = str_replace(DIRECTORY_SEPARATOR, $delimiter, $path);
247
- $path = rtrim($path, $delimiter);
248
-
249
- return $path;
250
  }
251
 
252
  /**
253
- * Returns the system filepath to the relevant user uploads directory for this
254
- * site. This is a multisite capable function.
255
  *
256
- * @param string $path The relative path that needs to be completed to get the absolute path.
257
- * @return string The full filesystem path including the directory specified.
258
  */
259
  public static function dataStorePath($path = '')
260
  {
261
- $content_dir = defined('WP_CONTENT_DIR')
262
- ? rtrim(WP_CONTENT_DIR, '/')
263
- : ABSPATH . '/wp-content';
 
 
 
264
  $folder = $content_dir . '/uploads/sucuri';
265
 
266
  /* custom path no matter its existence */
@@ -288,8 +303,8 @@ class SucuriScan
288
  /**
289
  * Returns an URL from the admin dashboard.
290
  *
291
- * @param string $url Optional trailing of the URL.
292
- * @return string Full valid URL from the admin dashboard.
293
  */
294
  public static function adminURL($url = '')
295
  {
@@ -309,22 +324,23 @@ class SucuriScan
309
  */
310
  public static function siteVersion()
311
  {
312
- global $wp_version;
313
-
314
- if ($wp_version === null) {
315
- $filename = ABSPATH . '/' . WPINC . '/version.php';
316
- $lines = SucuriScanFileInfo::fileLines($filename);
317
-
318
- foreach ($lines as $line) {
319
- if (strpos($line, '$wp_version') === 0) {
320
- $version = str_replace("\x20", '', $line);
321
- $index = strpos($version, "'");
322
- $version = substr($version, $index+1);
323
- $index = strpos($version, "'");
324
- $version = substr($version, 0, $index);
325
- $wp_version = $version;
326
- break;
327
- }
 
328
  }
329
  }
330
 
@@ -336,7 +352,7 @@ class SucuriScan
336
  *
337
  * @return string|bool Absolute path of the WordPress configuration file.
338
  */
339
- public static function getWPConfigPath()
340
  {
341
  $filename = ABSPATH . '/wp-config.php';
342
 
@@ -386,6 +402,8 @@ class SucuriScan
386
 
387
  /**
388
  * Execute the plugin' scheduled tasks.
 
 
389
  */
390
  public static function runScheduledTask()
391
  {
@@ -407,8 +425,8 @@ class SucuriScan
407
  * specify the main HTTP header. This is a list of the allowed headers that the
408
  * user can choose.
409
  *
410
- * @param bool $with_keys Return the array with its values are keys.
411
- * @return array Allowed HTTP headers to retrieve real IP.
412
  */
413
  public static function allowedHttpHeaders($with_keys = false)
414
  {
@@ -447,10 +465,10 @@ class SucuriScan
447
  /**
448
  * List HTTP headers ordered.
449
  *
450
- * The list of HTTP headers is ordered per relevancy, and having the main HTTP
451
- * header as the first entry, this guarantees that the IP address of the
452
- * visitors will be retrieved from the HTTP header chosen by the user first and
453
- * fallback to the other alternatives if available.
454
  *
455
  * @return array Ordered allowed HTTP headers.
456
  */
@@ -473,13 +491,13 @@ class SucuriScan
473
  /**
474
  * Retrieve the real ip address of the user in the current request.
475
  *
476
- * @param bool $with_header Return HTTP header where the IP address was found.
477
- * @return string Real IP address of the user in the current request.
478
  */
479
  public static function getRemoteAddr($with_header = false)
480
  {
481
  $remote_addr = false;
482
- $header_used = __('Unknown', SUCURISCAN_TEXTDOMAIN);
483
  $headers = self::orderedHttpHeaders();
484
 
485
  foreach ($headers as $header) {
@@ -531,8 +549,9 @@ class SucuriScan
531
  * Get the clean version of the current domain.
532
  *
533
  * @see https://developer.wordpress.org/reference/functions/get_site_url/
534
- * @param bool $return_tld Returns the top-level domain instead.
535
- * @return string The domain of the current site.
 
536
  */
537
  public static function getDomain($return_tld = false)
538
  {
@@ -565,71 +584,52 @@ class SucuriScan
565
  }
566
 
567
  /**
568
- * Check whether the DNS lookups should be execute or not.
569
  *
570
- * DNS lookups are only necessary if you are planning to use a reverse proxy
571
- * or firewall, this is used to set the correct IP address when the firewall
572
- * filters the requests. If you are not planning to use any of these is better
573
- * to disable this option, otherwise the load time of your site may be affected.
574
  *
575
- * @return bool True if the DNS lookups should be executed, false otherwise.
576
  */
577
- public static function executeDNSLookups()
578
  {
579
- if (( defined('NOT_USING_CLOUDPROXY') && NOT_USING_CLOUDPROXY === true )
580
- || SucuriScanOption::isDisabled(':dns_lookups')
581
- ) {
582
  return false;
583
  }
584
 
585
- return true;
 
 
 
 
 
 
 
586
  }
587
 
588
  /**
589
  * Check whether the site is behind the firewall network.
590
  *
591
- * @param bool $verbose Return array with HTTP and HOST information.
592
- * @return array|bool True if the firewall is in use, false otherwise.
593
  */
594
  public static function isBehindFirewall($verbose = false)
595
  {
596
- $status = false;
597
- $host_by_addr = '::1';
598
- $host_by_name = 'localhost';
599
- $http_host = self::getTopLevelDomain();
600
-
601
- /**
602
- * Consider firewall protected if the API key is set.
603
- *
604
- * Assume that the Sucuri Firewall is protecting this website if there
605
- * is a valid API key set in the Firewall page, otherwise execute the
606
- * DNS lookup to try to find the name of the server which will help us
607
- * determine if the website is behind the Sucuri Firewall or not.
608
- *
609
- * Notice that this DNS lookup may slow down a website that is hosted in
610
- * a hosting pointing to a slow DNS server, the latency in the queries
611
- * will be noticeable. Webmasters can disable this behavior by setting
612
- * the ":dns_lookups" option as "disabled" or adding this constant to
613
- * the WordPress configuration file: (NOT_USING_CLOUDPROXY, true)
614
- */
615
- if (SucuriScanFirewall::getKey()) {
616
- $status = true;
617
- } elseif (self::executeDNSLookups()) {
618
- $host_by_addr = @gethostbyname($http_host);
619
- $host_by_name = @gethostbyaddr($host_by_addr);
620
- $status = (bool) preg_match('/^cloudproxy[0-9]+\.sucuri\.net$/', $host_by_name);
621
  }
622
 
623
- if ($verbose) {
624
- return array(
625
- 'http_host' => $http_host,
626
- 'host_name' => $host_by_name,
627
- 'host_addr' => $host_by_addr,
628
- 'status' => $status,
629
- );
630
- }
631
 
632
- return $status;
 
 
 
 
 
633
  }
634
 
635
  /**
@@ -654,8 +654,9 @@ class SucuriScan
654
  * Get user data by field and data.
655
  *
656
  * @see https://developer.wordpress.org/reference/functions/get_user_by/
657
- * @param int $identifier User account identifier.
658
- * @return array WordPress user object with data.
 
659
  */
660
  public static function getUserByID($identifier = 0)
661
  {
@@ -666,6 +667,7 @@ class SucuriScan
666
  * Retrieve a list of all admin user accounts.
667
  *
668
  * @see https://developer.wordpress.org/reference/functions/get_users/
 
669
  * @return array|bool List of admin users, false otherwise.
670
  */
671
  public static function getAdminUsers()
@@ -710,9 +712,9 @@ class SucuriScan
710
  * take over the format for the date. If it isn't, then the date format string
711
  * will be used instead.
712
  *
713
- * @param null|string|int $timestamp Unix timestamp.
714
- * @param string $format Optional format for the date and time.
715
- * @return string The date, translated if locale specifies it.
716
  */
717
  public static function datetime($timestamp = null, $format = null)
718
  {
@@ -751,8 +753,8 @@ class SucuriScan
751
  /**
752
  * Check whether an IP address has a valid format or not.
753
  *
754
- * @param string $remote_addr The host IP address.
755
- * @return bool Whether the IP address specified is valid or not.
756
  */
757
  public static function isValidIP($remote_addr = '')
758
  {
@@ -763,8 +765,8 @@ class SucuriScan
763
  /**
764
  * Check whether an IP address is formatted as CIDR or not.
765
  *
766
- * @param string $remote_addr The supposed ip address that will be checked.
767
- * @return bool Either TRUE or FALSE if the ip address specified is valid or not.
768
  */
769
  public static function isValidCIDR($remote_addr = '')
770
  {
@@ -782,8 +784,8 @@ class SucuriScan
782
  /**
783
  * Separate the parts of an IP address.
784
  *
785
- * @param string $remote_addr The supposed ip address that will be formatted.
786
- * @return array|bool Clean address, CIDR range, and CIDR format; FALSE otherwise.
787
  */
788
  public static function getIPInfo($remote_addr = '')
789
  {
@@ -813,8 +815,8 @@ class SucuriScan
813
  *
814
  * @see https://www.php.net/manual/en/function.filter-var.php
815
  *
816
- * @param string $email The string that will be validated as an email address.
817
- * @return bool TRUE if the email address passed to the method is valid, FALSE if not.
818
  */
819
  public static function isValidEmail($email = '')
820
  {
@@ -824,9 +826,9 @@ class SucuriScan
824
  /**
825
  * Cut a long text to the length specified, and append suspensive points at the end.
826
  *
827
- * @param string $text String of characters that will be cut.
828
- * @param int $length Maximum length of the returned string, default is 10.
829
- * @return string Short version of the text specified.
830
  */
831
  public static function excerpt($text = '', $length = 10)
832
  {
@@ -842,8 +844,8 @@ class SucuriScan
842
  /**
843
  * Check whether an list is a multidimensional array or not.
844
  *
845
- * @param array $list An array or multidimensional array of different values.
846
- * @return bool TRUE if the list is multidimensional, FALSE otherwise.
847
  */
848
  public static function isMultiList($list = array())
849
  {
@@ -866,9 +868,9 @@ class SucuriScan
866
  /**
867
  * Join array elements with a string no matter if it is multidimensional.
868
  *
869
- * @param string $separator Character that will act as a separator, default to an empty string.
870
- * @param array $list The array of strings to implode.
871
- * @return string String of all the items in the list, with the separator between them.
872
  */
873
  public static function implode($separator = '', $list = array())
874
  {
@@ -904,167 +906,4 @@ class SucuriScan
904
  {
905
  return (bool) (stripos(@$_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false);
906
  }
907
-
908
- /**
909
- * Returns list of supported languages.
910
- *
911
- * @return array Supported languages abbreviated.
912
- */
913
- public static function languages()
914
- {
915
- return array(
916
- 'af' => 'af',
917
- 'ak' => 'ak',
918
- 'sq' => 'sq',
919
- 'arq' => 'arq',
920
- 'am' => 'am',
921
- 'ar' => 'ar',
922
- 'hy' => 'hy',
923
- 'rup_MK' => 'rup_MK',
924
- 'frp' => 'frp',
925
- 'as' => 'as',
926
- 'az' => 'az',
927
- 'az_TR' => 'az_TR',
928
- 'bcc' => 'bcc',
929
- 'ba' => 'ba',
930
- 'eu' => 'eu',
931
- 'bel' => 'bel',
932
- 'bn_BD' => 'bn_BD',
933
- 'bs_BA' => 'bs_BA',
934
- 'bre' => 'bre',
935
- 'bg_BG' => 'bg_BG',
936
- 'ca' => 'ca',
937
- 'bal' => 'bal',
938
- 'zh_CN' => 'zh_CN',
939
- 'zh_HK' => 'zh_HK',
940
- 'zh_TW' => 'zh_TW',
941
- 'co' => 'co',
942
- 'hr' => 'hr',
943
- 'cs_CZ' => 'cs_CZ',
944
- 'da_DK' => 'da_DK',
945
- 'dv' => 'dv',
946
- 'nl_NL' => 'nl_NL',
947
- 'nl_BE' => 'nl_BE',
948
- 'dzo' => 'dzo',
949
- 'en_US' => 'en_US',
950
- 'en_AU' => 'en_AU',
951
- 'en_CA' => 'en_CA',
952
- 'en_ZA' => 'en_ZA',
953
- 'en_GB' => 'en_GB',
954
- 'eo' => 'eo',
955
- 'et' => 'et',
956
- 'fo' => 'fo',
957
- 'fi' => 'fi',
958
- 'fr_BE' => 'fr_BE',
959
- 'fr_CA' => 'fr_CA',
960
- 'fr_FR' => 'fr_FR',
961
- 'fy' => 'fy',
962
- 'fuc' => 'fuc',
963
- 'gl_ES' => 'gl_ES',
964
- 'ka_GE' => 'ka_GE',
965
- 'de_DE' => 'de_DE',
966
- 'de_CH' => 'de_CH',
967
- 'el' => 'el',
968
- 'gn' => 'gn',
969
- 'gu' => 'gu',
970
- 'haw_US' => 'haw_US',
971
- 'haz' => 'haz',
972
- 'he_IL' => 'he_IL',
973
- 'hi_IN' => 'hi_IN',
974
- 'hu_HU' => 'hu_HU',
975
- 'is_IS' => 'is_IS',
976
- 'ido' => 'ido',
977
- 'id_ID' => 'id_ID',
978
- 'ga' => 'ga',
979
- 'it_IT' => 'it_IT',
980
- 'ja' => 'ja',
981
- 'jv_ID' => 'jv_ID',
982
- 'kab' => 'kab',
983
- 'kn' => 'kn',
984
- 'kk' => 'kk',
985
- 'km' => 'km',
986
- 'kin' => 'kin',
987
- 'ky_KY' => 'ky_KY',
988
- 'ko_KR' => 'ko_KR',
989
- 'ckb' => 'ckb',
990
- 'lo' => 'lo',
991
- 'lv' => 'lv',
992
- 'li' => 'li',
993
- 'lin' => 'lin',
994
- 'lt_LT' => 'lt_LT',
995
- 'lb_LU' => 'lb_LU',
996
- 'mk_MK' => 'mk_MK',
997
- 'mg_MG' => 'mg_MG',
998
- 'ms_MY' => 'ms_MY',
999
- 'ml_IN' => 'ml_IN',
1000
- 'mri' => 'mri',
1001
- 'mr' => 'mr',
1002
- 'xmf' => 'xmf',
1003
- 'mn' => 'mn',
1004
- 'me_ME' => 'me_ME',
1005
- 'my_MM' => 'my_MM',
1006
- 'ne_NP' => 'ne_NP',
1007
- 'nb_NO' => 'nb_NO',
1008
- 'nn_NO' => 'nn_NO',
1009
- 'oci' => 'oci',
1010
- 'ory' => 'ory',
1011
- 'os' => 'os',
1012
- 'ps' => 'ps',
1013
- 'fa_IR' => 'fa_IR',
1014
- 'fa_AF' => 'fa_AF',
1015
- 'pl_PL' => 'pl_PL',
1016
- 'pt_BR' => 'pt_BR',
1017
- 'pt_PT' => 'pt_PT',
1018
- 'pa_IN' => 'pa_IN',
1019
- 'rhg' => 'rhg',
1020
- 'ro_RO' => 'ro_RO',
1021
- 'roh' => 'roh',
1022
- 'ru_RU' => 'ru_RU',
1023
- 'ru_UA' => 'ru_UA',
1024
- 'rue' => 'rue',
1025
- 'sah' => 'sah',
1026
- 'sa_IN' => 'sa_IN',
1027
- 'srd' => 'srd',
1028
- 'gd' => 'gd',
1029
- 'sr_RS' => 'sr_RS',
1030
- 'szl' => 'szl',
1031
- 'sd_PK' => 'sd_PK',
1032
- 'si_LK' => 'si_LK',
1033
- 'sk_SK' => 'sk_SK',
1034
- 'sl_SI' => 'sl_SI',
1035
- 'so_SO' => 'so_SO',
1036
- 'azb' => 'azb',
1037
- 'es_AR' => 'es_AR',
1038
- 'es_CL' => 'es_CL',
1039
- 'es_CO' => 'es_CO',
1040
- 'es_MX' => 'es_MX',
1041
- 'es_PE' => 'es_PE',
1042
- 'es_PR' => 'es_PR',
1043
- 'es_ES' => 'es_ES',
1044
- 'es_VE' => 'es_VE',
1045
- 'su_ID' => 'su_ID',
1046
- 'sw' => 'sw',
1047
- 'sv_SE' => 'sv_SE',
1048
- 'gsw' => 'gsw',
1049
- 'tl' => 'tl',
1050
- 'tg' => 'tg',
1051
- 'tzm' => 'tzm',
1052
- 'ta_IN' => 'ta_IN',
1053
- 'ta_LK' => 'ta_LK',
1054
- 'tt_RU' => 'tt_RU',
1055
- 'te' => 'te',
1056
- 'th' => 'th',
1057
- 'bo' => 'bo',
1058
- 'tir' => 'tir',
1059
- 'tr_TR' => 'tr_TR',
1060
- 'tuk' => 'tuk',
1061
- 'ug_CN' => 'ug_CN',
1062
- 'uk' => 'uk',
1063
- 'ur' => 'ur',
1064
- 'uz_UZ' => 'uz_UZ',
1065
- 'vi' => 'vi',
1066
- 'wa' => 'wa',
1067
- 'cy' => 'cy',
1068
- );
1069
- }
1070
  }
3
  /**
4
  * Code related to the sucuriscan.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
25
  /**
26
  * Miscellaneous library.
27
  *
28
+ * Multiple and generic methods that will be used through out the code of other
29
+ * libraries extending from this and methods defined in other files, be aware of
30
+ * the hierarchy and check the other libraries for duplicated methods.
31
+ *
32
+ * @category Library
33
+ * @package Sucuri
34
+ * @subpackage SucuriScanner
35
+ * @author Daniel Cid <dcid@sucuri.net>
36
+ * @copyright 2010-2017 Sucuri Inc.
37
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
38
+ * @link https://wordpress.org/plugins/sucuri-scanner
39
  */
40
  class SucuriScan
41
  {
42
  /**
43
  * Throw generic exception instead of silent failure for unit-tests.
44
  *
 
 
45
  * @throws Exception
46
+ *
47
+ * @param string $message Error or information message.
48
+ * @param string $type Either info or error.
49
+ * @return bool False all the time.
50
  */
51
  public static function throwException($message, $type = 'error')
52
  {
72
  * Return name of a variable with the plugin's prefix (if needed).
73
  *
74
  * To facilitate the development, you can prefix the name of the key in the
75
+ * request (when accessing it) with a single colon, this method will auto-
76
+ * matically replace that character with the unique plugin ID.
 
77
  *
78
+ * @param string $name Text with optional colon prefix.
79
+ * @return string Real variable name.
80
  */
81
+ public static function varPrefix($name = '')
82
  {
83
+ if (!empty($name) && $name[0] === ':') {
84
+ return sprintf(
85
  '%s_%s',
86
  SUCURISCAN,
87
+ substr($name, 1)
88
  );
89
  }
90
 
91
+ return $name;
92
  }
93
 
94
  /**
95
  * Gets the value of a configuration option.
96
  *
97
+ * @param string $property The configuration option name.
98
+ * @param bool $raw Return the original value from the php.ini file.
99
+ * @return string Value of the option as a string on success.
100
  */
101
  public static function iniGet($property = '', $raw = false)
102
  {
108
 
109
  $default = array(
110
  'error_log' => 'error_log',
111
+ 'safe_mode' => 'not active',
112
  'memory_limit' => '128M',
113
  'upload_max_filesize' => '2M',
114
  'post_max_size' => '8M',
117
  );
118
 
119
  if ($ini_value === false) {
120
+ $ini_value = 'unknown';
121
  } elseif (empty($ini_value) || $ini_value === null) {
122
  if (array_key_exists($property, $default)) {
123
  $ini_value = $default[$property];
124
  } else {
125
+ $ini_value = 'not active';
126
  }
127
  }
128
 
138
  * quote characters, will never double encode entities.
139
  *
140
  * @see https://developer.wordpress.org/reference/functions/esc_attr/
141
+ *
142
+ * @param string $text The text which is to be encoded.
143
+ * @return string The encoded text with HTML entities.
144
  */
145
  public static function escape($text = '')
146
  {
151
  * Translate a given number in bytes to a human readable file size using the
152
  * a approximate value in Kylo, Mega, Giga, etc.
153
  *
154
+ * @see https://www.php.net/manual/en/function.filesize.php#106569
155
+ *
156
+ * @param int $bytes Integer representing a file size in bytes.
157
+ * @param int $decimals How many decimals should be returned.
158
+ * @return string Human readable representation of the given number.
159
  */
160
  public static function humanFileSize($bytes = 0, $decimals = 2)
161
  {
162
  $sz = 'BKMGTP';
163
+ $factor = (int) floor((strlen((string) $bytes) - 1) / 3);
164
  $number = $bytes / pow(1024, $factor);
165
  $result = sprintf("%.{$decimals}f", $number) . @$sz[$factor];
166
  $zeroes = '.' . str_repeat('0', $decimals);
167
+ $result = str_replace($zeroes, '', $result);
168
 
169
  return $result;
170
  }
178
  * in the form of "in X time". If the timestamp is the same as the current
179
  * time it will return "right now".
180
  *
181
+ * @param int $time Unix timestamp.
182
+ * @return string Different between timestamp and current time.
183
  */
184
  public static function humanTime($time = 0)
185
  {
254
  * differs from what other file systems use. To keep consistency during the
255
  * unit-tests we have decided to replace any non forward slash with it.
256
  *
257
+ * @param string $path Directory path to fix.
258
+ * @return string Fixed file path.
259
  */
260
  public static function fixPath($path = '')
261
  {
262
+ return rtrim(str_replace(DIRECTORY_SEPARATOR, '/', $path), '/');
 
 
 
 
263
  }
264
 
265
  /**
266
+ * Returns the full path to a specific directory or file.
 
267
  *
268
+ * @param string $path Path that needs to be completed.
269
+ * @return string Full path including the base directory.
270
  */
271
  public static function dataStorePath($path = '')
272
  {
273
+ if (defined('WP_CONTENT_DIR')) {
274
+ $content_dir = rtrim(WP_CONTENT_DIR, '/');
275
+ } else {
276
+ $content_dir = ABSPATH . '/wp-content';
277
+ }
278
+
279
  $folder = $content_dir . '/uploads/sucuri';
280
 
281
  /* custom path no matter its existence */
303
  /**
304
  * Returns an URL from the admin dashboard.
305
  *
306
+ * @param string $url Optional trailing of the URL.
307
+ * @return string Full valid URL from the admin dashboard.
308
  */
309
  public static function adminURL($url = '')
310
  {
324
  */
325
  public static function siteVersion()
326
  {
327
+ if (isset($GLOBALS['wp_version'])) {
328
+ return self::escape($GLOBALS['wp_version']);
329
+ }
330
+
331
+ $wp_version = '';
332
+ $filename = ABSPATH . '/' . WPINC . '/version.php';
333
+ $lines = SucuriScanFileInfo::fileLines($filename);
334
+
335
+ foreach ($lines as $line) {
336
+ if (strpos($line, '$wp_version') === 0) {
337
+ $version = str_replace("\x20", '', $line);
338
+ $index = strpos($version, "'");
339
+ $version = substr($version, $index + 1);
340
+ $index = strpos($version, "'");
341
+ $version = substr($version, 0, $index);
342
+ $wp_version = $version;
343
+ break;
344
  }
345
  }
346
 
352
  *
353
  * @return string|bool Absolute path of the WordPress configuration file.
354
  */
355
+ public static function getConfigPath()
356
  {
357
  $filename = ABSPATH . '/wp-config.php';
358
 
402
 
403
  /**
404
  * Execute the plugin' scheduled tasks.
405
+ *
406
+ * @return void
407
  */
408
  public static function runScheduledTask()
409
  {
425
  * specify the main HTTP header. This is a list of the allowed headers that the
426
  * user can choose.
427
  *
428
+ * @param bool $with_keys Return the array with its values are keys.
429
+ * @return array Allowed HTTP headers to retrieve real IP.
430
  */
431
  public static function allowedHttpHeaders($with_keys = false)
432
  {
465
  /**
466
  * List HTTP headers ordered.
467
  *
468
+ * The list of HTTP headers is ordered per relevancy, and having the main
469
+ * HTTP header as the first entry, this guarantees that the IP address of
470
+ * the visitors will be retrieved from the HTTP header chosen by the user
471
+ * first and fallback to the other alternatives if available.
472
  *
473
  * @return array Ordered allowed HTTP headers.
474
  */
491
  /**
492
  * Retrieve the real ip address of the user in the current request.
493
  *
494
+ * @param bool $with_header Return HTTP header where the IP address was found.
495
+ * @return string Real IP address of the user in the current request.
496
  */
497
  public static function getRemoteAddr($with_header = false)
498
  {
499
  $remote_addr = false;
500
+ $header_used = 'unknown';
501
  $headers = self::orderedHttpHeaders();
502
 
503
  foreach ($headers as $header) {
549
  * Get the clean version of the current domain.
550
  *
551
  * @see https://developer.wordpress.org/reference/functions/get_site_url/
552
+ *
553
+ * @param bool $return_tld Returns the top-level domain instead.
554
+ * @return string The domain of the current site.
555
  */
556
  public static function getDomain($return_tld = false)
557
  {
584
  }
585
 
586
  /**
587
+ * Checks if the server IP is part of the Firewall network.
588
  *
589
+ * Assumming that the website is being protected by the Sucuri Firewall, we
590
+ * will check if the client IP address is part of the range of addresses
591
+ * that we know are ours.
 
592
  *
593
+ * @return boolean True if the website is using one of our IP addresses.
594
  */
595
+ private static function isFirewallAddr()
596
  {
597
+ if (!array_key_exists('HTTP_X_SUCURI_CLIENTIP', $_SERVER)) {
 
 
598
  return false;
599
  }
600
 
601
+ if (SucuriScanFirewall::getKey()
602
+ || preg_match('/^192\.88\.13[45]/', $_SERVER['REMOTE_ADDR'])
603
+ || preg_match('/^185\.93\.(228|229|230|231)/', $_SERVER['REMOTE_ADDR'])
604
+ ) {
605
+ return true;
606
+ }
607
+
608
+ return false;
609
  }
610
 
611
  /**
612
  * Check whether the site is behind the firewall network.
613
  *
614
+ * @param bool $verbose Return array with HTTP and HOST information.
615
+ * @return array|bool True if the firewall is in use, false otherwise.
616
  */
617
  public static function isBehindFirewall($verbose = false)
618
  {
619
+ if (!$verbose) {
620
+ return (bool) self::isFirewallAddr();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  }
622
 
623
+ $http_host = self::getTopLevelDomain();
624
+ $host_by_addr = @gethostbyname($http_host);
625
+ $host_by_name = @gethostbyaddr($host_by_addr);
 
 
 
 
 
626
 
627
+ return array(
628
+ 'status' => self::isFirewallAddr(),
629
+ 'http_host' => self::getTopLevelDomain(),
630
+ 'host_name' => $host_by_name,
631
+ 'host_addr' => $host_by_addr,
632
+ );
633
  }
634
 
635
  /**
654
  * Get user data by field and data.
655
  *
656
  * @see https://developer.wordpress.org/reference/functions/get_user_by/
657
+ *
658
+ * @param int $identifier User account identifier.
659
+ * @return array WordPress user object with data.
660
  */
661
  public static function getUserByID($identifier = 0)
662
  {
667
  * Retrieve a list of all admin user accounts.
668
  *
669
  * @see https://developer.wordpress.org/reference/functions/get_users/
670
+ *
671
  * @return array|bool List of admin users, false otherwise.
672
  */
673
  public static function getAdminUsers()
712
  * take over the format for the date. If it isn't, then the date format string
713
  * will be used instead.
714
  *
715
+ * @param null|string|int $timestamp Unix timestamp.
716
+ * @param string $format Optional format for the date and time.
717
+ * @return string The date, translated if locale specifies it.
718
  */
719
  public static function datetime($timestamp = null, $format = null)
720
  {
753
  /**
754
  * Check whether an IP address has a valid format or not.
755
  *
756
+ * @param string $remote_addr The host IP address.
757
+ * @return bool Whether the IP address specified is valid or not.
758
  */
759
  public static function isValidIP($remote_addr = '')
760
  {
765
  /**
766
  * Check whether an IP address is formatted as CIDR or not.
767
  *
768
+ * @param string $remote_addr The supposed ip address that will be checked.
769
+ * @return bool Either TRUE or FALSE if the ip address specified is valid or not.
770
  */
771
  public static function isValidCIDR($remote_addr = '')
772
  {
784
  /**
785
  * Separate the parts of an IP address.
786
  *
787
+ * @param string $remote_addr The supposed ip address that will be formatted.
788
+ * @return array|bool Clean address, CIDR range, and CIDR format; FALSE otherwise.
789
  */
790
  public static function getIPInfo($remote_addr = '')
791
  {
815
  *
816
  * @see https://www.php.net/manual/en/function.filter-var.php
817
  *
818
+ * @param string $email The string that will be validated as an email address.
819
+ * @return bool TRUE if the email address passed to the method is valid, FALSE if not.
820
  */
821
  public static function isValidEmail($email = '')
822
  {
826
  /**
827
  * Cut a long text to the length specified, and append suspensive points at the end.
828
  *
829
+ * @param string $text String of characters that will be cut.
830
+ * @param int $length Maximum length of the returned string, default is 10.
831
+ * @return string Short version of the text specified.
832
  */
833
  public static function excerpt($text = '', $length = 10)
834
  {
844
  /**
845
  * Check whether an list is a multidimensional array or not.
846
  *
847
+ * @param array $list An array or multidimensional array of different values.
848
+ * @return bool TRUE if the list is multidimensional, FALSE otherwise.
849
  */
850
  public static function isMultiList($list = array())
851
  {
868
  /**
869
  * Join array elements with a string no matter if it is multidimensional.
870
  *
871
+ * @param string $separator Character that will act as a separator, default to an empty string.
872
+ * @param array $list The array of strings to implode.
873
+ * @return string String of all the items in the list, with the separator between them.
874
  */
875
  public static function implode($separator = '', $list = array())
876
  {
906
  {
907
  return (bool) (stripos(@$_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false);
908
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
909
  }
src/template.lib.php CHANGED
@@ -3,9 +3,15 @@
3
  /**
4
  * Code related to the template.lib.php interface.
5
  *
6
- * @package Sucuri Security
7
- * @subpackage template.lib.php
8
- * @copyright Since 2010 Sucuri Inc.
 
 
 
 
 
 
9
  */
10
 
11
  if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
@@ -28,53 +34,30 @@ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
28
  * Web templates can be used like the template of a form letter to either
29
  * generate a large number of "static" (unchanging) web pages in advance, or to
30
  * produce "dynamic" web pages on demand.
 
 
 
 
 
 
 
 
31
  */
32
  class SucuriScanTemplate extends SucuriScanRequest
33
  {
34
- /**
35
- * Translates text using l10n and gettext.
36
- *
37
- * A translatable text can be inserted into any template file following this
38
- * format @@SUCURI.TextID@@ where "TextID" corresponds to the msgid in the
39
- * POT files. You can embed pseudo-variables into the translations like so:
40
- *
41
- * msgid "Copyright"
42
- * msgstr "Copyright %%SUCURI.Year%% Sucuri Inc"
43
- *
44
- * @see https://www.gnu.org/software/gettext/
45
- * @see https://codex.wordpress.org/I18n_for_WordPress_Developers
46
- * @see https://developer.wordpress.org/themes/functionality/internationalization/
47
- *
48
- * @param string $content Content of the template to be translated.
49
- * @return string New template content with the translated text.
50
- */
51
- private static function translateContent($content = '')
52
- {
53
- if (@preg_match_all('/@@SUCURI\.([0-9a-zA-Z\.\_]+)@@/', $content, $matches)) {
54
- foreach ($matches[0] as $key => $placeholder) {
55
- $translation = __($matches[1][$key], SUCURISCAN_TEXTDOMAIN);
56
- $content = str_replace($placeholder, $translation, $content);
57
- }
58
- }
59
-
60
- return $content;
61
- }
62
-
63
  /**
64
  * Replace all pseudo-variables from a string of characters.
65
  *
66
  * @see http://php.net/manual/en/function.gettype.php
67
  *
68
- * @param string $content The content of a template file which contains pseudo-variables.
69
- * @param array $params List of pseudo-variables that will be replaced in the template.
70
- * @return string The content of the template with the pseudo-variables replated.
71
  */
72
  private static function replacePseudoVars($content = '', $params = array())
73
  {
74
  $params = is_array($params) ? $params : array();
75
 
76
- $content = self::translateContent($content);
77
-
78
  foreach ($params as $keyname => $kvalue) {
79
  $tplkey = 'SUCURI.' . $keyname;
80
  $with_escape = '%%' . $tplkey . '%%';
@@ -104,9 +87,9 @@ class SucuriScanTemplate extends SucuriScanRequest
104
  /**
105
  * Gather and generate the information required globally by all the template files.
106
  *
107
- * @param string $target Scenario where the params are going to be replaced.
108
- * @param array $params Key-value array with variables shared with the template.
109
- * @return array Additional list of variables for the template files.
110
  */
111
  private static function sharedParams($target = null, $params = array())
112
  {
@@ -135,11 +118,14 @@ class SucuriScanTemplate extends SucuriScanRequest
135
  $params['GenerateAPIKey.Visibility'] = 'visible';
136
  $params['GenerateAPIKey.Modal'] = /* register-site */
137
 
138
- SucuriScanTemplate::getModal('register-site', array(
139
- 'Title' => __('GenerateAPIKey', SUCURISCAN_TEXTDOMAIN),
140
- 'Identifier' => 'register-site',
141
- 'Visibility' => 'hidden',
142
- ));
 
 
 
143
  }
144
 
145
  // Get a list of admin users for the API key generation.
@@ -154,8 +140,8 @@ class SucuriScanTemplate extends SucuriScanRequest
154
  /**
155
  * Return a string indicating the visibility of a HTML component.
156
  *
157
- * @param bool $visible Whether the condition executed returned a positive value or not.
158
- * @return string A string indicating the visibility of a HTML component.
159
  */
160
  public static function visibility($visible = false)
161
  {
@@ -166,9 +152,9 @@ class SucuriScanTemplate extends SucuriScanRequest
166
  * Generate an URL pointing to the page indicated in the method and that must
167
  * be loaded through the administrator panel.
168
  *
169
- * @param string $page Short name of the page that will be generated.
170
- * @param bool $ajax True if the URL should point to the Ajax handler.
171
- * @return string Full string containing the link of the page.
172
  */
173
  public static function getUrl($page = '', $ajax = false)
174
  {
@@ -193,8 +179,8 @@ class SucuriScanTemplate extends SucuriScanRequest
193
  * Generate an URL pointing to the page indicated in the method and that must
194
  * be loaded through the Ajax handler of the administrator panel.
195
  *
196
- * @param string $page Short name of the page that will be generated.
197
- * @return string Full string containing the link of the page.
198
  */
199
  public static function getAjaxUrl($page = '')
200
  {
@@ -206,12 +192,12 @@ class SucuriScanTemplate extends SucuriScanRequest
206
  * template files, this will also generate the navigation bar and detect which
207
  * items in it are selected by the current page.
208
  *
209
- * @param array $params Key-value array with pseudo-variables shared with the template.
210
- * @return array A complementary list of pseudo-variables for the template files.
211
  */
212
  private static function linksAndNavbar($params = array())
213
  {
214
- $pages = sucuriscan_pages();
215
  $params = is_array($params) ? $params : array();
216
  $sub_pages = is_array($pages) ? $pages : array();
217
 
@@ -241,9 +227,9 @@ class SucuriScanTemplate extends SucuriScanRequest
241
  * by the dynamic variables provided by the developer through one of the parameters
242
  * of the function.
243
  *
244
- * @param string $html The HTML content of a template file with its pseudo-variables parsed.
245
- * @param array $params Key-value array with pseudo-variables shared with the template.
246
- * @return string The formatted HTML content of the base template.
247
  */
248
  public static function getBaseTemplate($html = '', $params = array())
249
  {
@@ -260,10 +246,10 @@ class SucuriScanTemplate extends SucuriScanRequest
260
  * by the dynamic variables provided by the developer through one of the parameters
261
  * of the function.
262
  *
263
- * @param string $template Filename of the template that will be used to generate the page.
264
- * @param array $params Key-value array with pseudo-variables shared with the template.
265
- * @param string $type Template type; either page, section or snippet.
266
- * @return string Formatted HTML code after pseudo-variables replacement.
267
  */
268
  public static function getTemplate($template = '', $params = array(), $type = 'page')
269
  {
@@ -311,9 +297,9 @@ class SucuriScanTemplate extends SucuriScanRequest
311
  * by the dynamic variables provided by the developer through one of the parameters
312
  * of the function.
313
  *
314
- * @param string $template Filename of the template that will be used to generate the page.
315
- * @param array $params Key-value array with pseudo-variables shared with the template.
316
- * @return string The formatted HTML page after replace all the pseudo-variables.
317
  */
318
  public static function getSection($template = '', $params = array())
319
  {
@@ -327,9 +313,9 @@ class SucuriScanTemplate extends SucuriScanRequest
327
  * by the dynamic variables provided by the developer through one of the parameters
328
  * of the function.
329
  *
330
- * @param string $template Filename of the template that will be used to generate the page.
331
- * @param array $params Key-value array with pseudo-variables shared with the template.
332
- * @return string The formatted HTML page after replace all the pseudo-variables.
333
  */
334
  public static function getModal($template = '', $params = array())
335
  {
@@ -338,14 +324,14 @@ class SucuriScanTemplate extends SucuriScanRequest
338
  'Visibility' => 'visible',
339
  'Identifier' => 'foobar',
340
  'CssClass' => '',
341
- 'Content' => '<p>Lorem ipsum dolor sit amet, consectetur adipisicin'
342
- . 'g elit, sed do eiusmod tempor incididunt ut labore et dolore mag'
343
- . 'na aliqua. Ut enim ad minim veniam, quis nostrud exercitation ul'
344
- . 'lamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute'
345
- . ' irure dolor in reprehenderit in voluptate velit esse cillum dol'
346
- . 'ore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat '
347
- . 'non proident, sunt in culpa qui officia deserunt mollit anim id '
348
- . 'est laborum.</p>',
349
  );
350
 
351
  if (!empty($template) && $template !== 'none') {
@@ -370,9 +356,9 @@ class SucuriScanTemplate extends SucuriScanRequest
370
  * by the dynamic variables provided by the developer through one of the parameters
371
  * of the function.
372
  *
373
- * @param string $template Filename of the template that will be used to generate the page.
374
- * @param array $params Key-value array with pseudo-variables shared with the template.
375
- * @return string The formatted HTML page after replace all the pseudo-variables.
376
  */
377
  public static function getSnippet($template = '', $params = array())
378
  {
@@ -384,25 +370,25 @@ class SucuriScanTemplate extends SucuriScanRequest
384
  /**
385
  * Generate the HTML code necessary to render a list of options in a form.
386
  *
387
- * @param array $allowed_values List with keys and values allowed for the options.
388
- * @param string|int $selected_val Value of the option that will be selected by default.
389
- * @return string Option list for a select form field.
390
  */
391
- public static function selectOptions($allowed_values = array(), $selected_val = '')
392
  {
393
  $options = '';
394
 
395
- foreach ((array) $allowed_values as $option_name => $option_label) {
396
- $selected = '';
397
 
398
- if ($option_name === $selected_val) {
399
- $selected = "\x20selected=\"selected\"";
400
  }
401
 
402
  $options .= sprintf(
403
  "<option value=\"%s\"%s>%s</option>\n",
404
  SucuriScan::escape($option_name),
405
- $selected, /* do not escape HTML */
406
  SucuriScan::escape($option_label)
407
  );
408
  }
@@ -425,10 +411,10 @@ class SucuriScanTemplate extends SucuriScanRequest
425
  /**
426
  * Generate the HTML code to display a pagination.
427
  *
428
- * @param string $base_url Base URL for the links before the page number.
429
- * @param int $total_items Total quantity of items retrieved from a query.
430
- * @param int $max_per_page Maximum number of items that will be shown per page.
431
- * @return string HTML code for a pagination generated using the provided data.
432
  */
433
  public static function pagination($base_url = '', $total_items = 0, $max_per_page = 1)
434
  {
@@ -441,7 +427,9 @@ class SucuriScanTemplate extends SucuriScanRequest
441
  $extra_url = '';
442
 
443
  /* fix for inline anchor URLs */
444
- if (($offset = strpos($base_url, '#')) !== false) {
 
 
445
  $clean_url = substr($base_url, 0, $offset);
446
  $extra_url = substr($base_url, $offset);
447
  $base_url = $clean_url;
3
  /**
4
  * Code related to the template.lib.php interface.
5
  *
6
+ * PHP version 5
7
+ *
8
+ * @category Library
9
+ * @package Sucuri
10
+ * @subpackage SucuriScanner
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) {
34
  * Web templates can be used like the template of a form letter to either
35
  * generate a large number of "static" (unchanging) web pages in advance, or to
36
  * produce "dynamic" web pages on demand.
37
+ *
38
+ * @category Library
39
+ * @package Sucuri
40
+ * @subpackage SucuriScanner
41
+ * @author Daniel Cid <dcid@sucuri.net>
42
+ * @copyright 2010-2017 Sucuri Inc.
43
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
44
+ * @link https://wordpress.org/plugins/sucuri-scanner
45
  */
46
  class SucuriScanTemplate extends SucuriScanRequest
47
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  /**
49
  * Replace all pseudo-variables from a string of characters.
50
  *
51
  * @see http://php.net/manual/en/function.gettype.php
52
  *
53
+ * @param string $content The content of a template file which contains pseudo-variables.
54
+ * @param array $params List of pseudo-variables that will be replaced in the template.
55
+ * @return string The content of the template with the pseudo-variables replated.
56
  */
57
  private static function replacePseudoVars($content = '', $params = array())
58
  {
59
  $params = is_array($params) ? $params : array();
60
 
 
 
61
  foreach ($params as $keyname => $kvalue) {
62
  $tplkey = 'SUCURI.' . $keyname;
63
  $with_escape = '%%' . $tplkey . '%%';
87
  /**
88
  * Gather and generate the information required globally by all the template files.
89
  *
90
+ * @param string $target Scenario where the params are going to be replaced.
91
+ * @param array $params Key-value array with variables shared with the template.
92
+ * @return array Additional list of variables for the template files.
93
  */
94
  private static function sharedParams($target = null, $params = array())
95
  {
118
  $params['GenerateAPIKey.Visibility'] = 'visible';
119
  $params['GenerateAPIKey.Modal'] = /* register-site */
120
 
121
+ SucuriScanTemplate::getModal(
122
+ 'register-site',
123
+ array(
124
+ 'Title' => 'Generate API Key',
125
+ 'Identifier' => 'register-site',
126
+ 'Visibility' => 'hidden',
127
+ )
128
+ );
129
  }
130
 
131
  // Get a list of admin users for the API key generation.
140
  /**
141
  * Return a string indicating the visibility of a HTML component.
142
  *
143
+ * @param bool $visible Whether the condition executed returned a positive value or not.
144
+ * @return string A string indicating the visibility of a HTML component.
145
  */
146
  public static function visibility($visible = false)
147
  {
152
  * Generate an URL pointing to the page indicated in the method and that must
153
  * be loaded through the administrator panel.
154
  *
155
+ * @param string $page Short name of the page that will be generated.
156
+ * @param bool $ajax True if the URL should point to the Ajax handler.
157
+ * @return string Full string containing the link of the page.
158
  */
159
  public static function getUrl($page = '', $ajax = false)
160
  {
179
  * Generate an URL pointing to the page indicated in the method and that must
180
  * be loaded through the Ajax handler of the administrator panel.
181
  *
182
+ * @param string $page Short name of the page that will be generated.
183
+ * @return string Full string containing the link of the page.
184
  */
185
  public static function getAjaxUrl($page = '')
186
  {
192
  * template files, this will also generate the navigation bar and detect which
193
  * items in it are selected by the current page.
194
  *
195
+ * @param array $params Key-value array with pseudo-variables shared with the template.
196
+ * @return array A complementary list of pseudo-variables for the template files.
197
  */
198
  private static function linksAndNavbar($params = array())
199
  {
200
+ $pages = sucuriscanMainPages();
201
  $params = is_array($params) ? $params : array();
202
  $sub_pages = is_array($pages) ? $pages : array();
203
 
227
  * by the dynamic variables provided by the developer through one of the parameters
228
  * of the function.
229
  *
230
+ * @param string $html The HTML content of a template file with its pseudo-variables parsed.
231
+ * @param array $params Key-value array with pseudo-variables shared with the template.
232
+ * @return string The formatted HTML content of the base template.
233
  */
234
  public static function getBaseTemplate($html = '', $params = array())
235
  {
246
  * by the dynamic variables provided by the developer through one of the parameters
247
  * of the function.
248
  *
249
+ * @param string $template Filename of the template that will be used to generate the page.
250
+ * @param array $params Key-value array with pseudo-variables shared with the template.
251
+ * @param string $type Template type; either page, section or snippet.
252
+ * @return string Formatted HTML code after pseudo-variables replacement.
253
  */
254
  public static function getTemplate($template = '', $params = array(), $type = 'page')
255
  {
297
  * by the dynamic variables provided by the developer through one of the parameters
298
  * of the function.
299
  *
300
+ * @param string $template Filename of the template that will be used to generate the page.
301
+ * @param array $params Key-value array with pseudo-variables shared with the template.
302
+ * @return string The formatted HTML page after replace all the pseudo-variables.
303
  */
304
  public static function getSection($template = '', $params = array())
305
  {
313
  * by the dynamic variables provided by the developer through one of the parameters
314
  * of the function.
315
  *
316
+ * @param string $template Filename of the template that will be used to generate the page.
317
+ * @param array $params Key-value array with pseudo-variables shared with the template.
318
+ * @return string The formatted HTML page after replace all the pseudo-variables.
319
  */
320
  public static function getModal($template = '', $params = array())
321
  {
324
  'Visibility' => 'visible',
325
  'Identifier' => 'foobar',
326
  'CssClass' => '',
327
+ 'Content' => '<p>Lorem ipsum dolor sit amet, consectetur adipisici'
328
+ . 'ng elit, sed do eiusmod tempor incididunt ut labore et dolore m'
329
+ . 'agna aliqua. Ut enim ad minim veniam, quis nostrud exercitation'
330
+ . ' ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis '
331
+ . 'aute irure dolor in reprehenderit in voluptate velit esse cillu'
332
+ . 'm dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupi'
333
+ . 'datat non proident, sunt in culpa qui officia deserunt mollit a'
334
+ . 'nim id est laborum.</p>',
335
  );
336
 
337
  if (!empty($template) && $template !== 'none') {
356
  * by the dynamic variables provided by the developer through one of the parameters
357
  * of the function.
358
  *
359
+ * @param string $template Filename of the template that will be used to generate the page.
360
+ * @param array $params Key-value array with pseudo-variables shared with the template.
361
+ * @return string The formatted HTML page after replace all the pseudo-variables.
362
  */
363
  public static function getSnippet($template = '', $params = array())
364
  {
370
  /**
371
  * Generate the HTML code necessary to render a list of options in a form.
372
  *
373
+ * @param array $allowed Key-value array with allowed options.
374
+ * @param string|int $selected Optional selected value from the list.
375
+ * @return string HTML code for the select box.
376
  */
377
+ public static function selectOptions($allowed = array(), $selected = '')
378
  {
379
  $options = '';
380
 
381
+ foreach ((array) $allowed as $option_name => $option_label) {
382
+ $selectedAttr = '';
383
 
384
+ if ($option_name === $selected) {
385
+ $selectedAttr = "\x20selected=\"selected\"";
386
  }
387
 
388
  $options .= sprintf(
389
  "<option value=\"%s\"%s>%s</option>\n",
390
  SucuriScan::escape($option_name),
391
+ $selectedAttr, /* do not escape HTML */
392
  SucuriScan::escape($option_label)
393
  );
394
  }
411
  /**
412
  * Generate the HTML code to display a pagination.
413
  *
414
+ * @param string $base_url Base URL for the links before the page number.
415
+ * @param int $total_items Total quantity of items retrieved from a query.
416
+ * @param int $max_per_page Maximum number of items that will be shown per page.
417
+ * @return string HTML code for a pagination generated using the provided data.
418
  */
419
  public static function pagination($base_url = '', $total_items = 0, $max_per_page = 1)
420
  {
427
  $extra_url = '';
428
 
429
  /* fix for inline anchor URLs */
430
+ $offset = strpos($base_url, '#');
431
+
432
+ if ($offset !== false) {
433
  $clean_url = substr($base_url, 0, $offset);
434
  $extra_url = substr($base_url, $offset);
435
  $base_url = $clean_url;
sucuri.php CHANGED
@@ -5,12 +5,20 @@
5
  * Description: The <a href="https://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
  * Plugin URI: https://wordpress.sucuri.net/
7
  * Author URI: https://sucuri.net/
8
- * Text Domain: sucuri-scanner
9
  * Author: Sucuri Inc.
10
  * Version: 1.8.8
 
 
 
 
 
 
 
 
 
 
11
  */
12
 
13
-
14
  /**
15
  * Main file to control the plugin.
16
  *
@@ -19,14 +27,6 @@
19
  * during the direct access of any of the extra PHP files the interpreter will
20
  * return a 403/Forbidden response and immediately exit the execution, this will
21
  * prevent unwanted access to code with unmet dependencies.
22
- *
23
- * @package Sucuri Security
24
- * @author Daniel Cid <dcid@sucuri.net>
25
- * @license Released under the GPL.
26
- * @copyright Since 2010 Sucuri Inc.
27
- * @since File available since Release 0.1
28
- * @link https://wordpress.org/plugins/sucuri-scanner/
29
- * @link https://wordpress.sucuri.net/
30
  */
31
  define('SUCURISCAN_INIT', true);
32
 
@@ -85,11 +85,6 @@ define('SUCURISCAN', 'sucuriscan');
85
  */
86
  define('SUCURISCAN_VERSION', '1.8.8');
87
 
88
- /**
89
- * Unique name of the plugin text domain.
90
- */
91
- define('SUCURISCAN_TEXTDOMAIN', 'sucuri-scanner');
92
-
93
  /**
94
  * The name of the folder where the plugin's files will be located.
95
  *
@@ -109,7 +104,7 @@ define('SUCURISCAN_PLUGIN_PATH', WP_PLUGIN_DIR . '/' . SUCURISCAN_PLUGIN_FOLDER)
109
  /**
110
  * The local URL where the plugin's files and assets are served.
111
  */
112
- define('SUCURISCAN_URL', plugin_dir_url(__FILE__));
113
 
114
  /**
115
  * Remote URL where the public Sucuri API service is running.
@@ -166,7 +161,7 @@ define('SUCURISCAN_SCANNER_FREQUENCY', 10800);
166
  /**
167
  * The life time of the cache for the results of the SiteCheck scans.
168
  */
169
- define('SUCURISCAN_SITECHECK_LIFETIME', 1200);
170
 
171
  /**
172
  * The life time of the cache for the results of the get_plugins function.
@@ -194,48 +189,48 @@ if (!array_key_exists('SERVER_NAME', $_SERVER)) {
194
  }
195
 
196
  /* Load all classes before anything else. */
197
- require_once('src/sucuriscan.lib.php');
198
- require_once('src/request.lib.php');
199
- require_once('src/fileinfo.lib.php');
200
- require_once('src/cache.lib.php');
201
- require_once('src/option.lib.php');
202
- require_once('src/event.lib.php');
203
- require_once('src/hook.lib.php');
204
- require_once('src/api.lib.php');
205
- require_once('src/mail.lib.php');
206
- require_once('src/command.lib.php');
207
- require_once('src/template.lib.php');
208
- require_once('src/fsscanner.lib.php');
209
- require_once('src/hardening.lib.php');
210
- require_once('src/interface.lib.php');
211
- require_once('src/auditlogs.lib.php');
212
- require_once('src/sitecheck.lib.php');
213
- require_once('src/integrity.lib.php');
214
- require_once('src/firewall.lib.php');
215
- require_once('src/installer-skin.lib.php');
216
 
217
  /* Load page and ajax handlers */
218
- require_once('src/pagehandler.php');
219
 
220
  /* Load handlers for main pages (lastlogins). */
221
- require_once('src/lastlogins.php');
222
- require_once('src/lastlogins-loggedin.php');
223
- require_once('src/lastlogins-failed.php');
224
- require_once('src/lastlogins-blocked.php');
225
 
226
  /* Load handlers for main pages (settings). */
227
- require_once('src/settings.php');
228
- require_once('src/settings-general.php');
229
- require_once('src/settings-scanner.php');
230
- require_once('src/settings-integrity.php');
231
- require_once('src/settings-hardening.php');
232
- require_once('src/settings-posthack.php');
233
- require_once('src/settings-alerts.php');
234
- require_once('src/settings-apiservice.php');
235
- require_once('src/settings-webinfo.php');
236
 
237
  /* Load global variables and triggers */
238
- require_once('src/globals.php');
239
 
240
  /**
241
  * Uninstalls the plugin, its settings and reverts the hardening.
@@ -246,15 +241,18 @@ require_once('src/globals.php');
246
  * inserted into the sub-database associated to a multi-site installation, will
247
  * revert the hardening applied to the core directories, and will delete all the
248
  * security logs, cache and additional data stored in the storage directory.
 
 
249
  */
250
- function sucuriscan_deactivate()
251
  {
252
- global $wpdb;
 
 
 
 
 
253
 
254
- if ($wpdb) {
255
- /* Delete all the possible plugin related options from the database */
256
- $sql = "SELECT * FROM {$wpdb->options} WHERE option_name LIKE 'sucuriscan%'";
257
- $options = $wpdb->get_results($sql);
258
  foreach ($options as $option) {
259
  delete_site_option($option->option_name);
260
  delete_option($option->option_name);
@@ -288,4 +286,4 @@ function sucuriscan_deactivate()
288
  $fifo->removeDirectoryTree($directory);
289
  }
290
 
291
- register_deactivation_hook(__FILE__, 'sucuriscan_deactivate');
5
  * Description: The <a href="https://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
  * Plugin URI: https://wordpress.sucuri.net/
7
  * Author URI: https://sucuri.net/
 
8
  * Author: Sucuri Inc.
9
  * Version: 1.8.8
10
+ *
11
+ * PHP version 5
12
+ *
13
+ * @category Library
14
+ * @package Sucuri
15
+ * @subpackage SucuriScanner
16
+ * @author Daniel Cid <dcid@sucuri.net>
17
+ * @copyright 2010-2017 Sucuri Inc.
18
+ * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
19
+ * @link https://wordpress.org/plugins/sucuri-scanner
20
  */
21
 
 
22
  /**
23
  * Main file to control the plugin.
24
  *
27
  * during the direct access of any of the extra PHP files the interpreter will
28
  * return a 403/Forbidden response and immediately exit the execution, this will
29
  * prevent unwanted access to code with unmet dependencies.
 
 
 
 
 
 
 
 
30
  */
31
  define('SUCURISCAN_INIT', true);
32
 
85
  */
86
  define('SUCURISCAN_VERSION', '1.8.8');
87
 
 
 
 
 
 
88
  /**
89
  * The name of the folder where the plugin's files will be located.
90
  *
104
  /**
105
  * The local URL where the plugin's files and assets are served.
106
  */
107
+ define('SUCURISCAN_URL', rtrim(plugin_dir_url(__FILE__), '/'));
108
 
109
  /**
110
  * Remote URL where the public Sucuri API service is running.
161
  /**
162
  * The life time of the cache for the results of the SiteCheck scans.
163
  */
164
+ define('SUCURISCAN_SITECHECK_LIFETIME', 21600);
165
 
166
  /**
167
  * The life time of the cache for the results of the get_plugins function.
189
  }
190
 
191
  /* Load all classes before anything else. */
192
+ require_once 'src/sucuriscan.lib.php';
193
+ require_once 'src/request.lib.php';
194
+ require_once 'src/fileinfo.lib.php';
195
+ require_once 'src/cache.lib.php';
196
+ require_once 'src/option.lib.php';
197
+ require_once 'src/event.lib.php';
198
+ require_once 'src/hook.lib.php';
199
+ require_once 'src/api.lib.php';
200
+ require_once 'src/mail.lib.php';
201
+ require_once 'src/command.lib.php';
202
+ require_once 'src/template.lib.php';
203
+ require_once 'src/fsscanner.lib.php';
204
+ require_once 'src/hardening.lib.php';
205
+ require_once 'src/interface.lib.php';
206
+ require_once 'src/auditlogs.lib.php';
207
+ require_once 'src/sitecheck.lib.php';
208
+ require_once 'src/integrity.lib.php';
209
+ require_once 'src/firewall.lib.php';
210
+ require_once 'src/installer-skin.lib.php';
211
 
212
  /* Load page and ajax handlers */
213
+ require_once 'src/pagehandler.php';
214
 
215
  /* Load handlers for main pages (lastlogins). */
216
+ require_once 'src/lastlogins.php';
217
+ require_once 'src/lastlogins-loggedin.php';
218
+ require_once 'src/lastlogins-failed.php';
219
+ require_once 'src/lastlogins-blocked.php';
220
 
221
  /* Load handlers for main pages (settings). */
222
+ require_once 'src/settings.php';
223
+ require_once 'src/settings-general.php';
224
+ require_once 'src/settings-scanner.php';
225
+ require_once 'src/settings-integrity.php';
226
+ require_once 'src/settings-hardening.php';
227
+ require_once 'src/settings-posthack.php';
228
+ require_once 'src/settings-alerts.php';
229
+ require_once 'src/settings-apiservice.php';
230
+ require_once 'src/settings-webinfo.php';
231
 
232
  /* Load global variables and triggers */
233
+ require_once 'src/globals.php';
234
 
235
  /**
236
  * Uninstalls the plugin, its settings and reverts the hardening.
241
  * inserted into the sub-database associated to a multi-site installation, will
242
  * revert the hardening applied to the core directories, and will delete all the
243
  * security logs, cache and additional data stored in the storage directory.
244
+ *
245
+ * @return void
246
  */
247
+ function sucuriscanResetAndDeactivate()
248
  {
249
+ if (array_key_exists('wpdb', $GLOBALS)) {
250
+ /* Delete all plugin related options from the database */
251
+ $options = $GLOBALS['wpdb']->get_results(
252
+ 'SELECT option_id, option_name FROM ' . $GLOBALS['wpdb']->options
253
+ . ' WHERE option_name LIKE "' . SUCURISCAN . '%"'
254
+ );
255
 
 
 
 
 
256
  foreach ($options as $option) {
257
  delete_site_option($option->option_name);
258
  delete_option($option->option_name);
286
  $fifo->removeDirectoryTree($directory);
287
  }
288
 
289
+ register_deactivation_hook(__FILE__, 'sucuriscanResetAndDeactivate');