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

Version Description

  • Added API service failback mechanism
  • Added core integrity email on force scan
  • Slight interface redesign
  • Various bugfixes and improvements
Download this release

Release Info

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

Code changes from version 1.7.16 to 1.7.17

Files changed (100) hide show
  1. .htaccess +2 -8
  2. inc/css/sucuri-scanner.min.css +1 -1
  3. inc/images/blank.png +0 -0
  4. inc/images/flags.sprite.png +0 -0
  5. inc/js/sucuri-scanner.min.js +1 -1
  6. inc/tpl/base.html.tpl +8 -9
  7. inc/tpl/bsidebar.html.tpl +2 -2
  8. inc/tpl/{notification-corefiles.html.tpl → corefiles-notification.html.tpl} +1 -1
  9. inc/tpl/corefiles-page.html.tpl +39 -0
  10. inc/tpl/corefiles.html.tpl +82 -0
  11. inc/tpl/{integrity-corefiles.snippet.tpl → corefiles.snippet.tpl} +3 -3
  12. inc/tpl/firewall-auditlogs.html.tpl +73 -0
  13. inc/tpl/firewall-auditlogs.snippet.tpl +43 -0
  14. inc/tpl/firewall-clearcache.html.tpl +59 -0
  15. inc/tpl/firewall-settings.html.tpl +65 -0
  16. inc/tpl/firewall-settings.snippet.tpl +5 -0
  17. inc/tpl/firewall.html.tpl +28 -0
  18. inc/tpl/hardening-panel.html.tpl +14 -14
  19. inc/tpl/hardening-whitelist.html.tpl +1 -1
  20. inc/tpl/hardening.html.tpl +4 -4
  21. inc/tpl/hardening.snippet.tpl +3 -3
  22. inc/tpl/infosys-cronjobs.html.tpl +1 -1
  23. inc/tpl/infosys-errorlogs.html.tpl +2 -2
  24. inc/tpl/infosys-htaccess.html.tpl +18 -3
  25. inc/tpl/infosys-serverinfo.html.tpl +1 -1
  26. inc/tpl/infosys-wpconfig.html.tpl +1 -1
  27. inc/tpl/infosys.html.tpl +10 -10
  28. inc/tpl/integrity-auditlogs.html.tpl +3 -3
  29. inc/tpl/integrity-auditlogs.snippet.tpl +1 -1
  30. inc/tpl/integrity-auditreport.html.tpl +15 -14
  31. inc/tpl/integrity-corefiles.html.tpl +0 -97
  32. inc/tpl/integrity-modifiedfiles.html.tpl +56 -37
  33. inc/tpl/integrity-modifiedfiles.snippet.tpl +2 -2
  34. inc/tpl/integrity.html.tpl +4 -4
  35. inc/tpl/lastlogins-admins.html.tpl +1 -1
  36. inc/tpl/lastlogins-admins.snippet.tpl +3 -3
  37. inc/tpl/lastlogins-all.html.tpl +1 -1
  38. inc/tpl/lastlogins-failedlogins.html.tpl +6 -8
  39. inc/tpl/lastlogins-failedlogins.snippet.tpl +2 -2
  40. inc/tpl/lastlogins-loggedin.html.tpl +1 -1
  41. inc/tpl/lastlogins.html.tpl +8 -8
  42. inc/tpl/malwarescan-resblacklist.html.tpl +1 -5
  43. inc/tpl/malwarescan-resmalware.html.tpl +9 -15
  44. inc/tpl/malwarescan-resmalware.snippet.tpl +0 -2
  45. inc/tpl/malwarescan-results.html.tpl +12 -15
  46. inc/tpl/malwarescan-reswebdetails.html.tpl +5 -11
  47. inc/tpl/malwarescan-resweblinks.html.tpl +1 -4
  48. inc/tpl/malwarescan-weblinktitle.snippet.tpl +1 -1
  49. inc/tpl/malwarescan.html.tpl +3 -3
  50. inc/tpl/modalwindow.html.tpl +1 -2
  51. inc/tpl/monitoring-logs.html.tpl +0 -73
  52. inc/tpl/monitoring-logs.snippet.tpl +0 -71
  53. inc/tpl/monitoring-settings.html.tpl +0 -28
  54. inc/tpl/monitoring-settings.snippet.tpl +0 -5
  55. inc/tpl/monitoring.html.tpl +0 -58
  56. inc/tpl/notification-admin.html.tpl +5 -0
  57. inc/tpl/notification-pretty.html.tpl +2 -2
  58. inc/tpl/notification-resetpwd.html.tpl +1 -0
  59. inc/tpl/posthack-resetpassword.html.tpl +2 -2
  60. inc/tpl/posthack-resetplugins.html.tpl +1 -9
  61. inc/tpl/posthack-updatesecretkeys.html.tpl +1 -1
  62. inc/tpl/posthack.html.tpl +6 -6
  63. inc/tpl/settings-alert-bruteforce.html.tpl +35 -0
  64. inc/tpl/settings-alert-events.html.tpl +46 -0
  65. inc/tpl/settings-alert-events.snippet.tpl +10 -0
  66. inc/tpl/settings-alert-perhour.html.tpl +35 -0
  67. inc/tpl/settings-alert-recipients.html.tpl +58 -0
  68. inc/tpl/settings-alert-recipients.snippet.tpl +9 -0
  69. inc/tpl/settings-alert-subject.html.tpl +34 -0
  70. inc/tpl/settings-alert-subject.snippet.tpl +8 -0
  71. inc/tpl/settings-alert.html.tpl +12 -0
  72. inc/tpl/{settings-general-apiproxy.html.tpl → settings-apiservice-proxy.html.tpl} +1 -1
  73. inc/tpl/{settings-general-apissl.html.tpl → settings-apiservice-ssl.html.tpl} +2 -2
  74. inc/tpl/settings-apiservice-status.html.tpl +44 -0
  75. inc/tpl/{settings-general-apitimeout.html.tpl → settings-apiservice-timeout.html.tpl} +12 -6
  76. inc/tpl/settings-apiservice.html.tpl +10 -0
  77. inc/tpl/settings-emailsubject.snippet.tpl +0 -7
  78. inc/tpl/settings-general-apikey.html.tpl +3 -3
  79. inc/tpl/settings-general-auditlogstats.html.tpl +40 -0
  80. inc/tpl/settings-general-datastorage.html.tpl +2 -2
  81. inc/tpl/settings-general-datetime.html.tpl +21 -0
  82. inc/tpl/settings-general-ipdiscoverer.html.tpl +21 -0
  83. inc/tpl/settings-general-xhrmonitor.html.tpl +1 -1
  84. inc/tpl/settings-general.html.tpl +10 -106
  85. inc/tpl/settings-heartbeat.html.tpl +7 -7
  86. inc/tpl/settings-ignorerules.html.tpl +2 -2
  87. inc/tpl/settings-ignorerules.snippet.tpl +1 -1
  88. inc/tpl/settings-ignorescanning.html.tpl +4 -4
  89. inc/tpl/settings-notifications.html.tpl +0 -75
  90. inc/tpl/settings-notifications.snippet.tpl +0 -12
  91. inc/tpl/settings-scanner.html.tpl +15 -27
  92. inc/tpl/settings-selfhosting-monitor.html.tpl +56 -0
  93. inc/tpl/settings-selfhosting.html.tpl +4 -0
  94. inc/tpl/settings-trustip.html.tpl +3 -3
  95. inc/tpl/settings.html.tpl +35 -21
  96. inc/tpl/setup-form.html.tpl +1 -1
  97. inc/tpl/setup-notice.html.tpl +1 -1
  98. readme.txt +20 -12
  99. sucuri.php +5181 -4420
  100. uninstall.php +24 -22
.htaccess CHANGED
@@ -1,14 +1,8 @@
1
 
2
  Order Deny,Allow
3
  Deny from all
4
- Allow from 127.0.0.1
5
-
6
- <Files index.php>
7
- Order Allow,Deny
8
- Allow from all
9
- </Files>
10
 
11
  <FilesMatch "\.(gif|jpe?g|png|css|js)$">
12
- Order Allow,Deny
13
- Allow from all
14
  </FilesMatch>
1
 
2
  Order Deny,Allow
3
  Deny from all
 
 
 
 
 
 
4
 
5
  <FilesMatch "\.(gif|jpe?g|png|css|js)$">
6
+ Order Allow,Deny
7
+ Allow from all
8
  </FilesMatch>
inc/css/sucuri-scanner.min.css CHANGED
@@ -1 +1 @@
1
- .sucuriscan-wrap *,.sucuriscan-wrap *:before,.sucuriscan-wrap *:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.sucuriscan-clearfix:before,.sucuriscan-clearfix:after{display:table;content:' '}.sucuriscan-clearfix:after{clear:both}.sucuriscan-hidden{display:none !important}.sucuriscan-opacity{opacity:.6}.sucuriscan-monospace{font-family:Menlo,Monaco,monospace,courier}.sucuriscan-ellipsis{overflow:hidden;display:inline-block;white-space:nowrap;text-overflow:ellipsis}.sucuriscan-wraptext{word-break:break-all}.sucuriscan-pull-left{float:left}.sucuriscan-pull-right{float:right}.sucuriscan-list li{list-style:disc;margin:0 0 5px 15px}.sucuriscan-gradient,.sucuriscan-modal-header,.sucuriscan-maincontent .sucuriscan-table tr>th,.sucuriscan-leftside #poststuff h3{background-color:#f1f1f1;background-image:-webkit-gradient(linear,left top,left bottom,from(#f9f9f9),to(#ececec));background-image:-webkit-linear-gradient(top,#f9f9f9,#ececec);background-image:-moz-linear-gradient(top,#f9f9f9,#ececec);background-image:-ms-linear-gradient(top,#f9f9f9,#ececec);background-image:-o-linear-gradient(top,#f9f9f9,#ececec);background-image:linear-gradient(top,#f9f9f9,#ececec);filter:"progid: DXImageTransform.Microsoft.Gradient(startColorstr=#f9f9f9, endColorstr=#ececec)";-ms-filter:"progid: DXImageTransform.Microsoft.Gradient(startColorstr=#f9f9f9, endColorstr=#ececec)"}.wp-core-ui .button-success,.wp-core-ui .button-success.focus,.wp-core-ui .button-success.hover,.wp-core-ui .button-success:focus,.wp-core-ui .button-success:hover{background:#8dcd5a;border-color:#48a325;box-shadow:inset 0 1px 0 rgba(195,230,180,0.6)}.wp-core-ui .button-success.focus,.wp-core-ui .button-success.hover,.wp-core-ui .button-success:focus,.wp-core-ui .button-success:hover{background:#69be48}.wp-core-ui .button-success.focus,.wp-core-ui .button-success:focus{border-color:#23500e}.wp-core-ui .button-success.active,.wp-core-ui .button-success.active:focus,.wp-core-ui .button-success.active:hover,.wp-core-ui .button-success:active{background:#47a61b;border-color:#358400}.wp-core-ui .button-success-disabled,.wp-core-ui .button-success.disabled,.wp-core-ui .button-success:disabled,.wp-core-ui .button-success[disabled]{color:#b2e794 !important;background:#74ba29 !important;border-color:#3f7f1b !important}.wp-core-ui .button-danger,.wp-core-ui .button-danger.focus,.wp-core-ui .button-danger.hover,.wp-core-ui .button-danger:focus,.wp-core-ui .button-danger:hover{background:#cd5050;border-color:#a52121;box-shadow:inset 0 1px 0 rgba(230,170,170,0.6)}.wp-core-ui .button-danger.focus,.wp-core-ui .button-danger.hover,.wp-core-ui .button-danger:focus,.wp-core-ui .button-danger:hover{background:#be4242}.wp-core-ui .button-danger.focus,.wp-core-ui .button-danger:focus{border-color:#500e0e}.wp-core-ui .button-danger.active,.wp-core-ui .button-danger.active:focus,.wp-core-ui .button-danger.active:hover,.wp-core-ui .button-danger:active{background:#a61b1b;border-color:#840000}.wp-core-ui .button-danger-disabled,.wp-core-ui .button-danger.disabled,.wp-core-ui .button-danger:disabled,.wp-core-ui .button-danger[disabled]{color:#e79494 !important;background:#ba2929 !important;border-color:#7f1b1b !important}.wp-core-ui .sucuriscan-btnblock{display:block;width:100%;text-align:center}.sucuriscan-overlay{position:fixed;top:0;left:0;bottom:0;right:0;z-index:9990;background:rgba(0,0,0,0.5)}.sucuriscan-modal{position:absolute;top:25px;left:15%;z-index:9990;width:55%}.sucuriscan-modal-outside{position:relative;left:0;border:1px solid #ddd}.sucuriscan-modal-inside{background:#fff;padding:20px}.sucuriscan-modal-header{padding:0;border-bottom:1px solid #ddd}#poststuff h3.sucuriscan-modal-title,.sucuriscan-leftside #poststuff h3.sucuriscan-modal-title,.sucuriscan-modal-header .sucuriscan-modal-title{margin:0;padding:0;float:left;line-height:38px;margin-left:10px;border-bottom:0}.sucuriscan-modal-header .sucuriscan-modal-logo{display:inline-block;float:left;margin-top:8px;margin-left:18px}.sucuriscan-modal-header .sucuriscan-modal-logo img{height:22px}.sucuriscan-modal-close{display:inline-block;position:absolute;top:0;right:0;font-size:16px;font-weight:bold;text-decoration:none;line-height:38px;padding:0 15px;border-left:1px solid #ddd}.sucuriscan-modal-inside p:first-child{margin-top:0}.sucuriscan-modal-inside p:last-child{margin-bottom:0}.sucuriscan-label,.sucuriscan-label-default,.sucuriscan-label-unknown,.sucuriscan-label-primary,.sucuriscan-label-success,.sucuriscan-label-info,.sucuriscan-label-notice,.sucuriscan-label-warning,.sucuriscan-label-danger,.sucuriscan-label-error{display:inline;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;padding:.2em .6em .3em;border-radius:.25em}.sucuriscan-label-default,.sucuriscan-label-unknown{background:#777}.sucuriscan-label-danger,.sucuriscan-label-error{background:#d9534f}.sucuriscan-label-info,.sucuriscan-label-notice{background:#5bc0de}.sucuriscan-label-warning{background:#f0ad4e}.sucuriscan-label-success{background:#5cb85c}.sucuriscan-label-primary{background:#428bca}.sucuriscan-wrap{margin-top:20px}.sucuriscan-wrap .sucuriscan-maincontent{margin:20px 0}.sucuriscan-wrap .sucuriscan-leftside{width:73.5%;float:left}.sucuriscan-wrap .sucuriscan-onecolumn{width:100%}.sucuriscan-wrap .sucuriscan-sidebar{width:25%;float:right}.sucuriscan-wrap #warnings_hook{line-height:initial;padding:0}.sucuriscan-wrap .sucuriscan-navbar{padding-top:20px;padding-left:6px}.sucuriscan-wrap .sucuriscan-navbar .nav-tab{margin-right:0}.sucuriscan-header,.sucuriscan-footer{position:relative;min-width:255px;background:#333;margin:0;padding:10px;border-radius:4px}.sucuriscan-footer .sucuriscan-help{color:#fff;float:right;text-align:right}.sucuriscan-footer .sucuriscan-help p{line-height:38px;margin:0 10px 0 0;padding:0}.sucuriscan-wrap .sucuriscan-logo,.sucuriscan-wrap .sucuriscan-header h2,.sucuriscan-wrap .sucuriscan-footer h2{float:left;margin:0;padding:0}.sucuriscan-wrap .sucuriscan-logo{display:inline-block}.sucuriscan-wrap .sucuriscan-logo img{display:block}.sucuriscan-wrap .sucuriscan-header h2,.sucuriscan-wrap .sucuriscan-footer h2{color:#fff;line-height:38px;margin-left:10px;text-shadow:#000 0 1px 0}.sucuriscan-leftside #poststuff .postbox:last-child{margin-bottom:0}.sucuriscan-leftside #poststuff .postbox h3{border-bottom:1px solid #ddd}.sucuriscan-maincontent abbr{text-decoration:underline;cursor:help}.wrap div.sucuriscan-setup-notice{background:#bbe8f5;margin:0 0 20px 0;padding:0;border:1px solid #bbb;border-radius:3px;-webkit-box-shadow:none;box-shadow:none}.wrap div.sucuriscan-setup-notice .sucuriscan-setup-image,.wrap div.sucuriscan-setup-notice .sucuriscan-setup-image img{border-radius:3px 0 0 3px}.wrap div.sucuriscan-setup-notice .sucuriscan-setup-image{background:#333;margin:-1px 0 -1px -1px;padding:7px 10px;border-right:1px solid transparent}.wrap div.sucuriscan-setup-notice .sucuriscan-setup-form{padding:4px;padding-left:0}.wrap div.sucuriscan-setup-notice p{font-size:14px;line-height:20px;margin:0 0 0 10px;padding:7px 0}.wrap div.sucuriscan-setup-notice,.wrap div.sucuriscan-setup-notice .sucuriscan-setup-image{border-color:#4393ac}.wp-core-ui .sucuriscan-review-hero,.wp-core-ui .button.sucuriscan-review-hero{position:relative;top:-2px;right:-15px;height:initial;line-height:30px;float:right;padding:0 20px}.sucuriscan-input-group>label{display:inline-block;border:1px solid #ddd;border-right:0;line-height:26px;float:left;padding:0 10px;background:#eee}.sucuriscan-input-group>input[type=text]{margin:0;padding-bottom:4px}.sucuriscan-input-group>select{vertical-align:initial;margin:0}.sucuriscan-table-setup td{vertical-align:top}.sucuriscan-table-setup .sucuriscan-description{font-size:12px;margin-top:10px}.sucuriscan-dismiss-setup{font-size:10px;line-height:28px}.sucuriscan-maincontent .sucuriscan-table{margin-top:12px}.sucuriscan-maincontent .sucuriscan-table tr>th{border-top:1px solid #e5e5e5;border-bottom:1px solid #e5e5e5}.sucuriscan-maincontent .sucuriscan-table tr:first-child th{border-top:0}.sucuriscan-maincontent .sucuriscan-table td.check-column{padding:8px 10px}.sucuriscan-maincontent .sucuriscan-striped-table tr:nth-child(even){background:#f5f5f5}.sucuriscan-table-double-title tr:first-child th{border-bottom:0}.sucuriscan-table-triple-title tr:first-child th,.sucuriscan-table-triple-title tr:first-child+tr th{border-bottom:0}.sucuriscan-table-quad-title tr:first-child th,.sucuriscan-table-quad-title tr:first-child+tr th,.sucuriscan-table-quad-title tr:first-child+tr+tr th{border-bottom:0}.sucuriscan-table-description{border-left-width:1px !important;box-shadow:none}.sucuriscan-table-description .inside{border-bottom:0 !important}.widefat td.td-with-button{text-align:right;padding:3px 10px}.widefat td.td-with-button button{min-width:90px}.widefat td.td-with-button select{height:initial;line-height:initial;vertical-align:top;margin:0;padding:2px 0 3px 0}.widefat th.check-column{line-height:36px;padding:0}.widefat th.check-column input[type=checkbox]{margin:1px 0 0 10px}.sucuriscan-list-as-table{background:#fff;border:1px solid #e5e5e5}.sucuriscan-list-as-table li{line-height:30px;word-break:break-all;margin:0;padding:0 10px}.sucuriscan-list-as-table li:nth-child(odd){background:#f5f5f5}.sucuriscan-list-as-table-scrollable{height:300px;overflow:hidden;overflow-y:scroll}.sucuriscan-maincontent .thead-with-button{padding:5px 5px 5px 10px}.sucuriscan-maincontent .thead-with-button>span{display:inline-block;line-height:28px}.sucuriscan-maincontent .thead-with-button .input-text{line-height:26px}.sucuriscan-maincontent .thead-with-button select{margin:0;padding:0}.sucuriscan-maincontent .thead-topright-action{display:inline-block;float:right}.sucuriscan-ad{color:#fff;padding:20px;margin-bottom:20px}.sucuriscan-ad h3,.sucuriscan-ad h4,.sucuriscan-ad .sucuriscan-ad-btn{font-family:Arial,Helvetica,sans-serif;color:#fff;margin:0}.sucuriscan-ad h3{font-size:18px;font-weight:300}.sucuriscan-ad h4{font-size:22px;font-weight:bold;margin-top:10px}.sucuriscan-ad .sucuriscan-ad-btn{display:block;font-size:13px;font-weight:bold;text-align:center;text-decoration:none;text-transform:uppercase;margin-top:20px;padding:5px;border-radius:20px}.sucuriscan-ad .sucuriscan-ad-footer{margin-top:20px;margin-bottom:0}.sucuriscan-ad .sucuriscan-ad-footer ul{margin:0}.sucuriscan-ad .sucuriscan-ad-footer li{font-size:12px;color:#fff;list-style:disc;margin:0 0 0 16px}.sucuriscan-ad .sucuriscan-ad-footer li.featured{color:#fde44c}.sucuriscan-scanner-video{width:100%;background:#fff;border:1px solid #ddd}.sucuriscan-sidebar .sucuriscan-supportbtn{width:100%;height:initial;text-align:center;line-height:36px;margin-top:15px;padding:0}.wp-core-ui .sucuriscan-hide-ads{display:block;color:#666;font-size:11px;text-decoration:underline;margin-top:15px;padding:0}.wp-core-ui .sucuriscan-hide-ads:focus{color:#000;box-shadow:none}.sucuriscan-ad-firewall{background:#606e77}.sucuriscan-ad-firewall .sucuriscan-ad-btn{background:#606e77;border:1px solid #fff}.sucuriscan-ad-firewall .sucuriscan-ad-btn:hover{background:#85929b}.sucuriscan-ad-antivirus{background:#04833e;padding-bottom:0}.sucuriscan-ad-antivirus .sucuriscan-ad-website{display:block;text-decoration:none;margin-top:20px}.sucuriscan-ad-antivirus .sucuriscan-ad-website img{display:block;max-width:100%}.sucuriscan-ad-antivirus .sucuriscan-ad-btn{background-color:#e8840a;background-image:-webkit-gradient(linear,left top,left bottom,from(#e8840a),to(#ef7f02));background-image:-webkit-linear-gradient(top,#e8840a,#ef7f02);background-image:-moz-linear-gradient(top,#e8840a,#ef7f02);background-image:-ms-linear-gradient(top,#e8840a,#ef7f02);background-image:-o-linear-gradient(top,#e8840a,#ef7f02);background-image:linear-gradient(top,#e8840a,#ef7f02);filter:"progid: DXImageTransform.Microsoft.Gradient(startColorstr=#e8840a, endColorstr=#ef7f02)";-ms-filter:"progid: DXImageTransform.Microsoft.Gradient(startColorstr=#e8840a, endColorstr=#ef7f02)";box-shadow:inset 0 1px 1px #eaac3a;border:1px solid #d17301}div.sucuriscan-alert{position:relative;margin:0 0 20px 0}div.sucuriscan-alert>a.close{position:absolute;top:10px;right:10px;font-size:18px;font-weight:bold;text-decoration:none}.sucuriscan-inline-alert,.sucuriscan-inline-alert-updated,.sucuriscan-inline-alert-error,.sucuriscan-inline-alert-warning,.sucuriscan-inline-alert-info{background:#fff;box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);padding:0;border-left:4px solid #ddd}.sucuriscan-inline-alert>p,.sucuriscan-inline-alert-updated>p,.sucuriscan-inline-alert-error>p,.sucuriscan-inline-alert-warning>p,.sucuriscan-inline-alert-info>p{margin:0;padding:8px 12px;border:1px solid #ddd;border-left:0}.sucuriscan-inline-alert-updated,.sucuriscan-inline-alert-warning,.sucuriscan-inline-alert-error,.sucuriscan-inline-alert-info{margin-bottom:10px}.sucuriscan-inline-alert-updated{border-left-color:#7ad03a}.sucuriscan-inline-alert-warning{border-left-color:#ffba00}.sucuriscan-inline-alert-error{border-left-color:#dd3d36}.sucuriscan-inline-alert-info{border-left-color:#2ea2cc}.sucuriscan-tabs>ul{margin:0}.sucuriscan-tabs>ul li,.sucuriscan-tabs>ul li>a{display:inline-block}.sucuriscan-tabs>ul li{margin-bottom:0}.sucuriscan-tabs>ul li>a{background:#e5e5e5;font-size:13px;font-weight:bold;color:#333;line-height:38px;text-decoration:none;padding:0 10px}.sucuriscan-tabs>ul li>a.sucuriscan-tab-active{background:#fff;border:1px solid #e1e1e1;border-bottom:0}.sucuriscan-tabs>ul li.sucuriscan-red-tab a{background:#ff8a83;color:#fff}.sucuriscan-tabs>ul li.sucuriscan-red-tab a.sucuriscan-tab-active{background:#dd3d36;border-color:#dd3d36}.sucuriscan-maincontent .sucuriscan-tab-containers>div>table{margin-top:0}.sucuriscan-maincontent .sucuriscan-tab-containers>div>#poststuff{margin-top:0}.sucuriscan-getapi-div{background:#fff;margin:0 0 20px 0;border:1px solid #e5e5e5;border-radius:3px}.sucuriscan-getapi-div p{margin:0;padding:10px}.sucuriscan-getapi-form button.button-primary{width:100%;height:initial;line-height:30px;margin:0 0 -1px 0;padding:0;border-radius:0 0 3px 3px}.sucuriscan-malwarescan-message{margin-bottom:20px !important}.sucuriscan-loading{background:#fff;text-align:center;padding:30px;padding-bottom:15px;border:1px solid #ddd;border-radius:4px}.sucuriscan-loading p,.sucuriscan-loading h3{margin:0;padding:0}.sucuriscan-loading .title{font-size:28px;margin-bottom:10px}.sucuriscan-loading .description{font-size:16px}.sucuriscan-sitelogo{width:190px;height:100px;background:url('http://sitecheck.sucuri.net/images/sucuri-sprite.png') no-repeat;margin:0 auto}.sucuriscan-sitecheck-form{margin:20px 0 0 0}.sucuriscan-sitecheck-form .button.button-hero{padding:0 46px}.sucuriscan-loading .sucuriscan-sitecheck-disclaimer{text-align:justify;padding-top:20px;border-top:1px solid #ddd}.sucuriscan-loading .sucuriscan-sitecheck-disclaimer p{font-size:10px}.sucuriscan-maincontent .sucuriscan-border{border:0;border-left:4px solid #ddd}.sucuriscan-maincontent .sucuriscan-border>h3,.sucuriscan-maincontent .sucuriscan-border>.inside{border-top:1px solid #e5e5e5;border-right:1px solid #e5e5e5}.sucuriscan-maincontent .sucuriscan-border>h3{border-bottom:0}.sucuriscan-maincontent .sucuriscan-border>.inside{margin-top:0 !important;border-bottom:1px solid #ddd}.sucuriscan-maincontent .sucuriscan-border-good,.sucuriscan-maincontent .sucuriscan-border-success{border-left-color:#7ad03a}.sucuriscan-maincontent .sucuriscan-border-bad,.sucuriscan-maincontent .sucuriscan-border-danger{border-left-color:#dd3d36}.sucuriscan-maincontent .sucuriscan-border-info{border-left-color:#2ea2cc}.sucuriscan-maincontent .sucuriscan-cleanup-btn{margin:20px 0 0 0}.sucuriscan-scanner-results .sucuriscan-scanner-details tr:nth-child(even),.sucuriscan-scanner-results .sucuriscan-scanner-links tr:nth-child(even){background:#f5f5f5}.sucuriscan-scanner-results td.sucuriscan-border-bad{border-left-width:4px;border-left-style:solid}.sucuriscan-scanner-results .sucuriscan-malware-link{text-align:right}.sucuriscan-scanner-results .sucuriscan-malware-link a:hover{color:#fff}.sucuriscan-malware-payload{background:#f5f5f5;word-break:break-all;margin:-2px -15px -15px -15px;padding:15px}.sucuriscan-maincontent .sucuriscan-corefiles,.sucuriscan-maincontent .sucuriscan-integrity-message,.sucuriscan-maincontent .sucuriscan-wordpress-outdated,.sucuriscan-maincontent .sucuriscan-auditlogs{margin-top:0;margin-bottom:20px}.sucuriscan-maincontent .sucuriscan-auditlogs{margin-bottom:0}.sucuriscan-auditlogs .sucuriscan-list-as-table{margin-bottom:0}.sucuriscan-auditlogs .sucuriscan-maxper-page{text-align:right}.sucuriscan-auditlogs .sucuriscan-label{display:inline-block;width:18px;text-transform:uppercase;line-height:13px;cursor:pointer;border-radius:50%}.sucuriscan-auditlogs .sucuriscan-auditlog-success,.sucuriscan-label-added{background:#5cb85c}.sucuriscan-auditlogs .sucuriscan-auditlog-debug{background:#c690ec}.sucuriscan-auditlogs .sucuriscan-auditlog-info{background:#5bc0de}.sucuriscan-auditlogs .sucuriscan-auditlog-notice{background:#428bca}.sucuriscan-auditlogs .sucuriscan-auditlog-warning,.sucuriscan-label-modified{background:#f0ad4e}.sucuriscan-auditlogs .sucuriscan-auditlog-error,.sucuriscan-label-removed{background:#f27d7d}.sucuriscan-auditlogs .sucuriscan-auditlog-critical{background:#000}.sucuriscan-maincontent .sucuriscan-audit-report{border-left-width:1px}.sucuriscan-audit-report .sucuriscan-report-row{margin-bottom:10px}.sucuriscan-audit-report .sucuriscan-report-row:last-child{margin-bottom:0}.sucuriscan-audit-report .sucuriscan-report-chart{width:49%;border:1px solid #ddd}.sucuriscan-audit-report .sucuriscan-report-chart h4,.sucuriscan-audit-report .sucuriscan-report-chart h5{font-weight:normal;text-align:center;margin:0}.sucuriscan-audit-report .sucuriscan-report-chart h4{font-size:18px;margin-top:10px}.sucuriscan-audit-report .sucuriscan-report-chart h5{font-size:12px;margin-top:5px}.sucuriscan-maincontent .sucuriscan-audit-report .sucuriscan-inline-alert-info{margin-top:10px}.sucuriscan-status-type{display:inline-block;width:20px;background:#ddd;text-align:center;text-transform:uppercase;margin-right:10px;padding:0 3px;border:1px solid transparent;border-radius:3px}td.sucuriscan-corefiles-warning>div{background:#f2dede;color:#a94442;border-color:#ebccd1}.sucuriscan-maincontent .sucuriscan-corefiles .sucuriscan-label{text-transform:capitalize}.sucuriscan-maincontent td.sucuriscan-corefiles-warning,.sucuriscan-maincontent td.sucuriscan-corefiles-warning p{margin:0;padding:0}.sucuriscan-maincontent td.sucuriscan-corefiles-warning div{padding:10px;border-width:1px;border-style:solid}.sucuriscan-maincontent td.sucuriscan-corefiles-warning code{font-size:12px;padding:0 5px}.sucuriscan-maincontent .sucuriscan-integrity-message{position:relative}.sucuriscan-maincontent .sucuriscan-integrity-message .sucuriscan-integrity-mark,.sucuriscan-maincontent .sucuriscan-integrity-message .sucuriscan-integrity-failure{position:absolute;top:1px;right:1px;background:#ddd;font-weight:bold;color:#fff;line-height:35px;padding:0 10px;border-left:1px solid #ddd}.sucuriscan-maincontent .sucuriscan-integrity-message .sucuriscan-integrity-mark{background:#7ad03a}.sucuriscan-maincontent .sucuriscan-integrity-message .sucuriscan-integrity-failure{background:#dd3d36;border-left:0}.sucuriscan-maincontent .sucuriscan-ignoredfiles{margin-top:0}.sucuriscan-ignore-file form{padding:10px;padding-top:0;border-bottom:1px solid #ddd;border-right:1px solid #ddd}.sucuriscan-ignore-file p{border-bottom:0}.sucuriscan-ignore-file-input{width:80%}.sucuriscan-ignore-file-button{width:18%}.sucuriscan-maincontent .sucuriscan-modifiedfiles .sucuriscan-ellipsis{width:100px}.sucuriscan-monitoring-settings{margin-bottom:20px}.sucuriscan-monitoring-settings td.td-with-button{text-align:left}.sucuriscan-monitoring-settings .sucuriscan-list-as-table{margin:0}.sucuriscan-monitoring-apikey-form .input-text{width:85%}.sucuriscan-monitoring-logs .thead-with-button .button{width:65px}.sucuriscan-monitoring-logs .thead-with-button .input-text,.sucuriscan-monitoring-logs .thead-with-button select{width:250px}.sucuriscan-monitoring-logs .sucuriscan-monitoring-date-form select{width:70px}.sucuriscan-monitoring-logs .sucuriscan-monitoring-date-form select+select{width:112px}.sucuriscan-monitoring-logs .sucuriscan-monitoring-date-form select+select+select{width:60px}.sucuriscan-monitoring-logs .sucuriscan-target-date{font-size:12px;color:#999;margin-right:5px}.sucuriscan-monitoring-logs .sucuriscan-denial-type{font-size:14px}.sucuriscan-monitoring-logs .sucuriscan-denial-type-date{font-style:italic;color:#999}.sucuriscan-request-summary{margin:-15px;margin-top:-3px}.sucuriscan-request-summary td{font-size:14px}.sucuriscan-request-summary tr td:first-child{font-weight:bold}.sucuriscan-request-summary td+td{word-break:break-all}.sucuriscan-hstatus{position:relative;margin:0 -12px;padding:10px 12px;border:1px solid transparent}.sucuriscan-hstatus-0{background-color:#f2dede;color:#a94442;border-color:#ebccd1}.sucuriscan-hstatus-1{background-color:#dff0d8;color:#3c763d;border-color:#d6e9c6}.sucuriscan-hstatus-2{background-color:#dee4f2;color:#4263a9;border-color:#ccd0eb}.sucuriscan-hstatus .button-primary,.sucuriscan-hstatus .button-secondary{position:absolute;top:5px;right:5px}.sucuriscan-hardening .postbox .inside pre{background:#eaeaea;padding:10px}.sucuriscan-hardening-whitelist form{margin-top:15px}.sucuriscan-hardening-whitelist form label{line-height:29px;font-size:12px;background-color:#eee;padding:0 10px;display:inline-block;border:1px solid #ddd;border-right:0}.sucuriscan-hardening-whitelist form input[type=text]{margin:0;padding:5px}.sucuriscan-hardening-whitelist form select{height:initial;padding:4px;margin:0}.sucuriscan-hardening-whitelist form select,.sucuriscan-hardening-whitelist form input[type=text],.sucuriscan-hardening-whitelist form .button{margin-right:5px}.sucuriscan-maincontent .sucuriscan-table.sucuriscan-hardening-whitelist-table{margin-top:0}.sucuriscan-lastlogin-outof{font-style:italic;color:#999;margin-right:10px}.sucuriscan-admins-lastlogins .sucuriscan-ellipsis{width:170px}.sucuriscan-admins-lastlogins td{padding:4px 8px}.sucuriscan-pattern-search-inputbox{margin-top:12px}.sucuriscan-pattern-search-inputbox .input-text{width:84.7777%;line-height:30px;margin:0;margin-right:6px}.sucuriscan-pattern-search-inputbox .input-button{width:14%;height:initial;line-height:35px}.sucuriscan-pattern-search .sucuriscan-cleanup-btn{margin-top:12px}.sucuriscan-pattern-search table label{color:#999}.sucuriscan-pattern-search .sucuriscan-grep-text em{color:#ea3838}.sucuriscan-about ul{margin-left:20px}.sucuriscan-about ul li{list-style:initial}.sucuriscan-about li label{font-weight:bold;vertical-align:initial}.sucuriscan-apikey-registered .sucuriscan-pull-right{width:400px}.sucuriscan-apikey-registered .sucuriscan-sitelogo{background-position:0 -17px;height:83px}.sucuriscan-setup-instructions .form-table{margin-top:15px}.sucuriscan-setup-instructions .form-table td{padding:0;padding-bottom:12px}.sucuriscan-setup-instructions .form-table select{max-width:400px}.sucuriscan-pagination{display:inline-block;margin:0;padding:0;border-radius:4px}.sucuriscan-pagination>li{display:inline}.sucuriscan-pagination>li>a,.sucuriscan-pagination>li>span{position:relative;background:#fff;color:#428bca;line-height:1.42857143;text-decoration:none;float:left;margin-left:-1px;padding:6px 12px;border:1px solid #ddd}.sucuriscan-pagination>li:first-child>a,.sucuriscan-pagination>li:first-child>span{margin-left:0;border-radius:4px 0 0 4px}.sucuriscan-pagination>li:last-child>a,.sucuriscan-pagination>li:last-child>span{border-radius:0 4px 4px 0}.sucuriscan-pagination>li>a.sucuriscan-pagination-active,.sucuriscan-pagination>li>a:hover{background:#0074a2;color:#fff}.sucuriscan_wpconfig_keys_updated textarea{width:100%;height:250px;background:#f5f5f5;font-size:12px;resize:vertical;margin:20px 0 0 0}.sucuriscan-maincontent .sucuriscan-last-logins{margin-top:0}.sucuriscan-maincontent .sucuriscan-last-logins .sucuriscan-ellipsis{width:150px;line-height:inherit}.sucuriscan-maincontent .sucuriscan-full-textarea{width:100%;height:400px;line-height:normal;resize:vertical;padding:10px}.sucuriscan-maincontent .sucuriscan-settings{margin-top:0}.sucuriscan-maincontent .sucuriscan-settings form{display:inline-block}.sucuriscan-maincontent .sucuriscan-settings select,.sucuriscan-maincontent .sucuriscan-settings .input-text{width:220px;margin:0}.sucuriscan-maincontent .sucuriscan-settings-notifications{margin-top:0}.sucuriscan-maincontent .sucuriscan-settings-notifications .dashicons-before:before{margin-right:5px}.sucuriscan-maincontent .sucuriscan-settings-ignorescanning{margin-top:0}.sucuriscan-maincontent .sucuriscan-settings-trustip{margin-top:0}.sucuriscan-maincontent .sucuriscan-wpcron-list{margin-top:0}.sucuriscan-maincontent .sucuriscan-infosys-htaccess .inside{border-bottom:1px solid #ddd !important}.sucuriscan-maincontent .sucuriscan-infosys-htaccess .inside .sucuriscan-inline-alert-updated{margin-bottom:10px}.sucuriscan-maincontent .sucuriscan-errorlogs .inside .sucuriscan-inline-alert-error{margin-top:10px}.sucuriscan-maincontent .sucuriscan-subject-formats{margin:0}.sucuriscan-maincontent .sucuriscan-subject-formats input[type=text]{width:40%;margin-left:10px}.c3 svg{font:10px sans-serif}.c3 line,.c3 path{fill:none;stroke:#000}.c3 text{-webkit-user-select:none;-moz-user-select:none;user-select:none}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#aaa}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke-dasharray:3 3}.c3-text.c3-empty{fill:gray;font-size:2em}.c3-line{stroke-width:1px}.c3-circle._expanded_{stroke-width:1px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:2px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:.75}.c3-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3 !important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item{font-size:12px}.c3-legend-item-hidden{opacity:.15}.c3-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;background-color:#fff;empty-cells:show;-webkit-box-shadow:7px 7px 12px -9px #777;-moz-box-shadow:7px 7px 12px -9px #777;box-shadow:7px 7px 12px -9px #777}.c3-tooltip tr{border:1px solid #CCC}.c3-tooltip th{background-color:#aaa;font-size:14px;padding:2px 5px;text-align:left;color:#FFF}.c3-tooltip td{font-size:13px;padding:3px 6px;background-color:#fff;border-left:1px dotted #999}.c3-tooltip td>span{display:inline-block;width:10px;height:10px;margin-right:6px}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.2}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:none}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max,.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000}@media(max-width:620px){.sucuriscan-tabs>ul li,.sucuriscan-tabs>ul li>a{display:block}.sucuriscan-getapi-form button.button-primary{line-height:40px}}@media(max-width:768px){.sucuriscan-wrap .sucuriscan-leftside,.sucuriscan-wrap .sucuriscan-sidebar,.sucuriscan-wrap .sucuriscan-footer .sucuriscan-logo,.sucuriscan-wrap .sucuriscan-footer .sucuriscan-help{float:none}.sucuriscan-wrap .sucuriscan-leftside,.sucuriscan-wrap .sucuriscan-sidebar{width:100%}.sucuriscan-wrap .sucuriscan-sidebar{margin-top:20px}.sucuriscan-wrap .sucuriscan-footer .sucuriscan-logo{display:table;margin:0 auto}}@media(max-width:920px){.sucuriscan-wrap .sucuriscan-navbar{padding-left:0;padding-right:0}.sucuriscan-wrap .sucuriscan-navbar .nav-tab{display:block;line-height:20px;margin:0}.sucuriscan-wrap .sucuriscan-navbar .nav-tab:last-child{border-bottom:1px solid #ccc}.wp-core-ui .sucuriscan-review-hero,.wp-core-ui .button.sucuriscan-review-hero{top:0;right:0;display:block;width:100%;margin:10px 0}}.sucuriscan-maincontent #poststuff{min-width:initial;padding-top:0}.sucuriscan-maincontent .widefat tbody th.check-column{padding:6px 0 3px 0}.sucuriscan-maincontent .hardening-box .primary-secondary{margin:0 0 0 10px}.sucuriscan-maincontent hr{border:0;border-top:1px solid #999}.sucuriscan-maincontent table td>table{background:#fff}.sucuriscan-maincontent table td>table th{padding:4px 8px}
1
+ .sucuriscan-malware-payload,.sucuriscan-request-summary td+td,.sucuriscan-wraptext{word-break:break-all}.sucuriscan-wrap *,.sucuriscan-wrap :after,.sucuriscan-wrap :before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.sucuriscan-clearfix:after,.sucuriscan-clearfix:before{display:table;content:' '}.sucuriscan-clearfix:after{clear:both}.sucuriscan-hidden{display:none!important}.sucuriscan-opacity{opacity:.6}.sucuriscan-monospace{font-family:Menlo,Monaco,monospace,courier}.sucuriscan-ellipsis{overflow:hidden;display:inline-block;white-space:nowrap;text-overflow:ellipsis}.sucuriscan-pull-left{float:left}.sucuriscan-pull-right{float:right}.sucuriscan-list li{list-style:disc;margin:0 0 5px 15px}.sucuriscan-gradient,.sucuriscan-leftside #poststuff h3,.sucuriscan-maincontent .sucuriscan-table tr>th,.sucuriscan-modal-header{background-color:#f1f1f1;background-image:-webkit-gradient(linear,left top,left bottom,from(#f9f9f9),to(#ececec));background-image:-webkit-linear-gradient(top,#f9f9f9,#ececec);background-image:-moz-linear-gradient(top,#f9f9f9,#ececec);background-image:-ms-linear-gradient(top,#f9f9f9,#ececec);background-image:-o-linear-gradient(top,#f9f9f9,#ececec);background-image:linear-gradient(top,#f9f9f9,#ececec);filter:"progid: DXImageTransform.Microsoft.Gradient(startColorstr=#f9f9f9, endColorstr=#ececec)";-ms-filter:"progid: DXImageTransform.Microsoft.Gradient(startColorstr=#f9f9f9, endColorstr=#ececec)"}.wp-core-ui .button-success,.wp-core-ui .button-success.focus,.wp-core-ui .button-success.hover,.wp-core-ui .button-success:focus,.wp-core-ui .button-success:hover,.wp-core-ui .button.button-success.button-hero{-webkit-box-shadow:0 1px 0 #109900;-moz-box-shadow:0 1px 0 #109900;box-shadow:0 1px 0 #109900}.wp-core-ui .button-success,.wp-core-ui .button-success.focus,.wp-core-ui .button-success.hover,.wp-core-ui .button-success:focus,.wp-core-ui .button-success:hover{background:#8dcd5a;border-color:#48a325;box-shadow:0 1px 0 #109900;text-shadow:0 -1px 1px #109900,1px 0 1px #109900,0 1px 1px #109900,-1px 0 1px #109900}.wp-core-ui .button-success.focus,.wp-core-ui .button-success.hover,.wp-core-ui .button-success:focus,.wp-core-ui .button-success:hover{background:#69be48}.wp-core-ui .button-success.focus,.wp-core-ui .button-success:focus{border-color:#23500e}.wp-core-ui .button-success.active,.wp-core-ui .button-success.active:focus,.wp-core-ui .button-success.active:hover,.wp-core-ui .button-success:active{background:#47a61b;border-color:#358400}.wp-core-ui .button-success-disabled,.wp-core-ui .button-success.disabled,.wp-core-ui .button-success:disabled,.wp-core-ui .button-success[disabled]{color:#b2e794!important;background:#74ba29!important;border-color:#3f7f1b!important}.wp-core-ui .button-danger,.wp-core-ui .button-danger.focus,.wp-core-ui .button-danger.hover,.wp-core-ui .button-danger:focus,.wp-core-ui .button-danger:hover,.wp-core-ui .button.button-danger.button-hero{-webkit-box-shadow:0 1px 0 #99000e;-moz-box-shadow:0 1px 0 #99000e;box-shadow:0 1px 0 #99000e}.wp-core-ui .button-danger,.wp-core-ui .button-danger.focus,.wp-core-ui .button-danger.hover,.wp-core-ui .button-danger:focus,.wp-core-ui .button-danger:hover{background:#cd5050;border-color:#a52121;text-shadow:0 -1px 1px #99000e,1px 0 1px #99000e,0 1px 1px #99000e,-1px 0 1px #99000e}.wp-core-ui .button-danger.focus,.wp-core-ui .button-danger.hover,.wp-core-ui .button-danger:focus,.wp-core-ui .button-danger:hover{background:#be4242}.wp-core-ui .button-danger.focus,.wp-core-ui .button-danger:focus{border-color:#500e0e}.wp-core-ui .button-danger.active,.wp-core-ui .button-danger.active:focus,.wp-core-ui .button-danger.active:hover,.wp-core-ui .button-danger:active{background:#a61b1b;border-color:#840000}.wp-core-ui .button-danger-disabled,.wp-core-ui .button-danger.disabled,.wp-core-ui .button-danger:disabled,.wp-core-ui .button-danger[disabled]{color:#e79494!important;background:#ba2929!important;border-color:#7f1b1b!important}.wp-core-ui .sucuriscan-btnblock{display:block;width:100%;text-align:center}.sucuriscan-overlay{position:fixed;top:0;left:0;bottom:0;right:0;z-index:9990;background:#666;background:rgba(0,0,0,.5)}.sucuriscan-modal{position:absolute;top:25px;left:15%;z-index:9990;width:55%}.sucuriscan-modal-outside{position:relative;left:0;border:1px solid #ddd}.sucuriscan-modal-inside{background:#fff;padding:20px}.sucuriscan-modal-header{padding:0;border-bottom:1px solid #ddd}#poststuff h3.sucuriscan-modal-title,.sucuriscan-leftside #poststuff h3.sucuriscan-modal-title,.sucuriscan-modal-header .sucuriscan-modal-title{margin:0 0 0 10px;padding:0;float:left;line-height:38px;border-bottom:0}.sucuriscan-modal-header .sucuriscan-modal-logo{display:inline-block;float:left;margin-top:8px;margin-left:18px}.sucuriscan-modal-header .sucuriscan-modal-logo img{height:22px}.sucuriscan-modal-close{display:inline-block;position:absolute;top:0;right:0;font-size:16px;font-weight:700;text-decoration:none;line-height:38px;padding:0 15px;border-left:1px solid #ddd}.sucuriscan-modal-inside p:first-child{margin-top:0}.postbox .inside p:last-child,.sucuriscan-modal-inside p:last-child{margin-bottom:0}.sucuriscan-label,.sucuriscan-label-danger,.sucuriscan-label-default,.sucuriscan-label-error,.sucuriscan-label-info,.sucuriscan-label-notice,.sucuriscan-label-primary,.sucuriscan-label-success,.sucuriscan-label-unknown,.sucuriscan-label-warning{display:inline;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;padding:.2em .6em .3em;border-radius:.25em}.sucuriscan-label-default,.sucuriscan-label-unknown{background:#777}.sucuriscan-label-danger,.sucuriscan-label-error{background:#d9534f}.sucuriscan-label-info,.sucuriscan-label-notice{background:#5bc0de}.sucuriscan-label-warning{background:#f0ad4e}.sucuriscan-label-success{background:#5cb85c}.sucuriscan-label-primary{background:#428bca}.sucuriscan-wrap{margin-top:20px}.sucuriscan-wrap .sucuriscan-maincontent{margin:20px 0}.sucuriscan-wrap .sucuriscan-leftside{width:73.5%;float:left}.sucuriscan-wrap .sucuriscan-onecolumn{width:100%}.sucuriscan-wrap .sucuriscan-sidebar{width:25%;float:right}.sucuriscan-wrap #warnings_hook{line-height:normal;padding:0}.sucuriscan-wrap .sucuriscan-navbar{padding-top:20px;padding-left:6px}.sucuriscan-wrap .sucuriscan-navbar .nav-tab{margin-right:0}.sucuriscan-footer,.sucuriscan-header{position:relative;min-width:255px;background:#333;margin:0;padding:10px;border-radius:4px}.sucuriscan-footer .sucuriscan-help{color:#fff;float:right;text-align:right}.sucuriscan-footer .sucuriscan-help p{line-height:38px;margin:0 10px 0 0;padding:0}.sucuriscan-wrap .sucuriscan-footer h2,.sucuriscan-wrap .sucuriscan-header h2,.sucuriscan-wrap .sucuriscan-logo{float:left;margin:0;padding:0}.sucuriscan-wrap .sucuriscan-logo{display:inline-block}.sucuriscan-wrap .sucuriscan-logo img{display:block}.sucuriscan-wrap .sucuriscan-footer h2,.sucuriscan-wrap .sucuriscan-header h2{color:#fff;line-height:38px;margin-left:10px;text-shadow:#000 0 1px 0}.sucuriscan-leftside #poststuff .postbox:last-child{margin-bottom:0}.sucuriscan-leftside #poststuff .postbox h3{margin:0;padding:10px;border-bottom:1px solid #ddd}.sucuriscan-maincontent abbr{text-decoration:underline;cursor:help}.wrap div.sucuriscan-setup-notice{background:#bbe8f5;margin:0 0 20px;padding:0;border:1px solid #bbb;border-radius:3px;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.wrap div.sucuriscan-setup-notice .sucuriscan-setup-image,.wrap div.sucuriscan-setup-notice .sucuriscan-setup-image img{border-radius:3px 0 0 3px}.wrap div.sucuriscan-setup-notice .sucuriscan-setup-image{background:#333;margin:-1px 0 -1px -1px;padding:7px 10px;border-right:1px solid transparent}.wrap div.sucuriscan-setup-notice .sucuriscan-setup-form{padding:4px 4px 4px 0}.wrap div.sucuriscan-setup-notice p{font-size:14px;line-height:20px;margin:0 0 0 10px;padding:7px 0}.wrap div.sucuriscan-setup-notice,.wrap div.sucuriscan-setup-notice .sucuriscan-setup-image{border-color:#4393ac}.wp-core-ui .button.sucuriscan-review-hero,.wp-core-ui .sucuriscan-review-hero{height:initial;line-height:36px;float:right;padding:0 20px}.sucuriscan-input-group>label{display:inline-block;border:1px solid #ddd;border-right:0;line-height:26px;float:left;padding:0 10px;background:#eee}.sucuriscan-input-group>input[type=text]{margin:0;padding-bottom:4px}.sucuriscan-input-group>select{vertical-align:initial;margin:0}.sucuriscan-table-setup td{vertical-align:top}.sucuriscan-table-setup .sucuriscan-description{font-size:12px;margin-top:10px}.sucuriscan-dismiss-setup{font-size:10px;line-height:28px}.sucuriscan-maincontent .sucuriscan-table{margin-top:12px}.sucuriscan-maincontent .sucuriscan-table tr>th{border-top:1px solid #e5e5e5;border-bottom:1px solid #e5e5e5}.sucuriscan-maincontent .sucuriscan-table tr:first-child th{border-top:0}.sucuriscan-maincontent .sucuriscan-table td.check-column{padding:8px 10px}.sucuriscan-maincontent .sucuriscan-striped-table tr:nth-child(even){background:#f5f5f5}.sucuriscan-table-double-title tr:first-child th,.sucuriscan-table-quad-title tr:first-child th,.sucuriscan-table-quad-title tr:first-child+tr th,.sucuriscan-table-quad-title tr:first-child+tr+tr th,.sucuriscan-table-triple-title tr:first-child th,.sucuriscan-table-triple-title tr:first-child+tr th{border-bottom:0}.sucuriscan-table-description{border-left-width:1px!important;box-shadow:none}.sucuriscan-table-description .inside{border-bottom:0!important}.widefat td.td-with-button{text-align:right;padding:3px 10px}.widefat td.td-with-button button{min-width:90px}.widefat td.td-with-button select{height:initial;line-height:initial;vertical-align:top;margin:0;padding:2px 0 3px}.widefat th.check-column{line-height:36px;padding:0}.widefat th.check-column input[type=checkbox]{margin:1px 0 0 10px}.sucuriscan-list-as-table{background:#fff;border:1px solid #e5e5e5}.sucuriscan-list-as-table li{line-height:30px;word-break:break-all;margin:0;padding:0 10px}.sucuriscan-list-as-table li:nth-child(odd){background:#f5f5f5}.sucuriscan-list-as-table-scrollable{height:300px;overflow:hidden;overflow-y:scroll}.sucuriscan-maincontent .thead-with-button{padding:5px 5px 5px 10px}.sucuriscan-maincontent .thead-with-button>span{display:inline-block;line-height:28px}.sucuriscan-maincontent .thead-with-button .input-text{line-height:26px}.sucuriscan-maincontent .thead-with-button select{margin:0;padding:0}.sucuriscan-maincontent .thead-topright-action{display:inline-block;float:right}.sucuriscan-ad{color:#fff;padding:20px;margin-bottom:20px}.sucuriscan-ad .sucuriscan-ad-btn,.sucuriscan-ad h3,.sucuriscan-ad h4{font-family:Arial,Helvetica,sans-serif;color:#fff;margin:0}.sucuriscan-ad h3{font-size:18px;font-weight:300}.sucuriscan-ad h4{font-size:22px;font-weight:700;margin-top:10px}.sucuriscan-ad .sucuriscan-ad-btn{display:block;font-size:13px;font-weight:700;text-align:center;text-decoration:none;text-transform:uppercase;margin-top:20px;padding:5px;border-radius:20px}.sucuriscan-ad .sucuriscan-ad-footer{margin-top:20px;margin-bottom:0}.sucuriscan-ad .sucuriscan-ad-footer ul{margin:0}.sucuriscan-ad .sucuriscan-ad-footer li{font-size:12px;color:#fff;list-style:disc;margin:0 0 0 16px}.sucuriscan-ad .sucuriscan-ad-footer li.featured{color:#fde44c}.sucuriscan-scanner-video{width:100%;background:#fff;border:1px solid #ddd}.sucuriscan-sidebar .sucuriscan-supportbtn{width:100%;height:initial;text-align:center;line-height:36px;margin-top:15px;padding:0}.wp-core-ui .sucuriscan-hide-ads{display:block;color:#666;font-size:11px;text-decoration:underline;margin-top:15px;padding:0}.wp-core-ui .sucuriscan-hide-ads:focus{color:#000;box-shadow:none}.sucuriscan-ad-firewall{background:#606e77}.sucuriscan-ad-firewall .sucuriscan-ad-btn{background:#606e77;border:1px solid #fff}.sucuriscan-ad-firewall .sucuriscan-ad-btn:hover{background:#85929b}.sucuriscan-ad-antivirus{background:#04833e;padding-bottom:0}.sucuriscan-ad-antivirus .sucuriscan-ad-website{display:block;text-decoration:none;margin-top:20px}.sucuriscan-ad-antivirus .sucuriscan-ad-website img{display:block;max-width:100%}.sucuriscan-ad-antivirus .sucuriscan-ad-btn{background-color:#e8840a;background-image:-webkit-gradient(linear,left top,left bottom,from(#e8840a),to(#ef7f02));background-image:-webkit-linear-gradient(top,#e8840a,#ef7f02);background-image:-moz-linear-gradient(top,#e8840a,#ef7f02);background-image:-ms-linear-gradient(top,#e8840a,#ef7f02);background-image:-o-linear-gradient(top,#e8840a,#ef7f02);background-image:linear-gradient(top,#e8840a,#ef7f02);filter:"progid: DXImageTransform.Microsoft.Gradient(startColorstr=#e8840a, endColorstr=#ef7f02)";-ms-filter:"progid: DXImageTransform.Microsoft.Gradient(startColorstr=#e8840a, endColorstr=#ef7f02)";box-shadow:inset 0 1px 1px #eaac3a;border:1px solid #d17301}div.sucuriscan-alert{position:relative;margin:0 0 20px}div.sucuriscan-alert>a.close{position:absolute;top:10px;right:10px;font-size:18px;font-weight:700;text-decoration:none}.sucuriscan-inline-alert,.sucuriscan-inline-alert-error,.sucuriscan-inline-alert-info,.sucuriscan-inline-alert-updated,.sucuriscan-inline-alert-warning{background:#fff;box-shadow:0 1px 1px 0 rgba(0,0,0,.1);padding:0;border-left:4px solid #ddd}.sucuriscan-inline-alert-error>p,.sucuriscan-inline-alert-info>p,.sucuriscan-inline-alert-updated>p,.sucuriscan-inline-alert-warning>p,.sucuriscan-inline-alert>p{margin:0;padding:8px 12px;border:1px solid #ddd;border-left:0}.sucuriscan-inline-alert,.sucuriscan-inline-alert-error,.sucuriscan-inline-alert-info,.sucuriscan-inline-alert-updated,.sucuriscan-inline-alert-warning{margin-bottom:10px}.postbox .inside .sucuriscan-inline-alert-error:last-child,.postbox .inside .sucuriscan-inline-alert-info:last-child,.postbox .inside .sucuriscan-inline-alert-updated:last-child,.postbox .inside .sucuriscan-inline-alert-warning:last-child,.postbox .inside .sucuriscan-inline-alert:last-child,.sucuriscan-tabs>ul li{margin-bottom:0}.sucuriscan-inline-alert-updated{border-left-color:#7ad03a}.sucuriscan-inline-alert-warning{border-left-color:#ffba00}.sucuriscan-inline-alert-error{border-left-color:#dd3d36}.sucuriscan-inline-alert-info{border-left-color:#2ea2cc}.sucuriscan-tabs>ul{margin:0}.sucuriscan-tabs>ul li,.sucuriscan-tabs>ul li>a{display:inline-block}.sucuriscan-tabs>ul li>a{background:#e5e5e5;font-size:13px;font-weight:700;color:#333;line-height:38px;text-decoration:none;padding:0 10px}.sucuriscan-tabs>ul li>a.sucuriscan-tab-active{background:#fff;border:1px solid #e1e1e1;border-bottom:0}.sucuriscan-tabs>ul li.sucuriscan-red-tab a{background:#ff8a83;color:#fff}.sucuriscan-tabs>ul li.sucuriscan-red-tab a.sucuriscan-tab-active{background:#dd3d36;border-color:#dd3d36}.sucuriscan-maincontent .sucuriscan-tab-containers>div>#poststuff,.sucuriscan-maincontent .sucuriscan-tab-containers>div>table{margin-top:0}.sucuriscan-getapi-div{background:#fff;margin:0 0 20px;border:1px solid #e5e5e5;border-radius:3px}.sucuriscan-getapi-div p{margin:0;padding:10px}.sucuriscan-getapi-form button.button-primary{width:100%;height:initial;line-height:30px;margin:0 0 -1px;padding:0;border-radius:0 0 3px 3px}.sucuriscan-malwarescan-message{margin-bottom:20px!important}.sucuriscan-loading{background:#fff;text-align:center;padding:30px 30px 15px;border:1px solid #ddd;border-radius:4px}.sucuriscan-loading h3,.sucuriscan-loading p{margin:0;padding:0}.sucuriscan-loading .title{font-size:28px;margin-bottom:10px}.sucuriscan-loading .description{font-size:16px}.sucuriscan-sitelogo{width:190px;height:100px;background:url(https://sitecheck.sucuri.net/images/sucuri-sprite.png) no-repeat;margin:0 auto}.sucuriscan-sitecheck-form{margin:20px 0 0}.sucuriscan-sitecheck-form .button.button-hero{padding:0 46px}.sucuriscan-loading .sucuriscan-sitecheck-disclaimer{text-align:justify;padding-top:20px;border-top:1px solid #ddd}.sucuriscan-auditlogs .sucuriscan-maxper-page,.sucuriscan-scanner-results .sucuriscan-malware-link{text-align:right}.sucuriscan-loading .sucuriscan-sitecheck-disclaimer p{font-size:10px}.sucuriscan-maincontent .sucuriscan-border{border:0;border-left:4px solid #ddd}.sucuriscan-maincontent .sucuriscan-border>.inside,.sucuriscan-maincontent .sucuriscan-border>h3{border-top:1px solid #e5e5e5;border-right:1px solid #e5e5e5}.sucuriscan-maincontent .sucuriscan-border>h3{border-bottom:0}.sucuriscan-maincontent .sucuriscan-border>.inside{margin-top:0!important;border-bottom:1px solid #ddd}.sucuriscan-maincontent .sucuriscan-border-good,.sucuriscan-maincontent .sucuriscan-border-success{border-left-color:#7ad03a}.sucuriscan-maincontent .sucuriscan-border-bad,.sucuriscan-maincontent .sucuriscan-border-danger{border-left-color:#dd3d36}.sucuriscan-maincontent .sucuriscan-border-info{border-left-color:#2ea2cc}.sucuriscan-maincontent .sucuriscan-cleanup-btn{margin:20px 0 0}.sucuriscan-scanner-results .sucuriscan-scanner-details tr:nth-child(even),.sucuriscan-scanner-results .sucuriscan-scanner-links tr:nth-child(even){background:#f5f5f5}.sucuriscan-scanner-results td.sucuriscan-border-bad{border-left-width:4px;border-left-style:solid}.sucuriscan-scanner-results .sucuriscan-malware-link a:hover{color:#fff}.sucuriscan-malware-payload{background:#f5f5f5;margin:-2px -15px -15px;padding:15px}.sucuriscan-maincontent .sucuriscan-auditlogs,.sucuriscan-maincontent .sucuriscan-corefiles,.sucuriscan-maincontent .sucuriscan-wordpress-outdated{margin-top:0;margin-bottom:20px}.sucuriscan-auditlogs .sucuriscan-list-as-table,.sucuriscan-maincontent .sucuriscan-auditlogs{margin-bottom:0}.sucuriscan-auditlogs .sucuriscan-label{display:inline-block;width:18px;text-transform:uppercase;line-height:13px;cursor:pointer;border-radius:50%}.sucuriscan-auditlogs .sucuriscan-auditlog-success,.sucuriscan-label-added{background:#5cb85c}.sucuriscan-auditlogs .sucuriscan-auditlog-debug{background:#c690ec}.sucuriscan-auditlogs .sucuriscan-auditlog-info{background:#5bc0de}.sucuriscan-auditlogs .sucuriscan-auditlog-notice{background:#428bca}.sucuriscan-auditlogs .sucuriscan-auditlog-warning,.sucuriscan-label-modified{background:#f0ad4e}.sucuriscan-auditlogs .sucuriscan-auditlog-error,.sucuriscan-label-removed{background:#f27d7d}.sucuriscan-auditlogs .sucuriscan-auditlog-critical{background:#000}.sucuriscan-maincontent .sucuriscan-audit-report{border-left-width:1px}.sucuriscan-audit-report .sucuriscan-report-row{margin-bottom:10px}.sucuriscan-audit-report .sucuriscan-report-row:last-child,.sucuriscan-maincontent .sucuriscan-corefiles{margin-bottom:0}.sucuriscan-audit-report .sucuriscan-report-chart{width:49%;border:1px solid #ddd}.sucuriscan-audit-report .sucuriscan-report-chart h4,.sucuriscan-audit-report .sucuriscan-report-chart h5{font-weight:400;text-align:center;margin:0}.sucuriscan-firewall-accesslog .sucuriscan-accesslog-label,.sucuriscan-request-summary tr td:first-child{font-weight:700}.sucuriscan-audit-report .sucuriscan-report-chart h4{font-size:18px;margin-top:10px}.sucuriscan-audit-report .sucuriscan-report-chart h5{font-size:12px;margin-top:5px}.sucuriscan-firewall-auditlogs .sucuriscan-denial-type,.sucuriscan-request-summary td{font-size:14px}.sucuriscan-maincontent .sucuriscan-audit-report .sucuriscan-inline-alert-info{margin-top:10px}.sucuriscan-status-type{display:inline-block;width:20px;background:#ddd;text-align:center;text-transform:uppercase;margin-right:10px;padding:0 3px;border:1px solid transparent;border-radius:3px}.sucuriscan-maincontent .sucuriscan-corefiles .sucuriscan-label{text-transform:capitalize}.sucuriscan-maincontent .sucuriscan-ignoredfiles{margin-top:0}.sucuriscan-ignore-file form{padding:0 10px 10px;border-bottom:1px solid #ddd;border-right:1px solid #ddd}.sucuriscan-ignore-file p{border-bottom:none}.sucuriscan-ignore-file-input{width:80%}.sucuriscan-ignore-file-button{width:18%}.sucuriscan-maincontent .sucuriscan-modifiedfiles .sucuriscan-ellipsis{width:100px}.sucuriscan-maincontent .sucuriscan-firewall-apikey{margin-bottom:10px}.sucuriscan-firewall-settings .sucuriscan-list-as-table{margin-top:4px;margin-bottom:4px}.sucuriscan-firewall-auditlogs .thead-with-button .button{width:65px}.sucuriscan-firewall-auditlogs .thead-with-button .input-text,.sucuriscan-firewall-auditlogs .thead-with-button select{width:250px}.sucuriscan-firewall-auditlogs .sucuriscan-denial-type-date{font-style:italic;color:#999}.sucuriscan-firewall-auditlogs .sucuriscan-alert,.wrap .sucuriscan-firewall-auditlogs .error,.wrap .sucuriscan-firewall-auditlogs .updated{background:#eee;border:1px solid #ddd;border-left-width:4px;margin:10px}.sucuriscan-firewall-accesslog .sucuriscan-accesslog-origin img{margin-right:6px}.sucuriscan-firewall-accesslog .sucuriscan-accesslog-datetime,.sucuriscan-firewall-accesslog .sucuriscan-accesslog-origin,.sucuriscan-firewall-accesslog .sucuriscan-accesslog-referer,.sucuriscan-firewall-accesslog .sucuriscan-accesslog-request,.sucuriscan-firewall-accesslog .sucuriscan-accesslog-signature,.sucuriscan-firewall-accesslog .sucuriscan-accesslog-target,.sucuriscan-firewall-accesslog .sucuriscan-accesslog-useragent{display:block;padding-left:30px}.sucuriscan-firewall-accesslog .sucuriscan-accesslog-origin{padding-left:0}.sucuriscan-request-summary{margin:-3px -15px -15px}.sucuriscan-hstatus{position:relative;margin:0 -12px;padding:10px 12px;border:1px solid transparent}.sucuriscan-hstatus-0{background-color:#f2dede;color:#a94442;border-color:#ebccd1}.sucuriscan-hstatus-1{background-color:#dff0d8;color:#3c763d;border-color:#d6e9c6}.sucuriscan-hstatus-2{background-color:#dee4f2;color:#4263a9;border-color:#ccd0eb}.sucuriscan-hstatus .button-primary,.sucuriscan-hstatus .button-secondary{position:absolute;top:5px;right:5px}.sucuriscan-hardening .postbox .inside pre{background:#eaeaea;padding:10px}.sucuriscan-hardening-whitelist form{margin-top:15px}.sucuriscan-hardening-whitelist form label{line-height:29px;font-size:12px;background-color:#eee;padding:0 10px;display:inline-block;border:1px solid #ddd;border-right:0}.sucuriscan-hardening-whitelist form input[type=text]{margin:0;padding:5px}.sucuriscan-hardening-whitelist form select{height:initial;padding:4px;margin:0}.sucuriscan-hardening-whitelist form .button,.sucuriscan-hardening-whitelist form input[type=text],.sucuriscan-hardening-whitelist form select{margin-right:5px}.sucuriscan-maincontent .sucuriscan-table.sucuriscan-hardening-whitelist-table{margin-top:0}.sucuriscan-lastlogin-outof{font-style:italic;color:#999;margin-right:10px}.sucuriscan-admins-lastlogins .sucuriscan-ellipsis{width:170px}.sucuriscan-admins-lastlogins td{padding:4px 8px}.sucuriscan-pattern-search-inputbox{margin-top:12px}.sucuriscan-pattern-search-inputbox .input-text{width:84.7777%;line-height:30px;margin:0 6px 0 0}.sucuriscan-pattern-search-inputbox .input-button{width:14%;height:initial;line-height:35px}.sucuriscan-pattern-search .sucuriscan-cleanup-btn{margin-top:12px}.sucuriscan-pattern-search table label{color:#999}.sucuriscan-pattern-search .sucuriscan-grep-text em{color:#ea3838}.sucuriscan-about ul{margin-left:20px}.sucuriscan-about ul li{list-style:outside}.sucuriscan-about li label{font-weight:700;vertical-align:initial}.sucuriscan-apikey-registered .sucuriscan-pull-right{width:400px}.sucuriscan-apikey-registered .sucuriscan-sitelogo{background-position:0 -17px;height:83px}.sucuriscan-setup-instructions .form-table{margin-top:15px}.sucuriscan-setup-instructions .form-table td{padding:0 0 12px}.sucuriscan-setup-instructions .form-table select{max-width:400px}.sucuriscan-pagination{display:inline-block;margin:0;padding:0;border-radius:4px}.sucuriscan-pagination>li{display:inline}.c3-tooltip td>span,.sucuriscan-maincontent .sucuriscan-settings form{display:inline-block}.sucuriscan-pagination>li>a,.sucuriscan-pagination>li>span{position:relative;background:#fff;color:#428bca;line-height:1.42857143;text-decoration:none;float:left;margin-left:-1px;padding:6px 12px;border:1px solid #ddd}.sucuriscan-pagination>li:first-child>a,.sucuriscan-pagination>li:first-child>span{margin-left:0;border-radius:4px 0 0 4px}.sucuriscan-pagination>li:last-child>a,.sucuriscan-pagination>li:last-child>span{border-radius:0 4px 4px 0}.sucuriscan-pagination>li>a.sucuriscan-pagination-active,.sucuriscan-pagination>li>a:hover{background:#0074a2;color:#fff}.sucuriscan_wpconfig_keys_updated textarea{width:100%;height:250px;background:#f5f5f5;font-size:12px;resize:vertical;margin:20px 0 0}.sucuriscan-maincontent .sucuriscan-last-logins,.sucuriscan-maincontent .sucuriscan-settings{margin-top:0}.sucuriscan-maincontent .sucuriscan-last-logins .sucuriscan-ellipsis{width:150px;line-height:inherit}.sucuriscan-maincontent .sucuriscan-full-textarea{width:100%;height:400px;line-height:normal;resize:vertical;padding:10px}.sucuriscan-maincontent .sucuriscan-settings .input-text,.sucuriscan-maincontent .sucuriscan-settings select{width:220px;margin:0}.sucuriscan-maincontent .sucuriscan-infosys-htaccess .inside .sucuriscan-inline-alert-updated,.sucuriscan-maincontent .sucuriscan-monitor-fpath{margin-bottom:10px}.sucuriscan-maincontent .sucuriscan-recipient-form{margin-top:10px}.sucuriscan-maincontent .sucuriscan-settings-ignorescanning,.sucuriscan-maincontent .sucuriscan-settings-notifications,.sucuriscan-maincontent .sucuriscan-settings-trustip,.sucuriscan-maincontent .sucuriscan-wpcron-list{margin-top:0}.sucuriscan-maincontent .sucuriscan-settings-notifications .dashicons-before:before{margin-right:5px}.sucuriscan-maincontent .sucuriscan-infosys-htaccess .inside{border-bottom:1px solid #ddd!important}.sucuriscan-maincontent .sucuriscan-errorlogs .inside .sucuriscan-inline-alert-error{margin-top:10px}.sucuriscan-maincontent .sucuriscan-subject-formats{margin:0}.sucuriscan-maincontent .sucuriscan-subject-formats input[type=text]{width:40%;margin-left:10px}.sucuriscan-flag{width:16px;height:11px;background:url(../images/flags.sprite.png) no-repeat}.sucuriscan-flag-ad{background-position:-16px 0}.sucuriscan-flag-ae{background-position:-32px 0}.sucuriscan-flag-af{background-position:-48px 0}.sucuriscan-flag-ag{background-position:-64px 0}.sucuriscan-flag-ai{background-position:-80px 0}.sucuriscan-flag-al{background-position:-96px 0}.sucuriscan-flag-am{background-position:-112px 0}.sucuriscan-flag-an{background-position:-128px 0}.sucuriscan-flag-ao{background-position:-144px 0}.sucuriscan-flag-ar{background-position:-160px 0}.sucuriscan-flag-as{background-position:-176px 0}.sucuriscan-flag-at{background-position:-192px 0}.sucuriscan-flag-au{background-position:-208px 0}.sucuriscan-flag-aw{background-position:-224px 0}.sucuriscan-flag-az{background-position:-240px 0}.sucuriscan-flag-ba{background-position:0 -11px}.sucuriscan-flag-bb{background-position:-16px -11px}.sucuriscan-flag-bd{background-position:-32px -11px}.sucuriscan-flag-be{background-position:-48px -11px}.sucuriscan-flag-bf{background-position:-64px -11px}.sucuriscan-flag-bg{background-position:-80px -11px}.sucuriscan-flag-bh{background-position:-96px -11px}.sucuriscan-flag-bi{background-position:-112px -11px}.sucuriscan-flag-bj{background-position:-128px -11px}.sucuriscan-flag-bm{background-position:-144px -11px}.sucuriscan-flag-bn{background-position:-160px -11px}.sucuriscan-flag-bo{background-position:-176px -11px}.sucuriscan-flag-br{background-position:-192px -11px}.sucuriscan-flag-bs{background-position:-208px -11px}.sucuriscan-flag-bt{background-position:-224px -11px}.sucuriscan-flag-bv{background-position:-240px -11px}.sucuriscan-flag-bw{background-position:0 -22px}.sucuriscan-flag-by{background-position:-16px -22px}.sucuriscan-flag-bz{background-position:-32px -22px}.sucuriscan-flag-ca{background-position:-48px -22px}.sucuriscan-flag-catalonia{background-position:-64px -22px}.sucuriscan-flag-cd{background-position:-80px -22px}.sucuriscan-flag-cf{background-position:-96px -22px}.sucuriscan-flag-cg{background-position:-112px -22px}.sucuriscan-flag-ch{background-position:-128px -22px}.sucuriscan-flag-ci{background-position:-144px -22px}.sucuriscan-flag-ck{background-position:-160px -22px}.sucuriscan-flag-cl{background-position:-176px -22px}.sucuriscan-flag-cm{background-position:-192px -22px}.sucuriscan-flag-cn{background-position:-208px -22px}.sucuriscan-flag-co{background-position:-224px -22px}.sucuriscan-flag-cr{background-position:-240px -22px}.sucuriscan-flag-cu{background-position:0 -33px}.sucuriscan-flag-cv{background-position:-16px -33px}.sucuriscan-flag-cw{background-position:-32px -33px}.sucuriscan-flag-cy{background-position:-48px -33px}.sucuriscan-flag-cz{background-position:-64px -33px}.sucuriscan-flag-de{background-position:-80px -33px}.sucuriscan-flag-dj{background-position:-96px -33px}.sucuriscan-flag-dk{background-position:-112px -33px}.sucuriscan-flag-dm{background-position:-128px -33px}.sucuriscan-flag-do{background-position:-144px -33px}.sucuriscan-flag-dz{background-position:-160px -33px}.sucuriscan-flag-ec{background-position:-176px -33px}.sucuriscan-flag-ee{background-position:-192px -33px}.sucuriscan-flag-eg{background-position:-208px -33px}.sucuriscan-flag-eh{background-position:-224px -33px}.sucuriscan-flag-england{background-position:-240px -33px}.sucuriscan-flag-er{background-position:0 -44px}.sucuriscan-flag-es{background-position:-16px -44px}.sucuriscan-flag-et{background-position:-32px -44px}.sucuriscan-flag-eu{background-position:-48px -44px}.sucuriscan-flag-fi{background-position:-64px -44px}.sucuriscan-flag-fj{background-position:-80px -44px}.sucuriscan-flag-fk{background-position:-96px -44px}.sucuriscan-flag-fm{background-position:-112px -44px}.sucuriscan-flag-fo{background-position:-128px -44px}.sucuriscan-flag-fr{background-position:-144px -44px}.sucuriscan-flag-ga{background-position:-160px -44px}.sucuriscan-flag-gb{background-position:-176px -44px}.sucuriscan-flag-gd{background-position:-192px -44px}.sucuriscan-flag-ge{background-position:-208px -44px}.sucuriscan-flag-gf{background-position:-224px -44px}.sucuriscan-flag-gg{background-position:-240px -44px}.sucuriscan-flag-gh{background-position:0 -55px}.sucuriscan-flag-gi{background-position:-16px -55px}.sucuriscan-flag-gl{background-position:-32px -55px}.sucuriscan-flag-gm{background-position:-48px -55px}.sucuriscan-flag-gn{background-position:-64px -55px}.sucuriscan-flag-gp{background-position:-80px -55px}.sucuriscan-flag-gq{background-position:-96px -55px}.sucuriscan-flag-gr{background-position:-112px -55px}.sucuriscan-flag-gs{background-position:-128px -55px}.sucuriscan-flag-gt{background-position:-144px -55px}.sucuriscan-flag-gu{background-position:-160px -55px}.sucuriscan-flag-gw{background-position:-176px -55px}.sucuriscan-flag-gy{background-position:-192px -55px}.sucuriscan-flag-hk{background-position:-208px -55px}.sucuriscan-flag-hm{background-position:-224px -55px}.sucuriscan-flag-hn{background-position:-240px -55px}.sucuriscan-flag-hr{background-position:0 -66px}.sucuriscan-flag-ht{background-position:-16px -66px}.sucuriscan-flag-hu{background-position:-32px -66px}.sucuriscan-flag-ic{background-position:-48px -66px}.sucuriscan-flag-id{background-position:-64px -66px}.sucuriscan-flag-ie{background-position:-80px -66px}.sucuriscan-flag-il{background-position:-96px -66px}.sucuriscan-flag-im{background-position:-112px -66px}.sucuriscan-flag-in{background-position:-128px -66px}.sucuriscan-flag-io{background-position:-144px -66px}.sucuriscan-flag-iq{background-position:-160px -66px}.sucuriscan-flag-ir{background-position:-176px -66px}.sucuriscan-flag-is{background-position:-192px -66px}.sucuriscan-flag-it{background-position:-208px -66px}.sucuriscan-flag-je{background-position:-224px -66px}.sucuriscan-flag-jm{background-position:-240px -66px}.sucuriscan-flag-jo{background-position:0 -77px}.sucuriscan-flag-jp{background-position:-16px -77px}.sucuriscan-flag-ke{background-position:-32px -77px}.sucuriscan-flag-kg{background-position:-48px -77px}.sucuriscan-flag-kh{background-position:-64px -77px}.sucuriscan-flag-ki{background-position:-80px -77px}.sucuriscan-flag-km{background-position:-96px -77px}.sucuriscan-flag-kn{background-position:-112px -77px}.sucuriscan-flag-kp{background-position:-128px -77px}.sucuriscan-flag-kr{background-position:-144px -77px}.sucuriscan-flag-kurdistan{background-position:-160px -77px}.sucuriscan-flag-kw{background-position:-176px -77px}.sucuriscan-flag-ky{background-position:-192px -77px}.sucuriscan-flag-kz{background-position:-208px -77px}.sucuriscan-flag-la{background-position:-224px -77px}.sucuriscan-flag-lb{background-position:-240px -77px}.sucuriscan-flag-lc{background-position:0 -88px}.sucuriscan-flag-li{background-position:-16px -88px}.sucuriscan-flag-lk{background-position:-32px -88px}.sucuriscan-flag-lr{background-position:-48px -88px}.sucuriscan-flag-ls{background-position:-64px -88px}.sucuriscan-flag-lt{background-position:-80px -88px}.sucuriscan-flag-lu{background-position:-96px -88px}.sucuriscan-flag-lv{background-position:-112px -88px}.sucuriscan-flag-ly{background-position:-128px -88px}.sucuriscan-flag-ma{background-position:-144px -88px}.sucuriscan-flag-mc{background-position:-160px -88px}.sucuriscan-flag-md{background-position:-176px -88px}.sucuriscan-flag-me{background-position:-192px -88px}.sucuriscan-flag-mg{background-position:-208px -88px}.sucuriscan-flag-mh{background-position:-224px -88px}.sucuriscan-flag-mk{background-position:-240px -88px}.sucuriscan-flag-ml{background-position:0 -99px}.sucuriscan-flag-mm{background-position:-16px -99px}.sucuriscan-flag-mn{background-position:-32px -99px}.sucuriscan-flag-mo{background-position:-48px -99px}.sucuriscan-flag-mp{background-position:-64px -99px}.sucuriscan-flag-mq{background-position:-80px -99px}.sucuriscan-flag-mr{background-position:-96px -99px}.sucuriscan-flag-ms{background-position:-112px -99px}.sucuriscan-flag-mt{background-position:-128px -99px}.sucuriscan-flag-mu{background-position:-144px -99px}.sucuriscan-flag-mv{background-position:-160px -99px}.sucuriscan-flag-mw{background-position:-176px -99px}.sucuriscan-flag-mx{background-position:-192px -99px}.sucuriscan-flag-my{background-position:-208px -99px}.sucuriscan-flag-mz{background-position:-224px -99px}.sucuriscan-flag-na{background-position:-240px -99px}.sucuriscan-flag-nc{background-position:0 -110px}.sucuriscan-flag-ne{background-position:-16px -110px}.sucuriscan-flag-nf{background-position:-32px -110px}.sucuriscan-flag-ng{background-position:-48px -110px}.sucuriscan-flag-ni{background-position:-64px -110px}.sucuriscan-flag-nl{background-position:-80px -110px}.sucuriscan-flag-no{background-position:-96px -110px}.sucuriscan-flag-np{background-position:-112px -110px}.sucuriscan-flag-nr{background-position:-128px -110px}.sucuriscan-flag-nu{background-position:-144px -110px}.sucuriscan-flag-nz{background-position:-160px -110px}.sucuriscan-flag-om{background-position:-176px -110px}.sucuriscan-flag-pa{background-position:-192px -110px}.sucuriscan-flag-pe{background-position:-208px -110px}.sucuriscan-flag-pf{background-position:-224px -110px}.sucuriscan-flag-pg{background-position:-240px -110px}.sucuriscan-flag-ph{background-position:0 -121px}.sucuriscan-flag-pk{background-position:-16px -121px}.sucuriscan-flag-pl{background-position:-32px -121px}.sucuriscan-flag-pm{background-position:-48px -121px}.sucuriscan-flag-pn{background-position:-64px -121px}.sucuriscan-flag-pr{background-position:-80px -121px}.sucuriscan-flag-ps{background-position:-96px -121px}.sucuriscan-flag-pt{background-position:-112px -121px}.sucuriscan-flag-pw{background-position:-128px -121px}.sucuriscan-flag-py{background-position:-144px -121px}.sucuriscan-flag-qa{background-position:-160px -121px}.sucuriscan-flag-re{background-position:-176px -121px}.sucuriscan-flag-ro{background-position:-192px -121px}.sucuriscan-flag-rs{background-position:-208px -121px}.sucuriscan-flag-ru{background-position:-224px -121px}.sucuriscan-flag-rw{background-position:-240px -121px}.sucuriscan-flag-sa{background-position:0 -132px}.sucuriscan-flag-sb{background-position:-16px -132px}.sucuriscan-flag-sc{background-position:-32px -132px}.sucuriscan-flag-scotland{background-position:-48px -132px}.sucuriscan-flag-sd{background-position:-64px -132px}.sucuriscan-flag-se{background-position:-80px -132px}.sucuriscan-flag-sg{background-position:-96px -132px}.sucuriscan-flag-sh{background-position:-112px -132px}.sucuriscan-flag-si{background-position:-128px -132px}.sucuriscan-flag-sk{background-position:-144px -132px}.sucuriscan-flag-sl{background-position:-160px -132px}.sucuriscan-flag-sm{background-position:-176px -132px}.sucuriscan-flag-sn{background-position:-192px -132px}.sucuriscan-flag-so{background-position:-208px -132px}.sucuriscan-flag-somaliland{background-position:-224px -132px}.sucuriscan-flag-sr{background-position:-240px -132px}.sucuriscan-flag-ss{background-position:0 -143px}.sucuriscan-flag-st{background-position:-16px -143px}.sucuriscan-flag-sv{background-position:-32px -143px}.sucuriscan-flag-sx{background-position:-48px -143px}.sucuriscan-flag-sy{background-position:-64px -143px}.sucuriscan-flag-sz{background-position:-80px -143px}.sucuriscan-flag-tc{background-position:-96px -143px}.sucuriscan-flag-td{background-position:-112px -143px}.sucuriscan-flag-tf{background-position:-128px -143px}.sucuriscan-flag-tg{background-position:-144px -143px}.sucuriscan-flag-th{background-position:-160px -143px}.sucuriscan-flag-tj{background-position:-176px -143px}.sucuriscan-flag-tk{background-position:-192px -143px}.sucuriscan-flag-tl{background-position:-208px -143px}.sucuriscan-flag-tm{background-position:-224px -143px}.sucuriscan-flag-tn{background-position:-240px -143px}.sucuriscan-flag-to{background-position:0 -154px}.sucuriscan-flag-tr{background-position:-16px -154px}.sucuriscan-flag-tt{background-position:-32px -154px}.sucuriscan-flag-tv{background-position:-48px -154px}.sucuriscan-flag-tw{background-position:-64px -154px}.sucuriscan-flag-tz{background-position:-80px -154px}.sucuriscan-flag-ua{background-position:-96px -154px}.sucuriscan-flag-ug{background-position:-112px -154px}.sucuriscan-flag-um{background-position:-128px -154px}.sucuriscan-flag-us{background-position:-144px -154px}.sucuriscan-flag-uy{background-position:-160px -154px}.sucuriscan-flag-uz{background-position:-176px -154px}.sucuriscan-flag-va{background-position:-192px -154px}.sucuriscan-flag-vc{background-position:-208px -154px}.sucuriscan-flag-ve{background-position:-224px -154px}.sucuriscan-flag-vg{background-position:-240px -154px}.sucuriscan-flag-vi{background-position:0 -165px}.sucuriscan-flag-vn{background-position:-16px -165px}.sucuriscan-flag-vu{background-position:-32px -165px}.sucuriscan-flag-wales{background-position:-48px -165px}.sucuriscan-flag-wf{background-position:-64px -165px}.sucuriscan-flag-ws{background-position:-80px -165px}.sucuriscan-flag-ye{background-position:-96px -165px}.sucuriscan-flag-yt{background-position:-112px -165px}.sucuriscan-flag-za{background-position:-128px -165px}.sucuriscan-flag-zanzibar{background-position:-144px -165px}.sucuriscan-flag-zm{background-position:-160px -165px}.sucuriscan-flag-zw{background-position:-176px -165px}.c3 svg{font:10px sans-serif}.c3 line,.c3 path{fill:none;stroke:#000}.c3 text{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#aaa}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke-dasharray:3 3}.c3-text.c3-empty{fill:gray;font-size:2em}.c3-line{stroke-width:1px}.c3-circle._expanded_{stroke-width:1px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:2px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:.75}.c3-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3!important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item{font-size:12px}.c3-legend-item-hidden{opacity:.15}.c3-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;background-color:#fff;empty-cells:show;-webkit-box-shadow:7px 7px 12px -9px #777;-moz-box-shadow:7px 7px 12px -9px #777;box-shadow:7px 7px 12px -9px #777}.c3-tooltip tr{border:1px solid #CCC}.c3-tooltip th{background-color:#aaa;font-size:14px;padding:2px 5px;text-align:left;color:#FFF}.c3-tooltip td{font-size:13px;padding:3px 6px;background-color:#fff;border-left:1px dotted #999}.c3-tooltip td>span{width:10px;height:10px;margin-right:6px}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.2}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:none}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max,.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000}@media (max-width:510px){.wp-core-ui .button.sucuriscan-review-hero,.wp-core-ui .sucuriscan-review-hero{display:none}}@media (max-width:620px){.sucuriscan-tabs>ul li,.sucuriscan-tabs>ul li>a{display:block}.sucuriscan-getapi-form button.button-primary{line-height:40px}}@media (max-width:768px){.sucuriscan-wrap .sucuriscan-footer .sucuriscan-help,.sucuriscan-wrap .sucuriscan-footer .sucuriscan-logo,.sucuriscan-wrap .sucuriscan-leftside,.sucuriscan-wrap .sucuriscan-sidebar{float:none}.sucuriscan-wrap .sucuriscan-leftside,.sucuriscan-wrap .sucuriscan-sidebar{width:100%}.sucuriscan-wrap .sucuriscan-sidebar{margin-top:20px}.sucuriscan-wrap .sucuriscan-footer .sucuriscan-logo{display:table;margin:0 auto}}@media (max-width:920px){.sucuriscan-wrap .sucuriscan-navbar{padding-left:0;padding-right:0}.sucuriscan-wrap .sucuriscan-navbar .nav-tab{display:block;line-height:20px;margin:0}.sucuriscan-wrap .sucuriscan-navbar .nav-tab:last-child{border-bottom:1px solid #ccc}}@media (min-width:600px) and (max-width:1060px){.sucuriscan-wrap .sucuriscan-leftside,.sucuriscan-wrap .sucuriscan-sidebar{width:initial;float:none}.sucuriscan-wrap .sucuriscan-sidebar{margin-top:20px}.sucuriscan-wrap .sucuriscan-sidebar>div{width:49%;float:left;min-height:339px}.sucuriscan-wrap .sucuriscan-sidebar .sucuriscan-ad-antivirus{margin-left:2%}.sucuriscan-wrap .sucuriscan-scanner-video{height:450px}}.sucuriscan-maincontent #poststuff{min-width:initial;padding-top:0}.sucuriscan-maincontent .widefat tbody th.check-column{padding:6px 0 3px}.sucuriscan-maincontent .hardening-box .primary-secondary{margin:0 0 0 10px}.sucuriscan-maincontent hr{border:none;border-top:1px solid #999}.sucuriscan-maincontent table td>table{background:#fff}.sucuriscan-maincontent table td>table th{padding:4px 8px}
inc/images/blank.png ADDED
Binary file
inc/images/flags.sprite.png ADDED
Binary file
inc/js/sucuri-scanner.min.js CHANGED
@@ -1 +1 @@
1
- function sucuriscan_alert_close(b){var a=document.getElementById("sucuriscan-alert-"+b);a.parentNode.removeChild(a)}jQuery(document).ready(function(c){c(".sucuriscan-modal-btn").on("click",function(e){e.preventDefault();var f=c(this).data("modalid");c("div."+f).removeClass("sucuriscan-hidden")});c(".sucuriscan-overlay, .sucuriscan-modal-close").on("click",function(e){e.preventDefault();c(".sucuriscan-overlay").addClass("sucuriscan-hidden");c(".sucuriscan-modal").addClass("sucuriscan-hidden")});if(c(".sucuriscan-tabs").length){var d="sucuriscan-hidden";var b="sucuriscan-tab-active";var a=location.href.split("#")[1];c(".sucuriscan-tabs > ul a").on("click",function(k){k.preventDefault();var h=c(this);var j=h.data("tabname");var f=c(".sucuriscan-tab-containers > #sucuriscan-"+j);if(f.length){var g=location.href.replace(location.hash,"");var i=g+"#"+j;window.history.pushState({},document.title,i);c(".sucuriscan-tabs > ul a").removeClass(b);c(".sucuriscan-tab-containers > div").addClass(d);h.addClass(b);f.removeClass(d)}});c(".sucuriscan-tab-containers > div").addClass(d);if(a!=undefined){c(".sucuriscan-tabs > ul li a").each(function(e,f){if(c(f).data("tabname")==a){c(f).trigger("click")}})}else{c(".sucuriscan-tabs > ul li:first-child a").trigger("click")}}c("#sucuriscan-corefiles-show").on("click",function(h){h.preventDefault();var f=c(this);var g=f.data("action");if(g=="show"){c(".sucuriscan-corefiles thead tr:last-child, .sucuriscan-corefiles tbody > tr").removeClass("sucuriscan-hidden");f.html("Hide files").data("action","hide")}else{c(".sucuriscan-corefiles thead tr:last-child, .sucuriscan-corefiles tbody > tr").addClass("sucuriscan-hidden");f.html("Show files").data("action","show")}});c("#sucuriscan_last_days").on("change",function(){c(this).closest("form").submit()})});
1
+ function sucuriscan_alert_close(b){var a=document.getElementById("sucuriscan-alert-"+b);a.parentNode.removeChild(a)}jQuery(document).ready(function(c){c(".sucuriscan-modal-btn").on("click",function(e){e.preventDefault();var f=c(this).data("modalid");c("div."+f).removeClass("sucuriscan-hidden")});c(".sucuriscan-overlay, .sucuriscan-modal-close").on("click",function(e){e.preventDefault();c(".sucuriscan-overlay").addClass("sucuriscan-hidden");c(".sucuriscan-modal").addClass("sucuriscan-hidden")});if(c(".sucuriscan-tabs").length){var d="sucuriscan-hidden";var b="sucuriscan-tab-active";var a=location.href.split("#")[1];c(".sucuriscan-tabs > ul a").on("click",function(k){k.preventDefault();var h=c(this);var j=h.data("tabname");var f=c(".sucuriscan-tab-containers > #sucuriscan-"+j);if(f.length){var g=location.href.replace(location.hash,"");var i=g+"#"+j;window.history.pushState({},document.title,i);c(".sucuriscan-tabs > ul a").removeClass(b);c(".sucuriscan-tab-containers > div").addClass(d);h.addClass(b);f.removeClass(d)}});c(".sucuriscan-tab-containers > div").addClass(d);if(a!==undefined){c(".sucuriscan-tabs > ul li a").each(function(e,f){if(c(f).data("tabname")===a){c(f).trigger("click")}})}else{c(".sucuriscan-tabs > ul li:first-child a").trigger("click")}}c("body").on("click",".sucuriscan-corefiles .manage-column :checkbox",function(e){c(".sucuriscan-corefiles tbody :checkbox").each(function(g,h){var f=c(h).is(":checked");c(h).attr("checked",!f)})})});
inc/tpl/base.html.tpl CHANGED
@@ -4,40 +4,39 @@
4
  <h2 id="warnings_hook"></h2>
5
 
6
  <div class="sucuriscan-header sucuriscan-clearfix">
7
- <a href="http://sucuri.net/signup" target="_blank" title="Sucuri Security" class="sucuriscan-logo">
8
  <img src="%%SUCURI.SucuriURL%%/inc/images/logo.png" alt="Sucuri Security" />
9
  </a>
10
  <h2>Sucuri Security %%SUCURI.PageTitle%%</h2>
 
 
11
  </div>
12
 
13
  <h2 class="nav-tab-wrapper sucuriscan-navbar">
14
- %%SUCURI.Navbar%%
15
-
16
- <a class="button button-hero button-primary sucuriscan-review-hero"
17
- href="http://goo.gl/aByqP5" target="_blank">Review Plugin</a>
18
  </h2>
19
 
20
  <div class="sucuriscan-maincontent sucuriscan-clearfix">
21
 
22
  <div class="sucuriscan-leftside sucuriscan-%%SUCURI.LayoutType%% sucuriscan-%%SUCURI.PageStyleClass%%">
23
 
24
- %%SUCURI.PageContent%%
25
 
26
  </div>
27
 
28
- %%SUCURI.PageSidebarContent%%
29
 
30
  </div>
31
 
32
  <div class="sucuriscan-footer sucuriscan-clearfix">
33
- <a href="http://sucuri.net/signup" target="_blank" title="Sucuri Security" class="sucuriscan-logo">
34
  <img src="%%SUCURI.SucuriURL%%/inc/images/logo.png" alt="Sucuri Security" />
35
  </a>
36
  <div class="sucuriscan-help">
37
  <p>
38
  If you have any questions about these checks or this plugin, contact us at
39
  <a href="mailto:info@sucuri.net">info@sucuri.net</a> or visit
40
- <a href="http://sucuri.net/" target="_blank">sucuri.net</a>
41
  </p>
42
  </div>
43
  </div>
4
  <h2 id="warnings_hook"></h2>
5
 
6
  <div class="sucuriscan-header sucuriscan-clearfix">
7
+ <a href="https://sucuri.net/signup" target="_blank" title="Sucuri Security" class="sucuriscan-logo">
8
  <img src="%%SUCURI.SucuriURL%%/inc/images/logo.png" alt="Sucuri Security" />
9
  </a>
10
  <h2>Sucuri Security %%SUCURI.PageTitle%%</h2>
11
+ <a class="button button-hero button-primary sucuriscan-review-hero"
12
+ href="https://goo.gl/aByqP5" target="_blank">Review Plugin</a>
13
  </div>
14
 
15
  <h2 class="nav-tab-wrapper sucuriscan-navbar">
16
+ %%%SUCURI.Navbar%%%
 
 
 
17
  </h2>
18
 
19
  <div class="sucuriscan-maincontent sucuriscan-clearfix">
20
 
21
  <div class="sucuriscan-leftside sucuriscan-%%SUCURI.LayoutType%% sucuriscan-%%SUCURI.PageStyleClass%%">
22
 
23
+ %%%SUCURI.PageContent%%%
24
 
25
  </div>
26
 
27
+ %%%SUCURI.PageSidebarContent%%%
28
 
29
  </div>
30
 
31
  <div class="sucuriscan-footer sucuriscan-clearfix">
32
+ <a href="https://sucuri.net/signup" target="_blank" title="Sucuri Security" class="sucuriscan-logo">
33
  <img src="%%SUCURI.SucuriURL%%/inc/images/logo.png" alt="Sucuri Security" />
34
  </a>
35
  <div class="sucuriscan-help">
36
  <p>
37
  If you have any questions about these checks or this plugin, contact us at
38
  <a href="mailto:info@sucuri.net">info@sucuri.net</a> or visit
39
+ <a href="https://sucuri.net/" target="_blank">sucuri.net</a>
40
  </p>
41
  </div>
42
  </div>
inc/tpl/bsidebar.html.tpl CHANGED
@@ -12,7 +12,7 @@
12
  </div>
13
  </div>
14
 
15
- <a href="http://goo.gl/9sD2sh" target="_blank" class="sucuriscan-ad-btn">Protect Your Website Today</a>
16
 
17
  <div class="sucuriscan-ad-footer">
18
  <ul>
@@ -33,7 +33,7 @@
33
  <h4>Blacklisted by Google?</h4>
34
  </div>
35
 
36
- <a href="http://goo.gl/vEwZq6" target="_blank" class="sucuriscan-ad-btn">Get Clean Today</a>
37
 
38
  <div class="sucuriscan-ad-footer sucuriscan-clearfix">
39
  <div class="sucuriscan-pull-left">
12
  </div>
13
  </div>
14
 
15
+ <a href="https://goo.gl/9sD2sh" target="_blank" class="sucuriscan-ad-btn">Protect Your Website Today</a>
16
 
17
  <div class="sucuriscan-ad-footer">
18
  <ul>
33
  <h4>Blacklisted by Google?</h4>
34
  </div>
35
 
36
+ <a href="https://goo.gl/vEwZq6" target="_blank" class="sucuriscan-ad-btn">Get Clean Today</a>
37
 
38
  <div class="sucuriscan-ad-footer sucuriscan-clearfix">
39
  <div class="sucuriscan-pull-left">
inc/tpl/{notification-corefiles.html.tpl → corefiles-notification.html.tpl} RENAMED
@@ -26,7 +26,7 @@
26
  </thead>
27
 
28
  <tbody>
29
- %%SUCURI.CoreFiles.List%%
30
  </tbody>
31
 
32
  <tfoot>
26
  </thead>
27
 
28
  <tbody>
29
+ %%%SUCURI.CoreFiles.List%%%
30
  </tbody>
31
 
32
  <tfoot>
inc/tpl/corefiles-page.html.tpl ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="postbox">
3
+ <h3>Core Integrity</h3>
4
+
5
+ <div class="inside">
6
+ <p>
7
+ Every WordPress release comes with a set of files that are part of the standard
8
+ installation process of each version, none of these files should be modified as
9
+ they are overwritten on each upgrade, it is not advised that web developers
10
+ modify the core files and instead extend the base functionality with themes or
11
+ plugins. Only three directories are scanned: admin, includes, and the document
12
+ root where the configuration and startup files are located.
13
+ </p>
14
+
15
+ <div class="sucuriscan-inline-alert-info">
16
+ <p>
17
+ Use a <a href="https://sucuri.net/website-antivirus/" target="_blank"> server
18
+ side scanner</a> or a <a href="https://sitecheck.sucuri.net/" target="_blank">
19
+ web scanner</a> to find the source of the infection and broken pages respectively.
20
+ </p>
21
+ </div>
22
+
23
+ <div id="sucuriscan-corefiles-response">
24
+ <em>Loading...</em>
25
+ </div>
26
+
27
+ <script type="text/javascript">
28
+ jQuery(function($){
29
+ $.post('%%SUCURI.AjaxURL.Home%%', {
30
+ action: 'sucuriscan_ajax',
31
+ sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
32
+ form_action: 'get_core_files',
33
+ }, function(data){
34
+ $('#sucuriscan-corefiles-response').html(data);
35
+ });
36
+ });
37
+ </script>
38
+ </div>
39
+ </div>
inc/tpl/corefiles.html.tpl ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="sucuriscan-inline-alert-updated sucuriscan-%%SUCURI.CoreFiles.GoodVisibility%%">
3
+ <p>
4
+ Your WordPress core files are clean and were not modified.
5
+ </p>
6
+ </div>
7
+
8
+ <div class="sucuriscan-inline-alert-warning sucuriscan-%%SUCURI.CoreFiles.NotFixableVisibility%%">
9
+ <p>
10
+ Files marked with the text <em>"not fixable"</em> are files without write
11
+ permissions, you have to fix them manually.
12
+ </p>
13
+ </div>
14
+
15
+ <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.CoreFiles.FailureVisibility%%">
16
+ <p>
17
+ Error retrieving the <a href="%%SUCURI.CoreFiles.RemoteChecksumsURL%%" target="_blank">
18
+ WordPress core hashes</a>. The information used by the plugin to determine the
19
+ integrity of the core files is retrieved and controlled by WordPress. Any error
20
+ message related with this tool is likely related with a modification in their
21
+ API service that is not supported yet. It is also possible that your website is
22
+ not able to communicate with this server due to a missing HTTP transport tool.
23
+ </p>
24
+ </div>
25
+
26
+ <form action="%%SUCURI.URL.Home%%" method="post">
27
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
28
+
29
+ <table class="wp-list-table widefat sucuriscan-table sucuriscan-corefiles sucuriscan-%%SUCURI.CoreFiles.BadVisibility%%">
30
+ <thead>
31
+ <tr>
32
+ <th colspan="5">Core Integrity (%%SUCURI.CoreFiles.ListCount%% files)</th>
33
+ </tr>
34
+
35
+ <tr>
36
+ <th class="manage-column column-cb check-column">
37
+ <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
38
+ <input id="cb-select-all-1" type="checkbox">
39
+ </th>
40
+ <th width="80" class="manage-column">Status</th>
41
+ <th width="100" class="manage-column">File Size</th>
42
+ <th width="200" class="manage-column">Modified At</th>
43
+ <th class="manage-column">File Path</th>
44
+ </tr>
45
+ </thead>
46
+
47
+ <tbody>
48
+ %%%SUCURI.CoreFiles.List%%%
49
+ </tbody>
50
+ </table>
51
+
52
+ <div class="sucuriscan-%%SUCURI.CoreFiles.BadVisibility%%">
53
+ <p>
54
+ Marking one or more files as fixed will force the plugin to ignore them during
55
+ the next scan, very useful when you find false positives. Additionally you can
56
+ restore the original content of the core files that appear as modified or deleted,
57
+ this will tell the plugin to download a copy of the original files from the official
58
+ <a href="https://core.svn.wordpress.org/tags/" target="_blank">WordPress repository</a>.
59
+ Deleting a file is an irreversible action, be careful.
60
+ </p>
61
+
62
+ <div class="sucuriscan-recipient-form">
63
+ <p>
64
+ <label>
65
+ <input type="hidden" name="sucuriscan_process_form" value="0" />
66
+ <input type="checkbox" name="sucuriscan_process_form" value="1" />
67
+ <span>I understand that this operation can not be reverted.</span>
68
+ </label>
69
+ </p>
70
+
71
+ <span class="sucuriscan-input-group">
72
+ <label>Choose Action:</label>
73
+ <select name="sucuriscan_integrity_action">
74
+ <option value="fixed">Mark as fixed</option>
75
+ <option value="restore">Restore source</option>
76
+ <option value="delete">Delete file</option>
77
+ </select>
78
+ </span>
79
+ <button type="submit" class="button button-primary">Proceed</button>
80
+ </div>
81
+ </div>
82
+ </form>
inc/tpl/{integrity-corefiles.snippet.tpl → corefiles.snippet.tpl} RENAMED
@@ -1,8 +1,8 @@
1
 
2
- <tr class="%%SUCURI.CoreFiles.CssClass%% sucuriscan-hidden">
3
  <td class="check-column">
4
- <input type="checkbox" name="sucuriscan_integrity_files[]" value="%%SUCURI.CoreFiles.FilePath%%" />
5
- <input type="hidden" name="sucuriscan_integrity_types[]" value="%%SUCURI.CoreFiles.StatusType%%" %%SUCURI.CoreFiles.IsFixtableFile%% />
6
  </td>
7
  <td><span class="sucuriscan-label sucuriscan-label-%%SUCURI.CoreFiles.StatusType%%">%%SUCURI.CoreFiles.StatusType%%</span></td>
8
  <td><em title="%%SUCURI.CoreFiles.FileSizeNumber%% bytes">~%%SUCURI.CoreFiles.FileSizeHuman%%</em></td>
1
 
2
+ <tr class="%%SUCURI.CoreFiles.CssClass%%">
3
  <td class="check-column">
4
+ <input type="checkbox" name="sucuriscan_corefiles[]"
5
+ value="%%SUCURI.CoreFiles.StatusType%%@%%SUCURI.CoreFiles.FilePath%%" />
6
  </td>
7
  <td><span class="sucuriscan-label sucuriscan-label-%%SUCURI.CoreFiles.StatusType%%">%%SUCURI.CoreFiles.StatusType%%</span></td>
8
  <td><em title="%%SUCURI.CoreFiles.FileSizeNumber%% bytes">~%%SUCURI.CoreFiles.FileSizeHuman%%</em></td>
inc/tpl/firewall-auditlogs.html.tpl ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div id="poststuff">
3
+ <div class="postbox">
4
+ <h3>Firewall Audit Logs</h3>
5
+
6
+ <div class="inside">
7
+ <p>
8
+ CloudProxy logs every request involved in an attack and separates them from the
9
+ legitimate requests. You can analyze the data from the latest entries in the
10
+ logs using this tool and take action either enabling the advanced features of
11
+ the IDS <em>(Intrusion Detection System)</em> from the <a target="_blank"
12
+ href="https://waf.sucuri.net/?settings">CloudProxy Dashboard</a> and/or blocking
13
+ IP addresses and URL paths directly from the <a href="https://waf.sucuri.net/?audit"
14
+ target="_blank">CloudProxy Audit Trails</a> page.
15
+ </p>
16
+
17
+ <div class="sucuriscan-inline-alert-info">
18
+ <p>Note that non-blocked requests are hidden from the logs, this is intentional.</p>
19
+ </div>
20
+
21
+ <div>
22
+ <form action="%%SUCURI.URL.Firewall%%#auditlogs" method="post">
23
+ <span class="sucuriscan-input-group">
24
+ <label>Query:</label>
25
+ <input type="text" id="sucuriscan_firewall_query" class="input-text" />
26
+ <select id="sucuriscan_firewall_day">%%%SUCURI.AuditLogs.DateDays%%%</select>
27
+ <select id="sucuriscan_firewall_month">%%%SUCURI.AuditLogs.DateMonths%%%</select>
28
+ <select id="sucuriscan_firewall_year">%%%SUCURI.AuditLogs.DateYears%%%</select>
29
+ </span>
30
+ <button id="sucuriscan-firewall-auditlogs-button" class="button button-primary">Retrieve Logs</button>
31
+ </form>
32
+ </div>
33
+
34
+ <script type="text/javascript">
35
+ jQuery(function($){
36
+ $('#sucuriscan-firewall-auditlogs-button').on('click', function(ev){
37
+ ev.preventDefault();
38
+ $('.sucuriscan-firewall-auditlogs tbody').html(
39
+ '<tr><td><em>Loading...</em></td></tr>'
40
+ );
41
+ var params = {
42
+ action: 'sucuriscan_firewall_ajax',
43
+ sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
44
+ form_action: 'get_audit_logs',
45
+ };
46
+ params.sucuriscan_query = $('#sucuriscan_firewall_query').val();
47
+ params.sucuriscan_month = $('#sucuriscan_firewall_month').val();
48
+ params.sucuriscan_year = $('#sucuriscan_firewall_year').val();
49
+ params.sucuriscan_day = $('#sucuriscan_firewall_day').val();
50
+ $.post('%%SUCURI.AjaxURL.Firewall%%', params, function(data){
51
+ $('.sucuriscan-firewall-auditlogs tbody').html(data);
52
+ });
53
+ });
54
+ $('#sucuriscan-firewall-auditlogs-button').click();
55
+ });
56
+ </script>
57
+
58
+ <table class="wp-list-table widefat sucuriscan-table sucuriscan-firewall-auditlogs">
59
+ <thead>
60
+ <tr>
61
+ <th>Audit Logs</th>
62
+ </tr>
63
+ </thead>
64
+
65
+ <tbody>
66
+ <tr>
67
+ <td><em>Loading...</em></td>
68
+ </tr>
69
+ </tbody>
70
+ </table>
71
+ </div>
72
+ </div>
73
+ </div>
inc/tpl/firewall-auditlogs.snippet.tpl ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <tr class="%%SUCURI.AccessLog.CssClass%%">
3
+ <td>
4
+ <div class="sucuriscan-firewall-accesslog sucuriscan-monospace">
5
+ <div class="sucuriscan-accesslog-origin">
6
+ <img src="%%SUCURI.SucuriURL%%/inc/images/blank.png"
7
+ class="sucuriscan-flag sucuriscan-flag-%%SUCURI.AccessLog.RequestCountryCode%%" />
8
+ <span>%%SUCURI.AccessLog.RemoteAddr%%</span>
9
+ <span>(%%SUCURI.AccessLog.RequestCountryName%%)</span>
10
+ </div>
11
+ <div class="sucuriscan-accesslog-datetime">
12
+ <span class="sucuriscan-accesslog-label">Date/Time:</span>
13
+ <span>%%SUCURI.AccessLog.RequestDate%%</span>
14
+ <span>%%SUCURI.AccessLog.RequestTime%%</span>
15
+ <span>%%SUCURI.AccessLog.RequestTimezone%%</span>
16
+ </div>
17
+ <div class="sucuriscan-accesslog-signature">
18
+ <span class="sucuriscan-accesslog-label">Signature:</span>
19
+ <span>%%SUCURI.AccessLog.SucuriBlockCode%%</span>
20
+ <span>(%%SUCURI.AccessLog.SucuriBlockReason%%)</span>
21
+ </div>
22
+ <div class="sucuriscan-accesslog-request">
23
+ <span class="sucuriscan-accesslog-label">Request:</span>
24
+ <span>%%SUCURI.AccessLog.HttpProtocol%%</span>
25
+ <span>%%SUCURI.AccessLog.RequestMethod%%</span>
26
+ <span>%%SUCURI.AccessLog.HttpStatus%%</span>
27
+ <span>%%SUCURI.AccessLog.HttpStatusTitle%%</span>
28
+ </div>
29
+ <div class="sucuriscan-accesslog-useragent">
30
+ <span class="sucuriscan-accesslog-label">U-Agent:</span>
31
+ <span>%%SUCURI.AccessLog.HttpUserAgent%%</span>
32
+ </div>
33
+ <div class="sucuriscan-accesslog-target">
34
+ <span class="sucuriscan-accesslog-label">Target.:</span>
35
+ <span>%%SUCURI.AccessLog.ResourcePath%%</span>
36
+ </div>
37
+ <div class="sucuriscan-accesslog-referer">
38
+ <span class="sucuriscan-accesslog-label">Referer:</span>
39
+ <span>%%SUCURI.AccessLog.HttpReferer%%</span>
40
+ </div>
41
+ </span>
42
+ </td>
43
+ </tr>
inc/tpl/firewall-clearcache.html.tpl ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div id="poststuff">
3
+ <div class="postbox">
4
+ <h3>Clear Cache</h3>
5
+
6
+ <div class="inside">
7
+ <p>
8
+ CloudProxy offers multiple options to configure the cache level applied to your
9
+ website. You can either enable the full cache functionality which is the
10
+ recommended setting, or you can set the cache level to minimal which will keep
11
+ the pages static for a couple of minutes, or force the usage of the website
12
+ headers <em>(only for advanced users)</em>, or in extreme cases where you do not
13
+ need the cache you can simply disable it. You can find more information about it
14
+ in the <a href="https://kb.sucuri.net/cloudproxy/Performance/caching-options"
15
+ target="_blank">Sucuri Knowledge Base</a> website.
16
+ </p>
17
+
18
+ <div class="sucuriscan-inline-alert-info">
19
+ <p>
20
+ Note that CloudProxy has <a href="https://kb.sucuri.net/cloudproxy/Performance/cache-exceptions"
21
+ target="_blank">special caching rules</a> for Images, Cascading Style Sheets,
22
+ JavaScript, PDF, TXT, media files and a few more extensions that are stored on
23
+ our <a href="https://en.wikipedia.org/wiki/Edge_device" target="_blank">edge</a>.
24
+ The only way to flush the cache for these files is by clearing CloudProxy's cache
25
+ completely <em>(for the whole website)</em>.
26
+ </p>
27
+ </div>
28
+
29
+ <div class="sucuriscan-inline-alert-warning">
30
+ <p>
31
+ Due to our caching of JavaScript and CSS files, often, as is best practice, the
32
+ use of versioning during development will ensure updates going live as expected.
33
+ This is done by adding a query string such as <code>?ver=1.2.3</code> and
34
+ incrementing on each update.
35
+ </p>
36
+ </div>
37
+
38
+ <p>
39
+ A web cache (or HTTP cache) is an information technology for the temporary
40
+ storage (caching) of web documents, such as HTML pages and images, to reduce
41
+ bandwidth usage, server load, and perceived lag. A web cache system stores
42
+ copies of documents passing through it; subsequent requests may be satisfied
43
+ from the cache if certain conditions are met. A web cache system can refer
44
+ either to an appliance, or to a computer program.
45
+ </p>
46
+
47
+ <p>
48
+ More info at <a href="https://en.wikipedia.org/wiki/Web_cache" target="_blank">
49
+ WikiPedia - Web Cache</a>
50
+ </p>
51
+
52
+ <form action="%%SUCURI.URL.Firewall%%#clearcache" method="post">
53
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
54
+ <input type="hidden" name="sucuriscan_clear_cache" value="1" />
55
+ <input type="submit" value="Clear Cache" class="button button-primary" />
56
+ </form>
57
+ </div>
58
+ </div>
59
+ </div>
inc/tpl/firewall-settings.html.tpl ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div id="poststuff">
3
+ <div class="postbox">
4
+ <h3>Firewall Settings</h3>
5
+
6
+ <div class="inside">
7
+ <p>
8
+ A powerful <b>WAF</b> <em>(Web Application Firewall)</em> and <b>Intrusion
9
+ Prevention</b> system for any WordPress user and many other platforms. This page
10
+ will help you to configure and monitor your site through <strong>Sucuri
11
+ CloudProxy</strong>. Once enabled, our firewall will act as a shield, protecting
12
+ your site from attacks and preventing malware infections and reinfections. It
13
+ will block SQL injection attempts, brute force attacks, XSS, RFI, backdoors and
14
+ many other threats against your site.
15
+ </p>
16
+
17
+ <div class="sucuriscan-inline-alert-info">
18
+ <p>
19
+ Add your <a href="https://waf.sucuri.net/?settings&panel=api" target="_blank">
20
+ CloudProxy API key</a> in the form below to start communicating with the firewall
21
+ API service.
22
+ </p>
23
+ </div>
24
+
25
+ <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-firewall-apikey sucuriscan-%%SUCURI.Firewall.APIKeyVisibility%%">
26
+ <span class="sucuriscan-monospace">%%SUCURI.Firewall.APIKey%%</span>
27
+ <form action="%%SUCURI.URL.Firewall%%" method="post">
28
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
29
+ <button type="submit" name="sucuriscan_delete_wafkey" class="button-primary button-danger">Delete</button>
30
+ </form>
31
+ </div>
32
+
33
+ <form action="%%SUCURI.URL.Firewall%%" method="post" class="sucuriscan-%%SUCURI.Firewall.APIKeyFormVisibility%%">
34
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
35
+ <span class="sucuriscan-input-group">
36
+ <label>CloudProxy API Key:</label>
37
+ <input type="text" name="sucuriscan_cloudproxy_apikey" class="input-text" />
38
+ </span>
39
+ <button type="submit" class="button-primary">Save</button>
40
+ </form>
41
+
42
+ <table class="wp-list-table widefat sucuriscan-table sucuriscan-firewall-settings sucuriscan-%%SUCURI.Firewall.SettingsVisibility%%">
43
+ <thead>
44
+ <tr>
45
+ <th>Setting Name</th>
46
+ <th>Setting Value</th>
47
+ </tr>
48
+ </thead>
49
+
50
+ <tbody>
51
+ %%%SUCURI.Firewall.SettingOptions%%%
52
+ </tbody>
53
+ </table>
54
+
55
+ <p>
56
+ <em>[1]</em> More information about <a href="https://sucuri.net/website-firewall/"
57
+ target="_blank">CloudProxy</a>, features and pricing.<br>
58
+ <em>[2]</em> Instructions and videos in the official <a href="https://kb.sucuri.net/cloudproxy"
59
+ target="_blank">Knowledge Base</a> site.<br>
60
+ <em>[3]</em> <a href="https://login.sucuri.net/signup2/create?CloudProxy" target="_blank">
61
+ Sign up</a> for a new account and start protecting your site with CloudProxy.
62
+ </p>
63
+ </div>
64
+ </div>
65
+ </div>
inc/tpl/firewall-settings.snippet.tpl ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+
2
+ <tr class="%%SUCURI.Firewall.OptionCssClass%%">
3
+ <td><label>%%SUCURI.Firewall.OptionName%%</label></td>
4
+ <td><span class="sucuriscan-monospace">%%%SUCURI.Firewall.OptionValue%%%</span></td>
5
+ </tr>
inc/tpl/firewall.html.tpl ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="sucuriscan-tabs">
3
+ <ul>
4
+ <li>
5
+ <a href="#settings" data-tabname="settings">Settings</a>
6
+ </li>
7
+ <li>
8
+ <a href="#auditlogs" data-tabname="auditlogs">Audit Logs</a>
9
+ </li>
10
+ <li>
11
+ <a href="#clearcache" data-tabname="clearcache">Clear Cache</a>
12
+ </li>
13
+ </ul>
14
+
15
+ <div class="sucuriscan-tab-containers">
16
+ <div id="sucuriscan-settings">
17
+ %%%SUCURI.Firewall.Settings%%%
18
+ </div>
19
+
20
+ <div id="sucuriscan-auditlogs">
21
+ %%%SUCURI.Firewall.AuditLogs%%%
22
+ </div>
23
+
24
+ <div id="sucuriscan-clearcache">
25
+ %%%SUCURI.Firewall.ClearCache%%%
26
+ </div>
27
+ </div>
28
+ </div>
inc/tpl/hardening-panel.html.tpl CHANGED
@@ -4,32 +4,32 @@
4
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
5
  <input type="hidden" name="sucuriscan_run_hardening" value="1" />
6
 
7
- %%SUCURI.Hardening.Version%%
8
 
9
- %%SUCURI.Hardening.CloudProxy%%
10
 
11
- %%SUCURI.Hardening.RemoveGenerator%%
12
 
13
- %%SUCURI.Hardening.NginxPhpFpm%%
14
 
15
- %%SUCURI.Hardening.Upload%%
16
 
17
- %%SUCURI.Hardening.WpContent%%
18
 
19
- %%SUCURI.Hardening.WpIncludes%%
20
 
21
- %%SUCURI.Hardening.PhpVersion%%
22
 
23
- %%SUCURI.Hardening.SecretKeys%%
24
 
25
- %%SUCURI.Hardening.Readme%%
26
 
27
- %%SUCURI.Hardening.AdminUser%%
28
 
29
- %%SUCURI.Hardening.FileEditor%%
30
 
31
- %%SUCURI.Hardening.DBTables%%
32
 
33
- %%SUCURI.Hardening.ErrorLog%%
34
  </form>
35
  </div>
4
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
5
  <input type="hidden" name="sucuriscan_run_hardening" value="1" />
6
 
7
+ %%%SUCURI.Hardening.Version%%%
8
 
9
+ %%%SUCURI.Hardening.CloudProxy%%%
10
 
11
+ %%%SUCURI.Hardening.RemoveGenerator%%%
12
 
13
+ %%%SUCURI.Hardening.NginxPhpFpm%%%
14
 
15
+ %%%SUCURI.Hardening.Upload%%%
16
 
17
+ %%%SUCURI.Hardening.WpContent%%%
18
 
19
+ %%%SUCURI.Hardening.WpIncludes%%%
20
 
21
+ %%%SUCURI.Hardening.PhpVersion%%%
22
 
23
+ %%%SUCURI.Hardening.SecretKeys%%%
24
 
25
+ %%%SUCURI.Hardening.Readme%%%
26
 
27
+ %%%SUCURI.Hardening.AdminUser%%%
28
 
29
+ %%%SUCURI.Hardening.FileEditor%%%
30
 
31
+ %%%SUCURI.Hardening.DBTables%%%
32
 
33
+ %%%SUCURI.Hardening.ErrorLog%%%
34
  </form>
35
  </div>
inc/tpl/hardening-whitelist.html.tpl CHANGED
@@ -76,7 +76,7 @@
76
  </thead>
77
 
78
  <tbody>
79
- %%SUCURI.HardeningWhitelist.List%%
80
 
81
  <tr class="sucuriscan-%%SUCURI.HardeningWhitelist.NoItemsVisibility%%">
82
  <td colspan="4">
76
  </thead>
77
 
78
  <tbody>
79
+ %%%SUCURI.HardeningWhitelist.List%%%
80
 
81
  <tr class="sucuriscan-%%SUCURI.HardeningWhitelist.NoItemsVisibility%%">
82
  <td colspan="4">
inc/tpl/hardening.html.tpl CHANGED
@@ -2,20 +2,20 @@
2
  <div class="sucuriscan-tabs">
3
  <ul>
4
  <li>
5
- <a href="#" data-tabname="hardening">Hardening Options</a>
6
  </li>
7
  <li>
8
- <a href="#" data-tabname="whitelist">Whitelist Blocked PHP Files</a>
9
  </li>
10
  </ul>
11
 
12
  <div class="sucuriscan-tab-containers">
13
  <div id="sucuriscan-hardening">
14
- %%SUCURI.Hardening.Panel%%
15
  </div>
16
 
17
  <div id="sucuriscan-whitelist">
18
- %%SUCURI.Hardening.Whitelist%%
19
  </div>
20
  </div>
21
  </div>
2
  <div class="sucuriscan-tabs">
3
  <ul>
4
  <li>
5
+ <a href="#hardening" data-tabname="hardening">Hardening Options</a>
6
  </li>
7
  <li>
8
+ <a href="#whitelist" data-tabname="whitelist">Whitelist Blocked PHP Files</a>
9
  </li>
10
  </ul>
11
 
12
  <div class="sucuriscan-tab-containers">
13
  <div id="sucuriscan-hardening">
14
+ %%%SUCURI.Hardening.Panel%%%
15
  </div>
16
 
17
  <div id="sucuriscan-whitelist">
18
+ %%%SUCURI.Hardening.Whitelist%%%
19
  </div>
20
  </div>
21
  </div>
inc/tpl/hardening.snippet.tpl CHANGED
@@ -3,7 +3,7 @@
3
  <h3>%%SUCURI.Hardening.Title%%</h3>
4
 
5
  <div class="inside">
6
- %%SUCURI.Hardening.Description%%
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-%%SUCURI.Hardening.Status%%">
9
  <input type="submit" name="%%SUCURI.Hardening.FieldName%%"
@@ -11,9 +11,9 @@
11
  %%SUCURI.Hardening.FieldAttributes%%
12
  class="button-secondary" />
13
 
14
- <span>%%SUCURI.Hardening.Information%%</span>
15
  </div>
16
 
17
- %%SUCURI.Hardening.UpdateMessage%%
18
  </div>
19
  </div>
3
  <h3>%%SUCURI.Hardening.Title%%</h3>
4
 
5
  <div class="inside">
6
+ %%%SUCURI.Hardening.Description%%%
7
 
8
  <div class="sucuriscan-hstatus sucuriscan-hstatus-%%SUCURI.Hardening.Status%%">
9
  <input type="submit" name="%%SUCURI.Hardening.FieldName%%"
11
  %%SUCURI.Hardening.FieldAttributes%%
12
  class="button-secondary" />
13
 
14
+ <span>%%%SUCURI.Hardening.Information%%%</span>
15
  </div>
16
 
17
+ %%%SUCURI.Hardening.UpdateMessage%%%
18
  </div>
19
  </div>
inc/tpl/infosys-cronjobs.html.tpl CHANGED
@@ -42,7 +42,7 @@
42
  </thead>
43
 
44
  <tbody>
45
- %%SUCURI.Cronjobs.List%%
46
  </tbody>
47
 
48
  <tfoot>
42
  </thead>
43
 
44
  <tbody>
45
+ %%%SUCURI.Cronjobs.List%%%
46
  </tbody>
47
 
48
  <tfoot>
inc/tpl/infosys-errorlogs.html.tpl CHANGED
@@ -36,7 +36,7 @@
36
  Note that if the log file is not empty but the table is, it means that the
37
  format of the logs used by the web server is not supported by the scanner,
38
  you can try to increase the number of lines processed though from
39
- <a href="%%SUCURI.URL.Settings%%#settings-scanner">here</a> in case that
40
  other lines have a different format which is very common on servers with
41
  mixed configurations.
42
  </p>
@@ -71,7 +71,7 @@
71
  </thead>
72
 
73
  <tbody>
74
- %%SUCURI.ErrorLog.List%%
75
 
76
  <tr class="sucuriscan-%%SUCURI.ErrorLog.InvalidFormatVisibility%%">
77
  <td colspan="5">
36
  Note that if the log file is not empty but the table is, it means that the
37
  format of the logs used by the web server is not supported by the scanner,
38
  you can try to increase the number of lines processed though from
39
+ <a href="%%SUCURI.URL.Settings%%#scanner">here</a> in case that
40
  other lines have a different format which is very common on servers with
41
  mixed configurations.
42
  </p>
71
  </thead>
72
 
73
  <tbody>
74
+ %%%SUCURI.ErrorLog.List%%%
75
 
76
  <tr class="sucuriscan-%%SUCURI.ErrorLog.InvalidFormatVisibility%%">
77
  <td colspan="5">
inc/tpl/infosys-htaccess.html.tpl CHANGED
@@ -12,14 +12,29 @@
12
  able to handle pretty permalinks.
13
  </p>
14
 
15
- <div class="sucuriscan-inline-alert-%%SUCURI.HTAccess.MessageType%% sucuriscan-%%SUCURI.HTAccess.MessageVisible%%">
16
- <p>%%SUCURI.HTAccess.Message%%</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  </div>
18
 
19
  <textarea class="sucuriscan-full-textarea sucuriscan-monospace %%SUCURI.HTAccess.TextareaVisible%%">%%SUCURI.HTAccess.Content%%</textarea>
20
 
21
  <p>
22
- <small>Source <a href="http://codex.wordpress.org/htaccess" target="_blank">Codex WordPress HTAccess</a></small>
23
  </p>
24
  </div>
25
  </div>
12
  able to handle pretty permalinks.
13
  </p>
14
 
15
+ <div class="sucuriscan-inline-alert-updated sucuriscan-%%SUCURI.HTAccess.FoundVisible%%">
16
+ <p>HTAccess file found in this path <code>%%SUCURI.HTAccess.Fpath%%</code></p>
17
+ </div>
18
+
19
+ <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.HTAccess.NotFoundVisible%%">
20
+ <p>Your website has no <code>.htaccess</code> file or it was not found in the default location.</p>
21
+ </div>
22
+
23
+ <div class="sucuriscan-inline-alert-info sucuriscan-%%SUCURI.HTAccess.StandardVisible%%">
24
+ <p>
25
+ The main <code>.htaccess</code> file in your site has the standard rules for a
26
+ WordPress installation. You can customize it to improve the performance and
27
+ change the behaviour of the redirections for pages and posts in your site. To
28
+ get more information visit the official documentation at <a target="_blank"
29
+ href="https://codex.wordpress.org/Using_Permalinks#Creating_and_editing_.28.htaccess.29">
30
+ Codex WordPrexx - Creating and editing (.htaccess)</a>
31
+ </p>
32
  </div>
33
 
34
  <textarea class="sucuriscan-full-textarea sucuriscan-monospace %%SUCURI.HTAccess.TextareaVisible%%">%%SUCURI.HTAccess.Content%%</textarea>
35
 
36
  <p>
37
+ <small>Source <a href="https://codex.wordpress.org/htaccess" target="_blank">Codex WordPress HTAccess</a></small>
38
  </p>
39
  </div>
40
  </div>
inc/tpl/infosys-serverinfo.html.tpl CHANGED
@@ -1,6 +1,6 @@
1
 
2
  <table class="wp-list-table widefat sucuriscan-server-info">
3
  <tbody>
4
- %%SUCURI.ServerInfo.Variables%%
5
  </tbody>
6
  </table>
1
 
2
  <table class="wp-list-table widefat sucuriscan-server-info">
3
  <tbody>
4
+ %%%SUCURI.ServerInfo.Variables%%%
5
  </tbody>
6
  </table>
inc/tpl/infosys-wpconfig.html.tpl CHANGED
@@ -8,6 +8,6 @@
8
  </thead>
9
 
10
  <tbody>
11
- %%SUCURI.WordpressConfig.Rules%%
12
  </tbody>
13
  </table>
8
  </thead>
9
 
10
  <tbody>
11
+ %%%SUCURI.WordpressConfig.Rules%%%
12
  </tbody>
13
  </table>
inc/tpl/infosys.html.tpl CHANGED
@@ -2,41 +2,41 @@
2
  <div class="sucuriscan-tabs">
3
  <ul>
4
  <li>
5
- <a href="#" data-tabname="server-info">Plugin &amp; Server Info</a>
6
  </li>
7
  <li>
8
- <a href="#" data-tabname="wordpress-cronjobs">Scheduled Tasks</a>
9
  </li>
10
  <li>
11
- <a href="#" data-tabname="htaccess-integrity">Access File Integrity</a>
12
  </li>
13
  <li>
14
- <a href="#" data-tabname="wpconfig-vars">Config. Variables</a>
15
  </li>
16
  <li>
17
- <a href="#" data-tabname="error-logs">Error Logs</a>
18
  </li>
19
  </ul>
20
 
21
  <div class="sucuriscan-tab-containers">
22
  <div id="sucuriscan-server-info">
23
- %%SUCURI.ServerInfo%%
24
  </div>
25
 
26
  <div id="sucuriscan-wordpress-cronjobs">
27
- %%SUCURI.Cronjobs%%
28
  </div>
29
 
30
  <div id="sucuriscan-htaccess-integrity">
31
- %%SUCURI.HTAccessIntegrity%%
32
  </div>
33
 
34
  <div id="sucuriscan-wpconfig-vars">
35
- %%SUCURI.WordpressConfig%%
36
  </div>
37
 
38
  <div id="sucuriscan-error-logs">
39
- %%SUCURI.ErrorLogs%%
40
  </div>
41
  </div>
42
  </div>
2
  <div class="sucuriscan-tabs">
3
  <ul>
4
  <li>
5
+ <a href="#server-info" data-tabname="server-info">Plugin &amp; Server Info</a>
6
  </li>
7
  <li>
8
+ <a href="#wordpress-cronjobs" data-tabname="wordpress-cronjobs">Scheduled Tasks</a>
9
  </li>
10
  <li>
11
+ <a href="#htaccess-integrity" data-tabname="htaccess-integrity">Access File Integrity</a>
12
  </li>
13
  <li>
14
+ <a href="#wpconfig-vars" data-tabname="wpconfig-vars">Config. Variables</a>
15
  </li>
16
  <li>
17
+ <a href="#error-logs" data-tabname="error-logs">Error Logs</a>
18
  </li>
19
  </ul>
20
 
21
  <div class="sucuriscan-tab-containers">
22
  <div id="sucuriscan-server-info">
23
+ %%%SUCURI.ServerInfo%%%
24
  </div>
25
 
26
  <div id="sucuriscan-wordpress-cronjobs">
27
+ %%%SUCURI.Cronjobs%%%
28
  </div>
29
 
30
  <div id="sucuriscan-htaccess-integrity">
31
+ %%%SUCURI.HTAccessIntegrity%%%
32
  </div>
33
 
34
  <div id="sucuriscan-wpconfig-vars">
35
+ %%%SUCURI.WordpressConfig%%%
36
  </div>
37
 
38
  <div id="sucuriscan-error-logs">
39
+ %%%SUCURI.ErrorLogs%%%
40
  </div>
41
  </div>
42
  </div>
inc/tpl/integrity-auditlogs.html.tpl CHANGED
@@ -15,7 +15,7 @@
15
 
16
  <tr>
17
  <th>&nbsp;</th>
18
- <th width="170">Date</th>
19
  <th>Username</th>
20
  <th>IP Address</th>
21
  <th>Event Message</th>
@@ -23,7 +23,7 @@
23
  </thead>
24
 
25
  <tbody>
26
- %%SUCURI.AuditLogs.List%%
27
 
28
  <tr class="sucuriscan-%%SUCURI.AuditLogs.NoItemsVisibility%%">
29
  <td colspan="5">
@@ -34,7 +34,7 @@
34
  <tr class="sucuriscan-%%SUCURI.AuditLogs.PaginationVisibility%%">
35
  <td colspan="5">
36
  <ul class="sucuriscan-pagination">
37
- %%SUCURI.AuditLogs.PaginationLinks%%
38
  </ul>
39
  </td>
40
  </tr>
15
 
16
  <tr>
17
  <th>&nbsp;</th>
18
+ <th width="200">Date</th>
19
  <th>Username</th>
20
  <th>IP Address</th>
21
  <th>Event Message</th>
23
  </thead>
24
 
25
  <tbody>
26
+ %%%SUCURI.AuditLogs.List%%%
27
 
28
  <tr class="sucuriscan-%%SUCURI.AuditLogs.NoItemsVisibility%%">
29
  <td colspan="5">
34
  <tr class="sucuriscan-%%SUCURI.AuditLogs.PaginationVisibility%%">
35
  <td colspan="5">
36
  <ul class="sucuriscan-pagination">
37
+ %%%SUCURI.AuditLogs.PaginationLinks%%%
38
  </ul>
39
  </td>
40
  </tr>
inc/tpl/integrity-auditlogs.snippet.tpl CHANGED
@@ -10,6 +10,6 @@
10
  <td>
11
  <span>%%SUCURI.AuditLog.Message%%</span>
12
 
13
- %%SUCURI.AuditLog.Extra%%
14
  </td>
15
  </tr>
10
  <td>
11
  <span>%%SUCURI.AuditLog.Message%%</span>
12
 
13
+ %%%SUCURI.AuditLog.Extra%%%
14
  </td>
15
  </tr>
inc/tpl/integrity-auditreport.html.tpl CHANGED
@@ -6,9 +6,10 @@
6
 
7
  <div class="sucuriscan-inline-alert-info">
8
  <p>
9
- The data used to generate these charts comes from the last <strong>%%SUCURI.AuditReport.Logs4Report%%
10
- audit logs</strong>, you can configure this number from the plugin settings page,
11
- you can also disable and enable this panel from there at any time.
 
12
  </p>
13
  </div>
14
 
@@ -16,13 +17,13 @@
16
 
17
  <div class="sucuriscan-pull-left sucuriscan-report-chart">
18
  <h4>Audit Logs per Event</h4>
19
- <h5>source http://sucuri.net/</h5>
20
  <div id="sucuriscan-report-events-per-type"></div>
21
  </div>
22
 
23
  <div class="sucuriscan-pull-right sucuriscan-report-chart">
24
  <h4>Successful/Failed Logins</h4>
25
- <h5>source http://sucuri.net/</h5>
26
  <div id="sucuriscan-report-events-per-login"></div>
27
  </div>
28
 
@@ -32,13 +33,13 @@
32
 
33
  <div class="sucuriscan-pull-left sucuriscan-report-chart">
34
  <h4>Audit Logs per User</h4>
35
- <h5>source http://sucuri.net/</h5>
36
  <div id="sucuriscan-report-events-per-user"></div>
37
  </div>
38
 
39
  <div class="sucuriscan-pull-right sucuriscan-report-chart">
40
  <h4>Audit Logs per IP Address</h4>
41
- <h5>source http://sucuri.net/</h5>
42
  <div id="sucuriscan-report-events-per-ipaddress"></div>
43
  </div>
44
 
@@ -79,29 +80,29 @@ jQuery(document).ready(function($){
79
  /* Pie-chart with number of audit logs per event type. */
80
  sucuriscan_pie_chart(
81
  '#sucuriscan-report-events-per-type',
82
- [ %%SUCURI.AuditReport.EventsPerType%% ],
83
- [ %%SUCURI.AuditReport.EventColors%% ]
84
  );
85
 
86
  /* Column-chart with number of audit logs per event login. */
87
  sucuriscan_pie_chart(
88
  '#sucuriscan-report-events-per-login',
89
- [ %%SUCURI.AuditReport.EventsPerLogin%% ],
90
  [ '#5cb85c', '#f27d7d' ]
91
  );
92
 
93
  /* Bar-chart with number of audit logs per user account. */
94
  sucuriscan_bar_chart(
95
  '#sucuriscan-report-events-per-user',
96
- [ %%SUCURI.AuditReport.EventsPerUserCategories%% ],
97
- [ 'data', %%SUCURI.AuditReport.EventsPerUserSeries%% ]
98
  );
99
 
100
  /* Bar-chart with number of audit logs per remote address. */
101
  sucuriscan_bar_chart(
102
  '#sucuriscan-report-events-per-ipaddress',
103
- [ %%SUCURI.AuditReport.EventsPerIPAddressCategories%% ],
104
- [ 'data', %%SUCURI.AuditReport.EventsPerIPAddressSeries%% ]
105
  );
106
 
107
  });
6
 
7
  <div class="sucuriscan-inline-alert-info">
8
  <p>
9
+ The data used to generate these charts comes from the last
10
+ <strong>%%SUCURI.AuditReport.Logs4Report%% audit logs</strong>, you can
11
+ configure this number from the plugin settings page, you can also disable
12
+ and enable this panel from there at any time.
13
  </p>
14
  </div>
15
 
17
 
18
  <div class="sucuriscan-pull-left sucuriscan-report-chart">
19
  <h4>Audit Logs per Event</h4>
20
+ <h5>source https://sucuri.net/</h5>
21
  <div id="sucuriscan-report-events-per-type"></div>
22
  </div>
23
 
24
  <div class="sucuriscan-pull-right sucuriscan-report-chart">
25
  <h4>Successful/Failed Logins</h4>
26
+ <h5>source https://sucuri.net/</h5>
27
  <div id="sucuriscan-report-events-per-login"></div>
28
  </div>
29
 
33
 
34
  <div class="sucuriscan-pull-left sucuriscan-report-chart">
35
  <h4>Audit Logs per User</h4>
36
+ <h5>source https://sucuri.net/</h5>
37
  <div id="sucuriscan-report-events-per-user"></div>
38
  </div>
39
 
40
  <div class="sucuriscan-pull-right sucuriscan-report-chart">
41
  <h4>Audit Logs per IP Address</h4>
42
+ <h5>source https://sucuri.net/</h5>
43
  <div id="sucuriscan-report-events-per-ipaddress"></div>
44
  </div>
45
 
80
  /* Pie-chart with number of audit logs per event type. */
81
  sucuriscan_pie_chart(
82
  '#sucuriscan-report-events-per-type',
83
+ [ %%%SUCURI.AuditReport.EventsPerType%%% ],
84
+ [ %%%SUCURI.AuditReport.EventColors%%% ]
85
  );
86
 
87
  /* Column-chart with number of audit logs per event login. */
88
  sucuriscan_pie_chart(
89
  '#sucuriscan-report-events-per-login',
90
+ [ %%%SUCURI.AuditReport.EventsPerLogin%%% ],
91
  [ '#5cb85c', '#f27d7d' ]
92
  );
93
 
94
  /* Bar-chart with number of audit logs per user account. */
95
  sucuriscan_bar_chart(
96
  '#sucuriscan-report-events-per-user',
97
+ [ %%%SUCURI.AuditReport.EventsPerUserCategories%%% ],
98
+ [ 'data', %%%SUCURI.AuditReport.EventsPerUserSeries%%% ]
99
  );
100
 
101
  /* Bar-chart with number of audit logs per remote address. */
102
  sucuriscan_bar_chart(
103
  '#sucuriscan-report-events-per-ipaddress',
104
+ [ %%%SUCURI.AuditReport.EventsPerIPAddressCategories%%% ],
105
+ [ 'data', %%%SUCURI.AuditReport.EventsPerIPAddressSeries%%% ]
106
  );
107
 
108
  });
inc/tpl/integrity-corefiles.html.tpl DELETED
@@ -1,97 +0,0 @@
1
-
2
- <div class="postbox sucuriscan-border sucuriscan-border-good sucuriscan-integrity-message sucuriscan-%%SUCURI.CoreFiles.GoodVisibility%%">
3
- <span class="sucuriscan-integrity-mark">OK</span>
4
- <h3>Core integrity</h3>
5
-
6
- <div class="inside">
7
- <p>Your WordPress core files are clean and were not modified.</p>
8
- </div>
9
- </div>
10
-
11
- <div class="postbox sucuriscan-border sucuriscan-border-bad sucuriscan-integrity-message sucuriscan-%%SUCURI.CoreFiles.FailureVisibility%%">
12
- <span class="sucuriscan-integrity-failure">FAILURE</span>
13
- <h3>Core integrity</h3>
14
-
15
- <div class="inside">
16
- <p>
17
- Error retrieving the <a href="%%SUCURI.CoreFiles.RemoteChecksumsURL%%" target="_blank">
18
- WordPress core hashes</a>. The information used by the plugin to determine the
19
- integrity of the core files is retrieved and controlled by WordPress. Any error
20
- message related with this tool is likely related with a modification in their
21
- API service that is not supported yet. It is also possible that your website is
22
- not able to communicate with this server due to a missing HTTP transport tool.
23
- </p>
24
- </div>
25
- </div>
26
-
27
- <form action="%%SUCURI.URL.Home%%" method="post">
28
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
29
-
30
- <table class="wp-list-table widefat sucuriscan-table sucuriscan-corefiles sucuriscan-%%SUCURI.CoreFiles.BadVisibility%%">
31
- <thead>
32
- <tr>
33
- <th colspan="5" class="sucuriscan-clearfix thead-with-button">
34
- <span>Core integrity (%%SUCURI.CoreFiles.ListCount%% files)</span>
35
- <button id="sucuriscan-corefiles-show" class="button button-primary thead-topright-action" data-action="show">Show files</button>
36
- </th>
37
- </tr>
38
-
39
- <tr>
40
- <td colspan="5" class="sucuriscan-corefiles-warning">
41
- <div>
42
- <p>
43
- Changes in the integrity of your core files were detected, you may want to check
44
- each file to determine if they were infected with malicious code. The WordPress
45
- core directories <code>/&lt;root&gt;</code>, <code>/wp-admin</code> and <code>
46
- /wp-includes</code> are the only ones being scanned; the content, uploads, and
47
- custom directories are not part of the official archives so you have to check
48
- them manually.
49
- </p>
50
- </div>
51
- </td>
52
- </tr>
53
-
54
- <tr class="sucuriscan-hidden">
55
- <th class="manage-column column-cb check-column">
56
- <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
57
- <input id="cb-select-all-1" type="checkbox">
58
- </th>
59
- <th width="80" class="manage-column">Status</th>
60
- <th width="100" class="manage-column">File Size</th>
61
- <th width="180" class="manage-column">Modified At</th>
62
- <th class="manage-column">File Path</th>
63
- </tr>
64
- </thead>
65
-
66
- <tbody>
67
- %%SUCURI.CoreFiles.List%%
68
- </tbody>
69
-
70
- <tfoot>
71
- <tr>
72
- <td colspan="5">
73
- <p>
74
- <strong>Note.</strong> This is not a malware scanner but an integrity checker
75
- which is a completely different thing, if you want to check if your site is
76
- generating malicious code then use the <a href="%%SUCURI.URL.Scanner%%">malware
77
- scan</a> tool. If you see the text <em>"must be fixed manually"</em> in any of
78
- these files that means that they do not have write permissions so you can not
79
- fix them using this tool.
80
- </p>
81
-
82
- <label>
83
- <select name="sucuriscan_integrity_action">
84
- <option value="">Choose action</option>
85
- <option value="restore">Restore file(s) content</option>
86
- <option value="delete">Delete file(s)</option>
87
- <option value="fixed">Mark as fixed</option>
88
- </select>
89
- </label>
90
-
91
- <button type="submit" class="button button-primary">Send action</button>
92
- </td>
93
- </tr>
94
- </tfoot>
95
- </table>
96
-
97
- </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/tpl/integrity-modifiedfiles.html.tpl CHANGED
@@ -4,66 +4,85 @@
4
  <h3>Modified Files</h3>
5
 
6
  <div class="inside">
7
-
8
  <p>
9
- If your site was recently attacked, you can see which files were modified to
10
- assist with any investigation. Note that in most Unix file systems, a file is
11
- considered modified when its inode data is changed; that is, when the
12
- permissions, owner, group, or other metadata from the inode is updated.
 
 
 
13
  </p>
14
 
15
- <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.ModifiedFiles.DisabledVisibility%%">
 
 
 
 
 
 
 
 
 
16
  <p>
17
- The scanner that searches for modified files under the content directory is
18
- disabled. This tool is disabled by default to prevent an overflow in the memory
19
- of the PHP interpreter in the majority of websites that have too many files in
20
- their projects, but you can enable this scanner from
21
- <a href="%%SUCURI.URL.Settings%%#settings-scanner">here</a> though, and if you
22
- experience issues like <em>"Internal Server Error"</em> messages or blank pages
23
- just disable the scanner again.
24
  </p>
25
  </div>
26
 
 
 
 
 
 
 
 
 
 
 
27
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  </div>
29
  </div>
30
 
31
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-table-double-title sucuriscan-modifiedfiles">
32
  <thead>
33
  <tr>
34
- <th colspan="3" class="thead-with-button">
35
  <span>Modified files <em>(inside the content directory)</em></span>
36
-
37
- <form action="%%SUCURI.CurrentURL%%#modified-files" method="post" class="thead-topright-action">
38
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
39
- <label>
40
- Modified in the last
41
- <select name="sucuriscan_last_days" id="sucuriscan_last_days">
42
- %%SUCURI.ModifiedFiles.SelectOptions%%
43
- </select>
44
- days
45
- </label>
46
-
47
- <!-- This field was added to give backward compatibility with the SiteCheck form. -->
48
- <input type="hidden" name="sucuriscan_malware_scan" value="1" />
49
- </form>
50
  </th>
51
  </tr>
52
 
53
  <tr>
54
- <th>File Path</th>
55
  <th width="100">File Size</th>
56
- <th width="190">Modified at</th>
57
  </tr>
58
  </thead>
59
 
60
  <tbody>
61
- %%SUCURI.ModifiedFiles.List%%
62
-
63
- <tr class="sucuriscan-%%SUCURI.ModifiedFiles.NoFilesVisibility%%">
64
- <td colspan="3">
65
- <em>No files modified in the last %%SUCURI.ModifiedFiles.Days%% days</em>
66
- </td>
67
- </tr>
68
  </tbody>
69
  </table>
4
  <h3>Modified Files</h3>
5
 
6
  <div class="inside">
 
7
  <p>
8
+ The scanner searches the WordPress content directory for files that were
9
+ modified during the number of days specified by the user in the request. If your
10
+ site was recently attacked, you can see which files were modified to assist with
11
+ any investigation. Other WordPress core directories are scanned automatically
12
+ with the core integrity checker, if you want to scan other directories that are
13
+ not part of the official WordPress packages you have to ask for assistance to
14
+ your hosting provider.
15
  </p>
16
 
17
+ <div class="sucuriscan-inline-alert-info">
18
+ <p>
19
+ Note that in most Unix file systems, a file is considered modified when its
20
+ inode data is changed; that is, when the permissions, owner, group, or other
21
+ metadata from the inode is updated. Considering this it may be possible to have
22
+ false-positives in the result.
23
+ </p>
24
+ </div>
25
+
26
+ <div class="sucuriscan-inline-alert-warning">
27
  <p>
28
+ Depending on the number of files stored in your website this operation may fail
29
+ due to limitations set by your hosting provider to keep the memory assignation
30
+ of the PHP scripts in certain numbers. If you have issues executing this tool
31
+ ask your hosting provider to assist you in the configuration of your website to
32
+ allow the execution of this script.
 
 
33
  </p>
34
  </div>
35
 
36
+ <form action="%%SUCURI.URL.Scanner%%" method="post" id="sucuriscan-modfiles-form">
37
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
38
+ <span class="sucuriscan-input-group">
39
+ <label>Search Files Modified</label>
40
+ <select id="sucuriscan_back_days">
41
+ %%%SUCURI.ModifiedFiles.SelectOptions%%%
42
+ </select>
43
+ </span>
44
+ <button id="sucuriscan-modfiles-button" class="button-primary">Proceed</button>
45
+ </form>
46
  </div>
47
+
48
+ <script type="text/javascript">
49
+ jQuery(function($){
50
+ $('#sucuriscan-modfiles-button').on('click', function(ev){
51
+ ev.preventDefault();
52
+ $('.sucuriscan-modifiedfiles tbody').html(
53
+ '<tr><td colspan="3"><span>Loading <em>(may take '
54
+ + 'several seconds)</em>...</span></td></tr>'
55
+ );
56
+ $.post('%%SUCURI.AjaxURL.Scanner%%', {
57
+ action: 'sucuriscan_scanner_ajax',
58
+ sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
59
+ form_action: 'get_modfiles',
60
+ }, function(data){
61
+ $('.sucuriscan-modifiedfiles tbody').html(data);
62
+ });
63
+ });
64
+ $('#sucuriscan-modfiles-button').click();
65
+ });
66
+ </script>
67
  </div>
68
  </div>
69
 
70
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-table-double-title sucuriscan-modifiedfiles">
71
  <thead>
72
  <tr>
73
+ <th colspan="3" class="sucuriscan-clearfix thead-with-button">
74
  <span>Modified files <em>(inside the content directory)</em></span>
75
+ <span class="thead-topright-action">&nbsp;</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  </th>
77
  </tr>
78
 
79
  <tr>
80
+ <th width="200">Modification</th>
81
  <th width="100">File Size</th>
82
+ <th>File Path</th>
83
  </tr>
84
  </thead>
85
 
86
  <tbody>
 
 
 
 
 
 
 
87
  </tbody>
88
  </table>
inc/tpl/integrity-modifiedfiles.snippet.tpl CHANGED
@@ -1,6 +1,6 @@
1
 
2
  <tr class="sucuriscan-wraptext %%SUCURI.ModifiedFiles.CssClass%%">
3
- <td><span class="sucuriscan-monospace">%%SUCURI.ModifiedFiles.FilePath%%</span></td>
4
- <td><em title="%%SUCURI.ModifiedFiles.FileSizeNumber%% bytes">~%%SUCURI.ModifiedFiles.FileSizeHuman%%</em></td>
5
  <td>%%SUCURI.ModifiedFiles.DateTime%%</td>
 
 
6
  </tr>
1
 
2
  <tr class="sucuriscan-wraptext %%SUCURI.ModifiedFiles.CssClass%%">
 
 
3
  <td>%%SUCURI.ModifiedFiles.DateTime%%</td>
4
+ <td><em title="%%SUCURI.ModifiedFiles.FileSizeNumber%% bytes">~%%SUCURI.ModifiedFiles.FileSizeHuman%%</em></td>
5
+ <td><span class="sucuriscan-monospace">%%SUCURI.ModifiedFiles.FilePath%%</span></td>
6
  </tr>
inc/tpl/integrity.html.tpl CHANGED
@@ -1,10 +1,10 @@
1
 
2
  <div id="poststuff">
3
- %%SUCURI.WordpressVersion%%
4
 
5
- %%SUCURI.CoreFiles%%
6
 
7
- %%SUCURI.AuditReports%%
8
 
9
- %%SUCURI.AuditLogs%%
10
  </div>
1
 
2
  <div id="poststuff">
3
+ %%%SUCURI.WordpressVersion%%%
4
 
5
+ %%%SUCURI.CoreFiles%%%
6
 
7
+ %%%SUCURI.AuditReports%%%
8
 
9
+ %%%SUCURI.AuditLogs%%%
10
  </div>
inc/tpl/lastlogins-admins.html.tpl CHANGED
@@ -10,6 +10,6 @@
10
  </thead>
11
 
12
  <tbody>
13
- %%SUCURI.AdminUsers.List%%
14
  </tbody>
15
  </table>
10
  </thead>
11
 
12
  <tbody>
13
+ %%%SUCURI.AdminUsers.List%%%
14
  </tbody>
15
  </table>
inc/tpl/lastlogins-admins.snippet.tpl CHANGED
@@ -4,18 +4,18 @@
4
  <td>%%SUCURI.AdminUsers.RegisteredAt%%</td>
5
  <td class="adminusers-lastlogin">
6
  <div class="sucuriscan-%%SUCURI.AdminUsers.NoLastLogins%%">
7
- <i>No data available for this account.</i>
8
  </div>
9
 
10
  <table class="widefat sucuriscan-admins-lastlogins sucuriscan-%%SUCURI.AdminUsers.NoLastLoginsTable%%">
11
  <thead>
12
  <tr>
13
  <th>IP Address</th>
14
- <th>Date & Time</th>
15
  </tr>
16
  </thead>
17
  <tbody>
18
- %%SUCURI.AdminUsers.LastLogins%%
19
  </tbody>
20
  </table>
21
  </td>
4
  <td>%%SUCURI.AdminUsers.RegisteredAt%%</td>
5
  <td class="adminusers-lastlogin">
6
  <div class="sucuriscan-%%SUCURI.AdminUsers.NoLastLogins%%">
7
+ <i>No data available.</i>
8
  </div>
9
 
10
  <table class="widefat sucuriscan-admins-lastlogins sucuriscan-%%SUCURI.AdminUsers.NoLastLoginsTable%%">
11
  <thead>
12
  <tr>
13
  <th>IP Address</th>
14
+ <th>Date &amp; Time</th>
15
  </tr>
16
  </thead>
17
  <tbody>
18
+ %%%SUCURI.AdminUsers.LastLogins%%%
19
  </tbody>
20
  </table>
21
  </td>
inc/tpl/lastlogins-all.html.tpl CHANGED
@@ -23,7 +23,7 @@
23
  </thead>
24
 
25
  <tbody>
26
- %%SUCURI.UserList%%
27
 
28
  <tr class="sucuriscan-%%SUCURI.UserList.NoItemsVisibility%%">
29
  <td colspan="6">
23
  </thead>
24
 
25
  <tbody>
26
+ %%%SUCURI.UserList%%%
27
 
28
  <tr class="sucuriscan-%%SUCURI.UserList.NoItemsVisibility%%">
29
  <td colspan="6">
inc/tpl/lastlogins-failedlogins.html.tpl CHANGED
@@ -6,11 +6,11 @@
6
  <div class="inside">
7
  <p>
8
  This information will be used to determine if your site is being victim of
9
- <a href="http://kb.sucuri.net/definitions/attacks/brute-force/password-guessing"
10
  target="_blank">Password Guessing Brute Force Attacks</a>. These logs will be
11
  accumulated and the plugin will send a report via email if there are more than
12
  <code>%%SUCURI.FailedLogins.MaxFailedLogins%%</code> failed login attempts during
13
- the same hour, you can change this number from <a href="%%SUCURI.URL.Settings%%#settings-general">here</a>.
14
  <strong>Note.</strong> Some <em>"Two-Factor Authentication"</em> plugins do not
15
  follow the same rules that WordPress have to report failed login attempts, so
16
  you may not see all the attempts in this panel if you have one of these plugins
@@ -19,11 +19,11 @@
19
 
20
  <div class="sucuriscan-inline-alert-warning sucuriscan-%%SUCURI.FailedLogins.WarningVisibility%%">
21
  <p>
22
- The option to alert possible <a href="http://kb.sucuri.net/definitions/attacks/brute-force/password-guessing"
23
  target="_blank">Password Guessing Brute Force Attacks</a> is disabled, you will
24
  not receive email reports with the attempts collected during the attacks, but
25
  you will continue receiving the alerts of failed logins if you have enabled that
26
- option. Go to the <a href="%%SUCURI.URL.Settings%%#settings-notifications">alert
27
  settings</a> panel to change this configuration.
28
  </p>
29
  </div>
@@ -34,7 +34,7 @@
34
  the security logs. If someone get access to your API key, or your server fails
35
  to process the PHP files (which is not usual but may happen) then an attacker
36
  may steal your credentials and get access to your site. Change this from the <a
37
- href="%%SUCURI.URL.Settings%%#settings-general">general settings</a>
38
  </p>
39
  </div>
40
  </div>
@@ -54,8 +54,7 @@
54
  </thead>
55
 
56
  <tbody>
57
-
58
- %%SUCURI.FailedLogins.List%%
59
 
60
  <tr class="sucuriscan-%%SUCURI.FailedLogins.NoItemsVisibility%%">
61
  <td colspan="6">
@@ -70,6 +69,5 @@
70
  </ul>
71
  </td>
72
  </tr>
73
-
74
  </tbody>
75
  </table>
6
  <div class="inside">
7
  <p>
8
  This information will be used to determine if your site is being victim of
9
+ <a href="https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing"
10
  target="_blank">Password Guessing Brute Force Attacks</a>. These logs will be
11
  accumulated and the plugin will send a report via email if there are more than
12
  <code>%%SUCURI.FailedLogins.MaxFailedLogins%%</code> failed login attempts during
13
+ the same hour, you can change this number from <a href="%%SUCURI.URL.Settings%%#general">here</a>.
14
  <strong>Note.</strong> Some <em>"Two-Factor Authentication"</em> plugins do not
15
  follow the same rules that WordPress have to report failed login attempts, so
16
  you may not see all the attempts in this panel if you have one of these plugins
19
 
20
  <div class="sucuriscan-inline-alert-warning sucuriscan-%%SUCURI.FailedLogins.WarningVisibility%%">
21
  <p>
22
+ The option to alert possible <a href="https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing"
23
  target="_blank">Password Guessing Brute Force Attacks</a> is disabled, you will
24
  not receive email reports with the attempts collected during the attacks, but
25
  you will continue receiving the alerts of failed logins if you have enabled that
26
+ option. Go to the <a href="%%SUCURI.URL.Settings%%#notifications">alert
27
  settings</a> panel to change this configuration.
28
  </p>
29
  </div>
34
  the security logs. If someone get access to your API key, or your server fails
35
  to process the PHP files (which is not usual but may happen) then an attacker
36
  may steal your credentials and get access to your site. Change this from the <a
37
+ href="%%SUCURI.URL.Settings%%#general">general settings</a>
38
  </p>
39
  </div>
40
  </div>
54
  </thead>
55
 
56
  <tbody>
57
+ %%%SUCURI.FailedLogins.List%%%
 
58
 
59
  <tr class="sucuriscan-%%SUCURI.FailedLogins.NoItemsVisibility%%">
60
  <td colspan="6">
69
  </ul>
70
  </td>
71
  </tr>
 
72
  </tbody>
73
  </table>
inc/tpl/lastlogins-failedlogins.snippet.tpl CHANGED
@@ -1,8 +1,8 @@
1
 
2
  <tr class="%%SUCURI.FailedLogins.CssClass%%">
3
  <td>%%SUCURI.FailedLogins.Num%%</td>
4
- <td>%%SUCURI.FailedLogins.Username%%</td>
5
- <td>%%SUCURI.FailedLogins.Password%%</td>
6
  <td><span class="sucuriscan-monospace">%%SUCURI.FailedLogins.RemoteAddr%%</span></td>
7
  <td><em>%%SUCURI.FailedLogins.Datetime%%</em></td>
8
  <td><div class="sucuriscan-wraptext">%%SUCURI.FailedLogins.UserAgent%%</div></td>
1
 
2
  <tr class="%%SUCURI.FailedLogins.CssClass%%">
3
  <td>%%SUCURI.FailedLogins.Num%%</td>
4
+ <td><span class="sucuriscan-monospace">%%SUCURI.FailedLogins.Username%%</span></td>
5
+ <td><span class="sucuriscan-label-%%SUCURI.FailedLogins.PasswordColor%%">%%SUCURI.FailedLogins.Password%%</span></td>
6
  <td><span class="sucuriscan-monospace">%%SUCURI.FailedLogins.RemoteAddr%%</span></td>
7
  <td><em>%%SUCURI.FailedLogins.Datetime%%</em></td>
8
  <td><div class="sucuriscan-wraptext">%%SUCURI.FailedLogins.UserAgent%%</div></td>
inc/tpl/lastlogins-loggedin.html.tpl CHANGED
@@ -15,6 +15,6 @@
15
  </thead>
16
 
17
  <tbody>
18
- %%SUCURI.LoggedInUsers.List%%
19
  </tbody>
20
  </table>
15
  </thead>
16
 
17
  <tbody>
18
+ %%%SUCURI.LoggedInUsers.List%%%
19
  </tbody>
20
  </table>
inc/tpl/lastlogins.html.tpl CHANGED
@@ -2,34 +2,34 @@
2
  <div class="sucuriscan-tabs">
3
  <ul>
4
  <li>
5
- <a href="#" data-tabname="lastlogins-allusers">All Users</a>
6
  </li>
7
  <li>
8
- <a href="#" data-tabname="lastlogins-admins">Admin Users</a>
9
  </li>
10
  <li>
11
- <a href="#" data-tabname="loggedin-users">Logged In Users</a>
12
  </li>
13
  <li>
14
- <a href="#" data-tabname="failed-logins">Failed Logins</a>
15
  </li>
16
  </ul>
17
 
18
  <div class="sucuriscan-tab-containers">
19
  <div id="sucuriscan-lastlogins-allusers">
20
- %%SUCURI.LastLogins.AllUsers%%
21
  </div>
22
 
23
  <div id="sucuriscan-lastlogins-admins">
24
- %%SUCURI.LastLogins.Admins%%
25
  </div>
26
 
27
  <div id="sucuriscan-loggedin-users">
28
- %%SUCURI.LoggedInUsers%%
29
  </div>
30
 
31
  <div id="sucuriscan-failed-logins">
32
- %%SUCURI.FailedLogins%%
33
  </div>
34
  </div>
35
  </div>
2
  <div class="sucuriscan-tabs">
3
  <ul>
4
  <li>
5
+ <a href="#lastlogins-allusers" data-tabname="lastlogins-allusers">All Users</a>
6
  </li>
7
  <li>
8
+ <a href="#lastlogins-admins" data-tabname="lastlogins-admins">Admin Users</a>
9
  </li>
10
  <li>
11
+ <a href="#loggedin-users" data-tabname="loggedin-users">Logged In Users</a>
12
  </li>
13
  <li>
14
+ <a href="#failed-logins" data-tabname="failed-logins">Failed Logins</a>
15
  </li>
16
  </ul>
17
 
18
  <div class="sucuriscan-tab-containers">
19
  <div id="sucuriscan-lastlogins-allusers">
20
+ %%%SUCURI.LastLogins.AllUsers%%%
21
  </div>
22
 
23
  <div id="sucuriscan-lastlogins-admins">
24
+ %%%SUCURI.LastLogins.Admins%%%
25
  </div>
26
 
27
  <div id="sucuriscan-loggedin-users">
28
+ %%%SUCURI.LoggedInUsers%%%
29
  </div>
30
 
31
  <div id="sucuriscan-failed-logins">
32
+ %%%SUCURI.FailedLogins%%%
33
  </div>
34
  </div>
35
  </div>
inc/tpl/malwarescan-resblacklist.html.tpl CHANGED
@@ -1,6 +1,5 @@
1
 
2
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-scanner-details">
3
-
4
  <thead>
5
  <tr>
6
  <th colspan="3" class="thead-with-button">
@@ -10,9 +9,6 @@
10
  </thead>
11
 
12
  <tbody>
13
-
14
- %%SUCURI.BlacklistStatusList%%
15
-
16
  </tbody>
17
-
18
  </table>
1
 
2
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-scanner-details">
 
3
  <thead>
4
  <tr>
5
  <th colspan="3" class="thead-with-button">
9
  </thead>
10
 
11
  <tbody>
12
+ %%%SUCURI.BlacklistStatusList%%%
 
 
13
  </tbody>
 
14
  </table>
inc/tpl/malwarescan-resmalware.html.tpl CHANGED
@@ -1,21 +1,17 @@
1
 
2
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-scanner-details">
3
-
4
  <thead>
5
-
6
  <tr>
7
  <th colspan="3" class="thead-with-button">
8
  <span>%%SUCURI.WebsiteStatus%%</span>
9
- <a href="http://sucuri.net/website-antivirus/" target="_blank"
10
  class="thead-topright-action button-primary sucuriscan-%%SUCURI.FixButtonVisibility%%">
11
  Request Malware Cleanup</a>
12
  </th>
13
  </tr>
14
-
15
  </thead>
16
 
17
  <tbody>
18
-
19
  <tr>
20
  <td colspan="3">
21
  <p>
@@ -23,7 +19,7 @@
23
  website to the SiteCheck API service this plugin will cache the results of every
24
  scan for <strong>%%SUCURI.CacheLifeTime%% seconds</strong>. If you want to get
25
  fresh results after modifications suggested by the scanner then go to the <a
26
- href="%%SUCURI.URL.Settings%%#settings-scanner">scanner settings</a> page and
27
  click the button in front of the label <em>"Reset sitecheck logs"</em>, then
28
  come back to this page and run a new malware scan. Note that SiteCheck may cache
29
  the results of the scan as well in its own server and there is no way you can
@@ -32,7 +28,7 @@
32
  </td>
33
  </tr>
34
 
35
- %%SUCURI.MalwarePayloadList%%
36
 
37
  <tr class="sucuriscan-%%SUCURI.NoMalwareRowVisibility%%">
38
  <td><span class="sucuriscan-label sucuriscan-label-success">CLEAN</span></td>
@@ -42,7 +38,7 @@
42
  <tr class="sucuriscan-%%SUCURI.NoMalwareRowVisibility%%">
43
  <td><span class="sucuriscan-label sucuriscan-label-success">CLEAN</span></td>
44
  <td width="220">
45
- <a href="http://kb.sucuri.net/malware/encoded-javascript" target="_blank">
46
  Malicious javascript
47
  </a>
48
  </td>
@@ -59,7 +55,7 @@
59
  <tr class="sucuriscan-%%SUCURI.NoMalwareRowVisibility%%">
60
  <td><span class="sucuriscan-label sucuriscan-label-success">CLEAN</span></td>
61
  <td width="220">
62
- <a href="http://kb.sucuri.net/malware/malicious-iframes" target="_blank">
63
  Malicious iframes
64
  </a>
65
  </td>
@@ -77,7 +73,7 @@
77
  <tr class="sucuriscan-%%SUCURI.NoMalwareRowVisibility%%">
78
  <td><span class="sucuriscan-label sucuriscan-label-success">CLEAN</span></td>
79
  <td width="220">
80
- <a href="http://kb.sucuri.net/malware/conditional-redirections" target="_blank">
81
  Suspicious redirections (htaccess)
82
  </a>
83
  </td>
@@ -103,18 +99,16 @@
103
 
104
  <tr>
105
  <td colspan="3">
106
- <hr/>
107
  <em>
108
- More details at <a href="http://sitecheck.sucuri.net/results/%%SUCURI.ScannedDomainName%%"
109
  target="_blank">SiteCheck/%%SUCURI.ScannedDomainName%%</a>. If our free scanner
110
  did not detect any issue, you may have a more complicated and hidden problem.
111
- You can <a href="http://sucuri.net/signup" target="_blank">sign up</a> with
112
  Sucuri for a complete and in depth scan+cleanup <strong>(not included in the
113
  free checks)</strong>.
114
  </em>
115
  </td>
116
  </tr>
117
-
118
  </tbody>
119
-
120
  </table>
1
 
2
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-scanner-details">
 
3
  <thead>
 
4
  <tr>
5
  <th colspan="3" class="thead-with-button">
6
  <span>%%SUCURI.WebsiteStatus%%</span>
7
+ <a href="https://sucuri.net/website-antivirus/" target="_blank"
8
  class="thead-topright-action button-primary sucuriscan-%%SUCURI.FixButtonVisibility%%">
9
  Request Malware Cleanup</a>
10
  </th>
11
  </tr>
 
12
  </thead>
13
 
14
  <tbody>
 
15
  <tr>
16
  <td colspan="3">
17
  <p>
19
  website to the SiteCheck API service this plugin will cache the results of every
20
  scan for <strong>%%SUCURI.CacheLifeTime%% seconds</strong>. If you want to get
21
  fresh results after modifications suggested by the scanner then go to the <a
22
+ href="%%SUCURI.URL.Settings%%#scanner">scanner settings</a> page and
23
  click the button in front of the label <em>"Reset sitecheck logs"</em>, then
24
  come back to this page and run a new malware scan. Note that SiteCheck may cache
25
  the results of the scan as well in its own server and there is no way you can
28
  </td>
29
  </tr>
30
 
31
+ %%%SUCURI.MalwarePayloadList%%%
32
 
33
  <tr class="sucuriscan-%%SUCURI.NoMalwareRowVisibility%%">
34
  <td><span class="sucuriscan-label sucuriscan-label-success">CLEAN</span></td>
38
  <tr class="sucuriscan-%%SUCURI.NoMalwareRowVisibility%%">
39
  <td><span class="sucuriscan-label sucuriscan-label-success">CLEAN</span></td>
40
  <td width="220">
41
+ <a href="https://kb.sucuri.net/malware/encoded-javascript" target="_blank">
42
  Malicious javascript
43
  </a>
44
  </td>
55
  <tr class="sucuriscan-%%SUCURI.NoMalwareRowVisibility%%">
56
  <td><span class="sucuriscan-label sucuriscan-label-success">CLEAN</span></td>
57
  <td width="220">
58
+ <a href="https://kb.sucuri.net/malware/malicious-iframes" target="_blank">
59
  Malicious iframes
60
  </a>
61
  </td>
73
  <tr class="sucuriscan-%%SUCURI.NoMalwareRowVisibility%%">
74
  <td><span class="sucuriscan-label sucuriscan-label-success">CLEAN</span></td>
75
  <td width="220">
76
+ <a href="https://kb.sucuri.net/malware/conditional-redirections" target="_blank">
77
  Suspicious redirections (htaccess)
78
  </a>
79
  </td>
99
 
100
  <tr>
101
  <td colspan="3">
102
+ <hr>
103
  <em>
104
+ More details at <a href="https://sitecheck.sucuri.net/results/%%SUCURI.ScannedDomainName%%"
105
  target="_blank">SiteCheck/%%SUCURI.ScannedDomainName%%</a>. If our free scanner
106
  did not detect any issue, you may have a more complicated and hidden problem.
107
+ You can <a href="https://sucuri.net/signup" target="_blank">sign up</a> with
108
  Sucuri for a complete and in depth scan+cleanup <strong>(not included in the
109
  free checks)</strong>.
110
  </em>
111
  </td>
112
  </tr>
 
113
  </tbody>
 
114
  </table>
inc/tpl/malwarescan-resmalware.snippet.tpl CHANGED
@@ -1,6 +1,5 @@
1
 
2
  <tr>
3
-
4
  <td>
5
  <a href="%%SUCURI.MalwareDocs%%" target="_blank">%%SUCURI.AlertMessage%%</a>
6
  </td>
@@ -23,5 +22,4 @@
23
  <div class="sucuriscan-malware-payload sucuriscan-monospace">%%SUCURI.MalwarePayload%%</div>
24
  </div>
25
  </td>
26
-
27
  </tr>
1
 
2
  <tr>
 
3
  <td>
4
  <a href="%%SUCURI.MalwareDocs%%" target="_blank">%%SUCURI.AlertMessage%%</a>
5
  </td>
22
  <div class="sucuriscan-malware-payload sucuriscan-monospace">%%SUCURI.MalwarePayload%%</div>
23
  </div>
24
  </td>
 
25
  </tr>
inc/tpl/malwarescan-results.html.tpl CHANGED
@@ -1,51 +1,48 @@
1
- <div class="sucuriscan-tabs">
2
 
 
3
  <ul>
4
  <li class="%%SUCURI.ScannerResults.CssClass%%">
5
- <a href="#" data-tabname="sitecheck-results">Remote Scanner Results</a>
6
  </li>
7
  <li class="%%SUCURI.WebsiteDetails.CssClass%%">
8
- <a href="#" data-tabname="website-details">Website Details</a>
9
  </li>
10
  <li class="%%SUCURI.WebsiteLinks.CssClass%%">
11
- <a href="#" data-tabname="website-links">IFrames / Links / Scripts</a>
12
  </li>
13
  <li class="%%SUCURI.BlacklistStatus.CssClass%%">
14
- <a href="#" data-tabname="blacklist-status">Blacklist Status</a>
15
  </li>
16
  <li class="%%SUCURI.ModifiedFiles.CssClass%%">
17
- <a href="#" data-tabname="modified-files">Modified Files</a>
18
  </li>
19
  </ul>
20
 
21
  <div class="sucuriscan-tab-containers">
22
-
23
  <div id="sucuriscan-sitecheck-results">
24
- %%SUCURI.ScannerResults.Content%%
25
  </div>
26
 
27
  <div id="sucuriscan-website-details">
28
- %%SUCURI.WebsiteDetails.Content%%
29
  </div>
30
 
31
  <div id="sucuriscan-website-links">
32
- %%SUCURI.WebsiteLinks.Content%%
33
  </div>
34
 
35
  <div id="sucuriscan-blacklist-status">
36
- %%SUCURI.BlacklistStatus.Content%%
37
  </div>
38
 
39
  <div id="sucuriscan-modified-files">
40
- %%SUCURI.ModifiedFiles.Content%%
41
  </div>
42
-
43
  </div>
44
-
45
  </div>
46
 
47
  <div>
48
- <a href="http://sucuri.net/signup/" target="_blank"
49
  class="button button-primary button-hero sucuriscan-cleanup-btn
50
  sucuriscan-btnblock sucuriscan-%%SUCURI.SignupButtonVisibility%%">
51
  Get your site protected with Sucuri</a>
 
1
 
2
+ <div class="sucuriscan-tabs">
3
  <ul>
4
  <li class="%%SUCURI.ScannerResults.CssClass%%">
5
+ <a href="#sitecheck-results" data-tabname="sitecheck-results">Remote Scanner Results</a>
6
  </li>
7
  <li class="%%SUCURI.WebsiteDetails.CssClass%%">
8
+ <a href="#website-details" data-tabname="website-details">Website Details</a>
9
  </li>
10
  <li class="%%SUCURI.WebsiteLinks.CssClass%%">
11
+ <a href="#website-links" data-tabname="website-links">IFrames / Links / Scripts</a>
12
  </li>
13
  <li class="%%SUCURI.BlacklistStatus.CssClass%%">
14
+ <a href="#blacklist-status" data-tabname="blacklist-status">Blacklist Status</a>
15
  </li>
16
  <li class="%%SUCURI.ModifiedFiles.CssClass%%">
17
+ <a href="#modified-files" data-tabname="modified-files">Modified Files</a>
18
  </li>
19
  </ul>
20
 
21
  <div class="sucuriscan-tab-containers">
 
22
  <div id="sucuriscan-sitecheck-results">
23
+ %%%SUCURI.ScannerResults.Content%%%
24
  </div>
25
 
26
  <div id="sucuriscan-website-details">
27
+ %%%SUCURI.WebsiteDetails.Content%%%
28
  </div>
29
 
30
  <div id="sucuriscan-website-links">
31
+ %%%SUCURI.WebsiteLinks.Content%%%
32
  </div>
33
 
34
  <div id="sucuriscan-blacklist-status">
35
+ %%%SUCURI.BlacklistStatus.Content%%%
36
  </div>
37
 
38
  <div id="sucuriscan-modified-files">
39
+ %%%SUCURI.ModifiedFiles.Content%%%
40
  </div>
 
41
  </div>
 
42
  </div>
43
 
44
  <div>
45
+ <a href="https://sucuri.net/signup/" target="_blank"
46
  class="button button-primary button-hero sucuriscan-cleanup-btn
47
  sucuriscan-btnblock sucuriscan-%%SUCURI.SignupButtonVisibility%%">
48
  Get your site protected with Sucuri</a>
inc/tpl/malwarescan-reswebdetails.html.tpl CHANGED
@@ -1,8 +1,6 @@
1
 
2
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-scanner-details">
3
-
4
  <thead>
5
-
6
  <tr>
7
  <th colspan="2" class="thead-with-button">
8
  <span>System information</span>
@@ -14,22 +12,20 @@
14
  </a>
15
  </th>
16
  </tr>
17
-
18
  </thead>
19
 
20
  <tbody>
21
-
22
  <!-- List of generic information from the site. -->
23
- %%SUCURI.GenericInformationList%%
24
 
25
  <!-- List of application details from the site. -->
26
  <tr>
27
  <th colspan="2">Web application details</th>
28
  </tr>
29
 
30
- %%SUCURI.ApplicationDetailsList%%
31
 
32
- %%SUCURI.SystemNoticeList%%
33
 
34
  <tr class="sucuriscan-%%SUCURI.NoAppDetailsVisibility%%">
35
  <td colspan="2"><em>No more information was found.</em></td>
@@ -41,11 +37,9 @@
41
  </tr>
42
 
43
  <!-- Possible outdated software on the site. -->
44
- %%SUCURI.OutdatedSoftwareList%%
45
 
46
  <!-- Possible recommendations for the site. -->
47
- %%SUCURI.SecurityRecomendationList%%
48
-
49
  </tbody>
50
-
51
  </table>
1
 
2
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-scanner-details">
 
3
  <thead>
 
4
  <tr>
5
  <th colspan="2" class="thead-with-button">
6
  <span>System information</span>
12
  </a>
13
  </th>
14
  </tr>
 
15
  </thead>
16
 
17
  <tbody>
 
18
  <!-- List of generic information from the site. -->
19
+ %%%SUCURI.GenericInformationList%%%
20
 
21
  <!-- List of application details from the site. -->
22
  <tr>
23
  <th colspan="2">Web application details</th>
24
  </tr>
25
 
26
+ %%%SUCURI.ApplicationDetailsList%%%
27
 
28
+ %%%SUCURI.SystemNoticeList%%%
29
 
30
  <tr class="sucuriscan-%%SUCURI.NoAppDetailsVisibility%%">
31
  <td colspan="2"><em>No more information was found.</em></td>
37
  </tr>
38
 
39
  <!-- Possible outdated software on the site. -->
40
+ %%%SUCURI.OutdatedSoftwareList%%%
41
 
42
  <!-- Possible recommendations for the site. -->
43
+ %%%SUCURI.SecurityRecomendationList%%%
 
44
  </tbody>
 
45
  </table>
inc/tpl/malwarescan-resweblinks.html.tpl CHANGED
@@ -1,13 +1,10 @@
1
 
2
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-scanner-links">
3
  <tbody>
4
-
5
- %%SUCURI.WebsiteLinksAllList%%
6
 
7
  <tr class="sucuriscan-%%SUCURI.NoLinksVisibility%%">
8
  <td><em>No iFrames, links, or script files were found.</em></td>
9
  </tr>
10
-
11
  </tbody>
12
-
13
  </table>
1
 
2
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-scanner-links">
3
  <tbody>
4
+ %%%SUCURI.WebsiteLinksAllList%%%
 
5
 
6
  <tr class="sucuriscan-%%SUCURI.NoLinksVisibility%%">
7
  <td><em>No iFrames, links, or script files were found.</em></td>
8
  </tr>
 
9
  </tbody>
 
10
  </table>
inc/tpl/malwarescan-weblinktitle.snippet.tpl CHANGED
@@ -6,4 +6,4 @@
6
  </th>
7
  </tr>
8
 
9
- %%SUCURI.WebsiteLinksSectionItems%%
6
  </th>
7
  </tr>
8
 
9
+ %%%SUCURI.WebsiteLinksSectionItems%%%
inc/tpl/malwarescan.html.tpl CHANGED
@@ -2,7 +2,7 @@
2
  <div class="sucuriscan-loading">
3
  <h3 class="title">Website Security Scans by Sucuri Sitecheck</h3>
4
 
5
- <p class="description">Visit our <a href="http://sucuri.net/signup?fromloader" target="_blank">coverage &amp; pricing</a> page for details on how sucuri can help you.</p>
6
 
7
  <form action="%%SUCURI.URL.Scanner%%" method="post" class="sucuriscan-sitecheck-form">
8
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
@@ -24,11 +24,11 @@
24
 
25
  <div class="sucuriscan-sitecheck-disclaimer">
26
  <p>
27
- The malware scanner is a free tool powered by <a href="http://sitecheck.sucuri.net/" target="_blank">
28
  Sucuri SiteCheck</a>, it will check your website for known malware, blacklisting
29
  status, website errors, and out-of-date software. Although we do our best to
30
  provide the best results, 100% accuracy is not realistic, and not guaranteed.
31
- You can also <a href="%%SUCURI.URL.Settings%%#settings-scanner">disable this
32
  feature</a> from the settings page if you do not want to allow any of your
33
  registered users to use it.
34
  </p>
2
  <div class="sucuriscan-loading">
3
  <h3 class="title">Website Security Scans by Sucuri Sitecheck</h3>
4
 
5
+ <p class="description">Visit our <a href="https://sucuri.net/signup?fromloader" target="_blank">coverage &amp; pricing</a> page for details on how sucuri can help you.</p>
6
 
7
  <form action="%%SUCURI.URL.Scanner%%" method="post" class="sucuriscan-sitecheck-form">
8
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
24
 
25
  <div class="sucuriscan-sitecheck-disclaimer">
26
  <p>
27
+ The malware scanner is a free tool powered by <a href="https://sitecheck.sucuri.net/" target="_blank">
28
  Sucuri SiteCheck</a>, it will check your website for known malware, blacklisting
29
  status, website errors, and out-of-date software. Although we do our best to
30
  provide the best results, 100% accuracy is not realistic, and not guaranteed.
31
+ You can also <a href="%%SUCURI.URL.Settings%%#scanner">disable this
32
  feature</a> from the settings page if you do not want to allow any of your
33
  registered users to use it.
34
  </p>
inc/tpl/modalwindow.html.tpl CHANGED
@@ -13,8 +13,7 @@
13
  </div>
14
 
15
  <div class="sucuriscan-modal-inside">
16
- %%SUCURI.Content%%
17
  </div>
18
-
19
  </div>
20
  </div>
13
  </div>
14
 
15
  <div class="sucuriscan-modal-inside">
16
+ %%%SUCURI.Content%%%
17
  </div>
 
18
  </div>
19
  </div>
inc/tpl/monitoring-logs.html.tpl DELETED
@@ -1,73 +0,0 @@
1
-
2
- <table class="wp-list-table widefat sucuriscan-table sucuriscan-table-quad-title sucuriscan-monitoring-logs">
3
- <thead>
4
- <tr>
5
- <th colspan="4" class="thead-with-button">
6
- <span>Search among the logs:</span>
7
- <div class="thead-topright-action">
8
- <form action="%%SUCURI.URL.Monitoring%%#monitoring-logs" method="post" class="sucuriscan-monitoring-search-form">
9
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
- <input type="text" name="sucuriscan_monitoring_log_filter" class="input-text" />
11
- <input type="submit" value="Search" class="button button-primary" />
12
- </form>
13
- </div>
14
- </th>
15
- </tr>
16
-
17
- <tr>
18
- <th colspan="4" class="thead-with-button">
19
- <span>Filter by the denial type:</span>
20
- <div class="thead-topright-action">
21
- <form action="%%SUCURI.URL.Monitoring%%#monitoring-logs" method="post" class="sucuriscan-monitoring-denial-types-form">
22
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
23
- <select name="sucuriscan_monitoring_denial_type">%%SUCURI.AuditLogs.DenialTypeOptions%%</select>
24
- <input type="submit" value="Filter" class="button button-primary" />
25
- </form>
26
- </div>
27
- </th>
28
- </tr>
29
-
30
- <tr>
31
- <th colspan="4" class="thead-with-button">
32
- <span>Filter by date:</span>
33
- <div class="thead-topright-action">
34
- <form action="%%SUCURI.URL.Monitoring%%#monitoring-logs" method="post" class="sucuriscan-monitoring-date-form">
35
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
36
- <input type="hidden" name="sucuriscan_monitoring_date" value="1" />
37
- <em class="sucuriscan-target-date">(%%SUCURI.AuditLogs.TargetDate%%)</em>
38
- <select name="sucuriscan_year">%%SUCURI.AuditLogs.DateYears%%</select>
39
- <select name="sucuriscan_month">%%SUCURI.AuditLogs.DateMonths%%</select>
40
- <select name="sucuriscan_day">%%SUCURI.AuditLogs.DateDays%%</select>
41
- <input type="submit" value="Filter" class="button button-primary" />
42
- </form>
43
- </div>
44
- </th>
45
- </tr>
46
-
47
- <tr>
48
- <th width="250">Denial Type</th>
49
- <th width="120">Remote Address</th>
50
- <th>Request Path</th>
51
- </tr>
52
- </thead>
53
-
54
- <tbody>
55
- %%SUCURI.AuditLogs.List%%
56
-
57
- <tr class="sucuriscan-%%SUCURI.AuditLogs.NoItemsVisibility%%">
58
- <td colspan="4">
59
- <em>Audit trails is empty.</em>
60
- </td>
61
- </tr>
62
- </tbody>
63
-
64
- <tfoot>
65
- <tr class="sucuriscan-%%SUCURI.AuditLogs.PaginationVisibility%%">
66
- <td colspan="4">
67
- <div class='pagination' style="float:right;">
68
- %%SUCURI.AuditLogs.AuditPagination%%
69
- </div>
70
- </td>
71
- </tr>
72
- </tfoot>
73
- </table>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/tpl/monitoring-logs.snippet.tpl DELETED
@@ -1,71 +0,0 @@
1
-
2
- <tr class="%%SUCURI.AuditLog.CssClass%%">
3
- <td>
4
- <span class="sucuriscan-denial-type">%%SUCURI.AuditLog.SucuriBlockReason%%</span><br>
5
- <span class="sucuriscan-denial-type-date">Date/Time: %%SUCURI.AuditLog.LocalRequestTime%%</span>
6
- </td>
7
- <td><span class="sucuriscan-monospace">%%SUCURI.AuditLog.RemoteAddr%%</span></td>
8
- <td>
9
- <div class="sucuriscan-wraptext">
10
- <a href="#TB_inline?width=600&height=300&inlineId=sucuriscan-reqsummary-%%SUCURI.AuditLog.Id%%" title="Access Log Summary" class="thickbox">
11
- <span class="sucuriscan-monospace">%%SUCURI.AuditLog.ResourcePath%%</span>
12
- </a>
13
- </div>
14
-
15
- <div id="sucuriscan-reqsummary-%%SUCURI.AuditLog.Id%%" style="display:none">
16
- <div class="sucuriscan-request-summary">
17
- <table class="wp-list-table widefat">
18
- <thead>
19
- <tr>
20
- <th width="200">Information</th>
21
- <th>&nbsp;</th>
22
- </tr>
23
- </thead>
24
-
25
- <tbody>
26
- <tr class="alternate">
27
- <td>Blocked Reason</td>
28
- <td class="sucuriscan-monospace">%%SUCURI.AuditLog.SucuriBlockReason%%</td>
29
- </tr>
30
- <tr>
31
- <td>Remote Address</td>
32
- <td class="sucuriscan-monospace">%%SUCURI.AuditLog.RemoteAddr%%</td>
33
- </tr>
34
- <tr class="alternate">
35
- <td>Date &amp; Time (Local Time)</td>
36
- <td class="sucuriscan-monospace">%%SUCURI.AuditLog.LocalRequestTime%%</td>
37
- </tr>
38
- <tr>
39
- <td>Resource Path</td>
40
- <td class="sucuriscan-monospace">%%SUCURI.AuditLog.ResourcePath%%</td>
41
- </tr>
42
- <tr class="alternate">
43
- <td>Request Method</td>
44
- <td class="sucuriscan-monospace">%%SUCURI.AuditLog.RequestMethod%%</td>
45
- </tr>
46
- <tr>
47
- <td>HTTP Protocol</td>
48
- <td class="sucuriscan-monospace">%%SUCURI.AuditLog.HttpProtocol%%</td>
49
- </tr>
50
- <tr class="alternate">
51
- <td>HTTP Status</td>
52
- <td class="sucuriscan-monospace">%%SUCURI.AuditLog.HttpStatus%% %%SUCURI.AuditLog.HttpStatusTitle%%</td>
53
- </tr>
54
- <tr>
55
- <td>HTTP Bytes Sent</td>
56
- <td class="sucuriscan-monospace">%%SUCURI.AuditLog.HttpBytesSent%%</td>
57
- </tr>
58
- <tr class="alternate">
59
- <td>HTTP Referer</td>
60
- <td class="sucuriscan-monospace">%%SUCURI.AuditLog.HttpReferer%%</td>
61
- </tr>
62
- <tr>
63
- <td>HTTP User Agent</td>
64
- <td class="sucuriscan-monospace">%%SUCURI.AuditLog.HttpUserAgent%%</td>
65
- </tr>
66
- </tbody>
67
- </table>
68
- </div>
69
- </div>
70
- </td>
71
- </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/tpl/monitoring-settings.html.tpl DELETED
@@ -1,28 +0,0 @@
1
-
2
- <table class="wp-list-table widefat sucuriscan-table sucuriscan-monitoring-settings">
3
- <tbody>
4
- <tr>
5
- <td width="200"><label>CloudProxy API key</label></td>
6
- <td class="td-with-button">
7
- <form action="%%SUCURI.URL.Monitoring%%#monitoring-settings" method="post" class="sucuriscan-monitoring-apikey-form">
8
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
9
- <input type="text" name="sucuriscan_cloudproxy_apikey" value="%%SUCURI.Monitoring.APIKey%%" class="input-text" />
10
- <input type="submit" value="Save" class="button button-primary" />
11
- </form>
12
- </td>
13
- </tr>
14
-
15
- %%SUCURI.Monitoring.SettingOptions%%
16
-
17
- <tr class="alternate sucuriscan-%%SUCURI.Monitoring.SettingsVisibility%%">
18
- <td><label>Clear cache</label></td>
19
- <td class="td-with-button">
20
- <form action="%%SUCURI.URL.Monitoring%%#monitoring-settings" method="post" class="sucuriscan-monitoring-clear-cache-form">
21
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
22
- <input type="hidden" name="sucuriscan_clear_cache" value="1" />
23
- <input type="submit" value="Clear Cache" class="button button-primary" />
24
- </form>
25
- </td>
26
- </tr>
27
- </tbody>
28
- </table>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/tpl/monitoring-settings.snippet.tpl DELETED
@@ -1,5 +0,0 @@
1
-
2
- <tr class="%%SUCURI.Monitoring.OptionCssClass%%">
3
- <td><label>%%SUCURI.Monitoring.OptionName%%</label></td>
4
- <td><span class="sucuriscan-monospace">%%SUCURI.Monitoring.OptionValue%%</span></td>
5
- </tr>
 
 
 
 
 
inc/tpl/monitoring.html.tpl DELETED
@@ -1,58 +0,0 @@
1
-
2
- <div id="poststuff">
3
-
4
- <div class="postbox sucuriscan-border sucuriscan-border-info sucuriscan-%%SUCURI.Monitoring.InstructionsVisibility%%">
5
- <h3>Activation instructions</h3>
6
-
7
- <div class="inside">
8
- <p>
9
- A powerful <b>WAF</b> <em>(Web Application Firewall)</em> and <b>Intrusion
10
- Prevention</b> system for any WordPress user and many other platforms. This page
11
- will help you to configure and monitor your site through <strong>Sucuri
12
- CloudProxy</strong>. Once enabled, our firewall will act as a shield, protecting
13
- your site from attacks and preventing malware infections and reinfections. It
14
- will block SQL injection attempts, brute force attacks, XSS, RFI, backdoors and
15
- many other threats against your site.
16
- </p>
17
-
18
- <p>
19
- Add your <strong>API key</strong> in the form bellow, click in the
20
- <em>activate</em> button and after that your site will start communicating with
21
- the official CloudProxy API service. Your account settings, whitelisted IP
22
- addresses, audit logs, and the cache status will be displayed when the API key
23
- is validated.
24
- </p>
25
-
26
- <p>
27
- <em>[1]</em> More information about <a href="http://cloudproxy.sucuri.net/" target="_blank">CloudProxy</a>.<br>
28
- <em>[2]</em> Configuration instructions and videos in the official <a href="http://kb.sucuri.net/cloudproxy"
29
- target="_blank">Knowledge Base</a> site.<br>
30
- <em>[3]</em> <a href="https://login.sucuri.net/signup2/create?CloudProxy" target="_blank">Sign up</a> for a new
31
- account and start protecting your site with CloudProxy.
32
- </p>
33
- </div>
34
- </div>
35
-
36
-
37
- <div class="sucuriscan-tabs">
38
- <ul>
39
- <li>
40
- <a href="#" data-tabname="monitoring-settings">Firewall (WAF) Settings</a>
41
- </li>
42
- <li>
43
- <a href="#" data-tabname="monitoring-logs">Firewall (WAF) Logs</a>
44
- </li>
45
- </ul>
46
-
47
- <div class="sucuriscan-tab-containers">
48
- <div id="sucuriscan-monitoring-settings">
49
- %%SUCURI.Monitoring.Settings%%
50
- </div>
51
-
52
- <div id="sucuriscan-monitoring-logs">
53
- %%SUCURI.Monitoring.Logs%%
54
- </div>
55
- </div>
56
- </div>
57
-
58
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/tpl/notification-admin.html.tpl ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+
2
+ <div id="sucuriscan-alert-%%SUCURI.AlertUnique%%" class="%%SUCURI.AlertType%% sucuriscan-alert sucuriscan-alert-%%SUCURI.AlertType%%">
3
+ <a href="javascript:void(0)" class="close" onclick="sucuriscan_alert_close('%%SUCURI.AlertUnique%%')">&times;</a>
4
+ <p>%%%SUCURI.AlertMessage%%%</p>
5
+ </div>
inc/tpl/notification-pretty.html.tpl CHANGED
@@ -3,7 +3,7 @@
3
  <thead sytle="border-bottom:1px solid #ccc">
4
  <tr style="background-color:#4b4b4b;background-image:-moz-linear-gradient(top, #555555, #3b3b3b);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#3b3b3b));background-image:-webkit-linear-gradient(top, #555555, #3b3b3b);background-image:-o-linear-gradient(top, #555555, #3b3b3b);background-image:linear-gradient(to bottom, #555555, #3b3b3b);background-repeat:repeat-x">
5
  <td sytle="font-size:20px;font-weight:normal;color:#ffffff;padding:10px;border-right:1px solid #2f2f2f;border-left:1px solid #6f6f6f;-webkit-box-shadow:inset 0 1px 0 #888888;-moz-box-shadow:inset 0 1px 0 #888888;box-shadow:inset 0 1px 0 #888888;text-shadow:1px 1px 2px rgba(0, 0, 0, 0.5)">
6
- <a href="http://sucuri.net/" style="text-decoration:none;display:inline-block;margin:8px 0 5px 20px">
7
  <img src="%%SUCURI.SucuriURL%%/inc/images/main-logo.png" alt="Sucuri, Inc." style="border:none" />
8
  </a>
9
  <span style="display:inline-block;line-height:46px;margin:0 20px 0 0;float:right;color:#ffffff">%%SUCURI.TemplateTitle%%</span>
@@ -24,7 +24,7 @@
24
  IP Address: %%SUCURI.RemoteAddress%%<br>
25
  </p>
26
  <h4 style="text-transform:uppercase;margin:0">Notification Message:</h4>
27
- <p style="margin:0 0 10px 0">%%SUCURI.Message%%</p>
28
  </td>
29
  </tr>
30
  </tbody>
3
  <thead sytle="border-bottom:1px solid #ccc">
4
  <tr style="background-color:#4b4b4b;background-image:-moz-linear-gradient(top, #555555, #3b3b3b);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#3b3b3b));background-image:-webkit-linear-gradient(top, #555555, #3b3b3b);background-image:-o-linear-gradient(top, #555555, #3b3b3b);background-image:linear-gradient(to bottom, #555555, #3b3b3b);background-repeat:repeat-x">
5
  <td sytle="font-size:20px;font-weight:normal;color:#ffffff;padding:10px;border-right:1px solid #2f2f2f;border-left:1px solid #6f6f6f;-webkit-box-shadow:inset 0 1px 0 #888888;-moz-box-shadow:inset 0 1px 0 #888888;box-shadow:inset 0 1px 0 #888888;text-shadow:1px 1px 2px rgba(0, 0, 0, 0.5)">
6
+ <a href="https://sucuri.net/" style="text-decoration:none;display:inline-block;margin:8px 0 5px 20px">
7
  <img src="%%SUCURI.SucuriURL%%/inc/images/main-logo.png" alt="Sucuri, Inc." style="border:none" />
8
  </a>
9
  <span style="display:inline-block;line-height:46px;margin:0 20px 0 0;float:right;color:#ffffff">%%SUCURI.TemplateTitle%%</span>
24
  IP Address: %%SUCURI.RemoteAddress%%<br>
25
  </p>
26
  <h4 style="text-transform:uppercase;margin:0">Notification Message:</h4>
27
+ <p style="margin:0 0 10px 0">%%%SUCURI.Message%%%</p>
28
  </td>
29
  </tr>
30
  </tbody>
inc/tpl/notification-resetpwd.html.tpl CHANGED
@@ -3,6 +3,7 @@ Your password for <strong>%%SUCURI.ResetPassword.UserName%%</strong>
3
  <em>(%%SUCURI.ResetPassword.DisplayName%%)</em> at <a target="_blank"
4
  href="http://%%SUCURI.ResetPassword.Website%%">%%SUCURI.ResetPassword.Website%%</a>
5
  has been reset for security reasons.<br>
 
6
  You can use this temporary password to log in:
7
  <span style="display:inline-block;background:#f5f5f5;padding:2px 6px;
8
  font-family:Menlo, Monaco, monospace, serif;border:1px solid #ddd">
3
  <em>(%%SUCURI.ResetPassword.DisplayName%%)</em> at <a target="_blank"
4
  href="http://%%SUCURI.ResetPassword.Website%%">%%SUCURI.ResetPassword.Website%%</a>
5
  has been reset for security reasons.<br>
6
+
7
  You can use this temporary password to log in:
8
  <span style="display:inline-block;background:#f5f5f5;padding:2px 6px;
9
  font-family:Menlo, Monaco, monospace, serif;border:1px solid #ddd">
inc/tpl/posthack-resetpassword.html.tpl CHANGED
@@ -36,12 +36,12 @@
36
  </thead>
37
 
38
  <tbody>
39
- %%SUCURI.ResetPassword.UserList%%
40
 
41
  <tr class="sucuriscan-%%SUCURI.ResetPassword.PaginationVisibility%%">
42
  <td colspan="4">
43
  <ul class="sucuriscan-pagination">
44
- %%SUCURI.ResetPassword.PaginationLinks%%
45
  </ul>
46
  </td>
47
  </tr>
36
  </thead>
37
 
38
  <tbody>
39
+ %%%SUCURI.ResetPassword.UserList%%%
40
 
41
  <tr class="sucuriscan-%%SUCURI.ResetPassword.PaginationVisibility%%">
42
  <td colspan="4">
43
  <ul class="sucuriscan-pagination">
44
+ %%%SUCURI.ResetPassword.PaginationLinks%%%
45
  </ul>
46
  </td>
47
  </tr>
inc/tpl/posthack-resetplugins.html.tpl CHANGED
@@ -1,12 +1,8 @@
1
 
2
  <div id="poststuff" class="sucuriscan-reset-plugins">
3
-
4
  <div class="postbox">
5
-
6
  <div class="inside">
7
-
8
  <form action="%%SUCURI.URL.Posthack%%#reset-plugins" method="post">
9
-
10
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
11
  <input type="hidden" name="sucuriscan_reset_plugins" value="1" />
12
 
@@ -60,12 +56,11 @@
60
  </p>
61
 
62
  <input type="submit" value="Process selected items" class="button button-primary" />
63
-
64
  </form>
65
 
66
  <script type="text/javascript">
67
  jQuery(function($){
68
- $.post( '%%SUCURI.AjaxURL.Posthack%%', {
69
  action: 'sucuriscan_posthack_ajax',
70
  sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
71
  form_action: 'get_plugins_data',
@@ -74,9 +69,6 @@
74
  });
75
  });
76
  </script>
77
-
78
  </div>
79
-
80
  </div>
81
-
82
  </div>
1
 
2
  <div id="poststuff" class="sucuriscan-reset-plugins">
 
3
  <div class="postbox">
 
4
  <div class="inside">
 
5
  <form action="%%SUCURI.URL.Posthack%%#reset-plugins" method="post">
 
6
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
7
  <input type="hidden" name="sucuriscan_reset_plugins" value="1" />
8
 
56
  </p>
57
 
58
  <input type="submit" value="Process selected items" class="button button-primary" />
 
59
  </form>
60
 
61
  <script type="text/javascript">
62
  jQuery(function($){
63
+ $.post('%%SUCURI.AjaxURL.Posthack%%', {
64
  action: 'sucuriscan_posthack_ajax',
65
  sucuriscan_page_nonce: '%%SUCURI.PageNonce%%',
66
  form_action: 'get_plugins_data',
69
  });
70
  });
71
  </script>
 
72
  </div>
 
73
  </div>
 
74
  </div>
inc/tpl/posthack-updatesecretkeys.html.tpl CHANGED
@@ -26,7 +26,7 @@
26
  </thead>
27
 
28
  <tbody>
29
- %%SUCURI.SecurityKeys.List%%
30
  </tbody>
31
  </table>
32
 
26
  </thead>
27
 
28
  <tbody>
29
+ %%%SUCURI.SecurityKeys.List%%%
30
  </tbody>
31
  </table>
32
 
inc/tpl/posthack.html.tpl CHANGED
@@ -2,27 +2,27 @@
2
  <div class="sucuriscan-tabs">
3
  <ul>
4
  <li>
5
- <a href="#" data-tabname="update-security-keys">Security Keys</a>
6
  </li>
7
  <li>
8
- <a href="#" data-tabname="reset-users-password">Reset User's Password</a>
9
  </li>
10
  <li>
11
- <a href="#" data-tabname="reset-plugins">Reset Plugins</a>
12
  </li>
13
  </ul>
14
 
15
  <div class="sucuriscan-tab-containers">
16
  <div id="sucuriscan-update-security-keys">
17
- %%SUCURI.UpdateSecretKeys%%
18
  </div>
19
 
20
  <div id="sucuriscan-reset-users-password">
21
- %%SUCURI.ResetPassword%%
22
  </div>
23
 
24
  <div id="sucuriscan-reset-plugins">
25
- %%SUCURI.ResetPlugins%%
26
  </div>
27
  </div>
28
  </div>
2
  <div class="sucuriscan-tabs">
3
  <ul>
4
  <li>
5
+ <a href="#update-security-keys" data-tabname="update-security-keys">Security Keys</a>
6
  </li>
7
  <li>
8
+ <a href="#reset-users-password" data-tabname="reset-users-password">Reset User's Password</a>
9
  </li>
10
  <li>
11
+ <a href="#reset-plugins" data-tabname="reset-plugins">Reset Plugins</a>
12
  </li>
13
  </ul>
14
 
15
  <div class="sucuriscan-tab-containers">
16
  <div id="sucuriscan-update-security-keys">
17
+ %%%SUCURI.UpdateSecretKeys%%%
18
  </div>
19
 
20
  <div id="sucuriscan-reset-users-password">
21
+ %%%SUCURI.ResetPassword%%%
22
  </div>
23
 
24
  <div id="sucuriscan-reset-plugins">
25
+ %%%SUCURI.ResetPlugins%%%
26
  </div>
27
  </div>
28
  </div>
inc/tpl/settings-alert-bruteforce.html.tpl ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="postbox">
3
+ <h3>Password Guessing Brute Force Attacks</h3>
4
+
5
+ <div class="inside">
6
+ <p>
7
+ Password guessing brute force attacks are very common against web sites and web
8
+ servers. They are one of the most common vectors used to compromise web sites.
9
+ The process is very simple and the attackers basically try multiple combinations
10
+ of usernames and passwords until they find one that works. Once they get in,
11
+ they can compromise the web site with malware, spam , phishing or anything else
12
+ they want.
13
+ </p>
14
+
15
+ <div class="sucuriscan-inline-alert-warning">
16
+ <p>This option overrides the <em>"Alerts Per Hour"</em> setting.</p>
17
+ </div>
18
+
19
+ <form action="%%SUCURI.URL.Settings%%#notifications" method="post">
20
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
21
+ <span class="sucuriscan-input-group">
22
+ <label>Consider Brute-Force Attack After:</label>
23
+ <select name="sucuriscan_maximum_failed_logins">
24
+ %%%SUCURI.AlertSettings.BruteForce%%%
25
+ </select>
26
+ </span>
27
+ <button type="submit" class="button-primary">Save</button>
28
+ </form>
29
+
30
+ <p>
31
+ More info at <a href="https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing"
32
+ target="_blank">Sucuri KB - Password Guessing Brute Force Attacks</a>.
33
+ </p>
34
+ </div>
35
+ </div>
inc/tpl/settings-alert-events.html.tpl ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="postbox">
3
+ <h3>Alert Events</h3>
4
+
5
+ <div class="inside">
6
+ <p>
7
+ Configure the alert settings to your needs, and make sure to read the purpose of
8
+ each option below otherwise you will end up enabling and/or disabling things
9
+ that will affect your personal inbox. If you experience issues with one or more
10
+ of these options revert them to their original state.
11
+ </p>
12
+
13
+ <div class="sucuriscan-inline-alert-warning">
14
+ <p>
15
+ Enabling the alerts for failed login attempts may become an indirect mail spam
16
+ attack as you will receive tons of emails if your website is victim of a brute
17
+ force attack. Disable this option and enable the brute force attack reports to
18
+ get a summary of all the failed logins detected each hour.
19
+ </p>
20
+ </div>
21
+
22
+ <form action="%%SUCURI.URL.Settings%%#notifications" method="post">
23
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
24
+
25
+ <table class="wp-list-table widefat sucuriscan-table sucuriscan-settings-notifications">
26
+ <thead>
27
+ <tr>
28
+ <th class="manage-column column-cb check-column">
29
+ <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
30
+ <input id="cb-select-all-1" type="checkbox">
31
+ </th>
32
+ <th class="manage-column">Event Description</th>
33
+ </tr>
34
+ </thead>
35
+
36
+ <tbody>
37
+ %%%SUCURI.AlertSettings.Events%%%
38
+ </tbody>
39
+ </table>
40
+
41
+ <div class="sucuriscan-recipient-form">
42
+ <button type="submit" name="sucuriscan_save_alert_events" class="button-primary">Save</button>
43
+ </div>
44
+ </form>
45
+ </div>
46
+ </div>
inc/tpl/settings-alert-events.snippet.tpl ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <tr class="%%SUCURI.Event.CssClass%%">
3
+ <td class="check-column">
4
+ <input type="hidden" name="%%SUCURI.Event.Name%%" value="0" />
5
+ <input type="checkbox" name="%%SUCURI.Event.Name%%" value="1" %%SUCURI.Event.Checked%% />
6
+ </td>
7
+ <td>
8
+ <span class="%%SUCURI.Event.LabelIcon%%">%%%SUCURI.Event.Label%%%</span>
9
+ </td>
10
+ </tr>
inc/tpl/settings-alert-perhour.html.tpl ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="postbox">
3
+ <h3>Alerts Per Hour</h3>
4
+
5
+ <div class="inside">
6
+ <p>
7
+ Depending on the number of yours registered in your website or the number of
8
+ actions performed by these users the recipients of the alerts sent when the site
9
+ triggers an action that the plugin monitors may become annoying or irrelevant
10
+ after some time. You can use this option to configure the maximum number of
11
+ alerts to receive during the same hour.
12
+ </p>
13
+
14
+ <div class="sucuriscan-inline-alert-warning">
15
+ <p>
16
+ If you have enabled the alerts for <a href="https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing"
17
+ target="_blank">password guessing brute force attacks</a> and lowered the number
18
+ of alerts sent during the hour has reached its limit, the plugin will force the
19
+ sending of the alert; you can consider the limit for alerts per hour a
20
+ <em>"limit + one"</em> if the brute force attack summary is generated.
21
+ </p>
22
+ </div>
23
+
24
+ <form action="%%SUCURI.URL.Settings%%#notifications" method="post">
25
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
26
+ <span class="sucuriscan-input-group">
27
+ <label>Maximum Alerts Per Hour:</label>
28
+ <select name="sucuriscan_emails_per_hour">
29
+ %%%SUCURI.AlertSettings.PerHour%%%
30
+ </select>
31
+ </span>
32
+ <button type="submit" class="button-primary">Save</button>
33
+ </form>
34
+ </div>
35
+ </div>
inc/tpl/settings-alert-recipients.html.tpl ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="postbox">
3
+ <h3>Alert Recipients</h3>
4
+
5
+ <div class="inside">
6
+ <p>
7
+ By default the plugin will send email alerts to the email address of the
8
+ original user account created during the installation process of your website,
9
+ you can change this adding a new address below and then deleting the old entry.
10
+ Additionally, you are allowed to send a copy of the same alerts to other email
11
+ addresses.
12
+ </p>
13
+
14
+ <div class="sucuriscan-inline-alert-info">
15
+ <p>
16
+ Make sure to check your spam folder if you do not see the alerts in your inbox,
17
+ if at least one of the recipients listed below receives the alert it means that
18
+ the message was delivered correctly, if you or one of the other recipients is
19
+ not receiving the alerts is probably because of a filter in your email service.
20
+ This is because the plugin only sends one single message per alert, so either
21
+ everyone gets the message or no one gets it.
22
+ </p>
23
+ </div>
24
+
25
+ <form action="%%SUCURI.URL.Settings%%#notifications" method="post">
26
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
27
+
28
+ <table class="wp-list-table widefat sucuriscan-table">
29
+ <thead>
30
+ <tr>
31
+ <th class="manage-column column-cb check-column">
32
+ <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
33
+ <input id="cb-select-all-1" type="checkbox">
34
+ </th>
35
+ <th class="manage-column">E-mail Address</th>
36
+ </tr>
37
+ </thead>
38
+
39
+ <tbody>
40
+ %%%SUCURI.AlertSettings.Recipients%%%
41
+ </tbody>
42
+ </table>
43
+
44
+ <div class="sucuriscan-recipient-form">
45
+ <span class="sucuriscan-input-group">
46
+ <label>E-mail Address:</label>
47
+ <input type="text" name="sucuriscan_recipient" class="input-text" placeholder="e.g. user@example.com" />
48
+ </span>
49
+ <button type="submit" name="sucuriscan_save_recipient" class="button-primary">Add Recipient</button>
50
+ </div>
51
+
52
+ <div class="sucuriscan-recipient-form">
53
+ <button type="submit" name="sucuriscan_delete_recipients" class="button-primary button-danger">Delete Selected</button>
54
+ <button type="submit" name="sucuriscan_debug_email" value="1" class="button-primary">Test Alert Delivery</button>
55
+ </div>
56
+ </form>
57
+ </div>
58
+ </div>
inc/tpl/settings-alert-recipients.snippet.tpl ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+
2
+ <tr class="%%SUCURI.Recipient.CssClass%%">
3
+ <td class="check-column">
4
+ <input type="checkbox" name="sucuriscan_recipients[]" value="%%SUCURI.Recipient.Email%%" />
5
+ </td>
6
+ <td>
7
+ <a href="mailto:%%SUCURI.Recipient.Email%%">%%SUCURI.Recipient.Email%%</a>
8
+ </td>
9
+ </tr>
inc/tpl/settings-alert-subject.html.tpl ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="postbox">
3
+ <h3>Alert Subject</h3>
4
+
5
+ <div class="inside">
6
+ <p>
7
+ Format of the subject for the email alerts, by default the plugin will use the
8
+ website name and the event identifier that is being reported, you can use this
9
+ panel to include the IP address of that user that triggered the event and some
10
+ additional data. You can create filters in your email client creating a custom
11
+ email subject using the pseudo-tags shown below.
12
+ </p>
13
+
14
+ <form action="%%SUCURI.URL.Settings%%#notifications" method="post">
15
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
16
+
17
+ <ul class="sucuriscan-subject-formats">
18
+ %%%SUCURI.AlertSettings.Subject%%%
19
+
20
+ <li>
21
+ <label>
22
+ <input type="radio" name="sucuriscan_email_subject" value="custom" %%SUCURI.AlertSettings.CustomChecked%% />
23
+ <span>Custom format</span>
24
+ <input type="text" name="sucuriscan_custom_email_subject" value="%%SUCURI.AlertSettings.CustomValue%%" />
25
+ </label>
26
+ </li>
27
+ </ul>
28
+
29
+ <div class="sucuriscan-recipient-form">
30
+ <button type="submit" class="button-primary">Save</button>
31
+ </div>
32
+ </form>
33
+ </div>
34
+ </div>
inc/tpl/settings-alert-subject.snippet.tpl ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+
2
+ <li>
3
+ <label>
4
+ <input type="radio" name="sucuriscan_email_subject"
5
+ value="%%SUCURI.EmailSubject.Value%%" %%SUCURI.EmailSubject.Checked%% />
6
+ <span>%%SUCURI.EmailSubject.Name%%</span>
7
+ </label>
8
+ </li>
inc/tpl/settings-alert.html.tpl ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div id="poststuff">
3
+ %%%SUCURI.AlertSettings.Recipients%%%
4
+
5
+ %%%SUCURI.AlertSettings.Subject%%%
6
+
7
+ %%%SUCURI.AlertSettings.PerHour%%%
8
+
9
+ %%%SUCURI.AlertSettings.BruteForce%%%
10
+
11
+ %%%SUCURI.AlertSettings.Events%%%
12
+ </div>
inc/tpl/{settings-general-apiproxy.html.tpl → settings-apiservice-proxy.html.tpl} RENAMED
@@ -7,7 +7,7 @@
7
  All the HTTP requests used to communicate with the API service are being sent
8
  using the WordPress built-in functions, so <em>(almost)</em> all its official
9
  features are inherited, this is useful if you need to pass these HTTP requests
10
- through a proxy. According to the <a href="http://codex.wordpress.org/HTTP_API"
11
  target="_blank">official documentation</a> you have to add some constants to the
12
  main configuration file: <em>WP_PROXY_HOST, WP_PROXY_PORT, WP_PROXY_USERNAME,
13
  WP_PROXY_PASSWORD</em>.
7
  All the HTTP requests used to communicate with the API service are being sent
8
  using the WordPress built-in functions, so <em>(almost)</em> all its official
9
  features are inherited, this is useful if you need to pass these HTTP requests
10
+ through a proxy. According to the <a href="https://codex.wordpress.org/HTTP_API"
11
  target="_blank">official documentation</a> you have to add some constants to the
12
  main configuration file: <em>WP_PROXY_HOST, WP_PROXY_PORT, WP_PROXY_USERNAME,
13
  WP_PROXY_PASSWORD</em>.
inc/tpl/{settings-general-apissl.html.tpl → settings-apiservice-ssl.html.tpl} RENAMED
@@ -24,12 +24,12 @@
24
  can take advantage of this to steal information from your website.
25
  </p>
26
 
27
- <form action="%%SUCURI.URL.Settings%%" method="post">
28
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
29
  <span class="sucuriscan-input-group">
30
  <label>SSL Certificate Verification:</label>
31
  <select name="sucuriscan_verify_ssl_cert">
32
- %%SUCURI.VerifySSLCertOptions%%
33
  </select>
34
  </span>
35
  <button type="submit" class="button-primary">Proceed</button>
24
  can take advantage of this to steal information from your website.
25
  </p>
26
 
27
+ <form action="%%SUCURI.URL.Settings%%#apiservice" method="post">
28
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
29
  <span class="sucuriscan-input-group">
30
  <label>SSL Certificate Verification:</label>
31
  <select name="sucuriscan_verify_ssl_cert">
32
+ %%%SUCURI.VerifySSLCertOptions%%%
33
  </select>
34
  </span>
35
  <button type="submit" class="button-primary">Proceed</button>
inc/tpl/settings-apiservice-status.html.tpl ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="postbox">
3
+ <h3>API Service Communication</h3>
4
+
5
+ <div class="inside">
6
+ <p>
7
+ Once the API key is generate the plugin will communicate with a remote API
8
+ service that will act as a safe data storage for the audit logs generated when
9
+ the website triggers certain events that the plugin monitors. If the website is
10
+ hacked the attacker will not have access to these logs and that way you can
11
+ investigate what was modified <em>(for malware infaction)</em> and/or how the
12
+ malicious person was able to gain access to the website.
13
+ </p>
14
+
15
+ <div class="sucuriscan-inline-alert-warning sucuriscan-%%SUCURI.ApiStatus.WarningVisibility%%">
16
+ <p>
17
+ The latency of the HTTP requests may slow down the website depending on the
18
+ location of the server that is hosting it. Additionally, if the API goes down
19
+ the plugin will throw warnings that may affect your workflow, in this case you
20
+ may want to stop the communication with the API service to keep the latency at
21
+ zero and be able to continue working in the website without interruptions.
22
+ </p>
23
+ </div>
24
+
25
+ <div class="sucuriscan-inline-alert-error sucuriscan-%%SUCURI.ApiStatus.ErrorVisibility%%">
26
+ <p>
27
+ Disabling the API service communication will stop the event monitoring, consider
28
+ to enable the <a href="%%SUCURI.URL.Settings%%#selfhosting">Log Exporter</a> to
29
+ keep the monitoring working while the HTTP requests are ignored, otherwise an
30
+ attacker may execute an action that will not be registered in the security logs
31
+ and you will not have a way to investigate the attack in the future.
32
+ </p>
33
+ </div>
34
+
35
+ <div class="sucuriscan-hstatus sucuriscan-hstatus-%%SUCURI.ApiStatus.StatusNum%%">
36
+ <span>API Service Communication is %%SUCURI.ApiStatus.Status%%</span>
37
+ <form action="%%SUCURI.URL.Settings%%#apiservice" method="post">
38
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
39
+ <input type="hidden" name="sucuriscan_api_service" value="%%SUCURI.ApiStatus.SwitchValue%%" />
40
+ <button type="submit" class="button-primary %%SUCURI.ApiStatus.SwitchCssClass%%">%%SUCURI.ApiStatus.SwitchText%%</button>
41
+ </form>
42
+ </div>
43
+ </div>
44
+ </div>
inc/tpl/{settings-general-apitimeout.html.tpl → settings-apiservice-timeout.html.tpl} RENAMED
@@ -4,13 +4,19 @@
4
 
5
  <div class="inside">
6
  <p>
7
- The plugin sends the data of events triggered by WordPress when it considers
8
- the action is suspicious, it sends this information via HTTP requests using
9
- <a href="https://developer.wordpress.org/reference/functions/wp_remote_post/"
10
- target="_blank">built-in functions</a> provided by WordPress and waits some
11
- seconds <em>(90 by default)</em> to get a response.
12
  </p>
13
 
 
 
 
 
 
 
14
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
15
  <span>Wait <b>%%SUCURI.RequestTimeout%%</b> before timeout</span>
16
  </div>
@@ -22,7 +28,7 @@
22
  something in the server blocking the connection.
23
  </p>
24
 
25
- <form action="%%SUCURI.URL.Settings%%" method="post">
26
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
27
  <span class="sucuriscan-input-group">
28
  <label>HTTP Request Timeout (in secs)</label>
4
 
5
  <div class="inside">
6
  <p>
7
+ The plugin sends the data associated to the events triggered by WordPress when
8
+ it considers the action is suspicious, it sends this information via HTTP requests
9
+ using the HTTP transport protocol available in the system and the <a target="_blank"
10
+ href="https://developer.wordpress.org/reference/functions/wp_remote_post/">built-in
11
+ functions</a> provided by WordPress, then it waits for the response.
12
  </p>
13
 
14
+ <div class="sucuriscan-inline-alert-info">
15
+ <p>
16
+ You can set up to %%SUCURI.MaxRequestTimeout%% seconds for the timeout, more than that is not allowed.
17
+ </p>
18
+ </div>
19
+
20
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
21
  <span>Wait <b>%%SUCURI.RequestTimeout%%</b> before timeout</span>
22
  </div>
28
  something in the server blocking the connection.
29
  </p>
30
 
31
+ <form action="%%SUCURI.URL.Settings%%#apiservice" method="post">
32
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
33
  <span class="sucuriscan-input-group">
34
  <label>HTTP Request Timeout (in secs)</label>
inc/tpl/settings-apiservice.html.tpl ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div id="poststuff" class="sucuriscan-general-apiservice">
3
+ %%%SUCURI.SettingsSection.ApiStatus%%%
4
+
5
+ %%%SUCURI.SettingsSection.ApiProxy%%%
6
+
7
+ %%%SUCURI.SettingsSection.ApiSSL%%%
8
+
9
+ %%%SUCURI.SettingsSection.ApiTimeout%%%
10
+ </div>
inc/tpl/settings-emailsubject.snippet.tpl DELETED
@@ -1,7 +0,0 @@
1
-
2
- <li>
3
- <label>
4
- <input type="radio" name="sucuriscan_email_subject" value="%%SUCURI.EmailSubject.Value%%" %%SUCURI.EmailSubject.Checked%% />
5
- <span>%%SUCURI.EmailSubject.Name%%</span>
6
- </label>
7
- </li>
 
 
 
 
 
 
 
inc/tpl/settings-general-apikey.html.tpl CHANGED
@@ -1,7 +1,7 @@
1
 
2
- %%SUCURI.ModalWhenAPIRegistered%%
3
 
4
- %%SUCURI.ModalForApiKeyRecovery%%
5
 
6
  <div class="postbox">
7
  <h3>Plugin API Key</h3>
@@ -49,7 +49,7 @@
49
  </div>
50
 
51
  <div class="sucuriscan-hstatus sucuriscan-hstatus-1 sucuriscan-%%SUCURI.APIKey.RemoveVisibility%%">
52
- <div class="sucuriscan-monospace">Plugin API Key: ec456e75e9e5ad48577da3382b627a42</div>
53
  <form action="%%SUCURI.URL.Settings%%" method="post">
54
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
55
  <button type="submit" name="sucuriscan_remove_api_key" class="button-primary button-danger">Remove</button>
1
 
2
+ %%%SUCURI.ModalWhenAPIRegistered%%%
3
 
4
+ %%%SUCURI.ModalForApiKeyRecovery%%%
5
 
6
  <div class="postbox">
7
  <h3>Plugin API Key</h3>
49
  </div>
50
 
51
  <div class="sucuriscan-hstatus sucuriscan-hstatus-1 sucuriscan-%%SUCURI.APIKey.RemoveVisibility%%">
52
+ <div class="sucuriscan-monospace">Plugin API Key: %%SUCURI.APIKey%%</div>
53
  <form action="%%SUCURI.URL.Settings%%" method="post">
54
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
55
  <button type="submit" name="sucuriscan_remove_api_key" class="button-primary button-danger">Remove</button>
inc/tpl/settings-general-auditlogstats.html.tpl ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="postbox">
3
+ <h3>Audit Log Statistics</h3>
4
+
5
+ <div class="inside">
6
+ <p>
7
+ Enabling this option allows you to have a quick view of the range of the
8
+ activity of your users and/or the attacks directed against your website. By
9
+ default, the plugin uses the <strong>latest %%SUCURI.AuditLogStats.Limit%%
10
+ entries</strong> in the audit logs and uses that information to draw bar and
11
+ pie charts in the dashboard.
12
+ </p>
13
+
14
+ <div class="sucuriscan-hstatus sucuriscan-hstatus-%%SUCURI.AuditLogStats.StatusNum%%">
15
+ <span>Audit Log Statistics are %%SUCURI.AuditLogStats.Status%%</span>
16
+ <form action="%%SUCURI.URL.Settings%%" method="post">
17
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
18
+ <input type="hidden" name="sucuriscan_audit_report" value="%%SUCURI.AuditLogStats.SwitchValue%%" />
19
+ <button type="submit" class="button-primary %%SUCURI.AuditLogStats.SwitchCssClass%%">%%SUCURI.AuditLogStats.SwitchText%%</button>
20
+ </form>
21
+ </div>
22
+
23
+ <p>
24
+ The statistic charts are generated with a limited number of logs stored in the
25
+ remote API server, you can increase the number to draw the charts with more data
26
+ and represent the activity during a wider range of days, but you must consider
27
+ that the bigger the number the slower the plugin dashboard will be because it
28
+ will take more time to read more logs.
29
+ </p>
30
+
31
+ <form action="%%SUCURI.URL.Settings%%" method="post">
32
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
33
+ <span class="sucuriscan-input-group">
34
+ <label>Audit Logs Limit:</label>
35
+ <input type="text" name="sucuriscan_logs4report" class="input-text" placeholder="e.g. 500" />
36
+ </span>
37
+ <button type="submit" class="button-primary">Save</button>
38
+ </form>
39
+ </div>
40
+ </div>
inc/tpl/settings-general-datastorage.html.tpl CHANGED
@@ -1,6 +1,6 @@
1
 
2
  <div class="postbox">
3
- <h3>Plugin Data Storage Path</h3>
4
 
5
  <div class="inside">
6
  <p>
@@ -45,7 +45,7 @@
45
  <form action="%%SUCURI.URL.Settings%%" method="post">
46
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
47
  <span class="sucuriscan-input-group">
48
- <label>Data storage path:</label>
49
  <input type="text" name="sucuriscan_datastore_path" class="input-text" />
50
  </span>
51
  <button type="submit" class="button-primary">Proceed</button>
1
 
2
  <div class="postbox">
3
+ <h3>Data Storage Path</h3>
4
 
5
  <div class="inside">
6
  <p>
45
  <form action="%%SUCURI.URL.Settings%%" method="post">
46
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
47
  <span class="sucuriscan-input-group">
48
+ <label>Data Storage Path:</label>
49
  <input type="text" name="sucuriscan_datastore_path" class="input-text" />
50
  </span>
51
  <button type="submit" class="button-primary">Proceed</button>
inc/tpl/settings-general-datetime.html.tpl ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="postbox">
3
+ <h3>Date &amp; Time</h3>
4
+
5
+ <div class="inside">
6
+ <p>
7
+ The plugin uses built-in WordPress functions to retrieve the current date and
8
+ time, as well to translate timestamps to human readable text. Below is shown the
9
+ data returned by the main three functions used by this plugin to get the date
10
+ for the logs and email alerts, if you notice an inconsistency with any of these
11
+ values please change the timezone settings.
12
+ </p>
13
+
14
+ <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
15
+ <span>Current Date &amp; Time is</span>
16
+ <strong>%%SUCURI.Datetime.HumanReadable%%</strong>
17
+ <em>(%%SUCURI.Datetime.Timezone%% - %%SUCURI.Datetime.Timestamp%%)</em>
18
+ <a href="%%SUCURI.Datetime.AdminURL%%" target="_blank" class="button-primary">Change</a>
19
+ </div>
20
+ </div>
21
+ </div>
inc/tpl/settings-general-ipdiscoverer.html.tpl CHANGED
@@ -31,6 +31,27 @@
31
  </form>
32
  </div>
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  <p>
35
  If you are experiencing issues with the automatic detection of IP address of
36
  your visitors, with the security logs, or with the response time of your website
31
  </form>
32
  </div>
33
 
34
+ <p>
35
+ Once the feature is enabled you may choose the HTTP header that will be used by
36
+ default to retrieve the real IP address of each HTTP request, generally you do
37
+ not need to set this but in rare cases your hosting provider may have a load
38
+ balancer that can interfere in the process, in which case you will have to
39
+ explicitly specify the main HTTP header. Note that if you select a HTTP header
40
+ that is not being set by the server the plugin will fallback to the default
41
+ <em>Remote-Addr</em>.
42
+ </p>
43
+
44
+ <form action="%%SUCURI.URL.Settings%%" method="post">
45
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
46
+ <span class="sucuriscan-input-group">
47
+ <label>Main IP HTTP Header:</label>
48
+ <select name="sucuriscan_addr_header">
49
+ %%%SUCURI.AddrHeaderOptions%%%
50
+ </select>
51
+ </span>
52
+ <button type="submit" class="button-primary">Proceed</button>
53
+ </form>
54
+
55
  <p>
56
  If you are experiencing issues with the automatic detection of IP address of
57
  your visitors, with the security logs, or with the response time of your website
inc/tpl/settings-general-xhrmonitor.html.tpl CHANGED
@@ -31,7 +31,7 @@
31
  </div>
32
 
33
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
34
- <span>User Comment Monitor is %%SUCURI.XhrMonitorStatus%%</span>
35
 
36
  <form action="%%SUCURI.URL.Settings%%" method="post">
37
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
31
  </div>
32
 
33
  <div class="sucuriscan-hstatus sucuriscan-hstatus-2">
34
+ <span>XML HTTP Request Monitor is %%SUCURI.XhrMonitorStatus%%</span>
35
 
36
  <form action="%%SUCURI.URL.Settings%%" method="post">
37
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
inc/tpl/settings-general.html.tpl CHANGED
@@ -1,118 +1,22 @@
1
 
2
  <div id="poststuff" class="sucuriscan-general-settings">
3
- %%SUCURI.SettingsSection.ApiKey%%
4
 
5
- %%SUCURI.SettingsSection.DataStorage%%
6
 
7
- %%SUCURI.SettingsSection.ApiProxy%%
8
 
9
- %%SUCURI.SettingsSection.ApiSSL%%
10
 
11
- %%SUCURI.SettingsSection.ApiTimeout%%
12
 
13
- %%SUCURI.SettingsSection.ReverseProxy%%
14
 
15
- %%SUCURI.SettingsSection.PasswordCollector%%
16
 
17
- %%SUCURI.SettingsSection.IPDiscoverer%%
18
 
19
- %%SUCURI.SettingsSection.CommentMonitor%%
20
 
21
- %%SUCURI.SettingsSection.XhrMonitor%%
22
-
23
- %%SUCURI.SettingsSection.ResetOptions%%
24
  </div>
25
-
26
- <table class="wp-list-table widefat sucuriscan-table sucuriscan-striped-table sucuriscan-settings">
27
- <tbody>
28
- <tr>
29
- <td>Test email alerts</td>
30
- <td><em>(Test ability to send email alerts)</em></td>
31
- <td class="td-with-button">
32
- <form action="%%SUCURI.URL.Settings%%" method="post">
33
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
34
- <input type="hidden" name="sucuriscan_debug_email" value="1" />
35
- <button type="submit" class="button-primary">Proceed</button>
36
- </form>
37
- </td>
38
- </tr>
39
-
40
- <tr>
41
- <td>Send plugin alerts to</td>
42
- <td>%%SUCURI.NotifyTo%%</td>
43
- <td class="td-with-button">
44
- <form action="%%SUCURI.URL.Settings%%" method="post">
45
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
46
- <input type="text" name="sucuriscan_notify_to" class="input-text" placeholder="Separated by commas" />
47
- <button type="submit" class="button-primary">Change</button>
48
- </form>
49
- </td>
50
- </tr>
51
-
52
- <tr>
53
- <td>Maximum alerts per hour</td>
54
- <td>%%SUCURI.EmailsPerHour%%</td>
55
- <td class="td-with-button">
56
- <form action="%%SUCURI.URL.Settings%%" method="post">
57
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
58
- <select name="sucuriscan_emails_per_hour">
59
- %%SUCURI.EmailsPerHourOptions%%
60
- </select>
61
- <button type="submit" class="button-primary">Change</button>
62
- </form>
63
- </td>
64
- </tr>
65
-
66
- <tr>
67
- <td>Consider brute-force after</td>
68
- <td>%%SUCURI.MaximumFailedLogins%%</td>
69
- <td class="td-with-button">
70
- <form action="%%SUCURI.URL.Settings%%" method="post">
71
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
72
- <select name="sucuriscan_maximum_failed_logins">
73
- %%SUCURI.MaximumFailedLoginsOptions%%
74
- </select>
75
- <button type="submit" class="button-primary">Change</button>
76
- </form>
77
- </td>
78
- </tr>
79
-
80
- <tr>
81
- <td>Display audit report</td>
82
- <td>%%SUCURI.AuditReportStatus%%</td>
83
- <td class="td-with-button">
84
- <form action="%%SUCURI.URL.Settings%%" method="post">
85
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
86
- <input type="hidden" name="sucuriscan_audit_report" value="%%SUCURI.AuditReportSwitchValue%%" />
87
- <button type="submit" class="button-primary %%SUCURI.AuditReportSwitchCssClass%%">%%SUCURI.AuditReportSwitchText%%</button>
88
- </form>
89
- </td>
90
- </tr>
91
-
92
- <tr>
93
- <td>Audit report limit</td>
94
- <td>Process latest %%SUCURI.AuditReportLimit%% logs</td>
95
- <td class="td-with-button">
96
- <form action="%%SUCURI.URL.Settings%%" method="post">
97
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
98
- <input type="text" name="sucuriscan_logs4report" class="input-text" placeholder="e.g. 500" />
99
- <button type="submit" class="button-primary">Change</button>
100
- </form>
101
- </td>
102
- </tr>
103
-
104
- <tr>
105
- <td>Current Timezone</td>
106
- <td>%%SUCURI.CustomTimezone%% <em>(%%SUCURI.CurrentDatetime%%)</em></td>
107
- <td class="td-with-button">
108
- <form action="%%SUCURI.URL.Settings%%" method="post">
109
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
110
- <a href="options-general.php" target="_blank" class="button">
111
- <span>Change Timezone from the Settings/General page</span>
112
- </a>
113
- </form>
114
- </td>
115
- </tr>
116
-
117
- </tbody>
118
- </table>
1
 
2
  <div id="poststuff" class="sucuriscan-general-settings">
3
+ %%%SUCURI.SettingsSection.ApiKey%%%
4
 
5
+ %%%SUCURI.SettingsSection.DataStorage%%%
6
 
7
+ %%%SUCURI.SettingsSection.ReverseProxy%%%
8
 
9
+ %%%SUCURI.SettingsSection.IPDiscoverer%%%
10
 
11
+ %%%SUCURI.SettingsSection.PasswordCollector%%%
12
 
13
+ %%%SUCURI.SettingsSection.CommentMonitor%%%
14
 
15
+ %%%SUCURI.SettingsSection.XhrMonitor%%%
16
 
17
+ %%%SUCURI.SettingsSection.AuditLogStats%%%
18
 
19
+ %%%SUCURI.SettingsSection.Datetime%%%
20
 
21
+ %%%SUCURI.SettingsSection.ResetOptions%%%
 
 
22
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/tpl/settings-heartbeat.html.tpl CHANGED
@@ -31,10 +31,10 @@
31
  <td>Heartbeat status</td>
32
  <td>%%SUCURI.HeartbeatStatus%%</td>
33
  <td class="td-with-button">
34
- <form action="%%SUCURI.URL.Settings%%#settings-heartbeat" method="post">
35
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
36
  <select name="sucuriscan_heartbeat_status">
37
- %%SUCURI.HeartbeatStatusOptions%%
38
  </select>
39
  <button type="submit" class="button-primary">Change</button>
40
  </form>
@@ -45,10 +45,10 @@
45
  <td>Pulse interval</td>
46
  <td>%%SUCURI.HeartbeatPulse%%</td>
47
  <td class="td-with-button">
48
- <form action="%%SUCURI.URL.Settings%%#settings-heartbeat" method="post">
49
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
50
  <select name="sucuriscan_heartbeat_pulse">
51
- %%SUCURI.HeartbeatPulseOptions%%
52
  </select>
53
  <button type="submit" class="button-primary">Change</button>
54
  </form>
@@ -59,10 +59,10 @@
59
  <td>Interval speed</td>
60
  <td>%%SUCURI.HeartbeatInterval%%</td>
61
  <td class="td-with-button">
62
- <form action="%%SUCURI.URL.Settings%%#settings-heartbeat" method="post">
63
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
64
  <select name="sucuriscan_heartbeat_interval">
65
- %%SUCURI.HeartbeatIntervalOptions%%
66
  </select>
67
  <button type="submit" class="button-primary">Change</button>
68
  </form>
@@ -73,7 +73,7 @@
73
  <td>Auto-start</td>
74
  <td>%%SUCURI.HeartbeatAutostart%%</td>
75
  <td class="td-with-button">
76
- <form action="%%SUCURI.URL.Settings%%#settings-heartbeat" method="post">
77
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
78
  <input type="hidden" name="sucuriscan_heartbeat_autostart" value="%%SUCURI.HeartbeatAutostartSwitchValue%%" />
79
  <button type="submit" class="button-primary %%SUCURI.HeartbeatAutostartSwitchCssClass%%">%%SUCURI.HeartbeatAutostartSwitchText%%</button>
31
  <td>Heartbeat status</td>
32
  <td>%%SUCURI.HeartbeatStatus%%</td>
33
  <td class="td-with-button">
34
+ <form action="%%SUCURI.URL.Settings%%#heartbeat" method="post">
35
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
36
  <select name="sucuriscan_heartbeat_status">
37
+ %%%SUCURI.HeartbeatStatusOptions%%%
38
  </select>
39
  <button type="submit" class="button-primary">Change</button>
40
  </form>
45
  <td>Pulse interval</td>
46
  <td>%%SUCURI.HeartbeatPulse%%</td>
47
  <td class="td-with-button">
48
+ <form action="%%SUCURI.URL.Settings%%#heartbeat" method="post">
49
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
50
  <select name="sucuriscan_heartbeat_pulse">
51
+ %%%SUCURI.HeartbeatPulseOptions%%%
52
  </select>
53
  <button type="submit" class="button-primary">Change</button>
54
  </form>
59
  <td>Interval speed</td>
60
  <td>%%SUCURI.HeartbeatInterval%%</td>
61
  <td class="td-with-button">
62
+ <form action="%%SUCURI.URL.Settings%%#heartbeat" method="post">
63
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
64
  <select name="sucuriscan_heartbeat_interval">
65
+ %%%SUCURI.HeartbeatIntervalOptions%%%
66
  </select>
67
  <button type="submit" class="button-primary">Change</button>
68
  </form>
73
  <td>Auto-start</td>
74
  <td>%%SUCURI.HeartbeatAutostart%%</td>
75
  <td class="td-with-button">
76
+ <form action="%%SUCURI.URL.Settings%%#heartbeat" method="post">
77
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
78
  <input type="hidden" name="sucuriscan_heartbeat_autostart" value="%%SUCURI.HeartbeatAutostartSwitchValue%%" />
79
  <button type="submit" class="button-primary %%SUCURI.HeartbeatAutostartSwitchCssClass%%">%%SUCURI.HeartbeatAutostartSwitchText%%</button>
inc/tpl/settings-ignorerules.html.tpl CHANGED
@@ -21,7 +21,7 @@
21
 
22
  <div class="inside">
23
  <p>
24
- This is a list of registered <a href="http://codex.wordpress.org/Post_Types"
25
  target="_blank">Post Types</a>, since you have enabled the <strong>email alerts
26
  for new or modified content</strong>, we will send you an alert if any of these
27
  <code>post-types</code> are created and/or updated. You may want to ignore some
@@ -44,6 +44,6 @@
44
  </thead>
45
 
46
  <tbody>
47
- %%SUCURI.IgnoreRules.PostTypes%%
48
  </tbody>
49
  </table>
21
 
22
  <div class="inside">
23
  <p>
24
+ This is a list of registered <a href="https://codex.wordpress.org/Post_Types"
25
  target="_blank">Post Types</a>, since you have enabled the <strong>email alerts
26
  for new or modified content</strong>, we will send you an alert if any of these
27
  <code>post-types</code> are created and/or updated. You may want to ignore some
44
  </thead>
45
 
46
  <tbody>
47
+ %%%SUCURI.IgnoreRules.PostTypes%%%
48
  </tbody>
49
  </table>
inc/tpl/settings-ignorerules.snippet.tpl CHANGED
@@ -5,7 +5,7 @@
5
  <td><span class="sucuriscan-label-%%SUCURI.IgnoreRules.IsIgnoredClass%%">%%SUCURI.IgnoreRules.IsIgnored%%</span></td>
6
  <td><em class="sucuriscan-monospace">%%SUCURI.IgnoreRules.WasIgnoredAt%%</em></td>
7
  <td class="td-with-button">
8
- <form action="%%SUCURI.URL.Settings%%#settings-ignorerules" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
  <input type="hidden" name="sucuriscan_ignorerule" value="%%SUCURI.IgnoreRules.PostType%%" />
11
  <input type="hidden" name="sucuriscan_ignorerule_action" value="%%SUCURI.IgnoreRules.Action%%" />
5
  <td><span class="sucuriscan-label-%%SUCURI.IgnoreRules.IsIgnoredClass%%">%%SUCURI.IgnoreRules.IsIgnored%%</span></td>
6
  <td><em class="sucuriscan-monospace">%%SUCURI.IgnoreRules.WasIgnoredAt%%</em></td>
7
  <td class="td-with-button">
8
+ <form action="%%SUCURI.URL.Settings%%#ignorerules" method="post">
9
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
10
  <input type="hidden" name="sucuriscan_ignorerule" value="%%SUCURI.IgnoreRules.PostType%%" />
11
  <input type="hidden" name="sucuriscan_ignorerule_action" value="%%SUCURI.IgnoreRules.Action%%" />
inc/tpl/settings-ignorescanning.html.tpl CHANGED
@@ -30,7 +30,7 @@
30
  prevent the misuse of this tool.
31
  </p>
32
 
33
- <form action="%%SUCURI.URL.Settings%%#settings-ignorescanning" method="post">
34
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
35
  <input type="hidden" name="sucuriscan_ignorescanning_action" value="ignore" />
36
  <input type="text" name="sucuriscan_ignorescanning_file"
@@ -44,7 +44,7 @@
44
  </div>
45
  </div>
46
 
47
- <form action="%%SUCURI.URL.Settings%%#settings-ignorescanning" method="post">
48
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
49
 
50
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-settings-ignorescanning">
@@ -55,11 +55,11 @@
55
  </th>
56
  <th class="manage-column">&nbsp;</th>
57
  <th class="manage-column">Directory</th>
58
- <th class="manage-column" width="180">Ignored At</th>
59
  </thead>
60
 
61
  <tbody>
62
- %%SUCURI.IgnoreScanning.ResourceList%%
63
 
64
  <tr class="sucuriscan-%%SUCURI.IgnoreScanning.NoItemsVisibility%%">
65
  <td colspan="4">
30
  prevent the misuse of this tool.
31
  </p>
32
 
33
+ <form action="%%SUCURI.URL.Settings%%#ignorescanning" method="post">
34
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
35
  <input type="hidden" name="sucuriscan_ignorescanning_action" value="ignore" />
36
  <input type="text" name="sucuriscan_ignorescanning_file"
44
  </div>
45
  </div>
46
 
47
+ <form action="%%SUCURI.URL.Settings%%#ignorescanning" method="post">
48
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
49
 
50
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-settings-ignorescanning">
55
  </th>
56
  <th class="manage-column">&nbsp;</th>
57
  <th class="manage-column">Directory</th>
58
+ <th class="manage-column" width="200">Ignored At</th>
59
  </thead>
60
 
61
  <tbody>
62
+ %%%SUCURI.IgnoreScanning.ResourceList%%%
63
 
64
  <tr class="sucuriscan-%%SUCURI.IgnoreScanning.NoItemsVisibility%%">
65
  <td colspan="4">
inc/tpl/settings-notifications.html.tpl DELETED
@@ -1,75 +0,0 @@
1
-
2
- <div id="poststuff">
3
- <div class="postbox sucuriscan-border sucuriscan-table-description">
4
- <h3>Alert Settings</h3>
5
-
6
- <div class="inside">
7
- <p>
8
- Check the boxes bellow to receive alerts via email of the events explained in
9
- the table, by the default the notifications will be sent to the address
10
- configured during the installation of your site, you can change this in the
11
- <em>General Settings</em> panel. You can specify multiple recipients separating
12
- each address with a comma.
13
- </p>
14
-
15
- <div class="sucuriscan-inline-alert-warning sucuriscan-%%SUCURI.PrettifyMailsWarningVisibility%%">
16
- <p>
17
- Some emails sent by this plugin will be rejected outright by some popular email
18
- services. To fix this you will need to use a third-party email service, or use a
19
- plugin to force the site to use SMTP <em>(Simple Mail Transfer Protocol)</em>
20
- for sending emails, and then configure your SMTP server to properly handle
21
- messages. You can also <strong>disable HTML alerts</strong> to get notifications
22
- in <em>text/plain</em> format.
23
- </p>
24
- </div>
25
- </div>
26
- </div>
27
- </div>
28
-
29
- <form action="%%SUCURI.URL.Settings%%#settings-notifications" method="post">
30
- <table class="wp-list-table widefat sucuriscan-table sucuriscan-settings-notifications">
31
- <thead>
32
- <tr>
33
- <th class="thead-with-button">
34
- <span>Alert Settings</span>
35
- <div class="thead-topright-action">
36
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
37
- <button type="submit" name="sucuriscan_save_notification_settings" class="button-primary">Save</button>
38
- </div>
39
- </th>
40
- </tr>
41
- </thead>
42
-
43
- <tbody>
44
-
45
- <tr>
46
- <td>
47
- <p>
48
- Format of the subject for the email alerts, by default the plugin will use the
49
- domain of the site, and the event that is going to be reported, you can change
50
- this to also report the remote address of the user involved in the operation
51
- that is being reported to quickly determine if the event is valid or not reading
52
- the subject of the email.
53
- </p>
54
-
55
- <ul class="sucuriscan-subject-formats">
56
-
57
- %%SUCURI.EmailSubjectOptions%%
58
-
59
- <li>
60
- <label>
61
- <input type="radio" name="sucuriscan_email_subject" value="custom" %%SUCURI.EmailSubjectCustom.Checked%% />
62
- <span>Custom format</span>
63
- <input type="text" name="sucuriscan_custom_email_subject" value="%%SUCURI.EmailSubjectCustom.Value%%" />
64
- </label>
65
- </li>
66
-
67
- </ul>
68
- </td>
69
- </tr>
70
-
71
- %%SUCURI.NotificationOptions%%
72
-
73
- </tbody>
74
- </table>
75
- </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/tpl/settings-notifications.snippet.tpl DELETED
@@ -1,12 +0,0 @@
1
-
2
- <tr class="%%SUCURI.Notification.CssClass%%">
3
- <td>
4
- <div>
5
- <label>
6
- <input type="hidden" name="%%SUCURI.Notification.Name%%" value="0" />
7
- <input type="checkbox" name="%%SUCURI.Notification.Name%%" value="1" %%SUCURI.Notification.Checked%% />
8
- <span class="%%SUCURI.Notification.LabelIcon%%">%%SUCURI.Notification.Label%%</span>
9
- </label>
10
- </div>
11
- </td>
12
- </tr>
 
 
 
 
 
 
 
 
 
 
 
 
inc/tpl/settings-scanner.html.tpl CHANGED
@@ -55,10 +55,10 @@
55
  <td>Scanning algorithm</td>
56
  <td>%%SUCURI.ScanningInterface%%</td>
57
  <td class="td-with-button">
58
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
59
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
60
  <select name="sucuriscan_scan_interface">
61
- %%SUCURI.ScanningInterfaceOptions%%
62
  </select>
63
  <button type="submit" class="button-primary">Change</button>
64
  </form>
@@ -69,10 +69,10 @@
69
  <td>Scanning frequency</td>
70
  <td>%%SUCURI.ScanningFrequency%%</td>
71
  <td class="td-with-button">
72
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
73
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
74
  <select name="sucuriscan_scan_frequency">
75
- %%SUCURI.ScanningFrequencyOptions%%
76
  </select>
77
  <button type="submit" class="button-primary">Change</button>
78
  </form>
@@ -83,7 +83,7 @@
83
  <td>Main <abbr title="File System Scanner">FS Scanner</abbr></td>
84
  <td>%%SUCURI.FsScannerStatus%%</td>
85
  <td class="td-with-button">
86
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
87
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
88
  <input type="hidden" name="sucuriscan_fs_scanner" value="%%SUCURI.FsScannerSwitchValue%%" />
89
  <button type="submit" class="button-primary %%SUCURI.FsScannerSwitchCssClass%%">%%SUCURI.FsScannerSwitchText%%</button>
@@ -91,23 +91,11 @@
91
  </td>
92
  </tr>
93
 
94
- <tr>
95
- <td>FS Scanner, Modified files</td>
96
- <td>%%SUCURI.ScanModfilesStatus%%</td>
97
- <td class="td-with-button">
98
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
99
- <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
100
- <input type="hidden" name="sucuriscan_scan_modfiles" value="%%SUCURI.ScanModfilesSwitchValue%%" />
101
- <button type="submit" class="button-primary %%SUCURI.ScanModfilesSwitchCssClass%%">%%SUCURI.ScanModfilesSwitchText%%</button>
102
- </form>
103
- </td>
104
- </tr>
105
-
106
  <tr class="alternate">
107
  <td>FS Scanner, Core integrity checks</td>
108
  <td>%%SUCURI.ScanChecksumsStatus%%</td>
109
  <td class="td-with-button">
110
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
111
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
112
  <input type="hidden" name="sucuriscan_scan_checksums" value="%%SUCURI.ScanChecksumsSwitchValue%%" />
113
  <button type="submit" class="button-primary %%SUCURI.ScanChecksumsSwitchCssClass%%">%%SUCURI.ScanChecksumsSwitchText%%</button>
@@ -119,7 +107,7 @@
119
  <td>FS Scanner, Ignore scanning</td>
120
  <td>%%SUCURI.IgnoreScanningStatus%%</td>
121
  <td class="td-with-button">
122
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
123
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
124
  <input type="hidden" name="sucuriscan_ignore_scanning" value="%%SUCURI.IgnoreScanningSwitchValue%%" />
125
  <button type="submit" class="button-primary %%SUCURI.IgnoreScanningSwitchCssClass%%">%%SUCURI.IgnoreScanningSwitchText%%</button>
@@ -131,7 +119,7 @@
131
  <td>FS Scanner, Error log files</td>
132
  <td>%%SUCURI.ScanErrorlogsStatus%%</td>
133
  <td class="td-with-button">
134
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
135
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
136
  <input type="hidden" name="sucuriscan_scan_errorlogs" value="%%SUCURI.ScanErrorlogsSwitchValue%%" />
137
  <button type="submit" class="button-primary %%SUCURI.ScanErrorlogsSwitchCssClass%%">%%SUCURI.ScanErrorlogsSwitchText%%</button>
@@ -143,7 +131,7 @@
143
  <td>SiteCheck scanner</td>
144
  <td>%%SUCURI.SiteCheckScannerStatus%%</td>
145
  <td class="td-with-button">
146
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
147
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
148
  <input type="hidden" name="sucuriscan_sitecheck_scanner" value="%%SUCURI.SiteCheckScannerSwitchValue%%" />
149
  <button type="submit" class="button-primary %%SUCURI.SiteCheckScannerSwitchCssClass%%">%%SUCURI.SiteCheckScannerSwitchText%%</button>
@@ -167,7 +155,7 @@
167
  <td>Analyze error logs</td>
168
  <td>%%SUCURI.ParseErrorLogsStatus%%</td>
169
  <td class="td-with-button">
170
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
171
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
172
  <input type="hidden" name="sucuriscan_parse_errorlogs" value="%%SUCURI.ParseErrorLogsSwitchValue%%" />
173
  <button type="submit" class="button-primary %%SUCURI.ParseErrorLogsSwitchCssClass%%">%%SUCURI.ParseErrorLogsSwitchText%%</button>
@@ -179,7 +167,7 @@
179
  <td>Error logs limit</td>
180
  <td>Analyze last %%SUCURI.ErrorLogsLimit%% logs</td>
181
  <td class="td-with-button">
182
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
183
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
184
  <input type="text" name="sucuriscan_errorlogs_limit" placeholder="Number of lines to analyze" class="input-text" />
185
  <button type="submit" class="button-primary">Change</button>
@@ -191,7 +179,7 @@
191
  <td>Reset core integrity logs</td>
192
  <td><span class="sucuriscan-monospace">%%SUCURI.IntegrityLogLife%% of data</span></td>
193
  <td class="td-with-button">
194
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
195
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
196
  <input type="hidden" name="sucuriscan_reset_logfile" value="integrity" />
197
  <button type="submit" class="button-primary">Reset logs</button>
@@ -203,7 +191,7 @@
203
  <td>Reset last login logs</td>
204
  <td><span class="sucuriscan-monospace">%%SUCURI.LastLoginLogLife%% of data</span></td>
205
  <td class="td-with-button">
206
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
207
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
208
  <input type="hidden" name="sucuriscan_reset_logfile" value="lastlogins" />
209
  <button type="submit" class="button-primary">Reset logs</button>
@@ -215,7 +203,7 @@
215
  <td>Reset failed login logs</td>
216
  <td><span class="sucuriscan-monospace">%%SUCURI.FailedLoginLogLife%% of data</span></td>
217
  <td class="td-with-button">
218
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
219
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
220
  <input type="hidden" name="sucuriscan_reset_logfile" value="failedlogins" />
221
  <button type="submit" class="button-primary">Reset logs</button>
@@ -227,7 +215,7 @@
227
  <td>Reset sitecheck logs</td>
228
  <td><span class="sucuriscan-monospace">%%SUCURI.SiteCheckLogLife%% of data</span></td>
229
  <td class="td-with-button">
230
- <form action="%%SUCURI.URL.Settings%%#settings-scanner" method="post">
231
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
232
  <input type="hidden" name="sucuriscan_reset_logfile" value="sitecheck" />
233
  <button type="submit" class="button-primary">Reset logs</button>
55
  <td>Scanning algorithm</td>
56
  <td>%%SUCURI.ScanningInterface%%</td>
57
  <td class="td-with-button">
58
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
59
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
60
  <select name="sucuriscan_scan_interface">
61
+ %%%SUCURI.ScanningInterfaceOptions%%%
62
  </select>
63
  <button type="submit" class="button-primary">Change</button>
64
  </form>
69
  <td>Scanning frequency</td>
70
  <td>%%SUCURI.ScanningFrequency%%</td>
71
  <td class="td-with-button">
72
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
73
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
74
  <select name="sucuriscan_scan_frequency">
75
+ %%%SUCURI.ScanningFrequencyOptions%%%
76
  </select>
77
  <button type="submit" class="button-primary">Change</button>
78
  </form>
83
  <td>Main <abbr title="File System Scanner">FS Scanner</abbr></td>
84
  <td>%%SUCURI.FsScannerStatus%%</td>
85
  <td class="td-with-button">
86
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
87
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
88
  <input type="hidden" name="sucuriscan_fs_scanner" value="%%SUCURI.FsScannerSwitchValue%%" />
89
  <button type="submit" class="button-primary %%SUCURI.FsScannerSwitchCssClass%%">%%SUCURI.FsScannerSwitchText%%</button>
91
  </td>
92
  </tr>
93
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  <tr class="alternate">
95
  <td>FS Scanner, Core integrity checks</td>
96
  <td>%%SUCURI.ScanChecksumsStatus%%</td>
97
  <td class="td-with-button">
98
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
99
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
100
  <input type="hidden" name="sucuriscan_scan_checksums" value="%%SUCURI.ScanChecksumsSwitchValue%%" />
101
  <button type="submit" class="button-primary %%SUCURI.ScanChecksumsSwitchCssClass%%">%%SUCURI.ScanChecksumsSwitchText%%</button>
107
  <td>FS Scanner, Ignore scanning</td>
108
  <td>%%SUCURI.IgnoreScanningStatus%%</td>
109
  <td class="td-with-button">
110
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
111
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
112
  <input type="hidden" name="sucuriscan_ignore_scanning" value="%%SUCURI.IgnoreScanningSwitchValue%%" />
113
  <button type="submit" class="button-primary %%SUCURI.IgnoreScanningSwitchCssClass%%">%%SUCURI.IgnoreScanningSwitchText%%</button>
119
  <td>FS Scanner, Error log files</td>
120
  <td>%%SUCURI.ScanErrorlogsStatus%%</td>
121
  <td class="td-with-button">
122
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
123
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
124
  <input type="hidden" name="sucuriscan_scan_errorlogs" value="%%SUCURI.ScanErrorlogsSwitchValue%%" />
125
  <button type="submit" class="button-primary %%SUCURI.ScanErrorlogsSwitchCssClass%%">%%SUCURI.ScanErrorlogsSwitchText%%</button>
131
  <td>SiteCheck scanner</td>
132
  <td>%%SUCURI.SiteCheckScannerStatus%%</td>
133
  <td class="td-with-button">
134
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
135
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
136
  <input type="hidden" name="sucuriscan_sitecheck_scanner" value="%%SUCURI.SiteCheckScannerSwitchValue%%" />
137
  <button type="submit" class="button-primary %%SUCURI.SiteCheckScannerSwitchCssClass%%">%%SUCURI.SiteCheckScannerSwitchText%%</button>
155
  <td>Analyze error logs</td>
156
  <td>%%SUCURI.ParseErrorLogsStatus%%</td>
157
  <td class="td-with-button">
158
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
159
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
160
  <input type="hidden" name="sucuriscan_parse_errorlogs" value="%%SUCURI.ParseErrorLogsSwitchValue%%" />
161
  <button type="submit" class="button-primary %%SUCURI.ParseErrorLogsSwitchCssClass%%">%%SUCURI.ParseErrorLogsSwitchText%%</button>
167
  <td>Error logs limit</td>
168
  <td>Analyze last %%SUCURI.ErrorLogsLimit%% logs</td>
169
  <td class="td-with-button">
170
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
171
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
172
  <input type="text" name="sucuriscan_errorlogs_limit" placeholder="Number of lines to analyze" class="input-text" />
173
  <button type="submit" class="button-primary">Change</button>
179
  <td>Reset core integrity logs</td>
180
  <td><span class="sucuriscan-monospace">%%SUCURI.IntegrityLogLife%% of data</span></td>
181
  <td class="td-with-button">
182
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
183
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
184
  <input type="hidden" name="sucuriscan_reset_logfile" value="integrity" />
185
  <button type="submit" class="button-primary">Reset logs</button>
191
  <td>Reset last login logs</td>
192
  <td><span class="sucuriscan-monospace">%%SUCURI.LastLoginLogLife%% of data</span></td>
193
  <td class="td-with-button">
194
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
195
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
196
  <input type="hidden" name="sucuriscan_reset_logfile" value="lastlogins" />
197
  <button type="submit" class="button-primary">Reset logs</button>
203
  <td>Reset failed login logs</td>
204
  <td><span class="sucuriscan-monospace">%%SUCURI.FailedLoginLogLife%% of data</span></td>
205
  <td class="td-with-button">
206
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
207
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
208
  <input type="hidden" name="sucuriscan_reset_logfile" value="failedlogins" />
209
  <button type="submit" class="button-primary">Reset logs</button>
215
  <td>Reset sitecheck logs</td>
216
  <td><span class="sucuriscan-monospace">%%SUCURI.SiteCheckLogLife%% of data</span></td>
217
  <td class="td-with-button">
218
+ <form action="%%SUCURI.URL.Settings%%#scanner" method="post">
219
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
220
  <input type="hidden" name="sucuriscan_reset_logfile" value="sitecheck" />
221
  <button type="submit" class="button-primary">Reset logs</button>
inc/tpl/settings-selfhosting-monitor.html.tpl ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="postbox">
3
+ <h3>Log Exporter</h3>
4
+
5
+ <div class="inside">
6
+ <p>
7
+ This option allows you to export the WordPress audit logs to a local log file
8
+ that can be read by a SIEM or any log analysis software <em>(we recommend OSSEC)
9
+ </em>. That will give visibility from within WordPress to complement your log
10
+ monitoring infrastructure.
11
+ </p>
12
+
13
+ <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-%%SUCURI.SelfHostingMonitor.DisabledVisibility%%">
14
+ <span>Log Exporter is %%SUCURI.SelfHostingMonitor.Status%%</span>
15
+ </div>
16
+
17
+ <div class="sucuriscan-hstatus sucuriscan-hstatus-2 sucuriscan-monitor-fpath sucuriscan-%%SUCURI.SelfHostingMonitor.FpathVisibility%%">
18
+ <span class="sucuriscan-monospace">%%SUCURI.SelfHostingMonitor.Fpath%%</span>
19
+ <form action="%%SUCURI.URL.Settings%%#selfhosting" method="post">
20
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
21
+ <input type="hidden" name="sucuriscan_selfhosting_fpath" class="input-text" />
22
+ <button type="submit" class="button-primary %%SUCURI.SelfHostingMonitor.SwitchCssClass%%">
23
+ %%SUCURI.SelfHostingMonitor.SwitchText%%</button>
24
+ </form>
25
+ </div>
26
+
27
+ <p>
28
+ Specify the absolute location of the file <em>(including the extension)</em>
29
+ that you want to use to store a copy of the events that are being monitored by
30
+ the plugin. The file must exists and be writable by the PHP interpreter. Note
31
+ that the events that are being triggered when this option is disabled will not
32
+ be copied to this file even if you have enabled this feature before, you must
33
+ consider this when you give access to other administrator users to change the
34
+ settings of your website.
35
+ </p>
36
+
37
+ <div class="sucuriscan-inline-alert-warning">
38
+ <p>
39
+ Do not use a public location to store the logs, you will end up leaking
40
+ sensitive information about your website and the activity of your users. If you
41
+ decide to use a file located in the public directory for any particular reason
42
+ we recommend you to name it with a random-unique string so malicious users can
43
+ not easily access it.
44
+ </p>
45
+ </div>
46
+
47
+ <form action="%%SUCURI.URL.Settings%%#selfhosting" method="post" class="sucuriscan-%%SUCURI.SelfHostingMonitor.DisabledVisibility%%">
48
+ <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
49
+ <span class="sucuriscan-input-group">
50
+ <label>Absolute File Path:</label>
51
+ <input type="text" name="sucuriscan_selfhosting_fpath" class="input-text" />
52
+ </span>
53
+ <button type="submit" class="button-primary">Save</button>
54
+ </form>
55
+ </div>
56
+ </div>
inc/tpl/settings-selfhosting.html.tpl ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+
2
+ <div id="poststuff">
3
+ %%%SUCURI.SelfHosting.Monitor%%%
4
+ </div>
inc/tpl/settings-trustip.html.tpl CHANGED
@@ -12,7 +12,7 @@
12
  specify ranges of IP addresses <em>(only 8, 16, and 24)</em>.
13
  </p>
14
 
15
- <form action="%%SUCURI.URL.Settings%%#settings-trustip" method="POST">
16
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
17
  <input type="text" name="sucuriscan_trust_ip" placeholder="e.g. 182.120.56.0/24" />
18
  <input type="submit" value="Add Entry" class="button button-primary" />
@@ -21,7 +21,7 @@
21
  </div>
22
  </div>
23
 
24
- <form action="%%SUCURI.URL.Settings%%#settings-trustip" method="post">
25
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
26
 
27
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-settings-trustip">
@@ -38,7 +38,7 @@
38
  </thead>
39
 
40
  <tbody>
41
- %%SUCURI.TrustedIPs.List%%
42
 
43
  <tr class="sucuriscan-%%SUCURI.TrustedIPs.NoItems.Visibility%%">
44
  <td colspan="4">
12
  specify ranges of IP addresses <em>(only 8, 16, and 24)</em>.
13
  </p>
14
 
15
+ <form action="%%SUCURI.URL.Settings%%#trustip" method="POST">
16
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
17
  <input type="text" name="sucuriscan_trust_ip" placeholder="e.g. 182.120.56.0/24" />
18
  <input type="submit" value="Add Entry" class="button button-primary" />
21
  </div>
22
  </div>
23
 
24
+ <form action="%%SUCURI.URL.Settings%%#trustip" method="post">
25
  <input type="hidden" name="sucuriscan_page_nonce" value="%%SUCURI.PageNonce%%" />
26
 
27
  <table class="wp-list-table widefat sucuriscan-table sucuriscan-settings-trustip">
38
  </thead>
39
 
40
  <tbody>
41
+ %%%SUCURI.TrustedIPs.List%%%
42
 
43
  <tr class="sucuriscan-%%SUCURI.TrustedIPs.NoItems.Visibility%%">
44
  <td colspan="4">
inc/tpl/settings.html.tpl CHANGED
@@ -2,55 +2,69 @@
2
  <div class="sucuriscan-tabs">
3
  <ul>
4
  <li>
5
- <a href="#" data-tabname="settings-general">General Settings</a>
6
  </li>
7
  <li>
8
- <a href="#" data-tabname="settings-scanner">Scanner Settings</a>
9
  </li>
10
  <li>
11
- <a href="#" data-tabname="settings-ignorescanning">Ignore Scanning</a>
12
  </li>
13
  <li>
14
- <a href="#" data-tabname="settings-notifications">Alert Settings</a>
15
  </li>
16
  <li>
17
- <a href="#" data-tabname="settings-ignorerules">Ignore Alerts</a>
18
  </li>
19
  <li>
20
- <a href="#" data-tabname="settings-trustip">Trust IP</a>
21
  </li>
22
  <li>
23
- <a href="#" data-tabname="settings-heartbeat">Heartbeat</a>
 
 
 
 
 
 
24
  </li>
25
  </ul>
26
 
27
  <div class="sucuriscan-tab-containers">
28
- <div id="sucuriscan-settings-general">
29
- %%SUCURI.Settings.General%%
 
 
 
 
 
 
 
 
30
  </div>
31
 
32
- <div id="sucuriscan-settings-scanner">
33
- %%SUCURI.Settings.Scanner%%
34
  </div>
35
 
36
- <div id="sucuriscan-settings-ignorescanning">
37
- %%SUCURI.Settings.IgnoreScanning%%
38
  </div>
39
 
40
- <div id="sucuriscan-settings-notifications">
41
- %%SUCURI.Settings.Notifications%%
42
  </div>
43
 
44
- <div id="sucuriscan-settings-ignorerules">
45
- %%SUCURI.Settings.IgnoreRules%%
46
  </div>
47
 
48
- <div id="sucuriscan-settings-trustip">
49
- %%SUCURI.Settings.TrustIP%%
50
  </div>
51
 
52
- <div id="sucuriscan-settings-heartbeat">
53
- %%SUCURI.Settings.Heartbeat%%
54
  </div>
55
  </div>
56
  </div>
2
  <div class="sucuriscan-tabs">
3
  <ul>
4
  <li>
5
+ <a href="#general" data-tabname="general">General</a>
6
  </li>
7
  <li>
8
+ <a href="#scanner" data-tabname="scanner">Scanner</a>
9
  </li>
10
  <li>
11
+ <a href="#notifications" data-tabname="notifications">Alerts</a>
12
  </li>
13
  <li>
14
+ <a href="#apiservice" data-tabname="apiservice">API Service</a>
15
  </li>
16
  <li>
17
+ <a href="#selfhosting" data-tabname="selfhosting">Log Exporter</a>
18
  </li>
19
  <li>
20
+ <a href="#ignorescanning" data-tabname="ignorescanning">Ignore Scanning</a>
21
  </li>
22
  <li>
23
+ <a href="#ignorerules" data-tabname="ignorerules">Ignore Alerts</a>
24
+ </li>
25
+ <li>
26
+ <a href="#trustip" data-tabname="trustip">Trust IP</a>
27
+ </li>
28
+ <li>
29
+ <a href="#heartbeat" data-tabname="heartbeat">Heartbeat</a>
30
  </li>
31
  </ul>
32
 
33
  <div class="sucuriscan-tab-containers">
34
+ <div id="sucuriscan-general">
35
+ %%%SUCURI.Settings.General%%%
36
+ </div>
37
+
38
+ <div id="sucuriscan-scanner">
39
+ %%%SUCURI.Settings.Scanner%%%
40
+ </div>
41
+
42
+ <div id="sucuriscan-notifications">
43
+ %%%SUCURI.Settings.Alerts%%%
44
  </div>
45
 
46
+ <div id="sucuriscan-apiservice">
47
+ %%%SUCURI.Settings.ApiService%%%
48
  </div>
49
 
50
+ <div id="sucuriscan-selfhosting">
51
+ %%%SUCURI.Settings.SelfHosting%%%
52
  </div>
53
 
54
+ <div id="sucuriscan-ignorescanning">
55
+ %%%SUCURI.Settings.IgnoreScanning%%%
56
  </div>
57
 
58
+ <div id="sucuriscan-ignorerules">
59
+ %%%SUCURI.Settings.IgnoreRules%%%
60
  </div>
61
 
62
+ <div id="sucuriscan-trustip">
63
+ %%%SUCURI.Settings.TrustIP%%%
64
  </div>
65
 
66
+ <div id="sucuriscan-heartbeat">
67
+ %%%SUCURI.Settings.Heartbeat%%%
68
  </div>
69
  </div>
70
  </div>
inc/tpl/setup-form.html.tpl CHANGED
@@ -32,7 +32,7 @@
32
  <td>E-mail Address:</td>
33
  <td>
34
  <select name="sucuriscan_setup_user">
35
- %%SUCURI.AdminEmails%%
36
  </select>
37
  </td>
38
  </tr>
32
  <td>E-mail Address:</td>
33
  <td>
34
  <select name="sucuriscan_setup_user">
35
+ %%%SUCURI.AdminEmails%%%
36
  </select>
37
  </td>
38
  </tr>
inc/tpl/setup-notice.html.tpl CHANGED
@@ -1,6 +1,6 @@
1
 
2
  <div class="updated sucuriscan-setup-notice sucuriscan-clearfix">
3
- <a href="http://sucuri.net/" target="_blank" class="sucuriscan-pull-left sucuriscan-setup-image">
4
  <img src="%%SUCURI.SucuriURL%%/inc/images/logo.png" alt="Sucuri Scanner" />
5
  </a>
6
 
1
 
2
  <div class="updated sucuriscan-setup-notice sucuriscan-clearfix">
3
+ <a href="https://sucuri.net/" target="_blank" class="sucuriscan-pull-left sucuriscan-setup-image">
4
  <img src="%%SUCURI.SucuriURL%%/inc/images/logo.png" alt="Sucuri Scanner" />
5
  </a>
6
 
readme.txt CHANGED
@@ -1,10 +1,10 @@
1
  === Sucuri Security - Auditing, Malware Scanner and Security Hardening ===
2
  Contributors: dd@sucuri.net
3
- Donate Link: http://sucuri.net/
4
  Tags: malware, security, firewall, scan, spam, virus, sucuri, protection,WordPress Security, Login Security,Security Auditing,File Integrity,htaccess,phishing,backdoors,SQL Injection, RFI, LFI, XSS, CSRF, website firewall, Website Security, Performance Optimization, Zero Day, Software Vulnerability, Exploits, Hacks, Attackers, Bad Actors, Reverse Proxy, Two Factor Security, Two Factor Authentication, Security Logs, HeatBleed Vulnerability, Website Protection, Bash Vulnerability, RevSlider Vulnerability, MailPoet Vulnerability, Malware Prevention, Website Firewall, Website AntiVirus, Security Response, Security Detection, Security Prevention
5
  Requires at least:3.2
6
- Stable tag: 1.7.16
7
- Tested up to: 4.3.1
8
 
9
  The Sucuri WordPress Security plugin is a security toolset for security integrity monitoring, malware detection and security hardening.
10
 
@@ -79,10 +79,9 @@ Here is a video of the Security File Integrity Monitoring feature:
79
  = Remote Security Malware Scanning =
80
 
81
  This feature is powered by our very powerful scanning engine, found on our
82
- free security scanner - <a href="http://sitecheck.sucuri.net">SiteCheck</a>. It’s
83
- important to take some time to <a
84
- href="http://blog.sucuri.net/2012/10/ask-sucuri-how-does-sitecheck-work.html">understand
85
- how this scanner works</a>.
86
 
87
  Here is a video of the Remote Security Malware Scanning feature:
88
 
@@ -178,8 +177,11 @@ This is coupled with a number of features like:
178
  <li>Failover and Redundancy</li>
179
  </ol>
180
 
181
- This is not included as a <strong>Free</strong> option to the plugin, but is
182
- integrated so that if purchased you are able to activate. If you prefer to leverage the Sucuri Security Website Firewall (CloudProxy) product by itself, you have the option to operate the <a href="https://wordpress.org/plugins/sucuri-cloudproxy-waf/">Website Firewall WordPress Security</a> plugin in standalone mode.
 
 
 
183
 
184
  Here is a video of the Sucuri Security Website Firewall (Add On Security Service) feature:
185
 
@@ -208,7 +210,7 @@ To install Sucuri Security and complement your Security posture:
208
 
209
 
210
  1. You will want to log into your WordPress administration panel - (e.g.,
211
- http://yourdomain/wp-admin)
212
 
213
  2. Navigate to <strong>Plugins Menu</strong> option in your WordPress
214
  administration panel
@@ -253,7 +255,7 @@ security needs as you see fit.
253
  == FAQ ==
254
 
255
  More information can be found on the the Sucuri Security WordPress Security
256
- plugin via our free [Knowledge Base](http://kb.sucuri.net/plugins/WordPress+Plugin/index).
257
 
258
  = What does this plugin do that other WordPress security plugins don't do? =
259
 
@@ -352,6 +354,12 @@ service from the WordPress dashboard.
352
 
353
  == Changelog ==
354
 
 
 
 
 
 
 
355
  = 1.7.16 =
356
  * Fixing a low severity XSS (needs admin access to create it)
357
 
@@ -587,5 +595,5 @@ service from the WordPress dashboard.
587
 
588
  == Credits ==
589
 
590
- * <a href="http://sucuri.net">Sucuri Security</a>
591
 
1
  === Sucuri Security - Auditing, Malware Scanner and Security Hardening ===
2
  Contributors: dd@sucuri.net
3
+ Donate Link: https://sucuri.net/
4
  Tags: malware, security, firewall, scan, spam, virus, sucuri, protection,WordPress Security, Login Security,Security Auditing,File Integrity,htaccess,phishing,backdoors,SQL Injection, RFI, LFI, XSS, CSRF, website firewall, Website Security, Performance Optimization, Zero Day, Software Vulnerability, Exploits, Hacks, Attackers, Bad Actors, Reverse Proxy, Two Factor Security, Two Factor Authentication, Security Logs, HeatBleed Vulnerability, Website Protection, Bash Vulnerability, RevSlider Vulnerability, MailPoet Vulnerability, Malware Prevention, Website Firewall, Website AntiVirus, Security Response, Security Detection, Security Prevention
5
  Requires at least:3.2
6
+ Stable tag: 1.7.17
7
+ Tested up to: 4.4.1
8
 
9
  The Sucuri WordPress Security plugin is a security toolset for security integrity monitoring, malware detection and security hardening.
10
 
79
  = Remote Security Malware Scanning =
80
 
81
  This feature is powered by our very powerful scanning engine, found on our
82
+ free security scanner - <a href="https://sitecheck.sucuri.net">SiteCheck</a>. It’s
83
+ important to take some time to <a href="https://blog.sucuri.net/2012/10/ask-sucuri-how-does-sitecheck-work.html">
84
+ understand how this scanner works</a>.
 
85
 
86
  Here is a video of the Remote Security Malware Scanning feature:
87
 
177
  <li>Failover and Redundancy</li>
178
  </ol>
179
 
180
+ This is <strong>not included as a free</strong> option to the plugin, but is
181
+ integrated so that if purchased you are able to activate. If you prefer to
182
+ leverage the Sucuri Security Website Firewall (CloudProxy) product by itself,
183
+ you have the option to operate the <a href="https://wordpress.org/plugins/sucuri-cloudproxy-waf/">
184
+ Website Firewall WordPress Security</a> plugin in standalone mode.
185
 
186
  Here is a video of the Sucuri Security Website Firewall (Add On Security Service) feature:
187
 
210
 
211
 
212
  1. You will want to log into your WordPress administration panel - (e.g.,
213
+ https://yourdomain/wp-admin)
214
 
215
  2. Navigate to <strong>Plugins Menu</strong> option in your WordPress
216
  administration panel
255
  == FAQ ==
256
 
257
  More information can be found on the the Sucuri Security WordPress Security
258
+ plugin via our free [Knowledge Base](https://kb.sucuri.net/plugins/WordPress+Plugin/index).
259
 
260
  = What does this plugin do that other WordPress security plugins don't do? =
261
 
354
 
355
  == Changelog ==
356
 
357
+ = 1.7.17 =
358
+ * Added API service failback mechanism
359
+ * Added core integrity email on force scan
360
+ * Slight interface redesign
361
+ * Various bugfixes and improvements
362
+
363
  = 1.7.16 =
364
  * Fixing a low severity XSS (needs admin access to create it)
365
 
595
 
596
  == Credits ==
597
 
598
+ * <a href="https://sucuri.net">Sucuri Security</a>
599
 
sucuri.php CHANGED
@@ -1,11 +1,11 @@
1
  <?php
2
  /*
3
  Plugin Name: Sucuri Security - Auditing, Malware Scanner and Hardening
4
- Plugin URI: http://wordpress.sucuri.net/
5
- Description: The <a href="http://sucuri.net/" target="_blank">Sucuri</a> plugin provides the website owner the best Activity Auditing, SiteCheck Remote Malware Scanning, Effective Security Hardening and Post-Hack features. SiteCheck will check for malware, spam, blacklisting and other security issues like .htaccess redirects, hidden eval code, etc. The best thing about it is it's completely free.
6
  Author: Sucuri, INC
7
- Version: 1.7.16
8
- Author URI: http://sucuri.net
9
  */
10
 
11
 
@@ -42,8 +42,8 @@ $sucuriscan_dependencies = array(
42
  );
43
 
44
  // Terminate execution if any of the functions mentioned above is not defined.
45
- foreach ( $sucuriscan_dependencies as $dependency ) {
46
- if ( ! function_exists( $dependency ) ) {
47
  exit(0);
48
  }
49
  }
@@ -61,92 +61,97 @@ foreach ( $sucuriscan_dependencies as $dependency ) {
61
  /**
62
  * Unique name of the plugin through out all the code.
63
  */
64
- define( 'SUCURISCAN', 'sucuriscan' );
65
 
66
  /**
67
  * Current version of the plugin's code.
68
  */
69
- define( 'SUCURISCAN_VERSION', '1.7.16' );
70
 
71
  /**
72
  * The name of the Sucuri plugin main file.
73
  */
74
- define( 'SUCURISCAN_PLUGIN_FILE', 'sucuri.php' );
75
 
76
  /**
77
  * The name of the folder where the plugin's files will be located.
78
  */
79
- define( 'SUCURISCAN_PLUGIN_FOLDER', 'sucuri-scanner' );
80
 
81
  /**
82
  * The fullpath where the plugin's files will be located.
83
  */
84
- define( 'SUCURISCAN_PLUGIN_PATH', WP_PLUGIN_DIR.'/'.SUCURISCAN_PLUGIN_FOLDER );
85
 
86
  /**
87
  * The fullpath of the main plugin file.
88
  */
89
- define( 'SUCURISCAN_PLUGIN_FILEPATH', SUCURISCAN_PLUGIN_PATH.'/'.SUCURISCAN_PLUGIN_FILE );
90
 
91
  /**
92
  * The local URL where the plugin's files and assets are served.
93
  */
94
- define( 'SUCURISCAN_URL', rtrim( plugin_dir_url( SUCURISCAN_PLUGIN_FILEPATH ), '/' ) );
95
 
96
  /**
97
  * Checksum of this file to check the integrity of the plugin.
98
  */
99
- define( 'SUCURISCAN_PLUGIN_CHECKSUM', @md5_file( SUCURISCAN_PLUGIN_FILEPATH ) );
100
 
101
  /**
102
  * Remote URL where the public Sucuri API service is running.
103
  */
104
- define( 'SUCURISCAN_API', 'https://wordpress.sucuri.net/api/' );
105
 
106
  /**
107
  * Latest version of the public Sucuri API.
108
  */
109
- define( 'SUCURISCAN_API_VERSION', 'v1' );
110
 
111
  /**
112
  * Remote URL where the CloudProxy API service is running.
113
  */
114
- define( 'SUCURISCAN_CLOUDPROXY_API', 'https://waf.sucuri.net/api' );
115
 
116
  /**
117
  * Latest version of the CloudProxy API.
118
  */
119
- define( 'SUCURISCAN_CLOUDPROXY_API_VERSION', 'v2' );
120
 
121
  /**
122
  * The maximum quantity of entries that will be displayed in the last login page.
123
  */
124
- define( 'SUCURISCAN_LASTLOGINS_USERSLIMIT', 25 );
125
 
126
  /**
127
  * The maximum quantity of entries that will be displayed in the audit logs page.
128
  */
129
- define( 'SUCURISCAN_AUDITLOGS_PER_PAGE', 50 );
130
 
131
  /**
132
  * The maximum quantity of buttons in the paginations.
133
  */
134
- define( 'SUCURISCAN_MAX_PAGINATION_BUTTONS', 20 );
135
 
136
  /**
137
  * The minimum quantity of seconds to wait before each filesystem scan.
138
  */
139
- define( 'SUCURISCAN_MINIMUM_RUNTIME', 10800 );
140
 
141
  /**
142
  * The life time of the cache for the results of the SiteCheck scans.
143
  */
144
- define( 'SUCURISCAN_SITECHECK_LIFETIME', 1200 );
145
 
146
  /**
147
  * The life time of the cache for the results of the get_plugins function.
148
  */
149
- define( 'SUCURISCAN_GET_PLUGINS_LIFETIME', 1800 );
 
 
 
 
 
150
 
151
  /**
152
  * Plugin's global variables.
@@ -156,7 +161,7 @@ define( 'SUCURISCAN_GET_PLUGINS_LIFETIME', 1800 );
156
  * conditional will act as a container helping in the readability of the code
157
  * considering the total number of lines that this file will have.
158
  */
159
- if ( defined( 'SUCURISCAN' ) ) {
160
  /**
161
  * Define the prefix for some actions and filters that rely in the
162
  * differentiation of the type of site where the extension is being used. There
@@ -176,7 +181,7 @@ if ( defined( 'SUCURISCAN' ) ) {
176
  $sucuriscan_pages = array(
177
  'sucuriscan' => 'Dashboard',
178
  'sucuriscan_scanner' => 'Malware Scan',
179
- 'sucuriscan_monitoring' => 'Firewall (WAF)',
180
  'sucuriscan_hardening' => 'Hardening',
181
  'sucuriscan_posthack' => 'Post-Hack',
182
  'sucuriscan_lastlogins' => 'Last Logins',
@@ -197,30 +202,30 @@ if ( defined( 'SUCURISCAN' ) ) {
197
  */
198
 
199
  $sucuriscan_notify_options = array(
200
- 'sucuriscan_notify_plugin_change' => 'Receive email alerts for <strong>Sucuri</strong> plugin changes',
201
  'sucuriscan_prettify_mails' => 'Receive email alerts in HTML <em>(there may be issues with some mail services)</em>',
202
  'sucuriscan_use_wpmail' => 'Use WordPress functions to send mails <em>(uncheck to use native PHP functions)</em>',
203
  'sucuriscan_lastlogin_redirection' => 'Allow redirection after login to report the last-login information',
204
  'sucuriscan_notify_scan_checksums' => 'Receive email alerts for core integrity checks',
205
  'sucuriscan_notify_user_registration' => 'user:Receive email alerts for new user registration',
206
  'sucuriscan_notify_success_login' => 'user:Receive email alerts for successful login attempts',
207
- 'sucuriscan_notify_failed_login' => 'user:Receive email alerts for failed login attempts',
208
- 'sucuriscan_notify_bruteforce_attack' => 'user:Receive email alerts for password guessing brute force attacks',
209
  'sucuriscan_notify_post_publication' => 'Receive email alerts for Post-Type changes <em>(configure from Ignore Alerts)</em>',
210
  'sucuriscan_notify_website_updated' => 'Receive email alerts when the WordPress version is updated',
211
  'sucuriscan_notify_settings_updated' => 'Receive email alerts when your website settings are updated',
212
  'sucuriscan_notify_theme_editor' => 'Receive email alerts when a file is modified with theme/plugin editor',
213
- 'sucuriscan_notify_plugin_installed' => 'plugin:Receive email alerts when a <strong>plugin is installed</strong>',
214
- 'sucuriscan_notify_plugin_activated' => 'plugin:Receive email alerts when a <strong>plugin is activated</strong>',
215
- 'sucuriscan_notify_plugin_deactivated' => 'plugin:Receive email alerts when a <strong>plugin is deactivated</strong>',
216
- 'sucuriscan_notify_plugin_updated' => 'plugin:Receive email alerts when a <strong>plugin is updated</strong>',
217
- 'sucuriscan_notify_plugin_deleted' => 'plugin:Receive email alerts when a <strong>plugin is deleted</strong>',
218
- 'sucuriscan_notify_widget_added' => 'widget:Receive email alerts when a <strong>widget is added</strong> to a sidebar',
219
- 'sucuriscan_notify_widget_deleted' => 'widget:Receive email alerts when a <strong>widget is deleted</strong> from a sidebar',
220
- 'sucuriscan_notify_theme_installed' => 'theme:Receive email alerts when a <strong>theme is installed</strong>',
221
- 'sucuriscan_notify_theme_activated' => 'theme:Receive email alerts when a <strong>theme is activated</strong>',
222
- 'sucuriscan_notify_theme_updated' => 'theme:Receive email alerts when a <strong>theme is updated</strong>',
223
- 'sucuriscan_notify_theme_deleted' => 'theme:Receive email alerts when a <strong>theme is deleted</strong>',
224
  );
225
 
226
  $sucuriscan_schedule_allowed = array(
@@ -275,7 +280,7 @@ if ( defined( 'SUCURISCAN' ) ) {
275
  /**
276
  * Remove the WordPress generator meta-tag from the source code.
277
  */
278
- remove_action( 'wp_head', 'wp_generator' );
279
 
280
  /**
281
  * Run a specific function defined in the plugin's code to locate every
@@ -283,7 +288,7 @@ if ( defined( 'SUCURISCAN' ) ) {
283
  * information to the Sucuri API service where a security and integrity scan
284
  * will be performed against the hashes provided and the official versions.
285
  */
286
- add_action( 'sucuriscan_scheduled_scan', 'SucuriScan::run_scheduled_task' );
287
 
288
  /**
289
  * Initialize the execute of the main plugin's functions.
@@ -291,10 +296,10 @@ if ( defined( 'SUCURISCAN' ) ) {
291
  * This will load the menu options in the WordPress administrator panel, and
292
  * execute the bootstrap function of the plugin.
293
  */
294
- add_action( 'init', 'SucuriScanInterface::initialize', 1 );
295
- add_action( 'admin_init', 'SucuriScanInterface::create_datastore_folder' );
296
- add_action( 'admin_init', 'SucuriScanInterface::handle_old_plugins' );
297
- add_action( 'admin_enqueue_scripts', 'SucuriScanInterface::enqueue_scripts', 1 );
298
 
299
  /**
300
  * Display extension menu and submenu items in the correct interface. For single
@@ -302,16 +307,16 @@ if ( defined( 'SUCURISCAN' ) ) {
302
  * multisite installations the menu items must be available only in the network
303
  * panel and hidden in the administration panel of the subsites.
304
  */
305
- add_action( $sucuriscan_action_prefix . 'admin_menu', 'SucuriScanInterface::add_interface_menu' );
306
 
307
  /**
308
  * Attach Ajax requests to a custom page handler.
309
  */
310
- foreach ( $sucuriscan_pages as $page_func => $page_title ) {
311
  $ajax_func = $page_func . '_ajax';
312
 
313
- if ( function_exists( $ajax_func ) ) {
314
- add_action( 'wp_ajax_' . $ajax_func, $ajax_func );
315
  }
316
  }
317
 
@@ -325,7 +330,7 @@ if ( defined( 'SUCURISCAN' ) ) {
325
  *
326
  * @see Class SucuriScanHook
327
  */
328
- if ( class_exists( 'SucuriScanHook' ) ) {
329
  $sucuriscan_hooks = array(
330
  'add_attachment',
331
  'add_link',
@@ -347,19 +352,19 @@ if ( defined( 'SUCURISCAN' ) ) {
347
  'xmlrpc_publish_post',
348
  );
349
 
350
- if ( SucuriScanOption::is_enabled( ':xhr_monitor' ) ) {
351
  $sucuriscan_hooks[] = 'all';
352
  }
353
 
354
- foreach ( $sucuriscan_hooks as $hook_name ) {
355
  $hook_func = 'SucuriScanHook::hook_' . $hook_name;
356
- add_action( $hook_name, $hook_func, 50, 5 );
357
  }
358
 
359
- add_action( 'admin_init', 'SucuriScanHook::hook_undefined_actions' );
360
- add_action( 'login_form', 'SucuriScanHook::hook_undefined_actions' );
361
  } else {
362
- SucuriScanInterface::error( 'Function call interceptors are not working properly.' );
363
  }
364
 
365
  /**
@@ -371,7 +376,7 @@ if ( defined( 'SUCURISCAN' ) ) {
371
  * the plugin to execute the filesystem scans, the project integrity, and the
372
  * email notifications.
373
  */
374
- add_action( $sucuriscan_action_prefix . 'admin_notices', 'SucuriScanInterface::setup_notice' );
375
 
376
  /**
377
  * Heartbeat API
@@ -382,12 +387,12 @@ if ( defined( 'SUCURISCAN' ) ) {
382
  * cases it may improve the performance of the site by reducing the quantity of
383
  * requests sent to the server per session.
384
  */
385
- add_filter( 'init', 'SucuriScanHeartbeat::register_script', 1 );
386
- add_filter( 'heartbeat_settings', 'SucuriScanHeartbeat::update_settings' );
387
- add_filter( 'heartbeat_send', 'SucuriScanHeartbeat::respond_to_send', 10, 3 );
388
- add_filter( 'heartbeat_received', 'SucuriScanHeartbeat::respond_to_received', 10, 3 );
389
- add_filter( 'heartbeat_nopriv_send', 'SucuriScanHeartbeat::respond_to_send', 10, 3 );
390
- add_filter( 'heartbeat_nopriv_received', 'SucuriScanHeartbeat::respond_to_received', 10, 3 );
391
  }
392
 
393
  /**
@@ -397,12 +402,13 @@ if ( defined( 'SUCURISCAN' ) ) {
397
  * other libraries extending from this and functions defined in other files, be
398
  * aware of the hierarchy and check the other libraries for duplicated methods.
399
  */
400
- class SucuriScan {
401
-
402
  /**
403
  * Class constructor.
404
  */
405
- public function __construct(){
 
406
  }
407
 
408
  /**
@@ -416,9 +422,14 @@ class SucuriScan {
416
  * @param string $var_name Name of a variable with an optional colon at the beginning.
417
  * @return string Full name of the variable with the extra characters (if needed).
418
  */
419
- public static function variable_prefix( $var_name = '' ){
420
- if ( preg_match( '/^:(.*)/', $var_name, $match ) ) {
421
- $var_name = sprintf( '%s_%s', SUCURISCAN, $match[1] );
 
 
 
 
 
422
  }
423
 
424
  return $var_name;
@@ -430,27 +441,31 @@ class SucuriScan {
430
  * @param string $property The configuration option name.
431
  * @return string Value of the configuration option as a string on success.
432
  */
433
- public static function ini_get( $property = '' ){
434
- $ini_value = ini_get( $property );
 
 
 
 
 
 
 
 
 
 
435
 
436
- if ( $ini_value === false ) {
437
  $ini_value = 'Undefined';
438
- } elseif ( empty($ini_value) ) {
439
- $ini_value = 'Off';
440
- } elseif ( is_null( $ini_value ) ) {
441
- switch ( $property ) {
442
- case 'error_log': $ini_value = 'error_log'; break;
443
- case 'safe_mode': $ini_value = 'Off'; break;
444
- case 'memory_limit': $ini_value = '128M'; break;
445
- case 'upload_max_filesize': $ini_value = '2M'; break;
446
- case 'post_max_size': $ini_value = '8M'; break;
447
- case 'max_execution_time': $ini_value = '30'; break;
448
- case 'max_input_time': $ini_value = '-1'; break;
449
  }
450
  }
451
 
452
- if ( $property == 'error_log' ) {
453
- $ini_value = basename( $ini_value );
454
  }
455
 
456
  return $ini_value;
@@ -463,12 +478,13 @@ class SucuriScan {
463
  * @param string $text The text which is to be encoded.
464
  * @return string The encoded text with HTML entities.
465
  */
466
- public static function escape( $text = '' ){
 
467
  // Escape the value of the variable using a built-in function if possible.
468
- if ( function_exists( 'esc_attr' ) ) {
469
- $text = esc_attr( $text );
470
  } else {
471
- $text = htmlspecialchars( $text );
472
  }
473
 
474
  return $text;
@@ -480,12 +496,18 @@ class SucuriScan {
480
  * @param integer $length Length of the string that will be generated.
481
  * @return string The random string generated.
482
  */
483
- public static function random_char( $length = 4 ){
 
484
  $string = '';
485
- $chars = range( 'a','z' );
 
 
 
 
486
 
487
- for ( $i = 0; $i < $length; $i++ ) {
488
- $string .= $chars[ rand( 0, count( $chars ) -1 ) ];
 
489
  }
490
 
491
  return $string;
@@ -495,15 +517,16 @@ class SucuriScan {
495
  * Translate a given number in bytes to a human readable file size using the
496
  * a approximate value in Kylo, Mega, Giga, etc.
497
  *
498
- * @link http://www.php.net/manual/en/function.filesize.php#106569
499
  * @param integer $bytes An integer representing a file size in bytes.
500
  * @param integer $decimals How many decimals should be returned after the translation.
501
  * @return string Human readable representation of the given number in Kylo, Mega, Giga, etc.
502
  */
503
- public static function human_filesize( $bytes = 0, $decimals = 2 ){
 
504
  $sz = 'BKMGTP';
505
- $factor = floor( (strlen( $bytes ) - 1) / 3 );
506
- return sprintf( "%.{$decimals}f", $bytes / pow( 1024, $factor ) ) . @$sz[ $factor ];
507
  }
508
 
509
  /**
@@ -513,38 +536,37 @@ class SucuriScan {
513
  * @param string $path The relative path that needs to be completed to get the absolute path.
514
  * @return string The full filesystem path including the directory specified.
515
  */
516
- public static function datastore_folder_path( $path = '' ){
517
- $datastore_path = SucuriScanOption::get_option( ':datastore_path' );
518
- $datastore_dirname = 'sucuri';
 
519
 
520
  // Use the uploads folder by default.
521
- if ( empty($datastore_path) ) {
522
  $uploads_path = false;
523
 
524
  // Multisite installations may have different paths.
525
- if ( function_exists( 'wp_upload_dir' ) ) {
526
  $upload_dir = wp_upload_dir();
527
 
528
- if ( isset($upload_dir['basedir']) ) {
529
- $uploads_path = rtrim( $upload_dir['basedir'], '/' );
530
  }
531
  }
532
 
533
- if ( $uploads_path === false ) {
534
- if ( defined( 'WP_CONTENT_DIR' ) ) {
535
- $uploads_path = rtrim( WP_CONTENT_DIR, '/' ) . '/uploads';
536
  } else {
537
- $uploads_path = rtrim( ABSPATH, '/' ) . '/wp-content/uploads';
538
  }
539
  }
540
 
541
- $datastore_path = $uploads_path . '/' . $datastore_dirname;
542
- SucuriScanOption::update_option( ':datastore_path', $datastore_path );
543
  }
544
 
545
- $wp_filepath = rtrim( $datastore_path, '/' ) . '/' . $path;
546
-
547
- return $wp_filepath;
548
  }
549
 
550
  /**
@@ -552,15 +574,18 @@ class SucuriScan {
552
  *
553
  * @return boolean Either TRUE or FALSE in case WordPress is being used as a multi-site instance.
554
  */
555
- public static function is_multisite(){
556
- if (
557
- function_exists( 'is_multisite' )
558
- && is_multisite()
559
- ) {
560
- return true;
561
- }
562
 
563
- return false;
 
 
 
 
 
 
564
  }
565
 
566
  /**
@@ -568,22 +593,23 @@ class SucuriScan {
568
  *
569
  * @return string The version number of Wordpress installed.
570
  */
571
- public static function site_version(){
 
572
  global $wp_version;
573
 
574
- if ( $wp_version === null ) {
575
  $wp_version_path = ABSPATH . WPINC . '/version.php';
576
 
577
- if ( file_exists( $wp_version_path ) ) {
578
  include($wp_version_path);
579
  $wp_version = isset($wp_version) ? $wp_version : '0.0';
580
  } else {
581
- $option_version = get_option( 'version' );
582
  $wp_version = $option_version ? $option_version : '0.0';
583
  }
584
  }
585
 
586
- $wp_version = self::escape( $wp_version );
587
 
588
  return $wp_version;
589
  }
@@ -593,19 +619,20 @@ class SucuriScan {
593
  *
594
  * @return string Absolute path of the WordPress configuration file.
595
  */
596
- public static function get_wpconfig_path(){
597
- if ( defined( 'ABSPATH' ) ) {
 
598
  $file_path = ABSPATH . '/wp-config.php';
599
 
600
  // if wp-config.php doesn't exist, or is not readable check one directory up.
601
- if ( ! file_exists( $file_path ) ) {
602
  $file_path = ABSPATH . '/../wp-config.php';
603
  }
604
 
605
  // Remove duplicated double slashes.
606
- $file_path = @realpath( $file_path );
607
 
608
- if ( $file_path ) {
609
  return $file_path;
610
  }
611
  }
@@ -618,18 +645,19 @@ class SucuriScan {
618
  *
619
  * @return string Absolute path of the main WordPress htaccess file.
620
  */
621
- public static function get_htaccess_path(){
622
- if ( defined( 'ABSPATH' ) ) {
 
623
  $base_dirs = array(
624
- rtrim( ABSPATH, '/' ),
625
- dirname( ABSPATH ),
626
- dirname( dirname( ABSPATH ) ),
627
  );
628
 
629
- foreach ( $base_dirs as $base_dir ) {
630
- $htaccess_path = sprintf( '%s/.htaccess', $base_dir );
631
 
632
- if ( file_exists( $htaccess_path ) ) {
633
  return $htaccess_path;
634
  }
635
  }
@@ -643,7 +671,8 @@ class SucuriScan {
643
  *
644
  * @return string Secret key definition pattern.
645
  */
646
- public static function secret_key_pattern(){
 
647
  return '/define\(\s*\'([A-Z_]+)\',(\s*)\'(.+)\'\s*\);/';
648
  }
649
 
@@ -652,57 +681,106 @@ class SucuriScan {
652
  *
653
  * @return void
654
  */
655
- public static function run_scheduled_task(){
 
656
  SucuriScanEvent::filesystem_scan();
657
- sucuriscan_core_files( true );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
658
  }
659
 
660
  /**
661
  * Retrieve the real ip address of the user in the current request.
662
  *
663
- * @param boolean $return_header Whether the header name where the address was found must be returned.
664
- * @return string The real ip address of the user in the current request.
665
  */
666
- public static function get_remote_addr( $return_header = false ){
667
- $remote_addr = '';
 
668
  $header_used = 'unknown';
 
669
 
670
- if (
671
- self::support_reverse_proxy()
672
- || self::is_behind_cloudproxy()
673
- ) {
674
- $alternatives = array(
675
- 'HTTP_X_SUCURI_CLIENTIP',
676
- 'HTTP_X_REAL_IP',
677
- 'HTTP_CLIENT_IP',
678
- 'HTTP_X_FORWARDED_FOR',
679
- 'HTTP_X_FORWARDED',
680
- 'HTTP_FORWARDED_FOR',
681
- 'HTTP_FORWARDED',
682
- 'SUCURI_RIP',
683
- 'REMOTE_ADDR',
684
- );
685
-
686
- foreach ( $alternatives as $alternative ) {
687
- if (
688
- isset($_SERVER[ $alternative ])
689
- && self::is_valid_ip( $_SERVER[ $alternative ] )
690
- ) {
691
- $remote_addr = $_SERVER[ $alternative ];
692
- $header_used = $alternative;
693
- break;
694
- }
695
  }
696
- } elseif ( isset($_SERVER['REMOTE_ADDR']) ) {
697
- $remote_addr = $_SERVER['REMOTE_ADDR'];
698
- $header_used = 'REMOTE_ADDR';
699
  }
700
 
701
- if ( $remote_addr == '::1' ) {
702
  $remote_addr = '127.0.0.1';
703
  }
704
 
705
- if ( $return_header ) {
706
  return $header_used;
707
  }
708
 
@@ -714,8 +792,9 @@ class SucuriScan {
714
  *
715
  * @return string The HTTP header used to retrieve the remote address.
716
  */
717
- public static function get_remote_addr_header(){
718
- return self::get_remote_addr( true );
 
719
  }
720
 
721
  /**
@@ -723,9 +802,10 @@ class SucuriScan {
723
  *
724
  * @return string The user-agent from the current request.
725
  */
726
- public static function get_user_agent(){
727
- if ( isset($_SERVER['HTTP_USER_AGENT']) ) {
728
- return self::escape( $_SERVER['HTTP_USER_AGENT'] );
 
729
  }
730
 
731
  return false;
@@ -736,12 +816,13 @@ class SucuriScan {
736
  *
737
  * @return string The domain of the current site.
738
  */
739
- public static function get_domain( $return_tld = false ){
740
- if ( function_exists( 'get_site_url' ) ) {
 
741
  $site_url = get_site_url();
742
  $pattern = '/([fhtps]+:\/\/)?([^:\/]+)(:[0-9:]+)?(\/.*)?/';
743
  $replacement = ( $return_tld === true ) ? '$2' : '$2$3$4';
744
- $domain_name = @preg_replace( $pattern, $replacement, $site_url );
745
 
746
  return $domain_name;
747
  }
@@ -754,8 +835,9 @@ class SucuriScan {
754
  *
755
  * @return string Top-level domain (TLD) of the website.
756
  */
757
- public static function get_top_level_domain(){
758
- return self::get_domain( true );
 
759
  }
760
 
761
  /**
@@ -763,8 +845,9 @@ class SucuriScan {
763
  *
764
  * @return boolean TRUE if reverse proxies must be supported, FALSE otherwise.
765
  */
766
- public static function support_reverse_proxy(){
767
- return SucuriScanOption::is_enabled( ':revproxy' );
 
768
  }
769
 
770
  /**
@@ -778,10 +861,10 @@ class SucuriScan {
778
  *
779
  * @return boolean True if the DNS lookups should be executed, false otherwise.
780
  */
781
- public static function execute_dns_lookups(){
782
- if (
783
- ( defined( 'NOT_USING_CLOUDPROXY' ) && NOT_USING_CLOUDPROXY === true )
784
- || SucuriScanOption::is_disabled( ':dns_lookups' )
785
  ) {
786
  return false;
787
  }
@@ -795,13 +878,14 @@ class SucuriScan {
795
  * @param boolean $verbose Return an array with the hostname, address, and status, or not.
796
  * @return boolean Either TRUE or FALSE if the site is behind CloudProxy.
797
  */
798
- public static function is_behind_cloudproxy( $verbose = false ){
 
799
  $http_host = self::get_top_level_domain();
800
 
801
- if ( self::execute_dns_lookups() ) {
802
- $host_by_addr = @gethostbyname( $http_host );
803
- $host_by_name = @gethostbyaddr( $host_by_addr );
804
- $status = (bool) preg_match( '/^cloudproxy[0-9]+\.sucuri\.net$/', $host_by_name );
805
  } else {
806
  $status = false;
807
  $host_by_addr = '::1';
@@ -813,14 +897,13 @@ class SucuriScan {
813
  * the site as protected by a firewall. A fake key can be used to bypass the DNS
814
  * checking, but that is not something that will affect us, only the client.
815
  */
816
- if (
817
- $status === false
818
- && SucuriScanAPI::get_cloudproxy_key()
819
  ) {
820
  $status = true;
821
  }
822
 
823
- if ( $verbose ) {
824
  return array(
825
  'http_host' => $http_host,
826
  'host_name' => $host_by_name,
@@ -839,10 +922,11 @@ class SucuriScan {
839
  *
840
  * @return string The administrator email address.
841
  */
842
- public static function get_site_email(){
843
- $email = get_option( 'admin_email' );
 
844
 
845
- if ( self::is_valid_email( $email ) ) {
846
  return $email;
847
  }
848
 
@@ -855,11 +939,12 @@ class SucuriScan {
855
  * @param integer $identifier User account identifier.
856
  * @return object WordPress user object with data.
857
  */
858
- public static function get_user_by_id( $identifier = 0 ){
859
- if ( function_exists( 'get_user_by' ) ) {
860
- $user = get_user_by( 'id', $identifier );
 
861
 
862
- if ( $user instanceof WP_User ) {
863
  return $user;
864
  }
865
  }
@@ -872,11 +957,12 @@ class SucuriScan {
872
  *
873
  * @return array List of admin users, false otherwise.
874
  */
875
- public static function get_admin_users(){
876
- if ( function_exists( 'get_users' ) ) {
 
877
  $args = array( 'role' => 'administrator' );
878
 
879
- return get_users( $args );
880
  }
881
 
882
  return false;
@@ -892,13 +978,14 @@ class SucuriScan {
892
  *
893
  * @return array List of user identifiers and email addresses.
894
  */
895
- public static function get_users_for_api_key(){
 
896
  $valid_users = array();
897
  $users = self::get_admin_users();
898
 
899
- if ( $users !== false ) {
900
- foreach ( $users as $user ) {
901
- if ( $user->user_status === '0' ) {
902
  $valid_users[ $user->ID ] = sprintf(
903
  '%s - %s',
904
  $user->user_login,
@@ -916,9 +1003,10 @@ class SucuriScan {
916
  *
917
  * @return integer Return current Unix timestamp.
918
  */
919
- public static function local_time(){
920
- if ( function_exists( 'current_time' ) ) {
921
- return current_time( 'timestamp' );
 
922
  } else {
923
  return time();
924
  }
@@ -934,7 +1022,8 @@ class SucuriScan {
934
  * @param integer $timestamp Unix timestamp.
935
  * @return string The date, translated if locale specifies it.
936
  */
937
- public static function datetime($timestamp = null){
 
938
  $date_format = get_option('date_format');
939
  $time_format = get_option('time_format');
940
  $tz_format = sprintf('%s %s', $date_format, $time_format);
@@ -951,7 +1040,8 @@ class SucuriScan {
951
  *
952
  * @return string The date, translated if locale specifies it.
953
  */
954
- public static function current_datetime(){
 
955
  return self::datetime();
956
  }
957
 
@@ -961,15 +1051,16 @@ class SucuriScan {
961
  * @param integer $timestamp The Unix time number of the date/time before now.
962
  * @return string The time passed since the timestamp specified.
963
  */
964
- public static function time_ago( $timestamp = 0 ){
965
- if ( ! is_numeric( $timestamp ) ) {
966
- $timestamp = strtotime( $timestamp );
 
967
  }
968
 
969
  $local_time = self::local_time();
970
- $diff = abs( $local_time - intval( $timestamp ) );
971
 
972
- if ( $diff == 0 ) {
973
  return 'just now';
974
  }
975
 
@@ -983,7 +1074,7 @@ class SucuriScan {
983
  $diff < 60 => array( 'second', 1, ),
984
  );
985
 
986
- $value = floor( $diff / $intervals[1][1] );
987
  $time_ago = sprintf(
988
  '%s %s%s ago',
989
  $value,
@@ -997,15 +1088,16 @@ class SucuriScan {
997
  /**
998
  * Convert an string of characters into a valid variable name.
999
  *
1000
- * @see http://www.php.net/manual/en/language.variables.basics.php
1001
  *
1002
  * @param string $text A text containing alpha-numeric and special characters.
1003
  * @return string A valid variable name.
1004
  */
1005
- public static function human2var( $text = '' ){
1006
- $text = strtolower( $text );
 
1007
  $pattern = '/[^a-z0-9_]/';
1008
- $var_name = preg_replace( $pattern, '_', $text );
1009
 
1010
  return $var_name;
1011
  }
@@ -1016,8 +1108,9 @@ class SucuriScan {
1016
  * @param string $data The data that will be checked.
1017
  * @return boolean TRUE if the data was serialized, FALSE otherwise.
1018
  */
1019
- public static function is_serialized( $data = '' ){
1020
- return ( is_string( $data ) && preg_match( '/^(a|O):[0-9]+:.+/', $data ) );
 
1021
  }
1022
 
1023
  /**
@@ -1026,15 +1119,16 @@ class SucuriScan {
1026
  * @param string $remote_addr The host IP address.
1027
  * @return boolean Whether the IP address specified is valid or not.
1028
  */
1029
- public static function is_valid_ip( $remote_addr = '' ){
1030
- if ( function_exists( 'filter_var' ) ) {
1031
- return (bool) filter_var( $remote_addr, FILTER_VALIDATE_IP );
1032
- } elseif ( strlen( $remote_addr ) >= 7 ) {
 
1033
  $pattern = '/^([0-9]{1,3}\.) {3}[0-9]{1,3}$/';
1034
 
1035
- if ( preg_match( $pattern, $remote_addr, $match ) ) {
1036
- for ( $i = 0; $i < 4; $i++ ) {
1037
- if ( $match[ $i ] > 255 ) {
1038
  return false;
1039
  }
1040
  }
@@ -1053,9 +1147,10 @@ class SucuriScan {
1053
  * @param string $remote_addr The supposed ip address that will be checked.
1054
  * @return boolean Either TRUE or FALSE if the ip address specified is valid or not.
1055
  */
1056
- public static function is_valid_cidr( $remote_addr = '' ){
1057
- if ( preg_match( '/^([0-9\.]{7,15})\/(8|16|24)$/', $remote_addr, $match ) ) {
1058
- if ( self::is_valid_ip( $match[1] ) ) {
 
1059
  return true;
1060
  }
1061
  }
@@ -1069,13 +1164,13 @@ class SucuriScan {
1069
  * @param string $remote_addr The supposed ip address that will be formatted.
1070
  * @return array Clean address, CIDR range, and CIDR format; FALSE otherwise.
1071
  */
1072
- public static function get_ip_info( $remote_addr = '' ){
1073
- if ( $remote_addr ) {
1074
- $ip_parts = explode( '/', $remote_addr );
 
1075
 
1076
- if (
1077
- array_key_exists( 0, $ip_parts )
1078
- && self::is_valid_ip( $ip_parts[0] )
1079
  ) {
1080
  $addr_info = array();
1081
  $addr_info['remote_addr'] = $ip_parts[0];
@@ -1096,17 +1191,18 @@ class SucuriScan {
1096
  * 5.2.0 if it is not found in the interpreter this function will sue regular
1097
  * expressions to check whether the email address passed is valid or not.
1098
  *
1099
- * @see http://www.php.net/manual/en/function.filter-var.php
1100
  *
1101
  * @param string $email The string that will be validated as an email address.
1102
  * @return boolean TRUE if the email address passed to the function is valid, FALSE if not.
1103
  */
1104
- public static function is_valid_email( $email = '' ){
1105
- if ( function_exists( 'filter_var' ) ) {
1106
- return (bool) filter_var( $email, FILTER_VALIDATE_EMAIL );
 
1107
  } else {
1108
  $pattern = '/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix';
1109
- return (bool) preg_match( $pattern, $email );
1110
  }
1111
  }
1112
 
@@ -1116,58 +1212,15 @@ class SucuriScan {
1116
  * @param string $pattern The regular expression to check.
1117
  * @return boolean True if the regular expression is valid, false otherwise.
1118
  */
1119
- public static function is_valid_pattern( $pattern = '' ){
 
1120
  return (bool) (
1121
- is_string( $pattern )
1122
- && ! empty( $pattern )
1123
- && @preg_match( $pattern, null ) !== false
1124
  );
1125
  }
1126
 
1127
- /**
1128
- * Return a string with all the valid email addresses.
1129
- *
1130
- * @param string $email The string that will be validated as an email address.
1131
- * @param boolean $as_array TRUE to return the list of valid email addresses as an array.
1132
- * @return string All the valid email addresses separated by a comma.
1133
- */
1134
- public static function get_valid_email( $email = '', $as_array = false ){
1135
- $valid_emails = array();
1136
- $is_valid_string = (bool) ( is_string( $email ) && ! empty($email) );
1137
-
1138
- if (
1139
- $is_valid_string === true
1140
- && strpos( $email, ',' ) !== false
1141
- ) {
1142
- $addresses = explode( ',', $email );
1143
-
1144
- foreach ( $addresses as $address ) {
1145
- $address = trim( $address );
1146
-
1147
- if ( self::is_valid_email( $address ) ) {
1148
- $valid_emails[] = $address;
1149
- }
1150
- }
1151
- } elseif (
1152
- $is_valid_string === true
1153
- && self::is_valid_email( $email )
1154
- ) {
1155
- $valid_emails[] = $email;
1156
- }
1157
-
1158
- if ( ! empty($valid_emails) ) {
1159
- $valid_emails = array_unique( $valid_emails );
1160
-
1161
- if ( $as_array === true ) {
1162
- return $valid_emails;
1163
- }
1164
-
1165
- return self::implode( ', ', $valid_emails );
1166
- }
1167
-
1168
- return false;
1169
- }
1170
-
1171
  /**
1172
  * Cut a long text to the length specified, and append suspensive points at the end.
1173
  *
@@ -1175,11 +1228,12 @@ class SucuriScan {
1175
  * @param integer $length Maximum length of the returned string, default is 10.
1176
  * @return string Short version of the text specified.
1177
  */
1178
- public static function excerpt( $text = '', $length = 10 ){
1179
- $text_length = strlen( $text );
 
1180
 
1181
- if ( $text_length > $length ) {
1182
- return substr( $text, 0, $length ) . '...';
1183
  }
1184
 
1185
  return $text;
@@ -1192,10 +1246,11 @@ class SucuriScan {
1192
  * @param integer $length Maximum length of the returned string, default is 10.
1193
  * @return string Short version of the text specified.
1194
  */
1195
- public static function excerpt_rev( $text = '', $length = 10 ){
1196
- $str_reversed = strrev( $text );
1197
- $str_excerpt = self::excerpt( $str_reversed, $length );
1198
- $text_transformed = strrev( $str_excerpt );
 
1199
 
1200
  return $text_transformed;
1201
  }
@@ -1206,10 +1261,11 @@ class SucuriScan {
1206
  * @param array $list An array or multidimensional array of different values.
1207
  * @return boolean TRUE if the list is multidimensional, FALSE otherwise.
1208
  */
1209
- public static function is_multi_list( $list = array() ){
1210
- if ( ! empty($list) ) {
1211
- foreach ( (array) $list as $item ) {
1212
- if ( is_array( $item ) ) {
 
1213
  return true;
1214
  }
1215
  }
@@ -1225,19 +1281,20 @@ class SucuriScan {
1225
  * @param array $list The array of strings to implode.
1226
  * @return string String of all the items in the list, with the separator between them.
1227
  */
1228
- public static function implode( $separator = '', $list = array() ){
1229
- if ( self::is_multi_list( $list ) ) {
 
1230
  $pieces = array();
1231
 
1232
- foreach ( $list as $items ) {
1233
- $pieces[] = @implode( $separator, $items );
1234
  }
1235
 
1236
- $joined_pieces = '(' . implode( '), (', $pieces ) . ')';
1237
 
1238
  return $joined_pieces;
1239
  } else {
1240
- return implode( $separator, $list );
1241
  }
1242
  }
1243
 
@@ -1247,19 +1304,19 @@ class SucuriScan {
1247
  * @param string $current_page Identifier of the current page.
1248
  * @return boolean TRUE if the current page must not have noticies.
1249
  */
1250
- public static function no_notices_here( $current_page = false ){
 
1251
  global $sucuriscan_no_notices_in;
1252
 
1253
- if ( $current_page === false ) {
1254
- $current_page = SucuriScanRequest::get( 'page' );
1255
  }
1256
 
1257
- if (
1258
- isset($sucuriscan_no_notices_in)
1259
- && is_array( $sucuriscan_no_notices_in )
1260
- && ! empty($sucuriscan_no_notices_in)
1261
  ) {
1262
- return (bool) in_array( $current_page, $sucuriscan_no_notices_in );
1263
  }
1264
 
1265
  return false;
@@ -1270,8 +1327,9 @@ class SucuriScan {
1270
  *
1271
  * @return boolean TRUE if the site is running over Nginx, FALSE otherwise.
1272
  */
1273
- public static function is_nginx_server(){
1274
- return (bool) preg_match( '/^nginx(\/[0-9\.]+)?$/', @$_SERVER['SERVER_SOFTWARE'] );
 
1275
  }
1276
 
1277
  /**
@@ -1279,10 +1337,10 @@ class SucuriScan {
1279
  *
1280
  * @return boolean TRUE if the site is running over Nginx, FALSE otherwise.
1281
  */
1282
- public static function is_iis_server(){
1283
- return (bool) preg_match( '/Microsoft-IIS/i', @$_SERVER['SERVER_SOFTWARE'] );
 
1284
  }
1285
-
1286
  }
1287
 
1288
  /**
@@ -1293,7 +1351,8 @@ class SucuriScan {
1293
  * these methods at most instead of accessing an index in the global PHP
1294
  * variables _POST, _GET, _REQUEST since they may come with insecure data.
1295
  */
1296
- class SucuriScanRequest extends SucuriScan {
 
1297
 
1298
  /**
1299
  * Returns the value stored in a specific index in the global _GET, _POST or
@@ -1305,38 +1364,48 @@ class SucuriScanRequest extends SucuriScan {
1305
  * @param string $pattern Optional pattern to match allowed values in the requested key.
1306
  * @return string The value stored in the specified key inside the global _GET variable.
1307
  */
1308
- public static function request( $list = array(), $key = '', $pattern = '' ){
1309
- $key = self::variable_prefix( $key );
 
1310
 
1311
- if (
1312
- is_array( $list )
1313
- && is_string( $key )
1314
  && isset($list[ $key ])
1315
  ) {
1316
  // Select the key from the list and escape its content.
1317
  $key_value = $list[ $key ];
1318
 
1319
  // Define regular expressions for specific value types.
1320
- if ( $pattern === '' ) {
1321
  $pattern = '/.*/';
1322
  } else {
1323
- switch ( $pattern ) {
1324
- case '_nonce': $pattern = '/^[a-z0-9]{10}$/'; break;
1325
- case '_page': $pattern = '/^[a-z_]+$/'; break;
1326
- case '_array': $pattern = '_array'; break;
1327
- case '_yyyymmdd': $pattern = '/^[0-9]{4}(\-[0-9]{2}) {2}$/'; break;
1328
- default: $pattern = '/^'.$pattern.'$/'; break;
 
 
 
 
 
 
 
 
 
 
1329
  }
1330
  }
1331
 
1332
  // If the request data is an array, then only cast the value.
1333
- if ( $pattern == '_array' && is_array( $key_value ) ) {
1334
  return (array) $key_value;
1335
  }
1336
 
1337
  // Check the format of the request data with a regex defined above.
1338
- if ( @preg_match( $pattern, $key_value ) ) {
1339
- return self::escape( $key_value );
1340
  }
1341
  }
1342
 
@@ -1351,8 +1420,9 @@ class SucuriScanRequest extends SucuriScan {
1351
  * @param string $pattern Optional pattern to match allowed values in the requested key.
1352
  * @return string The value stored in the specified key inside the global _GET variable.
1353
  */
1354
- public static function get( $key = '', $pattern = '' ){
1355
- return self::request( $_GET, $key, $pattern );
 
1356
  }
1357
 
1358
  /**
@@ -1363,8 +1433,9 @@ class SucuriScanRequest extends SucuriScan {
1363
  * @param string $pattern Optional pattern to match allowed values in the requested key.
1364
  * @return string The value stored in the specified key inside the global _POST variable.
1365
  */
1366
- public static function post( $key = '', $pattern = '' ){
1367
- return self::request( $_POST, $key, $pattern );
 
1368
  }
1369
 
1370
  /**
@@ -1375,10 +1446,10 @@ class SucuriScanRequest extends SucuriScan {
1375
  * @param string $pattern Optional pattern to match allowed values in the requested key.
1376
  * @return string The value stored in the specified key inside the global _POST variable.
1377
  */
1378
- public static function get_or_post( $key = '', $pattern = '' ){
1379
- return self::request( $_REQUEST, $key, $pattern );
 
1380
  }
1381
-
1382
  }
1383
 
1384
  /**
@@ -1389,7 +1460,8 @@ class SucuriScanRequest extends SucuriScan {
1389
  * offers a high-level object oriented interface to information for an individual
1390
  * file.
1391
  */
1392
- class SucuriScanFileInfo extends SucuriScan {
 
1393
 
1394
  /**
1395
  * Define the interface that will be used to execute the file system scans, the
@@ -1451,7 +1523,8 @@ class SucuriScanFileInfo extends SucuriScan {
1451
  /**
1452
  * Class constructor.
1453
  */
1454
- public function __construct(){
 
1455
  }
1456
 
1457
  /**
@@ -1464,38 +1537,39 @@ class SucuriScanFileInfo extends SucuriScan {
1464
  * @param boolean $as_array Whether the result of the operation will be returned as an array or string.
1465
  * @return array List of files in the main and subdirectories of the folder specified.
1466
  */
1467
- public function get_directory_tree_md5( $directory = '', $as_array = false ){
 
1468
  $project_signatures = '';
1469
- $abs_path = rtrim( ABSPATH, DIRECTORY_SEPARATOR );
1470
- $files = $this->get_directory_tree( $directory );
1471
 
1472
- if ( $as_array ) {
1473
  $project_signatures = array();
1474
  }
1475
 
1476
- if ( $files ) {
1477
- sort( $files );
1478
 
1479
- foreach ( $files as $filepath ) {
1480
- $file_checksum = @md5_file( $filepath );
1481
- $filesize = @filesize( $filepath );
1482
 
1483
- if ( $as_array ) {
1484
- $basename = str_replace( $abs_path . DIRECTORY_SEPARATOR, '', $filepath );
1485
  $project_signatures[ $basename ] = array(
1486
  'filepath' => $filepath,
1487
  'checksum' => $file_checksum,
1488
  'filesize' => $filesize,
1489
- 'created_at' => @filectime( $filepath ),
1490
- 'modified_at' => @filemtime( $filepath ),
1491
  );
1492
  } else {
1493
- $filepath = str_replace( $abs_path, $abs_path . DIRECTORY_SEPARATOR, $filepath );
1494
  $project_signatures .= sprintf(
1495
  "%s%s%s%s\n",
1496
  $file_checksum,
1497
  $filesize,
1498
- chr( 32 ),
1499
  $filepath
1500
  );
1501
  }
@@ -1513,37 +1587,38 @@ class SucuriScanFileInfo extends SucuriScan {
1513
  * @param string $directory Parent directory where the filesystem scan will start.
1514
  * @return array List of files in the main and subdirectories of the folder specified.
1515
  */
1516
- public function get_directory_tree( $directory = '' ){
1517
- if ( file_exists( $directory ) && is_dir( $directory ) ) {
 
1518
  $tree = array();
1519
 
1520
  // Check whether the ignore scanning feature is enabled or not.
1521
- if ( SucuriScanFSScanner::will_ignore_scanning() ) {
1522
  $this->ignored_directories = SucuriScanFSScanner::get_ignored_directories();
1523
  }
1524
 
1525
- switch ( $this->scan_interface ) {
1526
  case 'spl':
1527
- if ( $this->is_spl_available() ) {
1528
- $tree = $this->get_directory_tree_with_spl( $directory );
1529
  } else {
1530
  $this->scan_interface = 'opendir';
1531
- SucuriScanOption::update_option( ':scan_interface', $this->scan_interface );
1532
- $tree = $this->get_directory_tree( $directory );
1533
  }
1534
  break;
1535
 
1536
  case 'glob':
1537
- $tree = $this->get_directory_tree_with_glob( $directory );
1538
  break;
1539
 
1540
  case 'opendir':
1541
- $tree = $this->get_directory_tree_with_opendir( $directory );
1542
  break;
1543
 
1544
  default:
1545
  $this->scan_interface = 'spl';
1546
- $tree = $this->get_directory_tree( $directory );
1547
  break;
1548
  }
1549
 
@@ -1560,21 +1635,21 @@ class SucuriScanFileInfo extends SucuriScan {
1560
  * @param string $directory Directory where the scanner is located at the moment.
1561
  * @return array List of file paths where the file was found.
1562
  */
1563
- public function find_file( $filename = '', $directory = null ){
 
1564
  $file_paths = array();
1565
 
1566
- if (
1567
- is_null( $directory )
1568
- && defined( 'ABSPATH' )
1569
  ) {
1570
  $directory = ABSPATH;
1571
  }
1572
 
1573
- if ( is_dir( $directory ) ) {
1574
- $dir_tree = $this->get_directory_tree( $directory );
1575
 
1576
- foreach ( $dir_tree as $filepath ) {
1577
- if ( stripos( $filepath, $filename ) !== false ) {
1578
  $file_paths[] = $filepath;
1579
  }
1580
  }
@@ -1588,12 +1663,13 @@ class SucuriScanFileInfo extends SucuriScan {
1588
  * or not, it is required to have PHP >= 5.1.0. The SplFileObject class offers
1589
  * an object oriented interface for a file.
1590
  *
1591
- * @link http://www.php.net/manual/en/class.splfileobject.php
1592
  *
1593
  * @return boolean Whether the PHP class "SplFileObject" is available or not.
1594
  */
1595
- public static function is_spl_available(){
1596
- return (bool) class_exists( 'SplFileObject' );
 
1597
  }
1598
 
1599
  /**
@@ -1601,7 +1677,7 @@ class SucuriScanFileInfo extends SucuriScan {
1601
  * of the folder specified. Some folders and files will be ignored depending
1602
  * on some rules defined by the developer.
1603
  *
1604
- * @link http://www.php.net/manual/en/class.recursivedirectoryiterator.php
1605
  * @see RecursiveDirectoryIterator extends FilesystemIterator
1606
  * @see FilesystemIterator extends DirectoryIterator
1607
  * @see DirectoryIterator extends SplFileInfo
@@ -1610,47 +1686,47 @@ class SucuriScanFileInfo extends SucuriScan {
1610
  * @param string $directory Parent directory where the filesystem scan will start.
1611
  * @return array List of files in the main and subdirectories of the folder specified.
1612
  */
1613
- private function get_directory_tree_with_spl( $directory = '' ){
 
1614
  $files = array();
1615
- $filepath = @realpath( $directory );
1616
  $objects = array();
1617
 
1618
  // Exception for directory name must not be empty.
1619
- if ( $filepath === false ) {
1620
  return $files;
1621
  }
1622
 
1623
- if ( ! class_exists( 'FilesystemIterator' ) ) {
1624
  $this->scan_interface = 'opendir';
1625
- SucuriScanOption::update_option( ':scan_interface', $this->scan_interface );
1626
- $alternative_tree = $this->get_directory_tree( $directory );
1627
 
1628
  return $alternative_tree;
1629
  }
1630
 
1631
  try {
1632
- if ( $this->run_recursively ) {
1633
  $flags = FilesystemIterator::KEY_AS_PATHNAME
1634
  | FilesystemIterator::CURRENT_AS_FILEINFO
1635
  | FilesystemIterator::SKIP_DOTS
1636
  | FilesystemIterator::UNIX_PATHS;
1637
  $objects = new RecursiveIteratorIterator(
1638
- new RecursiveDirectoryIterator( $filepath, $flags ),
1639
  RecursiveIteratorIterator::SELF_FIRST,
1640
  RecursiveIteratorIterator::CATCH_GET_CHILD
1641
  );
1642
  } else {
1643
- $objects = new DirectoryIterator( $filepath );
1644
  }
1645
- } catch ( RuntimeException $exception ) {
1646
- SucuriScanEvent::report_exception( $exception );
1647
  }
1648
 
1649
- foreach ( $objects as $filepath => $fileinfo ) {
1650
  $filename = $fileinfo->getFilename();
1651
 
1652
- if (
1653
- $this->ignore_folderpath( null, $filename )
1654
  || (
1655
  $this->skip_directories === true
1656
  && $fileinfo->isDir()
@@ -1659,16 +1735,15 @@ class SucuriScanFileInfo extends SucuriScan {
1659
  continue;
1660
  }
1661
 
1662
- if ( $this->run_recursively ) {
1663
- $directory = dirname( $filepath );
1664
  } else {
1665
  $directory = $fileinfo->getPath();
1666
  $filepath = $directory . '/' . $filename;
1667
  }
1668
 
1669
- if (
1670
- $this->ignore_folderpath( $directory, $filename )
1671
- || $this->ignore_filepath( $filename )
1672
  ) {
1673
  continue;
1674
  }
@@ -1687,31 +1762,32 @@ class SucuriScanFileInfo extends SucuriScan {
1687
  * @param string $directory Parent directory where the filesystem scan will start.
1688
  * @return array List of files in the main and subdirectories of the folder specified.
1689
  */
1690
- private function get_directory_tree_with_glob( $directory = '' ){
 
1691
  $files = array();
1692
- $directory_pattern = sprintf( '%s/*', rtrim( $directory, '/' ) );
1693
- $files_found = @glob( $directory_pattern );
1694
-
1695
- if ( is_array( $files_found ) ) {
1696
- foreach ( $files_found as $filepath ) {
1697
- $filepath = @realpath( $filepath );
1698
- $directory = dirname( $filepath );
1699
- $filepath_parts = explode( '/', $filepath );
1700
- $filename = array_pop( $filepath_parts );
1701
-
1702
- if ( is_dir( $filepath ) ) {
1703
- if ( $this->ignore_folderpath( $directory, $filename ) ) {
1704
  continue;
1705
  }
1706
 
1707
- if ( $this->run_recursively ) {
1708
- $sub_files = $this->get_directory_tree_with_glob( $filepath );
1709
 
1710
- if ( $sub_files ) {
1711
- $files = array_merge( $files, $sub_files );
1712
  }
1713
  }
1714
- } elseif ( $this->ignore_filepath( $filename ) ) {
1715
  continue;
1716
  } else {
1717
  $files[] = $filepath;
@@ -1730,40 +1806,41 @@ class SucuriScanFileInfo extends SucuriScan {
1730
  * @param string $directory Parent directory where the filesystem scan will start.
1731
  * @return array List of files in the main and subdirectories of the folder specified.
1732
  */
1733
- private function get_directory_tree_with_opendir( $directory = '' ){
 
1734
  $files = array();
1735
- $dh = @opendir( $directory );
1736
 
1737
- if ( ! $dh ) {
1738
  return false;
1739
  }
1740
 
1741
- while ( ($filename = readdir( $dh )) !== false ) {
1742
- $filepath = @realpath( $directory . '/' . $filename );
1743
 
1744
- if ( $filepath === false ) {
1745
  continue;
1746
- } elseif ( is_dir( $filepath ) ) {
1747
- if ( $this->ignore_folderpath( $directory, $filename ) ) {
1748
  continue;
1749
  }
1750
 
1751
- if ( $this->run_recursively ) {
1752
- $sub_files = $this->get_directory_tree_with_opendir( $filepath );
1753
 
1754
- if ( $sub_files ) {
1755
- $files = array_merge( $files, $sub_files );
1756
  }
1757
  }
1758
  } else {
1759
- if ( $this->ignore_filepath( $filename ) ) {
1760
  continue;
1761
  }
1762
  $files[] = $filepath;
1763
  }
1764
  }
1765
 
1766
- closedir( $dh );
1767
  return $files;
1768
  }
1769
 
@@ -1774,27 +1851,27 @@ class SucuriScanFileInfo extends SucuriScan {
1774
  * @param string $filename Name of the folder or file being scanned at the moment.
1775
  * @return boolean Either TRUE or FALSE representing that the scan should ignore this folder or not.
1776
  */
1777
- private function ignore_folderpath( $directory = '', $filename = '' ){
 
1778
  // Ignoring current and parent folders.
1779
- if ( $filename == '.' || $filename == '..' ) {
1780
  return true;
1781
  }
1782
 
1783
- if ( $this->ignore_directories ) {
1784
  // Ignore directories based on a common regular expression.
1785
- $filepath = @realpath( $directory . '/' . $filename );
1786
  $pattern = '/\/wp-content\/(uploads|cache|backup|w3tc)/';
1787
 
1788
- if ( preg_match( $pattern, $filepath ) ) {
1789
  return true;
1790
  }
1791
 
1792
  // Ignore directories specified by the administrator.
1793
- if ( ! empty($this->ignored_directories) ) {
1794
- foreach ( $this->ignored_directories['directories'] as $ignored_dir ) {
1795
- if (
1796
- strpos( $directory, $ignored_dir ) !== false
1797
- || strpos( $filepath, $ignored_dir ) !== false
1798
  ) {
1799
  return true;
1800
  }
@@ -1811,32 +1888,32 @@ class SucuriScanFileInfo extends SucuriScan {
1811
  * @param string $filename Name of the folder or file being scanned at the moment.
1812
  * @return boolean Either TRUE or FALSE representing that the scan should ignore this filename or not.
1813
  */
1814
- private function ignore_filepath( $filename = '' ){
1815
- if ( ! $this->ignore_files ) {
 
1816
  return false;
1817
  }
1818
 
1819
  // Ignoring backup files from our clean ups.
1820
- if ( strpos( $filename, '_sucuribackup.' ) !== false ) {
1821
  return true;
1822
  }
1823
 
1824
  // Ignore files specified by the administrator.
1825
- if ( ! empty($this->ignored_directories) ) {
1826
- foreach ( $this->ignored_directories['directories'] as $ignored_dir ) {
1827
- if ( strpos( $ignored_dir, $filename ) !== false ) {
1828
  return true;
1829
  }
1830
  }
1831
  }
1832
 
1833
  // Any file maching one of these rules WILL NOT be ignored.
1834
- if (
1835
- ( strpos( $filename, '.php' ) !== false) ||
1836
- ( strpos( $filename, '.htm' ) !== false) ||
1837
- ( strpos( $filename, '.js' ) !== false) ||
1838
- ( strcmp( $filename, '.htaccess' ) == 0 ) ||
1839
- ( strcmp( $filename, 'php.ini' ) == 0 )
1840
  ) {
1841
  return false;
1842
  }
@@ -1850,20 +1927,23 @@ class SucuriScanFileInfo extends SucuriScan {
1850
  * @param array $dir_tree A list of files under a directory.
1851
  * @return array A list of unique directory paths.
1852
  */
1853
- public function get_diretories_only( $dir_tree = array() ){
 
1854
  $dirs = array();
1855
 
1856
- if ( is_string( $dir_tree ) ) {
1857
- $dir_tree = $this->get_directory_tree( $dir_tree );
1858
  }
1859
 
1860
- if ( is_array( $dir_tree ) && ! empty($dir_tree) ) {
1861
- foreach ( $dir_tree as $filepath ) {
1862
- $dir_path = dirname( $filepath );
1863
 
1864
- if (
1865
- ! in_array( $dir_path, $dirs )
1866
- && ! in_array( $dir_path, $this->ignored_directories['directories'] )
 
 
1867
  ) {
1868
  $dirs[] = $dir_path;
1869
  }
@@ -1883,26 +1963,26 @@ class SucuriScanFileInfo extends SucuriScan {
1883
  * @param string $pattern Text that will be searched inside each file.
1884
  * @return array Associative list with the file path and line number of the match.
1885
  */
1886
- public function grep_pattern( $directory = '', $pattern = '' ){
1887
- $dir_tree = $this->get_directory_tree( $directory );
1888
- $pattern = '/.*' . str_replace( '/', '\/', $pattern ) . '.*/';
 
1889
  $results = array();
1890
 
1891
- if (
1892
- class_exists( 'SplFileObject' )
1893
- && class_exists( 'RegexIterator' )
1894
- && SucuriScan::is_valid_pattern( $pattern )
1895
  ) {
1896
- foreach ( $dir_tree as $file_path ) {
1897
  try {
1898
- $fobject = new SplFileObject( $file_path );
1899
- $fstream = new RegexIterator( $fobject, $pattern, RegexIterator::MATCH );
1900
 
1901
- foreach ( $fstream as $key => $ltext ) {
1902
  $lnumber = ( $key + 1 );
1903
- $ltext = str_replace( "\n", '', $ltext );
1904
- $fpath = str_replace( $directory, '', $file_path );
1905
- $loutput = sprintf( '%s:%d:%s', $fpath, $lnumber, $ltext );
1906
  $results[] = array(
1907
  'file_path' => $file_path,
1908
  'relative_path' => $fpath,
@@ -1911,8 +1991,8 @@ class SucuriScanFileInfo extends SucuriScan {
1911
  'output' => $loutput,
1912
  );
1913
  }
1914
- } catch ( RuntimeException $exception ) {
1915
- SucuriScanEvent::report_exception( $exception );
1916
  }
1917
  }
1918
  }
@@ -1926,10 +2006,11 @@ class SucuriScanFileInfo extends SucuriScan {
1926
  * @param string $directory Path of the existing directory that will be removed.
1927
  * @return boolean TRUE if all the files and folder inside the directory were removed.
1928
  */
1929
- public function remove_directory_tree( $directory = '' ){
1930
- $dir_tree = $this->get_directory_tree( $directory );
 
1931
 
1932
- if ( $dir_tree ) {
1933
  $dirs_only = array();
1934
 
1935
  // Include the parent directory as the first entry.
@@ -1941,15 +2022,15 @@ class SucuriScanFileInfo extends SucuriScan {
1941
  * were successfully deleted, this is because PHP does not allows to delete non-
1942
  * empty folders.
1943
  */
1944
- foreach ( $dir_tree as $filepath ) {
1945
- if ( is_dir( $filepath ) ) {
1946
  $dirs_only[] = $filepath;
1947
  } else {
1948
- @unlink( $filepath );
1949
  }
1950
  }
1951
 
1952
- if ( ! function_exists( 'sucuriscan_strlen_diff' ) ) {
1953
  /**
1954
  * Evaluates the difference between the length of two strings.
1955
  *
@@ -1957,18 +2038,19 @@ class SucuriScanFileInfo extends SucuriScan {
1957
  * @param string $b Second string of characters that will be measured.
1958
  * @return integer The difference in length between the two strings.
1959
  */
1960
- function sucuriscan_strlen_diff( $a = '', $b = '' ){
1961
- return strlen( $b ) - strlen( $a );
 
1962
  }
1963
  }
1964
 
1965
  // Sort the directories by deep level in ascendant order.
1966
- $dirs_only = array_unique( $dirs_only );
1967
- usort( $dirs_only, 'sucuriscan_strlen_diff' );
1968
 
1969
  // Delete all the directories starting from the deepest level.
1970
- foreach ( $dirs_only as $dir_path ) {
1971
- @rmdir( $dir_path );
1972
  }
1973
 
1974
  return true;
@@ -1985,8 +2067,9 @@ class SucuriScanFileInfo extends SucuriScan {
1985
  * @param string $filepath Path to the file.
1986
  * @return array An array where each element is a line in the file.
1987
  */
1988
- public static function file_lines( $filepath = '' ){
1989
- return @file( $filepath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
 
1990
  }
1991
 
1992
  /**
@@ -1999,42 +2082,43 @@ class SucuriScanFileInfo extends SucuriScan {
1999
  * @param boolean $adaptive Whether the buffer will adapt to a specific number of bytes or not.
2000
  * @return string Text contained at the end of the file.
2001
  */
2002
- public static function tail_file( $file_path = '', $lines = 1, $adaptive = true ){
2003
- $file = @fopen( $file_path, 'rb' );
 
2004
  $limit = $lines;
2005
 
2006
- if ( $file ) {
2007
- fseek( $file, -1, SEEK_END );
2008
 
2009
- if ( $adaptive && $lines < 2 ) {
2010
  $buffer = 64;
2011
- } elseif ( $adaptive && $lines < 10 ) {
2012
  $buffer = 512;
2013
  } else {
2014
  $buffer = 4096;
2015
  }
2016
 
2017
- if ( fread( $file, 1 ) != "\n" ) {
2018
  $lines -= 1;
2019
  }
2020
 
2021
  $output = '';
2022
  $chunk = '';
2023
 
2024
- while ( ftell( $file ) > 0 && $lines >= 0 ) {
2025
- $seek = min( ftell( $file ), $buffer );
2026
- fseek( $file, -$seek, SEEK_CUR );
2027
- $chunk = fread( $file, $seek );
2028
  $output = $chunk . $output;
2029
- fseek( $file, -mb_strlen( $chunk, '8bit' ), SEEK_CUR );
2030
- $lines -= substr_count( $chunk, "\n" );
2031
  }
2032
 
2033
- fclose( $file );
2034
 
2035
- $lines_arr = explode( "\n", $output );
2036
- $lines_count = count( $lines_arr );
2037
- $result = array_slice( $lines_arr, ($lines_count - $limit) );
2038
 
2039
  return $result;
2040
  }
@@ -2048,10 +2132,11 @@ class SucuriScanFileInfo extends SucuriScan {
2048
  * @param string $file_path Path to the file.
2049
  * @return integer Time the file was last changed.
2050
  */
2051
- public static function creation_time( $file_path = '' ){
2052
- if ( file_exists( $file_path ) ) {
2053
- clearstatcache( $file_path );
2054
- return filectime( $file_path );
 
2055
  }
2056
 
2057
  return 0;
@@ -2063,10 +2148,11 @@ class SucuriScanFileInfo extends SucuriScan {
2063
  * @param string $file_path Path to the file.
2064
  * @return integer Time the file was last modified.
2065
  */
2066
- public static function modification_time( $file_path = '' ){
2067
- if ( file_exists( $file_path ) ) {
2068
- clearstatcache( $file_path );
2069
- return filemtime( $file_path );
 
2070
  }
2071
 
2072
  return 0;
@@ -2078,18 +2164,18 @@ class SucuriScanFileInfo extends SucuriScan {
2078
  * @param string $path Path to the file.
2079
  * @return string Type of resource: dir, link, file.
2080
  */
2081
- public static function get_resource_type( $path = '' ){
2082
- if ( is_dir( $path ) ) {
 
2083
  return 'dir';
2084
- } elseif ( is_link( $path ) ) {
2085
  return 'link';
2086
- } elseif ( is_file( $path ) ) {
2087
  return 'file';
2088
  } else {
2089
  return 'unknown';
2090
  }
2091
  }
2092
-
2093
  }
2094
 
2095
  /**
@@ -2102,11 +2188,11 @@ class SucuriScanFileInfo extends SucuriScan {
2102
  * the request. Cached data will not be stored persistently across page loads
2103
  * unless of the installation of a 3party persistent caching plugin [2].
2104
  *
2105
- * [1] http://codex.wordpress.org/Class_Reference/WP_Object_Cache
2106
- * [2] http://codex.wordpress.org/Class_Reference/WP_Object_Cache#Persistent_Caching
2107
  */
2108
- class SucuriScanCache extends SucuriScan {
2109
-
2110
  /**
2111
  * The unique name (or identifier) of the file with the data.
2112
  *
@@ -2145,9 +2231,10 @@ class SucuriScanCache extends SucuriScan {
2145
  * @param string $datastore Unique name (or identifier) of the file with the data.
2146
  * @return void
2147
  */
2148
- public function __construct( $datastore = '', $auto_create = true ){
 
2149
  $this->datastore = $datastore;
2150
- $this->datastore_path = $this->datastore_file_path( $auto_create );
2151
  $this->usable_datastore = (bool) $this->datastore_path;
2152
  }
2153
 
@@ -2156,7 +2243,8 @@ class SucuriScanCache extends SucuriScan {
2156
  *
2157
  * @return string Default attributes for every datastore file.
2158
  */
2159
- private function datastore_default_info(){
 
2160
  $attrs = array(
2161
  'datastore' => $this->datastore,
2162
  'created_on' => time(),
@@ -2172,21 +2260,21 @@ class SucuriScanCache extends SucuriScan {
2172
  * @param array $finfo Rainbow table with the key names and decoded values.
2173
  * @return string Default content of every datastore file.
2174
  */
2175
- private function datastore_info( $finfo = array() ){
2176
- $attrs = $this->datastore_default_info();
 
2177
  $info_is_available = (bool) isset($finfo['info']);
2178
  $info = "<?php\n";
2179
 
2180
- foreach ( $attrs as $attr_name => $attr_value ) {
2181
- if (
2182
- $info_is_available
2183
  && $attr_name != 'updated_on'
2184
- && isset($finfo['info'][ $attr_name ])
2185
  ) {
2186
- $attr_value = $finfo['info'][ $attr_name ];
2187
  }
2188
 
2189
- $info .= sprintf( "// %s=%s;\n", $attr_name, $attr_value );
2190
  }
2191
 
2192
  $info .= "exit(0);\n";
@@ -2203,30 +2291,29 @@ class SucuriScanCache extends SucuriScan {
2203
  * @param boolean $auto_create Automatically create the file if not exists or not.
2204
  * @return string The full path where the datastore file is located, FALSE otherwise.
2205
  */
2206
- private function datastore_file_path( $auto_create = false ){
2207
- if ( ! is_null( $this->datastore ) ) {
 
2208
  $folder_path = $this->datastore_folder_path();
2209
  $file_path = $folder_path . 'sucuri-' . $this->datastore . '.php';
2210
 
2211
  // Create the datastore parent directory.
2212
- if ( ! file_exists( $folder_path ) ) {
2213
- @mkdir( $folder_path, 0755, true );
2214
  }
2215
 
2216
  // Create the datastore file is it does not exists and the folder is writable.
2217
- if (
2218
- ! file_exists( $file_path )
2219
- && is_writable( $folder_path )
2220
  && $auto_create === true
2221
  ) {
2222
- @file_put_contents( $file_path, $this->datastore_info(), LOCK_EX );
2223
  }
2224
 
2225
  // Continue the operation after an attemp to create the datastore file.
2226
- if (
2227
- file_exists( $file_path )
2228
- && is_writable( $file_path )
2229
- && is_readable( $file_path )
2230
  ) {
2231
  return $file_path;
2232
  }
@@ -2243,16 +2330,13 @@ class SucuriScanCache extends SucuriScan {
2243
  * @param string $action Either "valid", "content", or "header".
2244
  * @return string Cache key pattern.
2245
  */
2246
- private function key_pattern( $action = 'valid' ){
2247
- if ( $action == 'valid' ) {
 
2248
  return '/^([0-9a-zA-Z_]+)$/';
2249
- }
2250
-
2251
- if ( $action == 'content' ) {
2252
  return '/^([0-9a-zA-Z_]+):(.+)/';
2253
- }
2254
-
2255
- if ( $action == 'header' ) {
2256
  return '/^\/\/ ([a-z_]+)=(.*);$/';
2257
  }
2258
 
@@ -2265,8 +2349,9 @@ class SucuriScanCache extends SucuriScan {
2265
  * @param string $key Unique name to identify the data in the datastore file.
2266
  * @return boolean TRUE if the format of the key name is valid, FALSE otherwise.
2267
  */
2268
- private function valid_key_name( $key = '' ){
2269
- return (bool) preg_match( $this->key_pattern( 'valid' ), $key );
 
2270
  }
2271
 
2272
  /**
@@ -2275,21 +2360,20 @@ class SucuriScanCache extends SucuriScan {
2275
  * @param array $finfo Rainbow table with the key names and decoded values.
2276
  * @return boolean TRUE if the operation finished successfully, FALSE otherwise.
2277
  */
2278
- private function save_new_entries( $finfo = array() ){
2279
- $data_string = $this->datastore_info( $finfo );
 
2280
 
2281
- if ( ! empty($finfo) ) {
2282
- foreach ( $finfo['entries'] as $key => $data ) {
2283
- if ( $this->valid_key_name( $key ) ) {
2284
- $data = json_encode( $data );
2285
- $data_string .= sprintf( "%s:%s\n", $key, $data );
2286
  }
2287
  }
2288
  }
2289
 
2290
- $saved = @file_put_contents( $this->datastore_path, $data_string, LOCK_EX );
2291
-
2292
- return (bool) $saved;
2293
  }
2294
 
2295
  /**
@@ -2301,25 +2385,25 @@ class SucuriScanCache extends SucuriScan {
2301
  * @param boolean $assoc When TRUE returned objects will be converted into associative arrays.
2302
  * @return array Rainbow table with the key names and decoded values.
2303
  */
2304
- private function get_datastore_content( $assoc = false ){
 
2305
  $data_object = array(
2306
  'info' => array(),
2307
  'entries' => array(),
2308
  );
2309
 
2310
- if ( $this->usable_datastore ) {
2311
- $data_lines = SucuriScanFileInfo::file_lines( $this->datastore_path );
2312
 
2313
- if ( ! empty($data_lines) ) {
2314
- foreach ( $data_lines as $line ) {
2315
- if ( preg_match( $this->key_pattern( 'header' ), $line, $match ) ) {
2316
  $data_object['info'][ $match[1] ] = $match[2];
2317
- } elseif ( preg_match( $this->key_pattern( 'content' ), $line, $match ) ) {
2318
- if (
2319
- $this->valid_key_name( $match[1] )
2320
- && ! array_key_exists( $match[1], $data_object )
2321
  ) {
2322
- $data_object['entries'][ $match[1] ] = @json_decode( $match[2], $assoc );
2323
  }
2324
  }
2325
  }
@@ -2337,14 +2421,15 @@ class SucuriScanCache extends SucuriScan {
2337
  * functionality of these headers please refer to the function that contains the
2338
  * default attributes and their values [1].
2339
  *
2340
- * [1] SucuriScanCache::datastore_default_info()
2341
  *
2342
  * @return array Default content of every datastore file.
2343
  */
2344
- public function get_datastore_info(){
2345
- $finfo = $this->get_datastore_content();
 
2346
 
2347
- if ( ! empty($finfo['info']) ) {
2348
  return $finfo['info'];
2349
  }
2350
 
@@ -2357,12 +2442,13 @@ class SucuriScanCache extends SucuriScan {
2357
  * @param array $finfo Rainbow table with the key names and decoded values.
2358
  * @return integer Total number of unique entries found in the datastore file.
2359
  */
2360
- public function get_count( $finfo = null ){
2361
- if ( ! is_array( $finfo ) ) {
2362
- $finfo = $this->get_datastore_content();
 
2363
  }
2364
 
2365
- return count( $finfo['entries'] );
2366
  }
2367
 
2368
  /**
@@ -2375,15 +2461,16 @@ class SucuriScanCache extends SucuriScan {
2375
  * @param array $finfo Rainbow table with the key names and decoded values.
2376
  * @return boolean TRUE if the life time of the data has expired, FALSE otherwise.
2377
  */
2378
- public function data_has_expired( $lifetime = 0, $finfo = null ){
2379
- if ( is_null( $finfo ) ) {
2380
- $finfo = $this->get_datastore_content();
 
2381
  }
2382
 
2383
- if ( $lifetime > 0 && ! empty($finfo['info']) ) {
2384
- $diff_time = time() - intval( $finfo['info']['updated_on'] );
2385
 
2386
- if ( $diff_time >= $lifetime ) {
2387
  return true;
2388
  }
2389
  }
@@ -2401,45 +2488,37 @@ class SucuriScanCache extends SucuriScan {
2401
  * @param boolean $assoc When TRUE returned objects will be converted into associative arrays.
2402
  * @return boolean TRUE if the operation finished successfully, FALSE otherwise.
2403
  */
2404
- private function handle_key_data( $key = '', $data = null, $action = '', $lifetime = 0, $assoc = false ){
2405
- if ( preg_match( '/^(add|set|get|get_all|exists|delete)$/', $action ) ) {
2406
- if (
2407
- $this->valid_key_name( $key )
2408
- && $this->usable_datastore
2409
- ) {
2410
- $finfo = $this->get_datastore_content( $assoc );
2411
 
2412
- switch ( $action ) {
2413
- case 'add': /* no_break */
2414
- case 'set':
2415
- $finfo['entries'][ $key ] = $data;
2416
- return $this->save_new_entries( $finfo );
2417
- break;
2418
- case 'get':
2419
- if (
2420
- ! $this->data_has_expired( $lifetime, $finfo )
2421
- && array_key_exists( $key, $finfo['entries'] )
2422
- ) {
2423
- return $finfo['entries'][ $key ];
2424
- }
2425
- break;
2426
- case 'get_all': /* no_break */
2427
- if ( ! $this->data_has_expired( $lifetime, $finfo ) ) {
2428
- return $finfo['entries'];
2429
- }
2430
- case 'exists':
2431
- if (
2432
- ! $this->data_has_expired( $lifetime, $finfo )
2433
- && array_key_exists( $key, $finfo['entries'] )
2434
- ) {
2435
- return true;
2436
- }
2437
- break;
2438
- case 'delete':
2439
- unset($finfo['entries'][ $key ]);
2440
- return $this->save_new_entries( $finfo );
2441
- break;
2442
  }
 
 
 
 
 
 
 
 
 
 
2443
  }
2444
  }
2445
 
@@ -2456,8 +2535,9 @@ class SucuriScanCache extends SucuriScan {
2456
  * @param string $data Mixed data stored in the datastore file following the unique key name.
2457
  * @return boolean TRUE if the data was stored successfully, FALSE otherwise.
2458
  */
2459
- public function add( $key = '', $data = '' ){
2460
- return $this->handle_key_data( $key, $data, 'add' );
 
2461
  }
2462
 
2463
  /**
@@ -2467,8 +2547,9 @@ class SucuriScanCache extends SucuriScan {
2467
  * @param string $data Mixed data stored in the datastore file following the unique key name.
2468
  * @return boolean TRUE if the data was stored successfully, FALSE otherwise.
2469
  */
2470
- public function set( $key = '', $data = '' ){
2471
- return $this->handle_key_data( $key, $data, 'set' );
 
2472
  }
2473
 
2474
  /**
@@ -2479,10 +2560,11 @@ class SucuriScanCache extends SucuriScan {
2479
  * @param boolean $assoc When TRUE returned objects will be converted into associative arrays.
2480
  * @return string Mixed data stored in the datastore file following the unique key name.
2481
  */
2482
- public function get( $key = '', $lifetime = 0, $assoc = false ){
2483
- $assoc = ( $assoc == 'array' ? true : $assoc );
 
2484
 
2485
- return $this->handle_key_data( $key, null, 'get', $lifetime, $assoc );
2486
  }
2487
 
2488
  /**
@@ -2492,10 +2574,11 @@ class SucuriScanCache extends SucuriScan {
2492
  * @param boolean $assoc When TRUE returned objects will be converted into associative arrays.
2493
  * @return string Mixed data stored in the datastore file following the unique key name.
2494
  */
2495
- public function get_all( $lifetime = 0, $assoc = false ){
2496
- $assoc = ( $assoc == 'array' ? true : $assoc );
 
2497
 
2498
- return $this->handle_key_data( 'temp', null, 'get_all', $lifetime, $assoc );
2499
  }
2500
 
2501
  /**
@@ -2504,8 +2587,9 @@ class SucuriScanCache extends SucuriScan {
2504
  * @param string $key Unique name to identify the data in the datastore file.
2505
  * @return boolean TRUE if the key exists in the datastore file, FALSE otherwise.
2506
  */
2507
- public function exists( $key = '' ){
2508
- return $this->handle_key_data( $key, null, 'exists' );
 
2509
  }
2510
 
2511
  /**
@@ -2514,8 +2598,9 @@ class SucuriScanCache extends SucuriScan {
2514
  * @param string $key Unique name to identify the data in the datastore file.
2515
  * @return boolean TRUE if the entries were removed, FALSE otherwise.
2516
  */
2517
- public function delete( $key = '' ){
2518
- return $this->handle_key_data( $key, null, 'delete' );
 
2519
  }
2520
 
2521
  /**
@@ -2523,12 +2608,12 @@ class SucuriScanCache extends SucuriScan {
2523
  *
2524
  * @return boolean Always TRUE unless the datastore file is not writable.
2525
  */
2526
- public function flush(){
2527
- $finfo = $this->get_datastore_content();
 
2528
 
2529
- return $this->save_new_entries( $finfo );
2530
  }
2531
-
2532
  }
2533
 
2534
  /**
@@ -2551,21 +2636,25 @@ class SucuriScanCache extends SucuriScan {
2551
  * apply network-wide and the data is stored in the wp_sitemeta table under the
2552
  * given custom name.
2553
  *
2554
- * @see http://codex.wordpress.org/Option_Reference
2555
- * @see http://codex.wordpress.org/Options_API
2556
  */
2557
- class SucuriScanOption extends SucuriScanRequest {
 
2558
 
2559
  /**
2560
  * Default values for all the plugin's options.
2561
  *
2562
  * @return array Default values for all the plugin's options.
2563
  */
2564
- public static function get_default_option_values(){
 
2565
  $defaults = array(
2566
  'sucuriscan_account' => '',
 
2567
  'sucuriscan_ads_visibility' => 'enabled',
2568
  'sucuriscan_api_key' => false,
 
2569
  'sucuriscan_audit_report' => 'disabled',
2570
  'sucuriscan_cloudproxy_apikey' => '',
2571
  'sucuriscan_collect_wrong_passwords' => 'disabled',
@@ -2612,14 +2701,15 @@ class SucuriScanOption extends SucuriScanRequest {
2612
  'sucuriscan_notify_widget_deleted' => 'disabled',
2613
  'sucuriscan_parse_errorlogs' => 'enabled',
2614
  'sucuriscan_prettify_mails' => 'disabled',
2615
- 'sucuriscan_request_timeout' => 90,
2616
  'sucuriscan_revproxy' => 'disabled',
2617
  'sucuriscan_runtime' => 0,
2618
  'sucuriscan_scan_checksums' => 'enabled',
2619
  'sucuriscan_scan_errorlogs' => 'disabled',
2620
  'sucuriscan_scan_frequency' => 'twicedaily',
2621
  'sucuriscan_scan_interface' => 'spl',
2622
- 'sucuriscan_scan_modfiles' => 'disabled',
 
2623
  'sucuriscan_site_version' => '0.0',
2624
  'sucuriscan_sitecheck_counter' => 0,
2625
  'sucuriscan_sitecheck_scanner' => 'enabled',
@@ -2636,9 +2726,10 @@ class SucuriScanOption extends SucuriScanRequest {
2636
  *
2637
  * @return array Name of all valid plugin's options.
2638
  */
2639
- public static function get_default_option_names(){
 
2640
  $options = self::get_default_option_values();
2641
- $names = array_keys( $options );
2642
 
2643
  return $names;
2644
  }
@@ -2649,9 +2740,10 @@ class SucuriScanOption extends SucuriScanRequest {
2649
  * @param string $option_name Name of the option that will be checked.
2650
  * @return boolean True if the option is part of the plugin, False otherwise.
2651
  */
2652
- public static function is_valid_plugin_option( $option_name = '' ){
 
2653
  $valid_options = self::get_default_option_names();
2654
- $is_valid_option = (bool) array_key_exists( $option_name, $valid_options );
2655
 
2656
  return $is_valid_option;
2657
  }
@@ -2662,19 +2754,20 @@ class SucuriScanOption extends SucuriScanRequest {
2662
  * @param string|array $settings Either an array that will be complemented or a string with the name of the option.
2663
  * @return string|array The default values for the specified options.
2664
  */
2665
- public static function get_default_options( $settings = '' ){
 
2666
  $default_options = self::get_default_option_values();
2667
 
2668
  // Use framework built-in function.
2669
- if ( function_exists( 'get_option' ) ) {
2670
- $admin_email = get_option( 'admin_email' );
2671
  $default_options['sucuriscan_account'] = $admin_email;
2672
  $default_options['sucuriscan_notify_to'] = $admin_email;
2673
  }
2674
 
2675
- if ( is_array( $settings ) ) {
2676
- foreach ( $default_options as $option_name => $option_value ) {
2677
- if ( ! isset($settings[ $option_name ]) ) {
2678
  $settings[ $option_name ] = $option_value;
2679
  }
2680
  }
@@ -2682,10 +2775,9 @@ class SucuriScanOption extends SucuriScanRequest {
2682
  return $settings;
2683
  }
2684
 
2685
- if (
2686
- is_string( $settings )
2687
- && ! empty($settings)
2688
- && array_key_exists( $settings, $default_options )
2689
  ) {
2690
  return $default_options[ $settings ];
2691
  }
@@ -2705,18 +2797,19 @@ class SucuriScanOption extends SucuriScanRequest {
2705
  * automatically replace that character with the unique identifier of the
2706
  * plugin.
2707
  *
2708
- * @see http://codex.wordpress.org/Function_Reference/get_option
2709
  *
2710
  * @param string $option_name Optional parameter that you can use to filter the results to one option.
2711
  * @return string The value (or default value) of the option specified.
2712
  */
2713
- public static function get_option( $option_name = '' ){
2714
- if ( function_exists( 'update_option' ) ) {
2715
- $option_name = self::variable_prefix( $option_name );
2716
- $option_value = get_option( $option_name );
 
2717
 
2718
- if ( $option_value === false && preg_match( '/^sucuriscan_/', $option_name ) ) {
2719
- $option_value = self::get_default_options( $option_name );
2720
  }
2721
 
2722
  return $option_value;
@@ -2733,17 +2826,18 @@ class SucuriScanOption extends SucuriScanRequest {
2733
  * the insert SQL statement but not the option value, this value should always
2734
  * be properly sanitized.
2735
  *
2736
- * @see http://codex.wordpress.org/Function_Reference/update_option
2737
  *
2738
  * @param string $option_name Name of the option to update which must not exceed 64 characters.
2739
  * @param string $option_value The new value for the option, can be an integer, string, array, or object.
2740
  * @return boolean True if option value has changed, false if not or if update failed.
2741
  */
2742
- public static function update_option( $option_name = '', $option_value = '' ){
2743
- if ( function_exists( 'update_option' ) ) {
2744
- $option_name = self::variable_prefix( $option_name );
 
2745
 
2746
- return update_option( $option_name, $option_value );
2747
  }
2748
 
2749
  return false;
@@ -2754,16 +2848,17 @@ class SucuriScanOption extends SucuriScanRequest {
2754
  *
2755
  * A safe way of removing a named option/value pair from the options database table.
2756
  *
2757
- * @see http://codex.wordpress.org/Function_Reference/delete_option
2758
  *
2759
  * @param string $option_name Name of the option to be deleted.
2760
  * @return boolean True, if option is successfully deleted. False on failure, or option does not exist.
2761
  */
2762
- public static function delete_option( $option_name = '' ){
2763
- if ( function_exists( 'delete_option' ) ) {
2764
- $option_name = self::variable_prefix( $option_name );
 
2765
 
2766
- return delete_option( $option_name );
2767
  }
2768
 
2769
  return false;
@@ -2775,8 +2870,9 @@ class SucuriScanOption extends SucuriScanRequest {
2775
  * @param string $option Name of the option to be deleted.
2776
  * @return boolean True if the option is enabled, false otherwise.
2777
  */
2778
- public static function is_enabled( $option = '' ){
2779
- return (bool) ( self::get_option( $option ) === 'enabled' );
 
2780
  }
2781
 
2782
  /**
@@ -2785,8 +2881,9 @@ class SucuriScanOption extends SucuriScanRequest {
2785
  * @param string $option Name of the option to be deleted.
2786
  * @return boolean True if the option is disabled, false otherwise.
2787
  */
2788
- public static function is_disabled( $option = '' ){
2789
- return (bool) ( self::get_option( $option ) === 'disabled' );
 
2790
  }
2791
 
2792
  /**
@@ -2794,7 +2891,8 @@ class SucuriScanOption extends SucuriScanRequest {
2794
  *
2795
  * @return void
2796
  */
2797
- public static function delete_plugin_options(){
 
2798
  global $wpdb;
2799
 
2800
  $options = $wpdb->get_results(
@@ -2803,8 +2901,8 @@ class SucuriScanOption extends SucuriScanRequest {
2803
  ORDER BY option_id ASC"
2804
  );
2805
 
2806
- foreach ( $options as $option ) {
2807
- self::delete_option( $option->option_name );
2808
  }
2809
  }
2810
 
@@ -2815,7 +2913,8 @@ class SucuriScanOption extends SucuriScanRequest {
2815
  *
2816
  * @return array All the options stored by Wordpress in the database, except the transient options.
2817
  */
2818
- public static function get_site_options(){
 
2819
  global $wpdb;
2820
 
2821
  $settings = array();
@@ -2825,7 +2924,7 @@ class SucuriScanOption extends SucuriScanRequest {
2825
  ORDER BY option_id ASC"
2826
  );
2827
 
2828
- foreach ( $results as $row ) {
2829
  $settings[ $row->option_name ] = $row->option_value;
2830
  }
2831
 
@@ -2839,7 +2938,8 @@ class SucuriScanOption extends SucuriScanRequest {
2839
  * @param array $request The content of the global variable GET or POST considering SERVER[REQUEST_METHOD].
2840
  * @return array A list of all the options that were changes through this request.
2841
  */
2842
- public static function what_options_were_changed( $request = array() ){
 
2843
  $options_changed = array(
2844
  'original' => array(),
2845
  'changed' => array()
@@ -2847,9 +2947,8 @@ class SucuriScanOption extends SucuriScanRequest {
2847
 
2848
  $site_options = self::get_site_options();
2849
 
2850
- foreach ( $request as $req_name => $req_value ) {
2851
- if (
2852
- array_key_exists( $req_name, $site_options )
2853
  && $site_options[ $req_name ] != $req_value
2854
  ) {
2855
  $options_changed['original'][ $req_name ] = $site_options[ $req_name ];
@@ -2865,21 +2964,21 @@ class SucuriScanOption extends SucuriScanRequest {
2865
  *
2866
  * @return boolean TRUE if the nonce is valid, FALSE otherwise.
2867
  */
2868
- public static function check_options_nonce(){
 
2869
  // Create the option_page value if permalink submission.
2870
- if (
2871
- ! isset($_POST['option_page'])
2872
  && isset($_POST['permalink_structure'])
2873
  ) {
2874
  $_POST['option_page'] = 'permalink';
2875
  }
2876
 
2877
  // Check if the option_page has an allowed value.
2878
- if ( $option_page = SucuriScanRequest::post( 'option_page' ) ) {
2879
  $nonce = '_wpnonce';
2880
  $action = '';
2881
 
2882
- switch ( $option_page ) {
2883
  case 'general': /* no_break */
2884
  case 'writing': /* no_break */
2885
  case 'reading': /* no_break */
@@ -2894,10 +2993,9 @@ class SucuriScanOption extends SucuriScanRequest {
2894
  }
2895
 
2896
  // Check the nonce validity.
2897
- if (
2898
- ! empty($action)
2899
  && isset($_REQUEST[ $nonce ])
2900
- && wp_verify_nonce( $_REQUEST[ $nonce ], $action )
2901
  ) {
2902
  return true;
2903
  }
@@ -2912,25 +3010,24 @@ class SucuriScanOption extends SucuriScanRequest {
2912
  *
2913
  * @return array List of ignored posts-types to send notifications.
2914
  */
2915
- public static function get_ignored_events(){
2916
- $post_types = self::get_option( ':ignored_events' );
 
2917
  $post_types_arr = false;
2918
 
2919
  // Encode (old) serialized data into JSON.
2920
- if ( self::is_serialized( $post_types ) ) {
2921
- $post_types_arr = @unserialize( $post_types );
2922
- $post_types_fix = json_encode( $post_types_arr );
2923
- self::update_option( ':ignored_events', $post_types_fix );
2924
 
2925
  return $post_types_arr;
 
 
 
2926
  }
2927
 
2928
- // Decode JSON-encoded data as an array.
2929
- elseif ( preg_match( '/^\{.+\}$/', $post_types ) ) {
2930
- $post_types_arr = @json_decode( $post_types, true );
2931
- }
2932
-
2933
- if ( ! is_array( $post_types_arr ) ) {
2934
  $post_types_arr = array();
2935
  }
2936
 
@@ -2943,18 +3040,19 @@ class SucuriScanOption extends SucuriScanRequest {
2943
  * @param string $event_name Unique post-type name.
2944
  * @return boolean Whether the event was ignored or not.
2945
  */
2946
- public static function add_ignored_event( $event_name = '' ){
2947
- if ( function_exists( 'get_post_types' ) ) {
 
2948
  $post_types = get_post_types();
2949
 
2950
  // Check if the event is a registered post-type.
2951
- if ( array_key_exists( $event_name, $post_types ) ) {
2952
  $ignored_events = self::get_ignored_events();
2953
 
2954
  // Check if the event is not ignored already.
2955
- if ( ! array_key_exists( $event_name, $ignored_events ) ) {
2956
  $ignored_events[ $event_name ] = time();
2957
- $saved = self::update_option( ':ignored_events', json_encode( $ignored_events ) );
2958
 
2959
  return $saved;
2960
  }
@@ -2970,12 +3068,13 @@ class SucuriScanOption extends SucuriScanRequest {
2970
  * @param string $event_name Unique post-type name.
2971
  * @return boolean Whether the event was removed from the list or not.
2972
  */
2973
- public static function remove_ignored_event( $event_name = '' ){
 
2974
  $ignored_events = self::get_ignored_events();
2975
 
2976
- if ( array_key_exists( $event_name, $ignored_events ) ) {
2977
- unset( $ignored_events[ $event_name ] );
2978
- $saved = self::update_option( ':ignored_events', json_encode( $ignored_events ) );
2979
 
2980
  return $saved;
2981
  }
@@ -2989,11 +3088,12 @@ class SucuriScanOption extends SucuriScanRequest {
2989
  * @param string $event_name Unique post-type name.
2990
  * @return boolean Whether an event is being ignored or not.
2991
  */
2992
- public static function is_ignored_event( $event_name = '' ){
2993
- $event_name = strtolower( $event_name );
 
2994
  $ignored_events = self::get_ignored_events();
2995
 
2996
- if ( array_key_exists( $event_name, $ignored_events ) ) {
2997
  return true;
2998
  }
2999
 
@@ -3006,7 +3106,8 @@ class SucuriScanOption extends SucuriScanRequest {
3006
  *
3007
  * @return array Array with three keys: good, missing, bad.
3008
  */
3009
- public static function get_security_keys(){
 
3010
  $response = array(
3011
  'good' => array(),
3012
  'missing' => array(),
@@ -3023,11 +3124,11 @@ class SucuriScanOption extends SucuriScanRequest {
3023
  'SECURE_AUTH_SALT',
3024
  );
3025
 
3026
- foreach ( $key_names as $key_name ) {
3027
- if ( defined( $key_name ) ) {
3028
- $key_value = constant( $key_name );
3029
 
3030
- if ( stripos( $key_value, 'unique phrase' ) !== false ) {
3031
  $response['bad'][ $key_name ] = $key_value;
3032
  } else {
3033
  $response['good'][ $key_name ] = $key_value;
@@ -3040,6 +3141,51 @@ class SucuriScanOption extends SucuriScanRequest {
3040
  return $response;
3041
  }
3042
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3043
  }
3044
 
3045
  /**
@@ -3055,23 +3201,25 @@ class SucuriScanOption extends SucuriScanRequest {
3055
  * response to events is said to be event-driven, often with the goal of being
3056
  * interactive.
3057
  *
3058
- * @see http://en.wikipedia.org/wiki/Event_(computing)
3059
  */
3060
- class SucuriScanEvent extends SucuriScan {
 
3061
 
3062
  /**
3063
  * Schedule the task to run the first filesystem scan.
3064
  *
3065
  * @return void
3066
  */
3067
- public static function schedule_task(){
 
3068
  $task_name = 'sucuriscan_scheduled_scan';
3069
 
3070
- if ( ! wp_next_scheduled( $task_name ) ) {
3071
- wp_schedule_event( time() + 10, 'twicedaily', $task_name );
3072
  }
3073
 
3074
- wp_schedule_single_event( time() + 300, $task_name );
3075
  }
3076
 
3077
  /**
@@ -3081,29 +3229,29 @@ class SucuriScanEvent extends SucuriScan {
3081
  * @param boolean $force_scan Whether the filesystem scan was forced by an administrator user or not.
3082
  * @return boolean Either TRUE or FALSE representing the success or fail of the operation respectively.
3083
  */
3084
- private static function verify_run( $runtime = 0, $force_scan = false ){
 
3085
  $option_name = ':runtime';
3086
- $last_run = SucuriScanOption::get_option( $option_name );
3087
  $current_time = time();
3088
 
3089
  // The filesystem scanner can be disabled from the settings page.
3090
- if (
3091
- SucuriScanOption::is_disabled( ':fs_scanner' )
3092
  && $force_scan === false
3093
  ) {
3094
  return false;
3095
  }
3096
 
3097
  // Check if the last runtime is too near the current time.
3098
- if ( $last_run && ! $force_scan ) {
3099
  $runtime_diff = $current_time - $runtime;
3100
 
3101
- if ( $last_run >= $runtime_diff ) {
3102
  return false;
3103
  }
3104
  }
3105
 
3106
- SucuriScanOption::update_option( $option_name, $current_time );
3107
 
3108
  return true;
3109
  }
@@ -3114,14 +3262,15 @@ class SucuriScanEvent extends SucuriScan {
3114
  *
3115
  * @return boolean TRUE if the current WordPress version must be reported, FALSE otherwise.
3116
  */
3117
- private static function report_site_version(){
 
3118
  $option_name = ':site_version';
3119
- $reported_version = SucuriScanOption::get_option( $option_name );
3120
  $wp_version = self::site_version();
3121
 
3122
- if ( $reported_version != $wp_version ) {
3123
- SucuriScanEvent::report_info_event( 'WordPress version detected ' . $wp_version );
3124
- SucuriScanOption::update_option( $option_name, $wp_version );
3125
 
3126
  return true;
3127
  }
@@ -3137,31 +3286,31 @@ class SucuriScanEvent extends SucuriScan {
3137
  * @param boolean $force_scan Whether the filesystem scan was forced by an administrator user or not.
3138
  * @return boolean TRUE if the filesystem scan was successful, FALSE otherwise.
3139
  */
3140
- public static function filesystem_scan( $force_scan = false ){
 
3141
  $minimum_runtime = SUCURISCAN_MINIMUM_RUNTIME;
3142
 
3143
- if (
3144
- self::verify_run( $minimum_runtime, $force_scan )
3145
- && class_exists( 'SucuriScanFileInfo' )
3146
- && SucuriScanAPI::get_plugin_key()
3147
  ) {
3148
  self::report_site_version();
3149
 
3150
  $file_info = new SucuriScanFileInfo();
3151
- $file_info->scan_interface = SucuriScanOption::get_option( ':scan_interface' );
3152
- $signatures = $file_info->get_directory_tree_md5( ABSPATH );
3153
 
3154
- if ( $signatures ) {
3155
- $hashes_sent = SucuriScanAPI::send_hashes( $signatures );
3156
 
3157
- if ( $hashes_sent ) {
3158
- SucuriScanOption::update_option( ':runtime', time() );
3159
  return true;
3160
  } else {
3161
- SucuriScanInterface::error( 'The file hashes could not be stored.' );
3162
  }
3163
  } else {
3164
- SucuriScanInterface::error( 'The file hashes could not be retrieved, the filesystem scan failed.' );
3165
  }
3166
  }
3167
 
@@ -3177,22 +3326,22 @@ class SucuriScanEvent extends SucuriScan {
3177
  * @param boolean $internal Whether the event will be publicly visible or not.
3178
  * @return boolean TRUE if the event was logged in the monitoring service, FALSE otherwise.
3179
  */
3180
- private static function report_event( $severity = 0, $location = '', $message = '', $internal = false ){
 
3181
  $user = wp_get_current_user();
3182
  $username = false;
3183
- $current_time = date( 'Y-m-d H:i:s' );
3184
  $remote_ip = self::get_remote_addr();
3185
 
3186
  // Identify current user in session.
3187
- if (
3188
- $user instanceof WP_User
3189
  && isset($user->user_login)
3190
- && ! empty($user->user_login)
3191
  ) {
3192
- if ( $user->user_login != $user->display_name ) {
3193
- $username = sprintf( "\x20%s (%s),", $user->display_name, $user->user_login );
3194
  } else {
3195
- $username = sprintf( "\x20%s,", $user->user_login );
3196
  }
3197
  }
3198
 
@@ -3200,26 +3349,40 @@ class SucuriScanEvent extends SucuriScan {
3200
  $severity = (int) $severity;
3201
 
3202
  // Convert the severity number into a readable string.
3203
- switch ( $severity ) {
3204
- case 0: $severity_name = 'Debug'; break;
3205
- case 1: $severity_name = 'Notice'; break;
3206
- case 2: $severity_name = 'Info'; break;
3207
- case 3: $severity_name = 'Warning'; break;
3208
- case 4: $severity_name = 'Error'; break;
3209
- case 5: $severity_name = 'Critical'; break;
3210
- default: $severity_name = 'Info'; break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3211
  }
3212
 
3213
  // Mark the event as internal if necessary.
3214
- if ( $internal === true ) {
3215
  $severity_name = '@' . $severity_name;
3216
  }
3217
 
3218
  // Clear event message.
3219
- $message = strip_tags( $message );
3220
- $message = str_replace( "\r", '', $message );
3221
- $message = str_replace( "\n", '', $message );
3222
- $message = str_replace( "\t", '', $message );
3223
 
3224
  $event_message = sprintf(
3225
  '%s:%s %s; %s',
@@ -3229,19 +3392,58 @@ class SucuriScanEvent extends SucuriScan {
3229
  $message
3230
  );
3231
 
3232
- return SucuriScanAPI::send_log( $event_message );
3233
  }
3234
 
3235
- /**
3236
- * Reports a debug event on the website.
3237
- *
3238
- * @param string $message Text witht the explanation of the event or action performed.
3239
- * @param boolean $internal Whether the event will be publicly visible or not.
3240
- * @return boolean Either true or false depending on the success of the operation.
3241
- */
3242
- public static function report_debug_event( $message = '', $internal = false ){
3243
- return self::report_event( 0, 'core', $message, $internal );
3244
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3245
 
3246
  /**
3247
  * Reports a notice event on the website.
@@ -3250,8 +3452,9 @@ class SucuriScanEvent extends SucuriScan {
3250
  * @param boolean $internal Whether the event will be publicly visible or not.
3251
  * @return boolean Either true or false depending on the success of the operation.
3252
  */
3253
- public static function report_notice_event( $message = '', $internal = false ){
3254
- return self::report_event( 1, 'core', $message, $internal );
 
3255
  }
3256
 
3257
  /**
@@ -3261,8 +3464,9 @@ class SucuriScanEvent extends SucuriScan {
3261
  * @param boolean $internal Whether the event will be publicly visible or not.
3262
  * @return boolean Either true or false depending on the success of the operation.
3263
  */
3264
- public static function report_info_event( $message = '', $internal = false ){
3265
- return self::report_event( 2, 'core', $message, $internal );
 
3266
  }
3267
 
3268
  /**
@@ -3272,8 +3476,9 @@ class SucuriScanEvent extends SucuriScan {
3272
  * @param boolean $internal Whether the event will be publicly visible or not.
3273
  * @return boolean Either true or false depending on the success of the operation.
3274
  */
3275
- public static function report_warning_event( $message = '', $internal = false ){
3276
- return self::report_event( 3, 'core', $message, $internal );
 
3277
  }
3278
 
3279
  /**
@@ -3283,8 +3488,9 @@ class SucuriScanEvent extends SucuriScan {
3283
  * @param boolean $internal Whether the event will be publicly visible or not.
3284
  * @return boolean Either true or false depending on the success of the operation.
3285
  */
3286
- public static function report_error_event( $message = '', $internal = false ){
3287
- return self::report_event( 4, 'core', $message, $internal );
 
3288
  }
3289
 
3290
  /**
@@ -3294,8 +3500,9 @@ class SucuriScanEvent extends SucuriScan {
3294
  * @param boolean $internal Whether the event will be publicly visible or not.
3295
  * @return boolean Either true or false depending on the success of the operation.
3296
  */
3297
- public static function report_critical_event( $message = '', $internal = false ){
3298
- return self::report_event( 5, 'core', $message, $internal );
 
3299
  }
3300
 
3301
  /**
@@ -3306,21 +3513,22 @@ class SucuriScanEvent extends SucuriScan {
3306
  * @param boolean $internal Whether the event will be publicly visible or not.
3307
  * @return boolean Either true or false depending on the success of the operation.
3308
  */
3309
- public static function report_auto_event( $message = '', $action = '', $internal = false ){
3310
- $message = strip_tags( $message );
 
3311
 
3312
  // Auto-detect the action performed, either enabled or disabled.
3313
- if ( preg_match( '/( was )?(enabled|disabled)$/', $message, $match ) ) {
3314
  $action = $match[2];
3315
  }
3316
 
3317
  // Report the correct event for the action performed.
3318
- if ( $action == 'enabled' ) {
3319
- return self::report_notice_event( $message, $internal );
3320
- } elseif ( $action == 'disabled' ) {
3321
- return self::report_error_event( $message, $internal );
3322
  } else {
3323
- return self::report_info_event( $message, $internal );
3324
  }
3325
  }
3326
 
@@ -3330,19 +3538,20 @@ class SucuriScanEvent extends SucuriScan {
3330
  * @param Exception $exception A valid exception object of any type.
3331
  * @return boolean Whether the report was filled correctly or not.
3332
  */
3333
- public static function report_exception( $exception = false ){
3334
- if ( $exception ) {
 
3335
  $e_trace = $exception->getTrace();
3336
  $multiple_entries = array();
3337
 
3338
- foreach ( $e_trace as $e_child ) {
3339
- $e_file = array_key_exists( 'file', $e_child )
3340
- ? basename( $e_child['file'] )
3341
  : '[internal function]';
3342
- $e_line = array_key_exists( 'line', $e_child )
3343
- ? basename( $e_child['line'] )
3344
  : '0';
3345
- $e_function = array_key_exists( 'class', $e_child )
3346
  ? $e_child['class'] . $e_child['type'] . $e_child['function']
3347
  : $e_child['function'];
3348
  $multiple_entries[] = sprintf(
@@ -3356,10 +3565,10 @@ class SucuriScanEvent extends SucuriScan {
3356
  $report_message = sprintf(
3357
  '%s: (multiple entries): %s',
3358
  $exception->getMessage(),
3359
- @implode( ',', $multiple_entries )
3360
  );
3361
 
3362
- return self::report_debug_event( $report_message );
3363
  }
3364
 
3365
  return false;
@@ -3373,34 +3582,42 @@ class SucuriScanEvent extends SucuriScan {
3373
  * @param string $content Body of the email that will be sent to the administrator.
3374
  * @return void
3375
  */
3376
- public static function notify_event( $event = '', $content = '' ){
3377
- $notify = SucuriScanOption::get_option( ':notify_' . $event );
3378
- $email = SucuriScanOption::get_option( ':notify_to' );
 
3379
  $email_params = array();
3380
 
3381
- if ( self::is_trusted_ip() ) {
3382
  $notify = 'disabled';
3383
  }
3384
 
3385
- if ( $notify == 'enabled' ) {
3386
- if ( $event == 'post_publication' ) {
3387
  $event = 'post_update';
3388
- } elseif ( $event == 'failed_login' ) {
3389
- $content .= "<br>\n<br>\n<em>Explanation: Someone failed to login to your site. If you";
3390
- $content .= ' are getting too many of these messages, it is likely your site is under a brute';
3391
- $content .= ' force attack. You can disable the notifications for failed logins from here [1].';
3392
- $content .= " More details at Password Guessing Brute Force Attacks [2].</em><br>\n<br>\n";
3393
- $content .= '[1] ' . SucuriScanTemplate::get_url( 'settings' ) . " <br>\n";
3394
- $content .= "[2] http://kb.sucuri.net/definitions/attacks/brute-force/password-guessing <br>\n";
3395
- } elseif ( $event == 'bruteforce_attack' ) {
 
 
 
 
 
 
 
3396
  // Send a notification even if the limit of emails per hour was reached.
3397
  $email_params['Force'] = true;
3398
- } elseif ( $event == 'scan_checksums' ) {
3399
  $event = 'core_integrity_checks';
3400
  $email_params['Force'] = true;
3401
  }
3402
 
3403
- $title = str_replace( '_', chr( 32 ), $event );
3404
  $mail_sent = SucuriScanMail::send_mail(
3405
  $email,
3406
  $title,
@@ -3420,44 +3637,44 @@ class SucuriScanEvent extends SucuriScan {
3420
  * @param string $remote_addr The supposed ip address that will be checked.
3421
  * @return boolean TRUE if the IP address of the user is trusted, FALSE otherwise.
3422
  */
3423
- private static function is_trusted_ip( $remote_addr = '' ){
3424
- $cache = new SucuriScanCache( 'trustip', false );
3425
- $trusted_ips = $cache->get_all();
 
3426
 
3427
- if ( ! $remote_addr ) {
3428
  $remote_addr = SucuriScan::get_remote_addr();
3429
  }
3430
 
3431
- $addr_md5 = md5( $remote_addr );
3432
 
3433
  // Check if the CIDR in range 32 of this IP is trusted.
3434
- if (
3435
- is_array( $trusted_ips )
3436
- && ! empty($trusted_ips)
3437
- && array_key_exists( $addr_md5, $trusted_ips )
3438
  ) {
3439
  return true;
3440
  }
3441
 
3442
- if ( $trusted_ips ) {
3443
- foreach ( $trusted_ips as $cache_key => $ip_info ) {
3444
- $ip_parts = explode( '.', $ip_info->remote_addr );
3445
  $ip_pattern = false;
3446
 
3447
  // Generate the regular expression for a specific CIDR range.
3448
- switch ( $ip_info->cidr_range ) {
3449
  case 24:
3450
- $ip_pattern = sprintf( '/^%d\.%d\.%d\.[0-9]{1,3}$/', $ip_parts[0], $ip_parts[1], $ip_parts[2] );
3451
  break;
3452
  case 16:
3453
- $ip_pattern = sprintf( '/^%d\.%d(\.[0-9]{1,3}) {2}$/', $ip_parts[0], $ip_parts[1] );
3454
  break;
3455
  case 8:
3456
- $ip_pattern = sprintf( '/^%d(\.[0-9]{1,3}) {3}$/', $ip_parts[0] );
3457
  break;
3458
  }
3459
 
3460
- if ( $ip_pattern && preg_match( $ip_pattern, $remote_addr ) ) {
3461
  return true;
3462
  }
3463
  }
@@ -3472,29 +3689,30 @@ class SucuriScanEvent extends SucuriScan {
3472
  * @param integer $user_id The user identifier that will be changed, this must be different than the user in session.
3473
  * @return boolean Either TRUE or FALSE in case of success or error respectively.
3474
  */
3475
- public static function set_new_password( $user_id = 0 ){
3476
- $user_id = intval( $user_id );
 
3477
 
3478
- if ( $user_id > 0 && function_exists( 'wp_generate_password' ) ) {
3479
- $user = get_userdata( $user_id );
3480
 
3481
- if ( $user instanceof WP_User ) {
3482
  $website = SucuriScan::get_domain();
3483
  $user_login = $user->user_login;
3484
  $display_name = $user->display_name;
3485
- $new_password = wp_generate_password( 15, true, false );
3486
 
3487
- $message = SucuriScanTemplate::get_section( 'notification-resetpwd', array(
3488
  'ResetPassword.UserName' => $user_login,
3489
  'ResetPassword.DisplayName' => $display_name,
3490
  'ResetPassword.Password' => $new_password,
3491
  'ResetPassword.Website' => $website,
3492
- ) );
3493
 
3494
  $data_set = array( 'Force' => true ); // Skip limit for emails per hour.
3495
- SucuriScanMail::send_mail( $user->user_email, 'Password changed', $message, $data_set );
3496
 
3497
- wp_set_password( $new_password, $user_id );
3498
 
3499
  return true;
3500
  }
@@ -3512,28 +3730,29 @@ class SucuriScanEvent extends SucuriScan {
3512
  *
3513
  * @return false|array Either FALSE in case of error, or an array with the old and new keys.
3514
  */
3515
- public static function set_new_config_keys(){
 
3516
  $new_wpconfig = '';
3517
  $config_path = self::get_wpconfig_path();
3518
 
3519
- if ( $config_path ) {
3520
  $pattern = self::secret_key_pattern();
3521
  $define_tpl = "define('%s',%s'%s');";
3522
- $config_lines = SucuriScanFileInfo::file_lines( $config_path );
3523
- $new_keys = SucuriScanAPI::get_new_secret_keys();
3524
  $old_keys = array();
3525
  $old_keys_string = '';
3526
  $new_keys_string = '';
3527
 
3528
- foreach ( (array) $config_lines as $config_line ) {
3529
- if ( preg_match( $pattern, $config_line, $match ) ) {
3530
  $key_name = $match[1];
3531
 
3532
- if ( array_key_exists( $key_name, $new_keys ) ) {
3533
  $white_spaces = $match[2];
3534
  $old_keys[ $key_name ] = $match[3];
3535
- $config_line = sprintf( $define_tpl, $key_name, $white_spaces, $new_keys[ $key_name ] );
3536
- $old_keys_string .= sprintf( $define_tpl, $key_name, $white_spaces, $old_keys[ $key_name ] ) . "\n";
3537
  $new_keys_string .= $config_line . "\n";
3538
  }
3539
  }
@@ -3542,7 +3761,7 @@ class SucuriScanEvent extends SucuriScan {
3542
  }
3543
 
3544
  $response = array(
3545
- 'updated' => is_writable( $config_path ),
3546
  'old_keys' => $old_keys,
3547
  'old_keys_string' => $old_keys_string,
3548
  'new_keys' => $new_keys,
@@ -3550,8 +3769,8 @@ class SucuriScanEvent extends SucuriScan {
3550
  'new_wpconfig' => $new_wpconfig,
3551
  );
3552
 
3553
- if ( $response['updated'] ) {
3554
- file_put_contents( $config_path, $new_wpconfig, LOCK_EX );
3555
  }
3556
 
3557
  return $response;
@@ -3559,7 +3778,6 @@ class SucuriScanEvent extends SucuriScan {
3559
 
3560
  return false;
3561
  }
3562
-
3563
  }
3564
 
3565
  /**
@@ -3577,7 +3795,8 @@ class SucuriScanEvent extends SucuriScan {
3577
  * calls in order to monitor behavior or modify the function of an application
3578
  * or other component; it is also widely used in benchmarking programs.
3579
  */
3580
- class SucuriScanHook extends SucuriScanEvent {
 
3581
 
3582
  /**
3583
  * Send to Sucuri servers an alert notifying that an attachment was added to a post.
@@ -3585,8 +3804,9 @@ class SucuriScanHook extends SucuriScanEvent {
3585
  * @param integer $id The post identifier.
3586
  * @return void
3587
  */
3588
- public static function hook_add_attachment( $id = 0 ){
3589
- if ( $data = get_post( $id ) ) {
 
3590
  $id = $data->ID;
3591
  $title = $data->post_title;
3592
  $mime_type = $data->post_mime_type;
@@ -3595,9 +3815,9 @@ class SucuriScanHook extends SucuriScanEvent {
3595
  $mime_type = 'unknown';
3596
  }
3597
 
3598
- $message = sprintf( 'Media file added; identifier: %s; name: %s; type: %s', $id, $title, $mime_type );
3599
- self::report_notice_event( $message );
3600
- self::notify_event( 'post_publication', $message );
3601
  }
3602
 
3603
  /**
@@ -3606,8 +3826,9 @@ class SucuriScanHook extends SucuriScanEvent {
3606
  * @param integer $id Identifier of the new link created;
3607
  * @return void
3608
  */
3609
- public static function hook_add_link( $id = 0 ){
3610
- if ( $data = get_bookmark( $id ) ) {
 
3611
  $id = $data->link_id;
3612
  $title = $data->link_name;
3613
  $url = $data->link_url;
@@ -3620,10 +3841,13 @@ class SucuriScanHook extends SucuriScanEvent {
3620
 
3621
  $message = sprintf(
3622
  'Bookmark link added; identifier: %s; name: %s; url: %s; target: %s',
3623
- $id, $title, $url, $target
 
 
 
3624
  );
3625
- self::report_warning_event( $message );
3626
- self::notify_event( 'post_publication', $message );
3627
  }
3628
 
3629
  /**
@@ -3632,12 +3856,13 @@ class SucuriScanHook extends SucuriScanEvent {
3632
  * @param integer $id The identifier of the category created.
3633
  * @return void
3634
  */
3635
- public static function hook_create_category( $id = 0 ){
3636
- $title = ( is_int( $id ) ? get_cat_name( $id ) : 'Unknown' );
 
3637
 
3638
- $message = sprintf( 'Category created; identifier: %s; name: %s', $id, $title );
3639
- self::report_notice_event( $message );
3640
- self::notify_event( 'post_publication', $message );
3641
  }
3642
 
3643
  /**
@@ -3646,8 +3871,9 @@ class SucuriScanHook extends SucuriScanEvent {
3646
  * @param integer $id The identifier of the post deleted.
3647
  * @return void
3648
  */
3649
- public static function hook_delete_post( $id = 0 ){
3650
- self::report_warning_event( 'Post deleted; identifier: ' . $id );
 
3651
  }
3652
 
3653
  /**
@@ -3656,8 +3882,9 @@ class SucuriScanHook extends SucuriScanEvent {
3656
  * @param integer $id The identifier of the trashed post.
3657
  * @return void
3658
  */
3659
- public static function hook_wp_trash_post( $id = 0 ){
3660
- if ( $data = get_post( $id ) ) {
 
3661
  $title = $data->post_title;
3662
  $status = $data->post_status;
3663
  } else {
@@ -3667,9 +3894,11 @@ class SucuriScanHook extends SucuriScanEvent {
3667
 
3668
  $message = sprintf(
3669
  'Post moved to trash; identifier: %s; name: %s; status: %s',
3670
- $id, $title, $status
 
 
3671
  );
3672
- self::report_warning_event( $message );
3673
  }
3674
 
3675
  /**
@@ -3678,8 +3907,9 @@ class SucuriScanHook extends SucuriScanEvent {
3678
  * @param integer $id The identifier of the user account deleted.
3679
  * @return void
3680
  */
3681
- public static function hook_delete_user( $id = 0 ){
3682
- self::report_warning_event( 'User account deleted; identifier: ' . $id );
 
3683
  }
3684
 
3685
  /**
@@ -3688,10 +3918,11 @@ class SucuriScanHook extends SucuriScanEvent {
3688
  *
3689
  * @return void
3690
  */
3691
- public static function hook_login_form_resetpass(){
 
3692
  // Detecting WordPress 2.8.3 vulnerability - $key is array.
3693
- if ( isset($_GET['key']) && is_array( $_GET['key'] ) ) {
3694
- self::report_critical_event( 'Attempt to reset password by attacking WP/2.8.3 bug' );
3695
  }
3696
  }
3697
 
@@ -3702,23 +3933,26 @@ class SucuriScanHook extends SucuriScanEvent {
3702
  * @param integer $id The identifier of the post changed.
3703
  * @return void
3704
  */
3705
- public static function hook_private_to_published( $id = 0 ){
3706
- if ( $data = get_post( $id ) ) {
 
3707
  $title = $data->post_title;
3708
- $p_type = ucwords( $data->post_type );
3709
  } else {
3710
  $title = 'Unknown';
3711
  $p_type = 'Publication';
3712
  }
3713
 
3714
  // Check whether the post-type is being ignored to send notifications.
3715
- if ( ! SucuriScanOption::is_ignored_event( $p_type ) ) {
3716
  $message = sprintf(
3717
  '%s (private to published); identifier: %s; name: %s',
3718
- $p_type, $id, $title
 
 
3719
  );
3720
- self::report_notice_event( $message );
3721
- self::notify_event( 'post_publication', $message );
3722
  }
3723
  }
3724
 
@@ -3728,10 +3962,11 @@ class SucuriScanHook extends SucuriScanEvent {
3728
  * @param integer $id The identifier of the post or page published.
3729
  * @return void
3730
  */
3731
- public static function hook_publish( $id = 0 ){
3732
- if ( $data = get_post( $id ) ) {
 
3733
  $title = $data->post_title;
3734
- $p_type = ucwords( $data->post_type );
3735
  $action = ( $data->post_date == $data->post_modified ? 'created' : 'updated' );
3736
  } else {
3737
  $title = 'Unknown';
@@ -3741,10 +3976,13 @@ class SucuriScanHook extends SucuriScanEvent {
3741
 
3742
  $message = sprintf(
3743
  '%s was %s; identifier: %s; name: %s',
3744
- $p_type, $action, $id, $title
 
 
 
3745
  );
3746
- self::report_notice_event( $message );
3747
- self::notify_event( 'post_publication', $message );
3748
  }
3749
 
3750
  /**
@@ -3753,8 +3991,9 @@ class SucuriScanHook extends SucuriScanEvent {
3753
  * @param integer $id The identifier of the post or page published.
3754
  * @return void
3755
  */
3756
- public static function hook_publish_page( $id = 0 ){
3757
- self::hook_publish( $id );
 
3758
  }
3759
 
3760
  /**
@@ -3763,8 +4002,9 @@ class SucuriScanHook extends SucuriScanEvent {
3763
  * @param integer $id The identifier of the post or page published.
3764
  * @return void
3765
  */
3766
- public static function hook_publish_post( $id = 0 ){
3767
- self::hook_publish( $id );
 
3768
  }
3769
 
3770
  /**
@@ -3773,8 +4013,9 @@ class SucuriScanHook extends SucuriScanEvent {
3773
  * @param integer $id The identifier of the post or page published.
3774
  * @return void
3775
  */
3776
- public static function hook_publish_phone( $id = 0 ){
3777
- self::hook_publish( $id );
 
3778
  }
3779
 
3780
  /**
@@ -3783,8 +4024,9 @@ class SucuriScanHook extends SucuriScanEvent {
3783
  * @param integer $id The identifier of the post or page published.
3784
  * @return void
3785
  */
3786
- public static function hook_xmlrpc_publish_post( $id = 0 ){
3787
- self::hook_publish( $id );
 
3788
  }
3789
 
3790
  /**
@@ -3794,12 +4036,13 @@ class SucuriScanHook extends SucuriScanEvent {
3794
  * @param string $title The name of the user account involved in the trasaction.
3795
  * @return void
3796
  */
3797
- public static function hook_retrieve_password( $title = '' ){
3798
- if ( empty($title) ) {
 
3799
  $title = 'unknown';
3800
  }
3801
 
3802
- self::report_error_event( 'Password retrieval attempt: ' . $title );
3803
  }
3804
 
3805
  /**
@@ -3808,14 +4051,15 @@ class SucuriScanHook extends SucuriScanEvent {
3808
  * @param string $title The name of the new theme selected to used through out the site.
3809
  * @return void
3810
  */
3811
- public static function hook_switch_theme( $title = '' ){
3812
- if ( empty($title) ) {
 
3813
  $title = 'unknown';
3814
  }
3815
 
3816
  $message = 'Theme activated: ' . $title;
3817
- self::report_warning_event( $message );
3818
- self::notify_event( 'theme_activated', $message );
3819
  }
3820
 
3821
  /**
@@ -3824,11 +4068,12 @@ class SucuriScanHook extends SucuriScanEvent {
3824
  * @param integer $id The identifier of the new user account created.
3825
  * @return void
3826
  */
3827
- public static function hook_user_register( $id = 0 ){
3828
- if ( $data = get_userdata( $id ) ) {
 
3829
  $title = $data->user_login;
3830
  $email = $data->user_email;
3831
- $roles = @implode( ', ', $data->roles );
3832
  } else {
3833
  $title = 'unknown';
3834
  $email = 'user@domain.com';
@@ -3837,10 +4082,13 @@ class SucuriScanHook extends SucuriScanEvent {
3837
 
3838
  $message = sprintf(
3839
  'User account created; identifier: %s; name: %s; email: %s; roles: %s',
3840
- $id, $title, $email, $roles
 
 
 
3841
  );
3842
- self::report_warning_event( $message );
3843
- self::notify_event( 'user_registration', $message );
3844
  }
3845
 
3846
  /**
@@ -3850,14 +4098,15 @@ class SucuriScanHook extends SucuriScanEvent {
3850
  * @param string $title The name of the user account involved in the transaction.
3851
  * @return void
3852
  */
3853
- public static function hook_wp_login( $title = '' ){
3854
- if ( empty($title) ) {
 
3855
  $title = 'Unknown';
3856
  }
3857
 
3858
  $message = 'User authentication succeeded: ' . $title;
3859
- self::report_notice_event( $message );
3860
- self::notify_event( 'success_login', $message );
3861
  }
3862
 
3863
  /**
@@ -3867,33 +4116,34 @@ class SucuriScanHook extends SucuriScanEvent {
3867
  * @param string $title The name of the user account involved in the transaction.
3868
  * @return void
3869
  */
3870
- public static function hook_wp_login_failed( $title = '' ){
3871
- if ( empty($title) ) {
 
3872
  $title = 'Unknown';
3873
  }
3874
 
3875
- $title = sanitize_user( $title, true );
3876
- $password = SucuriScanRequest::post( 'pwd' );
3877
  $message = 'User authentication failed: ' . $title;
3878
 
3879
- self::report_error_event( $message );
3880
 
3881
- if ( sucuriscan_collect_wrong_passwords() === true ) {
3882
  $message .= "<br>\nUser wrong password: " . $password;
3883
  }
3884
 
3885
- self::notify_event( 'failed_login', $message );
3886
 
3887
  // Log the failed login in the internal datastore for future reports.
3888
- $logged = sucuriscan_log_failed_login( $title, $password );
3889
 
3890
  // Check if the quantity of failed logins will be considered as a brute-force attack.
3891
- if ( $logged ) {
3892
  $failed_logins = sucuriscan_get_failed_logins();
3893
 
3894
- if ( $failed_logins ) {
3895
  $max_time = 3600;
3896
- $maximum_failed_logins = SucuriScanOption::get_option( 'sucuriscan_maximum_failed_logins' );
3897
 
3898
  /**
3899
  * If the time passed is within the hour, and the quantity of failed logins
@@ -3902,14 +4152,11 @@ class SucuriScanHook extends SucuriScanEvent {
3902
  * settings page), then send an email notification reporting the event and
3903
  * specifying that it may be a brute-force attack against the login page.
3904
  */
3905
- if (
3906
- $failed_logins['diff_time'] <= $max_time
3907
  && $failed_logins['count'] >= $maximum_failed_logins
3908
  ) {
3909
- sucuriscan_report_failed_logins( $failed_logins );
3910
- }
3911
-
3912
- /**
3913
  * If there time passed is superior to the hour, then reset the content of the
3914
  * datastore file containing the failed logins so far, any entry in that file
3915
  * will not be considered as part of a brute-force attack (if it exists) because
@@ -3918,9 +4165,9 @@ class SucuriScanHook extends SucuriScanEvent {
3918
  * first entry of that file in case of future attempts during the next sixty
3919
  * minutes.
3920
  */
3921
- elseif ( $failed_logins['diff_time'] > $max_time ) {
3922
  sucuriscan_reset_failed_logins();
3923
- sucuriscan_log_failed_login( $title );
3924
  }
3925
  }
3926
  }
@@ -3942,13 +4189,13 @@ class SucuriScanHook extends SucuriScanEvent {
3942
  * @param object $comment The comment object.
3943
  * @return void
3944
  */
3945
- public static function hook_wp_insert_comment( $id = 0, $comment = false ){
3946
- if (
3947
- $comment instanceof stdClass
3948
- && property_exists( $comment, 'comment_ID' )
3949
- && property_exists( $comment, 'comment_agent' )
3950
- && property_exists( $comment, 'comment_author_IP' )
3951
- && SucuriScanOption::is_enabled( ':comment_monitor' )
3952
  ) {
3953
  $data_set = array(
3954
  'id' => $comment->comment_ID,
@@ -3962,8 +4209,8 @@ class SucuriScanHook extends SucuriScanEvent {
3962
  'content' => $comment->comment_content,
3963
  'user_agent' => $comment->comment_agent,
3964
  );
3965
- $message = base64_encode( json_encode( $data_set ) );
3966
- self::report_notice_event( 'Base64:' . $message, true );
3967
  }
3968
  }
3969
 
@@ -3979,22 +4226,22 @@ class SucuriScanHook extends SucuriScanEvent {
3979
  *
3980
  * @return void
3981
  */
3982
- public static function hook_all( $action = null, $data = false ){
 
3983
  global $wp_filter, $wp_actions;
3984
 
3985
- if (
3986
- is_array( $wp_filter )
3987
- && is_array( $wp_actions )
3988
- && array_key_exists( $action, $wp_actions )
3989
- && ! array_key_exists( $action, $wp_filter )
3990
  && (
3991
- substr( $action, 0, 11 ) === 'admin_post_'
3992
- || substr( $action, 0, 8 ) === 'wp_ajax_'
3993
  )
3994
  ) {
3995
- $message = sprintf( 'Undefined XHR action %s', $action );
3996
- self::report_error_event( $message );
3997
- header( 'HTTP/1.1 400 Bad Request' );
3998
  exit(1);
3999
  }
4000
  }
@@ -4006,313 +4253,272 @@ class SucuriScanHook extends SucuriScanEvent {
4006
  *
4007
  * @return integer Either one or zero representing the success or fail of the operation.
4008
  */
4009
- public static function hook_undefined_actions(){
 
4010
 
4011
  $plugin_activate_actions = '(activate|deactivate)(\-selected)?';
4012
  $plugin_update_actions = '(upgrade-plugin|do-plugin-upgrade|update-selected)';
4013
 
4014
  // Plugin activation and/or deactivation.
4015
- if (
4016
- current_user_can( 'activate_plugins' )
4017
  && (
4018
- SucuriScanRequest::get_or_post( 'action', $plugin_activate_actions )
4019
- || SucuriScanRequest::get_or_post( 'action2', $plugin_activate_actions )
4020
  )
4021
  ) {
4022
  $plugin_list = array();
4023
  $items_affected = array();
4024
 
4025
  // Get the action performed through action or action2 params.
4026
- $action_d = SucuriScanRequest::get_or_post( 'action' );
4027
- if ( $action_d == '-1' ) {
4028
- $action_d = SucuriScanRequest::get_or_post( 'action2' );
4029
  }
4030
  $action_d .= 'd';
4031
 
4032
- if (
4033
- SucuriScanRequest::get( 'plugin', '.+' )
4034
- && strpos( $_SERVER['REQUEST_URI'], 'plugins.php' ) !== false
4035
  ) {
4036
- $plugin_list[] = SucuriScanRequest::get( 'plugin' );
4037
- }
4038
-
4039
- elseif (
4040
- isset($_POST['checked'])
4041
- && is_array( $_POST['checked'] )
4042
- && ! empty($_POST['checked'])
4043
  ) {
4044
- $plugin_list = SucuriScanRequest::post( 'checked', '_array' );
4045
- $action_d = str_replace( '-selected', '', $action_d );
4046
  }
4047
 
4048
- foreach ( $plugin_list as $plugin ) {
4049
- $plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
4050
 
4051
- if (
4052
- ! empty($plugin_info['Name'])
4053
- && ! empty($plugin_info['Version'])
4054
  ) {
4055
  $items_affected[] = sprintf(
4056
  '%s (v%s; %s)',
4057
- self::escape( $plugin_info['Name'] ),
4058
- self::escape( $plugin_info['Version'] ),
4059
- self::escape( $plugin )
4060
  );
4061
  }
4062
  }
4063
 
4064
  // Report activated/deactivated plugins at once.
4065
- if ( ! empty($items_affected) ) {
4066
- $message_tpl = ( count( $items_affected ) > 1 )
4067
  ? 'Plugins %s: (multiple entries): %s'
4068
  : 'Plugin %s: %s';
4069
  $message = sprintf(
4070
  $message_tpl,
4071
  $action_d,
4072
- @implode( ',', $items_affected )
4073
  );
4074
- self::report_warning_event( $message );
4075
- self::notify_event( 'plugin_' . $action_d, $message );
4076
  }
4077
- }
4078
-
4079
- // Plugin update request.
4080
- elseif (
4081
- current_user_can( 'update_plugins' )
4082
  && (
4083
- SucuriScanRequest::get_or_post( 'action', $plugin_update_actions )
4084
- || SucuriScanRequest::get_or_post( 'action2', $plugin_update_actions )
4085
  )
4086
  ) {
4087
  $plugin_list = array();
4088
  $items_affected = array();
4089
 
4090
- if (
4091
- SucuriScanRequest::get( 'plugin', '.+' )
4092
- && strpos( $_SERVER['REQUEST_URI'], 'wp-admin/update.php' ) !== false
4093
  ) {
4094
- $plugin_list[] = SucuriScanRequest::get( 'plugin', '.+' );
4095
- }
4096
-
4097
- elseif (
4098
- isset($_POST['checked'])
4099
- && is_array( $_POST['checked'] )
4100
- && ! empty($_POST['checked'])
4101
  ) {
4102
- $plugin_list = SucuriScanRequest::post( 'checked', '_array' );
4103
  }
4104
 
4105
- foreach ( $plugin_list as $plugin ) {
4106
- $plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
4107
 
4108
- if (
4109
- ! empty($plugin_info['Name'])
4110
- && ! empty($plugin_info['Version'])
4111
  ) {
4112
  $items_affected[] = sprintf(
4113
  '%s (v%s; %s)',
4114
- self::escape( $plugin_info['Name'] ),
4115
- self::escape( $plugin_info['Version'] ),
4116
- self::escape( $plugin )
4117
  );
4118
  }
4119
  }
4120
 
4121
  // Report updated plugins at once.
4122
- if ( ! empty($items_affected) ) {
4123
- $message_tpl = ( count( $items_affected ) > 1 )
4124
  ? 'Plugins updated: (multiple entries): %s'
4125
  : 'Plugin updated: %s';
4126
  $message = sprintf(
4127
  $message_tpl,
4128
- @implode( ',', $items_affected )
4129
  );
4130
- self::report_warning_event( $message );
4131
- self::notify_event( 'plugin_updated', $message );
4132
  }
4133
- }
4134
-
4135
- // Plugin installation request.
4136
- elseif (
4137
- current_user_can( 'install_plugins' )
4138
- && SucuriScanRequest::get( 'action', '(install|upload)-plugin' )
4139
  ) {
4140
- if ( isset($_FILES['pluginzip']) ) {
4141
- $plugin = self::escape( $_FILES['pluginzip']['name'] );
4142
  } else {
4143
- $plugin = SucuriScanRequest::get( 'plugin', '.+' );
4144
 
4145
- if ( ! $plugin ) {
4146
  $plugin = 'Unknown';
4147
  }
4148
  }
4149
 
4150
- $message = 'Plugin installed: ' . self::escape( $plugin );
4151
- SucuriScanEvent::report_warning_event( $message );
4152
- self::notify_event( 'plugin_installed', $message );
4153
- }
4154
-
4155
- // Plugin deletion request.
4156
- elseif (
4157
- current_user_can( 'delete_plugins' )
4158
- && SucuriScanRequest::post( 'action', 'delete-selected' )
4159
- && SucuriScanRequest::post( 'verify-delete', '1' )
4160
  ) {
4161
- $plugin_list = SucuriScanRequest::post( 'checked', '_array' );
4162
  $items_affected = array();
4163
 
4164
- foreach ( (array) $plugin_list as $plugin ) {
4165
- $plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
4166
 
4167
- if (
4168
- ! empty($plugin_info['Name'])
4169
- && ! empty($plugin_info['Version'])
4170
  ) {
4171
  $items_affected[] = sprintf(
4172
  '%s (v%s; %s)',
4173
- self::escape( $plugin_info['Name'] ),
4174
- self::escape( $plugin_info['Version'] ),
4175
- self::escape( $plugin )
4176
  );
4177
  }
4178
  }
4179
 
4180
  // Report deleted plugins at once.
4181
- if ( ! empty($items_affected) ) {
4182
- $message_tpl = ( count( $items_affected ) > 1 )
4183
  ? 'Plugins deleted: (multiple entries): %s'
4184
  : 'Plugin deleted: %s';
4185
  $message = sprintf(
4186
  $message_tpl,
4187
- @implode( ',', $items_affected )
4188
  );
4189
- self::report_warning_event( $message );
4190
- self::notify_event( 'plugin_deleted', $message );
4191
- }
4192
- }
4193
-
4194
- // Plugin editor request.
4195
- elseif (
4196
- current_user_can( 'edit_plugins' )
4197
- && SucuriScanRequest::post( 'action', 'update' )
4198
- && SucuriScanRequest::post( 'plugin', '.+' )
4199
- && SucuriScanRequest::post( 'file', '.+' )
4200
- && strpos( $_SERVER['REQUEST_URI'], 'plugin-editor.php' ) !== false
4201
  ) {
4202
- $filename = SucuriScanRequest::post( 'file' );
4203
- $message = 'Plugin editor used in: ' . SucuriScan::escape( $filename );
4204
- self::report_error_event( $message );
4205
- self::notify_event( 'theme_editor', $message );
4206
- }
4207
-
4208
- // Theme editor request.
4209
- elseif (
4210
- current_user_can( 'edit_themes' )
4211
- && SucuriScanRequest::post( 'action', 'update' )
4212
- && SucuriScanRequest::post( 'theme', '.+' )
4213
- && SucuriScanRequest::post( 'file', '.+' )
4214
- && strpos( $_SERVER['REQUEST_URI'], 'theme-editor.php' ) !== false
4215
  ) {
4216
- $theme_name = SucuriScanRequest::post( 'theme' );
4217
- $filename = SucuriScanRequest::post( 'file' );
4218
- $message = 'Theme editor used in: ' . SucuriScan::escape( $theme_name ) . '/' . SucuriScan::escape( $filename );
4219
- self::report_error_event( $message );
4220
- self::notify_event( 'theme_editor', $message );
4221
- }
4222
-
4223
- // Theme installation request.
4224
- elseif (
4225
- current_user_can( 'install_themes' )
4226
- && SucuriScanRequest::get( 'action', 'install-theme' )
4227
  ) {
4228
- $theme = SucuriScanRequest::get( 'theme', '.+' );
4229
 
4230
- if ( ! $theme ) {
4231
  $theme = 'Unknown';
4232
  }
4233
 
4234
- $message = 'Theme installed: ' . self::escape( $theme );
4235
- SucuriScanEvent::report_warning_event( $message );
4236
- self::notify_event( 'theme_installed', $message );
4237
- }
4238
-
4239
- // Theme deletion request.
4240
- elseif (
4241
- current_user_can( 'delete_themes' )
4242
- && SucuriScanRequest::get_or_post( 'action', 'delete' )
4243
- && SucuriScanRequest::get_or_post( 'stylesheet', '.+' )
4244
  ) {
4245
- $theme = SucuriScanRequest::get( 'stylesheet', '.+' );
4246
 
4247
- if ( ! $theme ) {
4248
  $theme = 'Unknown';
4249
  }
4250
 
4251
- $message = 'Theme deleted: ' . self::escape( $theme );
4252
- SucuriScanEvent::report_warning_event( $message );
4253
- self::notify_event( 'theme_deleted', $message );
4254
- }
4255
-
4256
- // Theme update request.
4257
- elseif (
4258
- current_user_can( 'update_themes' )
4259
- && SucuriScanRequest::get( 'action', '(upgrade-theme|do-theme-upgrade)' )
4260
- && SucuriScanRequest::post( 'checked', '_array' )
4261
  ) {
4262
- $themes = SucuriScanRequest::post( 'checked', '_array' );
4263
  $items_affected = array();
4264
 
4265
- foreach ( (array) $themes as $theme ) {
4266
- $theme_info = wp_get_theme( $theme );
4267
- $theme_name = ucwords( $theme );
4268
  $theme_version = '0.0';
4269
 
4270
- if ( $theme_info->exists() ) {
4271
- $theme_name = $theme_info->get( 'Name' );
4272
- $theme_version = $theme_info->get( 'Version' );
4273
  }
4274
 
4275
  $items_affected[] = sprintf(
4276
  '%s (v%s; %s)',
4277
- self::escape( $theme_name ),
4278
- self::escape( $theme_version ),
4279
- self::escape( $theme )
4280
  );
4281
  }
4282
 
4283
  // Report updated themes at once.
4284
- if ( ! empty($items_affected) ) {
4285
- $message_tpl = ( count( $items_affected ) > 1 )
4286
  ? 'Themes updated: (multiple entries): %s'
4287
  : 'Theme updated: %s';
4288
  $message = sprintf(
4289
  $message_tpl,
4290
- @implode( ',', $items_affected )
4291
  );
4292
- self::report_warning_event( $message );
4293
- self::notify_event( 'theme_updated', $message );
4294
  }
4295
- }
4296
-
4297
- // WordPress update request.
4298
- elseif (
4299
- current_user_can( 'update_core' )
4300
- && SucuriScanRequest::get( 'action', '(do-core-upgrade|do-core-reinstall)' )
4301
- && SucuriScanRequest::post( 'upgrade' )
4302
  ) {
4303
- $message = 'WordPress updated to version: ' . SucuriScanRequest::post( 'version' );
4304
- self::report_critical_event( $message );
4305
- self::notify_event( 'website_updated', $message );
4306
- }
4307
-
4308
- // Widget addition or deletion.
4309
- elseif (
4310
- current_user_can( 'edit_theme_options' )
4311
- && SucuriScanRequest::post( 'action', 'save-widget' )
4312
- && SucuriScanRequest::post( 'id_base' ) !== false
4313
- && SucuriScanRequest::post( 'sidebar' ) !== false
4314
  ) {
4315
- if ( SucuriScanRequest::post( 'delete_widget', '1' ) ) {
4316
  $action_d = 'deleted';
4317
  $action_text = 'deleted from';
4318
  } else {
@@ -4322,44 +4528,41 @@ class SucuriScanHook extends SucuriScanEvent {
4322
 
4323
  $message = sprintf(
4324
  'Widget %s (%s) %s %s (#%d; size %dx%d)',
4325
- SucuriScanRequest::post( 'id_base' ),
4326
- SucuriScanRequest::post( 'widget-id' ),
4327
  $action_text,
4328
- SucuriScanRequest::post( 'sidebar' ),
4329
- SucuriScanRequest::post( 'widget_number' ),
4330
- SucuriScanRequest::post( 'widget-width' ),
4331
- SucuriScanRequest::post( 'widget-height' )
4332
  );
4333
 
4334
- self::report_warning_event( $message );
4335
- self::notify_event( 'widget_' . $action_d, $message );
4336
- }
4337
-
4338
- // Detect any Wordpress settings modification.
4339
- elseif (
4340
- current_user_can( 'manage_options' )
4341
  && SucuriScanOption::check_options_nonce()
4342
  ) {
4343
  // Get the settings available in the database and compare them with the submission.
4344
- $options_changed = SucuriScanOption::what_options_were_changed( $_POST );
4345
  $options_changed_str = '';
4346
  $options_changed_simple = '';
4347
  $options_changed_count = 0;
4348
 
4349
  // Generate the list of options changed.
4350
- foreach ( $options_changed['original'] as $option_name => $option_value ) {
4351
  $options_changed_count += 1;
4352
  $options_changed_str .= sprintf(
4353
  "The value of the option <b>%s</b> was changed from <b>'%s'</b> to <b>'%s'</b>.<br>\n",
4354
- self::escape( $option_name ),
4355
- self::escape( $option_value ),
4356
- self::escape( $options_changed['changed'][ $option_name ] )
4357
  );
4358
  $options_changed_simple .= sprintf(
4359
  "%s: from '%s' to '%s',",
4360
- self::escape( $option_name ),
4361
- self::escape( $option_value ),
4362
- self::escape( $options_changed['changed'][ $option_name ] )
4363
  );
4364
  }
4365
 
@@ -4368,7 +4571,7 @@ class SucuriScanHook extends SucuriScanEvent {
4368
  $page_referer = false;
4369
 
4370
  // Check which of these option groups where modified.
4371
- switch ( $option_page ) {
4372
  case 'options':
4373
  $page_referer = 'Global';
4374
  break;
@@ -4378,26 +4581,25 @@ class SucuriScanHook extends SucuriScanEvent {
4378
  case 'discussion': /* no_break */
4379
  case 'media': /* no_break */
4380
  case 'permalink':
4381
- $page_referer = ucwords( $option_page );
4382
  break;
4383
  default:
4384
  $page_referer = 'Common';
4385
  break;
4386
  }
4387
 
4388
- if ( $page_referer && $options_changed_count > 0 ) {
4389
  $message = $page_referer . ' settings changed';
4390
- SucuriScanEvent::report_error_event( sprintf(
4391
  '%s: (multiple entries): %s',
4392
  $message,
4393
- rtrim( $options_changed_simple, ',' )
4394
- ) );
4395
- self::notify_event( 'settings_updated', $message . "<br>\n" . $options_changed_str );
4396
  }
4397
  }
4398
 
4399
  }
4400
-
4401
  }
4402
 
4403
  /**
@@ -4417,10 +4619,10 @@ class SucuriScanHook extends SucuriScanEvent {
4417
  * APIs allow the combination of multiple APIs into new applications known as
4418
  * mashups.
4419
  *
4420
- * @see http://en.wikipedia.org/wiki/Application_programming_interface#Web_APIs
4421
  */
4422
- class SucuriScanAPI extends SucuriScanOption {
4423
-
4424
  /**
4425
  * Check whether the SSL certificates will be verified while executing a HTTP
4426
  * request or not. This is only for customization of the administrator, in fact
@@ -4428,17 +4630,34 @@ class SucuriScanAPI extends SucuriScanOption {
4428
  *
4429
  * @return boolean Whether the SSL certs will be verified while sending a request.
4430
  */
4431
- public static function verify_ssl_cert(){
4432
- return ( self::get_option( ':verify_ssl_cert' ) === 'true' );
 
4433
  }
4434
 
4435
  /**
4436
  * Seconds before consider a HTTP request as timeout.
4437
  *
 
 
 
 
 
 
 
4438
  * @return integer Seconds to consider a HTTP request timeout.
4439
  */
4440
- public static function request_timeout(){
4441
- return intval( self::get_option( ':request_timeout' ) );
 
 
 
 
 
 
 
 
 
4442
  }
4443
 
4444
  /**
@@ -4446,22 +4665,21 @@ class SucuriScanAPI extends SucuriScanOption {
4446
  *
4447
  * @return string An user-agent for the HTTP requests.
4448
  */
4449
- private static function user_agent(){
4450
- $user_agent = sprintf(
 
4451
  'WordPress/%s; %s',
4452
  self::site_version(),
4453
  self::get_domain()
4454
  );
4455
-
4456
- return $user_agent;
4457
  }
4458
 
4459
  /**
4460
  * Retrieves a URL using a changeable HTTP method, returning results in an
4461
  * array. Results include HTTP headers and content.
4462
  *
4463
- * @see http://codex.wordpress.org/Function_Reference/wp_remote_post
4464
- * @see http://codex.wordpress.org/Function_Reference/wp_remote_get
4465
  *
4466
  * @param string $url The target URL where the request will be sent.
4467
  * @param string $method HTTP method that will be used to send the request.
@@ -4469,52 +4687,53 @@ class SucuriScanAPI extends SucuriScanOption {
4469
  * @param array $args Request arguments like the timeout, redirections, headers, cookies, etc.
4470
  * @return array Response object after the HTTP request is executed.
4471
  */
4472
- private static function api_call( $url = '', $method = 'GET', $params = array(), $args = array() ){
4473
- if ( ! $url ) {
 
4474
  return false;
4475
  }
4476
 
4477
  $req_args = array(
4478
  'method' => $method,
4479
- 'timeout' => self::request_timeout(),
4480
  'redirection' => 2,
4481
  'httpversion' => '1.0',
4482
- 'user-agent' => self::user_agent(),
4483
  'blocking' => true,
4484
  'headers' => array(),
4485
  'cookies' => array(),
4486
  'compress' => false,
4487
  'decompress' => false,
4488
- 'sslverify' => self::verify_ssl_cert(),
4489
  );
4490
 
4491
  // Update the request arguments with the values passed tot he function.
4492
- foreach ( $args as $arg_name => $arg_value ) {
4493
- if ( array_key_exists( $arg_name, $req_args ) ) {
4494
- $req_args[ $arg_name ] = $arg_value;
4495
  }
4496
  }
4497
 
4498
  // Add random request parameter to avoid request reset.
4499
- if ( ! empty($params) ) {
4500
  $params['time'] = time();
4501
  }
4502
 
4503
- if ( $method == 'GET' ) {
4504
- if ( ! empty($params) ) {
4505
- $url = sprintf( '%s?%s', $url, http_build_query( $params ) );
4506
  }
4507
 
4508
- $response = wp_remote_get( $url, $req_args );
4509
- } elseif ( $method == 'POST' ) {
4510
  $req_args['body'] = $params;
4511
- $response = wp_remote_post( $url, $req_args );
4512
  } else {
4513
  $response = false;
4514
- SucuriScanInterface::error( 'HTTP method not allowed: ' . $method );
4515
  }
4516
 
4517
- return self::process_response( $response, $params, $args );
4518
  }
4519
 
4520
  /**
@@ -4523,10 +4742,9 @@ class SucuriScanAPI extends SucuriScanOption {
4523
  * @param string $api_key An unique string to identify this installation.
4524
  * @return boolean True if the API key is valid, false otherwise.
4525
  */
4526
- private static function is_valid_key( $api_key = '' ){
4527
- $pattern = '/^[a-z0-9]{32}$/';
4528
-
4529
- return (bool) ( @preg_match( $pattern, $api_key ) );
4530
  }
4531
 
4532
  /**
@@ -4536,19 +4754,20 @@ class SucuriScanAPI extends SucuriScanOption {
4536
  * @param boolean $validate Whether the format of the key should be validated before store it.
4537
  * @return boolean Either true or false if the key was saved successfully or not respectively.
4538
  */
4539
- public static function set_plugin_key( $api_key = '', $validate = false ){
4540
- if ( $validate ) {
4541
- if ( ! self::is_valid_key( $api_key ) ) {
4542
- SucuriScanInterface::error( 'Invalid API key format' );
 
4543
  return false;
4544
  }
4545
  }
4546
 
4547
- if ( ! empty($api_key) ) {
4548
- SucuriScanEvent::notify_event( 'plugin_change', 'API key updated successfully: ' . $api_key );
4549
  }
4550
 
4551
- return self::update_option( ':api_key', $api_key );
4552
  }
4553
 
4554
  /**
@@ -4556,12 +4775,12 @@ class SucuriScanAPI extends SucuriScanOption {
4556
  *
4557
  * @return string|boolean The API key or false if it does not exists.
4558
  */
4559
- public static function get_plugin_key(){
4560
- $api_key = self::get_option( ':api_key' );
 
4561
 
4562
- if (
4563
- is_string( $api_key )
4564
- && self::is_valid_key( $api_key )
4565
  ) {
4566
  return $api_key;
4567
  }
@@ -4578,24 +4797,25 @@ class SucuriScanAPI extends SucuriScanOption {
4578
  *
4579
  * @return array|boolean false if the key is invalid or not present, an array otherwise.
4580
  */
4581
- public static function get_cloudproxy_key(){
 
4582
  $option_name = ':cloudproxy_apikey';
4583
- $api_key = self::get_option( $option_name );
4584
 
4585
  // Check if the cloudproxy-waf plugin was previously installed.
4586
- if ( ! $api_key ) {
4587
- $api_key = self::get_option( 'sucuriwaf_apikey' );
4588
 
4589
- if ( $api_key ) {
4590
- self::update_option( $option_name, $api_key );
4591
- self::delete_option( 'sucuriwaf_apikey' );
4592
  }
4593
  }
4594
 
4595
  // Check the validity of the API key.
4596
- $match = self::is_valid_cloudproxy_key( $api_key, true );
4597
 
4598
- if ( $match ) {
4599
  return array(
4600
  'string' => $match[1].'/'.$match[2],
4601
  'k' => $match[1],
@@ -4613,11 +4833,12 @@ class SucuriScanAPI extends SucuriScanOption {
4613
  * @param boolean $return_match Whether the parts of the API key must be returned or not.
4614
  * @return boolean true if the API key specified is valid, false otherwise.
4615
  */
4616
- public static function is_valid_cloudproxy_key( $api_key = '', $return_match = false ){
 
4617
  $pattern = '/^([a-z0-9]{32})\/([a-z0-9]{32})$/';
4618
 
4619
- if ( $api_key && preg_match( $pattern, $api_key, $match ) ) {
4620
- if ( $return_match ) {
4621
  return $match;
4622
  }
4623
 
@@ -4636,24 +4857,23 @@ class SucuriScanAPI extends SucuriScanOption {
4636
  * @param array $args Request arguments like the timeout, redirections, headers, cookies, etc.
4637
  * @return array Response object after the HTTP request is executed.
4638
  */
4639
- public static function api_call_wordpress( $method = 'GET', $params = array(), $send_api_key = true, $args = array() ){
 
4640
  $url = SUCURISCAN_API;
4641
  $params[ SUCURISCAN_API_VERSION ] = 1;
4642
  $params['p'] = 'wordpress';
4643
 
4644
- if ( $send_api_key ) {
4645
- $api_key = self::get_plugin_key();
4646
 
4647
- if ( ! $api_key ) {
4648
  return false;
4649
  }
4650
 
4651
  $params['k'] = $api_key;
4652
  }
4653
 
4654
- $response = self::api_call( $url, $method, $params, $args );
4655
-
4656
- return $response;
4657
  }
4658
 
4659
  /**
@@ -4663,29 +4883,28 @@ class SucuriScanAPI extends SucuriScanOption {
4663
  * @param array $params Parameters for the request defined in an associative array of key-value.
4664
  * @return array Response object after the HTTP request is executed.
4665
  */
4666
- public static function api_call_cloudproxy( $method = 'GET', $params = array() ){
 
4667
  $send_request = false;
4668
 
4669
- if ( isset($params['k']) && isset($params['s']) ) {
4670
  $send_request = true;
4671
  } else {
4672
- $api_key = self::get_cloudproxy_key();
4673
 
4674
- if ( $api_key ) {
4675
  $send_request = true;
4676
  $params['k'] = $api_key['k'];
4677
  $params['s'] = $api_key['s'];
4678
  }
4679
  }
4680
 
4681
- if ( $send_request ) {
4682
  $url = SUCURISCAN_CLOUDPROXY_API;
4683
  $params[ SUCURISCAN_CLOUDPROXY_API_VERSION ] = 1;
4684
- unset( $params['string'] );
4685
-
4686
- $response = self::api_call( $url, $method, $params );
4687
 
4688
- return $response;
4689
  }
4690
 
4691
  return false;
@@ -4699,16 +4918,17 @@ class SucuriScanAPI extends SucuriScanOption {
4699
  * @param array $args Request arguments like the timeout, redirections, headers, cookies, etc.
4700
  * @return array Response object with some modifications.
4701
  */
4702
- private static function process_response( $response = array(), $params = array(), $args = array() ){
 
4703
  /**
4704
  * Convert the error message generated by the code base functions after the HTTP
4705
  * request is executed to a valid response object that will allow this code
4706
  * process the data according to the specified standards.
4707
  */
4708
- if ( is_wp_error( $response ) ) {
4709
  // Extract information from the error object.
4710
  $error_message = $response->get_error_message();
4711
- $request_action = isset( $params['a'] ) ? $params['a'] : 'unknown';
4712
 
4713
  // Build a fake request response with custom data.
4714
  $data_set = array(
@@ -4722,11 +4942,11 @@ class SucuriScanAPI extends SucuriScanOption {
4722
 
4723
  // Build the response object and encode data.
4724
  $response = array();
4725
- $response['body'] = json_encode( $data_set );
4726
- $response['headers']['date'] = date( 'r' );
4727
  $response['headers']['connection'] = 'close';
4728
  $response['headers']['content-type'] = 'application/json';
4729
- $response['headers']['content-length'] = strlen( $response['body'] );
4730
  $response['response']['code'] = 500;
4731
  $response['response']['message'] = 'ERROR';
4732
  }
@@ -4742,23 +4962,33 @@ class SucuriScanAPI extends SucuriScanOption {
4742
  * they will see extra information explaining the response and how to proceed
4743
  * with it.
4744
  */
4745
- if (
4746
- is_array( $response )
4747
- && array_key_exists( 'body', $response )
4748
- && array_key_exists( 'headers', $response )
4749
- && array_key_exists( 'response', $response )
4750
  ) {
 
4751
  $response['body_raw'] = $response['body'];
4752
 
4753
- // Check if the response data is JSON-encoded, then decode it.
4754
- if (
4755
- isset($response['headers']['content-type'])
 
 
 
 
 
 
 
 
 
 
4756
  && $response['headers']['content-type'] == 'application/json'
4757
  ) {
4758
- $assoc = ( isset($args['assoc']) && $args['assoc'] === true ) ? true : false;
4759
- $response['body'] = @json_decode( $response['body_raw'], $assoc );
4760
- } elseif ( self::is_serialized( $response['body'] ) ) {
4761
- // Check if the response data is serialized (which we will consider as insecure).
4762
  $response['body_raw'] = null;
4763
  $response['body'] = 'ERROR:Serialized data is not supported.';
4764
  }
@@ -4774,129 +5004,179 @@ class SucuriScanAPI extends SucuriScanOption {
4774
  * generic variables and types, in case of an error a notification will appears
4775
  * in the administrator panel explaining the result of the operation.
4776
  *
4777
- * @param array $response Response object after the HTTP request is executed.
4778
- * @return boolean Either true or false in case of success or failure of the API response (respectively).
 
4779
  */
4780
- private static function handle_response( $response = array() ){
4781
- if ( $response ) {
4782
- if ( $response['body'] instanceof stdClass ) {
4783
- if ( isset($response['body']->status) ) {
4784
- if ( $response['body']->status == 1 ) {
 
 
 
4785
  return true;
4786
  } else {
4787
- $action_message = 'Unknown error, there is no more information.';
4788
-
4789
- // Check whether the message list is empty or not.
4790
- if ( isset($response['body']->messages[0]) ) {
4791
- $action_message = $response['body']->messages[0] . '.';
4792
- }
4793
-
4794
- // Keep a copy of the original API response message.
4795
- $raw_message = $action_message;
4796
-
4797
- // Special response for invalid API keys.
4798
- if ( stripos( $raw_message, 'log file not found' ) !== false ) {
4799
- SucuriScanOption::delete_option( ':api_key' );
4800
-
4801
- $action_message .= ' This generally happens when you add an invalid API key, the'
4802
- . ' key will be deleted automatically to hide these warnings, if you want to'
4803
- . ' recover it go to the settings page and use the recover button to send the'
4804
- . ' key to your email address.';
4805
- }
4806
-
4807
- // Special response for invalid CloudProxy API keys.
4808
- if ( stripos( $raw_message, 'wrong api key' ) !== false ) {
4809
- SucuriScanOption::delete_option( ':cloudproxy_apikey' );
4810
- SucuriScanOption::delete_option( ':revproxy' );
4811
-
4812
- $action_message .= ' The CloudProxy API key does not seems to be valid.';
4813
- }
4814
-
4815
- // Special response for connection time outs.
4816
- if ( stripos( $raw_message, 'timed out' ) !== false ) {
4817
- $current_timeout = SucuriScanOption::get_option( ':request_timeout' );
4818
-
4819
- if ( $current_timeout < 300 ) {
4820
- SucuriScanOption::update_option( ':request_timeout', 300 );
4821
- }
4822
-
4823
- $action_message .= ' This generally happens when the API service fails to respond'
4824
- . ' in time, you currently have configured the plugin to discard the network'
4825
- . ' connection after ' . $current_timeout . ' seconds. Wait a few minutes until'
4826
- . ' the issue is resolved by itself, or change the timeout limit from the general'
4827
- . ' settings page of the plugin, the option is named "API request timeout".';
4828
- }
4829
-
4830
- // Stop SSL peer verification on connection failures.
4831
- if (
4832
- stripos( $raw_message, 'no alternative certificate' )
4833
- || stripos( $raw_message, 'error setting certificate' )
4834
- ) {
4835
- SucuriScanOption::update_option( ':verify_ssl_cert', 'false' );
4836
-
4837
- $action_message .= 'There were some issues with the SSL certificate either in this'
4838
- . ' server or with the remote API service. The automatic verification of the'
4839
- . ' certificates has been deactivated to reduce the noise during the execution'
4840
- . ' of the HTTP requests.';
4841
- }
4842
-
4843
- SucuriScanInterface::error(
4844
- sprintf(
4845
- '(%d) %s: %s',
4846
- SucuriScan::local_time(),
4847
- ucwords( $response['body']->action ),
4848
- $action_message
4849
- )
4850
- );
4851
  }
4852
  } else {
4853
- SucuriScanInterface::error( 'Could not determine the status of an API call.' );
4854
  }
4855
  } else {
4856
- $error_message = 'non JSON-encoded response.';
4857
 
4858
- if (
4859
- isset($response['response'])
4860
  && isset($response['response']['message'])
4861
  && isset($response['response']['code'])
4862
  && $response['response']['code'] !== 200
4863
  ) {
4864
- $error_message = sprintf(
4865
  '(%s) %s',
4866
  $response['response']['code'],
4867
  $response['response']['message']
4868
  );
4869
  }
4870
 
4871
- SucuriScanInterface::error( 'Malformed API response: ' . $error_message );
4872
  }
4873
  }
4874
 
 
 
 
 
4875
  return false;
4876
  }
4877
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4878
  /**
4879
  * Send a request to the API to register this site.
4880
  *
4881
  * @param string $email Optional email address for the registration.
4882
  * @return boolean True if the API key was generated, false otherwise.
4883
  */
4884
- public static function register_site( $email = '' ){
4885
- if ( ! is_string($email) || empty( $email ) ) {
 
4886
  $email = self::get_site_email();
4887
  }
4888
 
4889
- $response = self::api_call_wordpress( 'POST', array(
4890
  'e' => $email,
4891
  's' => self::get_domain(),
4892
  'a' => 'register_site',
4893
- ), false );
4894
 
4895
- if ( self::handle_response( $response ) ) {
4896
- self::set_plugin_key( $response['body']->output->api_key );
4897
  SucuriScanEvent::schedule_task();
4898
- SucuriScanEvent::notify_event( 'plugin_change', 'Site registered and API key generated' );
4899
- SucuriScanInterface::info( 'The API key for your site was successfully generated and saved.' );
4900
 
4901
  return true;
4902
  }
@@ -4909,18 +5189,19 @@ class SucuriScanAPI extends SucuriScanOption {
4909
  *
4910
  * @return boolean true if the API key was sent to the administrator email, false otherwise.
4911
  */
4912
- public static function recover_key(){
 
4913
  $clean_domain = self::get_domain();
4914
 
4915
- $response = self::api_call_wordpress( 'GET', array(
4916
  'e' => self::get_site_email(),
4917
  's' => $clean_domain,
4918
  'a' => 'recover_key',
4919
- ), false );
4920
 
4921
- if ( self::handle_response( $response ) ) {
4922
- SucuriScanEvent::notify_event( 'plugin_change', 'API key recovered for domain: ' . $clean_domain );
4923
- SucuriScanInterface::info( $response['body']->output->message );
4924
 
4925
  return true;
4926
  }
@@ -4934,17 +5215,25 @@ class SucuriScanAPI extends SucuriScanOption {
4934
  * settings or files in the administrator panel, or a notification generated by
4935
  * this plugin.
4936
  *
4937
- * @param string $event The information gathered through out the normal functioning of the site.
4938
- * @return boolean true if the event was logged in the monitoring service, false otherwise.
 
 
4939
  */
4940
- public static function send_log( $event = '' ){
4941
- if ( ! empty($event) ) {
4942
- $response = self::api_call_wordpress( 'POST', array(
4943
- 'a' => 'send_log',
4944
- 'm' => $event,
4945
- ), true, array( 'timeout' => 20 ) );
 
 
 
 
 
 
4946
 
4947
- if ( self::handle_response( $response ) ) {
4948
  return true;
4949
  }
4950
  }
@@ -4952,21 +5241,63 @@ class SucuriScanAPI extends SucuriScanOption {
4952
  return false;
4953
  }
4954
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4955
  /**
4956
  * Retrieve all the event logs registered by the API service.
4957
  *
4958
  * @return array The object with the data returned from the API service.
4959
  */
4960
- public static function get_all_logs(){
 
4961
  // Get the total number of lines in the logs.
4962
- $response = self::api_call_wordpress( 'GET', array(
4963
  'a' => 'get_logs',
4964
  'l' => 0,
4965
- ) );
4966
 
4967
  // If success continue with the retrieval of the logs data.
4968
- if ( self::handle_response( $response ) ) {
4969
- return self::get_logs( $response['body']->total_entries );
4970
  }
4971
 
4972
  return false;
@@ -4978,21 +5309,22 @@ class SucuriScanAPI extends SucuriScanOption {
4978
  * @param integer $lines How many lines from the log file will be retrieved.
4979
  * @return string The response of the API service.
4980
  */
4981
- public static function get_logs( $lines = 50 ){
4982
- $response = self::api_call_wordpress( 'GET', array(
 
4983
  'a' => 'get_logs',
4984
  'l' => $lines,
4985
- ) );
4986
 
4987
- if ( self::handle_response( $response ) ) {
4988
  $response['body']->output_data = array();
4989
  $log_pattern = '/^([0-9\-]+) ([0-9:]+) (\S+) : (.+)/';
4990
  $extra_pattern = '/(.+ \(multiple entries\):) (.+)/';
4991
- $generic_pattern = '/^@?([A-Z][a-z]{3,7}): ([^:;]+; )?(.+)/';
4992
  $auth_pattern = '/^User authentication (succeeded|failed): ([^<;]+)/';
4993
 
4994
- foreach ( $response['body']->output as $log ) {
4995
- if ( preg_match( $log_pattern, $log, $log_match ) ) {
4996
  $log_data = array(
4997
  'event' => 'notice',
4998
  'date' => '',
@@ -5001,37 +5333,37 @@ class SucuriScanAPI extends SucuriScanOption {
5001
  'timestamp' => 0,
5002
  'account' => $log_match[3],
5003
  'username' => 'system',
5004
- 'remote_addr' => '::1',
5005
  'message' => $log_match[4],
5006
  'file_list' => false,
5007
  'file_list_count' => 0,
5008
  );
5009
 
5010
  // Extract and fix the date and time using the Eastern time zone.
5011
- $datetime = sprintf( '%s %s EDT', $log_match[1], $log_match[2] );
5012
- $log_data['timestamp'] = strtotime( $datetime );
5013
- $log_data['datetime'] = date( 'Y-m-d H:i:s', $log_data['timestamp'] );
5014
- $log_data['date'] = date( 'Y-m-d', $log_data['timestamp'] );
5015
- $log_data['time'] = date( 'H:i:s', $log_data['timestamp'] );
5016
 
5017
  // Extract more information from the generic audit logs.
5018
- $log_data['message'] = str_replace( '<br>', '; ', $log_data['message'] );
5019
 
5020
- if ( preg_match( $generic_pattern, $log_data['message'], $log_extra ) ) {
5021
- $log_data['event'] = strtolower( $log_extra[1] );
5022
- $log_data['message'] = trim( $log_extra[3] );
5023
 
5024
  // Extract the username and remote address from the log.
5025
- if ( ! empty($log_extra[2]) ) {
5026
- $username_address = rtrim( $log_extra[2], ";\x20" );
5027
 
5028
  // Separate the username from the remote address.
5029
- if ( strpos( $username_address, ",\x20" ) !== false ) {
5030
- $usip_parts = explode( ",\x20", $username_address, 2 );
5031
 
5032
- if ( count( $usip_parts ) == 2 ) {
5033
  // Separate the username from the display name.
5034
- $log_data['username'] = preg_replace( '/^.+ \((.+)\)$/', '$1', $usip_parts[0] );
5035
  $log_data['remote_addr'] = $usip_parts[1];
5036
  }
5037
  } else {
@@ -5046,18 +5378,18 @@ class SucuriScanAPI extends SucuriScanOption {
5046
  $log_data['message']
5047
  );
5048
 
5049
- if ( preg_match( $auth_pattern, $log_data['message'], $user_match ) ) {
5050
  $log_data['username'] = $user_match[2];
5051
  }
5052
  }
5053
 
5054
  // Extract more information from the special formatted logs.
5055
- if ( preg_match( $extra_pattern, $log_data['message'], $log_extra ) ) {
5056
  $log_data['message'] = $log_extra[1];
5057
- $log_extra[2] = str_replace( ', new size', '; new size', $log_extra[2] );
5058
- $log_extra[2] = str_replace( ",\x20", ";\x20", $log_extra[2] );
5059
- $log_data['file_list'] = explode( ',', $log_extra[2] );
5060
- $log_data['file_list_count'] = count( $log_data['file_list'] );
5061
  }
5062
 
5063
  $response['body']->output_data[] = $log_data;
@@ -5075,8 +5407,9 @@ class SucuriScanAPI extends SucuriScanOption {
5075
  *
5076
  * @return array Valid audit event types with their colors.
5077
  */
5078
- public static function get_audit_event_types(){
5079
- $event_types = array(
 
5080
  'critical' => '#000000',
5081
  'debug' => '#c690ec',
5082
  'error' => '#f27d7d',
@@ -5084,8 +5417,6 @@ class SucuriScanAPI extends SucuriScanOption {
5084
  'notice' => '#428bca',
5085
  'warning' => '#f0ad4e',
5086
  );
5087
-
5088
- return $event_types;
5089
  }
5090
 
5091
  /**
@@ -5094,12 +5425,13 @@ class SucuriScanAPI extends SucuriScanOption {
5094
  * @param string $event_log Event log that will be processed.
5095
  * @return array List of parts of the event log.
5096
  */
5097
- public static function parse_multiple_entries( $event_log = '' ){
5098
- if ( preg_match( '/^(.*:\s)\(multiple entries\):\s(.+)/', $event_log, $match ) ) {
 
5099
  $event_log = array();
5100
- $event_log[] = trim( $match[1] );
5101
- $grouped_items = @explode( ',', $match[2] );
5102
- $event_log = array_merge( $event_log, $grouped_items );
5103
  }
5104
 
5105
  return $event_log;
@@ -5111,14 +5443,14 @@ class SucuriScanAPI extends SucuriScanOption {
5111
  * @param integer $lines How many lines from the log file will be retrieved.
5112
  * @return array All the information necessary to display the audit logs report.
5113
  */
5114
- public static function get_audit_report( $lines = 50 ){
5115
- $audit_logs = self::get_logs( $lines );
 
5116
 
5117
- if (
5118
- $audit_logs instanceof stdClass
5119
- && property_exists( $audit_logs, 'total_entries' )
5120
- && property_exists( $audit_logs, 'output_data' )
5121
- && ! empty($audit_logs->output_data)
5122
  ) {
5123
  // Data structure that will be returned.
5124
  $report = array(
@@ -5136,66 +5468,67 @@ class SucuriScanAPI extends SucuriScanOption {
5136
  );
5137
 
5138
  // Get a list of valid audit event types.
5139
- $event_types = self::get_audit_event_types();
5140
- foreach ( $event_types as $event => $event_color ) {
5141
- $report['events_per_type'][ $event ] = 0;
5142
- $report['event_colors'][] = sprintf( "'%s'", $event_color );
5143
  }
5144
 
5145
  // Collect information for each report chart.
5146
- foreach ( $audit_logs->output_data as $event ) {
5147
  $report['total_events'] += 1;
5148
 
5149
  // Increment the number of events for this event type.
5150
- if ( array_key_exists( $event['event'], $report['events_per_type'] ) ) {
5151
  $report['events_per_type'][ $event['event'] ] += 1;
5152
  } else {
5153
  $report['events_per_type'][ $event['event'] ] = 1;
5154
  }
5155
 
5156
  // Find the lowest datetime among the filtered events.
5157
- if (
5158
- $event['timestamp'] <= $report['start_timestamp']
5159
  || $report['start_timestamp'] === 0
5160
  ) {
5161
  $report['start_timestamp'] = $event['timestamp'];
5162
  }
5163
 
5164
  // Find the highest datetime among the filtered events.
5165
- if ( $event['timestamp'] >= $report['end_timestamp'] ) {
5166
  $report['end_timestamp'] = $event['timestamp'];
5167
  }
5168
 
5169
  // Increment the number of events generated by this user account.
5170
- if ( array_key_exists( $event['username'], $report['events_per_user'] ) ) {
5171
- $report['events_per_user'][ $event['username'] ] += 1;
 
5172
  } else {
5173
- $report['events_per_user'][ $event['username'] ] = 1;
5174
  }
5175
 
5176
  // Increment the number of events generated from this remote address.
5177
- if ( array_key_exists( $event['remote_addr'], $report['events_per_ipaddress'] ) ) {
5178
- $report['events_per_ipaddress'][ $event['remote_addr'] ] += 1;
 
5179
  } else {
5180
- $report['events_per_ipaddress'][ $event['remote_addr'] ] = 1;
5181
  }
5182
 
5183
  // Detect successful and failed user authentications.
5184
  $auth_pattern = '/^User authentication (succeeded|failed):/';
5185
 
5186
- if ( preg_match( $auth_pattern, $event['message'], $match ) ) {
5187
- if ( $match[1] == 'succeeded' ) {
5188
  $report['events_per_login']['successful'] += 1;
5189
  } else {
5190
  $report['events_per_login']['failed'] += 1;
5191
  }
5192
- } elseif ( preg_match( '/^User logged in:/', $event['message'] ) ) {
5193
  // Backward compatibility for previous user login messages.
5194
  $report['events_per_login']['successful'] += 1;
5195
  }
5196
  }
5197
 
5198
- if ( $report['total_events'] > 0 ) {
5199
  return $report;
5200
  }
5201
  }
@@ -5212,14 +5545,15 @@ class SucuriScanAPI extends SucuriScanOption {
5212
  * @param string $hashes The information gathered after the scanning of the site's files.
5213
  * @return boolean true if the hashes were stored, false otherwise.
5214
  */
5215
- public static function send_hashes( $hashes = '' ){
5216
- if ( ! empty($hashes) ) {
5217
- $response = self::api_call_wordpress( 'POST', array(
 
5218
  'a' => 'send_hashes',
5219
  'h' => $hashes,
5220
- ) );
5221
 
5222
- if ( self::handle_response( $response ) ) {
5223
  return true;
5224
  }
5225
  }
@@ -5236,16 +5570,17 @@ class SucuriScanAPI extends SucuriScanOption {
5236
  * @param boolean $api_key The CloudProxy API key.
5237
  * @return array A hash with the settings of a CloudProxy account.
5238
  */
5239
- public static function get_cloudproxy_settings( $api_key = false ){
5240
- $params = array( 'a' => 'show_settings' );
 
5241
 
5242
- if ( $api_key ) {
5243
- $params = array_merge( $params, $api_key );
5244
  }
5245
 
5246
- $response = self::api_call_cloudproxy( 'GET', $params );
5247
 
5248
- if ( self::handle_response( $response ) ) {
5249
  return $response['body']->output;
5250
  }
5251
 
@@ -5258,16 +5593,17 @@ class SucuriScanAPI extends SucuriScanOption {
5258
  * @param boolean $api_key The CloudProxy API key.
5259
  * @return string Message explaining the result of the operation.
5260
  */
5261
- public static function clear_cloudproxy_cache( $api_key = false ){
 
5262
  $params = array( 'a' => 'clear_cache' );
5263
 
5264
- if ( $api_key ) {
5265
- $params = array_merge( $params, $api_key );
5266
  }
5267
 
5268
- $response = self::api_call_cloudproxy( 'GET', $params );
5269
 
5270
- if ( self::handle_response( $response ) ) {
5271
  return $response['body'];
5272
  }
5273
 
@@ -5285,28 +5621,31 @@ class SucuriScanAPI extends SucuriScanOption {
5285
  * the logs of previous days you will need to add a new parameter to the request
5286
  * URL named "date" with format yyyy-mm-dd.
5287
  *
5288
- * @param boolean $api_key The CloudProxy API key.
5289
- * @param string $date An optional date to filter the result to a specific timespan: yyyy-mm-dd.
5290
- * @return array A list of objects with the detailed version of each request blocked by our service.
 
 
 
5291
  */
5292
- public static function get_cloudproxy_logs( $api_key = false, $date = '' ){
 
5293
  $params = array(
5294
  'a' => 'audit_trails',
5295
- 'date' => date( 'Y-m-d' ),
 
 
 
5296
  );
5297
 
5298
- if ( preg_match( '/^[0-9]{4}(\-[0-9]{2}){2}$/', $date ) ) {
5299
- $params['date'] = $date;
5300
- }
5301
-
5302
- if ( $api_key ) {
5303
- $params = array_merge( $params, $api_key );
5304
  }
5305
 
5306
- $response = self::api_call_cloudproxy( 'GET', $params );
5307
 
5308
- if ( self::handle_response( $response ) ) {
5309
- return $response['body']->output;
5310
  }
5311
 
5312
  return false;
@@ -5316,15 +5655,16 @@ class SucuriScanAPI extends SucuriScanOption {
5316
  * Scan a website through the public SiteCheck API [1] for known malware,
5317
  * blacklisting status, website errors, and out-of-date software.
5318
  *
5319
- * [1] http://sitecheck.sucuri.net/
5320
  *
5321
  * @param string $domain The clean version of the website's domain.
5322
  * @return object Serialized data of the scanning results for the site specified.
5323
  */
5324
- public static function get_sitecheck_results( $domain = '' ){
5325
- if ( ! empty($domain) ) {
5326
- $url = 'http://sitecheck.sucuri.net/';
5327
- $response = self::api_call(
 
5328
  $url,
5329
  'GET',
5330
  array(
@@ -5338,7 +5678,7 @@ class SucuriScanAPI extends SucuriScanOption {
5338
  )
5339
  );
5340
 
5341
- if ( $response ) {
5342
  return $response['body'];
5343
  }
5344
  }
@@ -5352,8 +5692,9 @@ class SucuriScanAPI extends SucuriScanOption {
5352
  * @param array $malware Array with two entries with basic malware information.
5353
  * @return array Detailed information of the malware found by SiteCheck.
5354
  */
5355
- public static function get_sitecheck_malware( $malware = array() ){
5356
- if ( count( $malware ) >= 2 ) {
 
5357
  $data_set = array(
5358
  'alert_message' => '',
5359
  'infected_url' => '',
@@ -5363,27 +5704,27 @@ class SucuriScanAPI extends SucuriScanOption {
5363
  );
5364
 
5365
  // Extract the information from the alert message.
5366
- $alert_parts = explode( ':', $malware[0], 2 );
5367
 
5368
- if ( isset($alert_parts[1]) ) {
5369
  $data_set['alert_message'] = $alert_parts[0];
5370
  $data_set['infected_url'] = $alert_parts[1];
5371
  }
5372
 
5373
  // Extract the information from the malware message.
5374
- $malware_parts = explode( "\n", $malware[1] );
5375
 
5376
- if ( isset($malware_parts[1]) ) {
5377
- if ( preg_match( '/(.+)\. Details: (.+)/', $malware_parts[0], $match ) ) {
5378
  $data_set['malware_type'] = $match[1];
5379
  $data_set['malware_docs'] = $match[2];
5380
  }
5381
 
5382
- $payload = trim( $malware_parts[1] );
5383
- $payload = html_entity_decode( $payload );
5384
 
5385
- if ( preg_match( '/<div id=\'HiddenDiv\'>(.+)<\/div>/', $payload, $match ) ) {
5386
- $data_set['malware_payload'] = trim( $match[1] );
5387
  }
5388
  }
5389
 
@@ -5399,15 +5740,16 @@ class SucuriScanAPI extends SucuriScanOption {
5399
  *
5400
  * @return array A list of the new set of keys generated by WordPress API.
5401
  */
5402
- public static function get_new_secret_keys(){
 
5403
  $pattern = self::secret_key_pattern();
5404
- $response = self::api_call( 'https://api.wordpress.org/secret-key/1.1/salt/', 'GET' );
5405
 
5406
- if ( $response && preg_match_all( $pattern, $response['body'], $match ) ) {
5407
  $new_keys = array();
5408
 
5409
- foreach ( $match[1] as $i => $value ) {
5410
- $new_keys[ $value ] = $match[3][ $i ];
5411
  }
5412
 
5413
  return $new_keys;
@@ -5419,33 +5761,32 @@ class SucuriScanAPI extends SucuriScanOption {
5419
  /**
5420
  * Retrieve a list with the checksums of the files in a specific version of WordPress.
5421
  *
5422
- * @see Release Archive http://wordpress.org/download/release-archive/
5423
  *
5424
  * @param integer $version Valid version number of the WordPress project.
5425
  * @return object Associative object with the relative filepath and the checksums of the project files.
5426
  */
5427
- public static function get_official_checksums( $version = 0 ){
5428
- $url = 'http://api.wordpress.org/core/checksums/1.0/';
 
5429
  $language = 'en_US'; /* WPLANG does not works. */
5430
- $response = self::api_call( $url, 'GET', array(
5431
  'version' => $version,
5432
  'locale' => $language,
5433
  ));
5434
 
5435
- if ( $response ) {
5436
- if ( $response['body'] instanceof stdClass ) {
5437
  $json_data = $response['body'];
5438
  } else {
5439
- $json_data = @json_decode( $response['body'] );
5440
  }
5441
 
5442
- if (
5443
- isset($json_data->checksums)
5444
- && ! empty($json_data->checksums)
5445
  ) {
5446
- if (
5447
- count( (array) $json_data->checksums ) <= 1
5448
- && property_exists( $json_data->checksums, $version )
5449
  ) {
5450
  $checksums = $json_data->checksums->{$version};
5451
  } else {
@@ -5453,7 +5794,7 @@ class SucuriScanAPI extends SucuriScanOption {
5453
  }
5454
 
5455
  // Check whether the list of file is an object.
5456
- if ( $checksums instanceof stdClass ) {
5457
  return (array) $checksums;
5458
  }
5459
  }
@@ -5469,16 +5810,17 @@ class SucuriScanAPI extends SucuriScanOption {
5469
  *
5470
  * @return array Key is the plugin file path and the value is an array of the plugin data.
5471
  */
5472
- public static function get_plugins(){
 
5473
  // Check if the cache library was loaded.
5474
- $can_cache = class_exists( 'SucuriScanCache' );
5475
 
5476
- if ( $can_cache ) {
5477
- $cache = new SucuriScanCache( 'plugindata' );
5478
- $cached_data = $cache->get( 'plugins', SUCURISCAN_GET_PLUGINS_LIFETIME, 'array' );
5479
 
5480
  // Return the previously cached results of this function.
5481
- if ( $cached_data !== false ) {
5482
  return $cached_data;
5483
  }
5484
  }
@@ -5489,7 +5831,7 @@ class SucuriScanAPI extends SucuriScanOption {
5489
  $wp_market = 'https://wordpress.org/plugins/%s/';
5490
 
5491
  // Loop through each plugin data and complement its information with more attributes.
5492
- foreach ( $plugins as $plugin_path => $plugin_data ) {
5493
  // Default values for the plugin extra attributes.
5494
  $repository = '';
5495
  $repository_name = '';
@@ -5502,26 +5844,24 @@ class SucuriScanAPI extends SucuriScanOption {
5502
  * official WordPress server it means that it is premium and is being
5503
  * distributed by an independent developer.
5504
  */
5505
- if (
5506
- isset($plugin_data['PluginURI'])
5507
- && preg_match( $pattern, $plugin_data['PluginURI'], $match )
5508
  ) {
5509
  $repository = $match[0];
5510
  $repository_name = $match[2];
5511
  $is_free_plugin = true;
5512
  } else {
5513
- if ( strpos( $plugin_path, '/' ) !== false ) {
5514
- $plugin_path_parts = explode( '/', $plugin_path, 2 );
5515
  } else {
5516
- $plugin_path_parts = explode( '.', $plugin_path, 2 );
5517
  }
5518
 
5519
- if ( isset($plugin_path_parts[0]) ) {
5520
- $possible_repository = sprintf( $wp_market, $plugin_path_parts[0] );
5521
- $resp = wp_remote_head( $possible_repository );
5522
 
5523
- if (
5524
- ! is_wp_error( $resp )
5525
  && $resp['response']['code'] == 200
5526
  ) {
5527
  $repository = $possible_repository;
@@ -5532,26 +5872,26 @@ class SucuriScanAPI extends SucuriScanOption {
5532
  }
5533
 
5534
  // Complement the plugin's information with these attributes.
5535
- $plugins[ $plugin_path ]['Repository'] = $repository;
5536
- $plugins[ $plugin_path ]['RepositoryName'] = $repository_name;
5537
- $plugins[ $plugin_path ]['InstallationPath'] = sprintf( '%s/%s', WP_PLUGIN_DIR, $repository_name );
5538
- $plugins[ $plugin_path ]['IsFreePlugin'] = $is_free_plugin;
5539
- $plugins[ $plugin_path ]['PluginType'] = ( $is_free_plugin ? 'free' : 'premium' );
5540
- $plugins[ $plugin_path ]['IsPluginActive'] = false;
5541
- $plugins[ $plugin_path ]['IsPluginInstalled'] = false;
5542
 
5543
- if ( is_plugin_active( $plugin_path ) ) {
5544
- $plugins[ $plugin_path ]['IsPluginActive'] = true;
5545
  }
5546
 
5547
- if ( is_dir( $plugins[ $plugin_path ]['InstallationPath'] ) ) {
5548
- $plugins[ $plugin_path ]['IsPluginInstalled'] = true;
5549
  }
5550
  }
5551
 
5552
- if ( $can_cache ) {
5553
  // Add the information of the plugins to the file-based cache.
5554
- $cache->add( 'plugins', $plugins );
5555
  }
5556
 
5557
  return $plugins;
@@ -5574,13 +5914,14 @@ class SucuriScanAPI extends SucuriScanOption {
5574
  * @param string $plugin Frienly name of the plugin.
5575
  * @return object Object on success, WP_Error on failure.
5576
  */
5577
- public static function get_remote_plugin_data( $plugin = '' ){
5578
- if ( ! empty($plugin) ) {
5579
- $url = sprintf( 'http://api.wordpress.org/plugins/info/1.0/%s.json', $plugin );
5580
- $response = self::api_call( $url, 'GET' );
 
5581
 
5582
- if ( $response ) {
5583
- if ( $response['body'] instanceof stdClass ) {
5584
  return $response['body'];
5585
  }
5586
  }
@@ -5594,28 +5935,28 @@ class SucuriScanAPI extends SucuriScanOption {
5594
  * the content of the file is determined by the tags defined using the site
5595
  * version specified. Only official core files are allowed to fetch.
5596
  *
5597
- * @see http://core.svn.wordpress.org/
5598
- * @see http://i18n.svn.wordpress.org/
5599
- * @see http://core.svn.wordpress.org/tags/VERSION_NUMBER/
5600
  *
5601
  * @param string $filepath Relative file path of a project core file.
5602
  * @param string $version Optional site version, default will be the global version number.
5603
  * @return string Full content of the official file retrieved, false if the file was not found.
5604
  */
5605
- public static function get_original_core_file( $filepath = '', $version = 0 ){
5606
- if ( ! empty($filepath) ) {
5607
- if ( $version == 0 ) {
 
5608
  $version = self::site_version();
5609
  }
5610
 
5611
- $url = sprintf( 'http://core.svn.wordpress.org/tags/%s/%s', $version, $filepath );
5612
- $response = self::api_call( $url, 'GET' );
5613
 
5614
- if ( $response ) {
5615
- if (
5616
- isset($response['headers']['content-length'])
5617
  && $response['headers']['content-length'] > 0
5618
- && is_string( $response['body'] )
5619
  ) {
5620
  return $response['body'];
5621
  }
@@ -5624,7 +5965,6 @@ class SucuriScanAPI extends SucuriScanOption {
5624
 
5625
  return false;
5626
  }
5627
-
5628
  }
5629
 
5630
  /**
@@ -5635,15 +5975,17 @@ class SucuriScanAPI extends SucuriScanOption {
5635
  * will be sent to the site email address (an address that can be configured in
5636
  * the settings page).
5637
  */
5638
- class SucuriScanMail extends SucuriScanOption {
 
5639
 
5640
  /**
5641
  * Check whether the email notifications will be sent in HTML or Plain/Text.
5642
  *
5643
  * @return boolean Whether the emails will be in HTML or Plain/Text.
5644
  */
5645
- public static function prettify_mails(){
5646
- return self::is_enabled( ':prettify_mails' );
 
5647
  }
5648
 
5649
  /**
@@ -5655,15 +5997,15 @@ class SucuriScanMail extends SucuriScanOption {
5655
  * @param array $data_set Optional parameter to add more information to the notification.
5656
  * @return boolean Whether the email contents were sent successfully.
5657
  */
5658
- public static function send_mail( $email = '', $subject = '', $message = '', $data_set = array() ){
 
5659
  $headers = array();
5660
- $subject = ucwords( strtolower( $subject ) );
5661
  $force = false;
5662
  $debug = false;
5663
 
5664
  // Check whether the mail will be printed in the site instead of sent.
5665
- if (
5666
- isset($data_set['Debug'])
5667
  && $data_set['Debug'] == true
5668
  ) {
5669
  $debug = true;
@@ -5671,8 +6013,7 @@ class SucuriScanMail extends SucuriScanOption {
5671
  }
5672
 
5673
  // Check whether the mail will be even if the limit per hour was reached or not.
5674
- if (
5675
- isset($data_set['Force'])
5676
  && $data_set['Force'] == true
5677
  ) {
5678
  $force = true;
@@ -5680,21 +6021,21 @@ class SucuriScanMail extends SucuriScanOption {
5680
  }
5681
 
5682
  // Check whether the email notifications will be sent in HTML or Plain/Text.
5683
- if ( self::prettify_mails() ) {
5684
  $headers = array( 'content-type: text/html' );
5685
  $data_set['PrettifyType'] = 'pretty';
5686
  } else {
5687
- $message = strip_tags( $message );
5688
  }
5689
 
5690
- if ( ! self::emails_per_hour_reached() || $force || $debug ) {
5691
- $message = self::prettify_mail( $subject, $message, $data_set );
5692
 
5693
- if ( $debug ) {
5694
  die($message);
5695
  }
5696
 
5697
- $subject = self::get_email_subject( $subject );
5698
 
5699
  /**
5700
  * WordPress uses a library named PHPMailer [1] to send emails through the
@@ -5710,17 +6051,17 @@ class SucuriScanMail extends SucuriScanOption {
5710
  *
5711
  * @var boolean
5712
  */
5713
- if ( SucuriScanOption::is_enabled( ':use_wpmail' ) ) {
5714
- $mail_sent = wp_mail( $email, $subject, $message, $headers );
5715
  } else {
5716
- $headers = implode( "\r\n", $headers );
5717
- $mail_sent = @mail( $email, $subject, $message, $headers );
5718
  }
5719
 
5720
- if ( $mail_sent ) {
5721
- $emails_sent_num = (int) self::get_option( ':emails_sent' );
5722
- self::update_option( ':emails_sent', $emails_sent_num + 1 );
5723
- self::update_option( ':last_email_at', time() );
5724
 
5725
  return true;
5726
  }
@@ -5735,24 +6076,25 @@ class SucuriScanMail extends SucuriScanOption {
5735
  * @param string $event The reason of the message that will be sent.
5736
  * @return string A text with the subject for the email alert.
5737
  */
5738
- private static function get_email_subject( $event = '' ){
5739
- $subject = self::get_option( ':email_subject' );
 
5740
 
5741
  /**
5742
  * Probably a bad value in the options table. Delete the entry from the database
5743
  * and call this function to try again, it will probably fall in an infinite
5744
  * loop, but this is the easiest way to control this procedure.
5745
  */
5746
- if ( ! $subject ) {
5747
- self::delete_option( ':email_subject' );
5748
 
5749
- return self::get_email_subject( $event );
5750
  }
5751
 
5752
- $subject = strip_tags( $subject );
5753
- $subject = str_replace( ':event', $event, $subject );
5754
- $subject = str_replace( ':domain', self::get_domain(), $subject );
5755
- $subject = str_replace( ':remoteaddr', self::get_remote_addr(), $subject );
5756
 
5757
  /**
5758
  * Extract user data from the current session.
@@ -5761,22 +6103,21 @@ class SucuriScanMail extends SucuriScanOption {
5761
  * the username and/or email address are necessary to build the email subject,
5762
  * otherwise this operation may delay the sending of the alerts.
5763
  */
5764
- if ( preg_match( '/:(username|email)/', $subject ) ) {
5765
  $user = wp_get_current_user();
5766
  $username = 'unknown';
5767
  $eaddress = 'unknown';
5768
 
5769
- if (
5770
- $user instanceof WP_User
5771
- && isset( $user->user_login )
5772
- && isset( $user->user_email )
5773
  ) {
5774
  $username = $user->user_login;
5775
  $eaddress = $user->user_email;
5776
  }
5777
 
5778
- $subject = str_replace( ':username', $user->user_login, $subject );
5779
- $subject = str_replace( ':email', $user->user_email, $subject );
5780
  }
5781
 
5782
  return $subject;
@@ -5790,33 +6131,33 @@ class SucuriScanMail extends SucuriScanOption {
5790
  * @param array $data_set Optional parameter to add more information to the notification.
5791
  * @return string The message formatted in a HTML template.
5792
  */
5793
- private static function prettify_mail( $subject = '', $message = '', $data_set = array() ){
 
5794
  $prettify_type = isset($data_set['PrettifyType']) ? $data_set['PrettifyType'] : 'simple';
5795
  $template_name = 'notification-' . $prettify_type;
5796
  $user = wp_get_current_user();
5797
  $display_name = '';
5798
 
5799
- if (
5800
- $user instanceof WP_User
5801
  && isset($user->user_login)
5802
- && ! empty($user->user_login)
5803
  ) {
5804
- $display_name = sprintf( 'User: %s (%s)', $user->display_name, $user->user_login );
5805
  }
5806
 
5807
  // Format list of items when the event has multiple entries.
5808
- if ( strpos( $message, 'multiple' ) !== false ) {
5809
- $message_parts = SucuriScanAPI::parse_multiple_entries( $message );
5810
 
5811
- if ( is_array( $message_parts ) ) {
5812
  $message = ( $prettify_type == 'pretty' ) ? $message_parts[0] . '<ul>' : $message_parts[0];
5813
  unset($message_parts[0]);
5814
 
5815
- foreach ( $message_parts as $msg_part ) {
5816
- if ( $prettify_type == 'pretty' ) {
5817
- $message .= sprintf( "<li>%s</li>\n", $msg_part );
5818
  } else {
5819
- $message .= sprintf( "- %s\n", $msg_part );
5820
  }
5821
  }
5822
 
@@ -5827,18 +6168,18 @@ class SucuriScanMail extends SucuriScanOption {
5827
  $mail_variables = array(
5828
  'TemplateTitle' => 'Sucuri Alert',
5829
  'Subject' => $subject,
5830
- 'Website' => self::get_option( 'siteurl' ),
5831
  'RemoteAddress' => self::get_remote_addr(),
5832
  'Message' => $message,
5833
  'User' => $display_name,
5834
  'Time' => SucuriScan::current_datetime(),
5835
  );
5836
 
5837
- foreach ( $data_set as $var_key => $var_value ) {
5838
  $mail_variables[ $var_key ] = $var_value;
5839
  }
5840
 
5841
- return SucuriScanTemplate::get_section( $template_name, $mail_variables );
5842
  }
5843
 
5844
  /**
@@ -5846,32 +6187,32 @@ class SucuriScanMail extends SucuriScanOption {
5846
  *
5847
  * @return boolean Whether the quota emails per hour was reached.
5848
  */
5849
- private static function emails_per_hour_reached(){
5850
- $max_per_hour = self::get_option( ':emails_per_hour' );
 
5851
 
5852
- if ( $max_per_hour != 'unlimited' ) {
5853
  // Check if we are still in that sixty minutes.
5854
  $current_time = time();
5855
- $last_email_at = self::get_option( ':last_email_at' );
5856
- $diff_time = abs( $current_time - $last_email_at );
5857
 
5858
- if ( $diff_time <= 3600 ) {
5859
  // Check if the quantity of emails sent is bigger than the configured.
5860
- $emails_sent = (int) self::get_option( ':emails_sent' );
5861
- $max_per_hour = intval( $max_per_hour );
5862
 
5863
- if ( $emails_sent >= $max_per_hour ) {
5864
  return true;
5865
  }
5866
  } else {
5867
  // Reset the counter of emails sent.
5868
- self::update_option( ':emails_sent', 0 );
5869
  }
5870
  }
5871
 
5872
  return false;
5873
  }
5874
-
5875
  }
5876
 
5877
  /**
@@ -5887,8 +6228,8 @@ class SucuriScanMail extends SucuriScanOption {
5887
  * generate a large number of "static" (unchanging) web pages in advance, or to
5888
  * produce "dynamic" web pages on demand.
5889
  */
5890
- class SucuriScanTemplate extends SucuriScanRequest {
5891
-
5892
  /**
5893
  * Replace all pseudo-variables from a string of characters.
5894
  *
@@ -5896,11 +6237,20 @@ class SucuriScanTemplate extends SucuriScanRequest {
5896
  * @param array $params List of pseudo-variables that will be replaced in the template.
5897
  * @return string The content of the template with the pseudo-variables replated.
5898
  */
5899
- private static function replace_pseudovars( $content = '', $params = array() ){
5900
- if ( is_array( $params ) ) {
5901
- foreach ( $params as $tpl_key => $tpl_value ) {
5902
- $tpl_key = '%%SUCURI.' . $tpl_key . '%%';
5903
- $content = str_replace( $tpl_key, $tpl_value, $content );
 
 
 
 
 
 
 
 
 
5904
  }
5905
 
5906
  return $content;
@@ -5912,26 +6262,27 @@ class SucuriScanTemplate extends SucuriScanRequest {
5912
  /**
5913
  * Gather and generate the information required globally by all the template files.
5914
  *
5915
- * @param array $params A hash containing the pseudo-variable name as the key and the value that will replace it.
5916
  * @return array A complementary list of pseudo-variables for the template files.
5917
  */
5918
- private static function shared_params( $params = array() ){
5919
- $params = is_array( $params ) ? $params : array();
 
5920
 
5921
  // Base parameters, required to render all the pages.
5922
- $params = self::links_and_navbar( $params );
5923
 
5924
  // Global parameters, used through out all the pages.
5925
  $params['PageTitle'] = isset($params['PageTitle']) ? '('.$params['PageTitle'].')' : '';
5926
- $params['PageNonce'] = wp_create_nonce( 'sucuriscan_page_nonce' );
5927
  $params['PageStyleClass'] = isset($params['PageStyleClass']) ? $params['PageStyleClass'] : 'base';
5928
  $params['CleanDomain'] = self::get_domain();
5929
  $params['AdminEmails'] = '';
5930
 
5931
  // Get a list of admin users for the API key generation.
5932
- if ( SucuriScanAPI::get_plugin_key() === false ) {
5933
  $admin_users = SucuriScan::get_users_for_api_key();
5934
- $params['AdminEmails'] = self::get_select_options( $admin_users );
5935
  }
5936
 
5937
  // Hide the advertisements from the layout.
@@ -5944,7 +6295,7 @@ class SucuriScanTemplate extends SucuriScanRequest {
5944
  $params['LayoutType'] = 'twocolumns';
5945
  $params['AdsVisibility'] = 'visible';
5946
  $params['ReviewNavbarButton'] = 'hidden';
5947
- $params['PageSidebarContent'] = self::get_template('bsidebar', $params, 'section');
5948
  }
5949
 
5950
  return $params;
@@ -5956,22 +6307,26 @@ class SucuriScanTemplate extends SucuriScanRequest {
5956
  * @param boolean $visible Whether the condition executed returned a positive value or not.
5957
  * @return string A string indicating the visibility of a HTML component.
5958
  */
5959
- public static function visibility( $visible = false ){
5960
- return ( $visible === true ? 'visible' : 'hidden' );
 
5961
  }
5962
 
5963
  /**
5964
  * Generate an URL pointing to the page indicated in the function and that must
5965
  * be loaded through the administrator panel.
5966
  *
5967
- * @param string $page Short name of the page that will be generated.
5968
- * @return string Full string containing the link of the page.
 
5969
  */
5970
- public static function get_url( $page = '' ){
5971
- $url_path = admin_url( 'admin.php?page=sucuriscan' );
 
 
5972
 
5973
- if ( ! empty($page) ) {
5974
- $url_path .= '_' . strtolower( $page );
5975
  }
5976
 
5977
  return $url_path;
@@ -5984,14 +6339,9 @@ class SucuriScanTemplate extends SucuriScanRequest {
5984
  * @param string $page Short name of the page that will be generated.
5985
  * @return string Full string containing the link of the page.
5986
  */
5987
- public static function get_ajax_url( $page = '' ){
5988
- $url_path = admin_url( 'admin-ajax.php?page=sucuriscan' );
5989
-
5990
- if ( ! empty($page) ) {
5991
- $url_path .= '_' . strtolower( $page );
5992
- }
5993
-
5994
- return $url_path;
5995
  }
5996
 
5997
  /**
@@ -5999,56 +6349,56 @@ class SucuriScanTemplate extends SucuriScanRequest {
5999
  * template files, this will also generate the navigation bar and detect which
6000
  * items in it are selected by the current page.
6001
  *
6002
- * @param array $params A hash containing the pseudo-variable name as the key and the value that will replace it.
6003
  * @return array A complementary list of pseudo-variables for the template files.
6004
  */
6005
- private static function links_and_navbar( $params = array() ){
 
6006
  global $sucuriscan_pages;
6007
 
6008
- $params = is_array( $params ) ? $params : array();
6009
- $sub_pages = is_array( $sucuriscan_pages ) ? $sucuriscan_pages : array();
6010
 
6011
  $params['Navbar'] = '';
6012
  $params['CurrentPageFunc'] = '';
6013
 
6014
- if ( $_page = self::get( 'page', '_page' ) ) {
6015
  $params['CurrentPageFunc'] = $_page;
6016
  }
6017
 
6018
- foreach ( $sub_pages as $sub_page_func => $sub_page_title ) {
6019
- if (
6020
- $sub_page_func == 'sucuriscan_scanner'
6021
- && SucuriScanOption::is_disabled( ':sitecheck_scanner' )
6022
  ) {
6023
  continue;
6024
  }
6025
 
6026
- $func_parts = explode( '_', $sub_page_func, 2 );
6027
 
6028
- if ( isset($func_parts[1]) ) {
6029
  $unique_name = $func_parts[1];
6030
- $pseudo_var = 'URL.' . ucwords( $unique_name );
6031
  } else {
6032
  $unique_name = '';
6033
  $pseudo_var = 'URL.Home';
6034
  }
6035
 
6036
- $params[ $pseudo_var ] = self::get_url( $unique_name );
6037
 
6038
  // Copy URL variable and create an Ajax handler.
6039
  $pseudo_var_ajax = 'Ajax' . $pseudo_var;
6040
- $params[ $pseudo_var_ajax ] = self::get_ajax_url( $unique_name );
6041
 
6042
  $navbar_item_css_class = 'nav-tab';
6043
 
6044
- if ( $params['CurrentPageFunc'] == $sub_page_func ) {
6045
- $navbar_item_css_class .= chr( 32 ) . 'nav-tab-active';
6046
  }
6047
 
6048
  $params['Navbar'] .= sprintf(
6049
- '<a class="%s" href="%s">%s</a>' . "\n",
6050
  $navbar_item_css_class,
6051
- $params[ $pseudo_var ],
6052
  $sub_page_title
6053
  );
6054
  }
@@ -6062,16 +6412,17 @@ class SucuriScanTemplate extends SucuriScanRequest {
6062
  * of the function.
6063
  *
6064
  * @param string $html The HTML content of a template file with its pseudo-variables parsed.
6065
- * @param array $params A hash containing the pseudo-variable name as the key and the value that will replace it.
6066
  * @return string The formatted HTML content of the base template.
6067
  */
6068
- public static function get_base_template( $html = '', $params = array() ){
6069
- $params = is_array( $params ) ? $params : array();
 
6070
 
6071
- $params = self::shared_params( $params );
6072
  $params['PageContent'] = $html;
6073
 
6074
- return self::get_template( 'base', $params );
6075
  }
6076
 
6077
  /**
@@ -6080,54 +6431,59 @@ class SucuriScanTemplate extends SucuriScanRequest {
6080
  * of the function.
6081
  *
6082
  * @param string $template Filename of the template that will be used to generate the page.
6083
- * @param array $params A hash containing the pseudo-variable name as the key and the value that will replace it.
6084
- * @param boolean $type Either page, section or snippet indicating the type of template that will be retrieved.
6085
- * @return string The formatted HTML page after replace all the pseudo-variables.
6086
- */
6087
- public static function get_template( $template = '', $params = array(), $type = 'page' ){
6088
- switch ( $type ) {
6089
- case 'page': /* no_break */
6090
- case 'section':
6091
- $template_path_pattern = '%s/%s/inc/tpl/%s.html.tpl';
6092
- break;
6093
- case 'snippet':
6094
- $template_path_pattern = '%s/%s/inc/tpl/%s.snippet.tpl';
6095
- break;
6096
  }
6097
 
6098
- $template_content = '';
6099
- $template_path = sprintf( $template_path_pattern, WP_PLUGIN_DIR, SUCURISCAN_PLUGIN_FOLDER, $template );
6100
- $params = is_array( $params ) ? $params : array();
 
 
 
 
6101
 
6102
- if ( file_exists( $template_path ) && is_readable( $template_path ) ) {
6103
- $template_content = @file_get_contents( $template_path );
 
6104
 
6105
- $params['SucuriURL'] = SUCURISCAN_URL;
 
6106
 
6107
- // Detect the current page URL.
6108
- if ( $_page = self::get( 'page', '_page' ) ) {
6109
- $params['CurrentURL'] = admin_url( 'admin.php?page=' . $_page );
6110
- } else {
6111
- $params['CurrentURL'] = admin_url();
6112
- }
6113
 
6114
- // Replace the global pseudo-variables in the section/snippets templates.
6115
- if (
6116
- $template == 'base'
6117
- && isset($params['PageContent'])
6118
- && preg_match( '/%%SUCURI\.(.+)%%/', $params['PageContent'] )
6119
- ) {
6120
- $params['PageContent'] = self::replace_pseudovars( $params['PageContent'], $params );
 
 
 
 
 
 
 
 
 
6121
  }
6122
 
6123
- $template_content = self::replace_pseudovars( $template_content, $params );
6124
- }
 
6125
 
6126
- if ( $template == 'base' || $type != 'page' ) {
6127
- return $template_content;
6128
  }
6129
 
6130
- return self::get_base_template( $template_content, $params );
6131
  }
6132
 
6133
  /**
@@ -6136,13 +6492,14 @@ class SucuriScanTemplate extends SucuriScanRequest {
6136
  * of the function.
6137
  *
6138
  * @param string $template Filename of the template that will be used to generate the page.
6139
- * @param array $params A hash containing the pseudo-variable name as the key and the value that will replace it.
6140
  * @return string The formatted HTML page after replace all the pseudo-variables.
6141
  */
6142
- public static function get_section( $template = '', $params = array() ){
6143
- $params = self::shared_params( $params );
 
6144
 
6145
- return self::get_template( $template, $params, 'section' );
6146
  }
6147
 
6148
  /**
@@ -6151,10 +6508,11 @@ class SucuriScanTemplate extends SucuriScanRequest {
6151
  * of the function.
6152
  *
6153
  * @param string $template Filename of the template that will be used to generate the page.
6154
- * @param array $params A hash containing the pseudo-variable name as the key and the value that will replace it.
6155
  * @return string The formatted HTML page after replace all the pseudo-variables.
6156
  */
6157
- public static function get_modal( $template = '', $params = array() ){
 
6158
  $required = array(
6159
  'Title' => 'Lorem ipsum dolor sit amet',
6160
  'Visibility' => 'visible',
@@ -6168,21 +6526,21 @@ class SucuriScanTemplate extends SucuriScanRequest {
6168
  proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>',
6169
  );
6170
 
6171
- if ( ! empty($template) && $template != 'none' ) {
6172
- $params['Content'] = self::get_section( $template );
6173
  }
6174
 
6175
- foreach ( $required as $param_name => $param_value ) {
6176
- if ( ! isset($params[ $param_name ]) ) {
6177
- $params[ $param_name ] = $param_value;
6178
  }
6179
  }
6180
 
6181
  $params['Visibility'] = 'sucuriscan-' . $params['Visibility'];
6182
  $params['Identifier'] = 'sucuriscan-' . $template . '-modal';
6183
- $params = self::shared_params( $params );
6184
 
6185
- return self::get_template( 'modalwindow', $params, 'section' );
6186
  }
6187
 
6188
  /**
@@ -6191,11 +6549,12 @@ class SucuriScanTemplate extends SucuriScanRequest {
6191
  * of the function.
6192
  *
6193
  * @param string $template Filename of the template that will be used to generate the page.
6194
- * @param array $params A hash containing the pseudo-variable name as the key and the value that will replace it.
6195
  * @return string The formatted HTML page after replace all the pseudo-variables.
6196
  */
6197
- public static function get_snippet( $template = '', $params = array() ){
6198
- return self::get_template( $template, $params, 'snippet' );
 
6199
  }
6200
 
6201
  /**
@@ -6205,21 +6564,16 @@ class SucuriScanTemplate extends SucuriScanRequest {
6205
  * @param string $selected_val Value of the option that will be selected by default.
6206
  * @return string Option list for a select form field.
6207
  */
6208
- public static function get_select_options( $allowed_values = array(), $selected_val = '' ){
 
6209
  $options = '';
6210
 
6211
- foreach ( $allowed_values as $option_name => $option_label ) {
6212
- $selected_str = '';
6213
-
6214
- if ( $option_name == $selected_val ) {
6215
- $selected_str = 'selected="selected"';
6216
- }
6217
-
6218
  $options .= sprintf(
6219
- "<option value=\"%s\" %s>%s</option>\n",
6220
- SucuriScan::escape( $option_name ),
6221
- SucuriScan::escape( $selected_str ),
6222
- SucuriScan::escape( $option_label )
6223
  );
6224
  }
6225
 
@@ -6231,10 +6585,11 @@ class SucuriScanTemplate extends SucuriScanRequest {
6231
  *
6232
  * @return integer Page number of the link clicked in a pagination.
6233
  */
6234
- public static function get_page_number(){
6235
- $paged = self::get( 'paged', '[0-9]{1,5}' );
 
6236
 
6237
- return ( $paged ? intval( $paged ) : 1 );
6238
  }
6239
 
6240
  /**
@@ -6245,36 +6600,40 @@ class SucuriScanTemplate extends SucuriScanRequest {
6245
  * @param integer $max_per_page Maximum number of items that will be shown per page.
6246
  * @return string HTML code for a pagination generated using the provided data.
6247
  */
6248
- public static function get_pagination( $base_url = '', $total_items = 0, $max_per_page = 1 ){
 
6249
  // Calculate the number of links for the pagination.
6250
  $html_links = '';
6251
- $page_number = self::get_page_number();
6252
- $max_pages = ceil( $total_items / $max_per_page );
6253
  $extra_url = '';
6254
 
6255
  // Fix for inline anchor URLs.
6256
- if ( preg_match( '/^(.+)(#.+)$/', $base_url, $match ) ) {
6257
  $base_url = $match[1];
6258
  $extra_url = $match[2];
6259
  }
6260
 
6261
  // Generate the HTML links for the pagination.
6262
- for ( $j = 1; $j <= $max_pages; $j++ ) {
6263
  $link_class = 'sucuriscan-pagination-link';
6264
 
6265
- if ( $page_number == $j ) {
6266
- $link_class .= chr( 32 ) . 'sucuriscan-pagination-active';
6267
  }
6268
 
6269
  $html_links .= sprintf(
6270
  '<li><a href="%s&paged=%d%s" class="%s">%s</a></li>',
6271
- $base_url, $j, $extra_url, $link_class, $j
 
 
 
 
6272
  );
6273
  }
6274
 
6275
  return $html_links;
6276
  }
6277
-
6278
  }
6279
 
6280
  /**
@@ -6286,7 +6645,8 @@ class SucuriScanTemplate extends SucuriScanRequest {
6286
  * content is then submitted to the remote server and it is stored for future
6287
  * analysis.
6288
  */
6289
- class SucuriScanFSScanner extends SucuriScan {
 
6290
 
6291
  /**
6292
  * Retrieve the last time when the filesystem scan was ran.
@@ -6294,19 +6654,20 @@ class SucuriScanFSScanner extends SucuriScan {
6294
  * @param boolean $format Whether the timestamp must be formatted as date/time or not.
6295
  * @return string The timestamp of the runtime, or an string with the date/time.
6296
  */
6297
- public static function get_filesystem_runtime( $format = false ){
6298
- $runtime = SucuriScanOption::get_option( ':runtime' );
 
6299
 
6300
- if ( $runtime > 0 ) {
6301
- if ( $format ) {
6302
- return SucuriScan::datetime( $runtime );
6303
  }
6304
 
6305
  return $runtime;
6306
  }
6307
 
6308
- if ( $format ) {
6309
- return '<em>Unknown</em>';
6310
  }
6311
 
6312
  return false;
@@ -6320,8 +6681,9 @@ class SucuriScanFSScanner extends SucuriScan {
6320
  *
6321
  * @return boolean Whether the feature to ignore files is enabled or not.
6322
  */
6323
- public static function will_ignore_scanning(){
6324
- return SucuriScanOption::is_enabled( ':ignore_scanning' );
 
6325
  }
6326
 
6327
  /**
@@ -6330,18 +6692,19 @@ class SucuriScanFSScanner extends SucuriScan {
6330
  * @param string $directory_path The (full) absolute path of a directory.
6331
  * @return boolean TRUE if the directory path was added to the list, FALSE otherwise.
6332
  */
6333
- public static function ignore_directory( $directory_path = '' ){
6334
- $cache = new SucuriScanCache( 'ignorescanning' );
 
6335
 
6336
  // Use the checksum of the directory path as the cache key.
6337
- $cache_key = md5( $directory_path );
6338
- $resource_type = SucuriScanFileInfo::get_resource_type( $directory_path );
6339
  $cache_value = array(
6340
  'directory_path' => $directory_path,
6341
  'ignored_at' => self::local_time(),
6342
  'resource_type' => $resource_type,
6343
  );
6344
- $cached = $cache->add( $cache_key, $cache_value );
6345
 
6346
  return $cached;
6347
  }
@@ -6352,12 +6715,13 @@ class SucuriScanFSScanner extends SucuriScan {
6352
  * @param string $directory_path The (full) absolute path of a directory.
6353
  * @return boolean TRUE if the directory path was removed to the list, FALSE otherwise.
6354
  */
6355
- public static function unignore_directory( $directory_path = '' ){
6356
- $cache = new SucuriScanCache( 'ignorescanning' );
 
6357
 
6358
  // Use the checksum of the directory path as the cache key.
6359
- $cache_key = md5( $directory_path );
6360
- $removed = $cache->delete( $cache_key );
6361
 
6362
  return $removed;
6363
  }
@@ -6383,7 +6747,8 @@ class SucuriScanFSScanner extends SucuriScan {
6383
  *
6384
  * @return array List of ignored directory paths.
6385
  */
6386
- public static function get_ignored_directories(){
 
6387
  $response = array(
6388
  'raw' => array(),
6389
  'checksums' => array(),
@@ -6391,17 +6756,16 @@ class SucuriScanFSScanner extends SucuriScan {
6391
  'ignored_at_list' => array(),
6392
  );
6393
 
6394
- $cache = new SucuriScanCache( 'ignorescanning' );
6395
  $cache_lifetime = 0; // It is not necessary to expire this cache.
6396
- $ignored_directories = $cache->get_all( $cache_lifetime, 'array' );
6397
 
6398
- if ( $ignored_directories ) {
6399
  $response['raw'] = $ignored_directories;
6400
 
6401
- foreach ( $ignored_directories as $checksum => $data ) {
6402
- if (
6403
- array_key_exists( 'directory_path', $data )
6404
- && array_key_exists( 'ignored_at', $data )
6405
  ) {
6406
  $response['checksums'][] = $checksum;
6407
  $response['directories'][] = $data['directory_path'];
@@ -6423,7 +6787,8 @@ class SucuriScanFSScanner extends SucuriScan {
6423
  *
6424
  * @return array List of ignored and not ignored directories.
6425
  */
6426
- public static function get_ignored_directories_live(){
 
6427
  $response = array(
6428
  'is_ignored' => array(),
6429
  'is_not_ignored' => array(),
@@ -6432,7 +6797,7 @@ class SucuriScanFSScanner extends SucuriScan {
6432
  // Get the ignored directories from the cache.
6433
  $ignored_directories = self::get_ignored_directories();
6434
 
6435
- if ( $ignored_directories ) {
6436
  $response['is_ignored'] = $ignored_directories['raw'];
6437
  }
6438
 
@@ -6440,10 +6805,10 @@ class SucuriScanFSScanner extends SucuriScan {
6440
  $file_info = new SucuriScanFileInfo();
6441
  $file_info->ignore_files = true;
6442
  $file_info->ignore_directories = true;
6443
- $file_info->scan_interface = SucuriScanOption::get_option( ':scan_interface' );
6444
- $directory_list = $file_info->get_diretories_only( ABSPATH );
6445
 
6446
- if ( $directory_list ) {
6447
  $response['is_not_ignored'] = $directory_list;
6448
  }
6449
 
@@ -6456,7 +6821,8 @@ class SucuriScanFSScanner extends SucuriScan {
6456
  * @param array $error_logs The content of an error log file, or an array with the lines.
6457
  * @return array List of valid error logs with their attributes separated.
6458
  */
6459
- public static function parse_error_logs( $error_logs = array() ){
 
6460
  $logs_arr = array();
6461
  $pattern = '/^'
6462
  . '(\[(\S+) ([0-9:]{5,8})( \S+)?\] )?' // Detect date, time, and timezone.
@@ -6465,16 +6831,16 @@ class SucuriScanFSScanner extends SucuriScan {
6465
  . '(:| on line )([0-9]+)' // Detect line number.
6466
  . '$/';
6467
 
6468
- if ( is_string( $error_logs ) ) {
6469
- $error_logs = explode( "\n", $error_logs );
6470
  }
6471
 
6472
- foreach ( (array) $error_logs as $line ) {
6473
- if ( ! is_string( $line ) || empty($line) ) {
6474
  continue;
6475
  }
6476
 
6477
- if ( preg_match( $pattern, $line, $match ) ) {
6478
  $data_set = array(
6479
  'date' => '',
6480
  'time' => '',
@@ -6491,25 +6857,25 @@ class SucuriScanFSScanner extends SucuriScan {
6491
  // Basic attributes from the scrapping.
6492
  $data_set['date'] = $match[2];
6493
  $data_set['time'] = $match[3];
6494
- $data_set['time_zone'] = trim( $match[4] );
6495
- $data_set['error_type'] = trim( $match[6] );
6496
- $data_set['error_message'] = trim( $match[7] );
6497
- $data_set['file_path'] = trim( $match[8] );
6498
  $data_set['line_number'] = (int) $match[10];
6499
 
6500
  // Additional data from the attributes.
6501
- if ( $data_set['date'] ) {
6502
  $data_set['date_time'] = $data_set['date']
6503
  . "\x20" . $data_set['time']
6504
  . "\x20" . $data_set['time_zone'];
6505
- $data_set['timestamp'] = strtotime( $data_set['date_time'] );
6506
  }
6507
 
6508
- if ( $data_set['error_type'] ) {
6509
  $valid_types = array( 'warning', 'notice', 'error' );
6510
 
6511
- foreach ( $valid_types as $valid_type ) {
6512
- if ( stripos( $data_set['error_type'], $valid_type ) !== false ) {
6513
  $data_set['error_code'] = $valid_type;
6514
  break;
6515
  }
@@ -6522,7 +6888,6 @@ class SucuriScanFSScanner extends SucuriScan {
6522
 
6523
  return $logs_arr;
6524
  }
6525
-
6526
  }
6527
 
6528
  /**
@@ -6537,43 +6902,37 @@ class SucuriScanFSScanner extends SucuriScan {
6537
  *
6538
  * @see https://core.trac.wordpress.org/ticket/23216
6539
  */
6540
- class SucuriScanHeartbeat extends SucuriScanOption {
 
6541
 
6542
  /**
6543
  * Stop execution of the heartbeat API in certain parts of the site.
6544
  *
6545
  * @return void
6546
  */
6547
- public static function register_script(){
 
6548
  global $pagenow;
6549
 
6550
- $status = SucuriScanOption::get_option( ':heartbeat' );
6551
 
6552
  // Enable heartbeat everywhere.
6553
- if ( $status == 'enabled' ) {
6554
  /* Do nothing */
6555
- }
6556
-
6557
- // Disable heartbeat everywhere.
6558
- elseif ( $status == 'disabled' ) {
6559
- wp_deregister_script( 'heartbeat' );
6560
- }
6561
-
6562
- // Disable heartbeat only on the dashboard and home pages.
6563
- elseif (
6564
- $status == 'dashboard'
6565
  && $pagenow == 'index.php'
6566
  ) {
6567
- wp_deregister_script( 'heartbeat' );
6568
- }
6569
-
6570
- // Disable heartbeat everywhere except in post edition.
6571
- elseif (
6572
- $status == 'addpost'
6573
  && $pagenow != 'post.php'
6574
  && $pagenow != 'post-new.php'
6575
  ) {
6576
- wp_deregister_script( 'heartbeat' );
6577
  }
6578
  }
6579
 
@@ -6587,12 +6946,13 @@ class SucuriScanHeartbeat extends SucuriScanOption {
6587
  * @param array $settings Heartbeat settings.
6588
  * @return array Updated version of the heartbeat settings.
6589
  */
6590
- public static function update_settings( $settings = array() ){
6591
- $pulse = SucuriScanOption::get_option( ':heartbeat_pulse' );
6592
- $autostart = SucuriScanOption::get_option( ':heartbeat_autostart' );
 
6593
 
6594
- if ( $pulse < 15 || $pulse > 60 ) {
6595
- SucuriScanOption::delete_option( ':heartbeat_pulse' );
6596
  $pulse = 15;
6597
  }
6598
 
@@ -6610,17 +6970,17 @@ class SucuriScanHeartbeat extends SucuriScanOption {
6610
  * @param string $screen_id Identifier of the screen the heartbeat occurred on.
6611
  * @return array Response with new data.
6612
  */
6613
- public static function respond_to_received( $response = array(), $data = array(), $screen_id = '' ){
6614
- $interval = SucuriScanOption::get_option( ':heartbeat_interval' );
 
6615
 
6616
- if (
6617
- $interval == 'slow'
6618
  || $interval == 'fast'
6619
  || $interval == 'standard'
6620
  ) {
6621
  $response['heartbeat_interval'] = $interval;
6622
  } else {
6623
- SucuriScanOption::delete_option( ':heartbeat_interval' );
6624
  }
6625
 
6626
  return $response;
@@ -6633,7 +6993,8 @@ class SucuriScanHeartbeat extends SucuriScanOption {
6633
  * @param string $screen_id Identifier of the screen the heartbeat occurred on.
6634
  * @return array Response with new data.
6635
  */
6636
- public static function respond_to_send( $response = array(), $screen_id = '' ){
 
6637
  return $response;
6638
  }
6639
 
@@ -6642,7 +7003,8 @@ class SucuriScanHeartbeat extends SucuriScanOption {
6642
  *
6643
  * @return array Allowed values for the heartbeat status.
6644
  */
6645
- public static function statuses_allowed(){
 
6646
  return array(
6647
  'enabled' => 'Enable everywhere',
6648
  'disabled' => 'Disable everywhere',
@@ -6656,7 +7018,8 @@ class SucuriScanHeartbeat extends SucuriScanOption {
6656
  *
6657
  * @return array Allowed values for the heartbeat intervals.
6658
  */
6659
- public static function intervals_allowed(){
 
6660
  return array(
6661
  'slow' => 'Slow interval',
6662
  'fast' => 'Fast interval',
@@ -6669,16 +7032,16 @@ class SucuriScanHeartbeat extends SucuriScanOption {
6669
  *
6670
  * @return array Allowed values for the heartbeat pulses.
6671
  */
6672
- public static function pulses_allowed(){
 
6673
  $pulses = array();
6674
 
6675
- for ( $i = 15; $i <= 60; $i++ ) {
6676
- $pulses[ $i ] = sprintf( 'Run every %d seconds', $i );
6677
  }
6678
 
6679
  return $pulses;
6680
  }
6681
-
6682
  }
6683
 
6684
  /**
@@ -6690,8 +7053,8 @@ class SucuriScanHeartbeat extends SucuriScanOption {
6690
  * sites with old versions of the premium plugin (that was deprecated at
6691
  * July/2014).
6692
  */
6693
- class SucuriScanInterface {
6694
-
6695
  /**
6696
  * Initialization code for the plugin.
6697
  *
@@ -6702,8 +7065,11 @@ class SucuriScanInterface {
6702
  *
6703
  * @return void
6704
  */
6705
- public static function initialize(){
6706
- if ( SucuriScan::is_behind_cloudproxy() ) {
 
 
 
6707
  $_SERVER['SUCURIREAL_REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
6708
  $_SERVER['REMOTE_ADDR'] = SucuriScan::get_remote_addr();
6709
  }
@@ -6715,23 +7081,24 @@ class SucuriScanInterface {
6715
  *
6716
  * @return void
6717
  */
6718
- public static function enqueue_scripts(){
 
6719
  $asset_version = '';
6720
 
6721
- if ( strlen( SUCURISCAN_PLUGIN_CHECKSUM ) >= 7 ) {
6722
- $asset_version = substr( SUCURISCAN_PLUGIN_CHECKSUM, 0, 7 );
6723
  }
6724
 
6725
- wp_register_style( 'sucuriscan', SUCURISCAN_URL . '/inc/css/sucuri-scanner.min.css', array(), $asset_version );
6726
- wp_register_script( 'sucuriscan', SUCURISCAN_URL . '/inc/js/sucuri-scanner.min.js', array(), $asset_version );
6727
- wp_enqueue_style( 'sucuriscan' );
6728
- wp_enqueue_script( 'sucuriscan' );
6729
 
6730
- if ( SucuriScanRequest::get( 'page', 'sucuriscan' ) !== false ) {
6731
- wp_register_script( 'sucuriscan2', SUCURISCAN_URL . '/inc/js/d3.min.js', array(), $asset_version );
6732
- wp_register_script( 'sucuriscan3', SUCURISCAN_URL . '/inc/js/c3.min.js', array(), $asset_version );
6733
- wp_enqueue_script( 'sucuriscan2' );
6734
- wp_enqueue_script( 'sucuriscan3' );
6735
  }
6736
  }
6737
 
@@ -6740,14 +7107,14 @@ class SucuriScanInterface {
6740
  *
6741
  * @return void
6742
  */
6743
- public static function add_interface_menu(){
 
6744
  global $sucuriscan_pages;
6745
 
6746
- if (
6747
- function_exists( 'add_menu_page' )
6748
  && $sucuriscan_pages
6749
- && is_array( $sucuriscan_pages )
6750
- && array_key_exists( 'sucuriscan', $sucuriscan_pages )
6751
  ) {
6752
  // Add main menu link.
6753
  add_menu_page(
@@ -6759,10 +7126,9 @@ class SucuriScanInterface {
6759
  SUCURISCAN_URL . '/inc/images/menu-icon.png'
6760
  );
6761
 
6762
- foreach ( $sucuriscan_pages as $sub_page_func => $sub_page_title ) {
6763
- if (
6764
- $sub_page_func == 'sucuriscan_scanner'
6765
- && SucuriScanOption::is_disabled( ':sitecheck_scanner' )
6766
  ) {
6767
  continue;
6768
  }
@@ -6789,8 +7155,9 @@ class SucuriScanInterface {
6789
  *
6790
  * @return void
6791
  */
6792
- public static function handle_old_plugins(){
6793
- if ( class_exists( 'SucuriScanFileInfo' ) ) {
 
6794
  $file_info = new SucuriScanFileInfo();
6795
  $file_info->ignore_files = false;
6796
  $file_info->ignore_directories = false;
@@ -6800,15 +7167,15 @@ class SucuriScanInterface {
6800
  'sucuri-cloudproxy-waf/cloudproxy.php',
6801
  );
6802
 
6803
- foreach ( $plugins as $plugin ) {
6804
- $plugin_directory = dirname( WP_PLUGIN_DIR . '/' . $plugin );
6805
 
6806
- if ( file_exists( $plugin_directory ) ) {
6807
- if ( is_plugin_active( $plugin ) ) {
6808
- deactivate_plugins( $plugin );
6809
  }
6810
 
6811
- $plugin_removed = $file_info->remove_directory_tree( $plugin_directory );
6812
  }
6813
  }
6814
  }
@@ -6820,14 +7187,18 @@ class SucuriScanInterface {
6820
  *
6821
  * @return void
6822
  */
6823
- public static function create_datastore_folder(){
 
6824
  $directory = SucuriScan::datastore_folder_path();
6825
 
6826
  if (!file_exists($directory)) {
6827
  @mkdir($directory, 0755, true);
6828
  }
6829
 
6830
- if (file_exists($directory)) {
 
 
 
6831
  // Create last-logins datastore file.
6832
  sucuriscan_lastlogins_datastore_exists();
6833
 
@@ -6848,7 +7219,7 @@ class SucuriScanInterface {
6848
  SucuriScanOption::delete_option(':datastore_path');
6849
  SucuriScanInterface::error(
6850
  'Data folder does not exists and could not be created. Try to <a href="' .
6851
- SucuriScanTemplate::get_url('settings') . '">click this link</a> to see
6852
  if the plugin is able to fix this error automatically, if this message
6853
  reappears you will need to either change the location of the directory from
6854
  the plugin general settings page or create this directory manually and give
@@ -6862,13 +7233,14 @@ class SucuriScanInterface {
6862
  *
6863
  * @return void
6864
  */
6865
- public static function check_permissions(){
6866
- if (
6867
- ! function_exists( 'current_user_can' )
6868
- || ! current_user_can( 'manage_options' )
6869
  ) {
6870
- $page = SucuriScanRequest::get( 'page', '_page' );
6871
- wp_die( __( 'Access denied by <b>Sucuri</b> to see <code>' . $page . '</code>' ) );
 
6872
  }
6873
  }
6874
 
@@ -6879,13 +7251,14 @@ class SucuriScanInterface {
6879
  *
6880
  * @return boolean Either TRUE or FALSE if the nonce is valid or not respectively.
6881
  */
6882
- public static function check_nonce(){
6883
- if ( ! empty($_POST) ) {
 
6884
  $nonce_name = 'sucuriscan_page_nonce';
6885
- $nonce_value = SucuriScanRequest::post( $nonce_name, '_nonce' );
6886
 
6887
- if ( ! $nonce_value || ! wp_verify_nonce( $nonce_value, $nonce_name ) ) {
6888
- wp_die( __( 'WordPress Nonce verification failed, try again going back and checking the form.' ) );
6889
 
6890
  return false;
6891
  }
@@ -6901,7 +7274,8 @@ class SucuriScanInterface {
6901
  * @param string $message The message that will be printed in the alert.
6902
  * @return void
6903
  */
6904
- private static function admin_notice( $type = 'updated', $message = '' ){
 
6905
  $display_notice = true;
6906
 
6907
  /**
@@ -6913,24 +7287,24 @@ class SucuriScanInterface {
6913
  * user authentication process is executed may cause a "headers already sent"
6914
  * error.
6915
  */
6916
- if (
6917
- ! empty( $_POST )
6918
- && SucuriScanRequest::post( 'log' )
6919
- && SucuriScanRequest::post( 'pwd' )
6920
- && SucuriScanRequest::post( 'wp-submit' )
6921
  ) {
6922
  $display_notice = false;
6923
  }
6924
 
6925
  // Display the HTML notice to the current user.
6926
- if ( $display_notice === true ) {
6927
- $alert_id = rand( 100, 999 );
6928
- if ( ! empty($message) ): ?>
6929
- <div id="sucuriscan-alert-<?php echo $alert_id; ?>" class="<?php echo $type; ?> sucuriscan-alert sucuriscan-alert-<?php echo $type; ?>">
6930
- <a href="javascript:void(0)" class="close" onclick="sucuriscan_alert_close('<?php echo $alert_id; ?>')">&times;</a>
6931
- <p><?php _e( $message ); ?></p>
6932
- </div>
6933
- <?php endif;
 
6934
  }
6935
  }
6936
 
@@ -6940,8 +7314,9 @@ class SucuriScanInterface {
6940
  * @param string $error_msg The message that will be printed in the alert.
6941
  * @return void
6942
  */
6943
- public static function error( $error_msg = '' ){
6944
- self::admin_notice( 'error', '<b>Sucuri:</b> ' . $error_msg );
 
6945
  }
6946
 
6947
  /**
@@ -6950,8 +7325,9 @@ class SucuriScanInterface {
6950
  * @param string $info_msg The message that will be printed in the alert.
6951
  * @return void
6952
  */
6953
- public static function info( $info_msg = '' ){
6954
- self::admin_notice( 'updated', '<b>Sucuri:</b> ' . $info_msg );
 
6955
  }
6956
 
6957
  /**
@@ -6961,22 +7337,22 @@ class SucuriScanInterface {
6961
  *
6962
  * @return void
6963
  */
6964
- public static function setup_notice(){
6965
- if (
6966
- current_user_can( 'manage_options' )
6967
  && SucuriScan::no_notices_here() === false
6968
- && ! SucuriScanAPI::get_plugin_key()
6969
- && SucuriScanRequest::post( ':plugin_api_key' ) === false
6970
- && SucuriScanRequest::post( ':recover_key' ) === false
6971
- && ! SucuriScanRequest::post( ':manual_api_key' )
6972
  ) {
6973
  if (SucuriScanRequest::get(':dismiss_setup') !== false) {
6974
  SucuriScanOption::update_option(':dismiss_setup', 'enabled');
6975
  } elseif (SucuriScanOption::is_enabled(':dismiss_setup')) {
6976
  /* Do not display API key generation form. */
6977
  } else {
6978
- echo SucuriScanTemplate::get_section('setup-notice');
6979
- echo SucuriScanTemplate::get_modal('setup-form', array(
6980
  'Visibility' => 'hidden',
6981
  'Title' => 'Sucuri API key generation',
6982
  'CssClass' => 'sucuriscan-setup-instructions',
@@ -6984,7 +7360,102 @@ class SucuriScanInterface {
6984
  }
6985
  }
6986
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6987
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6988
  }
6989
 
6990
  /**
@@ -6994,33 +7465,49 @@ class SucuriScanInterface {
6994
  *
6995
  * @return void
6996
  */
6997
- function sucuriscan_scanner_page(){
 
6998
  SucuriScanInterface::check_permissions();
6999
 
7000
- $template_variables = array();
7001
- $cache = new SucuriScanCache( 'sitecheck' );
7002
- $scan_results = $cache->get( 'scan_results', SUCURISCAN_SITECHECK_LIFETIME, 'array' );
7003
- $report_results = (bool) ( $scan_results && ! empty($scan_results) );
7004
 
7005
- if (
7006
- SucuriScanInterface::check_nonce()
7007
- && SucuriScanRequest::post( ':malware_scan', '1' )
7008
  ) {
7009
  $report_results = true;
7010
  }
7011
 
7012
- if ( $report_results === true ) {
7013
  $template_name = 'malwarescan-results';
7014
- $template_variables = sucuriscan_sitecheck_info( $scan_results );
7015
- $template_variables['PageTitle'] = 'Malware Scan';
7016
- $template_variables['PageStyleClass'] = 'scanner-results';
7017
  } else {
7018
  $template_name = 'malwarescan';
7019
- $template_variables['PageTitle'] = 'Malware Scan';
7020
- $template_variables['PageStyleClass'] = 'scanner-loading';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7021
  }
7022
 
7023
- echo SucuriScanTemplate::get_template( $template_name, $template_variables );
7024
  }
7025
 
7026
  /**
@@ -7029,9 +7516,10 @@ function sucuriscan_scanner_page(){
7029
  * @param array $scan_results Array with information of the scanning.
7030
  * @return array Array with psuedo-variables to build the template.
7031
  */
7032
- function sucuriscan_sitecheck_info( $scan_results = array() ){
 
7033
  $clean_domain = SucuriScan::get_domain();
7034
- $template_variables = array(
7035
  'ScannedDomainName' => $clean_domain,
7036
  'ScannerResults.CssClass' => '',
7037
  'ScannerResults.Content' => '',
@@ -7047,47 +7535,49 @@ function sucuriscan_sitecheck_info( $scan_results = array() ){
7047
  );
7048
 
7049
  // If the results are not cached, then request a new scan and store in cache.
7050
- if ( $scan_results === false ) {
7051
- $scan_results = SucuriScanAPI::get_sitecheck_results( $clean_domain );
7052
 
7053
  // Check for error messages in the request's response.
7054
- if ( is_string( $scan_results ) ) {
7055
- if ( preg_match( '/^ERROR:(.*)/', $scan_results, $error_m ) ) {
7056
- SucuriScanInterface::error( 'The site <code>' . $clean_domain . '</code> was not scanned: ' . $error_m[1] );
 
 
 
7057
  } else {
7058
- SucuriScanInterface::error( 'SiteCheck error: ' . $scan_results );
7059
  }
7060
  } else {
7061
- $cache = new SucuriScanCache( 'sitecheck' );
7062
- $results_were_cached = $cache->add( 'scan_results', $scan_results );
7063
 
7064
- if ( ! $results_were_cached ) {
7065
- SucuriScanInterface::error( 'Could not cache the malware scan results.' );
7066
  }
7067
  }
7068
  }
7069
 
7070
- if ( is_array( $scan_results ) && ! empty($scan_results) ) {
7071
  // Increase the malware scan counter.
7072
- $sitecheck_counter = (int) SucuriScanOption::get_option( ':sitecheck_counter' );
7073
- SucuriScanOption::update_option( ':sitecheck_counter', $sitecheck_counter + 1 );
7074
  add_thickbox();
7075
 
7076
- $template_variables = sucuriscan_sitecheck_scanner_results( $scan_results, $template_variables );
7077
- $template_variables = sucuriscan_sitecheck_website_details( $scan_results, $template_variables );
7078
- $template_variables = sucuriscan_sitecheck_website_links( $scan_results, $template_variables );
7079
- $template_variables = sucuriscan_sitecheck_blacklist_status( $scan_results, $template_variables );
7080
- $template_variables = sucuriscan_sitecheck_modified_files( $scan_results, $template_variables );
7081
 
7082
- if (
7083
- isset($scan_results['MALWARE']['WARN'])
7084
  || isset($scan_results['BLACKLIST']['WARN'])
7085
  ) {
7086
- $template_variables['SignupButtonVisibility'] = 'visible';
7087
  }
7088
  }
7089
 
7090
- return $template_variables;
7091
  }
7092
 
7093
  /**
@@ -7095,11 +7585,12 @@ function sucuriscan_sitecheck_info( $scan_results = array() ){
7095
  * the HTML code to display the information in the malware scan page inside the
7096
  * remote scanner results tab.
7097
  *
7098
- * @param array $scan_results Array with information of the scanning.
7099
- * @param array $template_variables Array with psuedo-variables to build the template.
7100
- * @return array Array with psuedo-variables to build the template including extra information.
7101
  */
7102
- function sucuriscan_sitecheck_scanner_results( $scan_results = false, $template_variables = array() ){
 
7103
  $secvars = array(
7104
  'CacheLifeTime' => SUCURISCAN_SITECHECK_LIFETIME,
7105
  'WebsiteStatus' => 'Site status unknown',
@@ -7108,33 +7599,36 @@ function sucuriscan_sitecheck_scanner_results( $scan_results = false, $template_
7108
  'MalwarePayloadList' => '',
7109
  );
7110
 
7111
- if ( isset($scan_results['MALWARE']['WARN']) ) {
7112
- $template_variables['ScannerResults.CssClass'] = 'sucuriscan-red-tab';
7113
  $secvars['WebsiteStatus'] = 'Site compromised (malware was identified)';
7114
  $secvars['NoMalwareRowVisibility'] = 'hidden';
7115
  $secvars['FixButtonVisibility'] = 'visible';
7116
 
7117
- foreach ( $scan_results['MALWARE']['WARN'] as $key => $malres ) {
7118
- $malres = SucuriScanAPI::get_sitecheck_malware( $malres );
7119
-
7120
- if ( $malres !== false ) {
7121
- $secvars['MalwarePayloadList'] .= SucuriScanTemplate::get_snippet( 'malwarescan-resmalware', array(
7122
- 'MalwareKey' => $key,
7123
- 'MalwareDocs' => SucuriScan::escape( $malres['malware_docs'] ),
7124
- 'MalwareType' => SucuriScan::escape( $malres['malware_type'] ),
7125
- 'MalwarePayload' => SucuriScan::escape( $malres['malware_payload'] ),
7126
- 'AlertMessage' => SucuriScan::escape( $malres['alert_message'] ),
7127
- 'InfectedUrl' => SucuriScan::escape( $malres['infected_url'] ),
7128
- ) );
 
 
 
7129
  }
7130
  }
7131
  } else {
7132
  $secvars['WebsiteStatus'] = 'Site clean (no malware was identified)';
7133
  }
7134
 
7135
- $template_variables['ScannerResults.Content'] = SucuriScanTemplate::get_section( 'malwarescan-resmalware', $secvars );
7136
 
7137
- return $template_variables;
7138
  }
7139
 
7140
  /**
@@ -7142,15 +7636,16 @@ function sucuriscan_sitecheck_scanner_results( $scan_results = false, $template_
7142
  * the HTML code to display the information in the malware scan page inside the
7143
  * website details tab.
7144
  *
7145
- * @param array $scan_results Array with information of the scanning.
7146
- * @param array $template_variables Array with psuedo-variables to build the template.
7147
- * @return array Array with psuedo-variables to build the template including extra information.
7148
  */
7149
- function sucuriscan_sitecheck_website_details( $scan_results = false, $template_variables = array() ){
 
7150
  $secvars = array(
7151
  'UpdateWebsiteButtonVisibility' => 'hidden',
7152
  'VersionNumberOfTheUpdate' => '0.0',
7153
- 'AdminUrlForUpdates' => admin_url( 'update-core.php' ),
7154
  'GenericInformationList' => '',
7155
  'NoAppDetailsVisibility' => 'visible',
7156
  'ApplicationDetailsList' => '',
@@ -7161,11 +7656,10 @@ function sucuriscan_sitecheck_website_details( $scan_results = false, $template_
7161
  );
7162
 
7163
  // Check whether this WordPress installation needs an update.
7164
- if ( function_exists( 'get_core_updates' ) ) {
7165
  $site_updates = get_core_updates();
7166
 
7167
- if (
7168
- ! is_array( $site_updates )
7169
  || empty($site_updates)
7170
  || $site_updates[0]->response == 'latest'
7171
  ) {
@@ -7173,22 +7667,21 @@ function sucuriscan_sitecheck_website_details( $scan_results = false, $template_
7173
  }
7174
  }
7175
 
7176
- if (
7177
- isset($scan_results['OUTDATEDSCAN'])
7178
  || isset($scan_results['RECOMMENDATIONS'])
7179
  ) {
7180
- $template_variables['WebsiteDetails.CssClass'] = 'sucuriscan-red-tab';
7181
  }
7182
 
7183
- $secvars = sucuriscan_sitecheck_general_information( $scan_results, $secvars );
7184
- $secvars = sucuriscan_sitecheck_application_details( $scan_results, $secvars );
7185
- $secvars = sucuriscan_sitecheck_system_notices( $scan_results, $secvars );
7186
- $secvars = sucuriscan_sitecheck_outdated_software( $scan_results, $secvars );
7187
- $secvars = sucuriscan_sitecheck_recommendations( $scan_results, $secvars );
7188
 
7189
- $template_variables['WebsiteDetails.Content'] = SucuriScanTemplate::get_section( 'malwarescan-reswebdetails', $secvars );
7190
 
7191
- return $template_variables;
7192
  }
7193
 
7194
  /**
@@ -7196,11 +7689,12 @@ function sucuriscan_sitecheck_website_details( $scan_results = false, $template_
7196
  * the HTML code to display the information in the malware scan page inside the
7197
  * website details tab and specifically in the general information panel.
7198
  *
7199
- * @param array $scan_results Array with information of the scanning.
7200
- * @param array $template_variables Array with psuedo-variables to build the template.
7201
- * @return array Array with psuedo-variables to build the template including extra information.
7202
  */
7203
- function sucuriscan_sitecheck_general_information( $scan_results = false, $secvars = array() ){
 
7204
  $possible_keys = array(
7205
  'DOMAIN' => 'Domain Scanned',
7206
  'IP' => 'Site IP Address',
@@ -7210,18 +7704,25 @@ function sucuriscan_sitecheck_general_information( $scan_results = false, $secva
7210
  'PHP_VERSION' => 'PHP Version',
7211
  );
7212
 
7213
- if ( isset($scan_results['SCAN']) ) {
7214
- $scan_results['SCAN']['WP_VERSION'] = array( SucuriScan::site_version() );
7215
- $scan_results['SCAN']['PHP_VERSION'] = array( phpversion() );
7216
 
7217
- foreach ( $possible_keys as $result_key => $result_title ) {
7218
- if ( isset($scan_results['SCAN'][ $result_key ]) ) {
7219
- $result_value = implode( ', ', $scan_results['SCAN'][ $result_key ] );
 
 
 
 
7220
 
7221
- $secvars['GenericInformationList'] .= SucuriScanTemplate::get_snippet( 'malwarescan-appdetail', array(
7222
- 'InformationTitle' => SucuriScan::escape( $result_title ),
7223
- 'InformationValue' => SucuriScan::escape( $result_value ),
7224
- ) );
 
 
 
7225
  }
7226
  }
7227
  }
@@ -7234,29 +7735,33 @@ function sucuriscan_sitecheck_general_information( $scan_results = false, $secva
7234
  * the HTML code to display the information in the malware scan page inside the
7235
  * website details tab and specifically in the application details panel.
7236
  *
7237
- * @param array $scan_results Array with information of the scanning.
7238
- * @param array $template_variables Array with psuedo-variables to build the template.
7239
- * @return array Array with psuedo-variables to build the template including extra information.
7240
  */
7241
- function sucuriscan_sitecheck_application_details( $scan_results = false, $secvars = array() ){
7242
- if ( isset($scan_results['WEBAPP']) ) {
7243
- foreach ( $scan_results['WEBAPP'] as $webapp_key => $webapp_details ) {
7244
- if ( is_array( $webapp_details ) ) {
7245
- foreach ( $webapp_details as $i => $details ) {
 
7246
  $secvars['NoAppDetailsVisibility'] = 'hidden';
7247
 
7248
- if ( is_array( $details ) ) {
7249
  $details = isset($details[0]) ? $details[0] : '';
7250
  }
7251
 
7252
- $details_parts = explode( ':', $details, 2 );
7253
- $result_title = isset($details_parts[0]) ? trim( $details_parts[0] ) : '';
7254
- $result_value = isset($details_parts[1]) ? trim( $details_parts[1] ) : '';
7255
 
7256
- $secvars['ApplicationDetailsList'] .= SucuriScanTemplate::get_snippet( 'malwarescan-appdetail', array(
7257
- 'InformationTitle' => SucuriScan::escape( $result_title ),
7258
- 'InformationValue' => SucuriScan::escape( $result_value ),
7259
- ) );
 
 
 
7260
  }
7261
  }
7262
  }
@@ -7270,22 +7775,26 @@ function sucuriscan_sitecheck_application_details( $scan_results = false, $secva
7270
  * the HTML code to display the information in the malware scan page inside the
7271
  * website details tab and specifically in the system notices panel.
7272
  *
7273
- * @param array $scan_results Array with information of the scanning.
7274
- * @param array $template_variables Array with psuedo-variables to build the template.
7275
- * @return array Array with psuedo-variables to build the template including extra information.
7276
  */
7277
- function sucuriscan_sitecheck_system_notices( $scan_results = false, $secvars = array() ){
7278
- if ( isset($scan_results['SYSTEM']['NOTICE']) ) {
7279
- foreach ( $scan_results['SYSTEM']['NOTICE'] as $notice ) {
 
7280
  $secvars['NoAppDetailsVisibility'] = 'hidden';
7281
 
7282
- if ( is_array( $notice ) ) {
7283
- $notice = implode( ', ', $notice );
7284
  }
7285
 
7286
- $secvars['SystemNoticeList'] .= SucuriScanTemplate::get_snippet( 'malwarescan-sysnotice', array(
7287
- 'SystemNotice' => SucuriScan::escape( $notice ),
7288
- ) );
 
 
 
7289
  }
7290
  }
7291
 
@@ -7297,20 +7806,24 @@ function sucuriscan_sitecheck_system_notices( $scan_results = false, $secvars =
7297
  * the HTML code to display the information in the malware scan page inside the
7298
  * website details tab and specifically in the outdated software panel.
7299
  *
7300
- * @param array $scan_results Array with information of the scanning.
7301
- * @param array $template_variables Array with psuedo-variables to build the template.
7302
- * @return array Array with psuedo-variables to build the template including extra information.
7303
  */
7304
- function sucuriscan_sitecheck_outdated_software( $scan_results = false, $secvars = array() ){
7305
- if ( isset($scan_results['OUTDATEDSCAN']) ) {
7306
- foreach ( $scan_results['OUTDATEDSCAN'] as $outdated ) {
7307
- if ( count( $outdated ) >= 3 ) {
 
7308
  $secvars['HasRecommendationsVisibility'] = 'visible';
7309
- $secvars['OutdatedSoftwareList'] .= SucuriScanTemplate::get_snippet( 'malwarescan-outdated', array(
7310
- 'OutdatedSoftwareTitle' => SucuriScan::escape( $outdated[0] ),
7311
- 'OutdatedSoftwareUrl' => $outdated[1],
7312
- 'OutdatedSoftwareValue' => SucuriScan::escape( $outdated[2] ),
7313
- ) );
 
 
 
7314
  }
7315
  }
7316
  }
@@ -7323,21 +7836,25 @@ function sucuriscan_sitecheck_outdated_software( $scan_results = false, $secvars
7323
  * the HTML code to display the information in the malware scan page inside the
7324
  * website details tab and specifically in the security recommendations panel.
7325
  *
7326
- * @param array $scan_results Array with information of the scanning.
7327
- * @param array $template_variables Array with psuedo-variables to build the template.
7328
- * @return array Array with psuedo-variables to build the template including extra information.
7329
  */
7330
- function sucuriscan_sitecheck_recommendations( $scan_results = false, $secvars = array() ){
7331
- if ( isset($scan_results['RECOMMENDATIONS']) ) {
7332
- foreach ( $scan_results['RECOMMENDATIONS'] as $recommendation ) {
7333
- if ( count( $recommendation ) >= 3 ) {
 
7334
  $secvars['HasRecommendationsVisibility'] = 'visible';
7335
- $secvars['SecurityRecomendationList'] .= SucuriScanTemplate::get_snippet( 'malwarescan-recommendation', array(
7336
- 'RecommendationTitle' => SucuriScan::escape( $recommendation[0] ),
7337
- 'RecommendationValue' => SucuriScan::escape( $recommendation[1] ),
7338
- 'RecommendationUrl' => SucuriScan::escape( $recommendation[2] ),
7339
- 'RecommendationUrlTitle' => SucuriScan::escape( $recommendation[2] ),
7340
- ) );
 
 
 
7341
  }
7342
  }
7343
  }
@@ -7350,11 +7867,12 @@ function sucuriscan_sitecheck_recommendations( $scan_results = false, $secvars =
7350
  * the HTML code to display the information in the malware scan page inside the
7351
  * website links tab.
7352
  *
7353
- * @param array $scan_results Array with information of the scanning.
7354
- * @param array $template_variables Array with psuedo-variables to build the template.
7355
- * @return array Array with psuedo-variables to build the template including extra information.
7356
  */
7357
- function sucuriscan_sitecheck_website_links( $scan_results = false, $template_variables = array() ){
 
7358
  $possible_url_keys = array(
7359
  'IFRAME' => 'List of iframes found',
7360
  'JSEXTERNAL' => 'List of external scripts included',
@@ -7366,33 +7884,39 @@ function sucuriscan_sitecheck_website_links( $scan_results = false, $template_va
7366
  'NoLinksVisibility' => 'hidden',
7367
  );
7368
 
7369
- if ( isset($scan_results['LINKS']) ) {
7370
- foreach ( $possible_url_keys as $result_key => $result_title ) {
7371
- if ( isset($scan_results['LINKS'][ $result_key ]) ) {
7372
  $result_value = 0;
7373
  $result_items = '';
7374
 
7375
- foreach ( $scan_results['LINKS'][ $result_key ] as $url_path ) {
7376
  $result_value += 1;
7377
- $result_items .= SucuriScanTemplate::get_snippet( 'malwarescan-weblinkitems', array(
7378
- 'WebsiteLinksItemTitle' => SucuriScan::escape( $url_path ),
7379
- ) );
 
 
 
7380
  }
7381
 
7382
- $secvars['WebsiteLinksAllList'] .= SucuriScanTemplate::get_snippet( 'malwarescan-weblinktitle', array(
7383
- 'WebsiteLinksSectionTitle' => SucuriScan::escape( $result_title ),
7384
- 'WebsiteLinksSectionTotal' => SucuriScan::escape( $result_value ),
7385
- 'WebsiteLinksSectionItems' => $result_items,
7386
- ) );
 
 
 
7387
  }
7388
  }
7389
  } else {
7390
  $secvars['NoLinksVisibility'] = 'visible';
7391
  }
7392
 
7393
- $template_variables['WebsiteLinks.Content'] = SucuriScanTemplate::get_section( 'malwarescan-resweblinks', $secvars );
7394
 
7395
- return $template_variables;
7396
  }
7397
 
7398
  /**
@@ -7400,11 +7924,12 @@ function sucuriscan_sitecheck_website_links( $scan_results = false, $template_va
7400
  * the HTML code to display the information in the malware scan page inside the
7401
  * blacklist status tab.
7402
  *
7403
- * @param array $scan_results Array with information of the scanning.
7404
- * @param array $template_variables Array with psuedo-variables to build the template.
7405
- * @return array Array with psuedo-variables to build the template including extra information.
7406
  */
7407
- function sucuriscan_sitecheck_blacklist_status( $scan_results = false, $template_variables = array() ){
 
7408
  $blacklist_types = array(
7409
  'INFO' => 'CLEAN',
7410
  'WARN' => 'WARNING',
@@ -7414,29 +7939,32 @@ function sucuriscan_sitecheck_blacklist_status( $scan_results = false, $template
7414
  'BlacklistStatusList' => '',
7415
  );
7416
 
7417
- if ( isset($scan_results['BLACKLIST']['WARN']) ) {
7418
- $template_variables['BlacklistStatusTitle'] = 'Site blacklisted';
7419
- $template_variables['BlacklistStatus.CssClass'] = 'sucuriscan-red-tab';
7420
  }
7421
 
7422
- foreach ( $blacklist_types as $type => $group_title ) {
7423
- if ( isset($scan_results['BLACKLIST'][ $type ]) ) {
7424
- foreach ( $scan_results['BLACKLIST'][ $type ] as $blres ) {
7425
- $css_blacklist = ( $type == 'INFO' ) ? 'success' : 'danger';
7426
 
7427
- $secvars['BlacklistStatusList'] .= SucuriScanTemplate::get_snippet( 'malwarescan-resblacklist', array(
7428
- 'BlacklistStatusCssClass' => $css_blacklist,
7429
- 'BlacklistStatusGroupTitle' => SucuriScan::escape( $group_title ),
7430
- 'BlacklistStatusReporterName' => SucuriScan::escape( $blres[0] ),
7431
- 'BlacklistStatusReporterUrl' => SucuriScan::escape( $blres[1] ),
7432
- ) );
 
 
 
7433
  }
7434
  }
7435
  }
7436
 
7437
- $template_variables['BlacklistStatus.Content'] = SucuriScanTemplate::get_section( 'malwarescan-resblacklist', $secvars );
7438
 
7439
- return $template_variables;
7440
  }
7441
 
7442
  /**
@@ -7444,424 +7972,236 @@ function sucuriscan_sitecheck_blacklist_status( $scan_results = false, $template
7444
  * the HTML code to display the information in the malware scan page inside the
7445
  * modified files tab.
7446
  *
7447
- * @param array $scan_results Array with information of the scanning.
7448
- * @param array $template_variables Array with psuedo-variables to build the template.
7449
- * @return array Array with psuedo-variables to build the template including extra information.
7450
- */
7451
- function sucuriscan_sitecheck_modified_files( $scan_results = false, $template_variables = array() ){
7452
- $template_variables['ModifiedFiles.Content'] = sucuriscan_modified_files();
7453
-
7454
- return $template_variables;
7455
- }
7456
-
7457
- /**
7458
- * CloudProxy monitoring page.
7459
- *
7460
- * It checks whether the WordPress core files are the original ones, and the state
7461
- * of the themes and plugins reporting the availability of updates. It also checks
7462
- * the user accounts under the administrator group.
7463
- *
7464
- * @return void
7465
- */
7466
- function sucuriscan_monitoring_page(){
7467
- SucuriScanInterface::check_permissions();
7468
-
7469
- // Process all form submissions.
7470
- sucuriscan_monitoring_form_submissions();
7471
-
7472
- // Get the dynamic values for the template variables.
7473
- $api_key = SucuriScanAPI::get_cloudproxy_key();
7474
-
7475
- // Page pseudo-variables initialization.
7476
- $template_variables = array(
7477
- 'PageTitle' => 'Firewall WAF',
7478
- 'Monitoring.InstructionsVisibility' => 'visible',
7479
- 'Monitoring.Settings' => sucuriscan_monitoring_settings( $api_key ),
7480
- 'Monitoring.Logs' => sucuriscan_monitoring_logs( $api_key ),
7481
-
7482
- /* Pseudo-variables for the monitoring logs. */
7483
- 'AuditLogs.List' => '',
7484
- 'AuditLogs.CountText' => '',
7485
- 'AuditLogs.DenialTypeOptions' => '',
7486
- 'AuditLogs.NoItemsVisibility' => '',
7487
- 'AuditLogs.PaginationVisibility' => '',
7488
- 'AuditLogs.AuditPagination' => '',
7489
- );
7490
-
7491
- if ( $api_key ) {
7492
- $template_variables['Monitoring.InstructionsVisibility'] = 'hidden';
7493
- }
7494
-
7495
- echo SucuriScanTemplate::get_template( 'monitoring', $template_variables );
7496
- }
7497
-
7498
- /**
7499
- * Process the requests sent by the form submissions originated in the monitoring
7500
- * page, all forms must have a nonce field that will be checked against the one
7501
- * generated in the template render function.
7502
- *
7503
- * @return void
7504
  */
7505
- function sucuriscan_monitoring_form_submissions(){
7506
-
7507
- if ( SucuriScanInterface::check_nonce() ) {
7508
- // Add and/or Update the Sucuri WAF API Key (do it before anything else).
7509
- $option_name = ':cloudproxy_apikey';
7510
- $api_key = SucuriScanRequest::post( $option_name );
7511
-
7512
- if ( $api_key !== false ) {
7513
- if ( SucuriScanAPI::is_valid_cloudproxy_key( $api_key ) ) {
7514
- SucuriScanOption::update_option( $option_name, $api_key );
7515
- SucuriScanOption::update_option( ':revproxy', 'enabled' );
7516
- SucuriScanInterface::info( 'CloudProxy API key saved successfully' );
7517
- } elseif ( empty($api_key) ) {
7518
- SucuriScanOption::delete_option( $option_name );
7519
- SucuriScanOption::update_option( ':revproxy', 'disabled' );
7520
- SucuriScanInterface::info( 'CloudProxy API key removed successfully' );
7521
- } else {
7522
- SucuriScanInterface::error( 'Invalid CloudProxy API key, check your settings and try again.' );
7523
- }
7524
- }
7525
-
7526
- // Flush the cache of the site(s) associated with the API key.
7527
- if ( SucuriScanRequest::post( ':clear_cache', '1' ) ) {
7528
- $clear_cache_resp = SucuriScanAPI::clear_cloudproxy_cache();
7529
-
7530
- if ( $clear_cache_resp ) {
7531
- if ( isset($clear_cache_resp->messages[0]) ) {
7532
- // Clear W3 Total Cache if it is installed.
7533
- if ( function_exists( 'w3tc_flush_all' ) ) {
7534
- w3tc_flush_all();
7535
- }
7536
-
7537
- SucuriScanInterface::info( $clear_cache_resp->messages[0] );
7538
- } else {
7539
- SucuriScanInterface::error( 'Could not clear the cache of your site, try later again.' );
7540
- }
7541
- } else {
7542
- SucuriScanInterface::error( 'CloudProxy is not enabled on your site, or your API key is invalid.' );
7543
- }
7544
- }
7545
- }
7546
 
 
7547
  }
7548
 
7549
  /**
7550
- * Generate the HTML code for the monitoring settings panel.
7551
  *
7552
  * @param string $api_key The CloudProxy API key.
7553
- * @return string The parsed-content of the monitoring settings panel.
7554
  */
7555
- function sucuriscan_monitoring_settings( $api_key = '' ){
7556
- $template_variables = array(
7557
- 'Monitoring.APIKey' => '',
7558
- 'Monitoring.SettingsVisibility' => 'hidden',
7559
- 'Monitoring.SettingOptions' => '',
 
 
 
7560
  );
7561
 
7562
- if ( $api_key ) {
7563
- $settings = SucuriScanAPI::get_cloudproxy_settings( $api_key );
7564
 
7565
- $template_variables['Monitoring.APIKey'] = $api_key['string'];
 
 
7566
 
7567
- if ( $settings ) {
7568
  $counter = 0;
7569
- $template_variables['Monitoring.SettingsVisibility'] = 'visible';
7570
- $settings = sucuriscan_explain_monitoring_settings( $settings );
7571
-
7572
- foreach ( $settings as $option_name => $option_value ) {
7573
- // Change the name of some options.
7574
- if ( $option_name == 'internal_ip' ) {
7575
- $option_name = 'hosting_ip';
7576
- }
7577
 
7578
- $css_class = ( $counter % 2 == 0 ) ? 'alternate' : '';
7579
- $option_title = ucwords( str_replace( '_', chr( 32 ), $option_name ) );
 
7580
 
7581
  // Generate a HTML list when the option's value is an array.
7582
- if ( is_array( $option_value ) ) {
7583
- $css_scrollable = count( $option_value ) > 10 ? 'sucuriscan-list-as-table-scrollable' : '';
7584
  $html_list = '<ul class="sucuriscan-list-as-table ' . $css_scrollable . '">';
7585
 
7586
- foreach ( $option_value as $single_value ) {
7587
- $html_list .= '<li>' . $single_value . '</li>';
7588
  }
7589
 
7590
  $html_list .= '</ul>';
7591
  $option_value = $html_list;
 
 
7592
  }
7593
 
7594
  // Parse the snippet template and replace the pseudo-variables.
7595
- $template_variables['Monitoring.SettingOptions'] .= SucuriScanTemplate::get_snippet('monitoring-settings', array(
7596
- 'Monitoring.OptionCssClass' => $css_class,
7597
- 'Monitoring.OptionName' => $option_title,
7598
- 'Monitoring.OptionValue' => $option_value,
7599
- ));
7600
- $counter += 1;
 
 
 
7601
  }
7602
  }
7603
  }
7604
 
7605
- return SucuriScanTemplate::get_section( 'monitoring-settings', $template_variables );
7606
  }
7607
 
7608
  /**
7609
- * Converts the value of some of the monitoring settings into a human-readable
7610
  * text, for example changing numbers or variable names into a more explicit
7611
  * text so the administrator can understand the meaning of these settings.
7612
  *
7613
  * @param array $settings A hash with the settings of a CloudProxy account.
7614
  * @return array The explained version of the CloudProxy settings.
7615
  */
7616
- function sucuriscan_explain_monitoring_settings( $settings = array() ){
7617
- if ( $settings ) {
7618
- foreach ( $settings as $option_name => $option_value ) {
7619
- switch ( $option_name ) {
7620
- case 'security_level':
7621
- $new_value = ucwords( $option_value );
7622
- break;
7623
- case 'proxy_active':
7624
- $new_value = ( $option_value == 1 ) ? 'Active' : 'not active';
7625
- break;
7626
- case 'cache_mode':
7627
- $new_value = sucuriscan_cache_mode_title( $option_value );
7628
- break;
7629
- }
7630
 
7631
- if ( isset($new_value) ) {
7632
- $settings->{$option_name} = $new_value;
 
 
 
 
 
 
 
 
 
7633
  }
7634
  }
7635
-
7636
- return $settings;
7637
  }
7638
 
7639
- return false;
7640
  }
7641
 
7642
  /**
7643
- * Get an explanation of the meaning of the value set for the account's attribute cache_mode.
7644
  *
7645
- * @param string $mode The value set for the cache settings of the site.
7646
- * @return string Explanation of the meaning of the cache_mode value.
7647
  */
7648
- function sucuriscan_cache_mode_title( $mode = '' ){
7649
- $title = '';
 
 
7650
 
7651
- switch ( $mode ) {
7652
- case 'docache': $title = 'Enabled (recommended)'; break;
7653
- case 'sitecache': $title = 'Site caching (using your site headers)'; break;
7654
- case 'nocache': $title = 'Minimal (only for a few minutes)'; break;
7655
- case 'nocacheatall': $title = 'Caching disabled (use with caution)'; break;
7656
- default: $title = 'Unknown'; break;
7657
- }
7658
 
7659
- return $title;
7660
  }
7661
 
7662
- /**
7663
- * Generate the HTML code for the monitoring logs panel.
7664
- *
7665
- * @param string $api_key The CloudProxy API key.
7666
- * @return string The parsed-content of the monitoring logs panel.
7667
- */
7668
- function sucuriscan_monitoring_logs( $api_key = '' ){
7669
- $template_variables = array(
7670
- 'AuditLogs.List' => '',
7671
- 'AuditLogs.CountText' => 0,
7672
- 'AuditLogs.DenialTypeOptions' => '',
7673
- 'AuditLogs.NoItemsVisibility' => 'visible',
7674
- 'AuditLogs.PaginationVisibility' => 'hidden',
7675
- 'AuditLogs.AuditPagination' => '',
7676
- 'AuditLogs.TargetDate' => '',
7677
- 'AuditLogs.DateYears' => '',
7678
- 'AuditLogs.DateMonths' => '',
7679
- 'AuditLogs.DateDays' => '',
7680
- );
7681
-
7682
- $date = date( 'Y-m-d' );
7683
 
7684
- if ( $api_key ) {
7685
- // Retrieve the date filter from the GET request (if any).
7686
- if ( $date_by_get = SucuriScanRequest::get( 'date', '_yyyymmdd' ) ) {
7687
- $date = $date_by_get;
7688
- }
 
 
7689
 
7690
- // Retrieve the date filter from the POST request (if any).
7691
- $year = SucuriScanRequest::post( ':year' );
7692
- $month = SucuriScanRequest::post( ':month' );
7693
- $day = SucuriScanRequest::post( ':day' );
7694
 
7695
- if ( $year && $month && $day ) {
7696
- $date = sprintf( '%s-%s-%s', $year, $month, $day );
 
 
 
 
7697
  }
7698
 
7699
- $logs_data = SucuriScanAPI::get_cloudproxy_logs( $api_key, $date );
7700
-
7701
- if ( $logs_data ) {
7702
- add_thickbox(); /* Include the Thickbox library. */
7703
- $template_variables['AuditLogs.NoItemsVisibility'] = 'hidden';
7704
- $template_variables['AuditLogs.CountText'] = $logs_data->limit . '/' . $logs_data->total_lines;
7705
- $template_variables['AuditLogs.List'] = sucuriscan_monitoring_access_logs( $logs_data->access_logs );
7706
- $template_variables['AuditLogs.DenialTypeOptions'] = sucuriscan_monitoring_denial_types( $logs_data->access_logs );
7707
- }
7708
  }
7709
-
7710
- $template_variables['AuditLogs.TargetDate'] = SucuriScan::escape( $date );
7711
- $template_variables['AuditLogs.DateYears'] = sucuriscan_monitoring_dates( 'years', $date );
7712
- $template_variables['AuditLogs.DateMonths'] = sucuriscan_monitoring_dates( 'months', $date );
7713
- $template_variables['AuditLogs.DateDays'] = sucuriscan_monitoring_dates( 'days', $date );
7714
-
7715
- return SucuriScanTemplate::get_section( 'monitoring-logs', $template_variables );
7716
  }
7717
 
7718
- /**
7719
- * Generate the HTML code to show the table with the access-logs.
7720
- *
7721
- * @param array $access_logs The logs retrieved from the remote API service.
7722
- * @return string The HTML code to show the access-logs in the page as a table.
7723
- */
7724
- function sucuriscan_monitoring_access_logs( $access_logs = array() ){
7725
- $logs_html = '';
 
 
 
 
 
 
 
 
 
 
 
 
7726
 
7727
- if ( $access_logs && ! empty($access_logs) ) {
7728
  $counter = 0;
7729
- $needed_attrs = array(
7730
- 'request_date',
7731
- 'request_time',
7732
- 'request_timezone',
7733
- 'request_timestamp',
7734
- 'local_request_time',
7735
- 'remote_addr',
7736
- 'sucuri_block_reason',
7737
- 'resource_path',
7738
- 'request_method',
7739
- 'http_protocol',
7740
- 'http_status',
7741
- 'http_status_title',
7742
- 'http_bytes_sent',
7743
- 'http_referer',
7744
- 'http_user_agent',
7745
- );
7746
-
7747
- $filter_by_denial_type = false;
7748
- $filter_by_keyword = false;
7749
- $filter_query = false;
7750
-
7751
- if ( $q = SucuriScanRequest::post( ':monitoring_denial_type' ) ) {
7752
- $filter_by_denial_type = true;
7753
- $filter_query = $q;
7754
- }
7755
-
7756
- if ( $q = SucuriScanRequest::post( ':monitoring_log_filter' ) ) {
7757
- $filter_by_keyword = true;
7758
- $filter_query = $q;
7759
- }
7760
-
7761
- foreach ( $access_logs as $access_log ) {
7762
- $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
7763
- $audit_log_snippet = array(
7764
- 'AuditLog.Id' => $counter,
7765
- 'AuditLog.CssClass' => $css_class,
7766
- );
7767
-
7768
- // If there is a filter, check the access_log data and break the operation if needed.
7769
- if ( $filter_query ) {
7770
- if ( $filter_by_denial_type ) {
7771
- $denial_type_slug = SucuriScan::human2var( $access_log->sucuri_block_reason );
7772
-
7773
- if ( $denial_type_slug != $filter_query ) {
7774
- continue;
7775
- }
7776
- }
7777
-
7778
- if (
7779
- $filter_by_keyword
7780
- && strpos( $access_log->remote_addr, $filter_query ) === false
7781
- && strpos( $access_log->resource_path, $filter_query ) === false
7782
- ) {
7783
- continue;
7784
- }
7785
- }
7786
-
7787
- // Generate (dynamically) the pseudo-variables for the template.
7788
- foreach ( $needed_attrs as $attr_name ) {
7789
- $attr_value = '';
7790
 
7791
- $attr_title = str_replace( '_', chr( 32 ), $attr_name );
7792
- $attr_title = ucwords( $attr_title );
7793
- $attr_title = str_replace( chr( 32 ), '', $attr_title );
7794
- $attr_title = 'AuditLog.' . $attr_title;
7795
 
7796
- if ( isset($access_log->{$attr_name}) ) {
7797
- $attr_value = $access_log->{$attr_name};
 
 
 
 
7798
 
7799
- if (
7800
- empty($attr_value)
7801
- && $attr_name == 'sucuri_block_reason'
7802
- ) {
7803
- $attr_value = 'Unknown';
7804
- }
7805
- } elseif ( $attr_name == 'local_request_time' ) {
7806
- $attr_value = SucuriScan::datetime( $access_log->request_timestamp );
7807
  }
7808
 
7809
- $audit_log_snippet[ $attr_title ] = SucuriScan::escape( $attr_value );
7810
- }
7811
-
7812
- $logs_html .= SucuriScanTemplate::get_snippet( 'monitoring-logs', $audit_log_snippet );
7813
- $counter += 1;
7814
- }
7815
- }
7816
-
7817
- return $logs_html;
7818
- }
7819
-
7820
- /**
7821
- * Get a list of denial types using the reason of the blocking of a request from
7822
- * the from the audit logs. Examples of denial types can be: "Bad bot access
7823
- * denied", "Access to restricted folder", "Blocked by IDS", etc.
7824
- *
7825
- * @param array $access_logs A list of objects with the detailed version of each request blocked by our service.
7826
- * @param boolean $in_html Whether the list should be converted to a HTML select options or not.
7827
- * @return array Either a list of unique blocking types, or a HTML code.
7828
- */
7829
- function sucuriscan_monitoring_denial_types( $access_logs = array(), $in_html = true ){
7830
- $types = array();
7831
-
7832
- if ( $access_logs && ! empty($access_logs) ) {
7833
- foreach ( $access_logs as $access_log ) {
7834
- if ( ! array_key_exists( $access_log->sucuri_block_reason, $types ) ) {
7835
- $denial_type_k = SucuriScan::human2var( $access_log->sucuri_block_reason );
7836
- $denial_type_v = $access_log->sucuri_block_reason;
7837
-
7838
- if ( empty($denial_type_v) ) {
7839
- $denial_type_v = 'Unknown';
7840
  }
7841
-
7842
- $types[ $denial_type_k ] = $denial_type_v;
7843
  }
7844
- }
7845
- }
7846
-
7847
- if ( $in_html ) {
7848
- $html_types = '<option value="">Filter</option>';
7849
- $selected = SucuriScanRequest::post( ':monitoring_denial_type', '.+' );
7850
 
7851
- foreach ( $types as $type_key => $type_value ) {
7852
- $selected_tag = ( $type_key === $selected ) ? 'selected="selected"' : '';
7853
- $html_types .= sprintf(
7854
- '<option value="%s" %s>%s</option>',
7855
- SucuriScan::escape( $type_key ),
7856
- $selected_tag,
7857
- SucuriScan::escape( $type_value )
7858
- );
7859
  }
7860
-
7861
- return $html_types;
7862
  }
7863
 
7864
- return $types;
7865
  }
7866
 
7867
  /**
@@ -7872,26 +8212,27 @@ function sucuriscan_monitoring_denial_types( $access_logs = array(), $in_html =
7872
  * @param boolean $in_html Whether the list should be converted to a HTML select options or not.
7873
  * @return array Either an array with the expected values, or a HTML code.
7874
  */
7875
- function sucuriscan_monitoring_dates( $type = '', $date = '', $in_html = true ){
 
7876
  $options = array();
7877
  $selected = '';
 
 
 
 
7878
 
7879
- if ( preg_match( '/^([0-9]{4})\-([0-9]{2})\-([0-9]{2})$/', $date, $date_m ) ) {
7880
  $s_year = $date_m[1];
7881
  $s_month = $date_m[2];
7882
  $s_day = $date_m[3];
7883
- } else {
7884
- $s_year = '';
7885
- $s_month = '';
7886
- $s_day = '';
7887
  }
7888
 
7889
- switch ( $type ) {
7890
  case 'years':
7891
  $selected = $s_year;
7892
- $current_year = (int) date( 'Y' );
7893
  $max_years = 5; /* Maximum number of years to keep the logs. */
7894
- $options = range( ($current_year - $max_years), $current_year );
7895
  break;
7896
  case 'months':
7897
  $selected = $s_month;
@@ -7911,25 +8252,25 @@ function sucuriscan_monitoring_dates( $type = '', $date = '', $in_html = true ){
7911
  );
7912
  break;
7913
  case 'days':
7914
- $options = range( 1, 31 );
7915
  $selected = $s_day;
7916
  break;
7917
  }
7918
 
7919
- if ( $in_html ) {
7920
  $html_options = '';
7921
 
7922
- foreach ( $options as $key => $value ) {
7923
- if ( is_numeric( $value ) ) {
7924
- $value = str_pad( $value, 2, 0, STR_PAD_LEFT );
7925
  }
7926
 
7927
- if ( $type != 'months' ) {
7928
  $key = $value;
7929
  }
7930
 
7931
  $selected_tag = ( $key == $selected ) ? 'selected="selected"' : '';
7932
- $html_options .= sprintf( '<option value="%s" %s>%s</option>', $key, $selected_tag, $value );
7933
  }
7934
 
7935
  return $html_options;
@@ -7938,6 +8279,125 @@ function sucuriscan_monitoring_dates( $type = '', $date = '', $in_html = true ){
7938
  return $options;
7939
  }
7940
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7941
  /**
7942
  * Project hardening library.
7943
  *
@@ -7956,7 +8416,8 @@ function sucuriscan_monitoring_dates( $type = '', $date = '', $in_html = true ){
7956
  * Apache/PHP Hardener that can, for example, deactivate unneeded features in
7957
  * configuration files or perform various other protective measures.
7958
  */
7959
- class SucuriScanHardening extends SucuriScan {
 
7960
 
7961
  /**
7962
  * Returns a list of access control rules for the Apache web server that can be
@@ -8170,7 +8631,8 @@ class SucuriScanHardening extends SucuriScan {
8170
  *
8171
  * @return void
8172
  */
8173
- function sucuriscan_hardening_page(){
 
8174
  SucuriScanInterface::check_permissions();
8175
 
8176
  $template_variables = array(
@@ -8178,12 +8640,13 @@ function sucuriscan_hardening_page(){
8178
  'Hardening.Whitelist' => sucuriscan_hardening_whitelist(),
8179
  );
8180
 
8181
- echo SucuriScanTemplate::get_template('hardening', $template_variables);
8182
  }
8183
 
8184
- function sucuriscan_hardening_panel(){
 
8185
  if (SucuriScanRequest::post(':run_hardening')
8186
- && ! SucuriScanInterface::check_nonce()
8187
  ) {
8188
  unset($_POST['sucuriscan_run_hardening']);
8189
  }
@@ -8216,10 +8679,11 @@ function sucuriscan_hardening_panel(){
8216
  $template_variables['Hardening.WpIncludes'] = sucuriscan_harden_wpincludes();
8217
  }
8218
 
8219
- return SucuriScanTemplate::get_section('hardening-panel', $template_variables);
8220
  }
8221
 
8222
- function sucuriscan_hardening_whitelist(){
 
8223
  $template_variables = array(
8224
  'HardeningWhitelist.List' => '',
8225
  'HardeningWhitelist.NoItemsVisibility' => 'visible',
@@ -8268,19 +8732,22 @@ function sucuriscan_hardening_whitelist(){
8268
  foreach ($files as $file) {
8269
  $css_class = ($counter % 2 === 0) ? '' : 'alternate';
8270
  $fregexp = sprintf('%s/.*/%s', $folder, $file);
8271
- $html = SucuriScanTemplate::get_snippet('hardening-whitelist', array(
8272
- 'HardeningWhitelist.CssClass' => $css_class,
8273
- 'HardeningWhitelist.File' => SucuriScan::escape($file),
8274
- 'HardeningWhitelist.Folder' => SucuriScan::escape($folder),
8275
- 'HardeningWhitelist.Regexp' => SucuriScan::escape($fregexp),
8276
- ));
 
 
 
8277
  $template_variables['HardeningWhitelist.List'] .= $html;
8278
  $counter++;
8279
  }
8280
  }
8281
  }
8282
 
8283
- return SucuriScanTemplate::get_section('hardening-whitelist', $template_variables);
8284
  }
8285
 
8286
  /**
@@ -8297,9 +8764,10 @@ function sucuriscan_hardening_whitelist(){
8297
  * @param string $updatemsg Optional explanation of the hardening after the submission of the form.
8298
  * @return void
8299
  */
8300
- function sucuriscan_harden_status( $title = '', $status = 0, $type = '', $messageok = '', $messagewarn = '', $desc = null, $updatemsg = null ){
 
8301
  $template_variables = array(
8302
- 'Hardening.Title' => SucuriScan::escape( $title ),
8303
  'Hardening.Description' => '',
8304
  'Hardening.Status' => 'unknown',
8305
  'Hardening.FieldName' => '',
@@ -8309,18 +8777,18 @@ function sucuriscan_harden_status( $title = '', $status = 0, $type = '', $messag
8309
  'Hardening.UpdateMessage' => '',
8310
  );
8311
 
8312
- if ( is_null( $type ) ) {
8313
  $type = 'unknown';
8314
  $template_variables['Hardening.FieldAttributes'] = 'disabled="disabled"';
8315
  }
8316
 
8317
  $template_variables['Hardening.Status'] = (string) $status;
8318
 
8319
- if ( $status === 1 ) {
8320
  $template_variables['Hardening.FieldName'] = $type . '_unharden';
8321
  $template_variables['Hardening.FieldValue'] = 'Revert hardening';
8322
  $template_variables['Hardening.Information'] = $messageok;
8323
- } elseif ( $status === 0 ) {
8324
  $template_variables['Hardening.FieldName'] = $type;
8325
  $template_variables['Hardening.FieldValue'] = 'Harden';
8326
  $template_variables['Hardening.Information'] = $messagewarn;
@@ -8331,15 +8799,15 @@ function sucuriscan_harden_status( $title = '', $status = 0, $type = '', $messag
8331
  $template_variables['Hardening.FieldAttributes'] = 'disabled="disabled"';
8332
  }
8333
 
8334
- if ( ! is_null( $desc ) ) {
8335
  $template_variables['Hardening.Description'] = '<p>' . $desc . '</p>';
8336
  }
8337
 
8338
- if ( ! is_null( $updatemsg ) ) {
8339
  $template_variables['Hardening.UpdateMessage'] = '<p>' . $updatemsg . '</p>';
8340
  }
8341
 
8342
- return SucuriScanTemplate::get_snippet( 'hardening', $template_variables );
8343
  }
8344
 
8345
  /**
@@ -8348,21 +8816,21 @@ function sucuriscan_harden_status( $title = '', $status = 0, $type = '', $messag
8348
  *
8349
  * @return void
8350
  */
8351
- function sucuriscan_harden_version(){
 
8352
  $site_version = SucuriScan::site_version();
8353
  $updates = get_core_updates();
8354
- $cp = ( ! is_array( $updates ) || empty($updates) ? 1 : 0 );
8355
 
8356
- if ( isset($updates[0]) && $updates[0] instanceof stdClass ) {
8357
- if (
8358
- $updates[0]->response == 'latest'
8359
  || $updates[0]->response == 'development'
8360
  ) {
8361
  $cp = 1;
8362
  }
8363
  }
8364
 
8365
- if ( strcmp( $site_version, '3.7' ) < 0 ) {
8366
  $cp = 0;
8367
  }
8368
 
@@ -8371,14 +8839,14 @@ function sucuriscan_harden_version(){
8371
  to the source code are made public, if there were security fixes then
8372
  someone with malicious intent can use this information to attack any site
8373
  that has not been upgraded.';
8374
- $messageok = sprintf( 'Your WordPress installation (%s) is current.', $site_version );
8375
  $messagewarn = sprintf(
8376
  'Your current version (%s) is not current.<br>
8377
  <a href="update-core.php" class="button-primary">Update now!</a>',
8378
  $site_version
8379
  );
8380
 
8381
- return sucuriscan_harden_status( 'Verify WordPress version', $cp, null, $messageok, $messagewarn, $initial_msg );
8382
  }
8383
 
8384
  /**
@@ -8388,7 +8856,8 @@ function sucuriscan_harden_version(){
8388
  *
8389
  * @return void
8390
  */
8391
- function sucuriscan_harden_removegenerator(){
 
8392
  return sucuriscan_harden_status(
8393
  'Remove WordPress version',
8394
  1,
@@ -8400,14 +8869,15 @@ function sucuriscan_harden_removegenerator(){
8400
  );
8401
  }
8402
 
8403
- function sucuriscan_harden_nginx_phpfpm(){
 
8404
  $description = 'It seems that you are using the Nginx web server, if that is
8405
  the case then you will need to add the following code into the global
8406
  <code>nginx.conf</code> file or the virtualhost associated with this
8407
  website. Choose the correct rules for the directories that you want to
8408
  protect. If you encounter errors after restart the web server then revert
8409
  the changes and contact the support team of your hosting company, or read
8410
- the official article about <a href="http://codex.wordpress.org/Nginx">
8411
  WordPress on Nginx</a>.</p>';
8412
 
8413
  $description .= "<pre class='code'># Block PHP files in uploads directory.\nlocation ~* /(?:uploads|files)/.*\.php$ {\n\x20\x20deny all;\n}</pre>";
@@ -8441,35 +8911,36 @@ function sucuriscan_harden_nginx_phpfpm(){
8441
  *
8442
  * @return void
8443
  */
8444
- function sucuriscan_harden_upload(){
 
8445
  $dpath = WP_CONTENT_DIR . '/uploads';
8446
 
8447
- if ( SucuriScanRequest::post( ':run_hardening' ) ) {
8448
- if ( SucuriScanRequest::post( ':harden_upload' ) ) {
8449
- $result = SucuriScanHardening::harden_directory( $dpath );
8450
 
8451
- if ( $result === true ) {
8452
  $message = 'Hardening applied to the uploads directory';
8453
- SucuriScanEvent::report_notice_event( $message );
8454
- SucuriScanInterface::info( $message );
8455
  } else {
8456
- SucuriScanInterface::error( 'Error hardening directory, check the permissions.' );
8457
  }
8458
- } elseif ( SucuriScanRequest::post( ':harden_upload_unharden' ) ) {
8459
- $result = SucuriScanHardening::unharden_directory( $dpath );
8460
 
8461
- if ( $result === true ) {
8462
  $message = 'Hardening reverted in the uploads directory';
8463
- SucuriScanEvent::report_error_event( $message );
8464
- SucuriScanInterface::info( $message );
8465
  } else {
8466
- SucuriScanInterface::info( 'Access file is not writable, check the permissions.' );
8467
  }
8468
  }
8469
  }
8470
 
8471
  // Check whether the directory is already hardened or not.
8472
- $is_hardened = SucuriScanHardening::is_hardened( $dpath );
8473
  $cp = ( $is_hardened === true ) ? 1 : 0;
8474
 
8475
  $description = 'It checks if the uploads directory of this site allows the direct execution'
@@ -8501,33 +8972,34 @@ function sucuriscan_harden_upload(){
8501
  *
8502
  * @return void
8503
  */
8504
- function sucuriscan_harden_wpcontent(){
8505
- if ( SucuriScanRequest::post( ':run_hardening' ) ) {
8506
- if ( SucuriScanRequest::post( ':harden_wpcontent' ) ) {
8507
- $result = SucuriScanHardening::harden_directory( WP_CONTENT_DIR );
 
8508
 
8509
- if ( $result === true ) {
8510
  $message = 'Hardening applied to the content directory';
8511
- SucuriScanEvent::report_notice_event( $message );
8512
- SucuriScanInterface::info( $message );
8513
  } else {
8514
- SucuriScanInterface::error( 'Error hardening directory, check the permissions.' );
8515
  }
8516
- } elseif ( SucuriScanRequest::post( ':harden_wpcontent_unharden' ) ) {
8517
- $result = SucuriScanHardening::unharden_directory( WP_CONTENT_DIR );
8518
 
8519
- if ( $result === true ) {
8520
  $message = 'Hardening reverted in the content directory';
8521
- SucuriScanEvent::report_error_event( $message );
8522
- SucuriScanInterface::info( $message );
8523
  } else {
8524
- SucuriScanInterface::info( 'Access file is not writable, check the permissions.' );
8525
  }
8526
  }
8527
  }
8528
 
8529
  // Check whether the directory is already hardened or not.
8530
- $is_hardened = SucuriScanHardening::is_hardened( WP_CONTENT_DIR );
8531
  $cp = ( $is_hardened === true ) ? 1 : 0;
8532
 
8533
  $description = 'This option blocks direct access to any PHP file located under the content'
@@ -8558,39 +9030,40 @@ function sucuriscan_harden_wpcontent(){
8558
  *
8559
  * @return void
8560
  */
8561
- function sucuriscan_harden_wpincludes(){
 
8562
  $dpath = ABSPATH . '/wp-includes';
8563
 
8564
- if ( SucuriScanRequest::post( ':run_hardening' ) ) {
8565
- if ( SucuriScanRequest::post( ':harden_wpincludes' ) ) {
8566
- $result = SucuriScanHardening::harden_directory( $dpath );
8567
 
8568
- if ( $result === true ) {
8569
  $message = 'Hardening applied to the library directory';
8570
  SucuriScanHardening::whitelist('wp-tinymce.php', 'wp-includes');
8571
  SucuriScanHardening::whitelist('ms-files.php', 'wp-includes');
8572
- SucuriScanEvent::report_notice_event( $message );
8573
- SucuriScanInterface::info( $message );
8574
  } else {
8575
- SucuriScanInterface::error( 'Error hardening directory, check the permissions.' );
8576
  }
8577
- } elseif ( SucuriScanRequest::post( ':harden_wpincludes_unharden' ) ) {
8578
- $result = SucuriScanHardening::unharden_directory( $dpath );
8579
 
8580
- if ( $result === true ) {
8581
  $message = 'Hardening reverted in the library directory';
8582
  SucuriScanHardening::dewhitelist('wp-tinymce.php', 'wp-includes');
8583
  SucuriScanHardening::dewhitelist('ms-files.php', 'wp-includes');
8584
- SucuriScanEvent::report_error_event( $message );
8585
- SucuriScanInterface::info( $message );
8586
  } else {
8587
- SucuriScanInterface::info( 'Access file is not writable, check the permissions.' );
8588
  }
8589
  }
8590
  }
8591
 
8592
  // Check whether the directory is already hardened or not.
8593
- $is_hardened = SucuriScanHardening::is_hardened( $dpath );
8594
  $cp = ( $is_hardened === true ) ? 1 : 0;
8595
 
8596
  return sucuriscan_harden_status(
@@ -8610,9 +9083,10 @@ function sucuriscan_harden_wpincludes(){
8610
  *
8611
  * @return void
8612
  */
8613
- function sucuriscan_harden_phpversion(){
 
8614
  $phpv = phpversion();
8615
- $cp = ( strncmp( $phpv, '5.', 2 ) < 0 ) ? 0 : 1;
8616
 
8617
  return sucuriscan_harden_status(
8618
  'Verify PHP version',
@@ -8630,18 +9104,19 @@ function sucuriscan_harden_phpversion(){
8630
  *
8631
  * @return void
8632
  */
8633
- function sucuriscan_cloudproxy_enabled(){
 
8634
  $btn_string = '';
8635
  $proxy_info = SucuriScan::is_behind_cloudproxy();
8636
  $status = 1;
8637
 
8638
  $description = 'A WAF is a protection layer for your web site, blocking all sort of attacks (brute force attempts, '
8639
  . 'DDoS, SQL injections, etc) and helping it remain malware and blacklist free. This test checks if your site is '
8640
- . 'using <a href="http://cloudproxy.sucuri.net/" target="_blank">Sucuri\'s CloudProxy WAF</a> to protect your site.';
8641
 
8642
- if ( $proxy_info === false ) {
8643
  $status = 0;
8644
- $btn_string = '<a href="http://goo.gl/qfNkMq" target="_blank" class="button button-primary">Harden</a>';
8645
  }
8646
 
8647
  return sucuriscan_harden_status(
@@ -8665,18 +9140,17 @@ function sucuriscan_cloudproxy_enabled(){
8665
  *
8666
  * @return void
8667
  */
8668
- function sucuriscan_harden_secretkeys(){
 
8669
  $wp_config_path = SucuriScan::get_wpconfig_path();
8670
  $current_keys = SucuriScanOption::get_security_keys();
8671
 
8672
- if ( $wp_config_path ) {
8673
  $cp = 1;
8674
- $message = 'The main configuration file was found at: <code>'.$wp_config_path.'</code><br>';
 
8675
 
8676
- if (
8677
- ! empty($current_keys['bad'])
8678
- || ! empty($current_keys['missing'])
8679
- ) {
8680
  $cp = 0;
8681
  }
8682
  } else {
@@ -8685,13 +9159,13 @@ function sucuriscan_harden_secretkeys(){
8685
  }
8686
 
8687
  $message .= '<br>It checks whether you have proper random keys/salts created for WordPress. A
8688
- <a href="http://codex.wordpress.org/Editing_wp-config.php#Security_Keys" target="_blank">
8689
  secret key</a> makes your site harder to hack and access harder to crack by adding
8690
  random elements to the password. In simple terms, a secret key is a password with
8691
  elements that make it harder to generate enough options to break through your
8692
  security barriers.';
8693
  $messageok = 'Security keys and salts not set, we recommend to create them for security reasons'
8694
- . '<a href="' . SucuriScanTemplate::get_url( 'posthack' ) . '" class="button button-primary">'
8695
  . 'Harden</a>';
8696
 
8697
  return sucuriscan_harden_status(
@@ -8712,23 +9186,24 @@ function sucuriscan_harden_secretkeys(){
8712
  *
8713
  * @return void
8714
  */
8715
- function sucuriscan_harden_readme(){
 
8716
  $upmsg = null;
8717
- $cp = is_readable( ABSPATH.'/readme.html' ) ? 0 : 1;
8718
 
8719
  // TODO: After hardening create an option to automatically remove this after WP upgrade.
8720
- if ( SucuriScanRequest::post( ':run_hardening' ) ) {
8721
- if ( SucuriScanRequest::post( ':harden_readme' ) && $cp == 0 ) {
8722
- if ( @unlink( ABSPATH.'/readme.html' ) === false ) {
8723
- $upmsg = SucuriScanInterface::error( 'Unable to remove <code>readme.html</code> file.' );
8724
  } else {
8725
  $cp = 1;
8726
  $message = 'Hardening applied to the <code>readme.html</code> file';
8727
- SucuriScanEvent::report_notice_event( $message );
8728
- SucuriScanInterface::info( $message );
8729
  }
8730
- } elseif ( SucuriScanRequest::post( ':harden_readme_unharden' ) ) {
8731
- SucuriScanInterface::error( 'We can not revert this action, you must create the <code>readme.html</code> manually.' );
8732
  }
8733
  }
8734
 
@@ -8749,7 +9224,8 @@ function sucuriscan_harden_readme(){
8749
  *
8750
  * @return void
8751
  */
8752
- function sucuriscan_harden_adminuser(){
 
8753
  global $wpdb;
8754
 
8755
  $upmsg = null;
@@ -8759,12 +9235,12 @@ function sucuriscan_harden_adminuser(){
8759
  'search_columns' => array( 'user_login' ),
8760
  ));
8761
  $results = $user_query->get_results();
8762
- $account_removed = ( count( $results ) === 0 ? 1 : 0 );
8763
 
8764
- if ( $account_removed === 0 ) {
8765
  $upmsg = '<i><strong>Notice.</strong> We do not offer an option to automatically change the user name.
8766
- Go to the <a href="'.admin_url( 'users.php' ).'" target="_blank">user list</a> and create a new
8767
- administrator user. Once created, log in as that user and remove the default <code>admin</code>
8768
  (make sure to assign all the admin posts to the new user too).</i>';
8769
  }
8770
 
@@ -8784,49 +9260,50 @@ function sucuriscan_harden_adminuser(){
8784
  *
8785
  * @return void
8786
  */
8787
- function sucuriscan_harden_fileeditor(){
8788
- $file_editor_disabled = defined( 'DISALLOW_FILE_EDIT' ) ? DISALLOW_FILE_EDIT : false;
 
8789
 
8790
- if ( SucuriScanRequest::post( ':run_hardening' ) ) {
8791
- $current_time = date( 'r' );
8792
  $wp_config_path = SucuriScan::get_wpconfig_path();
8793
 
8794
- $wp_config_writable = ( file_exists( $wp_config_path ) && is_writable( $wp_config_path ) ) ? true : false;
8795
- $new_wpconfig = $wp_config_writable ? @file_get_contents( $wp_config_path ) : '';
8796
 
8797
- if ( SucuriScanRequest::post( ':harden_fileeditor' ) ) {
8798
- if ( $wp_config_writable ) {
8799
- if ( preg_match( '/(.*define\(.DB_COLLATE..*)/', $new_wpconfig, $match ) ) {
8800
  $disallow_fileedit_definition = "\n\ndefine('DISALLOW_FILE_EDIT', TRUE); // Sucuri Security: {$current_time}\n";
8801
- $new_wpconfig = str_replace( $match[0], $match[0].$disallow_fileedit_definition, $new_wpconfig );
8802
  }
8803
 
8804
  $file_editor_disabled = true;
8805
- @file_put_contents( $wp_config_path, $new_wpconfig, LOCK_EX );
8806
  $message = 'Hardening applied to the plugin and theme editor';
8807
- SucuriScanEvent::report_notice_event( $message );
8808
- SucuriScanInterface::info( $message );
8809
  } else {
8810
- SucuriScanInterface::error( 'The <code>wp-config.php</code> file is not in the default location
8811
  or is not writable, you will need to put the following code manually there:
8812
- <code>define("DISALLOW_FILE_EDIT", TRUE);</code>' );
8813
  }
8814
- } elseif ( SucuriScanRequest::post( ':harden_fileeditor_unharden' ) ) {
8815
- if ( preg_match( "/(.*define\('DISALLOW_FILE_EDIT', TRUE\);.*)/", $new_wpconfig, $match ) ) {
8816
- if ( $wp_config_writable ) {
8817
- $new_wpconfig = str_replace( "\n{$match[1]}", '', $new_wpconfig );
8818
- file_put_contents( $wp_config_path, $new_wpconfig, LOCK_EX );
8819
  $file_editor_disabled = false;
8820
  $message = 'Hardening reverted in the plugin and theme editor';
8821
- SucuriScanEvent::report_error_event( $message );
8822
- SucuriScanInterface::info( $message );
8823
  } else {
8824
- SucuriScanInterface::error( 'The <code>wp-config.php</code> file is not in the default location
8825
  or is not writable, you will need to remove the following code manually from there:
8826
- <code>define("DISALLOW_FILE_EDIT", TRUE);</code>' );
8827
  }
8828
  } else {
8829
- SucuriScanInterface::error( 'The theme and plugin editor are not disabled from the configuration file.' );
8830
  }
8831
  }
8832
  }
@@ -8854,7 +9331,8 @@ function sucuriscan_harden_fileeditor(){
8854
  *
8855
  * @return void
8856
  */
8857
- function sucuriscan_harden_dbtables(){
 
8858
  global $table_prefix;
8859
 
8860
  $hardened = ( $table_prefix == 'wp_' ? 0 : 1 );
@@ -8875,10 +9353,11 @@ function sucuriscan_harden_dbtables(){
8875
  *
8876
  * @return void
8877
  */
8878
- function sucuriscan_harden_errorlog(){
 
8879
  $hardened = 1;
8880
- $log_filename = SucuriScan::ini_get( 'error_log' );
8881
- $scan_errorlogs = SucuriScanOption::get_option( ':scan_errorlogs' );
8882
 
8883
  $description = 'PHP uses files named as <code>' . $log_filename . '</code> to log errors found in '
8884
  . 'the code, these files may leak sensitive information of your project allowing an attacker '
@@ -8886,12 +9365,12 @@ function sucuriscan_harden_errorlog(){
8886
  . 'a development environment, and remove them in production mode.';
8887
 
8888
  // Search error log files in the project.
8889
- if ( $scan_errorlogs != 'disabled' ) {
8890
  $file_info = new SucuriScanFileInfo();
8891
  $file_info->ignore_files = false;
8892
  $file_info->ignore_directories = false;
8893
- $error_logs = $file_info->find_file( $log_filename );
8894
- $total_log_files = count( $error_logs );
8895
  } else {
8896
  $hardened = 2;
8897
  $error_logs = array();
@@ -8903,33 +9382,33 @@ function sucuriscan_harden_errorlog(){
8903
  }
8904
 
8905
  // Remove every error log file found in the filesystem scan.
8906
- if ( SucuriScanRequest::post( ':run_hardening' ) ) {
8907
- if ( SucuriScanRequest::post( ':harden_errorlog' ) ) {
8908
  $removed_logs = 0;
8909
- SucuriScanEvent::report_notice_event( sprintf(
8910
  'Error log files deleted: (multiple entries): %s',
8911
- @implode( ',', $error_logs )
8912
- ) );
8913
 
8914
- foreach ( $error_logs as $i => $error_log_path ) {
8915
- if ( unlink( $error_log_path ) ) {
8916
  unset($error_logs[ $i ]);
8917
  $removed_logs += 1;
8918
  }
8919
  }
8920
 
8921
- SucuriScanInterface::info( 'Error log files deleted <code>' . $removed_logs . ' out of ' . $total_log_files . '</code>' );
8922
  }
8923
  }
8924
 
8925
  // List the error log files in a HTML table.
8926
- if ( ! empty($error_logs) ) {
8927
  $hardened = 0;
8928
  $description .= '</p><ul class="sucuriscan-list-as-table">';
8929
 
8930
- foreach ( $error_logs as $error_log_path ) {
8931
- $error_log_path = str_replace( ABSPATH, '/', $error_log_path );
8932
- $description .= '<li>' . $error_log_path . '</li>';
8933
  }
8934
 
8935
  $description .= '</ul><p>';
@@ -8962,141 +9441,30 @@ function sucuriscan_page()
8962
  // Process all form submissions.
8963
  sucuriscan_integrity_form_submissions();
8964
 
8965
- $template_variables = array(
8966
  'WordpressVersion' => sucuriscan_wordpress_outdated(),
8967
  'CoreFiles' => sucuriscan_core_files(),
8968
  'AuditReports' => sucuriscan_auditreport(),
8969
  'AuditLogs' => sucuriscan_auditlogs(),
8970
  );
8971
 
8972
- echo SucuriScanTemplate::get_template('integrity', $template_variables);
8973
- }
8974
-
8975
- /**
8976
- * Process the requests sent by the form submissions originated in the integrity
8977
- * page, all forms must have a nonce field that will be checked against the one
8978
- * generated in the template render function.
8979
- *
8980
- * @return void
8981
- */
8982
- function sucuriscan_integrity_form_submissions()
8983
- {
8984
- if (SucuriScanInterface::check_nonce()) {
8985
- // Force the execution of the filesystem scanner.
8986
- if (SucuriScanRequest::post(':force_scan') !== false) {
8987
- SucuriScanEvent::notify_event('plugin_change', 'Filesystem scan forced at: ' . date('r'));
8988
- SucuriScanEvent::filesystem_scan(true);
8989
- }
8990
-
8991
- // Restore, Remove, Mark as fixed the core files.
8992
- $allowed_actions = '(restore|delete|fixed)';
8993
- $integrity_action = SucuriScanRequest::post(':integrity_action', $allowed_actions);
8994
-
8995
- if ($integrity_action !== false) {
8996
- $cache = new SucuriScanCache('integrity');
8997
- $integrity_files = SucuriScanRequest::post(':integrity_files', '_array');
8998
- $integrity_types = SucuriScanRequest::post(':integrity_types', '_array');
8999
- $files_selected = count($integrity_files);
9000
- $files_affected = array();
9001
- $files_processed = 0;
9002
- $action_titles = array(
9003
- 'restore' => 'Core file restored',
9004
- 'delete' => 'Non-core file deleted',
9005
- 'fixed' => 'Core file marked as fixed',
9006
- );
9007
-
9008
- if ($integrity_files) {
9009
- foreach ((array) $integrity_files as $i => $file_path) {
9010
- $full_path = ABSPATH . $file_path;
9011
- $status_type = $integrity_types[ $i ];
9012
-
9013
- switch ($integrity_action) {
9014
- case 'restore':
9015
- $file_content = SucuriScanAPI::get_original_core_file($file_path);
9016
- if ($file_content) {
9017
- $restored = @file_put_contents($full_path, $file_content, LOCK_EX);
9018
- $files_processed += ( $restored ? 1 : 0 );
9019
- $files_affected[] = $full_path;
9020
- }
9021
- break;
9022
- case 'delete':
9023
- if (@unlink($full_path)) {
9024
- $files_processed += 1;
9025
- $files_affected[] = $full_path;
9026
- }
9027
- break;
9028
- case 'fixed':
9029
- $cache_key = md5($file_path);
9030
- $cache_value = array(
9031
- 'file_path' => $file_path,
9032
- 'file_status' => $status_type,
9033
- 'ignored_at' => time(),
9034
- );
9035
- $cached = $cache->add($cache_key, $cache_value);
9036
- $files_processed += ( $cached ? 1 : 0 );
9037
- $files_affected[] = $full_path;
9038
- break;
9039
- }
9040
- }
9041
-
9042
- // Report files affected as a single event.
9043
- if (! empty($files_affected)) {
9044
- $message_tpl = ( count($files_affected) > 1 )
9045
- ? '%s: (multiple entries): %s'
9046
- : '%s: %s';
9047
- $message = sprintf(
9048
- $message_tpl,
9049
- $action_titles[ $integrity_action ],
9050
- @implode(',', $files_affected)
9051
- );
9052
-
9053
- switch ($integrity_action) {
9054
- case 'restore':
9055
- SucuriScanEvent::report_info_event($message);
9056
- break;
9057
- case 'delete':
9058
- SucuriScanEvent::report_notice_event($message);
9059
- break;
9060
- case 'fixed':
9061
- SucuriScanEvent::report_warning_event($message);
9062
- break;
9063
- }
9064
- }
9065
-
9066
- SucuriScanInterface::info(sprintf(
9067
- '<code>%d</code> out of <code>%d</code> files were successfully processed.',
9068
- $files_selected,
9069
- $files_processed
9070
- ));
9071
- }
9072
- }
9073
- }
9074
  }
9075
 
9076
  /**
9077
- * Retrieve a list of md5sum and last modification time of all the files in the
9078
- * folder specified. This is a recursive function.
9079
  *
9080
- * @param string $dir The base path where the scanning will start.
9081
- * @param boolean $recursive Either TRUE or FALSE if the scan should be performed recursively.
9082
- * @return array List of arrays containing the md5sum and last modification time of the files found.
9083
  */
9084
- function sucuriscan_get_integrity_tree($dir = './', $recursive = false)
9085
  {
9086
- $abs_path = rtrim(ABSPATH, '/');
9087
-
9088
- $file_info = new SucuriScanFileInfo();
9089
- $file_info->ignore_files = false;
9090
- $file_info->ignore_directories = false;
9091
- $file_info->run_recursively = $recursive;
9092
- $file_info->scan_interface = SucuriScanOption::get_option(':scan_interface');
9093
- $integrity_tree = $file_info->get_directory_tree_md5($dir, true);
9094
 
9095
- if (! $integrity_tree) {
9096
- $integrity_tree = array();
9097
  }
9098
 
9099
- return $integrity_tree;
9100
  }
9101
 
9102
  /**
@@ -9109,11 +9477,11 @@ function sucuriscan_auditlogs()
9109
  {
9110
  // Initialize the values for the pagination.
9111
  $max_per_page = SUCURISCAN_AUDITLOGS_PER_PAGE;
9112
- $page_number = SucuriScanTemplate::get_page_number();
9113
  $logs_limit = $page_number * $max_per_page;
9114
- $audit_logs = SucuriScanAPI::get_logs($logs_limit);
9115
 
9116
- $template_variables = array(
9117
  'PageTitle' => 'Audit Logs',
9118
  'AuditLogs.List' => '',
9119
  'AuditLogs.Count' => 0,
@@ -9133,7 +9501,7 @@ function sucuriscan_auditlogs()
9133
  if ($audit_logs->total_entries >= $max_per_page
9134
  && SucuriScanOption::is_disabled(':audit_report')
9135
  ) {
9136
- $template_variables['AuditLogs.EnableAuditReportVisibility'] = 'visible';
9137
  }
9138
 
9139
  for ($i = $iterator_start; $i < $total_items; $i++) {
@@ -9147,13 +9515,14 @@ function sucuriscan_auditlogs()
9147
  $css_class = ( $counter_i % 2 == 0 ) ? '' : 'alternate';
9148
  $snippet_data = array(
9149
  'AuditLog.CssClass' => $css_class,
9150
- 'AuditLog.Event' => SucuriScan::escape($audit_log['event']),
9151
- 'AuditLog.EventTitle' => SucuriScan::escape(ucfirst($audit_log['event'])),
 
9152
  'AuditLog.DateTime' => SucuriScan::datetime($audit_log['timestamp']),
9153
- 'AuditLog.Account' => SucuriScan::escape($audit_log['account']),
9154
- 'AuditLog.Username' => SucuriScan::escape($audit_log['username']),
9155
- 'AuditLog.RemoteAddress' => SucuriScan::escape($audit_log['remote_addr']),
9156
- 'AuditLog.Message' => SucuriScan::escape($audit_log['message']),
9157
  'AuditLog.Extra' => '',
9158
  );
9159
 
@@ -9167,13 +9536,13 @@ function sucuriscan_auditlogs()
9167
  $snippet_data['AuditLog.Extra'] .= '</ul>';
9168
  }
9169
 
9170
- $template_variables['AuditLogs.List'] .= SucuriScanTemplate::get_snippet('integrity-auditlogs', $snippet_data);
9171
  $counter_i += 1;
9172
  }
9173
  }
9174
 
9175
- $template_variables['AuditLogs.Count'] = $counter_i;
9176
- $template_variables['AuditLogs.NoItemsVisibility'] = 'hidden';
9177
 
9178
  if ($total_items > 1) {
9179
  $max_pages = ceil($audit_logs->total_entries / $max_per_page);
@@ -9183,8 +9552,8 @@ function sucuriscan_auditlogs()
9183
  }
9184
 
9185
  if ($max_pages > 1) {
9186
- $template_variables['AuditLogs.PaginationVisibility'] = 'visible';
9187
- $template_variables['AuditLogs.PaginationLinks'] = SucuriScanTemplate::get_pagination(
9188
  '%%SUCURI.URL.Home%%',
9189
  $max_per_page * $max_pages,
9190
  $max_per_page
@@ -9193,8 +9562,9 @@ function sucuriscan_auditlogs()
9193
  }
9194
  }
9195
 
9196
- return SucuriScanTemplate::get_section('integrity-auditlogs', $template_variables);
9197
  }
 
9198
  /**
9199
  * Print a HTML code with the content of the logs audited by the remote Sucuri
9200
  * API service, this page is part of the monitoring tool.
@@ -9207,10 +9577,10 @@ function sucuriscan_auditreport()
9207
  $logs4report = SucuriScanOption::get_option(':logs4report');
9208
 
9209
  if (SucuriScanOption::is_enabled(':audit_report')) {
9210
- $audit_report = SucuriScanAPI::get_audit_report($logs4report);
9211
  }
9212
 
9213
- $template_variables = array(
9214
  'PageTitle' => 'Audit Reports',
9215
  'AuditReport.EventColors' => '',
9216
  'AuditReport.EventsPerType' => '',
@@ -9223,11 +9593,11 @@ function sucuriscan_auditreport()
9223
  );
9224
 
9225
  if ($audit_report) {
9226
- $template_variables['AuditReport.EventColors'] = @implode(',', $audit_report['event_colors']);
9227
 
9228
  // Generate report chart data for the events per type.
9229
  foreach ($audit_report['events_per_type'] as $event => $times) {
9230
- $template_variables['AuditReport.EventsPerType'] .= sprintf(
9231
  "[ '%s', %d ],\n",
9232
  ucwords($event . "\x20events"),
9233
  $times
@@ -9236,7 +9606,7 @@ function sucuriscan_auditreport()
9236
 
9237
  // Generate report chart data for the events per login.
9238
  foreach ($audit_report['events_per_login'] as $event => $times) {
9239
- $template_variables['AuditReport.EventsPerLogin'] .= sprintf(
9240
  "[ '%s', %d ],\n",
9241
  ucwords($event . "\x20logins"),
9242
  $times
@@ -9245,17 +9615,17 @@ function sucuriscan_auditreport()
9245
 
9246
  // Generate report chart data for the events per user.
9247
  foreach ($audit_report['events_per_user'] as $event => $times) {
9248
- $template_variables['AuditReport.EventsPerUserCategories'] .= sprintf('"%s",', $event);
9249
- $template_variables['AuditReport.EventsPerUserSeries'] .= sprintf('%d,', $times);
9250
  }
9251
 
9252
  // Generate report chart data for the events per remote address.
9253
  foreach ($audit_report['events_per_ipaddress'] as $event => $times) {
9254
- $template_variables['AuditReport.EventsPerIPAddressCategories'] .= sprintf('"%s",', $event);
9255
- $template_variables['AuditReport.EventsPerIPAddressSeries'] .= sprintf('%d,', $times);
9256
  }
9257
 
9258
- return SucuriScanTemplate::get_section('integrity-auditreport', $template_variables);
9259
  }
9260
 
9261
  return '';
@@ -9270,13 +9640,13 @@ function sucuriscan_wordpress_outdated()
9270
  {
9271
  $site_version = SucuriScan::site_version();
9272
  $updates = get_core_updates();
9273
- $cp = ( ! is_array($updates) || empty($updates) ? 1 : 0 );
9274
 
9275
- $template_variables = array(
9276
  'WordPress.Version' => $site_version,
9277
  'WordPress.NewVersion' => '0.0.0',
9278
  'WordPress.NewLocale' => 'default',
9279
- 'WordPress.UpdateURL' => admin_url('update-core.php'),
9280
  'WordPress.DownloadURL' => '#',
9281
  'WordPress.UpdateVisibility' => 'hidden',
9282
  );
@@ -9286,11 +9656,11 @@ function sucuriscan_wordpress_outdated()
9286
  && property_exists($updates[0], 'version')
9287
  && property_exists($updates[0], 'download')
9288
  ) {
9289
- $template_variables['WordPress.NewVersion'] = $updates[0]->version;
9290
- $template_variables['WordPress.DownloadURL'] = $updates[0]->download;
9291
 
9292
  if (property_exists($updates[0], 'locale')) {
9293
- $template_variables['WordPress.NewLocale'] = $updates[0]->locale;
9294
  }
9295
 
9296
  if ($updates[0]->response == 'latest'
@@ -9302,10 +9672,10 @@ function sucuriscan_wordpress_outdated()
9302
  }
9303
 
9304
  if ($cp == 0) {
9305
- $template_variables['WordPress.UpdateVisibility'] = 'visible';
9306
  }
9307
 
9308
- return SucuriScanTemplate::get_section('integrity-wpoutdate', $template_variables);
9309
  }
9310
 
9311
  /**
@@ -9315,39 +9685,54 @@ function sucuriscan_wordpress_outdated()
9315
  * send a notification to the administrator with a list of files that were added,
9316
  * modified and/or deleted so far.
9317
  *
9318
- * @param boolean $send_email If the HTML code returned will be sent via email.
9319
- * @return string HTML code with a list of files that were affected.
9320
  */
9321
- function sucuriscan_core_files($send_email = false)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9322
  {
9323
- $site_version = SucuriScan::site_version();
9324
  $affected_files = 0;
 
9325
 
9326
- $template_variables = array(
9327
  'CoreFiles.List' => '',
9328
  'CoreFiles.ListCount' => 0,
9329
- 'CoreFiles.GoodVisibility' => 'visible',
9330
  'CoreFiles.BadVisibility' => 'hidden',
 
9331
  'CoreFiles.FailureVisibility' => 'hidden',
9332
- 'CoreFiles.RemoteChecksumsURL' => '',
9333
  );
9334
 
9335
  if ($site_version && SucuriScanOption::is_enabled(':scan_checksums')) {
9336
  // Check if there are added, removed, or modified files.
9337
  $latest_hashes = sucuriscan_check_core_integrity($site_version);
9338
- $template_variables['CoreFiles.RemoteChecksumsURL'] =
9339
- 'http://api.wordpress.org/core/checksums/1.0/'
9340
  . '?version=' . $site_version . '&locale=en_US';
9341
 
9342
  if ($latest_hashes) {
9343
  $cache = new SucuriScanCache('integrity');
9344
- $ignored_files = $cache->get_all();
9345
  $counter = 0;
9346
 
9347
  foreach ($latest_hashes as $list_type => $file_list) {
9348
- if ($list_type == 'stable'
9349
- || empty($file_list)
9350
- ) {
9351
  continue;
9352
  }
9353
 
@@ -9368,49 +9753,50 @@ function sucuriscan_core_files($send_email = false)
9368
  // Add extra information to the file list.
9369
  $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
9370
  $file_size = @filesize($full_filepath);
9371
- $is_fixable_html = '';
9372
  $is_fixable_text = '';
9373
 
9374
  // Check whether the file can be fixed automatically or not.
9375
  if ($file_info['is_fixable'] !== true) {
9376
  $css_class .= ' sucuriscan-opacity';
9377
- $is_fixable_html = 'disabled="disbled"';
9378
- $is_fixable_text = '(must be fixed manually)';
9379
  }
9380
 
9381
  // Generate the HTML code from the snippet template for this file.
9382
- $template_variables['CoreFiles.List'] .= SucuriScanTemplate::get_snippet('integrity-corefiles', array(
9383
- 'CoreFiles.CssClass' => $css_class,
9384
- 'CoreFiles.StatusType' => $list_type,
9385
- 'CoreFiles.FilePath' => SucuriScan::escape($file_path),
9386
- 'CoreFiles.FileSize' => $file_size,
9387
- 'CoreFiles.FileSizeHuman' => SucuriScan::human_filesize($file_size),
9388
- 'CoreFiles.FileSizeNumber' => number_format($file_size),
9389
- 'CoreFiles.ModifiedAt' => SucuriScan::datetime($file_info['modified_at']),
9390
- 'CoreFiles.IsFixtableFile' => $is_fixable_html,
9391
- 'CoreFiles.IsNotFixable' => $is_fixable_text,
9392
- ));
 
 
9393
  $counter += 1;
9394
  $affected_files += 1;
9395
  }
9396
  }
9397
 
9398
  if ($counter > 0) {
9399
- $template_variables['CoreFiles.ListCount'] = $counter;
9400
- $template_variables['CoreFiles.GoodVisibility'] = 'hidden';
9401
- $template_variables['CoreFiles.BadVisibility'] = 'visible';
9402
  }
9403
  } else {
9404
- $template_variables['CoreFiles.GoodVisibility'] = 'hidden';
9405
- $template_variables['CoreFiles.BadVisibility'] = 'hidden';
9406
- $template_variables['CoreFiles.FailureVisibility'] = 'visible';
9407
  }
9408
  }
9409
 
9410
  // Send an email notification with the affected files.
9411
  if ($send_email === true) {
9412
  if ($affected_files > 0) {
9413
- $content = SucuriScanTemplate::get_section('notification-corefiles', $template_variables);
9414
  $sent = SucuriScanEvent::notify_event('scan_checksums', $content);
9415
 
9416
  return $sent;
@@ -9419,7 +9805,158 @@ function sucuriscan_core_files($send_email = false)
9419
  return false;
9420
  }
9421
 
9422
- return SucuriScanTemplate::get_section('integrity-corefiles', $template_variables);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9423
  }
9424
 
9425
  /**
@@ -9428,7 +9965,7 @@ function sucuriscan_core_files($send_email = false)
9428
  * these keys:
9429
  *
9430
  * <ul>
9431
- * <li>modified: Files with a different checksum according to the official files of the WordPress version filtered,</li>
9432
  * <li>stable: Files with the same checksums than the official files,</li>
9433
  * <li>removed: Official files which are not present in the local project,</li>
9434
  * <li>added: Files present in the local project but not in the official WordPress packages.</li>
@@ -9439,12 +9976,12 @@ function sucuriscan_core_files($send_email = false)
9439
  */
9440
  function sucuriscan_check_core_integrity($version = 0)
9441
  {
9442
- $latest_hashes = SucuriScanAPI::get_official_checksums($version);
9443
  $base_content_dir = defined('WP_CONTENT_DIR')
9444
  ? basename(rtrim(WP_CONTENT_DIR, '/'))
9445
  : '';
9446
 
9447
- if (! $latest_hashes) {
9448
  return false;
9449
  }
9450
 
@@ -9470,7 +10007,7 @@ function sucuriscan_check_core_integrity($version = 0)
9470
  $full_filepath = sprintf('%s/%s', ABSPATH, $file_path);
9471
 
9472
  // Patch for custom content directory path.
9473
- if (! file_exists($full_filepath)
9474
  && strpos($file_path, 'wp-content') !== false
9475
  && defined('WP_CONTENT_DIR')
9476
  ) {
@@ -9513,13 +10050,13 @@ function sucuriscan_check_core_integrity($version = 0)
9513
  // Search added files (files not common in a normal wordpress installation).
9514
  foreach ($wp_core_hashes as $file_path => $extra_info) {
9515
  $file_path = str_replace(DIRECTORY_SEPARATOR, '/', $file_path);
9516
- $file_path = preg_replace('/^\.\/(.*)/', '$1', $file_path);
9517
 
9518
  if (sucuriscan_ignore_integrity_filepath($file_path)) {
9519
  continue;
9520
  }
9521
 
9522
- if (! array_key_exists($file_path, $latest_hashes)) {
9523
  $full_filepath = ABSPATH . '/' . $file_path;
9524
  $modified_at = @filemtime($full_filepath);
9525
  $is_fixable = (bool) is_writable($full_filepath);
@@ -9546,7 +10083,8 @@ function sucuriscan_ignore_integrity_filepath($file_path = '')
9546
 
9547
  // List of files that will be ignored from the integrity checking.
9548
  $ignore_files = array(
9549
- '^sucuri-[0-9a-z]+\.php$',
 
9550
  '^favicon\.ico$',
9551
  '^php\.ini$',
9552
  '^\.htaccess$',
@@ -9581,7 +10119,7 @@ function sucuriscan_ignore_integrity_filepath($file_path = '')
9581
 
9582
  // Determine whether a file must be ignored from the integrity checks or not.
9583
  foreach ($ignore_files as $ignore_pattern) {
9584
- if (preg_match('/'.$ignore_pattern.'/', $file_path)) {
9585
  return true;
9586
  }
9587
  }
@@ -9589,109 +10127,26 @@ function sucuriscan_ignore_integrity_filepath($file_path = '')
9589
  return false;
9590
  }
9591
 
9592
- /**
9593
- * List all files inside wp-content that have been modified in the last days.
9594
- *
9595
- * @return void
9596
- */
9597
- function sucuriscan_modified_files()
9598
- {
9599
- $valid_day_ranges = array( 1, 3, 7, 30, 60 );
9600
- $template_variables = array(
9601
- 'ModifiedFiles.List' => '',
9602
- 'ModifiedFiles.SelectOptions' => '',
9603
- 'ModifiedFiles.NoFilesVisibility' => 'visible',
9604
- 'ModifiedFiles.DisabledVisibility' => 'hidden',
9605
- 'ModifiedFiles.Days' => 0,
9606
- );
9607
-
9608
- // Find files modified in the last days.
9609
- $back_days = SucuriScanRequest::post(':last_days', '[0-9]+');
9610
-
9611
- if ($back_days !== false) {
9612
- if ($back_days <= 0) {
9613
- $back_days = 1;
9614
- } elseif ($back_days >= 60) {
9615
- $back_days = 60;
9616
- }
9617
- } else {
9618
- $back_days = 7;
9619
- }
9620
-
9621
- // Fix data type for the back days variable.
9622
- $back_days = intval($back_days);
9623
- $template_variables['ModifiedFiles.Days'] = $back_days;
9624
-
9625
- // Generate the options for the select field of the page form.
9626
- foreach ($valid_day_ranges as $day) {
9627
- $selected_option = ($back_days == $day) ? 'selected="selected"' : '';
9628
- $template_variables['ModifiedFiles.SelectOptions'] .= sprintf(
9629
- '<option value="%d" %s>%d</option>',
9630
- $day,
9631
- $selected_option,
9632
- $day
9633
- );
9634
- }
9635
-
9636
- // The scanner for modified files can be disabled from the settings page.
9637
- if (SucuriScanOption::is_enabled(':scan_modfiles')) {
9638
- // Search modified files among the project's files.
9639
- $content_hashes = sucuriscan_get_integrity_tree(WP_CONTENT_DIR, true);
9640
-
9641
- if (! empty($content_hashes)) {
9642
- $back_days = current_time('timestamp') - ( $back_days * 86400);
9643
- $counter = 0;
9644
-
9645
- foreach ($content_hashes as $file_path => $file_info) {
9646
- if (isset($file_info['modified_at'])
9647
- && $file_info['modified_at'] >= $back_days
9648
- ) {
9649
- $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
9650
- $mod_date = SucuriScan::datetime($file_info['modified_at']);
9651
-
9652
- $template_variables['ModifiedFiles.List'] .= SucuriScanTemplate::get_snippet('integrity-modifiedfiles', array(
9653
- 'ModifiedFiles.CssClass' => $css_class,
9654
- 'ModifiedFiles.CheckSum' => $file_info['checksum'],
9655
- 'ModifiedFiles.FilePath' => SucuriScan::escape($file_path),
9656
- 'ModifiedFiles.DateTime' => $mod_date,
9657
- 'ModifiedFiles.FileSize' => $file_info['filesize'],
9658
- 'ModifiedFiles.FileSizeHuman' => SucuriScan::human_filesize($file_info['filesize']),
9659
- 'ModifiedFiles.FileSizeNumber' => number_format($file_info['filesize']),
9660
- ));
9661
- $counter += 1;
9662
- }
9663
- }
9664
-
9665
- if ($counter > 0) {
9666
- $template_variables['ModifiedFiles.NoFilesVisibility'] = 'hidden';
9667
- }
9668
- }
9669
- } else {
9670
- $template_variables['ModifiedFiles.DisabledVisibility'] = 'visible';
9671
- }
9672
-
9673
- return SucuriScanTemplate::get_section('integrity-modifiedfiles', $template_variables);
9674
- }
9675
-
9676
  /**
9677
  * Generate and print the HTML code for the Post-Hack page.
9678
  *
9679
  * @return void
9680
  */
9681
- function sucuriscan_posthack_page(){
 
9682
  SucuriScanInterface::check_permissions();
9683
 
9684
  $process_form = sucuriscan_posthack_process_form();
9685
 
9686
  // Page pseudo-variables initialization.
9687
- $template_variables = array(
9688
  'PageTitle' => 'Post-Hack',
9689
- 'UpdateSecretKeys' => sucuriscan_update_secret_keys( $process_form ),
9690
- 'ResetPassword' => sucuriscan_posthack_users( $process_form ),
9691
- 'ResetPlugins' => sucuriscan_posthack_plugins( $process_form ),
9692
  );
9693
 
9694
- echo SucuriScanTemplate::get_template( 'posthack', $template_variables );
9695
  }
9696
 
9697
  /**
@@ -9699,10 +10154,11 @@ function sucuriscan_posthack_page(){
9699
  *
9700
  * @return mixed.
9701
  */
9702
- function sucuriscan_posthack_ajax(){
 
9703
  SucuriScanInterface::check_permissions();
9704
 
9705
- if ( SucuriScanInterface::check_nonce() ) {
9706
  sucuriscan_posthack_plugins_ajax();
9707
  }
9708
 
@@ -9714,17 +10170,17 @@ function sucuriscan_posthack_ajax(){
9714
  *
9715
  * @return boolean TRUE if a form submission should be processed, FALSE otherwise.
9716
  */
9717
- function sucuriscan_posthack_process_form(){
9718
- $process_form = SucuriScanRequest::post( ':process_form', '(0|1)' );
 
9719
 
9720
- if (
9721
- SucuriScanInterface::check_nonce()
9722
  && $process_form !== false
9723
  ) {
9724
- if ( $process_form === '1' ) {
9725
  return true;
9726
  } else {
9727
- SucuriScanInterface::error( 'You need to confirm that you understand the risk of this operation.' );
9728
  }
9729
  }
9730
 
@@ -9737,37 +10193,38 @@ function sucuriscan_posthack_process_form(){
9737
  * @param $process_form Whether a form was submitted or not.
9738
  * @return string HTML code with the information of the process.
9739
  */
9740
- function sucuriscan_update_secret_keys( $process_form = false ){
9741
- $template_variables = array(
 
9742
  'WPConfigUpdate.Visibility' => 'hidden',
9743
  'WPConfigUpdate.NewConfig' => '',
9744
  'SecurityKeys.List' => '',
9745
  );
9746
 
9747
  // Update all WordPress secret keys.
9748
- if ( $process_form && SucuriScanRequest::post( ':update_wpconfig', '1' ) ) {
9749
  $wpconfig_process = SucuriScanEvent::set_new_config_keys();
9750
 
9751
- if ( $wpconfig_process ) {
9752
- $template_variables['WPConfigUpdate.Visibility'] = 'visible';
9753
- SucuriScanEvent::report_notice_event( 'Generate new security keys' );
9754
-
9755
- if ( $wpconfig_process['updated'] === true ) {
9756
- SucuriScanInterface::info( 'Secret keys updated successfully (summary of the operation bellow).' );
9757
- $template_variables['WPConfigUpdate.NewConfig'] .= "// Old Keys\n";
9758
- $template_variables['WPConfigUpdate.NewConfig'] .= $wpconfig_process['old_keys_string'];
9759
- $template_variables['WPConfigUpdate.NewConfig'] .= "//\n";
9760
- $template_variables['WPConfigUpdate.NewConfig'] .= "// New Keys\n";
9761
- $template_variables['WPConfigUpdate.NewConfig'] .= $wpconfig_process['new_keys_string'];
9762
  } else {
9763
  SucuriScanInterface::error(
9764
  '<code>wp-config.php</code> file is not writable, replace the '
9765
  . 'old configuration file with the new values shown bellow.'
9766
  );
9767
- $template_variables['WPConfigUpdate.NewConfig'] = $wpconfig_process['new_wpconfig'];
9768
  }
9769
  } else {
9770
- SucuriScanInterface::error( '<code>wp-config.php</code> file was not found in the default location.' );
9771
  }
9772
  }
9773
 
@@ -9775,12 +10232,12 @@ function sucuriscan_update_secret_keys( $process_form = false ){
9775
  $current_keys = SucuriScanOption::get_security_keys();
9776
  $counter = 0;
9777
 
9778
- foreach ( $current_keys as $key_status => $key_list ) {
9779
- foreach ( $key_list as $key_name => $key_value ) {
9780
  $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
9781
- $key_value = SucuriScan::excerpt( $key_value, 50 );
9782
 
9783
- switch ( $key_status ) {
9784
  case 'good':
9785
  $key_status_text = 'good';
9786
  $key_status_css_class = 'success';
@@ -9796,20 +10253,23 @@ function sucuriscan_update_secret_keys( $process_form = false ){
9796
  break;
9797
  }
9798
 
9799
- if ( isset($key_status_text) ) {
9800
- $template_variables['SecurityKeys.List'] .= SucuriScanTemplate::get_snippet('posthack-updatesecretkeys', array(
9801
- 'SecurityKey.CssClass' => $css_class,
9802
- 'SecurityKey.KeyName' => SucuriScan::escape( $key_name ),
9803
- 'SecurityKey.KeyValue' => SucuriScan::escape( $key_value ),
9804
- 'SecurityKey.KeyStatusText' => $key_status_text,
9805
- 'SecurityKey.KeyStatusCssClass' => $key_status_css_class,
9806
- ));
9807
- $counter += 1;
 
 
 
9808
  }
9809
  }
9810
  }
9811
 
9812
- return SucuriScanTemplate::get_section( 'posthack-updatesecretkeys', $template_variables );
9813
  }
9814
 
9815
  /**
@@ -9819,70 +10279,73 @@ function sucuriscan_update_secret_keys( $process_form = false ){
9819
  * @param $process_form Whether a form was submitted or not.
9820
  * @return string HTML code for a table where a list of user accounts will be shown.
9821
  */
9822
- function sucuriscan_posthack_users( $process_form = false ){
9823
- $template_variables = array(
 
9824
  'ResetPassword.UserList' => '',
9825
  'ResetPassword.PaginationLinks' => '',
9826
  'ResetPassword.PaginationVisibility' => 'hidden',
9827
  );
9828
 
9829
  // Process the form submission (if any).
9830
- sucuriscan_reset_user_password( $process_form );
9831
 
9832
  // Fill the user list for ResetPassword action.
9833
  $user_list = false;
9834
- $page_number = SucuriScanTemplate::get_page_number();
9835
  $max_per_page = SUCURISCAN_MAX_PAGINATION_BUTTONS;
9836
- $dbquery = new WP_User_Query( array(
9837
  'number' => $max_per_page,
9838
- 'offset' => ( $page_number - 1 ) * $max_per_page,
9839
  'fields' => 'all_with_meta',
9840
  'orderby' => 'ID',
9841
- ) );
9842
 
9843
  // Retrieve the results and build the pagination links.
9844
- if ( $dbquery ) {
9845
  $total_items = $dbquery->get_total();
9846
  $user_list = $dbquery->get_results();
9847
 
9848
- $template_variables['ResetPassword.PaginationLinks'] = SucuriScanTemplate::get_pagination(
9849
  '%%SUCURI.URL.Posthack%%#reset-users-password',
9850
  $total_items,
9851
  $max_per_page
9852
  );
9853
 
9854
- if ( $total_items > $max_per_page ) {
9855
- $template_variables['ResetPassword.PaginationVisibility'] = 'visible';
9856
  }
9857
  }
9858
 
9859
- if ( $user_list !== false ) {
9860
  $counter = 0;
9861
 
9862
- foreach ( $user_list as $user ) {
9863
- $user->user_registered_timestamp = strtotime( $user->user_registered );
9864
- $user->user_registered_formatted = SucuriScan::datetime( $user->user_registered_timestamp );
9865
  $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
9866
  $display_username = ( $user->user_login != $user->display_name )
9867
- ? sprintf( '%s (%s)', $user->user_login, $user->display_name )
9868
  : $user->user_login;
9869
 
9870
- $template_variables['ResetPassword.UserList'] .= SucuriScanTemplate::get_snippet('posthack-resetpassword', array(
9871
- 'ResetPassword.UserId' => $user->ID,
9872
- 'ResetPassword.Username' => SucuriScan::escape( $user->user_login ),
9873
- 'ResetPassword.Displayname' => SucuriScan::escape( $user->display_name ),
9874
- 'ResetPassword.DisplayUsername' => SucuriScan::escape( $display_username ),
9875
- 'ResetPassword.Email' => SucuriScan::escape( $user->user_email ),
9876
- 'ResetPassword.Registered' => $user->user_registered_formatted,
9877
- 'ResetPassword.Roles' => @implode( ', ', $user->roles ),
9878
- 'ResetPassword.CssClass' => $css_class,
9879
- ));
9880
-
9881
- $counter += 1;
 
 
9882
  }
9883
  }
9884
 
9885
- return SucuriScanTemplate::get_section( 'posthack-resetpassword', $template_variables );
9886
  }
9887
 
9888
  /**
@@ -9891,37 +10354,38 @@ function sucuriscan_posthack_users( $process_form = false ){
9891
  * @param $process_form Whether a form was submitted or not.
9892
  * @return void
9893
  */
9894
- function sucuriscan_reset_user_password( $process_form = false ){
9895
- if ( $process_form && SucuriScanRequest::post( ':reset_password' ) ) {
9896
- $user_identifiers = SucuriScanRequest::post( 'user_ids', '_array' );
 
9897
  $pwd_changed = array();
9898
  $pwd_not_changed = array();
9899
 
9900
- if ( is_array( $user_identifiers ) && ! empty($user_identifiers) ) {
9901
- arsort( $user_identifiers );
9902
 
9903
- foreach ( $user_identifiers as $user_id ) {
9904
- $user_id = intval( $user_id );
9905
 
9906
- if ( SucuriScanEvent::set_new_password( $user_id ) ) {
9907
  $pwd_changed[] = $user_id;
9908
  } else {
9909
  $pwd_not_changed[] = $user_id;
9910
  }
9911
  }
9912
 
9913
- if ( ! empty($pwd_changed) ) {
9914
- $message = 'Password changed for user identifiers <code>' . @implode( ', ', $pwd_changed ) . '</code>';
9915
 
9916
- SucuriScanEvent::report_notice_event( $message );
9917
- SucuriScanInterface::info( $message );
9918
  }
9919
 
9920
- if ( ! empty($pwd_not_changed) ) {
9921
- SucuriScanInterface::error( 'Password change failed for users: ' . implode( ', ', $pwd_not_changed ) );
9922
  }
9923
  } else {
9924
- SucuriScanInterface::error( 'You did not select a user from the list.' );
9925
  }
9926
  }
9927
  }
@@ -9932,19 +10396,20 @@ function sucuriscan_reset_user_password( $process_form = false ){
9932
  * @param boolean $process_form Whether a form was submitted or not.
9933
  * @return void
9934
  */
9935
- function sucuriscan_posthack_plugins( $process_form = false ){
9936
- $template_variables = array(
 
9937
  'ResetPlugin.PluginList' => '',
9938
  'ResetPlugin.CacheLifeTime' => 'unknown',
9939
  );
9940
 
9941
- if ( defined( 'SUCURISCAN_GET_PLUGINS_LIFETIME' ) ) {
9942
- $template_variables['ResetPlugin.CacheLifeTime'] = SUCURISCAN_GET_PLUGINS_LIFETIME;
9943
  }
9944
 
9945
- sucuriscan_posthack_reinstall_plugins( $process_form );
9946
 
9947
- return SucuriScanTemplate::get_section( 'posthack-resetplugins', $template_variables );
9948
  }
9949
 
9950
  /**
@@ -9952,32 +10417,35 @@ function sucuriscan_posthack_plugins( $process_form = false ){
9952
  *
9953
  * @return string HTML code for a table with the plugins metadata.
9954
  */
9955
- function sucuriscan_posthack_plugins_ajax(){
9956
- if ( SucuriScanRequest::post( 'form_action' ) == 'get_plugins_data' ) {
9957
- $all_plugins = SucuriScanAPI::get_plugins();
 
9958
  $response = '';
9959
  $counter = 0;
9960
 
9961
- foreach ( $all_plugins as $plugin_path => $plugin_data ) {
9962
  $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
9963
  $plugin_type_class = ( $plugin_data['PluginType'] == 'free' ) ? 'primary' : 'warning';
9964
  $input_disabled = ( $plugin_data['PluginType'] == 'free' ) ? '' : 'disabled="disabled"';
9965
  $plugin_status = $plugin_data['IsPluginActive'] ? 'active' : 'not active';
9966
  $plugin_status_class = $plugin_data['IsPluginActive'] ? 'success' : 'default';
9967
 
9968
- $response .= SucuriScanTemplate::get_snippet('posthack-resetplugins', array(
9969
- 'ResetPlugin.CssClass' => $css_class,
9970
- 'ResetPlugin.Disabled' => $input_disabled,
9971
- 'ResetPlugin.PluginPath' => SucuriScan::escape( $plugin_path ),
9972
- 'ResetPlugin.Plugin' => SucuriScan::excerpt( $plugin_data['Name'], 35 ),
9973
- 'ResetPlugin.Version' => $plugin_data['Version'],
9974
- 'ResetPlugin.Type' => $plugin_data['PluginType'],
9975
- 'ResetPlugin.TypeClass' => $plugin_type_class,
9976
- 'ResetPlugin.Status' => $plugin_status,
9977
- 'ResetPlugin.StatusClass' => $plugin_status_class,
9978
- ));
9979
-
9980
- $counter += 1;
 
 
9981
  }
9982
 
9983
  print( $response );
@@ -9994,12 +10462,13 @@ function sucuriscan_posthack_plugins_ajax(){
9994
  * @param boolean $process_form Whether a form was submitted or not.
9995
  * @return void
9996
  */
9997
- function sucuriscan_posthack_reinstall_plugins( $process_form = false ){
9998
- if ( $process_form && isset($_POST['sucuriscan_reset_plugins']) ) {
9999
- include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
10000
- include_once( ABSPATH . 'wp-admin/includes/plugin-install.php' ); // For plugins_api.
 
10001
 
10002
- if ( $plugin_list = SucuriScanRequest::post( 'plugin_path', '_array' ) ) {
10003
  // Create an instance of the FileInfo interface.
10004
  $file_info = new SucuriScanFileInfo();
10005
  $file_info->ignore_files = false;
@@ -10007,37 +10476,37 @@ function sucuriscan_posthack_reinstall_plugins( $process_form = false ){
10007
  $file_info->skip_directories = false;
10008
 
10009
  // Get (possible) cached information from the installed plugins.
10010
- $all_plugins = SucuriScanAPI::get_plugins();
10011
 
10012
  // Loop through all the installed plugins.
10013
- foreach ( $_POST['plugin_path'] as $plugin_path ) {
10014
- if ( array_key_exists( $plugin_path, $all_plugins ) ) {
10015
  $plugin_data = $all_plugins[ $plugin_path ];
10016
 
10017
  // Check if the plugin can be downloaded from the free market.
10018
- if ( $plugin_data['IsFreePlugin'] === true ) {
10019
- $plugin_info = SucuriScanAPI::get_remote_plugin_data( $plugin_data['RepositoryName'] );
10020
 
10021
- if ( $plugin_info ) {
10022
  // First, remove all files/sub-folders from the plugin's directory.
10023
- if ( substr_count( $plugin_path, '/' ) >= 1 ) {
10024
- $plugin_directory = dirname( WP_PLUGIN_DIR . '/' . $plugin_path );
10025
- $file_info->remove_directory_tree( $plugin_directory );
10026
  }
10027
 
10028
  // Install a fresh copy of the plugin's files.
10029
  $upgrader_skin = new Plugin_Installer_Skin();
10030
- $upgrader = new Plugin_Upgrader( $upgrader_skin );
10031
- $upgrader->install( $plugin_info->download_link );
10032
- SucuriScanEvent::report_notice_event( 'Plugin re-installed: ' . $plugin_path );
10033
  } else {
10034
- SucuriScanInterface::error( 'Could not establish a stable connection with the WordPress plugins market.' );
10035
  }
10036
  }
10037
  }
10038
  }
10039
  } else {
10040
- SucuriScanInterface::error( 'You did not select a free plugin to reinstall.' );
10041
  }
10042
  }
10043
  }
@@ -10049,26 +10518,26 @@ function sucuriscan_posthack_reinstall_plugins( $process_form = false ){
10049
  *
10050
  * @return string Last-logings for the administrator accounts.
10051
  */
10052
- function sucuriscan_lastlogins_page(){
 
10053
  SucuriScanInterface::check_permissions();
10054
 
10055
  // Reset the file with the last-logins logs.
10056
- if (
10057
- SucuriScanInterface::check_nonce()
10058
- && SucuriScanRequest::post( ':reset_lastlogins' ) !== false
10059
  ) {
10060
  $file_path = sucuriscan_lastlogins_datastore_filepath();
10061
 
10062
- if ( unlink( $file_path ) ) {
10063
  sucuriscan_lastlogins_datastore_exists();
10064
- SucuriScanInterface::info( 'Last-Logins logs were reset successfully.' );
10065
  } else {
10066
- SucuriScanInterface::error( 'Could not reset the last-logins logs.' );
10067
  }
10068
  }
10069
 
10070
  // Page pseudo-variables initialization.
10071
- $template_variables = array(
10072
  'PageTitle' => 'Last Logins',
10073
  'LastLogins.Admins' => sucuriscan_lastlogins_admins(),
10074
  'LastLogins.AllUsers' => sucuriscan_lastlogins_all(),
@@ -10076,64 +10545,70 @@ function sucuriscan_lastlogins_page(){
10076
  'FailedLogins' => sucuriscan_failed_logins_panel(),
10077
  );
10078
 
10079
- echo SucuriScanTemplate::get_template( 'lastlogins', $template_variables );
10080
  }
10081
 
10082
  /**
10083
  * List all the user administrator accounts.
10084
  *
10085
- * @see http://codex.wordpress.org/Class_Reference/WP_User_Query
10086
  *
10087
  * @return void
10088
  */
10089
- function sucuriscan_lastlogins_admins(){
 
10090
  // Page pseudo-variables initialization.
10091
- $template_variables = array(
10092
  'AdminUsers.List' => '',
10093
  );
10094
 
10095
- $user_query = new WP_User_Query( array( 'role' => 'Administrator' ) );
10096
  $admins = $user_query->get_results();
10097
 
10098
- foreach ( (array) $admins as $admin ) {
10099
- $last_logins = sucuriscan_get_logins( 5, 0, $admin->ID );
10100
  $admin->lastlogins = $last_logins['entries'];
10101
 
10102
  $user_snippet = array(
10103
- 'AdminUsers.Username' => SucuriScan::escape( $admin->user_login ),
10104
- 'AdminUsers.Email' => SucuriScan::escape( $admin->user_email ),
10105
  'AdminUsers.LastLogins' => '',
10106
- 'AdminUsers.RegisteredAt' => 'Undefined',
10107
- 'AdminUsers.UserURL' => admin_url( 'user-edit.php?user_id='.$admin->ID ),
10108
  'AdminUsers.NoLastLogins' => 'visible',
10109
  'AdminUsers.NoLastLoginsTable' => 'hidden',
10110
  );
10111
 
10112
- if ( ! empty($admin->lastlogins) ) {
10113
  $user_snippet['AdminUsers.NoLastLogins'] = 'hidden';
10114
  $user_snippet['AdminUsers.NoLastLoginsTable'] = 'visible';
10115
  $user_snippet['AdminUsers.RegisteredAt'] = 'Unknown';
10116
  $counter = 0;
10117
 
10118
- foreach ( $admin->lastlogins as $i => $lastlogin ) {
10119
- if ( $i == 0 ) {
10120
- $user_snippet['AdminUsers.RegisteredAt'] = SucuriScan::datetime( $lastlogin->user_registered_timestamp );
 
 
10121
  }
10122
 
10123
- $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
10124
- $user_snippet['AdminUsers.LastLogins'] .= SucuriScanTemplate::get_snippet('lastlogins-admins-lastlogin', array(
10125
- 'AdminUsers.RemoteAddr' => SucuriScan::escape( $lastlogin->user_remoteaddr ),
10126
- 'AdminUsers.Datetime' => SucuriScan::datetime( $lastlogin->user_lastlogin_timestamp ),
10127
- 'AdminUsers.CssClass' => $css_class,
10128
- ));
10129
- $counter += 1;
 
 
 
10130
  }
10131
  }
10132
 
10133
- $template_variables['AdminUsers.List'] .= SucuriScanTemplate::get_snippet( 'lastlogins-admins', $user_snippet );
10134
  }
10135
 
10136
- return SucuriScanTemplate::get_section( 'lastlogins-admins', $template_variables );
10137
  }
10138
 
10139
  /**
@@ -10143,12 +10618,13 @@ function sucuriscan_lastlogins_admins(){
10143
  *
10144
  * @return string Last-logings for all user accounts.
10145
  */
10146
- function sucuriscan_lastlogins_all(){
 
10147
  $max_per_page = SUCURISCAN_LASTLOGINS_USERSLIMIT;
10148
- $page_number = SucuriScanTemplate::get_page_number();
10149
  $offset = ($max_per_page * $page_number) - $max_per_page;
10150
 
10151
- $template_variables = array(
10152
  'UserList' => '',
10153
  'UserList.Limit' => $max_per_page,
10154
  'UserList.Total' => 0,
@@ -10157,59 +10633,60 @@ function sucuriscan_lastlogins_all(){
10157
  'UserList.NoItemsVisibility' => 'visible',
10158
  );
10159
 
10160
- if ( ! sucuriscan_lastlogins_datastore_is_writable() ) {
10161
- SucuriScanInterface::error( 'Last-logins datastore file is not writable: <code>'.sucuriscan_lastlogins_datastore_filepath().'</code>' );
 
10162
  }
10163
 
10164
  $counter = 0;
10165
- $last_logins = sucuriscan_get_logins( $max_per_page, $offset );
10166
- $template_variables['UserList.Total'] = $last_logins['total'];
10167
 
10168
- if ( $last_logins['total'] > $max_per_page ) {
10169
- $template_variables['UserList.PaginationVisibility'] = 'visible';
10170
  }
10171
 
10172
- if ( $last_logins['total'] > 0 ) {
10173
- $template_variables['UserList.NoItemsVisibility'] = 'hidden';
10174
  }
10175
 
10176
- foreach ( $last_logins['entries'] as $user ) {
10177
- $counter += 1;
10178
- $css_class = ( $counter % 2 == 0 ) ? 'alternate' : '';
10179
 
10180
  $user_dataset = array(
10181
  'UserList.Number' => $user->line_num,
10182
  'UserList.UserId' => $user->user_id,
10183
- 'UserList.Username' => '<em>Unknown</em>',
10184
  'UserList.Displayname' => '',
10185
  'UserList.Email' => '',
10186
  'UserList.Registered' => '',
10187
- 'UserList.RemoteAddr' => SucuriScan::escape( $user->user_remoteaddr ),
10188
- 'UserList.Hostname' => SucuriScan::escape( $user->user_hostname ),
10189
- 'UserList.Datetime' => SucuriScan::escape( $user->user_lastlogin ),
10190
- 'UserList.TimeAgo' => SucuriScan::time_ago( $user->user_lastlogin ),
10191
- 'UserList.UserURL' => admin_url( 'user-edit.php?user_id='.$user->user_id ),
10192
  'UserList.CssClass' => $css_class,
10193
  );
10194
 
10195
- if ( $user->user_exists ) {
10196
- $user_dataset['UserList.Username'] = SucuriScan::escape( $user->user_login );
10197
- $user_dataset['UserList.Displayname'] = SucuriScan::escape( $user->display_name );
10198
- $user_dataset['UserList.Email'] = SucuriScan::escape( $user->user_email );
10199
- $user_dataset['UserList.Registered'] = SucuriScan::escape( $user->user_registered );
10200
  }
10201
 
10202
- $template_variables['UserList'] .= SucuriScanTemplate::get_snippet( 'lastlogins-all', $user_dataset );
 
10203
  }
10204
 
10205
  // Generate the pagination for the list.
10206
- $template_variables['UserList.Pagination'] = SucuriScanTemplate::get_pagination(
10207
  '%%SUCURI.URL.Lastlogins%%',
10208
  $last_logins['total'],
10209
  $max_per_page
10210
  );
10211
 
10212
- return SucuriScanTemplate::get_section( 'lastlogins-all', $template_variables );
10213
  }
10214
 
10215
  /**
@@ -10217,8 +10694,9 @@ function sucuriscan_lastlogins_all(){
10217
  *
10218
  * @return string Absolute filepath where the user's last login information is stored.
10219
  */
10220
- function sucuriscan_lastlogins_datastore_filepath(){
10221
- return SucuriScan::datastore_folder_path( 'sucuri-lastlogins.php' );
 
10222
  }
10223
 
10224
  /**
@@ -10227,7 +10705,8 @@ function sucuriscan_lastlogins_datastore_filepath(){
10227
  *
10228
  * @return string Absolute filepath where the user's last login information is stored.
10229
  */
10230
- function sucuriscan_lastlogins_datastore_exists(){
 
10231
  $fpath = sucuriscan_lastlogins_datastore_filepath();
10232
 
10233
  if (!file_exists($fpath)) {
@@ -10247,15 +10726,16 @@ function sucuriscan_lastlogins_datastore_exists(){
10247
  *
10248
  * @return boolean Whether the user's last login datastore file is writable or not.
10249
  */
10250
- function sucuriscan_lastlogins_datastore_is_writable(){
 
10251
  $datastore_filepath = sucuriscan_lastlogins_datastore_exists();
10252
 
10253
- if ( $datastore_filepath ) {
10254
- if ( ! is_writable( $datastore_filepath ) ) {
10255
- @chmod( $datastore_filepath, 0644 );
10256
  }
10257
 
10258
- if ( is_writable( $datastore_filepath ) ) {
10259
  return $datastore_filepath;
10260
  }
10261
  }
@@ -10269,42 +10749,44 @@ function sucuriscan_lastlogins_datastore_is_writable(){
10269
  *
10270
  * @return boolean Whether the user's last login datastore file is readable or not.
10271
  */
10272
- function sucuriscan_lastlogins_datastore_is_readable(){
 
10273
  $datastore_filepath = sucuriscan_lastlogins_datastore_exists();
10274
 
10275
- if ( $datastore_filepath && is_readable( $datastore_filepath ) ) {
10276
  return $datastore_filepath;
10277
  }
10278
 
10279
  return false;
10280
  }
10281
 
10282
- if ( ! function_exists( 'sucuri_set_lastlogin' ) ) {
10283
  /**
10284
  * Add a new user session to the list of last user logins.
10285
  *
10286
  * @param string $user_login The name of the user account involved in the operation.
10287
  * @return void
10288
  */
10289
- function sucuriscan_set_lastlogin( $user_login = '' ){
 
10290
  $datastore_filepath = sucuriscan_lastlogins_datastore_is_writable();
10291
 
10292
- if ( $datastore_filepath ) {
10293
- $current_user = get_user_by( 'login', $user_login );
10294
  $remote_addr = SucuriScan::get_remote_addr();
10295
 
10296
  $login_info = array(
10297
  'user_id' => $current_user->ID,
10298
  'user_login' => $current_user->user_login,
10299
  'user_remoteaddr' => $remote_addr,
10300
- 'user_hostname' => @gethostbyaddr( $remote_addr ),
10301
- 'user_lastlogin' => current_time( 'mysql' )
10302
  );
10303
 
10304
- @file_put_contents( $datastore_filepath, json_encode( $login_info )."\n", FILE_APPEND );
10305
  }
10306
  }
10307
- add_action( 'wp_login', 'sucuriscan_set_lastlogin', 50 );
10308
  }
10309
 
10310
  /**
@@ -10318,18 +10800,19 @@ if ( ! function_exists( 'sucuri_set_lastlogin' ) ) {
10318
  * @param integer $user_id Optional user identifier to filter the results.
10319
  * @return array The list of all the user logins, and total of entries registered.
10320
  */
10321
- function sucuriscan_get_logins( $limit = 10, $offset = 0, $user_id = 0 ){
 
10322
  $datastore_filepath = sucuriscan_lastlogins_datastore_is_readable();
10323
  $last_logins = array(
10324
  'total' => 0,
10325
  'entries' => array(),
10326
  );
10327
 
10328
- if ( $datastore_filepath ) {
10329
  $parsed_lines = 0;
10330
- $data_lines = SucuriScanFileInfo::file_lines( $datastore_filepath );
10331
 
10332
- if ( $data_lines ) {
10333
  /**
10334
  * This count will not be 100% accurate considering that we are checking the
10335
  * syntax of each line in the loop bellow, there may be some lines without the
@@ -10338,11 +10821,11 @@ function sucuriscan_get_logins( $limit = 10, $offset = 0, $user_id = 0 ){
10338
  *
10339
  * @var integer
10340
  */
10341
- $total_lines = count( $data_lines );
10342
  $last_logins['total'] = $total_lines;
10343
 
10344
  // Get a list with the latest entries in the first positions.
10345
- $reversed_lines = array_reverse( $data_lines );
10346
 
10347
  /**
10348
  * Only the user accounts with administrative privileges can see the logs of all
@@ -10351,44 +10834,44 @@ function sucuriscan_get_logins( $limit = 10, $offset = 0, $user_id = 0 ){
10351
  * @var object
10352
  */
10353
  $current_user = wp_get_current_user();
10354
- $is_admin_user = (bool) current_user_can( 'manage_options' );
10355
 
10356
- for ( $i = $offset; $i < $total_lines; $i++ ) {
10357
- $line = $reversed_lines[ $i ] ? trim( $reversed_lines[ $i ] ) : '';
10358
 
10359
  // Check if the data is serialized (which we will consider as insecure).
10360
- if ( SucuriScan::is_serialized( $line ) ) {
10361
- $last_login = @unserialize( $line ); // TODO: Remove after version 1.7.5
10362
  } else {
10363
- $last_login = @json_decode( $line, true );
10364
  }
10365
 
10366
- if ( $last_login ) {
10367
- $last_login['user_lastlogin_timestamp'] = strtotime( $last_login['user_lastlogin'] );
10368
  $last_login['user_registered_timestamp'] = 0;
10369
 
10370
  // Only administrators can see all login stats.
10371
- if ( ! $is_admin_user && $current_user->user_login != $last_login['user_login'] ) {
10372
  continue;
10373
  }
10374
 
10375
  // Filter the user identifiers using the value passed tot his function.
10376
- if ( $user_id > 0 && $last_login['user_id'] != $user_id ) {
10377
  continue;
10378
  }
10379
 
10380
  // Get the WP_User object and add extra information from the last-login data.
10381
  $last_login['user_exists'] = false;
10382
- $user_account = get_userdata( $last_login['user_id'] );
10383
 
10384
- if ( $user_account ) {
10385
  $last_login['user_exists'] = true;
10386
 
10387
- foreach ( $user_account->data as $var_name => $var_value ) {
10388
  $last_login[ $var_name ] = $var_value;
10389
 
10390
- if ( $var_name == 'user_registered' ) {
10391
- $last_login['user_registered_timestamp'] = strtotime( $var_value );
10392
  }
10393
  }
10394
  }
@@ -10400,8 +10883,8 @@ function sucuriscan_get_logins( $limit = 10, $offset = 0, $user_id = 0 ){
10400
  $last_logins['total'] -= 1;
10401
  }
10402
 
10403
- if ( preg_match( '/^[0-9]+$/', $limit ) && $limit > 0 ) {
10404
- if ( $parsed_lines >= $limit ) {
10405
  break;
10406
  }
10407
  }
@@ -10412,7 +10895,7 @@ function sucuriscan_get_logins( $limit = 10, $offset = 0, $user_id = 0 ){
10412
  return $last_logins;
10413
  }
10414
 
10415
- if ( ! function_exists( 'sucuri_login_redirect' ) ) {
10416
  /**
10417
  * Hook for the wp-login action to redirect the user to a specific URL after
10418
  * his successfully login to the administrator interface.
@@ -10422,57 +10905,57 @@ if ( ! function_exists( 'sucuri_login_redirect' ) ) {
10422
  * @param boolean $user WordPress user object with the information of the account involved in the operation.
10423
  * @return string URL where the browser must be redirected to.
10424
  */
10425
- function sucuriscan_login_redirect( $redirect_to = '', $request = null, $user = false ){
10426
- $login_url = ! empty($redirect_to) ? $redirect_to : admin_url();
 
10427
 
10428
- if (
10429
- $user instanceof WP_User
10430
- && in_array( 'administrator', $user->roles )
10431
- && SucuriScanOption::is_enabled( ':lastlogin_redirection' )
10432
  ) {
10433
- $login_url = add_query_arg( 'sucuriscan_lastlogin', 1, $login_url );
10434
  }
10435
 
10436
  return $login_url;
10437
  }
10438
 
10439
- if ( SucuriScanOption::is_enabled( ':lastlogin_redirection' ) ) {
10440
- add_filter( 'login_redirect', 'sucuriscan_login_redirect', 10, 3 );
10441
  }
10442
  }
10443
 
10444
- if ( ! function_exists( 'sucuri_get_user_lastlogin' ) ) {
10445
  /**
10446
  * Display the last user login at the top of the admin interface.
10447
  *
10448
  * @return void
10449
  */
10450
- function sucuriscan_get_user_lastlogin(){
10451
- if (
10452
- current_user_can( 'manage_options' )
10453
- && SucuriScanRequest::get( ':lastlogin', '1' )
10454
  ) {
10455
  $current_user = wp_get_current_user();
10456
 
10457
  // Select the penultimate entry, not the last one.
10458
- $last_logins = sucuriscan_get_logins( 2, 0, $current_user->ID );
10459
 
10460
- if ( isset($last_logins['entries'][1]) ) {
10461
  $row = $last_logins['entries'][1];
10462
-
10463
- $lastlogin_message = sprintf(
10464
- 'Last time you logged in was at <code>%s</code> from <code>%s</code> - <code>%s</code>',
10465
- SucuriScan::datetime( $row->user_lastlogin_timestamp ),
10466
- $row->user_remoteaddr,
10467
- $row->user_hostname
10468
  );
10469
- $lastlogin_message .= chr( 32 ).'(<a href="'.SucuriScanTemplate::get_url( 'lastlogins' ).'">view all logs</a>)';
10470
- SucuriScanInterface::info( $lastlogin_message );
10471
  }
10472
  }
10473
  }
10474
 
10475
- add_action( 'admin_notices', 'sucuriscan_get_user_lastlogin' );
10476
  }
10477
 
10478
  /**
@@ -10480,38 +10963,42 @@ if ( ! function_exists( 'sucuri_get_user_lastlogin' ) ) {
10480
  *
10481
  * @return string The HTML code displaying a list of all the users logged in at the moment.
10482
  */
10483
- function sucuriscan_loggedin_users_panel(){
 
10484
  // Get user logged in list.
10485
- $template_variables = array(
10486
  'LoggedInUsers.List' => '',
10487
  'LoggedInUsers.Total' => 0,
10488
  );
10489
 
10490
- $logged_in_users = sucuriscan_get_online_users( true );
10491
 
10492
- if ( is_array( $logged_in_users ) && ! empty($logged_in_users) ) {
10493
- $template_variables['LoggedInUsers.Total'] = count( $logged_in_users );
10494
  $counter = 0;
10495
 
10496
- foreach ( (array) $logged_in_users as $logged_in_user ) {
10497
- $counter += 1;
10498
- $logged_in_user['last_activity_datetime'] = SucuriScan::datetime( $logged_in_user['last_activity'] );
10499
- $logged_in_user['user_registered_datetime'] = SucuriScan::datetime( strtotime( $logged_in_user['user_registered'] ) );
10500
-
10501
- $template_variables['LoggedInUsers.List'] .= SucuriScanTemplate::get_snippet('lastlogins-loggedin', array(
10502
- 'LoggedInUsers.Id' => SucuriScan::escape( $logged_in_user['user_id'] ),
10503
- 'LoggedInUsers.UserURL' => admin_url( 'user-edit.php?user_id='.$logged_in_user['user_id'] ),
10504
- 'LoggedInUsers.UserLogin' => SucuriScan::escape( $logged_in_user['user_login'] ),
10505
- 'LoggedInUsers.UserEmail' => SucuriScan::escape( $logged_in_user['user_email'] ),
10506
- 'LoggedInUsers.LastActivity' => SucuriScan::escape( $logged_in_user['last_activity_datetime'] ),
10507
- 'LoggedInUsers.Registered' => SucuriScan::escape( $logged_in_user['user_registered_datetime'] ),
10508
- 'LoggedInUsers.RemoveAddr' => SucuriScan::escape( $logged_in_user['remote_addr'] ),
10509
- 'LoggedInUsers.CssClass' => ( $counter % 2 == 0 ) ? '' : 'alternate',
10510
- ));
 
 
 
10511
  }
10512
  }
10513
 
10514
- return SucuriScanTemplate::get_section( 'lastlogins-loggedin', $template_variables );
10515
  }
10516
 
10517
  /**
@@ -10520,21 +11007,22 @@ function sucuriscan_loggedin_users_panel(){
10520
  * @param boolean $add_current_user Whether the current user should be added to the list or not.
10521
  * @return array List of registered users currently in session.
10522
  */
10523
- function sucuriscan_get_online_users( $add_current_user = false ){
 
10524
  $users = array();
10525
 
10526
- if ( SucuriScan::is_multisite() ) {
10527
- $users = get_site_transient( 'online_users' );
10528
  } else {
10529
- $users = get_transient( 'online_users' );
10530
  }
10531
 
10532
  // If not online users but current user is logged in, add it to the list.
10533
- if ( empty($users) && $add_current_user ) {
10534
  $current_user = wp_get_current_user();
10535
 
10536
- if ( $current_user->ID > 0 ) {
10537
- sucuriscan_set_online_user( $current_user->user_login, $current_user );
10538
 
10539
  return sucuriscan_get_online_users();
10540
  }
@@ -10551,32 +11039,34 @@ function sucuriscan_get_online_users( $add_current_user = false ){
10551
  * @param array $logged_in_users List of registered users currently in session.
10552
  * @return boolean Either TRUE or FALSE representing the success or fail of the operation.
10553
  */
10554
- function sucuriscan_save_online_users( $logged_in_users = array() ){
 
10555
  $expiration = 30 * 60;
10556
 
10557
- if ( SucuriScan::is_multisite() ) {
10558
- return set_site_transient( 'online_users', $logged_in_users, $expiration );
10559
  } else {
10560
- return set_transient( 'online_users', $logged_in_users, $expiration );
10561
  }
10562
  }
10563
 
10564
- if ( ! function_exists( 'sucuriscan_unset_online_user_on_logout' ) ) {
10565
  /**
10566
  * Remove a logged in user from the list of registered users in session when
10567
  * the logout page is requested.
10568
  *
10569
  * @return void
10570
  */
10571
- function sucuriscan_unset_online_user_on_logout(){
 
10572
  $remote_addr = SucuriScan::get_remote_addr();
10573
  $current_user = wp_get_current_user();
10574
  $user_id = $current_user->ID;
10575
 
10576
- sucuriscan_unset_online_user( $user_id, $remote_addr );
10577
  }
10578
 
10579
- add_action( 'wp_logout', 'sucuriscan_unset_online_user_on_logout' );
10580
  }
10581
 
10582
  /**
@@ -10587,15 +11077,15 @@ if ( ! function_exists( 'sucuriscan_unset_online_user_on_logout' ) ) {
10587
  * @param integer $remote_addr IP address of the computer where the user logged in.
10588
  * @return boolean Either TRUE or FALSE representing the success or fail of the operation.
10589
  */
10590
- function sucuriscan_unset_online_user( $user_id = 0, $remote_addr = 0 ){
 
10591
  $logged_in_users = sucuriscan_get_online_users();
10592
 
10593
  // Remove the specified user identifier from the list.
10594
- if ( is_array( $logged_in_users ) && ! empty($logged_in_users) ) {
10595
- foreach ( $logged_in_users as $i => $user ) {
10596
- if (
10597
- $user['user_id'] == $user_id
10598
- && strcmp( $user['remote_addr'], $remote_addr ) == 0
10599
  ) {
10600
  unset($logged_in_users[ $i ]);
10601
  break;
@@ -10603,10 +11093,10 @@ function sucuriscan_unset_online_user( $user_id = 0, $remote_addr = 0 ){
10603
  }
10604
  }
10605
 
10606
- return sucuriscan_save_online_users( $logged_in_users );
10607
  }
10608
 
10609
- if ( ! function_exists( 'sucuriscan_set_online_user' ) ) {
10610
  /**
10611
  * Add an user account to the list of registered users in session.
10612
  *
@@ -10614,13 +11104,14 @@ if ( ! function_exists( 'sucuriscan_set_online_user' ) ) {
10614
  * @param boolean $user The WordPress object containing all the information associated to the user.
10615
  * @return void
10616
  */
10617
- function sucuriscan_set_online_user( $user_login = '', $user = false ){
10618
- if ( $user ) {
 
10619
  // Get logged in user information.
10620
  $current_user = ($user instanceof WP_User) ? $user : wp_get_current_user();
10621
  $current_user_id = $current_user->ID;
10622
  $remote_addr = SucuriScan::get_remote_addr();
10623
- $current_time = current_time( 'timestamp' );
10624
  $logged_in_users = sucuriscan_get_online_users();
10625
 
10626
  // Build the dataset array that will be stored in the transient variable.
@@ -10633,21 +11124,20 @@ if ( ! function_exists( 'sucuriscan_set_online_user' ) ) {
10633
  'remote_addr' => $remote_addr,
10634
  );
10635
 
10636
- if ( ! is_array( $logged_in_users ) || empty($logged_in_users) ) {
10637
  $logged_in_users = array( $current_user_info );
10638
- sucuriscan_save_online_users( $logged_in_users );
10639
  } else {
10640
  $do_nothing = false;
10641
  $update_existing = false;
10642
  $item_index = 0;
10643
 
10644
  // Check if the user is already in the logged-in-user list and update it if is necessary.
10645
- foreach ( $logged_in_users as $i => $user ) {
10646
- if (
10647
- $user['user_id'] == $current_user_id
10648
- && strcmp( $user['remote_addr'], $remote_addr ) == 0
10649
  ) {
10650
- if ( $user['last_activity'] < ($current_time - (15 * 60)) ) {
10651
  $update_existing = true;
10652
  $item_index = $i;
10653
  break;
@@ -10658,20 +11148,20 @@ if ( ! function_exists( 'sucuriscan_set_online_user' ) ) {
10658
  }
10659
  }
10660
 
10661
- if ( $update_existing ) {
10662
  $logged_in_users[ $item_index ] = $current_user_info;
10663
- sucuriscan_save_online_users( $logged_in_users );
10664
- } elseif ( $do_nothing ) {
10665
  // Do nothing.
10666
  } else {
10667
  $logged_in_users[] = $current_user_info;
10668
- sucuriscan_save_online_users( $logged_in_users );
10669
  }
10670
  }
10671
  }
10672
  }
10673
 
10674
- add_action( 'wp_login', 'sucuriscan_set_online_user', 10, 2 );
10675
  }
10676
 
10677
  /**
@@ -10679,7 +11169,8 @@ if ( ! function_exists( 'sucuriscan_set_online_user' ) ) {
10679
  *
10680
  * @return string A list with the failed logins occurred during the last hour.
10681
  */
10682
- function sucuriscan_failed_logins_panel(){
 
10683
  $template_variables = array(
10684
  'FailedLogins.List' => '',
10685
  'FailedLogins.Total' => '',
@@ -10692,91 +11183,88 @@ function sucuriscan_failed_logins_panel(){
10692
  );
10693
 
10694
  // Define variables for the pagination.
10695
- $page_number = SucuriScanTemplate::get_page_number();
10696
  $max_per_page = SUCURISCAN_MAX_PAGINATION_BUTTONS;
10697
- $page_offset = ( $page_number - 1 ) * $max_per_page;
10698
- $page_limit = ( $page_offset + $max_per_page );
10699
 
10700
- $max_failed_logins = SucuriScanOption::get_option( ':maximum_failed_logins' );
10701
- $notify_bruteforce_attack = SucuriScanOption::get_option( ':notify_bruteforce_attack' );
10702
  $failed_logins = sucuriscan_get_failed_logins();
10703
- $old_failed_logins = sucuriscan_get_failed_logins( true );
10704
 
10705
  // Merge the new and old failed logins.
10706
- if (
10707
- is_array( $old_failed_logins )
10708
- && ! empty($old_failed_logins)
10709
- ) {
10710
- if (
10711
- is_array( $failed_logins )
10712
- && ! empty($failed_logins)
10713
- ) {
10714
- $failed_logins = array_merge( $failed_logins, $old_failed_logins );
10715
  } else {
10716
  $failed_logins = $old_failed_logins;
10717
  }
10718
  }
10719
 
10720
- if ( $failed_logins ) {
10721
  $counter = 0;
10722
 
10723
- for ( $key = $page_offset; $key < $page_limit; $key++ ) {
10724
- if ( array_key_exists( $key, $failed_logins['entries'] ) ) {
10725
  $login_data = $failed_logins['entries'][ $key ];
10726
  $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
10727
- $wrong_user_password = '<span class="sucuriscan-label-default">hidden</span>';
 
10728
 
10729
- if ( sucuriscan_collect_wrong_passwords() === true ) {
10730
- if (
10731
- isset($login_data['user_password'])
10732
- && ! empty($login_data['user_password'])
10733
- ) {
10734
- $wrong_user_password = SucuriScan::escape( $login_data['user_password'] );
10735
  } else {
10736
- $wrong_user_password = '<span class="sucuriscan-label-info">empty</span>';
 
10737
  }
10738
  }
10739
 
10740
- $template_variables['FailedLogins.List'] .= SucuriScanTemplate::get_snippet('lastlogins-failedlogins', array(
10741
- 'FailedLogins.CssClass' => $css_class,
10742
- 'FailedLogins.Num' => $login_data['attempt_count'],
10743
- 'FailedLogins.Username' => SucuriScan::escape( $login_data['user_login'] ),
10744
- 'FailedLogins.Password' => $wrong_user_password,
10745
- 'FailedLogins.RemoteAddr' => SucuriScan::escape( $login_data['remote_addr'] ),
10746
- 'FailedLogins.Datetime' => SucuriScan::datetime( $login_data['attempt_time'] ),
10747
- 'FailedLogins.UserAgent' => SucuriScan::escape( $login_data['user_agent'] ),
10748
- ));
10749
-
10750
- $counter += 1;
 
 
 
10751
  }
10752
  }
10753
 
10754
- if ( $counter > 0 ) {
10755
  $template_variables['FailedLogins.NoItemsVisibility'] = 'hidden';
10756
  }
10757
 
10758
- $template_variables['FailedLogins.PaginationLinks'] = SucuriScanTemplate::get_pagination(
10759
  '%%SUCURI.URL.Lastlogins%%#failed-logins',
10760
  $failed_logins['count'],
10761
  $max_per_page
10762
  );
10763
 
10764
- if ( $failed_logins['count'] > $max_per_page ) {
10765
  $template_variables['FailedLogins.PaginationVisibility'] = 'visible';
10766
  }
10767
  }
10768
 
10769
  $template_variables['FailedLogins.MaxFailedLogins'] = $max_failed_logins;
10770
 
10771
- if ( $notify_bruteforce_attack == 'enabled' ) {
10772
  $template_variables['FailedLogins.WarningVisibility'] = 'hidden';
10773
  }
10774
 
10775
- if ( sucuriscan_collect_wrong_passwords() !== true ) {
10776
  $template_variables['FailedLogins.CollectPasswordsVisibility'] = 'hidden';
10777
  }
10778
 
10779
- return SucuriScanTemplate::get_section( 'lastlogins-failedlogins', $template_variables );
10780
  }
10781
 
10782
  /**
@@ -10784,8 +11272,9 @@ function sucuriscan_failed_logins_panel(){
10784
  *
10785
  * @return boolean TRUE if the password must be collected, FALSE otherwise.
10786
  */
10787
- function sucuriscan_collect_wrong_passwords(){
10788
- return SucuriScanOption::is_enabled( ':collect_wrong_passwords' );
 
10789
  }
10790
 
10791
  /**
@@ -10800,21 +11289,19 @@ function sucuriscan_collect_wrong_passwords(){
10800
  * @param boolean $reset Whether the file will be resetted or not.
10801
  * @return string The full (relative) path where the file is located.
10802
  */
10803
- function sucuriscan_failed_logins_datastore_path( $get_old_logs = false, $reset = false ){
 
10804
  $file_name = $get_old_logs ? 'sucuri-oldfailedlogins.php' : 'sucuri-failedlogins.php';
10805
- $datastore_path = SucuriScan::datastore_folder_path( $file_name );
10806
  $default_content = sucuriscan_failed_logins_default_content();
10807
 
10808
  // Create the file if it does not exists.
10809
- if ( ! file_exists( $datastore_path ) || $reset ) {
10810
- @file_put_contents( $datastore_path, $default_content, LOCK_EX );
10811
  }
10812
 
10813
  // Return the datastore path if the file exists (or was created).
10814
- if (
10815
- file_exists( $datastore_path )
10816
- && is_readable( $datastore_path )
10817
- ) {
10818
  return $datastore_path;
10819
  }
10820
 
@@ -10826,7 +11313,8 @@ function sucuriscan_failed_logins_datastore_path( $get_old_logs = false, $reset
10826
  *
10827
  * @return string Default content of the file.
10828
  */
10829
- function sucuriscan_failed_logins_default_content(){
 
10830
  $default_content = "<?php exit(0); ?>\n";
10831
 
10832
  return $default_content;
@@ -10843,15 +11331,16 @@ function sucuriscan_failed_logins_default_content(){
10843
  * @param boolean $get_old_logs Whether the old logs will be retrieved or not.
10844
  * @return array Information and entries gathered from the failed logins datastore file.
10845
  */
10846
- function sucuriscan_get_failed_logins( $get_old_logs = false ){
10847
- $datastore_path = sucuriscan_failed_logins_datastore_path( $get_old_logs );
 
10848
  $default_content = sucuriscan_failed_logins_default_content();
10849
- $default_content_n = substr_count( $default_content, "\n" );
10850
 
10851
- if ( $datastore_path ) {
10852
- $lines = SucuriScanFileInfo::file_lines( $datastore_path );
10853
 
10854
- if ( $lines ) {
10855
  $failed_logins = array(
10856
  'count' => 0,
10857
  'first_attempt' => 0,
@@ -10861,21 +11350,21 @@ function sucuriscan_get_failed_logins( $get_old_logs = false ){
10861
  );
10862
 
10863
  // Read and parse all the entries found in the datastore file.
10864
- $offset = count( $lines ) - 1;
10865
 
10866
- for ( $key = $offset; $key >= 0; $key-- ) {
10867
- $line = trim( $lines[ $key ] );
10868
- $login_data = @json_decode( $line, true );
10869
 
10870
- if ( is_array( $login_data ) ) {
10871
- $login_data['attempt_date'] = date( 'r', $login_data['attempt_time'] );
10872
  $login_data['attempt_count'] = ( $key + 1 );
10873
 
10874
- if ( ! $login_data['user_agent'] ) {
10875
  $login_data['user_agent'] = 'Unknown';
10876
  }
10877
 
10878
- if ( ! isset($login_data['user_password']) ) {
10879
  $login_data['user_password'] = '';
10880
  }
10881
 
@@ -10885,11 +11374,11 @@ function sucuriscan_get_failed_logins( $get_old_logs = false ){
10885
  }
10886
 
10887
  // Calculate the different time between the first and last attempt.
10888
- if ( $failed_logins['count'] > 0 ) {
10889
- $z = abs( $failed_logins['count'] - 1 );
10890
  $failed_logins['last_attempt'] = $failed_logins['entries'][ $z ]['attempt_time'];
10891
  $failed_logins['first_attempt'] = $failed_logins['entries'][0]['attempt_time'];
10892
- $failed_logins['diff_time'] = abs( $failed_logins['last_attempt'] - $failed_logins['first_attempt'] );
10893
 
10894
  return $failed_logins;
10895
  }
@@ -10909,15 +11398,16 @@ function sucuriscan_get_failed_logins( $get_old_logs = false ){
10909
  * @param string $wrong_password Wrong password used during the supposed attack.
10910
  * @return boolean Whether the information of the current failed login event was stored or not.
10911
  */
10912
- function sucuriscan_log_failed_login( $user_login = '', $wrong_password = '' ){
 
10913
  $datastore_path = sucuriscan_failed_logins_datastore_path();
10914
 
10915
  // Do not collect wrong passwords if it is not necessary.
10916
- if ( sucuriscan_collect_wrong_passwords() !== true ) {
10917
  $wrong_password = '';
10918
  }
10919
 
10920
- if ( $datastore_path ) {
10921
  $login_data = json_encode(array(
10922
  'user_login' => $user_login,
10923
  'user_password' => $wrong_password,
@@ -10926,9 +11416,15 @@ function sucuriscan_log_failed_login( $user_login = '', $wrong_password = '' ){
10926
  'user_agent' => SucuriScan::get_user_agent(),
10927
  ));
10928
 
10929
- $logged = @file_put_contents( $datastore_path, $login_data . "\n", FILE_APPEND );
 
 
 
 
10930
 
10931
- return $logged;
 
 
10932
  }
10933
 
10934
  return false;
@@ -10943,13 +11439,14 @@ function sucuriscan_log_failed_login( $user_login = '', $wrong_password = '' ){
10943
  * @param array $failed_logins Information and entries gathered from the failed logins datastore file.
10944
  * @return boolean Whether the report was sent via email or not.
10945
  */
10946
- function sucuriscan_report_failed_logins( $failed_logins = array() ){
10947
- if ( $failed_logins && $failed_logins['count'] > 0 ) {
 
10948
  $prettify_mails = SucuriScanMail::prettify_mails();
10949
  $collect_wrong_passwords = sucuriscan_collect_wrong_passwords();
10950
  $mail_content = '';
10951
 
10952
- if ( $prettify_mails ) {
10953
  $table_html = '<table border="1" cellspacing="0" cellpadding="0">';
10954
 
10955
  // Add the table headers.
@@ -10957,7 +11454,7 @@ function sucuriscan_report_failed_logins( $failed_logins = array() ){
10957
  $table_html .= '<tr>';
10958
  $table_html .= '<th>Username</th>';
10959
 
10960
- if ( $collect_wrong_passwords === true ) {
10961
  $table_html .= '<th>Password</th>';
10962
  }
10963
 
@@ -10970,16 +11467,16 @@ function sucuriscan_report_failed_logins( $failed_logins = array() ){
10970
  $table_html .= '<tbody>';
10971
  }
10972
 
10973
- foreach ( $failed_logins['entries'] as $login_data ) {
10974
- if ( $prettify_mails ) {
10975
  $table_html .= '<tr>';
10976
- $table_html .= '<td>' . esc_attr( $login_data['user_login'] ) . '</td>';
10977
 
10978
- if ( $collect_wrong_passwords === true ) {
10979
- $table_html .= '<td>' . esc_attr( $login_data['user_password'] ) . '</td>';
10980
  }
10981
 
10982
- $table_html .= '<td>' . esc_attr( $login_data['remote_addr'] ) . '</td>';
10983
  $table_html .= '<td>' . $login_data['attempt_time'] . '</td>';
10984
  $table_html .= '<td>' . $login_data['attempt_date'] . '</td>';
10985
  $table_html .= '</tr>';
@@ -10987,7 +11484,7 @@ function sucuriscan_report_failed_logins( $failed_logins = array() ){
10987
  $mail_content .= "\n";
10988
  $mail_content .= 'Username: ' . $login_data['user_login'] . "\n";
10989
 
10990
- if ( $collect_wrong_passwords === true ) {
10991
  $mail_content .= 'Password: ' . $login_data['user_password'] . "\n";
10992
  }
10993
 
@@ -10997,13 +11494,13 @@ function sucuriscan_report_failed_logins( $failed_logins = array() ){
10997
  }
10998
  }
10999
 
11000
- if ( $prettify_mails ) {
11001
  $table_html .= '</tbody>';
11002
  $table_html .= '</table>';
11003
  $mail_content = $table_html;
11004
  }
11005
 
11006
- if ( SucuriScanEvent::notify_event( 'bruteforce_attack', $mail_content ) ) {
11007
  sucuriscan_reset_failed_logins();
11008
 
11009
  return true;
@@ -11021,12 +11518,13 @@ function sucuriscan_report_failed_logins( $failed_logins = array() ){
11021
  *
11022
  * @return boolean Whether the datastore file was resetted or not.
11023
  */
11024
- function sucuriscan_reset_failed_logins(){
11025
- $datastore_path = SucuriScan::datastore_folder_path( 'sucuri-failedlogins.php' );
11026
- $datastore_backup_path = sucuriscan_failed_logins_datastore_path( true, false );
 
11027
  $default_content = sucuriscan_failed_logins_default_content();
11028
- $current_content = @file_get_contents( $datastore_path );
11029
- $current_content = str_replace( $default_content, '', $current_content );
11030
 
11031
  @file_put_contents(
11032
  $datastore_backup_path,
@@ -11034,7 +11532,7 @@ function sucuriscan_reset_failed_logins(){
11034
  FILE_APPEND
11035
  );
11036
 
11037
- return (bool) sucuriscan_failed_logins_datastore_path( false, true );
11038
  }
11039
 
11040
  /**
@@ -11045,596 +11543,396 @@ function sucuriscan_reset_failed_logins(){
11045
  * @param boolean $page_nonce True if the nonce is valid, False otherwise.
11046
  * @return void
11047
  */
11048
- function sucuriscan_settings_form_submissions( $page_nonce = null ){
 
11049
  global $sucuriscan_schedule_allowed,
11050
  $sucuriscan_interface_allowed,
11051
  $sucuriscan_notify_options,
11052
- $sucuriscan_emails_per_hour,
11053
- $sucuriscan_maximum_failed_logins,
11054
  $sucuriscan_email_subjects;
11055
 
11056
  // Use this conditional to avoid double checking.
11057
- if ( is_null( $page_nonce ) ) {
11058
  $page_nonce = SucuriScanInterface::check_nonce();
11059
  }
11060
 
11061
- if ( $page_nonce ) {
11062
  // Save API key after it was recovered by the administrator.
11063
- if ( $api_key = SucuriScanRequest::post( ':manual_api_key' ) ) {
11064
- SucuriScanAPI::set_plugin_key( $api_key, true );
11065
  SucuriScanEvent::schedule_task();
11066
- SucuriScanEvent::report_info_event( 'Sucuri API key was added manually.' );
11067
  }
11068
 
11069
  // Remove API key from the local storage.
11070
- if ( SucuriScanRequest::post( ':remove_api_key' ) !== false ) {
11071
- SucuriScanAPI::set_plugin_key( '' );
11072
- wp_clear_scheduled_hook( 'sucuriscan_scheduled_scan' );
11073
- SucuriScanEvent::report_critical_event( 'Sucuri API key was deleted.' );
11074
- SucuriScanEvent::notify_event( 'plugin_change', 'Sucuri API key removed' );
11075
  }
11076
 
11077
  // Enable or disable the filesystem scanner.
11078
- if ( $fs_scanner = SucuriScanRequest::post( ':fs_scanner', '(en|dis)able' ) ) {
11079
  $action_d = $fs_scanner . 'd';
11080
  $message = 'Main file system scanner was <code>' . $action_d . '</code>';
11081
 
11082
- SucuriScanOption::update_option( ':fs_scanner', $action_d );
11083
- SucuriScanEvent::report_auto_event( $message );
11084
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11085
- SucuriScanInterface::info( $message );
11086
- }
11087
-
11088
- // Enable or disable the filesystem scanner for modified files.
11089
- if ( $scan_modfiles = SucuriScanRequest::post( ':scan_modfiles', '(en|dis)able' ) ) {
11090
- $action_d = $scan_modfiles . 'd';
11091
- $message = 'File system scanner for modified files was <code>' . $action_d . '</code>';
11092
-
11093
- SucuriScanOption::update_option( ':scan_modfiles', $action_d );
11094
- SucuriScanEvent::report_auto_event( $message );
11095
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11096
- SucuriScanInterface::info( $message );
11097
  }
11098
 
11099
  // Enable or disable the filesystem scanner for file integrity.
11100
- if ( $scan_checksums = SucuriScanRequest::post( ':scan_checksums', '(en|dis)able' ) ) {
11101
  $action_d = $scan_checksums . 'd';
11102
  $message = 'File system scanner for file integrity was <code>' . $action_d . '</code>';
11103
 
11104
- SucuriScanOption::update_option( ':scan_checksums', $action_d );
11105
- SucuriScanEvent::report_auto_event( $message );
11106
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11107
- SucuriScanInterface::info( $message );
11108
  }
11109
 
11110
  // Enable or disable the filesystem scanner for error logs.
11111
- if ( $ignore_scanning = SucuriScanRequest::post( ':ignore_scanning', '(en|dis)able' ) ) {
11112
  $action_d = $ignore_scanning . 'd';
11113
  $message = 'File system scanner rules to ignore directories was <code>' . $action_d . '</code>';
11114
 
11115
- SucuriScanOption::update_option( ':ignore_scanning', $action_d );
11116
- SucuriScanEvent::report_auto_event( $message );
11117
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11118
- SucuriScanInterface::info( $message );
11119
  }
11120
 
11121
  // Enable or disable the filesystem scanner for error logs.
11122
- if ( $scan_errorlogs = SucuriScanRequest::post( ':scan_errorlogs', '(en|dis)able' ) ) {
11123
  $action_d = $scan_errorlogs . 'd';
11124
  $message = 'File system scanner for error logs was <code>' . $action_d . '</code>';
11125
 
11126
- SucuriScanOption::update_option( ':scan_errorlogs', $action_d );
11127
- SucuriScanEvent::report_auto_event( $message );
11128
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11129
- SucuriScanInterface::info( $message );
11130
  }
11131
 
11132
  // Enable or disable the error logs parsing.
11133
- if ( $parse_errorlogs = SucuriScanRequest::post( ':parse_errorlogs', '(en|dis)able' ) ) {
11134
  $action_d = $parse_errorlogs . 'd';
11135
  $message = 'Analysis of main error log file was <code>' . $action_d . '</code>';
11136
 
11137
- SucuriScanOption::update_option( ':parse_errorlogs', $action_d );
11138
- SucuriScanEvent::report_auto_event( $message );
11139
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11140
- SucuriScanInterface::info( $message );
11141
  }
11142
 
11143
  // Enable or disable the SiteCheck scanner and the malware scan page.
11144
- if ( $sitecheck_scanner = SucuriScanRequest::post( ':sitecheck_scanner', '(en|dis)able' ) ) {
11145
  $action_d = $sitecheck_scanner . 'd';
11146
  $message = 'SiteCheck malware and blacklist scanner was <code>' . $action_d . '</code>';
11147
 
11148
- SucuriScanOption::update_option( ':sitecheck_scanner', $action_d );
11149
- SucuriScanEvent::report_auto_event( $message );
11150
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11151
- SucuriScanInterface::info( $message );
11152
  }
11153
 
11154
  // Modify the schedule of the filesystem scanner.
11155
- if ( $frequency = SucuriScanRequest::post( ':scan_frequency' ) ) {
11156
- if ( array_key_exists( $frequency, $sucuriscan_schedule_allowed ) ) {
11157
- SucuriScanOption::update_option( ':scan_frequency', $frequency );
11158
- wp_clear_scheduled_hook( 'sucuriscan_scheduled_scan' );
11159
 
11160
- if ( $frequency != '_oneoff' ) {
11161
- wp_schedule_event( time() + 10, $frequency, 'sucuriscan_scheduled_scan' );
11162
  }
11163
 
11164
- $frequency_title = strtolower( $sucuriscan_schedule_allowed[ $frequency ] );
11165
  $message = 'File system scanning frequency set to <code>' . $frequency_title . '</code>';
11166
 
11167
- SucuriScanEvent::report_info_event( $message );
11168
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11169
- SucuriScanInterface::info( $message );
11170
  }
11171
  }
11172
 
11173
  // Set the method (aka. interface) that will be used to scan the site.
11174
- if ( $interface = SucuriScanRequest::post( ':scan_interface' ) ) {
11175
- $allowed_values = array_keys( $sucuriscan_interface_allowed );
11176
 
11177
- if ( in_array( $interface, $allowed_values ) ) {
11178
  $message = 'File system scanning interface set to <code>' . $interface . '</code>';
11179
 
11180
- SucuriScanOption::update_option( ':scan_interface', $interface );
11181
- SucuriScanEvent::report_info_event( $message );
11182
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11183
- SucuriScanInterface::info( $message );
11184
  }
11185
  }
11186
 
11187
  // Update the limit of error log lines to parse.
11188
- if ( $errorlogs_limit = SucuriScanRequest::post( ':errorlogs_limit', '[0-9]+' ) ) {
11189
- if ( $errorlogs_limit > 1000 ) {
11190
- SucuriScanInterface::error( 'Analyze more than 1,000 lines will take too much time.' );
11191
  } else {
11192
- SucuriScanOption::update_option( ':errorlogs_limit', $errorlogs_limit );
11193
- SucuriScanInterface::info( 'Analyze last <code>' . $errorlogs_limit . '</code> entries encountered in the error logs.' );
11194
 
11195
- if ( $errorlogs_limit == 0 ) {
11196
- SucuriScanOption::update_option( ':parse_errorlogs', 'disabled' );
11197
  }
11198
  }
11199
  }
11200
 
11201
  // Reset the plugin security logs.
11202
  $allowed_log_files = '(integrity|lastlogins|failedlogins|sitecheck)';
11203
- if ( $reset_logfile = SucuriScanRequest::post( ':reset_logfile', $allowed_log_files ) ) {
11204
  $files_to_delete = array(
11205
  'sucuri-' . $reset_logfile . '.php',
11206
  'sucuri-old' . $reset_logfile . '.php',
11207
  );
11208
 
11209
- foreach ( $files_to_delete as $log_filename ) {
11210
- $log_filepath = SucuriScan::datastore_folder_path( $log_filename );
11211
 
11212
- if ( @unlink( $log_filepath ) ) {
11213
- $log_filename_simple = str_replace( '.php', '', $log_filename );
11214
  $message = 'Deleted security log <code>' . $log_filename_simple . '</code>';
11215
 
11216
- SucuriScanEvent::report_debug_event( $message );
11217
- SucuriScanInterface::info( $message );
11218
- }
11219
- }
11220
- }
11221
-
11222
- // Update the value for the maximum emails per hour.
11223
- if ( $per_hour = SucuriScanRequest::post( ':emails_per_hour' ) ) {
11224
- if ( array_key_exists( $per_hour, $sucuriscan_emails_per_hour ) ) {
11225
- $per_hour_label = strtolower( $sucuriscan_emails_per_hour[ $per_hour ] );
11226
- $message = 'Maximum email alerts per hour set to <code>' . $per_hour_label . '</code>';
11227
-
11228
- SucuriScanOption::update_option( ':emails_per_hour', $per_hour );
11229
- SucuriScanEvent::report_info_event( $message );
11230
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11231
- SucuriScanInterface::info( $message );
11232
- } else {
11233
- SucuriScanInterface::error( 'Invalid value for the maximum emails per hour.' );
11234
- }
11235
- }
11236
-
11237
- // Update the email where the event notifications will be sent.
11238
- if ( $new_email = SucuriScanRequest::post( ':notify_to' ) ) {
11239
- $valid_email = SucuriScan::get_valid_email( $new_email );
11240
-
11241
- if ( $valid_email ) {
11242
- $message = 'Sucuri alerts will be sent to this email: <code>' . $valid_email . '</code>';
11243
-
11244
- SucuriScanOption::update_option( ':notify_to', $valid_email );
11245
- SucuriScanEvent::report_info_event( $message );
11246
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11247
- SucuriScanInterface::info( $message );
11248
- } else {
11249
- SucuriScanInterface::error( 'Email format not supported.' );
11250
- }
11251
- }
11252
-
11253
- // Update the maximum failed logins per hour before consider it a brute-force attack.
11254
- if ( $failed_logins = SucuriScanRequest::post( ':maximum_failed_logins' ) ) {
11255
- if ( array_key_exists( $failed_logins, $sucuriscan_maximum_failed_logins ) ) {
11256
- $message = 'Consider brute-force attack after <code>' . $failed_logins . '</code> failed logins per hour';
11257
-
11258
- SucuriScanOption::update_option( ':maximum_failed_logins', $failed_logins );
11259
- SucuriScanEvent::report_info_event( $message );
11260
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11261
- SucuriScanInterface::info( $message );
11262
- } else {
11263
- SucuriScanInterface::error( 'Invalid value for the maximum failed logins per hour before consider it a brute-force attack.' );
11264
- }
11265
- }
11266
-
11267
- // Enable or disable the audit logs report.
11268
- if ( $audit_report = SucuriScanRequest::post( ':audit_report', '(en|dis)able' ) ) {
11269
- $action_d = $audit_report . 'd';
11270
- $message = 'Audit logs report was <code>' . $action_d . '</code>';
11271
-
11272
- SucuriScanOption::update_option( ':audit_report', $action_d );
11273
- SucuriScanEvent::report_info_event( $message );
11274
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11275
- SucuriScanInterface::info( $message );
11276
- }
11277
-
11278
- // Update the limit for audit logs report.
11279
- if ( $logs4report = SucuriScanRequest::post( ':logs4report', '[0-9]{1,4}' ) ) {
11280
- $message = 'Limit for audit logs report set to <code>' . $logs4report . '</code>';
11281
-
11282
- SucuriScanOption::update_option( ':logs4report', $logs4report );
11283
- SucuriScanEvent::report_info_event( $message );
11284
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11285
- SucuriScanInterface::info( $message );
11286
- }
11287
-
11288
- // Update the notification settings.
11289
- if ( SucuriScanRequest::post( ':save_notification_settings' ) !== false ) {
11290
- $options_updated_counter = 0;
11291
-
11292
- if ( SucuriScanRequest::post( ':notify_scan_checksums', '1' ) ) {
11293
- $_POST['sucuriscan_prettify_mails'] = '1';
11294
- }
11295
-
11296
- foreach ( $sucuriscan_notify_options as $alert_type => $alert_label ) {
11297
- $option_value = SucuriScanRequest::post( $alert_type, '(1|0)' );
11298
-
11299
- if ( $option_value !== false ) {
11300
- $current_value = SucuriScanOption::get_option( $alert_type );
11301
- $option_value = ( $option_value == '1' ) ? 'enabled' : 'disabled';
11302
-
11303
- // Check that the option value was actually changed.
11304
- if ( $current_value !== $option_value ) {
11305
- SucuriScanOption::update_option( $alert_type, $option_value );
11306
- $options_updated_counter += 1;
11307
- }
11308
- }
11309
- }
11310
-
11311
- if ( $options_updated_counter > 0 ) {
11312
- $message = 'Alert settings were changed <code>' . $options_updated_counter . ' options</code>';
11313
-
11314
- SucuriScanEvent::report_info_event( $message );
11315
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11316
- SucuriScanInterface::info( $message );
11317
- }
11318
- }
11319
-
11320
- // Update the subject format for the email alerts.
11321
- if ( $email_subject = SucuriScanRequest::post( ':email_subject' ) ) {
11322
- $new_email_subject = false;
11323
- $current_value = SucuriScanOption::get_option( ':email_subject' );
11324
-
11325
- /**
11326
- * Validate the format of the email subject format.
11327
- *
11328
- * If the user chooses the option to build the subject of the email alerts
11329
- * manually we will need to validate the characters. Otherwise we will need to
11330
- * check if the pseudo-tags selected by the user are allowed and supported.
11331
- */
11332
- if ( $email_subject == 'custom' ) {
11333
- $format_pattern = '/^[0-9a-zA-Z:,\s]+$/';
11334
- $custom_email_subject = SucuriScanRequest::post( ':custom_email_subject' );
11335
-
11336
- if (
11337
- $custom_email_subject !== false
11338
- && ! empty($custom_email_subject)
11339
- && preg_match( $format_pattern, $custom_email_subject )
11340
- ) {
11341
- $new_email_subject = trim( $custom_email_subject );
11342
- } else {
11343
- SucuriScanInterface::error( 'Invalid characters found in the email alert subject format.' );
11344
  }
11345
- } elseif (
11346
- is_array( $sucuriscan_email_subjects )
11347
- && in_array( $email_subject, $sucuriscan_email_subjects )
11348
- ) {
11349
- $new_email_subject = trim( $email_subject );
11350
- }
11351
-
11352
- // Proceed with the operation saving the new subject.
11353
- if (
11354
- $new_email_subject !== false
11355
- && $current_value !== $new_email_subject
11356
- ) {
11357
- $message = 'Alert subject format set to <code>' . $new_email_subject . '</code>';
11358
-
11359
- SucuriScanOption::update_option( ':email_subject', $new_email_subject );
11360
- SucuriScanEvent::report_info_event( $message );
11361
- SucuriScanEvent::notify_event( 'plugin_change', $message );
11362
- SucuriScanInterface::info( $message );
11363
  }
11364
  }
11365
 
11366
  // Ignore a new event for email notifications.
11367
- if ( $action = SucuriScanRequest::post( ':ignorerule_action', '(add|remove)' ) ) {
11368
- $ignore_rule = SucuriScanRequest::post( ':ignorerule' );
11369
 
11370
- if ( $action == 'add' ) {
11371
- if ( SucuriScanOption::add_ignored_event( $ignore_rule ) ) {
11372
- SucuriScanInterface::info( 'Post-type ignored successfully.' );
11373
- SucuriScanEvent::report_warning_event( 'Changes in <code>' . $ignore_rule . '</code> post-type will be ignored' );
11374
  } else {
11375
- SucuriScanInterface::error( 'The post-type is invalid or it may be already ignored.' );
11376
  }
11377
- } elseif ( $action == 'remove' ) {
11378
- SucuriScanOption::remove_ignored_event( $ignore_rule );
11379
- SucuriScanInterface::info( 'Post-type removed from the list successfully.' );
11380
- SucuriScanEvent::report_notice_event( 'Changes in <code>' . $ignore_rule . '</code> post-type will not be ignored' );
11381
  }
11382
  }
11383
 
11384
  // Ignore a new directory path for the file system scans.
11385
- if ( $action = SucuriScanRequest::post( ':ignorescanning_action', '(ignore|unignore)' ) ) {
11386
- $ignore_directories = SucuriScanRequest::post( ':ignorescanning_dirs', '_array' );
11387
- $ignore_file = SucuriScanRequest::post( ':ignorescanning_file' );
11388
 
11389
- if ( $action == 'ignore' ) {
11390
  // Target a single file path to be ignored.
11391
- if ( $ignore_file !== false ) {
11392
  $ignore_directories = array( $ignore_file );
11393
  }
11394
 
11395
  // Target a list of directories to be ignored.
11396
- if ( ! empty( $ignore_directories ) ) {
11397
  $were_ignored = array();
11398
 
11399
- foreach ( $ignore_directories as $resource_path ) {
11400
- if (
11401
- file_exists( $resource_path )
11402
- && SucuriScanFSScanner::ignore_directory( $resource_path )
11403
  ) {
11404
  $were_ignored[] = $resource_path;
11405
  }
11406
  }
11407
 
11408
- if ( ! empty( $were_ignored ) ) {
11409
- SucuriScanInterface::info( 'Items selected will be ignored in future scans.' );
11410
- SucuriScanEvent::report_warning_event( sprintf(
11411
  'Resources will not be scanned: (multiple entries): %s',
11412
- @implode( ',', $ignore_directories )
11413
- ) );
11414
  }
11415
  }
11416
- } elseif ( $action == 'unignore' ) {
11417
- foreach ( $ignore_directories as $directory_path ) {
11418
- SucuriScanFSScanner::unignore_directory( $directory_path );
11419
  }
11420
 
11421
- SucuriScanInterface::info( 'Items selected will not be ignored anymore.' );
11422
- SucuriScanEvent::report_notice_event( sprintf(
11423
  'Resources will be scanned: (multiple entries): %s',
11424
- @implode( ',', $ignore_directories )
11425
- ) );
11426
  }
11427
  }
11428
 
11429
  // Trust and IP address to ignore notifications for a subnet.
11430
- if ( $trust_ip = SucuriScanRequest::post( ':trust_ip' ) ) {
11431
- if (
11432
- SucuriScan::is_valid_ip( $trust_ip )
11433
- || SucuriScan::is_valid_cidr( $trust_ip )
11434
  ) {
11435
- $cache = new SucuriScanCache( 'trustip' );
11436
- $ip_info = SucuriScan::get_ip_info( $trust_ip );
11437
  $ip_info['added_at'] = SucuriScan::local_time();
11438
- $cache_key = md5( $ip_info['remote_addr'] );
11439
 
11440
- if ( $cache->exists( $cache_key ) ) {
11441
- SucuriScanInterface::error( 'The IP address specified was already trusted.' );
11442
- } elseif ( $cache->add( $cache_key, $ip_info ) ) {
11443
  $message = 'Changes from <code>' . $trust_ip . '</code> will be ignored';
11444
 
11445
- SucuriScanEvent::report_warning_event( $message );
11446
- SucuriScanInterface::info( $message );
11447
  } else {
11448
- SucuriScanInterface::error( 'The new entry was not saved in the datastore file.' );
11449
  }
11450
  }
11451
  }
11452
 
11453
  // Trust and IP address to ignore notifications for a subnet.
11454
- if ( $del_trust_ip = SucuriScanRequest::post( ':del_trust_ip', '_array' ) ) {
11455
- $cache = new SucuriScanCache( 'trustip' );
11456
 
11457
- foreach ( $del_trust_ip as $cache_key ) {
11458
- $cache->delete( $cache_key );
11459
  }
11460
 
11461
- SucuriScanInterface::info( 'The IP addresses selected were deleted successfully.' );
11462
  }
11463
 
11464
  // Update the settings for the heartbeat API.
11465
- if ( $heartbeat_status = SucuriScanRequest::post( ':heartbeat_status' ) ) {
11466
  $statuses_allowed = SucuriScanHeartbeat::statuses_allowed();
11467
 
11468
- if ( array_key_exists( $heartbeat_status, $statuses_allowed ) ) {
11469
  $message = 'Heartbeat status set to <code>' . $heartbeat_status . '</code>';
11470
 
11471
- SucuriScanOption::update_option( ':heartbeat', $heartbeat_status );
11472
- SucuriScanEvent::report_info_event( $message );
11473
- SucuriScanInterface::info( $message );
11474
  } else {
11475
- SucuriScanInterface::error( 'Heartbeat status not allowed.' );
11476
  }
11477
  }
11478
 
11479
  // Update the value of the heartbeat pulse.
11480
- if ( $heartbeat_pulse = SucuriScanRequest::post( ':heartbeat_pulse' ) ) {
11481
  $pulses_allowed = SucuriScanHeartbeat::pulses_allowed();
11482
 
11483
- if ( array_key_exists( $heartbeat_pulse, $pulses_allowed ) ) {
11484
  $message = 'Heartbeat pulse set to <code>' . $heartbeat_pulse . '</code> seconds.';
11485
 
11486
- SucuriScanOption::update_option( ':heartbeat_pulse', $heartbeat_pulse );
11487
- SucuriScanEvent::report_info_event( $message );
11488
- SucuriScanInterface::info( $message );
11489
  } else {
11490
- SucuriScanInterface::error( 'Heartbeat pulse not allowed.' );
11491
  }
11492
  }
11493
 
11494
  // Update the value of the heartbeat interval.
11495
- if ( $heartbeat_interval = SucuriScanRequest::post( ':heartbeat_interval' ) ) {
11496
  $intervals_allowed = SucuriScanHeartbeat::intervals_allowed();
11497
 
11498
- if ( array_key_exists( $heartbeat_interval, $intervals_allowed ) ) {
11499
  $message = 'Heartbeat interval set to <code>' . $heartbeat_interval . '</code>';
11500
 
11501
- SucuriScanOption::update_option( ':heartbeat_interval', $heartbeat_interval );
11502
- SucuriScanEvent::report_info_event( $message );
11503
- SucuriScanInterface::info( $message );
11504
  } else {
11505
- SucuriScanInterface::error( 'Heartbeat interval not allowed.' );
11506
  }
11507
  }
11508
 
11509
  // Enable or disable the auto-start execution of heartbeat.
11510
- if ( $heartbeat_autostart = SucuriScanRequest::post( ':heartbeat_autostart', '(en|dis)able' ) ) {
11511
  $action_d = $heartbeat_autostart . 'd';
11512
  $message = 'Heartbeat auto-start was <code>' . $action_d . '</code>';
11513
 
11514
- SucuriScanOption::update_option( ':heartbeat_autostart', $action_d );
11515
- SucuriScanEvent::report_info_event( $message );
11516
- SucuriScanInterface::info( $message );
11517
- }
11518
-
11519
- // Debug ability of the plugin to send email alerts correctly.
11520
- if ( SucuriScanRequest::post( ':debug_email' ) ) {
11521
- $recipient = SucuriScanOption::get_option( ':notify_to' );
11522
- $mail_sent = SucuriScanMail::send_mail(
11523
- $recipient,
11524
- 'Test email alert',
11525
- sprintf( 'Test email alert sent at %s', date('r') ),
11526
- array( 'Force' => true )
11527
- );
11528
- SucuriScanInterface::info( 'Test email alert sent, check your inbox.' );
11529
  }
11530
  }
11531
  }
11532
 
11533
- /**
11534
- * Print a HTML code with the settings of the plugin.
11535
- *
11536
- * @return void
11537
- */
11538
- function sucuriscan_settings_page(){
11539
- SucuriScanInterface::check_permissions();
11540
-
11541
- $template_variables = array(
11542
- 'PageTitle' => 'Settings',
11543
- 'Settings.General' => sucuriscan_settings_general(),
11544
- 'Settings.Scanner' => sucuriscan_settings_scanner(),
11545
- 'Settings.IgnoreScanning' => sucuriscan_settings_ignorescanning(),
11546
- 'Settings.Notifications' => sucuriscan_settings_notifications(),
11547
- 'Settings.IgnoreRules' => sucuriscan_settings_ignore_rules(),
11548
- 'Settings.TrustIP' => sucuriscan_settings_trust_ip(),
11549
- 'Settings.Heartbeat' => sucuriscan_settings_heartbeat(),
11550
- );
11551
-
11552
- echo SucuriScanTemplate::get_template( 'settings', $template_variables );
11553
- }
11554
-
11555
  /**
11556
  * Read and parse the content of the general settings template.
11557
  *
11558
  * @return string Parsed HTML code for the general settings panel.
11559
  */
11560
- function sucuriscan_settings_general(){
11561
-
11562
- global $sucuriscan_emails_per_hour,
11563
- $sucuriscan_maximum_failed_logins;
11564
 
11565
- // Check the nonce here to populate the value through other functions.
11566
- $page_nonce = SucuriScanInterface::check_nonce();
11567
 
11568
- // Process all form submissions.
11569
- sucuriscan_settings_form_submissions( $page_nonce );
11570
 
11571
- // Get initial variables to decide some things bellow.
11572
- $emails_per_hour = SucuriScanOption::get_option( ':emails_per_hour' );
11573
- $maximum_failed_logins = SucuriScanOption::get_option( ':maximum_failed_logins' );
11574
- $audit_report = SucuriScanOption::get_option( ':audit_report' );
11575
- $logs4report = SucuriScanOption::get_option( ':logs4report' );
 
 
 
 
 
11576
 
11577
- // Generate the HTML code for the option list in the form select fields.
11578
- $emails_per_hour_options = SucuriScanTemplate::get_select_options( $sucuriscan_emails_per_hour, $emails_per_hour );
11579
- $maximum_failed_logins_options = SucuriScanTemplate::get_select_options( $sucuriscan_maximum_failed_logins, $maximum_failed_logins );
11580
 
11581
- $template_variables = array(
11582
- 'NotifyTo' => SucuriScanOption::get_option( ':notify_to' ),
11583
- 'EmailsPerHour' => 'Undefined',
11584
- 'EmailsPerHourOptions' => $emails_per_hour_options,
11585
- 'MaximumFailedLogins' => 'Undefined',
11586
- 'MaximumFailedLoginsOptions' => $maximum_failed_logins_options,
11587
- /* Audit Logs Report */
11588
- 'AuditReportStatus' => 'Enabled',
11589
- 'AuditReportSwitchText' => 'Disable',
11590
- 'AuditReportSwitchValue' => 'disable',
11591
- 'AuditReportSwitchCssClass' => 'button-danger',
11592
- 'AuditReportLimit' => $logs4report,
11593
- /* Timezone Settings */
11594
- 'CustomTimezone' => '',
11595
- 'CurrentDatetime' => '',
11596
- );
11597
 
11598
- // Keep the reset options panel and form submission processor before anything else.
11599
- $template_variables['SettingsSection.ResetOptions'] = sucuriscan_settings_general_resetoptions($page_nonce);
 
 
 
11600
 
11601
- // Build HTML code for the additional general settings panels.
11602
- $template_variables['SettingsSection.ApiKey'] = sucuriscan_settings_general_apikey($page_nonce);
11603
- $template_variables['SettingsSection.DataStorage'] = sucuriscan_settings_general_datastorage($page_nonce);
11604
- $template_variables['SettingsSection.ApiProxy'] = sucuriscan_settings_general_apiproxy();
11605
- $template_variables['SettingsSection.ApiSSL'] = sucuriscan_settings_general_apissl($page_nonce);
11606
- $template_variables['SettingsSection.ApiTimeout'] = sucuriscan_settings_general_apitimeout($page_nonce);
11607
- $template_variables['SettingsSection.ReverseProxy'] = sucuriscan_settings_general_reverseproxy($page_nonce);
11608
- $template_variables['SettingsSection.PasswordCollector'] = sucuriscan_settings_general_pwdcollector($page_nonce);
11609
- $template_variables['SettingsSection.IPDiscoverer'] = sucuriscan_settings_general_ipdiscoverer($page_nonce);
11610
- $template_variables['SettingsSection.CommentMonitor'] = sucuriscan_settings_general_commentmonitor($page_nonce);
11611
- $template_variables['SettingsSection.XhrMonitor'] = sucuriscan_settings_general_xhrmonitor($page_nonce);
11612
 
11613
- sucuriscan_settings_general_adsvisibility($page_nonce);
 
11614
 
11615
- if ( array_key_exists( $emails_per_hour, $sucuriscan_emails_per_hour ) ) {
11616
- $template_variables['EmailsPerHour'] = $sucuriscan_emails_per_hour[ $emails_per_hour ];
11617
- }
11618
 
11619
- if ( array_key_exists( $maximum_failed_logins, $sucuriscan_maximum_failed_logins ) ) {
11620
- $template_variables['MaximumFailedLogins'] = $sucuriscan_maximum_failed_logins[ $maximum_failed_logins ];
11621
- }
 
 
 
 
 
 
 
 
11622
 
11623
- if ( $audit_report == 'disabled' ) {
11624
- $template_variables['AuditReportStatus'] = 'Disabled';
11625
- $template_variables['AuditReportSwitchText'] = 'Enable';
11626
- $template_variables['AuditReportSwitchValue'] = 'enable';
11627
- $template_variables['AuditReportSwitchCssClass'] = 'button-success';
11628
- }
11629
 
11630
- if ( function_exists( 'wp_timezone_choice' ) ) {
11631
- $gmt_offset = SucuriScanOption::get_option( 'gmt_offset' );
11632
- $tzstring = SucuriScanOption::get_option( 'timezone_string' );
11633
- $template_variables['CurrentDatetime'] = SucuriScan::current_datetime();
11634
- $template_variables['CustomTimezone'] = empty( $tzstring ) ? 'UTC' . $gmt_offset : $tzstring;
11635
  }
11636
 
11637
- return SucuriScanTemplate::get_section('settings-general', $template_variables);
11638
  }
11639
 
11640
  function sucuriscan_settings_general_apikey($nonce)
@@ -11654,8 +11952,8 @@ function sucuriscan_settings_general_apikey($nonce)
11654
 
11655
  if ($user_obj !== false && user_can($user_obj, 'administrator')) {
11656
  // Send request to generate new API key or display form to set manually.
11657
- if (SucuriScanAPI::register_site($user_obj->user_email)) {
11658
- $api_registered_modal = SucuriScanTemplate::get_modal(
11659
  'settings-apiregistered',
11660
  array(
11661
  'Title' => 'Site registered successfully',
@@ -11670,9 +11968,9 @@ function sucuriscan_settings_general_apikey($nonce)
11670
 
11671
  // Recover API key through the email registered previously.
11672
  if (SucuriScanRequest::post(':recover_key') !== false) {
11673
- SucuriScanAPI::recover_key();
11674
  SucuriScanEvent::report_info_event('Recovery of the Sucuri API key was requested.');
11675
- $api_recovery_modal = SucuriScanTemplate::get_modal(
11676
  'settings-apirecovery',
11677
  array(
11678
  'Title' => 'Plugin API Key Recovery',
@@ -11682,7 +11980,7 @@ function sucuriscan_settings_general_apikey($nonce)
11682
  }
11683
  }
11684
 
11685
- $api_key = SucuriScanAPI::get_plugin_key();
11686
 
11687
  // Check whether the domain name is valid or not.
11688
  if (!$api_key) {
@@ -11691,7 +11989,7 @@ function sucuriscan_settings_general_apikey($nonce)
11691
  $invalid_domain = (bool) ($domain_address === $clean_domain);
11692
  }
11693
 
11694
- $params['APIKey'] = (!$api_key ? '<em>(not set)</em>' : $api_key);
11695
  $params['APIKey.RecoverVisibility'] = SucuriScanTemplate::visibility(!$api_key && !$display_manual_key_form);
11696
  $params['APIKey.ManualKeyFormVisibility'] = SucuriScanTemplate::visibility($display_manual_key_form);
11697
  $params['APIKey.RemoveVisibility'] = SucuriScanTemplate::visibility((bool) $api_key);
@@ -11699,40 +11997,7 @@ function sucuriscan_settings_general_apikey($nonce)
11699
  $params['ModalWhenAPIRegistered'] = $api_registered_modal;
11700
  $params['ModalForApiKeyRecovery'] = $api_recovery_modal;
11701
 
11702
- return SucuriScanTemplate::get_section('settings-general-apikey', $params);
11703
- }
11704
-
11705
- function sucuriscan_settings_general_apiproxy()
11706
- {
11707
- $params = array(
11708
- 'APIProxy.Host' => 'no_proxy_host',
11709
- 'APIProxy.Port' => 'no_proxy_port',
11710
- 'APIProxy.Username' => 'no_proxy_username',
11711
- 'APIProxy.Password' => 'no_proxy_password',
11712
- 'APIProxy.PasswordType' => 'default',
11713
- 'APIProxy.PasswordText' => 'empty',
11714
- );
11715
-
11716
- if (class_exists('WP_HTTP_Proxy')) {
11717
- $wp_http_proxy = new WP_HTTP_Proxy();
11718
-
11719
- if ($wp_http_proxy->is_enabled()) {
11720
- $proxy_host = SucuriScan::escape($wp_http_proxy->host());
11721
- $proxy_port = SucuriScan::escape($wp_http_proxy->port());
11722
- $proxy_username = SucuriScan::escape($wp_http_proxy->username());
11723
- $proxy_password = SucuriScan::escape($wp_http_proxy->password());
11724
-
11725
- $template_variables['APIProxy.Host'] = $proxy_host;
11726
- $template_variables['APIProxy.Port'] = $proxy_port;
11727
- $template_variables['APIProxy.Username'] = $proxy_username;
11728
- $template_variables['APIProxy.Password'] = $proxy_password;
11729
- $template_variables['APIProxy.PasswordType'] = 'info';
11730
- $template_variables['APIProxy.PasswordText'] = 'hidden';
11731
-
11732
- }
11733
- }
11734
-
11735
- return SucuriScanTemplate::get_section('settings-general-apiproxy', $params);
11736
  }
11737
 
11738
  function sucuriscan_settings_general_datastorage($nonce)
@@ -11780,75 +12045,7 @@ function sucuriscan_settings_general_datastorage($nonce)
11780
 
11781
  $params['DatastorePath'] = SucuriScanOption::get_option(':datastore_path');
11782
 
11783
- return SucuriScanTemplate::get_section('settings-general-datastorage', $params);
11784
- }
11785
-
11786
- function sucuriscan_settings_general_apissl($nonce)
11787
- {
11788
- global $sucuriscan_verify_ssl_cert;
11789
-
11790
- $params = array(
11791
- 'VerifySSLCert' => 'Undefined',
11792
- 'VerifySSLCertCssClass' => 0,
11793
- 'VerifySSLCertOptions' => '',
11794
- );
11795
-
11796
- // Update the configuration for the SSL certificate verification.
11797
- if ($nonce) {
11798
- $verify_ssl_cert = SucuriScanRequest::post(':verify_ssl_cert');
11799
-
11800
- if ($verify_ssl_cert) {
11801
- if (array_key_exists($verify_ssl_cert, $sucuriscan_verify_ssl_cert)) {
11802
- $message = 'SSL certificate verification for API calls set to <code>' . $verify_ssl_cert . '</code>';
11803
-
11804
- SucuriScanOption::update_option(':verify_ssl_cert', $verify_ssl_cert);
11805
- SucuriScanEvent::report_warning_event($message);
11806
- SucuriScanEvent::notify_event('plugin_change', $message);
11807
- SucuriScanInterface::info($message);
11808
- } else {
11809
- SucuriScanInterface::error('Invalid value for the SSL certificate verification.');
11810
- }
11811
- }
11812
- }
11813
-
11814
- $verify_ssl_cert = SucuriScanOption::get_option(':verify_ssl_cert');
11815
- $params['VerifySSLCertOptions'] = SucuriScanTemplate::get_select_options(
11816
- $sucuriscan_verify_ssl_cert,
11817
- $verify_ssl_cert
11818
- );
11819
-
11820
- if (array_key_exists($verify_ssl_cert, $sucuriscan_verify_ssl_cert)) {
11821
- $params['VerifySSLCert'] = $sucuriscan_verify_ssl_cert[$verify_ssl_cert];
11822
-
11823
- if ($verify_ssl_cert === 'true') {
11824
- $params['VerifySSLCertCssClass'] = 1;
11825
- }
11826
- }
11827
-
11828
- return SucuriScanTemplate::get_section('settings-general-apissl', $params);
11829
- }
11830
-
11831
- function sucuriscan_settings_general_apitimeout($nonce)
11832
- {
11833
- $params = array();
11834
-
11835
- // Update the API request timeout.
11836
- if ($nonce) {
11837
- $timeout = SucuriScanRequest::post(':request_timeout', '[0-9]+');
11838
-
11839
- if ($timeout !== false) {
11840
- $message = 'API request timeout set to <code>' . $timeout . '</code> seconds.';
11841
-
11842
- SucuriScanOption::update_option(':request_timeout', $timeout);
11843
- SucuriScanEvent::report_info_event($message);
11844
- SucuriScanEvent::notify_event('plugin_change', $message);
11845
- SucuriScanInterface::info($message);
11846
- }
11847
- }
11848
-
11849
- $params['RequestTimeout'] = SucuriScanOption::get_option(':request_timeout') . ' seconds';
11850
-
11851
- return SucuriScanTemplate::get_section('settings-general-apitimeout', $params);
11852
  }
11853
 
11854
  function sucuriscan_settings_general_reverseproxy($nonce)
@@ -11865,13 +12062,13 @@ function sucuriscan_settings_general_reverseproxy($nonce)
11865
  $revproxy = SucuriScanRequest::post(':revproxy', '(en|dis)able');
11866
 
11867
  if ($revproxy) {
11868
- $action_d = $revproxy . 'd';
11869
- $message = 'Reverse proxy support was <code>' . $action_d . '</code>';
11870
-
11871
- SucuriScanOption::update_option(':revproxy', $action_d);
11872
- SucuriScanEvent::report_info_event($message);
11873
- SucuriScanEvent::notify_event('plugin_change', $message);
11874
- SucuriScanInterface::info($message);
11875
  }
11876
  }
11877
 
@@ -11882,7 +12079,7 @@ function sucuriscan_settings_general_reverseproxy($nonce)
11882
  $params['ReverseProxySwitchCssClass'] = 'button-success';
11883
  }
11884
 
11885
- return SucuriScanTemplate::get_section('settings-general-reverseproxy', $params);
11886
  }
11887
 
11888
  function sucuriscan_settings_general_pwdcollector($nonce)
@@ -11925,7 +12122,7 @@ function sucuriscan_settings_general_pwdcollector($nonce)
11925
  $params['PwdCollectorSwitchCssClass'] = 'button-danger';
11926
  }
11927
 
11928
- return SucuriScanTemplate::get_section('settings-general-pwdcollector', $params);
11929
  }
11930
 
11931
  function sucuriscan_settings_general_ipdiscoverer($nonce)
@@ -11938,6 +12135,7 @@ function sucuriscan_settings_general_ipdiscoverer($nonce)
11938
  'WebsiteURL' => 'Unknown',
11939
  'RemoteAddress' => '127.0.0.1',
11940
  'RemoteAddressHeader' => 'INVALID',
 
11941
  /* Switch form information. */
11942
  'DnsLookupsStatus' => 'Enabled',
11943
  'DnsLookupsSwitchText' => 'Disable',
@@ -11945,9 +12143,13 @@ function sucuriscan_settings_general_ipdiscoverer($nonce)
11945
  'DnsLookupsSwitchCssClass' => 'button-danger',
11946
  );
11947
 
 
 
 
11948
  // Configure the DNS lookups option for reverse proxy detection.
11949
  if ($nonce) {
11950
  $dns_lookups = SucuriScanRequest::post(':dns_lookups', '(en|dis)able');
 
11951
 
11952
  if ($dns_lookups) {
11953
  $action_d = $dns_lookups . 'd';
@@ -11958,6 +12160,16 @@ function sucuriscan_settings_general_ipdiscoverer($nonce)
11958
  SucuriScanEvent::notify_event('plugin_change', $message);
11959
  SucuriScanInterface::info($message);
11960
  }
 
 
 
 
 
 
 
 
 
 
11961
  }
11962
 
11963
  if (SucuriScanOption::is_disabled(':dns_lookups')) {
@@ -11977,12 +12189,16 @@ function sucuriscan_settings_general_ipdiscoverer($nonce)
11977
  $params['RemoteAddressHeader'] = SucuriScan::get_remote_addr_header();
11978
  $params['RemoteAddress'] = SucuriScan::get_remote_addr();
11979
  $params['WebsiteURL'] = SucuriScan::get_domain();
 
 
 
 
11980
 
11981
  if ($base_domain !== $proxy_info['http_host']) {
11982
  $params['TopLevelDomain'] = sprintf('%s (%s)', $params['TopLevelDomain'], $base_domain);
11983
  }
11984
 
11985
- return SucuriScanTemplate::get_section('settings-general-ipdiscoverer', $params);
11986
  }
11987
 
11988
  function sucuriscan_settings_general_commentmonitor($nonce)
@@ -12016,7 +12232,7 @@ function sucuriscan_settings_general_commentmonitor($nonce)
12016
  $params['CommentMonitorSwitchCssClass'] = 'button-success';
12017
  }
12018
 
12019
- return SucuriScanTemplate::get_section('settings-general-commentmonitor', $params);
12020
  }
12021
 
12022
  function sucuriscan_settings_general_xhrmonitor($nonce)
@@ -12050,70 +12266,91 @@ function sucuriscan_settings_general_xhrmonitor($nonce)
12050
  $params['XhrMonitorSwitchCssClass'] = 'button-success';
12051
  }
12052
 
12053
- return SucuriScanTemplate::get_section('settings-general-xhrmonitor', $params);
12054
  }
12055
 
12056
- function sucuriscan_settings_general_adsvisibility($nonce)
12057
  {
12058
- // Update the advertisement visibility settings.
12059
- if ($nonce) {
12060
- $ads_visibility = SucuriScanRequest::post(':ads_visibility');
 
 
 
 
12061
 
12062
- if ($ads_visibility === 'disable') {
12063
- $option_value = $ads_visibility . 'd';
12064
- $message = sprintf('Plugin advertisement set to <code>%s</code>', $option_value);
 
 
12065
 
12066
- SucuriScanOption::update_option(':ads_visibility', $option_value);
12067
  SucuriScanEvent::report_info_event($message);
 
12068
  SucuriScanInterface::info($message);
12069
  }
12070
- }
12071
- }
12072
 
12073
- function sucuriscan_settings_general_resetoptions($nonce)
12074
- {
12075
- // Reset all the plugin's options.
12076
- if ($nonce && SucuriScanRequest::post(':reset_options') !== false) {
12077
- $process = SucuriScanRequest::post(':process_form');
12078
 
12079
- if (intval($process) === 1) {
12080
- // Notify the event before the API key is removed.
12081
- $message = 'Sucuri plugin options were reset';
12082
- SucuriScanEvent::report_critical_event($message);
12083
  SucuriScanEvent::notify_event('plugin_change', $message);
 
 
 
12084
 
12085
- // Remove all plugin options from the database.
12086
- SucuriScanOption::delete_plugin_options();
 
12087
 
12088
- // Remove the scheduled tasks.
12089
- wp_clear_scheduled_hook('sucuriscan_scheduled_scan');
 
 
 
 
 
12090
 
12091
- // Remove all the local security logs.
12092
- @unlink(SucuriScan::datastore_folder_path('.htaccess'));
12093
- @unlink(SucuriScan::datastore_folder_path('index.html'));
12094
- @unlink(SucuriScan::datastore_folder_path('sucuri-failedlogins.php'));
12095
- @unlink(SucuriScan::datastore_folder_path('sucuri-integrity.php'));
12096
- @unlink(SucuriScan::datastore_folder_path('sucuri-lastlogins.php'));
12097
- @unlink(SucuriScan::datastore_folder_path('sucuri-oldfailedlogins.php'));
12098
- @unlink(SucuriScan::datastore_folder_path('sucuri-plugindata.php'));
12099
- @unlink(SucuriScan::datastore_folder_path('sucuri-sitecheck.php'));
12100
- @unlink(SucuriScan::datastore_folder_path('sucuri-trustip.php'));
12101
- @rmdir(SucuriScan::datastore_folder_path());
12102
 
12103
- // Revert hardening of core directories (includes, content, uploads).
12104
- SucuriScanHardening::dewhitelist('ms-files.php', 'wp-includes');
12105
- SucuriScanHardening::dewhitelist('wp-tinymce.php', 'wp-includes');
12106
- SucuriScanHardening::unharden_directory(ABSPATH . '/wp-includes');
12107
- SucuriScanHardening::unharden_directory(WP_CONTENT_DIR . '/uploads');
12108
- SucuriScanHardening::unharden_directory(WP_CONTENT_DIR);
 
12109
 
12110
- SucuriScanInterface::info('Plugin options, core directory hardening, and security logs were reset');
12111
- } else {
12112
- SucuriScanInterface::error('You need to confirm that you understand the risk of this operation.');
12113
- }
 
12114
  }
12115
 
12116
- return SucuriScanTemplate::get_section('settings-general-resetoptions');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12117
  }
12118
 
12119
  /**
@@ -12121,46 +12358,40 @@ function sucuriscan_settings_general_resetoptions($nonce)
12121
  *
12122
  * @return string Parsed HTML code for the scanner settings panel.
12123
  */
12124
- function sucuriscan_settings_scanner(){
12125
-
12126
  global $sucuriscan_schedule_allowed,
12127
  $sucuriscan_interface_allowed;
12128
 
12129
  // Get initial variables to decide some things bellow.
12130
- $fs_scanner = SucuriScanOption::get_option( ':fs_scanner' );
12131
- $scan_freq = SucuriScanOption::get_option( ':scan_frequency' );
12132
- $scan_interface = SucuriScanOption::get_option( ':scan_interface' );
12133
- $scan_modfiles = SucuriScanOption::get_option( ':scan_modfiles' );
12134
- $scan_checksums = SucuriScanOption::get_option( ':scan_checksums' );
12135
- $scan_errorlogs = SucuriScanOption::get_option( ':scan_errorlogs' );
12136
- $parse_errorlogs = SucuriScanOption::get_option( ':parse_errorlogs' );
12137
- $errorlogs_limit = SucuriScanOption::get_option( ':errorlogs_limit' );
12138
- $ignore_scanning = SucuriScanOption::get_option( ':ignore_scanning' );
12139
- $sitecheck_scanner = SucuriScanOption::get_option( ':sitecheck_scanner' );
12140
- $sitecheck_counter = SucuriScanOption::get_option( ':sitecheck_counter' );
12141
- $runtime_scan_human = SucuriScanFSScanner::get_filesystem_runtime( true );
12142
 
12143
  // Get the file path of the security logs.
12144
- $integrity_log_path = SucuriScan::datastore_folder_path( 'sucuri-integrity.php' );
12145
- $lastlogins_log_path = SucuriScan::datastore_folder_path( 'sucuri-lastlogins.php' );
12146
- $failedlogins_log_path = SucuriScan::datastore_folder_path( 'sucuri-failedlogins.php' );
12147
- $sitecheck_log_path = SucuriScan::datastore_folder_path( 'sucuri-sitecheck.php' );
12148
 
12149
  // Generate the HTML code for the option list in the form select fields.
12150
- $scan_freq_options = SucuriScanTemplate::get_select_options( $sucuriscan_schedule_allowed, $scan_freq );
12151
- $scan_interface_options = SucuriScanTemplate::get_select_options( $sucuriscan_interface_allowed, $scan_interface );
12152
 
12153
- $template_variables = array(
12154
  /* Filesystem scanner */
12155
  'FsScannerStatus' => 'Enabled',
12156
  'FsScannerSwitchText' => 'Disable',
12157
  'FsScannerSwitchValue' => 'disable',
12158
  'FsScannerSwitchCssClass' => 'button-danger',
12159
- /* Scan modified files. */
12160
- 'ScanModfilesStatus' => 'Enabled',
12161
- 'ScanModfilesSwitchText' => 'Disable',
12162
- 'ScanModfilesSwitchValue' => 'disable',
12163
- 'ScanModfilesSwitchCssClass' => 'button-danger',
12164
  /* Scan files checksum. */
12165
  'ScanChecksumsStatus' => 'Enabled',
12166
  'ScanChecksumsSwitchText' => 'Disable',
@@ -12201,324 +12432,840 @@ function sucuriscan_settings_scanner(){
12201
  'SiteCheckLogLife' => '0B',
12202
  );
12203
 
12204
- if ( $fs_scanner == 'disabled' ) {
12205
- $template_variables['FsScannerStatus'] = 'Disabled';
12206
- $template_variables['FsScannerSwitchText'] = 'Enable';
12207
- $template_variables['FsScannerSwitchValue'] = 'enable';
12208
- $template_variables['FsScannerSwitchCssClass'] = 'button-success';
12209
  }
12210
 
12211
- if ( $scan_modfiles == 'disabled' ) {
12212
- $template_variables['ScanModfilesStatus'] = 'Disabled';
12213
- $template_variables['ScanModfilesSwitchText'] = 'Enable';
12214
- $template_variables['ScanModfilesSwitchValue'] = 'enable';
12215
- $template_variables['ScanModfilesSwitchCssClass'] = 'button-success';
12216
  }
12217
 
12218
- if ( $scan_checksums == 'disabled' ) {
12219
- $template_variables['ScanChecksumsStatus'] = 'Disabled';
12220
- $template_variables['ScanChecksumsSwitchText'] = 'Enable';
12221
- $template_variables['ScanChecksumsSwitchValue'] = 'enable';
12222
- $template_variables['ScanChecksumsSwitchCssClass'] = 'button-success';
12223
  }
12224
 
12225
- if ( $ignore_scanning == 'disabled' ) {
12226
- $template_variables['IgnoreScanningStatus'] = 'Disabled';
12227
- $template_variables['IgnoreScanningSwitchText'] = 'Enable';
12228
- $template_variables['IgnoreScanningSwitchValue'] = 'enable';
12229
- $template_variables['IgnoreScanningSwitchCssClass'] = 'button-success';
12230
  }
12231
 
12232
- if ( $scan_errorlogs == 'disabled' ) {
12233
- $template_variables['ScanErrorlogsStatus'] = 'Disabled';
12234
- $template_variables['ScanErrorlogsSwitchText'] = 'Enable';
12235
- $template_variables['ScanErrorlogsSwitchValue'] = 'enable';
12236
- $template_variables['ScanErrorlogsSwitchCssClass'] = 'button-success';
12237
  }
12238
 
12239
- if ( $parse_errorlogs == 'disabled' ) {
12240
- $template_variables['ParseErrorLogsStatus'] = 'Disabled';
12241
- $template_variables['ParseErrorLogsSwitchText'] = 'Enable';
12242
- $template_variables['ParseErrorLogsSwitchValue'] = 'enable';
12243
- $template_variables['ParseErrorLogsSwitchCssClass'] = 'button-success';
12244
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12245
 
12246
- if ( $sitecheck_scanner == 'disabled' ) {
12247
- $template_variables['SiteCheckScannerStatus'] = 'Disabled';
12248
- $template_variables['SiteCheckScannerSwitchText'] = 'Enable';
12249
- $template_variables['SiteCheckScannerSwitchValue'] = 'enable';
12250
- $template_variables['SiteCheckScannerSwitchCssClass'] = 'button-success';
12251
- }
12252
 
12253
- if ( array_key_exists( $scan_freq, $sucuriscan_schedule_allowed ) ) {
12254
- $template_variables['ScanningFrequency'] = $sucuriscan_schedule_allowed[ $scan_freq ];
12255
  }
12256
 
12257
- // Determine the age of the security log files.
12258
- $template_variables['IntegrityLogLife'] = SucuriScan::human_filesize( @filesize( $integrity_log_path ) );
12259
- $template_variables['LastLoginLogLife'] = SucuriScan::human_filesize( @filesize( $lastlogins_log_path ) );
12260
- $template_variables['FailedLoginLogLife'] = SucuriScan::human_filesize( @filesize( $failedlogins_log_path ) );
12261
- $template_variables['SiteCheckLogLife'] = SucuriScan::human_filesize( @filesize( $sitecheck_log_path ) );
12262
-
12263
- return SucuriScanTemplate::get_section( 'settings-scanner', $template_variables );
12264
  }
12265
 
12266
- /**
12267
- * Read and parse the content of the notification settings template.
12268
- *
12269
- * @return string Parsed HTML code for the notification settings panel.
12270
- */
12271
- function sucuriscan_settings_notifications(){
12272
- global $sucuriscan_notify_options,
12273
- $sucuriscan_email_subjects;
12274
 
12275
- $template_variables = array(
12276
- 'NotificationOptions' => '',
12277
- 'EmailSubjectOptions' => '',
12278
- 'EmailSubjectCustom.Checked' => '',
12279
- 'EmailSubjectCustom.Value' => '',
12280
- 'PrettifyMailsWarningVisibility' => SucuriScanTemplate::visibility( SucuriScanMail::prettify_mails() ),
12281
  );
12282
 
12283
- if ( $sucuriscan_email_subjects ) {
12284
- $email_subject = SucuriScanOption::get_option( ':email_subject' );
12285
- $is_official_subject = false;
12286
 
12287
- foreach ( $sucuriscan_email_subjects as $subject_format ) {
12288
- if ( $email_subject == $subject_format ) {
12289
- $is_official_subject = true;
12290
- $checked = 'checked="checked"';
 
 
 
 
12291
  } else {
12292
- $checked = '';
12293
  }
12294
-
12295
- $template_variables['EmailSubjectOptions'] .= SucuriScanTemplate::get_snippet('settings-emailsubject', array(
12296
- 'EmailSubject.Name' => $subject_format,
12297
- 'EmailSubject.Value' => $subject_format,
12298
- 'EmailSubject.Checked' => $checked,
12299
- ));
12300
  }
 
 
 
 
 
 
 
12301
 
12302
- if ( $is_official_subject === false ) {
12303
- $template_variables['EmailSubjectCustom.Checked'] = 'checked="checked"';
12304
- $template_variables['EmailSubjectCustom.Value'] = SucuriScan::escape( $email_subject );
 
 
12305
  }
12306
  }
12307
 
12308
- $counter = 0;
12309
- $alert_pattern = '/^([a-z]+:)?(.+)/';
 
 
 
 
12310
 
12311
- foreach ( $sucuriscan_notify_options as $alert_type => $alert_label ) {
12312
- $alert_value = SucuriScanOption::get_option( $alert_type );
12313
- $checked = ( $alert_value == 'enabled' ? 'checked="checked"' : '' );
12314
- $css_class = ( $counter % 2 == 0 ) ? 'alternate' : '';
12315
- $alert_icon = '';
12316
 
12317
- if ( preg_match( $alert_pattern, $alert_label, $match ) ) {
12318
- $alert_group = str_replace( ':', '', $match[1] );
12319
- $alert_label = $match[2];
12320
 
12321
- switch ( $alert_group ) {
12322
- case 'user': $alert_icon = 'dashicons-before dashicons-admin-users'; break;
12323
- case 'plugin': $alert_icon = 'dashicons-before dashicons-admin-plugins'; break;
12324
- case 'theme': $alert_icon = 'dashicons-before dashicons-admin-appearance'; break;
 
 
12325
  }
12326
  }
12327
-
12328
- $template_variables['NotificationOptions'] .= SucuriScanTemplate::get_snippet('settings-notifications', array(
12329
- 'Notification.CssClass' => $css_class,
12330
- 'Notification.Name' => $alert_type,
12331
- 'Notification.Checked' => $checked,
12332
- 'Notification.Label' => $alert_label,
12333
- 'Notification.LabelIcon' => $alert_icon,
12334
- ));
12335
- $counter += 1;
12336
  }
12337
 
12338
- return SucuriScanTemplate::get_section( 'settings-notifications', $template_variables );
 
 
 
12339
  }
12340
 
12341
  /**
12342
- * Read and parse the content of the ignored-rules settings template.
12343
  *
12344
- * @return string Parsed HTML code for the ignored-rules settings panel.
12345
  */
12346
- function sucuriscan_settings_ignore_rules(){
12347
- $notify_new_site_content = SucuriScanOption::get_option( ':notify_post_publication' );
12348
-
12349
- $template_variables = array(
12350
- 'IgnoreRules.MessageVisibility' => 'visible',
12351
- 'IgnoreRules.TableVisibility' => 'hidden',
12352
- 'IgnoreRules.PostTypes' => '',
12353
- );
12354
-
12355
- if ( $notify_new_site_content == 'enabled' ) {
12356
- $post_types = get_post_types();
12357
- $ignored_events = SucuriScanOption::get_ignored_events();
12358
 
12359
- $template_variables['IgnoreRules.MessageVisibility'] = 'hidden';
12360
- $template_variables['IgnoreRules.TableVisibility'] = 'visible';
12361
- $counter = 0;
12362
 
12363
- foreach ( $post_types as $post_type => $post_type_object ) {
12364
- $counter += 1;
12365
- $css_class = ( $counter % 2 == 0 ) ? 'alternate' : '';
12366
- $post_type_title = ucwords( str_replace( '_', chr( 32 ), $post_type ) );
12367
 
12368
- if ( array_key_exists( $post_type, $ignored_events ) ) {
12369
- $is_ignored_text = 'YES';
12370
- $was_ignored_at = SucuriScan::datetime( $ignored_events[ $post_type ] );
12371
- $is_ignored_class = 'danger';
12372
- $button_action = 'remove';
12373
- $button_class = 'button-primary';
12374
- $button_text = 'Allow';
12375
- } else {
12376
- $is_ignored_text = 'NO';
12377
- $was_ignored_at = 'Not ignored';
12378
- $is_ignored_class = 'success';
12379
- $button_action = 'add';
12380
- $button_class = 'button-primary button-danger';
12381
- $button_text = 'Ignore';
12382
- }
12383
 
12384
- $template_variables['IgnoreRules.PostTypes'] .= SucuriScanTemplate::get_snippet('settings-ignorerules', array(
12385
- 'IgnoreRules.CssClass' => $css_class,
12386
- 'IgnoreRules.Num' => $counter,
12387
- 'IgnoreRules.PostTypeTitle' => $post_type_title,
12388
- 'IgnoreRules.IsIgnored' => $is_ignored_text,
12389
- 'IgnoreRules.WasIgnoredAt' => $was_ignored_at,
12390
- 'IgnoreRules.IsIgnoredClass' => $is_ignored_class,
12391
- 'IgnoreRules.PostType' => $post_type,
12392
- 'IgnoreRules.Action' => $button_action,
12393
- 'IgnoreRules.ButtonClass' => 'button ' . $button_class,
12394
- 'IgnoreRules.ButtonText' => $button_text,
12395
- ));
12396
- }
12397
  }
12398
 
12399
- return SucuriScanTemplate::get_section( 'settings-ignorerules', $template_variables );
12400
  }
12401
 
12402
- /**
12403
- * Read and parse the content of the trust-ip settings template.
12404
- *
12405
- * @return string Parsed HTML code for the trust-ip settings panel.
12406
- */
12407
- function sucuriscan_settings_trust_ip(){
12408
- $template_variables = array(
12409
- 'TrustedIPs.List' => '',
12410
- 'TrustedIPs.NoItems.Visibility' => 'visible',
12411
- );
12412
 
12413
- $cache = new SucuriScanCache( 'trustip' );
12414
- $trusted_ips = $cache->get_all();
 
 
 
 
 
12415
 
12416
- if ( $trusted_ips ) {
12417
- $counter = 0;
 
12418
 
12419
- foreach ( $trusted_ips as $cache_key => $ip_info ) {
12420
- $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
 
12421
 
12422
- if ( $ip_info->cidr_range == 32 ) {
12423
- $ip_info->cidr_format = 'n/a';
12424
- }
 
 
 
 
 
 
 
 
 
 
12425
 
12426
- $template_variables['TrustedIPs.List'] .= SucuriScanTemplate::get_snippet('settings-trustip', array(
12427
- 'TrustIP.CssClass' => $css_class,
12428
- 'TrustIP.CacheKey' => $cache_key,
12429
- 'TrustIP.RemoteAddr' => SucuriScan::escape( $ip_info->remote_addr ),
12430
- 'TrustIP.CIDRFormat' => SucuriScan::escape( $ip_info->cidr_format ),
12431
- 'TrustIP.AddedAt' => SucuriScan::datetime( $ip_info->added_at ),
12432
- ));
12433
- $counter += 1;
12434
  }
 
12435
 
12436
- if ( $counter > 0 ) {
12437
- $template_variables['TrustedIPs.NoItems.Visibility'] = 'hidden';
12438
- }
 
 
 
 
 
 
 
 
 
 
 
12439
  }
12440
 
12441
- return SucuriScanTemplate::get_section( 'settings-trustip', $template_variables );
12442
  }
12443
 
12444
  /**
12445
- * Read and parse the content of the ignore-scanning settings template.
12446
  *
12447
- * @return string Parsed HTML code for the ignore-scanning settings panel.
12448
  */
12449
- function sucuriscan_settings_ignorescanning(){
12450
- $template_variables = array(
12451
- 'IgnoreScanning.ResourceList' => '',
12452
- 'IgnoreScanning.DisabledVisibility' => 'visible',
12453
- 'IgnoreScanning.NoItemsVisibility' => 'visible',
12454
- );
12455
-
12456
- $ignore_scanning = SucuriScanFSScanner::will_ignore_scanning();
12457
 
12458
- // Allow disable of this option temporarily.
12459
- if ( SucuriScanRequest::get( 'no_scan' ) == 1 ) {
12460
- $ignore_scanning = false;
12461
- }
12462
 
12463
- // Scan the project and get the ignored paths.
12464
- if ( $ignore_scanning === true ) {
12465
  $counter = 0;
12466
- $template_variables['IgnoreScanning.DisabledVisibility'] = 'hidden';
12467
- $dir_list_list = SucuriScanFSScanner::get_ignored_directories_live();
12468
-
12469
- foreach ( $dir_list_list as $group => $dir_list ) {
12470
- foreach ( $dir_list as $dir_data ) {
12471
- $valid_entry = false;
12472
- $snippet_data = array(
12473
- 'IgnoreScanning.CssClass' => '',
12474
- 'IgnoreScanning.Directory' => '',
12475
- 'IgnoreScanning.DirectoryPath' => '',
12476
- 'IgnoreScanning.IgnoredAt' => '',
12477
- 'IgnoreScanning.IgnoredAtText' => 'ok',
12478
- 'IgnoreScanning.IgnoredCssClass' => 'success',
12479
- );
12480
 
12481
- if ( $group == 'is_ignored' ) {
12482
- $valid_entry = true;
12483
- $snippet_data['IgnoreScanning.Directory'] = urlencode( $dir_data['directory_path'] );
12484
- $snippet_data['IgnoreScanning.DirectoryPath'] = SucuriScan::escape( $dir_data['directory_path'] );
12485
- $snippet_data['IgnoreScanning.IgnoredAt'] = SucuriScan::datetime( $dir_data['ignored_at'] );
12486
- $snippet_data['IgnoreScanning.IgnoredAtText'] = 'ignored';
12487
- $snippet_data['IgnoreScanning.IgnoredCssClass'] = 'warning';
12488
- } elseif ( $group == 'is_not_ignored' ) {
12489
- $valid_entry = true;
12490
- $snippet_data['IgnoreScanning.Directory'] = urlencode( $dir_data );
12491
- $snippet_data['IgnoreScanning.DirectoryPath'] = SucuriScan::escape( $dir_data );
12492
- }
12493
 
12494
- if ( $valid_entry ) {
12495
- $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
12496
- $snippet_data['IgnoreScanning.CssClass'] = $css_class;
12497
- $template_variables['IgnoreScanning.ResourceList'] .= SucuriScanTemplate::get_snippet( 'settings-ignorescanning', $snippet_data );
12498
- $counter += 1;
12499
- }
12500
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
12501
  }
12502
 
12503
- if ( $counter > 0 ) {
12504
- $template_variables['IgnoreScanning.NoItemsVisibility'] = 'hidden';
12505
  }
12506
  }
12507
 
12508
- return SucuriScanTemplate::get_section( 'settings-ignorescanning', $template_variables );
12509
  }
12510
 
 
12511
  /**
12512
  * Read and parse the content of the heartbeat settings template.
12513
  *
12514
  * @return string Parsed HTML code for the heartbeat settings panel.
12515
  */
12516
- function sucuriscan_settings_heartbeat(){
 
12517
  // Current values set in the options table.
12518
- $heartbeat_status = SucuriScanOption::get_option( ':heartbeat' );
12519
- $heartbeat_pulse = SucuriScanOption::get_option( ':heartbeat_pulse' );
12520
- $heartbeat_interval = SucuriScanOption::get_option( ':heartbeat_interval' );
12521
- $heartbeat_autostart = SucuriScanOption::get_option( ':heartbeat_autostart' );
12522
 
12523
  // Allowed values for each setting.
12524
  $statuses_allowed = SucuriScanHeartbeat::statuses_allowed();
@@ -12526,11 +13273,11 @@ function sucuriscan_settings_heartbeat(){
12526
  $intervals_allowed = SucuriScanHeartbeat::intervals_allowed();
12527
 
12528
  // HTML select form fields.
12529
- $heartbeat_options = SucuriScanTemplate::get_select_options( $statuses_allowed, $heartbeat_status );
12530
- $heartbeat_pulse_options = SucuriScanTemplate::get_select_options( $pulses_allowed, $heartbeat_pulse );
12531
- $heartbeat_interval_options = SucuriScanTemplate::get_select_options( $intervals_allowed, $heartbeat_interval );
12532
 
12533
- $template_variables = array(
12534
  'HeartbeatStatus' => 'Undefined',
12535
  'HeartbeatPulse' => 'Undefined',
12536
  'HeartbeatInterval' => 'Undefined',
@@ -12545,26 +13292,52 @@ function sucuriscan_settings_heartbeat(){
12545
  'HeartbeatAutostartSwitchCssClass' => 'button-danger',
12546
  );
12547
 
12548
- if ( array_key_exists( $heartbeat_status, $statuses_allowed ) ) {
12549
- $template_variables['HeartbeatStatus'] = $statuses_allowed[ $heartbeat_status ];
12550
  }
12551
 
12552
- if ( array_key_exists( $heartbeat_pulse, $pulses_allowed ) ) {
12553
- $template_variables['HeartbeatPulse'] = $pulses_allowed[ $heartbeat_pulse ];
12554
  }
12555
 
12556
- if ( array_key_exists( $heartbeat_interval, $intervals_allowed ) ) {
12557
- $template_variables['HeartbeatInterval'] = $intervals_allowed[ $heartbeat_interval ];
12558
  }
12559
 
12560
- if ( $heartbeat_autostart == 'disabled' ) {
12561
- $template_variables['HeartbeatAutostart'] = 'Disabled';
12562
- $template_variables['HeartbeatAutostartSwitchText'] = 'Enable';
12563
- $template_variables['HeartbeatAutostartSwitchValue'] = 'enable';
12564
- $template_variables['HeartbeatAutostartSwitchCssClass'] = 'button-success';
12565
  }
12566
 
12567
- return SucuriScanTemplate::get_section( 'settings-heartbeat', $template_variables );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12568
  }
12569
 
12570
  /**
@@ -12576,14 +13349,15 @@ function sucuriscan_settings_heartbeat(){
12576
  *
12577
  * @return void
12578
  */
12579
- function sucuriscan_infosys_page(){
 
12580
  SucuriScanInterface::check_permissions();
12581
 
12582
  // Process all form submissions.
12583
  sucuriscan_infosys_form_submissions();
12584
 
12585
  // Page pseudo-variables initialization.
12586
- $template_variables = array(
12587
  'PageTitle' => 'Site Info',
12588
  'ServerInfo' => sucuriscan_server_info(),
12589
  'Cronjobs' => sucuriscan_show_cronjobs(),
@@ -12592,7 +13366,7 @@ function sucuriscan_infosys_page(){
12592
  'ErrorLogs' => sucuriscan_infosys_errorlogs(),
12593
  );
12594
 
12595
- echo SucuriScanTemplate::get_template( 'infosys', $template_variables );
12596
  }
12597
 
12598
  /**
@@ -12601,87 +13375,64 @@ function sucuriscan_infosys_page(){
12601
  *
12602
  * @return string The HTML code displaying the information about the HTAccess rules.
12603
  */
12604
- function sucuriscan_infosys_htaccess(){
 
12605
  $htaccess_path = SucuriScan::get_htaccess_path();
12606
-
12607
- $template_variables = array(
12608
  'HTAccess.Content' => '',
12609
- 'HTAccess.Message' => '',
12610
- 'HTAccess.MessageType' => '',
12611
- 'HTAccess.MessageVisible' => 'hidden',
12612
  'HTAccess.TextareaVisible' => 'hidden',
 
 
 
 
12613
  );
12614
 
12615
- if ( $htaccess_path ) {
12616
- $htaccess_rules = file_get_contents( $htaccess_path );
12617
 
12618
- $template_variables['HTAccess.MessageType'] = 'updated';
12619
- $template_variables['HTAccess.MessageVisible'] = 'visible';
12620
- $template_variables['HTAccess.TextareaVisible'] = 'visible';
12621
- $template_variables['HTAccess.Content'] = $htaccess_rules;
12622
- $template_variables['HTAccess.Message'] .= 'HTAccess file found in this path <code>'.$htaccess_path.'</code>';
12623
 
12624
- if ( empty($htaccess_rules) ) {
12625
- $template_variables['HTAccess.TextareaVisible'] = 'hidden';
12626
- $template_variables['HTAccess.Message'] .= '</p><p>The HTAccess file found is completely empty.';
12627
- }
12628
- if ( sucuriscan_htaccess_is_standard( $htaccess_rules ) ) {
12629
- $template_variables['HTAccess.Message'] .= '</p><p>
12630
- The main <code>.htaccess</code> file in your site has the standard rules for a WordPress installation. You can customize it to improve the
12631
- performance and change the behaviour of the redirections for pages and posts in your site. To get more information visit the official documentation at
12632
- <a href="http://codex.wordpress.org/Using_Permalinks#Creating_and_editing_.28.htaccess.29" target="_blank">Codex WordPrexx - Creating and editing (.htaccess)</a>';
12633
  }
12634
  } else {
12635
- $template_variables['HTAccess.Message'] = 'Your website does not contains a <code>.htaccess</code> file or it was not found in the default location.';
12636
- $template_variables['HTAccess.MessageType'] = 'error';
12637
- $template_variables['HTAccess.MessageVisible'] = 'visible';
12638
  }
12639
 
12640
- return SucuriScanTemplate::get_section( 'infosys-htaccess', $template_variables );
12641
  }
12642
 
12643
  /**
12644
- * Check whether the rules in a htaccess file are the default options generated
12645
- * by WordPress or if the file has custom options added by other Plugins.
 
 
 
12646
  *
12647
- * @param string $rules Optional parameter containing a text string with the content of the main htaccess file.
12648
- * @return boolean Either TRUE or FALSE if the rules found in the htaccess file specified are the default ones or not.
12649
  */
12650
- function sucuriscan_htaccess_is_standard( $rules = false ){
12651
- if ( $rules === false ) {
 
 
12652
  $htaccess_path = SucuriScan::get_htaccess_path();
12653
- $rules = $htaccess_path ? file_get_contents( $htaccess_path ) : '';
12654
- }
12655
-
12656
- if ( ! empty($rules) ) {
12657
- $standard_lines = array(
12658
- '# BEGIN WordPress',
12659
- '<IfModule mod_rewrite\.c>',
12660
- 'RewriteEngine On',
12661
- 'RewriteBase \/',
12662
- 'RewriteRule .index.\.php. - \[L\]',
12663
- 'RewriteCond %\{REQUEST_FILENAME\} \!-f',
12664
- 'RewriteCond %\{REQUEST_FILENAME\} \!-d',
12665
- 'RewriteRule \. \/index\.php \[L\]',
12666
- '<\/IfModule>',
12667
- '# END WordPress',
12668
- );
12669
- $pattern = '';
12670
- $standard_lines_total = count( $standard_lines );
12671
- foreach ( $standard_lines as $i => $line ) {
12672
- if ( $i < ($standard_lines_total -1) ) {
12673
- $end_of_line = "\n";
12674
- } else {
12675
- $end_of_line = '';
12676
- }
12677
- $pattern .= sprintf( '%s%s', $line, $end_of_line );
12678
- }
12679
 
12680
- if ( preg_match( "/{$pattern}/", $rules ) ) {
12681
- return true;
12682
  }
12683
  }
12684
 
 
 
 
 
 
 
 
12685
  return false;
12686
  }
12687
 
@@ -12692,65 +13443,64 @@ function sucuriscan_htaccess_is_standard( $rules = false ){
12692
  *
12693
  * @return string The HTML code displaying the constants and variables found in the wp-config file.
12694
  */
12695
- function sucuriscan_infosys_wpconfig(){
12696
- $template_variables = array(
 
12697
  'WordpressConfig.Rules' => '',
12698
  'WordpressConfig.Total' => 0,
12699
  );
12700
 
12701
- $ignore_wp_rules = array( 'DB_PASSWORD' );
12702
  $wp_config_path = SucuriScan::get_wpconfig_path();
12703
 
12704
- if ( $wp_config_path ) {
12705
  $wp_config_rules = array();
12706
- $wp_config_content = SucuriScanFileInfo::file_lines( $wp_config_path );
12707
 
12708
  // Parse the main configuration file and look for constants and global variables.
12709
- foreach ( (array) $wp_config_content as $line ) {
12710
- if ( preg_match( '/^\s?(#|\/\/)/', $line ) ) {
12711
- // Ignore commented lines.
12712
- continue;
12713
- } elseif ( preg_match( '/define\(/', $line ) ) {
12714
  // Detect PHP constants even if the line if indented.
12715
- $line = preg_replace( '/.*define\((.+)\);.*/', '$1', $line );
12716
- $line_parts = explode( ',', $line, 2 );
12717
- } elseif ( preg_match( '/^\$[a-zA-Z_]+/', $line ) ) {
12718
  // Detect global variables like the database table prefix.
12719
- $line = preg_replace( '/;\s\/\/.*/', ';', $line );
12720
- $line_parts = explode( '=', $line, 2 );
12721
  } else {
12722
- // Ignore other lines.
12723
- continue;
12724
  }
12725
 
12726
  // Clean and append the rule to the wp_config_rules variable.
12727
- if ( isset($line_parts) && count( $line_parts ) == 2 ) {
12728
  $key_name = '';
12729
  $key_value = '';
12730
 
12731
  // TODO: A foreach loop is not really necessary, find a better way.
12732
- foreach ( $line_parts as $i => $line_part ) {
12733
- $line_part = trim( $line_part );
12734
- $line_part = ltrim( $line_part, '$' );
12735
- $line_part = rtrim( $line_part, ';' );
12736
 
12737
  // Remove single/double quotes at the beginning and end of the string.
12738
- $line_part = ltrim( $line_part, "'" );
12739
- $line_part = rtrim( $line_part, "'" );
12740
- $line_part = ltrim( $line_part, '"' );
12741
- $line_part = rtrim( $line_part, '"' );
12742
 
12743
  // Assign the clean strings to specific variables.
12744
- if ( $i == 0 ) {
12745
  $key_name = $line_part;
12746
  }
12747
 
12748
- if ( $i == 1 ) {
12749
- if ( defined( $key_name ) ) {
12750
- $key_value = constant( $key_name );
12751
 
12752
- if ( is_bool( $key_value ) ) {
12753
- $key_value = ( $key_value === true ) ? 'TRUE' : 'FALSE';
12754
  }
12755
  } else {
12756
  $key_value = $line_part;
@@ -12759,40 +13509,43 @@ function sucuriscan_infosys_wpconfig(){
12759
  }
12760
 
12761
  // Remove the value of sensitive variables like the database password.
12762
- if ( in_array( $key_name, $ignore_wp_rules ) ) {
12763
  $key_value = 'hidden';
12764
  }
12765
 
12766
  // Append the value to the configuration rules.
12767
- $wp_config_rules[ $key_name ] = $key_value;
12768
  }
12769
  }
12770
 
12771
  // Pass the WordPress configuration rules to the template and show them.
12772
  $counter = 0;
12773
- foreach ( $wp_config_rules as $var_name => $var_value ) {
12774
- $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
12775
  $label_css = 'sucuriscan-monospace';
12776
 
12777
- if ( empty($var_value) ) {
12778
  $var_value = 'empty';
12779
  $label_css = 'sucuriscan-label-default';
12780
- } elseif ( $var_value == 'hidden' ) {
12781
  $label_css = 'sucuriscan-label-info';
12782
  }
12783
 
12784
- $template_variables['WordpressConfig.Total'] += 1;
12785
- $template_variables['WordpressConfig.Rules'] .= SucuriScanTemplate::get_snippet('infosys-wpconfig', array(
12786
- 'WordpressConfig.VariableName' => SucuriScan::escape( $var_name ),
12787
- 'WordpressConfig.VariableValue' => SucuriScan::escape( $var_value ),
12788
- 'WordpressConfig.VariableCssClass' => $label_css,
12789
- 'WordpressConfig.CssClass' => $css_class,
12790
- ));
12791
- $counter += 1;
 
 
 
12792
  }
12793
  }
12794
 
12795
- return SucuriScanTemplate::get_section( 'infosys-wpconfig', $template_variables );
12796
  }
12797
 
12798
  /**
@@ -12800,8 +13553,9 @@ function sucuriscan_infosys_wpconfig(){
12800
  *
12801
  * @return array A list of pseudo-variables and values that will replace them in the HTML template.
12802
  */
12803
- function sucuriscan_show_cronjobs(){
12804
- $template_variables = array(
 
12805
  'Cronjobs.List' => '',
12806
  'Cronjobs.Total' => 0,
12807
  );
@@ -12810,27 +13564,30 @@ function sucuriscan_show_cronjobs(){
12810
  $schedules = wp_get_schedules();
12811
  $counter = 0;
12812
 
12813
- foreach ( $cronjobs as $timestamp => $cronhooks ) {
12814
- foreach ( (array) $cronhooks as $hook => $events ) {
12815
- foreach ( (array) $events as $key => $event ) {
12816
- if ( empty($event['args']) ) {
12817
- $event['args'] = array( '<em>empty</em>' );
12818
  }
12819
 
12820
- $template_variables['Cronjobs.Total'] += 1;
12821
- $template_variables['Cronjobs.List'] .= SucuriScanTemplate::get_snippet('infosys-cronjobs', array(
12822
- 'Cronjob.Hook' => $hook,
12823
- 'Cronjob.Schedule' => $event['schedule'],
12824
- 'Cronjob.NextTime' => SucuriScan::datetime( $timestamp ),
12825
- 'Cronjob.Arguments' => SucuriScan::implode( ', ', $event['args'] ),
12826
- 'Cronjob.CssClass' => ( $counter % 2 == 0 ) ? '' : 'alternate',
12827
- ));
12828
- $counter += 1;
 
 
 
12829
  }
12830
  }
12831
  }
12832
 
12833
- return SucuriScanTemplate::get_section( 'infosys-cronjobs', $template_variables );
12834
  }
12835
 
12836
  /**
@@ -12841,64 +13598,60 @@ function sucuriscan_show_cronjobs(){
12841
  * @param boolean $page_nonce True if the nonce is valid, False otherwise.
12842
  * @return void
12843
  */
12844
- function sucuriscan_infosys_form_submissions(){
12845
- if ( SucuriScanInterface::check_nonce() ) {
 
12846
  // Modify the scheduled tasks (run now, remove, re-schedule).
12847
  $allowed_actions = '(runnow|hourly|twicedaily|daily|remove)';
12848
 
12849
- if ( $cronjob_action = SucuriScanRequest::post( ':cronjob_action', $allowed_actions ) ) {
12850
- $cronjobs = SucuriScanRequest::post( ':cronjobs', '_array' );
12851
 
12852
- if ( ! empty($cronjobs) ) {
12853
- $total_tasks = count( $cronjobs );
12854
 
12855
  // Force execution of the selected scheduled tasks.
12856
- if ( $cronjob_action == 'runnow' ) {
12857
- SucuriScanInterface::info( $total_tasks . ' tasks were scheduled to run in the next ten seconds.' );
12858
- SucuriScanEvent::report_notice_event( sprintf(
12859
  'Force execution of scheduled tasks: (multiple entries): %s',
12860
- @implode( ',', $cronjobs )
12861
- ) );
12862
 
12863
- foreach ( $cronjobs as $task_name ) {
12864
- wp_schedule_single_event( time() + 10, $task_name );
12865
  }
12866
- }
12867
-
12868
- // Force deletion of the selected scheduled tasks.
12869
- elseif ( $cronjob_action == 'remove' ) {
12870
- SucuriScanInterface::info( $total_tasks . ' scheduled tasks were removed.' );
12871
- SucuriScanEvent::report_notice_event( sprintf(
12872
  'Delete scheduled tasks: (multiple entries): %s',
12873
- @implode( ',', $cronjobs )
12874
- ) );
12875
 
12876
- foreach ( $cronjobs as $task_name ) {
12877
- wp_clear_scheduled_hook( $task_name );
12878
  }
12879
- }
12880
-
12881
- // Re-schedule the selected scheduled tasks.
12882
- elseif (
12883
- $cronjob_action == 'hourly'
12884
  || $cronjob_action == 'twicedaily'
12885
  || $cronjob_action == 'daily'
12886
  ) {
12887
- SucuriScanInterface::info( $total_tasks . ' tasks were re-scheduled to run <code>' . $cronjob_action . '</code>.' );
12888
- SucuriScanEvent::report_notice_event( sprintf(
12889
  'Re-configure scheduled tasks %s: (multiple entries): %s',
12890
  $cronjob_action,
12891
- @implode( ',', $cronjobs )
12892
- ) );
12893
 
12894
- foreach ( $cronjobs as $task_name ) {
12895
- wp_clear_scheduled_hook( $task_name );
12896
- $next_due = wp_next_scheduled( $task_name );
12897
- wp_schedule_event( $next_due, $cronjob_action, $task_name );
12898
  }
12899
  }
12900
  } else {
12901
- SucuriScanInterface::error( 'No scheduled tasks were selected from the list.' );
12902
  }
12903
  }
12904
  }
@@ -12909,8 +13662,9 @@ function sucuriscan_infosys_form_submissions(){
12909
  *
12910
  * @return array A list of pseudo-variables and values that will replace them in the HTML template.
12911
  */
12912
- function sucuriscan_infosys_errorlogs(){
12913
- $template_variables = array(
 
12914
  'ErrorLog.Path' => '',
12915
  'ErrorLog.Exists' => 'No',
12916
  'ErrorLog.NoItemsVisibility' => 'hidden',
@@ -12922,52 +13676,55 @@ function sucuriscan_infosys_errorlogs(){
12922
  );
12923
 
12924
  $error_log_path = false;
12925
- $log_filename = SucuriScan::ini_get( 'error_log' );
12926
- $errorlogs_limit = SucuriScanOption::get_option( ':errorlogs_limit' );
12927
- $template_variables['ErrorLog.LogsLimit'] = $errorlogs_limit;
12928
- $errorlogs_counter = 0;
12929
-
12930
- if ( $log_filename ) {
12931
- $error_log_path = @realpath( ABSPATH . '/' . $log_filename );
12932
- }
12933
-
12934
- if ( SucuriScanOption::is_disabled( ':parse_errorlogs' ) ) {
12935
- $template_variables['ErrorLog.DisabledVisibility'] = 'visible';
12936
- }
12937
-
12938
- if ( $error_log_path ) {
12939
- $template_variables['ErrorLog.Path'] = $error_log_path;
12940
- $template_variables['ErrorLog.Exists'] = 'Yes';
12941
- $template_variables['ErrorLog.FileSize'] = SucuriScan::human_filesize( filesize( $error_log_path ) );
12942
-
12943
- $last_lines = SucuriScanFileInfo::tail_file( $error_log_path, $errorlogs_limit );
12944
- $error_logs = SucuriScanFSScanner::parse_error_logs( $last_lines );
12945
- $error_logs = array_reverse( $error_logs );
12946
- $errorlogs_counter = 0;
12947
-
12948
- foreach ( $error_logs as $error_log ) {
12949
- $css_class = ( $errorlogs_counter % 2 == 0 ) ? '' : 'alternate';
12950
- $template_variables['ErrorLog.List'] .= SucuriScanTemplate::get_snippet('infosys-errorlogs', array(
12951
- 'ErrorLog.CssClass' => $css_class,
12952
- 'ErrorLog.DateTime' => SucuriScan::datetime( $error_log->timestamp ),
12953
- 'ErrorLog.ErrorType' => SucuriScan::escape( $error_log->error_type ),
12954
- 'ErrorLog.ErrorCode' => SucuriScan::escape( $error_log->error_code ),
12955
- 'ErrorLog.ErrorAbbr' => strtoupper( substr( $error_log->error_code, 0, 1 ) ),
12956
- 'ErrorLog.ErrorMessage' => SucuriScan::escape( $error_log->error_message ),
12957
- 'ErrorLog.FilePath' => SucuriScan::escape( $error_log->file_path ),
12958
- 'ErrorLog.LineNumber' => SucuriScan::escape( $error_log->line_number ),
12959
- ));
12960
- $errorlogs_counter += 1;
 
 
 
12961
  }
12962
 
12963
- if ( $errorlogs_counter <= 0 ) {
12964
- $template_variables['ErrorLog.InvalidFormatVisibility'] = 'visible';
12965
  }
12966
  } else {
12967
- $template_variables['ErrorLog.NoItemsVisibility'] = 'visible';
12968
  }
12969
 
12970
- return SucuriScanTemplate::get_section( 'infosys-errorlogs', $template_variables );
12971
  }
12972
 
12973
  /**
@@ -12975,19 +13732,20 @@ function sucuriscan_infosys_errorlogs(){
12975
  *
12976
  * @return array A list of pseudo-variables and values that will replace them in the HTML template.
12977
  */
12978
- function sucuriscan_server_info(){
 
12979
  global $wpdb;
12980
 
12981
- $template_variables = array(
12982
  'ServerInfo.Variables' => '',
12983
  );
12984
 
12985
  $info_vars = array(
12986
  'Plugin_version' => SUCURISCAN_VERSION,
12987
  'Plugin_checksum' => SUCURISCAN_PLUGIN_CHECKSUM,
12988
- 'Last_filesystem_scan' => SucuriScanFSScanner::get_filesystem_runtime( true ),
12989
  'Datetime_and_Timezone' => '',
12990
- 'Operating_system' => sprintf( '%s (%d Bit)', PHP_OS, PHP_INT_SIZE * 8 ),
12991
  'Server' => 'Unknown',
12992
  'Developer_mode' => 'OFF',
12993
  'Memory_usage' => 'N/A',
@@ -12999,26 +13757,26 @@ function sucuriscan_server_info(){
12999
  $info_vars['Datetime_and_Timezone'] = sprintf(
13000
  '%s (GMT %s)',
13001
  SucuriScan::current_datetime(),
13002
- get_option( 'gmt_offset' )
13003
  );
13004
 
13005
- if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
13006
  $info_vars['Developer_mode'] = 'ON';
13007
  }
13008
 
13009
- if ( function_exists( 'memory_get_usage' ) ) {
13010
- $info_vars['Memory_usage'] = round( memory_get_usage() / 1024 / 1024, 2 ).' MB';
13011
  }
13012
 
13013
- if ( isset($_SERVER['SERVER_SOFTWARE']) ) {
13014
- $info_vars['Server'] = SucuriScan::escape( $_SERVER['SERVER_SOFTWARE'] );
13015
  }
13016
 
13017
- if ( $wpdb ) {
13018
- $info_vars['MySQL_version'] = $wpdb->get_var( 'SELECT VERSION() AS version' );
13019
 
13020
- $mysql_info = $wpdb->get_results( 'SHOW VARIABLES LIKE "sql_mode"' );
13021
- if ( is_array( $mysql_info ) && ! empty($mysql_info[0]->Value) ) {
13022
  $info_vars['SQL_mode'] = $mysql_info[0]->Value;
13023
  }
13024
  }
@@ -13034,26 +13792,29 @@ function sucuriscan_server_info(){
13034
  'max_input_time',
13035
  );
13036
 
13037
- foreach ( $field_names as $php_flag ) {
13038
- $php_flag_value = SucuriScan::ini_get( $php_flag );
13039
  $php_flag_name = 'PHP_' . $php_flag;
13040
- $info_vars[ $php_flag_name ] = $php_flag_value ? $php_flag_value : 'N/A';
13041
  }
13042
 
13043
  $counter = 0;
13044
 
13045
- foreach ( $info_vars as $var_name => $var_value ) {
13046
- $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
13047
- $var_name = str_replace( '_', chr( 32 ), $var_name );
13048
 
13049
- $template_variables['ServerInfo.Variables'] .= SucuriScanTemplate::get_snippet('infosys-serverinfo', array(
13050
- 'ServerInfo.CssClass' => $css_class,
13051
- 'ServerInfo.Title' => $var_name,
13052
- 'ServerInfo.Value' => $var_value,
13053
- ));
 
 
 
13054
  $counter += 1;
13055
  }
13056
 
13057
- return SucuriScanTemplate::get_section( 'infosys-serverinfo', $template_variables );
13058
  }
13059
 
1
  <?php
2
  /*
3
  Plugin Name: Sucuri Security - Auditing, Malware Scanner and Hardening
4
+ Plugin URI: https://wordpress.sucuri.net/
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
  Author: Sucuri, INC
7
+ Version: 1.7.17
8
+ Author URI: https://sucuri.net
9
  */
10
 
11
 
42
  );
43
 
44
  // Terminate execution if any of the functions mentioned above is not defined.
45
+ foreach ($sucuriscan_dependencies as $dependency) {
46
+ if (!function_exists($dependency)) {
47
  exit(0);
48
  }
49
  }
61
  /**
62
  * Unique name of the plugin through out all the code.
63
  */
64
+ define('SUCURISCAN', 'sucuriscan');
65
 
66
  /**
67
  * Current version of the plugin's code.
68
  */
69
+ define('SUCURISCAN_VERSION', '1.7.17');
70
 
71
  /**
72
  * The name of the Sucuri plugin main file.
73
  */
74
+ define('SUCURISCAN_PLUGIN_FILE', 'sucuri.php');
75
 
76
  /**
77
  * The name of the folder where the plugin's files will be located.
78
  */
79
+ define('SUCURISCAN_PLUGIN_FOLDER', 'sucuri-scanner');
80
 
81
  /**
82
  * The fullpath where the plugin's files will be located.
83
  */
84
+ define('SUCURISCAN_PLUGIN_PATH', WP_PLUGIN_DIR.'/'.SUCURISCAN_PLUGIN_FOLDER);
85
 
86
  /**
87
  * The fullpath of the main plugin file.
88
  */
89
+ define('SUCURISCAN_PLUGIN_FILEPATH', SUCURISCAN_PLUGIN_PATH.'/'.SUCURISCAN_PLUGIN_FILE);
90
 
91
  /**
92
  * The local URL where the plugin's files and assets are served.
93
  */
94
+ define('SUCURISCAN_URL', rtrim(plugin_dir_url(SUCURISCAN_PLUGIN_FILEPATH), '/'));
95
 
96
  /**
97
  * Checksum of this file to check the integrity of the plugin.
98
  */
99
+ define('SUCURISCAN_PLUGIN_CHECKSUM', @md5_file(SUCURISCAN_PLUGIN_FILEPATH));
100
 
101
  /**
102
  * Remote URL where the public Sucuri API service is running.
103
  */
104
+ define('SUCURISCAN_API', 'https://wordpress.sucuri.net/api/');
105
 
106
  /**
107
  * Latest version of the public Sucuri API.
108
  */
109
+ define('SUCURISCAN_API_VERSION', 'v1');
110
 
111
  /**
112
  * Remote URL where the CloudProxy API service is running.
113
  */
114
+ define('SUCURISCAN_CLOUDPROXY_API', 'https://waf.sucuri.net/api');
115
 
116
  /**
117
  * Latest version of the CloudProxy API.
118
  */
119
+ define('SUCURISCAN_CLOUDPROXY_API_VERSION', 'v2');
120
 
121
  /**
122
  * The maximum quantity of entries that will be displayed in the last login page.
123
  */
124
+ define('SUCURISCAN_LASTLOGINS_USERSLIMIT', 25);
125
 
126
  /**
127
  * The maximum quantity of entries that will be displayed in the audit logs page.
128
  */
129
+ define('SUCURISCAN_AUDITLOGS_PER_PAGE', 50);
130
 
131
  /**
132
  * The maximum quantity of buttons in the paginations.
133
  */
134
+ define('SUCURISCAN_MAX_PAGINATION_BUTTONS', 20);
135
 
136
  /**
137
  * The minimum quantity of seconds to wait before each filesystem scan.
138
  */
139
+ define('SUCURISCAN_MINIMUM_RUNTIME', 10800);
140
 
141
  /**
142
  * The life time of the cache for the results of the SiteCheck scans.
143
  */
144
+ define('SUCURISCAN_SITECHECK_LIFETIME', 1200);
145
 
146
  /**
147
  * The life time of the cache for the results of the get_plugins function.
148
  */
149
+ define('SUCURISCAN_GET_PLUGINS_LIFETIME', 1800);
150
+
151
+ /**
152
+ * The maximum execution time of a HTTP request before timeout.
153
+ */
154
+ define('SUCURISCAN_MAX_REQUEST_TIMEOUT', 15);
155
 
156
  /**
157
  * Plugin's global variables.
161
  * conditional will act as a container helping in the readability of the code
162
  * considering the total number of lines that this file will have.
163
  */
164
+ if (defined('SUCURISCAN')) {
165
  /**
166
  * Define the prefix for some actions and filters that rely in the
167
  * differentiation of the type of site where the extension is being used. There
181
  $sucuriscan_pages = array(
182
  'sucuriscan' => 'Dashboard',
183
  'sucuriscan_scanner' => 'Malware Scan',
184
+ 'sucuriscan_firewall' => 'Firewall (WAF)',
185
  'sucuriscan_hardening' => 'Hardening',
186
  'sucuriscan_posthack' => 'Post-Hack',
187
  'sucuriscan_lastlogins' => 'Last Logins',
202
  */
203
 
204
  $sucuriscan_notify_options = array(
205
+ 'sucuriscan_notify_plugin_change' => 'Receive email alerts for <b>Sucuri</b> plugin changes',
206
  'sucuriscan_prettify_mails' => 'Receive email alerts in HTML <em>(there may be issues with some mail services)</em>',
207
  'sucuriscan_use_wpmail' => 'Use WordPress functions to send mails <em>(uncheck to use native PHP functions)</em>',
208
  'sucuriscan_lastlogin_redirection' => 'Allow redirection after login to report the last-login information',
209
  'sucuriscan_notify_scan_checksums' => 'Receive email alerts for core integrity checks',
210
  'sucuriscan_notify_user_registration' => 'user:Receive email alerts for new user registration',
211
  'sucuriscan_notify_success_login' => 'user:Receive email alerts for successful login attempts',
212
+ 'sucuriscan_notify_failed_login' => 'user:Receive email alerts for failed login attempts <em>(you may receive tons of emails)</em>',
213
+ 'sucuriscan_notify_bruteforce_attack' => 'user:Receive email alerts for password guessing attacks <em>(summary of failed logins per hour)</em>',
214
  'sucuriscan_notify_post_publication' => 'Receive email alerts for Post-Type changes <em>(configure from Ignore Alerts)</em>',
215
  'sucuriscan_notify_website_updated' => 'Receive email alerts when the WordPress version is updated',
216
  'sucuriscan_notify_settings_updated' => 'Receive email alerts when your website settings are updated',
217
  'sucuriscan_notify_theme_editor' => 'Receive email alerts when a file is modified with theme/plugin editor',
218
+ 'sucuriscan_notify_plugin_installed' => 'plugin:Receive email alerts when a <b>plugin is installed</b>',
219
+ 'sucuriscan_notify_plugin_activated' => 'plugin:Receive email alerts when a <b>plugin is activated</b>',
220
+ 'sucuriscan_notify_plugin_deactivated' => 'plugin:Receive email alerts when a <b>plugin is deactivated</b>',
221
+ 'sucuriscan_notify_plugin_updated' => 'plugin:Receive email alerts when a <b>plugin is updated</b>',
222
+ 'sucuriscan_notify_plugin_deleted' => 'plugin:Receive email alerts when a <b>plugin is deleted</b>',
223
+ 'sucuriscan_notify_widget_added' => 'widget:Receive email alerts when a <b>widget is added</b> to a sidebar',
224
+ 'sucuriscan_notify_widget_deleted' => 'widget:Receive email alerts when a <b>widget is deleted</b> from a sidebar',
225
+ 'sucuriscan_notify_theme_installed' => 'theme:Receive email alerts when a <b>theme is installed</b>',
226
+ 'sucuriscan_notify_theme_activated' => 'theme:Receive email alerts when a <b>theme is activated</b>',
227
+ 'sucuriscan_notify_theme_updated' => 'theme:Receive email alerts when a <b>theme is updated</b>',
228
+ 'sucuriscan_notify_theme_deleted' => 'theme:Receive email alerts when a <b>theme is deleted</b>',
229
  );
230
 
231
  $sucuriscan_schedule_allowed = array(
280
  /**
281
  * Remove the WordPress generator meta-tag from the source code.
282
  */
283
+ remove_action('wp_head', 'wp_generator');
284
 
285
  /**
286
  * Run a specific function defined in the plugin's code to locate every
288
  * information to the Sucuri API service where a security and integrity scan
289
  * will be performed against the hashes provided and the official versions.
290
  */
291
+ add_action('sucuriscan_scheduled_scan', 'SucuriScan::run_scheduled_task');
292
 
293
  /**
294
  * Initialize the execute of the main plugin's functions.
296
  * This will load the menu options in the WordPress administrator panel, and
297
  * execute the bootstrap function of the plugin.
298
  */
299
+ add_action('init', 'SucuriScanInterface::initialize', 1);
300
+ add_action('admin_init', 'SucuriScanInterface::create_datastore_folder');
301
+ add_action('admin_init', 'SucuriScanInterface::handle_old_plugins');
302
+ add_action('admin_enqueue_scripts', 'SucuriScanInterface::enqueue_scripts', 1);
303
 
304
  /**
305
  * Display extension menu and submenu items in the correct interface. For single
307
  * multisite installations the menu items must be available only in the network
308
  * panel and hidden in the administration panel of the subsites.
309
  */
310
+ add_action($sucuriscan_action_prefix . 'admin_menu', 'SucuriScanInterface::add_interface_menu');
311
 
312
  /**
313
  * Attach Ajax requests to a custom page handler.
314
  */
315
+ foreach ($sucuriscan_pages as $page_func => $page_title) {
316
  $ajax_func = $page_func . '_ajax';
317
 
318
+ if (function_exists($ajax_func)) {
319
+ add_action('wp_ajax_' . $ajax_func, $ajax_func);
320
  }
321
  }
322
 
330
  *
331
  * @see Class SucuriScanHook
332
  */
333
+ if (class_exists('SucuriScanHook')) {
334
  $sucuriscan_hooks = array(
335
  'add_attachment',
336
  'add_link',
352
  'xmlrpc_publish_post',
353
  );
354
 
355
+ if (SucuriScanOption::is_enabled(':xhr_monitor')) {
356
  $sucuriscan_hooks[] = 'all';
357
  }
358
 
359
+ foreach ($sucuriscan_hooks as $hook_name) {
360
  $hook_func = 'SucuriScanHook::hook_' . $hook_name;
361
+ add_action($hook_name, $hook_func, 50, 5);
362
  }
363
 
364
+ add_action('admin_init', 'SucuriScanHook::hook_undefined_actions');
365
+ add_action('login_form', 'SucuriScanHook::hook_undefined_actions');
366
  } else {
367
+ SucuriScanInterface::error('Function call interceptors are not working properly.');
368
  }
369
 
370
  /**
376
  * the plugin to execute the filesystem scans, the project integrity, and the
377
  * email notifications.
378
  */
379
+ add_action($sucuriscan_action_prefix . 'admin_notices', 'SucuriScanInterface::setup_notice');
380
 
381
  /**
382
  * Heartbeat API
387
  * cases it may improve the performance of the site by reducing the quantity of
388
  * requests sent to the server per session.
389
  */
390
+ add_filter('init', 'SucuriScanHeartbeat::register_script', 1);
391
+ add_filter('heartbeat_settings', 'SucuriScanHeartbeat::update_settings');
392
+ add_filter('heartbeat_send', 'SucuriScanHeartbeat::respond_to_send', 10, 3);
393
+ add_filter('heartbeat_received', 'SucuriScanHeartbeat::respond_to_received', 10, 3);
394
+ add_filter('heartbeat_nopriv_send', 'SucuriScanHeartbeat::respond_to_send', 10, 3);
395
+ add_filter('heartbeat_nopriv_received', 'SucuriScanHeartbeat::respond_to_received', 10, 3);
396
  }
397
 
398
  /**
402
  * other libraries extending from this and functions defined in other files, be
403
  * aware of the hierarchy and check the other libraries for duplicated methods.
404
  */
405
+ class SucuriScan
406
+ {
407
  /**
408
  * Class constructor.
409
  */
410
+ public function __construct()
411
+ {
412
  }
413
 
414
  /**
422
  * @param string $var_name Name of a variable with an optional colon at the beginning.
423
  * @return string Full name of the variable with the extra characters (if needed).
424
  */
425
+ public static function variable_prefix($var_name = '')
426
+ {
427
+ if (!empty($var_name) && $var_name[0] === ':') {
428
+ $var_name = sprintf(
429
+ '%s_%s',
430
+ SUCURISCAN,
431
+ substr($var_name, 1)
432
+ );
433
  }
434
 
435
  return $var_name;
441
  * @param string $property The configuration option name.
442
  * @return string Value of the configuration option as a string on success.
443
  */
444
+ public static function ini_get($property = '')
445
+ {
446
+ $ini_value = ini_get($property);
447
+ $default = array(
448
+ 'error_log' => 'error_log',
449
+ 'safe_mode' => 'Off',
450
+ 'memory_limit' => '128M',
451
+ 'upload_max_filesize' => '2M',
452
+ 'post_max_size' => '8M',
453
+ 'max_execution_time' => '30',
454
+ 'max_input_time' => '-1',
455
+ );
456
 
457
+ if ($ini_value === false) {
458
  $ini_value = 'Undefined';
459
+ } elseif (empty($ini_value) || $ini_value === null) {
460
+ if (array_key_exists($property, $default)) {
461
+ $ini_value = $default[$property];
462
+ } else {
463
+ $ini_value = 'Off';
 
 
 
 
 
 
464
  }
465
  }
466
 
467
+ if ($property == 'error_log') {
468
+ $ini_value = basename($ini_value);
469
  }
470
 
471
  return $ini_value;
478
  * @param string $text The text which is to be encoded.
479
  * @return string The encoded text with HTML entities.
480
  */
481
+ public static function escape($text = '')
482
+ {
483
  // Escape the value of the variable using a built-in function if possible.
484
+ if (function_exists('esc_attr')) {
485
+ $text = esc_attr($text);
486
  } else {
487
+ $text = htmlspecialchars($text);
488
  }
489
 
490
  return $text;
496
  * @param integer $length Length of the string that will be generated.
497
  * @return string The random string generated.
498
  */
499
+ public static function random_char($length = 4)
500
+ {
501
  $string = '';
502
+ $offset = 25;
503
+ $chars = array(
504
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
505
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
506
+ );
507
 
508
+ for ($i = 0; $i < $length; $i++) {
509
+ $index = rand(0, $offset);
510
+ $string .= $chars[$index];
511
  }
512
 
513
  return $string;
517
  * Translate a given number in bytes to a human readable file size using the
518
  * a approximate value in Kylo, Mega, Giga, etc.
519
  *
520
+ * @link https://www.php.net/manual/en/function.filesize.php#106569
521
  * @param integer $bytes An integer representing a file size in bytes.
522
  * @param integer $decimals How many decimals should be returned after the translation.
523
  * @return string Human readable representation of the given number in Kylo, Mega, Giga, etc.
524
  */
525
+ public static function human_filesize($bytes = 0, $decimals = 2)
526
+ {
527
  $sz = 'BKMGTP';
528
+ $factor = floor((strlen($bytes) - 1) / 3);
529
+ return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[ $factor ];
530
  }
531
 
532
  /**
536
  * @param string $path The relative path that needs to be completed to get the absolute path.
537
  * @return string The full filesystem path including the directory specified.
538
  */
539
+ public static function datastore_folder_path($path = '')
540
+ {
541
+ $default_dir = 'sucuri';
542
+ $datastore = SucuriScanOption::get_option(':datastore_path');
543
 
544
  // Use the uploads folder by default.
545
+ if (empty($datastore)) {
546
  $uploads_path = false;
547
 
548
  // Multisite installations may have different paths.
549
+ if (function_exists('wp_upload_dir')) {
550
  $upload_dir = wp_upload_dir();
551
 
552
+ if (isset($upload_dir['basedir'])) {
553
+ $uploads_path = rtrim($upload_dir['basedir'], '/');
554
  }
555
  }
556
 
557
+ if ($uploads_path === false) {
558
+ if (defined('WP_CONTENT_DIR')) {
559
+ $uploads_path = WP_CONTENT_DIR . '/uploads';
560
  } else {
561
+ $uploads_path = ABSPATH . '/wp-content/uploads';
562
  }
563
  }
564
 
565
+ $datastore = $uploads_path . '/' . $default_dir;
566
+ SucuriScanOption::update_option(':datastore_path', $datastore);
567
  }
568
 
569
+ return $datastore . '/' . $path;
 
 
570
  }
571
 
572
  /**
574
  *
575
  * @return boolean Either TRUE or FALSE in case WordPress is being used as a multi-site instance.
576
  */
577
+ public static function is_multisite()
578
+ {
579
+ return (bool) (function_exists('is_multisite') && is_multisite());
580
+ }
 
 
 
581
 
582
+ public static function admin_url($url = '')
583
+ {
584
+ if (self::is_multisite()) {
585
+ return network_admin_url($url);
586
+ } else {
587
+ return admin_url($url);
588
+ }
589
  }
590
 
591
  /**
593
  *
594
  * @return string The version number of Wordpress installed.
595
  */
596
+ public static function site_version()
597
+ {
598
  global $wp_version;
599
 
600
+ if ($wp_version === null) {
601
  $wp_version_path = ABSPATH . WPINC . '/version.php';
602
 
603
+ if (file_exists($wp_version_path)) {
604
  include($wp_version_path);
605
  $wp_version = isset($wp_version) ? $wp_version : '0.0';
606
  } else {
607
+ $option_version = get_option('version');
608
  $wp_version = $option_version ? $option_version : '0.0';
609
  }
610
  }
611
 
612
+ $wp_version = self::escape($wp_version);
613
 
614
  return $wp_version;
615
  }
619
  *
620
  * @return string Absolute path of the WordPress configuration file.
621
  */
622
+ public static function get_wpconfig_path()
623
+ {
624
+ if (defined('ABSPATH')) {
625
  $file_path = ABSPATH . '/wp-config.php';
626
 
627
  // if wp-config.php doesn't exist, or is not readable check one directory up.
628
+ if (!file_exists($file_path)) {
629
  $file_path = ABSPATH . '/../wp-config.php';
630
  }
631
 
632
  // Remove duplicated double slashes.
633
+ $file_path = @realpath($file_path);
634
 
635
+ if ($file_path) {
636
  return $file_path;
637
  }
638
  }
645
  *
646
  * @return string Absolute path of the main WordPress htaccess file.
647
  */
648
+ public static function get_htaccess_path()
649
+ {
650
+ if (defined('ABSPATH')) {
651
  $base_dirs = array(
652
+ rtrim(ABSPATH, '/'),
653
+ dirname(ABSPATH),
654
+ dirname(dirname(ABSPATH)),
655
  );
656
 
657
+ foreach ($base_dirs as $base_dir) {
658
+ $htaccess_path = sprintf('%s/.htaccess', $base_dir);
659
 
660
+ if (file_exists($htaccess_path)) {
661
  return $htaccess_path;
662
  }
663
  }
671
  *
672
  * @return string Secret key definition pattern.
673
  */
674
+ public static function secret_key_pattern()
675
+ {
676
  return '/define\(\s*\'([A-Z_]+)\',(\s*)\'(.+)\'\s*\);/';
677
  }
678
 
681
  *
682
  * @return void
683
  */
684
+ public static function run_scheduled_task()
685
+ {
686
  SucuriScanEvent::filesystem_scan();
687
+ sucuriscan_core_files_data(true);
688
+ }
689
+
690
+ /**
691
+ * List of allowed HTTP headers to retrieve the real IP.
692
+ *
693
+ * Once the DNS lookups are enabled to discover the real IP address of the
694
+ * visitors the user may choose the HTTP header that will be used by default to
695
+ * retrieve the real IP address of each HTTP request, generally they do not need
696
+ * to set this but in rare cases the hosting provider may have a load balancer
697
+ * that can interfere in the process, in which case they will have to explicitly
698
+ * specify the main HTTP header. This is a list of the allowed headers that the
699
+ * user can choose.
700
+ *
701
+ * @param boolean $with_keys Return the array with its values are keys.
702
+ * @return array Allowed HTTP headers to retrieve real IP.
703
+ */
704
+ public static function allowedHttpHeaders($with_keys = false)
705
+ {
706
+ $allowed = array(
707
+ 'HTTP_X_SUCURI_CLIENTIP',
708
+ 'HTTP_X_REAL_IP',
709
+ 'HTTP_CLIENT_IP',
710
+ 'HTTP_X_FORWARDED_FOR',
711
+ 'HTTP_X_FORWARDED',
712
+ 'HTTP_FORWARDED_FOR',
713
+ 'HTTP_FORWARDED',
714
+ 'SUCURI_RIP',
715
+ 'REMOTE_ADDR',
716
+ );
717
+
718
+ if ($with_keys === true) {
719
+ $verbose = array();
720
+
721
+ foreach ($allowed as $header) {
722
+ $verbose[$header] = $header;
723
+ }
724
+
725
+ return $verbose;
726
+ }
727
+
728
+ return $allowed;
729
+ }
730
+
731
+ /**
732
+ * List HTTP headers ordered.
733
+ *
734
+ * The list of HTTP headers is ordered per relevancy, and having the main HTTP
735
+ * header as the first entry, this guarantees that the IP address of the
736
+ * visitors will be retrieved from the HTTP header chosen by the user first and
737
+ * fallback to the other alternatives if available.
738
+ *
739
+ * @return array Ordered allowed HTTP headers.
740
+ */
741
+ private static function orderedHttpHeaders()
742
+ {
743
+ $ordered = array();
744
+ $allowed = self::allowedHttpHeaders();
745
+ $addr_header = SucuriScanOption::get_option(':addr_header');
746
+ $ordered[] = $addr_header;
747
+
748
+ foreach ($allowed as $header) {
749
+ if (!in_array($header, $ordered)) {
750
+ $ordered[] = $header;
751
+ }
752
+ }
753
+
754
+ return $ordered;
755
  }
756
 
757
  /**
758
  * Retrieve the real ip address of the user in the current request.
759
  *
760
+ * @param boolean $with_header Return HTTP header where the IP address was found.
761
+ * @return string Real IP address of the user in the current request.
762
  */
763
+ public static function get_remote_addr($with_header = false)
764
+ {
765
+ $remote_addr = false;
766
  $header_used = 'unknown';
767
+ $headers = self::orderedHttpHeaders();
768
 
769
+ foreach ($headers as $header) {
770
+ if (array_key_exists($header, $_SERVER)
771
+ && self::is_valid_ip($_SERVER[$header])
772
+ ) {
773
+ $remote_addr = $_SERVER[$header];
774
+ $header_used = $header;
775
+ break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
776
  }
 
 
 
777
  }
778
 
779
+ if (!$remote_addr || $remote_addr === '::1') {
780
  $remote_addr = '127.0.0.1';
781
  }
782
 
783
+ if ($with_header) {
784
  return $header_used;
785
  }
786
 
792
  *
793
  * @return string The HTTP header used to retrieve the remote address.
794
  */
795
+ public static function get_remote_addr_header()
796
+ {
797
+ return self::get_remote_addr(true);
798
  }
799
 
800
  /**
802
  *
803
  * @return string The user-agent from the current request.
804
  */
805
+ public static function get_user_agent()
806
+ {
807
+ if (isset($_SERVER['HTTP_USER_AGENT'])) {
808
+ return self::escape($_SERVER['HTTP_USER_AGENT']);
809
  }
810
 
811
  return false;
816
  *
817
  * @return string The domain of the current site.
818
  */
819
+ public static function get_domain($return_tld = false)
820
+ {
821
+ if (function_exists('get_site_url')) {
822
  $site_url = get_site_url();
823
  $pattern = '/([fhtps]+:\/\/)?([^:\/]+)(:[0-9:]+)?(\/.*)?/';
824
  $replacement = ( $return_tld === true ) ? '$2' : '$2$3$4';
825
+ $domain_name = @preg_replace($pattern, $replacement, $site_url);
826
 
827
  return $domain_name;
828
  }
835
  *
836
  * @return string Top-level domain (TLD) of the website.
837
  */
838
+ public static function get_top_level_domain()
839
+ {
840
+ return self::get_domain(true);
841
  }
842
 
843
  /**
845
  *
846
  * @return boolean TRUE if reverse proxies must be supported, FALSE otherwise.
847
  */
848
+ public static function support_reverse_proxy()
849
+ {
850
+ return SucuriScanOption::is_enabled(':revproxy');
851
  }
852
 
853
  /**
861
  *
862
  * @return boolean True if the DNS lookups should be executed, false otherwise.
863
  */
864
+ public static function execute_dns_lookups()
865
+ {
866
+ if (( defined('NOT_USING_CLOUDPROXY') && NOT_USING_CLOUDPROXY === true )
867
+ || SucuriScanOption::is_disabled(':dns_lookups')
868
  ) {
869
  return false;
870
  }
878
  * @param boolean $verbose Return an array with the hostname, address, and status, or not.
879
  * @return boolean Either TRUE or FALSE if the site is behind CloudProxy.
880
  */
881
+ public static function is_behind_cloudproxy($verbose = false)
882
+ {
883
  $http_host = self::get_top_level_domain();
884
 
885
+ if (self::execute_dns_lookups()) {
886
+ $host_by_addr = @gethostbyname($http_host);
887
+ $host_by_name = @gethostbyaddr($host_by_addr);
888
+ $status = (bool) preg_match('/^cloudproxy[0-9]+\.sucuri\.net$/', $host_by_name);
889
  } else {
890
  $status = false;
891
  $host_by_addr = '::1';
897
  * the site as protected by a firewall. A fake key can be used to bypass the DNS
898
  * checking, but that is not something that will affect us, only the client.
899
  */
900
+ if ($status === false
901
+ && SucuriScanAPI::getCloudproxyKey()
 
902
  ) {
903
  $status = true;
904
  }
905
 
906
+ if ($verbose) {
907
  return array(
908
  'http_host' => $http_host,
909
  'host_name' => $host_by_name,
922
  *
923
  * @return string The administrator email address.
924
  */
925
+ public static function get_site_email()
926
+ {
927
+ $email = get_option('admin_email');
928
 
929
+ if (self::is_valid_email($email)) {
930
  return $email;
931
  }
932
 
939
  * @param integer $identifier User account identifier.
940
  * @return object WordPress user object with data.
941
  */
942
+ public static function get_user_by_id($identifier = 0)
943
+ {
944
+ if (function_exists('get_user_by')) {
945
+ $user = get_user_by('id', $identifier);
946
 
947
+ if ($user instanceof WP_User) {
948
  return $user;
949
  }
950
  }
957
  *
958
  * @return array List of admin users, false otherwise.
959
  */
960
+ public static function get_admin_users()
961
+ {
962
+ if (function_exists('get_users')) {
963
  $args = array( 'role' => 'administrator' );
964
 
965
+ return get_users($args);
966
  }
967
 
968
  return false;
978
  *
979
  * @return array List of user identifiers and email addresses.
980
  */
981
+ public static function get_users_for_api_key()
982
+ {
983
  $valid_users = array();
984
  $users = self::get_admin_users();
985
 
986
+ if ($users !== false) {
987
+ foreach ($users as $user) {
988
+ if ($user->user_status === '0') {
989
  $valid_users[ $user->ID ] = sprintf(
990
  '%s - %s',
991
  $user->user_login,
1003
  *
1004
  * @return integer Return current Unix timestamp.
1005
  */
1006
+ public static function local_time()
1007
+ {
1008
+ if (function_exists('current_time')) {
1009
+ return current_time('timestamp');
1010
  } else {
1011
  return time();
1012
  }
1022
  * @param integer $timestamp Unix timestamp.
1023
  * @return string The date, translated if locale specifies it.
1024
  */
1025
+ public static function datetime($timestamp = null)
1026
+ {
1027
  $date_format = get_option('date_format');
1028
  $time_format = get_option('time_format');
1029
  $tz_format = sprintf('%s %s', $date_format, $time_format);
1040
  *
1041
  * @return string The date, translated if locale specifies it.
1042
  */
1043
+ public static function current_datetime()
1044
+ {
1045
  return self::datetime();
1046
  }
1047
 
1051
  * @param integer $timestamp The Unix time number of the date/time before now.
1052
  * @return string The time passed since the timestamp specified.
1053
  */
1054
+ public static function time_ago($timestamp = 0)
1055
+ {
1056
+ if (!is_numeric($timestamp)) {
1057
+ $timestamp = strtotime($timestamp);
1058
  }
1059
 
1060
  $local_time = self::local_time();
1061
+ $diff = abs($local_time - intval($timestamp));
1062
 
1063
+ if ($diff == 0) {
1064
  return 'just now';
1065
  }
1066
 
1074
  $diff < 60 => array( 'second', 1, ),
1075
  );
1076
 
1077
+ $value = floor($diff / $intervals[1][1]);
1078
  $time_ago = sprintf(
1079
  '%s %s%s ago',
1080
  $value,
1088
  /**
1089
  * Convert an string of characters into a valid variable name.
1090
  *
1091
+ * @see https://www.php.net/manual/en/language.variables.basics.php
1092
  *
1093
  * @param string $text A text containing alpha-numeric and special characters.
1094
  * @return string A valid variable name.
1095
  */
1096
+ public static function human2var($text = '')
1097
+ {
1098
+ $text = strtolower($text);
1099
  $pattern = '/[^a-z0-9_]/';
1100
+ $var_name = preg_replace($pattern, '_', $text);
1101
 
1102
  return $var_name;
1103
  }
1108
  * @param string $data The data that will be checked.
1109
  * @return boolean TRUE if the data was serialized, FALSE otherwise.
1110
  */
1111
+ public static function is_serialized($data = '')
1112
+ {
1113
+ return ( is_string($data) && preg_match('/^(a|O):[0-9]+:.+/', $data) );
1114
  }
1115
 
1116
  /**
1119
  * @param string $remote_addr The host IP address.
1120
  * @return boolean Whether the IP address specified is valid or not.
1121
  */
1122
+ public static function is_valid_ip($remote_addr = '')
1123
+ {
1124
+ if (function_exists('filter_var')) {
1125
+ return (bool) filter_var($remote_addr, FILTER_VALIDATE_IP);
1126
+ } elseif (strlen($remote_addr) >= 7) {
1127
  $pattern = '/^([0-9]{1,3}\.) {3}[0-9]{1,3}$/';
1128
 
1129
+ if (preg_match($pattern, $remote_addr, $match)) {
1130
+ for ($i = 0; $i < 4; $i++) {
1131
+ if ($match[ $i ] > 255) {
1132
  return false;
1133
  }
1134
  }
1147
  * @param string $remote_addr The supposed ip address that will be checked.
1148
  * @return boolean Either TRUE or FALSE if the ip address specified is valid or not.
1149
  */
1150
+ public static function is_valid_cidr($remote_addr = '')
1151
+ {
1152
+ if (preg_match('/^([0-9\.]{7,15})\/(8|16|24)$/', $remote_addr, $match)) {
1153
+ if (self::is_valid_ip($match[1])) {
1154
  return true;
1155
  }
1156
  }
1164
  * @param string $remote_addr The supposed ip address that will be formatted.
1165
  * @return array Clean address, CIDR range, and CIDR format; FALSE otherwise.
1166
  */
1167
+ public static function get_ip_info($remote_addr = '')
1168
+ {
1169
+ if ($remote_addr) {
1170
+ $ip_parts = explode('/', $remote_addr);
1171
 
1172
+ if (array_key_exists(0, $ip_parts)
1173
+ && self::is_valid_ip($ip_parts[0])
 
1174
  ) {
1175
  $addr_info = array();
1176
  $addr_info['remote_addr'] = $ip_parts[0];
1191
  * 5.2.0 if it is not found in the interpreter this function will sue regular
1192
  * expressions to check whether the email address passed is valid or not.
1193
  *
1194
+ * @see https://www.php.net/manual/en/function.filter-var.php
1195
  *
1196
  * @param string $email The string that will be validated as an email address.
1197
  * @return boolean TRUE if the email address passed to the function is valid, FALSE if not.
1198
  */
1199
+ public static function is_valid_email($email = '')
1200
+ {
1201
+ if (function_exists('filter_var')) {
1202
+ return (bool) filter_var($email, FILTER_VALIDATE_EMAIL);
1203
  } else {
1204
  $pattern = '/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix';
1205
+ return (bool) preg_match($pattern, $email);
1206
  }
1207
  }
1208
 
1212
  * @param string $pattern The regular expression to check.
1213
  * @return boolean True if the regular expression is valid, false otherwise.
1214
  */
1215
+ public static function is_valid_pattern($pattern = '')
1216
+ {
1217
  return (bool) (
1218
+ is_string($pattern)
1219
+ && !empty($pattern)
1220
+ && @preg_match($pattern, null) !== false
1221
  );
1222
  }
1223
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1224
  /**
1225
  * Cut a long text to the length specified, and append suspensive points at the end.
1226
  *
1228
  * @param integer $length Maximum length of the returned string, default is 10.
1229
  * @return string Short version of the text specified.
1230
  */
1231
+ public static function excerpt($text = '', $length = 10)
1232
+ {
1233
+ $text_length = strlen($text);
1234
 
1235
+ if ($text_length > $length) {
1236
+ return substr($text, 0, $length) . '...';
1237
  }
1238
 
1239
  return $text;
1246
  * @param integer $length Maximum length of the returned string, default is 10.
1247
  * @return string Short version of the text specified.
1248
  */
1249
+ public static function excerpt_rev($text = '', $length = 10)
1250
+ {
1251
+ $str_reversed = strrev($text);
1252
+ $str_excerpt = self::excerpt($str_reversed, $length);
1253
+ $text_transformed = strrev($str_excerpt);
1254
 
1255
  return $text_transformed;
1256
  }
1261
  * @param array $list An array or multidimensional array of different values.
1262
  * @return boolean TRUE if the list is multidimensional, FALSE otherwise.
1263
  */
1264
+ public static function is_multi_list($list = array())
1265
+ {
1266
+ if (!empty($list)) {
1267
+ foreach ((array) $list as $item) {
1268
+ if (is_array($item)) {
1269
  return true;
1270
  }
1271
  }
1281
  * @param array $list The array of strings to implode.
1282
  * @return string String of all the items in the list, with the separator between them.
1283
  */
1284
+ public static function implode($separator = '', $list = array())
1285
+ {
1286
+ if (self::is_multi_list($list)) {
1287
  $pieces = array();
1288
 
1289
+ foreach ($list as $items) {
1290
+ $pieces[] = @implode($separator, $items);
1291
  }
1292
 
1293
+ $joined_pieces = '(' . implode('), (', $pieces) . ')';
1294
 
1295
  return $joined_pieces;
1296
  } else {
1297
+ return implode($separator, $list);
1298
  }
1299
  }
1300
 
1304
  * @param string $current_page Identifier of the current page.
1305
  * @return boolean TRUE if the current page must not have noticies.
1306
  */
1307
+ public static function no_notices_here($current_page = false)
1308
+ {
1309
  global $sucuriscan_no_notices_in;
1310
 
1311
+ if ($current_page === false) {
1312
+ $current_page = SucuriScanRequest::get('page');
1313
  }
1314
 
1315
+ if (isset($sucuriscan_no_notices_in)
1316
+ && is_array($sucuriscan_no_notices_in)
1317
+ && !empty($sucuriscan_no_notices_in)
 
1318
  ) {
1319
+ return (bool) in_array($current_page, $sucuriscan_no_notices_in);
1320
  }
1321
 
1322
  return false;
1327
  *
1328
  * @return boolean TRUE if the site is running over Nginx, FALSE otherwise.
1329
  */
1330
+ public static function is_nginx_server()
1331
+ {
1332
+ return (bool) preg_match('/^nginx(\/[0-9\.]+)?$/', @$_SERVER['SERVER_SOFTWARE']);
1333
  }
1334
 
1335
  /**
1337
  *
1338
  * @return boolean TRUE if the site is running over Nginx, FALSE otherwise.
1339
  */
1340
+ public static function is_iis_server()
1341
+ {
1342
+ return (bool) preg_match('/Microsoft-IIS/i', @$_SERVER['SERVER_SOFTWARE']);
1343
  }
 
1344
  }
1345
 
1346
  /**
1351
  * these methods at most instead of accessing an index in the global PHP
1352
  * variables _POST, _GET, _REQUEST since they may come with insecure data.
1353
  */
1354
+ class SucuriScanRequest extends SucuriScan
1355
+ {
1356
 
1357
  /**
1358
  * Returns the value stored in a specific index in the global _GET, _POST or
1364
  * @param string $pattern Optional pattern to match allowed values in the requested key.
1365
  * @return string The value stored in the specified key inside the global _GET variable.
1366
  */
1367
+ public static function request($list = array(), $key = '', $pattern = '')
1368
+ {
1369
+ $key = self::variable_prefix($key);
1370
 
1371
+ if (is_array($list)
1372
+ && is_string($key)
 
1373
  && isset($list[ $key ])
1374
  ) {
1375
  // Select the key from the list and escape its content.
1376
  $key_value = $list[ $key ];
1377
 
1378
  // Define regular expressions for specific value types.
1379
+ if ($pattern === '') {
1380
  $pattern = '/.*/';
1381
  } else {
1382
+ switch ($pattern) {
1383
+ case '_nonce':
1384
+ $pattern = '/^[a-z0-9]{10}$/';
1385
+ break;
1386
+ case '_page':
1387
+ $pattern = '/^[a-z_]+$/';
1388
+ break;
1389
+ case '_array':
1390
+ $pattern = '_array';
1391
+ break;
1392
+ case '_yyyymmdd':
1393
+ $pattern = '/^[0-9]{4}(\-[0-9]{2}) {2}$/';
1394
+ break;
1395
+ default:
1396
+ $pattern = '/^'.$pattern.'$/';
1397
+ break;
1398
  }
1399
  }
1400
 
1401
  // If the request data is an array, then only cast the value.
1402
+ if ($pattern == '_array' && is_array($key_value)) {
1403
  return (array) $key_value;
1404
  }
1405
 
1406
  // Check the format of the request data with a regex defined above.
1407
+ if (@preg_match($pattern, $key_value)) {
1408
+ return self::escape($key_value);
1409
  }
1410
  }
1411
 
1420
  * @param string $pattern Optional pattern to match allowed values in the requested key.
1421
  * @return string The value stored in the specified key inside the global _GET variable.
1422
  */
1423
+ public static function get($key = '', $pattern = '')
1424
+ {
1425
+ return self::request($_GET, $key, $pattern);
1426
  }
1427
 
1428
  /**
1433
  * @param string $pattern Optional pattern to match allowed values in the requested key.
1434
  * @return string The value stored in the specified key inside the global _POST variable.
1435
  */
1436
+ public static function post($key = '', $pattern = '')
1437
+ {
1438
+ return self::request($_POST, $key, $pattern);
1439
  }
1440
 
1441
  /**
1446
  * @param string $pattern Optional pattern to match allowed values in the requested key.
1447
  * @return string The value stored in the specified key inside the global _POST variable.
1448
  */
1449
+ public static function get_or_post($key = '', $pattern = '')
1450
+ {
1451
+ return self::request($_REQUEST, $key, $pattern);
1452
  }
 
1453
  }
1454
 
1455
  /**
1460
  * offers a high-level object oriented interface to information for an individual
1461
  * file.
1462
  */
1463
+ class SucuriScanFileInfo extends SucuriScan
1464
+ {
1465
 
1466
  /**
1467
  * Define the interface that will be used to execute the file system scans, the
1523
  /**
1524
  * Class constructor.
1525
  */
1526
+ public function __construct()
1527
+ {
1528
  }
1529
 
1530
  /**
1537
  * @param boolean $as_array Whether the result of the operation will be returned as an array or string.
1538
  * @return array List of files in the main and subdirectories of the folder specified.
1539
  */
1540
+ public function get_directory_tree_md5($directory = '', $as_array = false)
1541
+ {
1542
  $project_signatures = '';
1543
+ $abs_path = rtrim(ABSPATH, DIRECTORY_SEPARATOR);
1544
+ $files = $this->get_directory_tree($directory);
1545
 
1546
+ if ($as_array) {
1547
  $project_signatures = array();
1548
  }
1549
 
1550
+ if ($files) {
1551
+ sort($files);
1552
 
1553
+ foreach ($files as $filepath) {
1554
+ $file_checksum = @md5_file($filepath);
1555
+ $filesize = @filesize($filepath);
1556
 
1557
+ if ($as_array) {
1558
+ $basename = str_replace($abs_path . DIRECTORY_SEPARATOR, '', $filepath);
1559
  $project_signatures[ $basename ] = array(
1560
  'filepath' => $filepath,
1561
  'checksum' => $file_checksum,
1562
  'filesize' => $filesize,
1563
+ 'created_at' => @filectime($filepath),
1564
+ 'modified_at' => @filemtime($filepath),
1565
  );
1566
  } else {
1567
+ $filepath = str_replace($abs_path, $abs_path . DIRECTORY_SEPARATOR, $filepath);
1568
  $project_signatures .= sprintf(
1569
  "%s%s%s%s\n",
1570
  $file_checksum,
1571
  $filesize,
1572
+ chr(32),
1573
  $filepath
1574
  );
1575
  }
1587
  * @param string $directory Parent directory where the filesystem scan will start.
1588
  * @return array List of files in the main and subdirectories of the folder specified.
1589
  */
1590
+ public function get_directory_tree($directory = '')
1591
+ {
1592
+ if (file_exists($directory) && is_dir($directory)) {
1593
  $tree = array();
1594
 
1595
  // Check whether the ignore scanning feature is enabled or not.
1596
+ if (SucuriScanFSScanner::will_ignore_scanning()) {
1597
  $this->ignored_directories = SucuriScanFSScanner::get_ignored_directories();
1598
  }
1599
 
1600
+ switch ($this->scan_interface) {
1601
  case 'spl':
1602
+ if ($this->is_spl_available()) {
1603
+ $tree = $this->get_directory_tree_with_spl($directory);
1604
  } else {
1605
  $this->scan_interface = 'opendir';
1606
+ SucuriScanOption::update_option(':scan_interface', $this->scan_interface);
1607
+ $tree = $this->get_directory_tree($directory);
1608
  }
1609
  break;
1610
 
1611
  case 'glob':
1612
+ $tree = $this->get_directory_tree_with_glob($directory);
1613
  break;
1614
 
1615
  case 'opendir':
1616
+ $tree = $this->get_directory_tree_with_opendir($directory);
1617
  break;
1618
 
1619
  default:
1620
  $this->scan_interface = 'spl';
1621
+ $tree = $this->get_directory_tree($directory);
1622
  break;
1623
  }
1624
 
1635
  * @param string $directory Directory where the scanner is located at the moment.
1636
  * @return array List of file paths where the file was found.
1637
  */
1638
+ public function find_file($filename = '', $directory = null)
1639
+ {
1640
  $file_paths = array();
1641
 
1642
+ if (is_null($directory)
1643
+ && defined('ABSPATH')
 
1644
  ) {
1645
  $directory = ABSPATH;
1646
  }
1647
 
1648
+ if (is_dir($directory)) {
1649
+ $dir_tree = $this->get_directory_tree($directory);
1650
 
1651
+ foreach ($dir_tree as $filepath) {
1652
+ if (stripos($filepath, $filename) !== false) {
1653
  $file_paths[] = $filepath;
1654
  }
1655
  }
1663
  * or not, it is required to have PHP >= 5.1.0. The SplFileObject class offers
1664
  * an object oriented interface for a file.
1665
  *
1666
+ * @link https://www.php.net/manual/en/class.splfileobject.php
1667
  *
1668
  * @return boolean Whether the PHP class "SplFileObject" is available or not.
1669
  */
1670
+ public static function is_spl_available()
1671
+ {
1672
+ return (bool) class_exists('SplFileObject');
1673
  }
1674
 
1675
  /**
1677
  * of the folder specified. Some folders and files will be ignored depending
1678
  * on some rules defined by the developer.
1679
  *
1680
+ * @link https://www.php.net/manual/en/class.recursivedirectoryiterator.php
1681
  * @see RecursiveDirectoryIterator extends FilesystemIterator
1682
  * @see FilesystemIterator extends DirectoryIterator
1683
  * @see DirectoryIterator extends SplFileInfo
1686
  * @param string $directory Parent directory where the filesystem scan will start.
1687
  * @return array List of files in the main and subdirectories of the folder specified.
1688
  */
1689
+ private function get_directory_tree_with_spl($directory = '')
1690
+ {
1691
  $files = array();
1692
+ $filepath = @realpath($directory);
1693
  $objects = array();
1694
 
1695
  // Exception for directory name must not be empty.
1696
+ if ($filepath === false) {
1697
  return $files;
1698
  }
1699
 
1700
+ if (!class_exists('FilesystemIterator')) {
1701
  $this->scan_interface = 'opendir';
1702
+ SucuriScanOption::update_option(':scan_interface', $this->scan_interface);
1703
+ $alternative_tree = $this->get_directory_tree($directory);
1704
 
1705
  return $alternative_tree;
1706
  }
1707
 
1708
  try {
1709
+ if ($this->run_recursively) {
1710
  $flags = FilesystemIterator::KEY_AS_PATHNAME
1711
  | FilesystemIterator::CURRENT_AS_FILEINFO
1712
  | FilesystemIterator::SKIP_DOTS
1713
  | FilesystemIterator::UNIX_PATHS;
1714
  $objects = new RecursiveIteratorIterator(
1715
+ new RecursiveDirectoryIterator($filepath, $flags),
1716
  RecursiveIteratorIterator::SELF_FIRST,
1717
  RecursiveIteratorIterator::CATCH_GET_CHILD
1718
  );
1719
  } else {
1720
+ $objects = new DirectoryIterator($filepath);
1721
  }
1722
+ } catch (RuntimeException $exception) {
1723
+ SucuriScanEvent::report_exception($exception);
1724
  }
1725
 
1726
+ foreach ($objects as $filepath => $fileinfo) {
1727
  $filename = $fileinfo->getFilename();
1728
 
1729
+ if ($this->ignore_folderpath(null, $filename)
 
1730
  || (
1731
  $this->skip_directories === true
1732
  && $fileinfo->isDir()
1735
  continue;
1736
  }
1737
 
1738
+ if ($this->run_recursively) {
1739
+ $directory = dirname($filepath);
1740
  } else {
1741
  $directory = $fileinfo->getPath();
1742
  $filepath = $directory . '/' . $filename;
1743
  }
1744
 
1745
+ if ($this->ignore_folderpath($directory, $filename)
1746
+ || $this->ignore_filepath($filename)
 
1747
  ) {
1748
  continue;
1749
  }
1762
  * @param string $directory Parent directory where the filesystem scan will start.
1763
  * @return array List of files in the main and subdirectories of the folder specified.
1764
  */
1765
+ private function get_directory_tree_with_glob($directory = '')
1766
+ {
1767
  $files = array();
1768
+ $directory_pattern = sprintf('%s/*', rtrim($directory, '/'));
1769
+ $files_found = @glob($directory_pattern);
1770
+
1771
+ if (is_array($files_found)) {
1772
+ foreach ($files_found as $filepath) {
1773
+ $filepath = @realpath($filepath);
1774
+ $directory = dirname($filepath);
1775
+ $filepath_parts = explode('/', $filepath);
1776
+ $filename = array_pop($filepath_parts);
1777
+
1778
+ if (is_dir($filepath)) {
1779
+ if ($this->ignore_folderpath($directory, $filename)) {
1780
  continue;
1781
  }
1782
 
1783
+ if ($this->run_recursively) {
1784
+ $sub_files = $this->get_directory_tree_with_glob($filepath);
1785
 
1786
+ if ($sub_files) {
1787
+ $files = array_merge($files, $sub_files);
1788
  }
1789
  }
1790
+ } elseif ($this->ignore_filepath($filename)) {
1791
  continue;
1792
  } else {
1793
  $files[] = $filepath;
1806
  * @param string $directory Parent directory where the filesystem scan will start.
1807
  * @return array List of files in the main and subdirectories of the folder specified.
1808
  */
1809
+ private function get_directory_tree_with_opendir($directory = '')
1810
+ {
1811
  $files = array();
1812
+ $dh = @opendir($directory);
1813
 
1814
+ if (!$dh) {
1815
  return false;
1816
  }
1817
 
1818
+ while (($filename = readdir($dh)) !== false) {
1819
+ $filepath = @realpath($directory . '/' . $filename);
1820
 
1821
+ if ($filepath === false) {
1822
  continue;
1823
+ } elseif (is_dir($filepath)) {
1824
+ if ($this->ignore_folderpath($directory, $filename)) {
1825
  continue;
1826
  }
1827
 
1828
+ if ($this->run_recursively) {
1829
+ $sub_files = $this->get_directory_tree_with_opendir($filepath);
1830
 
1831
+ if ($sub_files) {
1832
+ $files = array_merge($files, $sub_files);
1833
  }
1834
  }
1835
  } else {
1836
+ if ($this->ignore_filepath($filename)) {
1837
  continue;
1838
  }
1839
  $files[] = $filepath;
1840
  }
1841
  }
1842
 
1843
+ closedir($dh);
1844
  return $files;
1845
  }
1846
 
1851
  * @param string $filename Name of the folder or file being scanned at the moment.
1852
  * @return boolean Either TRUE or FALSE representing that the scan should ignore this folder or not.
1853
  */
1854
+ private function ignore_folderpath($directory = '', $filename = '')
1855
+ {
1856
  // Ignoring current and parent folders.
1857
+ if ($filename == '.' || $filename == '..') {
1858
  return true;
1859
  }
1860
 
1861
+ if ($this->ignore_directories) {
1862
  // Ignore directories based on a common regular expression.
1863
+ $filepath = @realpath($directory . '/' . $filename);
1864
  $pattern = '/\/wp-content\/(uploads|cache|backup|w3tc)/';
1865
 
1866
+ if (preg_match($pattern, $filepath)) {
1867
  return true;
1868
  }
1869
 
1870
  // Ignore directories specified by the administrator.
1871
+ if (!empty($this->ignored_directories)) {
1872
+ foreach ($this->ignored_directories['directories'] as $ignored_dir) {
1873
+ if (strpos($directory, $ignored_dir) !== false
1874
+ || strpos($filepath, $ignored_dir) !== false
 
1875
  ) {
1876
  return true;
1877
  }
1888
  * @param string $filename Name of the folder or file being scanned at the moment.
1889
  * @return boolean Either TRUE or FALSE representing that the scan should ignore this filename or not.
1890
  */
1891
+ private function ignore_filepath($filename = '')
1892
+ {
1893
+ if (!$this->ignore_files) {
1894
  return false;
1895
  }
1896
 
1897
  // Ignoring backup files from our clean ups.
1898
+ if (strpos($filename, '_sucuribackup.') !== false) {
1899
  return true;
1900
  }
1901
 
1902
  // Ignore files specified by the administrator.
1903
+ if (!empty($this->ignored_directories)) {
1904
+ foreach ($this->ignored_directories['directories'] as $ignored_dir) {
1905
+ if (strpos($ignored_dir, $filename) !== false) {
1906
  return true;
1907
  }
1908
  }
1909
  }
1910
 
1911
  // Any file maching one of these rules WILL NOT be ignored.
1912
+ if (( strpos($filename, '.php') !== false) ||
1913
+ ( strpos($filename, '.htm') !== false) ||
1914
+ ( strpos($filename, '.js') !== false) ||
1915
+ ( strcmp($filename, '.htaccess') == 0 ) ||
1916
+ ( strcmp($filename, 'php.ini') == 0 )
 
1917
  ) {
1918
  return false;
1919
  }
1927
  * @param array $dir_tree A list of files under a directory.
1928
  * @return array A list of unique directory paths.
1929
  */
1930
+ public function get_diretories_only($dir_tree = array())
1931
+ {
1932
  $dirs = array();
1933
 
1934
+ if (is_string($dir_tree)) {
1935
+ $dir_tree = $this->get_directory_tree($dir_tree);
1936
  }
1937
 
1938
+ if (is_array($dir_tree) && !empty($dir_tree)) {
1939
+ foreach ($dir_tree as $filepath) {
1940
+ $dir_path = dirname($filepath);
1941
 
1942
+ if (is_array($dirs)
1943
+ && !in_array($dir_path, $dirs)
1944
+ && array_key_exists('directories', $this->ignored_directories)
1945
+ && is_array($this->ignored_directories['directories'])
1946
+ && !in_array($dir_path, $this->ignored_directories['directories'])
1947
  ) {
1948
  $dirs[] = $dir_path;
1949
  }
1963
  * @param string $pattern Text that will be searched inside each file.
1964
  * @return array Associative list with the file path and line number of the match.
1965
  */
1966
+ public function grep_pattern($directory = '', $pattern = '')
1967
+ {
1968
+ $dir_tree = $this->get_directory_tree($directory);
1969
+ $pattern = '/.*' . str_replace('/', '\/', $pattern) . '.*/';
1970
  $results = array();
1971
 
1972
+ if (class_exists('SplFileObject')
1973
+ && class_exists('RegexIterator')
1974
+ && SucuriScan::is_valid_pattern($pattern)
 
1975
  ) {
1976
+ foreach ($dir_tree as $file_path) {
1977
  try {
1978
+ $fobject = new SplFileObject($file_path);
1979
+ $fstream = new RegexIterator($fobject, $pattern, RegexIterator::MATCH);
1980
 
1981
+ foreach ($fstream as $key => $ltext) {
1982
  $lnumber = ( $key + 1 );
1983
+ $ltext = str_replace("\n", '', $ltext);
1984
+ $fpath = str_replace($directory, '', $file_path);
1985
+ $loutput = sprintf('%s:%d:%s', $fpath, $lnumber, $ltext);
1986
  $results[] = array(
1987
  'file_path' => $file_path,
1988
  'relative_path' => $fpath,
1991
  'output' => $loutput,
1992
  );
1993
  }
1994
+ } catch (RuntimeException $exception) {
1995
+ SucuriScanEvent::report_exception($exception);
1996
  }
1997
  }
1998
  }
2006
  * @param string $directory Path of the existing directory that will be removed.
2007
  * @return boolean TRUE if all the files and folder inside the directory were removed.
2008
  */
2009
+ public function remove_directory_tree($directory = '')
2010
+ {
2011
+ $dir_tree = $this->get_directory_tree($directory);
2012
 
2013
+ if ($dir_tree) {
2014
  $dirs_only = array();
2015
 
2016
  // Include the parent directory as the first entry.
2022
  * were successfully deleted, this is because PHP does not allows to delete non-
2023
  * empty folders.
2024
  */
2025
+ foreach ($dir_tree as $filepath) {
2026
+ if (is_dir($filepath)) {
2027
  $dirs_only[] = $filepath;
2028
  } else {
2029
+ @unlink($filepath);
2030
  }
2031
  }
2032
 
2033
+ if (!function_exists('sucuriscan_strlen_diff')) {
2034
  /**
2035
  * Evaluates the difference between the length of two strings.
2036
  *
2038
  * @param string $b Second string of characters that will be measured.
2039
  * @return integer The difference in length between the two strings.
2040
  */
2041
+ function sucuriscan_strlen_diff($a = '', $b = '')
2042
+ {
2043
+ return strlen($b) - strlen($a);
2044
  }
2045
  }
2046
 
2047
  // Sort the directories by deep level in ascendant order.
2048
+ $dirs_only = array_unique($dirs_only);
2049
+ usort($dirs_only, 'sucuriscan_strlen_diff');
2050
 
2051
  // Delete all the directories starting from the deepest level.
2052
+ foreach ($dirs_only as $dir_path) {
2053
+ @rmdir($dir_path);
2054
  }
2055
 
2056
  return true;
2067
  * @param string $filepath Path to the file.
2068
  * @return array An array where each element is a line in the file.
2069
  */
2070
+ public static function file_lines($filepath = '')
2071
+ {
2072
+ return @file($filepath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2073
  }
2074
 
2075
  /**
2082
  * @param boolean $adaptive Whether the buffer will adapt to a specific number of bytes or not.
2083
  * @return string Text contained at the end of the file.
2084
  */
2085
+ public static function tail_file($file_path = '', $lines = 1, $adaptive = true)
2086
+ {
2087
+ $file = @fopen($file_path, 'rb');
2088
  $limit = $lines;
2089
 
2090
+ if ($file) {
2091
+ fseek($file, -1, SEEK_END);
2092
 
2093
+ if ($adaptive && $lines < 2) {
2094
  $buffer = 64;
2095
+ } elseif ($adaptive && $lines < 10) {
2096
  $buffer = 512;
2097
  } else {
2098
  $buffer = 4096;
2099
  }
2100
 
2101
+ if (fread($file, 1) != "\n") {
2102
  $lines -= 1;
2103
  }
2104
 
2105
  $output = '';
2106
  $chunk = '';
2107
 
2108
+ while (ftell($file) > 0 && $lines >= 0) {
2109
+ $seek = min(ftell($file), $buffer);
2110
+ fseek($file, -$seek, SEEK_CUR);
2111
+ $chunk = fread($file, $seek);
2112
  $output = $chunk . $output;
2113
+ fseek($file, -mb_strlen($chunk, '8bit'), SEEK_CUR);
2114
+ $lines -= substr_count($chunk, "\n");
2115
  }
2116
 
2117
+ fclose($file);
2118
 
2119
+ $lines_arr = explode("\n", $output);
2120
+ $lines_count = count($lines_arr);
2121
+ $result = array_slice($lines_arr, ($lines_count - $limit));
2122
 
2123
  return $result;
2124
  }
2132
  * @param string $file_path Path to the file.
2133
  * @return integer Time the file was last changed.
2134
  */
2135
+ public static function creation_time($file_path = '')
2136
+ {
2137
+ if (file_exists($file_path)) {
2138
+ clearstatcache($file_path);
2139
+ return filectime($file_path);
2140
  }
2141
 
2142
  return 0;
2148
  * @param string $file_path Path to the file.
2149
  * @return integer Time the file was last modified.
2150
  */
2151
+ public static function modification_time($file_path = '')
2152
+ {
2153
+ if (file_exists($file_path)) {
2154
+ clearstatcache($file_path);
2155
+ return filemtime($file_path);
2156
  }
2157
 
2158
  return 0;
2164
  * @param string $path Path to the file.
2165
  * @return string Type of resource: dir, link, file.
2166
  */
2167
+ public static function get_resource_type($path = '')
2168
+ {
2169
+ if (is_dir($path)) {
2170
  return 'dir';
2171
+ } elseif (is_link($path)) {
2172
  return 'link';
2173
+ } elseif (is_file($path)) {
2174
  return 'file';
2175
  } else {
2176
  return 'unknown';
2177
  }
2178
  }
 
2179
  }
2180
 
2181
  /**
2188
  * the request. Cached data will not be stored persistently across page loads
2189
  * unless of the installation of a 3party persistent caching plugin [2].
2190
  *
2191
+ * [1] https://codex.wordpress.org/Class_Reference/WP_Object_Cache
2192
+ * [2] https://codex.wordpress.org/Class_Reference/WP_Object_Cache#Persistent_Caching
2193
  */
2194
+ class SucuriScanCache extends SucuriScan
2195
+ {
2196
  /**
2197
  * The unique name (or identifier) of the file with the data.
2198
  *
2231
  * @param string $datastore Unique name (or identifier) of the file with the data.
2232
  * @return void
2233
  */
2234
+ public function __construct($datastore = '', $auto_create = true)
2235
+ {
2236
  $this->datastore = $datastore;
2237
+ $this->datastore_path = $this->datastoreFilePath($auto_create);
2238
  $this->usable_datastore = (bool) $this->datastore_path;
2239
  }
2240
 
2243
  *
2244
  * @return string Default attributes for every datastore file.
2245
  */
2246
+ private function datastoreDefaultInfo()
2247
+ {
2248
  $attrs = array(
2249
  'datastore' => $this->datastore,
2250
  'created_on' => time(),
2260
  * @param array $finfo Rainbow table with the key names and decoded values.
2261
  * @return string Default content of every datastore file.
2262
  */
2263
+ private function datastoreInfo($finfo = array())
2264
+ {
2265
+ $attrs = $this->datastoreDefaultInfo();
2266
  $info_is_available = (bool) isset($finfo['info']);
2267
  $info = "<?php\n";
2268
 
2269
+ foreach ($attrs as $attr_name => $attr_value) {
2270
+ if ($info_is_available
 
2271
  && $attr_name != 'updated_on'
2272
+ && isset($finfo['info'][$attr_name])
2273
  ) {
2274
+ $attr_value = $finfo['info'][$attr_name];
2275
  }
2276
 
2277
+ $info .= sprintf("// %s=%s;\n", $attr_name, $attr_value);
2278
  }
2279
 
2280
  $info .= "exit(0);\n";
2291
  * @param boolean $auto_create Automatically create the file if not exists or not.
2292
  * @return string The full path where the datastore file is located, FALSE otherwise.
2293
  */
2294
+ private function datastoreFilePath($auto_create = false)
2295
+ {
2296
+ if (!is_null($this->datastore)) {
2297
  $folder_path = $this->datastore_folder_path();
2298
  $file_path = $folder_path . 'sucuri-' . $this->datastore . '.php';
2299
 
2300
  // Create the datastore parent directory.
2301
+ if (!file_exists($folder_path)) {
2302
+ @mkdir($folder_path, 0755, true);
2303
  }
2304
 
2305
  // Create the datastore file is it does not exists and the folder is writable.
2306
+ if (!file_exists($file_path)
2307
+ && is_writable($folder_path)
 
2308
  && $auto_create === true
2309
  ) {
2310
+ @file_put_contents($file_path, $this->datastoreInfo());
2311
  }
2312
 
2313
  // Continue the operation after an attemp to create the datastore file.
2314
+ if (file_exists($file_path)
2315
+ && is_writable($file_path)
2316
+ && is_readable($file_path)
 
2317
  ) {
2318
  return $file_path;
2319
  }
2330
  * @param string $action Either "valid", "content", or "header".
2331
  * @return string Cache key pattern.
2332
  */
2333
+ private function keyPattern($action = 'valid')
2334
+ {
2335
+ if ($action == 'valid') {
2336
  return '/^([0-9a-zA-Z_]+)$/';
2337
+ } elseif ($action == 'content') {
 
 
2338
  return '/^([0-9a-zA-Z_]+):(.+)/';
2339
+ } elseif ($action == 'header') {
 
 
2340
  return '/^\/\/ ([a-z_]+)=(.*);$/';
2341
  }
2342
 
2349
  * @param string $key Unique name to identify the data in the datastore file.
2350
  * @return boolean TRUE if the format of the key name is valid, FALSE otherwise.
2351
  */
2352
+ private function validKeyName($key = '')
2353
+ {
2354
+ return (bool) @preg_match($this->keyPattern('valid'), $key);
2355
  }
2356
 
2357
  /**
2360
  * @param array $finfo Rainbow table with the key names and decoded values.
2361
  * @return boolean TRUE if the operation finished successfully, FALSE otherwise.
2362
  */
2363
+ private function saveNewEntries($finfo = array())
2364
+ {
2365
+ $data_string = $this->datastoreInfo($finfo);
2366
 
2367
+ if (!empty($finfo)) {
2368
+ foreach ($finfo['entries'] as $key => $data) {
2369
+ if ($this->validKeyName($key)) {
2370
+ $data = json_encode($data);
2371
+ $data_string .= sprintf("%s:%s\n", $key, $data);
2372
  }
2373
  }
2374
  }
2375
 
2376
+ return (bool) @file_put_contents($this->datastore_path, $data_string);
 
 
2377
  }
2378
 
2379
  /**
2385
  * @param boolean $assoc When TRUE returned objects will be converted into associative arrays.
2386
  * @return array Rainbow table with the key names and decoded values.
2387
  */
2388
+ private function getDatastoreContent($assoc = false)
2389
+ {
2390
  $data_object = array(
2391
  'info' => array(),
2392
  'entries' => array(),
2393
  );
2394
 
2395
+ if ($this->usable_datastore) {
2396
+ $data_lines = SucuriScanFileInfo::file_lines($this->datastore_path);
2397
 
2398
+ if (!empty($data_lines)) {
2399
+ foreach ($data_lines as $line) {
2400
+ if (preg_match($this->keyPattern('header'), $line, $match)) {
2401
  $data_object['info'][ $match[1] ] = $match[2];
2402
+ } elseif (preg_match($this->keyPattern('content'), $line, $match)) {
2403
+ if ($this->validKeyName($match[1])
2404
+ && !array_key_exists($match[1], $data_object)
 
2405
  ) {
2406
+ $data_object['entries'][$match[1]] = @json_decode($match[2], $assoc);
2407
  }
2408
  }
2409
  }
2421
  * functionality of these headers please refer to the function that contains the
2422
  * default attributes and their values [1].
2423
  *
2424
+ * [1] SucuriScanCache::datastoreDefaultInfo()
2425
  *
2426
  * @return array Default content of every datastore file.
2427
  */
2428
+ public function getDatastoreInfo()
2429
+ {
2430
+ $finfo = $this->getDatastoreContent();
2431
 
2432
+ if (!empty($finfo['info'])) {
2433
  return $finfo['info'];
2434
  }
2435
 
2442
  * @param array $finfo Rainbow table with the key names and decoded values.
2443
  * @return integer Total number of unique entries found in the datastore file.
2444
  */
2445
+ public function getCount($finfo = null)
2446
+ {
2447
+ if (!is_array($finfo)) {
2448
+ $finfo = $this->getDatastoreContent();
2449
  }
2450
 
2451
+ return count($finfo['entries']);
2452
  }
2453
 
2454
  /**
2461
  * @param array $finfo Rainbow table with the key names and decoded values.
2462
  * @return boolean TRUE if the life time of the data has expired, FALSE otherwise.
2463
  */
2464
+ public function dataHasExpired($lifetime = 0, $finfo = null)
2465
+ {
2466
+ if (is_null($finfo)) {
2467
+ $finfo = $this->getDatastoreContent();
2468
  }
2469
 
2470
+ if ($lifetime > 0 && !empty($finfo['info'])) {
2471
+ $diff_time = time() - intval($finfo['info']['updated_on']);
2472
 
2473
+ if ($diff_time >= $lifetime) {
2474
  return true;
2475
  }
2476
  }
2488
  * @param boolean $assoc When TRUE returned objects will be converted into associative arrays.
2489
  * @return boolean TRUE if the operation finished successfully, FALSE otherwise.
2490
  */
2491
+ private function handleKeyData($key = '', $data = null, $action = '', $lifetime = 0, $assoc = false)
2492
+ {
2493
+ if ($this->validKeyName($key)
2494
+ && $this->usable_datastore
2495
+ ) {
2496
+ $finfo = $this->getDatastoreContent($assoc);
 
2497
 
2498
+ if ($action == 'set' || $action == 'add') {
2499
+ $finfo['entries'][$key] = $data;
2500
+
2501
+ return $this->saveNewEntries($finfo);
2502
+ } elseif ($action == 'get') {
2503
+ if (!$this->dataHasExpired($lifetime, $finfo)
2504
+ && array_key_exists($key, $finfo['entries'])
2505
+ ) {
2506
+ return $finfo['entries'][$key];
2507
+ }
2508
+ } elseif ($action == 'get_all') {
2509
+ if (!$this->dataHasExpired($lifetime, $finfo)) {
2510
+ return $finfo['entries'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2511
  }
2512
+ } elseif ($action == 'exists') {
2513
+ if (!$this->dataHasExpired($lifetime, $finfo)
2514
+ && array_key_exists($key, $finfo['entries'])
2515
+ ) {
2516
+ return true;
2517
+ }
2518
+ } elseif ($action == 'delete') {
2519
+ unset($finfo['entries'][$key]);
2520
+
2521
+ return $this->saveNewEntries($finfo);
2522
  }
2523
  }
2524
 
2535
  * @param string $data Mixed data stored in the datastore file following the unique key name.
2536
  * @return boolean TRUE if the data was stored successfully, FALSE otherwise.
2537
  */
2538
+ public function add($key = '', $data = '')
2539
+ {
2540
+ return $this->handleKeyData($key, $data, 'add');
2541
  }
2542
 
2543
  /**
2547
  * @param string $data Mixed data stored in the datastore file following the unique key name.
2548
  * @return boolean TRUE if the data was stored successfully, FALSE otherwise.
2549
  */
2550
+ public function set($key = '', $data = '')
2551
+ {
2552
+ return $this->handleKeyData($key, $data, 'set');
2553
  }
2554
 
2555
  /**
2560
  * @param boolean $assoc When TRUE returned objects will be converted into associative arrays.
2561
  * @return string Mixed data stored in the datastore file following the unique key name.
2562
  */
2563
+ public function get($key = '', $lifetime = 0, $assoc = false)
2564
+ {
2565
+ $assoc = ($assoc == 'array' ? true : $assoc);
2566
 
2567
+ return $this->handleKeyData($key, null, 'get', $lifetime, $assoc);
2568
  }
2569
 
2570
  /**
2574
  * @param boolean $assoc When TRUE returned objects will be converted into associative arrays.
2575
  * @return string Mixed data stored in the datastore file following the unique key name.
2576
  */
2577
+ public function getAll($lifetime = 0, $assoc = false)
2578
+ {
2579
+ $assoc = ($assoc == 'array' ? true : $assoc);
2580
 
2581
+ return $this->handleKeyData('temp', null, 'get_all', $lifetime, $assoc);
2582
  }
2583
 
2584
  /**
2587
  * @param string $key Unique name to identify the data in the datastore file.
2588
  * @return boolean TRUE if the key exists in the datastore file, FALSE otherwise.
2589
  */
2590
+ public function exists($key = '')
2591
+ {
2592
+ return $this->handleKeyData($key, null, 'exists');
2593
  }
2594
 
2595
  /**
2598
  * @param string $key Unique name to identify the data in the datastore file.
2599
  * @return boolean TRUE if the entries were removed, FALSE otherwise.
2600
  */
2601
+ public function delete($key = '')
2602
+ {
2603
+ return $this->handleKeyData($key, null, 'delete');
2604
  }
2605
 
2606
  /**
2608
  *
2609
  * @return boolean Always TRUE unless the datastore file is not writable.
2610
  */
2611
+ public function flush()
2612
+ {
2613
+ $finfo = $this->getDatastoreContent();
2614
 
2615
+ return $this->saveNewEntries($finfo);
2616
  }
 
2617
  }
2618
 
2619
  /**
2636
  * apply network-wide and the data is stored in the wp_sitemeta table under the
2637
  * given custom name.
2638
  *
2639
+ * @see https://codex.wordpress.org/Option_Reference
2640
+ * @see https://codex.wordpress.org/Options_API
2641
  */
2642
+ class SucuriScanOption extends SucuriScanRequest
2643
+ {
2644
 
2645
  /**
2646
  * Default values for all the plugin's options.
2647
  *
2648
  * @return array Default values for all the plugin's options.
2649
  */
2650
+ public static function get_default_option_values()
2651
+ {
2652
  $defaults = array(
2653
  'sucuriscan_account' => '',
2654
+ 'sucuriscan_addr_header' => 'HTTP_X_SUCURI_CLIENTIP',
2655
  'sucuriscan_ads_visibility' => 'enabled',
2656
  'sucuriscan_api_key' => false,
2657
+ 'sucuriscan_api_service' => 'enabled',
2658
  'sucuriscan_audit_report' => 'disabled',
2659
  'sucuriscan_cloudproxy_apikey' => '',
2660
  'sucuriscan_collect_wrong_passwords' => 'disabled',
2701
  'sucuriscan_notify_widget_deleted' => 'disabled',
2702
  'sucuriscan_parse_errorlogs' => 'enabled',
2703
  'sucuriscan_prettify_mails' => 'disabled',
2704
+ 'sucuriscan_request_timeout' => 5,
2705
  'sucuriscan_revproxy' => 'disabled',
2706
  'sucuriscan_runtime' => 0,
2707
  'sucuriscan_scan_checksums' => 'enabled',
2708
  'sucuriscan_scan_errorlogs' => 'disabled',
2709
  'sucuriscan_scan_frequency' => 'twicedaily',
2710
  'sucuriscan_scan_interface' => 'spl',
2711
+ 'sucuriscan_selfhosting_fpath' => '',
2712
+ 'sucuriscan_selfhosting_monitor' => 'disabled',
2713
  'sucuriscan_site_version' => '0.0',
2714
  'sucuriscan_sitecheck_counter' => 0,
2715
  'sucuriscan_sitecheck_scanner' => 'enabled',
2726
  *
2727
  * @return array Name of all valid plugin's options.
2728
  */
2729
+ public static function get_default_option_names()
2730
+ {
2731
  $options = self::get_default_option_values();
2732
+ $names = array_keys($options);
2733
 
2734
  return $names;
2735
  }
2740
  * @param string $option_name Name of the option that will be checked.
2741
  * @return boolean True if the option is part of the plugin, False otherwise.
2742
  */
2743
+ public static function is_valid_plugin_option($option_name = '')
2744
+ {
2745
  $valid_options = self::get_default_option_names();
2746
+ $is_valid_option = (bool) array_key_exists($option_name, $valid_options);
2747
 
2748
  return $is_valid_option;
2749
  }
2754
  * @param string|array $settings Either an array that will be complemented or a string with the name of the option.
2755
  * @return string|array The default values for the specified options.
2756
  */
2757
+ public static function get_default_options($settings = '')
2758
+ {
2759
  $default_options = self::get_default_option_values();
2760
 
2761
  // Use framework built-in function.
2762
+ if (function_exists('get_option')) {
2763
+ $admin_email = get_option('admin_email');
2764
  $default_options['sucuriscan_account'] = $admin_email;
2765
  $default_options['sucuriscan_notify_to'] = $admin_email;
2766
  }
2767
 
2768
+ if (is_array($settings)) {
2769
+ foreach ($default_options as $option_name => $option_value) {
2770
+ if (!isset($settings[ $option_name ])) {
2771
  $settings[ $option_name ] = $option_value;
2772
  }
2773
  }
2775
  return $settings;
2776
  }
2777
 
2778
+ if (is_string($settings)
2779
+ && !empty($settings)
2780
+ && array_key_exists($settings, $default_options)
 
2781
  ) {
2782
  return $default_options[ $settings ];
2783
  }
2797
  * automatically replace that character with the unique identifier of the
2798
  * plugin.
2799
  *
2800
+ * @see https://codex.wordpress.org/Function_Reference/get_option
2801
  *
2802
  * @param string $option_name Optional parameter that you can use to filter the results to one option.
2803
  * @return string The value (or default value) of the option specified.
2804
  */
2805
+ public static function get_option($option_name = '')
2806
+ {
2807
+ if (function_exists('update_option')) {
2808
+ $option_name = self::variable_prefix($option_name);
2809
+ $option_value = get_option($option_name);
2810
 
2811
+ if ($option_value === false && preg_match('/^sucuriscan_/', $option_name)) {
2812
+ $option_value = self::get_default_options($option_name);
2813
  }
2814
 
2815
  return $option_value;
2826
  * the insert SQL statement but not the option value, this value should always
2827
  * be properly sanitized.
2828
  *
2829
+ * @see https://codex.wordpress.org/Function_Reference/update_option
2830
  *
2831
  * @param string $option_name Name of the option to update which must not exceed 64 characters.
2832
  * @param string $option_value The new value for the option, can be an integer, string, array, or object.
2833
  * @return boolean True if option value has changed, false if not or if update failed.
2834
  */
2835
+ public static function update_option($option_name = '', $option_value = '')
2836
+ {
2837
+ if (function_exists('update_option')) {
2838
+ $option_name = self::variable_prefix($option_name);
2839
 
2840
+ return update_option($option_name, $option_value);
2841
  }
2842
 
2843
  return false;
2848
  *
2849
  * A safe way of removing a named option/value pair from the options database table.
2850
  *
2851
+ * @see https://codex.wordpress.org/Function_Reference/delete_option
2852
  *
2853
  * @param string $option_name Name of the option to be deleted.
2854
  * @return boolean True, if option is successfully deleted. False on failure, or option does not exist.
2855
  */
2856
+ public static function delete_option($option_name = '')
2857
+ {
2858
+ if (function_exists('delete_option')) {
2859
+ $option_name = self::variable_prefix($option_name);
2860
 
2861
+ return delete_option($option_name);
2862
  }
2863
 
2864
  return false;
2870
  * @param string $option Name of the option to be deleted.
2871
  * @return boolean True if the option is enabled, false otherwise.
2872
  */
2873
+ public static function is_enabled($option = '')
2874
+ {
2875
+ return (bool) ( self::get_option($option) === 'enabled' );
2876
  }
2877
 
2878
  /**
2881
  * @param string $option Name of the option to be deleted.
2882
  * @return boolean True if the option is disabled, false otherwise.
2883
  */
2884
+ public static function is_disabled($option = '')
2885
+ {
2886
+ return (bool) ( self::get_option($option) === 'disabled' );
2887
  }
2888
 
2889
  /**
2891
  *
2892
  * @return void
2893
  */
2894
+ public static function delete_plugin_options()
2895
+ {
2896
  global $wpdb;
2897
 
2898
  $options = $wpdb->get_results(
2901
  ORDER BY option_id ASC"
2902
  );
2903
 
2904
+ foreach ($options as $option) {
2905
+ self::delete_option($option->option_name);
2906
  }
2907
  }
2908
 
2913
  *
2914
  * @return array All the options stored by Wordpress in the database, except the transient options.
2915
  */
2916
+ public static function get_site_options()
2917
+ {
2918
  global $wpdb;
2919
 
2920
  $settings = array();
2924
  ORDER BY option_id ASC"
2925
  );
2926
 
2927
+ foreach ($results as $row) {
2928
  $settings[ $row->option_name ] = $row->option_value;
2929
  }
2930
 
2938
  * @param array $request The content of the global variable GET or POST considering SERVER[REQUEST_METHOD].
2939
  * @return array A list of all the options that were changes through this request.
2940
  */
2941
+ public static function what_options_were_changed($request = array())
2942
+ {
2943
  $options_changed = array(
2944
  'original' => array(),
2945
  'changed' => array()
2947
 
2948
  $site_options = self::get_site_options();
2949
 
2950
+ foreach ($request as $req_name => $req_value) {
2951
+ if (array_key_exists($req_name, $site_options)
 
2952
  && $site_options[ $req_name ] != $req_value
2953
  ) {
2954
  $options_changed['original'][ $req_name ] = $site_options[ $req_name ];
2964
  *
2965
  * @return boolean TRUE if the nonce is valid, FALSE otherwise.
2966
  */
2967
+ public static function check_options_nonce()
2968
+ {
2969
  // Create the option_page value if permalink submission.
2970
+ if (!isset($_POST['option_page'])
 
2971
  && isset($_POST['permalink_structure'])
2972
  ) {
2973
  $_POST['option_page'] = 'permalink';
2974
  }
2975
 
2976
  // Check if the option_page has an allowed value.
2977
+ if ($option_page = SucuriScanRequest::post('option_page')) {
2978
  $nonce = '_wpnonce';
2979
  $action = '';
2980
 
2981
+ switch ($option_page) {
2982
  case 'general': /* no_break */
2983
  case 'writing': /* no_break */
2984
  case 'reading': /* no_break */
2993
  }
2994
 
2995
  // Check the nonce validity.
2996
+ if (!empty($action)
 
2997
  && isset($_REQUEST[ $nonce ])
2998
+ && wp_verify_nonce($_REQUEST[ $nonce ], $action)
2999
  ) {
3000
  return true;
3001
  }
3010
  *
3011
  * @return array List of ignored posts-types to send notifications.
3012
  */
3013
+ public static function get_ignored_events()
3014
+ {
3015
+ $post_types = self::get_option(':ignored_events');
3016
  $post_types_arr = false;
3017
 
3018
  // Encode (old) serialized data into JSON.
3019
+ if (self::is_serialized($post_types)) {
3020
+ $post_types_arr = @unserialize($post_types);
3021
+ $post_types_fix = json_encode($post_types_arr);
3022
+ self::update_option(':ignored_events', $post_types_fix);
3023
 
3024
  return $post_types_arr;
3025
+ } // Decode JSON-encoded data as an array.
3026
+ elseif (preg_match('/^\{.+\}$/', $post_types)) {
3027
+ $post_types_arr = @json_decode($post_types, true);
3028
  }
3029
 
3030
+ if (!is_array($post_types_arr)) {
 
 
 
 
 
3031
  $post_types_arr = array();
3032
  }
3033
 
3040
  * @param string $event_name Unique post-type name.
3041
  * @return boolean Whether the event was ignored or not.
3042
  */
3043
+ public static function add_ignored_event($event_name = '')
3044
+ {
3045
+ if (function_exists('get_post_types')) {
3046
  $post_types = get_post_types();
3047
 
3048
  // Check if the event is a registered post-type.
3049
+ if (array_key_exists($event_name, $post_types)) {
3050
  $ignored_events = self::get_ignored_events();
3051
 
3052
  // Check if the event is not ignored already.
3053
+ if (!array_key_exists($event_name, $ignored_events)) {
3054
  $ignored_events[ $event_name ] = time();
3055
+ $saved = self::update_option(':ignored_events', json_encode($ignored_events));
3056
 
3057
  return $saved;
3058
  }
3068
  * @param string $event_name Unique post-type name.
3069
  * @return boolean Whether the event was removed from the list or not.
3070
  */
3071
+ public static function remove_ignored_event($event_name = '')
3072
+ {
3073
  $ignored_events = self::get_ignored_events();
3074
 
3075
+ if (array_key_exists($event_name, $ignored_events)) {
3076
+ unset($ignored_events[ $event_name ]);
3077
+ $saved = self::update_option(':ignored_events', json_encode($ignored_events));
3078
 
3079
  return $saved;
3080
  }
3088
  * @param string $event_name Unique post-type name.
3089
  * @return boolean Whether an event is being ignored or not.
3090
  */
3091
+ public static function is_ignored_event($event_name = '')
3092
+ {
3093
+ $event_name = strtolower($event_name);
3094
  $ignored_events = self::get_ignored_events();
3095
 
3096
+ if (array_key_exists($event_name, $ignored_events)) {
3097
  return true;
3098
  }
3099
 
3106
  *
3107
  * @return array Array with three keys: good, missing, bad.
3108
  */
3109
+ public static function get_security_keys()
3110
+ {
3111
  $response = array(
3112
  'good' => array(),
3113
  'missing' => array(),
3124
  'SECURE_AUTH_SALT',
3125
  );
3126
 
3127
+ foreach ($key_names as $key_name) {
3128
+ if (defined($key_name)) {
3129
+ $key_value = constant($key_name);
3130
 
3131
+ if (stripos($key_value, 'unique phrase') !== false) {
3132
  $response['bad'][ $key_name ] = $key_value;
3133
  } else {
3134
  $response['good'][ $key_name ] = $key_value;
3141
  return $response;
3142
  }
3143
 
3144
+ /**
3145
+ * Change the reverse proxy setting.
3146
+ *
3147
+ * When enabled this option forces the plugin to override the value of the
3148
+ * global IP address variable from the HTTP header selected by the user from the
3149
+ * settings. Note that this may also be automatically enabled when the firewall
3150
+ * page is activated as it assumes that the proxy is creating a custom HTTP
3151
+ * header for the real IP.
3152
+ *
3153
+ * @param string $header Valid HTTP header name.
3154
+ * @return void
3155
+ */
3156
+ public static function setRevProxy($action = 'disable')
3157
+ {
3158
+ $action_d = $action . 'd';
3159
+ $message = 'Reverse proxy support was <code>' . $action_d . '</code>';
3160
+
3161
+ self::update_option(':revproxy', $action_d);
3162
+ SucuriScanEvent::report_info_event($message);
3163
+ SucuriScanEvent::notify_event('plugin_change', $message);
3164
+ SucuriScanInterface::info($message);
3165
+ }
3166
+
3167
+ /**
3168
+ * Change the HTTP header to retrieve the real IP address.
3169
+ *
3170
+ * @param string $header Valid HTTP header name.
3171
+ * @return void
3172
+ */
3173
+ public static function setAddrHeader($header = 'REMOTE_ADDR')
3174
+ {
3175
+ $header = strtoupper($header);
3176
+ $allowed = SucuriScan::allowedHttpHeaders(true);
3177
+
3178
+ if (array_key_exists($header, $allowed)) {
3179
+ $message = 'HTTP header was set to <code>' . $header . '</code>';
3180
+
3181
+ self::update_option(':addr_header', $header);
3182
+ SucuriScanEvent::report_info_event($message);
3183
+ SucuriScanEvent::notify_event('plugin_change', $message);
3184
+ SucuriScanInterface::info($message);
3185
+ } else {
3186
+ SucuriScanInterface::error('HTTP header is not in the allowed list');
3187
+ }
3188
+ }
3189
  }
3190
 
3191
  /**
3201
  * response to events is said to be event-driven, often with the goal of being
3202
  * interactive.
3203
  *
3204
+ * @see https://en.wikipedia.org/wiki/Event_(computing)
3205
  */
3206
+ class SucuriScanEvent extends SucuriScan
3207
+ {
3208
 
3209
  /**
3210
  * Schedule the task to run the first filesystem scan.
3211
  *
3212
  * @return void
3213
  */
3214
+ public static function schedule_task()
3215
+ {
3216
  $task_name = 'sucuriscan_scheduled_scan';
3217
 
3218
+ if (!wp_next_scheduled($task_name)) {
3219
+ wp_schedule_event(time() + 10, 'twicedaily', $task_name);
3220
  }
3221
 
3222
+ wp_schedule_single_event(time() + 300, $task_name);
3223
  }
3224
 
3225
  /**
3229
  * @param boolean $force_scan Whether the filesystem scan was forced by an administrator user or not.
3230
  * @return boolean Either TRUE or FALSE representing the success or fail of the operation respectively.
3231
  */
3232
+ private static function verify_run($runtime = 0, $force_scan = false)
3233
+ {
3234
  $option_name = ':runtime';
3235
+ $last_run = SucuriScanOption::get_option($option_name);
3236
  $current_time = time();
3237
 
3238
  // The filesystem scanner can be disabled from the settings page.
3239
+ if (SucuriScanOption::is_disabled(':fs_scanner')
 
3240
  && $force_scan === false
3241
  ) {
3242
  return false;
3243
  }
3244
 
3245
  // Check if the last runtime is too near the current time.
3246
+ if ($last_run && !$force_scan) {
3247
  $runtime_diff = $current_time - $runtime;
3248
 
3249
+ if ($last_run >= $runtime_diff) {
3250
  return false;
3251
  }
3252
  }
3253
 
3254
+ SucuriScanOption::update_option($option_name, $current_time);
3255
 
3256
  return true;
3257
  }
3262
  *
3263
  * @return boolean TRUE if the current WordPress version must be reported, FALSE otherwise.
3264
  */
3265
+ private static function report_site_version()
3266
+ {
3267
  $option_name = ':site_version';
3268
+ $reported_version = SucuriScanOption::get_option($option_name);
3269
  $wp_version = self::site_version();
3270
 
3271
+ if ($reported_version != $wp_version) {
3272
+ SucuriScanEvent::report_info_event('WordPress version detected ' . $wp_version);
3273
+ SucuriScanOption::update_option($option_name, $wp_version);
3274
 
3275
  return true;
3276
  }
3286
  * @param boolean $force_scan Whether the filesystem scan was forced by an administrator user or not.
3287
  * @return boolean TRUE if the filesystem scan was successful, FALSE otherwise.
3288
  */
3289
+ public static function filesystem_scan($force_scan = false)
3290
+ {
3291
  $minimum_runtime = SUCURISCAN_MINIMUM_RUNTIME;
3292
 
3293
+ if (self::verify_run($minimum_runtime, $force_scan)
3294
+ && class_exists('SucuriScanFileInfo')
3295
+ && SucuriScanAPI::getPluginKey()
 
3296
  ) {
3297
  self::report_site_version();
3298
 
3299
  $file_info = new SucuriScanFileInfo();
3300
+ $file_info->scan_interface = SucuriScanOption::get_option(':scan_interface');
3301
+ $signatures = $file_info->get_directory_tree_md5(ABSPATH);
3302
 
3303
+ if ($signatures) {
3304
+ $hashes_sent = SucuriScanAPI::sendHashes($signatures);
3305
 
3306
+ if ($hashes_sent) {
3307
+ SucuriScanOption::update_option(':runtime', time());
3308
  return true;
3309
  } else {
3310
+ SucuriScanInterface::error('The file hashes could not be stored.');
3311
  }
3312
  } else {
3313
+ SucuriScanInterface::error('The file hashes could not be retrieved, the filesystem scan failed.');
3314
  }
3315
  }
3316
 
3326
  * @param boolean $internal Whether the event will be publicly visible or not.
3327
  * @return boolean TRUE if the event was logged in the monitoring service, FALSE otherwise.
3328
  */
3329
+ private static function report_event($severity = 0, $location = '', $message = '', $internal = false)
3330
+ {
3331
  $user = wp_get_current_user();
3332
  $username = false;
3333
+ $current_time = date('Y-m-d H:i:s');
3334
  $remote_ip = self::get_remote_addr();
3335
 
3336
  // Identify current user in session.
3337
+ if ($user instanceof WP_User
 
3338
  && isset($user->user_login)
3339
+ && !empty($user->user_login)
3340
  ) {
3341
+ if ($user->user_login != $user->display_name) {
3342
+ $username = sprintf("\x20%s (%s),", $user->display_name, $user->user_login);
3343
  } else {
3344
+ $username = sprintf("\x20%s,", $user->user_login);
3345
  }
3346
  }
3347
 
3349
  $severity = (int) $severity;
3350
 
3351
  // Convert the severity number into a readable string.
3352
+ switch ($severity) {
3353
+ case 0:
3354
+ $severity_name = 'Debug';
3355
+ break;
3356
+ case 1:
3357
+ $severity_name = 'Notice';
3358
+ break;
3359
+ case 2:
3360
+ $severity_name = 'Info';
3361
+ break;
3362
+ case 3:
3363
+ $severity_name = 'Warning';
3364
+ break;
3365
+ case 4:
3366
+ $severity_name = 'Error';
3367
+ break;
3368
+ case 5:
3369
+ $severity_name = 'Critical';
3370
+ break;
3371
+ default:
3372
+ $severity_name = 'Info';
3373
+ break;
3374
  }
3375
 
3376
  // Mark the event as internal if necessary.
3377
+ if ($internal === true) {
3378
  $severity_name = '@' . $severity_name;
3379
  }
3380
 
3381
  // Clear event message.
3382
+ $message = strip_tags($message);
3383
+ $message = str_replace("\r", '', $message);
3384
+ $message = str_replace("\n", '', $message);
3385
+ $message = str_replace("\t", '', $message);
3386
 
3387
  $event_message = sprintf(
3388
  '%s:%s %s; %s',
3392
  $message
3393
  );
3394
 
3395
+ return self::sendEventLog($event_message);
3396
  }
3397
 
3398
+ public static function sendEventLog($event_message = '')
3399
+ {
3400
+ /**
3401
+ * Self-hosted Monitor.
3402
+ *
3403
+ * Send a copy of the event log to a local file, this will allow the
3404
+ * administrator of the server to integrate the events monitored by the plugin
3405
+ * with a 3rd-party service like OSSEC or similar. More information in the Self-
3406
+ * Hosting panel located in the plugin' settings page.
3407
+ */
3408
+ if (function_exists('sucuriscan_selfhosting_fpath')) {
3409
+ $monitor_fpath = sucuriscan_selfhosting_fpath();
3410
+
3411
+ if ($monitor_fpath !== false) {
3412
+ $local_event = sprintf(
3413
+ "%s WordPressAudit %s %s : %s\n",
3414
+ date('Y-m-d H:i:s'),
3415
+ SucuriScan::get_top_level_domain(),
3416
+ SucuriScanOption::get_option(':account'),
3417
+ $event_message
3418
+ );
3419
+ @file_put_contents(
3420
+ $monitor_fpath,
3421
+ $local_event,
3422
+ FILE_APPEND
3423
+ );
3424
+ }
3425
+ }
3426
+
3427
+ if (SucuriScanOption::is_enabled(':api_service')) {
3428
+ SucuriScanAPI::sendLogsFromQueue();
3429
+
3430
+ return SucuriScanAPI::sendLog($event_message);
3431
+ }
3432
+
3433
+ return true;
3434
+ }
3435
+
3436
+ /**
3437
+ * Reports a debug event on the website.
3438
+ *
3439
+ * @param string $message Text witht the explanation of the event or action performed.
3440
+ * @param boolean $internal Whether the event will be publicly visible or not.
3441
+ * @return boolean Either true or false depending on the success of the operation.
3442
+ */
3443
+ public static function report_debug_event($message = '', $internal = false)
3444
+ {
3445
+ return self::report_event(0, 'core', $message, $internal);
3446
+ }
3447
 
3448
  /**
3449
  * Reports a notice event on the website.
3452
  * @param boolean $internal Whether the event will be publicly visible or not.
3453
  * @return boolean Either true or false depending on the success of the operation.
3454
  */
3455
+ public static function report_notice_event($message = '', $internal = false)
3456
+ {
3457
+ return self::report_event(1, 'core', $message, $internal);
3458
  }
3459
 
3460
  /**
3464
  * @param boolean $internal Whether the event will be publicly visible or not.
3465
  * @return boolean Either true or false depending on the success of the operation.
3466
  */
3467
+ public static function report_info_event($message = '', $internal = false)
3468
+ {
3469
+ return self::report_event(2, 'core', $message, $internal);
3470
  }
3471
 
3472
  /**
3476
  * @param boolean $internal Whether the event will be publicly visible or not.
3477
  * @return boolean Either true or false depending on the success of the operation.
3478
  */
3479
+ public static function report_warning_event($message = '', $internal = false)
3480
+ {
3481
+ return self::report_event(3, 'core', $message, $internal);
3482
  }
3483
 
3484
  /**
3488
  * @param boolean $internal Whether the event will be publicly visible or not.
3489
  * @return boolean Either true or false depending on the success of the operation.
3490
  */
3491
+ public static function report_error_event($message = '', $internal = false)
3492
+ {
3493
+ return self::report_event(4, 'core', $message, $internal);
3494
  }
3495
 
3496
  /**
3500
  * @param boolean $internal Whether the event will be publicly visible or not.
3501
  * @return boolean Either true or false depending on the success of the operation.
3502
  */
3503
+ public static function report_critical_event($message = '', $internal = false)
3504
+ {
3505
+ return self::report_event(5, 'core', $message, $internal);
3506
  }
3507
 
3508
  /**
3513
  * @param boolean $internal Whether the event will be publicly visible or not.
3514
  * @return boolean Either true or false depending on the success of the operation.
3515
  */
3516
+ public static function report_auto_event($message = '', $action = '', $internal = false)
3517
+ {
3518
+ $message = strip_tags($message);
3519
 
3520
  // Auto-detect the action performed, either enabled or disabled.
3521
+ if (preg_match('/( was )?(enabled|disabled)$/', $message, $match)) {
3522
  $action = $match[2];
3523
  }
3524
 
3525
  // Report the correct event for the action performed.
3526
+ if ($action == 'enabled') {
3527
+ return self::report_notice_event($message, $internal);
3528
+ } elseif ($action == 'disabled') {
3529
+ return self::report_error_event($message, $internal);
3530
  } else {
3531
+ return self::report_info_event($message, $internal);
3532
  }
3533
  }
3534
 
3538
  * @param Exception $exception A valid exception object of any type.
3539
  * @return boolean Whether the report was filled correctly or not.
3540
  */
3541
+ public static function report_exception($exception = false)
3542
+ {
3543
+ if ($exception) {
3544
  $e_trace = $exception->getTrace();
3545
  $multiple_entries = array();
3546
 
3547
+ foreach ($e_trace as $e_child) {
3548
+ $e_file = array_key_exists('file', $e_child)
3549
+ ? basename($e_child['file'])
3550
  : '[internal function]';
3551
+ $e_line = array_key_exists('line', $e_child)
3552
+ ? basename($e_child['line'])
3553
  : '0';
3554
+ $e_function = array_key_exists('class', $e_child)
3555
  ? $e_child['class'] . $e_child['type'] . $e_child['function']
3556
  : $e_child['function'];
3557
  $multiple_entries[] = sprintf(
3565
  $report_message = sprintf(
3566
  '%s: (multiple entries): %s',
3567
  $exception->getMessage(),
3568
+ @implode(',', $multiple_entries)
3569
  );
3570
 
3571
+ return self::report_debug_event($report_message);
3572
  }
3573
 
3574
  return false;
3582
  * @param string $content Body of the email that will be sent to the administrator.
3583
  * @return void
3584
  */
3585
+ public static function notify_event($event = '', $content = '')
3586
+ {
3587
+ $notify = SucuriScanOption::get_option(':notify_' . $event);
3588
+ $email = SucuriScanOption::get_option(':notify_to');
3589
  $email_params = array();
3590
 
3591
+ if (self::is_trusted_ip()) {
3592
  $notify = 'disabled';
3593
  }
3594
 
3595
+ if ($notify == 'enabled') {
3596
+ if ($event == 'post_publication') {
3597
  $event = 'post_update';
3598
+ } elseif ($event == 'failed_login') {
3599
+ $settings_url = SucuriScanTemplate::getUrl('settings');
3600
+ $content .= "<br>\n<br>\n<em>Explanation: Someone failed to login to your "
3601
+ . "site. If you are getting too many of these messages, it is likely your "
3602
+ . "site is under a password guessing brute-force attack [1]. You can disable "
3603
+ . "the failed login alerts from here [2]. Alternatively, you can consider "
3604
+ . "to install a firewall between your website and your visitors to filter "
3605
+ . "out these and other attacks, take a look at Sucuri CloudProxy [3].</em>"
3606
+ . "<br>\n<br>\n"
3607
+ . "[1] <a href='https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing'>"
3608
+ . "https://kb.sucuri.net/definitions/attacks/brute-force/password-guessing</a><br>\n"
3609
+ . "[2] <a href='" . $settings_url . "'>" . $settings_url . "</a> <br>\n"
3610
+ . "[3] <a href='https://sucuri.net/website-firewall/?wpalert'>"
3611
+ . "https://sucuri.net/website-firewall/</a> <br>\n";
3612
+ } elseif ($event == 'bruteforce_attack') {
3613
  // Send a notification even if the limit of emails per hour was reached.
3614
  $email_params['Force'] = true;
3615
+ } elseif ($event == 'scan_checksums') {
3616
  $event = 'core_integrity_checks';
3617
  $email_params['Force'] = true;
3618
  }
3619
 
3620
+ $title = str_replace('_', "\x20", $event);
3621
  $mail_sent = SucuriScanMail::send_mail(
3622
  $email,
3623
  $title,
3637
  * @param string $remote_addr The supposed ip address that will be checked.
3638
  * @return boolean TRUE if the IP address of the user is trusted, FALSE otherwise.
3639
  */
3640
+ private static function is_trusted_ip($remote_addr = '')
3641
+ {
3642
+ $cache = new SucuriScanCache('trustip', false);
3643
+ $trusted_ips = $cache->getAll();
3644
 
3645
+ if (!$remote_addr) {
3646
  $remote_addr = SucuriScan::get_remote_addr();
3647
  }
3648
 
3649
+ $addr_md5 = md5($remote_addr);
3650
 
3651
  // Check if the CIDR in range 32 of this IP is trusted.
3652
+ if (is_array($trusted_ips)
3653
+ && !empty($trusted_ips)
3654
+ && array_key_exists($addr_md5, $trusted_ips)
 
3655
  ) {
3656
  return true;
3657
  }
3658
 
3659
+ if ($trusted_ips) {
3660
+ foreach ($trusted_ips as $cache_key => $ip_info) {
3661
+ $ip_parts = explode('.', $ip_info->remote_addr);
3662
  $ip_pattern = false;
3663
 
3664
  // Generate the regular expression for a specific CIDR range.
3665
+ switch ($ip_info->cidr_range) {
3666
  case 24:
3667
+ $ip_pattern = sprintf('/^%d\.%d\.%d\.[0-9]{1,3}$/', $ip_parts[0], $ip_parts[1], $ip_parts[2]);
3668
  break;
3669
  case 16:
3670
+ $ip_pattern = sprintf('/^%d\.%d(\.[0-9]{1,3}) {2}$/', $ip_parts[0], $ip_parts[1]);
3671
  break;
3672
  case 8:
3673
+ $ip_pattern = sprintf('/^%d(\.[0-9]{1,3}) {3}$/', $ip_parts[0]);
3674
  break;
3675
  }
3676
 
3677
+ if ($ip_pattern && preg_match($ip_pattern, $remote_addr)) {
3678
  return true;
3679
  }
3680
  }
3689
  * @param integer $user_id The user identifier that will be changed, this must be different than the user in session.
3690
  * @return boolean Either TRUE or FALSE in case of success or error respectively.
3691
  */
3692
+ public static function set_new_password($user_id = 0)
3693
+ {
3694
+ $user_id = intval($user_id);
3695
 
3696
+ if ($user_id > 0 && function_exists('wp_generate_password')) {
3697
+ $user = get_userdata($user_id);
3698
 
3699
+ if ($user instanceof WP_User) {
3700
  $website = SucuriScan::get_domain();
3701
  $user_login = $user->user_login;
3702
  $display_name = $user->display_name;
3703
+ $new_password = wp_generate_password(15, true, false);
3704
 
3705
+ $message = SucuriScanTemplate::getSection('notification-resetpwd', array(
3706
  'ResetPassword.UserName' => $user_login,
3707
  'ResetPassword.DisplayName' => $display_name,
3708
  'ResetPassword.Password' => $new_password,
3709
  'ResetPassword.Website' => $website,
3710
+ ));
3711
 
3712
  $data_set = array( 'Force' => true ); // Skip limit for emails per hour.
3713
+ SucuriScanMail::send_mail($user->user_email, 'Password changed', $message, $data_set);
3714
 
3715
+ wp_set_password($new_password, $user_id);
3716
 
3717
  return true;
3718
  }
3730
  *
3731
  * @return false|array Either FALSE in case of error, or an array with the old and new keys.
3732
  */
3733
+ public static function set_new_config_keys()
3734
+ {
3735
  $new_wpconfig = '';
3736
  $config_path = self::get_wpconfig_path();
3737
 
3738
+ if ($config_path) {
3739
  $pattern = self::secret_key_pattern();
3740
  $define_tpl = "define('%s',%s'%s');";
3741
+ $config_lines = SucuriScanFileInfo::file_lines($config_path);
3742
+ $new_keys = SucuriScanAPI::getNewSecretKeys();
3743
  $old_keys = array();
3744
  $old_keys_string = '';
3745
  $new_keys_string = '';
3746
 
3747
+ foreach ((array) $config_lines as $config_line) {
3748
+ if (preg_match($pattern, $config_line, $match)) {
3749
  $key_name = $match[1];
3750
 
3751
+ if (array_key_exists($key_name, $new_keys)) {
3752
  $white_spaces = $match[2];
3753
  $old_keys[ $key_name ] = $match[3];
3754
+ $config_line = sprintf($define_tpl, $key_name, $white_spaces, $new_keys[ $key_name ]);
3755
+ $old_keys_string .= sprintf($define_tpl, $key_name, $white_spaces, $old_keys[ $key_name ]) . "\n";
3756
  $new_keys_string .= $config_line . "\n";
3757
  }
3758
  }
3761
  }
3762
 
3763
  $response = array(
3764
+ 'updated' => is_writable($config_path),
3765
  'old_keys' => $old_keys,
3766
  'old_keys_string' => $old_keys_string,
3767
  'new_keys' => $new_keys,
3769
  'new_wpconfig' => $new_wpconfig,
3770
  );
3771
 
3772
+ if ($response['updated']) {
3773
+ file_put_contents($config_path, $new_wpconfig, LOCK_EX);
3774
  }
3775
 
3776
  return $response;
3778
 
3779
  return false;
3780
  }
 
3781
  }
3782
 
3783
  /**
3795
  * calls in order to monitor behavior or modify the function of an application
3796
  * or other component; it is also widely used in benchmarking programs.
3797
  */
3798
+ class SucuriScanHook extends SucuriScanEvent
3799
+ {
3800
 
3801
  /**
3802
  * Send to Sucuri servers an alert notifying that an attachment was added to a post.
3804
  * @param integer $id The post identifier.
3805
  * @return void
3806
  */
3807
+ public static function hook_add_attachment($id = 0)
3808
+ {
3809
+ if ($data = get_post($id)) {
3810
  $id = $data->ID;
3811
  $title = $data->post_title;
3812
  $mime_type = $data->post_mime_type;
3815
  $mime_type = 'unknown';
3816
  }
3817
 
3818
+ $message = sprintf('Media file added; identifier: %s; name: %s; type: %s', $id, $title, $mime_type);
3819
+ self::report_notice_event($message);
3820
+ self::notify_event('post_publication', $message);
3821
  }
3822
 
3823
  /**
3826
  * @param integer $id Identifier of the new link created;
3827
  * @return void
3828
  */
3829
+ public static function hook_add_link($id = 0)
3830
+ {
3831
+ if ($data = get_bookmark($id)) {
3832
  $id = $data->link_id;
3833
  $title = $data->link_name;
3834
  $url = $data->link_url;
3841
 
3842
  $message = sprintf(
3843
  'Bookmark link added; identifier: %s; name: %s; url: %s; target: %s',
3844
+ $id,
3845
+ $title,
3846
+ $url,
3847
+ $target
3848
  );
3849
+ self::report_warning_event($message);
3850
+ self::notify_event('post_publication', $message);
3851
  }
3852
 
3853
  /**
3856
  * @param integer $id The identifier of the category created.
3857
  * @return void
3858
  */
3859
+ public static function hook_create_category($id = 0)
3860
+ {
3861
+ $title = ( is_int($id) ? get_cat_name($id) : 'Unknown' );
3862
 
3863
+ $message = sprintf('Category created; identifier: %s; name: %s', $id, $title);
3864
+ self::report_notice_event($message);
3865
+ self::notify_event('post_publication', $message);
3866
  }
3867
 
3868
  /**
3871
  * @param integer $id The identifier of the post deleted.
3872
  * @return void
3873
  */
3874
+ public static function hook_delete_post($id = 0)
3875
+ {
3876
+ self::report_warning_event('Post deleted; identifier: ' . $id);
3877
  }
3878
 
3879
  /**
3882
  * @param integer $id The identifier of the trashed post.
3883
  * @return void
3884
  */
3885
+ public static function hook_wp_trash_post($id = 0)
3886
+ {
3887
+ if ($data = get_post($id)) {
3888
  $title = $data->post_title;
3889
  $status = $data->post_status;
3890
  } else {
3894
 
3895
  $message = sprintf(
3896
  'Post moved to trash; identifier: %s; name: %s; status: %s',
3897
+ $id,
3898
+ $title,
3899
+ $status
3900
  );
3901
+ self::report_warning_event($message);
3902
  }
3903
 
3904
  /**
3907
  * @param integer $id The identifier of the user account deleted.
3908
  * @return void
3909
  */
3910
+ public static function hook_delete_user($id = 0)
3911
+ {
3912
+ self::report_warning_event('User account deleted; identifier: ' . $id);
3913
  }
3914
 
3915
  /**
3918
  *
3919
  * @return void
3920
  */
3921
+ public static function hook_login_form_resetpass()
3922
+ {
3923
  // Detecting WordPress 2.8.3 vulnerability - $key is array.
3924
+ if (isset($_GET['key']) && is_array($_GET['key'])) {
3925
+ self::report_critical_event('Attempt to reset password by attacking WP/2.8.3 bug');
3926
  }
3927
  }
3928
 
3933
  * @param integer $id The identifier of the post changed.
3934
  * @return void
3935
  */
3936
+ public static function hook_private_to_published($id = 0)
3937
+ {
3938
+ if ($data = get_post($id)) {
3939
  $title = $data->post_title;
3940
+ $p_type = ucwords($data->post_type);
3941
  } else {
3942
  $title = 'Unknown';
3943
  $p_type = 'Publication';
3944
  }
3945
 
3946
  // Check whether the post-type is being ignored to send notifications.
3947
+ if (!SucuriScanOption::is_ignored_event($p_type)) {
3948
  $message = sprintf(
3949
  '%s (private to published); identifier: %s; name: %s',
3950
+ $p_type,
3951
+ $id,
3952
+ $title
3953
  );
3954
+ self::report_notice_event($message);
3955
+ self::notify_event('post_publication', $message);
3956
  }
3957
  }
3958
 
3962
  * @param integer $id The identifier of the post or page published.
3963
  * @return void
3964
  */
3965
+ public static function hook_publish($id = 0)
3966
+ {
3967
+ if ($data = get_post($id)) {
3968
  $title = $data->post_title;
3969
+ $p_type = ucwords($data->post_type);
3970
  $action = ( $data->post_date == $data->post_modified ? 'created' : 'updated' );
3971
  } else {
3972
  $title = 'Unknown';
3976
 
3977
  $message = sprintf(
3978
  '%s was %s; identifier: %s; name: %s',
3979
+ $p_type,
3980
+ $action,
3981
+ $id,
3982
+ $title
3983
  );
3984
+ self::report_notice_event($message);
3985
+ self::notify_event('post_publication', $message);
3986
  }
3987
 
3988
  /**
3991
  * @param integer $id The identifier of the post or page published.
3992
  * @return void
3993
  */
3994
+ public static function hook_publish_page($id = 0)
3995
+ {
3996
+ self::hook_publish($id);
3997
  }
3998
 
3999
  /**
4002
  * @param integer $id The identifier of the post or page published.
4003
  * @return void
4004
  */
4005
+ public static function hook_publish_post($id = 0)
4006
+ {
4007
+ self::hook_publish($id);
4008
  }
4009
 
4010
  /**
4013
  * @param integer $id The identifier of the post or page published.
4014
  * @return void
4015
  */
4016
+ public static function hook_publish_phone($id = 0)
4017
+ {
4018
+ self::hook_publish($id);
4019
  }
4020
 
4021
  /**
4024
  * @param integer $id The identifier of the post or page published.
4025
  * @return void
4026
  */
4027
+ public static function hook_xmlrpc_publish_post($id = 0)
4028
+ {
4029
+ self::hook_publish($id);
4030
  }
4031
 
4032
  /**
4036
  * @param string $title The name of the user account involved in the trasaction.
4037
  * @return void
4038
  */
4039
+ public static function hook_retrieve_password($title = '')
4040
+ {
4041
+ if (empty($title)) {
4042
  $title = 'unknown';
4043
  }
4044
 
4045
+ self::report_error_event('Password retrieval attempt: ' . $title);
4046
  }
4047
 
4048
  /**
4051
  * @param string $title The name of the new theme selected to used through out the site.
4052
  * @return void
4053
  */
4054
+ public static function hook_switch_theme($title = '')
4055
+ {
4056
+ if (empty($title)) {
4057
  $title = 'unknown';
4058
  }
4059
 
4060
  $message = 'Theme activated: ' . $title;
4061
+ self::report_warning_event($message);
4062
+ self::notify_event('theme_activated', $message);
4063
  }
4064
 
4065
  /**
4068
  * @param integer $id The identifier of the new user account created.
4069
  * @return void
4070
  */
4071
+ public static function hook_user_register($id = 0)
4072
+ {
4073
+ if ($data = get_userdata($id)) {
4074
  $title = $data->user_login;
4075
  $email = $data->user_email;
4076
+ $roles = @implode(', ', $data->roles);
4077
  } else {
4078
  $title = 'unknown';
4079
  $email = 'user@domain.com';
4082
 
4083
  $message = sprintf(
4084
  'User account created; identifier: %s; name: %s; email: %s; roles: %s',
4085
+ $id,
4086
+ $title,
4087
+ $email,
4088
+ $roles
4089
  );
4090
+ self::report_warning_event($message);
4091
+ self::notify_event('user_registration', $message);
4092
  }
4093
 
4094
  /**
4098
  * @param string $title The name of the user account involved in the transaction.
4099
  * @return void
4100
  */
4101
+ public static function hook_wp_login($title = '')
4102
+ {
4103
+ if (empty($title)) {
4104
  $title = 'Unknown';
4105
  }
4106
 
4107
  $message = 'User authentication succeeded: ' . $title;
4108
+ self::report_notice_event($message);
4109
+ self::notify_event('success_login', $message);
4110
  }
4111
 
4112
  /**
4116
  * @param string $title The name of the user account involved in the transaction.
4117
  * @return void
4118
  */
4119
+ public static function hook_wp_login_failed($title = '')
4120
+ {
4121
+ if (empty($title)) {
4122
  $title = 'Unknown';
4123
  }
4124
 
4125
+ $title = sanitize_user($title, true);
4126
+ $password = SucuriScanRequest::post('pwd');
4127
  $message = 'User authentication failed: ' . $title;
4128
 
4129
+ self::report_error_event($message);
4130
 
4131
+ if (sucuriscan_collect_wrong_passwords() === true) {
4132
  $message .= "<br>\nUser wrong password: " . $password;
4133
  }
4134
 
4135
+ self::notify_event('failed_login', $message);
4136
 
4137
  // Log the failed login in the internal datastore for future reports.
4138
+ $logged = sucuriscan_log_failed_login($title, $password);
4139
 
4140
  // Check if the quantity of failed logins will be considered as a brute-force attack.
4141
+ if ($logged) {
4142
  $failed_logins = sucuriscan_get_failed_logins();
4143
 
4144
+ if ($failed_logins) {
4145
  $max_time = 3600;
4146
+ $maximum_failed_logins = SucuriScanOption::get_option('sucuriscan_maximum_failed_logins');
4147
 
4148
  /**
4149
  * If the time passed is within the hour, and the quantity of failed logins
4152
  * settings page), then send an email notification reporting the event and
4153
  * specifying that it may be a brute-force attack against the login page.
4154
  */
4155
+ if ($failed_logins['diff_time'] <= $max_time
 
4156
  && $failed_logins['count'] >= $maximum_failed_logins
4157
  ) {
4158
+ sucuriscan_report_failed_logins($failed_logins);
4159
+ } /**
 
 
4160
  * If there time passed is superior to the hour, then reset the content of the
4161
  * datastore file containing the failed logins so far, any entry in that file
4162
  * will not be considered as part of a brute-force attack (if it exists) because
4165
  * first entry of that file in case of future attempts during the next sixty
4166
  * minutes.
4167
  */
4168
+ elseif ($failed_logins['diff_time'] > $max_time) {
4169
  sucuriscan_reset_failed_logins();
4170
+ sucuriscan_log_failed_login($title);
4171
  }
4172
  }
4173
  }
4189
  * @param object $comment The comment object.
4190
  * @return void
4191
  */
4192
+ public static function hook_wp_insert_comment($id = 0, $comment = false)
4193
+ {
4194
+ if ($comment instanceof stdClass
4195
+ && property_exists($comment, 'comment_ID')
4196
+ && property_exists($comment, 'comment_agent')
4197
+ && property_exists($comment, 'comment_author_IP')
4198
+ && SucuriScanOption::is_enabled(':comment_monitor')
4199
  ) {
4200
  $data_set = array(
4201
  'id' => $comment->comment_ID,
4209
  'content' => $comment->comment_content,
4210
  'user_agent' => $comment->comment_agent,
4211
  );
4212
+ $message = base64_encode(json_encode($data_set));
4213
+ self::report_notice_event('Base64:' . $message, true);
4214
  }
4215
  }
4216
 
4226
  *
4227
  * @return void
4228
  */
4229
+ public static function hook_all($action = null, $data = false)
4230
+ {
4231
  global $wp_filter, $wp_actions;
4232
 
4233
+ if (is_array($wp_filter)
4234
+ && is_array($wp_actions)
4235
+ && array_key_exists($action, $wp_actions)
4236
+ && !array_key_exists($action, $wp_filter)
 
4237
  && (
4238
+ substr($action, 0, 11) === 'admin_post_'
4239
+ || substr($action, 0, 8) === 'wp_ajax_'
4240
  )
4241
  ) {
4242
+ $message = sprintf('Undefined XHR action %s', $action);
4243
+ self::report_error_event($message);
4244
+ header('HTTP/1.1 400 Bad Request');
4245
  exit(1);
4246
  }
4247
  }
4253
  *
4254
  * @return integer Either one or zero representing the success or fail of the operation.
4255
  */
4256
+ public static function hook_undefined_actions()
4257
+ {
4258
 
4259
  $plugin_activate_actions = '(activate|deactivate)(\-selected)?';
4260
  $plugin_update_actions = '(upgrade-plugin|do-plugin-upgrade|update-selected)';
4261
 
4262
  // Plugin activation and/or deactivation.
4263
+ if (current_user_can('activate_plugins')
 
4264
  && (
4265
+ SucuriScanRequest::get_or_post('action', $plugin_activate_actions)
4266
+ || SucuriScanRequest::get_or_post('action2', $plugin_activate_actions)
4267
  )
4268
  ) {
4269
  $plugin_list = array();
4270
  $items_affected = array();
4271
 
4272
  // Get the action performed through action or action2 params.
4273
+ $action_d = SucuriScanRequest::get_or_post('action');
4274
+ if ($action_d == '-1') {
4275
+ $action_d = SucuriScanRequest::get_or_post('action2');
4276
  }
4277
  $action_d .= 'd';
4278
 
4279
+ if (SucuriScanRequest::get('plugin', '.+')
4280
+ && strpos($_SERVER['REQUEST_URI'], 'plugins.php') !== false
 
4281
  ) {
4282
+ $plugin_list[] = SucuriScanRequest::get('plugin');
4283
+ } elseif (isset($_POST['checked'])
4284
+ && is_array($_POST['checked'])
4285
+ && !empty($_POST['checked'])
 
 
 
4286
  ) {
4287
+ $plugin_list = SucuriScanRequest::post('checked', '_array');
4288
+ $action_d = str_replace('-selected', '', $action_d);
4289
  }
4290
 
4291
+ foreach ($plugin_list as $plugin) {
4292
+ $plugin_info = get_plugin_data(WP_PLUGIN_DIR . '/' . $plugin);
4293
 
4294
+ if (!empty($plugin_info['Name'])
4295
+ && !empty($plugin_info['Version'])
 
4296
  ) {
4297
  $items_affected[] = sprintf(
4298
  '%s (v%s; %s)',
4299
+ self::escape($plugin_info['Name']),
4300
+ self::escape($plugin_info['Version']),
4301
+ self::escape($plugin)
4302
  );
4303
  }
4304
  }
4305
 
4306
  // Report activated/deactivated plugins at once.
4307
+ if (!empty($items_affected)) {
4308
+ $message_tpl = ( count($items_affected) > 1 )
4309
  ? 'Plugins %s: (multiple entries): %s'
4310
  : 'Plugin %s: %s';
4311
  $message = sprintf(
4312
  $message_tpl,
4313
  $action_d,
4314
+ @implode(',', $items_affected)
4315
  );
4316
+ self::report_warning_event($message);
4317
+ self::notify_event('plugin_' . $action_d, $message);
4318
  }
4319
+ } // Plugin update request.
4320
+ elseif (current_user_can('update_plugins')
 
 
 
4321
  && (
4322
+ SucuriScanRequest::get_or_post('action', $plugin_update_actions)
4323
+ || SucuriScanRequest::get_or_post('action2', $plugin_update_actions)
4324
  )
4325
  ) {
4326
  $plugin_list = array();
4327
  $items_affected = array();
4328
 
4329
+ if (SucuriScanRequest::get('plugin', '.+')
4330
+ && strpos($_SERVER['REQUEST_URI'], 'wp-admin/update.php') !== false
 
4331
  ) {
4332
+ $plugin_list[] = SucuriScanRequest::get('plugin', '.+');
4333
+ } elseif (isset($_POST['checked'])
4334
+ && is_array($_POST['checked'])
4335
+ && !empty($_POST['checked'])
 
 
 
4336
  ) {
4337
+ $plugin_list = SucuriScanRequest::post('checked', '_array');
4338
  }
4339
 
4340
+ foreach ($plugin_list as $plugin) {
4341
+ $plugin_info = get_plugin_data(WP_PLUGIN_DIR . '/' . $plugin);
4342
 
4343
+ if (!empty($plugin_info['Name'])
4344
+ && !empty($plugin_info['Version'])
 
4345
  ) {
4346
  $items_affected[] = sprintf(
4347
  '%s (v%s; %s)',
4348
+ self::escape($plugin_info['Name']),
4349
+ self::escape($plugin_info['Version']),
4350
+ self::escape($plugin)
4351
  );
4352
  }
4353
  }
4354
 
4355
  // Report updated plugins at once.
4356
+ if (!empty($items_affected)) {
4357
+ $message_tpl = ( count($items_affected) > 1 )
4358
  ? 'Plugins updated: (multiple entries): %s'
4359
  : 'Plugin updated: %s';
4360
  $message = sprintf(
4361
  $message_tpl,
4362
+ @implode(',', $items_affected)
4363
  );
4364
+ self::report_warning_event($message);
4365
+ self::notify_event('plugin_updated', $message);
4366
  }
4367
+ } // Plugin installation request.
4368
+ elseif (current_user_can('install_plugins')
4369
+ && SucuriScanRequest::get('action', '(install|upload)-plugin')
 
 
 
4370
  ) {
4371
+ if (isset($_FILES['pluginzip'])) {
4372
+ $plugin = self::escape($_FILES['pluginzip']['name']);
4373
  } else {
4374
+ $plugin = SucuriScanRequest::get('plugin', '.+');
4375
 
4376
+ if (!$plugin) {
4377
  $plugin = 'Unknown';
4378
  }
4379
  }
4380
 
4381
+ $message = 'Plugin installed: ' . self::escape($plugin);
4382
+ SucuriScanEvent::report_warning_event($message);
4383
+ self::notify_event('plugin_installed', $message);
4384
+ } // Plugin deletion request.
4385
+ elseif (current_user_can('delete_plugins')
4386
+ && SucuriScanRequest::post('action', 'delete-selected')
4387
+ && SucuriScanRequest::post('verify-delete', '1')
 
 
 
4388
  ) {
4389
+ $plugin_list = SucuriScanRequest::post('checked', '_array');
4390
  $items_affected = array();
4391
 
4392
+ foreach ((array) $plugin_list as $plugin) {
4393
+ $plugin_info = get_plugin_data(WP_PLUGIN_DIR . '/' . $plugin);
4394
 
4395
+ if (!empty($plugin_info['Name'])
4396
+ && !empty($plugin_info['Version'])
 
4397
  ) {
4398
  $items_affected[] = sprintf(
4399
  '%s (v%s; %s)',
4400
+ self::escape($plugin_info['Name']),
4401
+ self::escape($plugin_info['Version']),
4402
+ self::escape($plugin)
4403
  );
4404
  }
4405
  }
4406
 
4407
  // Report deleted plugins at once.
4408
+ if (!empty($items_affected)) {
4409
+ $message_tpl = ( count($items_affected) > 1 )
4410
  ? 'Plugins deleted: (multiple entries): %s'
4411
  : 'Plugin deleted: %s';
4412
  $message = sprintf(
4413
  $message_tpl,
4414
+ @implode(',', $items_affected)
4415
  );
4416
+ self::report_warning_event($message);
4417
+ self::notify_event('plugin_deleted', $message);
4418
+ }
4419
+ } // Plugin editor request.
4420
+ elseif (current_user_can('edit_plugins')
4421
+ && SucuriScanRequest::post('action', 'update')
4422
+ && SucuriScanRequest::post('plugin', '.+')
4423
+ && SucuriScanRequest::post('file', '.+')
4424
+ && strpos($_SERVER['REQUEST_URI'], 'plugin-editor.php') !== false
 
 
 
4425
  ) {
4426
+ $filename = SucuriScanRequest::post('file');
4427
+ $message = 'Plugin editor used in: ' . SucuriScan::escape($filename);
4428
+ self::report_error_event($message);
4429
+ self::notify_event('theme_editor', $message);
4430
+ } // Theme editor request.
4431
+ elseif (current_user_can('edit_themes')
4432
+ && SucuriScanRequest::post('action', 'update')
4433
+ && SucuriScanRequest::post('theme', '.+')
4434
+ && SucuriScanRequest::post('file', '.+')
4435
+ && strpos($_SERVER['REQUEST_URI'], 'theme-editor.php') !== false
 
 
 
4436
  ) {
4437
+ $theme_name = SucuriScanRequest::post('theme');
4438
+ $filename = SucuriScanRequest::post('file');
4439
+ $message = 'Theme editor used in: ' . SucuriScan::escape($theme_name) . '/' . SucuriScan::escape($filename);
4440
+ self::report_error_event($message);
4441
+ self::notify_event('theme_editor', $message);
4442
+ } // Theme installation request.
4443
+ elseif (current_user_can('install_themes')
4444
+ && SucuriScanRequest::get('action', 'install-theme')
 
 
 
4445
  ) {
4446
+ $theme = SucuriScanRequest::get('theme', '.+');
4447
 
4448
+ if (!$theme) {
4449
  $theme = 'Unknown';
4450
  }
4451
 
4452
+ $message = 'Theme installed: ' . self::escape($theme);
4453
+ SucuriScanEvent::report_warning_event($message);
4454
+ self::notify_event('theme_installed', $message);
4455
+ } // Theme deletion request.
4456
+ elseif (current_user_can('delete_themes')
4457
+ && SucuriScanRequest::get_or_post('action', 'delete')
4458
+ && SucuriScanRequest::get_or_post('stylesheet', '.+')
 
 
 
4459
  ) {
4460
+ $theme = SucuriScanRequest::get('stylesheet', '.+');
4461
 
4462
+ if (!$theme) {
4463
  $theme = 'Unknown';
4464
  }
4465
 
4466
+ $message = 'Theme deleted: ' . self::escape($theme);
4467
+ SucuriScanEvent::report_warning_event($message);
4468
+ self::notify_event('theme_deleted', $message);
4469
+ } // Theme update request.
4470
+ elseif (current_user_can('update_themes')
4471
+ && SucuriScanRequest::get('action', '(upgrade-theme|do-theme-upgrade)')
4472
+ && SucuriScanRequest::post('checked', '_array')
 
 
 
4473
  ) {
4474
+ $themes = SucuriScanRequest::post('checked', '_array');
4475
  $items_affected = array();
4476
 
4477
+ foreach ((array) $themes as $theme) {
4478
+ $theme_info = wp_get_theme($theme);
4479
+ $theme_name = ucwords($theme);
4480
  $theme_version = '0.0';
4481
 
4482
+ if ($theme_info->exists()) {
4483
+ $theme_name = $theme_info->get('Name');
4484
+ $theme_version = $theme_info->get('Version');
4485
  }
4486
 
4487
  $items_affected[] = sprintf(
4488
  '%s (v%s; %s)',
4489
+ self::escape($theme_name),
4490
+ self::escape($theme_version),
4491
+ self::escape($theme)
4492
  );
4493
  }
4494
 
4495
  // Report updated themes at once.
4496
+ if (!empty($items_affected)) {
4497
+ $message_tpl = ( count($items_affected) > 1 )
4498
  ? 'Themes updated: (multiple entries): %s'
4499
  : 'Theme updated: %s';
4500
  $message = sprintf(
4501
  $message_tpl,
4502
+ @implode(',', $items_affected)
4503
  );
4504
+ self::report_warning_event($message);
4505
+ self::notify_event('theme_updated', $message);
4506
  }
4507
+ } // WordPress update request.
4508
+ elseif (current_user_can('update_core')
4509
+ && SucuriScanRequest::get('action', '(do-core-upgrade|do-core-reinstall)')
4510
+ && SucuriScanRequest::post('upgrade')
 
 
 
4511
  ) {
4512
+ $message = 'WordPress updated to version: ' . SucuriScanRequest::post('version');
4513
+ self::report_critical_event($message);
4514
+ self::notify_event('website_updated', $message);
4515
+ } // Widget addition or deletion.
4516
+ elseif (current_user_can('edit_theme_options')
4517
+ && SucuriScanRequest::post('action', 'save-widget')
4518
+ && SucuriScanRequest::post('id_base') !== false
4519
+ && SucuriScanRequest::post('sidebar') !== false
 
 
 
4520
  ) {
4521
+ if (SucuriScanRequest::post('delete_widget', '1')) {
4522
  $action_d = 'deleted';
4523
  $action_text = 'deleted from';
4524
  } else {
4528
 
4529
  $message = sprintf(
4530
  'Widget %s (%s) %s %s (#%d; size %dx%d)',
4531
+ SucuriScanRequest::post('id_base'),
4532
+ SucuriScanRequest::post('widget-id'),
4533
  $action_text,
4534
+ SucuriScanRequest::post('sidebar'),
4535
+ SucuriScanRequest::post('widget_number'),
4536
+ SucuriScanRequest::post('widget-width'),
4537
+ SucuriScanRequest::post('widget-height')
4538
  );
4539
 
4540
+ self::report_warning_event($message);
4541
+ self::notify_event('widget_' . $action_d, $message);
4542
+ } // Detect any Wordpress settings modification.
4543
+ elseif (current_user_can('manage_options')
 
 
 
4544
  && SucuriScanOption::check_options_nonce()
4545
  ) {
4546
  // Get the settings available in the database and compare them with the submission.
4547
+ $options_changed = SucuriScanOption::what_options_were_changed($_POST);
4548
  $options_changed_str = '';
4549
  $options_changed_simple = '';
4550
  $options_changed_count = 0;
4551
 
4552
  // Generate the list of options changed.
4553
+ foreach ($options_changed['original'] as $option_name => $option_value) {
4554
  $options_changed_count += 1;
4555
  $options_changed_str .= sprintf(
4556
  "The value of the option <b>%s</b> was changed from <b>'%s'</b> to <b>'%s'</b>.<br>\n",
4557
+ self::escape($option_name),
4558
+ self::escape($option_value),
4559
+ self::escape($options_changed['changed'][ $option_name ])
4560
  );
4561
  $options_changed_simple .= sprintf(
4562
  "%s: from '%s' to '%s',",
4563
+ self::escape($option_name),
4564
+ self::escape($option_value),
4565
+ self::escape($options_changed['changed'][ $option_name ])
4566
  );
4567
  }
4568
 
4571
  $page_referer = false;
4572
 
4573
  // Check which of these option groups where modified.
4574
+ switch ($option_page) {
4575
  case 'options':
4576
  $page_referer = 'Global';
4577
  break;
4581
  case 'discussion': /* no_break */
4582
  case 'media': /* no_break */
4583
  case 'permalink':
4584
+ $page_referer = ucwords($option_page);
4585
  break;
4586
  default:
4587
  $page_referer = 'Common';
4588
  break;
4589
  }
4590
 
4591
+ if ($page_referer && $options_changed_count > 0) {
4592
  $message = $page_referer . ' settings changed';
4593
+ SucuriScanEvent::report_error_event(sprintf(
4594
  '%s: (multiple entries): %s',
4595
  $message,
4596
+ rtrim($options_changed_simple, ',')
4597
+ ));
4598
+ self::notify_event('settings_updated', $message . "<br>\n" . $options_changed_str);
4599
  }
4600
  }
4601
 
4602
  }
 
4603
  }
4604
 
4605
  /**
4619
  * APIs allow the combination of multiple APIs into new applications known as
4620
  * mashups.
4621
  *
4622
+ * @see https://en.wikipedia.org/wiki/Application_programming_interface#Web_APIs
4623
  */
4624
+ class SucuriScanAPI extends SucuriScanOption
4625
+ {
4626
  /**
4627
  * Check whether the SSL certificates will be verified while executing a HTTP
4628
  * request or not. This is only for customization of the administrator, in fact
4630
  *
4631
  * @return boolean Whether the SSL certs will be verified while sending a request.
4632
  */
4633
+ public static function verifySslCert()
4634
+ {
4635
+ return (self::get_option(':verify_ssl_cert') === 'true');
4636
  }
4637
 
4638
  /**
4639
  * Seconds before consider a HTTP request as timeout.
4640
  *
4641
+ * As for the 01/Jan/2016 if the number of seconds before a timeout is greater
4642
+ * than sixty (which is one minute) the function will reset the option to its
4643
+ * default value to keep the latency of the HTTP requests in a minimum to
4644
+ * minimize the interruptions in the admins workflow. The normal connection
4645
+ * timeout should be in the range of ten seconds, or fifteen if the DNS lookups
4646
+ * are slow.
4647
+ *
4648
  * @return integer Seconds to consider a HTTP request timeout.
4649
  */
4650
+ public static function requestTimeout()
4651
+ {
4652
+ $timeout = (int) self::get_option(':request_timeout');
4653
+
4654
+ if ($timeout > SUCURISCAN_MAX_REQUEST_TIMEOUT) {
4655
+ self::delete_option(':request_timeout');
4656
+
4657
+ return self::requestTimeout();
4658
+ }
4659
+
4660
+ return $timeout;
4661
  }
4662
 
4663
  /**
4665
  *
4666
  * @return string An user-agent for the HTTP requests.
4667
  */
4668
+ private static function userAgent()
4669
+ {
4670
+ return sprintf(
4671
  'WordPress/%s; %s',
4672
  self::site_version(),
4673
  self::get_domain()
4674
  );
 
 
4675
  }
4676
 
4677
  /**
4678
  * Retrieves a URL using a changeable HTTP method, returning results in an
4679
  * array. Results include HTTP headers and content.
4680
  *
4681
+ * @see https://codex.wordpress.org/Function_Reference/wp_remote_post
4682
+ * @see https://codex.wordpress.org/Function_Reference/wp_remote_get
4683
  *
4684
  * @param string $url The target URL where the request will be sent.
4685
  * @param string $method HTTP method that will be used to send the request.
4687
  * @param array $args Request arguments like the timeout, redirections, headers, cookies, etc.
4688
  * @return array Response object after the HTTP request is executed.
4689
  */
4690
+ private static function apiCall($url = '', $method = 'GET', $params = array(), $args = array())
4691
+ {
4692
+ if (!$url) {
4693
  return false;
4694
  }
4695
 
4696
  $req_args = array(
4697
  'method' => $method,
4698
+ 'timeout' => self::requestTimeout(),
4699
  'redirection' => 2,
4700
  'httpversion' => '1.0',
4701
+ 'user-agent' => self::userAgent(),
4702
  'blocking' => true,
4703
  'headers' => array(),
4704
  'cookies' => array(),
4705
  'compress' => false,
4706
  'decompress' => false,
4707
+ 'sslverify' => self::verifySslCert(),
4708
  );
4709
 
4710
  // Update the request arguments with the values passed tot he function.
4711
+ foreach ($args as $arg_name => $arg_value) {
4712
+ if (array_key_exists($arg_name, $req_args)) {
4713
+ $req_args[$arg_name] = $arg_value;
4714
  }
4715
  }
4716
 
4717
  // Add random request parameter to avoid request reset.
4718
+ if (!empty($params) && !array_key_exists('time', $params)) {
4719
  $params['time'] = time();
4720
  }
4721
 
4722
+ if ($method == 'GET') {
4723
+ if (!empty($params)) {
4724
+ $url = sprintf('%s?%s', $url, http_build_query($params));
4725
  }
4726
 
4727
+ $response = wp_remote_get($url, $req_args);
4728
+ } elseif ($method == 'POST') {
4729
  $req_args['body'] = $params;
4730
+ $response = wp_remote_post($url, $req_args);
4731
  } else {
4732
  $response = false;
4733
+ SucuriScanInterface::error('HTTP method not allowed: ' . $method);
4734
  }
4735
 
4736
+ return self::processResponse($response, $params, $args);
4737
  }
4738
 
4739
  /**
4742
  * @param string $api_key An unique string to identify this installation.
4743
  * @return boolean True if the API key is valid, false otherwise.
4744
  */
4745
+ private static function isValidKey($api_key = '')
4746
+ {
4747
+ return (bool) @preg_match('/^[a-z0-9]{32}$/', $api_key);
 
4748
  }
4749
 
4750
  /**
4754
  * @param boolean $validate Whether the format of the key should be validated before store it.
4755
  * @return boolean Either true or false if the key was saved successfully or not respectively.
4756
  */
4757
+ public static function setPluginKey($api_key = '', $validate = false)
4758
+ {
4759
+ if ($validate) {
4760
+ if (!self::isValidKey($api_key)) {
4761
+ SucuriScanInterface::error('Invalid API key format');
4762
  return false;
4763
  }
4764
  }
4765
 
4766
+ if (!empty($api_key)) {
4767
+ SucuriScanEvent::notify_event('plugin_change', 'API key updated successfully: ' . $api_key);
4768
  }
4769
 
4770
+ return self::update_option(':api_key', $api_key);
4771
  }
4772
 
4773
  /**
4775
  *
4776
  * @return string|boolean The API key or false if it does not exists.
4777
  */
4778
+ public static function getPluginKey()
4779
+ {
4780
+ $api_key = self::get_option(':api_key');
4781
 
4782
+ if (is_string($api_key)
4783
+ && self::isValidKey($api_key)
 
4784
  ) {
4785
  return $api_key;
4786
  }
4797
  *
4798
  * @return array|boolean false if the key is invalid or not present, an array otherwise.
4799
  */
4800
+ public static function getCloudproxyKey()
4801
+ {
4802
  $option_name = ':cloudproxy_apikey';
4803
+ $api_key = self::get_option($option_name);
4804
 
4805
  // Check if the cloudproxy-waf plugin was previously installed.
4806
+ if (!$api_key) {
4807
+ $api_key = self::get_option('sucuriwaf_apikey');
4808
 
4809
+ if ($api_key) {
4810
+ self::update_option($option_name, $api_key);
4811
+ self::delete_option('sucuriwaf_apikey');
4812
  }
4813
  }
4814
 
4815
  // Check the validity of the API key.
4816
+ $match = self::isValidCloudproxyKey($api_key, true);
4817
 
4818
+ if ($match) {
4819
  return array(
4820
  'string' => $match[1].'/'.$match[2],
4821
  'k' => $match[1],
4833
  * @param boolean $return_match Whether the parts of the API key must be returned or not.
4834
  * @return boolean true if the API key specified is valid, false otherwise.
4835
  */
4836
+ public static function isValidCloudproxyKey($api_key = '', $return_match = false)
4837
+ {
4838
  $pattern = '/^([a-z0-9]{32})\/([a-z0-9]{32})$/';
4839
 
4840
+ if ($api_key && preg_match($pattern, $api_key, $match)) {
4841
+ if ($return_match) {
4842
  return $match;
4843
  }
4844
 
4857
  * @param array $args Request arguments like the timeout, redirections, headers, cookies, etc.
4858
  * @return array Response object after the HTTP request is executed.
4859
  */
4860
+ public static function apiCallWordpress($method = 'GET', $params = array(), $send_api_key = true, $args = array())
4861
+ {
4862
  $url = SUCURISCAN_API;
4863
  $params[ SUCURISCAN_API_VERSION ] = 1;
4864
  $params['p'] = 'wordpress';
4865
 
4866
+ if ($send_api_key) {
4867
+ $api_key = self::getPluginKey();
4868
 
4869
+ if (!$api_key) {
4870
  return false;
4871
  }
4872
 
4873
  $params['k'] = $api_key;
4874
  }
4875
 
4876
+ return self::apiCall($url, $method, $params, $args);
 
 
4877
  }
4878
 
4879
  /**
4883
  * @param array $params Parameters for the request defined in an associative array of key-value.
4884
  * @return array Response object after the HTTP request is executed.
4885
  */
4886
+ public static function apiCallCloudproxy($method = 'GET', $params = array())
4887
+ {
4888
  $send_request = false;
4889
 
4890
+ if (isset($params['k']) && isset($params['s'])) {
4891
  $send_request = true;
4892
  } else {
4893
+ $api_key = self::getCloudproxyKey();
4894
 
4895
+ if ($api_key) {
4896
  $send_request = true;
4897
  $params['k'] = $api_key['k'];
4898
  $params['s'] = $api_key['s'];
4899
  }
4900
  }
4901
 
4902
+ if ($send_request) {
4903
  $url = SUCURISCAN_CLOUDPROXY_API;
4904
  $params[ SUCURISCAN_CLOUDPROXY_API_VERSION ] = 1;
4905
+ unset($params['string']);
 
 
4906
 
4907
+ return self::apiCall($url, $method, $params);
4908
  }
4909
 
4910
  return false;
4918
  * @param array $args Request arguments like the timeout, redirections, headers, cookies, etc.
4919
  * @return array Response object with some modifications.
4920
  */
4921
+ private static function processResponse($response = array(), $params = array(), $args = array())
4922
+ {
4923
  /**
4924
  * Convert the error message generated by the code base functions after the HTTP
4925
  * request is executed to a valid response object that will allow this code
4926
  * process the data according to the specified standards.
4927
  */
4928
+ if (is_wp_error($response)) {
4929
  // Extract information from the error object.
4930
  $error_message = $response->get_error_message();
4931
+ $request_action = isset($params['a']) ? $params['a'] : 'unknown';
4932
 
4933
  // Build a fake request response with custom data.
4934
  $data_set = array(
4942
 
4943
  // Build the response object and encode data.
4944
  $response = array();
4945
+ $response['body'] = json_encode($data_set);
4946
+ $response['headers']['date'] = date('r');
4947
  $response['headers']['connection'] = 'close';
4948
  $response['headers']['content-type'] = 'application/json';
4949
+ $response['headers']['content-length'] = strlen($response['body']);
4950
  $response['response']['code'] = 500;
4951
  $response['response']['message'] = 'ERROR';
4952
  }
4962
  * they will see extra information explaining the response and how to proceed
4963
  * with it.
4964
  */
4965
+ if (is_array($response)
4966
+ && array_key_exists('body', $response)
4967
+ && array_key_exists('headers', $response)
4968
+ && array_key_exists('response', $response)
 
4969
  ) {
4970
+ // Keep a copy of the raw HTTP response.
4971
  $response['body_raw'] = $response['body'];
4972
 
4973
+ // Append the non-private HTTP request parameters.
4974
+ $response['params'] = $params;
4975
+ unset($response['params']['k']);
4976
+
4977
+ /**
4978
+ * Check and decode the API response.
4979
+ *
4980
+ * Note that serialized data is going to be ignored, the old API service used to
4981
+ * respond to all endpoints with serialized data and considering the risk that
4982
+ * it poses to unserialize in PHP it was decided to drop that option and stick
4983
+ * to JSON which is a bit safer.
4984
+ */
4985
+ if (isset($response['headers']['content-type'])
4986
  && $response['headers']['content-type'] == 'application/json'
4987
  ) {
4988
+ $assoc = (isset($args['assoc']) && $args['assoc'] === true) ? true : false;
4989
+ $response['body'] = @json_decode($response['body_raw'], $assoc);
4990
+ $response['body_arr'] = @json_decode($response['body_raw'], true);
4991
+ } elseif (self::is_serialized($response['body'])) {
4992
  $response['body_raw'] = null;
4993
  $response['body'] = 'ERROR:Serialized data is not supported.';
4994
  }
5004
  * generic variables and types, in case of an error a notification will appears
5005
  * in the administrator panel explaining the result of the operation.
5006
  *
5007
+ * @param array $response HTTP response after API endpoint execution.
5008
+ * @param boolean $enqueue Add the log to the local queue on a failure.
5009
+ * @return boolean False if the API call failed, true otherwise.
5010
  */
5011
+ private static function handleResponse($response = array(), $enqueue = true)
5012
+ {
5013
+ $error_msg = '';
5014
+
5015
+ if ($response) {
5016
+ if ($response['body'] instanceof stdClass) {
5017
+ if (isset($response['body']->status)) {
5018
+ if ($response['body']->status == 1) {
5019
  return true;
5020
  } else {
5021
+ return self::handleErrorResponse($response, $enqueue);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5022
  }
5023
  } else {
5024
+ $error_msg = 'Could not determine the status of an API call.';
5025
  }
5026
  } else {
5027
+ $message = 'non JSON-encoded response.';
5028
 
5029
+ if (isset($response['response'])
 
5030
  && isset($response['response']['message'])
5031
  && isset($response['response']['code'])
5032
  && $response['response']['code'] !== 200
5033
  ) {
5034
+ $message = sprintf(
5035
  '(%s) %s',
5036
  $response['response']['code'],
5037
  $response['response']['message']
5038
  );
5039
  }
5040
 
5041
+ $error_msg = 'Malformed API response: ' . $message;
5042
  }
5043
  }
5044
 
5045
+ if (!empty($error_msg) && $enqueue) {
5046
+ SucuriScanInterface::error($error_msg);
5047
+ }
5048
+
5049
  return false;
5050
  }
5051
 
5052
+ /**
5053
+ * Process failures in the HTTP response.
5054
+ *
5055
+ * Log file not found: means that the API key used to execute the request is
5056
+ * not associated to the website, this may indicate that either the key was
5057
+ * invalidated by an administrator of the service or that the API key was
5058
+ * custom generated with invalid data.
5059
+ *
5060
+ * Wrong API key: means that the TLD of the origin of the request is not the
5061
+ * domain used to generate the API key in the first place, or that the email
5062
+ * address of the site administrator was changed so the data is not valid
5063
+ * anymore.
5064
+ *
5065
+ * Connection timeout: means that the API service is down either because the
5066
+ * hosting provider has connectivity issues or because the code is being
5067
+ * deployed. There is an option in the settings page that allows to temporarily
5068
+ * disable the communication with the API service while the server is down, this
5069
+ * allows the admins to keep the latency at zero and continue working in their
5070
+ * websites without interruptions.
5071
+ *
5072
+ * SSL issues: depending on the options used to compile the OpenSSL library
5073
+ * built by each hosting provider, the connection with the HTTPs version of the
5074
+ * API service may be rejected because of a failure in the SSL algorithm check.
5075
+ * There is an option in the settings page that allows to disable the SSL pair
5076
+ * verification, this option it disable automatically when the error is detected
5077
+ * for the first time.
5078
+ *
5079
+ * @param array $response HTTP response after API endpoint execution.
5080
+ * @param boolean $enqueue Add the log to the local queue on a failure.
5081
+ * @return boolean False if the API call failed, true otherwise.
5082
+ */
5083
+ private static function handleErrorResponse($response = array(), $enqueue = true)
5084
+ {
5085
+ $action_message = 'Unknown error, there is no more information.';
5086
+
5087
+ // Check whether the message list is empty or not.
5088
+ if (isset($response['body']->messages[0])) {
5089
+ $action_message = $response['body']->messages[0] . '.';
5090
+ }
5091
+
5092
+ // Keep a copy of the original API response message.
5093
+ $raw_message = $action_message;
5094
+
5095
+ // Special response for invalid API keys.
5096
+ if (stripos($raw_message, 'log file not found') !== false) {
5097
+ SucuriScanOption::delete_option(':api_key');
5098
+
5099
+ $action_message .= ' This generally happens when you add an invalid API key, the'
5100
+ . ' key will be deleted automatically to hide these warnings, if you want to'
5101
+ . ' recover it go to the settings page and use the recover button to send the'
5102
+ . ' key to your email address.';
5103
+ }
5104
+
5105
+ // Special response for invalid CloudProxy API keys.
5106
+ if (stripos($raw_message, 'wrong api key') !== false) {
5107
+ SucuriScanOption::delete_option(':cloudproxy_apikey');
5108
+ SucuriScanOption::setRevProxy('disable');
5109
+ SucuriScanOption::setAddrHeader('REMOTE_ADDR');
5110
+
5111
+ $action_message .= ' The CloudProxy API key does not seems to be valid.';
5112
+ }
5113
+
5114
+ // Special response for connection timeouts.
5115
+ if ($enqueue && @preg_match('/time(d\s)?out/', $raw_message)) {
5116
+ $action_message = ''; /* Empty the error message. */
5117
+ $cache = new SucuriScanCache('auditqueue');
5118
+ $cache_key = md5($response['params']['time']);
5119
+ $cache_value = array(
5120
+ 'created_at' => $response['params']['time'],
5121
+ 'message' => $response['params']['m'],
5122
+ );
5123
+ $cache->add($cache_key, $cache_value);
5124
+ }
5125
+
5126
+ // Stop SSL peer verification on connection failures.
5127
+ if (stripos($raw_message, 'no alternative certificate')
5128
+ || stripos($raw_message, 'error setting certificate')
5129
+ || stripos($raw_message, 'SSL connect error')
5130
+ ) {
5131
+ SucuriScanOption::update_option(':verify_ssl_cert', 'false');
5132
+
5133
+ $action_message .= 'There were some issues with the SSL certificate either in this'
5134
+ . ' server or with the remote API service. The automatic verification of the'
5135
+ . ' certificates has been deactivated to reduce the noise during the execution'
5136
+ . ' of the HTTP requests.';
5137
+ }
5138
+
5139
+ if (!empty($action_message)) {
5140
+ if ($enqueue) {
5141
+ SucuriScanInterface::error(
5142
+ sprintf(
5143
+ '(%d) %s: %s',
5144
+ SucuriScan::local_time(),
5145
+ ucwords($response['body']->action),
5146
+ $action_message
5147
+ )
5148
+ );
5149
+ }
5150
+
5151
+ return false;
5152
+ }
5153
+
5154
+ return true;
5155
+ }
5156
+
5157
  /**
5158
  * Send a request to the API to register this site.
5159
  *
5160
  * @param string $email Optional email address for the registration.
5161
  * @return boolean True if the API key was generated, false otherwise.
5162
  */
5163
+ public static function registerSite($email = '')
5164
+ {
5165
+ if (!is_string($email) || empty($email)) {
5166
  $email = self::get_site_email();
5167
  }
5168
 
5169
+ $response = self::apiCallWordpress('POST', array(
5170
  'e' => $email,
5171
  's' => self::get_domain(),
5172
  'a' => 'register_site',
5173
+ ), false);
5174
 
5175
+ if (self::handleResponse($response)) {
5176
+ self::setPluginKey($response['body']->output->api_key);
5177
  SucuriScanEvent::schedule_task();
5178
+ SucuriScanEvent::notify_event('plugin_change', 'Site registered and API key generated');
5179
+ SucuriScanInterface::info('The API key for your site was successfully generated and saved.');
5180
 
5181
  return true;
5182
  }
5189
  *
5190
  * @return boolean true if the API key was sent to the administrator email, false otherwise.
5191
  */
5192
+ public static function recoverKey()
5193
+ {
5194
  $clean_domain = self::get_domain();
5195
 
5196
+ $response = self::apiCallWordpress('GET', array(
5197
  'e' => self::get_site_email(),
5198
  's' => $clean_domain,
5199
  'a' => 'recover_key',
5200
+ ), false);
5201
 
5202
+ if (self::handleResponse($response)) {
5203
+ SucuriScanEvent::notify_event('plugin_change', 'API key recovered for domain: ' . $clean_domain);
5204
+ SucuriScanInterface::info($response['body']->output->message);
5205
 
5206
  return true;
5207
  }
5215
  * settings or files in the administrator panel, or a notification generated by
5216
  * this plugin.
5217
  *
5218
+ * @param string $event Event triggered by the core system functions.
5219
+ * @param integer $time Timestamp when the event was originally triggered.
5220
+ * @param boolean $enqueue Add the log to the local queue on a failure.
5221
+ * @return boolean True if the event was logged, false otherwise.
5222
  */
5223
+ public static function sendLog($event = '', $time = 0, $enqueue = true)
5224
+ {
5225
+ if (!empty($event)) {
5226
+ $params = array();
5227
+ $params['a'] = 'send_log';
5228
+ $params['m'] = $event;
5229
+
5230
+ if (intval($time) > 0) {
5231
+ $params['time'] = (int) $time;
5232
+ }
5233
+
5234
+ $response = self::apiCallWordpress('POST', $params, true);
5235
 
5236
+ if (self::handleResponse($response, $enqueue)) {
5237
  return true;
5238
  }
5239
  }
5241
  return false;
5242
  }
5243
 
5244
+ /**
5245
+ * Send all logs from the queue.
5246
+ *
5247
+ * Retry the HTTP calls for the logs that were not sent to the API service
5248
+ * because of a connection failure or misconfiguration. Each successful call
5249
+ * will remove the log from the queue and the failures will keep them until the
5250
+ * next function call is executed.
5251
+ *
5252
+ * @return void
5253
+ */
5254
+ public static function sendLogsFromQueue()
5255
+ {
5256
+ $cache = new SucuriScanCache('auditqueue');
5257
+ $entries = $cache->getAll();
5258
+
5259
+ if (is_array($entries) && !empty($entries)) {
5260
+ foreach ($entries as $key => $entry) {
5261
+ $result = self::sendLog(
5262
+ $entry->message,
5263
+ $entry->created_at,
5264
+ false
5265
+ );
5266
+
5267
+ if ($result === true) {
5268
+ $cache->delete($key);
5269
+ } else {
5270
+ /**
5271
+ * Stop loop on failures.
5272
+ *
5273
+ * If the log was successfully sent to the API service then we can continue
5274
+ * sending the other logs in the queue, otherwise the operation must be stopped
5275
+ * so it can be executed next time when the service is online, not stopping the
5276
+ * operation when one or more of the API calls fails will cause a very long
5277
+ * delay in the load of the page that is being requested.
5278
+ */
5279
+ break;
5280
+ }
5281
+ }
5282
+ }
5283
+ }
5284
+
5285
  /**
5286
  * Retrieve all the event logs registered by the API service.
5287
  *
5288
  * @return array The object with the data returned from the API service.
5289
  */
5290
+ public static function getAllLogs()
5291
+ {
5292
  // Get the total number of lines in the logs.
5293
+ $response = self::apiCallWordpress('GET', array(
5294
  'a' => 'get_logs',
5295
  'l' => 0,
5296
+ ));
5297
 
5298
  // If success continue with the retrieval of the logs data.
5299
+ if (self::handleResponse($response)) {
5300
+ return self::getLogs($response['body']->total_entries);
5301
  }
5302
 
5303
  return false;
5309
  * @param integer $lines How many lines from the log file will be retrieved.
5310
  * @return string The response of the API service.
5311
  */
5312
+ public static function getLogs($lines = 50)
5313
+ {
5314
+ $response = self::apiCallWordpress('GET', array(
5315
  'a' => 'get_logs',
5316
  'l' => $lines,
5317
+ ));
5318
 
5319
+ if (self::handleResponse($response)) {
5320
  $response['body']->output_data = array();
5321
  $log_pattern = '/^([0-9\-]+) ([0-9:]+) (\S+) : (.+)/';
5322
  $extra_pattern = '/(.+ \(multiple entries\):) (.+)/';
5323
+ $generic_pattern = '/^@?([A-Z][a-z]{3,7}): ([^;]+; )?(.+)/';
5324
  $auth_pattern = '/^User authentication (succeeded|failed): ([^<;]+)/';
5325
 
5326
+ foreach ($response['body']->output as $log) {
5327
+ if (@preg_match($log_pattern, $log, $log_match)) {
5328
  $log_data = array(
5329
  'event' => 'notice',
5330
  'date' => '',
5333
  'timestamp' => 0,
5334
  'account' => $log_match[3],
5335
  'username' => 'system',
5336
+ 'remote_addr' => '127.0.0.1',
5337
  'message' => $log_match[4],
5338
  'file_list' => false,
5339
  'file_list_count' => 0,
5340
  );
5341
 
5342
  // Extract and fix the date and time using the Eastern time zone.
5343
+ $datetime = sprintf('%s %s EDT', $log_match[1], $log_match[2]);
5344
+ $log_data['timestamp'] = strtotime($datetime);
5345
+ $log_data['datetime'] = date('Y-m-d H:i:s', $log_data['timestamp']);
5346
+ $log_data['date'] = date('Y-m-d', $log_data['timestamp']);
5347
+ $log_data['time'] = date('H:i:s', $log_data['timestamp']);
5348
 
5349
  // Extract more information from the generic audit logs.
5350
+ $log_data['message'] = str_replace('<br>', '; ', $log_data['message']);
5351
 
5352
+ if (@preg_match($generic_pattern, $log_data['message'], $log_extra)) {
5353
+ $log_data['event'] = strtolower($log_extra[1]);
5354
+ $log_data['message'] = trim($log_extra[3]);
5355
 
5356
  // Extract the username and remote address from the log.
5357
+ if (!empty($log_extra[2])) {
5358
+ $username_address = rtrim($log_extra[2], ";\x20");
5359
 
5360
  // Separate the username from the remote address.
5361
+ if (strpos($username_address, ",\x20") !== false) {
5362
+ $usip_parts = explode(",\x20", $username_address, 2);
5363
 
5364
+ if (count($usip_parts) == 2) {
5365
  // Separate the username from the display name.
5366
+ $log_data['username'] = @preg_replace('/^.+ \((.+)\)$/', '$1', $usip_parts[0]);
5367
  $log_data['remote_addr'] = $usip_parts[1];
5368
  }
5369
  } else {
5378
  $log_data['message']
5379
  );
5380
 
5381
+ if (@preg_match($auth_pattern, $log_data['message'], $user_match)) {
5382
  $log_data['username'] = $user_match[2];
5383
  }
5384
  }
5385
 
5386
  // Extract more information from the special formatted logs.
5387
+ if (@preg_match($extra_pattern, $log_data['message'], $log_extra)) {
5388
  $log_data['message'] = $log_extra[1];
5389
+ $log_extra[2] = str_replace(', new size', '; new size', $log_extra[2]);
5390
+ $log_extra[2] = str_replace(",\x20", ";\x20", $log_extra[2]);
5391
+ $log_data['file_list'] = explode(',', $log_extra[2]);
5392
+ $log_data['file_list_count'] = count($log_data['file_list']);
5393
  }
5394
 
5395
  $response['body']->output_data[] = $log_data;
5407
  *
5408
  * @return array Valid audit event types with their colors.
5409
  */
5410
+ public static function getAuditEventTypes()
5411
+ {
5412
+ return array(
5413
  'critical' => '#000000',
5414
  'debug' => '#c690ec',
5415
  'error' => '#f27d7d',
5417
  'notice' => '#428bca',
5418
  'warning' => '#f0ad4e',
5419
  );
 
 
5420
  }
5421
 
5422
  /**
5425
  * @param string $event_log Event log that will be processed.
5426
  * @return array List of parts of the event log.
5427
  */
5428
+ public static function parseMultipleEntries($event_log = '')
5429
+ {
5430
+ if (@preg_match('/^(.*:\s)\(multiple entries\):\s(.+)/', $event_log, $match)) {
5431
  $event_log = array();
5432
+ $event_log[] = trim($match[1]);
5433
+ $grouped_items = @explode(',', $match[2]);
5434
+ $event_log = array_merge($event_log, $grouped_items);
5435
  }
5436
 
5437
  return $event_log;
5443
  * @param integer $lines How many lines from the log file will be retrieved.
5444
  * @return array All the information necessary to display the audit logs report.
5445
  */
5446
+ public static function getAuditReport($lines = 50)
5447
+ {
5448
+ $audit_logs = self::getLogs($lines);
5449
 
5450
+ if ($audit_logs instanceof stdClass
5451
+ && property_exists($audit_logs, 'total_entries')
5452
+ && property_exists($audit_logs, 'output_data')
5453
+ && !empty($audit_logs->output_data)
 
5454
  ) {
5455
  // Data structure that will be returned.
5456
  $report = array(
5468
  );
5469
 
5470
  // Get a list of valid audit event types.
5471
+ $event_types = self::getAuditEventTypes();
5472
+ foreach ($event_types as $event => $event_color) {
5473
+ $report['events_per_type'][$event] = 0;
5474
+ $report['event_colors'][] = sprintf("'%s'", $event_color);
5475
  }
5476
 
5477
  // Collect information for each report chart.
5478
+ foreach ($audit_logs->output_data as $event) {
5479
  $report['total_events'] += 1;
5480
 
5481
  // Increment the number of events for this event type.
5482
+ if (array_key_exists($event['event'], $report['events_per_type'])) {
5483
  $report['events_per_type'][ $event['event'] ] += 1;
5484
  } else {
5485
  $report['events_per_type'][ $event['event'] ] = 1;
5486
  }
5487
 
5488
  // Find the lowest datetime among the filtered events.
5489
+ if ($event['timestamp'] <= $report['start_timestamp']
 
5490
  || $report['start_timestamp'] === 0
5491
  ) {
5492
  $report['start_timestamp'] = $event['timestamp'];
5493
  }
5494
 
5495
  // Find the highest datetime among the filtered events.
5496
+ if ($event['timestamp'] >= $report['end_timestamp']) {
5497
  $report['end_timestamp'] = $event['timestamp'];
5498
  }
5499
 
5500
  // Increment the number of events generated by this user account.
5501
+ $_username = SucuriScan::escape($event['username']);
5502
+ if (array_key_exists($_username, $report['events_per_user'])) {
5503
+ $report['events_per_user'][$_username] += 1;
5504
  } else {
5505
+ $report['events_per_user'][$_username] = 1;
5506
  }
5507
 
5508
  // Increment the number of events generated from this remote address.
5509
+ $_remote_addr = SucuriScan::escape($event['remote_addr']);
5510
+ if (array_key_exists($_remote_addr, $report['events_per_ipaddress'])) {
5511
+ $report['events_per_ipaddress'][$_remote_addr] += 1;
5512
  } else {
5513
+ $report['events_per_ipaddress'][$_remote_addr] = 1;
5514
  }
5515
 
5516
  // Detect successful and failed user authentications.
5517
  $auth_pattern = '/^User authentication (succeeded|failed):/';
5518
 
5519
+ if (@preg_match($auth_pattern, $event['message'], $match)) {
5520
+ if ($match[1] == 'succeeded') {
5521
  $report['events_per_login']['successful'] += 1;
5522
  } else {
5523
  $report['events_per_login']['failed'] += 1;
5524
  }
5525
+ } elseif (@preg_match('/^User logged in:/', $event['message'])) {
5526
  // Backward compatibility for previous user login messages.
5527
  $report['events_per_login']['successful'] += 1;
5528
  }
5529
  }
5530
 
5531
+ if ($report['total_events'] > 0) {
5532
  return $report;
5533
  }
5534
  }
5545
  * @param string $hashes The information gathered after the scanning of the site's files.
5546
  * @return boolean true if the hashes were stored, false otherwise.
5547
  */
5548
+ public static function sendHashes($hashes = '')
5549
+ {
5550
+ if (!empty($hashes)) {
5551
+ $response = self::apiCallWordpress('POST', array(
5552
  'a' => 'send_hashes',
5553
  'h' => $hashes,
5554
+ ));
5555
 
5556
+ if (self::handleResponse($response)) {
5557
  return true;
5558
  }
5559
  }
5570
  * @param boolean $api_key The CloudProxy API key.
5571
  * @return array A hash with the settings of a CloudProxy account.
5572
  */
5573
+ public static function getCloudproxySettings($api_key = false)
5574
+ {
5575
+ $params = array('a' => 'show_settings');
5576
 
5577
+ if ($api_key) {
5578
+ $params = array_merge($params, $api_key);
5579
  }
5580
 
5581
+ $response = self::apiCallCloudproxy('GET', $params);
5582
 
5583
+ if (self::handleResponse($response)) {
5584
  return $response['body']->output;
5585
  }
5586
 
5593
  * @param boolean $api_key The CloudProxy API key.
5594
  * @return string Message explaining the result of the operation.
5595
  */
5596
+ public static function clearCloudproxyCache($api_key = false)
5597
+ {
5598
  $params = array( 'a' => 'clear_cache' );
5599
 
5600
+ if ($api_key) {
5601
+ $params = array_merge($params, $api_key);
5602
  }
5603
 
5604
+ $response = self::apiCallCloudproxy('GET', $params);
5605
 
5606
+ if (self::handleResponse($response)) {
5607
  return $response['body'];
5608
  }
5609
 
5621
  * the logs of previous days you will need to add a new parameter to the request
5622
  * URL named "date" with format yyyy-mm-dd.
5623
  *
5624
+ * @param string $api_key The CloudProxy API key.
5625
+ * @param string $date Retrieve the data from this date.
5626
+ * @param string $query Filter the data to match this query.
5627
+ * @param integer $limit Retrieve this maximum of data.
5628
+ * @param integer $offset Retrieve the data from this point.
5629
+ * @return array Objects with details of each blocked request.
5630
  */
5631
+ public static function firewallAuditLogs($api_key, $date = '', $query = '', $limit = 10, $offset = 0)
5632
+ {
5633
  $params = array(
5634
  'a' => 'audit_trails',
5635
+ 'date' => $date,
5636
+ 'query' => $query,
5637
+ 'limit' => $limit,
5638
+ 'offset' => $offset,
5639
  );
5640
 
5641
+ if (is_array($api_key) && !empty($api_key)) {
5642
+ $params = array_merge($params, $api_key);
 
 
 
 
5643
  }
5644
 
5645
+ $response = self::apiCallCloudproxy('GET', $params);
5646
 
5647
+ if (self::handleResponse($response)) {
5648
+ return $response['body_arr']['output'];
5649
  }
5650
 
5651
  return false;
5655
  * Scan a website through the public SiteCheck API [1] for known malware,
5656
  * blacklisting status, website errors, and out-of-date software.
5657
  *
5658
+ * [1] https://sitecheck.sucuri.net/
5659
  *
5660
  * @param string $domain The clean version of the website's domain.
5661
  * @return object Serialized data of the scanning results for the site specified.
5662
  */
5663
+ public static function getSitecheckResults($domain = '')
5664
+ {
5665
+ if (!empty($domain)) {
5666
+ $url = 'https://sitecheck.sucuri.net/';
5667
+ $response = self::apiCall(
5668
  $url,
5669
  'GET',
5670
  array(
5678
  )
5679
  );
5680
 
5681
+ if ($response) {
5682
  return $response['body'];
5683
  }
5684
  }
5692
  * @param array $malware Array with two entries with basic malware information.
5693
  * @return array Detailed information of the malware found by SiteCheck.
5694
  */
5695
+ public static function getSitecheckMalware($malware = array())
5696
+ {
5697
+ if (count($malware) >= 2) {
5698
  $data_set = array(
5699
  'alert_message' => '',
5700
  'infected_url' => '',
5704
  );
5705
 
5706
  // Extract the information from the alert message.
5707
+ $alert_parts = explode(':', $malware[0], 2);
5708
 
5709
+ if (isset($alert_parts[1])) {
5710
  $data_set['alert_message'] = $alert_parts[0];
5711
  $data_set['infected_url'] = $alert_parts[1];
5712
  }
5713
 
5714
  // Extract the information from the malware message.
5715
+ $malware_parts = explode("\n", $malware[1]);
5716
 
5717
+ if (isset($malware_parts[1])) {
5718
+ if (@preg_match('/(.+)\. Details: (.+)/', $malware_parts[0], $match)) {
5719
  $data_set['malware_type'] = $match[1];
5720
  $data_set['malware_docs'] = $match[2];
5721
  }
5722
 
5723
+ $payload = trim($malware_parts[1]);
5724
+ $payload = html_entity_decode($payload);
5725
 
5726
+ if (@preg_match('/<div id=\'HiddenDiv\'>(.+)<\/div>/', $payload, $match)) {
5727
+ $data_set['malware_payload'] = trim($match[1]);
5728
  }
5729
  }
5730
 
5740
  *
5741
  * @return array A list of the new set of keys generated by WordPress API.
5742
  */
5743
+ public static function getNewSecretKeys()
5744
+ {
5745
  $pattern = self::secret_key_pattern();
5746
+ $response = self::apiCall('https://api.wordpress.org/secret-key/1.1/salt/', 'GET');
5747
 
5748
+ if ($response && @preg_match_all($pattern, $response['body'], $match)) {
5749
  $new_keys = array();
5750
 
5751
+ foreach ($match[1] as $i => $value) {
5752
+ $new_keys[$value] = $match[3][$i];
5753
  }
5754
 
5755
  return $new_keys;
5761
  /**
5762
  * Retrieve a list with the checksums of the files in a specific version of WordPress.
5763
  *
5764
+ * @see Release Archive https://wordpress.org/download/release-archive/
5765
  *
5766
  * @param integer $version Valid version number of the WordPress project.
5767
  * @return object Associative object with the relative filepath and the checksums of the project files.
5768
  */
5769
+ public static function getOfficialChecksums($version = 0)
5770
+ {
5771
+ $url = 'https://api.wordpress.org/core/checksums/1.0/';
5772
  $language = 'en_US'; /* WPLANG does not works. */
5773
+ $response = self::apiCall($url, 'GET', array(
5774
  'version' => $version,
5775
  'locale' => $language,
5776
  ));
5777
 
5778
+ if ($response) {
5779
+ if ($response['body'] instanceof stdClass) {
5780
  $json_data = $response['body'];
5781
  } else {
5782
+ $json_data = @json_decode($response['body']);
5783
  }
5784
 
5785
+ if (isset($json_data->checksums)
5786
+ && !empty($json_data->checksums)
 
5787
  ) {
5788
+ if (count((array) $json_data->checksums) <= 1
5789
+ && property_exists($json_data->checksums, $version)
 
5790
  ) {
5791
  $checksums = $json_data->checksums->{$version};
5792
  } else {
5794
  }
5795
 
5796
  // Check whether the list of file is an object.
5797
+ if ($checksums instanceof stdClass) {
5798
  return (array) $checksums;
5799
  }
5800
  }
5810
  *
5811
  * @return array Key is the plugin file path and the value is an array of the plugin data.
5812
  */
5813
+ public static function getPlugins()
5814
+ {
5815
  // Check if the cache library was loaded.
5816
+ $can_cache = class_exists('SucuriScanCache');
5817
 
5818
+ if ($can_cache) {
5819
+ $cache = new SucuriScanCache('plugindata');
5820
+ $cached_data = $cache->get('plugins', SUCURISCAN_GET_PLUGINS_LIFETIME, 'array');
5821
 
5822
  // Return the previously cached results of this function.
5823
+ if ($cached_data !== false) {
5824
  return $cached_data;
5825
  }
5826
  }
5831
  $wp_market = 'https://wordpress.org/plugins/%s/';
5832
 
5833
  // Loop through each plugin data and complement its information with more attributes.
5834
+ foreach ($plugins as $plugin_path => $plugin_data) {
5835
  // Default values for the plugin extra attributes.
5836
  $repository = '';
5837
  $repository_name = '';
5844
  * official WordPress server it means that it is premium and is being
5845
  * distributed by an independent developer.
5846
  */
5847
+ if (isset($plugin_data['PluginURI'])
5848
+ && preg_match($pattern, $plugin_data['PluginURI'], $match)
 
5849
  ) {
5850
  $repository = $match[0];
5851
  $repository_name = $match[2];
5852
  $is_free_plugin = true;
5853
  } else {
5854
+ if (strpos($plugin_path, '/') !== false) {
5855
+ $plugin_path_parts = explode('/', $plugin_path, 2);
5856
  } else {
5857
+ $plugin_path_parts = explode('.', $plugin_path, 2);
5858
  }
5859
 
5860
+ if (isset($plugin_path_parts[0])) {
5861
+ $possible_repository = sprintf($wp_market, $plugin_path_parts[0]);
5862
+ $resp = wp_remote_head($possible_repository);
5863
 
5864
+ if (!is_wp_error($resp)
 
5865
  && $resp['response']['code'] == 200
5866
  ) {
5867
  $repository = $possible_repository;
5872
  }
5873
 
5874
  // Complement the plugin's information with these attributes.
5875
+ $plugins[$plugin_path]['Repository'] = $repository;
5876
+ $plugins[$plugin_path]['RepositoryName'] = $repository_name;
5877
+ $plugins[$plugin_path]['InstallationPath'] = sprintf('%s/%s', WP_PLUGIN_DIR, $repository_name);
5878
+ $plugins[$plugin_path]['IsFreePlugin'] = $is_free_plugin;
5879
+ $plugins[$plugin_path]['PluginType'] = ( $is_free_plugin ? 'free' : 'premium' );
5880
+ $plugins[$plugin_path]['IsPluginActive'] = false;
5881
+ $plugins[$plugin_path]['IsPluginInstalled'] = false;
5882
 
5883
+ if (is_plugin_active($plugin_path)) {
5884
+ $plugins[$plugin_path]['IsPluginActive'] = true;
5885
  }
5886
 
5887
+ if (is_dir($plugins[$plugin_path]['InstallationPath'])) {
5888
+ $plugins[$plugin_path]['IsPluginInstalled'] = true;
5889
  }
5890
  }
5891
 
5892
+ if ($can_cache) {
5893
  // Add the information of the plugins to the file-based cache.
5894
+ $cache->add('plugins', $plugins);
5895
  }
5896
 
5897
  return $plugins;
5914
  * @param string $plugin Frienly name of the plugin.
5915
  * @return object Object on success, WP_Error on failure.
5916
  */
5917
+ public static function getRemotePluginData($plugin = '')
5918
+ {
5919
+ if (!empty($plugin)) {
5920
+ $url = sprintf('https://api.wordpress.org/plugins/info/1.0/%s.json', $plugin);
5921
+ $response = self::apiCall($url, 'GET');
5922
 
5923
+ if ($response) {
5924
+ if ($response['body'] instanceof stdClass) {
5925
  return $response['body'];
5926
  }
5927
  }
5935
  * the content of the file is determined by the tags defined using the site
5936
  * version specified. Only official core files are allowed to fetch.
5937
  *
5938
+ * @see https://core.svn.wordpress.org/
5939
+ * @see https://i18n.svn.wordpress.org/
5940
+ * @see https://core.svn.wordpress.org/tags/VERSION_NUMBER/
5941
  *
5942
  * @param string $filepath Relative file path of a project core file.
5943
  * @param string $version Optional site version, default will be the global version number.
5944
  * @return string Full content of the official file retrieved, false if the file was not found.
5945
  */
5946
+ public static function getOriginalCoreFile($filepath = '', $version = 0)
5947
+ {
5948
+ if (!empty($filepath)) {
5949
+ if ($version == 0) {
5950
  $version = self::site_version();
5951
  }
5952
 
5953
+ $url = sprintf('https://core.svn.wordpress.org/tags/%s/%s', $version, $filepath);
5954
+ $response = self::apiCall($url, 'GET');
5955
 
5956
+ if ($response) {
5957
+ if (isset($response['headers']['content-length'])
 
5958
  && $response['headers']['content-length'] > 0
5959
+ && is_string($response['body'])
5960
  ) {
5961
  return $response['body'];
5962
  }
5965
 
5966
  return false;
5967
  }
 
5968
  }
5969
 
5970
  /**
5975
  * will be sent to the site email address (an address that can be configured in
5976
  * the settings page).
5977
  */
5978
+ class SucuriScanMail extends SucuriScanOption
5979
+ {
5980
 
5981
  /**
5982
  * Check whether the email notifications will be sent in HTML or Plain/Text.
5983
  *
5984
  * @return boolean Whether the emails will be in HTML or Plain/Text.
5985
  */
5986
+ public static function prettify_mails()
5987
+ {
5988
+ return self::is_enabled(':prettify_mails');
5989
  }
5990
 
5991
  /**
5997
  * @param array $data_set Optional parameter to add more information to the notification.
5998
  * @return boolean Whether the email contents were sent successfully.
5999
  */
6000
+ public static function send_mail($email = '', $subject = '', $message = '', $data_set = array())
6001
+ {
6002
  $headers = array();
6003
+ $subject = ucwords(strtolower($subject));
6004
  $force = false;
6005
  $debug = false;
6006
 
6007
  // Check whether the mail will be printed in the site instead of sent.
6008
+ if (isset($data_set['Debug'])
 
6009
  && $data_set['Debug'] == true
6010
  ) {
6011
  $debug = true;
6013
  }
6014
 
6015
  // Check whether the mail will be even if the limit per hour was reached or not.
6016
+ if (isset($data_set['Force'])
 
6017
  && $data_set['Force'] == true
6018
  ) {
6019
  $force = true;
6021
  }
6022
 
6023
  // Check whether the email notifications will be sent in HTML or Plain/Text.
6024
+ if (self::prettify_mails()) {
6025
  $headers = array( 'content-type: text/html' );
6026
  $data_set['PrettifyType'] = 'pretty';
6027
  } else {
6028
+ $message = strip_tags($message);
6029
  }
6030
 
6031
+ if (!self::emails_per_hour_reached() || $force || $debug) {
6032
+ $message = self::prettify_mail($subject, $message, $data_set);
6033
 
6034
+ if ($debug) {
6035
  die($message);
6036
  }
6037
 
6038
+ $subject = self::get_email_subject($subject);
6039
 
6040
  /**
6041
  * WordPress uses a library named PHPMailer [1] to send emails through the
6051
  *
6052
  * @var boolean
6053
  */
6054
+ if (SucuriScanOption::is_enabled(':use_wpmail')) {
6055
+ $mail_sent = wp_mail($email, $subject, $message, $headers);
6056
  } else {
6057
+ $headers = implode("\r\n", $headers);
6058
+ $mail_sent = @mail($email, $subject, $message, $headers);
6059
  }
6060
 
6061
+ if ($mail_sent) {
6062
+ $emails_sent_num = (int) self::get_option(':emails_sent');
6063
+ self::update_option(':emails_sent', $emails_sent_num + 1);
6064
+ self::update_option(':last_email_at', time());
6065
 
6066
  return true;
6067
  }
6076
  * @param string $event The reason of the message that will be sent.
6077
  * @return string A text with the subject for the email alert.
6078
  */
6079
+ private static function get_email_subject($event = '')
6080
+ {
6081
+ $subject = self::get_option(':email_subject');
6082
 
6083
  /**
6084
  * Probably a bad value in the options table. Delete the entry from the database
6085
  * and call this function to try again, it will probably fall in an infinite
6086
  * loop, but this is the easiest way to control this procedure.
6087
  */
6088
+ if (!$subject) {
6089
+ self::delete_option(':email_subject');
6090
 
6091
+ return self::get_email_subject($event);
6092
  }
6093
 
6094
+ $subject = strip_tags($subject);
6095
+ $subject = str_replace(':event', $event, $subject);
6096
+ $subject = str_replace(':domain', self::get_domain(), $subject);
6097
+ $subject = str_replace(':remoteaddr', self::get_remote_addr(), $subject);
6098
 
6099
  /**
6100
  * Extract user data from the current session.
6103
  * the username and/or email address are necessary to build the email subject,
6104
  * otherwise this operation may delay the sending of the alerts.
6105
  */
6106
+ if (preg_match('/:(username|email)/', $subject)) {
6107
  $user = wp_get_current_user();
6108
  $username = 'unknown';
6109
  $eaddress = 'unknown';
6110
 
6111
+ if ($user instanceof WP_User
6112
+ && isset($user->user_login)
6113
+ && isset($user->user_email)
 
6114
  ) {
6115
  $username = $user->user_login;
6116
  $eaddress = $user->user_email;
6117
  }
6118
 
6119
+ $subject = str_replace(':username', $user->user_login, $subject);
6120
+ $subject = str_replace(':email', $user->user_email, $subject);
6121
  }
6122
 
6123
  return $subject;
6131
  * @param array $data_set Optional parameter to add more information to the notification.
6132
  * @return string The message formatted in a HTML template.
6133
  */
6134
+ private static function prettify_mail($subject = '', $message = '', $data_set = array())
6135
+ {
6136
  $prettify_type = isset($data_set['PrettifyType']) ? $data_set['PrettifyType'] : 'simple';
6137
  $template_name = 'notification-' . $prettify_type;
6138
  $user = wp_get_current_user();
6139
  $display_name = '';
6140
 
6141
+ if ($user instanceof WP_User
 
6142
  && isset($user->user_login)
6143
+ && !empty($user->user_login)
6144
  ) {
6145
+ $display_name = sprintf('User: %s (%s)', $user->display_name, $user->user_login);
6146
  }
6147
 
6148
  // Format list of items when the event has multiple entries.
6149
+ if (strpos($message, 'multiple') !== false) {
6150
+ $message_parts = SucuriScanAPI::parseMultipleEntries($message);
6151
 
6152
+ if (is_array($message_parts)) {
6153
  $message = ( $prettify_type == 'pretty' ) ? $message_parts[0] . '<ul>' : $message_parts[0];
6154
  unset($message_parts[0]);
6155
 
6156
+ foreach ($message_parts as $msg_part) {
6157
+ if ($prettify_type == 'pretty') {
6158
+ $message .= sprintf("<li>%s</li>\n", $msg_part);
6159
  } else {
6160
+ $message .= sprintf("- %s\n", $msg_part);
6161
  }
6162
  }
6163
 
6168
  $mail_variables = array(
6169
  'TemplateTitle' => 'Sucuri Alert',
6170
  'Subject' => $subject,
6171
+ 'Website' => self::get_option('siteurl'),
6172
  'RemoteAddress' => self::get_remote_addr(),
6173
  'Message' => $message,
6174
  'User' => $display_name,
6175
  'Time' => SucuriScan::current_datetime(),
6176
  );
6177
 
6178
+ foreach ($data_set as $var_key => $var_value) {
6179
  $mail_variables[ $var_key ] = $var_value;
6180
  }
6181
 
6182
+ return SucuriScanTemplate::getSection($template_name, $mail_variables);
6183
  }
6184
 
6185
  /**
6187
  *
6188
  * @return boolean Whether the quota emails per hour was reached.
6189
  */
6190
+ private static function emails_per_hour_reached()
6191
+ {
6192
+ $max_per_hour = self::get_option(':emails_per_hour');
6193
 
6194
+ if ($max_per_hour != 'unlimited') {
6195
  // Check if we are still in that sixty minutes.
6196
  $current_time = time();
6197
+ $last_email_at = self::get_option(':last_email_at');
6198
+ $diff_time = abs($current_time - $last_email_at);
6199
 
6200
+ if ($diff_time <= 3600) {
6201
  // Check if the quantity of emails sent is bigger than the configured.
6202
+ $emails_sent = (int) self::get_option(':emails_sent');
6203
+ $max_per_hour = intval($max_per_hour);
6204
 
6205
+ if ($emails_sent >= $max_per_hour) {
6206
  return true;
6207
  }
6208
  } else {
6209
  // Reset the counter of emails sent.
6210
+ self::update_option(':emails_sent', 0);
6211
  }
6212
  }
6213
 
6214
  return false;
6215
  }
 
6216
  }
6217
 
6218
  /**
6228
  * generate a large number of "static" (unchanging) web pages in advance, or to
6229
  * produce "dynamic" web pages on demand.
6230
  */
6231
+ class SucuriScanTemplate extends SucuriScanRequest
6232
+ {
6233
  /**
6234
  * Replace all pseudo-variables from a string of characters.
6235
  *
6237
  * @param array $params List of pseudo-variables that will be replaced in the template.
6238
  * @return string The content of the template with the pseudo-variables replated.
6239
  */
6240
+ private static function replacePseudoVars($content = '', $params = array())
6241
+ {
6242
+ if (is_array($params)) {
6243
+ foreach ($params as $keyname => $kvalue) {
6244
+ $tplkey = 'SUCURI.' . $keyname;
6245
+ $with_escape = '%%' . $tplkey . '%%';
6246
+ $wout_escape = '%%%' . $tplkey . '%%%';
6247
+
6248
+ if (strpos($content, $wout_escape) !== false) {
6249
+ $content = str_replace($wout_escape, $kvalue, $content);
6250
+ } elseif (strpos($content, $with_escape) !== false) {
6251
+ $kvalue = SucuriScan::escape($kvalue);
6252
+ $content = str_replace($with_escape, $kvalue, $content);
6253
+ }
6254
  }
6255
 
6256
  return $content;
6262
  /**
6263
  * Gather and generate the information required globally by all the template files.
6264
  *
6265
+ * @param array $params Key-value array with pseudo-variables shared with the template.
6266
  * @return array A complementary list of pseudo-variables for the template files.
6267
  */
6268
+ private static function sharedParams($params = array())
6269
+ {
6270
+ $params = is_array($params) ? $params : array();
6271
 
6272
  // Base parameters, required to render all the pages.
6273
+ $params = self::linksAndNavbar($params);
6274
 
6275
  // Global parameters, used through out all the pages.
6276
  $params['PageTitle'] = isset($params['PageTitle']) ? '('.$params['PageTitle'].')' : '';
6277
+ $params['PageNonce'] = wp_create_nonce('sucuriscan_page_nonce');
6278
  $params['PageStyleClass'] = isset($params['PageStyleClass']) ? $params['PageStyleClass'] : 'base';
6279
  $params['CleanDomain'] = self::get_domain();
6280
  $params['AdminEmails'] = '';
6281
 
6282
  // Get a list of admin users for the API key generation.
6283
+ if (SucuriScanAPI::getPluginKey() === false) {
6284
  $admin_users = SucuriScan::get_users_for_api_key();
6285
+ $params['AdminEmails'] = self::selectOptions($admin_users);
6286
  }
6287
 
6288
  // Hide the advertisements from the layout.
6295
  $params['LayoutType'] = 'twocolumns';
6296
  $params['AdsVisibility'] = 'visible';
6297
  $params['ReviewNavbarButton'] = 'hidden';
6298
+ $params['PageSidebarContent'] = self::getTemplate('bsidebar', $params, 'section');
6299
  }
6300
 
6301
  return $params;
6307
  * @param boolean $visible Whether the condition executed returned a positive value or not.
6308
  * @return string A string indicating the visibility of a HTML component.
6309
  */
6310
+ public static function visibility($visible = false)
6311
+ {
6312
+ return ($visible === true ? 'visible' : 'hidden');
6313
  }
6314
 
6315
  /**
6316
  * Generate an URL pointing to the page indicated in the function and that must
6317
  * be loaded through the administrator panel.
6318
  *
6319
+ * @param string $page Short name of the page that will be generated.
6320
+ * @param boolean $ajax True if the URL should point to the Ajax handler.
6321
+ * @return string Full string containing the link of the page.
6322
  */
6323
+ public static function getUrl($page = '', $ajax = false)
6324
+ {
6325
+ $suffix = ($ajax === true) ? 'admin-ajax' : 'admin';
6326
+ $url_path = SucuriScan::admin_url($suffix . '.php?page=sucuriscan');
6327
 
6328
+ if (!empty($page)) {
6329
+ $url_path .= '_' . strtolower($page);
6330
  }
6331
 
6332
  return $url_path;
6339
  * @param string $page Short name of the page that will be generated.
6340
  * @return string Full string containing the link of the page.
6341
  */
6342
+ public static function getAjaxUrl($page = '')
6343
+ {
6344
+ return self::getUrl($page, true);
 
 
 
 
 
6345
  }
6346
 
6347
  /**
6349
  * template files, this will also generate the navigation bar and detect which
6350
  * items in it are selected by the current page.
6351
  *
6352
+ * @param array $params Key-value array with pseudo-variables shared with the template.
6353
  * @return array A complementary list of pseudo-variables for the template files.
6354
  */
6355
+ private static function linksAndNavbar($params = array())
6356
+ {
6357
  global $sucuriscan_pages;
6358
 
6359
+ $params = is_array($params) ? $params : array();
6360
+ $sub_pages = is_array($sucuriscan_pages) ? $sucuriscan_pages : array();
6361
 
6362
  $params['Navbar'] = '';
6363
  $params['CurrentPageFunc'] = '';
6364
 
6365
+ if ($_page = self::get('page', '_page')) {
6366
  $params['CurrentPageFunc'] = $_page;
6367
  }
6368
 
6369
+ foreach ($sub_pages as $sub_page_func => $sub_page_title) {
6370
+ if ($sub_page_func == 'sucuriscan_scanner'
6371
+ && SucuriScanOption::is_disabled(':sitecheck_scanner')
 
6372
  ) {
6373
  continue;
6374
  }
6375
 
6376
+ $func_parts = explode('_', $sub_page_func, 2);
6377
 
6378
+ if (isset($func_parts[1])) {
6379
  $unique_name = $func_parts[1];
6380
+ $pseudo_var = 'URL.' . ucwords($unique_name);
6381
  } else {
6382
  $unique_name = '';
6383
  $pseudo_var = 'URL.Home';
6384
  }
6385
 
6386
+ $params[$pseudo_var] = self::getUrl($unique_name);
6387
 
6388
  // Copy URL variable and create an Ajax handler.
6389
  $pseudo_var_ajax = 'Ajax' . $pseudo_var;
6390
+ $params[$pseudo_var_ajax] = self::getAjaxUrl($unique_name);
6391
 
6392
  $navbar_item_css_class = 'nav-tab';
6393
 
6394
+ if ($params['CurrentPageFunc'] == $sub_page_func) {
6395
+ $navbar_item_css_class .= "\x20nav-tab-active";
6396
  }
6397
 
6398
  $params['Navbar'] .= sprintf(
6399
+ "<a class='%s' href='%s'>%s</a>\n",
6400
  $navbar_item_css_class,
6401
+ $params[$pseudo_var],
6402
  $sub_page_title
6403
  );
6404
  }
6412
  * of the function.
6413
  *
6414
  * @param string $html The HTML content of a template file with its pseudo-variables parsed.
6415
+ * @param array $params Key-value array with pseudo-variables shared with the template.
6416
  * @return string The formatted HTML content of the base template.
6417
  */
6418
+ public static function getBaseTemplate($html = '', $params = array())
6419
+ {
6420
+ $params = is_array($params) ? $params : array();
6421
 
6422
+ $params = self::sharedParams($params);
6423
  $params['PageContent'] = $html;
6424
 
6425
+ return self::getTemplate('base', $params);
6426
  }
6427
 
6428
  /**
6431
  * of the function.
6432
  *
6433
  * @param string $template Filename of the template that will be used to generate the page.
6434
+ * @param array $params Key-value array with pseudo-variables shared with the template.
6435
+ * @param boolean $type Template type; either page, section or snippet.
6436
+ * @return string Formatted HTML code after pseudo-variables replacement.
6437
+ */
6438
+ public static function getTemplate($template = '', $params = array(), $type = 'page')
6439
+ {
6440
+ if (!is_array($params)) {
6441
+ $params = array();
 
 
 
 
 
6442
  }
6443
 
6444
+ if ($type == 'page' || $type == 'section') {
6445
+ $fpath_pattern = '%s/%s/inc/tpl/%s.html.tpl';
6446
+ } elseif ($type == 'snippet') {
6447
+ $fpath_pattern = '%s/%s/inc/tpl/%s.snippet.tpl';
6448
+ } else {
6449
+ $fpath_pattern = null;
6450
+ }
6451
 
6452
+ if ($fpath_pattern !== null) {
6453
+ $output = '';
6454
+ $fpath = sprintf($fpath_pattern, WP_PLUGIN_DIR, SUCURISCAN_PLUGIN_FOLDER, $template);
6455
 
6456
+ if (file_exists($fpath) && is_readable($fpath)) {
6457
+ $output = @file_get_contents($fpath);
6458
 
6459
+ $params['SucuriURL'] = SUCURISCAN_URL;
 
 
 
 
 
6460
 
6461
+ // Detect the current page URL.
6462
+ if ($_page = self::get('page', '_page')) {
6463
+ $params['CurrentURL'] = SucuriScan::admin_url('admin.php?page=' . $_page);
6464
+ } else {
6465
+ $params['CurrentURL'] = SucuriScan::admin_url();
6466
+ }
6467
+
6468
+ // Replace the global pseudo-variables in the section/snippets templates.
6469
+ if ($template == 'base'
6470
+ && array_key_exists('PageContent', $params)
6471
+ && @preg_match('/%%SUCURI\.(.+)%%/', $params['PageContent'])
6472
+ ) {
6473
+ $params['PageContent'] = self::replacePseudoVars($params['PageContent'], $params);
6474
+ }
6475
+
6476
+ $output = self::replacePseudoVars($output, $params);
6477
  }
6478
 
6479
+ if ($template == 'base' || $type != 'page') {
6480
+ return $output;
6481
+ }
6482
 
6483
+ return self::getBaseTemplate($output, $params);
 
6484
  }
6485
 
6486
+ return '';
6487
  }
6488
 
6489
  /**
6492
  * of the function.
6493
  *
6494
  * @param string $template Filename of the template that will be used to generate the page.
6495
+ * @param array $params Key-value array with pseudo-variables shared with the template.
6496
  * @return string The formatted HTML page after replace all the pseudo-variables.
6497
  */
6498
+ public static function getSection($template = '', $params = array())
6499
+ {
6500
+ $params = self::sharedParams($params);
6501
 
6502
+ return self::getTemplate($template, $params, 'section');
6503
  }
6504
 
6505
  /**
6508
  * of the function.
6509
  *
6510
  * @param string $template Filename of the template that will be used to generate the page.
6511
+ * @param array $params Key-value array with pseudo-variables shared with the template.
6512
  * @return string The formatted HTML page after replace all the pseudo-variables.
6513
  */
6514
+ public static function getModal($template = '', $params = array())
6515
+ {
6516
  $required = array(
6517
  'Title' => 'Lorem ipsum dolor sit amet',
6518
  'Visibility' => 'visible',
6526
  proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>',
6527
  );
6528
 
6529
+ if (!empty($template) && $template != 'none') {
6530
+ $params['Content'] = self::getSection($template);
6531
  }
6532
 
6533
+ foreach ($required as $param_name => $param_value) {
6534
+ if (!isset($params[$param_name])) {
6535
+ $params[$param_name] = $param_value;
6536
  }
6537
  }
6538
 
6539
  $params['Visibility'] = 'sucuriscan-' . $params['Visibility'];
6540
  $params['Identifier'] = 'sucuriscan-' . $template . '-modal';
6541
+ $params = self::sharedParams($params);
6542
 
6543
+ return self::getTemplate('modalwindow', $params, 'section');
6544
  }
6545
 
6546
  /**
6549
  * of the function.
6550
  *
6551
  * @param string $template Filename of the template that will be used to generate the page.
6552
+ * @param array $params Key-value array with pseudo-variables shared with the template.
6553
  * @return string The formatted HTML page after replace all the pseudo-variables.
6554
  */
6555
+ public static function getSnippet($template = '', $params = array())
6556
+ {
6557
+ return self::getTemplate($template, $params, 'snippet');
6558
  }
6559
 
6560
  /**
6564
  * @param string $selected_val Value of the option that will be selected by default.
6565
  * @return string Option list for a select form field.
6566
  */
6567
+ public static function selectOptions($allowed_values = array(), $selected_val = '')
6568
+ {
6569
  $options = '';
6570
 
6571
+ foreach ($allowed_values as $option_name => $option_label) {
 
 
 
 
 
 
6572
  $options .= sprintf(
6573
+ "<option %s value='%s'>%s</option>\n",
6574
+ ($option_name === $selected_val ? 'selected="selected"' : ''),
6575
+ SucuriScan::escape($option_name),
6576
+ SucuriScan::escape($option_label)
6577
  );
6578
  }
6579
 
6585
  *
6586
  * @return integer Page number of the link clicked in a pagination.
6587
  */
6588
+ public static function pageNumber()
6589
+ {
6590
+ $paged = self::get('paged', '[0-9]{1,5}');
6591
 
6592
+ return ($paged ? intval($paged) : 1);
6593
  }
6594
 
6595
  /**
6600
  * @param integer $max_per_page Maximum number of items that will be shown per page.
6601
  * @return string HTML code for a pagination generated using the provided data.
6602
  */
6603
+ public static function pagination($base_url = '', $total_items = 0, $max_per_page = 1)
6604
+ {
6605
  // Calculate the number of links for the pagination.
6606
  $html_links = '';
6607
+ $page_number = self::pageNumber();
6608
+ $max_pages = ceil($total_items / $max_per_page);
6609
  $extra_url = '';
6610
 
6611
  // Fix for inline anchor URLs.
6612
+ if (@preg_match('/^(.+)(#.+)$/', $base_url, $match)) {
6613
  $base_url = $match[1];
6614
  $extra_url = $match[2];
6615
  }
6616
 
6617
  // Generate the HTML links for the pagination.
6618
+ for ($j = 1; $j <= $max_pages; $j++) {
6619
  $link_class = 'sucuriscan-pagination-link';
6620
 
6621
+ if ($page_number == $j) {
6622
+ $link_class .= "\x20sucuriscan-pagination-active";
6623
  }
6624
 
6625
  $html_links .= sprintf(
6626
  '<li><a href="%s&paged=%d%s" class="%s">%s</a></li>',
6627
+ $base_url,
6628
+ $j,
6629
+ $extra_url,
6630
+ $link_class,
6631
+ $j
6632
  );
6633
  }
6634
 
6635
  return $html_links;
6636
  }
 
6637
  }
6638
 
6639
  /**
6645
  * content is then submitted to the remote server and it is stored for future
6646
  * analysis.
6647
  */
6648
+ class SucuriScanFSScanner extends SucuriScan
6649
+ {
6650
 
6651
  /**
6652
  * Retrieve the last time when the filesystem scan was ran.
6654
  * @param boolean $format Whether the timestamp must be formatted as date/time or not.
6655
  * @return string The timestamp of the runtime, or an string with the date/time.
6656
  */
6657
+ public static function get_filesystem_runtime($format = false)
6658
+ {
6659
+ $runtime = SucuriScanOption::get_option(':runtime');
6660
 
6661
+ if ($runtime > 0) {
6662
+ if ($format) {
6663
+ return SucuriScan::datetime($runtime);
6664
  }
6665
 
6666
  return $runtime;
6667
  }
6668
 
6669
+ if ($format) {
6670
+ return 'Unknown';
6671
  }
6672
 
6673
  return false;
6681
  *
6682
  * @return boolean Whether the feature to ignore files is enabled or not.
6683
  */
6684
+ public static function will_ignore_scanning()
6685
+ {
6686
+ return SucuriScanOption::is_enabled(':ignore_scanning');
6687
  }
6688
 
6689
  /**
6692
  * @param string $directory_path The (full) absolute path of a directory.
6693
  * @return boolean TRUE if the directory path was added to the list, FALSE otherwise.
6694
  */
6695
+ public static function ignore_directory($directory_path = '')
6696
+ {
6697
+ $cache = new SucuriScanCache('ignorescanning');
6698
 
6699
  // Use the checksum of the directory path as the cache key.
6700
+ $cache_key = md5($directory_path);
6701
+ $resource_type = SucuriScanFileInfo::get_resource_type($directory_path);
6702
  $cache_value = array(
6703
  'directory_path' => $directory_path,
6704
  'ignored_at' => self::local_time(),
6705
  'resource_type' => $resource_type,
6706
  );
6707
+ $cached = $cache->add($cache_key, $cache_value);
6708
 
6709
  return $cached;
6710
  }
6715
  * @param string $directory_path The (full) absolute path of a directory.
6716
  * @return boolean TRUE if the directory path was removed to the list, FALSE otherwise.
6717
  */
6718
+ public static function unignore_directory($directory_path = '')
6719
+ {
6720
+ $cache = new SucuriScanCache('ignorescanning');
6721
 
6722
  // Use the checksum of the directory path as the cache key.
6723
+ $cache_key = md5($directory_path);
6724
+ $removed = $cache->delete($cache_key);
6725
 
6726
  return $removed;
6727
  }
6747
  *
6748
  * @return array List of ignored directory paths.
6749
  */
6750
+ public static function get_ignored_directories()
6751
+ {
6752
  $response = array(
6753
  'raw' => array(),
6754
  'checksums' => array(),
6756
  'ignored_at_list' => array(),
6757
  );
6758
 
6759
+ $cache = new SucuriScanCache('ignorescanning');
6760
  $cache_lifetime = 0; // It is not necessary to expire this cache.
6761
+ $ignored_directories = $cache->getAll($cache_lifetime, 'array');
6762
 
6763
+ if ($ignored_directories) {
6764
  $response['raw'] = $ignored_directories;
6765
 
6766
+ foreach ($ignored_directories as $checksum => $data) {
6767
+ if (array_key_exists('directory_path', $data)
6768
+ && array_key_exists('ignored_at', $data)
 
6769
  ) {
6770
  $response['checksums'][] = $checksum;
6771
  $response['directories'][] = $data['directory_path'];
6787
  *
6788
  * @return array List of ignored and not ignored directories.
6789
  */
6790
+ public static function get_ignored_directories_live()
6791
+ {
6792
  $response = array(
6793
  'is_ignored' => array(),
6794
  'is_not_ignored' => array(),
6797
  // Get the ignored directories from the cache.
6798
  $ignored_directories = self::get_ignored_directories();
6799
 
6800
+ if ($ignored_directories) {
6801
  $response['is_ignored'] = $ignored_directories['raw'];
6802
  }
6803
 
6805
  $file_info = new SucuriScanFileInfo();
6806
  $file_info->ignore_files = true;
6807
  $file_info->ignore_directories = true;
6808
+ $file_info->scan_interface = SucuriScanOption::get_option(':scan_interface');
6809
+ $directory_list = $file_info->get_diretories_only(ABSPATH);
6810
 
6811
+ if ($directory_list) {
6812
  $response['is_not_ignored'] = $directory_list;
6813
  }
6814
 
6821
  * @param array $error_logs The content of an error log file, or an array with the lines.
6822
  * @return array List of valid error logs with their attributes separated.
6823
  */
6824
+ public static function parse_error_logs($error_logs = array())
6825
+ {
6826
  $logs_arr = array();
6827
  $pattern = '/^'
6828
  . '(\[(\S+) ([0-9:]{5,8})( \S+)?\] )?' // Detect date, time, and timezone.
6831
  . '(:| on line )([0-9]+)' // Detect line number.
6832
  . '$/';
6833
 
6834
+ if (is_string($error_logs)) {
6835
+ $error_logs = explode("\n", $error_logs);
6836
  }
6837
 
6838
+ foreach ((array) $error_logs as $line) {
6839
+ if (!is_string($line) || empty($line)) {
6840
  continue;
6841
  }
6842
 
6843
+ if (preg_match($pattern, $line, $match)) {
6844
  $data_set = array(
6845
  'date' => '',
6846
  'time' => '',
6857
  // Basic attributes from the scrapping.
6858
  $data_set['date'] = $match[2];
6859
  $data_set['time'] = $match[3];
6860
+ $data_set['time_zone'] = trim($match[4]);
6861
+ $data_set['error_type'] = trim($match[6]);
6862
+ $data_set['error_message'] = trim($match[7]);
6863
+ $data_set['file_path'] = trim($match[8]);
6864
  $data_set['line_number'] = (int) $match[10];
6865
 
6866
  // Additional data from the attributes.
6867
+ if ($data_set['date']) {
6868
  $data_set['date_time'] = $data_set['date']
6869
  . "\x20" . $data_set['time']
6870
  . "\x20" . $data_set['time_zone'];
6871
+ $data_set['timestamp'] = strtotime($data_set['date_time']);
6872
  }
6873
 
6874
+ if ($data_set['error_type']) {
6875
  $valid_types = array( 'warning', 'notice', 'error' );
6876
 
6877
+ foreach ($valid_types as $valid_type) {
6878
+ if (stripos($data_set['error_type'], $valid_type) !== false) {
6879
  $data_set['error_code'] = $valid_type;
6880
  break;
6881
  }
6888
 
6889
  return $logs_arr;
6890
  }
 
6891
  }
6892
 
6893
  /**
6902
  *
6903
  * @see https://core.trac.wordpress.org/ticket/23216
6904
  */
6905
+ class SucuriScanHeartbeat extends SucuriScanOption
6906
+ {
6907
 
6908
  /**
6909
  * Stop execution of the heartbeat API in certain parts of the site.
6910
  *
6911
  * @return void
6912
  */
6913
+ public static function register_script()
6914
+ {
6915
  global $pagenow;
6916
 
6917
+ $status = SucuriScanOption::get_option(':heartbeat');
6918
 
6919
  // Enable heartbeat everywhere.
6920
+ if ($status == 'enabled') {
6921
  /* Do nothing */
6922
+ } // Disable heartbeat everywhere.
6923
+ elseif ($status == 'disabled') {
6924
+ wp_deregister_script('heartbeat');
6925
+ } // Disable heartbeat only on the dashboard and home pages.
6926
+ elseif ($status == 'dashboard'
 
 
 
 
 
6927
  && $pagenow == 'index.php'
6928
  ) {
6929
+ wp_deregister_script('heartbeat');
6930
+ } // Disable heartbeat everywhere except in post edition.
6931
+ elseif ($status == 'addpost'
 
 
 
6932
  && $pagenow != 'post.php'
6933
  && $pagenow != 'post-new.php'
6934
  ) {
6935
+ wp_deregister_script('heartbeat');
6936
  }
6937
  }
6938
 
6946
  * @param array $settings Heartbeat settings.
6947
  * @return array Updated version of the heartbeat settings.
6948
  */
6949
+ public static function update_settings($settings = array())
6950
+ {
6951
+ $pulse = SucuriScanOption::get_option(':heartbeat_pulse');
6952
+ $autostart = SucuriScanOption::get_option(':heartbeat_autostart');
6953
 
6954
+ if ($pulse < 15 || $pulse > 60) {
6955
+ SucuriScanOption::delete_option(':heartbeat_pulse');
6956
  $pulse = 15;
6957
  }
6958
 
6970
  * @param string $screen_id Identifier of the screen the heartbeat occurred on.
6971
  * @return array Response with new data.
6972
  */
6973
+ public static function respond_to_received($response = array(), $data = array(), $screen_id = '')
6974
+ {
6975
+ $interval = SucuriScanOption::get_option(':heartbeat_interval');
6976
 
6977
+ if ($interval == 'slow'
 
6978
  || $interval == 'fast'
6979
  || $interval == 'standard'
6980
  ) {
6981
  $response['heartbeat_interval'] = $interval;
6982
  } else {
6983
+ SucuriScanOption::delete_option(':heartbeat_interval');
6984
  }
6985
 
6986
  return $response;
6993
  * @param string $screen_id Identifier of the screen the heartbeat occurred on.
6994
  * @return array Response with new data.
6995
  */
6996
+ public static function respond_to_send($response = array(), $screen_id = '')
6997
+ {
6998
  return $response;
6999
  }
7000
 
7003
  *
7004
  * @return array Allowed values for the heartbeat status.
7005
  */
7006
+ public static function statuses_allowed()
7007
+ {
7008
  return array(
7009
  'enabled' => 'Enable everywhere',
7010
  'disabled' => 'Disable everywhere',
7018
  *
7019
  * @return array Allowed values for the heartbeat intervals.
7020
  */
7021
+ public static function intervals_allowed()
7022
+ {
7023
  return array(
7024
  'slow' => 'Slow interval',
7025
  'fast' => 'Fast interval',
7032
  *
7033
  * @return array Allowed values for the heartbeat pulses.
7034
  */
7035
+ public static function pulses_allowed()
7036
+ {
7037
  $pulses = array();
7038
 
7039
+ for ($i = 15; $i <= 60; $i++) {
7040
+ $pulses[ $i ] = sprintf('Run every %d seconds', $i);
7041
  }
7042
 
7043
  return $pulses;
7044
  }
 
7045
  }
7046
 
7047
  /**
7053
  * sites with old versions of the premium plugin (that was deprecated at
7054
  * July/2014).
7055
  */
7056
+ class SucuriScanInterface
7057
+ {
7058
  /**
7059
  * Initialization code for the plugin.
7060
  *
7065
  *
7066
  * @return void
7067
  */
7068
+ public static function initialize()
7069
+ {
7070
+ if (SucuriScan::support_reverse_proxy()
7071
+ || SucuriScan::is_behind_cloudproxy()
7072
+ ) {
7073
  $_SERVER['SUCURIREAL_REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
7074
  $_SERVER['REMOTE_ADDR'] = SucuriScan::get_remote_addr();
7075
  }
7081
  *
7082
  * @return void
7083
  */
7084
+ public static function enqueue_scripts()
7085
+ {
7086
  $asset_version = '';
7087
 
7088
+ if (strlen(SUCURISCAN_PLUGIN_CHECKSUM) >= 7) {
7089
+ $asset_version = substr(SUCURISCAN_PLUGIN_CHECKSUM, 0, 7);
7090
  }
7091
 
7092
+ wp_register_style('sucuriscan', SUCURISCAN_URL . '/inc/css/sucuri-scanner.min.css', array(), $asset_version);
7093
+ wp_register_script('sucuriscan', SUCURISCAN_URL . '/inc/js/sucuri-scanner.min.js', array(), $asset_version);
7094
+ wp_enqueue_style('sucuriscan');
7095
+ wp_enqueue_script('sucuriscan');
7096
 
7097
+ if (SucuriScanRequest::get('page', 'sucuriscan') !== false) {
7098
+ wp_register_script('sucuriscan2', SUCURISCAN_URL . '/inc/js/d3.min.js', array(), $asset_version);
7099
+ wp_register_script('sucuriscan3', SUCURISCAN_URL . '/inc/js/c3.min.js', array(), $asset_version);
7100
+ wp_enqueue_script('sucuriscan2');
7101
+ wp_enqueue_script('sucuriscan3');
7102
  }
7103
  }
7104
 
7107
  *
7108
  * @return void
7109
  */
7110
+ public static function add_interface_menu()
7111
+ {
7112
  global $sucuriscan_pages;
7113
 
7114
+ if (function_exists('add_menu_page')
 
7115
  && $sucuriscan_pages
7116
+ && is_array($sucuriscan_pages)
7117
+ && array_key_exists('sucuriscan', $sucuriscan_pages)
7118
  ) {
7119
  // Add main menu link.
7120
  add_menu_page(
7126
  SUCURISCAN_URL . '/inc/images/menu-icon.png'
7127
  );
7128
 
7129
+ foreach ($sucuriscan_pages as $sub_page_func => $sub_page_title) {
7130
+ if ($sub_page_func == 'sucuriscan_scanner'
7131
+ && SucuriScanOption::is_disabled(':sitecheck_scanner')
 
7132
  ) {
7133
  continue;
7134
  }
7155
  *
7156
  * @return void
7157
  */
7158
+ public static function handle_old_plugins()
7159
+ {
7160
+ if (class_exists('SucuriScanFileInfo')) {
7161
  $file_info = new SucuriScanFileInfo();
7162
  $file_info->ignore_files = false;
7163
  $file_info->ignore_directories = false;
7167
  'sucuri-cloudproxy-waf/cloudproxy.php',
7168
  );
7169
 
7170
+ foreach ($plugins as $plugin) {
7171
+ $plugin_directory = dirname(WP_PLUGIN_DIR . '/' . $plugin);
7172
 
7173
+ if (file_exists($plugin_directory)) {
7174
+ if (is_plugin_active($plugin)) {
7175
+ deactivate_plugins($plugin);
7176
  }
7177
 
7178
+ $plugin_removed = $file_info->remove_directory_tree($plugin_directory);
7179
  }
7180
  }
7181
  }
7187
  *
7188
  * @return void
7189
  */
7190
+ public static function create_datastore_folder()
7191
+ {
7192
  $directory = SucuriScan::datastore_folder_path();
7193
 
7194
  if (!file_exists($directory)) {
7195
  @mkdir($directory, 0755, true);
7196
  }
7197
 
7198
+ if (@preg_match(';/uploads/$;', $directory)) {
7199
+ SucuriScanOption::delete_option(':datastore_path');
7200
+ SucuriScanInterface::error('Uploads directory must not be used as the data store path.');
7201
+ } elseif (file_exists($directory)) {
7202
  // Create last-logins datastore file.
7203
  sucuriscan_lastlogins_datastore_exists();
7204
 
7219
  SucuriScanOption::delete_option(':datastore_path');
7220
  SucuriScanInterface::error(
7221
  'Data folder does not exists and could not be created. Try to <a href="' .
7222
+ SucuriScanTemplate::getUrl('settings') . '">click this link</a> to see
7223
  if the plugin is able to fix this error automatically, if this message
7224
  reappears you will need to either change the location of the directory from
7225
  the plugin general settings page or create this directory manually and give
7233
  *
7234
  * @return void
7235
  */
7236
+ public static function check_permissions()
7237
+ {
7238
+ if (!function_exists('current_user_can')
7239
+ || !current_user_can('manage_options')
7240
  ) {
7241
+ $page = SucuriScanRequest::get('page', '_page');
7242
+ $page = SucuriScan::escape($page);
7243
+ wp_die(__('Access denied by <b>Sucuri</b> to see <code>' . $page . '</code>'));
7244
  }
7245
  }
7246
 
7251
  *
7252
  * @return boolean Either TRUE or FALSE if the nonce is valid or not respectively.
7253
  */
7254
+ public static function check_nonce()
7255
+ {
7256
+ if (!empty($_POST)) {
7257
  $nonce_name = 'sucuriscan_page_nonce';
7258
+ $nonce_value = SucuriScanRequest::post($nonce_name, '_nonce');
7259
 
7260
+ if (!$nonce_value || !wp_verify_nonce($nonce_value, $nonce_name)) {
7261
+ wp_die(__('WordPress Nonce verification failed, try again going back and checking the form.'));
7262
 
7263
  return false;
7264
  }
7274
  * @param string $message The message that will be printed in the alert.
7275
  * @return void
7276
  */
7277
+ private static function admin_notice($type = 'updated', $message = '')
7278
+ {
7279
  $display_notice = true;
7280
 
7281
  /**
7287
  * user authentication process is executed may cause a "headers already sent"
7288
  * error.
7289
  */
7290
+ if (!empty($_POST)
7291
+ && SucuriScanRequest::post('log')
7292
+ && SucuriScanRequest::post('pwd')
7293
+ && SucuriScanRequest::post('wp-submit')
 
7294
  ) {
7295
  $display_notice = false;
7296
  }
7297
 
7298
  // Display the HTML notice to the current user.
7299
+ if ($display_notice === true && !empty($message)) {
7300
+ echo SucuriScanTemplate::getSection(
7301
+ 'notification-admin',
7302
+ array(
7303
+ 'AlertType' => $type,
7304
+ 'AlertUnique' => rand(100, 999),
7305
+ 'AlertMessage' => $message,
7306
+ )
7307
+ );
7308
  }
7309
  }
7310
 
7314
  * @param string $error_msg The message that will be printed in the alert.
7315
  * @return void
7316
  */
7317
+ public static function error($error_msg = '')
7318
+ {
7319
+ self::admin_notice('error', '<b>Sucuri:</b> ' . $error_msg);
7320
  }
7321
 
7322
  /**
7325
  * @param string $info_msg The message that will be printed in the alert.
7326
  * @return void
7327
  */
7328
+ public static function info($info_msg = '')
7329
+ {
7330
+ self::admin_notice('updated', '<b>Sucuri:</b> ' . $info_msg);
7331
  }
7332
 
7333
  /**
7337
  *
7338
  * @return void
7339
  */
7340
+ public static function setup_notice()
7341
+ {
7342
+ if (current_user_can('manage_options')
7343
  && SucuriScan::no_notices_here() === false
7344
+ && !SucuriScanAPI::getPluginKey()
7345
+ && SucuriScanRequest::post(':plugin_api_key') === false
7346
+ && SucuriScanRequest::post(':recover_key') === false
7347
+ && !SucuriScanRequest::post(':manual_api_key')
7348
  ) {
7349
  if (SucuriScanRequest::get(':dismiss_setup') !== false) {
7350
  SucuriScanOption::update_option(':dismiss_setup', 'enabled');
7351
  } elseif (SucuriScanOption::is_enabled(':dismiss_setup')) {
7352
  /* Do not display API key generation form. */
7353
  } else {
7354
+ echo SucuriScanTemplate::getSection('setup-notice');
7355
+ echo SucuriScanTemplate::getModal('setup-form', array(
7356
  'Visibility' => 'hidden',
7357
  'Title' => 'Sucuri API key generation',
7358
  'CssClass' => 'sucuriscan-setup-instructions',
7360
  }
7361
  }
7362
  }
7363
+ }
7364
+
7365
+ /**
7366
+ * Scan the content directory for files that were modified during the last seven
7367
+ * days (by default) or during the number of days specified by the user in the
7368
+ * request. Note that this operation may fail with an internal server error if
7369
+ * the project contains too many files that the PHP interpreter can check in a
7370
+ * single run.
7371
+ *
7372
+ * @return void
7373
+ */
7374
+ function sucuriscan_modified_files()
7375
+ {
7376
+ // TODO: Keep the array values hardcoded for now.
7377
+ $valid_day_ranges = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 60);
7378
+ $template_variables = array(
7379
+ 'ModifiedFiles.List' => '',
7380
+ 'ModifiedFiles.SelectOptions' => '',
7381
+ 'ModifiedFiles.Days' => 0,
7382
+ );
7383
+
7384
+ // Generate the options for the select field of the page form.
7385
+ if ($valid_day_ranges) {
7386
+ $options = array();
7387
+ foreach ($valid_day_ranges as $day) {
7388
+ $options[$day] = sprintf(
7389
+ '%d day%s ago',
7390
+ $day,
7391
+ ($day === 1)?'':'s'
7392
+ );
7393
+ }
7394
+ $options_html = SucuriScanTemplate::selectOptions($options, 7);
7395
+ $template_variables['ModifiedFiles.SelectOptions'] = $options_html;
7396
+ }
7397
+
7398
+ return SucuriScanTemplate::getSection('integrity-modifiedfiles', $template_variables);
7399
+ }
7400
+
7401
+ function sucuriscan_scanner_modfiles_days($back_days = 0)
7402
+ {
7403
+ $default_number_of_days = 7;
7404
+ $back_days = intval($back_days);
7405
+
7406
+ // Keep the number of days in range.
7407
+ if ($back_days === false) {
7408
+ $back_days = $default_number_of_days;
7409
+ } else {
7410
+ if ($back_days <= 0) {
7411
+ $back_days = 1;
7412
+ } elseif ($back_days >= 60) {
7413
+ $back_days = 60;
7414
+ }
7415
+ }
7416
+
7417
+ return $back_days;
7418
+ }
7419
 
7420
+ function sucuriscan_scanner_modfiles_ajax()
7421
+ {
7422
+ if (SucuriScanRequest::post('form_action') == 'get_modfiles') {
7423
+ $response = '';
7424
+ $hashes = sucuriscan_get_integrity_tree(WP_CONTENT_DIR, true);
7425
+ $back_days = SucuriScanRequest::post(':last_days', '[0-9]+');
7426
+ $back_days = sucuriscan_scanner_modfiles_days($back_days);
7427
+
7428
+ if (!empty($hashes)) {
7429
+ $counter = 0;
7430
+ $back_days = current_time('timestamp') - ($back_days * 86400);
7431
+
7432
+ foreach ($hashes as $file_path => $file_info) {
7433
+ if (isset($file_info['modified_at'])
7434
+ && $file_info['modified_at'] >= $back_days
7435
+ ) {
7436
+ $css_class = ($counter % 2 === 0) ? '' : 'alternate';
7437
+ $mod_date = SucuriScan::datetime($file_info['modified_at']);
7438
+
7439
+ $response .= SucuriScanTemplate::getSnippet(
7440
+ 'integrity-modifiedfiles',
7441
+ array(
7442
+ 'ModifiedFiles.DateTime' => $mod_date,
7443
+ 'ModifiedFiles.FilePath' => $file_path,
7444
+ 'ModifiedFiles.CheckSum' => $file_info['checksum'],
7445
+ 'ModifiedFiles.FileSize' => $file_info['filesize'],
7446
+ 'ModifiedFiles.FileSizeHuman' => SucuriScan::human_filesize($file_info['filesize']),
7447
+ 'ModifiedFiles.FileSizeNumber' => number_format($file_info['filesize']),
7448
+ 'ModifiedFiles.CssClass' => $css_class,
7449
+ )
7450
+ );
7451
+ $counter++;
7452
+ }
7453
+ }
7454
+ }
7455
+
7456
+ print($response);
7457
+ exit(0);
7458
+ }
7459
  }
7460
 
7461
  /**
7465
  *
7466
  * @return void
7467
  */
7468
+ function sucuriscan_scanner_page()
7469
+ {
7470
  SucuriScanInterface::check_permissions();
7471
 
7472
+ $params = array();
7473
+ $cache = new SucuriScanCache('sitecheck');
7474
+ $scan_results = $cache->get('scan_results', SUCURISCAN_SITECHECK_LIFETIME, 'array');
7475
+ $report_results = (bool) ($scan_results && !empty($scan_results));
7476
 
7477
+ if (SucuriScanInterface::check_nonce()
7478
+ && SucuriScanRequest::post(':malware_scan', '1')
 
7479
  ) {
7480
  $report_results = true;
7481
  }
7482
 
7483
+ if ($report_results === true) {
7484
  $template_name = 'malwarescan-results';
7485
+ $params = sucuriscan_sitecheck_info($scan_results);
7486
+ $params['PageTitle'] = 'Malware Scan';
7487
+ $params['PageStyleClass'] = 'scanner-results';
7488
  } else {
7489
  $template_name = 'malwarescan';
7490
+ $params['PageTitle'] = 'Malware Scan';
7491
+ $params['PageStyleClass'] = 'scanner-loading';
7492
+ }
7493
+
7494
+ echo SucuriScanTemplate::getTemplate($template_name, $params);
7495
+ }
7496
+
7497
+ /**
7498
+ * Handle an Ajax request for this specific page.
7499
+ *
7500
+ * @return mixed.
7501
+ */
7502
+ function sucuriscan_scanner_ajax()
7503
+ {
7504
+ SucuriScanInterface::check_permissions();
7505
+
7506
+ if (SucuriScanInterface::check_nonce()) {
7507
+ sucuriscan_scanner_modfiles_ajax();
7508
  }
7509
 
7510
+ wp_die();
7511
  }
7512
 
7513
  /**
7516
  * @param array $scan_results Array with information of the scanning.
7517
  * @return array Array with psuedo-variables to build the template.
7518
  */
7519
+ function sucuriscan_sitecheck_info($scan_results = array())
7520
+ {
7521
  $clean_domain = SucuriScan::get_domain();
7522
+ $params = array(
7523
  'ScannedDomainName' => $clean_domain,
7524
  'ScannerResults.CssClass' => '',
7525
  'ScannerResults.Content' => '',
7535
  );
7536
 
7537
  // If the results are not cached, then request a new scan and store in cache.
7538
+ if ($scan_results === false) {
7539
+ $scan_results = SucuriScanAPI::getSitecheckResults($clean_domain);
7540
 
7541
  // Check for error messages in the request's response.
7542
+ if (is_string($scan_results)) {
7543
+ if (@preg_match('/^ERROR:(.*)/', $scan_results, $error_m)) {
7544
+ SucuriScanInterface::error(
7545
+ 'The site <code>' . SucuriScan::escape($clean_domain) . '</code>'
7546
+ . ' was not scanned: ' . SucuriScan::escape($error_m[1])
7547
+ );
7548
  } else {
7549
+ SucuriScanInterface::error('SiteCheck error: ' . $scan_results);
7550
  }
7551
  } else {
7552
+ $cache = new SucuriScanCache('sitecheck');
7553
+ $results_were_cached = $cache->add('scan_results', $scan_results);
7554
 
7555
+ if (!$results_were_cached) {
7556
+ SucuriScanInterface::error('Could not cache the malware scan results.');
7557
  }
7558
  }
7559
  }
7560
 
7561
+ if (is_array($scan_results) && !empty($scan_results)) {
7562
  // Increase the malware scan counter.
7563
+ $sitecheck_counter = (int) SucuriScanOption::get_option(':sitecheck_counter');
7564
+ SucuriScanOption::update_option(':sitecheck_counter', $sitecheck_counter + 1);
7565
  add_thickbox();
7566
 
7567
+ $params = sucuriscan_sitecheck_scanner_results($scan_results, $params);
7568
+ $params = sucuriscan_sitecheck_website_details($scan_results, $params);
7569
+ $params = sucuriscan_sitecheck_website_links($scan_results, $params);
7570
+ $params = sucuriscan_sitecheck_blacklist_status($scan_results, $params);
7571
+ $params = sucuriscan_sitecheck_modified_files($scan_results, $params);
7572
 
7573
+ if (isset($scan_results['MALWARE']['WARN'])
 
7574
  || isset($scan_results['BLACKLIST']['WARN'])
7575
  ) {
7576
+ $params['SignupButtonVisibility'] = 'visible';
7577
  }
7578
  }
7579
 
7580
+ return $params;
7581
  }
7582
 
7583
  /**
7585
  * the HTML code to display the information in the malware scan page inside the
7586
  * remote scanner results tab.
7587
  *
7588
+ * @param array $scan_results Array with information of the scanning.
7589
+ * @param array $params Array with psuedo-variables to build the template.
7590
+ * @return array Psuedo-variables to build the template including extra info.
7591
  */
7592
+ function sucuriscan_sitecheck_scanner_results($scan_results = false, $params = array())
7593
+ {
7594
  $secvars = array(
7595
  'CacheLifeTime' => SUCURISCAN_SITECHECK_LIFETIME,
7596
  'WebsiteStatus' => 'Site status unknown',
7599
  'MalwarePayloadList' => '',
7600
  );
7601
 
7602
+ if (isset($scan_results['MALWARE']['WARN'])) {
7603
+ $params['ScannerResults.CssClass'] = 'sucuriscan-red-tab';
7604
  $secvars['WebsiteStatus'] = 'Site compromised (malware was identified)';
7605
  $secvars['NoMalwareRowVisibility'] = 'hidden';
7606
  $secvars['FixButtonVisibility'] = 'visible';
7607
 
7608
+ foreach ($scan_results['MALWARE']['WARN'] as $key => $malres) {
7609
+ $malres = SucuriScanAPI::getSitecheckMalware($malres);
7610
+
7611
+ if ($malres !== false) {
7612
+ $secvars['MalwarePayloadList'] .= SucuriScanTemplate::getSnippet(
7613
+ 'malwarescan-resmalware',
7614
+ array(
7615
+ 'MalwareKey' => $key,
7616
+ 'MalwareDocs' => $malres['malware_docs'],
7617
+ 'MalwareType' => $malres['malware_type'],
7618
+ 'MalwarePayload' => $malres['malware_payload'],
7619
+ 'AlertMessage' => $malres['alert_message'],
7620
+ 'InfectedUrl' => $malres['infected_url'],
7621
+ )
7622
+ );
7623
  }
7624
  }
7625
  } else {
7626
  $secvars['WebsiteStatus'] = 'Site clean (no malware was identified)';
7627
  }
7628
 
7629
+ $params['ScannerResults.Content'] = SucuriScanTemplate::getSection('malwarescan-resmalware', $secvars);
7630
 
7631
+ return $params;
7632
  }
7633
 
7634
  /**
7636
  * the HTML code to display the information in the malware scan page inside the
7637
  * website details tab.
7638
  *
7639
+ * @param array $scan_results Array with information of the scanning.
7640
+ * @param array $params Array with psuedo-variables to build the template.
7641
+ * @return array Psuedo-variables to build the template including extra info.
7642
  */
7643
+ function sucuriscan_sitecheck_website_details($scan_results = false, $params = array())
7644
+ {
7645
  $secvars = array(
7646
  'UpdateWebsiteButtonVisibility' => 'hidden',
7647
  'VersionNumberOfTheUpdate' => '0.0',
7648
+ 'AdminUrlForUpdates' => SucuriScan::admin_url('update-core.php'),
7649
  'GenericInformationList' => '',
7650
  'NoAppDetailsVisibility' => 'visible',
7651
  'ApplicationDetailsList' => '',
7656
  );
7657
 
7658
  // Check whether this WordPress installation needs an update.
7659
+ if (function_exists('get_core_updates')) {
7660
  $site_updates = get_core_updates();
7661
 
7662
+ if (!is_array($site_updates)
 
7663
  || empty($site_updates)
7664
  || $site_updates[0]->response == 'latest'
7665
  ) {
7667
  }
7668
  }
7669
 
7670
+ if (isset($scan_results['OUTDATEDSCAN'])
 
7671
  || isset($scan_results['RECOMMENDATIONS'])
7672
  ) {
7673
+ $params['WebsiteDetails.CssClass'] = 'sucuriscan-red-tab';
7674
  }
7675
 
7676
+ $secvars = sucuriscan_sitecheck_general_information($scan_results, $secvars);
7677
+ $secvars = sucuriscan_sitecheck_application_details($scan_results, $secvars);
7678
+ $secvars = sucuriscan_sitecheck_system_notices($scan_results, $secvars);
7679
+ $secvars = sucuriscan_sitecheck_outdated_software($scan_results, $secvars);
7680
+ $secvars = sucuriscan_sitecheck_recommendations($scan_results, $secvars);
7681
 
7682
+ $params['WebsiteDetails.Content'] = SucuriScanTemplate::getSection('malwarescan-reswebdetails', $secvars);
7683
 
7684
+ return $params;
7685
  }
7686
 
7687
  /**
7689
  * the HTML code to display the information in the malware scan page inside the
7690
  * website details tab and specifically in the general information panel.
7691
  *
7692
+ * @param array $scan_results Array with information of the scanning.
7693
+ * @param array $params Array with psuedo-variables to build the template.
7694
+ * @return array Psuedo-variables to build the template including extra info.
7695
  */
7696
+ function sucuriscan_sitecheck_general_information($scan_results = false, $secvars = array())
7697
+ {
7698
  $possible_keys = array(
7699
  'DOMAIN' => 'Domain Scanned',
7700
  'IP' => 'Site IP Address',
7704
  'PHP_VERSION' => 'PHP Version',
7705
  );
7706
 
7707
+ if (isset($scan_results['SCAN'])) {
7708
+ $scan_results['SCAN']['WP_VERSION'] = array(SucuriScan::site_version());
7709
+ $scan_results['SCAN']['PHP_VERSION'] = array(phpversion());
7710
 
7711
+ foreach ($possible_keys as $result_key => $result_title) {
7712
+ if (isset($scan_results['SCAN'][$result_key])) {
7713
+ if (is_array($scan_results['SCAN'][$result_key])) {
7714
+ $result_value = implode(', ', $scan_results['SCAN'][$result_key]);
7715
+ } else {
7716
+ $result_value = json_encode($scan_results['SCAN'][$result_key]);
7717
+ }
7718
 
7719
+ $secvars['GenericInformationList'] .= SucuriScanTemplate::getSnippet(
7720
+ 'malwarescan-appdetail',
7721
+ array(
7722
+ 'InformationTitle' => $result_title,
7723
+ 'InformationValue' => $result_value,
7724
+ )
7725
+ );
7726
  }
7727
  }
7728
  }
7735
  * the HTML code to display the information in the malware scan page inside the
7736
  * website details tab and specifically in the application details panel.
7737
  *
7738
+ * @param array $scan_results Array with information of the scanning.
7739
+ * @param array $params Array with psuedo-variables to build the template.
7740
+ * @return array Psuedo-variables to build the template including extra info.
7741
  */
7742
+ function sucuriscan_sitecheck_application_details($scan_results = false, $secvars = array())
7743
+ {
7744
+ if (isset($scan_results['WEBAPP'])) {
7745
+ foreach ($scan_results['WEBAPP'] as $webapp_key => $webapp_details) {
7746
+ if (is_array($webapp_details)) {
7747
+ foreach ($webapp_details as $i => $details) {
7748
  $secvars['NoAppDetailsVisibility'] = 'hidden';
7749
 
7750
+ if (is_array($details)) {
7751
  $details = isset($details[0]) ? $details[0] : '';
7752
  }
7753
 
7754
+ $details_parts = explode(':', $details, 2);
7755
+ $result_title = isset($details_parts[0]) ? trim($details_parts[0]) : '';
7756
+ $result_value = isset($details_parts[1]) ? trim($details_parts[1]) : '';
7757
 
7758
+ $secvars['ApplicationDetailsList'] .= SucuriScanTemplate::getSnippet(
7759
+ 'malwarescan-appdetail',
7760
+ array(
7761
+ 'InformationTitle' => $result_title,
7762
+ 'InformationValue' => $result_value,
7763
+ )
7764
+ );
7765
  }
7766
  }
7767
  }
7775
  * the HTML code to display the information in the malware scan page inside the
7776
  * website details tab and specifically in the system notices panel.
7777
  *
7778
+ * @param array $scan_results Array with information of the scanning.
7779
+ * @param array $params Array with psuedo-variables to build the template.
7780
+ * @return array Psuedo-variables to build the template including extra info.
7781
  */
7782
+ function sucuriscan_sitecheck_system_notices($scan_results = false, $secvars = array())
7783
+ {
7784
+ if (isset($scan_results['SYSTEM']['NOTICE'])) {
7785
+ foreach ($scan_results['SYSTEM']['NOTICE'] as $notice) {
7786
  $secvars['NoAppDetailsVisibility'] = 'hidden';
7787
 
7788
+ if (is_array($notice)) {
7789
+ $notice = implode(', ', $notice);
7790
  }
7791
 
7792
+ $secvars['SystemNoticeList'] .= SucuriScanTemplate::getSnippet(
7793
+ 'malwarescan-sysnotice',
7794
+ array(
7795
+ 'SystemNotice' => $notice,
7796
+ )
7797
+ );
7798
  }
7799
  }
7800
 
7806
  * the HTML code to display the information in the malware scan page inside the
7807
  * website details tab and specifically in the outdated software panel.
7808
  *
7809
+ * @param array $scan_results Array with information of the scanning.
7810
+ * @param array $params Array with psuedo-variables to build the template.
7811
+ * @return array Psuedo-variables to build the template including extra info.
7812
  */
7813
+ function sucuriscan_sitecheck_outdated_software($scan_results = false, $secvars = array())
7814
+ {
7815
+ if (isset($scan_results['OUTDATEDSCAN'])) {
7816
+ foreach ($scan_results['OUTDATEDSCAN'] as $outdated) {
7817
+ if (count($outdated) >= 3) {
7818
  $secvars['HasRecommendationsVisibility'] = 'visible';
7819
+ $secvars['OutdatedSoftwareList'] .= SucuriScanTemplate::getSnippet(
7820
+ 'malwarescan-outdated',
7821
+ array(
7822
+ 'OutdatedSoftwareTitle' => $outdated[0],
7823
+ 'OutdatedSoftwareUrl' => $outdated[1],
7824
+ 'OutdatedSoftwareValue' => $outdated[2],
7825
+ )
7826
+ );
7827
  }
7828
  }
7829
  }
7836
  * the HTML code to display the information in the malware scan page inside the
7837
  * website details tab and specifically in the security recommendations panel.
7838
  *
7839
+ * @param array $scan_results Array with information of the scanning.
7840
+ * @param array $params Array with psuedo-variables to build the template.
7841
+ * @return array Psuedo-variables to build the template including extra info.
7842
  */
7843
+ function sucuriscan_sitecheck_recommendations($scan_results = false, $secvars = array())
7844
+ {
7845
+ if (isset($scan_results['RECOMMENDATIONS'])) {
7846
+ foreach ($scan_results['RECOMMENDATIONS'] as $recommendation) {
7847
+ if (count($recommendation) >= 3) {
7848
  $secvars['HasRecommendationsVisibility'] = 'visible';
7849
+ $secvars['SecurityRecomendationList'] .= SucuriScanTemplate::getSnippet(
7850
+ 'malwarescan-recommendation',
7851
+ array(
7852
+ 'RecommendationTitle' => $recommendation[0],
7853
+ 'RecommendationValue' => $recommendation[1],
7854
+ 'RecommendationUrl' => $recommendation[2],
7855
+ 'RecommendationUrlTitle' => $recommendation[2],
7856
+ )
7857
+ );
7858
  }
7859
  }
7860
  }
7867
  * the HTML code to display the information in the malware scan page inside the
7868
  * website links tab.
7869
  *
7870
+ * @param array $scan_results Array with information of the scanning.
7871
+ * @param array $params Array with psuedo-variables to build the template.
7872
+ * @return array Psuedo-variables to build the template including extra information.
7873
  */
7874
+ function sucuriscan_sitecheck_website_links($scan_results = false, $params = array())
7875
+ {
7876
  $possible_url_keys = array(
7877
  'IFRAME' => 'List of iframes found',
7878
  'JSEXTERNAL' => 'List of external scripts included',
7884
  'NoLinksVisibility' => 'hidden',
7885
  );
7886
 
7887
+ if (isset($scan_results['LINKS'])) {
7888
+ foreach ($possible_url_keys as $result_key => $result_title) {
7889
+ if (isset($scan_results['LINKS'][$result_key])) {
7890
  $result_value = 0;
7891
  $result_items = '';
7892
 
7893
+ foreach ($scan_results['LINKS'][$result_key] as $url_path) {
7894
  $result_value += 1;
7895
+ $result_items .= SucuriScanTemplate::getSnippet(
7896
+ 'malwarescan-weblinkitems',
7897
+ array(
7898
+ 'WebsiteLinksItemTitle' => $url_path,
7899
+ )
7900
+ );
7901
  }
7902
 
7903
+ $secvars['WebsiteLinksAllList'] .= SucuriScanTemplate::getSnippet(
7904
+ 'malwarescan-weblinktitle',
7905
+ array(
7906
+ 'WebsiteLinksSectionTitle' => $result_title,
7907
+ 'WebsiteLinksSectionTotal' => $result_value,
7908
+ 'WebsiteLinksSectionItems' => $result_items /* Do not escape. */,
7909
+ )
7910
+ );
7911
  }
7912
  }
7913
  } else {
7914
  $secvars['NoLinksVisibility'] = 'visible';
7915
  }
7916
 
7917
+ $params['WebsiteLinks.Content'] = SucuriScanTemplate::getSection('malwarescan-resweblinks', $secvars);
7918
 
7919
+ return $params;
7920
  }
7921
 
7922
  /**
7924
  * the HTML code to display the information in the malware scan page inside the
7925
  * blacklist status tab.
7926
  *
7927
+ * @param array $scan_results Array with information of the scanning.
7928
+ * @param array $params Array with psuedo-variables to build the template.
7929
+ * @return array Psuedo-variables to build the template including extra info.
7930
  */
7931
+ function sucuriscan_sitecheck_blacklist_status($scan_results = false, $params = array())
7932
+ {
7933
  $blacklist_types = array(
7934
  'INFO' => 'CLEAN',
7935
  'WARN' => 'WARNING',
7939
  'BlacklistStatusList' => '',
7940
  );
7941
 
7942
+ if (isset($scan_results['BLACKLIST']['WARN'])) {
7943
+ $params['BlacklistStatusTitle'] = 'Site blacklisted';
7944
+ $params['BlacklistStatus.CssClass'] = 'sucuriscan-red-tab';
7945
  }
7946
 
7947
+ foreach ($blacklist_types as $type => $group_title) {
7948
+ if (isset($scan_results['BLACKLIST'][$type])) {
7949
+ foreach ($scan_results['BLACKLIST'][$type] as $blres) {
7950
+ $css_blacklist = ($type == 'INFO') ? 'success' : 'danger';
7951
 
7952
+ $secvars['BlacklistStatusList'] .= SucuriScanTemplate::getSnippet(
7953
+ 'malwarescan-resblacklist',
7954
+ array(
7955
+ 'BlacklistStatusCssClass' => $css_blacklist,
7956
+ 'BlacklistStatusGroupTitle' => $group_title,
7957
+ 'BlacklistStatusReporterName' => $blres[0],
7958
+ 'BlacklistStatusReporterUrl' => $blres[1],
7959
+ )
7960
+ );
7961
  }
7962
  }
7963
  }
7964
 
7965
+ $params['BlacklistStatus.Content'] = SucuriScanTemplate::getSection('malwarescan-resblacklist', $secvars);
7966
 
7967
+ return $params;
7968
  }
7969
 
7970
  /**
7972
  * the HTML code to display the information in the malware scan page inside the
7973
  * modified files tab.
7974
  *
7975
+ * @param array $scan_results Array with information of the scanning.
7976
+ * @param array $params Array with psuedo-variables to build the template.
7977
+ * @return array Psuedo-variables to build the template including extra info.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7978
  */
7979
+ function sucuriscan_sitecheck_modified_files($scan_results = false, $params = array())
7980
+ {
7981
+ $params['ModifiedFiles.Content'] = sucuriscan_modified_files();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7982
 
7983
+ return $params;
7984
  }
7985
 
7986
  /**
7987
+ * Generate the HTML code for the firewall settings panel.
7988
  *
7989
  * @param string $api_key The CloudProxy API key.
7990
+ * @return string The parsed-content of the firewall settings panel.
7991
  */
7992
+ function sucuriscan_firewall_settings($api_key = '')
7993
+ {
7994
+ $params = array(
7995
+ 'Firewall.APIKey' => '',
7996
+ 'Firewall.APIKeyVisibility' => 'hidden',
7997
+ 'Firewall.APIKeyFormVisibility' => 'visible',
7998
+ 'Firewall.SettingsVisibility' => 'hidden',
7999
+ 'Firewall.SettingOptions' => '',
8000
  );
8001
 
8002
+ if ($api_key && array_key_exists('string', $api_key)) {
8003
+ $settings = SucuriScanAPI::getCloudproxySettings($api_key);
8004
 
8005
+ $params['Firewall.APIKeyVisibility'] = 'visible';
8006
+ $params['Firewall.APIKeyFormVisibility'] = 'hidden';
8007
+ $params['Firewall.APIKey'] = $api_key['string'];
8008
 
8009
+ if ($settings) {
8010
  $counter = 0;
8011
+ $params['Firewall.SettingsVisibility'] = 'visible';
8012
+ $settings = sucuriscan_explain_firewall_settings($settings);
 
 
 
 
 
 
8013
 
8014
+ foreach ($settings as $option_name => $option_value) {
8015
+ $css_class = ($counter % 2 === 0) ? 'alternate' : '';
8016
+ $option_title = ucwords(str_replace('_', "\x20", $option_name));
8017
 
8018
  // Generate a HTML list when the option's value is an array.
8019
+ if (is_array($option_value)) {
8020
+ $css_scrollable = count($option_value) > 10 ? 'sucuriscan-list-as-table-scrollable' : '';
8021
  $html_list = '<ul class="sucuriscan-list-as-table ' . $css_scrollable . '">';
8022
 
8023
+ foreach ($option_value as $single_value) {
8024
+ $html_list .= '<li>' . SucuriScan::escape($single_value) . '</li>';
8025
  }
8026
 
8027
  $html_list .= '</ul>';
8028
  $option_value = $html_list;
8029
+ } else {
8030
+ $option_value = SucuriScan::escape($option_value);
8031
  }
8032
 
8033
  // Parse the snippet template and replace the pseudo-variables.
8034
+ $params['Firewall.SettingOptions'] .= SucuriScanTemplate::getSnippet(
8035
+ 'firewall-settings',
8036
+ array(
8037
+ 'Firewall.OptionCssClass' => $css_class,
8038
+ 'Firewall.OptionName' => $option_title,
8039
+ 'Firewall.OptionValue' => $option_value,
8040
+ )
8041
+ );
8042
+ $counter++;
8043
  }
8044
  }
8045
  }
8046
 
8047
+ return SucuriScanTemplate::getSection('firewall-settings', $params);
8048
  }
8049
 
8050
  /**
8051
+ * Converts the value of some of the firewall settings into a human-readable
8052
  * text, for example changing numbers or variable names into a more explicit
8053
  * text so the administrator can understand the meaning of these settings.
8054
  *
8055
  * @param array $settings A hash with the settings of a CloudProxy account.
8056
  * @return array The explained version of the CloudProxy settings.
8057
  */
8058
+ function sucuriscan_explain_firewall_settings($settings = array())
8059
+ {
8060
+ $cache_modes = array(
8061
+ 'docache' => 'enabled (recommended)',
8062
+ 'sitecache' => 'site caching (using your site headers)',
8063
+ 'nocache' => 'minimal (only for a few minutes)',
8064
+ 'nocacheatall' => 'caching disabled (use with caution)',
8065
+ );
 
 
 
 
 
 
8066
 
8067
+ // TODO: Prefer Array over stdClass, modify the API library.
8068
+ $settings = @json_decode(json_encode($settings), true);
8069
+
8070
+ foreach ($settings as $keyname => $value) {
8071
+ if ($keyname == 'proxy_active') {
8072
+ $settings[$keyname] = ($value === 1) ? 'active' : 'not active';
8073
+ } elseif ($keyname == 'cache_mode') {
8074
+ if (array_key_exists($keyname, $cache_modes)) {
8075
+ $settings[$keyname] = $cache_modes[$keyname];
8076
+ } else {
8077
+ $settings[$keyname] = 'unknown';
8078
  }
8079
  }
 
 
8080
  }
8081
 
8082
+ return $settings;
8083
  }
8084
 
8085
  /**
8086
+ * Generate the HTML code for the firewall logs panel.
8087
  *
8088
+ * @param string $api_key The CloudProxy API key.
8089
+ * @return string The parsed-content of the firewall logs panel.
8090
  */
8091
+ function sucuriscan_firewall_auditlogs($api_key = '')
8092
+ {
8093
+ $date = date('Y-m-d');
8094
+ $params = array();
8095
 
8096
+ $params['AuditLogs.DateYears'] = sucuriscan_firewall_dates('years', $date);
8097
+ $params['AuditLogs.DateMonths'] = sucuriscan_firewall_dates('months', $date);
8098
+ $params['AuditLogs.DateDays'] = sucuriscan_firewall_dates('days', $date);
 
 
 
 
8099
 
8100
+ return SucuriScanTemplate::getSection('firewall-auditlogs', $params);
8101
  }
8102
 
8103
+ function sucuriscan_firewall_auditlogs_ajax()
8104
+ {
8105
+ if (SucuriScanRequest::post('form_action') == 'get_audit_logs') {
8106
+ $response = '';
8107
+ $api_key = SucuriScanAPI::getCloudproxyKey();
8108
+
8109
+ if ($api_key) {
8110
+ $query = SucuriScanRequest::post(':query');
8111
+ $month = SucuriScanRequest::post(':month');
8112
+ $year = SucuriScanRequest::post(':year');
8113
+ $day = SucuriScanRequest::post(':day');
8114
+ $limit = 50;
8115
+ $offset = 1;
8116
+
8117
+ if ($year && $month && $day) {
8118
+ $date = sprintf('%s-%s-%s', $year, $month, $day);
8119
+ } else {
8120
+ $date = date('Y-m-d');
8121
+ }
 
 
8122
 
8123
+ $auditlogs = SucuriScanAPI::firewallAuditLogs(
8124
+ $api_key,
8125
+ $date, /* Retrieve the data from this date. */
8126
+ $query, /* Filter the data to match this query. */
8127
+ $limit, /* Retrieve this maximum of data. */
8128
+ $offset /* Retrieve the data from this point. */
8129
+ );
8130
 
8131
+ if ($auditlogs && array_key_exists('total_lines', $auditlogs)) {
8132
+ $response = sucuriscan_firewall_auditlogs_entries($auditlogs['access_logs']);
 
 
8133
 
8134
+ if (empty($response)) {
8135
+ $response = '<tr><td>No data available for this filter.</td></tr>';
8136
+ }
8137
+ }
8138
+ } else {
8139
+ SucuriScanInterface::error('CloudProxy API Key was not found.');
8140
  }
8141
 
8142
+ print($response);
8143
+ exit(0);
 
 
 
 
 
 
 
8144
  }
 
 
 
 
 
 
 
8145
  }
8146
 
8147
+ function sucuriscan_firewall_auditlogs_entries($entries = array())
8148
+ {
8149
+ $output = '';
8150
+ $attributes = array(
8151
+ 'remote_addr',
8152
+ 'request_date',
8153
+ 'request_time',
8154
+ 'request_timezone',
8155
+ 'request_method',
8156
+ 'resource_path',
8157
+ 'http_protocol',
8158
+ 'http_status',
8159
+ 'http_status_title',
8160
+ 'http_referer',
8161
+ 'http_user_agent',
8162
+ 'sucuri_block_code',
8163
+ 'sucuri_block_reason',
8164
+ 'request_country_name',
8165
+ 'request_country_code',
8166
+ );
8167
 
8168
+ if (is_array($entries) && !empty($entries)) {
8169
  $counter = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8170
 
8171
+ foreach ($entries as $entry) {
8172
+ $data_set = array();
8173
+ $data_set['AccessLog.CssClass'] = ($counter % 2 == 0) ? '' : 'alternate';
 
8174
 
8175
+ foreach ($attributes as $attr) {
8176
+ // Generate variable name for the template pseudo-tags.
8177
+ $keyname = str_replace('_', "\x20", $attr);
8178
+ $keyname = ucwords($keyname);
8179
+ $keyname = str_replace("\x20", '', $keyname);
8180
+ $keyname = 'AccessLog.' . $keyname;
8181
 
8182
+ // Assign and escape variable value before rendering.
8183
+ if (array_key_exists($attr, $entry)) {
8184
+ $data_set[$keyname] = $entry[$attr];
8185
+ } else {
8186
+ $data_set[$keyname] = '';
 
 
 
8187
  }
8188
 
8189
+ // Special cases to convert value to readable data.
8190
+ if ($attr == 'resource_path' && $data_set[$keyname] == '/') {
8191
+ $data_set[$keyname] = '/ (root of the website)';
8192
+ } elseif ($attr == 'http_referer' && $data_set[$keyname] == '-') {
8193
+ $data_set[$keyname] = '- (no referer)';
8194
+ } elseif ($attr == 'request_country_name' && $data_set[$keyname] == '') {
8195
+ $data_set[$keyname] = 'Anonymous';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8196
  }
 
 
8197
  }
 
 
 
 
 
 
8198
 
8199
+ $output .= SucuriScanTemplate::getSnippet('firewall-auditlogs', $data_set);
8200
+ $counter++;
 
 
 
 
 
 
8201
  }
 
 
8202
  }
8203
 
8204
+ return $output;
8205
  }
8206
 
8207
  /**
8212
  * @param boolean $in_html Whether the list should be converted to a HTML select options or not.
8213
  * @return array Either an array with the expected values, or a HTML code.
8214
  */
8215
+ function sucuriscan_firewall_dates($type = '', $date = '', $in_html = true)
8216
+ {
8217
  $options = array();
8218
  $selected = '';
8219
+ $pattern = '/^([0-9]{4})\-([0-9]{2})\-([0-9]{2})$/';
8220
+ $s_year = '';
8221
+ $s_month = '';
8222
+ $s_day = '';
8223
 
8224
+ if (@preg_match($pattern, $date, $date_m)) {
8225
  $s_year = $date_m[1];
8226
  $s_month = $date_m[2];
8227
  $s_day = $date_m[3];
 
 
 
 
8228
  }
8229
 
8230
+ switch ($type) {
8231
  case 'years':
8232
  $selected = $s_year;
8233
+ $current_year = (int) date('Y');
8234
  $max_years = 5; /* Maximum number of years to keep the logs. */
8235
+ $options = range(($current_year - $max_years), $current_year);
8236
  break;
8237
  case 'months':
8238
  $selected = $s_month;
8252
  );
8253
  break;
8254
  case 'days':
8255
+ $options = range(1, 31);
8256
  $selected = $s_day;
8257
  break;
8258
  }
8259
 
8260
+ if ($in_html) {
8261
  $html_options = '';
8262
 
8263
+ foreach ($options as $key => $value) {
8264
+ if (is_numeric($value)) {
8265
+ $value = str_pad($value, 2, 0, STR_PAD_LEFT);
8266
  }
8267
 
8268
+ if ($type != 'months') {
8269
  $key = $value;
8270
  }
8271
 
8272
  $selected_tag = ( $key == $selected ) ? 'selected="selected"' : '';
8273
+ $html_options .= sprintf('<option value="%s" %s>%s</option>', $key, $selected_tag, $value);
8274
  }
8275
 
8276
  return $html_options;
8279
  return $options;
8280
  }
8281
 
8282
+ /**
8283
+ * Generate the HTML code for the firewall clear cache panel.
8284
+ *
8285
+ * @param string $nonce Identifier of the HTTP request for CSRF protection
8286
+ * @return string The parsed-content of the firewall clear cache panel.
8287
+ */
8288
+ function sucuriscan_firewall_clearcache($nonce)
8289
+ {
8290
+ $params = array();
8291
+
8292
+ if ($nonce) {
8293
+ // Flush the cache of the site(s) associated with the API key.
8294
+ if (SucuriScanRequest::post(':clear_cache') == 1) {
8295
+ $response = SucuriScanAPI::clearCloudproxyCache();
8296
+
8297
+ if ($response) {
8298
+ if (isset($response->messages[0])) {
8299
+ // Clear W3 Total Cache if it is installed.
8300
+ if (function_exists('w3tc_flush_all')) {
8301
+ w3tc_flush_all();
8302
+ }
8303
+
8304
+ SucuriScanInterface::info($response->messages[0]);
8305
+ } else {
8306
+ SucuriScanInterface::error('Could not clear the cache of your site, try later again.');
8307
+ }
8308
+ } else {
8309
+ SucuriScanInterface::error('CloudProxy is not enabled on your site, or your API key is invalid.');
8310
+ }
8311
+ }
8312
+ }
8313
+
8314
+ return SucuriScanTemplate::getSection('firewall-clearcache', $params);
8315
+ }
8316
+
8317
+ /**
8318
+ * CloudProxy firewall page.
8319
+ *
8320
+ * It checks whether the WordPress core files are the original ones, and the state
8321
+ * of the themes and plugins reporting the availability of updates. It also checks
8322
+ * the user accounts under the administrator group.
8323
+ *
8324
+ * @return void
8325
+ */
8326
+ function sucuriscan_firewall_page()
8327
+ {
8328
+ SucuriScanInterface::check_permissions();
8329
+
8330
+ // Process all form submissions.
8331
+ $nonce = SucuriScanInterface::check_nonce();
8332
+ sucuriscan_firewall_form_submissions($nonce);
8333
+
8334
+ // Get the dynamic values for the template variables.
8335
+ $api_key = SucuriScanAPI::getCloudproxyKey();
8336
+
8337
+ // Page pseudo-variables initialization.
8338
+ $params = array(
8339
+ 'PageTitle' => 'Firewall WAF',
8340
+ 'Firewall.Settings' => sucuriscan_firewall_settings($api_key),
8341
+ 'Firewall.AuditLogs' => sucuriscan_firewall_auditlogs($api_key),
8342
+ 'Firewall.ClearCache' => sucuriscan_firewall_clearcache($nonce),
8343
+ );
8344
+
8345
+ echo SucuriScanTemplate::getTemplate('firewall', $params);
8346
+ }
8347
+
8348
+ /**
8349
+ * Handle an Ajax request for this specific page.
8350
+ *
8351
+ * @return mixed.
8352
+ */
8353
+ function sucuriscan_firewall_ajax()
8354
+ {
8355
+ SucuriScanInterface::check_permissions();
8356
+
8357
+ if (SucuriScanInterface::check_nonce()) {
8358
+ sucuriscan_firewall_auditlogs_ajax();
8359
+ }
8360
+
8361
+ wp_die();
8362
+ }
8363
+
8364
+ /**
8365
+ * Process the requests sent by the form submissions originated in the firewall
8366
+ * page, all forms must have a nonce field that will be checked against the one
8367
+ * generated in the template render function.
8368
+ *
8369
+ * @return void
8370
+ */
8371
+ function sucuriscan_firewall_form_submissions($nonce)
8372
+ {
8373
+ if ($nonce) {
8374
+ // Add and/or Update the Sucuri WAF API Key (do it before anything else).
8375
+ $option_name = ':cloudproxy_apikey';
8376
+ $api_key = SucuriScanRequest::post($option_name);
8377
+
8378
+ if ($api_key !== false) {
8379
+ $api_key = trim($api_key);
8380
+
8381
+ if (SucuriScanAPI::isValidCloudproxyKey($api_key)) {
8382
+ SucuriScanOption::update_option($option_name, $api_key);
8383
+ SucuriScanInterface::info('CloudProxy API key saved successfully');
8384
+ SucuriScanOption::setRevProxy('enable');
8385
+ SucuriScanOption::setAddrHeader('HTTP_X_SUCURI_CLIENTIP');
8386
+ } else {
8387
+ SucuriScanInterface::error('Invalid CloudProxy API key.');
8388
+ }
8389
+ }
8390
+
8391
+ // Delete CloudProxy API key from the plugin.
8392
+ if (SucuriScanRequest::post(':delete_wafkey') !== false) {
8393
+ SucuriScanOption::delete_option($option_name);
8394
+ SucuriScanInterface::info('CloudProxy API key removed successfully');
8395
+ SucuriScanOption::setRevProxy('disable');
8396
+ SucuriScanOption::setAddrHeader('REMOTE_ADDR');
8397
+ }
8398
+ }
8399
+ }
8400
+
8401
  /**
8402
  * Project hardening library.
8403
  *
8416
  * Apache/PHP Hardener that can, for example, deactivate unneeded features in
8417
  * configuration files or perform various other protective measures.
8418
  */
8419
+ class SucuriScanHardening extends SucuriScan
8420
+ {
8421
 
8422
  /**
8423
  * Returns a list of access control rules for the Apache web server that can be
8631
  *
8632
  * @return void
8633
  */
8634
+ function sucuriscan_hardening_page()
8635
+ {
8636
  SucuriScanInterface::check_permissions();
8637
 
8638
  $template_variables = array(
8640
  'Hardening.Whitelist' => sucuriscan_hardening_whitelist(),
8641
  );
8642
 
8643
+ echo SucuriScanTemplate::getTemplate('hardening', $template_variables);
8644
  }
8645
 
8646
+ function sucuriscan_hardening_panel()
8647
+ {
8648
  if (SucuriScanRequest::post(':run_hardening')
8649
+ && !SucuriScanInterface::check_nonce()
8650
  ) {
8651
  unset($_POST['sucuriscan_run_hardening']);
8652
  }
8679
  $template_variables['Hardening.WpIncludes'] = sucuriscan_harden_wpincludes();
8680
  }
8681
 
8682
+ return SucuriScanTemplate::getSection('hardening-panel', $template_variables);
8683
  }
8684
 
8685
+ function sucuriscan_hardening_whitelist()
8686
+ {
8687
  $template_variables = array(
8688
  'HardeningWhitelist.List' => '',
8689
  'HardeningWhitelist.NoItemsVisibility' => 'visible',
8732
  foreach ($files as $file) {
8733
  $css_class = ($counter % 2 === 0) ? '' : 'alternate';
8734
  $fregexp = sprintf('%s/.*/%s', $folder, $file);
8735
+ $html = SucuriScanTemplate::getSnippet(
8736
+ 'hardening-whitelist',
8737
+ array(
8738
+ 'HardeningWhitelist.CssClass' => $css_class,
8739
+ 'HardeningWhitelist.Regexp' => $fregexp,
8740
+ 'HardeningWhitelist.Folder' => $folder,
8741
+ 'HardeningWhitelist.File' => $file,
8742
+ )
8743
+ );
8744
  $template_variables['HardeningWhitelist.List'] .= $html;
8745
  $counter++;
8746
  }
8747
  }
8748
  }
8749
 
8750
+ return SucuriScanTemplate::getSection('hardening-whitelist', $template_variables);
8751
  }
8752
 
8753
  /**
8764
  * @param string $updatemsg Optional explanation of the hardening after the submission of the form.
8765
  * @return void
8766
  */
8767
+ function sucuriscan_harden_status($title = '', $status = 0, $type = '', $messageok = '', $messagewarn = '', $desc = null, $updatemsg = null)
8768
+ {
8769
  $template_variables = array(
8770
+ 'Hardening.Title' => $title,
8771
  'Hardening.Description' => '',
8772
  'Hardening.Status' => 'unknown',
8773
  'Hardening.FieldName' => '',
8777
  'Hardening.UpdateMessage' => '',
8778
  );
8779
 
8780
+ if (is_null($type)) {
8781
  $type = 'unknown';
8782
  $template_variables['Hardening.FieldAttributes'] = 'disabled="disabled"';
8783
  }
8784
 
8785
  $template_variables['Hardening.Status'] = (string) $status;
8786
 
8787
+ if ($status === 1) {
8788
  $template_variables['Hardening.FieldName'] = $type . '_unharden';
8789
  $template_variables['Hardening.FieldValue'] = 'Revert hardening';
8790
  $template_variables['Hardening.Information'] = $messageok;
8791
+ } elseif ($status === 0) {
8792
  $template_variables['Hardening.FieldName'] = $type;
8793
  $template_variables['Hardening.FieldValue'] = 'Harden';
8794
  $template_variables['Hardening.Information'] = $messagewarn;
8799
  $template_variables['Hardening.FieldAttributes'] = 'disabled="disabled"';
8800
  }
8801
 
8802
+ if (!is_null($desc)) {
8803
  $template_variables['Hardening.Description'] = '<p>' . $desc . '</p>';
8804
  }
8805
 
8806
+ if (!is_null($updatemsg)) {
8807
  $template_variables['Hardening.UpdateMessage'] = '<p>' . $updatemsg . '</p>';
8808
  }
8809
 
8810
+ return SucuriScanTemplate::getSnippet('hardening', $template_variables);
8811
  }
8812
 
8813
  /**
8816
  *
8817
  * @return void
8818
  */
8819
+ function sucuriscan_harden_version()
8820
+ {
8821
  $site_version = SucuriScan::site_version();
8822
  $updates = get_core_updates();
8823
+ $cp = (!is_array($updates) || empty($updates) ? 1 : 0);
8824
 
8825
+ if (isset($updates[0]) && $updates[0] instanceof stdClass) {
8826
+ if ($updates[0]->response == 'latest'
 
8827
  || $updates[0]->response == 'development'
8828
  ) {
8829
  $cp = 1;
8830
  }
8831
  }
8832
 
8833
+ if (strcmp($site_version, '3.7') < 0) {
8834
  $cp = 0;
8835
  }
8836
 
8839
  to the source code are made public, if there were security fixes then
8840
  someone with malicious intent can use this information to attack any site
8841
  that has not been upgraded.';
8842
+ $messageok = sprintf('Your WordPress installation (%s) is current.', $site_version);
8843
  $messagewarn = sprintf(
8844
  'Your current version (%s) is not current.<br>
8845
  <a href="update-core.php" class="button-primary">Update now!</a>',
8846
  $site_version
8847
  );
8848
 
8849
+ return sucuriscan_harden_status('Verify WordPress version', $cp, null, $messageok, $messagewarn, $initial_msg);
8850
  }
8851
 
8852
  /**
8856
  *
8857
  * @return void
8858
  */
8859
+ function sucuriscan_harden_removegenerator()
8860
+ {
8861
  return sucuriscan_harden_status(
8862
  'Remove WordPress version',
8863
  1,
8869
  );
8870
  }
8871
 
8872
+ function sucuriscan_harden_nginx_phpfpm()
8873
+ {
8874
  $description = 'It seems that you are using the Nginx web server, if that is
8875
  the case then you will need to add the following code into the global
8876
  <code>nginx.conf</code> file or the virtualhost associated with this
8877
  website. Choose the correct rules for the directories that you want to
8878
  protect. If you encounter errors after restart the web server then revert
8879
  the changes and contact the support team of your hosting company, or read
8880
+ the official article about <a href="https://codex.wordpress.org/Nginx">
8881
  WordPress on Nginx</a>.</p>';
8882
 
8883
  $description .= "<pre class='code'># Block PHP files in uploads directory.\nlocation ~* /(?:uploads|files)/.*\.php$ {\n\x20\x20deny all;\n}</pre>";
8911
  *
8912
  * @return void
8913
  */
8914
+ function sucuriscan_harden_upload()
8915
+ {
8916
  $dpath = WP_CONTENT_DIR . '/uploads';
8917
 
8918
+ if (SucuriScanRequest::post(':run_hardening')) {
8919
+ if (SucuriScanRequest::post(':harden_upload')) {
8920
+ $result = SucuriScanHardening::harden_directory($dpath);
8921
 
8922
+ if ($result === true) {
8923
  $message = 'Hardening applied to the uploads directory';
8924
+ SucuriScanEvent::report_notice_event($message);
8925
+ SucuriScanInterface::info($message);
8926
  } else {
8927
+ SucuriScanInterface::error('Error hardening directory, check the permissions.');
8928
  }
8929
+ } elseif (SucuriScanRequest::post(':harden_upload_unharden')) {
8930
+ $result = SucuriScanHardening::unharden_directory($dpath);
8931
 
8932
+ if ($result === true) {
8933
  $message = 'Hardening reverted in the uploads directory';
8934
+ SucuriScanEvent::report_error_event($message);
8935
+ SucuriScanInterface::info($message);
8936
  } else {
8937
+ SucuriScanInterface::info('Access file is not writable, check the permissions.');
8938
  }
8939
  }
8940
  }
8941
 
8942
  // Check whether the directory is already hardened or not.
8943
+ $is_hardened = SucuriScanHardening::is_hardened($dpath);
8944
  $cp = ( $is_hardened === true ) ? 1 : 0;
8945
 
8946
  $description = 'It checks if the uploads directory of this site allows the direct execution'
8972
  *
8973
  * @return void
8974
  */
8975
+ function sucuriscan_harden_wpcontent()
8976
+ {
8977
+ if (SucuriScanRequest::post(':run_hardening')) {
8978
+ if (SucuriScanRequest::post(':harden_wpcontent')) {
8979
+ $result = SucuriScanHardening::harden_directory(WP_CONTENT_DIR);
8980
 
8981
+ if ($result === true) {
8982
  $message = 'Hardening applied to the content directory';
8983
+ SucuriScanEvent::report_notice_event($message);
8984
+ SucuriScanInterface::info($message);
8985
  } else {
8986
+ SucuriScanInterface::error('Error hardening directory, check the permissions.');
8987
  }
8988
+ } elseif (SucuriScanRequest::post(':harden_wpcontent_unharden')) {
8989
+ $result = SucuriScanHardening::unharden_directory(WP_CONTENT_DIR);
8990
 
8991
+ if ($result === true) {
8992
  $message = 'Hardening reverted in the content directory';
8993
+ SucuriScanEvent::report_error_event($message);
8994
+ SucuriScanInterface::info($message);
8995
  } else {
8996
+ SucuriScanInterface::info('Access file is not writable, check the permissions.');
8997
  }
8998
  }
8999
  }
9000
 
9001
  // Check whether the directory is already hardened or not.
9002
+ $is_hardened = SucuriScanHardening::is_hardened(WP_CONTENT_DIR);
9003
  $cp = ( $is_hardened === true ) ? 1 : 0;
9004
 
9005
  $description = 'This option blocks direct access to any PHP file located under the content'
9030
  *
9031
  * @return void
9032
  */
9033
+ function sucuriscan_harden_wpincludes()
9034
+ {
9035
  $dpath = ABSPATH . '/wp-includes';
9036
 
9037
+ if (SucuriScanRequest::post(':run_hardening')) {
9038
+ if (SucuriScanRequest::post(':harden_wpincludes')) {
9039
+ $result = SucuriScanHardening::harden_directory($dpath);
9040
 
9041
+ if ($result === true) {
9042
  $message = 'Hardening applied to the library directory';
9043
  SucuriScanHardening::whitelist('wp-tinymce.php', 'wp-includes');
9044
  SucuriScanHardening::whitelist('ms-files.php', 'wp-includes');
9045
+ SucuriScanEvent::report_notice_event($message);
9046
+ SucuriScanInterface::info($message);
9047
  } else {
9048
+ SucuriScanInterface::error('Error hardening directory, check the permissions.');
9049
  }
9050
+ } elseif (SucuriScanRequest::post(':harden_wpincludes_unharden')) {
9051
+ $result = SucuriScanHardening::unharden_directory($dpath);
9052
 
9053
+ if ($result === true) {
9054
  $message = 'Hardening reverted in the library directory';
9055
  SucuriScanHardening::dewhitelist('wp-tinymce.php', 'wp-includes');
9056
  SucuriScanHardening::dewhitelist('ms-files.php', 'wp-includes');
9057
+ SucuriScanEvent::report_error_event($message);
9058
+ SucuriScanInterface::info($message);
9059
  } else {
9060
+ SucuriScanInterface::info('Access file is not writable, check the permissions.');
9061
  }
9062
  }
9063
  }
9064
 
9065
  // Check whether the directory is already hardened or not.
9066
+ $is_hardened = SucuriScanHardening::is_hardened($dpath);
9067
  $cp = ( $is_hardened === true ) ? 1 : 0;
9068
 
9069
  return sucuriscan_harden_status(
9083
  *
9084
  * @return void
9085
  */
9086
+ function sucuriscan_harden_phpversion()
9087
+ {
9088
  $phpv = phpversion();
9089
+ $cp = ( strncmp($phpv, '5.', 2) < 0 ) ? 0 : 1;
9090
 
9091
  return sucuriscan_harden_status(
9092
  'Verify PHP version',
9104
  *
9105
  * @return void
9106
  */
9107
+ function sucuriscan_cloudproxy_enabled()
9108
+ {
9109
  $btn_string = '';
9110
  $proxy_info = SucuriScan::is_behind_cloudproxy();
9111
  $status = 1;
9112
 
9113
  $description = 'A WAF is a protection layer for your web site, blocking all sort of attacks (brute force attempts, '
9114
  . 'DDoS, SQL injections, etc) and helping it remain malware and blacklist free. This test checks if your site is '
9115
+ . 'using <a href="https://cloudproxy.sucuri.net/" target="_blank">Sucuri\'s CloudProxy WAF</a> to protect your site.';
9116
 
9117
+ if ($proxy_info === false) {
9118
  $status = 0;
9119
+ $btn_string = '<a href="https://goo.gl/qfNkMq" target="_blank" class="button button-primary">Harden</a>';
9120
  }
9121
 
9122
  return sucuriscan_harden_status(
9140
  *
9141
  * @return void
9142
  */
9143
+ function sucuriscan_harden_secretkeys()
9144
+ {
9145
  $wp_config_path = SucuriScan::get_wpconfig_path();
9146
  $current_keys = SucuriScanOption::get_security_keys();
9147
 
9148
+ if ($wp_config_path) {
9149
  $cp = 1;
9150
+ $wp_config_path = SucuriScan::escape($wp_config_path);
9151
+ $message = 'The main configuration file was found at: <code>' . $wp_config_path . '</code><br>';
9152
 
9153
+ if (!empty($current_keys['bad']) || !empty($current_keys['missing'])) {
 
 
 
9154
  $cp = 0;
9155
  }
9156
  } else {
9159
  }
9160
 
9161
  $message .= '<br>It checks whether you have proper random keys/salts created for WordPress. A
9162
+ <a href="https://codex.wordpress.org/Editing_wp-config.php#Security_Keys" target="_blank">
9163
  secret key</a> makes your site harder to hack and access harder to crack by adding
9164
  random elements to the password. In simple terms, a secret key is a password with
9165
  elements that make it harder to generate enough options to break through your
9166
  security barriers.';
9167
  $messageok = 'Security keys and salts not set, we recommend to create them for security reasons'
9168
+ . '<a href="' . SucuriScanTemplate::getUrl('posthack') . '" class="button button-primary">'
9169
  . 'Harden</a>';
9170
 
9171
  return sucuriscan_harden_status(
9186
  *
9187
  * @return void
9188
  */
9189
+ function sucuriscan_harden_readme()
9190
+ {
9191
  $upmsg = null;
9192
+ $cp = is_readable(ABSPATH.'/readme.html') ? 0 : 1;
9193
 
9194
  // TODO: After hardening create an option to automatically remove this after WP upgrade.
9195
+ if (SucuriScanRequest::post(':run_hardening')) {
9196
+ if (SucuriScanRequest::post(':harden_readme') && $cp == 0) {
9197
+ if (@unlink(ABSPATH.'/readme.html') === false) {
9198
+ $upmsg = SucuriScanInterface::error('Unable to remove <code>readme.html</code> file.');
9199
  } else {
9200
  $cp = 1;
9201
  $message = 'Hardening applied to the <code>readme.html</code> file';
9202
+ SucuriScanEvent::report_notice_event($message);
9203
+ SucuriScanInterface::info($message);
9204
  }
9205
+ } elseif (SucuriScanRequest::post(':harden_readme_unharden')) {
9206
+ SucuriScanInterface::error('We can not revert this action, you must create the <code>readme.html</code> manually.');
9207
  }
9208
  }
9209
 
9224
  *
9225
  * @return void
9226
  */
9227
+ function sucuriscan_harden_adminuser()
9228
+ {
9229
  global $wpdb;
9230
 
9231
  $upmsg = null;
9235
  'search_columns' => array( 'user_login' ),
9236
  ));
9237
  $results = $user_query->get_results();
9238
+ $account_removed = ( count($results) === 0 ? 1 : 0 );
9239
 
9240
+ if ($account_removed === 0) {
9241
  $upmsg = '<i><strong>Notice.</strong> We do not offer an option to automatically change the user name.
9242
+ Go to the <a href="'.SucuriScan::admin_url('users.php').'" target="_blank">user list</a> and create
9243
+ a new administrator user. Once created, log in as that user and remove the default <code>admin</code>
9244
  (make sure to assign all the admin posts to the new user too).</i>';
9245
  }
9246
 
9260
  *
9261
  * @return void
9262
  */
9263
+ function sucuriscan_harden_fileeditor()
9264
+ {
9265
+ $file_editor_disabled = defined('DISALLOW_FILE_EDIT') ? DISALLOW_FILE_EDIT : false;
9266
 
9267
+ if (SucuriScanRequest::post(':run_hardening')) {
9268
+ $current_time = date('r');
9269
  $wp_config_path = SucuriScan::get_wpconfig_path();
9270
 
9271
+ $wp_config_writable = ( file_exists($wp_config_path) && is_writable($wp_config_path) ) ? true : false;
9272
+ $new_wpconfig = $wp_config_writable ? @file_get_contents($wp_config_path) : '';
9273
 
9274
+ if (SucuriScanRequest::post(':harden_fileeditor')) {
9275
+ if ($wp_config_writable) {
9276
+ if (preg_match('/(.*define\(.DB_COLLATE..*)/', $new_wpconfig, $match)) {
9277
  $disallow_fileedit_definition = "\n\ndefine('DISALLOW_FILE_EDIT', TRUE); // Sucuri Security: {$current_time}\n";
9278
+ $new_wpconfig = str_replace($match[0], $match[0].$disallow_fileedit_definition, $new_wpconfig);
9279
  }
9280
 
9281
  $file_editor_disabled = true;
9282
+ @file_put_contents($wp_config_path, $new_wpconfig, LOCK_EX);
9283
  $message = 'Hardening applied to the plugin and theme editor';
9284
+ SucuriScanEvent::report_notice_event($message);
9285
+ SucuriScanInterface::info($message);
9286
  } else {
9287
+ SucuriScanInterface::error('The <code>wp-config.php</code> file is not in the default location
9288
  or is not writable, you will need to put the following code manually there:
9289
+ <code>define("DISALLOW_FILE_EDIT", TRUE);</code>');
9290
  }
9291
+ } elseif (SucuriScanRequest::post(':harden_fileeditor_unharden')) {
9292
+ if (preg_match("/(.*define\('DISALLOW_FILE_EDIT', TRUE\);.*)/", $new_wpconfig, $match)) {
9293
+ if ($wp_config_writable) {
9294
+ $new_wpconfig = str_replace("\n{$match[1]}", '', $new_wpconfig);
9295
+ file_put_contents($wp_config_path, $new_wpconfig, LOCK_EX);
9296
  $file_editor_disabled = false;
9297
  $message = 'Hardening reverted in the plugin and theme editor';
9298
+ SucuriScanEvent::report_error_event($message);
9299
+ SucuriScanInterface::info($message);
9300
  } else {
9301
+ SucuriScanInterface::error('The <code>wp-config.php</code> file is not in the default location
9302
  or is not writable, you will need to remove the following code manually from there:
9303
+ <code>define("DISALLOW_FILE_EDIT", TRUE);</code>');
9304
  }
9305
  } else {
9306
+ SucuriScanInterface::error('The theme and plugin editor are not disabled from the configuration file.');
9307
  }
9308
  }
9309
  }
9331
  *
9332
  * @return void
9333
  */
9334
+ function sucuriscan_harden_dbtables()
9335
+ {
9336
  global $table_prefix;
9337
 
9338
  $hardened = ( $table_prefix == 'wp_' ? 0 : 1 );
9353
  *
9354
  * @return void
9355
  */
9356
+ function sucuriscan_harden_errorlog()
9357
+ {
9358
  $hardened = 1;
9359
+ $log_filename = SucuriScan::ini_get('error_log');
9360
+ $scan_errorlogs = SucuriScanOption::get_option(':scan_errorlogs');
9361
 
9362
  $description = 'PHP uses files named as <code>' . $log_filename . '</code> to log errors found in '
9363
  . 'the code, these files may leak sensitive information of your project allowing an attacker '
9365
  . 'a development environment, and remove them in production mode.';
9366
 
9367
  // Search error log files in the project.
9368
+ if ($scan_errorlogs != 'disabled') {
9369
  $file_info = new SucuriScanFileInfo();
9370
  $file_info->ignore_files = false;
9371
  $file_info->ignore_directories = false;
9372
+ $error_logs = $file_info->find_file($log_filename);
9373
+ $total_log_files = count($error_logs);
9374
  } else {
9375
  $hardened = 2;
9376
  $error_logs = array();
9382
  }
9383
 
9384
  // Remove every error log file found in the filesystem scan.
9385
+ if (SucuriScanRequest::post(':run_hardening')) {
9386
+ if (SucuriScanRequest::post(':harden_errorlog')) {
9387
  $removed_logs = 0;
9388
+ SucuriScanEvent::report_notice_event(sprintf(
9389
  'Error log files deleted: (multiple entries): %s',
9390
+ @implode(',', $error_logs)
9391
+ ));
9392
 
9393
+ foreach ($error_logs as $i => $error_log_path) {
9394
+ if (unlink($error_log_path)) {
9395
  unset($error_logs[ $i ]);
9396
  $removed_logs += 1;
9397
  }
9398
  }
9399
 
9400
+ SucuriScanInterface::info('Error log files deleted <code>' . $removed_logs . ' out of ' . $total_log_files . '</code>');
9401
  }
9402
  }
9403
 
9404
  // List the error log files in a HTML table.
9405
+ if (!empty($error_logs)) {
9406
  $hardened = 0;
9407
  $description .= '</p><ul class="sucuriscan-list-as-table">';
9408
 
9409
+ foreach ($error_logs as $error_log_path) {
9410
+ $error_log_path = str_replace(ABSPATH, '/', $error_log_path);
9411
+ $description .= '<li>' . SucuriScan::escape($error_log_path) . '</li>';
9412
  }
9413
 
9414
  $description .= '</ul><p>';
9441
  // Process all form submissions.
9442
  sucuriscan_integrity_form_submissions();
9443
 
9444
+ $params = array(
9445
  'WordpressVersion' => sucuriscan_wordpress_outdated(),
9446
  'CoreFiles' => sucuriscan_core_files(),
9447
  'AuditReports' => sucuriscan_auditreport(),
9448
  'AuditLogs' => sucuriscan_auditlogs(),
9449
  );
9450
 
9451
+ echo SucuriScanTemplate::getTemplate('integrity', $params);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9452
  }
9453
 
9454
  /**
9455
+ * Handle an Ajax request for this specific page.
 
9456
  *
9457
+ * @return mixed.
 
 
9458
  */
9459
+ function sucuriscan_ajax()
9460
  {
9461
+ SucuriScanInterface::check_permissions();
 
 
 
 
 
 
 
9462
 
9463
+ if (SucuriScanInterface::check_nonce()) {
9464
+ sucuriscan_core_files_ajax();
9465
  }
9466
 
9467
+ wp_die();
9468
  }
9469
 
9470
  /**
9477
  {
9478
  // Initialize the values for the pagination.
9479
  $max_per_page = SUCURISCAN_AUDITLOGS_PER_PAGE;
9480
+ $page_number = SucuriScanTemplate::pageNumber();
9481
  $logs_limit = $page_number * $max_per_page;
9482
+ $audit_logs = SucuriScanAPI::getLogs($logs_limit);
9483
 
9484
+ $params = array(
9485
  'PageTitle' => 'Audit Logs',
9486
  'AuditLogs.List' => '',
9487
  'AuditLogs.Count' => 0,
9501
  if ($audit_logs->total_entries >= $max_per_page
9502
  && SucuriScanOption::is_disabled(':audit_report')
9503
  ) {
9504
+ $params['AuditLogs.EnableAuditReportVisibility'] = 'visible';
9505
  }
9506
 
9507
  for ($i = $iterator_start; $i < $total_items; $i++) {
9515
  $css_class = ( $counter_i % 2 == 0 ) ? '' : 'alternate';
9516
  $snippet_data = array(
9517
  'AuditLog.CssClass' => $css_class,
9518
+ 'AuditLog.Event' => $audit_log['event'],
9519
+ 'AuditLog.EventTitle' => ucfirst($audit_log['event']),
9520
+ 'AuditLog.Timestamp' => $audit_log['timestamp'],
9521
  'AuditLog.DateTime' => SucuriScan::datetime($audit_log['timestamp']),
9522
+ 'AuditLog.Account' => $audit_log['account'],
9523
+ 'AuditLog.Username' => $audit_log['username'],
9524
+ 'AuditLog.RemoteAddress' => $audit_log['remote_addr'],
9525
+ 'AuditLog.Message' => $audit_log['message'],
9526
  'AuditLog.Extra' => '',
9527
  );
9528
 
9536
  $snippet_data['AuditLog.Extra'] .= '</ul>';
9537
  }
9538
 
9539
+ $params['AuditLogs.List'] .= SucuriScanTemplate::getSnippet('integrity-auditlogs', $snippet_data);
9540
  $counter_i += 1;
9541
  }
9542
  }
9543
 
9544
+ $params['AuditLogs.Count'] = $counter_i;
9545
+ $params['AuditLogs.NoItemsVisibility'] = 'hidden';
9546
 
9547
  if ($total_items > 1) {
9548
  $max_pages = ceil($audit_logs->total_entries / $max_per_page);
9552
  }
9553
 
9554
  if ($max_pages > 1) {
9555
+ $params['AuditLogs.PaginationVisibility'] = 'visible';
9556
+ $params['AuditLogs.PaginationLinks'] = SucuriScanTemplate::pagination(
9557
  '%%SUCURI.URL.Home%%',
9558
  $max_per_page * $max_pages,
9559
  $max_per_page
9562
  }
9563
  }
9564
 
9565
+ return SucuriScanTemplate::getSection('integrity-auditlogs', $params);
9566
  }
9567
+
9568
  /**
9569
  * Print a HTML code with the content of the logs audited by the remote Sucuri
9570
  * API service, this page is part of the monitoring tool.
9577
  $logs4report = SucuriScanOption::get_option(':logs4report');
9578
 
9579
  if (SucuriScanOption::is_enabled(':audit_report')) {
9580
+ $audit_report = SucuriScanAPI::getAuditReport($logs4report);
9581
  }
9582
 
9583
+ $params = array(
9584
  'PageTitle' => 'Audit Reports',
9585
  'AuditReport.EventColors' => '',
9586
  'AuditReport.EventsPerType' => '',
9593
  );
9594
 
9595
  if ($audit_report) {
9596
+ $params['AuditReport.EventColors'] = @implode(',', $audit_report['event_colors']);
9597
 
9598
  // Generate report chart data for the events per type.
9599
  foreach ($audit_report['events_per_type'] as $event => $times) {
9600
+ $params['AuditReport.EventsPerType'] .= sprintf(
9601
  "[ '%s', %d ],\n",
9602
  ucwords($event . "\x20events"),
9603
  $times
9606
 
9607
  // Generate report chart data for the events per login.
9608
  foreach ($audit_report['events_per_login'] as $event => $times) {
9609
+ $params['AuditReport.EventsPerLogin'] .= sprintf(
9610
  "[ '%s', %d ],\n",
9611
  ucwords($event . "\x20logins"),
9612
  $times
9615
 
9616
  // Generate report chart data for the events per user.
9617
  foreach ($audit_report['events_per_user'] as $event => $times) {
9618
+ $params['AuditReport.EventsPerUserCategories'] .= sprintf('"%s",', $event);
9619
+ $params['AuditReport.EventsPerUserSeries'] .= sprintf('%d,', $times);
9620
  }
9621
 
9622
  // Generate report chart data for the events per remote address.
9623
  foreach ($audit_report['events_per_ipaddress'] as $event => $times) {
9624
+ $params['AuditReport.EventsPerIPAddressCategories'] .= sprintf('"%s",', $event);
9625
+ $params['AuditReport.EventsPerIPAddressSeries'] .= sprintf('%d,', $times);
9626
  }
9627
 
9628
+ return SucuriScanTemplate::getSection('integrity-auditreport', $params);
9629
  }
9630
 
9631
  return '';
9640
  {
9641
  $site_version = SucuriScan::site_version();
9642
  $updates = get_core_updates();
9643
+ $cp = (!is_array($updates) || empty($updates) ? 1 : 0);
9644
 
9645
+ $params = array(
9646
  'WordPress.Version' => $site_version,
9647
  'WordPress.NewVersion' => '0.0.0',
9648
  'WordPress.NewLocale' => 'default',
9649
+ 'WordPress.UpdateURL' => SucuriScan::admin_url('update-core.php'),
9650
  'WordPress.DownloadURL' => '#',
9651
  'WordPress.UpdateVisibility' => 'hidden',
9652
  );
9656
  && property_exists($updates[0], 'version')
9657
  && property_exists($updates[0], 'download')
9658
  ) {
9659
+ $params['WordPress.NewVersion'] = $updates[0]->version;
9660
+ $params['WordPress.DownloadURL'] = $updates[0]->download;
9661
 
9662
  if (property_exists($updates[0], 'locale')) {
9663
+ $params['WordPress.NewLocale'] = $updates[0]->locale;
9664
  }
9665
 
9666
  if ($updates[0]->response == 'latest'
9672
  }
9673
 
9674
  if ($cp == 0) {
9675
+ $params['WordPress.UpdateVisibility'] = 'visible';
9676
  }
9677
 
9678
+ return SucuriScanTemplate::getSection('integrity-wpoutdate', $params);
9679
  }
9680
 
9681
  /**
9685
  * send a notification to the administrator with a list of files that were added,
9686
  * modified and/or deleted so far.
9687
  *
9688
+ * @return string HTML code with a list of files that were affected.
 
9689
  */
9690
+ function sucuriscan_core_files()
9691
+ {
9692
+ $params = array();
9693
+
9694
+ return SucuriScanTemplate::getSection('corefiles-page', $params);
9695
+ }
9696
+
9697
+ function sucuriscan_core_files_ajax()
9698
+ {
9699
+ if (SucuriScanRequest::post('form_action') == 'get_core_files') {
9700
+ $response = sucuriscan_core_files_data();
9701
+
9702
+ print($response);
9703
+ exit(0);
9704
+ }
9705
+ }
9706
+
9707
+ function sucuriscan_core_files_data($send_email = false)
9708
  {
 
9709
  $affected_files = 0;
9710
+ $site_version = SucuriScan::site_version();
9711
 
9712
+ $params = array(
9713
  'CoreFiles.List' => '',
9714
  'CoreFiles.ListCount' => 0,
9715
+ 'CoreFiles.RemoteChecksumsURL' => '',
9716
  'CoreFiles.BadVisibility' => 'hidden',
9717
+ 'CoreFiles.GoodVisibility' => 'visible',
9718
  'CoreFiles.FailureVisibility' => 'hidden',
9719
+ 'CoreFiles.NotFixableVisibility' => 'hidden',
9720
  );
9721
 
9722
  if ($site_version && SucuriScanOption::is_enabled(':scan_checksums')) {
9723
  // Check if there are added, removed, or modified files.
9724
  $latest_hashes = sucuriscan_check_core_integrity($site_version);
9725
+ $params['CoreFiles.RemoteChecksumsURL'] =
9726
+ 'https://api.wordpress.org/core/checksums/1.0/'
9727
  . '?version=' . $site_version . '&locale=en_US';
9728
 
9729
  if ($latest_hashes) {
9730
  $cache = new SucuriScanCache('integrity');
9731
+ $ignored_files = $cache->getAll();
9732
  $counter = 0;
9733
 
9734
  foreach ($latest_hashes as $list_type => $file_list) {
9735
+ if ($list_type == 'stable' || empty($file_list)) {
 
 
9736
  continue;
9737
  }
9738
 
9753
  // Add extra information to the file list.
9754
  $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
9755
  $file_size = @filesize($full_filepath);
 
9756
  $is_fixable_text = '';
9757
 
9758
  // Check whether the file can be fixed automatically or not.
9759
  if ($file_info['is_fixable'] !== true) {
9760
  $css_class .= ' sucuriscan-opacity';
9761
+ $is_fixable_text = '(not fixable)';
9762
+ $params['CoreFiles.NotFixableVisibility'] = 'visible';
9763
  }
9764
 
9765
  // Generate the HTML code from the snippet template for this file.
9766
+ $params['CoreFiles.List'] .= SucuriScanTemplate::getSnippet(
9767
+ 'corefiles',
9768
+ array(
9769
+ 'CoreFiles.CssClass' => $css_class,
9770
+ 'CoreFiles.StatusType' => $list_type,
9771
+ 'CoreFiles.FilePath' => $file_path,
9772
+ 'CoreFiles.FileSize' => $file_size,
9773
+ 'CoreFiles.FileSizeHuman' => SucuriScan::human_filesize($file_size),
9774
+ 'CoreFiles.FileSizeNumber' => number_format($file_size),
9775
+ 'CoreFiles.ModifiedAt' => SucuriScan::datetime($file_info['modified_at']),
9776
+ 'CoreFiles.IsNotFixable' => $is_fixable_text,
9777
+ )
9778
+ );
9779
  $counter += 1;
9780
  $affected_files += 1;
9781
  }
9782
  }
9783
 
9784
  if ($counter > 0) {
9785
+ $params['CoreFiles.ListCount'] = $counter;
9786
+ $params['CoreFiles.GoodVisibility'] = 'hidden';
9787
+ $params['CoreFiles.BadVisibility'] = 'visible';
9788
  }
9789
  } else {
9790
+ $params['CoreFiles.GoodVisibility'] = 'hidden';
9791
+ $params['CoreFiles.BadVisibility'] = 'hidden';
9792
+ $params['CoreFiles.FailureVisibility'] = 'visible';
9793
  }
9794
  }
9795
 
9796
  // Send an email notification with the affected files.
9797
  if ($send_email === true) {
9798
  if ($affected_files > 0) {
9799
+ $content = SucuriScanTemplate::getSection('corefiles-notification', $params);
9800
  $sent = SucuriScanEvent::notify_event('scan_checksums', $content);
9801
 
9802
  return $sent;
9805
  return false;
9806
  }
9807
 
9808
+ return SucuriScanTemplate::getSection('corefiles', $params);
9809
+ }
9810
+
9811
+ /**
9812
+ * Process the requests sent by the form submissions originated in the integrity
9813
+ * page, all forms must have a nonce field that will be checked against the one
9814
+ * generated in the template render function.
9815
+ *
9816
+ * @return void
9817
+ */
9818
+ function sucuriscan_integrity_form_submissions()
9819
+ {
9820
+ if (SucuriScanInterface::check_nonce()) {
9821
+ // Force the execution of the filesystem scanner.
9822
+ if (SucuriScanRequest::post(':force_scan') !== false) {
9823
+ SucuriScanEvent::notify_event('plugin_change', 'Filesystem scan forced at: ' . date('r'));
9824
+ SucuriScanEvent::filesystem_scan(true);
9825
+ sucuriscan_core_files_data(true);
9826
+ }
9827
+
9828
+ // Restore, Remove, Mark as fixed the core files.
9829
+ $action = SucuriScanRequest::post(':integrity_action');
9830
+
9831
+ if ($action !== false) {
9832
+ if (SucuriScanRequest::post(':process_form') == 1) {
9833
+ if ($action == 'fixed'
9834
+ || $action == 'delete'
9835
+ || $action == 'restore'
9836
+ ) {
9837
+ $cache = new SucuriScanCache('integrity');
9838
+ $core_files = SucuriScanRequest::post(':corefiles', '_array');
9839
+ $files_selected = count($core_files);
9840
+ $files_affected = array();
9841
+ $files_processed = 0;
9842
+ $action_titles = array(
9843
+ 'restore' => 'Core file restored',
9844
+ 'delete' => 'Non-core file deleted',
9845
+ 'fixed' => 'Core file marked as fixed',
9846
+ );
9847
+
9848
+ if ($core_files) {
9849
+ $delimiter = '@';
9850
+ $parts_count = 2;
9851
+
9852
+ foreach ($core_files as $file_meta) {
9853
+ if (strpos($file_meta, $delimiter)) {
9854
+ $parts = explode($delimiter, $file_meta, $parts_count);
9855
+
9856
+ if (count($parts) === $parts_count) {
9857
+ $file_path = $parts[1];
9858
+ $status_type = $parts[0];
9859
+
9860
+ // Do not use realpath as the file may not exists.
9861
+ $full_path = ABSPATH . '/' . $file_path;
9862
+
9863
+ switch ($action) {
9864
+ case 'restore':
9865
+ $file_content = SucuriScanAPI::getOriginalCoreFile($file_path);
9866
+ if ($file_content) {
9867
+ $restored = @file_put_contents($full_path, $file_content);
9868
+ $files_processed += ($restored ? 1 : 0);
9869
+ $files_affected[] = $full_path;
9870
+ }
9871
+ break;
9872
+ case 'fixed':
9873
+ $cache_key = md5($file_path);
9874
+ $cache_value = array(
9875
+ 'file_path' => $file_path,
9876
+ 'file_status' => $status_type,
9877
+ 'ignored_at' => time(),
9878
+ );
9879
+ $cached = $cache->add($cache_key, $cache_value);
9880
+ $files_processed += ($cached ? 1 : 0);
9881
+ $files_affected[] = $full_path;
9882
+ break;
9883
+ case 'delete':
9884
+ if (@unlink($full_path)) {
9885
+ $files_processed += 1;
9886
+ $files_affected[] = $full_path;
9887
+ }
9888
+ break;
9889
+ }
9890
+ }
9891
+ }
9892
+ }
9893
+
9894
+ // Report files affected as a single event.
9895
+ if (!empty($files_affected)) {
9896
+ $message_tpl = (count($files_affected) > 1)
9897
+ ? '%s: (multiple entries): %s'
9898
+ : '%s: %s';
9899
+ $message = sprintf(
9900
+ $message_tpl,
9901
+ $action_titles[$action],
9902
+ @implode(',', $files_affected)
9903
+ );
9904
+
9905
+ switch ($action) {
9906
+ case 'restore':
9907
+ SucuriScanEvent::report_info_event($message);
9908
+ break;
9909
+ case 'delete':
9910
+ SucuriScanEvent::report_notice_event($message);
9911
+ break;
9912
+ case 'fixed':
9913
+ SucuriScanEvent::report_warning_event($message);
9914
+ break;
9915
+ }
9916
+ }
9917
+
9918
+ SucuriScanInterface::info(sprintf(
9919
+ '<b>%d</b> out of <b>%d</b> files were successfully processed.',
9920
+ $files_processed,
9921
+ $files_selected
9922
+ ));
9923
+ } else {
9924
+ SucuriScanInterface::error('No files were selected.');
9925
+ }
9926
+ } else {
9927
+ SucuriScanInterface::error('Action requested is not supported.');
9928
+ }
9929
+ } else {
9930
+ SucuriScanInterface::error('You need to confirm that you understand the risk of this operation.');
9931
+ }
9932
+ }
9933
+ }
9934
+ }
9935
+
9936
+ /**
9937
+ * Retrieve a list of md5sum and last modification time of all the files in the
9938
+ * folder specified. This is a recursive function.
9939
+ *
9940
+ * @param string $dir The base path where the scanning will start.
9941
+ * @param boolean $recursive Either TRUE or FALSE if the scan should be performed recursively.
9942
+ * @return array List of arrays containing the md5sum and last modification time of the files found.
9943
+ */
9944
+ function sucuriscan_get_integrity_tree($dir = './', $recursive = false)
9945
+ {
9946
+ $abs_path = rtrim(ABSPATH, '/');
9947
+
9948
+ $file_info = new SucuriScanFileInfo();
9949
+ $file_info->ignore_files = false;
9950
+ $file_info->ignore_directories = false;
9951
+ $file_info->run_recursively = $recursive;
9952
+ $file_info->scan_interface = SucuriScanOption::get_option(':scan_interface');
9953
+ $integrity_tree = $file_info->get_directory_tree_md5($dir, true);
9954
+
9955
+ if (!$integrity_tree) {
9956
+ $integrity_tree = array();
9957
+ }
9958
+
9959
+ return $integrity_tree;
9960
  }
9961
 
9962
  /**
9965
  * these keys:
9966
  *
9967
  * <ul>
9968
+ * <li>modified: Files with a different checksum according to the official WordPress archives,</li>
9969
  * <li>stable: Files with the same checksums than the official files,</li>
9970
  * <li>removed: Official files which are not present in the local project,</li>
9971
  * <li>added: Files present in the local project but not in the official WordPress packages.</li>
9976
  */
9977
  function sucuriscan_check_core_integrity($version = 0)
9978
  {
9979
+ $latest_hashes = SucuriScanAPI::getOfficialChecksums($version);
9980
  $base_content_dir = defined('WP_CONTENT_DIR')
9981
  ? basename(rtrim(WP_CONTENT_DIR, '/'))
9982
  : '';
9983
 
9984
+ if (!$latest_hashes) {
9985
  return false;
9986
  }
9987
 
10007
  $full_filepath = sprintf('%s/%s', ABSPATH, $file_path);
10008
 
10009
  // Patch for custom content directory path.
10010
+ if (!file_exists($full_filepath)
10011
  && strpos($file_path, 'wp-content') !== false
10012
  && defined('WP_CONTENT_DIR')
10013
  ) {
10050
  // Search added files (files not common in a normal wordpress installation).
10051
  foreach ($wp_core_hashes as $file_path => $extra_info) {
10052
  $file_path = str_replace(DIRECTORY_SEPARATOR, '/', $file_path);
10053
+ $file_path = @preg_replace('/^\.\/(.*)/', '$1', $file_path);
10054
 
10055
  if (sucuriscan_ignore_integrity_filepath($file_path)) {
10056
  continue;
10057
  }
10058
 
10059
+ if (!array_key_exists($file_path, $latest_hashes)) {
10060
  $full_filepath = ABSPATH . '/' . $file_path;
10061
  $modified_at = @filemtime($full_filepath);
10062
  $is_fixable = (bool) is_writable($full_filepath);
10083
 
10084
  // List of files that will be ignored from the integrity checking.
10085
  $ignore_files = array(
10086
+ '^sucuri-[0-9a-z\-]+\.php$',
10087
+ '^\S+-sucuri-db-dump-gzip-[0-9]{10}-[0-9a-z]{32}\.gz$',
10088
  '^favicon\.ico$',
10089
  '^php\.ini$',
10090
  '^\.htaccess$',
10119
 
10120
  // Determine whether a file must be ignored from the integrity checks or not.
10121
  foreach ($ignore_files as $ignore_pattern) {
10122
+ if (@preg_match('/'.$ignore_pattern.'/', $file_path)) {
10123
  return true;
10124
  }
10125
  }
10127
  return false;
10128
  }
10129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10130
  /**
10131
  * Generate and print the HTML code for the Post-Hack page.
10132
  *
10133
  * @return void
10134
  */
10135
+ function sucuriscan_posthack_page()
10136
+ {
10137
  SucuriScanInterface::check_permissions();
10138
 
10139
  $process_form = sucuriscan_posthack_process_form();
10140
 
10141
  // Page pseudo-variables initialization.
10142
+ $params = array(
10143
  'PageTitle' => 'Post-Hack',
10144
+ 'UpdateSecretKeys' => sucuriscan_update_secret_keys($process_form),
10145
+ 'ResetPassword' => sucuriscan_posthack_users($process_form),
10146
+ 'ResetPlugins' => sucuriscan_posthack_plugins($process_form),
10147
  );
10148
 
10149
+ echo SucuriScanTemplate::getTemplate('posthack', $params);
10150
  }
10151
 
10152
  /**
10154
  *
10155
  * @return mixed.
10156
  */
10157
+ function sucuriscan_posthack_ajax()
10158
+ {
10159
  SucuriScanInterface::check_permissions();
10160
 
10161
+ if (SucuriScanInterface::check_nonce()) {
10162
  sucuriscan_posthack_plugins_ajax();
10163
  }
10164
 
10170
  *
10171
  * @return boolean TRUE if a form submission should be processed, FALSE otherwise.
10172
  */
10173
+ function sucuriscan_posthack_process_form()
10174
+ {
10175
+ $process_form = SucuriScanRequest::post(':process_form', '(0|1)');
10176
 
10177
+ if (SucuriScanInterface::check_nonce()
 
10178
  && $process_form !== false
10179
  ) {
10180
+ if ($process_form === '1') {
10181
  return true;
10182
  } else {
10183
+ SucuriScanInterface::error('You need to confirm that you understand the risk of this operation.');
10184
  }
10185
  }
10186
 
10193
  * @param $process_form Whether a form was submitted or not.
10194
  * @return string HTML code with the information of the process.
10195
  */
10196
+ function sucuriscan_update_secret_keys($process_form = false)
10197
+ {
10198
+ $params = array(
10199
  'WPConfigUpdate.Visibility' => 'hidden',
10200
  'WPConfigUpdate.NewConfig' => '',
10201
  'SecurityKeys.List' => '',
10202
  );
10203
 
10204
  // Update all WordPress secret keys.
10205
+ if ($process_form && SucuriScanRequest::post(':update_wpconfig', '1')) {
10206
  $wpconfig_process = SucuriScanEvent::set_new_config_keys();
10207
 
10208
+ if ($wpconfig_process) {
10209
+ $params['WPConfigUpdate.Visibility'] = 'visible';
10210
+ SucuriScanEvent::report_notice_event('Generate new security keys');
10211
+
10212
+ if ($wpconfig_process['updated'] === true) {
10213
+ SucuriScanInterface::info('Secret keys updated successfully (summary of the operation bellow).');
10214
+ $params['WPConfigUpdate.NewConfig'] .= "// Old Keys\n";
10215
+ $params['WPConfigUpdate.NewConfig'] .= $wpconfig_process['old_keys_string'];
10216
+ $params['WPConfigUpdate.NewConfig'] .= "//\n";
10217
+ $params['WPConfigUpdate.NewConfig'] .= "// New Keys\n";
10218
+ $params['WPConfigUpdate.NewConfig'] .= $wpconfig_process['new_keys_string'];
10219
  } else {
10220
  SucuriScanInterface::error(
10221
  '<code>wp-config.php</code> file is not writable, replace the '
10222
  . 'old configuration file with the new values shown bellow.'
10223
  );
10224
+ $params['WPConfigUpdate.NewConfig'] = $wpconfig_process['new_wpconfig'];
10225
  }
10226
  } else {
10227
+ SucuriScanInterface::error('<code>wp-config.php</code> file was not found in the default location.');
10228
  }
10229
  }
10230
 
10232
  $current_keys = SucuriScanOption::get_security_keys();
10233
  $counter = 0;
10234
 
10235
+ foreach ($current_keys as $key_status => $key_list) {
10236
+ foreach ($key_list as $key_name => $key_value) {
10237
  $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
10238
+ $key_value = SucuriScan::excerpt($key_value, 50);
10239
 
10240
+ switch ($key_status) {
10241
  case 'good':
10242
  $key_status_text = 'good';
10243
  $key_status_css_class = 'success';
10253
  break;
10254
  }
10255
 
10256
+ if (isset($key_status_text)) {
10257
+ $params['SecurityKeys.List'] .= SucuriScanTemplate::getSnippet(
10258
+ 'posthack-updatesecretkeys',
10259
+ array(
10260
+ 'SecurityKey.CssClass' => $css_class,
10261
+ 'SecurityKey.KeyName' => $key_name,
10262
+ 'SecurityKey.KeyValue' => $key_value,
10263
+ 'SecurityKey.KeyStatusText' => $key_status_text,
10264
+ 'SecurityKey.KeyStatusCssClass' => $key_status_css_class,
10265
+ )
10266
+ );
10267
+ $counter++;
10268
  }
10269
  }
10270
  }
10271
 
10272
+ return SucuriScanTemplate::getSection('posthack-updatesecretkeys', $params);
10273
  }
10274
 
10275
  /**
10279
  * @param $process_form Whether a form was submitted or not.
10280
  * @return string HTML code for a table where a list of user accounts will be shown.
10281
  */
10282
+ function sucuriscan_posthack_users($process_form = false)
10283
+ {
10284
+ $params = array(
10285
  'ResetPassword.UserList' => '',
10286
  'ResetPassword.PaginationLinks' => '',
10287
  'ResetPassword.PaginationVisibility' => 'hidden',
10288
  );
10289
 
10290
  // Process the form submission (if any).
10291
+ sucuriscan_reset_user_password($process_form);
10292
 
10293
  // Fill the user list for ResetPassword action.
10294
  $user_list = false;
10295
+ $page_number = SucuriScanTemplate::pageNumber();
10296
  $max_per_page = SUCURISCAN_MAX_PAGINATION_BUTTONS;
10297
+ $dbquery = new WP_User_Query(array(
10298
  'number' => $max_per_page,
10299
+ 'offset' => ($page_number - 1) * $max_per_page,
10300
  'fields' => 'all_with_meta',
10301
  'orderby' => 'ID',
10302
+ ));
10303
 
10304
  // Retrieve the results and build the pagination links.
10305
+ if ($dbquery) {
10306
  $total_items = $dbquery->get_total();
10307
  $user_list = $dbquery->get_results();
10308
 
10309
+ $params['ResetPassword.PaginationLinks'] = SucuriScanTemplate::pagination(
10310
  '%%SUCURI.URL.Posthack%%#reset-users-password',
10311
  $total_items,
10312
  $max_per_page
10313
  );
10314
 
10315
+ if ($total_items > $max_per_page) {
10316
+ $params['ResetPassword.PaginationVisibility'] = 'visible';
10317
  }
10318
  }
10319
 
10320
+ if ($user_list !== false) {
10321
  $counter = 0;
10322
 
10323
+ foreach ($user_list as $user) {
10324
+ $user->user_registered_timestamp = strtotime($user->user_registered);
10325
+ $user->user_registered_formatted = SucuriScan::datetime($user->user_registered_timestamp);
10326
  $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
10327
  $display_username = ( $user->user_login != $user->display_name )
10328
+ ? sprintf('%s (%s)', $user->user_login, $user->display_name)
10329
  : $user->user_login;
10330
 
10331
+ $params['ResetPassword.UserList'] .= SucuriScanTemplate::getSnippet(
10332
+ 'posthack-resetpassword',
10333
+ array(
10334
+ 'ResetPassword.UserId' => $user->ID,
10335
+ 'ResetPassword.Username' => $user->user_login,
10336
+ 'ResetPassword.Displayname' => $user->display_name,
10337
+ 'ResetPassword.DisplayUsername' => $display_username,
10338
+ 'ResetPassword.Email' => $user->user_email,
10339
+ 'ResetPassword.Registered' => $user->user_registered_formatted,
10340
+ 'ResetPassword.Roles' => @implode(', ', $user->roles),
10341
+ 'ResetPassword.CssClass' => $css_class,
10342
+ )
10343
+ );
10344
+ $counter++;
10345
  }
10346
  }
10347
 
10348
+ return SucuriScanTemplate::getSection('posthack-resetpassword', $params);
10349
  }
10350
 
10351
  /**
10354
  * @param $process_form Whether a form was submitted or not.
10355
  * @return void
10356
  */
10357
+ function sucuriscan_reset_user_password($process_form = false)
10358
+ {
10359
+ if ($process_form && SucuriScanRequest::post(':reset_password')) {
10360
+ $user_identifiers = SucuriScanRequest::post('user_ids', '_array');
10361
  $pwd_changed = array();
10362
  $pwd_not_changed = array();
10363
 
10364
+ if (is_array($user_identifiers) && !empty($user_identifiers)) {
10365
+ arsort($user_identifiers);
10366
 
10367
+ foreach ($user_identifiers as $user_id) {
10368
+ $user_id = intval($user_id);
10369
 
10370
+ if (SucuriScanEvent::set_new_password($user_id)) {
10371
  $pwd_changed[] = $user_id;
10372
  } else {
10373
  $pwd_not_changed[] = $user_id;
10374
  }
10375
  }
10376
 
10377
+ if (!empty($pwd_changed)) {
10378
+ $message = 'Password changed for user identifiers <code>' . @implode(', ', $pwd_changed) . '</code>';
10379
 
10380
+ SucuriScanEvent::report_notice_event($message);
10381
+ SucuriScanInterface::info($message);
10382
  }
10383
 
10384
+ if (!empty($pwd_not_changed)) {
10385
+ SucuriScanInterface::error('Password change failed for users: ' . implode(', ', $pwd_not_changed));
10386
  }
10387
  } else {
10388
+ SucuriScanInterface::error('You did not select a user from the list.');
10389
  }
10390
  }
10391
  }
10396
  * @param boolean $process_form Whether a form was submitted or not.
10397
  * @return void
10398
  */
10399
+ function sucuriscan_posthack_plugins($process_form = false)
10400
+ {
10401
+ $params = array(
10402
  'ResetPlugin.PluginList' => '',
10403
  'ResetPlugin.CacheLifeTime' => 'unknown',
10404
  );
10405
 
10406
+ if (defined('SUCURISCAN_GET_PLUGINS_LIFETIME')) {
10407
+ $params['ResetPlugin.CacheLifeTime'] = SUCURISCAN_GET_PLUGINS_LIFETIME;
10408
  }
10409
 
10410
+ sucuriscan_posthack_reinstall_plugins($process_form);
10411
 
10412
+ return SucuriScanTemplate::getSection('posthack-resetplugins', $params);
10413
  }
10414
 
10415
  /**
10417
  *
10418
  * @return string HTML code for a table with the plugins metadata.
10419
  */
10420
+ function sucuriscan_posthack_plugins_ajax()
10421
+ {
10422
+ if (SucuriScanRequest::post('form_action') == 'get_plugins_data') {
10423
+ $all_plugins = SucuriScanAPI::getPlugins();
10424
  $response = '';
10425
  $counter = 0;
10426
 
10427
+ foreach ($all_plugins as $plugin_path => $plugin_data) {
10428
  $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
10429
  $plugin_type_class = ( $plugin_data['PluginType'] == 'free' ) ? 'primary' : 'warning';
10430
  $input_disabled = ( $plugin_data['PluginType'] == 'free' ) ? '' : 'disabled="disabled"';
10431
  $plugin_status = $plugin_data['IsPluginActive'] ? 'active' : 'not active';
10432
  $plugin_status_class = $plugin_data['IsPluginActive'] ? 'success' : 'default';
10433
 
10434
+ $response .= SucuriScanTemplate::getSnippet(
10435
+ 'posthack-resetplugins',
10436
+ array(
10437
+ 'ResetPlugin.CssClass' => $css_class,
10438
+ 'ResetPlugin.Disabled' => $input_disabled,
10439
+ 'ResetPlugin.PluginPath' => $plugin_path,
10440
+ 'ResetPlugin.Plugin' => SucuriScan::excerpt($plugin_data['Name'], 35),
10441
+ 'ResetPlugin.Version' => $plugin_data['Version'],
10442
+ 'ResetPlugin.Type' => $plugin_data['PluginType'],
10443
+ 'ResetPlugin.TypeClass' => $plugin_type_class,
10444
+ 'ResetPlugin.Status' => $plugin_status,
10445
+ 'ResetPlugin.StatusClass' => $plugin_status_class,
10446
+ )
10447
+ );
10448
+ $counter++;
10449
  }
10450
 
10451
  print( $response );
10462
  * @param boolean $process_form Whether a form was submitted or not.
10463
  * @return void
10464
  */
10465
+ function sucuriscan_posthack_reinstall_plugins($process_form = false)
10466
+ {
10467
+ if ($process_form && isset($_POST['sucuriscan_reset_plugins'])) {
10468
+ include_once(ABSPATH . 'wp-admin/includes/class-wp-upgrader.php');
10469
+ include_once(ABSPATH . 'wp-admin/includes/plugin-install.php'); // For plugins_api.
10470
 
10471
+ if ($plugin_list = SucuriScanRequest::post('plugin_path', '_array')) {
10472
  // Create an instance of the FileInfo interface.
10473
  $file_info = new SucuriScanFileInfo();
10474
  $file_info->ignore_files = false;
10476
  $file_info->skip_directories = false;
10477
 
10478
  // Get (possible) cached information from the installed plugins.
10479
+ $all_plugins = SucuriScanAPI::getPlugins();
10480
 
10481
  // Loop through all the installed plugins.
10482
+ foreach ($_POST['plugin_path'] as $plugin_path) {
10483
+ if (array_key_exists($plugin_path, $all_plugins)) {
10484
  $plugin_data = $all_plugins[ $plugin_path ];
10485
 
10486
  // Check if the plugin can be downloaded from the free market.
10487
+ if ($plugin_data['IsFreePlugin'] === true) {
10488
+ $plugin_info = SucuriScanAPI::getRemotePluginData($plugin_data['RepositoryName']);
10489
 
10490
+ if ($plugin_info) {
10491
  // First, remove all files/sub-folders from the plugin's directory.
10492
+ if (substr_count($plugin_path, '/') >= 1) {
10493
+ $plugin_directory = dirname(WP_PLUGIN_DIR . '/' . $plugin_path);
10494
+ $file_info->remove_directory_tree($plugin_directory);
10495
  }
10496
 
10497
  // Install a fresh copy of the plugin's files.
10498
  $upgrader_skin = new Plugin_Installer_Skin();
10499
+ $upgrader = new Plugin_Upgrader($upgrader_skin);
10500
+ $upgrader->install($plugin_info->download_link);
10501
+ SucuriScanEvent::report_notice_event('Plugin re-installed: ' . $plugin_path);
10502
  } else {
10503
+ SucuriScanInterface::error('Connection with the WordPress plugin market failed.');
10504
  }
10505
  }
10506
  }
10507
  }
10508
  } else {
10509
+ SucuriScanInterface::error('You did not select a free plugin to reinstall.');
10510
  }
10511
  }
10512
  }
10518
  *
10519
  * @return string Last-logings for the administrator accounts.
10520
  */
10521
+ function sucuriscan_lastlogins_page()
10522
+ {
10523
  SucuriScanInterface::check_permissions();
10524
 
10525
  // Reset the file with the last-logins logs.
10526
+ if (SucuriScanInterface::check_nonce()
10527
+ && SucuriScanRequest::post(':reset_lastlogins') !== false
 
10528
  ) {
10529
  $file_path = sucuriscan_lastlogins_datastore_filepath();
10530
 
10531
+ if (unlink($file_path)) {
10532
  sucuriscan_lastlogins_datastore_exists();
10533
+ SucuriScanInterface::info('Last-Logins logs were reset successfully.');
10534
  } else {
10535
+ SucuriScanInterface::error('Could not reset the last-logins logs.');
10536
  }
10537
  }
10538
 
10539
  // Page pseudo-variables initialization.
10540
+ $params = array(
10541
  'PageTitle' => 'Last Logins',
10542
  'LastLogins.Admins' => sucuriscan_lastlogins_admins(),
10543
  'LastLogins.AllUsers' => sucuriscan_lastlogins_all(),
10545
  'FailedLogins' => sucuriscan_failed_logins_panel(),
10546
  );
10547
 
10548
+ echo SucuriScanTemplate::getTemplate('lastlogins', $params);
10549
  }
10550
 
10551
  /**
10552
  * List all the user administrator accounts.
10553
  *
10554
+ * @see https://codex.wordpress.org/Class_Reference/WP_User_Query
10555
  *
10556
  * @return void
10557
  */
10558
+ function sucuriscan_lastlogins_admins()
10559
+ {
10560
  // Page pseudo-variables initialization.
10561
+ $params = array(
10562
  'AdminUsers.List' => '',
10563
  );
10564
 
10565
+ $user_query = new WP_User_Query(array('role' => 'Administrator'));
10566
  $admins = $user_query->get_results();
10567
 
10568
+ foreach ((array) $admins as $admin) {
10569
+ $last_logins = sucuriscan_get_logins(5, 0, $admin->ID);
10570
  $admin->lastlogins = $last_logins['entries'];
10571
 
10572
  $user_snippet = array(
10573
+ 'AdminUsers.Username' => $admin->user_login,
10574
+ 'AdminUsers.Email' => $admin->user_email,
10575
  'AdminUsers.LastLogins' => '',
10576
+ 'AdminUsers.RegisteredAt' => 'Unknown',
10577
+ 'AdminUsers.UserURL' => SucuriScan::admin_url('user-edit.php?user_id=' . $admin->ID),
10578
  'AdminUsers.NoLastLogins' => 'visible',
10579
  'AdminUsers.NoLastLoginsTable' => 'hidden',
10580
  );
10581
 
10582
+ if (!empty($admin->lastlogins)) {
10583
  $user_snippet['AdminUsers.NoLastLogins'] = 'hidden';
10584
  $user_snippet['AdminUsers.NoLastLoginsTable'] = 'visible';
10585
  $user_snippet['AdminUsers.RegisteredAt'] = 'Unknown';
10586
  $counter = 0;
10587
 
10588
+ foreach ($admin->lastlogins as $i => $lastlogin) {
10589
+ if ($i == 0) {
10590
+ $user_snippet['AdminUsers.RegisteredAt'] = SucuriScan::datetime(
10591
+ $lastlogin->user_registered_timestamp
10592
+ );
10593
  }
10594
 
10595
+ $css_class = ($counter % 2 === 0) ? '' : 'alternate';
10596
+ $user_snippet['AdminUsers.LastLogins'] .= SucuriScanTemplate::getSnippet(
10597
+ 'lastlogins-admins-lastlogin',
10598
+ array(
10599
+ 'AdminUsers.RemoteAddr' => $lastlogin->user_remoteaddr,
10600
+ 'AdminUsers.Datetime' => SucuriScan::datetime($lastlogin->user_lastlogin_timestamp),
10601
+ 'AdminUsers.CssClass' => $css_class,
10602
+ )
10603
+ );
10604
+ $counter++;
10605
  }
10606
  }
10607
 
10608
+ $params['AdminUsers.List'] .= SucuriScanTemplate::getSnippet('lastlogins-admins', $user_snippet);
10609
  }
10610
 
10611
+ return SucuriScanTemplate::getSection('lastlogins-admins', $params);
10612
  }
10613
 
10614
  /**
10618
  *
10619
  * @return string Last-logings for all user accounts.
10620
  */
10621
+ function sucuriscan_lastlogins_all()
10622
+ {
10623
  $max_per_page = SUCURISCAN_LASTLOGINS_USERSLIMIT;
10624
+ $page_number = SucuriScanTemplate::pageNumber();
10625
  $offset = ($max_per_page * $page_number) - $max_per_page;
10626
 
10627
+ $params = array(
10628
  'UserList' => '',
10629
  'UserList.Limit' => $max_per_page,
10630
  'UserList.Total' => 0,
10633
  'UserList.NoItemsVisibility' => 'visible',
10634
  );
10635
 
10636
+ if (!sucuriscan_lastlogins_datastore_is_writable()) {
10637
+ $fpath = SucuriScan::escape(sucuriscan_lastlogins_datastore_filepath());
10638
+ SucuriScanInterface::error('Last-logins datastore file is not writable: <code>' . $fpath . '</code>');
10639
  }
10640
 
10641
  $counter = 0;
10642
+ $last_logins = sucuriscan_get_logins($max_per_page, $offset);
10643
+ $params['UserList.Total'] = $last_logins['total'];
10644
 
10645
+ if ($last_logins['total'] > $max_per_page) {
10646
+ $params['UserList.PaginationVisibility'] = 'visible';
10647
  }
10648
 
10649
+ if ($last_logins['total'] > 0) {
10650
+ $params['UserList.NoItemsVisibility'] = 'hidden';
10651
  }
10652
 
10653
+ foreach ($last_logins['entries'] as $user) {
10654
+ $css_class = ($counter % 2 === 0) ? '' : 'alternate';
 
10655
 
10656
  $user_dataset = array(
10657
  'UserList.Number' => $user->line_num,
10658
  'UserList.UserId' => $user->user_id,
10659
+ 'UserList.Username' => 'Unknown',
10660
  'UserList.Displayname' => '',
10661
  'UserList.Email' => '',
10662
  'UserList.Registered' => '',
10663
+ 'UserList.RemoteAddr' => $user->user_remoteaddr,
10664
+ 'UserList.Hostname' => $user->user_hostname,
10665
+ 'UserList.Datetime' => $user->user_lastlogin,
10666
+ 'UserList.TimeAgo' => SucuriScan::time_ago($user->user_lastlogin),
10667
+ 'UserList.UserURL' => SucuriScan::admin_url('user-edit.php?user_id=' . $user->user_id),
10668
  'UserList.CssClass' => $css_class,
10669
  );
10670
 
10671
+ if ($user->user_exists) {
10672
+ $user_dataset['UserList.Username'] = $user->user_login;
10673
+ $user_dataset['UserList.Displayname'] = $user->display_name;
10674
+ $user_dataset['UserList.Email'] = $user->user_email;
10675
+ $user_dataset['UserList.Registered'] = $user->user_registered;
10676
  }
10677
 
10678
+ $params['UserList'] .= SucuriScanTemplate::getSnippet('lastlogins-all', $user_dataset);
10679
+ $counter++;
10680
  }
10681
 
10682
  // Generate the pagination for the list.
10683
+ $params['UserList.Pagination'] = SucuriScanTemplate::pagination(
10684
  '%%SUCURI.URL.Lastlogins%%',
10685
  $last_logins['total'],
10686
  $max_per_page
10687
  );
10688
 
10689
+ return SucuriScanTemplate::getSection('lastlogins-all', $params);
10690
  }
10691
 
10692
  /**
10694
  *
10695
  * @return string Absolute filepath where the user's last login information is stored.
10696
  */
10697
+ function sucuriscan_lastlogins_datastore_filepath()
10698
+ {
10699
+ return SucuriScan::datastore_folder_path('sucuri-lastlogins.php');
10700
  }
10701
 
10702
  /**
10705
  *
10706
  * @return string Absolute filepath where the user's last login information is stored.
10707
  */
10708
+ function sucuriscan_lastlogins_datastore_exists()
10709
+ {
10710
  $fpath = sucuriscan_lastlogins_datastore_filepath();
10711
 
10712
  if (!file_exists($fpath)) {
10726
  *
10727
  * @return boolean Whether the user's last login datastore file is writable or not.
10728
  */
10729
+ function sucuriscan_lastlogins_datastore_is_writable()
10730
+ {
10731
  $datastore_filepath = sucuriscan_lastlogins_datastore_exists();
10732
 
10733
+ if ($datastore_filepath) {
10734
+ if (!is_writable($datastore_filepath)) {
10735
+ @chmod($datastore_filepath, 0644);
10736
  }
10737
 
10738
+ if (is_writable($datastore_filepath)) {
10739
  return $datastore_filepath;
10740
  }
10741
  }
10749
  *
10750
  * @return boolean Whether the user's last login datastore file is readable or not.
10751
  */
10752
+ function sucuriscan_lastlogins_datastore_is_readable()
10753
+ {
10754
  $datastore_filepath = sucuriscan_lastlogins_datastore_exists();
10755
 
10756
+ if ($datastore_filepath && is_readable($datastore_filepath)) {
10757
  return $datastore_filepath;
10758
  }
10759
 
10760
  return false;
10761
  }
10762
 
10763
+ if (!function_exists('sucuri_set_lastlogin')) {
10764
  /**
10765
  * Add a new user session to the list of last user logins.
10766
  *
10767
  * @param string $user_login The name of the user account involved in the operation.
10768
  * @return void
10769
  */
10770
+ function sucuriscan_set_lastlogin($user_login = '')
10771
+ {
10772
  $datastore_filepath = sucuriscan_lastlogins_datastore_is_writable();
10773
 
10774
+ if ($datastore_filepath) {
10775
+ $current_user = get_user_by('login', $user_login);
10776
  $remote_addr = SucuriScan::get_remote_addr();
10777
 
10778
  $login_info = array(
10779
  'user_id' => $current_user->ID,
10780
  'user_login' => $current_user->user_login,
10781
  'user_remoteaddr' => $remote_addr,
10782
+ 'user_hostname' => @gethostbyaddr($remote_addr),
10783
+ 'user_lastlogin' => current_time('mysql')
10784
  );
10785
 
10786
+ @file_put_contents($datastore_filepath, json_encode($login_info)."\n", FILE_APPEND);
10787
  }
10788
  }
10789
+ add_action('wp_login', 'sucuriscan_set_lastlogin', 50);
10790
  }
10791
 
10792
  /**
10800
  * @param integer $user_id Optional user identifier to filter the results.
10801
  * @return array The list of all the user logins, and total of entries registered.
10802
  */
10803
+ function sucuriscan_get_logins($limit = 10, $offset = 0, $user_id = 0)
10804
+ {
10805
  $datastore_filepath = sucuriscan_lastlogins_datastore_is_readable();
10806
  $last_logins = array(
10807
  'total' => 0,
10808
  'entries' => array(),
10809
  );
10810
 
10811
+ if ($datastore_filepath) {
10812
  $parsed_lines = 0;
10813
+ $data_lines = SucuriScanFileInfo::file_lines($datastore_filepath);
10814
 
10815
+ if ($data_lines) {
10816
  /**
10817
  * This count will not be 100% accurate considering that we are checking the
10818
  * syntax of each line in the loop bellow, there may be some lines without the
10821
  *
10822
  * @var integer
10823
  */
10824
+ $total_lines = count($data_lines);
10825
  $last_logins['total'] = $total_lines;
10826
 
10827
  // Get a list with the latest entries in the first positions.
10828
+ $reversed_lines = array_reverse($data_lines);
10829
 
10830
  /**
10831
  * Only the user accounts with administrative privileges can see the logs of all
10834
  * @var object
10835
  */
10836
  $current_user = wp_get_current_user();
10837
+ $is_admin_user = (bool) current_user_can('manage_options');
10838
 
10839
+ for ($i = $offset; $i < $total_lines; $i++) {
10840
+ $line = $reversed_lines[$i] ? trim($reversed_lines[$i]) : '';
10841
 
10842
  // Check if the data is serialized (which we will consider as insecure).
10843
+ if (SucuriScan::is_serialized($line)) {
10844
+ $last_login = false; /* Do not unserialize; is unsafe. */
10845
  } else {
10846
+ $last_login = @json_decode($line, true);
10847
  }
10848
 
10849
+ if ($last_login) {
10850
+ $last_login['user_lastlogin_timestamp'] = strtotime($last_login['user_lastlogin']);
10851
  $last_login['user_registered_timestamp'] = 0;
10852
 
10853
  // Only administrators can see all login stats.
10854
+ if (!$is_admin_user && $current_user->user_login != $last_login['user_login']) {
10855
  continue;
10856
  }
10857
 
10858
  // Filter the user identifiers using the value passed tot his function.
10859
+ if ($user_id > 0 && $last_login['user_id'] != $user_id) {
10860
  continue;
10861
  }
10862
 
10863
  // Get the WP_User object and add extra information from the last-login data.
10864
  $last_login['user_exists'] = false;
10865
+ $user_account = get_userdata($last_login['user_id']);
10866
 
10867
+ if ($user_account) {
10868
  $last_login['user_exists'] = true;
10869
 
10870
+ foreach ($user_account->data as $var_name => $var_value) {
10871
  $last_login[ $var_name ] = $var_value;
10872
 
10873
+ if ($var_name == 'user_registered') {
10874
+ $last_login['user_registered_timestamp'] = strtotime($var_value);
10875
  }
10876
  }
10877
  }
10883
  $last_logins['total'] -= 1;
10884
  }
10885
 
10886
+ if (@preg_match('/^[0-9]+$/', $limit) && $limit > 0) {
10887
+ if ($parsed_lines >= $limit) {
10888
  break;
10889
  }
10890
  }
10895
  return $last_logins;
10896
  }
10897
 
10898
+ if (!function_exists('sucuri_login_redirect')) {
10899
  /**
10900
  * Hook for the wp-login action to redirect the user to a specific URL after
10901
  * his successfully login to the administrator interface.
10905
  * @param boolean $user WordPress user object with the information of the account involved in the operation.
10906
  * @return string URL where the browser must be redirected to.
10907
  */
10908
+ function sucuriscan_login_redirect($redirect_to = '', $request = null, $user = false)
10909
+ {
10910
+ $login_url = !empty($redirect_to) ? $redirect_to : SucuriScan::admin_url();
10911
 
10912
+ if ($user instanceof WP_User
10913
+ && in_array('administrator', $user->roles)
10914
+ && SucuriScanOption::is_enabled(':lastlogin_redirection')
 
10915
  ) {
10916
+ $login_url = add_query_arg('sucuriscan_lastlogin', 1, $login_url);
10917
  }
10918
 
10919
  return $login_url;
10920
  }
10921
 
10922
+ if (SucuriScanOption::is_enabled(':lastlogin_redirection')) {
10923
+ add_filter('login_redirect', 'sucuriscan_login_redirect', 10, 3);
10924
  }
10925
  }
10926
 
10927
+ if (!function_exists('sucuri_get_user_lastlogin')) {
10928
  /**
10929
  * Display the last user login at the top of the admin interface.
10930
  *
10931
  * @return void
10932
  */
10933
+ function sucuriscan_get_user_lastlogin()
10934
+ {
10935
+ if (current_user_can('manage_options')
10936
+ && SucuriScanRequest::get(':lastlogin', '1')
10937
  ) {
10938
  $current_user = wp_get_current_user();
10939
 
10940
  // Select the penultimate entry, not the last one.
10941
+ $last_logins = sucuriscan_get_logins(2, 0, $current_user->ID);
10942
 
10943
+ if (isset($last_logins['entries'][1])) {
10944
  $row = $last_logins['entries'][1];
10945
+ $page_url = SucuriScanTemplate::getUrl('lastlogins');
10946
+ $message = sprintf(
10947
+ 'Last login was at <b>%s</b> from <b>%s</b> <em>(%s)</em>',
10948
+ SucuriScan::datetime($row->user_lastlogin_timestamp),
10949
+ SucuriScan::escape($row->user_remoteaddr),
10950
+ SucuriScan::escape($row->user_hostname)
10951
  );
10952
+ $message .= "\x20(<a href='" . $page_url . "'>view all logs</a>)";
10953
+ SucuriScanInterface::info($message);
10954
  }
10955
  }
10956
  }
10957
 
10958
+ add_action('admin_notices', 'sucuriscan_get_user_lastlogin');
10959
  }
10960
 
10961
  /**
10963
  *
10964
  * @return string The HTML code displaying a list of all the users logged in at the moment.
10965
  */
10966
+ function sucuriscan_loggedin_users_panel()
10967
+ {
10968
  // Get user logged in list.
10969
+ $params = array(
10970
  'LoggedInUsers.List' => '',
10971
  'LoggedInUsers.Total' => 0,
10972
  );
10973
 
10974
+ $logged_in_users = sucuriscan_get_online_users(true);
10975
 
10976
+ if (is_array($logged_in_users) && !empty($logged_in_users)) {
10977
+ $params['LoggedInUsers.Total'] = count($logged_in_users);
10978
  $counter = 0;
10979
 
10980
+ foreach ((array) $logged_in_users as $logged_in_user) {
10981
+ $counter++;
10982
+ $logged_in_user['last_activity_datetime'] = SucuriScan::datetime($logged_in_user['last_activity']);
10983
+ $logged_in_user['user_registered_datetime'] = SucuriScan::datetime(strtotime($logged_in_user['user_registered']));
10984
+
10985
+ $params['LoggedInUsers.List'] .= SucuriScanTemplate::getSnippet(
10986
+ 'lastlogins-loggedin',
10987
+ array(
10988
+ 'LoggedInUsers.Id' => $logged_in_user['user_id'],
10989
+ 'LoggedInUsers.UserURL' => SucuriScan::admin_url('user-edit.php?user_id=' . $logged_in_user['user_id']),
10990
+ 'LoggedInUsers.UserLogin' => $logged_in_user['user_login'],
10991
+ 'LoggedInUsers.UserEmail' => $logged_in_user['user_email'],
10992
+ 'LoggedInUsers.LastActivity' => $logged_in_user['last_activity_datetime'],
10993
+ 'LoggedInUsers.Registered' => $logged_in_user['user_registered_datetime'],
10994
+ 'LoggedInUsers.RemoveAddr' => $logged_in_user['remote_addr'],
10995
+ 'LoggedInUsers.CssClass' => ($counter % 2 === 0) ? '' : 'alternate',
10996
+ )
10997
+ );
10998
  }
10999
  }
11000
 
11001
+ return SucuriScanTemplate::getSection('lastlogins-loggedin', $params);
11002
  }
11003
 
11004
  /**
11007
  * @param boolean $add_current_user Whether the current user should be added to the list or not.
11008
  * @return array List of registered users currently in session.
11009
  */
11010
+ function sucuriscan_get_online_users($add_current_user = false)
11011
+ {
11012
  $users = array();
11013
 
11014
+ if (SucuriScan::is_multisite()) {
11015
+ $users = get_site_transient('online_users');
11016
  } else {
11017
+ $users = get_transient('online_users');
11018
  }
11019
 
11020
  // If not online users but current user is logged in, add it to the list.
11021
+ if (empty($users) && $add_current_user) {
11022
  $current_user = wp_get_current_user();
11023
 
11024
+ if ($current_user->ID > 0) {
11025
+ sucuriscan_set_online_user($current_user->user_login, $current_user);
11026
 
11027
  return sucuriscan_get_online_users();
11028
  }
11039
  * @param array $logged_in_users List of registered users currently in session.
11040
  * @return boolean Either TRUE or FALSE representing the success or fail of the operation.
11041
  */
11042
+ function sucuriscan_save_online_users($logged_in_users = array())
11043
+ {
11044
  $expiration = 30 * 60;
11045
 
11046
+ if (SucuriScan::is_multisite()) {
11047
+ return set_site_transient('online_users', $logged_in_users, $expiration);
11048
  } else {
11049
+ return set_transient('online_users', $logged_in_users, $expiration);
11050
  }
11051
  }
11052
 
11053
+ if (!function_exists('sucuriscan_unset_online_user_on_logout')) {
11054
  /**
11055
  * Remove a logged in user from the list of registered users in session when
11056
  * the logout page is requested.
11057
  *
11058
  * @return void
11059
  */
11060
+ function sucuriscan_unset_online_user_on_logout()
11061
+ {
11062
  $remote_addr = SucuriScan::get_remote_addr();
11063
  $current_user = wp_get_current_user();
11064
  $user_id = $current_user->ID;
11065
 
11066
+ sucuriscan_unset_online_user($user_id, $remote_addr);
11067
  }
11068
 
11069
+ add_action('wp_logout', 'sucuriscan_unset_online_user_on_logout');
11070
  }
11071
 
11072
  /**
11077
  * @param integer $remote_addr IP address of the computer where the user logged in.
11078
  * @return boolean Either TRUE or FALSE representing the success or fail of the operation.
11079
  */
11080
+ function sucuriscan_unset_online_user($user_id = 0, $remote_addr = 0)
11081
+ {
11082
  $logged_in_users = sucuriscan_get_online_users();
11083
 
11084
  // Remove the specified user identifier from the list.
11085
+ if (is_array($logged_in_users) && !empty($logged_in_users)) {
11086
+ foreach ($logged_in_users as $i => $user) {
11087
+ if ($user['user_id'] == $user_id
11088
+ && strcmp($user['remote_addr'], $remote_addr) == 0
 
11089
  ) {
11090
  unset($logged_in_users[ $i ]);
11091
  break;
11093
  }
11094
  }
11095
 
11096
+ return sucuriscan_save_online_users($logged_in_users);
11097
  }
11098
 
11099
+ if (!function_exists('sucuriscan_set_online_user')) {
11100
  /**
11101
  * Add an user account to the list of registered users in session.
11102
  *
11104
  * @param boolean $user The WordPress object containing all the information associated to the user.
11105
  * @return void
11106
  */
11107
+ function sucuriscan_set_online_user($user_login = '', $user = false)
11108
+ {
11109
+ if ($user) {
11110
  // Get logged in user information.
11111
  $current_user = ($user instanceof WP_User) ? $user : wp_get_current_user();
11112
  $current_user_id = $current_user->ID;
11113
  $remote_addr = SucuriScan::get_remote_addr();
11114
+ $current_time = current_time('timestamp');
11115
  $logged_in_users = sucuriscan_get_online_users();
11116
 
11117
  // Build the dataset array that will be stored in the transient variable.
11124
  'remote_addr' => $remote_addr,
11125
  );
11126
 
11127
+ if (!is_array($logged_in_users) || empty($logged_in_users)) {
11128
  $logged_in_users = array( $current_user_info );
11129
+ sucuriscan_save_online_users($logged_in_users);
11130
  } else {
11131
  $do_nothing = false;
11132
  $update_existing = false;
11133
  $item_index = 0;
11134
 
11135
  // Check if the user is already in the logged-in-user list and update it if is necessary.
11136
+ foreach ($logged_in_users as $i => $user) {
11137
+ if ($user['user_id'] == $current_user_id
11138
+ && strcmp($user['remote_addr'], $remote_addr) == 0
 
11139
  ) {
11140
+ if ($user['last_activity'] < ($current_time - (15 * 60))) {
11141
  $update_existing = true;
11142
  $item_index = $i;
11143
  break;
11148
  }
11149
  }
11150
 
11151
+ if ($update_existing) {
11152
  $logged_in_users[ $item_index ] = $current_user_info;
11153
+ sucuriscan_save_online_users($logged_in_users);
11154
+ } elseif ($do_nothing) {
11155
  // Do nothing.
11156
  } else {
11157
  $logged_in_users[] = $current_user_info;
11158
+ sucuriscan_save_online_users($logged_in_users);
11159
  }
11160
  }
11161
  }
11162
  }
11163
 
11164
+ add_action('wp_login', 'sucuriscan_set_online_user', 10, 2);
11165
  }
11166
 
11167
  /**
11169
  *
11170
  * @return string A list with the failed logins occurred during the last hour.
11171
  */
11172
+ function sucuriscan_failed_logins_panel()
11173
+ {
11174
  $template_variables = array(
11175
  'FailedLogins.List' => '',
11176
  'FailedLogins.Total' => '',
11183
  );
11184
 
11185
  // Define variables for the pagination.
11186
+ $page_number = SucuriScanTemplate::pageNumber();
11187
  $max_per_page = SUCURISCAN_MAX_PAGINATION_BUTTONS;
11188
+ $page_offset = ($page_number - 1) * $max_per_page;
11189
+ $page_limit = ($page_offset + $max_per_page);
11190
 
11191
+ $max_failed_logins = SucuriScanOption::get_option(':maximum_failed_logins');
11192
+ $notify_bruteforce_attack = SucuriScanOption::get_option(':notify_bruteforce_attack');
11193
  $failed_logins = sucuriscan_get_failed_logins();
11194
+ $old_failed_logins = sucuriscan_get_failed_logins(true);
11195
 
11196
  // Merge the new and old failed logins.
11197
+ if (is_array($old_failed_logins) && !empty($old_failed_logins)) {
11198
+ if (is_array($failed_logins) && !empty($failed_logins)) {
11199
+ $failed_logins = array_merge($failed_logins, $old_failed_logins);
 
 
 
 
 
 
11200
  } else {
11201
  $failed_logins = $old_failed_logins;
11202
  }
11203
  }
11204
 
11205
+ if ($failed_logins) {
11206
  $counter = 0;
11207
 
11208
+ for ($key = $page_offset; $key < $page_limit; $key++) {
11209
+ if (array_key_exists($key, $failed_logins['entries'])) {
11210
  $login_data = $failed_logins['entries'][ $key ];
11211
  $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
11212
+ $wrong_user_password = 'hidden';
11213
+ $wrong_user_password_color = 'default';
11214
 
11215
+ if (sucuriscan_collect_wrong_passwords() === true) {
11216
+ if (isset($login_data['user_password']) && !empty($login_data['user_password'])) {
11217
+ $wrong_user_password = $login_data['user_password'];
11218
+ $wrong_user_password_color = 'none';
 
 
11219
  } else {
11220
+ $wrong_user_password = 'empty';
11221
+ $wrong_user_password_color = 'info';
11222
  }
11223
  }
11224
 
11225
+ $template_variables['FailedLogins.List'] .= SucuriScanTemplate::getSnippet(
11226
+ 'lastlogins-failedlogins',
11227
+ array(
11228
+ 'FailedLogins.CssClass' => $css_class,
11229
+ 'FailedLogins.Num' => $login_data['attempt_count'],
11230
+ 'FailedLogins.Username' => $login_data['user_login'],
11231
+ 'FailedLogins.RemoteAddr' => $login_data['remote_addr'],
11232
+ 'FailedLogins.UserAgent' => $login_data['user_agent'],
11233
+ 'FailedLogins.Password' => $wrong_user_password,
11234
+ 'FailedLogins.PasswordColor' => $wrong_user_password_color,
11235
+ 'FailedLogins.Datetime' => SucuriScan::datetime($login_data['attempt_time']),
11236
+ )
11237
+ );
11238
+ $counter++;
11239
  }
11240
  }
11241
 
11242
+ if ($counter > 0) {
11243
  $template_variables['FailedLogins.NoItemsVisibility'] = 'hidden';
11244
  }
11245
 
11246
+ $template_variables['FailedLogins.PaginationLinks'] = SucuriScanTemplate::pagination(
11247
  '%%SUCURI.URL.Lastlogins%%#failed-logins',
11248
  $failed_logins['count'],
11249
  $max_per_page
11250
  );
11251
 
11252
+ if ($failed_logins['count'] > $max_per_page) {
11253
  $template_variables['FailedLogins.PaginationVisibility'] = 'visible';
11254
  }
11255
  }
11256
 
11257
  $template_variables['FailedLogins.MaxFailedLogins'] = $max_failed_logins;
11258
 
11259
+ if ($notify_bruteforce_attack == 'enabled') {
11260
  $template_variables['FailedLogins.WarningVisibility'] = 'hidden';
11261
  }
11262
 
11263
+ if (sucuriscan_collect_wrong_passwords() !== true) {
11264
  $template_variables['FailedLogins.CollectPasswordsVisibility'] = 'hidden';
11265
  }
11266
 
11267
+ return SucuriScanTemplate::getSection('lastlogins-failedlogins', $template_variables);
11268
  }
11269
 
11270
  /**
11272
  *
11273
  * @return boolean TRUE if the password must be collected, FALSE otherwise.
11274
  */
11275
+ function sucuriscan_collect_wrong_passwords()
11276
+ {
11277
+ return SucuriScanOption::is_enabled(':collect_wrong_passwords');
11278
  }
11279
 
11280
  /**
11289
  * @param boolean $reset Whether the file will be resetted or not.
11290
  * @return string The full (relative) path where the file is located.
11291
  */
11292
+ function sucuriscan_failed_logins_datastore_path($get_old_logs = false, $reset = false)
11293
+ {
11294
  $file_name = $get_old_logs ? 'sucuri-oldfailedlogins.php' : 'sucuri-failedlogins.php';
11295
+ $datastore_path = SucuriScan::datastore_folder_path($file_name);
11296
  $default_content = sucuriscan_failed_logins_default_content();
11297
 
11298
  // Create the file if it does not exists.
11299
+ if (!file_exists($datastore_path) || $reset) {
11300
+ @file_put_contents($datastore_path, $default_content, LOCK_EX);
11301
  }
11302
 
11303
  // Return the datastore path if the file exists (or was created).
11304
+ if (file_exists($datastore_path) && is_readable($datastore_path)) {
 
 
 
11305
  return $datastore_path;
11306
  }
11307
 
11313
  *
11314
  * @return string Default content of the file.
11315
  */
11316
+ function sucuriscan_failed_logins_default_content()
11317
+ {
11318
  $default_content = "<?php exit(0); ?>\n";
11319
 
11320
  return $default_content;
11331
  * @param boolean $get_old_logs Whether the old logs will be retrieved or not.
11332
  * @return array Information and entries gathered from the failed logins datastore file.
11333
  */
11334
+ function sucuriscan_get_failed_logins($get_old_logs = false)
11335
+ {
11336
+ $datastore_path = sucuriscan_failed_logins_datastore_path($get_old_logs);
11337
  $default_content = sucuriscan_failed_logins_default_content();
11338
+ $default_content_n = substr_count($default_content, "\n");
11339
 
11340
+ if ($datastore_path) {
11341
+ $lines = SucuriScanFileInfo::file_lines($datastore_path);
11342
 
11343
+ if ($lines) {
11344
  $failed_logins = array(
11345
  'count' => 0,
11346
  'first_attempt' => 0,
11350
  );
11351
 
11352
  // Read and parse all the entries found in the datastore file.
11353
+ $offset = count($lines) - 1;
11354
 
11355
+ for ($key = $offset; $key >= 0; $key--) {
11356
+ $line = trim($lines[ $key ]);
11357
+ $login_data = @json_decode($line, true);
11358
 
11359
+ if (is_array($login_data)) {
11360
+ $login_data['attempt_date'] = date('r', $login_data['attempt_time']);
11361
  $login_data['attempt_count'] = ( $key + 1 );
11362
 
11363
+ if (!$login_data['user_agent']) {
11364
  $login_data['user_agent'] = 'Unknown';
11365
  }
11366
 
11367
+ if (!isset($login_data['user_password'])) {
11368
  $login_data['user_password'] = '';
11369
  }
11370
 
11374
  }
11375
 
11376
  // Calculate the different time between the first and last attempt.
11377
+ if ($failed_logins['count'] > 0) {
11378
+ $z = abs($failed_logins['count'] - 1);
11379
  $failed_logins['last_attempt'] = $failed_logins['entries'][ $z ]['attempt_time'];
11380
  $failed_logins['first_attempt'] = $failed_logins['entries'][0]['attempt_time'];
11381
+ $failed_logins['diff_time'] = abs($failed_logins['last_attempt'] - $failed_logins['first_attempt']);
11382
 
11383
  return $failed_logins;
11384
  }
11398
  * @param string $wrong_password Wrong password used during the supposed attack.
11399
  * @return boolean Whether the information of the current failed login event was stored or not.
11400
  */
11401
+ function sucuriscan_log_failed_login($user_login = '', $wrong_password = '')
11402
+ {
11403
  $datastore_path = sucuriscan_failed_logins_datastore_path();
11404
 
11405
  // Do not collect wrong passwords if it is not necessary.
11406
+ if (sucuriscan_collect_wrong_passwords() !== true) {
11407
  $wrong_password = '';
11408
  }
11409
 
11410
+ if ($datastore_path) {
11411
  $login_data = json_encode(array(
11412
  'user_login' => $user_login,
11413
  'user_password' => $wrong_password,
11416
  'user_agent' => SucuriScan::get_user_agent(),
11417
  ));
11418
 
11419
+ $written = @file_put_contents(
11420
+ $datastore_path,
11421
+ $login_data . "\n",
11422
+ FILE_APPEND
11423
+ );
11424
 
11425
+ if ($written > 0) {
11426
+ return true;
11427
+ }
11428
  }
11429
 
11430
  return false;
11439
  * @param array $failed_logins Information and entries gathered from the failed logins datastore file.
11440
  * @return boolean Whether the report was sent via email or not.
11441
  */
11442
+ function sucuriscan_report_failed_logins($failed_logins = array())
11443
+ {
11444
+ if ($failed_logins && $failed_logins['count'] > 0) {
11445
  $prettify_mails = SucuriScanMail::prettify_mails();
11446
  $collect_wrong_passwords = sucuriscan_collect_wrong_passwords();
11447
  $mail_content = '';
11448
 
11449
+ if ($prettify_mails) {
11450
  $table_html = '<table border="1" cellspacing="0" cellpadding="0">';
11451
 
11452
  // Add the table headers.
11454
  $table_html .= '<tr>';
11455
  $table_html .= '<th>Username</th>';
11456
 
11457
+ if ($collect_wrong_passwords === true) {
11458
  $table_html .= '<th>Password</th>';
11459
  }
11460
 
11467
  $table_html .= '<tbody>';
11468
  }
11469
 
11470
+ foreach ($failed_logins['entries'] as $login_data) {
11471
+ if ($prettify_mails) {
11472
  $table_html .= '<tr>';
11473
+ $table_html .= '<td>' . esc_attr($login_data['user_login']) . '</td>';
11474
 
11475
+ if ($collect_wrong_passwords === true) {
11476
+ $table_html .= '<td>' . esc_attr($login_data['user_password']) . '</td>';
11477
  }
11478
 
11479
+ $table_html .= '<td>' . esc_attr($login_data['remote_addr']) . '</td>';
11480
  $table_html .= '<td>' . $login_data['attempt_time'] . '</td>';
11481
  $table_html .= '<td>' . $login_data['attempt_date'] . '</td>';
11482
  $table_html .= '</tr>';
11484
  $mail_content .= "\n";
11485
  $mail_content .= 'Username: ' . $login_data['user_login'] . "\n";
11486
 
11487
+ if ($collect_wrong_passwords === true) {
11488
  $mail_content .= 'Password: ' . $login_data['user_password'] . "\n";
11489
  }
11490
 
11494
  }
11495
  }
11496
 
11497
+ if ($prettify_mails) {
11498
  $table_html .= '</tbody>';
11499
  $table_html .= '</table>';
11500
  $mail_content = $table_html;
11501
  }
11502
 
11503
+ if (SucuriScanEvent::notify_event('bruteforce_attack', $mail_content)) {
11504
  sucuriscan_reset_failed_logins();
11505
 
11506
  return true;
11518
  *
11519
  * @return boolean Whether the datastore file was resetted or not.
11520
  */
11521
+ function sucuriscan_reset_failed_logins()
11522
+ {
11523
+ $datastore_path = SucuriScan::datastore_folder_path('sucuri-failedlogins.php');
11524
+ $datastore_backup_path = sucuriscan_failed_logins_datastore_path(true, false);
11525
  $default_content = sucuriscan_failed_logins_default_content();
11526
+ $current_content = @file_get_contents($datastore_path);
11527
+ $current_content = str_replace($default_content, '', $current_content);
11528
 
11529
  @file_put_contents(
11530
  $datastore_backup_path,
11532
  FILE_APPEND
11533
  );
11534
 
11535
+ return (bool) sucuriscan_failed_logins_datastore_path(false, true);
11536
  }
11537
 
11538
  /**
11543
  * @param boolean $page_nonce True if the nonce is valid, False otherwise.
11544
  * @return void
11545
  */
11546
+ function sucuriscan_settings_form_submissions($page_nonce = null)
11547
+ {
11548
  global $sucuriscan_schedule_allowed,
11549
  $sucuriscan_interface_allowed,
11550
  $sucuriscan_notify_options,
 
 
11551
  $sucuriscan_email_subjects;
11552
 
11553
  // Use this conditional to avoid double checking.
11554
+ if (is_null($page_nonce)) {
11555
  $page_nonce = SucuriScanInterface::check_nonce();
11556
  }
11557
 
11558
+ if ($page_nonce) {
11559
  // Save API key after it was recovered by the administrator.
11560
+ if ($api_key = SucuriScanRequest::post(':manual_api_key')) {
11561
+ SucuriScanAPI::setPluginKey($api_key, true);
11562
  SucuriScanEvent::schedule_task();
11563
+ SucuriScanEvent::report_info_event('Sucuri API key was added manually.');
11564
  }
11565
 
11566
  // Remove API key from the local storage.
11567
+ if (SucuriScanRequest::post(':remove_api_key') !== false) {
11568
+ SucuriScanAPI::setPluginKey('');
11569
+ wp_clear_scheduled_hook('sucuriscan_scheduled_scan');
11570
+ SucuriScanEvent::report_critical_event('Sucuri API key was deleted.');
11571
+ SucuriScanEvent::notify_event('plugin_change', 'Sucuri API key removed');
11572
  }
11573
 
11574
  // Enable or disable the filesystem scanner.
11575
+ if ($fs_scanner = SucuriScanRequest::post(':fs_scanner', '(en|dis)able')) {
11576
  $action_d = $fs_scanner . 'd';
11577
  $message = 'Main file system scanner was <code>' . $action_d . '</code>';
11578
 
11579
+ SucuriScanOption::update_option(':fs_scanner', $action_d);
11580
+ SucuriScanEvent::report_auto_event($message);
11581
+ SucuriScanEvent::notify_event('plugin_change', $message);
11582
+ SucuriScanInterface::info($message);
 
 
 
 
 
 
 
 
 
 
 
11583
  }
11584
 
11585
  // Enable or disable the filesystem scanner for file integrity.
11586
+ if ($scan_checksums = SucuriScanRequest::post(':scan_checksums', '(en|dis)able')) {
11587
  $action_d = $scan_checksums . 'd';
11588
  $message = 'File system scanner for file integrity was <code>' . $action_d . '</code>';
11589
 
11590
+ SucuriScanOption::update_option(':scan_checksums', $action_d);
11591
+ SucuriScanEvent::report_auto_event($message);
11592
+ SucuriScanEvent::notify_event('plugin_change', $message);
11593
+ SucuriScanInterface::info($message);
11594
  }
11595
 
11596
  // Enable or disable the filesystem scanner for error logs.
11597
+ if ($ignore_scanning = SucuriScanRequest::post(':ignore_scanning', '(en|dis)able')) {
11598
  $action_d = $ignore_scanning . 'd';
11599
  $message = 'File system scanner rules to ignore directories was <code>' . $action_d . '</code>';
11600
 
11601
+ SucuriScanOption::update_option(':ignore_scanning', $action_d);
11602
+ SucuriScanEvent::report_auto_event($message);
11603
+ SucuriScanEvent::notify_event('plugin_change', $message);
11604
+ SucuriScanInterface::info($message);
11605
  }
11606
 
11607
  // Enable or disable the filesystem scanner for error logs.
11608
+ if ($scan_errorlogs = SucuriScanRequest::post(':scan_errorlogs', '(en|dis)able')) {
11609
  $action_d = $scan_errorlogs . 'd';
11610
  $message = 'File system scanner for error logs was <code>' . $action_d . '</code>';
11611
 
11612
+ SucuriScanOption::update_option(':scan_errorlogs', $action_d);
11613
+ SucuriScanEvent::report_auto_event($message);
11614
+ SucuriScanEvent::notify_event('plugin_change', $message);
11615
+ SucuriScanInterface::info($message);
11616
  }
11617
 
11618
  // Enable or disable the error logs parsing.
11619
+ if ($parse_errorlogs = SucuriScanRequest::post(':parse_errorlogs', '(en|dis)able')) {
11620
  $action_d = $parse_errorlogs . 'd';
11621
  $message = 'Analysis of main error log file was <code>' . $action_d . '</code>';
11622
 
11623
+ SucuriScanOption::update_option(':parse_errorlogs', $action_d);
11624
+ SucuriScanEvent::report_auto_event($message);
11625
+ SucuriScanEvent::notify_event('plugin_change', $message);
11626
+ SucuriScanInterface::info($message);
11627
  }
11628
 
11629
  // Enable or disable the SiteCheck scanner and the malware scan page.
11630
+ if ($sitecheck_scanner = SucuriScanRequest::post(':sitecheck_scanner', '(en|dis)able')) {
11631
  $action_d = $sitecheck_scanner . 'd';
11632
  $message = 'SiteCheck malware and blacklist scanner was <code>' . $action_d . '</code>';
11633
 
11634
+ SucuriScanOption::update_option(':sitecheck_scanner', $action_d);
11635
+ SucuriScanEvent::report_auto_event($message);
11636
+ SucuriScanEvent::notify_event('plugin_change', $message);
11637
+ SucuriScanInterface::info($message);
11638
  }
11639
 
11640
  // Modify the schedule of the filesystem scanner.
11641
+ if ($frequency = SucuriScanRequest::post(':scan_frequency')) {
11642
+ if (array_key_exists($frequency, $sucuriscan_schedule_allowed)) {
11643
+ SucuriScanOption::update_option(':scan_frequency', $frequency);
11644
+ wp_clear_scheduled_hook('sucuriscan_scheduled_scan');
11645
 
11646
+ if ($frequency != '_oneoff') {
11647
+ wp_schedule_event(time() + 10, $frequency, 'sucuriscan_scheduled_scan');
11648
  }
11649
 
11650
+ $frequency_title = strtolower($sucuriscan_schedule_allowed[ $frequency ]);
11651
  $message = 'File system scanning frequency set to <code>' . $frequency_title . '</code>';
11652
 
11653
+ SucuriScanEvent::report_info_event($message);
11654
+ SucuriScanEvent::notify_event('plugin_change', $message);
11655
+ SucuriScanInterface::info($message);
11656
  }
11657
  }
11658
 
11659
  // Set the method (aka. interface) that will be used to scan the site.
11660
+ if ($interface = SucuriScanRequest::post(':scan_interface')) {
11661
+ $allowed_values = array_keys($sucuriscan_interface_allowed);
11662
 
11663
+ if (in_array($interface, $allowed_values)) {
11664
  $message = 'File system scanning interface set to <code>' . $interface . '</code>';
11665
 
11666
+ SucuriScanOption::update_option(':scan_interface', $interface);
11667
+ SucuriScanEvent::report_info_event($message);
11668
+ SucuriScanEvent::notify_event('plugin_change', $message);
11669
+ SucuriScanInterface::info($message);
11670
  }
11671
  }
11672
 
11673
  // Update the limit of error log lines to parse.
11674
+ if ($errorlogs_limit = SucuriScanRequest::post(':errorlogs_limit', '[0-9]+')) {
11675
+ if ($errorlogs_limit > 1000) {
11676
+ SucuriScanInterface::error('Analyze more than 1,000 lines will take too much time.');
11677
  } else {
11678
+ SucuriScanOption::update_option(':errorlogs_limit', $errorlogs_limit);
11679
+ SucuriScanInterface::info('Analyze last <code>' . $errorlogs_limit . '</code> entries encountered in the error logs.');
11680
 
11681
+ if ($errorlogs_limit == 0) {
11682
+ SucuriScanOption::update_option(':parse_errorlogs', 'disabled');
11683
  }
11684
  }
11685
  }
11686
 
11687
  // Reset the plugin security logs.
11688
  $allowed_log_files = '(integrity|lastlogins|failedlogins|sitecheck)';
11689
+ if ($reset_logfile = SucuriScanRequest::post(':reset_logfile', $allowed_log_files)) {
11690
  $files_to_delete = array(
11691
  'sucuri-' . $reset_logfile . '.php',
11692
  'sucuri-old' . $reset_logfile . '.php',
11693
  );
11694
 
11695
+ foreach ($files_to_delete as $log_filename) {
11696
+ $log_filepath = SucuriScan::datastore_folder_path($log_filename);
11697
 
11698
+ if (@unlink($log_filepath)) {
11699
+ $log_filename_simple = str_replace('.php', '', $log_filename);
11700
  $message = 'Deleted security log <code>' . $log_filename_simple . '</code>';
11701
 
11702
+ SucuriScanEvent::report_debug_event($message);
11703
+ SucuriScanInterface::info($message);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11704
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11705
  }
11706
  }
11707
 
11708
  // Ignore a new event for email notifications.
11709
+ if ($action = SucuriScanRequest::post(':ignorerule_action', '(add|remove)')) {
11710
+ $ignore_rule = SucuriScanRequest::post(':ignorerule');
11711
 
11712
+ if ($action == 'add') {
11713
+ if (SucuriScanOption::add_ignored_event($ignore_rule)) {
11714
+ SucuriScanInterface::info('Post-type ignored successfully.');
11715
+ SucuriScanEvent::report_warning_event('Changes in <code>' . $ignore_rule . '</code> post-type will be ignored');
11716
  } else {
11717
+ SucuriScanInterface::error('The post-type is invalid or it may be already ignored.');
11718
  }
11719
+ } elseif ($action == 'remove') {
11720
+ SucuriScanOption::remove_ignored_event($ignore_rule);
11721
+ SucuriScanInterface::info('Post-type removed from the list successfully.');
11722
+ SucuriScanEvent::report_notice_event('Changes in <code>' . $ignore_rule . '</code> post-type will not be ignored');
11723
  }
11724
  }
11725
 
11726
  // Ignore a new directory path for the file system scans.
11727
+ if ($action = SucuriScanRequest::post(':ignorescanning_action', '(ignore|unignore)')) {
11728
+ $ignore_directories = SucuriScanRequest::post(':ignorescanning_dirs', '_array');
11729
+ $ignore_file = SucuriScanRequest::post(':ignorescanning_file');
11730
 
11731
+ if ($action == 'ignore') {
11732
  // Target a single file path to be ignored.
11733
+ if ($ignore_file !== false) {
11734
  $ignore_directories = array( $ignore_file );
11735
  }
11736
 
11737
  // Target a list of directories to be ignored.
11738
+ if (!empty($ignore_directories)) {
11739
  $were_ignored = array();
11740
 
11741
+ foreach ($ignore_directories as $resource_path) {
11742
+ if (file_exists($resource_path)
11743
+ && SucuriScanFSScanner::ignore_directory($resource_path)
 
11744
  ) {
11745
  $were_ignored[] = $resource_path;
11746
  }
11747
  }
11748
 
11749
+ if (!empty($were_ignored)) {
11750
+ SucuriScanInterface::info('Items selected will be ignored in future scans.');
11751
+ SucuriScanEvent::report_warning_event(sprintf(
11752
  'Resources will not be scanned: (multiple entries): %s',
11753
+ @implode(',', $ignore_directories)
11754
+ ));
11755
  }
11756
  }
11757
+ } elseif ($action == 'unignore') {
11758
+ foreach ($ignore_directories as $directory_path) {
11759
+ SucuriScanFSScanner::unignore_directory($directory_path);
11760
  }
11761
 
11762
+ SucuriScanInterface::info('Items selected will not be ignored anymore.');
11763
+ SucuriScanEvent::report_notice_event(sprintf(
11764
  'Resources will be scanned: (multiple entries): %s',
11765
+ @implode(',', $ignore_directories)
11766
+ ));
11767
  }
11768
  }
11769
 
11770
  // Trust and IP address to ignore notifications for a subnet.
11771
+ if ($trust_ip = SucuriScanRequest::post(':trust_ip')) {
11772
+ if (SucuriScan::is_valid_ip($trust_ip)
11773
+ || SucuriScan::is_valid_cidr($trust_ip)
 
11774
  ) {
11775
+ $cache = new SucuriScanCache('trustip');
11776
+ $ip_info = SucuriScan::get_ip_info($trust_ip);
11777
  $ip_info['added_at'] = SucuriScan::local_time();
11778
+ $cache_key = md5($ip_info['remote_addr']);
11779
 
11780
+ if ($cache->exists($cache_key)) {
11781
+ SucuriScanInterface::error('The IP address specified was already trusted.');
11782
+ } elseif ($cache->add($cache_key, $ip_info)) {
11783
  $message = 'Changes from <code>' . $trust_ip . '</code> will be ignored';
11784
 
11785
+ SucuriScanEvent::report_warning_event($message);
11786
+ SucuriScanInterface::info($message);
11787
  } else {
11788
+ SucuriScanInterface::error('The new entry was not saved in the datastore file.');
11789
  }
11790
  }
11791
  }
11792
 
11793
  // Trust and IP address to ignore notifications for a subnet.
11794
+ if ($del_trust_ip = SucuriScanRequest::post(':del_trust_ip', '_array')) {
11795
+ $cache = new SucuriScanCache('trustip');
11796
 
11797
+ foreach ($del_trust_ip as $cache_key) {
11798
+ $cache->delete($cache_key);
11799
  }
11800
 
11801
+ SucuriScanInterface::info('The IP addresses selected were deleted successfully.');
11802
  }
11803
 
11804
  // Update the settings for the heartbeat API.
11805
+ if ($heartbeat_status = SucuriScanRequest::post(':heartbeat_status')) {
11806
  $statuses_allowed = SucuriScanHeartbeat::statuses_allowed();
11807
 
11808
+ if (array_key_exists($heartbeat_status, $statuses_allowed)) {
11809
  $message = 'Heartbeat status set to <code>' . $heartbeat_status . '</code>';
11810
 
11811
+ SucuriScanOption::update_option(':heartbeat', $heartbeat_status);
11812
+ SucuriScanEvent::report_info_event($message);
11813
+ SucuriScanInterface::info($message);
11814
  } else {
11815
+ SucuriScanInterface::error('Heartbeat status not allowed.');
11816
  }
11817
  }
11818
 
11819
  // Update the value of the heartbeat pulse.
11820
+ if ($heartbeat_pulse = SucuriScanRequest::post(':heartbeat_pulse')) {
11821
  $pulses_allowed = SucuriScanHeartbeat::pulses_allowed();
11822
 
11823
+ if (array_key_exists($heartbeat_pulse, $pulses_allowed)) {
11824
  $message = 'Heartbeat pulse set to <code>' . $heartbeat_pulse . '</code> seconds.';
11825
 
11826
+ SucuriScanOption::update_option(':heartbeat_pulse', $heartbeat_pulse);
11827
+ SucuriScanEvent::report_info_event($message);
11828
+ SucuriScanInterface::info($message);
11829
  } else {
11830
+ SucuriScanInterface::error('Heartbeat pulse not allowed.');
11831
  }
11832
  }
11833
 
11834
  // Update the value of the heartbeat interval.
11835
+ if ($heartbeat_interval = SucuriScanRequest::post(':heartbeat_interval')) {
11836
  $intervals_allowed = SucuriScanHeartbeat::intervals_allowed();
11837
 
11838
+ if (array_key_exists($heartbeat_interval, $intervals_allowed)) {
11839
  $message = 'Heartbeat interval set to <code>' . $heartbeat_interval . '</code>';
11840
 
11841
+ SucuriScanOption::update_option(':heartbeat_interval', $heartbeat_interval);
11842
+ SucuriScanEvent::report_info_event($message);
11843
+ SucuriScanInterface::info($message);
11844
  } else {
11845
+ SucuriScanInterface::error('Heartbeat interval not allowed.');
11846
  }
11847
  }
11848
 
11849
  // Enable or disable the auto-start execution of heartbeat.
11850
+ if ($heartbeat_autostart = SucuriScanRequest::post(':heartbeat_autostart', '(en|dis)able')) {
11851
  $action_d = $heartbeat_autostart . 'd';
11852
  $message = 'Heartbeat auto-start was <code>' . $action_d . '</code>';
11853
 
11854
+ SucuriScanOption::update_option(':heartbeat_autostart', $action_d);
11855
+ SucuriScanEvent::report_info_event($message);
11856
+ SucuriScanInterface::info($message);
 
 
 
 
 
 
 
 
 
 
 
 
11857
  }
11858
  }
11859
  }
11860
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11861
  /**
11862
  * Read and parse the content of the general settings template.
11863
  *
11864
  * @return string Parsed HTML code for the general settings panel.
11865
  */
11866
+ function sucuriscan_settings_general($nonce)
11867
+ {
11868
+ // Process all form submissions.
11869
+ sucuriscan_settings_form_submissions($nonce);
11870
 
11871
+ $params = array();
 
11872
 
11873
+ // Keep the reset options panel and form submission processor before anything else.
11874
+ $params['SettingsSection.ResetOptions'] = sucuriscan_settings_general_resetoptions($nonce);
11875
 
11876
+ // Build HTML code for the additional general settings panels.
11877
+ $params['SettingsSection.ApiKey'] = sucuriscan_settings_general_apikey($nonce);
11878
+ $params['SettingsSection.DataStorage'] = sucuriscan_settings_general_datastorage($nonce);
11879
+ $params['SettingsSection.ReverseProxy'] = sucuriscan_settings_general_reverseproxy($nonce);
11880
+ $params['SettingsSection.PasswordCollector'] = sucuriscan_settings_general_pwdcollector($nonce);
11881
+ $params['SettingsSection.IPDiscoverer'] = sucuriscan_settings_general_ipdiscoverer($nonce);
11882
+ $params['SettingsSection.CommentMonitor'] = sucuriscan_settings_general_commentmonitor($nonce);
11883
+ $params['SettingsSection.XhrMonitor'] = sucuriscan_settings_general_xhrmonitor($nonce);
11884
+ $params['SettingsSection.AuditLogStats'] = sucuriscan_settings_general_auditlogstats($nonce);
11885
+ $params['SettingsSection.Datetime'] = sucuriscan_settings_general_datetime($nonce);
11886
 
11887
+ sucuriscan_settings_general_adsvisibility($nonce);
 
 
11888
 
11889
+ return SucuriScanTemplate::getSection('settings-general', $params);
11890
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11891
 
11892
+ function sucuriscan_settings_general_resetoptions($nonce)
11893
+ {
11894
+ // Reset all the plugin's options.
11895
+ if ($nonce && SucuriScanRequest::post(':reset_options') !== false) {
11896
+ $process = SucuriScanRequest::post(':process_form');
11897
 
11898
+ if (intval($process) === 1) {
11899
+ // Notify the event before the API key is removed.
11900
+ $message = 'Sucuri plugin options were reset';
11901
+ SucuriScanEvent::report_critical_event($message);
11902
+ SucuriScanEvent::notify_event('plugin_change', $message);
 
 
 
 
 
 
11903
 
11904
+ // Remove all plugin options from the database.
11905
+ SucuriScanOption::delete_plugin_options();
11906
 
11907
+ // Remove the scheduled tasks.
11908
+ wp_clear_scheduled_hook('sucuriscan_scheduled_scan');
 
11909
 
11910
+ // Remove all the local security logs.
11911
+ @unlink(SucuriScan::datastore_folder_path('.htaccess'));
11912
+ @unlink(SucuriScan::datastore_folder_path('index.html'));
11913
+ @unlink(SucuriScan::datastore_folder_path('sucuri-failedlogins.php'));
11914
+ @unlink(SucuriScan::datastore_folder_path('sucuri-integrity.php'));
11915
+ @unlink(SucuriScan::datastore_folder_path('sucuri-lastlogins.php'));
11916
+ @unlink(SucuriScan::datastore_folder_path('sucuri-oldfailedlogins.php'));
11917
+ @unlink(SucuriScan::datastore_folder_path('sucuri-plugindata.php'));
11918
+ @unlink(SucuriScan::datastore_folder_path('sucuri-sitecheck.php'));
11919
+ @unlink(SucuriScan::datastore_folder_path('sucuri-trustip.php'));
11920
+ @rmdir(SucuriScan::datastore_folder_path());
11921
 
11922
+ // Revert hardening of core directories (includes, content, uploads).
11923
+ SucuriScanHardening::dewhitelist('ms-files.php', 'wp-includes');
11924
+ SucuriScanHardening::dewhitelist('wp-tinymce.php', 'wp-includes');
11925
+ SucuriScanHardening::unharden_directory(ABSPATH . '/wp-includes');
11926
+ SucuriScanHardening::unharden_directory(WP_CONTENT_DIR . '/uploads');
11927
+ SucuriScanHardening::unharden_directory(WP_CONTENT_DIR);
11928
 
11929
+ SucuriScanInterface::info('Plugin options, core directory hardening, and security logs were reset');
11930
+ } else {
11931
+ SucuriScanInterface::error('You need to confirm that you understand the risk of this operation.');
11932
+ }
 
11933
  }
11934
 
11935
+ return SucuriScanTemplate::getSection('settings-general-resetoptions');
11936
  }
11937
 
11938
  function sucuriscan_settings_general_apikey($nonce)
11952
 
11953
  if ($user_obj !== false && user_can($user_obj, 'administrator')) {
11954
  // Send request to generate new API key or display form to set manually.
11955
+ if (SucuriScanAPI::registerSite($user_obj->user_email)) {
11956
+ $api_registered_modal = SucuriScanTemplate::getModal(
11957
  'settings-apiregistered',
11958
  array(
11959
  'Title' => 'Site registered successfully',
11968
 
11969
  // Recover API key through the email registered previously.
11970
  if (SucuriScanRequest::post(':recover_key') !== false) {
11971
+ SucuriScanAPI::recoverKey();
11972
  SucuriScanEvent::report_info_event('Recovery of the Sucuri API key was requested.');
11973
+ $api_recovery_modal = SucuriScanTemplate::getModal(
11974
  'settings-apirecovery',
11975
  array(
11976
  'Title' => 'Plugin API Key Recovery',
11980
  }
11981
  }
11982
 
11983
+ $api_key = SucuriScanAPI::getPluginKey();
11984
 
11985
  // Check whether the domain name is valid or not.
11986
  if (!$api_key) {
11989
  $invalid_domain = (bool) ($domain_address === $clean_domain);
11990
  }
11991
 
11992
+ $params['APIKey'] = (!$api_key ? '(not set)' : $api_key);
11993
  $params['APIKey.RecoverVisibility'] = SucuriScanTemplate::visibility(!$api_key && !$display_manual_key_form);
11994
  $params['APIKey.ManualKeyFormVisibility'] = SucuriScanTemplate::visibility($display_manual_key_form);
11995
  $params['APIKey.RemoveVisibility'] = SucuriScanTemplate::visibility((bool) $api_key);
11997
  $params['ModalWhenAPIRegistered'] = $api_registered_modal;
11998
  $params['ModalForApiKeyRecovery'] = $api_recovery_modal;
11999
 
12000
+ return SucuriScanTemplate::getSection('settings-general-apikey', $params);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12001
  }
12002
 
12003
  function sucuriscan_settings_general_datastorage($nonce)
12045
 
12046
  $params['DatastorePath'] = SucuriScanOption::get_option(':datastore_path');
12047
 
12048
+ return SucuriScanTemplate::getSection('settings-general-datastorage', $params);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12049
  }
12050
 
12051
  function sucuriscan_settings_general_reverseproxy($nonce)
12062
  $revproxy = SucuriScanRequest::post(':revproxy', '(en|dis)able');
12063
 
12064
  if ($revproxy) {
12065
+ if ($revproxy === 'enable') {
12066
+ SucuriScanOption::setRevProxy('enable');
12067
+ SucuriScanOption::setAddrHeader('HTTP_X_SUCURI_CLIENTIP');
12068
+ } else {
12069
+ SucuriScanOption::setRevProxy('disable');
12070
+ SucuriScanOption::setAddrHeader('REMOTE_ADDR');
12071
+ }
12072
  }
12073
  }
12074
 
12079
  $params['ReverseProxySwitchCssClass'] = 'button-success';
12080
  }
12081
 
12082
+ return SucuriScanTemplate::getSection('settings-general-reverseproxy', $params);
12083
  }
12084
 
12085
  function sucuriscan_settings_general_pwdcollector($nonce)
12122
  $params['PwdCollectorSwitchCssClass'] = 'button-danger';
12123
  }
12124
 
12125
+ return SucuriScanTemplate::getSection('settings-general-pwdcollector', $params);
12126
  }
12127
 
12128
  function sucuriscan_settings_general_ipdiscoverer($nonce)
12135
  'WebsiteURL' => 'Unknown',
12136
  'RemoteAddress' => '127.0.0.1',
12137
  'RemoteAddressHeader' => 'INVALID',
12138
+ 'AddrHeaderOptions' => '',
12139
  /* Switch form information. */
12140
  'DnsLookupsStatus' => 'Enabled',
12141
  'DnsLookupsSwitchText' => 'Disable',
12143
  'DnsLookupsSwitchCssClass' => 'button-danger',
12144
  );
12145
 
12146
+ // Get main HTTP header for IP retrieval.
12147
+ $allowed_headers = SucuriScan::allowedHttpHeaders(true);
12148
+
12149
  // Configure the DNS lookups option for reverse proxy detection.
12150
  if ($nonce) {
12151
  $dns_lookups = SucuriScanRequest::post(':dns_lookups', '(en|dis)able');
12152
+ $addr_header = SucuriScanRequest::post(':addr_header');
12153
 
12154
  if ($dns_lookups) {
12155
  $action_d = $dns_lookups . 'd';
12160
  SucuriScanEvent::notify_event('plugin_change', $message);
12161
  SucuriScanInterface::info($message);
12162
  }
12163
+
12164
+ if ($addr_header) {
12165
+ if ($addr_header === 'REMOTE_ADDR') {
12166
+ SucuriScanOption::setAddrHeader('REMOTE_ADDR');
12167
+ SucuriScanOption::setRevProxy('disable');
12168
+ } else {
12169
+ SucuriScanOption::setAddrHeader($addr_header);
12170
+ SucuriScanOption::setRevProxy('enable');
12171
+ }
12172
+ }
12173
  }
12174
 
12175
  if (SucuriScanOption::is_disabled(':dns_lookups')) {
12189
  $params['RemoteAddressHeader'] = SucuriScan::get_remote_addr_header();
12190
  $params['RemoteAddress'] = SucuriScan::get_remote_addr();
12191
  $params['WebsiteURL'] = SucuriScan::get_domain();
12192
+ $params['AddrHeaderOptions'] = SucuriScanTemplate::selectOptions(
12193
+ $allowed_headers,
12194
+ SucuriScanOption::get_option(':addr_header')
12195
+ );
12196
 
12197
  if ($base_domain !== $proxy_info['http_host']) {
12198
  $params['TopLevelDomain'] = sprintf('%s (%s)', $params['TopLevelDomain'], $base_domain);
12199
  }
12200
 
12201
+ return SucuriScanTemplate::getSection('settings-general-ipdiscoverer', $params);
12202
  }
12203
 
12204
  function sucuriscan_settings_general_commentmonitor($nonce)
12232
  $params['CommentMonitorSwitchCssClass'] = 'button-success';
12233
  }
12234
 
12235
+ return SucuriScanTemplate::getSection('settings-general-commentmonitor', $params);
12236
  }
12237
 
12238
  function sucuriscan_settings_general_xhrmonitor($nonce)
12266
  $params['XhrMonitorSwitchCssClass'] = 'button-success';
12267
  }
12268
 
12269
+ return SucuriScanTemplate::getSection('settings-general-xhrmonitor', $params);
12270
  }
12271
 
12272
+ function sucuriscan_settings_general_auditlogstats($nonce)
12273
  {
12274
+ $params = array();
12275
+ $params['AuditLogStats.StatusNum'] = '1';
12276
+ $params['AuditLogStats.Status'] = 'Enabled';
12277
+ $params['AuditLogStats.SwitchText'] = 'Disable';
12278
+ $params['AuditLogStats.SwitchValue'] = 'disable';
12279
+ $params['AuditLogStats.SwitchCssClass'] = 'button-danger';
12280
+ $params['AuditLogStats.Limit'] = 0;
12281
 
12282
+ if ($nonce) {
12283
+ // Update the limit for audit logs report.
12284
+ if ($logs4report = SucuriScanRequest::post(':logs4report', '[0-9]{1,4}')) {
12285
+ $_POST['sucuriscan_audit_report'] = 'enable';
12286
+ $message = 'Audit log statistics limit set to <code>' . $logs4report . '</code>';
12287
 
12288
+ SucuriScanOption::update_option(':logs4report', $logs4report);
12289
  SucuriScanEvent::report_info_event($message);
12290
+ SucuriScanEvent::notify_event('plugin_change', $message);
12291
  SucuriScanInterface::info($message);
12292
  }
 
 
12293
 
12294
+ // Enable or disable the audit logs report.
12295
+ if ($audit_report = SucuriScanRequest::post(':audit_report', '(en|dis)able')) {
12296
+ $action_d = $audit_report . 'd';
12297
+ $message = 'Audit log statistics were <code>' . $action_d . '</code>';
 
12298
 
12299
+ SucuriScanOption::update_option(':audit_report', $action_d);
12300
+ SucuriScanEvent::report_info_event($message);
 
 
12301
  SucuriScanEvent::notify_event('plugin_change', $message);
12302
+ SucuriScanInterface::info($message);
12303
+ }
12304
+ }
12305
 
12306
+ $logs4report = SucuriScanOption::get_option(':logs4report');
12307
+ $audit_report = SucuriScanOption::get_option(':audit_report');
12308
+ $params['AuditLogStats.Limit'] = SucuriScan::escape($logs4report);
12309
 
12310
+ if ($audit_report === 'disabled') {
12311
+ $params['AuditLogStats.StatusNum'] = '0';
12312
+ $params['AuditLogStats.Status'] = 'Disabled';
12313
+ $params['AuditLogStats.SwitchText'] = 'Enable';
12314
+ $params['AuditLogStats.SwitchValue'] = 'enable';
12315
+ $params['AuditLogStats.SwitchCssClass'] = 'button-success';
12316
+ }
12317
 
12318
+ return SucuriScanTemplate::getSection('settings-general-auditlogstats', $params);
12319
+ }
 
 
 
 
 
 
 
 
 
12320
 
12321
+ function sucuriscan_settings_general_datetime($nonce)
12322
+ {
12323
+ $params = array();
12324
+ $params['Datetime.AdminURL'] = SucuriScan::admin_url('options-general.php');
12325
+ $params['Datetime.HumanReadable'] = SucuriScan::current_datetime();
12326
+ $params['Datetime.Timestamp'] = SucuriScan::local_time();
12327
+ $params['Datetime.Timezone'] = 'Unknown';
12328
 
12329
+ if (function_exists('wp_timezone_choice')) {
12330
+ $gmt_offset = SucuriScanOption::get_option('gmt_offset');
12331
+ $tzstring = SucuriScanOption::get_option('timezone_string');
12332
+
12333
+ $params['Datetime.Timezone'] = empty($tzstring) ? 'UTC' . $gmt_offset : $tzstring;
12334
  }
12335
 
12336
+ return SucuriScanTemplate::getSection('settings-general-datetime', $params);
12337
+ }
12338
+
12339
+ function sucuriscan_settings_general_adsvisibility($nonce)
12340
+ {
12341
+ // Update the advertisement visibility settings.
12342
+ if ($nonce) {
12343
+ $ads_visibility = SucuriScanRequest::post(':ads_visibility');
12344
+
12345
+ if ($ads_visibility === 'disable') {
12346
+ $option_value = $ads_visibility . 'd';
12347
+ $message = sprintf('Plugin advertisement set to <code>%s</code>', $option_value);
12348
+
12349
+ SucuriScanOption::update_option(':ads_visibility', $option_value);
12350
+ SucuriScanEvent::report_info_event($message);
12351
+ SucuriScanInterface::info($message);
12352
+ }
12353
+ }
12354
  }
12355
 
12356
  /**
12358
  *
12359
  * @return string Parsed HTML code for the scanner settings panel.
12360
  */
12361
+ function sucuriscan_settings_scanner()
12362
+ {
12363
  global $sucuriscan_schedule_allowed,
12364
  $sucuriscan_interface_allowed;
12365
 
12366
  // Get initial variables to decide some things bellow.
12367
+ $fs_scanner = SucuriScanOption::get_option(':fs_scanner');
12368
+ $scan_freq = SucuriScanOption::get_option(':scan_frequency');
12369
+ $scan_interface = SucuriScanOption::get_option(':scan_interface');
12370
+ $scan_checksums = SucuriScanOption::get_option(':scan_checksums');
12371
+ $scan_errorlogs = SucuriScanOption::get_option(':scan_errorlogs');
12372
+ $parse_errorlogs = SucuriScanOption::get_option(':parse_errorlogs');
12373
+ $errorlogs_limit = SucuriScanOption::get_option(':errorlogs_limit');
12374
+ $ignore_scanning = SucuriScanOption::get_option(':ignore_scanning');
12375
+ $sitecheck_scanner = SucuriScanOption::get_option(':sitecheck_scanner');
12376
+ $sitecheck_counter = SucuriScanOption::get_option(':sitecheck_counter');
12377
+ $runtime_scan_human = SucuriScanFSScanner::get_filesystem_runtime(true);
 
12378
 
12379
  // Get the file path of the security logs.
12380
+ $integrity_log_path = SucuriScan::datastore_folder_path('sucuri-integrity.php');
12381
+ $lastlogins_log_path = SucuriScan::datastore_folder_path('sucuri-lastlogins.php');
12382
+ $failedlogins_log_path = SucuriScan::datastore_folder_path('sucuri-failedlogins.php');
12383
+ $sitecheck_log_path = SucuriScan::datastore_folder_path('sucuri-sitecheck.php');
12384
 
12385
  // Generate the HTML code for the option list in the form select fields.
12386
+ $scan_freq_options = SucuriScanTemplate::selectOptions($sucuriscan_schedule_allowed, $scan_freq);
12387
+ $scan_interface_options = SucuriScanTemplate::selectOptions($sucuriscan_interface_allowed, $scan_interface);
12388
 
12389
+ $params = array(
12390
  /* Filesystem scanner */
12391
  'FsScannerStatus' => 'Enabled',
12392
  'FsScannerSwitchText' => 'Disable',
12393
  'FsScannerSwitchValue' => 'disable',
12394
  'FsScannerSwitchCssClass' => 'button-danger',
 
 
 
 
 
12395
  /* Scan files checksum. */
12396
  'ScanChecksumsStatus' => 'Enabled',
12397
  'ScanChecksumsSwitchText' => 'Disable',
12432
  'SiteCheckLogLife' => '0B',
12433
  );
12434
 
12435
+ if ($fs_scanner == 'disabled') {
12436
+ $params['FsScannerStatus'] = 'Disabled';
12437
+ $params['FsScannerSwitchText'] = 'Enable';
12438
+ $params['FsScannerSwitchValue'] = 'enable';
12439
+ $params['FsScannerSwitchCssClass'] = 'button-success';
12440
  }
12441
 
12442
+ if ($scan_checksums == 'disabled') {
12443
+ $params['ScanChecksumsStatus'] = 'Disabled';
12444
+ $params['ScanChecksumsSwitchText'] = 'Enable';
12445
+ $params['ScanChecksumsSwitchValue'] = 'enable';
12446
+ $params['ScanChecksumsSwitchCssClass'] = 'button-success';
12447
  }
12448
 
12449
+ if ($ignore_scanning == 'disabled') {
12450
+ $params['IgnoreScanningStatus'] = 'Disabled';
12451
+ $params['IgnoreScanningSwitchText'] = 'Enable';
12452
+ $params['IgnoreScanningSwitchValue'] = 'enable';
12453
+ $params['IgnoreScanningSwitchCssClass'] = 'button-success';
12454
  }
12455
 
12456
+ if ($scan_errorlogs == 'disabled') {
12457
+ $params['ScanErrorlogsStatus'] = 'Disabled';
12458
+ $params['ScanErrorlogsSwitchText'] = 'Enable';
12459
+ $params['ScanErrorlogsSwitchValue'] = 'enable';
12460
+ $params['ScanErrorlogsSwitchCssClass'] = 'button-success';
12461
  }
12462
 
12463
+ if ($parse_errorlogs == 'disabled') {
12464
+ $params['ParseErrorLogsStatus'] = 'Disabled';
12465
+ $params['ParseErrorLogsSwitchText'] = 'Enable';
12466
+ $params['ParseErrorLogsSwitchValue'] = 'enable';
12467
+ $params['ParseErrorLogsSwitchCssClass'] = 'button-success';
12468
  }
12469
 
12470
+ if ($sitecheck_scanner == 'disabled') {
12471
+ $params['SiteCheckScannerStatus'] = 'Disabled';
12472
+ $params['SiteCheckScannerSwitchText'] = 'Enable';
12473
+ $params['SiteCheckScannerSwitchValue'] = 'enable';
12474
+ $params['SiteCheckScannerSwitchCssClass'] = 'button-success';
12475
+ }
12476
+
12477
+ if (array_key_exists($scan_freq, $sucuriscan_schedule_allowed)) {
12478
+ $params['ScanningFrequency'] = $sucuriscan_schedule_allowed[ $scan_freq ];
12479
+ }
12480
+
12481
+ // Determine the age of the security log files.
12482
+ $params['IntegrityLogLife'] = SucuriScan::human_filesize(@filesize($integrity_log_path));
12483
+ $params['LastLoginLogLife'] = SucuriScan::human_filesize(@filesize($lastlogins_log_path));
12484
+ $params['FailedLoginLogLife'] = SucuriScan::human_filesize(@filesize($failedlogins_log_path));
12485
+ $params['SiteCheckLogLife'] = SucuriScan::human_filesize(@filesize($sitecheck_log_path));
12486
+
12487
+ return SucuriScanTemplate::getSection('settings-scanner', $params);
12488
+ }
12489
+
12490
+ function sucuriscan_settings_ignorescanning()
12491
+ {
12492
+ $params = array(
12493
+ 'IgnoreScanning.ResourceList' => '',
12494
+ 'IgnoreScanning.DisabledVisibility' => 'visible',
12495
+ 'IgnoreScanning.NoItemsVisibility' => 'visible',
12496
+ );
12497
+
12498
+ $ignore_scanning = SucuriScanFSScanner::will_ignore_scanning();
12499
+
12500
+ // Allow disable of this option temporarily.
12501
+ if (SucuriScanRequest::get('no_scan') == 1) {
12502
+ $ignore_scanning = false;
12503
+ }
12504
+
12505
+ // Scan the project and get the ignored paths.
12506
+ if ($ignore_scanning === true) {
12507
+ $counter = 0;
12508
+ $params['IgnoreScanning.DisabledVisibility'] = 'hidden';
12509
+ $dir_list_list = SucuriScanFSScanner::get_ignored_directories_live();
12510
+
12511
+ foreach ($dir_list_list as $group => $dir_list) {
12512
+ foreach ($dir_list as $dir_data) {
12513
+ $valid_entry = false;
12514
+ $snippet_data = array(
12515
+ 'IgnoreScanning.CssClass' => '',
12516
+ 'IgnoreScanning.Directory' => '',
12517
+ 'IgnoreScanning.DirectoryPath' => '',
12518
+ 'IgnoreScanning.IgnoredAt' => '',
12519
+ 'IgnoreScanning.IgnoredAtText' => 'ok',
12520
+ 'IgnoreScanning.IgnoredCssClass' => 'success',
12521
+ );
12522
+
12523
+ if ($group == 'is_ignored') {
12524
+ $valid_entry = true;
12525
+ $snippet_data['IgnoreScanning.Directory'] = urlencode($dir_data['directory_path']);
12526
+ $snippet_data['IgnoreScanning.DirectoryPath'] = $dir_data['directory_path'];
12527
+ $snippet_data['IgnoreScanning.IgnoredAt'] = SucuriScan::datetime($dir_data['ignored_at']);
12528
+ $snippet_data['IgnoreScanning.IgnoredAtText'] = 'ignored';
12529
+ $snippet_data['IgnoreScanning.IgnoredCssClass'] = 'warning';
12530
+ } elseif ($group == 'is_not_ignored') {
12531
+ $valid_entry = true;
12532
+ $snippet_data['IgnoreScanning.Directory'] = urlencode($dir_data);
12533
+ $snippet_data['IgnoreScanning.DirectoryPath'] = $dir_data;
12534
+ }
12535
+
12536
+ if ($valid_entry) {
12537
+ $css_class = ( $counter % 2 == 0 ) ? '' : 'alternate';
12538
+ $snippet_data['IgnoreScanning.CssClass'] = $css_class;
12539
+ $params['IgnoreScanning.ResourceList'] .= SucuriScanTemplate::getSnippet(
12540
+ 'settings-ignorescanning',
12541
+ $snippet_data
12542
+ );
12543
+ $counter++;
12544
+ }
12545
+ }
12546
+ }
12547
+
12548
+ if ($counter > 0) {
12549
+ $params['IgnoreScanning.NoItemsVisibility'] = 'hidden';
12550
+ }
12551
+ }
12552
+
12553
+ return SucuriScanTemplate::getSection('settings-ignorescanning', $params);
12554
+ }
12555
+
12556
+ /**
12557
+ * Read and parse the content of the notification settings template.
12558
+ *
12559
+ * @return string Parsed HTML code for the notification settings panel.
12560
+ */
12561
+ function sucuriscan_settings_alert($nonce)
12562
+ {
12563
+ $params = array();
12564
+
12565
+ $params['AlertSettings.Recipients'] = sucuriscan_settings_alert_recipients($nonce);
12566
+ $params['AlertSettings.Subject'] = sucuriscan_settings_alert_subject($nonce);
12567
+ $params['AlertSettings.PerHour'] = sucuriscan_settings_alert_perhour($nonce);
12568
+ $params['AlertSettings.BruteForce'] = sucuriscan_settings_alert_bruteforce($nonce);
12569
+ $params['AlertSettings.Events'] = sucuriscan_settings_alert_events($nonce);
12570
+
12571
+ return SucuriScanTemplate::getSection('settings-alert', $params);
12572
+ }
12573
+
12574
+ function sucuriscan_settings_alert_recipients($nonce)
12575
+ {
12576
+ $params = array();
12577
+ $params['AlertSettings.Recipients'] = '';
12578
+ $notify_to = SucuriScanOption::get_option(':notify_to');
12579
+ $emails = array();
12580
+
12581
+ // If the recipient list is not empty, explode.
12582
+ if (is_string($notify_to)) {
12583
+ $emails = explode(',', $notify_to);
12584
+ }
12585
+
12586
+ // Process form submission.
12587
+ if ($nonce) {
12588
+ // Add new email address to the alert recipient list.
12589
+ if (SucuriScanRequest::post(':save_recipient') !== false) {
12590
+ $new_email = SucuriScanRequest::post(':recipient');
12591
+
12592
+ if (SucuriScan::is_valid_email($new_email)) {
12593
+ $emails[] = $new_email;
12594
+ $message = 'Sucuri will send email alerts to: <code>' . $new_email . '</code>';
12595
+
12596
+ SucuriScanOption::update_option(':notify_to', implode(',', $emails));
12597
+ SucuriScanEvent::report_info_event($message);
12598
+ SucuriScanEvent::notify_event('plugin_change', $message);
12599
+ SucuriScanInterface::info($message);
12600
+ } else {
12601
+ SucuriScanInterface::error('Email format not supported.');
12602
+ }
12603
+ }
12604
+
12605
+ // Delete one or more recipients from the list.
12606
+ if (SucuriScanRequest::post(':delete_recipients') !== false) {
12607
+ $deleted_emails = array();
12608
+ $recipients = SucuriScanRequest::post(':recipients', '_array');
12609
+
12610
+ foreach ($recipients as $address) {
12611
+ if (in_array($address, $emails)) {
12612
+ $deleted_emails[] = $address;
12613
+ $index = array_search($address, $emails);
12614
+ unset($emails[$index]);
12615
+ }
12616
+ }
12617
+
12618
+ if (!empty($deleted_emails)) {
12619
+ $deleted_emails_str = implode(",\x20", $deleted_emails);
12620
+ $message = 'Sucuri will not send email alerts to: <code>' . $deleted_emails_str . '</code>';
12621
+
12622
+ SucuriScanOption::update_option(':notify_to', implode(',', $emails));
12623
+ SucuriScanEvent::report_info_event($message);
12624
+ SucuriScanEvent::notify_event('plugin_change', $message);
12625
+ SucuriScanInterface::info($message);
12626
+ }
12627
+ }
12628
+
12629
+ // Debug ability of the plugin to send email alerts correctly.
12630
+ if (SucuriScanRequest::post(':debug_email')) {
12631
+ $recipients = SucuriScanOption::get_option(':notify_to');
12632
+ SucuriScanMail::send_mail(
12633
+ $recipients,
12634
+ 'Test Email Alert',
12635
+ sprintf('Test email alert sent at %s', date('r')),
12636
+ array('Force' => true)
12637
+ );
12638
+ SucuriScanInterface::info('Test email alert sent, check your inbox.');
12639
+ }
12640
+ }
12641
+
12642
+ $counter = 0;
12643
+
12644
+ foreach ($emails as $email) {
12645
+ if (!empty($email)) {
12646
+ $css_class = ($counter % 2 === 0) ? '' : 'alternate';
12647
+ $params['AlertSettings.Recipients'] .= SucuriScanTemplate::getSnippet(
12648
+ 'settings-alert-recipients',
12649
+ array(
12650
+ 'Recipient.CssClass' => $css_class,
12651
+ 'Recipient.Email' => $email,
12652
+ )
12653
+ );
12654
+ $counter++;
12655
+ }
12656
+ }
12657
+
12658
+ return SucuriScanTemplate::getSection('settings-alert-recipients', $params);
12659
+ }
12660
+
12661
+ function sucuriscan_settings_alert_subject($nonce)
12662
+ {
12663
+ global $sucuriscan_email_subjects;
12664
+
12665
+ $params = array(
12666
+ 'AlertSettings.Subject' => '',
12667
+ 'AlertSettings.CustomChecked' => '',
12668
+ 'AlertSettings.CustomValue' => '',
12669
+ );
12670
+
12671
+ // Process form submission to change the alert settings.
12672
+ if ($nonce) {
12673
+ if ($email_subject = SucuriScanRequest::post(':email_subject')) {
12674
+ $current_value = SucuriScanOption::get_option(':email_subject');
12675
+ $new_email_subject = false;
12676
+
12677
+ /**
12678
+ * Validate the format of the email subject format.
12679
+ *
12680
+ * If the user chooses the option to build the subject of the email alerts
12681
+ * manually we will need to validate the characters. Otherwise we will need to
12682
+ * check if the pseudo-tags selected by the user are allowed and supported.
12683
+ */
12684
+ if ($email_subject === 'custom') {
12685
+ $format_pattern = '/^[0-9a-zA-Z:,\s]+$/';
12686
+ $custom_subject = SucuriScanRequest::post(':custom_email_subject');
12687
+
12688
+ if ($custom_subject !== false
12689
+ && !empty($custom_subject)
12690
+ && @preg_match($format_pattern, $custom_subject)
12691
+ ) {
12692
+ $new_email_subject = trim($custom_subject);
12693
+ } else {
12694
+ SucuriScanInterface::error('Invalid characters in the email subject.');
12695
+ }
12696
+ } elseif (is_array($sucuriscan_email_subjects)
12697
+ && in_array($email_subject, $sucuriscan_email_subjects)
12698
+ ) {
12699
+ $new_email_subject = trim($email_subject);
12700
+ }
12701
+
12702
+ // Proceed with the operation saving the new subject.
12703
+ if ($new_email_subject !== false
12704
+ && $current_value !== $new_email_subject
12705
+ ) {
12706
+ $message = 'Email subject set to <code>' . $new_email_subject . '</code>';
12707
+
12708
+ SucuriScanOption::update_option(':email_subject', $new_email_subject);
12709
+ SucuriScanEvent::report_info_event($message);
12710
+ SucuriScanEvent::notify_event('plugin_change', $message);
12711
+ SucuriScanInterface::info($message);
12712
+ }
12713
+ }
12714
+ }
12715
+
12716
+ // Build the HTML code for the interface.
12717
+ if (is_array($sucuriscan_email_subjects)) {
12718
+ $email_subject = SucuriScanOption::get_option(':email_subject');
12719
+ $is_official_subject = false;
12720
+
12721
+ foreach ($sucuriscan_email_subjects as $subject_format) {
12722
+ if ($email_subject === $subject_format) {
12723
+ $is_official_subject = true;
12724
+ $checked = 'checked="checked"';
12725
+ } else {
12726
+ $checked = '';
12727
+ }
12728
+
12729
+ $params['AlertSettings.Subject'] .= SucuriScanTemplate::getSnippet(
12730
+ 'settings-alert-subject',
12731
+ array(
12732
+ 'EmailSubject.Name' => $subject_format,
12733
+ 'EmailSubject.Value' => $subject_format,
12734
+ 'EmailSubject.Checked' => $checked,
12735
+ )
12736
+ );
12737
+ }
12738
+
12739
+ if ($is_official_subject === false) {
12740
+ $params['AlertSettings.CustomChecked'] = 'checked="checked"';
12741
+ $params['AlertSettings.CustomValue'] = $email_subject;
12742
+ }
12743
+ }
12744
+
12745
+ return SucuriScanTemplate::getSection('settings-alert-subject', $params);
12746
+ }
12747
+
12748
+ function sucuriscan_settings_alert_perhour($nonce)
12749
+ {
12750
+ global $sucuriscan_emails_per_hour;
12751
+
12752
+ $params = array();
12753
+ $params['AlertSettings.PerHour'] = '';
12754
+
12755
+ if ($nonce) {
12756
+ // Update the value for the maximum emails per hour.
12757
+ if ($per_hour = SucuriScanRequest::post(':emails_per_hour')) {
12758
+ if (array_key_exists($per_hour, $sucuriscan_emails_per_hour)) {
12759
+ $per_hour_label = strtolower($sucuriscan_emails_per_hour[$per_hour]);
12760
+ $message = 'Maximum alerts per hour set to <code>' . $per_hour_label . '</code>';
12761
+
12762
+ SucuriScanOption::update_option(':emails_per_hour', $per_hour);
12763
+ SucuriScanEvent::report_info_event($message);
12764
+ SucuriScanEvent::notify_event('plugin_change', $message);
12765
+ SucuriScanInterface::info($message);
12766
+ } else {
12767
+ SucuriScanInterface::error('Invalid value for the maximum emails per hour.');
12768
+ }
12769
+ }
12770
+ }
12771
+
12772
+ $per_hour = SucuriScanOption::get_option(':emails_per_hour');
12773
+ $per_hour_options = SucuriScanTemplate::selectOptions($sucuriscan_emails_per_hour, $per_hour);
12774
+ $params['AlertSettings.PerHour'] = $per_hour_options;
12775
+
12776
+ return SucuriScanTemplate::getSection('settings-alert-perhour', $params);
12777
+ }
12778
+
12779
+ function sucuriscan_settings_alert_bruteforce($nonce)
12780
+ {
12781
+ global $sucuriscan_maximum_failed_logins;
12782
+
12783
+ $params = array();
12784
+ $params['AlertSettings.BruteForce'] = '';
12785
+
12786
+ if ($nonce) {
12787
+ // Update the maximum failed logins per hour before consider it a brute-force attack.
12788
+ if ($maximum = SucuriScanRequest::post(':maximum_failed_logins')) {
12789
+ if (array_key_exists($maximum, $sucuriscan_maximum_failed_logins)) {
12790
+ $message = 'Consider brute-force attack after <code>' . $maximum . '</code> failed logins per hour';
12791
+
12792
+ SucuriScanOption::update_option(':maximum_failed_logins', $maximum);
12793
+ SucuriScanEvent::report_info_event($message);
12794
+ SucuriScanEvent::notify_event('plugin_change', $message);
12795
+ SucuriScanInterface::info($message);
12796
+ } else {
12797
+ SucuriScanInterface::error('Invalid value for the brute-force alerts.');
12798
+ }
12799
+ }
12800
+ }
12801
+
12802
+ $maximum = SucuriScanOption::get_option(':maximum_failed_logins');
12803
+ $maximum_options = SucuriScanTemplate::selectOptions($sucuriscan_maximum_failed_logins, $maximum);
12804
+ $params['AlertSettings.BruteForce'] = $maximum_options;
12805
+
12806
+ return SucuriScanTemplate::getSection('settings-alert-bruteforce', $params);
12807
+ }
12808
+
12809
+ function sucuriscan_settings_alert_events($nonce)
12810
+ {
12811
+ global $sucuriscan_notify_options;
12812
+
12813
+ $params = array();
12814
+ $params['AlertSettings.Events'] = '';
12815
+
12816
+ // Process form submission to change the alert settings.
12817
+ if ($nonce) {
12818
+ // Update the notification settings.
12819
+ if (SucuriScanRequest::post(':save_alert_events') !== false) {
12820
+ $ucounter = 0;
12821
+
12822
+ if (SucuriScanRequest::post(':notify_scan_checksums') == 1) {
12823
+ $_POST['sucuriscan_prettify_mails'] = '1';
12824
+ }
12825
+
12826
+ foreach ($sucuriscan_notify_options as $alert_type => $alert_label) {
12827
+ $option_value = SucuriScanRequest::post($alert_type, '(1|0)');
12828
+
12829
+ if ($option_value !== false) {
12830
+ $current_value = SucuriScanOption::get_option($alert_type);
12831
+ $option_value = ($option_value == 1) ? 'enabled' : 'disabled';
12832
+
12833
+ // Check that the option value was actually changed.
12834
+ if ($current_value !== $option_value) {
12835
+ SucuriScanOption::update_option($alert_type, $option_value);
12836
+ $ucounter += 1;
12837
+ }
12838
+ }
12839
+ }
12840
+
12841
+ if ($ucounter > 0) {
12842
+ $message = 'A total of ' . $ucounter . ' alert events were changed';
12843
+
12844
+ SucuriScanEvent::report_info_event($message);
12845
+ SucuriScanEvent::notify_event('plugin_change', $message);
12846
+ SucuriScanInterface::info($message);
12847
+ }
12848
+ }
12849
+ }
12850
+
12851
+ // Build the HTML code for the interface.
12852
+ if (is_array($sucuriscan_notify_options)) {
12853
+ $pattern = '/^([a-z]+:)?(.+)/';
12854
+ $counter = 0;
12855
+
12856
+ foreach ($sucuriscan_notify_options as $alert_type => $alert_label) {
12857
+ $alert_value = SucuriScanOption::get_option($alert_type);
12858
+ $checked = ($alert_value == 'enabled') ? 'checked="checked"' : '';
12859
+ $css_class = ($counter % 2 === 0) ? 'alternate' : '';
12860
+ $alert_icon = '';
12861
+
12862
+ if (@preg_match($pattern, $alert_label, $match)) {
12863
+ $alert_group = str_replace(':', '', $match[1]);
12864
+ $alert_label = $match[2];
12865
+
12866
+ switch ($alert_group) {
12867
+ case 'user':
12868
+ $alert_icon = 'dashicons-before dashicons-admin-users';
12869
+ break;
12870
+ case 'plugin':
12871
+ $alert_icon = 'dashicons-before dashicons-admin-plugins';
12872
+ break;
12873
+ case 'theme':
12874
+ $alert_icon = 'dashicons-before dashicons-admin-appearance';
12875
+ break;
12876
+ }
12877
+ }
12878
+
12879
+ $params['AlertSettings.Events'] .= SucuriScanTemplate::getSnippet(
12880
+ 'settings-alert-events',
12881
+ array(
12882
+ 'Event.CssClass' => $css_class,
12883
+ 'Event.Name' => $alert_type,
12884
+ 'Event.Checked' => $checked,
12885
+ 'Event.Label' => $alert_label,
12886
+ 'Event.LabelIcon' => $alert_icon,
12887
+ )
12888
+ );
12889
+ $counter++;
12890
+ }
12891
+ }
12892
+
12893
+ return SucuriScanTemplate::getSection('settings-alert-events', $params);
12894
+ }
12895
+
12896
+ function sucuriscan_settings_ignore_rules()
12897
+ {
12898
+ $notify_new_site_content = SucuriScanOption::get_option(':notify_post_publication');
12899
+
12900
+ $template_variables = array(
12901
+ 'IgnoreRules.MessageVisibility' => 'visible',
12902
+ 'IgnoreRules.TableVisibility' => 'hidden',
12903
+ 'IgnoreRules.PostTypes' => '',
12904
+ );
12905
+
12906
+ if ($notify_new_site_content == 'enabled') {
12907
+ $post_types = get_post_types();
12908
+ $ignored_events = SucuriScanOption::get_ignored_events();
12909
+
12910
+ $template_variables['IgnoreRules.MessageVisibility'] = 'hidden';
12911
+ $template_variables['IgnoreRules.TableVisibility'] = 'visible';
12912
+ $counter = 0;
12913
+
12914
+ foreach ($post_types as $post_type => $post_type_object) {
12915
+ $counter++;
12916
+ $css_class = ($counter % 2 === 0) ? 'alternate' : '';
12917
+ $post_type_title = ucwords(str_replace('_', chr(32), $post_type));
12918
+
12919
+ if (array_key_exists($post_type, $ignored_events)) {
12920
+ $is_ignored_text = 'YES';
12921
+ $was_ignored_at = SucuriScan::datetime($ignored_events[ $post_type ]);
12922
+ $is_ignored_class = 'danger';
12923
+ $button_action = 'remove';
12924
+ $button_class = 'button-primary';
12925
+ $button_text = 'Allow';
12926
+ } else {
12927
+ $is_ignored_text = 'NO';
12928
+ $was_ignored_at = 'Not ignored';
12929
+ $is_ignored_class = 'success';
12930
+ $button_action = 'add';
12931
+ $button_class = 'button-primary button-danger';
12932
+ $button_text = 'Ignore';
12933
+ }
12934
+
12935
+ $template_variables['IgnoreRules.PostTypes'] .= SucuriScanTemplate::getSnippet(
12936
+ 'settings-ignorerules',
12937
+ array(
12938
+ 'IgnoreRules.CssClass' => $css_class,
12939
+ 'IgnoreRules.Num' => $counter,
12940
+ 'IgnoreRules.PostTypeTitle' => $post_type_title,
12941
+ 'IgnoreRules.IsIgnored' => $is_ignored_text,
12942
+ 'IgnoreRules.WasIgnoredAt' => $was_ignored_at,
12943
+ 'IgnoreRules.IsIgnoredClass' => $is_ignored_class,
12944
+ 'IgnoreRules.PostType' => $post_type,
12945
+ 'IgnoreRules.Action' => $button_action,
12946
+ 'IgnoreRules.ButtonClass' => 'button ' . $button_class,
12947
+ 'IgnoreRules.ButtonText' => $button_text,
12948
+ )
12949
+ );
12950
+ }
12951
+ }
12952
+
12953
+ return SucuriScanTemplate::getSection('settings-ignorerules', $template_variables);
12954
+ }
12955
+
12956
+ /**
12957
+ * Read and parse the content of the API service settings template.
12958
+ *
12959
+ * @return string Parsed HTML code for the API service settings panel.
12960
+ */
12961
+ function sucuriscan_settings_apiservice($nonce)
12962
+ {
12963
+ $params = array();
12964
+
12965
+ $params['SettingsSection.ApiStatus'] = sucuriscan_settings_apiservice_status($nonce);
12966
+ $params['SettingsSection.ApiProxy'] = sucuriscan_settings_apiservice_proxy($nonce);
12967
+ $params['SettingsSection.ApiSSL'] = sucuriscan_settings_apiservice_ssl($nonce);
12968
+ $params['SettingsSection.ApiTimeout'] = sucuriscan_settings_apiservice_timeout($nonce);
12969
+
12970
+ return SucuriScanTemplate::getSection('settings-apiservice', $params);
12971
+ }
12972
+
12973
+ function sucuriscan_settings_apiservice_status($nonce)
12974
+ {
12975
+ $params = array();
12976
+ $params['ApiStatus.StatusNum'] = '1';
12977
+ $params['ApiStatus.Status'] = 'Enabled';
12978
+ $params['ApiStatus.SwitchText'] = 'Disable';
12979
+ $params['ApiStatus.SwitchValue'] = 'disable';
12980
+ $params['ApiStatus.SwitchCssClass'] = 'button-danger';
12981
+ $params['ApiStatus.WarningVisibility'] = 'visible';
12982
+ $params['ApiStatus.ErrorVisibility'] = 'hidden';
12983
+
12984
+ if ($nonce) {
12985
+ // Enable or disable the API service communication.
12986
+ if ($api_service = SucuriScanRequest::post(':api_service', '(en|dis)able')) {
12987
+ $action_d = $api_service . 'd';
12988
+ $message = 'API service communication was <code>' . $action_d . '</code>';
12989
+
12990
+ SucuriScanEvent::report_info_event($message);
12991
+ SucuriScanEvent::notify_event('plugin_change', $message);
12992
+ SucuriScanOption::update_option(':api_service', $action_d);
12993
+ SucuriScanInterface::info($message);
12994
+ }
12995
+ }
12996
+
12997
+ $api_service = SucuriScanOption::get_option(':api_service');
12998
+
12999
+ if ($api_service === 'disabled') {
13000
+ $params['ApiStatus.StatusNum'] = '0';
13001
+ $params['ApiStatus.Status'] = 'Disabled';
13002
+ $params['ApiStatus.SwitchText'] = 'Enable';
13003
+ $params['ApiStatus.SwitchValue'] = 'enable';
13004
+ $params['ApiStatus.SwitchCssClass'] = 'button-success';
13005
+ $params['ApiStatus.WarningVisibility'] = 'hidden';
13006
+ $params['ApiStatus.ErrorVisibility'] = 'visible';
13007
+ }
13008
+
13009
+ return SucuriScanTemplate::getSection('settings-apiservice-status', $params);
13010
+ }
13011
+
13012
+ function sucuriscan_settings_apiservice_proxy($nonce)
13013
+ {
13014
+ $params = array(
13015
+ 'APIProxy.Host' => 'no_proxy_host',
13016
+ 'APIProxy.Port' => 'no_proxy_port',
13017
+ 'APIProxy.Username' => 'no_proxy_username',
13018
+ 'APIProxy.Password' => 'no_proxy_password',
13019
+ 'APIProxy.PasswordType' => 'default',
13020
+ 'APIProxy.PasswordText' => 'empty',
13021
+ );
13022
+
13023
+ if (class_exists('WP_HTTP_Proxy')) {
13024
+ $wp_http_proxy = new WP_HTTP_Proxy();
13025
+
13026
+ if ($wp_http_proxy->is_enabled()) {
13027
+ $proxy_host = SucuriScan::escape($wp_http_proxy->host());
13028
+ $proxy_port = SucuriScan::escape($wp_http_proxy->port());
13029
+ $proxy_username = SucuriScan::escape($wp_http_proxy->username());
13030
+ $proxy_password = SucuriScan::escape($wp_http_proxy->password());
13031
 
13032
+ $params['APIProxy.Host'] = $proxy_host;
13033
+ $params['APIProxy.Port'] = $proxy_port;
13034
+ $params['APIProxy.Username'] = $proxy_username;
13035
+ $params['APIProxy.Password'] = $proxy_password;
13036
+ $params['APIProxy.PasswordType'] = 'info';
13037
+ $params['APIProxy.PasswordText'] = 'hidden';
13038
 
13039
+ }
 
13040
  }
13041
 
13042
+ return SucuriScanTemplate::getSection('settings-apiservice-proxy', $params);
 
 
 
 
 
 
13043
  }
13044
 
13045
+ function sucuriscan_settings_apiservice_ssl($nonce)
13046
+ {
13047
+ global $sucuriscan_verify_ssl_cert;
 
 
 
 
 
13048
 
13049
+ $params = array(
13050
+ 'VerifySSLCert' => 'Undefined',
13051
+ 'VerifySSLCertCssClass' => 0,
13052
+ 'VerifySSLCertOptions' => '',
 
 
13053
  );
13054
 
13055
+ // Update the configuration for the SSL certificate verification.
13056
+ if ($nonce) {
13057
+ $verify_ssl_cert = SucuriScanRequest::post(':verify_ssl_cert');
13058
 
13059
+ if ($verify_ssl_cert) {
13060
+ if (array_key_exists($verify_ssl_cert, $sucuriscan_verify_ssl_cert)) {
13061
+ $message = 'SSL certificate verification for API calls set to <code>' . $verify_ssl_cert . '</code>';
13062
+
13063
+ SucuriScanOption::update_option(':verify_ssl_cert', $verify_ssl_cert);
13064
+ SucuriScanEvent::report_warning_event($message);
13065
+ SucuriScanEvent::notify_event('plugin_change', $message);
13066
+ SucuriScanInterface::info($message);
13067
  } else {
13068
+ SucuriScanInterface::error('Invalid value for the SSL certificate verification.');
13069
  }
 
 
 
 
 
 
13070
  }
13071
+ }
13072
+
13073
+ $verify_ssl_cert = SucuriScanOption::get_option(':verify_ssl_cert');
13074
+ $params['VerifySSLCertOptions'] = SucuriScanTemplate::selectOptions(
13075
+ $sucuriscan_verify_ssl_cert,
13076
+ $verify_ssl_cert
13077
+ );
13078
 
13079
+ if (array_key_exists($verify_ssl_cert, $sucuriscan_verify_ssl_cert)) {
13080
+ $params['VerifySSLCert'] = $sucuriscan_verify_ssl_cert[$verify_ssl_cert];
13081
+
13082
+ if ($verify_ssl_cert === 'true') {
13083
+ $params['VerifySSLCertCssClass'] = 1;
13084
  }
13085
  }
13086
 
13087
+ return SucuriScanTemplate::getSection('settings-apiservice-ssl', $params);
13088
+ }
13089
+
13090
+ function sucuriscan_settings_apiservice_timeout($nonce)
13091
+ {
13092
+ $params = array();
13093
 
13094
+ // Update the API request timeout.
13095
+ if ($nonce) {
13096
+ $timeout = (int) SucuriScanRequest::post(':request_timeout', '[0-9]+');
 
 
13097
 
13098
+ if ($timeout > 0) {
13099
+ if ($timeout <= SUCURISCAN_MAX_REQUEST_TIMEOUT) {
13100
+ $message = 'API request timeout set to <code>' . $timeout . '</code> seconds.';
13101
 
13102
+ SucuriScanOption::update_option(':request_timeout', $timeout);
13103
+ SucuriScanEvent::report_info_event($message);
13104
+ SucuriScanEvent::notify_event('plugin_change', $message);
13105
+ SucuriScanInterface::info($message);
13106
+ } else {
13107
+ SucuriScanInterface::error('API request timeout in seconds is too high.');
13108
  }
13109
  }
 
 
 
 
 
 
 
 
 
13110
  }
13111
 
13112
+ $params['MaxRequestTimeout'] = SUCURISCAN_MAX_REQUEST_TIMEOUT;
13113
+ $params['RequestTimeout'] = SucuriScanOption::get_option(':request_timeout') . ' seconds';
13114
+
13115
+ return SucuriScanTemplate::getSection('settings-apiservice-timeout', $params);
13116
  }
13117
 
13118
  /**
13119
+ * Read and parse the content of the self-hosting settings template.
13120
  *
13121
+ * @return string Parsed HTML code for the self-hosting settings panel.
13122
  */
13123
+ function sucuriscan_settings_selfhosting($nonce)
13124
+ {
13125
+ $params = array();
 
 
 
 
 
 
 
 
 
13126
 
13127
+ $params['SelfHosting.Monitor'] = sucuriscan_settings_selfhosting_monitor($nonce);
 
 
13128
 
13129
+ return SucuriScanTemplate::getSection('settings-selfhosting', $params);
13130
+ }
 
 
13131
 
13132
+ function sucuriscan_selfhosting_fpath()
13133
+ {
13134
+ $monitor = SucuriScanOption::get_option(':selfhosting_monitor');
13135
+ $monitor_fpath = SucuriScanOption::get_option(':selfhosting_fpath');
 
 
 
 
 
 
 
 
 
 
 
13136
 
13137
+ if ($monitor === 'enabled'
13138
+ && !empty($monitor_fpath)
13139
+ && file_exists($monitor_fpath)
13140
+ && is_writable($monitor_fpath)
13141
+ ) {
13142
+ return $monitor_fpath;
 
 
 
 
 
 
 
13143
  }
13144
 
13145
+ return false;
13146
  }
13147
 
13148
+ function sucuriscan_settings_selfhosting_monitor($nonce)
13149
+ {
13150
+ $params = array();
 
 
 
 
 
 
 
13151
 
13152
+ $params['SelfHostingMonitor.DisabledVisibility'] = 'visible';
13153
+ $params['SelfHostingMonitor.Status'] = 'Enabled';
13154
+ $params['SelfHostingMonitor.SwitchText'] = 'Disable';
13155
+ $params['SelfHostingMonitor.SwitchValue'] = 'disable';
13156
+ $params['SelfHostingMonitor.SwitchCssClass'] = 'button-danger';
13157
+ $params['SelfHostingMonitor.FpathVisibility'] = 'hidden';
13158
+ $params['SelfHostingMonitor.Fpath'] = '';
13159
 
13160
+ if ($nonce) {
13161
+ // Set a file path for the self-hosted event monitor.
13162
+ $monitor_fpath = SucuriScanRequest::post(':selfhosting_fpath');
13163
 
13164
+ if ($monitor_fpath !== false) {
13165
+ if (empty($monitor_fpath)) {
13166
+ $message = 'Log exporter was disabled.';
13167
 
13168
+ SucuriScanEvent::report_info_event($message);
13169
+ SucuriScanOption::delete_option(':selfhosting_fpath');
13170
+ SucuriScanOption::update_option(':selfhosting_monitor', 'disabled');
13171
+ SucuriScanEvent::notify_event('plugin_change', $message);
13172
+ SucuriScanInterface::info($message);
13173
+ } elseif (strpos($monitor_fpath, $_SERVER['DOCUMENT_ROOT']) !== false) {
13174
+ SucuriScanInterface::error('File should not be publicly accessible.');
13175
+ } elseif (!file_exists($monitor_fpath)) {
13176
+ SucuriScanInterface::error('File path does not exists.');
13177
+ } elseif (!is_writable($monitor_fpath)) {
13178
+ SucuriScanInterface::error('File path is not writable.');
13179
+ } else {
13180
+ $message = 'Log exporter file path was set correctly.';
13181
 
13182
+ SucuriScanEvent::report_info_event($message);
13183
+ SucuriScanOption::update_option(':selfhosting_monitor', 'enabled');
13184
+ SucuriScanOption::update_option(':selfhosting_fpath', $monitor_fpath);
13185
+ SucuriScanEvent::notify_event('plugin_change', $message);
13186
+ SucuriScanInterface::info($message);
13187
+ }
 
 
13188
  }
13189
+ }
13190
 
13191
+ $monitor = SucuriScanOption::get_option(':selfhosting_monitor');
13192
+ $monitor_fpath = SucuriScanOption::get_option(':selfhosting_fpath');
13193
+
13194
+ if ($monitor === 'disabled') {
13195
+ $params['SelfHostingMonitor.Status'] = 'Disabled';
13196
+ $params['SelfHostingMonitor.SwitchText'] = 'Enable';
13197
+ $params['SelfHostingMonitor.SwitchValue'] = 'enable';
13198
+ $params['SelfHostingMonitor.SwitchCssClass'] = 'button-success';
13199
+ }
13200
+
13201
+ if ($monitor === 'enabled' && $monitor_fpath) {
13202
+ $params['SelfHostingMonitor.DisabledVisibility'] = 'hidden';
13203
+ $params['SelfHostingMonitor.FpathVisibility'] = 'visible';
13204
+ $params['SelfHostingMonitor.Fpath'] = SucuriScan::escape($monitor_fpath);
13205
  }
13206
 
13207
+ return SucuriScanTemplate::getSection('settings-selfhosting-monitor', $params);
13208
  }
13209
 
13210
  /**
13211
+ * Read and parse the content of the trust-ip settings template.
13212
  *
13213
+ * @return string Parsed HTML code for the trust-ip settings panel.
13214
  */
13215
+ function sucuriscan_settings_trust_ip()
13216
+ {
13217
+ $params = array();
13218
+ $params['TrustedIPs.List'] = '';
13219
+ $params['TrustedIPs.NoItems.Visibility'] = 'visible';
 
 
 
13220
 
13221
+ $cache = new SucuriScanCache('trustip');
13222
+ $trusted_ips = $cache->getAll();
 
 
13223
 
13224
+ if ($trusted_ips) {
 
13225
  $counter = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13226
 
13227
+ foreach ($trusted_ips as $cache_key => $ip_info) {
13228
+ $css_class = ($counter % 2 === 0) ? '' : 'alternate';
 
 
 
 
 
 
 
 
 
 
13229
 
13230
+ if ($ip_info->cidr_range == 32) {
13231
+ $ip_info->cidr_format = 'n/a';
 
 
 
 
13232
  }
13233
+
13234
+ $params['TrustedIPs.List'] .= SucuriScanTemplate::getSnippet(
13235
+ 'settings-trustip',
13236
+ array(
13237
+ 'TrustIP.CssClass' => $css_class,
13238
+ 'TrustIP.CacheKey' => $cache_key,
13239
+ 'TrustIP.RemoteAddr' => SucuriScan::escape($ip_info->remote_addr),
13240
+ 'TrustIP.CIDRFormat' => SucuriScan::escape($ip_info->cidr_format),
13241
+ 'TrustIP.AddedAt' => SucuriScan::datetime($ip_info->added_at),
13242
+ )
13243
+ );
13244
+
13245
+ $counter++;
13246
  }
13247
 
13248
+ if ($counter > 0) {
13249
+ $params['TrustedIPs.NoItems.Visibility'] = 'hidden';
13250
  }
13251
  }
13252
 
13253
+ return SucuriScanTemplate::getSection('settings-trustip', $params);
13254
  }
13255
 
13256
+
13257
  /**
13258
  * Read and parse the content of the heartbeat settings template.
13259
  *
13260
  * @return string Parsed HTML code for the heartbeat settings panel.
13261
  */
13262
+ function sucuriscan_settings_heartbeat()
13263
+ {
13264
  // Current values set in the options table.
13265
+ $heartbeat_status = SucuriScanOption::get_option(':heartbeat');
13266
+ $heartbeat_pulse = SucuriScanOption::get_option(':heartbeat_pulse');
13267
+ $heartbeat_interval = SucuriScanOption::get_option(':heartbeat_interval');
13268
+ $heartbeat_autostart = SucuriScanOption::get_option(':heartbeat_autostart');
13269
 
13270
  // Allowed values for each setting.
13271
  $statuses_allowed = SucuriScanHeartbeat::statuses_allowed();
13273
  $intervals_allowed = SucuriScanHeartbeat::intervals_allowed();
13274
 
13275
  // HTML select form fields.
13276
+ $heartbeat_options = SucuriScanTemplate::selectOptions($statuses_allowed, $heartbeat_status);
13277
+ $heartbeat_pulse_options = SucuriScanTemplate::selectOptions($pulses_allowed, $heartbeat_pulse);
13278
+ $heartbeat_interval_options = SucuriScanTemplate::selectOptions($intervals_allowed, $heartbeat_interval);
13279
 
13280
+ $params = array(
13281
  'HeartbeatStatus' => 'Undefined',
13282
  'HeartbeatPulse' => 'Undefined',
13283
  'HeartbeatInterval' => 'Undefined',
13292
  'HeartbeatAutostartSwitchCssClass' => 'button-danger',
13293
  );
13294
 
13295
+ if (array_key_exists($heartbeat_status, $statuses_allowed)) {
13296
+ $params['HeartbeatStatus'] = $statuses_allowed[ $heartbeat_status ];
13297
  }
13298
 
13299
+ if (array_key_exists($heartbeat_pulse, $pulses_allowed)) {
13300
+ $params['HeartbeatPulse'] = $pulses_allowed[ $heartbeat_pulse ];
13301
  }
13302
 
13303
+ if (array_key_exists($heartbeat_interval, $intervals_allowed)) {
13304
+ $params['HeartbeatInterval'] = $intervals_allowed[ $heartbeat_interval ];
13305
  }
13306
 
13307
+ if ($heartbeat_autostart == 'disabled') {
13308
+ $params['HeartbeatAutostart'] = 'Disabled';
13309
+ $params['HeartbeatAutostartSwitchText'] = 'Enable';
13310
+ $params['HeartbeatAutostartSwitchValue'] = 'enable';
13311
+ $params['HeartbeatAutostartSwitchCssClass'] = 'button-success';
13312
  }
13313
 
13314
+ return SucuriScanTemplate::getSection('settings-heartbeat', $params);
13315
+ }
13316
+
13317
+ /**
13318
+ * Print a HTML code with the settings of the plugin.
13319
+ *
13320
+ * @return void
13321
+ */
13322
+ function sucuriscan_settings_page()
13323
+ {
13324
+ SucuriScanInterface::check_permissions();
13325
+
13326
+ $params = array();
13327
+ $nonce = SucuriScanInterface::check_nonce();
13328
+
13329
+ $params['PageTitle'] = 'Settings';
13330
+ $params['Settings.General'] = sucuriscan_settings_general($nonce);
13331
+ $params['Settings.Scanner'] = sucuriscan_settings_scanner();
13332
+ $params['Settings.Alerts'] = sucuriscan_settings_alert($nonce);
13333
+ $params['Settings.ApiService'] = sucuriscan_settings_apiservice($nonce);
13334
+ $params['Settings.SelfHosting'] = sucuriscan_settings_selfhosting($nonce);
13335
+ $params['Settings.IgnoreScanning'] = sucuriscan_settings_ignorescanning();
13336
+ $params['Settings.IgnoreRules'] = sucuriscan_settings_ignore_rules();
13337
+ $params['Settings.TrustIP'] = sucuriscan_settings_trust_ip();
13338
+ $params['Settings.Heartbeat'] = sucuriscan_settings_heartbeat();
13339
+
13340
+ echo SucuriScanTemplate::getTemplate('settings', $params);
13341
  }
13342
 
13343
  /**
13349
  *
13350
  * @return void
13351
  */
13352
+ function sucuriscan_infosys_page()
13353
+ {
13354
  SucuriScanInterface::check_permissions();
13355
 
13356
  // Process all form submissions.
13357
  sucuriscan_infosys_form_submissions();
13358
 
13359
  // Page pseudo-variables initialization.
13360
+ $params = array(
13361
  'PageTitle' => 'Site Info',
13362
  'ServerInfo' => sucuriscan_server_info(),
13363
  'Cronjobs' => sucuriscan_show_cronjobs(),
13366
  'ErrorLogs' => sucuriscan_infosys_errorlogs(),
13367
  );
13368
 
13369
+ echo SucuriScanTemplate::getTemplate('infosys', $params);
13370
  }
13371
 
13372
  /**
13375
  *
13376
  * @return string The HTML code displaying the information about the HTAccess rules.
13377
  */
13378
+ function sucuriscan_infosys_htaccess()
13379
+ {
13380
  $htaccess_path = SucuriScan::get_htaccess_path();
13381
+ $params = array(
 
13382
  'HTAccess.Content' => '',
 
 
 
13383
  'HTAccess.TextareaVisible' => 'hidden',
13384
+ 'HTAccess.StandardVisible' => 'hidden',
13385
+ 'HTAccess.NotFoundVisible' => 'hidden',
13386
+ 'HTAccess.FoundVisible' => 'hidden',
13387
+ 'HTAccess.Fpath' => 'unknown',
13388
  );
13389
 
13390
+ if ($htaccess_path) {
13391
+ $htaccess_rules = @file_get_contents($htaccess_path);
13392
 
13393
+ $params['HTAccess.TextareaVisible'] = 'visible';
13394
+ $params['HTAccess.Content'] = $htaccess_rules;
13395
+ $params['HTAccess.Fpath'] = $htaccess_path;
13396
+ $params['HTAccess.FoundVisible'] = 'visible';
 
13397
 
13398
+ if (sucuriscan_htaccess_is_standard($htaccess_rules)) {
13399
+ $params['HTAccess.StandardVisible'] = 'visible';
 
 
 
 
 
 
 
13400
  }
13401
  } else {
13402
+ $params['HTAccess.NotFoundVisible'] = 'visible';
 
 
13403
  }
13404
 
13405
+ return SucuriScanTemplate::getSection('infosys-htaccess', $params);
13406
  }
13407
 
13408
  /**
13409
+ * Check if the standard rules for a normal WordPress installation (not network
13410
+ * based) are inside the main htaccess file. This only applies to websites that
13411
+ * have permalinks enabled, or 3rd-party plugins that require custom rules
13412
+ * (generally based on mod_deflate) to compress and/or generate static files for
13413
+ * cache.
13414
  *
13415
+ * @param string $rules Content of the main htaccess file.
13416
+ * @return boolean True if the htaccess has the standard rules, false otherwise.
13417
  */
13418
+ function sucuriscan_htaccess_is_standard($rules = false)
13419
+ {
13420
+ if ($rules === false) {
13421
+ $rules = '';
13422
  $htaccess_path = SucuriScan::get_htaccess_path();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13423
 
13424
+ if ($htaccess_path) {
13425
+ $rules = @file_get_contents($htaccess_path);
13426
  }
13427
  }
13428
 
13429
+ if (is_string($rules) && !empty($rules)) {
13430
+ $rewrite = new WP_Rewrite();
13431
+ $standard = $rewrite->mod_rewrite_rules();
13432
+
13433
+ return (bool) (strpos($rules, $standard) !== false);
13434
+ }
13435
+
13436
  return false;
13437
  }
13438
 
13443
  *
13444
  * @return string The HTML code displaying the constants and variables found in the wp-config file.
13445
  */
13446
+ function sucuriscan_infosys_wpconfig()
13447
+ {
13448
+ $params = array(
13449
  'WordpressConfig.Rules' => '',
13450
  'WordpressConfig.Total' => 0,
13451
  );
13452
 
13453
+ $ignore_wp_rules = array('DB_PASSWORD');
13454
  $wp_config_path = SucuriScan::get_wpconfig_path();
13455
 
13456
+ if ($wp_config_path) {
13457
  $wp_config_rules = array();
13458
+ $wp_config_content = SucuriScanFileInfo::file_lines($wp_config_path);
13459
 
13460
  // Parse the main configuration file and look for constants and global variables.
13461
+ foreach ((array) $wp_config_content as $line) {
13462
+ if (@preg_match('/^\s?(#|\/\/)/', $line)) {
13463
+ continue; /* Ignore commented lines. */
13464
+ } elseif (@preg_match('/define\(/', $line)) {
 
13465
  // Detect PHP constants even if the line if indented.
13466
+ $line = preg_replace('/.*define\((.+)\);.*/', '$1', $line);
13467
+ $line_parts = explode(',', $line, 2);
13468
+ } elseif (@preg_match('/^\$[a-zA-Z_]+/', $line)) {
13469
  // Detect global variables like the database table prefix.
13470
+ $line = @preg_replace('/;\s\/\/.*/', ';', $line);
13471
+ $line_parts = explode('=', $line, 2);
13472
  } else {
13473
+ continue; /* Ignore other lines. */
 
13474
  }
13475
 
13476
  // Clean and append the rule to the wp_config_rules variable.
13477
+ if (isset($line_parts) && count($line_parts) === 2) {
13478
  $key_name = '';
13479
  $key_value = '';
13480
 
13481
  // TODO: A foreach loop is not really necessary, find a better way.
13482
+ foreach ($line_parts as $i => $line_part) {
13483
+ $line_part = trim($line_part);
13484
+ $line_part = ltrim($line_part, '$');
13485
+ $line_part = rtrim($line_part, ';');
13486
 
13487
  // Remove single/double quotes at the beginning and end of the string.
13488
+ $line_part = ltrim($line_part, "'");
13489
+ $line_part = rtrim($line_part, "'");
13490
+ $line_part = ltrim($line_part, '"');
13491
+ $line_part = rtrim($line_part, '"');
13492
 
13493
  // Assign the clean strings to specific variables.
13494
+ if ($i == 0) {
13495
  $key_name = $line_part;
13496
  }
13497
 
13498
+ if ($i == 1) {
13499
+ if (defined($key_name)) {
13500
+ $key_value = constant($key_name);
13501
 
13502
+ if (is_bool($key_value)) {
13503
+ $key_value = ($key_value === true) ? 'True' : 'False';
13504
  }
13505
  } else {
13506
  $key_value = $line_part;
13509
  }
13510
 
13511
  // Remove the value of sensitive variables like the database password.
13512
+ if (in_array($key_name, $ignore_wp_rules)) {
13513
  $key_value = 'hidden';
13514
  }
13515
 
13516
  // Append the value to the configuration rules.
13517
+ $wp_config_rules[$key_name] = $key_value;
13518
  }
13519
  }
13520
 
13521
  // Pass the WordPress configuration rules to the template and show them.
13522
  $counter = 0;
13523
+ foreach ($wp_config_rules as $var_name => $var_value) {
13524
+ $css_class = ($counter % 2 === 0) ? '' : 'alternate';
13525
  $label_css = 'sucuriscan-monospace';
13526
 
13527
+ if (empty($var_value)) {
13528
  $var_value = 'empty';
13529
  $label_css = 'sucuriscan-label-default';
13530
+ } elseif ($var_value == 'hidden') {
13531
  $label_css = 'sucuriscan-label-info';
13532
  }
13533
 
13534
+ $params['WordpressConfig.Total'] += 1;
13535
+ $params['WordpressConfig.Rules'] .= SucuriScanTemplate::getSnippet(
13536
+ 'infosys-wpconfig',
13537
+ array(
13538
+ 'WordpressConfig.VariableName' => $var_name,
13539
+ 'WordpressConfig.VariableValue' => $var_value,
13540
+ 'WordpressConfig.VariableCssClass' => $label_css,
13541
+ 'WordpressConfig.CssClass' => $css_class,
13542
+ )
13543
+ );
13544
+ $counter++;
13545
  }
13546
  }
13547
 
13548
+ return SucuriScanTemplate::getSection('infosys-wpconfig', $params);
13549
  }
13550
 
13551
  /**
13553
  *
13554
  * @return array A list of pseudo-variables and values that will replace them in the HTML template.
13555
  */
13556
+ function sucuriscan_show_cronjobs()
13557
+ {
13558
+ $params = array(
13559
  'Cronjobs.List' => '',
13560
  'Cronjobs.Total' => 0,
13561
  );
13564
  $schedules = wp_get_schedules();
13565
  $counter = 0;
13566
 
13567
+ foreach ($cronjobs as $timestamp => $cronhooks) {
13568
+ foreach ((array) $cronhooks as $hook => $events) {
13569
+ foreach ((array) $events as $key => $event) {
13570
+ if (empty($event['args'])) {
13571
+ $event['args'] = array('[]');
13572
  }
13573
 
13574
+ $params['Cronjobs.Total'] += 1;
13575
+ $params['Cronjobs.List'] .= SucuriScanTemplate::getSnippet(
13576
+ 'infosys-cronjobs',
13577
+ array(
13578
+ 'Cronjob.Hook' => $hook,
13579
+ 'Cronjob.Schedule' => $event['schedule'],
13580
+ 'Cronjob.NextTime' => SucuriScan::datetime($timestamp),
13581
+ 'Cronjob.Arguments' => SucuriScan::implode(', ', $event['args']),
13582
+ 'Cronjob.CssClass' => ($counter % 2 === 0) ? '' : 'alternate',
13583
+ )
13584
+ );
13585
+ $counter++;
13586
  }
13587
  }
13588
  }
13589
 
13590
+ return SucuriScanTemplate::getSection('infosys-cronjobs', $params);
13591
  }
13592
 
13593
  /**
13598
  * @param boolean $page_nonce True if the nonce is valid, False otherwise.
13599
  * @return void
13600
  */
13601
+ function sucuriscan_infosys_form_submissions()
13602
+ {
13603
+ if (SucuriScanInterface::check_nonce()) {
13604
  // Modify the scheduled tasks (run now, remove, re-schedule).
13605
  $allowed_actions = '(runnow|hourly|twicedaily|daily|remove)';
13606
 
13607
+ if ($cronjob_action = SucuriScanRequest::post(':cronjob_action', $allowed_actions)) {
13608
+ $cronjobs = SucuriScanRequest::post(':cronjobs', '_array');
13609
 
13610
+ if (!empty($cronjobs)) {
13611
+ $total_tasks = count($cronjobs);
13612
 
13613
  // Force execution of the selected scheduled tasks.
13614
+ if ($cronjob_action == 'runnow') {
13615
+ SucuriScanInterface::info($total_tasks . ' tasks were scheduled to run in the next ten seconds.');
13616
+ SucuriScanEvent::report_notice_event(sprintf(
13617
  'Force execution of scheduled tasks: (multiple entries): %s',
13618
+ @implode(',', $cronjobs)
13619
+ ));
13620
 
13621
+ foreach ($cronjobs as $task_name) {
13622
+ wp_schedule_single_event(time() + 10, $task_name);
13623
  }
13624
+ } // Force deletion of the selected scheduled tasks.
13625
+ elseif ($cronjob_action == 'remove') {
13626
+ SucuriScanInterface::info($total_tasks . ' scheduled tasks were removed.');
13627
+ SucuriScanEvent::report_notice_event(sprintf(
 
 
13628
  'Delete scheduled tasks: (multiple entries): %s',
13629
+ @implode(',', $cronjobs)
13630
+ ));
13631
 
13632
+ foreach ($cronjobs as $task_name) {
13633
+ wp_clear_scheduled_hook($task_name);
13634
  }
13635
+ } // Re-schedule the selected scheduled tasks.
13636
+ elseif ($cronjob_action == 'hourly'
 
 
 
13637
  || $cronjob_action == 'twicedaily'
13638
  || $cronjob_action == 'daily'
13639
  ) {
13640
+ SucuriScanInterface::info($total_tasks . ' tasks were re-scheduled to run <code>' . $cronjob_action . '</code>.');
13641
+ SucuriScanEvent::report_notice_event(sprintf(
13642
  'Re-configure scheduled tasks %s: (multiple entries): %s',
13643
  $cronjob_action,
13644
+ @implode(',', $cronjobs)
13645
+ ));
13646
 
13647
+ foreach ($cronjobs as $task_name) {
13648
+ wp_clear_scheduled_hook($task_name);
13649
+ $next_due = wp_next_scheduled($task_name);
13650
+ wp_schedule_event($next_due, $cronjob_action, $task_name);
13651
  }
13652
  }
13653
  } else {
13654
+ SucuriScanInterface::error('No scheduled tasks were selected from the list.');
13655
  }
13656
  }
13657
  }
13662
  *
13663
  * @return array A list of pseudo-variables and values that will replace them in the HTML template.
13664
  */
13665
+ function sucuriscan_infosys_errorlogs()
13666
+ {
13667
+ $params = array(
13668
  'ErrorLog.Path' => '',
13669
  'ErrorLog.Exists' => 'No',
13670
  'ErrorLog.NoItemsVisibility' => 'hidden',
13676
  );
13677
 
13678
  $error_log_path = false;
13679
+ $log_filename = SucuriScan::ini_get('error_log');
13680
+ $errorlogs_limit = SucuriScanOption::get_option(':errorlogs_limit');
13681
+ $params['ErrorLog.LogsLimit'] = $errorlogs_limit;
13682
+ $counter = 0;
13683
+
13684
+ if ($log_filename) {
13685
+ $error_log_path = @realpath(ABSPATH . '/' . $log_filename);
13686
+ }
13687
+
13688
+ if (SucuriScanOption::is_disabled(':parse_errorlogs')) {
13689
+ $params['ErrorLog.DisabledVisibility'] = 'visible';
13690
+ }
13691
+
13692
+ if ($error_log_path) {
13693
+ $params['ErrorLog.Exists'] = 'Yes';
13694
+ $params['ErrorLog.Path'] = $error_log_path;
13695
+ $params['ErrorLog.FileSize'] = SucuriScan::human_filesize(filesize($error_log_path));
13696
+
13697
+ $last_lines = SucuriScanFileInfo::tail_file($error_log_path, $errorlogs_limit);
13698
+ $error_logs = SucuriScanFSScanner::parse_error_logs($last_lines);
13699
+ $error_logs = array_reverse($error_logs);
13700
+ $counter = 0;
13701
+
13702
+ foreach ($error_logs as $error_log) {
13703
+ $css_class = ( $counter % 2 === 0 ) ? '' : 'alternate';
13704
+ $params['ErrorLog.List'] .= SucuriScanTemplate::getSnippet(
13705
+ 'infosys-errorlogs',
13706
+ array(
13707
+ 'ErrorLog.CssClass' => $css_class,
13708
+ 'ErrorLog.DateTime' => SucuriScan::datetime($error_log->timestamp),
13709
+ 'ErrorLog.ErrorType' => $error_log->error_type,
13710
+ 'ErrorLog.ErrorCode' => $error_log->error_code,
13711
+ 'ErrorLog.ErrorAbbr' => strtoupper(substr($error_log->error_code, 0, 1)),
13712
+ 'ErrorLog.ErrorMessage' => $error_log->error_message,
13713
+ 'ErrorLog.FilePath' => $error_log->file_path,
13714
+ 'ErrorLog.LineNumber' => $error_log->line_number,
13715
+ )
13716
+ );
13717
+ $counter += 1;
13718
  }
13719
 
13720
+ if ($counter <= 0) {
13721
+ $params['ErrorLog.InvalidFormatVisibility'] = 'visible';
13722
  }
13723
  } else {
13724
+ $params['ErrorLog.NoItemsVisibility'] = 'visible';
13725
  }
13726
 
13727
+ return SucuriScanTemplate::getSection('infosys-errorlogs', $params);
13728
  }
13729
 
13730
  /**
13732
  *
13733
  * @return array A list of pseudo-variables and values that will replace them in the HTML template.
13734
  */
13735
+ function sucuriscan_server_info()
13736
+ {
13737
  global $wpdb;
13738
 
13739
+ $params = array(
13740
  'ServerInfo.Variables' => '',
13741
  );
13742
 
13743
  $info_vars = array(
13744
  'Plugin_version' => SUCURISCAN_VERSION,
13745
  'Plugin_checksum' => SUCURISCAN_PLUGIN_CHECKSUM,
13746
+ 'Last_filesystem_scan' => SucuriScanFSScanner::get_filesystem_runtime(true),
13747
  'Datetime_and_Timezone' => '',
13748
+ 'Operating_system' => sprintf('%s (%d Bit)', PHP_OS, PHP_INT_SIZE * 8),
13749
  'Server' => 'Unknown',
13750
  'Developer_mode' => 'OFF',
13751
  'Memory_usage' => 'N/A',
13757
  $info_vars['Datetime_and_Timezone'] = sprintf(
13758
  '%s (GMT %s)',
13759
  SucuriScan::current_datetime(),
13760
+ get_option('gmt_offset')
13761
  );
13762
 
13763
+ if (defined('WP_DEBUG') && WP_DEBUG) {
13764
  $info_vars['Developer_mode'] = 'ON';
13765
  }
13766
 
13767
+ if (function_exists('memory_get_usage')) {
13768
+ $info_vars['Memory_usage'] = round(memory_get_usage() / 1024 / 1024, 2).' MB';
13769
  }
13770
 
13771
+ if (isset($_SERVER['SERVER_SOFTWARE'])) {
13772
+ $info_vars['Server'] = $_SERVER['SERVER_SOFTWARE'];
13773
  }
13774
 
13775
+ if ($wpdb) {
13776
+ $info_vars['MySQL_version'] = $wpdb->get_var('SELECT VERSION() AS version');
13777
 
13778
+ $mysql_info = $wpdb->get_results('SHOW VARIABLES LIKE "sql_mode"');
13779
+ if (is_array($mysql_info) && !empty($mysql_info[0]->Value)) {
13780
  $info_vars['SQL_mode'] = $mysql_info[0]->Value;
13781
  }
13782
  }
13792
  'max_input_time',
13793
  );
13794
 
13795
+ foreach ($field_names as $php_flag) {
13796
+ $php_flag_value = SucuriScan::ini_get($php_flag);
13797
  $php_flag_name = 'PHP_' . $php_flag;
13798
+ $info_vars[$php_flag_name] = $php_flag_value ? $php_flag_value : 'N/A';
13799
  }
13800
 
13801
  $counter = 0;
13802
 
13803
+ foreach ($info_vars as $var_name => $var_value) {
13804
+ $css_class = ($counter % 2 === 0) ? '' : 'alternate';
13805
+ $var_name = str_replace('_', "\x20", $var_name);
13806
 
13807
+ $params['ServerInfo.Variables'] .= SucuriScanTemplate::getSnippet(
13808
+ 'infosys-serverinfo',
13809
+ array(
13810
+ 'ServerInfo.CssClass' => $css_class,
13811
+ 'ServerInfo.Title' => $var_name,
13812
+ 'ServerInfo.Value' => $var_value,
13813
+ )
13814
+ );
13815
  $counter += 1;
13816
  }
13817
 
13818
+ return SucuriScanTemplate::getSection('infosys-serverinfo', $params);
13819
  }
13820
 
uninstall.php CHANGED
@@ -11,8 +11,7 @@
11
  * @since File available since Release 0.1
12
  */
13
 
14
- if (
15
- ! defined( 'WP_UNINSTALL_PLUGIN' )
16
  || WP_UNINSTALL_PLUGIN != 'sucuri-scanner/sucuri.php'
17
  ) {
18
  exit(0);
@@ -20,8 +19,10 @@ if (
20
 
21
  $sucuriscan_option_names = array(
22
  'account',
 
23
  'ads_visibility',
24
  'api_key',
 
25
  'audit_report',
26
  'cloudproxy_apikey',
27
  'collect_wrong_passwords',
@@ -76,6 +77,8 @@ $sucuriscan_option_names = array(
76
  'scan_frequency',
77
  'scan_interface',
78
  'scan_modfiles',
 
 
79
  'site_version',
80
  'sitecheck_counter',
81
  'sitecheck_scanner',
@@ -84,31 +87,30 @@ $sucuriscan_option_names = array(
84
  'xhr_monitor',
85
  );
86
 
87
- $sucuriscan_storage_path = get_option( 'sucuriscan_datastore_path' );
88
 
89
- if (
90
- $sucuriscan_storage_path !== false
91
- && file_exists( $sucuriscan_storage_path )
92
- && is_writable( $sucuriscan_storage_path )
93
- && is_dir( $sucuriscan_storage_path )
94
  ) {
95
- @unlink( $sucuriscan_storage_path . '/.htaccess' );
96
- @unlink( $sucuriscan_storage_path . '/index.html' );
97
- @unlink( $sucuriscan_storage_path . '/sucuri-failedlogins.php' );
98
- @unlink( $sucuriscan_storage_path . '/sucuri-ignorescanning.php' );
99
- @unlink( $sucuriscan_storage_path . '/sucuri-integrity.php' );
100
- @unlink( $sucuriscan_storage_path . '/sucuri-lastlogins.php' );
101
- @unlink( $sucuriscan_storage_path . '/sucuri-oldfailedlogins.php' );
102
- @unlink( $sucuriscan_storage_path . '/sucuri-plugindata.php' );
103
- @unlink( $sucuriscan_storage_path . '/sucuri-sitecheck.php' );
104
- @unlink( $sucuriscan_storage_path . '/sucuri-trustip.php' );
105
 
106
- @rmdir( $sucuriscan_storage_path );
107
  }
108
 
109
- foreach ( $sucuriscan_option_names as $option_name ) {
110
- delete_option( 'sucuriscan_' . $option_name );
111
- delete_site_option( 'sucuriscan_' . $option_name );
112
  }
113
 
114
  // EOF
11
  * @since File available since Release 0.1
12
  */
13
 
14
+ if (!defined('WP_UNINSTALL_PLUGIN')
 
15
  || WP_UNINSTALL_PLUGIN != 'sucuri-scanner/sucuri.php'
16
  ) {
17
  exit(0);
19
 
20
  $sucuriscan_option_names = array(
21
  'account',
22
+ 'addr_header',
23
  'ads_visibility',
24
  'api_key',
25
+ 'api_service',
26
  'audit_report',
27
  'cloudproxy_apikey',
28
  'collect_wrong_passwords',
77
  'scan_frequency',
78
  'scan_interface',
79
  'scan_modfiles',
80
+ 'selfhosting_fpath',
81
+ 'selfhosting_monitor',
82
  'site_version',
83
  'sitecheck_counter',
84
  'sitecheck_scanner',
87
  'xhr_monitor',
88
  );
89
 
90
+ $sucuriscan_storage_path = get_option('sucuriscan_datastore_path');
91
 
92
+ if ($sucuriscan_storage_path !== false
93
+ && file_exists($sucuriscan_storage_path)
94
+ && is_writable($sucuriscan_storage_path)
95
+ && is_dir($sucuriscan_storage_path)
 
96
  ) {
97
+ @unlink($sucuriscan_storage_path . '/.htaccess');
98
+ @unlink($sucuriscan_storage_path . '/index.html');
99
+ @unlink($sucuriscan_storage_path . '/sucuri-failedlogins.php');
100
+ @unlink($sucuriscan_storage_path . '/sucuri-ignorescanning.php');
101
+ @unlink($sucuriscan_storage_path . '/sucuri-integrity.php');
102
+ @unlink($sucuriscan_storage_path . '/sucuri-lastlogins.php');
103
+ @unlink($sucuriscan_storage_path . '/sucuri-oldfailedlogins.php');
104
+ @unlink($sucuriscan_storage_path . '/sucuri-plugindata.php');
105
+ @unlink($sucuriscan_storage_path . '/sucuri-sitecheck.php');
106
+ @unlink($sucuriscan_storage_path . '/sucuri-trustip.php');
107
 
108
+ @rmdir($sucuriscan_storage_path);
109
  }
110
 
111
+ foreach ($sucuriscan_option_names as $option_name) {
112
+ delete_option('sucuriscan_' . $option_name);
113
+ delete_site_option('sucuriscan_' . $option_name);
114
  }
115
 
116
  // EOF