Shield Security for WordPress - Version 12.0.0

Version Description

Download this release

Release Info

Developer paultgoodchild
Plugin Icon 128x128 Shield Security for WordPress
Version 12.0.0
Comparing to
See all releases

Code changes from version 11.5.5 to 12.0.0

Files changed (365) hide show
  1. cl.json +90 -0
  2. src/config/feature-admin_access_restriction.php → config/admin_access_restriction.json +7 -6
  3. config/audit_trail.json +433 -0
  4. src/config/feature-autoupdates.php → config/autoupdates.json +0 -0
  5. src/config/feature-comments_filter.php → config/comments_filter.json +5 -1
  6. src/config/feature-comms.php → config/comms.json +10 -0
  7. config/data.json +73 -0
  8. config/deprecated/admin_access_restriction.php +449 -0
  9. config/deprecated/audit_trail.php +433 -0
  10. config/deprecated/autoupdates.php +211 -0
  11. config/deprecated/comments_filter.php +353 -0
  12. config/deprecated/comms.php +74 -0
  13. config/deprecated/data.php +73 -0
  14. src/config/feature-email.php → config/deprecated/email.php +1 -1
  15. src/config/feature-events.php → config/deprecated/events.php +0 -0
  16. src/config/feature-firewall.php → config/deprecated/firewall.php +30 -49
  17. src/config/feature-hack_protect.php → config/deprecated/hack_protect.php +21 -62
  18. src/config/feature-headers.php → config/deprecated/headers.php +0 -0
  19. src/config/feature-insights.php → config/deprecated/insights.php +5 -5
  20. src/config/feature-integrations.php → config/deprecated/integrations.php +36 -12
  21. src/config/feature-ips.php → config/deprecated/ips.php +83 -42
  22. src/config/feature-license.php → config/deprecated/license.php +6 -4
  23. src/config/feature-lockdown.php → config/deprecated/lockdown.php +6 -2
  24. src/config/feature-login_protect.php → config/deprecated/login_protect.php +33 -31
  25. src/config/feature-plugin.php → config/deprecated/plugin.php +65 -37
  26. src/config/feature-reporting.php → config/deprecated/reporting.php +15 -0
  27. src/config/feature-sessions.php → config/deprecated/sessions.php +21 -10
  28. src/config/feature-traffic.php → config/deprecated/traffic.php +20 -17
  29. src/config/feature-user_management.php → config/deprecated/user_management.php +37 -8
  30. config/email.json +24 -0
  31. config/events.json +61 -0
  32. config/firewall.json +442 -0
  33. config/hack_protect.json +595 -0
  34. config/headers.json +198 -0
  35. config/insights.json +32 -0
  36. config/integrations.json +241 -0
  37. config/ips.json +775 -0
  38. config/license.json +167 -0
  39. config/lockdown.json +200 -0
  40. config/login_protect.json +551 -0
  41. config/plugin.json +785 -0
  42. config/reporting.json +174 -0
  43. config/sessions.json +103 -0
  44. config/traffic.json +252 -0
  45. config/user_management.json +473 -0
  46. icwp-wpsf.php +1 -1
  47. init.php +3 -3
  48. plugin-spec.php +95 -52
  49. plugin.json +95 -52
  50. readme.txt +1 -1
  51. resources/css/plugin.css +13 -21
  52. resources/css/shield/datatables.css +65 -0
  53. resources/images/bootstrap/exclamation-triangle.svg +4 -0
  54. resources/images/bootstrap/info-circle.svg +4 -0
  55. resources/images/bootstrap/info-square.svg +4 -0
  56. resources/images/bootstrap/question-diamond.svg +4 -0
  57. resources/images/bootstrap/tags.svg +4 -0
  58. resources/images/bootstrap/x-octagon.svg +4 -0
  59. resources/js/plugin.js +4 -4
  60. resources/js/shield/audit_trail.js +196 -0
  61. resources/js/{base64.min.js → shield/datatables.js} +0 -0
  62. resources/js/shield/options.js +0 -304
  63. resources/js/shield/traffic.js +158 -0
  64. src/config/feature-audit_trail.php +0 -271
  65. src/lib/src/Controller/Assets/Paths.php +4 -0
  66. src/lib/src/Controller/Assets/Urls.php +0 -10
  67. src/lib/src/Controller/Controller.php +132 -258
  68. src/lib/src/Controller/Plugin/PluginDeactivate.php +34 -0
  69. src/lib/src/Controller/Plugin/PluginDelete.php +62 -0
  70. src/lib/src/Crons/BaseCron.php +3 -0
  71. src/lib/src/Databases/AuditTrail/Handler.php +0 -7
  72. src/lib/src/Databases/Events/Handler.php +1 -4
  73. src/lib/src/Databases/Events/Select.php +5 -5
  74. src/lib/src/Databases/GeoIp/BaseGeoIp.php +0 -30
  75. src/lib/src/Databases/GeoIp/Delete.php +0 -10
  76. src/lib/src/Databases/GeoIp/EntryVO.php +0 -88
  77. src/lib/src/Databases/GeoIp/Handler.php +0 -10
  78. src/lib/src/Databases/GeoIp/Insert.php +0 -9
  79. src/lib/src/Databases/GeoIp/Select.php +0 -22
  80. src/lib/src/Databases/Scanner/Update.php +0 -34
  81. src/lib/src/Databases/Session/Select.php +1 -1
  82. src/lib/src/Databases/Traffic/Handler.php +0 -7
  83. src/lib/src/Logging/Processors/RequestMetaProcessor.php +33 -0
  84. src/lib/src/Logging/Processors/ShieldMetaProcessor.php +25 -0
  85. src/lib/src/Logging/Processors/UserMetaProcessor.php +36 -0
  86. src/lib/src/Logging/Processors/WpMetaProcessor.php +19 -0
  87. src/lib/src/Modules/AuditTrail/AdminNotices.php +59 -0
  88. src/lib/src/Modules/AuditTrail/AjaxHandler.php +13 -42
  89. src/lib/src/Modules/AuditTrail/Auditors/Emails.php +17 -23
  90. src/lib/src/Modules/AuditTrail/Auditors/Plugins.php +9 -9
  91. src/lib/src/Modules/AuditTrail/Auditors/Posts.php +29 -33
  92. src/lib/src/Modules/AuditTrail/Auditors/Themes.php +10 -11
  93. src/lib/src/Modules/AuditTrail/Auditors/Upgrades.php +33 -33
  94. src/lib/src/Modules/AuditTrail/Auditors/Users.php +14 -14
  95. src/lib/src/Modules/AuditTrail/Auditors/Wordpress.php +11 -11
  96. src/lib/src/Modules/AuditTrail/DB/LoadLogs.php +88 -0
  97. src/lib/src/Modules/AuditTrail/DB/LogRecord.php +11 -0
  98. src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Common.php +18 -0
  99. src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Delete.php +10 -0
  100. src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Handler.php +9 -0
  101. src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Record.php +13 -0
  102. src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Select.php +10 -0
  103. src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Common.php +18 -0
  104. src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Delete.php +10 -0
  105. src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Handler.php +9 -0
  106. src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Record.php +12 -0
  107. src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Select.php +10 -0
  108. src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Update.php +10 -0
  109. src/lib/src/Modules/AuditTrail/Insights/OverviewCards.php +1 -1
  110. src/lib/src/Modules/AuditTrail/Lib/AuditLogger.php +99 -0
  111. src/lib/src/Modules/AuditTrail/Lib/AuditMessageBuilder.php +19 -12
  112. src/lib/src/Modules/AuditTrail/Lib/AuditWriter.php +4 -1
  113. src/lib/src/Modules/AuditTrail/Lib/LogHandlers/LocalDbWriter.php +150 -0
  114. src/lib/src/Modules/AuditTrail/Lib/LogHandlers/LogFileHandler.php +46 -0
  115. src/lib/src/Modules/AuditTrail/Lib/LogHandlers/Utility/LogFileDirCreate.php +39 -0
  116. src/lib/src/Modules/AuditTrail/Lib/LogHandlers/Utility/LogFileRotate.php +59 -0
  117. src/lib/src/Modules/AuditTrail/Lib/LogTable/DelegateAjaxHandler.php +61 -0
  118. src/lib/src/Modules/AuditTrail/Lib/LogTable/LoadRawTableData.php +157 -0
  119. src/lib/src/Modules/AuditTrail/Lib/Ops/ConvertLegacy.php +146 -0
  120. src/lib/src/Modules/AuditTrail/Lib/Utility/AutoWhitelistParamFromAuditEntry.php +0 -55
  121. src/lib/src/Modules/AuditTrail/Lib/Utility/GetLogFileContent.php +21 -0
  122. src/lib/src/Modules/AuditTrail/ModCon.php +121 -65
  123. src/lib/src/Modules/AuditTrail/Options.php +64 -3
  124. src/lib/src/Modules/AuditTrail/Processor.php +4 -1
  125. src/lib/src/Modules/AuditTrail/Strings.php +158 -151
  126. src/lib/src/Modules/AuditTrail/UI.php +19 -39
  127. src/lib/src/Modules/AuditTrail/Upgrade.php +12 -0
  128. src/lib/src/Modules/AuditTrail/WpCli/Display.php +1 -6
  129. src/lib/src/Modules/Autoupdates/Strings.php +36 -36
  130. src/lib/src/Modules/Base/Config/LoadConfig.php +118 -0
  131. src/lib/src/Modules/Base/Databases.php +68 -0
  132. src/lib/src/Modules/Base/Lib/Components/UiTrack.php +40 -0
  133. src/lib/src/Modules/Base/ModCon.php +56 -87
  134. src/lib/src/Modules/Base/Options.php +335 -403
  135. src/lib/src/Modules/Base/Options/OptValueSanitize.php +22 -24
  136. src/lib/src/Modules/Base/Options/Storage.php +45 -0
  137. src/lib/src/Modules/Base/Processor.php +13 -2
  138. src/lib/src/Modules/Base/Strings.php +24 -17
  139. src/lib/src/Modules/Base/UI.php +51 -53
  140. src/lib/src/Modules/Base/WpCli/ModuleStandard.php +3 -3
  141. src/lib/src/Modules/BaseShield/ModCon.php +0 -9
  142. src/lib/src/Modules/CommentsFilter/Scan/Scanner.php +1 -1
  143. src/lib/src/Modules/CommentsFilter/Strings.php +39 -7
  144. src/lib/src/Modules/CommentsFilter/Upgrade.php +0 -32
  145. src/lib/src/Modules/Comms/Strings.php +20 -16
  146. src/lib/src/Modules/Data/DB/IPs/IPGeoVO.php +17 -0
  147. src/lib/src/Modules/Data/DB/IPs/IPRecords.php +42 -0
  148. src/lib/src/Modules/Data/DB/IPs/Ops/Common.php +14 -0
  149. src/lib/src/Modules/Data/DB/IPs/Ops/Delete.php +10 -0
  150. src/lib/src/Modules/Data/DB/IPs/Ops/Handler.php +9 -0
  151. src/lib/src/Modules/Data/DB/IPs/Ops/Insert.php +9 -0
  152. src/lib/src/Modules/Data/DB/IPs/Ops/Record.php +62 -0
  153. src/lib/src/Modules/Data/DB/IPs/Ops/Select.php +12 -0
  154. src/lib/src/Modules/Data/DB/ReqLogs/GetRequestMeta.php +73 -0
  155. src/lib/src/Modules/Data/DB/ReqLogs/LoadLogs.php +48 -0
  156. src/lib/src/Modules/Data/DB/ReqLogs/LogRecord.php +13 -0
  157. src/lib/src/Modules/Data/DB/ReqLogs/Ops/Common.php +14 -0
  158. src/lib/src/Modules/Data/DB/ReqLogs/Ops/Delete.php +10 -0
  159. src/lib/src/Modules/Data/DB/ReqLogs/Ops/Handler.php +9 -0
  160. src/lib/src/Modules/Data/DB/ReqLogs/Ops/Insert.php +9 -0
  161. src/lib/src/Modules/Data/DB/ReqLogs/Ops/Record.php +11 -0
  162. src/lib/src/Modules/Data/DB/ReqLogs/Ops/Select.php +10 -0
  163. src/lib/src/Modules/Data/DB/ReqLogs/RequestRecords.php +38 -0
  164. src/lib/src/Modules/Data/ModCon.php +43 -0
  165. src/lib/src/Modules/Events/Lib/EventsListener.php +5 -0
  166. src/lib/src/Modules/Events/Lib/EventsService.php +85 -19
  167. src/lib/src/Modules/Events/Lib/Reports/KeyStats.php +2 -3
  168. src/lib/src/Modules/Events/Lib/UI/BuildDataForStats.php +2 -5
  169. src/lib/src/Modules/Events/Strings.php +37 -246
  170. src/lib/src/Modules/Events/Upgrade.php +54 -0
  171. src/lib/src/Modules/Firewall/Lib/Scan/CanScan.php +8 -1
  172. src/lib/src/Modules/Firewall/Lib/Scan/Checks/ExeFiles.php +2 -2
  173. src/lib/src/Modules/Firewall/Lib/Scan/FirewallHandler.php +177 -0
  174. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/Aggressive.php +12 -0
  175. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/Base.php +91 -0
  176. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/BaseRequestParams.php +16 -0
  177. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/DirTraversal.php +12 -0
  178. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/ExeFiles.php +22 -0
  179. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/FieldTruncation.php +12 -0
  180. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/LeadingSchema.php +12 -0
  181. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/PhpCode.php +12 -0
  182. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/SqlQueries.php +12 -0
  183. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/WpTerms.php +12 -0
  184. src/lib/src/Modules/Firewall/Lib/Scan/ParametersToScan.php +24 -21
  185. src/lib/src/Modules/Firewall/Lib/Scan/PerformScan.php +0 -68
  186. src/lib/src/Modules/Firewall/Processor.php +70 -361
  187. src/lib/src/Modules/Firewall/Strings.php +37 -46
  188. src/lib/src/Modules/GeoIp/Lookup.php +32 -38
  189. src/lib/src/Modules/HackGuard/AjaxHandler.php +3 -152
  190. src/lib/src/Modules/HackGuard/Lib/Reports/ScanAlerts.php +1 -1
  191. src/lib/src/Modules/HackGuard/Lib/Reports/ScanRepairs.php +30 -24
  192. src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/ScheduleBuildAll.php +3 -3
  193. src/lib/src/Modules/HackGuard/ModCon.php +0 -1
  194. src/lib/src/Modules/HackGuard/Processor.php +0 -12
  195. src/lib/src/Modules/HackGuard/Scan/Controller/Base.php +13 -67
  196. src/lib/src/Modules/HackGuard/Scan/Queue/CompleteQueue.php +8 -8
  197. src/lib/src/Modules/HackGuard/Strings.php +81 -47
  198. src/lib/src/Modules/HackGuard/UI.php +113 -161
  199. src/lib/src/Modules/IPs/AjaxHandler.php +5 -4
  200. src/lib/src/Modules/IPs/BotTrack/Base.php +1 -1
  201. src/lib/src/Modules/IPs/BotTrack/TrackFakeWebCrawler.php +3 -7
  202. src/lib/src/Modules/IPs/BotTrack/TrackLoginFailed.php +1 -1
  203. src/lib/src/Modules/IPs/BotTrack/TrackLoginInvalid.php +1 -1
  204. src/lib/src/Modules/IPs/BotTrack/TrackUserAgent.php +0 -17
  205. src/lib/src/Modules/IPs/Components/ProcessOffense.php +18 -18
  206. src/lib/src/Modules/IPs/Components/UnblockIpByFlag.php +1 -1
  207. src/lib/src/Modules/IPs/Lib/BlockRequest.php +4 -3
  208. src/lib/src/Modules/IPs/Lib/Bots/BotEventListener.php +2 -2
  209. src/lib/src/Modules/IPs/Lib/Bots/BotSignalsController.php +1 -1
  210. src/lib/src/Modules/IPs/Lib/IpAnalyse/BuildDisplay.php +68 -47
  211. src/lib/src/Modules/IPs/Lib/IpAnalyse/FindAllPluginIps.php +4 -11
  212. src/lib/src/Modules/IPs/Lib/Ops/AddIp.php +8 -4
  213. src/lib/src/Modules/IPs/Lib/Ops/DeleteIp.php +1 -1
  214. src/lib/src/Modules/IPs/Lib/ProcessOffenses.php +11 -11
  215. src/lib/src/Modules/IPs/Strings.php +152 -66
  216. src/lib/src/Modules/IPs/Upgrade.php +0 -40
  217. src/lib/src/Modules/Insights/Lib/SideMenuBuilder.php +8 -9
  218. src/lib/src/Modules/Insights/ModCon.php +1 -6
  219. src/lib/src/Modules/Insights/Strings.php +0 -44
  220. src/lib/src/Modules/Insights/UI.php +56 -2
  221. src/lib/src/Modules/Integrations/Lib/Bots/Spam/Handlers/Base.php +1 -1
  222. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/Base.php +1 -1
  223. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/Buddyboss.php +30 -0
  224. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/Buddypress.php +1 -5
  225. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/EasyDigitalDownloads.php +1 -5
  226. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/LearnPress.php +1 -5
  227. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/LifterLMS.php +0 -2
  228. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/PaidMemberSubscriptions.php +0 -4
  229. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/ProfileBuilder.php +0 -3
  230. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/UltimateMember.php +0 -3
  231. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/WPMembers.php +0 -3
  232. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/WooCommerce.php +0 -4
  233. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/WordPress.php +0 -4
  234. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/UserFormsController.php +1 -0
  235. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/Base.php +0 -61
  236. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/ContactForm7.php +0 -20
  237. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/ElementorPro.php +0 -26
  238. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/FluentForms.php +0 -28
  239. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/FormidableForms.php +0 -29
  240. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/Forminator.php +0 -20
  241. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/GravityForms.php +0 -22
  242. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/Helpers/NinjaForms_ShieldSpamAction.php +0 -47
  243. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/KaliForms.php +0 -27
  244. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/NinjaForms.php +0 -48
  245. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/WPForms.php +0 -37
  246. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/WpForo.php +0 -40
  247. src/lib/src/Modules/Integrations/Strings.php +17 -5
  248. src/lib/src/Modules/License/ModCon.php +6 -4
  249. src/lib/src/Modules/License/Strings.php +26 -17
  250. src/lib/src/Modules/Lockdown/Processor.php +4 -4
  251. src/lib/src/Modules/Lockdown/Strings.php +58 -49
  252. src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GaspJs.php +2 -2
  253. src/lib/src/Modules/LoginGuard/Lib/Rename/RenameLogin.php +46 -44
  254. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BackupCodes.php +0 -16
  255. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BaseProvider.php +11 -1
  256. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Email.php +0 -25
  257. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/GoogleAuth.php +0 -16
  258. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/U2F.php +0 -16
  259. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Yubikey.php +1 -17
  260. src/lib/src/Modules/LoginGuard/Strings.php +67 -58
  261. src/lib/src/Modules/Plugin/AdminNotices.php +0 -32
  262. src/lib/src/Modules/Plugin/AjaxHandler.php +1 -1
  263. src/lib/src/Modules/Plugin/Components/BadgeWidget.php +6 -5
  264. src/lib/src/Modules/Plugin/Components/PluginBadge.php +28 -34
  265. src/lib/src/Modules/Plugin/Debug.php +17 -0
  266. src/lib/src/Modules/Plugin/Insights/OverviewCards.php +4 -3
  267. src/lib/src/Modules/Plugin/Lib/Debug/Collate.php +10 -5
  268. src/lib/src/Modules/Plugin/Lib/Debug/RecentEvents.php +29 -31
  269. src/lib/src/Modules/Plugin/Lib/ImportExport/Export.php +17 -21
  270. src/lib/src/Modules/Plugin/Lib/ImportExport/Import.php +13 -13
  271. src/lib/src/Modules/Plugin/Lib/ImportExport/ImportExportController.php +4 -7
  272. src/lib/src/Modules/Plugin/Lib/ImportExport/Options/BuildTransferableOptions.php +2 -2
  273. src/lib/src/Modules/Plugin/ModCon.php +7 -9
  274. src/lib/src/Modules/Plugin/Options.php +0 -5
  275. src/lib/src/Modules/Plugin/Processor.php +2 -2
  276. src/lib/src/Modules/Plugin/Strings.php +92 -38
  277. src/lib/src/Modules/Plugin/UI.php +7 -7
  278. src/lib/src/Modules/Plugin/Upgrade.php +19 -0
  279. src/lib/src/Modules/Plugin/WpCli/ForceOff.php +7 -2
  280. src/lib/src/Modules/Plugin/WpCli/ToggleDebug.php +5 -0
  281. src/lib/src/Modules/Reporting/Charts/BaseBuildChartData.php +2 -3
  282. src/lib/src/Modules/Reporting/Debug.php +1 -0
  283. src/lib/src/Modules/Reporting/Lib/ReportingController.php +27 -12
  284. src/lib/src/Modules/Reporting/Strings.php +29 -25
  285. src/lib/src/Modules/Reporting/UI.php +6 -7
  286. src/lib/src/Modules/SecurityAdmin/Strings.php +26 -20
  287. src/lib/src/Modules/Sessions/Lib/SessionController.php +16 -16
  288. src/lib/src/Modules/Sessions/Strings.php +32 -0
  289. src/lib/src/Modules/Sessions/Upgrade.php +0 -13
  290. src/lib/src/Modules/Statistics/Options.php +0 -15
  291. src/lib/src/Modules/Statistics/Strings.php +0 -58
  292. src/lib/src/Modules/Traffic/AjaxHandler.php +16 -12
  293. src/lib/src/Modules/Traffic/Lib/Limit/Limiter.php +20 -19
  294. src/lib/src/Modules/Traffic/Lib/Limit/RateLimitExceededException.php +19 -0
  295. src/lib/src/Modules/Traffic/Lib/Limit/TestIp.php +0 -43
  296. src/lib/src/Modules/Traffic/Lib/Limit/TestIpLimit.php +43 -0
  297. src/lib/src/Modules/Traffic/Lib/LogHandlers/LocalDbWriter.php +56 -0
  298. src/lib/src/Modules/Traffic/Lib/Logger.php +6 -29
  299. src/lib/src/Modules/Traffic/Lib/Ops/ConvertLegacy.php +88 -0
  300. src/lib/src/Modules/Traffic/Lib/RequestLogger.php +101 -0
  301. src/lib/src/Modules/Traffic/Lib/TrafficTable/DelegateAjaxHandler.php +44 -0
  302. src/lib/src/Modules/Traffic/Lib/TrafficTable/LoadRawTableData.php +210 -0
  303. src/lib/src/Modules/Traffic/ModCon.php +31 -13
  304. src/lib/src/Modules/Traffic/Options.php +13 -6
  305. src/lib/src/Modules/Traffic/Processor.php +7 -10
  306. src/lib/src/Modules/Traffic/Strings.php +6 -9
  307. src/lib/src/Modules/Traffic/UI.php +9 -14
  308. src/lib/src/Modules/Traffic/Upgrade.php +19 -0
  309. src/lib/src/Modules/UserManagement/AjaxHandler.php +9 -9
  310. src/lib/src/Modules/UserManagement/Lib/Password/UserPasswordHandler.php +35 -29
  311. src/lib/src/Modules/UserManagement/Lib/Registration/EmailValidate.php +6 -6
  312. src/lib/src/Modules/UserManagement/Lib/Session/UserSessionHandler.php +16 -15
  313. src/lib/src/Modules/UserManagement/Lib/Suspend/UserSuspendController.php +6 -6
  314. src/lib/src/Modules/UserManagement/ModCon.php +26 -27
  315. src/lib/src/Modules/UserManagement/Processor.php +55 -70
  316. src/lib/src/Modules/UserManagement/Strings.php +176 -154
  317. src/lib/src/Modules/UserManagement/Suspend/Base.php +0 -38
  318. src/lib/src/Modules/UserManagement/Suspend/Idle.php +0 -47
  319. src/lib/src/Modules/UserManagement/Suspend/PasswordExpiry.php +0 -71
  320. src/lib/src/Modules/UserManagement/Suspend/Suspended.php +0 -28
  321. src/lib/src/Modules/UserManagement/Upgrade.php +0 -10
  322. src/lib/src/Scans/Base/BaseFileScanActionVO.php +0 -3
  323. src/lib/src/Scans/Base/BaseScanActionVO.php +0 -11
  324. src/lib/src/Scans/Base/Table/BaseEntryFormatter.php +0 -82
  325. src/lib/src/Scans/Base/Table/BaseFileEntryFormatter.php +0 -77
  326. src/lib/src/Scans/Base/Utilities/ItemActionHandler.php +1 -1
  327. src/lib/src/Scans/Mal/BuildFileMap.php +0 -1
  328. src/lib/src/Scans/Mal/BuildScanAction.php +0 -1
  329. src/lib/src/Scans/Mal/Scan.php +0 -4
  330. src/lib/src/Scans/Mal/ScanFromFileMap.php +0 -4
  331. src/lib/src/Scans/Mal/Table/EntryFormatter.php +0 -84
  332. src/lib/src/Scans/Ptg/Table/EntryFormatter.php +0 -151
  333. src/lib/src/Scans/Ufc/Table/EntryFormatter.php +0 -34
  334. src/lib/src/Scans/Wcf/Table/EntryFormatter.php +0 -89
  335. src/lib/src/Scans/Wpv/Scan.php +7 -7
  336. src/lib/src/ShieldNetApi/SureSend/SendEmail.php +3 -3
  337. src/lib/src/Tables/Build/AuditTrail.php +2 -15
  338. src/lib/src/Tables/Build/BaseBuild.php +3 -3
  339. src/lib/src/Tables/Build/ScanAggregate.php +0 -131
  340. src/lib/src/Tables/Build/ScanApc.php +0 -55
  341. src/lib/src/Tables/Build/ScanBase.php +0 -81
  342. src/lib/src/Tables/Build/ScanMal.php +0 -19
  343. src/lib/src/Tables/Build/ScanPtg.php +0 -49
  344. src/lib/src/Tables/Build/ScanUfc.php +0 -19
  345. src/lib/src/Tables/Build/ScanWcf.php +0 -19
  346. src/lib/src/Tables/Build/ScanWpv.php +0 -72
  347. src/lib/src/Tables/Build/Traffic.php +0 -208
  348. src/lib/src/Tables/DataTables/Build/AuditTrail/ForAuditTrail.php +129 -76
  349. src/lib/src/Tables/DataTables/Build/Base.php +1 -1
  350. src/lib/src/Tables/DataTables/Build/Traffic/ForTraffic.php +152 -0
  351. src/lib/src/Tables/DataTables/LoadData/BaseLoadTableData.php +31 -0
  352. src/lib/src/Tables/Render/WpCliTable/AuditTrail.php +11 -5
  353. src/lib/src/Tables/Render/WpCliTable/Base.php +0 -29
  354. src/lib/src/Tables/Render/WpListTable/AuditTrail.php +1 -21
  355. src/lib/src/Tables/Render/WpListTable/ScanAggregate.php +0 -74
  356. src/lib/src/Tables/Render/WpListTable/ScanApc.php +0 -40
  357. src/lib/src/Tables/Render/WpListTable/ScanBase.php +0 -58
  358. src/lib/src/Tables/Render/WpListTable/ScanMal.php +0 -49
  359. src/lib/src/Tables/Render/WpListTable/ScanPtg.php +0 -31
  360. src/lib/src/Tables/Render/WpListTable/ScanUfc.php +0 -43
  361. src/lib/src/Tables/Render/WpListTable/ScanWcf.php +0 -43
  362. src/lib/src/Tables/Render/WpListTable/ScanWpv.php +0 -94
  363. src/lib/src/Tests/RunTests.php +27 -0
  364. src/lib/src/Tests/VerifyEvents.php +84 -0
  365. src/lib/src/Tests/VerifyUniqueEvents.php +2 -9
cl.json CHANGED
@@ -1,4 +1,94 @@
1
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  "11.5": {
3
  "version": "11.5",
4
  "released_at": 1626779164,
1
  {
2
+ "12.0": {
3
+ "version": "12.0",
4
+ "released_at": 1631783098,
5
+ "hrefs": {
6
+ "release": "https://shsec.io/shieldrelease120",
7
+ "upgrade": "https://shsec.io/shieldupgradeguide120"
8
+ },
9
+ "title": "Events & Audit Trail Overhaul",
10
+ "description": [
11
+ ],
12
+ "items": [
13
+ {
14
+ "type": "new",
15
+ "pro_only": false,
16
+ "title": "Complete Audit Trail Overhaul",
17
+ "description": [
18
+ "The Audit Trail and Events system has been completely rewritten.",
19
+ "It allows for extensions to log to any destination, severity levels, search and more."
20
+ ]
21
+ },
22
+ {
23
+ "type": "new",
24
+ "pro_only": false,
25
+ "title": "New Audit Trail Table & Filters",
26
+ "description": [
27
+ "Audit Trail now uses our preferred table UI with built-in, useful search and filter controls.",
28
+ "There's also rapid and reliable pagination and data reloading."
29
+ ]
30
+ },
31
+ {
32
+ "type": "new",
33
+ "pro_only": false,
34
+ "title": "Audit Trail Events With Severity",
35
+ "description": [
36
+ "All events are given a default severity of 'Alert', 'Warning', 'Info' and 'Debug.",
37
+ "Which event categories are logged can be adjusted in the Configuration."
38
+ ]
39
+ },
40
+ {
41
+ "type": "new",
42
+ "pro_only": false,
43
+ "title": "Audit Trails Logs To File",
44
+ "description": [
45
+ "As well as logging to the database, you can elect to log certain events to file."
46
+ ]
47
+ },
48
+ {
49
+ "type": "improved",
50
+ "pro_only": false,
51
+ "title": "Audit Trail Logs Description",
52
+ "description": [
53
+ "Logged events now have more descriptive messages along with more meta details for the event."
54
+ ]
55
+ },
56
+ {
57
+ "type": "improved",
58
+ "pro_only": false,
59
+ "title": "Audit Trail Meta Data",
60
+ "description": [
61
+ "By linking the Audit Trail to the Traffic Log, you can now see request data alongside Audit Logs."
62
+ ]
63
+ },
64
+ {
65
+ "type": "improved",
66
+ "pro_only": false,
67
+ "title": "Plugin Data Storage",
68
+ "description": [
69
+ "We're adding some smarter data storage to the plugin through more complex and interconnected database tables.",
70
+ "This approach reduces repeated and redundant data storage and disk usage."
71
+ ]
72
+ },
73
+ {
74
+ "type": "new",
75
+ "pro_only": false,
76
+ "title": "Traffic Logging UI.",
77
+ "description": [
78
+ "The Traffic Log feature now also uses the improved table UI for faster processing and better search."
79
+ ]
80
+ },
81
+ {
82
+ "type": "changed",
83
+ "pro_only": false,
84
+ "title": "Traffic Log Limits",
85
+ "description": [
86
+ "Traffic logs are no longer limited by amount.",
87
+ "They are instead limited by age (in days). Updated configuration options are available."
88
+ ]
89
+ }
90
+ ]
91
+ },
92
  "11.5": {
93
  "version": "11.5",
94
  "released_at": 1626779164,
src/config/feature-admin_access_restriction.php → config/admin_access_restriction.json RENAMED
@@ -48,11 +48,11 @@
48
  ]
49
  },
50
  {
51
- "slug": "section_admin_access_restriction_areas",
52
- "title": "Security Admin Restriction Zones",
53
- "title_short": "Access Restriction Zones",
54
- "beacon_id": 214,
55
- "summary": [
56
  "Purpose - Restricts access to key WordPress areas for all users not authenticated with the Security Admin Access system.",
57
  "Recommendation - Use of this feature is highly recommend."
58
  ]
@@ -408,10 +408,11 @@
408
  ],
409
  "events": {
410
  "key_success": {
 
411
  "recent": true
412
  },
413
  "key_fail": {
414
- "cat": 3,
415
  "recent": true,
416
  "offense": true
417
  }
48
  ]
49
  },
50
  {
51
+ "slug": "section_admin_access_restriction_areas",
52
+ "title": "Security Admin Restriction Zones",
53
+ "title_short": "Access Restriction Zones",
54
+ "beacon_id": 214,
55
+ "summary": [
56
  "Purpose - Restricts access to key WordPress areas for all users not authenticated with the Security Admin Access system.",
57
  "Recommendation - Use of this feature is highly recommend."
58
  ]
408
  ],
409
  "events": {
410
  "key_success": {
411
+ "level": "debug",
412
  "recent": true
413
  },
414
  "key_fail": {
415
+ "level": "warning",
416
  "recent": true,
417
  "offense": true
418
  }
config/audit_trail.json ADDED
@@ -0,0 +1,433 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "audit_trail",
3
+ "properties": {
4
+ "slug": "audit_trail",
5
+ "name": "Audit Trail",
6
+ "sidebar_name": "Audit Trail",
7
+ "show_module_menu_item": false,
8
+ "show_module_options": true,
9
+ "storage_key": "audit_trail",
10
+ "tagline": "Track All Site Activity: Who, What, When and Where",
11
+ "show_central": true,
12
+ "access_restricted": true,
13
+ "premium": false,
14
+ "run_if_whitelisted": true,
15
+ "run_if_verified_bot": false,
16
+ "run_if_wpcli": true,
17
+ "order": 110
18
+ },
19
+ "menu_items": [
20
+ {
21
+ "title": "Audit Trail",
22
+ "slug": "audit-redirect"
23
+ }
24
+ ],
25
+ "custom_redirects": [
26
+ {
27
+ "source_mod_page": "audit-redirect",
28
+ "target_mod_page": "insights",
29
+ "query_args": {
30
+ "inav": "audit"
31
+ }
32
+ }
33
+ ],
34
+ "admin_notices": {
35
+ "new-audit-trail": {
36
+ "id": "new-audit-trail",
37
+ "schedule": "conditions",
38
+ "valid_admin": true,
39
+ "plugin_page_only": true,
40
+ "can_dismiss": true,
41
+ "type": "info"
42
+ }
43
+ },
44
+ "sections": [
45
+ {
46
+ "slug": "section_localdb",
47
+ "primary": true,
48
+ "title": "Log To DB",
49
+ "title_short": "Log To DB",
50
+ "beacon_id": 241,
51
+ "summary": [
52
+ "Purpose - Provides finer control over the audit trail itself.",
53
+ "Recommendation - These settings are dependent on your requirements."
54
+ ]
55
+ },
56
+ {
57
+ "slug": "section_at_file",
58
+ "title": "Log To File",
59
+ "title_short": "Log To File",
60
+ "beacon_id": 241,
61
+ "summary": [
62
+ "Purpose - Provides finer control over the audit trail itself.",
63
+ "Recommendation - These settings are dependent on your requirements."
64
+ ]
65
+ },
66
+ {
67
+ "slug": "section_enable_plugin_feature_audit_trail",
68
+ "title": "Enable Module: Audit Trail",
69
+ "title_short": "Disable Module",
70
+ "beacon_id": 241,
71
+ "summary": [
72
+ "Purpose - The Audit Trail is designed so you can look back on events and analyse what happened and what may have gone wrong.",
73
+ "Recommendation - Keep the Audit Trail feature turned on."
74
+ ]
75
+ },
76
+ {
77
+ "slug": "section_non_ui",
78
+ "hidden": true
79
+ }
80
+ ],
81
+ "options": [
82
+ {
83
+ "key": "enable_audit_trail",
84
+ "section": "section_enable_plugin_feature_audit_trail",
85
+ "advanced": true,
86
+ "default": "Y",
87
+ "type": "checkbox",
88
+ "link_info": "https://shsec.io/5p",
89
+ "link_blog": "https://shsec.io/a1",
90
+ "beacon_id": 241,
91
+ "name": "Enable Audit Trail",
92
+ "summary": "Enable (or Disable) The Audit Trail module",
93
+ "description": "Un-Checking this option will completely disable the Audit Trail module"
94
+ },
95
+ {
96
+ "key": "log_level_db",
97
+ "section": "section_localdb",
98
+ "type": "multiple_select",
99
+ "default": [
100
+ "alert",
101
+ "warning",
102
+ "notice"
103
+ ],
104
+ "value_options": [
105
+ {
106
+ "value_key": "disabled",
107
+ "text": "Logging Disabled"
108
+ },
109
+ {
110
+ "value_key": "alert",
111
+ "text": "Alert"
112
+ },
113
+ {
114
+ "value_key": "warning",
115
+ "text": "Warning"
116
+ },
117
+ {
118
+ "value_key": "notice",
119
+ "text": "Notice"
120
+ },
121
+ {
122
+ "value_key": "info",
123
+ "text": "Info"
124
+ },
125
+ {
126
+ "value_key": "debug",
127
+ "text": "Debug"
128
+ }
129
+ ],
130
+ "link_info": "",
131
+ "link_blog": "",
132
+ "beacon_id": 375,
133
+ "name": "Logging Level",
134
+ "summary": "Logging Level For DB-Based Logs",
135
+ "description": "Logging Level For DB-Based Logs"
136
+ },
137
+ {
138
+ "key": "audit_trail_auto_clean",
139
+ "section": "section_localdb",
140
+ "default": 7,
141
+ "min": 1,
142
+ "type": "integer",
143
+ "link_info": "https://shsec.io/a2",
144
+ "link_blog": "https://shsec.io/a1",
145
+ "beacon_id": 375,
146
+ "name": "Auto Clean",
147
+ "summary": "Enable Audit Auto Cleaning",
148
+ "description": "Events older than the number of days specified will be automatically cleaned from the database"
149
+ },
150
+ {
151
+ "key": "log_level_file",
152
+ "section": "section_at_file",
153
+ "premium": true,
154
+ "type": "multiple_select",
155
+ "default": [
156
+ "disabled"
157
+ ],
158
+ "value_options": [
159
+ {
160
+ "value_key": "disabled",
161
+ "text": "Logging Disabled"
162
+ },
163
+ {
164
+ "value_key": "same_as_db",
165
+ "text": "Same As DB"
166
+ },
167
+ {
168
+ "value_key": "alert",
169
+ "text": "Alert"
170
+ },
171
+ {
172
+ "value_key": "warning",
173
+ "text": "Warning"
174
+ },
175
+ {
176
+ "value_key": "notice",
177
+ "text": "Notice"
178
+ },
179
+ {
180
+ "value_key": "info",
181
+ "text": "Info"
182
+ },
183
+ {
184
+ "value_key": "debug",
185
+ "text": "Debug"
186
+ }
187
+ ],
188
+ "link_info": "",
189
+ "link_blog": "",
190
+ "beacon_id": 375,
191
+ "name": "File Logging Level",
192
+ "summary": "Logging Level For File-Based Logs",
193
+ "description": "Logging Level For File-Based Logs"
194
+ },
195
+ {
196
+ "key": "legacy_db_deleted_at",
197
+ "section": "section_non_ui",
198
+ "transferable": false,
199
+ "type": "integer",
200
+ "default": ""
201
+ }
202
+ ],
203
+ "definitions": {
204
+ "db_handler_classes": {
205
+ "at_logs": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\AuditTrail\\DB\\Logs\\Ops\\Handler",
206
+ "at_meta": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\AuditTrail\\DB\\Meta\\Ops\\Handler"
207
+ },
208
+ "db_table_at_logs": {
209
+ "slug": "at_logs",
210
+ "has_updated_at": true,
211
+ "has_created_at": true,
212
+ "has_deleted_at": false,
213
+ "cols_custom": {
214
+ "req_ref": {
215
+ "macro_type": "foreign_key_id",
216
+ "foreign_key": {
217
+ "ref_table": "icwp_wpsf_req_logs"
218
+ }
219
+ },
220
+ "site_id": {
221
+ "macro_type": "unsigned_int",
222
+ "default": 1,
223
+ "comment": "Site ID"
224
+ },
225
+ "event_slug": {
226
+ "macro_type": "varchar",
227
+ "comment": "Event Slug"
228
+ }
229
+ }
230
+ },
231
+ "db_table_at_meta": {
232
+ "slug": "at_meta",
233
+ "has_updated_at": false,
234
+ "has_created_at": false,
235
+ "has_deleted_at": false,
236
+ "cols_custom": {
237
+ "log_ref": {
238
+ "macro_type": "foreign_key_id",
239
+ "foreign_key": {
240
+ "ref_table": "icwp_wpsf_at_logs"
241
+ },
242
+ "comment": "Reference to primary log entry"
243
+ },
244
+ "meta_key": {
245
+ "macro_type": "varchar",
246
+ "length": 32,
247
+ "comment": "Meta Key"
248
+ },
249
+ "meta_value": {
250
+ "macro_type": "text",
251
+ "comment": "Meta Data"
252
+ }
253
+ }
254
+ },
255
+ "db_classes": {
256
+ "audit_trail": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\AuditTrail\\Handler"
257
+ },
258
+ "db_table_audit_trail": {
259
+ "slug": "audit_trail",
260
+ "has_updated_at": true,
261
+ "cols_custom": {
262
+ "rid": "varchar(10) NOT NULL DEFAULT '' COMMENT 'Request ID'",
263
+ "ip": "varchar(40) NOT NULL DEFAULT 0 COMMENT 'Visitor IP Address'",
264
+ "wp_username": "varchar(255) NOT NULL DEFAULT '-' COMMENT 'WP User'",
265
+ "context": "varchar(32) NOT NULL DEFAULT 'none' COMMENT 'Audit Context'",
266
+ "event": "varchar(50) NOT NULL DEFAULT 'none' COMMENT 'Specific Audit Event'",
267
+ "category": "int(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Severity'",
268
+ "meta": "text COMMENT 'Audit Event Data'",
269
+ "count": "SMALLINT(5) UNSIGNED NOT NULL DEFAULT 1 COMMENT 'Repeat Count'"
270
+ }
271
+ },
272
+ "max_free_days": 7,
273
+ "audit_trail_table_name": "audit_trail",
274
+ "events": {
275
+ "plugin_activated": {
276
+ "audit_params": [
277
+ "plugin"
278
+ ],
279
+ "level": "notice",
280
+ "audit_multiple": true
281
+ },
282
+ "plugin_deactivated": {
283
+ "audit_params": [
284
+ "plugin"
285
+ ],
286
+ "level": "notice",
287
+ "audit_multiple": true
288
+ },
289
+ "plugin_file_edited": {
290
+ "audit_params": [
291
+ "file"
292
+ ],
293
+ "level": "warning"
294
+ },
295
+ "plugin_upgraded": {
296
+ "audit_params": [
297
+ "plugin",
298
+ "from",
299
+ "to"
300
+ ],
301
+ "level": "notice",
302
+ "audit_multiple": true
303
+ },
304
+ "theme_activated": {
305
+ "audit_params": [
306
+ "theme"
307
+ ],
308
+ "level": "notice"
309
+ },
310
+ "theme_file_edited": {
311
+ "audit_params": [
312
+ "file"
313
+ ],
314
+ "level": "warning"
315
+ },
316
+ "theme_upgraded": {
317
+ "audit_params": [
318
+ "theme",
319
+ "from",
320
+ "to"
321
+ ],
322
+ "level": "notice",
323
+ "audit_multiple": true
324
+ },
325
+ "core_updated": {
326
+ "audit_params": [
327
+ "from",
328
+ "to"
329
+ ],
330
+ "level": "notice"
331
+ },
332
+ "permalinks_structure": {
333
+ "audit_params": [
334
+ "from",
335
+ "to"
336
+ ],
337
+ "level": "warning"
338
+ },
339
+ "post_deleted": {
340
+ "audit_params": [
341
+ "title"
342
+ ],
343
+ "level": "warning",
344
+ "audit_multiple": true
345
+ },
346
+ "post_trashed": {
347
+ "audit_params": [
348
+ "title",
349
+ "type"
350
+ ],
351
+ "level": "warning",
352
+ "audit_multiple": true
353
+ },
354
+ "post_recovered": {
355
+ "audit_params": [
356
+ "title",
357
+ "type"
358
+ ],
359
+ "level": "info",
360
+ "audit_multiple": true
361
+ },
362
+ "post_updated": {
363
+ "audit_params": [
364
+ "title",
365
+ "type"
366
+ ],
367
+ "level": "notice",
368
+ "audit_multiple": true
369
+ },
370
+ "post_published": {
371
+ "audit_params": [
372
+ "title",
373
+ "type"
374
+ ],
375
+ "level": "notice",
376
+ "audit_multiple": true
377
+ },
378
+ "post_unpublished": {
379
+ "audit_params": [
380
+ "title",
381
+ "type"
382
+ ],
383
+ "level": "warning",
384
+ "audit_multiple": true
385
+ },
386
+ "user_login": {
387
+ "audit_params": [
388
+ "user_login"
389
+ ],
390
+ "level": "warning"
391
+ },
392
+ "user_login_app": {
393
+ "audit_params": [
394
+ "user_login"
395
+ ],
396
+ "level": "warning"
397
+ },
398
+ "user_registered": {
399
+ "audit_params": [
400
+ "user_login",
401
+ "email"
402
+ ],
403
+ "level": "alert"
404
+ },
405
+ "user_deleted": {
406
+ "audit_params": [
407
+ "user_login",
408
+ "email"
409
+ ],
410
+ "level": "warning",
411
+ "audit_multiple": true
412
+ },
413
+ "user_deleted_reassigned": {
414
+ "audit_params": [
415
+ "user_login"
416
+ ],
417
+ "level": "notice"
418
+ },
419
+ "email_attempt_send": {
420
+ "audit_params": [
421
+ "to",
422
+ "subject",
423
+ "cc",
424
+ "bcc",
425
+ "bt_file",
426
+ "bt_line"
427
+ ],
428
+ "level": "info",
429
+ "audit_multiple": true
430
+ }
431
+ }
432
+ }
433
+ }
src/config/feature-autoupdates.php → config/autoupdates.json RENAMED
File without changes
src/config/feature-comments_filter.php → config/comments_filter.json RENAMED
@@ -332,7 +332,7 @@
332
  "url_spam_blacklist_terms": "https://raw.githubusercontent.com/splorp/wordpress-comment-blacklist/master/blacklist.txt",
333
  "events": {
334
  "comment_spam_block": {
335
- "audit": false,
336
  "stat": false,
337
  "offense": true
338
  },
@@ -343,6 +343,10 @@
343
  "spam_block_recaptcha": {
344
  },
345
  "spam_block_human": {
 
 
 
 
346
  }
347
  }
348
  }
332
  "url_spam_blacklist_terms": "https://raw.githubusercontent.com/splorp/wordpress-comment-blacklist/master/blacklist.txt",
333
  "events": {
334
  "comment_spam_block": {
335
+ "level": "notice",
336
  "stat": false,
337
  "offense": true
338
  },
343
  "spam_block_recaptcha": {
344
  },
345
  "spam_block_human": {
346
+ "audit_params": [
347
+ "word",
348
+ "key"
349
+ ]
350
  }
351
  }
352
  }
src/config/feature-comms.php → config/comms.json RENAMED
@@ -56,8 +56,18 @@
56
  "definitions": {
57
  "events": {
58
  "suresend_success": {
 
 
 
 
 
59
  },
60
  "suresend_fail": {
 
 
 
 
 
61
  }
62
  }
63
  }
56
  "definitions": {
57
  "events": {
58
  "suresend_success": {
59
+ "audit_params": [
60
+ "email",
61
+ "slug"
62
+ ],
63
+ "level": "info"
64
  },
65
  "suresend_fail": {
66
+ "audit_params": [
67
+ "email",
68
+ "slug"
69
+ ],
70
+ "level": "warning"
71
  }
72
  }
73
  }
config/data.json ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "data",
3
+ "properties": {
4
+ "slug": "data",
5
+ "name": "Data",
6
+ "show_module_menu_item": false,
7
+ "auto_enabled": true,
8
+ "storage_key": "data",
9
+ "show_central": false,
10
+ "premium": false,
11
+ "access_restricted": true,
12
+ "run_if_whitelisted": true,
13
+ "run_if_wpcli": true,
14
+ "skip_processor": true,
15
+ "tracking_exclude": true
16
+ },
17
+ "wpcli": {
18
+ "enabled": false
19
+ },
20
+ "sections": [
21
+ ],
22
+ "options": [
23
+ ],
24
+ "definitions": {
25
+ "db_handler_classes": {
26
+ "ips": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Data\\DB\\IPs\\Ops\\Handler",
27
+ "req_logs": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Data\\DB\\ReqLogs\\Ops\\Handler"
28
+ },
29
+ "db_table_ips": {
30
+ "autoexpire": 0,
31
+ "slug": "ips",
32
+ "has_updated_at": false,
33
+ "has_deleted_at": false,
34
+ "col_older_than": "created_at",
35
+ "cols_custom": {
36
+ "ip": {
37
+ "macro_type": "ip",
38
+ "attr": [
39
+ "UNIQUE"
40
+ ]
41
+ },
42
+ "geo": {
43
+ "macro_type": "meta",
44
+ "comment": "GeoIP Data"
45
+ }
46
+ }
47
+ },
48
+ "db_table_req_logs": {
49
+ "slug": "req_logs",
50
+ "autoexpire": 0,
51
+ "has_updated_at": false,
52
+ "has_deleted_at": false,
53
+ "cols_custom": {
54
+ "req_id": {
55
+ "macro_type": "varchar",
56
+ "length": 10,
57
+ "attr": [
58
+ "UNIQUE"
59
+ ]
60
+ },
61
+ "ip_ref": {
62
+ "macro_type": "foreign_key_id",
63
+ "foreign_key": {
64
+ "ref_table": "icwp_wpsf_ips"
65
+ }
66
+ },
67
+ "meta": {
68
+ "macro_type": "meta"
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
config/deprecated/admin_access_restriction.php ADDED
@@ -0,0 +1,449 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "admin_access_restriction",
3
+ "properties": {
4
+ "slug": "admin_access_restriction",
5
+ "name": "Security Admin",
6
+ "sidebar_name": "Security Admin",
7
+ "show_module_menu_item": false,
8
+ "show_module_options": true,
9
+ "storage_key": "admin_access_restriction",
10
+ "tagline": "Protect your Security Plugin, not just your WordPress site",
11
+ "show_central": true,
12
+ "access_restricted": true,
13
+ "premium": false,
14
+ "run_if_whitelisted": false,
15
+ "run_if_verified_bot": true,
16
+ "run_if_wpcli": false,
17
+ "order": 20
18
+ },
19
+ "wpcli": {
20
+ "root": "secadmin"
21
+ },
22
+ "admin_notices": {
23
+ "certain-options-restricted": {
24
+ "id": "certain-options-restricted",
25
+ "schedule": "conditions",
26
+ "plugin_admin": "no",
27
+ "per_user": true,
28
+ "type": "warning"
29
+ },
30
+ "admin-users-restricted": {
31
+ "id": "admin-users-restricted",
32
+ "schedule": "conditions",
33
+ "plugin_admin": "no",
34
+ "type": "warning",
35
+ "per_user": true
36
+ }
37
+ },
38
+ "sections": [
39
+ {
40
+ "slug": "section_security_admin_settings",
41
+ "primary": true,
42
+ "title": "Security Admin Restriction Settings",
43
+ "title_short": "Security Admin Settings",
44
+ "beacon_id": 215,
45
+ "summary": [
46
+ "Purpose - Restrict access using a simple Access PIN.",
47
+ "Recommendation - Use of this feature is highly recommend."
48
+ ]
49
+ },
50
+ {
51
+ "slug": "section_admin_access_restriction_areas",
52
+ "title": "Security Admin Restriction Zones",
53
+ "title_short": "Access Restriction Zones",
54
+ "beacon_id": 214,
55
+ "summary": [
56
+ "Purpose - Restricts access to key WordPress areas for all users not authenticated with the Security Admin Access system.",
57
+ "Recommendation - Use of this feature is highly recommend."
58
+ ]
59
+ },
60
+ {
61
+ "slug": "section_whitelabel",
62
+ "title": "Shield White Label",
63
+ "title_short": "White Label",
64
+ "beacon_id": 131,
65
+ "summary": [
66
+ "Purpose - Rename and re-brand the Shield Security plugin for your client site installations."
67
+ ]
68
+ },
69
+ {
70
+ "slug": "section_enable_plugin_feature_admin_access_restriction",
71
+ "title": "Enable Module: WordPress Security Admin",
72
+ "title_short": "Disable Module",
73
+ "beacon_id": 213,
74
+ "summary": [
75
+ "Purpose - Restricts access to this plugin preventing unauthorized changes to your security settings.",
76
+ "Recommendation - Keep the Security Admin feature turned on.",
77
+ "You need to also enter a new Access PIN to enable this feature."
78
+ ]
79
+ },
80
+ {
81
+ "slug": "section_non_ui",
82
+ "hidden": true
83
+ }
84
+ ],
85
+ "options": [
86
+ {
87
+ "key": "enable_admin_access_restriction",
88
+ "section": "section_enable_plugin_feature_admin_access_restriction",
89
+ "advanced": true,
90
+ "default": "Y",
91
+ "type": "checkbox",
92
+ "link_info": "https://shsec.io/40",
93
+ "link_blog": "https://shsec.io/wpsf02",
94
+ "name": "Enable Security Admin",
95
+ "summary": "Enforce Security Admin Access Restriction",
96
+ "description": "Enable this with great care and consideration. When this Access PIN option is enabled, you must specify a key below and use it to gain access to this plugin."
97
+ },
98
+ {
99
+ "key": "admin_access_key",
100
+ "section": "section_security_admin_settings",
101
+ "sensitive": true,
102
+ "default": "",
103
+ "type": "password",
104
+ "link_info": "https://shsec.io/42",
105
+ "link_blog": "",
106
+ "beacon_id": 215,
107
+ "name": "Security Admin Access PIN",
108
+ "summary": "Provide/Update Security Admin Access PIN",
109
+ "description": "Careful: If you forget this, you could potentially lock yourself out from using this plugin."
110
+ },
111
+ {
112
+ "key": "sec_admin_users",
113
+ "section": "section_security_admin_settings",
114
+ "advanced": true,
115
+ "sensitive": true,
116
+ "premium": true,
117
+ "default": [],
118
+ "type": "array",
119
+ "link_info": "https://shsec.io/dk",
120
+ "link_blog": "",
121
+ "beacon_id": 132,
122
+ "name": "Security Admins",
123
+ "summary": "Persistent Security Admins",
124
+ "description": "All emails, usernames, or user IDs entered here will always be Security Admins."
125
+ },
126
+ {
127
+ "key": "admin_access_timeout",
128
+ "section": "section_security_admin_settings",
129
+ "advanced": true,
130
+ "default": 30,
131
+ "type": "integer",
132
+ "min": 1,
133
+ "link_info": "https://shsec.io/41",
134
+ "link_blog": "",
135
+ "beacon_id": 387,
136
+ "name": "Security Admin Timeout",
137
+ "summary": "Specify An Automatic Timeout Interval For Security Admin Access",
138
+ "description": "This will automatically expire your Security Admin Session. Does not apply until you enter the access PIN again. Default: 60 minutes."
139
+ },
140
+ {
141
+ "key": "allow_email_override",
142
+ "section": "section_security_admin_settings",
143
+ "advanced": true,
144
+ "default": "Y",
145
+ "type": "checkbox",
146
+ "link_info": "https://shsec.io/gf",
147
+ "link_blog": "",
148
+ "beacon_id": 385,
149
+ "name": "Allow Email Override",
150
+ "summary": "Allow Email Override Of Admin Access Restrictions",
151
+ "description": "Allow the use of verification emails to override and switch off the Security Admin restrictions."
152
+ },
153
+ {
154
+ "key": "admin_access_restrict_options",
155
+ "section": "section_admin_access_restriction_areas",
156
+ "default": "Y",
157
+ "type": "checkbox",
158
+ "link_info": "https://shsec.io/a0",
159
+ "link_blog": "https://shsec.io/wpsf32",
160
+ "beacon_id": 214,
161
+ "name": "Options",
162
+ "summary": "Restrict Access To Key WordPress Posts And Pages Actions",
163
+ "description": "Careful: This will restrict access to page/post creation, editing and deletion. Note: Selecting 'Edit' will also restrict all other options."
164
+ },
165
+ {
166
+ "key": "admin_access_restrict_admin_users",
167
+ "section": "section_admin_access_restriction_areas",
168
+ "advanced": true,
169
+ "default": "N",
170
+ "type": "checkbox",
171
+ "link_info": "https://shsec.io/a0",
172
+ "link_blog": "",
173
+ "beacon_id": 214,
174
+ "name": "Admin Users",
175
+ "summary": "Restrict Access To Create/Delete/Modify Other Admin Users",
176
+ "description": "Careful: This will restrict the ability of WordPress administrators from creating, modifying or promoting other administrators."
177
+ },
178
+ {
179
+ "key": "admin_access_restrict_plugins",
180
+ "section": "section_admin_access_restriction_areas",
181
+ "advanced": true,
182
+ "type": "multiple_select",
183
+ "default": [],
184
+ "value_options": [
185
+ {
186
+ "value_key": "activate_plugins",
187
+ "text": "Activate"
188
+ },
189
+ {
190
+ "value_key": "install_plugins",
191
+ "text": "Install"
192
+ },
193
+ {
194
+ "value_key": "update_plugins",
195
+ "text": "Update"
196
+ },
197
+ {
198
+ "value_key": "delete_plugins",
199
+ "text": "Delete"
200
+ }
201
+ ],
202
+ "link_info": "https://shsec.io/a0",
203
+ "link_blog": "https://shsec.io/wpsf21",
204
+ "beacon_id": 214,
205
+ "summary": "Restrict Access To Key WordPress Plugin Actions",
206
+ "description": "Careful: This will restrict access to plugin installation, update, activation and deletion. Note: Selecting 'Activate' will also restrict all other options."
207
+ },
208
+ {
209
+ "key": "admin_access_restrict_themes",
210
+ "section": "section_admin_access_restriction_areas",
211
+ "advanced": true,
212
+ "type": "multiple_select",
213
+ "default": [],
214
+ "value_options": [
215
+ {
216
+ "value_key": "switch_themes",
217
+ "text": "Activate"
218
+ },
219
+ {
220
+ "value_key": "edit_theme_options",
221
+ "text": "Edit Theme Options"
222
+ },
223
+ {
224
+ "value_key": "install_themes",
225
+ "text": "Install"
226
+ },
227
+ {
228
+ "value_key": "update_themes",
229
+ "text": "Update"
230
+ },
231
+ {
232
+ "value_key": "delete_themes",
233
+ "text": "Delete"
234
+ }
235
+ ],
236
+ "link_info": "https://shsec.io/a0",
237
+ "link_blog": "https://shsec.io/wpsf21",
238
+ "beacon_id": 214,
239
+ "summary": "Restrict Access To WordPress Theme Actions",
240
+ "description": "Careful: This will restrict access to theme installation, update, activation and deletion."
241
+ },
242
+ {
243
+ "key": "admin_access_restrict_posts",
244
+ "section": "section_admin_access_restriction_areas",
245
+ "advanced": true,
246
+ "type": "multiple_select",
247
+ "default": [],
248
+ "value_options": [
249
+ {
250
+ "value_key": "edit",
251
+ "text": "Create/Edit"
252
+ },
253
+ {
254
+ "value_key": "publish",
255
+ "text": "Publish"
256
+ },
257
+ {
258
+ "value_key": "delete",
259
+ "text": "Delete"
260
+ }
261
+ ],
262
+ "link_info": "https://shsec.io/a0",
263
+ "link_blog": "https://shsec.io/wpsf21",
264
+ "beacon_id": 214,
265
+ "summary": "Restrict Access To Key WordPress Posts And Pages Actions",
266
+ "description": "Careful: This will restrict access to page/post creation, editing and deletion."
267
+ },
268
+ {
269
+ "key": "whitelabel_enable",
270
+ "section": "section_whitelabel",
271
+ "premium": true,
272
+ "default": "N",
273
+ "type": "checkbox",
274
+ "link_info": "https://shsec.io/dr",
275
+ "link_blog": "https://shsec.io/ds",
276
+ "beacon_id": 131,
277
+ "name": "Enable White Label",
278
+ "summary": "Activate Your White Label Settings",
279
+ "description": "Use this option to turn on/off the whole White Label feature."
280
+ },
281
+ {
282
+ "key": "wl_hide_updates",
283
+ "section": "section_whitelabel",
284
+ "default": "Y",
285
+ "type": "checkbox",
286
+ "link_info": "",
287
+ "link_blog": "",
288
+ "name": "Hide Updates",
289
+ "summary": "Hide Available Updates From Non Security Admins",
290
+ "description": "Hides the availability of Shield updates from non-security admins."
291
+ },
292
+ {
293
+ "key": "wl_replace_badge_url",
294
+ "section": "section_whitelabel",
295
+ "default": "N",
296
+ "type": "checkbox",
297
+ "link_info": "",
298
+ "link_blog": "",
299
+ "name": "Replace Plugin Badge",
300
+ "summary": "Replace Plugin Badge URL and Images",
301
+ "description": "When using the plugin badge, replace the URL and link with your Whitelabel settings."
302
+ },
303
+ {
304
+ "key": "wl_pluginnamemain",
305
+ "section": "section_whitelabel",
306
+ "sensitive": true,
307
+ "default": "Shield",
308
+ "type": "text",
309
+ "link_info": "https://shsec.io/dt",
310
+ "link_blog": "",
311
+ "beacon_id": 216,
312
+ "name": "Plugin Name",
313
+ "summary": "The Name Of The Plugin",
314
+ "description": "The Name Of The Plugin."
315
+ },
316
+ {
317
+ "key": "wl_namemenu",
318
+ "section": "section_whitelabel",
319
+ "sensitive": true,
320
+ "default": "Shield Security",
321
+ "type": "text",
322
+ "link_info": "",
323
+ "link_blog": "",
324
+ "name": "Menu Title",
325
+ "summary": "The Main Menu Title Of The Plugin",
326
+ "description": "The Main Menu Title Of The Plugin. If left empty, the Plugin Name will be used."
327
+ },
328
+ {
329
+ "key": "wl_companyname",
330
+ "section": "section_whitelabel",
331
+ "sensitive": true,
332
+ "default": "Example Company Name",
333
+ "type": "text",
334
+ "link_info": "https://shsec.io/dt",
335
+ "link_blog": "",
336
+ "beacon_id": 216,
337
+ "name": "Company Name",
338
+ "summary": "The Name Of Your Company",
339
+ "description": "Provide the name of your company."
340
+ },
341
+ {
342
+ "key": "wl_description",
343
+ "section": "section_whitelabel",
344
+ "sensitive": true,
345
+ "default": "Secure Your Sites With The World's Most Powerful WordPress Security Plugin",
346
+ "type": "text",
347
+ "link_info": "",
348
+ "link_blog": "",
349
+ "name": "Plugin Tag Line",
350
+ "summary": "The Tag Line Of The Plugin",
351
+ "description": "The Tag Line Of The Plugin."
352
+ },
353
+ {
354
+ "key": "wl_homeurl",
355
+ "section": "section_whitelabel",
356
+ "sensitive": true,
357
+ "default": "https://shsec.io/7f",
358
+ "type": "text",
359
+ "link_info": "",
360
+ "link_blog": "",
361
+ "name": "Home URL",
362
+ "summary": "Plugin Home Page URL",
363
+ "description": "When a user clicks the home link for this plugin, this is where they'll be directed."
364
+ },
365
+ {
366
+ "key": "wl_menuiconurl",
367
+ "section": "section_whitelabel",
368
+ "sensitive": true,
369
+ "default": "pluginlogo_16x16.png",
370
+ "type": "text",
371
+ "link_info": "https://shsec.io/dt",
372
+ "link_blog": "",
373
+ "beacon_id": 216,
374
+ "name": "Menu Icon",
375
+ "summary": "Menu Icon URL",
376
+ "description": "The URL of the icon displayed in the menu."
377
+ },
378
+ {
379
+ "key": "wl_dashboardlogourl",
380
+ "section": "section_whitelabel",
381
+ "sensitive": true,
382
+ "default": "pluginlogo_128x128.png",
383
+ "type": "text",
384
+ "link_info": "",
385
+ "link_blog": "",
386
+ "beacon_id": 216,
387
+ "name": "Plugin Badge Logo",
388
+ "summary": "Plugin Badge Logo URL",
389
+ "description": "The URL of the logo displayed in the main dashboard. Should be 128x128px"
390
+ },
391
+ {
392
+ "key": "wl_login2fa_logourl",
393
+ "section": "section_whitelabel",
394
+ "sensitive": true,
395
+ "default": "pluginlogo_banner-772x250.png",
396
+ "type": "text",
397
+ "link_info": "https://shsec.io/dt",
398
+ "link_blog": "",
399
+ "name": "Dashboard and 2FA Logo",
400
+ "summary": "Dashboard and 2FA Logo URL",
401
+ "description": "The URL of the logo displayed in the main dashboard. Should be 128x128px"
402
+ }
403
+ ],
404
+ "definitions": {
405
+ "restricted_pages_users": [
406
+ "user-edit.php",
407
+ "users.php"
408
+ ],
409
+ "events": {
410
+ "key_success": {
411
+ "level": "debug",
412
+ "recent": true
413
+ },
414
+ "key_fail": {
415
+ "level": "warning",
416
+ "recent": true,
417
+ "offense": true
418
+ }
419
+ },
420
+ "options_to_restrict": {
421
+ "wpms_options": [
422
+ "admin_email",
423
+ "site_name",
424
+ "registration"
425
+ ],
426
+ "wpms_pages": [
427
+ "settings.php"
428
+ ],
429
+ "wp_options": [
430
+ "blogname",
431
+ "blogdescription",
432
+ "siteurl",
433
+ "home",
434
+ "admin_email",
435
+ "new_admin_email",
436
+ "users_can_register",
437
+ "comments_notify",
438
+ "comment_moderation",
439
+ "blog_public"
440
+ ],
441
+ "wp_pages": [
442
+ "options-general.php",
443
+ "options-discussion.php",
444
+ "options-reading.php",
445
+ "options.php"
446
+ ]
447
+ }
448
+ }
449
+ }
config/deprecated/audit_trail.php ADDED
@@ -0,0 +1,433 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "audit_trail",
3
+ "properties": {
4
+ "slug": "audit_trail",
5
+ "name": "Audit Trail",
6
+ "sidebar_name": "Audit Trail",
7
+ "show_module_menu_item": false,
8
+ "show_module_options": true,
9
+ "storage_key": "audit_trail",
10
+ "tagline": "Track All Site Activity: Who, What, When and Where",
11
+ "show_central": true,
12
+ "access_restricted": true,
13
+ "premium": false,
14
+ "run_if_whitelisted": true,
15
+ "run_if_verified_bot": false,
16
+ "run_if_wpcli": true,
17
+ "order": 110
18
+ },
19
+ "menu_items": [
20
+ {
21
+ "title": "Audit Trail",
22
+ "slug": "audit-redirect"
23
+ }
24
+ ],
25
+ "custom_redirects": [
26
+ {
27
+ "source_mod_page": "audit-redirect",
28
+ "target_mod_page": "insights",
29
+ "query_args": {
30
+ "inav": "audit"
31
+ }
32
+ }
33
+ ],
34
+ "admin_notices": {
35
+ "new-audit-trail": {
36
+ "id": "new-audit-trail",
37
+ "schedule": "conditions",
38
+ "valid_admin": true,
39
+ "plugin_page_only": true,
40
+ "can_dismiss": true,
41
+ "type": "info"
42
+ }
43
+ },
44
+ "sections": [
45
+ {
46
+ "slug": "section_localdb",
47
+ "primary": true,
48
+ "title": "Log To DB",
49
+ "title_short": "Log To DB",
50
+ "beacon_id": 241,
51
+ "summary": [
52
+ "Purpose - Provides finer control over the audit trail itself.",
53
+ "Recommendation - These settings are dependent on your requirements."
54
+ ]
55
+ },
56
+ {
57
+ "slug": "section_at_file",
58
+ "title": "Log To File",
59
+ "title_short": "Log To File",
60
+ "beacon_id": 241,
61
+ "summary": [
62
+ "Purpose - Provides finer control over the audit trail itself.",
63
+ "Recommendation - These settings are dependent on your requirements."
64
+ ]
65
+ },
66
+ {
67
+ "slug": "section_enable_plugin_feature_audit_trail",
68
+ "title": "Enable Module: Audit Trail",
69
+ "title_short": "Disable Module",
70
+ "beacon_id": 241,
71
+ "summary": [
72
+ "Purpose - The Audit Trail is designed so you can look back on events and analyse what happened and what may have gone wrong.",
73
+ "Recommendation - Keep the Audit Trail feature turned on."
74
+ ]
75
+ },
76
+ {
77
+ "slug": "section_non_ui",
78
+ "hidden": true
79
+ }
80
+ ],
81
+ "options": [
82
+ {
83
+ "key": "enable_audit_trail",
84
+ "section": "section_enable_plugin_feature_audit_trail",
85
+ "advanced": true,
86
+ "default": "Y",
87
+ "type": "checkbox",
88
+ "link_info": "https://shsec.io/5p",
89
+ "link_blog": "https://shsec.io/a1",
90
+ "beacon_id": 241,
91
+ "name": "Enable Audit Trail",
92
+ "summary": "Enable (or Disable) The Audit Trail module",
93
+ "description": "Un-Checking this option will completely disable the Audit Trail module"
94
+ },
95
+ {
96
+ "key": "log_level_db",
97
+ "section": "section_localdb",
98
+ "type": "multiple_select",
99
+ "default": [
100
+ "alert",
101
+ "warning",
102
+ "notice"
103
+ ],
104
+ "value_options": [
105
+ {
106
+ "value_key": "disabled",
107
+ "text": "Logging Disabled"
108
+ },
109
+ {
110
+ "value_key": "alert",
111
+ "text": "Alert"
112
+ },
113
+ {
114
+ "value_key": "warning",
115
+ "text": "Warning"
116
+ },
117
+ {
118
+ "value_key": "notice",
119
+ "text": "Notice"
120
+ },
121
+ {
122
+ "value_key": "info",
123
+ "text": "Info"
124
+ },
125
+ {
126
+ "value_key": "debug",
127
+ "text": "Debug"
128
+ }
129
+ ],
130
+ "link_info": "",
131
+ "link_blog": "",
132
+ "beacon_id": 375,
133
+ "name": "Logging Level",
134
+ "summary": "Logging Level For DB-Based Logs",
135
+ "description": "Logging Level For DB-Based Logs"
136
+ },
137
+ {
138
+ "key": "audit_trail_auto_clean",
139
+ "section": "section_localdb",
140
+ "default": 7,
141
+ "min": 1,
142
+ "type": "integer",
143
+ "link_info": "https://shsec.io/a2",
144
+ "link_blog": "https://shsec.io/a1",
145
+ "beacon_id": 375,
146
+ "name": "Auto Clean",
147
+ "summary": "Enable Audit Auto Cleaning",
148
+ "description": "Events older than the number of days specified will be automatically cleaned from the database"
149
+ },
150
+ {
151
+ "key": "log_level_file",
152
+ "section": "section_at_file",
153
+ "premium": true,
154
+ "type": "multiple_select",
155
+ "default": [
156
+ "disabled"
157
+ ],
158
+ "value_options": [
159
+ {
160
+ "value_key": "disabled",
161
+ "text": "Logging Disabled"
162
+ },
163
+ {
164
+ "value_key": "same_as_db",
165
+ "text": "Same As DB"
166
+ },
167
+ {
168
+ "value_key": "alert",
169
+ "text": "Alert"
170
+ },
171
+ {
172
+ "value_key": "warning",
173
+ "text": "Warning"
174
+ },
175
+ {
176
+ "value_key": "notice",
177
+ "text": "Notice"
178
+ },
179
+ {
180
+ "value_key": "info",
181
+ "text": "Info"
182
+ },
183
+ {
184
+ "value_key": "debug",
185
+ "text": "Debug"
186
+ }
187
+ ],
188
+ "link_info": "",
189
+ "link_blog": "",
190
+ "beacon_id": 375,
191
+ "name": "File Logging Level",
192
+ "summary": "Logging Level For File-Based Logs",
193
+ "description": "Logging Level For File-Based Logs"
194
+ },
195
+ {
196
+ "key": "legacy_db_deleted_at",
197
+ "section": "section_non_ui",
198
+ "transferable": false,
199
+ "type": "integer",
200
+ "default": ""
201
+ }
202
+ ],
203
+ "definitions": {
204
+ "db_handler_classes": {
205
+ "at_logs": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\AuditTrail\\DB\\Logs\\Ops\\Handler",
206
+ "at_meta": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\AuditTrail\\DB\\Meta\\Ops\\Handler"
207
+ },
208
+ "db_table_at_logs": {
209
+ "slug": "at_logs",
210
+ "has_updated_at": true,
211
+ "has_created_at": true,
212
+ "has_deleted_at": false,
213
+ "cols_custom": {
214
+ "req_ref": {
215
+ "macro_type": "foreign_key_id",
216
+ "foreign_key": {
217
+ "ref_table": "icwp_wpsf_req_logs"
218
+ }
219
+ },
220
+ "site_id": {
221
+ "macro_type": "unsigned_int",
222
+ "default": 1,
223
+ "comment": "Site ID"
224
+ },
225
+ "event_slug": {
226
+ "macro_type": "varchar",
227
+ "comment": "Event Slug"
228
+ }
229
+ }
230
+ },
231
+ "db_table_at_meta": {
232
+ "slug": "at_meta",
233
+ "has_updated_at": false,
234
+ "has_created_at": false,
235
+ "has_deleted_at": false,
236
+ "cols_custom": {
237
+ "log_ref": {
238
+ "macro_type": "foreign_key_id",
239
+ "foreign_key": {
240
+ "ref_table": "icwp_wpsf_at_logs"
241
+ },
242
+ "comment": "Reference to primary log entry"
243
+ },
244
+ "meta_key": {
245
+ "macro_type": "varchar",
246
+ "length": 32,
247
+ "comment": "Meta Key"
248
+ },
249
+ "meta_value": {
250
+ "macro_type": "text",
251
+ "comment": "Meta Data"
252
+ }
253
+ }
254
+ },
255
+ "db_classes": {
256
+ "audit_trail": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\AuditTrail\\Handler"
257
+ },
258
+ "db_table_audit_trail": {
259
+ "slug": "audit_trail",
260
+ "has_updated_at": true,
261
+ "cols_custom": {
262
+ "rid": "varchar(10) NOT NULL DEFAULT '' COMMENT 'Request ID'",
263
+ "ip": "varchar(40) NOT NULL DEFAULT 0 COMMENT 'Visitor IP Address'",
264
+ "wp_username": "varchar(255) NOT NULL DEFAULT '-' COMMENT 'WP User'",
265
+ "context": "varchar(32) NOT NULL DEFAULT 'none' COMMENT 'Audit Context'",
266
+ "event": "varchar(50) NOT NULL DEFAULT 'none' COMMENT 'Specific Audit Event'",
267
+ "category": "int(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Severity'",
268
+ "meta": "text COMMENT 'Audit Event Data'",
269
+ "count": "SMALLINT(5) UNSIGNED NOT NULL DEFAULT 1 COMMENT 'Repeat Count'"
270
+ }
271
+ },
272
+ "max_free_days": 7,
273
+ "audit_trail_table_name": "audit_trail",
274
+ "events": {
275
+ "plugin_activated": {
276
+ "audit_params": [
277
+ "plugin"
278
+ ],
279
+ "level": "notice",
280
+ "audit_multiple": true
281
+ },
282
+ "plugin_deactivated": {
283
+ "audit_params": [
284
+ "plugin"
285
+ ],
286
+ "level": "notice",
287
+ "audit_multiple": true
288
+ },
289
+ "plugin_file_edited": {
290
+ "audit_params": [
291
+ "file"
292
+ ],
293
+ "level": "warning"
294
+ },
295
+ "plugin_upgraded": {
296
+ "audit_params": [
297
+ "plugin",
298
+ "from",
299
+ "to"
300
+ ],
301
+ "level": "notice",
302
+ "audit_multiple": true
303
+ },
304
+ "theme_activated": {
305
+ "audit_params": [
306
+ "theme"
307
+ ],
308
+ "level": "notice"
309
+ },
310
+ "theme_file_edited": {
311
+ "audit_params": [
312
+ "file"
313
+ ],
314
+ "level": "warning"
315
+ },
316
+ "theme_upgraded": {
317
+ "audit_params": [
318
+ "theme",
319
+ "from",
320
+ "to"
321
+ ],
322
+ "level": "notice",
323
+ "audit_multiple": true
324
+ },
325
+ "core_updated": {
326
+ "audit_params": [
327
+ "from",
328
+ "to"
329
+ ],
330
+ "level": "notice"
331
+ },
332
+ "permalinks_structure": {
333
+ "audit_params": [
334
+ "from",
335
+ "to"
336
+ ],
337
+ "level": "warning"
338
+ },
339
+ "post_deleted": {
340
+ "audit_params": [
341
+ "title"
342
+ ],
343
+ "level": "warning",
344
+ "audit_multiple": true
345
+ },
346
+ "post_trashed": {
347
+ "audit_params": [
348
+ "title",
349
+ "type"
350
+ ],
351
+ "level": "warning",
352
+ "audit_multiple": true
353
+ },
354
+ "post_recovered": {
355
+ "audit_params": [
356
+ "title",
357
+ "type"
358
+ ],
359
+ "level": "info",
360
+ "audit_multiple": true
361
+ },
362
+ "post_updated": {
363
+ "audit_params": [
364
+ "title",
365
+ "type"
366
+ ],
367
+ "level": "notice",
368
+ "audit_multiple": true
369
+ },
370
+ "post_published": {
371
+ "audit_params": [
372
+ "title",
373
+ "type"
374
+ ],
375
+ "level": "notice",
376
+ "audit_multiple": true
377
+ },
378
+ "post_unpublished": {
379
+ "audit_params": [
380
+ "title",
381
+ "type"
382
+ ],
383
+ "level": "warning",
384
+ "audit_multiple": true
385
+ },
386
+ "user_login": {
387
+ "audit_params": [
388
+ "user_login"
389
+ ],
390
+ "level": "warning"
391
+ },
392
+ "user_login_app": {
393
+ "audit_params": [
394
+ "user_login"
395
+ ],
396
+ "level": "warning"
397
+ },
398
+ "user_registered": {
399
+ "audit_params": [
400
+ "user_login",
401
+ "email"
402
+ ],
403
+ "level": "alert"
404
+ },
405
+ "user_deleted": {
406
+ "audit_params": [
407
+ "user_login",
408
+ "email"
409
+ ],
410
+ "level": "warning",
411
+ "audit_multiple": true
412
+ },
413
+ "user_deleted_reassigned": {
414
+ "audit_params": [
415
+ "user_login"
416
+ ],
417
+ "level": "notice"
418
+ },
419
+ "email_attempt_send": {
420
+ "audit_params": [
421
+ "to",
422
+ "subject",
423
+ "cc",
424
+ "bcc",
425
+ "bt_file",
426
+ "bt_line"
427
+ ],
428
+ "level": "info",
429
+ "audit_multiple": true
430
+ }
431
+ }
432
+ }
433
+ }
config/deprecated/autoupdates.php ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "autoupdates",
3
+ "properties": {
4
+ "slug": "autoupdates",
5
+ "name": "Automatic Updates",
6
+ "sidebar_name": "Auto Updates",
7
+ "show_module_menu_item": false,
8
+ "show_module_options": true,
9
+ "storage_key": "autoupdates",
10
+ "tagline": "Take back full control of WordPress automatic updates",
11
+ "show_central": true,
12
+ "access_restricted": true,
13
+ "premium": false,
14
+ "run_if_whitelisted": true,
15
+ "run_if_verified_bot": true,
16
+ "run_if_wpcli": true,
17
+ "order": 60
18
+ },
19
+ "sections": [
20
+ {
21
+ "slug": "section_automatic_updates_for_wordpress_components",
22
+ "primary": true,
23
+ "title": "Automatic Updates For WordPress Components",
24
+ "title_short": "WordPress Components",
25
+ "beacon_id": 236,
26
+ "summary": [
27
+ "Purpose - Control how automatic updates for each WordPress component is handled.",
28
+ "Recommendation - You should at least allow minor updates for the WordPress core."
29
+ ]
30
+ },
31
+ {
32
+ "slug": "section_options",
33
+ "title": "Auto-Update Options",
34
+ "title_short": "Auto-Update Options",
35
+ "beacon_id": 234,
36
+ "summary": "Purpose - Make adjustments to how automatic updates are handled on your site."
37
+ },
38
+ {
39
+ "slug": "section_enable_plugin_feature_automatic_updates_control",
40
+ "title": "Enable Module: Automatic Updates",
41
+ "title_short": "Disable Module",
42
+ "beacon_id": 234,
43
+ "summary": [
44
+ "Purpose - Automatic Updates lets you manage the WordPress automatic updates engine so you choose what exactly gets updated automatically.",
45
+ "Recommendation - Keep the Automatic Updates feature turned on."
46
+ ]
47
+ },
48
+ {
49
+ "slug": "section_non_ui",
50
+ "hidden": true
51
+ }
52
+ ],
53
+ "options": [
54
+ {
55
+ "key": "enable_autoupdates",
56
+ "section": "section_enable_plugin_feature_automatic_updates_control",
57
+ "advanced": true,
58
+ "default": "Y",
59
+ "type": "checkbox",
60
+ "link_info": "https://shsec.io/3w",
61
+ "link_blog": "https://shsec.io/hj",
62
+ "beacon_id": 234,
63
+ "name": "Enable Automatic Updates",
64
+ "summary": "Enable (or Disable) The Automatic Updates module",
65
+ "description": "Un-Checking this option will completely disable the Automatic Updates module"
66
+ },
67
+ {
68
+ "key": "enable_autoupdate_disable_all",
69
+ "section": "section_automatic_updates_for_wordpress_components",
70
+ "advanced": true,
71
+ "default": "N",
72
+ "type": "checkbox",
73
+ "link_info": "https://shsec.io/3v",
74
+ "link_blog": "https://shsec.io/k6",
75
+ "beacon_id": 405,
76
+ "name": "Disable All",
77
+ "summary": "Completely Disable WordPress Automatic Updates",
78
+ "description": "When selected, regardless of any other settings, all WordPress automatic updates on this site will be completely disabled!"
79
+ },
80
+ {
81
+ "key": "autoupdate_core",
82
+ "section": "section_automatic_updates_for_wordpress_components",
83
+ "default": "core_minor",
84
+ "type": "select",
85
+ "value_options": [
86
+ {
87
+ "value_key": "core_never",
88
+ "text": "Never"
89
+ },
90
+ {
91
+ "value_key": "core_minor",
92
+ "text": "Minor Versions Only"
93
+ },
94
+ {
95
+ "value_key": "core_major",
96
+ "text": "Major and Minor Versions"
97
+ }
98
+ ],
99
+ "link_info": "https://shsec.io/k5",
100
+ "link_blog": "",
101
+ "beacon_id": 237,
102
+ "name": "WordPress Core Updates",
103
+ "summary": "Decide how the WordPress Core will automatically update, if at all",
104
+ "description": "At least automatically upgrading minor versions is recommended (and is the WordPress default)."
105
+ },
106
+ {
107
+ "key": "enable_autoupdate_plugins",
108
+ "section": "section_automatic_updates_for_wordpress_components",
109
+ "default": "N",
110
+ "type": "checkbox",
111
+ "link_info": "",
112
+ "link_blog": "",
113
+ "name": "Plugins",
114
+ "summary": "Automatically Update Plugins",
115
+ "description": "Note: Automatic updates for plugins are disabled on WordPress by default."
116
+ },
117
+ {
118
+ "key": "enable_autoupdate_themes",
119
+ "section": "section_automatic_updates_for_wordpress_components",
120
+ "advanced": true,
121
+ "default": "N",
122
+ "type": "checkbox",
123
+ "link_info": "",
124
+ "link_blog": "",
125
+ "name": "Themes",
126
+ "summary": "Automatically Update Themes",
127
+ "description": "Note: Automatic updates for themes are disabled on WordPress by default."
128
+ },
129
+ {
130
+ "key": "update_delay",
131
+ "section": "section_options",
132
+ "premium": true,
133
+ "default": "0",
134
+ "type": "integer",
135
+ "link_info": "https://shsec.io/e5",
136
+ "link_blog": "",
137
+ "beacon_id": 137,
138
+ "name": "Update Delay",
139
+ "summary": "Delay Automatic Updates For Period Of Stability",
140
+ "description": "Shield will delay upgrades until the new update has been available for the set number of days."
141
+ },
142
+ {
143
+ "key": "autoupdate_plugin_self",
144
+ "section": "section_options",
145
+ "advanced": true,
146
+ "default": "auto",
147
+ "type": "select",
148
+ "value_options": [
149
+ {
150
+ "value_key": "auto",
151
+ "text": "Let The Plugin Decide"
152
+ },
153
+ {
154
+ "value_key": "disabled",
155
+ "text": "Disabled"
156
+ },
157
+ {
158
+ "value_key": "immediate",
159
+ "text": "As Soon As Possible"
160
+ }
161
+ ],
162
+ "link_info": "https://shsec.io/3x",
163
+ "link_blog": "",
164
+ "beacon_id": 409,
165
+ "name": "Auto Update Plugin",
166
+ "summary": "Always Automatically Update This Plugin",
167
+ "description": "Regardless of any other settings, automatically update the Shield plugin."
168
+ },
169
+ {
170
+ "key": "enable_upgrade_notification_email",
171
+ "section": "section_options",
172
+ "default": "N",
173
+ "type": "checkbox",
174
+ "link_info": "",
175
+ "link_blog": "",
176
+ "name": "Send Report Email",
177
+ "summary": "Send email notices after automatic updates",
178
+ "description": "You can turn on/off email notices from automatic updates by un/checking this box."
179
+ },
180
+ {
181
+ "key": "override_email_address",
182
+ "section": "section_options",
183
+ "sensitive": true,
184
+ "default": "",
185
+ "type": "email",
186
+ "link_info": "",
187
+ "link_blog": "",
188
+ "name": "Report Email Address",
189
+ "summary": "Where to send upgrade notification reports",
190
+ "description": "If this is empty, it will default to the Site Admin email address"
191
+ },
192
+ {
193
+ "key": "delay_tracking",
194
+ "section": "section_non_ui",
195
+ "transferable": false,
196
+ "sensitive": true,
197
+ "type": "array",
198
+ "default": []
199
+ },
200
+ {
201
+ "key": "selected_plugins",
202
+ "section": "section_non_ui",
203
+ "transferable": false,
204
+ "type": "array",
205
+ "default": []
206
+ }
207
+ ],
208
+ "definitions": {
209
+ "action_hook_priority": 1000
210
+ }
211
+ }
config/deprecated/comments_filter.php ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "comments_filter",
3
+ "properties": {
4
+ "slug": "comments_filter",
5
+ "name": "Comments SPAM",
6
+ "sidebar_name": "SPAM",
7
+ "show_module_menu_item": false,
8
+ "show_module_options": true,
9
+ "storage_key": "commentsfilter",
10
+ "tagline": "Block comment SPAM and retain your privacy",
11
+ "show_central": true,
12
+ "access_restricted": true,
13
+ "premium": false,
14
+ "run_if_whitelisted": false,
15
+ "run_if_verified_bot": false,
16
+ "run_if_wpcli": false,
17
+ "order": 50
18
+ },
19
+ "admin_notices": {
20
+ "akismet-running": {
21
+ "id": "akismet-running",
22
+ "plugin_admin": "yes",
23
+ "plugin_page_only": true,
24
+ "type": "warning"
25
+ }
26
+ },
27
+ "sections": [
28
+ {
29
+ "primary": true,
30
+ "slug": "section_bot_comment_spam_protection_filter",
31
+ "title": "Automatic Bot Comment SPAM Protection Filter",
32
+ "title_short": "Bot SPAM",
33
+ "beacon_id": 260,
34
+ "summary": [
35
+ "Purpose - Blocks 100% of all automated bot-generated comment SPAM.",
36
+ "Recommendation - Use of this feature is highly recommend."
37
+ ]
38
+ },
39
+ {
40
+ "slug": "section_human_spam_filter",
41
+ "title": "Human Comment SPAM Protection Filter",
42
+ "title_short": "Human SPAM",
43
+ "beacon_id": 262,
44
+ "summary": [
45
+ "Purpose - Uses a 3rd party SPAM dictionary to detect human-based comment SPAM.",
46
+ "Recommendation - Use of this feature is highly recommend.This tool, unlike other SPAM tools such as Akismet, will not send your comment data to 3rd party services for analysis."
47
+ ]
48
+ },
49
+ {
50
+ "slug": "section_bot_comment_spam_common",
51
+ "title": "Common Settings For All SPAM Scanning",
52
+ "title_short": "Common Settings",
53
+ "beacon_id": 152,
54
+ "summary": [
55
+ "Purpose - Settings that apply to all comment SPAM scanning."
56
+ ]
57
+ },
58
+ {
59
+ "slug": "section_user_messages",
60
+ "title": "Customize Messages Shown To User",
61
+ "title_short": "Visitor Messages",
62
+ "beacon_id": 403,
63
+ "summary": [
64
+ "Purpose - Customize the messages shown to visitors.",
65
+ "Recommendation - Be sure to change the messages to suit your audience.",
66
+ "Hint - To reset any message to its default, enter the text exactly: default"
67
+ ]
68
+ },
69
+ {
70
+ "slug": "section_enable_plugin_feature_spam_comments_protection_filter",
71
+ "title": "Enable Module: Comments SPAM Protection",
72
+ "title_short": "Disable Module",
73
+ "beacon_id": 257,
74
+ "summary": [
75
+ "Purpose - The Comments Filter can block 100% of automated spam bots and also offer the option to analyse human-generated spam.",
76
+ "Recommendation - Keep the Comments Filter feature turned on."
77
+ ]
78
+ },
79
+ {
80
+ "slug": "section_non_ui",
81
+ "hidden": true
82
+ }
83
+ ],
84
+ "options": [
85
+ {
86
+ "key": "enable_comments_filter",
87
+ "section": "section_enable_plugin_feature_spam_comments_protection_filter",
88
+ "advanced": true,
89
+ "default": "Y",
90
+ "type": "checkbox",
91
+ "link_info": "https://shsec.io/3z",
92
+ "link_blog": "https://shsec.io/wpsf04",
93
+ "beacon_id": 257,
94
+ "name": "Enable SPAM Protection",
95
+ "summary": "Enable (or Disable) The Comments SPAM Protection module",
96
+ "description": "Un-Checking this option will completely disable the Comments SPAM Protection module"
97
+ },
98
+ {
99
+ "key": "trusted_commenter_minimum",
100
+ "section": "section_bot_comment_spam_common",
101
+ "default": 1,
102
+ "min": 1,
103
+ "type": "integer",
104
+ "link_info": "https://shsec.io/fu",
105
+ "link_blog": "",
106
+ "beacon_id": 152,
107
+ "name": "Trusted Commenter Minimum",
108
+ "summary": "Minimum Number Of Approved Comments Before Commenter Is Trusted",
109
+ "description": "Specify how many approved comments must exist before a commenter is trusted and their comments are no longer scanned."
110
+ },
111
+ {
112
+ "key": "trusted_user_roles",
113
+ "section": "section_bot_comment_spam_common",
114
+ "premium": true,
115
+ "default": [
116
+ "administrator",
117
+ "editor",
118
+ "author",
119
+ "contributor",
120
+ "subscriber"
121
+ ],
122
+ "type": "array",
123
+ "link_info": "https://shsec.io/fu",
124
+ "link_blog": "",
125
+ "beacon_id": 152,
126
+ "name": "Trusted Users",
127
+ "summary": "Don't Scan Comments For Users With The Following Roles",
128
+ "description": "Shield doesn't normally scan comments from logged-in or registered users. Specify user roles here that shouldn't be scanned."
129
+ },
130
+ {
131
+ "key": "enable_antibot_check",
132
+ "section": "section_bot_comment_spam_protection_filter",
133
+ "default": "N",
134
+ "type": "checkbox",
135
+ "link_info": "https://shsec.io/k1",
136
+ "link_blog": "https://shsec.io/jo",
137
+ "beacon_id": 427,
138
+ "name": "AntiBot Detection Engine",
139
+ "summary": "Use Experimental AntiBot Detection Engine",
140
+ "description": "Use Shield's AntiBot Detection Engine In-Place of GASP Bot checking."
141
+ },
142
+ {
143
+ "key": "comments_default_action_spam_bot",
144
+ "section": "section_bot_comment_spam_protection_filter",
145
+ "default": "spam",
146
+ "type": "select",
147
+ "value_options": [
148
+ {
149
+ "value_key": "0",
150
+ "text": "Move To Pending Moderation"
151
+ },
152
+ {
153
+ "value_key": "spam",
154
+ "text": "Move To SPAM"
155
+ },
156
+ {
157
+ "value_key": "trash",
158
+ "text": "Move To Trash"
159
+ },
160
+ {
161
+ "value_key": "reject",
162
+ "text": "Block And Redirect"
163
+ }
164
+ ],
165
+ "link_info": "https://shsec.io/6j",
166
+ "link_blog": "",
167
+ "beacon_id": 260,
168
+ "name": "SPAM Action",
169
+ "summary": "How To Categorise Comments When Identified To Be SPAM",
170
+ "description": "When a comment is detected as being SPAM from an automatic bot, the comment will be categorised based on this setting."
171
+ },
172
+ {
173
+ "key": "google_recaptcha_style_comments",
174
+ "section": "section_bot_comment_spam_protection_filter",
175
+ "default": "disabled",
176
+ "type": "select",
177
+ "value_options": [
178
+ {
179
+ "value_key": "disabled",
180
+ "text": "Disabled"
181
+ },
182
+ {
183
+ "value_key": "default",
184
+ "text": "Default Style"
185
+ },
186
+ {
187
+ "value_key": "light",
188
+ "text": "Light Theme"
189
+ },
190
+ {
191
+ "value_key": "dark",
192
+ "text": "Dark Theme"
193
+ },
194
+ {
195
+ "value_key": "invisible",
196
+ "text": "Invisible"
197
+ }
198
+ ],
199
+ "link_info": "https://shsec.io/e4",
200
+ "link_blog": "",
201
+ "beacon_id": 269,
202
+ "name": "CAPTCHA",
203
+ "summary": "Enable CAPTCHA To Protect Against SPAM Comments",
204
+ "description": "You can choose the CAPTCHA display format that best suits your site, including the newer Invisible CAPTCHA."
205
+ },
206
+ {
207
+ "key": "enable_comments_gasp_protection",
208
+ "section": "section_bot_comment_spam_protection_filter",
209
+ "default": "N",
210
+ "type": "checkbox",
211
+ "link_info": "https://shsec.io/3n",
212
+ "link_blog": "https://shsec.io/2n",
213
+ "beacon_id": 401,
214
+ "name": "GASP Protection",
215
+ "summary": "Block Bot Comment SPAM",
216
+ "description": "Taking the lead from the original GASP plugin for WordPress, we have extended it to include advanced spam-bot protection."
217
+ },
218
+ {
219
+ "key": "enable_comments_human_spam_filter",
220
+ "section": "section_human_spam_filter",
221
+ "default": "N",
222
+ "type": "checkbox",
223
+ "link_info": "https://shsec.io/57",
224
+ "link_blog": "https://shsec.io/9w",
225
+ "beacon_id": 262,
226
+ "name": "Human SPAM Filter",
227
+ "summary": "Enable (or Disable) The Human SPAM Filter module",
228
+ "description": "Scans the content of WordPress comments for keywords that are indicative of SPAM and marks the comment according to your preferred setting below."
229
+ },
230
+ {
231
+ "key": "comments_default_action_human_spam",
232
+ "section": "section_human_spam_filter",
233
+ "default": "0",
234
+ "type": "select",
235
+ "value_options": [
236
+ {
237
+ "value_key": "0",
238
+ "text": "Move To Pending Moderation"
239
+ },
240
+ {
241
+ "value_key": "spam",
242
+ "text": "Move To SPAM"
243
+ },
244
+ {
245
+ "value_key": "trash",
246
+ "text": "Move To Trash"
247
+ },
248
+ {
249
+ "value_key": "reject",
250
+ "text": "Block And Redirect"
251
+ }
252
+ ],
253
+ "name": "SPAM Action",
254
+ "summary": "How To Categorise Comments When Identified To Be SPAM'",
255
+ "description": "When a comment is detected as being SPAM from a human commenter, the comment will be categorised based on this setting."
256
+ },
257
+ {
258
+ "key": "custom_message_checkbox",
259
+ "section": "section_user_messages",
260
+ "sensitive": true,
261
+ "default": "default",
262
+ "type": "text",
263
+ "link_info": "https://shsec.io/3p",
264
+ "link_blog": "",
265
+ "beacon_id": 403,
266
+ "name": "Custom Checkbox Message",
267
+ "summary": "If you want a custom checkbox message, please provide this here",
268
+ "description": "You can customise the message beside the checkbox."
269
+ },
270
+ {
271
+ "key": "custom_message_alert",
272
+ "section": "section_user_messages",
273
+ "sensitive": true,
274
+ "default": "default",
275
+ "type": "text",
276
+ "link_info": "https://shsec.io/3p",
277
+ "link_blog": "",
278
+ "beacon_id": 403,
279
+ "name": "Custom Alert Message",
280
+ "summary": "If you want a custom alert message, please provide this here",
281
+ "description": "This alert message is displayed when a visitor attempts to submit a comment without checking the box."
282
+ },
283
+ {
284
+ "key": "custom_message_comment_wait",
285
+ "section": "section_user_messages",
286
+ "sensitive": true,
287
+ "default": "default",
288
+ "type": "text",
289
+ "link_info": "https://shsec.io/3p",
290
+ "link_blog": "",
291
+ "beacon_id": 403,
292
+ "name": "Custom Wait Message",
293
+ "summary": "If you want a custom submit-button wait message, please provide this here.",
294
+ "description": "Where you see the '%s' this will be the number of seconds. You must ensure you include 1, and only 1, of these."
295
+ },
296
+ {
297
+ "key": "custom_message_comment_reload",
298
+ "section": "section_user_messages",
299
+ "sensitive": true,
300
+ "default": "default",
301
+ "type": "text",
302
+ "link_info": "https://shsec.io/3p",
303
+ "link_blog": "",
304
+ "beacon_id": 403,
305
+ "name": "Custom Reload Message",
306
+ "summary": "If you want a custom message when the comment token has expired, please provide this here.",
307
+ "description": "This message is displayed on the submit-button when the comment token is expired."
308
+ },
309
+ {
310
+ "key": "comments_cooldown",
311
+ "section": "section_non_ui",
312
+ "default": 10,
313
+ "min": 0,
314
+ "type": "integer"
315
+ },
316
+ {
317
+ "key": "human_spam_items",
318
+ "section": "section_non_ui",
319
+ "type": "array",
320
+ "default": [
321
+ "author_name",
322
+ "author_email",
323
+ "comment_content",
324
+ "url",
325
+ "ip_address",
326
+ "user_agent"
327
+ ]
328
+ }
329
+ ],
330
+ "definitions": {
331
+ "comments_expire": 1800,
332
+ "url_spam_blacklist_terms": "https://raw.githubusercontent.com/splorp/wordpress-comment-blacklist/master/blacklist.txt",
333
+ "events": {
334
+ "comment_spam_block": {
335
+ "level": "notice",
336
+ "stat": false,
337
+ "offense": true
338
+ },
339
+ "spam_block_antibot": {
340
+ },
341
+ "spam_block_bot": {
342
+ },
343
+ "spam_block_recaptcha": {
344
+ },
345
+ "spam_block_human": {
346
+ "audit_params": [
347
+ "word",
348
+ "key"
349
+ ]
350
+ }
351
+ }
352
+ }
353
+ }
config/deprecated/comms.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "comms",
3
+ "properties": {
4
+ "slug": "comms",
5
+ "storage_key": "comms",
6
+ "name": "Comms",
7
+ "menu_title": "Comms",
8
+ "show_module_options": true,
9
+ "show_module_menu_item": false,
10
+ "auto_enabled": true,
11
+ "show_central": true,
12
+ "premium": false,
13
+ "access_restricted": true,
14
+ "run_if_whitelisted": true,
15
+ "run_if_wpcli": true,
16
+ "run_if_verified_bot": true,
17
+ "skip_processor": false,
18
+ "tracking_exclude": true
19
+ },
20
+ "wpcli": {
21
+ "enabled": true
22
+ },
23
+ "sections": [
24
+ {
25
+ "primary": true,
26
+ "slug": "section_suresend",
27
+ "title": "SureSend Email",
28
+ "title_short": "SureSend Email",
29
+ "beacon_id": 156
30
+ },
31
+ {
32
+ "slug": "section_non_ui",
33
+ "hidden": true
34
+ }
35
+ ],
36
+ "options": [
37
+ {
38
+ "key": "suresend_emails",
39
+ "section": "section_suresend",
40
+ "type": "multiple_select",
41
+ "premium": true,
42
+ "default": [],
43
+ "value_options": [
44
+ {
45
+ "value_key": "2fa",
46
+ "text": "2FA Login Codes (admins only)"
47
+ }
48
+ ],
49
+ "link_info": "https://icwp.io/ij",
50
+ "link_blog": "https://icwp.io/ik",
51
+ "name": "SureSend Emails",
52
+ "summary": "SureSend Emails",
53
+ "description": "SureSend Emails."
54
+ }
55
+ ],
56
+ "definitions": {
57
+ "events": {
58
+ "suresend_success": {
59
+ "audit_params": [
60
+ "email",
61
+ "slug"
62
+ ],
63
+ "level": "info"
64
+ },
65
+ "suresend_fail": {
66
+ "audit_params": [
67
+ "email",
68
+ "slug"
69
+ ],
70
+ "level": "warning"
71
+ }
72
+ }
73
+ }
74
+ }
config/deprecated/data.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "data",
3
+ "properties": {
4
+ "slug": "data",
5
+ "name": "Data",
6
+ "show_module_menu_item": false,
7
+ "auto_enabled": true,
8
+ "storage_key": "data",
9
+ "show_central": false,
10
+ "premium": false,
11
+ "access_restricted": true,
12
+ "run_if_whitelisted": true,
13
+ "run_if_wpcli": true,
14
+ "skip_processor": true,
15
+ "tracking_exclude": true
16
+ },
17
+ "wpcli": {
18
+ "enabled": false
19
+ },
20
+ "sections": [
21
+ ],
22
+ "options": [
23
+ ],
24
+ "definitions": {
25
+ "db_handler_classes": {
26
+ "ips": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Data\\DB\\IPs\\Ops\\Handler",
27
+ "req_logs": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Data\\DB\\ReqLogs\\Ops\\Handler"
28
+ },
29
+ "db_table_ips": {
30
+ "autoexpire": 0,
31
+ "slug": "ips",
32
+ "has_updated_at": false,
33
+ "has_deleted_at": false,
34
+ "col_older_than": "created_at",
35
+ "cols_custom": {
36
+ "ip": {
37
+ "macro_type": "ip",
38
+ "attr": [
39
+ "UNIQUE"
40
+ ]
41
+ },
42
+ "geo": {
43
+ "macro_type": "meta",
44
+ "comment": "GeoIP Data"
45
+ }
46
+ }
47
+ },
48
+ "db_table_req_logs": {
49
+ "slug": "req_logs",
50
+ "autoexpire": 0,
51
+ "has_updated_at": false,
52
+ "has_deleted_at": false,
53
+ "cols_custom": {
54
+ "req_id": {
55
+ "macro_type": "varchar",
56
+ "length": 10,
57
+ "attr": [
58
+ "UNIQUE"
59
+ ]
60
+ },
61
+ "ip_ref": {
62
+ "macro_type": "foreign_key_id",
63
+ "foreign_key": {
64
+ "ref_table": "icwp_wpsf_ips"
65
+ }
66
+ },
67
+ "meta": {
68
+ "macro_type": "meta"
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
src/config/feature-email.php → config/deprecated/email.php RENAMED
@@ -14,7 +14,7 @@
14
  "skip_processor": true,
15
  "tracking_exclude": true
16
  },
17
- "wpcli": {
18
  "enabled": false
19
  },
20
  "sections": [
14
  "skip_processor": true,
15
  "tracking_exclude": true
16
  },
17
+ "wpcli": {
18
  "enabled": false
19
  },
20
  "sections": [
src/config/feature-events.php → config/deprecated/events.php RENAMED
File without changes
src/config/feature-firewall.php → config/deprecated/firewall.php RENAMED
@@ -88,19 +88,6 @@
88
  "summary": "Enable (or Disable) The Firewall module",
89
  "description": "Un-Checking this option will completely disable the Firewall module"
90
  },
91
- {
92
- "key": "include_cookie_checks",
93
- "section": "section_firewall_blocking_options",
94
- "advanced": true,
95
- "default": "N",
96
- "type": "checkbox",
97
- "beacon_id": 333,
98
- "link_info": "",
99
- "link_blog": "",
100
- "name": "Include Cookies",
101
- "summary": "Also Test Cookie Values In Firewall Tests",
102
- "description": "The firewall tests GET and POST, but with this option checked it will also check COOKIE values."
103
- },
104
  {
105
  "key": "block_dir_traversal",
106
  "section": "section_firewall_blocking_options",
@@ -416,45 +403,39 @@
416
  }
417
  },
418
  "events": {
419
- "check_skip": {
420
- "cat": 2,
421
- "stat": false
422
- },
423
- "firewall_block": {
424
- "audit": false,
425
- "recent": true,
426
- "offense": true
427
- },
428
- "blockparam_dirtraversal": {
429
- "cat": 3
430
- },
431
- "blockparam_wpterms": {
432
- "cat": 3
433
- },
434
- "blockparam_fieldtruncation": {
435
- "cat": 3
436
- },
437
- "blockparam_sqlqueries": {
438
- "cat": 3
439
- },
440
- "blockparam_schema": {
441
- "cat": 3
442
- },
443
- "blockparam_aggressive": {
444
- "cat": 3
445
- },
446
- "blockparam_phpcode": {
447
- "cat": 3
448
  },
449
- "block_exefile": {
450
- "cat": 3
 
 
 
 
 
 
 
 
 
 
451
  },
452
- "fw_email_fail": {
453
- "cat": 2,
454
- "stat": false
 
 
 
455
  },
456
- "fw_email_success": {
457
- "stat": false
 
 
 
 
458
  }
459
  }
460
  }
88
  "summary": "Enable (or Disable) The Firewall module",
89
  "description": "Un-Checking this option will completely disable the Firewall module"
90
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  {
92
  "key": "block_dir_traversal",
93
  "section": "section_firewall_blocking_options",
403
  }
404
  },
405
  "events": {
406
+ "check_skip": {
407
+ "audit_params": [
408
+ "path"
409
+ ],
410
+ "level": "debug",
411
+ "stat": false
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  },
413
+ "firewall_block": {
414
+ "audit_params": [
415
+ "name",
416
+ "term",
417
+ "param",
418
+ "value",
419
+ "scan",
420
+ "type"
421
+ ],
422
+ "level": "warning",
423
+ "recent": true,
424
+ "offense": true
425
  },
426
+ "fw_email_fail": {
427
+ "audit_params": [
428
+ "to"
429
+ ],
430
+ "level": "warning",
431
+ "stat": false
432
  },
433
+ "fw_email_success": {
434
+ "audit_params": [
435
+ "to"
436
+ ],
437
+ "level": "debug",
438
+ "stat": false
439
  }
440
  }
441
  }
src/config/feature-hack_protect.php → config/deprecated/hack_protect.php RENAMED
@@ -555,80 +555,39 @@
555
  "xmlrpc.php"
556
  ],
557
  "events": {
558
- "apc_alert_sent": {
559
- },
560
- "mal_alert_sent": {
561
- },
562
- "ptg_alert_sent": {
563
- },
564
- "ufc_alert_sent": {
565
- },
566
- "wcf_alert_sent": {
567
- },
568
- "wpv_alert_sent": {
569
- },
570
- "apc_scan_run": {
571
- "audit": false,
572
- "recent": true
573
- },
574
- "mal_scan_run": {
575
- "audit": false,
576
- "recent": true
577
- },
578
- "ptg_scan_run": {
579
- "audit": false,
580
- "recent": true
581
- },
582
- "ufc_scan_run": {
583
- "audit": false,
584
- "recent": true
585
- },
586
- "wcf_scan_run": {
587
- "audit": false,
588
- "recent": true
589
- },
590
- "wpv_scan_run": {
591
- "audit": false,
592
- "recent": true
593
- },
594
- "apc_scan_found": {
595
- "cat": 2,
596
- "audit_multiple": true,
597
- "recent": true
598
- },
599
- "mal_scan_found": {
600
- "cat": 3,
601
- "audit_multiple": true,
602
- "recent": true
603
- },
604
- "ptg_scan_found": {
605
- "cat": 3,
606
- "audit_multiple": true,
607
- "recent": true
608
- },
609
- "ufc_scan_found": {
610
- "cat": 3,
611
- "audit_multiple": true,
612
- "recent": true
613
- },
614
- "wcf_scan_found": {
615
- "cat": 3,
616
- "audit_multiple": true,
617
- "recent": true
618
  },
619
- "wpv_scan_found": {
620
- "cat": 3,
 
 
 
 
621
  "audit_multiple": true,
622
  "recent": true
623
  },
624
  "scan_item_repair_success": {
 
 
 
625
  "audit_multiple": true,
626
  "recent": true
627
  },
628
  "scan_item_repair_fail": {
 
 
 
629
  "audit_multiple": true
630
  },
631
  "scan_item_delete_success": {
 
 
 
632
  "audit_multiple": true
633
  }
634
  }
555
  "xmlrpc.php"
556
  ],
557
  "events": {
558
+ "scan_run": {
559
+ "audit_params": [
560
+ "scan"
561
+ ],
562
+ "level": "debug",
563
+ "audit_multiple": true
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
564
  },
565
+ "scan_items_found": {
566
+ "audit_params": [
567
+ "scan",
568
+ "items"
569
+ ],
570
+ "level": "alert",
571
  "audit_multiple": true,
572
  "recent": true
573
  },
574
  "scan_item_repair_success": {
575
+ "audit_params": [
576
+ "path_full"
577
+ ],
578
  "audit_multiple": true,
579
  "recent": true
580
  },
581
  "scan_item_repair_fail": {
582
+ "audit_params": [
583
+ "path_full"
584
+ ],
585
  "audit_multiple": true
586
  },
587
  "scan_item_delete_success": {
588
+ "audit_params": [
589
+ "path_full"
590
+ ],
591
  "audit_multiple": true
592
  }
593
  }
src/config/feature-headers.php → config/deprecated/headers.php RENAMED
File without changes
src/config/feature-insights.php → config/deprecated/insights.php RENAMED
@@ -1,6 +1,6 @@
1
  {
2
- "slug": "insights",
3
- "properties": {
4
  "slug": "insights",
5
  "name": "Dashboard",
6
  "menu_title": "Security Dashboard",
@@ -18,15 +18,15 @@
18
  "skip_processor": true,
19
  "tracking_exclude": true
20
  },
21
- "wpcli": {
22
  "enabled": false
23
  },
24
- "sections": [
25
  {
26
  "slug": "section_non_ui",
27
  "hidden": true
28
  }
29
  ],
30
- "options": [
31
  ]
32
  }
1
  {
2
+ "slug": "insights",
3
+ "properties": {
4
  "slug": "insights",
5
  "name": "Dashboard",
6
  "menu_title": "Security Dashboard",
18
  "skip_processor": true,
19
  "tracking_exclude": true
20
  },
21
+ "wpcli": {
22
  "enabled": false
23
  },
24
+ "sections": [
25
  {
26
  "slug": "section_non_ui",
27
  "hidden": true
28
  }
29
  ],
30
+ "options": [
31
  ]
32
  }
src/config/feature-integrations.php → config/deprecated/integrations.php RENAMED
@@ -137,6 +137,10 @@
137
  "wordpress"
138
  ],
139
  "value_options": [
 
 
 
 
140
  {
141
  "value_key": "buddypress",
142
  "text": "BuddyPress"
@@ -193,24 +197,44 @@
193
  "definitions": {
194
  "events": {
195
  "spam_form_pass": {
196
- "stat": true,
197
- "audit": true,
198
- "offense": false
 
 
 
 
199
  },
200
  "spam_form_fail": {
201
- "stat": true,
202
- "audit": true,
203
- "offense": false
 
 
 
 
204
  },
205
  "user_form_bot_pass": {
206
- "stat": true,
207
- "audit": true,
208
- "offense": false
 
 
 
 
 
 
209
  },
210
  "user_form_bot_fail": {
211
- "stat": true,
212
- "audit": true,
213
- "offense": true
 
 
 
 
 
 
214
  }
215
  }
216
  }
137
  "wordpress"
138
  ],
139
  "value_options": [
140
+ {
141
+ "value_key": "buddyboss",
142
+ "text": "BuddyBoss"
143
+ },
144
  {
145
  "value_key": "buddypress",
146
  "text": "BuddyPress"
197
  "definitions": {
198
  "events": {
199
  "spam_form_pass": {
200
+ "audit_params": [
201
+ "form_provider"
202
+ ],
203
+ "level": "info",
204
+ "stat": true,
205
+ "audit": true,
206
+ "offense": false
207
  },
208
  "spam_form_fail": {
209
+ "audit_params": [
210
+ "form_provider"
211
+ ],
212
+ "level": "warning",
213
+ "stat": true,
214
+ "audit": true,
215
+ "offense": false
216
  },
217
  "user_form_bot_pass": {
218
+ "audit_params": [
219
+ "form_provider",
220
+ "action",
221
+ "username"
222
+ ],
223
+ "level": "info",
224
+ "stat": true,
225
+ "audit": true,
226
+ "offense": false
227
  },
228
  "user_form_bot_fail": {
229
+ "audit_params": [
230
+ "form_provider",
231
+ "action",
232
+ "username"
233
+ ],
234
+ "level": "warning",
235
+ "stat": true,
236
+ "audit": true,
237
+ "offense": true
238
  }
239
  }
240
  }
src/config/feature-ips.php → config/deprecated/ips.php RENAMED
@@ -642,90 +642,131 @@
642
  },
643
  "events": {
644
  "custom_offense": {
645
- "cat": 3,
646
- "offense": true
 
 
647
  },
648
  "conn_kill": {
649
- "cat": 3
 
 
 
 
650
  },
651
  "ip_offense": {
652
- "cat": 2
 
 
 
 
653
  },
654
  "ip_blocked": {
655
- "cat": 2
 
 
 
 
656
  },
657
  "ip_unblock": {
 
658
  "offense": false,
659
- "audit": false,
660
  "stat": false
661
  },
662
  "ip_block_auto": {
663
- "offense": false,
664
- "stat": false,
665
- "cat": 1
 
 
 
666
  },
667
  "ip_block_manual": {
668
- "offense": false,
669
- "stat": false,
670
- "cat": 1
 
 
 
671
  },
672
  "ip_bypass_add": {
673
- "offense": false,
674
- "stat": false,
675
- "cat": 1
 
 
 
676
  },
677
  "ip_bypass_remove": {
678
- "offense": false,
679
- "stat": false,
680
- "cat": 1
 
 
 
681
  },
682
  "ip_unblock_flag": {
683
- "cat": 1
 
 
 
684
  },
685
  "bottrack_notbot": {
686
- "cat": 0,
687
  "offense": false,
688
- "audit": false,
689
- "stat": false
690
  },
691
  "bottrack_404": {
692
- "cat": 1,
693
- "offense": true
 
 
694
  },
695
  "bottrack_fakewebcrawler": {
696
- "cat": 2,
697
- "offense": true
 
 
 
698
  },
699
  "bottrack_linkcheese": {
700
- "cat": 2,
701
- "offense": true
 
 
702
  },
703
  "bottrack_loginfailed": {
704
- "cat": 2,
705
- "offense": true
 
 
 
706
  },
707
  "bottrack_logininvalid": {
708
- "cat": 2,
709
- "offense": true
710
- },
711
- "bottrack_useragent": {
712
- "cat": 1,
713
- "offense": true
714
  },
715
  "bottrack_xmlrpc": {
716
- "cat": 2,
717
- "offense": true
 
 
718
  },
719
  "bottrack_invalidscript": {
720
- "cat": 2,
721
- "offense": true
 
 
722
  },
723
  "comment_markspam": {
724
- "cat": 2,
725
  "offense": true
726
  },
727
  "comment_unmarkspam": {
728
- "audit": false,
729
  "offense": false,
730
  "stat": false
731
  }
642
  },
643
  "events": {
644
  "custom_offense": {
645
+ "audit_params": [
646
+ "message"
647
+ ],
648
+ "offense": true
649
  },
650
  "conn_kill": {
651
+ "level": "warning",
652
+ "audit_countable": true
653
+ },
654
+ "conn_not_kill_high_rep": {
655
+ "level": "debug"
656
  },
657
  "ip_offense": {
658
+ "level": "warning",
659
+ "audit_params": [
660
+ "from",
661
+ "to"
662
+ ]
663
  },
664
  "ip_blocked": {
665
+ "audit_params": [
666
+ "from",
667
+ "to"
668
+ ],
669
+ "level": "alert"
670
  },
671
  "ip_unblock": {
672
+ "level": "notice",
673
  "offense": false,
 
674
  "stat": false
675
  },
676
  "ip_block_auto": {
677
+ "audit_params": [
678
+ "ip"
679
+ ],
680
+ "level": "alert",
681
+ "offense": false,
682
+ "stat": false
683
  },
684
  "ip_block_manual": {
685
+ "audit_params": [
686
+ "ip"
687
+ ],
688
+ "level": "alert",
689
+ "offense": false,
690
+ "stat": false
691
  },
692
  "ip_bypass_add": {
693
+ "audit_params": [
694
+ "ip"
695
+ ],
696
+ "level": "alert",
697
+ "offense": false,
698
+ "stat": false
699
  },
700
  "ip_bypass_remove": {
701
+ "audit_params": [
702
+ "ip"
703
+ ],
704
+ "level": "alert",
705
+ "offense": false,
706
+ "stat": false
707
  },
708
  "ip_unblock_flag": {
709
+ "audit_params": [
710
+ "ip"
711
+ ],
712
+ "level": "alert"
713
  },
714
  "bottrack_notbot": {
 
715
  "offense": false,
716
+ "stat": false,
717
+ "level": "debug"
718
  },
719
  "bottrack_404": {
720
+ "audit_params": [
721
+ "path"
722
+ ],
723
+ "offense": true
724
  },
725
  "bottrack_fakewebcrawler": {
726
+ "audit_params": [
727
+ "path",
728
+ "crawler"
729
+ ],
730
+ "offense": true
731
  },
732
  "bottrack_linkcheese": {
733
+ "audit_params": [
734
+ "path"
735
+ ],
736
+ "offense": true
737
  },
738
  "bottrack_loginfailed": {
739
+ "audit_params": [
740
+ "user_login"
741
+ ],
742
+ "level": "alert",
743
+ "offense": true
744
  },
745
  "bottrack_logininvalid": {
746
+ "audit_params": [
747
+ "user_login"
748
+ ],
749
+ "level": "alert",
750
+ "offense": true
 
751
  },
752
  "bottrack_xmlrpc": {
753
+ "audit_params": [
754
+ "path"
755
+ ],
756
+ "offense": true
757
  },
758
  "bottrack_invalidscript": {
759
+ "audit_params": [
760
+ "script"
761
+ ],
762
+ "offense": true
763
  },
764
  "comment_markspam": {
765
+ "level": "notice",
766
  "offense": true
767
  },
768
  "comment_unmarkspam": {
769
+ "level": "info",
770
  "offense": false,
771
  "stat": false
772
  }
src/config/feature-license.php → config/deprecated/license.php RENAMED
@@ -151,14 +151,16 @@
151
  "keyless_handshake_expire": 90,
152
  "events": {
153
  "lic_check_success": {
154
- "stat": false
 
155
  },
156
  "lic_fail_email": {
157
- "stat": false
 
158
  },
159
  "lic_fail_deactivate": {
160
- "cat": 2,
161
- "stat": false
162
  }
163
  }
164
  }
151
  "keyless_handshake_expire": 90,
152
  "events": {
153
  "lic_check_success": {
154
+ "level": "debug",
155
+ "stat": false
156
  },
157
  "lic_fail_email": {
158
+ "level": "warning",
159
+ "stat": false
160
  },
161
  "lic_fail_deactivate": {
162
+ "level": "warning",
163
+ "stat": false
164
  }
165
  }
166
  }
src/config/feature-lockdown.php → config/deprecated/lockdown.php RENAMED
@@ -184,10 +184,14 @@
184
  ],
185
  "events": {
186
  "block_anonymous_restapi": {
187
- "recent": true
 
 
 
 
188
  },
189
  "block_xml": {
190
- "audit": false,
191
  "recent": true,
192
  "offense": true
193
  }
184
  ],
185
  "events": {
186
  "block_anonymous_restapi": {
187
+ "audit_params": [
188
+ "namespace"
189
+ ],
190
+ "level": "warning",
191
+ "recent": true
192
  },
193
  "block_xml": {
194
+ "level": "notice",
195
  "recent": true,
196
  "offense": true
197
  }
src/config/feature-login_protect.php → config/deprecated/login_protect.php RENAMED
@@ -502,46 +502,48 @@
502
  "definitions": {
503
  "login_intent_timeout": 5,
504
  "events": {
505
- "2fa_backupcode_verified": {
 
 
 
 
 
506
  },
507
- "2fa_backupcode_fail": {
508
- "offense": true
509
- },
510
- "2fa_email_verified": {
511
- },
512
- "2fa_email_verify_fail": {
513
- "offense": true
514
- },
515
- "2fa_googleauth_verified": {
516
- },
517
- "2fa_google_fail": {
518
- "offense": true
519
- },
520
- "2fa_yubikey_verified": {
521
- },
522
- "2fa_yubikey_fail": {
523
- "offense": true
524
- },
525
- "2fa_email_send_success": {
526
- },
527
- "2fa_email_send_fail": {
528
  },
529
- "cooldown_fail": {
 
530
  },
531
- "honeypot_fail": {
 
 
 
 
 
532
  },
533
- "botbox_fail": {
 
 
 
 
 
534
  },
535
- "login_block": {
536
- "audit": false,
537
  "recent": true,
538
  "offense": true
539
  },
540
- "hide_login_url": {
541
- "audit": false
542
  },
543
- "2fa_success": {
544
- "audit": false,
545
  "recent": true
546
  }
547
  }
502
  "definitions": {
503
  "login_intent_timeout": 5,
504
  "events": {
505
+ "2fa_verify_success": {
506
+ "audit_params": [
507
+ "user_login",
508
+ "method"
509
+ ],
510
+ "level": "notice"
511
  },
512
+ "2fa_verify_fail": {
513
+ "audit_params": [
514
+ "user_login",
515
+ "method"
516
+ ],
517
+ "level": "warning",
518
+ "offense": true
 
 
 
 
 
 
 
 
 
 
 
 
 
 
519
  },
520
+ "cooldown_fail": {
521
+ "level": "warning"
522
  },
523
+ "honeypot_fail": {
524
+ "audit_params": [
525
+ "user_login",
526
+ "action"
527
+ ],
528
+ "level": "warning"
529
  },
530
+ "botbox_fail": {
531
+ "audit_params": [
532
+ "user_login",
533
+ "action"
534
+ ],
535
+ "level": "warning"
536
  },
537
+ "login_block": {
538
+ "level": "warning",
539
  "recent": true,
540
  "offense": true
541
  },
542
+ "hide_login_url": {
543
+ "level": "notice"
544
  },
545
+ "2fa_success": {
546
+ "level": "info",
547
  "recent": true
548
  }
549
  }
src/config/feature-plugin.php → config/deprecated/plugin.php RENAMED
@@ -50,14 +50,6 @@
50
  "can_dismiss": false,
51
  "type": "error"
52
  },
53
- "php7": {
54
- "id": "php7",
55
- "schedule": "conditions",
56
- "valid_admin": true,
57
- "plugin_page_only": false,
58
- "can_dismiss": true,
59
- "type": "warning"
60
- },
61
  "compat-sgoptimize": {
62
  "id": "compat-sgoptimize",
63
  "schedule": "conditions",
@@ -553,7 +545,6 @@
553
  "definitions": {
554
  "href_privacy_policy": "https://shsec.io/wpshieldprivacypolicy",
555
  "db_classes": {
556
- "geoip": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\GeoIp\\Handler",
557
  "notes": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\AdminNotes\\Handler"
558
  },
559
  "db_table_notes": {
@@ -564,18 +555,14 @@
564
  "note": "TEXT"
565
  }
566
  },
567
- "db_table_geoip": {
568
- "autoexpire": 30,
569
- "slug": "geoip",
570
- "cols_custom": {
571
- "ip": "varbinary(16) DEFAULT NULL COMMENT 'IP Address'",
572
- "meta": "TEXT"
573
- }
574
- },
575
  "active_plugin_features": [
 
 
 
 
576
  {
577
  "slug": "insights",
578
- "load_priority": 1,
579
  "menu_priority": 5
580
  },
581
  {
@@ -654,56 +641,97 @@
654
  }
655
  ],
656
  "events": {
 
 
 
 
 
 
 
657
  "test_cron_run": {
658
- "audit": false,
659
  "recent": true
660
  },
661
  "import_notify_sent": {
662
- "stat": false
 
663
  },
664
  "import_notify_received": {
665
- "stat": false
 
 
 
 
666
  },
667
  "options_exported": {
668
- "stat": true,
669
- "recent": true
 
 
 
 
670
  },
671
  "options_imported": {
672
- "stat": true,
673
- "recent": true
 
 
 
 
674
  },
675
  "whitelist_site_added": {
676
- "stat": false
 
 
 
 
677
  },
678
  "whitelist_site_removed": {
679
- "stat": false
 
 
 
 
680
  },
681
  "master_url_set": {
682
- "stat": false
 
 
 
 
683
  },
684
  "recaptcha_success": {
685
- "audit": false
686
  },
687
  "recaptcha_fail": {
 
688
  "audit": true
689
  },
690
  "antibot_pass": {
691
- "stat": true,
692
- "audit": true
 
 
 
 
693
  },
694
  "antibot_fail": {
695
- "stat": true,
696
- "audit": true
 
 
 
 
697
  },
698
  "frontpage_load": {
 
699
  "offense": false,
700
- "stat": false,
701
- "audit": false
702
  },
703
  "loginpage_load": {
 
704
  "offense": false,
705
- "stat": false,
706
- "audit": false
707
  }
708
  },
709
  "wizards": {
50
  "can_dismiss": false,
51
  "type": "error"
52
  },
 
 
 
 
 
 
 
 
53
  "compat-sgoptimize": {
54
  "id": "compat-sgoptimize",
55
  "schedule": "conditions",
545
  "definitions": {
546
  "href_privacy_policy": "https://shsec.io/wpshieldprivacypolicy",
547
  "db_classes": {
 
548
  "notes": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\AdminNotes\\Handler"
549
  },
550
  "db_table_notes": {
555
  "note": "TEXT"
556
  }
557
  },
 
 
 
 
 
 
 
 
558
  "active_plugin_features": [
559
+ {
560
+ "slug": "data",
561
+ "load_priority": 1
562
+ },
563
  {
564
  "slug": "insights",
565
+ "load_priority": 2,
566
  "menu_priority": 5
567
  },
568
  {
641
  }
642
  ],
643
  "events": {
644
+ "debug_log": {
645
+ "audit_params": [
646
+ "message"
647
+ ],
648
+ "level": "debug",
649
+ "stat": false
650
+ },
651
  "test_cron_run": {
652
+ "level": "debug",
653
  "recent": true
654
  },
655
  "import_notify_sent": {
656
+ "level": "debug",
657
+ "stat": false
658
  },
659
  "import_notify_received": {
660
+ "audit_params": [
661
+ "master_site"
662
+ ],
663
+ "level": "notice",
664
+ "stat": false
665
  },
666
  "options_exported": {
667
+ "audit_params": [
668
+ "site"
669
+ ],
670
+ "level": "notice",
671
+ "stat": true,
672
+ "recent": true
673
  },
674
  "options_imported": {
675
+ "audit_params": [
676
+ "site"
677
+ ],
678
+ "level": "notice",
679
+ "stat": true,
680
+ "recent": true
681
  },
682
  "whitelist_site_added": {
683
+ "audit_params": [
684
+ "site"
685
+ ],
686
+ "level": "warning",
687
+ "stat": false
688
  },
689
  "whitelist_site_removed": {
690
+ "audit_params": [
691
+ "site"
692
+ ],
693
+ "level": "notice",
694
+ "stat": false
695
  },
696
  "master_url_set": {
697
+ "audit_params": [
698
+ "site"
699
+ ],
700
+ "level": "warning",
701
+ "stat": false
702
  },
703
  "recaptcha_success": {
704
+ "level": "debug"
705
  },
706
  "recaptcha_fail": {
707
+ "level": "warning",
708
  "audit": true
709
  },
710
  "antibot_pass": {
711
+ "audit_params": [
712
+ "score",
713
+ "minimum"
714
+ ],
715
+ "level": "info",
716
+ "stat": true
717
  },
718
  "antibot_fail": {
719
+ "audit_params": [
720
+ "score",
721
+ "minimum"
722
+ ],
723
+ "level": "warning",
724
+ "stat": true
725
  },
726
  "frontpage_load": {
727
+ "level": "debug",
728
  "offense": false,
729
+ "stat": false
 
730
  },
731
  "loginpage_load": {
732
+ "level": "debug",
733
  "offense": false,
734
+ "stat": false
 
735
  }
736
  },
737
  "wizards": {
src/config/feature-reporting.php → config/deprecated/reporting.php RENAMED
@@ -154,6 +154,21 @@
154
  "interval_end_at": "Reporting Interval End",
155
  "sent_at": "Report Sent"
156
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  }
158
  }
159
  }
154
  "interval_end_at": "Reporting Interval End",
155
  "sent_at": "Report Sent"
156
  }
157
+ },
158
+ "events": {
159
+ "report_generated": {
160
+ "audit_params": [
161
+ "type",
162
+ "interval"
163
+ ],
164
+ "level": "debug"
165
+ },
166
+ "report_sent": {
167
+ "audit_params": [
168
+ "medium"
169
+ ],
170
+ "level": "debug"
171
+ }
172
  }
173
  }
174
  }
src/config/feature-sessions.php → config/deprecated/sessions.php RENAMED
@@ -69,22 +69,33 @@
69
  "last_activity_uri": "text NOT NULL DEFAULT ''"
70
  },
71
  "cols_timestamps": {
72
- "logged_in_at": "Session Started",
73
- "last_activity_at": "Last Seen At",
74
- "secadmin_at": "Security Admin Authenticated"
75
  }
76
  },
77
  "events": {
78
- "session_start": {
79
- "audit": false
 
 
 
 
80
  },
81
- "session_terminate": {
82
- "audit": false,
83
- "recent": true
84
  },
85
- "login_success": {
 
 
 
 
 
 
 
 
 
86
  "offense": false,
87
- "audit": false,
88
  "stat": false
89
  }
90
  }
69
  "last_activity_uri": "text NOT NULL DEFAULT ''"
70
  },
71
  "cols_timestamps": {
72
+ "logged_in_at": "Session Started",
73
+ "last_activity_at": "Last Seen At",
74
+ "secadmin_at": "Security Admin Authenticated"
75
  }
76
  },
77
  "events": {
78
+ "session_start": {
79
+ "audit_params": [
80
+ "user_login",
81
+ "session_id"
82
+ ],
83
+ "level": "info"
84
  },
85
+ "session_terminate": {
86
+ "level": "info"
 
87
  },
88
+ "session_terminate_current": {
89
+ "audit_params": [
90
+ "user_login",
91
+ "session_id"
92
+ ],
93
+ "level": "info",
94
+ "recent": true
95
+ },
96
+ "login_success": {
97
+ "level": "info",
98
  "offense": false,
 
99
  "stat": false
100
  }
101
  }
src/config/feature-traffic.php → config/deprecated/traffic.php RENAMED
@@ -101,6 +101,7 @@
101
  "advanced": true,
102
  "default": [
103
  "logged_in",
 
104
  "cron",
105
  "search",
106
  "uptime"
@@ -126,6 +127,10 @@
126
  "value_key": "cron",
127
  "text": "WP CRON"
128
  },
 
 
 
 
129
  {
130
  "value_key": "search",
131
  "text": "Search Engines"
@@ -160,7 +165,7 @@
160
  "key": "auto_clean",
161
  "section": "section_traffic_options",
162
  "advanced": true,
163
- "default": 3,
164
  "min": 1,
165
  "type": "integer",
166
  "link_info": "",
@@ -169,20 +174,6 @@
169
  "summary": "Enable Traffic Log Auto Expiry",
170
  "description": "Automated DB cleanup will delete logs older than this maximum value (in days)."
171
  },
172
- {
173
- "key": "max_entries",
174
- "section": "section_traffic_options",
175
- "advanced": true,
176
- "premium": true,
177
- "default": 1000,
178
- "min": 0,
179
- "type": "integer",
180
- "link_info": "",
181
- "link_blog": "",
182
- "name": "Max Log Length",
183
- "summary": "Maximum Traffic Log Length To Keep",
184
- "description": "Automated DB cleanup will delete logs to maintain this maximum number of records."
185
- },
186
  {
187
  "key": "enable_limiter",
188
  "section": "section_traffic_limiter",
@@ -219,6 +210,13 @@
219
  "name": "Request Limit Time Interval",
220
  "summary": "The Time Interval To Test For Excessive Requests",
221
  "description": "The time limit within which to monitor for excessive requests that exceed the limit."
 
 
 
 
 
 
 
222
  }
223
  ],
224
  "definitions": {
@@ -241,8 +239,13 @@
241
  "traffic_table_name": "traffic",
242
  "events": {
243
  "request_limit_exceeded": {
244
- "cat": 3,
245
- "offense": true
 
 
 
 
 
246
  }
247
  }
248
  }
101
  "advanced": true,
102
  "default": [
103
  "logged_in",
104
+ "server",
105
  "cron",
106
  "search",
107
  "uptime"
127
  "value_key": "cron",
128
  "text": "WP CRON"
129
  },
130
+ {
131
+ "value_key": "server",
132
+ "text": "This Server"
133
+ },
134
  {
135
  "value_key": "search",
136
  "text": "Search Engines"
165
  "key": "auto_clean",
166
  "section": "section_traffic_options",
167
  "advanced": true,
168
+ "default": 7,
169
  "min": 1,
170
  "type": "integer",
171
  "link_info": "",
174
  "summary": "Enable Traffic Log Auto Expiry",
175
  "description": "Automated DB cleanup will delete logs older than this maximum value (in days)."
176
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  {
178
  "key": "enable_limiter",
179
  "section": "section_traffic_limiter",
210
  "name": "Request Limit Time Interval",
211
  "summary": "The Time Interval To Test For Excessive Requests",
212
  "description": "The time limit within which to monitor for excessive requests that exceed the limit."
213
+ },
214
+ {
215
+ "key": "legacy_db_deleted_at",
216
+ "section": "section_non_ui",
217
+ "transferable": false,
218
+ "type": "text",
219
+ "default": 0
220
  }
221
  ],
222
  "definitions": {
239
  "traffic_table_name": "traffic",
240
  "events": {
241
  "request_limit_exceeded": {
242
+ "audit_params": [
243
+ "requests",
244
+ "count",
245
+ "span"
246
+ ],
247
+ "level": "alert",
248
+ "offense": true
249
  }
250
  }
251
  }
src/config/feature-user_management.php → config/deprecated/user_management.php RENAMED
@@ -412,32 +412,61 @@
412
  "pwned_api_url_password_range": "https://api.pwnedpasswords.com/range/",
413
  "events": {
414
  "session_notfound": {
 
 
 
415
  },
416
  "session_expired": {
 
 
 
417
  },
418
  "session_idle": {
 
 
 
419
  },
420
  "session_iplock": {
421
- },
422
- "session_browserlock": {
423
- },
424
- "session_unverified": {
425
  },
426
  "password_expired": {
 
 
 
 
427
  },
428
  "password_policy_force_change": {
429
- "recent": true
 
 
 
430
  },
431
  "password_policy_block": {
432
- "recent": true
433
  },
434
  "user_hard_suspended": {
435
- "recent": true
 
 
 
 
436
  },
437
  "user_hard_unsuspended": {
 
 
 
 
 
438
  },
439
  "reg_email_invalid": {
440
- "offense": true
 
 
 
 
 
441
  }
442
  }
443
  }
412
  "pwned_api_url_password_range": "https://api.pwnedpasswords.com/range/",
413
  "events": {
414
  "session_notfound": {
415
+ "audit_params": [
416
+ "user_login"
417
+ ]
418
  },
419
  "session_expired": {
420
+ "audit_params": [
421
+ "user_login"
422
+ ]
423
  },
424
  "session_idle": {
425
+ "audit_params": [
426
+ "user_login"
427
+ ]
428
  },
429
  "session_iplock": {
430
+ "audit_params": [
431
+ "user_login"
432
+ ]
 
433
  },
434
  "password_expired": {
435
+ "audit_params": [
436
+ "user_login"
437
+ ],
438
+ "level": "notice"
439
  },
440
  "password_policy_force_change": {
441
+ "audit_params": [
442
+ "user_login"
443
+ ],
444
+ "level": "notice"
445
  },
446
  "password_policy_block": {
447
+ "level": "notice"
448
  },
449
  "user_hard_suspended": {
450
+ "audit_params": [
451
+ "user_login",
452
+ "admin"
453
+ ],
454
+ "level": "warning"
455
  },
456
  "user_hard_unsuspended": {
457
+ "audit_params": [
458
+ "user_login",
459
+ "admin"
460
+ ],
461
+ "level": "warning"
462
  },
463
  "reg_email_invalid": {
464
+ "audit_params": [
465
+ "email",
466
+ "reason"
467
+ ],
468
+ "level": "warning",
469
+ "offense": true
470
  }
471
  }
472
  }
config/email.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "email",
3
+ "properties": {
4
+ "slug": "email",
5
+ "name": "Email",
6
+ "show_module_menu_item": false,
7
+ "auto_enabled": true,
8
+ "storage_key": "email",
9
+ "show_central": false,
10
+ "premium": false,
11
+ "access_restricted": true,
12
+ "run_if_whitelisted": true,
13
+ "run_if_wpcli": true,
14
+ "skip_processor": true,
15
+ "tracking_exclude": true
16
+ },
17
+ "wpcli": {
18
+ "enabled": false
19
+ },
20
+ "sections": [
21
+ ],
22
+ "options": [
23
+ ]
24
+ }
config/events.json ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "properties": {
3
+ "slug": "events",
4
+ "name": "Events",
5
+ "show_module_menu_item": false,
6
+ "storage_key": "events",
7
+ "tagline": "Collection of plugin events and stats",
8
+ "show_central": false,
9
+ "premium": false,
10
+ "access_restricted": true,
11
+ "run_if_whitelisted": true,
12
+ "run_if_verified_bot": true,
13
+ "run_if_wpcli": true,
14
+ "tracking_exclude": true
15
+ },
16
+ "wpcli": {
17
+ "enabled": false
18
+ },
19
+ "sections": [
20
+ {
21
+ "slug": "section_enable_plugin_feature_events",
22
+ "primary": true,
23
+ "title": "Enable Module: Events",
24
+ "title_short": "Disable Module",
25
+ "summary": [
26
+ "Purpose - Helps you see at a glance how effective the plugin has been.",
27
+ "Recommendation - Keep the Events feature turned on."
28
+ ]
29
+ },
30
+ {
31
+ "slug": "section_non_ui",
32
+ "hidden": true
33
+ }
34
+ ],
35
+ "options": [
36
+ {
37
+ "key": "enable_events",
38
+ "section": "section_enable_plugin_feature_events",
39
+ "default": "Y",
40
+ "type": "checkbox",
41
+ "link_info": "",
42
+ "link_blog": "",
43
+ "name": "Enable Events",
44
+ "summary": "Enable (or Disable) The Events module",
45
+ "description": "Un-Checking this option will completely disable the Events module"
46
+ }
47
+ ],
48
+ "definitions": {
49
+ "db_classes": {
50
+ "events": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\Handler"
51
+ },
52
+ "db_table_events": {
53
+ "slug": "events",
54
+ "cols_custom": {
55
+ "event": "varchar(50) NOT NULL DEFAULT 'none' COMMENT 'Event ID'",
56
+ "count": "int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Total'"
57
+ }
58
+ },
59
+ "events_table_name": "events"
60
+ }
61
+ }
config/firewall.json ADDED
@@ -0,0 +1,442 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "firewall",
3
+ "properties": {
4
+ "slug": "firewall",
5
+ "name": "Firewall",
6
+ "sidebar_name": "Firewall",
7
+ "show_module_menu_item": false,
8
+ "show_module_options": true,
9
+ "storage_key": "firewall",
10
+ "tagline": "Automatically block malicious URLs and data sent to your site",
11
+ "show_central": true,
12
+ "access_restricted": true,
13
+ "premium": false,
14
+ "run_if_whitelisted": false,
15
+ "run_if_verified_bot": false,
16
+ "run_if_wpcli": false,
17
+ "order": 30
18
+ },
19
+ "sections": [
20
+ {
21
+ "slug": "section_firewall_blocking_options",
22
+ "primary": true,
23
+ "title": "Firewall Blocking Options",
24
+ "title_short": "Firewall Blocking",
25
+ "beacon_id": 333,
26
+ "summary": [
27
+ "Here you choose what kind of malicious data to scan for.",
28
+ "Recommendation - Turn on as many options here as you can. If you find an incompatibility or something stops working, un-check 1 option at a time until you find the problem or review the Audit Trail."
29
+ ]
30
+ },
31
+ {
32
+ "slug": "section_choose_firewall_block_response",
33
+ "title": "Choose Firewall Block Response",
34
+ "title_short": "Firewall Response",
35
+ "beacon_id": 334,
36
+ "summary": [
37
+ "Here you choose how the plugin will respond when it detects malicious data.",
38
+ "Recommendation - Choose the option 'Die With Message'."
39
+ ]
40
+ },
41
+ {
42
+ "slug": "section_whitelist",
43
+ "title": "Whitelists - IPs, Pages, Parameters, and Users that bypass the Firewall",
44
+ "title_short": "Whitelist",
45
+ "beacon_id": 335,
46
+ "summary": [
47
+ "In principle you should not need to whitelist anything or anyone unless you have discovered a collision with another plugin.",
48
+ "Recommendation - Do not whitelist anything unless you are confident in what you are doing."
49
+ ]
50
+ },
51
+ {
52
+ "slug": "section_user_messages",
53
+ "title": "Customize Messages Shown To User",
54
+ "title_short": "Visitor Messages",
55
+ "beacon_id": 139,
56
+ "summary": [
57
+ "Purpose - Customize the messages shown to visitors.",
58
+ "Recommendation - Be sure to change the messages to suit your audience.",
59
+ "Hint - To reset any message to its default, enter the text exactly: default"
60
+ ]
61
+ },
62
+ {
63
+ "slug": "section_enable_plugin_feature_wordpress_firewall",
64
+ "title": "Enable Module: Firewall",
65
+ "title_short": "Disable Module",
66
+ "beacon_id": 253,
67
+ "summary": [
68
+ "Purpose - The Firewall is designed to analyse data sent to your website and block any requests that appear to be malicious.",
69
+ "Recommendation - Keep the Firewall feature turned on."
70
+ ]
71
+ },
72
+ {
73
+ "slug": "section_non_ui",
74
+ "hidden": true
75
+ }
76
+ ],
77
+ "options": [
78
+ {
79
+ "key": "enable_firewall",
80
+ "section": "section_enable_plugin_feature_wordpress_firewall",
81
+ "advanced": true,
82
+ "default": "Y",
83
+ "type": "checkbox",
84
+ "link_info": "https://shsec.io/43",
85
+ "link_blog": "https://shsec.io/wpsf01",
86
+ "beacon_id": 253,
87
+ "name": "Enable Firewall",
88
+ "summary": "Enable (or Disable) The Firewall module",
89
+ "description": "Un-Checking this option will completely disable the Firewall module"
90
+ },
91
+ {
92
+ "key": "block_dir_traversal",
93
+ "section": "section_firewall_blocking_options",
94
+ "default": "Y",
95
+ "type": "checkbox",
96
+ "link_info": "",
97
+ "link_blog": "",
98
+ "name": "Directory Traversals",
99
+ "summary": "Block Directory Traversals",
100
+ "description": "This will block directory traversal paths in in application parameters."
101
+ },
102
+ {
103
+ "key": "block_sql_queries",
104
+ "section": "section_firewall_blocking_options",
105
+ "default": "Y",
106
+ "type": "checkbox",
107
+ "link_info": "",
108
+ "link_blog": "",
109
+ "name": "SQL Queries",
110
+ "summary": "Block SQL Queries",
111
+ "description": "This will block SQL in application parameters."
112
+ },
113
+ {
114
+ "key": "block_wordpress_terms",
115
+ "section": "section_firewall_blocking_options",
116
+ "default": "N",
117
+ "type": "checkbox",
118
+ "link_info": "",
119
+ "link_blog": "",
120
+ "name": "WordPress Terms",
121
+ "summary": "Block WordPress Specific Terms",
122
+ "description": "This will block WordPress specific terms in application parameters (wp_, user_login, etc.)."
123
+ },
124
+ {
125
+ "key": "block_field_truncation",
126
+ "section": "section_firewall_blocking_options",
127
+ "default": "Y",
128
+ "type": "checkbox",
129
+ "link_info": "",
130
+ "link_blog": "",
131
+ "name": "Field Truncation",
132
+ "summary": "Block Field Truncation Attacks",
133
+ "description": "This will block field truncation attacks in application parameters"
134
+ },
135
+ {
136
+ "key": "block_php_code",
137
+ "section": "section_firewall_blocking_options",
138
+ "default": "N",
139
+ "type": "checkbox",
140
+ "link_info": "",
141
+ "link_blog": "",
142
+ "name": "PHP Code",
143
+ "summary": "Block PHP Code Includes",
144
+ "description": "This will block any data that appears to try and include PHP files. Will probably block saving within the Plugin/Theme file editors."
145
+ },
146
+ {
147
+ "key": "block_exe_file_uploads",
148
+ "section": "section_firewall_blocking_options",
149
+ "default": "N",
150
+ "type": "checkbox",
151
+ "link_info": "",
152
+ "link_blog": "",
153
+ "name": "Exe File Uploads",
154
+ "summary": "Block Executable File Uploads",
155
+ "description": "This will block executable file uploads (.php, .exe, etc.)."
156
+ },
157
+ {
158
+ "key": "block_leading_schema",
159
+ "section": "section_firewall_blocking_options",
160
+ "advanced": true,
161
+ "default": "N",
162
+ "type": "checkbox",
163
+ "link_info": "",
164
+ "link_blog": "",
165
+ "name": "Leading Schemas",
166
+ "summary": "Block Leading Schemas (HTTPS / HTTP)",
167
+ "description": "This will block leading schemas http:// and https:// in application parameters (off by default; may cause problems with other plugins)."
168
+ },
169
+ {
170
+ "key": "block_aggressive",
171
+ "section": "section_firewall_blocking_options",
172
+ "advanced": true,
173
+ "default": "N",
174
+ "type": "checkbox",
175
+ "link_info": "",
176
+ "link_blog": "",
177
+ "name": "Aggressive Scan",
178
+ "summary": "Aggressively Block Data",
179
+ "description": "Employs a set of aggressive rules to detect and block malicious data submitted to your site. Warning - May cause an increase in false-positive firewall blocks."
180
+ },
181
+ {
182
+ "key": "block_response",
183
+ "section": "section_choose_firewall_block_response",
184
+ "advanced": true,
185
+ "default": "redirect_die_message",
186
+ "type": "select",
187
+ "value_options": [
188
+ {
189
+ "value_key": "redirect_die_message",
190
+ "text": "Die With Message"
191
+ },
192
+ {
193
+ "value_key": "redirect_die",
194
+ "text": "Die"
195
+ },
196
+ {
197
+ "value_key": "redirect_home",
198
+ "text": "Redirect To Home Page"
199
+ },
200
+ {
201
+ "value_key": "redirect_404",
202
+ "text": "Return 404"
203
+ }
204
+ ],
205
+ "beacon_id": 334,
206
+ "link_info": "",
207
+ "link_blog": "",
208
+ "name": "Block Response",
209
+ "summary": "How the firewall responds when it blocks a request",
210
+ "description": "We recommend dying with a message so you know what might have occurred when the firewall blocks you."
211
+ },
212
+ {
213
+ "key": "block_send_email",
214
+ "section": "section_choose_firewall_block_response",
215
+ "default": "N",
216
+ "type": "checkbox",
217
+ "link_info": "",
218
+ "link_blog": "",
219
+ "name": "Send Email Report",
220
+ "summary": "When a visitor is blocked the firewall will send an email to the configured email address",
221
+ "description": "Use with caution - if you get hit by automated bots you may send out too many emails and you could get blocked by your host."
222
+ },
223
+ {
224
+ "key": "page_params_whitelist",
225
+ "section": "section_whitelist",
226
+ "advanced": true,
227
+ "default": "",
228
+ "type": "comma_separated_lists",
229
+ "link_info": "https://shsec.io/2a",
230
+ "link_blog": "",
231
+ "beacon_id": 335,
232
+ "name": "Whitelist Parameters",
233
+ "summary": "Detail pages and parameters that are whitelisted (ignored by the firewall)",
234
+ "description": "This should be used with caution and you should only provide parameter names that you must have excluded"
235
+ },
236
+ {
237
+ "key": "whitelist_admins",
238
+ "section": "section_whitelist",
239
+ "advanced": true,
240
+ "default": "N",
241
+ "type": "checkbox",
242
+ "link_info": "",
243
+ "link_blog": "",
244
+ "name": "Ignore Administrators",
245
+ "summary": "Ignore Administrators",
246
+ "description": "Authenticated administrator users will not be processed by the firewall rules."
247
+ },
248
+ {
249
+ "key": "text_firewalldie",
250
+ "section": "section_user_messages",
251
+ "sensitive": true,
252
+ "premium": true,
253
+ "default": "default",
254
+ "type": "text",
255
+ "beacon_id": 139,
256
+ "link_info": "",
257
+ "link_blog": "",
258
+ "name": "Firewall Block Message",
259
+ "summary": "Message Displayed To Visitor When A Firewall Block Is Triggered",
260
+ "description": "When you select the option to display a message to the visitor, this is the message that is displayed."
261
+ }
262
+ ],
263
+ "definitions": {
264
+ "default_whitelist": {
265
+ "/wp-admin/options-general.php": [],
266
+ "/wp-admin/options.php": [
267
+ "home",
268
+ "siteurl"
269
+ ],
270
+ "/wp-admin/plugins.php": [
271
+ "plugin"
272
+ ],
273
+ "/wp-admin/post-new.php": [],
274
+ "/wp-admin/page-new.php": [],
275
+ "/wp-admin/link-add.php": [],
276
+ "/wp-admin/media-upload.php": [],
277
+ "/wp-admin/admin.php": [
278
+ "page"
279
+ ],
280
+ "/wp-admin/post.php": [
281
+ "content"
282
+ ],
283
+ "/wp-admin/plugin-editor.php": [
284
+ "newcontent"
285
+ ],
286
+ "/wp-admin/page.php": [],
287
+ "/wp-admin/admin-ajax.php": [],
288
+ "/wp-comments-post.php": [
289
+ "url",
290
+ "comment"
291
+ ],
292
+ "*": [
293
+ "affwp_action",
294
+ "ajaxurl",
295
+ "g-recaptcha-response",
296
+ "verify_sign",
297
+ "txn_id",
298
+ "wp_http_referer",
299
+ "_wp_http_referer",
300
+ "_wp_original_http_referer",
301
+ "JCS_INENREF",
302
+ "pass1",
303
+ "pass1-text",
304
+ "pwd",
305
+ "url",
306
+ "referredby",
307
+ "redirect_to",
308
+ "jetpack_sso_original_request",
309
+ "jetpack_sso_redirect_to",
310
+ "/^wordpress_logged_in_[0-9a-f]+$/",
311
+ "edd_action",
312
+ "edd_redirect",
313
+ "wpcf7-form",
314
+ "yoast_wpseo_metadesc",
315
+ "icwp_wpsf_new_u2f_response",
316
+ "icwp_wpsf_u2f_otp",
317
+ "appId",
318
+ "/^et_.*/",
319
+ "ping_sites",
320
+ "aioseo-post-settings",
321
+ "joe-chnlcustid",
322
+ "spd-custhash",
323
+ "joe-custinfo"
324
+ ]
325
+ },
326
+ "firewall_patterns": {
327
+ "dirtraversal": {
328
+ "simple": [
329
+ "etc/passwd",
330
+ "proc/self/environ",
331
+ "etc/passwd",
332
+ "makefile",
333
+ "wwwroot",
334
+ "pingserver",
335
+ "../",
336
+ "loopback"
337
+ ]
338
+ },
339
+ "wpterms": {
340
+ "simple": [
341
+ "/**/",
342
+ "wp-config.php"
343
+ ],
344
+ "regex": [
345
+ "^wp_",
346
+ "^user_login",
347
+ "^user_pass"
348
+ ]
349
+ },
350
+ "fieldtruncation": {
351
+ "regex": [
352
+ "\\s{49,}",
353
+ "\\x00"
354
+ ]
355
+ },
356
+ "sqlqueries": {
357
+ "regex": [
358
+ "concat\\s*\\(",
359
+ "group_concat",
360
+ "union.*select"
361
+ ]
362
+ },
363
+ "exefile": {
364
+ "regex": [
365
+ "\\.(dll|rb|py|exe|php[3-6]?|pl|perl|ph[34]|phl|phtml|phtm|sql|ini|jsp|asp|git|svn|tar)$"
366
+ ]
367
+ },
368
+ "schema": {
369
+ "simple": [
370
+ ".shtml"
371
+ ],
372
+ "regex": [
373
+ "^(http|https|ftp|file):"
374
+ ]
375
+ },
376
+ "phpcode": {
377
+ "simple": null,
378
+ "regex": [
379
+ "(include|include_once|require|require_once)\\s*\\(.*\\)"
380
+ ]
381
+ },
382
+ "aggressive": {
383
+ "simple": [
384
+ "eval(",
385
+ "(null)",
386
+ "base64_",
387
+ "localhost",
388
+ "(function(",
389
+ "{x.html(",
390
+ ").html(",
391
+ "...",
392
+ "/httpdocs/",
393
+ "/tmp/",
394
+ "boot.ini"
395
+ ],
396
+ "regex": [
397
+ "GLOBALS(=|\\[|%%)",
398
+ "REQUEST(=|\\[|%%)",
399
+ "(`|\\<|\\>|\\[|\\]|\\{|\\}|\\?)",
400
+ "drop\\s+table\\s+(`|'?)[a-z0-9]+\\1",
401
+ "'\\s+OR\\s+'([a-z0-9]+)'\\s*=\\s*'\\1'\\s+(--|\\(\\{|\\/\\*)\\s+"
402
+ ]
403
+ }
404
+ },
405
+ "events": {
406
+ "check_skip": {
407
+ "audit_params": [
408
+ "path"
409
+ ],
410
+ "level": "debug",
411
+ "stat": false
412
+ },
413
+ "firewall_block": {
414
+ "audit_params": [
415
+ "name",
416
+ "term",
417
+ "param",
418
+ "value",
419
+ "scan",
420
+ "type"
421
+ ],
422
+ "level": "warning",
423
+ "recent": true,
424
+ "offense": true
425
+ },
426
+ "fw_email_fail": {
427
+ "audit_params": [
428
+ "to"
429
+ ],
430
+ "level": "warning",
431
+ "stat": false
432
+ },
433
+ "fw_email_success": {
434
+ "audit_params": [
435
+ "to"
436
+ ],
437
+ "level": "debug",
438
+ "stat": false
439
+ }
440
+ }
441
+ }
442
+ }
config/hack_protect.json ADDED
@@ -0,0 +1,595 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "hack_protect",
3
+ "properties": {
4
+ "slug": "hack_protect",
5
+ "name": "Hack Guard",
6
+ "sidebar_name": "Scanners",
7
+ "show_module_menu_item": false,
8
+ "show_module_options": true,
9
+ "storage_key": "hack_protect",
10
+ "tagline": "Automatically detect and repair vulnerable and suspicious items",
11
+ "show_central": true,
12
+ "access_restricted": true,
13
+ "premium": false,
14
+ "order": 70,
15
+ "run_if_whitelisted": true,
16
+ "run_if_verified_bot": true,
17
+ "run_if_wpcli": true
18
+ },
19
+ "wpcli": {
20
+ "root": "hack_guard"
21
+ },
22
+ "menu_items": [
23
+ {
24
+ "title": "Scans",
25
+ "slug": "scans-redirect"
26
+ }
27
+ ],
28
+ "custom_redirects": [
29
+ {
30
+ "source_mod_page": "scans-redirect",
31
+ "target_mod_page": "insights",
32
+ "query_args": {
33
+ "inav": "scans_results"
34
+ }
35
+ }
36
+ ],
37
+ "sections": [
38
+ {
39
+ "slug": "section_file_guard",
40
+ "primary": true,
41
+ "title": "File Guard",
42
+ "title_short": "File Guard",
43
+ "beacon_id": 217,
44
+ "summary": [
45
+ "Purpose - Monitor WordPress files and protect against malicious intrusion and hacking.",
46
+ "Recommendation - Keep the File Guard features turned on."
47
+ ]
48
+ },
49
+ {
50
+ "slug": "section_scan_wpv",
51
+ "title": "Vulnerability Scanner",
52
+ "title_short": "Vulnerability Scanner",
53
+ "beacon_id": 217,
54
+ "summary": [
55
+ "Purpose - Regularly scan your WordPress plugins and themes for known security vulnerabilities.",
56
+ "Recommendation - Ensure this is turned on and you will always know if any of your assets have known security vulnerabilities."
57
+ ]
58
+ },
59
+ {
60
+ "slug": "section_realtime",
61
+ "title": "Realtime Change Detection",
62
+ "title_short": "Realtime Change Detection",
63
+ "beacon_id": 226,
64
+ "summary": [
65
+ "Purpose - Monitor Your WordPress Site For Changes To Critical Components In Realtime.",
66
+ "Recommendation - Keep The Realtime Change Detection Active."
67
+ ]
68
+ },
69
+ {
70
+ "slug": "section_scan_ufc",
71
+ "title": "Unrecognised Files Scanner",
72
+ "title_short": "Unrecognised Files Scanner",
73
+ "beacon_id": 395,
74
+ "summary": [
75
+ "Purpose - Scan your WordPress core folders for unrecognised files that don't belong.",
76
+ "Recommendation - Keep the Unrecognised Files Scanner feature turned on."
77
+ ]
78
+ },
79
+ {
80
+ "slug": "section_scan_options",
81
+ "title": "Scan Options",
82
+ "title_short": "Scan Options",
83
+ "beacon_id": 217,
84
+ "summary": [
85
+ "Purpose - Set how often the Hack Guard scans will run."
86
+ ]
87
+ },
88
+ {
89
+ "slug": "section_enable_plugin_feature_hack_protection_tools",
90
+ "title": "Enable Module: Hack Guard",
91
+ "title_short": "Disable Module",
92
+ "beacon_id": 217,
93
+ "summary": [
94
+ "Purpose - Hack Guard is a set of tools to warn you and protect you against hacks on your site.",
95
+ "Recommendation - Keep the Hack Guard module turned on."
96
+ ]
97
+ },
98
+ {
99
+ "slug": "section_non_ui",
100
+ "hidden": true
101
+ }
102
+ ],
103
+ "options": [
104
+ {
105
+ "key": "enable_hack_protect",
106
+ "section": "section_enable_plugin_feature_hack_protection_tools",
107
+ "advanced": true,
108
+ "default": "Y",
109
+ "type": "checkbox",
110
+ "link_info": "https://shsec.io/wpsf38",
111
+ "link_blog": "https://shsec.io/9x",
112
+ "beacon_id": 217,
113
+ "name": "Enable Hack Guard",
114
+ "summary": "Enable (or Disable) The Hack Guard Module",
115
+ "description": "Un-Checking this option will completely disable the Hack Guard module"
116
+ },
117
+ {
118
+ "key": "enabled_scan_apc",
119
+ "section": "section_scan_wpv",
120
+ "type": "checkbox",
121
+ "default": "Y",
122
+ "link_info": "https://shsec.io/ew",
123
+ "link_blog": "https://shsec.io/eo",
124
+ "beacon_id": 225,
125
+ "name": "Abandoned Plugin Scanner",
126
+ "summary": "Enable The Abandoned Plugin Scanner",
127
+ "description": "Scan your WordPress.org assets for whether they've been abandoned."
128
+ },
129
+ {
130
+ "key": "enable_wpvuln_scan",
131
+ "section": "section_scan_wpv",
132
+ "premium": true,
133
+ "type": "checkbox",
134
+ "default": "Y",
135
+ "link_info": "https://shsec.io/du",
136
+ "link_blog": "https://shsec.io/ah",
137
+ "beacon_id": 137,
138
+ "name": "Vulnerability Scanner",
139
+ "summary": "Enable The Vulnerability Scanner",
140
+ "description": "Scan all your WordPress assets for known security vulnerabilities."
141
+ },
142
+ {
143
+ "key": "wpvuln_scan_autoupdate",
144
+ "section": "section_scan_wpv",
145
+ "premium": true,
146
+ "default": "N",
147
+ "type": "checkbox",
148
+ "link_info": "",
149
+ "link_blog": "",
150
+ "name": "Automatic Updates",
151
+ "summary": "Apply Updates Automatically To Vulnerable Plugins",
152
+ "description": "When an update becomes available, automatically apply updates to items with known vulnerabilities."
153
+ },
154
+ {
155
+ "key": "enable_core_file_integrity_scan",
156
+ "section": "section_file_guard",
157
+ "default": "Y",
158
+ "type": "checkbox",
159
+ "link_info": "https://shsec.io/hd",
160
+ "link_blog": "https://shsec.io/wpsf37",
161
+ "beacon_id": 224,
162
+ "name": "WP Core File Scanner",
163
+ "summary": "Automatically Scans WordPress Core Files For Alterations",
164
+ "description": "Compares all WordPress core files on your site against the official WordPress files. WordPress Core files should never be altered for any reason."
165
+ },
166
+ {
167
+ "key": "mal_scan_enable",
168
+ "section": "section_file_guard",
169
+ "premium": true,
170
+ "default": "Y",
171
+ "type": "checkbox",
172
+ "link_info": "https://shsec.io/fp",
173
+ "link_blog": "https://shsec.io/fx",
174
+ "beacon_id": 222,
175
+ "name": "Automatic Malware Scan",
176
+ "summary": "Enable Malware File Scanner",
177
+ "description": "When enabled the Malware scanner will run automatically."
178
+ },
179
+ {
180
+ "key": "ptg_enable",
181
+ "section": "section_file_guard",
182
+ "premium": true,
183
+ "default": "Y",
184
+ "type": "checkbox",
185
+ "link_info": "https://shsec.io/bl",
186
+ "link_blog": "https://shsec.io/bm",
187
+ "beacon_id": 133,
188
+ "name": "Enable/Disable Guard",
189
+ "summary": "Enable The Guard For Plugin And Theme Files",
190
+ "description": "When enabled the Guard will automatically scan for changes to your Plugin and Theme files."
191
+ },
192
+ {
193
+ "key": "file_locker",
194
+ "section": "section_realtime",
195
+ "premium": true,
196
+ "type": "multiple_select",
197
+ "default": [],
198
+ "value_options": [
199
+ {
200
+ "value_key": "wpconfig",
201
+ "text": "WP Config"
202
+ },
203
+ {
204
+ "value_key": "root_htaccess",
205
+ "text": "Root .htaccess"
206
+ },
207
+ {
208
+ "value_key": "root_index",
209
+ "text": "Root index.php"
210
+ },
211
+ {
212
+ "value_key": "root_webconfig",
213
+ "text": "Root Web.Config"
214
+ }
215
+ ],
216
+ "link_info": "https://shsec.io/h7",
217
+ "link_blog": "https://shsec.io/h8",
218
+ "beacon_id": 226,
219
+ "name": "File Locker",
220
+ "summary": "Lock Files Against Tampering and Changes",
221
+ "description": "As soon as changes are detected to any selected files, the contents may be examined and reverted."
222
+ },
223
+ {
224
+ "key": "file_repair_areas",
225
+ "section": "section_file_guard",
226
+ "type": "multiple_select",
227
+ "default": [
228
+ "wp",
229
+ "plugin"
230
+ ],
231
+ "value_options": [
232
+ {
233
+ "value_key": "wp",
234
+ "text": "WP Core"
235
+ },
236
+ {
237
+ "value_key": "plugin",
238
+ "text": "Plugin Files"
239
+ },
240
+ {
241
+ "value_key": "theme",
242
+ "text": "Theme Files"
243
+ }
244
+ ],
245
+ "link_info": "https://shsec.io/wpsf36",
246
+ "link_blog": "https://shsec.io/wpsf37",
247
+ "beacon_id": 228,
248
+ "name": "Auto File Repair",
249
+ "summary": "Which Files Should Be Automatically Repaired?",
250
+ "description": "When a file is modified, or malware is detected, Shield can try to repair files."
251
+ },
252
+ {
253
+ "key": "scan_frequency",
254
+ "section": "section_scan_options",
255
+ "premium": true,
256
+ "default": "1",
257
+ "type": "select",
258
+ "value_options": [
259
+ {
260
+ "value_key": "1",
261
+ "text": "Once"
262
+ },
263
+ {
264
+ "value_key": "2",
265
+ "text": "Twice (scan every 12hrs)"
266
+ },
267
+ {
268
+ "value_key": "3",
269
+ "text": "3 Times (scan every 8hrs)"
270
+ },
271
+ {
272
+ "value_key": "4",
273
+ "text": "4 Times (scan every 6hrs)"
274
+ },
275
+ {
276
+ "value_key": "6",
277
+ "text": "6 Times (scan every 4hrs)"
278
+ },
279
+ {
280
+ "value_key": "8",
281
+ "text": "8 Times (scan every 3hrs)"
282
+ },
283
+ {
284
+ "value_key": "12",
285
+ "text": "12 Times (scan every 2hrs)"
286
+ },
287
+ {
288
+ "value_key": "24",
289
+ "text": "24 Times (scan every hour)"
290
+ }
291
+ ],
292
+ "link_info": "https://shsec.io/b2",
293
+ "link_blog": "https://shsec.io/kd",
294
+ "beacon_id": 223,
295
+ "name": "Scan Frequency",
296
+ "summary": "Number Of Times To Automatically Scan Core Files In 24 Hours",
297
+ "description": "Default: Once every 24hrs. To improve security, increase the number of scans per day."
298
+ },
299
+ {
300
+ "key": "enable_unrecognised_file_cleaner_scan",
301
+ "section": "section_scan_ufc",
302
+ "default": "enabled_report_only",
303
+ "type": "select",
304
+ "value_options": [
305
+ {
306
+ "value_key": "disabled",
307
+ "text": "Automatic Scan Disabled"
308
+ },
309
+ {
310
+ "value_key": "enabled_report_only",
311
+ "text": "Scan Enabled - Report Only"
312
+ },
313
+ {
314
+ "value_key": "enabled_delete_only",
315
+ "text": "Scan Enabled - Automatically Delete Files"
316
+ }
317
+ ],
318
+ "link_info": "https://shsec.io/9y",
319
+ "link_blog": "https://shsec.io/95",
320
+ "beacon_id": 227,
321
+ "name": "Unrecognised Files Scanner",
322
+ "summary": "Scans Core Directories For Unrecognised Files",
323
+ "description": "Scans for, and automatically deletes, any files in your core WordPress folders that are not part of your WordPress installation."
324
+ },
325
+ {
326
+ "key": "ufc_scan_uploads",
327
+ "section": "section_scan_ufc",
328
+ "advanced": true,
329
+ "default": "N",
330
+ "type": "checkbox",
331
+ "link_info": "https://shsec.io/he",
332
+ "link_blog": "https://shsec.io/95",
333
+ "beacon_id": 347,
334
+ "name": "Scan Uploads",
335
+ "summary": "Scan Uploads Folder For PHP and Javascript",
336
+ "description": "The Uploads folder is primarily for media, but could be used to store nefarious files."
337
+ },
338
+ {
339
+ "key": "ufc_exclusions",
340
+ "section": "section_scan_ufc",
341
+ "advanced": true,
342
+ "default": [
343
+ "error_log",
344
+ "php_error_log",
345
+ ".htaccess",
346
+ ".htpasswd",
347
+ ".user.ini",
348
+ "php.ini",
349
+ "web.config",
350
+ "php_mail.log",
351
+ "mail.log",
352
+ "wp-content/uploads/bb-plugin/cache/",
353
+ "wp-content/uploads/cache/wpml/twig/"
354
+ ],
355
+ "type": "array",
356
+ "link_info": "https://shsec.io/9z",
357
+ "link_blog": "https://shsec.io/95",
358
+ "beacon_id": 231,
359
+ "name": "File Exclusions",
360
+ "summary": "Provide A List Of Files To Be Excluded From The Scan",
361
+ "description": "Take a new line for each file you wish to exclude from the scan. No commas are necessary."
362
+ },
363
+ {
364
+ "key": "mal_autorepair_surgical",
365
+ "section": "section_non_ui",
366
+ "premium": true,
367
+ "type": "checkbox",
368
+ "default": "N",
369
+ "link_info": "",
370
+ "link_blog": "",
371
+ "name": "Surgical Auto-Repair",
372
+ "summary": "Automatically Attempt To Surgically Remove Malware Code",
373
+ "description": "Attempts to automatically remove code from infected files."
374
+ },
375
+ {
376
+ "key": "ptg_reinstall_links",
377
+ "section": "section_scan_options",
378
+ "premium": true,
379
+ "type": "checkbox",
380
+ "default": "Y",
381
+ "link_info": "https://shsec.io/bp",
382
+ "link_blog": "",
383
+ "beacon_id": 135,
384
+ "name": "Show Re-Install Links",
385
+ "summary": "Show Re-Install Links For Plugins",
386
+ "description": "Show links to re-install plugins and offer re-install when activating plugins."
387
+ },
388
+ {
389
+ "key": "auto_filter_results",
390
+ "section": "section_scan_options",
391
+ "premium": false,
392
+ "type": "checkbox",
393
+ "default": "Y",
394
+ "link_info": "",
395
+ "link_blog": "",
396
+ "beacon_id": 439,
397
+ "name": "Auto-Filter Results",
398
+ "summary": "Automatically Filter Results Of Irrelevant Items",
399
+ "description": "Automatically remove items from results that are irrelevant."
400
+ },
401
+ {
402
+ "key": "scan_path_exclusions",
403
+ "section": "section_scan_options",
404
+ "advanced": true,
405
+ "premium": true,
406
+ "default": [
407
+ "wp-content/cache/",
408
+ "wp-content/nfwlog/",
409
+ "wp-content/wflogs/",
410
+ "*/error_log",
411
+ "*/php_error_log",
412
+ "*/mail.log",
413
+ "*/php_mail.log",
414
+ "*/.stylelintrc-css.json",
415
+ "*/.stylelintrc.json",
416
+ "*/sucuri-*.php"
417
+ ],
418
+ "type": "array",
419
+ "link_info": "",
420
+ "link_blog": "",
421
+ "beacon_id": 441,
422
+ "name": "Scan Exclusions",
423
+ "summary": "Scan File and Folder Exclusions",
424
+ "description": "Scan File and Folder Exclusions."
425
+ },
426
+ {
427
+ "key": "scans_to_build",
428
+ "section": "section_non_ui",
429
+ "transferable": false,
430
+ "tracking_exclude": true,
431
+ "type": "array",
432
+ "default": []
433
+ },
434
+ {
435
+ "key": "is_scan_cron",
436
+ "section": "section_non_ui",
437
+ "transferable": false,
438
+ "tracking_exclude": true,
439
+ "type": "boolean",
440
+ "default": false
441
+ },
442
+ {
443
+ "key": "mal_fp_reports",
444
+ "section": "section_non_ui",
445
+ "transferable": false,
446
+ "tracking_exclude": true,
447
+ "type": "array",
448
+ "default": []
449
+ },
450
+ {
451
+ "key": "filelocker_state",
452
+ "section": "section_non_ui",
453
+ "transferable": false,
454
+ "tracking_exclude": true,
455
+ "type": "array",
456
+ "default": []
457
+ }
458
+ ],
459
+ "definitions": {
460
+ "all_scan_slugs": [
461
+ "apc",
462
+ "mal",
463
+ "ptg",
464
+ "wpv",
465
+ "wcf",
466
+ "ufc"
467
+ ],
468
+ "file_scan_extensions": [
469
+ "php",
470
+ "php5",
471
+ "php7",
472
+ "js",
473
+ "json",
474
+ "css",
475
+ "htm",
476
+ "html",
477
+ "svg",
478
+ "twig",
479
+ "hbs"
480
+ ],
481
+ "db_classes": {
482
+ "filelocker": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\FileLocker\\Handler",
483
+ "scanner": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Scanner\\Handler",
484
+ "scanq": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\ScanQueue\\Handler"
485
+ },
486
+ "db_table_filelocker": {
487
+ "slug": "filelocker",
488
+ "has_updated_at": true,
489
+ "cols_custom": {
490
+ "file": "varchar(256) NOT NULL COMMENT 'File Path relative to ABSPATH'",
491
+ "hash_original": "varchar(40) NOT NULL COMMENT 'SHA1 File Hash Original'",
492
+ "hash_current": "varchar(40) NOT NULL COMMENT 'SHA1 File Hash Current'",
493
+ "content": "MEDIUMBLOB COMMENT 'Content'",
494
+ "public_key_id": "TINYINT(2) UNSIGNED NOT NULL COMMENT 'Public Key ID'"
495
+ },
496
+ "cols_timestamps": {
497
+ "detected_at": "Change Last Detected",
498
+ "reverted_at": "Reverted To Backup",
499
+ "notified_at": "Notification Sent"
500
+ }
501
+ },
502
+ "db_table_scanner": {
503
+ "slug": "scanner",
504
+ "cols_custom": {
505
+ "hash": "varchar(32) NOT NULL DEFAULT '' COMMENT 'Unique Item Hash'",
506
+ "meta": "text COMMENT 'Relevant Item Data'",
507
+ "scan": "varchar(10) NOT NULL DEFAULT 0 COMMENT 'Scan Type'",
508
+ "severity": "int(3) NOT NULL DEFAULT 1 COMMENT 'Severity'"
509
+ },
510
+ "cols_timestamps": {
511
+ "ignored_at": "Scan Result Ignored",
512
+ "notified_at": "Scan Notifiation Sent"
513
+ }
514
+ },
515
+ "db_table_scanq": {
516
+ "slug": "scanq",
517
+ "cols_custom": {
518
+ "scan": "varchar(3) NOT NULL DEFAULT '' COMMENT 'Scan Slug'",
519
+ "items": "text COMMENT 'Array of scan items'",
520
+ "results": "text COMMENT 'Array of results'",
521
+ "meta": "text COMMENT 'Meta Data'"
522
+ },
523
+ "cols_timestamps": {
524
+ "started_at": "Scan Started",
525
+ "finished_at": "Scan Completed"
526
+ }
527
+ },
528
+ "table_name_filelocker": "filelocker",
529
+ "url_mal_sigs_simple": "https://raw.githubusercontent.com/scr34m/php-malware-scanner/master/definitions/patterns_raw.txt",
530
+ "url_mal_sigs_regex": "https://raw.githubusercontent.com/scr34m/php-malware-scanner/master/definitions/patterns_re.txt",
531
+ "default_whitelist_paths": [
532
+ "wp-content/cache/*",
533
+ "wp-content/shield/*",
534
+ "wp-content/icwp/rollback/*",
535
+ "wp-content/plugins-before-restore/*",
536
+ "wp-content/themes-before-restore/*",
537
+ "wp-content/uploads/bb-plugin/cache/*",
538
+ "wp-content/uploads/cache/wpml/twig/*",
539
+ "wp-content/cache/*",
540
+ "*/error_log",
541
+ "*/php_error_log",
542
+ "*/mail.log",
543
+ "*/php_mail.log"
544
+ ],
545
+ "cron_all_scans": "all-scans",
546
+ "wcf_exclusions": [
547
+ "readme.html",
548
+ "license.txt",
549
+ "licens-sv_SE.txt",
550
+ "wp-config-sample.php",
551
+ "wp-content/"
552
+ ],
553
+ "wcf_exclusions_missing_only": [
554
+ "wp-admin/install.php",
555
+ "xmlrpc.php"
556
+ ],
557
+ "events": {
558
+ "scan_run": {
559
+ "audit_params": [
560
+ "scan"
561
+ ],
562
+ "level": "debug",
563
+ "audit_multiple": true
564
+ },
565
+ "scan_items_found": {
566
+ "audit_params": [
567
+ "scan",
568
+ "items"
569
+ ],
570
+ "level": "alert",
571
+ "audit_multiple": true,
572
+ "recent": true
573
+ },
574
+ "scan_item_repair_success": {
575
+ "audit_params": [
576
+ "path_full"
577
+ ],
578
+ "audit_multiple": true,
579
+ "recent": true
580
+ },
581
+ "scan_item_repair_fail": {
582
+ "audit_params": [
583
+ "path_full"
584
+ ],
585
+ "audit_multiple": true
586
+ },
587
+ "scan_item_delete_success": {
588
+ "audit_params": [
589
+ "path_full"
590
+ ],
591
+ "audit_multiple": true
592
+ }
593
+ }
594
+ }
595
+ }
config/headers.json ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "headers",
3
+ "properties": {
4
+ "slug": "headers",
5
+ "name": "HTTP Headers",
6
+ "sidebar_name": "HTTP Headers",
7
+ "show_module_menu_item": false,
8
+ "show_module_options": true,
9
+ "storage_key": "headers",
10
+ "tagline": "Control HTTP Security Headers",
11
+ "show_central": true,
12
+ "access_restricted": true,
13
+ "premium": false,
14
+ "run_if_whitelisted": false,
15
+ "run_if_verified_bot": true,
16
+ "run_if_wpcli": false,
17
+ "order": 80
18
+ },
19
+ "sections": [
20
+ {
21
+ "primary": true,
22
+ "slug": "section_security_headers",
23
+ "title": "Advanced Security Headers",
24
+ "title_short": "Security Headers",
25
+ "beacon_id": 267,
26
+ "summary": [
27
+ "Purpose - Protect visitors to your site by implementing increased security response headers.",
28
+ "Recommendation - Enabling these features are advised, but you must test them on your site thoroughly."
29
+ ]
30
+ },
31
+ {
32
+ "slug": "section_content_security_policy",
33
+ "title": "Content Security Policy",
34
+ "title_short": "Content Security Policy",
35
+ "beacon_id": 155,
36
+ "summary": [
37
+ "Purpose - Restrict the sources and types of content that may be loaded and processed by visitor browsers.",
38
+ "Recommendation - Enabling these features are advised, but you must test them on your site thoroughly."
39
+ ]
40
+ },
41
+ {
42
+ "slug": "section_enable_plugin_feature_headers",
43
+ "title": "Enable Module: HTTP Headers",
44
+ "title_short": "Disable Module",
45
+ "beacon_id": 265,
46
+ "summary": [
47
+ "Purpose - Protect visitors to your site by implementing increased security response headers.",
48
+ "Recommendation - Enabling these features are advised, but you must test them on your site thoroughly."
49
+ ]
50
+ },
51
+ {
52
+ "slug": "section_non_ui",
53
+ "hidden": true
54
+ }
55
+ ],
56
+ "options": [
57
+ {
58
+ "key": "enable_headers",
59
+ "section": "section_enable_plugin_feature_headers",
60
+ "advanced": true,
61
+ "default": "Y",
62
+ "type": "checkbox",
63
+ "link_info": "https://shsec.io/aj",
64
+ "link_blog": "https://shsec.io/7c",
65
+ "beacon_id": 265,
66
+ "name": "Enable HTTP Headers",
67
+ "summary": "Enable (or Disable) The HTTP Headers module",
68
+ "description": "Un-Checking this option will completely disable the HTTP Headers module"
69
+ },
70
+ {
71
+ "key": "x_frame",
72
+ "section": "section_security_headers",
73
+ "default": "on_sameorigin",
74
+ "type": "select",
75
+ "value_options": [
76
+ {
77
+ "value_key": "off",
78
+ "text": "Off: iFrames Not Blocked"
79
+ },
80
+ {
81
+ "value_key": "on_sameorigin",
82
+ "text": "On: Allow iFrames On The Same Domain"
83
+ },
84
+ {
85
+ "value_key": "on_deny",
86
+ "text": "On: Block All iFrames"
87
+ }
88
+ ],
89
+ "link_info": "https://shsec.io/78",
90
+ "link_blog": "https://shsec.io/7c",
91
+ "name": "Block iFrames",
92
+ "summary": "Block Remote iFrames Of This Site",
93
+ "description": "The setting prevents any external website from embedding your site in an iFrame. This is useful for preventing so-called ClickJack attacks."
94
+ },
95
+ {
96
+ "key": "x_xss_protect",
97
+ "section": "section_security_headers",
98
+ "default": "Y",
99
+ "type": "checkbox",
100
+ "link_info": "https://shsec.io/79",
101
+ "link_blog": "https://shsec.io/7c",
102
+ "name": "XSS Protection",
103
+ "summary": "Employ Built-In Browser XSS Protection",
104
+ "description": "Directs compatible browsers to block what they detect as Reflective XSS attacks."
105
+ },
106
+ {
107
+ "key": "x_content_type",
108
+ "section": "section_security_headers",
109
+ "default": "Y",
110
+ "type": "checkbox",
111
+ "link_info": "https://shsec.io/7a",
112
+ "link_blog": "https://shsec.io/7c",
113
+ "name": "Prevent Mime-Sniff",
114
+ "summary": "Turn-Off Browser Mime-Sniff",
115
+ "description": "Reduces visitor exposure to malicious user-uploaded content."
116
+ },
117
+ {
118
+ "key": "x_referrer_policy",
119
+ "section": "section_security_headers",
120
+ "sensitive": false,
121
+ "type": "select",
122
+ "default": "unsafe-url",
123
+ "value_options": [
124
+ {
125
+ "value_key": "unsafe-url",
126
+ "text": "Default: Full Referrer URL (aka 'Unsafe URL')"
127
+ },
128
+ {
129
+ "value_key": "no-referrer",
130
+ "text": "No Referrer"
131
+ },
132
+ {
133
+ "value_key": "no-referrer-when-downgrade",
134
+ "text": "No Referrer When Downgrade"
135
+ },
136
+ {
137
+ "value_key": "same-origin",
138
+ "text": "Same Origin"
139
+ },
140
+ {
141
+ "value_key": "origin",
142
+ "text": "Origin"
143
+ },
144
+ {
145
+ "value_key": "strict-origin",
146
+ "text": "Strict Origin"
147
+ },
148
+ {
149
+ "value_key": "origin-when-cross-origin",
150
+ "text": "Origin When Cross-Origin"
151
+ },
152
+ {
153
+ "value_key": "strict-origin-when-cross-origin",
154
+ "text": "Strict Origin When Cross-Origin"
155
+ },
156
+ {
157
+ "value_key": "empty",
158
+ "text": "Empty Header"
159
+ },
160
+ {
161
+ "value_key": "disabled",
162
+ "text": "Disabled - Don't Send This Header"
163
+ }
164
+ ],
165
+ "link_info": "https://shsec.io/a5",
166
+ "link_blog": "",
167
+ "name": "Referrer Policy",
168
+ "summary": "Referrer Policy Header",
169
+ "description": "The Referrer Policy Header allows you to control when and what referral information a browser may pass along with links clicked on your site."
170
+ },
171
+ {
172
+ "key": "enable_x_content_security_policy",
173
+ "section": "section_content_security_policy",
174
+ "premium": true,
175
+ "default": "N",
176
+ "type": "checkbox",
177
+ "link_info": "https://shsec.io/7d",
178
+ "link_blog": "https://shsec.io/7c",
179
+ "beacon_id": 155,
180
+ "name": "Enable Content Security Policy",
181
+ "summary": "Enable (or Disable) The Content Security Policy module",
182
+ "description": "Allows for permission and restriction of all resources loaded on your site."
183
+ },
184
+ {
185
+ "key": "xcsp_custom",
186
+ "section": "section_content_security_policy",
187
+ "premium": true,
188
+ "default": [],
189
+ "type": "array",
190
+ "link_info": "https://shsec.io/g9",
191
+ "link_blog": "",
192
+ "beacon_id": 155,
193
+ "name": "Manual Rules",
194
+ "summary": "Manual CSP Rules",
195
+ "description": "Manual CSP rules."
196
+ }
197
+ ]
198
+ }
config/insights.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "insights",
3
+ "properties": {
4
+ "slug": "insights",
5
+ "name": "Dashboard",
6
+ "menu_title": "Security Dashboard",
7
+ "menu_priority": "5",
8
+ "show_module_menu_item": true,
9
+ "show_module_options": false,
10
+ "auto_enabled": true,
11
+ "storage_key": "insights",
12
+ "show_central": false,
13
+ "premium": false,
14
+ "access_restricted": true,
15
+ "run_if_whitelisted": true,
16
+ "run_if_verified_bot": false,
17
+ "run_if_wpcli": false,
18
+ "skip_processor": true,
19
+ "tracking_exclude": true
20
+ },
21
+ "wpcli": {
22
+ "enabled": false
23
+ },
24
+ "sections": [
25
+ {
26
+ "slug": "section_non_ui",
27
+ "hidden": true
28
+ }
29
+ ],
30
+ "options": [
31
+ ]
32
+ }
config/integrations.json ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "integrations",
3
+ "properties": {
4
+ "slug": "integrations",
5
+ "storage_key": "integrations",
6
+ "name": "Integrations",
7
+ "menu_title": "Integrations",
8
+ "sidebar_name": "Integrations",
9
+ "show_module_options": true,
10
+ "show_module_menu_item": false,
11
+ "auto_enabled": false,
12
+ "show_central": true,
13
+ "premium": false,
14
+ "access_restricted": true,
15
+ "run_if_whitelisted": true,
16
+ "run_if_wpcli": true,
17
+ "run_if_verified_bot": true,
18
+ "skip_processor": false,
19
+ "tracking_exclude": false
20
+ },
21
+ "wpcli": {
22
+ "enabled": true
23
+ },
24
+ "sections": [
25
+ {
26
+ "slug": "section_integrations",
27
+ "primary": true,
28
+ "title": "Integrations",
29
+ "title_short": "Integrations",
30
+ "beacon_id": 404
31
+ },
32
+ {
33
+ "slug": "section_spam",
34
+ "title": "SPAM Detection",
35
+ "title_short": "SPAM Detection",
36
+ "beacon_id": 138
37
+ },
38
+ {
39
+ "slug": "section_user_forms",
40
+ "title": "User Forms Bot Detection",
41
+ "title_short": "User Forms Bot Detection",
42
+ "beacon_id": 239
43
+ },
44
+ {
45
+ "slug": "section_non_ui",
46
+ "hidden": true
47
+ }
48
+ ],
49
+ "options": [
50
+ {
51
+ "key": "enable_mainwp",
52
+ "section": "section_integrations",
53
+ "default": "Y",
54
+ "type": "checkbox",
55
+ "link_info": "https://shsec.io/ir",
56
+ "link_blog": "https://shsec.io/ke",
57
+ "beacon_id": 404,
58
+ "name": "Enable MainWP",
59
+ "summary": "Enable The Built-In MainWP Extension",
60
+ "description": "This option will enable Shield's built-in MainWP extension for both server and client."
61
+ },
62
+ {
63
+ "key": "form_spam_providers",
64
+ "section": "section_spam",
65
+ "premium": true,
66
+ "advanced": true,
67
+ "type": "multiple_select",
68
+ "default": [],
69
+ "value_options": [
70
+ {
71
+ "value_key": "contactform7",
72
+ "text": "Contact Form 7"
73
+ },
74
+ {
75
+ "value_key": "elementorpro",
76
+ "text": "Elementor Pro"
77
+ },
78
+ {
79
+ "value_key": "fluentforms",
80
+ "text": "Fluent Forms"
81
+ },
82
+ {
83
+ "value_key": "formidableforms",
84
+ "text": "Formidable Forms"
85
+ },
86
+ {
87
+ "value_key": "forminator",
88
+ "text": "Forminator"
89
+ },
90
+ {
91
+ "value_key": "gravityforms",
92
+ "text": "Gravity Forms"
93
+ },
94
+ {
95
+ "value_key": "groundhogg",
96
+ "text": "Groundhogg"
97
+ },
98
+ {
99
+ "value_key": "kaliforms",
100
+ "text": "Kali Forms"
101
+ },
102
+ {
103
+ "value_key": "ninjaforms",
104
+ "text": "Ninja Forms"
105
+ },
106
+ {
107
+ "value_key": "superforms",
108
+ "text": "Super Forms"
109
+ },
110
+ {
111
+ "value_key": "supportcandy",
112
+ "text": "Support Candy"
113
+ },
114
+ {
115
+ "value_key": "wpforo",
116
+ "text": "wpForo"
117
+ },
118
+ {
119
+ "value_key": "wpforms",
120
+ "text": "WPForms"
121
+ }
122
+ ],
123
+ "link_info": "https://shsec.io/k2",
124
+ "link_blog": "https://shsec.io/k3",
125
+ "beacon_id": 138,
126
+ "name": "SPAM Form Checking",
127
+ "summary": "Select The Form Providers That Should Be Checked For SPAM",
128
+ "description": "Select The Form Providers That Should Be Checked For SPAM."
129
+ },
130
+ {
131
+ "key": "user_form_providers",
132
+ "section": "section_user_forms",
133
+ "premium": true,
134
+ "advanced": true,
135
+ "type": "multiple_select",
136
+ "default": [
137
+ "wordpress"
138
+ ],
139
+ "value_options": [
140
+ {
141
+ "value_key": "buddyboss",
142
+ "text": "BuddyBoss"
143
+ },
144
+ {
145
+ "value_key": "buddypress",
146
+ "text": "BuddyPress"
147
+ },
148
+ {
149
+ "value_key": "easydigitaldownloads",
150
+ "text": "Easy Digital Downloads"
151
+ },
152
+ {
153
+ "value_key": "learnpress",
154
+ "text": "LearnPress"
155
+ },
156
+ {
157
+ "value_key": "lifterlms",
158
+ "text": "LifterLMS"
159
+ },
160
+ {
161
+ "value_key": "memberpress",
162
+ "text": "MemberPress"
163
+ },
164
+ {
165
+ "value_key": "paidmembersubscriptions",
166
+ "text": "Paid Member Subscriptions"
167
+ },
168
+ {
169
+ "value_key": "profilebuilder",
170
+ "text": "Profile Builder"
171
+ },
172
+ {
173
+ "value_key": "ultimatemember",
174
+ "text": "Ultimate Member"
175
+ },
176
+ {
177
+ "value_key": "woocommerce",
178
+ "text": "WooCommerce"
179
+ },
180
+ {
181
+ "value_key": "wordpress",
182
+ "text": "WordPress"
183
+ },
184
+ {
185
+ "value_key": "wpmembers",
186
+ "text": "WP Members"
187
+ }
188
+ ],
189
+ "link_info": "https://shsec.io/k4",
190
+ "link_blog": "https://shsec.io/k3",
191
+ "beacon_id": 239,
192
+ "name": "User Form Checking",
193
+ "summary": "Select The User Form Providers That Should Be Checked For SPAM Registrations and Logins",
194
+ "description": "Select The User Form Providers That Should Be Checked For SPAM Registrations and Logins"
195
+ }
196
+ ],
197
+ "definitions": {
198
+ "events": {
199
+ "spam_form_pass": {
200
+ "audit_params": [
201
+ "form_provider"
202
+ ],
203
+ "level": "info",
204
+ "stat": true,
205
+ "audit": true,
206
+ "offense": false
207
+ },
208
+ "spam_form_fail": {
209
+ "audit_params": [
210
+ "form_provider"
211
+ ],
212
+ "level": "warning",
213
+ "stat": true,
214
+ "audit": true,
215
+ "offense": false
216
+ },
217
+ "user_form_bot_pass": {
218
+ "audit_params": [
219
+ "form_provider",
220
+ "action",
221
+ "username"
222
+ ],
223
+ "level": "info",
224
+ "stat": true,
225
+ "audit": true,
226
+ "offense": false
227
+ },
228
+ "user_form_bot_fail": {
229
+ "audit_params": [
230
+ "form_provider",
231
+ "action",
232
+ "username"
233
+ ],
234
+ "level": "warning",
235
+ "stat": true,
236
+ "audit": true,
237
+ "offense": true
238
+ }
239
+ }
240
+ }
241
+ }
config/ips.json ADDED
@@ -0,0 +1,775 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "ips",
3
+ "properties": {
4
+ "slug": "ips",
5
+ "name": "Block Bad IPs/Visitors",
6
+ "sidebar_name": "IP Blocking",
7
+ "show_module_menu_item": false,
8
+ "show_module_options": true,
9
+ "storage_key": "ips",
10
+ "tagline": "Automatically detect bots and malicious visitors and stop them dead.",
11
+ "show_central": true,
12
+ "access_restricted": true,
13
+ "premium": false,
14
+ "run_if_whitelisted": true,
15
+ "run_if_verified_bot": true,
16
+ "run_if_wpcli": false,
17
+ "order": 100
18
+ },
19
+ "menu_items": [
20
+ {
21
+ "title": "IP Manager",
22
+ "slug": "ips-redirect"
23
+ }
24
+ ],
25
+ "custom_redirects": [
26
+ {
27
+ "source_mod_page": "ips-redirect",
28
+ "target_mod_page": "insights",
29
+ "query_args": {
30
+ "inav": "ips"
31
+ }
32
+ }
33
+ ],
34
+ "admin_notices": {
35
+ "visitor-whitelisted": {
36
+ "id": "visitor-whitelisted",
37
+ "schedule": "conditions",
38
+ "plugin_page_only": true,
39
+ "per_user": true,
40
+ "type": "info"
41
+ }
42
+ },
43
+ "requirements": {
44
+ "php": {
45
+ "functions": [
46
+ "filter_var"
47
+ ],
48
+ "constants": [
49
+ "FILTER_VALIDATE_IP",
50
+ "FILTER_FLAG_IPV4",
51
+ "FILTER_FLAG_IPV6",
52
+ "FILTER_FLAG_NO_PRIV_RANGE",
53
+ "FILTER_FLAG_NO_RES_RANGE"
54
+ ]
55
+ }
56
+ },
57
+ "sections": [
58
+ {
59
+ "slug": "section_auto_black_list",
60
+ "primary": true,
61
+ "title": "Automatic IP Black List",
62
+ "title_short": "Auto IP Blocking Rules",
63
+ "beacon_id": 208,
64
+ "summary": [
65
+ "Purpose - The Automatic IP Black List system will block the IP addresses of naughty visitors after a specified number of transgressions.",
66
+ "Recommendation - Keep the Automatic IP Black List feature turned on."
67
+ ]
68
+ },
69
+ {
70
+ "slug": "section_antibot",
71
+ "title": "AntiBot System",
72
+ "title_short": "AntiBot System"
73
+ },
74
+ {
75
+ "slug": "section_logins",
76
+ "title": "Capture Login Bots",
77
+ "title_short": "Login Bots",
78
+ "beacon_id": 122,
79
+ "summary": [
80
+ "Recommendation - Enable to capture bots/spiders that don't honour 'nofollow' directives."
81
+ ]
82
+ },
83
+ {
84
+ "slug": "section_probes",
85
+ "title": "Capture Probing Bots",
86
+ "title_short": "Probing Bots",
87
+ "beacon_id": 123,
88
+ "summary": [
89
+ "Recommendation - Enable to capture bots/spiders that don't honour 'nofollow' directives."
90
+ ]
91
+ },
92
+ {
93
+ "slug": "section_behaviours",
94
+ "title": "Identify Common Bot Behaviours",
95
+ "title_short": "Bot Behaviours",
96
+ "beacon_id": 124,
97
+ "summary": [
98
+ "Recommendation - Enable to capture bots/spiders that don't honour 'nofollow' directives."
99
+ ]
100
+ },
101
+ {
102
+ "slug": "section_user_messages",
103
+ "title": "Customize Messages Shown To User",
104
+ "title_short": "Visitor Messages",
105
+ "beacon_id": 139,
106
+ "summary": [
107
+ "Purpose - Customize the messages shown to visitors.",
108
+ "Recommendation - Be sure to change the messages to suit your audience.",
109
+ "Hint - To reset any message to its default, enter the text exactly: default"
110
+ ]
111
+ },
112
+ {
113
+ "slug": "section_enable_plugin_feature_ips",
114
+ "title": "Enable Module: IP Manager",
115
+ "title_short": "Disable Module",
116
+ "summary": [
117
+ "Purpose - The IP Manager allows you to whitelist, blacklist and configure auto-blacklist rules.",
118
+ "Recommendation - Keep the IP Manager feature turned on. You should also carefully review the automatic black list settings."
119
+ ]
120
+ },
121
+ {
122
+ "slug": "section_non_ui",
123
+ "hidden": true
124
+ }
125
+ ],
126
+ "options": [
127
+ {
128
+ "key": "enable_ips",
129
+ "section": "section_enable_plugin_feature_ips",
130
+ "advanced": true,
131
+ "default": "Y",
132
+ "type": "checkbox",
133
+ "link_info": "https://shsec.io/ea",
134
+ "link_blog": "https://shsec.io/wpsf26",
135
+ "beacon_id": 208,
136
+ "name": "Enable IP Manager",
137
+ "summary": "Enable (or Disable) The IP Manager module",
138
+ "description": "Un-Checking this option will completely disable the IP Manager module"
139
+ },
140
+ {
141
+ "key": "antibot_minimum",
142
+ "section": "section_antibot",
143
+ "default": 45,
144
+ "type": "integer",
145
+ "min": 0,
146
+ "max": 99,
147
+ "link_info": "https://shsec.io/jy",
148
+ "link_blog": "https://shsec.io/jz",
149
+ "beacon_id": 424,
150
+ "name": "AntiBot Threshold",
151
+ "summary": "AntiBot Testing Threshold (Percentage)",
152
+ "description": "When using Shield's AntiBot system, this is the threshold used for testing (between 1 and 99)."
153
+ },
154
+ {
155
+ "key": "antibot_high_reputation_minimum",
156
+ "section": "section_antibot",
157
+ "default": 200,
158
+ "type": "integer",
159
+ "min": 0,
160
+ "link_info": "https://shsec.io/jy",
161
+ "link_blog": "https://shsec.io/jz",
162
+ "beacon_id": 431,
163
+ "name": "High Reputation Bypass",
164
+ "summary": "Prevent IPs/Visitors With High Reputation Scores From Being Blocked",
165
+ "description": "Ensures that visitors with a high reputation are never blocked by Shield."
166
+ },
167
+ {
168
+ "key": "transgression_limit",
169
+ "section": "section_auto_black_list",
170
+ "default": 10,
171
+ "type": "integer",
172
+ "link_info": "https://shsec.io/wpsf24",
173
+ "link_blog": "https://shsec.io/wpsf26",
174
+ "beacon_id": 207,
175
+ "name": "Offense Limit",
176
+ "summary": "Visitor IP address will be Black Listed after X bad actions on your site",
177
+ "description": "A black mark is set against an IP address each time a visitor trips the defenses of the Shield plugin. When the number of these offenses exceeds specified limit, they are automatically blocked from accessing the site. Set this to 0 to turn off the Automatic IP Black List feature."
178
+ },
179
+ {
180
+ "key": "auto_expire",
181
+ "section": "section_auto_black_list",
182
+ "advanced": true,
183
+ "default": "day",
184
+ "type": "select",
185
+ "value_options": [
186
+ {
187
+ "value_key": "minute",
188
+ "text": "Minute"
189
+ },
190
+ {
191
+ "value_key": "hour",
192
+ "text": "Hour"
193
+ },
194
+ {
195
+ "value_key": "day",
196
+ "text": "Day"
197
+ },
198
+ {
199
+ "value_key": "week",
200
+ "text": "Week"
201
+ },
202
+ {
203
+ "value_key": "month",
204
+ "text": "Month"
205
+ }
206
+ ],
207
+ "link_info": "https://shsec.io/wpsf25",
208
+ "link_blog": "https://shsec.io/wpsf26",
209
+ "beacon_id": 210,
210
+ "name": "Auto Block Expiration",
211
+ "summary": "After 1 'X' a black listed IP will be removed from the black list",
212
+ "description": "Permanent and lengthy IP Black Lists are harmful to performance. You should allow IP addresses on the black list to be eventually removed over time. Shorter IP black lists are more efficient and a more intelligent use of an IP-based blocking system."
213
+ },
214
+ {
215
+ "key": "user_auto_recover",
216
+ "section": "section_auto_black_list",
217
+ "advanced": true,
218
+ "premium": true,
219
+ "default": [],
220
+ "type": "multiple_select",
221
+ "value_options": [
222
+ {
223
+ "value_key": "gasp",
224
+ "text": "With Shield Bot Protection"
225
+ },
226
+ {
227
+ "value_key": "email",
228
+ "text": "Magic Email Links To Unblock Logged-In Users"
229
+ }
230
+ ],
231
+ "link_info": "https://shsec.io/f8",
232
+ "link_blog": "",
233
+ "beacon_id": 125,
234
+ "name": "User Auto Unblock",
235
+ "summary": "Allow Visitors To Unblock Their IP",
236
+ "description": "Allow visitors blocked by the plugin to automatically unblock themselves."
237
+ },
238
+ {
239
+ "key": "request_whitelist",
240
+ "section": "section_auto_black_list",
241
+ "advanced": true,
242
+ "premium": true,
243
+ "default": [],
244
+ "type": "array",
245
+ "link_info": "https://shsec.io/gd",
246
+ "link_blog": "",
247
+ "beacon_id": 126,
248
+ "name": "Request Path Whitelist",
249
+ "summary": "Request Path Whitelist",
250
+ "description": "Request Path Whitelist."
251
+ },
252
+ {
253
+ "key": "text_loginfailed",
254
+ "section": "section_user_messages",
255
+ "sensitive": true,
256
+ "premium": true,
257
+ "default": "default",
258
+ "type": "text",
259
+ "link_info": "https://shsec.io/e8",
260
+ "link_blog": "",
261
+ "beacon_id": 139,
262
+ "name": "Login Failed",
263
+ "summary": "Visitor Triggers The IP Offenses System Through A Failed Login",
264
+ "description": "This message is displayed if the visitor fails a login attempt."
265
+ },
266
+ {
267
+ "key": "track_404",
268
+ "section": "section_probes",
269
+ "premium": true,
270
+ "default": "log",
271
+ "type": "select",
272
+ "value_options": [
273
+ {
274
+ "value_key": "disabled",
275
+ "text": "Disabled"
276
+ },
277
+ {
278
+ "value_key": "log",
279
+ "text": "Audit Log Only"
280
+ },
281
+ {
282
+ "value_key": "transgression-single",
283
+ "text": "Increment Offense Counter"
284
+ },
285
+ {
286
+ "value_key": "transgression-double",
287
+ "text": "Double-Increment Offense Counter"
288
+ },
289
+ {
290
+ "value_key": "block",
291
+ "text": "Immediate Block"
292
+ }
293
+ ],
294
+ "link_info": "https://shsec.io/fo",
295
+ "link_blog": "https://shsec.io/f7",
296
+ "beacon_id": 123,
297
+ "name": "404 Detect",
298
+ "summary": "Identify A Bot When It Hits A 404",
299
+ "description": "Detect When A Visitor Browses To A Non-Existent Page."
300
+ },
301
+ {
302
+ "key": "track_linkcheese",
303
+ "section": "section_probes",
304
+ "premium": true,
305
+ "default": "disabled",
306
+ "type": "select",
307
+ "value_options": [
308
+ {
309
+ "value_key": "disabled",
310
+ "text": "Disabled"
311
+ },
312
+ {
313
+ "value_key": "log",
314
+ "text": "Audit Log Only"
315
+ },
316
+ {
317
+ "value_key": "transgression-single",
318
+ "text": "Increment Offense Counter"
319
+ },
320
+ {
321
+ "value_key": "transgression-double",
322
+ "text": "Double-Increment Offense Counter"
323
+ },
324
+ {
325
+ "value_key": "block",
326
+ "text": "Immediate Block"
327
+ }
328
+ ],
329
+ "link_info": "https://shsec.io/fo",
330
+ "link_blog": "https://shsec.io/f6",
331
+ "beacon_id": 123,
332
+ "name": "Link Cheese",
333
+ "summary": "Tempt A Bot With A Fake Link To Follow",
334
+ "description": "Detect A Bot That Follows A 'no-follow' Link."
335
+ },
336
+ {
337
+ "key": "track_xmlrpc",
338
+ "section": "section_probes",
339
+ "premium": true,
340
+ "default": "log",
341
+ "type": "select",
342
+ "value_options": [
343
+ {
344
+ "value_key": "disabled",
345
+ "text": "Disabled"
346
+ },
347
+ {
348
+ "value_key": "log",
349
+ "text": "Audit Log Only"
350
+ },
351
+ {
352
+ "value_key": "transgression-single",
353
+ "text": "Increment Offense Counter"
354
+ },
355
+ {
356
+ "value_key": "transgression-double",
357
+ "text": "Double-Increment Offense Counter"
358
+ },
359
+ {
360
+ "value_key": "block",
361
+ "text": "Immediate Block"
362
+ }
363
+ ],
364
+ "link_info": "https://shsec.io/fo",
365
+ "link_blog": "https://shsec.io/f7",
366
+ "beacon_id": 123,
367
+ "name": "XML-RPC Access",
368
+ "summary": "Identify A Bot When It Accesses XML-RPC",
369
+ "description": "If you don't use XML-RPC, why would anyone access it?"
370
+ },
371
+ {
372
+ "key": "track_invalidscript",
373
+ "section": "section_probes",
374
+ "premium": true,
375
+ "default": "log",
376
+ "type": "select",
377
+ "value_options": [
378
+ {
379
+ "value_key": "disabled",
380
+ "text": "Disabled"
381
+ },
382
+ {
383
+ "value_key": "log",
384
+ "text": "Audit Log Only"
385
+ },
386
+ {
387
+ "value_key": "transgression-single",
388
+ "text": "Increment Offense Counter"
389
+ },
390
+ {
391
+ "value_key": "transgression-double",
392
+ "text": "Double-Increment Offense Counter"
393
+ },
394
+ {
395
+ "value_key": "block",
396
+ "text": "Immediate Block"
397
+ }
398
+ ],
399
+ "link_info": "https://shsec.io/fo",
400
+ "link_blog": "https://shsec.io/f7",
401
+ "beacon_id": 123,
402
+ "name": "Invalid Script Load",
403
+ "summary": "Identify A Bot Attempts To Load WordPress In A Non-Standard Way",
404
+ "description": "WordPress should only be loaded in a limited number of ways."
405
+ },
406
+ {
407
+ "key": "track_loginfailed",
408
+ "section": "section_logins",
409
+ "default": "transgression-single",
410
+ "type": "select",
411
+ "value_options": [
412
+ {
413
+ "value_key": "disabled",
414
+ "text": "Disabled"
415
+ },
416
+ {
417
+ "value_key": "log",
418
+ "text": "Audit Log Only"
419
+ },
420
+ {
421
+ "value_key": "transgression-single",
422
+ "text": "Increment Offense Counter"
423
+ },
424
+ {
425
+ "value_key": "transgression-double",
426
+ "text": "Double-Increment Offense Counter"
427
+ },
428
+ {
429
+ "value_key": "block",
430
+ "text": "Immediate Block"
431
+ }
432
+ ],
433
+ "link_info": "https://shsec.io/fn",
434
+ "link_blog": "https://shsec.io/f7",
435
+ "beacon_id": 122,
436
+ "name": "Failed Login",
437
+ "summary": "Detect Failed Login Attempts By Valid Usernames",
438
+ "description": "Penalise a visitor who fails to login using a valid username."
439
+ },
440
+ {
441
+ "key": "track_logininvalid",
442
+ "section": "section_logins",
443
+ "premium": true,
444
+ "default": "log",
445
+ "type": "select",
446
+ "value_options": [
447
+ {
448
+ "value_key": "disabled",
449
+ "text": "Disabled"
450
+ },
451
+ {
452
+ "value_key": "log",
453
+ "text": "Audit Log Only"
454
+ },
455
+ {
456
+ "value_key": "transgression-single",
457
+ "text": "Increment Offense Counter"
458
+ },
459
+ {
460
+ "value_key": "transgression-double",
461
+ "text": "Double-Increment Offense Counter"
462
+ },
463
+ {
464
+ "value_key": "block",
465
+ "text": "Immediate Block"
466
+ }
467
+ ],
468
+ "link_info": "https://shsec.io/fn",
469
+ "link_blog": "https://shsec.io/f7",
470
+ "beacon_id": 122,
471
+ "name": "Invalid Usernames",
472
+ "summary": "Detect Invalid Username Logins",
473
+ "description": "Identify A Bot When It Tries To Login With A Non-Existent Username."
474
+ },
475
+ {
476
+ "key": "track_fakewebcrawler",
477
+ "section": "section_behaviours",
478
+ "premium": true,
479
+ "default": "log",
480
+ "type": "select",
481
+ "value_options": [
482
+ {
483
+ "value_key": "disabled",
484
+ "text": "Disabled"
485
+ },
486
+ {
487
+ "value_key": "log",
488
+ "text": "Audit Log Only"
489
+ },
490
+ {
491
+ "value_key": "transgression-single",
492
+ "text": "Increment Offense Counter"
493
+ },
494
+ {
495
+ "value_key": "transgression-double",
496
+ "text": "Double-Increment Offense Counter"
497
+ },
498
+ {
499
+ "value_key": "block",
500
+ "text": "Immediate Block"
501
+ }
502
+ ],
503
+ "link_info": "https://shsec.io/f5",
504
+ "link_blog": "https://shsec.io/f7",
505
+ "beacon_id": 206,
506
+ "name": "Fake Web Crawler",
507
+ "summary": "Detect Fake Search Engine Crawlers",
508
+ "description": "Identify a Bot when it presents as an official web crawler, but analysis shows it's fake."
509
+ },
510
+ {
511
+ "key": "track_useragent",
512
+ "section": "section_behaviours",
513
+ "premium": true,
514
+ "default": "log",
515
+ "type": "select",
516
+ "value_options": [
517
+ {
518
+ "value_key": "disabled",
519
+ "text": "Disabled"
520
+ },
521
+ {
522
+ "value_key": "log",
523
+ "text": "Audit Log Only"
524
+ },
525
+ {
526
+ "value_key": "transgression-single",
527
+ "text": "Increment Offense Counter"
528
+ },
529
+ {
530
+ "value_key": "transgression-double",
531
+ "text": "Double-Increment Offense Counter"
532
+ },
533
+ {
534
+ "value_key": "block",
535
+ "text": "Immediate Block"
536
+ }
537
+ ],
538
+ "link_info": "https://shsec.io/fi",
539
+ "link_blog": "https://shsec.io/f7",
540
+ "beacon_id": 124,
541
+ "name": "Empty User Agents",
542
+ "summary": "Detect Requests With Empty User Agents",
543
+ "description": "Identify a request as a bot if the user agent is not provided."
544
+ },
545
+ {
546
+ "key": "text_remainingtrans",
547
+ "section": "section_user_messages",
548
+ "sensitive": true,
549
+ "premium": true,
550
+ "default": "default",
551
+ "type": "text",
552
+ "link_info": "https://shsec.io/e9",
553
+ "link_blog": "",
554
+ "beacon_id": 139,
555
+ "name": "Remaining Offenses",
556
+ "summary": "Visitor Triggers The IP Offenses System Through A Firewall Block",
557
+ "description": "This message is displayed if the visitor triggered the IP Offenses system and reports how many offenses remain before being blocked."
558
+ },
559
+ {
560
+ "key": "autounblock_ips",
561
+ "section": "section_non_ui",
562
+ "transferable": false,
563
+ "type": "array",
564
+ "default": []
565
+ },
566
+ {
567
+ "key": "autounblock_emailids",
568
+ "section": "section_non_ui",
569
+ "transferable": false,
570
+ "type": "array",
571
+ "default": []
572
+ }
573
+ ],
574
+ "definitions": {
575
+ "allowable_ext_404s": [
576
+ "js",
577
+ "css",
578
+ "gif",
579
+ "jpg",
580
+ "jpeg",
581
+ "png",
582
+ "map",
583
+ "ttf",
584
+ "woff",
585
+ "woff2"
586
+ ],
587
+ "db_classes": {
588
+ "botsignals": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\BotSignals\\Handler",
589
+ "ip_lists": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\IPs\\Handler"
590
+ },
591
+ "ip_lists_table_name": "ip_lists",
592
+ "db_table_ip_lists": {
593
+ "slug": "ip_lists",
594
+ "cols_custom": {
595
+ "ip": "varchar(60) NOT NULL DEFAULT '' COMMENT 'Human readable IP address or range'",
596
+ "label": "varchar(255) NOT NULL DEFAULT '' COMMENT 'Description'",
597
+ "list": "varchar(4) NOT NULL DEFAULT '' COMMENT 'Block or Bypass'",
598
+ "ip6": "tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Is IPv6'",
599
+ "is_range": "tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Is Range'",
600
+ "transgressions": "int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Total Offenses'"
601
+ },
602
+ "cols_timestamps": {
603
+ "last_access_at": "Last Access By IP",
604
+ "blocked_at": "IP Blocked"
605
+ }
606
+ },
607
+ "db_table_botsignals": {
608
+ "autoexpire": 3,
609
+ "slug": "botsignals",
610
+ "col_older_than": "updated_at",
611
+ "has_updated_at": true,
612
+ "cols_custom": {
613
+ "ip": "varbinary(16) DEFAULT NULL COMMENT 'IP Address'"
614
+ },
615
+ "cols_timestamps": {
616
+ "notbot_at": "NotBot",
617
+ "frontpage_at": "Any Frontend Page Loaded",
618
+ "loginpage_at": "Login Page Loaded",
619
+ "bt404_at": "BotTrack 404",
620
+ "btfake_at": "BotTrack FakeWebCrawler",
621
+ "btcheese_at": "BotTrack LinkCheese",
622
+ "btloginfail_at": "BotTrack LoginFailed",
623
+ "btua_at": "BotTrack Useragent Fail",
624
+ "btxml_at": "BotTrack XMLRPC Access",
625
+ "btlogininvalid_at": "BotTrack LoginInvalid",
626
+ "btinvalidscript_at": "BotTrack InvalidScript",
627
+ "cooldown_at": "Triggered Cooldown",
628
+ "humanspam_at": "Comment Marked As Human SPAM",
629
+ "markspam_at": "Mark Comment As SPAM",
630
+ "unmarkspam_at": "Unmark Comment As SPAM",
631
+ "captchapass_at": "Captcha Passed",
632
+ "captchafail_at": "Captcha Failed",
633
+ "auth_at": "Successful Login",
634
+ "firewall_at": "Triggered Firewall",
635
+ "ratelimit_at": "Rate Limit Exceeded",
636
+ "offense_at": "Last Offense",
637
+ "blocked_at": "Last Block",
638
+ "unblocked_at": "Unblocked",
639
+ "bypass_at": "Bypass",
640
+ "snsent_at": "Sent To ShieldNET"
641
+ }
642
+ },
643
+ "events": {
644
+ "custom_offense": {
645
+ "audit_params": [
646
+ "message"
647
+ ],
648
+ "offense": true
649
+ },
650
+ "conn_kill": {
651
+ "level": "warning",
652
+ "audit_countable": true
653
+ },
654
+ "conn_not_kill_high_rep": {
655
+ "level": "debug"
656
+ },
657
+ "ip_offense": {
658
+ "level": "warning",
659
+ "audit_params": [
660
+ "from",
661
+ "to"
662
+ ]
663
+ },
664
+ "ip_blocked": {
665
+ "audit_params": [
666
+ "from",
667
+ "to"
668
+ ],
669
+ "level": "alert"
670
+ },
671
+ "ip_unblock": {
672
+ "level": "notice",
673
+ "offense": false,
674
+ "stat": false
675
+ },
676
+ "ip_block_auto": {
677
+ "audit_params": [
678
+ "ip"
679
+ ],
680
+ "level": "alert",
681
+ "offense": false,
682
+ "stat": false
683
+ },
684
+ "ip_block_manual": {
685
+ "audit_params": [
686
+ "ip"
687
+ ],
688
+ "level": "alert",
689
+ "offense": false,
690
+ "stat": false
691
+ },
692
+ "ip_bypass_add": {
693
+ "audit_params": [
694
+ "ip"
695
+ ],
696
+ "level": "alert",
697
+ "offense": false,
698
+ "stat": false
699
+ },
700
+ "ip_bypass_remove": {
701
+ "audit_params": [
702
+ "ip"
703
+ ],
704
+ "level": "alert",
705
+ "offense": false,
706
+ "stat": false
707
+ },
708
+ "ip_unblock_flag": {
709
+ "audit_params": [
710
+ "ip"
711
+ ],
712
+ "level": "alert"
713
+ },
714
+ "bottrack_notbot": {
715
+ "offense": false,
716
+ "stat": false,
717
+ "level": "debug"
718
+ },
719
+ "bottrack_404": {
720
+ "audit_params": [
721
+ "path"
722
+ ],
723
+ "offense": true
724
+ },
725
+ "bottrack_fakewebcrawler": {
726
+ "audit_params": [
727
+ "path",
728
+ "crawler"
729
+ ],
730
+ "offense": true
731
+ },
732
+ "bottrack_linkcheese": {
733
+ "audit_params": [
734
+ "path"
735
+ ],
736
+ "offense": true
737
+ },
738
+ "bottrack_loginfailed": {
739
+ "audit_params": [
740
+ "user_login"
741
+ ],
742
+ "level": "alert",
743
+ "offense": true
744
+ },
745
+ "bottrack_logininvalid": {
746
+ "audit_params": [
747
+ "user_login"
748
+ ],
749
+ "level": "alert",
750
+ "offense": true
751
+ },
752
+ "bottrack_xmlrpc": {
753
+ "audit_params": [
754
+ "path"
755
+ ],
756
+ "offense": true
757
+ },
758
+ "bottrack_invalidscript": {
759
+ "audit_params": [
760
+ "script"
761
+ ],
762
+ "offense": true
763
+ },
764
+ "comment_markspam": {
765
+ "level": "notice",
766
+ "offense": true
767
+ },
768
+ "comment_unmarkspam": {
769
+ "level": "info",
770
+ "offense": false,
771
+ "stat": false
772
+ }
773
+ }
774
+ }
775
+ }
config/license.json ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "license",
3
+ "properties": {
4
+ "slug": "license",
5
+ "name": "Pro Security",
6
+ "menu_title": "",
7
+ "show_module_menu_item": false,
8
+ "highlight_menu_item": true,
9
+ "tagline": "The Best In WordPress Security, Only Better.",
10
+ "auto_enabled": true,
11
+ "storage_key": "license",
12
+ "show_central": false,
13
+ "premium": false,
14
+ "access_restricted": true,
15
+ "run_if_whitelisted": true,
16
+ "run_if_verified_bot": true,
17
+ "run_if_wpcli": true
18
+ },
19
+ "admin_notices": {
20
+ "wphashes-token-fail": {
21
+ "id": "wphashes-token-fail",
22
+ "schedule": "conditions",
23
+ "valid_admin": true,
24
+ "plugin_page_only": true,
25
+ "can_dismiss": false,
26
+ "type": "error"
27
+ }
28
+ },
29
+ "menu_items": [
30
+ {
31
+ "title": "Go PRO!",
32
+ "slug": "pro-redirect",
33
+ "highlight": true
34
+ }
35
+ ],
36
+ "custom_redirects": [
37
+ {
38
+ "source_mod_page": "pro-redirect",
39
+ "target_mod_page": "insights",
40
+ "query_args": {
41
+ "inav": "license"
42
+ }
43
+ }
44
+ ],
45
+ "sections": [
46
+ {
47
+ "slug": "section_non_ui",
48
+ "hidden": true
49
+ }
50
+ ],
51
+ "options": [
52
+ {
53
+ "key": "license_key",
54
+ "section": "section_non_ui",
55
+ "sensitive": true,
56
+ "transferable": false,
57
+ "type": "text",
58
+ "default": ""
59
+ },
60
+ {
61
+ "key": "license_activated_at",
62
+ "section": "section_non_ui",
63
+ "transferable": false,
64
+ "type": "integer",
65
+ "default": 0
66
+ },
67
+ {
68
+ "key": "license_deactivated_at",
69
+ "section": "section_non_ui",
70
+ "transferable": false,
71
+ "type": "integer",
72
+ "default": 0
73
+ },
74
+ {
75
+ "key": "license_last_checked_at",
76
+ "section": "section_non_ui",
77
+ "transferable": false,
78
+ "type": "integer",
79
+ "default": 0
80
+ },
81
+ {
82
+ "key": "last_warning_email_sent_at",
83
+ "section": "section_non_ui",
84
+ "transferable": false,
85
+ "type": "integer",
86
+ "default": 0
87
+ },
88
+ {
89
+ "key": "last_deactivated_email_sent_at",
90
+ "section": "section_non_ui",
91
+ "transferable": false,
92
+ "type": "integer",
93
+ "default": 0
94
+ },
95
+ {
96
+ "key": "last_errors",
97
+ "section": "section_non_ui",
98
+ "transferable": false,
99
+ "type": "array",
100
+ "default": ""
101
+ },
102
+ {
103
+ "key": "last_error_at",
104
+ "section": "section_non_ui",
105
+ "sensitive": true,
106
+ "transferable": false,
107
+ "type": "integer",
108
+ "default": 0
109
+ },
110
+ {
111
+ "key": "keyless_handshake_hash",
112
+ "section": "section_non_ui",
113
+ "sensitive": true,
114
+ "transferable": false,
115
+ "type": "text",
116
+ "default": ""
117
+ },
118
+ {
119
+ "key": "keyless_handshake_until",
120
+ "section": "section_non_ui",
121
+ "sensitive": true,
122
+ "transferable": false,
123
+ "type": "integer",
124
+ "default": 0
125
+ },
126
+ {
127
+ "key": "license_data",
128
+ "section": "section_non_ui",
129
+ "sensitive": true,
130
+ "transferable": false,
131
+ "type": "array",
132
+ "default": []
133
+ },
134
+ {
135
+ "key": "wphashes_api_token",
136
+ "transferable": false,
137
+ "section": "section_non_ui",
138
+ "type": "array",
139
+ "default": []
140
+ }
141
+ ],
142
+ "definitions": {
143
+ "license_store_url_api": "https://api.getshieldsecurity.com/wp-json/odp-eddkeyless/v1",
144
+ "keyless_cp": "https://shsec.io/c5",
145
+ "license_item_name": "Shield Security Pro",
146
+ "license_item_id": "6047",
147
+ "license_item_name_sc": "Shield Security Pro (via Shield Central)",
148
+ "lic_verify_expire_days": 7,
149
+ "lic_verify_expire_grace_days": 3,
150
+ "keyless": true,
151
+ "keyless_handshake_expire": 90,
152
+ "events": {
153
+ "lic_check_success": {
154
+ "level": "debug",
155
+ "stat": false
156
+ },
157
+ "lic_fail_email": {
158
+ "level": "warning",
159
+ "stat": false
160
+ },
161
+ "lic_fail_deactivate": {
162
+ "level": "warning",
163
+ "stat": false
164
+ }
165
+ }
166
+ }
167
+ }
config/lockdown.json ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "lockdown",
3
+ "properties": {
4
+ "slug": "lockdown",
5
+ "name": "WP Lockdown",
6
+ "sidebar_name": "Lockdown",
7
+ "show_module_menu_item": false,
8
+ "show_module_options": true,
9
+ "storage_key": "lockdown",
10
+ "tagline": "Harden the more loosely controlled settings of your site",
11
+ "show_central": true,
12
+ "access_restricted": true,
13
+ "premium": false,
14
+ "run_if_whitelisted": false,
15
+ "run_if_verified_bot": false,
16
+ "run_if_wpcli": false,
17
+ "order": 90
18
+ },
19
+ "sections": [
20
+ {
21
+ "primary": true,
22
+ "slug": "section_apixml",
23
+ "title": "WordPress System Lockdown",
24
+ "title_short": "System",
25
+ "beacon_id": 413,
26
+ "summary": [
27
+ "Purpose - Lockdown certain core WordPress system features.",
28
+ "Recommendation - This depends on your usage and needs for certain WordPress functions and features."
29
+ ]
30
+ },
31
+ {
32
+ "slug": "section_permission_access_options",
33
+ "title": "Permissions and Access Options",
34
+ "title_short": "Permissions",
35
+ "beacon_id": 415,
36
+ "summary": [
37
+ "Purpose - Provides finer control of certain WordPress permissions.",
38
+ "Recommendation - Only enable SSL if you have a valid certificate installed."
39
+ ]
40
+ },
41
+ {
42
+ "slug": "section_wordpress_obscurity_options",
43
+ "title": "WordPress Obscurity Options",
44
+ "title_short": "Obscurity",
45
+ "beacon_id": 418,
46
+ "summary": [
47
+ "Purpose - Obscures certain WordPress settings from public view.",
48
+ "Recommendation - Obscurity is not true security and so these settings are down to your personal tastes."
49
+ ]
50
+ },
51
+ {
52
+ "slug": "section_enable_plugin_feature_wordpress_lockdown",
53
+ "title": "Enable Module: Lockdown",
54
+ "title_short": "Disable Module",
55
+ "beacon_id": 272,
56
+ "summary": [
57
+ "Purpose - Lockdown helps secure-up certain loosely-controlled WordPress settings on your site.",
58
+ "Recommendation - Keep the Lockdown feature turned on."
59
+ ]
60
+ },
61
+ {
62
+ "slug": "section_non_ui",
63
+ "hidden": true
64
+ }
65
+ ],
66
+ "options": [
67
+ {
68
+ "key": "enable_lockdown",
69
+ "section": "section_enable_plugin_feature_wordpress_lockdown",
70
+ "advanced": true,
71
+ "default": "Y",
72
+ "type": "checkbox",
73
+ "link_info": "https://shsec.io/4r",
74
+ "link_blog": "",
75
+ "beacon_id": 272,
76
+ "name": "Enable Lockdown",
77
+ "summary": "Enable (or Disable) The Lockdown module",
78
+ "description": "Un-Checking this option will completely disable the Lockdown module"
79
+ },
80
+ {
81
+ "key": "disable_xmlrpc",
82
+ "section": "section_apixml",
83
+ "default": "N",
84
+ "type": "checkbox",
85
+ "link_info": "https://shsec.io/e6",
86
+ "link_blog": "https://shsec.io/fb",
87
+ "beacon_id": 414,
88
+ "name": "Disable XML-RPC",
89
+ "summary": "Disable The XML-RPC System",
90
+ "description": "Checking this option will completely turn off the whole XML-RPC system."
91
+ },
92
+ {
93
+ "key": "disable_anonymous_restapi",
94
+ "section": "section_apixml",
95
+ "advanced": true,
96
+ "default": "N",
97
+ "type": "checkbox",
98
+ "link_info": "",
99
+ "link_blog": "",
100
+ "name": "Anonymous Rest API",
101
+ "summary": "Disable The Anonymous Rest API",
102
+ "description": "Checking this option will completely turn off the whole Anonymous Rest API system."
103
+ },
104
+ {
105
+ "key": "api_namespace_exclusions",
106
+ "section": "section_non_ui",
107
+ "default": [
108
+ "contact-form-7",
109
+ "jetpack",
110
+ "woocommerce"
111
+ ],
112
+ "type": "array",
113
+ "link_info": "",
114
+ "link_blog": "",
115
+ "name": "Rest API Exclusions",
116
+ "summary": "Anonymous REST API Exclusions",
117
+ "description": "Any namespaces provided here will be excluded from the Anonymous API restriction."
118
+ },
119
+ {
120
+ "key": "disable_file_editing",
121
+ "section": "section_permission_access_options",
122
+ "default": "N",
123
+ "type": "checkbox",
124
+ "link_info": "https://shsec.io/4q",
125
+ "link_blog": "https://shsec.io/hk",
126
+ "beacon_id": 416,
127
+ "name": "Disable File Editing",
128
+ "summary": "Disable Ability To Edit Files From Within WordPress",
129
+ "description": "Removes the option to directly edit any files from within the WordPress admin area. Equivalent to setting 'DISALLOW_FILE_EDIT' to TRUE."
130
+ },
131
+ {
132
+ "key": "force_ssl_admin",
133
+ "section": "section_permission_access_options",
134
+ "default": "N",
135
+ "type": "checkbox",
136
+ "link_info": "https://shsec.io/4t",
137
+ "link_blog": "",
138
+ "beacon_id": 417,
139
+ "name": "Force SSL Admin",
140
+ "summary": "Forces WordPress Admin Dashboard To Be Delivered Over SSL",
141
+ "description": "Please only enable this option if you have a valid SSL certificate installed. Equivalent to setting 'FORCE_SSL_ADMIN' to TRUE."
142
+ },
143
+ {
144
+ "key": "block_author_discovery",
145
+ "section": "section_wordpress_obscurity_options",
146
+ "default": "Y",
147
+ "type": "checkbox",
148
+ "link_info": "https://shsec.io/wpsf23",
149
+ "link_blog": "",
150
+ "name": "Block Username Fishing",
151
+ "summary": "Block the ability to discover WordPress usernames based on author IDs",
152
+ "description": "When enabled, any URL requests containing 'author=' will be killed. Warning: Enabling this option may interfere with expected operations of your site."
153
+ },
154
+ {
155
+ "key": "clean_wp_rubbish",
156
+ "section": "section_wordpress_obscurity_options",
157
+ "default": "N",
158
+ "type": "checkbox",
159
+ "link_info": "",
160
+ "link_blog": "",
161
+ "name": "Clean WP Files",
162
+ "summary": "Automatically Delete Unnecessary WP Files",
163
+ "description": "Automatically delete WordPress files like wp-config-sample.php."
164
+ },
165
+ {
166
+ "key": "hide_wordpress_generator_tag",
167
+ "section": "section_wordpress_obscurity_options",
168
+ "default": "N",
169
+ "type": "checkbox",
170
+ "link_info": "",
171
+ "link_blog": "",
172
+ "name": "WP Generator Tag",
173
+ "summary": "Remove WP Generator Meta Tag",
174
+ "description": "Remove a meta tag from your WordPress pages that publicly displays that your site is WordPress and its current version."
175
+ }
176
+ ],
177
+ "definitions": {
178
+ "default_restapi_exclusions": [
179
+ "contact-form-7",
180
+ "jetpack",
181
+ "tho",
182
+ "wpstatistics",
183
+ "woocommerce"
184
+ ],
185
+ "events": {
186
+ "block_anonymous_restapi": {
187
+ "audit_params": [
188
+ "namespace"
189
+ ],
190
+ "level": "warning",
191
+ "recent": true
192
+ },
193
+ "block_xml": {
194
+ "level": "notice",
195
+ "recent": true,
196
+ "offense": true
197
+ }
198
+ }
199
+ }
200
+ }
config/login_protect.json ADDED
@@ -0,0 +1,551 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "login_protect",
3
+ "properties": {
4
+ "slug": "login_protect",
5
+ "name": "Login Guard",
6
+ "sidebar_name": "Login Protection",
7
+ "show_module_menu_item": false,
8
+ "show_module_options": true,
9
+ "storage_key": "loginprotect",
10
+ "tagline": "Block brute force attacks and secure user identities with Two-Factor Authentication",
11
+ "show_central": true,
12
+ "access_restricted": true,
13
+ "premium": false,
14
+ "run_if_whitelisted": true,
15
+ "run_if_verified_bot": false,
16
+ "run_if_wpcli": false,
17
+ "order": 40
18
+ },
19
+ "admin_notices": {
20
+ "email-verification-sent": {
21
+ "id": "email-verification-sent",
22
+ "schedule": "conditions",
23
+ "plugin_page_only": true,
24
+ "can_dismiss": false,
25
+ "type": "warning",
26
+ "plugin_admin": "yes",
27
+ "valid_admin": true
28
+ }
29
+ },
30
+ "sections": [
31
+ {
32
+ "slug": "section_brute_force_login_protection",
33
+ "primary": true,
34
+ "title": "Brute Force Login Guard",
35
+ "title_short": "Brute Force",
36
+ "beacon_id": 325,
37
+ "summary": [
38
+ "Purpose - Blocks brute force hacking attacks against your login and registration pages.",
39
+ "Recommendation - Use of this feature is highly recommend."
40
+ ]
41
+ },
42
+ {
43
+ "slug": "section_2fa_email",
44
+ "title": "Email Two-Factor Authentication",
45
+ "title_short": "2FA - Email",
46
+ "beacon_id": 246,
47
+ "summary": [
48
+ "Purpose - Verifies the identity of users who log in to your site using email-based one-time-passwords.",
49
+ "Recommendation - Use of this feature is highly recommend. However, if your host blocks email sending you may lock yourself out.",
50
+ "Note: You may combine multiple authentication factors for increased security."
51
+ ]
52
+ },
53
+ {
54
+ "slug": "section_2fa_ga",
55
+ "title": "Google Authenticator Two-Factor Authentication",
56
+ "title_short": "2FA - Google Authenticator",
57
+ "beacon_id": 244,
58
+ "summary": [
59
+ "Purpose - Verifies the identity of users who log in to your site using Google Authenticator one-time-passwords.",
60
+ "Recommendation - Use of this feature is highly recommend. However, if your host blocks email sending you may lock yourself out.",
61
+ "Note: You may combine multiple authentication factors for increased security."
62
+ ]
63
+ },
64
+ {
65
+ "slug": "section_hardware_authentication",
66
+ "title": "Hardware 2-Factor Authentication",
67
+ "title_short": "2FA - Hardware",
68
+ "beacon_id": 249,
69
+ "summary": [
70
+ "Purpose - Verifies the identity of users who log in to your site using Yubikey one-time-passwords.",
71
+ "Note: You may combine multiple authentication factors for increased security."
72
+ ]
73
+ },
74
+ {
75
+ "slug": "section_multifactor_authentication",
76
+ "title": "Multi-Factor Authentication",
77
+ "title_short": "2-Factor Auth",
78
+ "beacon_id": 326,
79
+ "summary": [
80
+ "Purpose - Verifies the identity of users who log in to your site - i.e. they are who they say they are.",
81
+ "Recommendation - Use of this feature is highly recommend. However, if your host blocks email sending you may lock yourself out.",
82
+ "Note: You may combine multiple authentication factors for increased security."
83
+ ]
84
+ },
85
+ {
86
+ "slug": "section_rename_wplogin",
87
+ "title": "Hide WP Login Page",
88
+ "title_short": "Hide Login Page",
89
+ "beacon_id": 316,
90
+ "summary": [
91
+ "Purpose - To hide your wp-login.php page from brute force attacks and hacking attempts - if your login page cannot be found, no-one can login.",
92
+ "Recommendation - This is not required for complete security and if your site has irregular or inconsistent configuration it may not work for you."
93
+ ]
94
+ },
95
+ {
96
+ "slug": "section_user_messages",
97
+ "title": "User Messages",
98
+ "title_short": "User Messages",
99
+ "beacon_id": 139,
100
+ "summary": [
101
+ "Purpose - Customize the messages shown to visitors.",
102
+ "Recommendation - Be sure to change the messages to suit your audience.",
103
+ "Hint - To reset any message to its default, enter the text exactly: default"
104
+ ]
105
+ },
106
+ {
107
+ "slug": "section_enable_plugin_feature_login_protection",
108
+ "title": "Disable Login Guard Module",
109
+ "title_short": "Disable",
110
+ "beacon_id": 249,
111
+ "summary": [
112
+ "Purpose - Login Guard blocks all automated and brute force attempts to log in to your site.",
113
+ "Recommendation - Keep the Login Guard module turned on."
114
+ ]
115
+ },
116
+ {
117
+ "slug": "section_non_ui",
118
+ "hidden": true
119
+ }
120
+ ],
121
+ "options": [
122
+ {
123
+ "key": "enable_login_protect",
124
+ "section": "section_enable_plugin_feature_login_protection",
125
+ "advanced": true,
126
+ "default": "Y",
127
+ "type": "checkbox",
128
+ "link_info": "https://shsec.io/51",
129
+ "link_blog": "https://shsec.io/wpsf03",
130
+ "beacon_id": 249,
131
+ "name": "Enable Login Guard",
132
+ "summary": "Enable (or Disable) The Login Guard Module",
133
+ "description": "Un-Checking this option will completely disable the Login Guard module"
134
+ },
135
+ {
136
+ "key": "rename_wplogin_path",
137
+ "section": "section_rename_wplogin",
138
+ "advanced": true,
139
+ "sensitive": true,
140
+ "default": "",
141
+ "type": "text",
142
+ "link_info": "https://shsec.io/5q",
143
+ "link_blog": "https://shsec.io/5r",
144
+ "beacon_id": 316,
145
+ "name": "Hide Login Page",
146
+ "summary": "Rename The WordPress Login Page",
147
+ "description": "Creating a path here will disable your 'wp-login.php'. Only letters and numbers are permitted: abc123"
148
+ },
149
+ {
150
+ "key": "enable_chained_authentication",
151
+ "section": "section_multifactor_authentication",
152
+ "default": "N",
153
+ "type": "checkbox",
154
+ "link_info": "https://shsec.io/9r",
155
+ "link_blog": "https://shsec.io/84",
156
+ "beacon_id": 326,
157
+ "name": "Multi-Factor Authentication",
158
+ "summary": "Require All Active Authentication Factors",
159
+ "description": "When enabled, all multi-factor authentication methods will be applied to a user login. Disable to only require one to pass."
160
+ },
161
+ {
162
+ "key": "mfa_skip",
163
+ "section": "section_multifactor_authentication",
164
+ "premium": true,
165
+ "default": 0,
166
+ "min": 0,
167
+ "type": "integer",
168
+ "link_info": "https://shsec.io/b1",
169
+ "link_blog": "",
170
+ "beacon_id": 141,
171
+ "name": "Multi-Factor Bypass",
172
+ "summary": "A User Can Bypass Multi-Factor Authentication (MFA) For The Set Number Of Days",
173
+ "description": "Enter the number of days a user can bypass future MFA after a successful MFA-login. 0 to disable."
174
+ },
175
+ {
176
+ "key": "allow_backupcodes",
177
+ "section": "section_multifactor_authentication",
178
+ "premium": true,
179
+ "default": "N",
180
+ "type": "checkbox",
181
+ "link_info": "https://shsec.io/dx",
182
+ "link_blog": "https://shsec.io/dy",
183
+ "beacon_id": 143,
184
+ "name": "Allow Backup Codes",
185
+ "summary": "Allow Users To Generate A Backup Code",
186
+ "description": "Allow users to generate a backup code that can be used to login if MFA factors are unavailable."
187
+ },
188
+ {
189
+ "key": "enable_google_authenticator",
190
+ "section": "section_2fa_ga",
191
+ "default": "N",
192
+ "type": "checkbox",
193
+ "link_info": "https://shsec.io/shld7",
194
+ "link_blog": "https://shsec.io/shld6",
195
+ "beacon_id": 245,
196
+ "name": "Enable Google Authenticator",
197
+ "summary": "Allow Users To Use Google Authenticator",
198
+ "description": "When enabled, users will have the option to add Google Authenticator to their WordPress user profile."
199
+ },
200
+ {
201
+ "key": "enable_email_authentication",
202
+ "section": "section_2fa_email",
203
+ "default": "N",
204
+ "type": "checkbox",
205
+ "link_info": "https://shsec.io/3t",
206
+ "link_blog": "https://shsec.io/9q",
207
+ "beacon_id": 247,
208
+ "name": "Enable Email Authentication",
209
+ "summary": "Two-Factor Login Authentication By Email",
210
+ "description": "All users will be required to verify their login by email-based two-factor authentication."
211
+ },
212
+ {
213
+ "key": "two_factor_auth_user_roles",
214
+ "section": "section_2fa_email",
215
+ "advanced": true,
216
+ "type": "multiple_select",
217
+ "default": [
218
+ "contributor",
219
+ "author",
220
+ "editor",
221
+ "administrator"
222
+ ],
223
+ "value_options": [
224
+ {
225
+ "value_key": "subscriber",
226
+ "text": "Subscribers"
227
+ },
228
+ {
229
+ "value_key": "contributor",
230
+ "text": "Contributors"
231
+ },
232
+ {
233
+ "value_key": "author",
234
+ "text": "Authors"
235
+ },
236
+ {
237
+ "value_key": "editor",
238
+ "text": "Editors"
239
+ },
240
+ {
241
+ "value_key": "administrator",
242
+ "text": "Administrators"
243
+ },
244
+ {
245
+ "value_key": "customer",
246
+ "text": "[Woo] Customer"
247
+ },
248
+ {
249
+ "value_key": "shop_manager",
250
+ "text": "[Woo/EDD] Shop Manager"
251
+ },
252
+ {
253
+ "value_key": "shop_accountant",
254
+ "text": "[EDD] Shop Accountant"
255
+ },
256
+ {
257
+ "value_key": "shop_worker",
258
+ "text": "[EDD] Shop Worker"
259
+ },
260
+ {
261
+ "value_key": "edd_subscriber",
262
+ "text": "[EDD] Customer"
263
+ }
264
+ ],
265
+ "link_info": "https://shsec.io/4v",
266
+ "link_blog": "",
267
+ "beacon_id": 243,
268
+ "name": "Enforce - Email Authentication",
269
+ "summary": "All User Roles Subject To Email Authentication",
270
+ "description": "Enforces email-based authentication on all users with the selected roles. Note: This setting only applies to email authentication."
271
+ },
272
+ {
273
+ "key": "email_any_user_set",
274
+ "section": "section_2fa_email",
275
+ "premium": true,
276
+ "default": "N",
277
+ "type": "checkbox",
278
+ "link_info": "https://shsec.io/gj",
279
+ "link_blog": "",
280
+ "beacon_id": 142,
281
+ "name": "Allow Any User",
282
+ "summary": "Allow Any User To Turn-On Two-Factor Authentication By Email",
283
+ "description": "Allow Any User To Turn-On Two-Factor Authentication By Email."
284
+ },
285
+ {
286
+ "key": "enable_antibot_check",
287
+ "section": "section_brute_force_login_protection",
288
+ "default": "N",
289
+ "type": "checkbox",
290
+ "link_info": "https://shsec.io/k0",
291
+ "link_blog": "https://shsec.io/jo",
292
+ "beacon_id": 426,
293
+ "name": "AntiBot",
294
+ "summary": "Use Experimental AntiBot Detection Engine",
295
+ "description": "Use Shield's AntiBot Detection Engine In-Place of GASP/CAPTCHA Bot checking."
296
+ },
297
+ {
298
+ "key": "bot_protection_locations",
299
+ "section": "section_brute_force_login_protection",
300
+ "type": "multiple_select",
301
+ "default": [
302
+ "login"
303
+ ],
304
+ "value_options": [
305
+ {
306
+ "value_key": "login",
307
+ "text": "Login"
308
+ },
309
+ {
310
+ "value_key": "register",
311
+ "text": "Register"
312
+ },
313
+ {
314
+ "value_key": "password",
315
+ "text": "Lost Password"
316
+ },
317
+ {
318
+ "value_key": "checkout_woo",
319
+ "text": "Checkout (WooCommerce)"
320
+ }
321
+ ],
322
+ "link_info": "https://shsec.io/dv",
323
+ "link_blog": "",
324
+ "beacon_id": 314,
325
+ "name": "Protection Locations",
326
+ "summary": "How Google reCAPTCHA Will Be Displayed",
327
+ "description": "Choose for which forms bot protection measures will be deployed."
328
+ },
329
+ {
330
+ "key": "login_limit_interval",
331
+ "section": "section_brute_force_login_protection",
332
+ "default": "5",
333
+ "min": 0,
334
+ "type": "integer",
335
+ "link_info": "https://shsec.io/3q",
336
+ "link_blog": "https://shsec.io/9o",
337
+ "beacon_id": 242,
338
+ "name": "Login Cooldown Interval",
339
+ "summary": "Limit login attempts to every X seconds",
340
+ "description": "WordPress will process only ONE login attempt for every number of seconds specified. Zero (0) turns this off."
341
+ },
342
+ {
343
+ "key": "enable_login_gasp_check",
344
+ "section": "section_brute_force_login_protection",
345
+ "default": "N",
346
+ "type": "checkbox",
347
+ "link_info": "https://shsec.io/3r",
348
+ "link_blog": "https://shsec.io/9n",
349
+ "beacon_id": 313,
350
+ "name": "Bot Protection",
351
+ "summary": "Protect WP Login From Automated Login Attempts By Bots",
352
+ "description": "Adds a dynamically (Javascript) generated checkbox to the login form that prevents bots using automated login techniques. Recommended: ON."
353
+ },
354
+ {
355
+ "key": "enable_google_recaptcha_login",
356
+ "section": "section_brute_force_login_protection",
357
+ "default": "disabled",
358
+ "type": "select",
359
+ "value_options": [
360
+ {
361
+ "value_key": "disabled",
362
+ "text": "Disabled"
363
+ },
364
+ {
365
+ "value_key": "default",
366
+ "text": "Default Style"
367
+ },
368
+ {
369
+ "value_key": "light",
370
+ "text": "Light Theme"
371
+ },
372
+ {
373
+ "value_key": "dark",
374
+ "text": "Dark Theme"
375
+ },
376
+ {
377
+ "value_key": "invisible",
378
+ "text": "Invisible"
379
+ }
380
+ ],
381
+ "link_info": "https://shsec.io/9m",
382
+ "link_blog": "",
383
+ "beacon_id": 269,
384
+ "name": "CAPTCHA",
385
+ "summary": "Enable CAPTCHA",
386
+ "description": "Use CAPTCHA on the login screen."
387
+ },
388
+ {
389
+ "key": "antibot_form_ids",
390
+ "section": "section_brute_force_login_protection",
391
+ "advanced": true,
392
+ "premium": true,
393
+ "type": "array",
394
+ "default": [],
395
+ "link_info": "https://shsec.io/hg",
396
+ "link_blog": "",
397
+ "beacon_id": 144,
398
+ "name": "AntiBot Forms",
399
+ "summary": "Enter The IDs Of The 3rd Party Login Forms For Use With AntiBot JS",
400
+ "description": "For Use With AnitBot JS (above)."
401
+ },
402
+ {
403
+ "key": "enable_u2f",
404
+ "section": "section_hardware_authentication",
405
+ "premium": true,
406
+ "default": "N",
407
+ "type": "checkbox",
408
+ "link_info": "https://shsec.io/i9",
409
+ "link_blog": "",
410
+ "name": "Allow U2F",
411
+ "summary": "Allow Registration Of U2F Devices",
412
+ "description": "Allow Registration Of U2F Devices."
413
+ },
414
+ {
415
+ "key": "enable_yubikey",
416
+ "section": "section_hardware_authentication",
417
+ "default": "N",
418
+ "type": "checkbox",
419
+ "link_info": "https://shsec.io/4f",
420
+ "link_blog": "https://shsec.io/9t",
421
+ "beacon_id": 358,
422
+ "name": "Allow Yubikey OTP",
423
+ "summary": "Allow Yubikey Registration For One Time Passwords",
424
+ "description": "Combined with your Yubikey API Key (below) this will form the basis of your Yubikey Authentication."
425
+ },
426
+ {
427
+ "key": "yubikey_app_id",
428
+ "section": "section_hardware_authentication",
429
+ "sensitive": true,
430
+ "default": "",
431
+ "type": "text",
432
+ "link_info": "https://shsec.io/4g",
433
+ "link_blog": "",
434
+ "beacon_id": 360,
435
+ "name": "Yubikey App ID",
436
+ "summary": "Your Unique Yubikey App ID",
437
+ "description": "Combined with your Yubikey API Key this will form the basis of your Yubikey Authentication."
438
+ },
439
+ {
440
+ "key": "yubikey_api_key",
441
+ "section": "section_hardware_authentication",
442
+ "sensitive": true,
443
+ "default": "",
444
+ "type": "text",
445
+ "link_info": "https://shsec.io/4g",
446
+ "link_blog": "",
447
+ "beacon_id": 360,
448
+ "name": "Yubikey API Key",
449
+ "summary": "Your Unique Yubikey App API Key",
450
+ "description": "Combined with your Yubikey App ID this will form the basis of your Yubikey Authentication."
451
+ },
452
+ {
453
+ "key": "text_imahuman",
454
+ "section": "section_user_messages",
455
+ "sensitive": true,
456
+ "premium": true,
457
+ "default": "default",
458
+ "type": "text",
459
+ "link_info": "https://shsec.io/dz",
460
+ "link_blog": "",
461
+ "name": "GASP Checkbox Text",
462
+ "summary": "The Message Displayed Next To The GASP Checkbox",
463
+ "description": "You can change the text displayed to the user beside the checkbox if you need a customized message."
464
+ },
465
+ {
466
+ "key": "text_pleasecheckbox",
467
+ "section": "section_user_messages",
468
+ "sensitive": true,
469
+ "premium": true,
470
+ "default": "default",
471
+ "type": "text",
472
+ "link_info": "https://shsec.io/dz",
473
+ "link_blog": "",
474
+ "name": "GASP Alert Text",
475
+ "summary": "The Message Displayed If The User Doesn't Check The Box",
476
+ "description": "You can change the text displayed to the user in the alert message if they don't check the box."
477
+ },
478
+ {
479
+ "key": "email_can_send_verified_at",
480
+ "section": "section_non_ui",
481
+ "transferable": false,
482
+ "type": "integer",
483
+ "default": 0,
484
+ "min": 0
485
+ },
486
+ {
487
+ "key": "gasp_key",
488
+ "section": "section_non_ui",
489
+ "transferable": false,
490
+ "sensitive": true,
491
+ "type": "text",
492
+ "default": ""
493
+ },
494
+ {
495
+ "key": "use_login_intent_page",
496
+ "section": "section_non_ui",
497
+ "transferable": false,
498
+ "type": "boolean",
499
+ "value": true
500
+ }
501
+ ],
502
+ "definitions": {
503
+ "login_intent_timeout": 5,
504
+ "events": {
505
+ "2fa_verify_success": {
506
+ "audit_params": [
507
+ "user_login",
508
+ "method"
509
+ ],
510
+ "level": "notice"
511
+ },
512
+ "2fa_verify_fail": {
513
+ "audit_params": [
514
+ "user_login",
515
+ "method"
516
+ ],
517
+ "level": "warning",
518
+ "offense": true
519
+ },
520
+ "cooldown_fail": {
521
+ "level": "warning"
522
+ },
523
+ "honeypot_fail": {
524
+ "audit_params": [
525
+ "user_login",
526
+ "action"
527
+ ],
528
+ "level": "warning"
529
+ },
530
+ "botbox_fail": {
531
+ "audit_params": [
532
+ "user_login",
533
+ "action"
534
+ ],
535
+ "level": "warning"
536
+ },
537
+ "login_block": {
538
+ "level": "warning",
539
+ "recent": true,
540
+ "offense": true
541
+ },
542
+ "hide_login_url": {
543
+ "level": "notice"
544
+ },
545
+ "2fa_success": {
546
+ "level": "info",
547
+ "recent": true
548
+ }
549
+ }
550
+ }
551
+ }
config/plugin.json ADDED
@@ -0,0 +1,785 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "properties": {
3
+ "slug": "plugin",
4
+ "name": "General Settings",
5
+ "sidebar_name": "General",
6
+ "menu_title": "Configuration",
7
+ "show_module_menu_item": true,
8
+ "show_module_options": true,
9
+ "storage_key": "plugin",
10
+ "tagline": "General Plugin Settings",
11
+ "auto_enabled": true,
12
+ "show_central": true,
13
+ "access_restricted": true,
14
+ "premium": false,
15
+ "run_if_whitelisted": true,
16
+ "run_if_verified_bot": true,
17
+ "run_if_wpcli": true,
18
+ "order": 10
19
+ },
20
+ "admin_notices": {
21
+ "plugin-too-old": {
22
+ "id": "plugin-too-old",
23
+ "schedule": "conditions",
24
+ "valid_admin": true,
25
+ "plugin_page_only": false,
26
+ "can_dismiss": true,
27
+ "type": "error"
28
+ },
29
+ "override-forceoff": {
30
+ "id": "override-forceoff",
31
+ "schedule": "conditions",
32
+ "valid_admin": true,
33
+ "plugin_page_only": false,
34
+ "can_dismiss": false,
35
+ "type": "error"
36
+ },
37
+ "plugin-disabled": {
38
+ "id": "plugin-disabled",
39
+ "schedule": "conditions",
40
+ "valid_admin": true,
41
+ "plugin_page_only": true,
42
+ "can_dismiss": false,
43
+ "type": "error"
44
+ },
45
+ "update-available": {
46
+ "id": "update-available",
47
+ "schedule": "conditions",
48
+ "valid_admin": true,
49
+ "plugin_page_only": true,
50
+ "can_dismiss": false,
51
+ "type": "error"
52
+ },
53
+ "compat-sgoptimize": {
54
+ "id": "compat-sgoptimize",
55
+ "schedule": "conditions",
56
+ "valid_admin": true,
57
+ "plugin_admin": "ignore",
58
+ "plugin_page_only": false,
59
+ "can_dismiss": false,
60
+ "type": "warning"
61
+ },
62
+ "wizard_welcome": {
63
+ "id": "wizard_welcome",
64
+ "per_user": false,
65
+ "type": "info"
66
+ },
67
+ "plugin-mailing-list-signup": {
68
+ "id": "plugin-mailing-list-signup",
69
+ "min_install_days": 5,
70
+ "type": "promo",
71
+ "drip_form_id": "250437573"
72
+ },
73
+ "allow-tracking": {
74
+ "id": "allow-tracking",
75
+ "plugin_admin": true,
76
+ "min_install_days": 3,
77
+ "type": "promo"
78
+ },
79
+ "rate-plugin": {
80
+ "id": "rate-plugin",
81
+ "min_install_days": 30,
82
+ "type": "promo"
83
+ }
84
+ },
85
+ "sections": [
86
+ {
87
+ "slug": "section_defaults",
88
+ "primary": true,
89
+ "title": "Plugin Defaults",
90
+ "title_short": "Plugin Defaults",
91
+ "beacon_id": 389
92
+ },
93
+ {
94
+ "slug": "section_general_plugin_options",
95
+ "title": "General Plugin Options",
96
+ "title_short": "General Options"
97
+ },
98
+ {
99
+ "slug": "section_third_party_captcha",
100
+ "title": "CAPTCHA",
101
+ "title_short": "CAPTCHA",
102
+ "beacon_id": 390
103
+ },
104
+ {
105
+ "slug": "section_importexport",
106
+ "title": "Import / Export",
107
+ "title_short": "Import / Export",
108
+ "beacon_id": 129
109
+ },
110
+ {
111
+ "slug": "section_integrations",
112
+ "title": "Integrations",
113
+ "title_short": "Integrations"
114
+ },
115
+ {
116
+ "slug": "section_global_security_options",
117
+ "title": "Global Plugin Security Options",
118
+ "title_short": "Disable Shield"
119
+ },
120
+ {
121
+ "slug": "section_non_ui",
122
+ "hidden": true
123
+ }
124
+ ],
125
+ "options": [
126
+ {
127
+ "key": "global_enable_plugin_features",
128
+ "section": "section_global_security_options",
129
+ "default": "Y",
130
+ "type": "checkbox",
131
+ "link_info": "",
132
+ "link_blog": "",
133
+ "beacon_id": 389,
134
+ "name": "Enable/Disable All Plugin Modules",
135
+ "summary": "Global Plugin On/Off Switch",
136
+ "description": "Uncheck this option to disable all Shield features"
137
+ },
138
+ {
139
+ "key": "enable_tracking",
140
+ "section": "section_general_plugin_options",
141
+ "default": "N",
142
+ "type": "checkbox",
143
+ "link_info": "https://shsec.io/7i",
144
+ "link_blog": "",
145
+ "name": "Enable Information Gathering",
146
+ "summary": "Permit Anonymous Usage Information Gathering",
147
+ "description": "Allows us to gather information on statistics and features in-use across our client installations. This information is strictly anonymous and contains no personally, or otherwise, identifiable data."
148
+ },
149
+ {
150
+ "key": "enable_shieldnet",
151
+ "section": "section_general_plugin_options",
152
+ "premium": true,
153
+ "default": "Y",
154
+ "type": "checkbox",
155
+ "beacon_id": 437,
156
+ "link_info": "https://shsec.io/kb",
157
+ "link_blog": "https://shsec.io/kc",
158
+ "name": "Enable ShieldNET",
159
+ "summary": "Enhanced Website Security Through Network Intelligence",
160
+ "description": "Enhanced Website Security Through Network Intelligence."
161
+ },
162
+ {
163
+ "key": "show_advanced",
164
+ "section": "section_non_ui",
165
+ "default": "Y",
166
+ "type": "checkbox",
167
+ "link_info": "",
168
+ "link_blog": "",
169
+ "name": "Show All Options",
170
+ "summary": "Show All Options Including Those Marked As Advanced",
171
+ "description": "Shield hides advanced options from view to simplify display. Turn this option on to display advanced options at all times."
172
+ },
173
+ {
174
+ "key": "visitor_address_source",
175
+ "section": "section_defaults",
176
+ "advanced": true,
177
+ "sensitive": false,
178
+ "type": "select",
179
+ "default": "AUTO_DETECT_IP",
180
+ "value_options": [
181
+ {
182
+ "value_key": "AUTO_DETECT_IP",
183
+ "text": "Automatically Detect Visitor IP"
184
+ },
185
+ {
186
+ "value_key": "REMOTE_ADDR",
187
+ "text": "REMOTE_ADDR"
188
+ },
189
+ {
190
+ "value_key": "HTTP_CF_CONNECTING_IP",
191
+ "text": "HTTP_CF_CONNECTING_IP"
192
+ },
193
+ {
194
+ "value_key": "HTTP_X_FORWARDED_FOR",
195
+ "text": "HTTP_X_FORWARDED_FOR"
196
+ },
197
+ {
198
+ "value_key": "HTTP_X_FORWARDED",
199
+ "text": "HTTP_X_FORWARDED"
200
+ },
201
+ {
202
+ "value_key": "HTTP_X_REAL_IP",
203
+ "text": "HTTP_X_REAL_IP"
204
+ },
205
+ {
206
+ "value_key": "HTTP_X_SUCURI_CLIENTIP",
207
+ "text": "HTTP_X_SUCURI_CLIENTIP"
208
+ },
209
+ {
210
+ "value_key": "HTTP_INCAP_CLIENT_IP",
211
+ "text": "HTTP_INCAP_CLIENT_IP"
212
+ },
213
+ {
214
+ "value_key": "HTTP_X_SP_FORWARDED_IP",
215
+ "text": "HTTP_X_SP_FORWARDED_IP"
216
+ },
217
+ {
218
+ "value_key": "HTTP_FORWARDED",
219
+ "text": "HTTP_FORWARDED"
220
+ },
221
+ {
222
+ "value_key": "HTTP_CLIENT_IP",
223
+ "text": "HTTP_CLIENT_IP"
224
+ }
225
+ ],
226
+ "link_info": "https://shsec.io/dn",
227
+ "link_blog": "",
228
+ "beacon_id": 391,
229
+ "name": "Visitor IP Address",
230
+ "summary": "Which Address Is Yours",
231
+ "description": "There are many way to detect visitor IP addresses. Please select yours from the list."
232
+ },
233
+ {
234
+ "key": "block_send_email_address",
235
+ "section": "section_defaults",
236
+ "sensitive": true,
237
+ "default": "",
238
+ "type": "email",
239
+ "link_info": "",
240
+ "link_blog": "",
241
+ "name": "Report Email",
242
+ "summary": "Where to send email reports",
243
+ "description": "If this is empty, it will default to the blog admin email address."
244
+ },
245
+ {
246
+ "key": "enable_upgrade_admin_notice",
247
+ "section": "section_general_plugin_options",
248
+ "default": "Y",
249
+ "type": "checkbox",
250
+ "link_info": "",
251
+ "link_blog": "",
252
+ "name": "In-Plugin Notices",
253
+ "summary": "Display Plugin Specific Notices",
254
+ "description": "Disable this option to hide certain plugin admin notices about available updates and post-update notices."
255
+ },
256
+ {
257
+ "key": "enable_wpcli",
258
+ "section": "section_general_plugin_options",
259
+ "advanced": true,
260
+ "premium": true,
261
+ "default": "Y",
262
+ "type": "checkbox",
263
+ "link_info": "https://shsec.io/i1",
264
+ "link_blog": "https://shsec.io/i2",
265
+ "beacon_id": 308,
266
+ "name": "Allow WP-CLI",
267
+ "summary": "Allow Access And Control Of This Plugin Via WP-CLI",
268
+ "description": "Turn off this option to disable this plugin's WP-CLI integration."
269
+ },
270
+ {
271
+ "key": "display_plugin_badge",
272
+ "section": "section_general_plugin_options",
273
+ "default": "N",
274
+ "type": "checkbox",
275
+ "link_info": "https://shsec.io/5v",
276
+ "link_blog": "https://shsec.io/wpsf20",
277
+ "beacon_id": 130,
278
+ "name": "Show Plugin Badge",
279
+ "summary": "Display Plugin Security Badge On Your Site",
280
+ "description": "Enabling this option helps support the plugin by spreading the word about it on your website. The plugin badge also demonstrates to visitors that you take your website security seriously."
281
+ },
282
+ {
283
+ "key": "enable_xmlrpc_compatibility",
284
+ "section": "section_defaults",
285
+ "default": "N",
286
+ "type": "checkbox",
287
+ "link_info": "",
288
+ "link_blog": "",
289
+ "name": "XML-RPC Compatibility",
290
+ "summary": "Allow Login Through XML-RPC To By-Pass Login Guard Rules",
291
+ "description": "Enable this if you need XML-RPC functionality e.g. if you use the WordPress iPhone/Android App."
292
+ },
293
+ {
294
+ "key": "importexport_enable",
295
+ "section": "section_importexport",
296
+ "advanced": true,
297
+ "premium": true,
298
+ "default": "Y",
299
+ "type": "checkbox",
300
+ "link_info": "https://shsec.io/do",
301
+ "link_blog": "https://shsec.io/dp",
302
+ "beacon_id": 129,
303
+ "name": "Allow Import/Export",
304
+ "summary": "Allow Import Of Options To, And Export Of Options From, This Site",
305
+ "description": "Uncheck this box to completely disable import and export of options."
306
+ },
307
+ {
308
+ "key": "importexport_masterurl",
309
+ "section": "section_importexport",
310
+ "advanced": true,
311
+ "default": "",
312
+ "type": "text",
313
+ "link_info": "",
314
+ "link_blog": "",
315
+ "name": "Auto-Import URL",
316
+ "summary": "Automatically Import Options From This Site",
317
+ "description": "Supplying a valid site URL here will make this site an 'Options Slave' and will automatically import options daily."
318
+ },
319
+ {
320
+ "key": "importexport_whitelist",
321
+ "section": "section_importexport",
322
+ "advanced": true,
323
+ "transferable": false,
324
+ "sensitive": true,
325
+ "default": [],
326
+ "type": "array",
327
+ "link_info": "",
328
+ "link_blog": "",
329
+ "name": "Export Whitelist",
330
+ "summary": "Whitelisted Sites Which Do Not Need The Secret Key To Export Options",
331
+ "description": "Each site on this list will be able to export options from this site without providing the secret key. Take a new line for each URL."
332
+ },
333
+ {
334
+ "key": "importexport_whitelist_notify",
335
+ "section": "section_importexport",
336
+ "advanced": true,
337
+ "sensitive": true,
338
+ "default": "N",
339
+ "type": "checkbox",
340
+ "link_info": "",
341
+ "link_blog": "",
342
+ "name": "Notify Whitelist",
343
+ "summary": "Notify Sites On The Whitelist To Update Options From Master",
344
+ "description": "When enabled, manual options saving will notify sites on the whitelist to export options from the Master site."
345
+ },
346
+ {
347
+ "key": "importexport_secretkey",
348
+ "section": "section_importexport",
349
+ "advanced": true,
350
+ "transferable": false,
351
+ "sensitive": true,
352
+ "default": "",
353
+ "type": "noneditable_text",
354
+ "link_info": "",
355
+ "link_blog": "",
356
+ "name": "Secret Key",
357
+ "summary": "Import/Export Secret Key",
358
+ "description": "Keep this Secret Key private as it will allow the import and export of options."
359
+ },
360
+ {
361
+ "key": "delete_on_deactivate",
362
+ "section": "section_general_plugin_options",
363
+ "default": "N",
364
+ "type": "checkbox",
365
+ "link_info": "",
366
+ "link_blog": "",
367
+ "name": "Delete Plugin Settings",
368
+ "summary": "Delete All Plugin Settings Upon Plugin Deactivation",
369
+ "description": "Careful: Removes all plugin options when you deactivate the plugin."
370
+ },
371
+ {
372
+ "key": "locale_override",
373
+ "section": "section_general_plugin_options",
374
+ "advanced": true,
375
+ "default": "",
376
+ "type": "text",
377
+ "link_info": "https://icwp.io/il",
378
+ "link_blog": "",
379
+ "name": "Locale Override",
380
+ "summary": "Delete All Plugin Settings Upon Plugin Deactivation",
381
+ "description": "Careful: Removes all plugin options when you deactivate the plugin."
382
+ },
383
+ {
384
+ "key": "captcha_provider",
385
+ "section": "section_third_party_captcha",
386
+ "default": "grecaptcha",
387
+ "type": "select",
388
+ "value_options": [
389
+ {
390
+ "value_key": "grecaptcha",
391
+ "text": "Google reCAPTCHA v2"
392
+ },
393
+ {
394
+ "value_key": "hcaptcha",
395
+ "text": "hCaptcha"
396
+ }
397
+ ],
398
+ "link_info": "https://shsec.io/dq",
399
+ "link_blog": "",
400
+ "beacon_id": 269,
401
+ "name": "CAPTCHA Provider",
402
+ "summary": "Which CAPTCHA Provider To Use Throughout",
403
+ "description": "You can choose the CAPTCHA provider depending on your preferences."
404
+ },
405
+ {
406
+ "key": "google_recaptcha_style",
407
+ "section": "section_third_party_captcha",
408
+ "premium": true,
409
+ "default": "light",
410
+ "type": "select",
411
+ "value_options": [
412
+ {
413
+ "value_key": "light",
414
+ "text": "Light Theme"
415
+ },
416
+ {
417
+ "value_key": "dark",
418
+ "text": "Dark Theme"
419
+ },
420
+ {
421
+ "value_key": "invisible",
422
+ "text": "Invisible"
423
+ }
424
+ ],
425
+ "link_info": "https://shsec.io/dq",
426
+ "link_blog": "",
427
+ "beacon_id": 269,
428
+ "name": "CAPTCHA Type",
429
+ "summary": "How Google reCAPTCHA Will Be Displayed By Default",
430
+ "description": "You can choose the reCAPTCHA display format that best suits your site, including the new Invisible Recaptcha."
431
+ },
432
+ {
433
+ "key": "google_recaptcha_site_key",
434
+ "section": "section_third_party_captcha",
435
+ "sensitive": true,
436
+ "default": "",
437
+ "type": "text",
438
+ "link_info": "https://shsec.io/shld5",
439
+ "link_blog": "",
440
+ "beacon_id": 390,
441
+ "name": "reCAPTCHA Site Key",
442
+ "summary": "Google reCAPTCHA Site Key - Only v2 or Invisible. v3 NOT supported.",
443
+ "description": "Enter your Google reCAPTCHA site key for use throughout the plugin."
444
+ },
445
+ {
446
+ "key": "google_recaptcha_secret_key",
447
+ "section": "section_third_party_captcha",
448
+ "sensitive": true,
449
+ "default": "",
450
+ "type": "text",
451
+ "link_info": "https://shsec.io/shld5",
452
+ "link_blog": "",
453
+ "beacon_id": 390,
454
+ "name": "reCAPTCHA Secret",
455
+ "summary": "Google reCAPTCHA Secret Key - Only v2 or Invisible. v3 NOT supported.",
456
+ "description": "Enter your Google reCAPTCHA secret key for use throughout the plugin."
457
+ },
458
+ {
459
+ "key": "tracking_last_sent_at",
460
+ "section": "section_non_ui",
461
+ "transferable": false,
462
+ "type": "integer",
463
+ "default": 0,
464
+ "min": 0
465
+ },
466
+ {
467
+ "key": "unique_installation_id",
468
+ "section": "section_non_ui",
469
+ "transferable": false,
470
+ "type": "text",
471
+ "default": 0
472
+ },
473
+ {
474
+ "key": "tracking_permission_set_at",
475
+ "section": "section_non_ui",
476
+ "type": "integer",
477
+ "default": 0
478
+ },
479
+ {
480
+ "key": "installation_time",
481
+ "section": "section_non_ui",
482
+ "transferable": false,
483
+ "type": "integer",
484
+ "default": 0
485
+ },
486
+ {
487
+ "key": "activated_at",
488
+ "transferable": false,
489
+ "section": "section_non_ui",
490
+ "type": "integer",
491
+ "default": 0
492
+ },
493
+ {
494
+ "key": "importexport_secretkey_expires_at",
495
+ "section": "section_non_ui",
496
+ "transferable": false,
497
+ "type": "integer",
498
+ "default": 0
499
+ },
500
+ {
501
+ "key": "importexport_handshake_expires_at",
502
+ "section": "section_non_ui",
503
+ "transferable": false,
504
+ "type": "integer",
505
+ "default": 0
506
+ },
507
+ {
508
+ "key": "last_ip_detect_source",
509
+ "transferable": false,
510
+ "section": "section_non_ui",
511
+ "type": "text",
512
+ "default": ""
513
+ },
514
+ {
515
+ "key": "openssl_private_key",
516
+ "transferable": false,
517
+ "sensitive": true,
518
+ "section": "section_non_ui",
519
+ "type": "text",
520
+ "default": ""
521
+ },
522
+ {
523
+ "key": "snapi_data",
524
+ "transferable": false,
525
+ "sensitive": true,
526
+ "section": "section_non_ui",
527
+ "type": "array",
528
+ "default": []
529
+ },
530
+ {
531
+ "key": "captcha_checked_at",
532
+ "transferable": false,
533
+ "section": "section_non_ui",
534
+ "type": "int",
535
+ "default": -1
536
+ },
537
+ {
538
+ "key": "cache_dir_write_test",
539
+ "transferable": false,
540
+ "section": "section_non_ui",
541
+ "type": "array",
542
+ "default": []
543
+ }
544
+ ],
545
+ "definitions": {
546
+ "href_privacy_policy": "https://shsec.io/wpshieldprivacypolicy",
547
+ "db_classes": {
548
+ "notes": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\AdminNotes\\Handler"
549
+ },
550
+ "db_table_notes": {
551
+ "slug": "notes",
552
+ "has_updated_at": true,
553
+ "cols_custom": {
554
+ "wp_username": "varchar(255) NOT NULL DEFAULT 'unknown'",
555
+ "note": "TEXT"
556
+ }
557
+ },
558
+ "active_plugin_features": [
559
+ {
560
+ "slug": "data",
561
+ "load_priority": 1
562
+ },
563
+ {
564
+ "slug": "insights",
565
+ "load_priority": 2,
566
+ "menu_priority": 5
567
+ },
568
+ {
569
+ "slug": "admin_access_restriction",
570
+ "namespace": "SecurityAdmin",
571
+ "load_priority": 11
572
+ },
573
+ {
574
+ "slug": "ips",
575
+ "load_priority": 15,
576
+ "namespace": "IPs"
577
+ },
578
+ {
579
+ "slug": "audit_trail",
580
+ "load_priority": 11,
581
+ "hidden": false
582
+ },
583
+ {
584
+ "slug": "hack_protect",
585
+ "namespace": "HackGuard"
586
+ },
587
+ {
588
+ "slug": "traffic",
589
+ "load_priority": 12
590
+ },
591
+ {
592
+ "slug": "firewall",
593
+ "load_priority": 1000
594
+ },
595
+ {
596
+ "slug": "login_protect",
597
+ "storage_key": "loginprotect",
598
+ "namespace": "LoginGuard"
599
+ },
600
+ {
601
+ "slug": "user_management"
602
+ },
603
+ {
604
+ "slug": "comments_filter",
605
+ "storage_key": "commentsfilter"
606
+ },
607
+ {
608
+ "slug": "events",
609
+ "load_priority": 11
610
+ },
611
+ {
612
+ "slug": "reporting",
613
+ "load_priority": 12
614
+ },
615
+ {
616
+ "slug": "sessions",
617
+ "load_priority": 5
618
+ },
619
+ {
620
+ "slug": "integrations",
621
+ "load_priority": 20
622
+ },
623
+ {
624
+ "slug": "license",
625
+ "load_priority": 10
626
+ },
627
+ {
628
+ "slug": "autoupdates"
629
+ },
630
+ {
631
+ "slug": "headers"
632
+ },
633
+ {
634
+ "slug": "lockdown"
635
+ },
636
+ {
637
+ "slug": "comms"
638
+ },
639
+ {
640
+ "slug": "email"
641
+ }
642
+ ],
643
+ "events": {
644
+ "debug_log": {
645
+ "audit_params": [
646
+ "message"
647
+ ],
648
+ "level": "debug",
649
+ "stat": false
650
+ },
651
+ "test_cron_run": {
652
+ "level": "debug",
653
+ "recent": true
654
+ },
655
+ "import_notify_sent": {
656
+ "level": "debug",
657
+ "stat": false
658
+ },
659
+ "import_notify_received": {
660
+ "audit_params": [
661
+ "master_site"
662
+ ],
663
+ "level": "notice",
664
+ "stat": false
665
+ },
666
+ "options_exported": {
667
+ "audit_params": [
668
+ "site"
669
+ ],
670
+ "level": "notice",
671
+ "stat": true,
672
+ "recent": true
673
+ },
674
+ "options_imported": {
675
+ "audit_params": [
676
+ "site"
677
+ ],
678
+ "level": "notice",
679
+ "stat": true,
680
+ "recent": true
681
+ },
682
+ "whitelist_site_added": {
683
+ "audit_params": [
684
+ "site"
685
+ ],
686
+ "level": "warning",
687
+ "stat": false
688
+ },
689
+ "whitelist_site_removed": {
690
+ "audit_params": [
691
+ "site"
692
+ ],
693
+ "level": "notice",
694
+ "stat": false
695
+ },
696
+ "master_url_set": {
697
+ "audit_params": [
698
+ "site"
699
+ ],
700
+ "level": "warning",
701
+ "stat": false
702
+ },
703
+ "recaptcha_success": {
704
+ "level": "debug"
705
+ },
706
+ "recaptcha_fail": {
707
+ "level": "warning",
708
+ "audit": true
709
+ },
710
+ "antibot_pass": {
711
+ "audit_params": [
712
+ "score",
713
+ "minimum"
714
+ ],
715
+ "level": "info",
716
+ "stat": true
717
+ },
718
+ "antibot_fail": {
719
+ "audit_params": [
720
+ "score",
721
+ "minimum"
722
+ ],
723
+ "level": "warning",
724
+ "stat": true
725
+ },
726
+ "frontpage_load": {
727
+ "level": "debug",
728
+ "offense": false,
729
+ "stat": false
730
+ },
731
+ "loginpage_load": {
732
+ "level": "debug",
733
+ "offense": false,
734
+ "stat": false
735
+ }
736
+ },
737
+ "wizards": {
738
+ "welcome": {
739
+ "title": "Getting Started Setup Wizard",
740
+ "desc": "An introduction to this security plugin, helping you get setup and started quickly with the core features.",
741
+ "min_user_permissions": "manage_options",
742
+ "steps": {
743
+ "welcome": {
744
+ "security_admin": false,
745
+ "title": "Welcome"
746
+ },
747
+ "ip_detect": {
748
+ "title": "IP Detection"
749
+ },
750
+ "admin_access_restriction": {
751
+ "title": "Security Admin"
752
+ },
753
+ "audit_trail": {
754
+ "title": "Audit Trail"
755
+ },
756
+ "ips": {
757
+ "title": "IP Blacklist"
758
+ },
759
+ "login_protect": {
760
+ "title": "Login Protection"
761
+ },
762
+ "comments_filter": {
763
+ "title": "Comment SPAM"
764
+ },
765
+ "plugin_badge": {
766
+ "title": "Security Badge"
767
+ },
768
+ "plugin_telemetry": {
769
+ "title": "Plugin Telemetry"
770
+ },
771
+ "free_trial": {
772
+ "title": "Free Trial"
773
+ },
774
+ "optin": {
775
+ "title": "Join Us!"
776
+ },
777
+ "thankyou": {
778
+ "security_admin": false,
779
+ "title": "Thank You!"
780
+ }
781
+ }
782
+ }
783
+ }
784
+ }
785
+ }
config/reporting.json ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "properties": {
3
+ "slug": "reporting",
4
+ "name": "Reporting",
5
+ "storage_key": "reporting",
6
+ "tagline": "Shield Reporting",
7
+ "show_central": true,
8
+ "show_module_menu_item": false,
9
+ "show_module_options": true,
10
+ "premium": false,
11
+ "access_restricted": true,
12
+ "run_if_whitelisted": true,
13
+ "run_if_verified_bot": false,
14
+ "run_if_wpcli": true,
15
+ "tracking_exclude": true
16
+ },
17
+ "menu_items": [
18
+ {
19
+ "title": "Stats (beta)",
20
+ "slug": "stats-redirect"
21
+ }
22
+ ],
23
+ "custom_redirects": [
24
+ {
25
+ "source_mod_page": "stats-redirect",
26
+ "target_mod_page": "insights",
27
+ "query_args": {
28
+ "inav": "reports"
29
+ }
30
+ }
31
+ ],
32
+ "sections": [
33
+ {
34
+ "slug": "section_timings",
35
+ "primary": true,
36
+ "title": "Report Frequencies",
37
+ "title_short": "Report Frequencies",
38
+ "beacon_id": 136,
39
+ "summary": [
40
+ "Purpose - Choose the most appropriate frequency to receive alerts from Shield according to your schedule."
41
+ ]
42
+ },
43
+ {
44
+ "slug": "section_enable_mod_reporting",
45
+ "title": "Enable Module: Reports",
46
+ "title_short": "Disable Module",
47
+ "beacon_id": 136,
48
+ "summary": [
49
+ "Purpose - Helps you see at a glance how effective the plugin has been.",
50
+ "Recommendation - Keep the Reporting feature turned on."
51
+ ]
52
+ },
53
+ {
54
+ "slug": "section_non_ui",
55
+ "hidden": true
56
+ }
57
+ ],
58
+ "options": [
59
+ {
60
+ "key": "enable_reporting",
61
+ "section": "section_enable_mod_reporting",
62
+ "advanced": true,
63
+ "default": "Y",
64
+ "type": "checkbox",
65
+ "link_info": "https://shsec.io/hb",
66
+ "link_blog": "",
67
+ "beacon_id": 136,
68
+ "name": "Enable Reporting",
69
+ "summary": "Enable (or Disable) The Reporting module",
70
+ "description": "Un-Checking this option will completely disable the Reporting module"
71
+ },
72
+ {
73
+ "key": "frequency_alert",
74
+ "section": "section_timings",
75
+ "type": "select",
76
+ "default": "daily",
77
+ "value_options": [
78
+ {
79
+ "value_key": "disabled",
80
+ "text": "Disabled"
81
+ },
82
+ {
83
+ "value_key": "hourly",
84
+ "text": "Hourly"
85
+ },
86
+ {
87
+ "value_key": "daily",
88
+ "text": "Daily"
89
+ },
90
+ {
91
+ "value_key": "weekly",
92
+ "text": "Weekly"
93
+ }
94
+ ],
95
+ "link_info": "https://shsec.io/h9",
96
+ "link_blog": "",
97
+ "beacon_id": 233,
98
+ "name": "Alert Frequency",
99
+ "summary": "How Often Should You Be Sent Important Alerts",
100
+ "description": "Decide when you should be sent important and critical alerts about your site security."
101
+ },
102
+ {
103
+ "key": "frequency_info",
104
+ "section": "section_timings",
105
+ "type": "select",
106
+ "default": "weekly",
107
+ "value_options": [
108
+ {
109
+ "value_key": "disabled",
110
+ "text": "Disabled"
111
+ },
112
+ {
113
+ "value_key": "hourly",
114
+ "text": "Hourly"
115
+ },
116
+ {
117
+ "value_key": "daily",
118
+ "text": "Daily"
119
+ },
120
+ {
121
+ "value_key": "weekly",
122
+ "text": "Weekly"
123
+ },
124
+ {
125
+ "value_key": "biweekly",
126
+ "text": "Bi-Weekly"
127
+ },
128
+ {
129
+ "value_key": "monthly",
130
+ "text": "Monthly"
131
+ }
132
+ ],
133
+ "link_info": "https://shsec.io/ha",
134
+ "link_blog": "",
135
+ "beacon_id": 232,
136
+ "name": "Info Frequency",
137
+ "summary": "How Often Should You Be Sent Information Reports",
138
+ "description": "Decide when you should be sent non-critical information and reports about your site security."
139
+ }
140
+ ],
141
+ "definitions": {
142
+ "db_classes": {
143
+ "reports": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Reports\\Handler"
144
+ },
145
+ "db_table_reports": {
146
+ "slug": "reports",
147
+ "autoexpire": 30,
148
+ "cols_custom": {
149
+ "rid": "int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Report ID'",
150
+ "type": "varchar(3) NOT NULL DEFAULT '' COMMENT 'Report Type'",
151
+ "frequency": "varchar(10) NOT NULL DEFAULT '' COMMENT 'Report Interval/Frequency'"
152
+ },
153
+ "cols_timestamps": {
154
+ "interval_end_at": "Reporting Interval End",
155
+ "sent_at": "Report Sent"
156
+ }
157
+ },
158
+ "events": {
159
+ "report_generated": {
160
+ "audit_params": [
161
+ "type",
162
+ "interval"
163
+ ],
164
+ "level": "debug"
165
+ },
166
+ "report_sent": {
167
+ "audit_params": [
168
+ "medium"
169
+ ],
170
+ "level": "debug"
171
+ }
172
+ }
173
+ }
174
+ }
config/sessions.json ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "properties": {
3
+ "slug": "sessions",
4
+ "name": "Sessions",
5
+ "show_module_menu_item": false,
6
+ "storage_key": "sessions",
7
+ "tagline": "User Sessions",
8
+ "auto_enabled": true,
9
+ "show_central": false,
10
+ "premium": false,
11
+ "access_restricted": true,
12
+ "auto_load_processor": true,
13
+ "run_if_whitelisted": true,
14
+ "run_if_verified_bot": true,
15
+ "run_if_wpcli": false,
16
+ "tracking_exclude": true
17
+ },
18
+ "wpcli": {
19
+ "enabled": false
20
+ },
21
+ "sections": [
22
+ {
23
+ "slug": "section_enable_plugin_feature_sessions",
24
+ "primary": true,
25
+ "title": "Enable Module: Sessions",
26
+ "title_short": "Disable Module",
27
+ "summary": [
28
+ "Purpose - Creates and Manages User Sessions.",
29
+ "Recommendation - Keep the Sessions feature turned on."
30
+ ]
31
+ },
32
+ {
33
+ "slug": "section_non_ui",
34
+ "hidden": true
35
+ }
36
+ ],
37
+ "options": [
38
+ {
39
+ "key": "enable_sessions",
40
+ "section": "section_enable_plugin_feature_sessions",
41
+ "default": "Y",
42
+ "type": "checkbox",
43
+ "link_info": "",
44
+ "link_blog": "",
45
+ "name": "Enable Sessions",
46
+ "summary": "Enable (or Disable) The Sessions module",
47
+ "description": "Un-Checking this option will completely disable the Sessions module"
48
+ },
49
+ {
50
+ "key": "autoadd_sessions_started_at",
51
+ "section": "section_non_ui",
52
+ "type": "integer",
53
+ "transferable": false,
54
+ "default": 0
55
+ }
56
+ ],
57
+ "definitions": {
58
+ "db_classes": {
59
+ "sessions": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Session\\Handler"
60
+ },
61
+ "sessions_table_name": "sessions",
62
+ "db_table_sessions": {
63
+ "slug": "sessions",
64
+ "cols_custom": {
65
+ "session_id": "varchar(32) NOT NULL DEFAULT ''",
66
+ "wp_username": "varchar(255) NOT NULL DEFAULT ''",
67
+ "ip": "varchar(60) NOT NULL DEFAULT ''",
68
+ "browser": "varchar(32) NOT NULL DEFAULT ''",
69
+ "last_activity_uri": "text NOT NULL DEFAULT ''"
70
+ },
71
+ "cols_timestamps": {
72
+ "logged_in_at": "Session Started",
73
+ "last_activity_at": "Last Seen At",
74
+ "secadmin_at": "Security Admin Authenticated"
75
+ }
76
+ },
77
+ "events": {
78
+ "session_start": {
79
+ "audit_params": [
80
+ "user_login",
81
+ "session_id"
82
+ ],
83
+ "level": "info"
84
+ },
85
+ "session_terminate": {
86
+ "level": "info"
87
+ },
88
+ "session_terminate_current": {
89
+ "audit_params": [
90
+ "user_login",
91
+ "session_id"
92
+ ],
93
+ "level": "info",
94
+ "recent": true
95
+ },
96
+ "login_success": {
97
+ "level": "info",
98
+ "offense": false,
99
+ "stat": false
100
+ }
101
+ }
102
+ }
103
+ }
config/traffic.json ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "traffic",
3
+ "properties": {
4
+ "slug": "traffic",
5
+ "name": "Traffic Watch",
6
+ "sidebar_name": "Traffic",
7
+ "show_module_menu_item": false,
8
+ "show_module_options": true,
9
+ "storage_key": "traffic",
10
+ "tagline": "Watch All Requests To Your Site",
11
+ "show_central": true,
12
+ "access_restricted": true,
13
+ "premium": false,
14
+ "run_if_whitelisted": false,
15
+ "run_if_verified_bot": true,
16
+ "run_if_wpcli": false,
17
+ "order": 110
18
+ },
19
+ "menu_items": [
20
+ {
21
+ "title": "Traffic Log",
22
+ "slug": "traffic-redirect"
23
+ }
24
+ ],
25
+ "custom_redirects": [
26
+ {
27
+ "source_mod_page": "traffic-redirect",
28
+ "target_mod_page": "insights",
29
+ "query_args": {
30
+ "inav": "traffic"
31
+ }
32
+ }
33
+ ],
34
+ "sections": [
35
+ {
36
+ "slug": "section_traffic_options",
37
+ "primary": true,
38
+ "title": "Traffic Watch Options",
39
+ "title_short": "Options",
40
+ "beacon_id": 153,
41
+ "summary": [
42
+ "Purpose - Provides finer control over the live traffic system.",
43
+ "Recommendation - These settings are dependent on your requirements."
44
+ ]
45
+ },
46
+ {
47
+ "slug": "section_traffic_limiter",
48
+ "title": "Traffic Rate Limiting",
49
+ "title_short": "Rate Limiting",
50
+ "beacon_id": 420,
51
+ "summary": [
52
+ "Purpose - Provides ability to restrict excessive requests from a single visitor.",
53
+ "Recommendation - These settings are dependent on your requirements."
54
+ ]
55
+ },
56
+ {
57
+ "slug": "section_enable_plugin_feature_traffic",
58
+ "title": "Enable Module: Traffic Watch",
59
+ "title_short": "Disable Module",
60
+ "beacon_id": 153,
61
+ "summary": [
62
+ "Purpose - The Traffic Watch module lets you monitor and review all requests to your site.",
63
+ "Recommendation - Required only if you need to review and investigate and monitor requests to your site."
64
+ ]
65
+ },
66
+ {
67
+ "slug": "section_non_ui",
68
+ "hidden": true
69
+ }
70
+ ],
71
+ "options": [
72
+ {
73
+ "key": "enable_traffic",
74
+ "section": "section_enable_plugin_feature_traffic",
75
+ "advanced": true,
76
+ "default": "Y",
77
+ "type": "checkbox",
78
+ "link_info": "https://shsec.io/ed",
79
+ "link_blog": "https://shsec.io/ee",
80
+ "beacon_id": 153,
81
+ "name": "Enable Traffic Watch",
82
+ "summary": "Enable (or Disable) The Traffic Watch Module",
83
+ "description": "Un-Checking this option will completely disable the Traffic Watch module."
84
+ },
85
+ {
86
+ "key": "enable_logger",
87
+ "section": "section_traffic_options",
88
+ "default": "N",
89
+ "type": "checkbox",
90
+ "link_info": "https://shsec.io/hf",
91
+ "link_blog": "",
92
+ "beacon_id": 153,
93
+ "name": "Enable Traffic Logger",
94
+ "summary": "Turn On The Traffic Logging Feature",
95
+ "description": "Enable or disable the ability to log and monitor requests to your site."
96
+ },
97
+ {
98
+ "key": "type_exclusions",
99
+ "section": "section_traffic_options",
100
+ "type": "multiple_select",
101
+ "advanced": true,
102
+ "default": [
103
+ "logged_in",
104
+ "server",
105
+ "cron",
106
+ "search",
107
+ "uptime"
108
+ ],
109
+ "value_options": [
110
+ {
111
+ "value_key": "simple",
112
+ "text": "Simple Requests"
113
+ },
114
+ {
115
+ "value_key": "api",
116
+ "text": "REST API"
117
+ },
118
+ {
119
+ "value_key": "ajax",
120
+ "text": "AJAX"
121
+ },
122
+ {
123
+ "value_key": "logged_in",
124
+ "text": "Logged-In Users"
125
+ },
126
+ {
127
+ "value_key": "cron",
128
+ "text": "WP CRON"
129
+ },
130
+ {
131
+ "value_key": "server",
132
+ "text": "This Server"
133
+ },
134
+ {
135
+ "value_key": "search",
136
+ "text": "Search Engines"
137
+ },
138
+ {
139
+ "value_key": "uptime",
140
+ "text": "Uptime Monitoring Services"
141
+ }
142
+ ],
143
+ "link_info": "https://shsec.io/eb",
144
+ "link_blog": "",
145
+ "beacon_id": 154,
146
+ "name": "Traffic Log Exclusions",
147
+ "summary": "Select Which Types Of Requests To Exclude",
148
+ "description": "Deselect any requests that you don't want to appear in the traffic viewer."
149
+ },
150
+ {
151
+ "key": "custom_exclusions",
152
+ "section": "section_traffic_options",
153
+ "advanced": true,
154
+ "premium": true,
155
+ "default": [],
156
+ "type": "array",
157
+ "link_info": "https://shsec.io/ec",
158
+ "link_blog": "",
159
+ "beacon_id": 154,
160
+ "name": "Custom Exclusions",
161
+ "summary": "Provide Custom Traffic Exclusions",
162
+ "description": "For each entry, if the text is present in either the User Agent or Page/Path, it will be excluded."
163
+ },
164
+ {
165
+ "key": "auto_clean",
166
+ "section": "section_traffic_options",
167
+ "advanced": true,
168
+ "default": 7,
169
+ "min": 1,
170
+ "type": "integer",
171
+ "link_info": "",
172
+ "link_blog": "",
173
+ "name": "Auto Expiry Cleaning",
174
+ "summary": "Enable Traffic Log Auto Expiry",
175
+ "description": "Automated DB cleanup will delete logs older than this maximum value (in days)."
176
+ },
177
+ {
178
+ "key": "enable_limiter",
179
+ "section": "section_traffic_limiter",
180
+ "premium": true,
181
+ "default": "N",
182
+ "type": "checkbox",
183
+ "link_info": "https://shsec.io/gw",
184
+ "link_blog": "https://shsec.io/gx",
185
+ "beacon_id": 420,
186
+ "name": "Enable Rate Limiting",
187
+ "summary": "Turn On The Rate Limiting Feature",
188
+ "description": "Enable or disable the rate limiting feature according to your rate limiting parameters."
189
+ },
190
+ {
191
+ "key": "limit_requests",
192
+ "section": "section_traffic_limiter",
193
+ "default": "60",
194
+ "min": 0,
195
+ "type": "integer",
196
+ "link_info": "",
197
+ "link_blog": "",
198
+ "name": "Max Request Limit",
199
+ "summary": "Maximum Number Of Requests Allowed In Time Limit",
200
+ "description": "The maximum number of requests that are allowed in the given time limit."
201
+ },
202
+ {
203
+ "key": "limit_time_span",
204
+ "section": "section_traffic_limiter",
205
+ "default": "60",
206
+ "min": 0,
207
+ "type": "integer",
208
+ "link_info": "",
209
+ "link_blog": "",
210
+ "name": "Request Limit Time Interval",
211
+ "summary": "The Time Interval To Test For Excessive Requests",
212
+ "description": "The time limit within which to monitor for excessive requests that exceed the limit."
213
+ },
214
+ {
215
+ "key": "legacy_db_deleted_at",
216
+ "section": "section_non_ui",
217
+ "transferable": false,
218
+ "type": "text",
219
+ "default": 0
220
+ }
221
+ ],
222
+ "definitions": {
223
+ "db_classes": {
224
+ "traffic": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Traffic\\Handler"
225
+ },
226
+ "db_table_traffic": {
227
+ "slug": "traffic",
228
+ "cols_custom": {
229
+ "rid": "varchar(10) NOT NULL DEFAULT '' COMMENT 'Request ID'",
230
+ "uid": "int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'User ID'",
231
+ "ip": "varbinary(16) DEFAULT NULL COMMENT 'Visitor IP Address'",
232
+ "path": "text NOT NULL DEFAULT '' COMMENT 'Request Path or URI'",
233
+ "code": "int(5) NOT NULL DEFAULT '200' COMMENT 'HTTP Response Code'",
234
+ "verb": "varchar(10) NOT NULL DEFAULT 'get' COMMENT 'HTTP Method'",
235
+ "ua": "text COMMENT 'Browser User Agent String'",
236
+ "trans": "tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Trangression'"
237
+ }
238
+ },
239
+ "traffic_table_name": "traffic",
240
+ "events": {
241
+ "request_limit_exceeded": {
242
+ "audit_params": [
243
+ "requests",
244
+ "count",
245
+ "span"
246
+ ],
247
+ "level": "alert",
248
+ "offense": true
249
+ }
250
+ }
251
+ }
252
+ }
config/user_management.json ADDED
@@ -0,0 +1,473 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "slug": "user_management",
3
+ "properties": {
4
+ "slug": "user_management",
5
+ "name": "User Management",
6
+ "sidebar_name": "Users",
7
+ "show_module_menu_item": false,
8
+ "show_module_options": true,
9
+ "storage_key": "user_management",
10
+ "tagline": "Control user sessions, duration, timeouts and account sharing",
11
+ "show_central": true,
12
+ "access_restricted": true,
13
+ "premium": false,
14
+ "run_if_whitelisted": true,
15
+ "run_if_verified_bot": false,
16
+ "run_if_wpcli": false,
17
+ "order": 40
18
+ },
19
+ "sections": [
20
+ {
21
+ "slug": "section_user_session_management",
22
+ "primary": true,
23
+ "title": "User Session Management",
24
+ "title_short": "Session Options",
25
+ "beacon_id": 397,
26
+ "summary": [
27
+ "Purpose - Allows you to better control user sessions on your site and expire idle sessions and prevent account sharing.",
28
+ "Recommendation - Use of this feature is highly recommend."
29
+ ]
30
+ },
31
+ {
32
+ "slug": "section_user_reg",
33
+ "title": "User Registration",
34
+ "title_short": "User Registration",
35
+ "beacon_id": 145,
36
+ "summary": [
37
+ "Purpose - Control user registration and prevent SPAM.",
38
+ "Recommendation - Use of this feature is highly recommend."
39
+ ]
40
+ },
41
+ {
42
+ "slug": "section_passwords",
43
+ "reqs": {
44
+ "wp_min": "4.4"
45
+ },
46
+ "title": "Password Policies",
47
+ "title_short": "Password Policies",
48
+ "beacon_id": 256,
49
+ "summary": [
50
+ "Purpose - Have full control over passwords used by users on the site.",
51
+ "Recommendation - Use of this feature is highly recommend."
52
+ ]
53
+ },
54
+ {
55
+ "slug": "section_suspend",
56
+ "title": "Automatic And Manual User Suspension",
57
+ "title_short": "User Suspension",
58
+ "beacon_id": 273,
59
+ "summary": [
60
+ "Purpose - Automatically suspend accounts to prevent login by certain users.",
61
+ "Recommendation - Use of this feature is highly recommend."
62
+ ]
63
+ },
64
+ {
65
+ "slug": "section_admin_login_notification",
66
+ "title": "Admin Login Notification",
67
+ "title_short": "Notifications",
68
+ "beacon_id": 147,
69
+ "summary": [
70
+ "Purpose - So you can be made aware of when a WordPress administrator has logged into your site when you are not expecting it.",
71
+ "Recommendation - Use of this feature is highly recommend."
72
+ ]
73
+ },
74
+ {
75
+ "slug": "section_enable_plugin_feature_user_accounts_management",
76
+ "title": "Enable Module: User Management",
77
+ "title_short": "Disable Module",
78
+ "beacon_id": 273,
79
+ "summary": [
80
+ "Purpose - User Management offers real user sessions, finer control over user session time-out, and ensures users have logged-in in a correct manner.",
81
+ "Recommendation - Keep the User Management feature turned on."
82
+ ]
83
+ },
84
+ {
85
+ "slug": "section_non_ui",
86
+ "hidden": true
87
+ }
88
+ ],
89
+ "options": [
90
+ {
91
+ "key": "enable_user_management",
92
+ "section": "section_enable_plugin_feature_user_accounts_management",
93
+ "advanced": true,
94
+ "default": "Y",
95
+ "type": "checkbox",
96
+ "link_info": "https://shsec.io/e3",
97
+ "link_blog": "https://shsec.io/hi",
98
+ "beacon_id": 273,
99
+ "name": "Enable User Management",
100
+ "summary": "Enable (or Disable) The User Management module",
101
+ "description": "Un-Checking this option will completely disable the User Management module"
102
+ },
103
+ {
104
+ "key": "enable_user_login_email_notification",
105
+ "section": "section_admin_login_notification",
106
+ "premium": true,
107
+ "sensitive": false,
108
+ "default": "N",
109
+ "type": "checkbox",
110
+ "link_info": "https://shsec.io/e2",
111
+ "link_blog": "",
112
+ "beacon_id": 147,
113
+ "name": "User Login Notification Email",
114
+ "summary": "Send Email Notification To Each User Upon Successful Login",
115
+ "description": "A notification is sent to each user when a successful login occurs for their account."
116
+ },
117
+ {
118
+ "key": "enable_admin_login_email_notification",
119
+ "section": "section_admin_login_notification",
120
+ "sensitive": true,
121
+ "default": "",
122
+ "type": "text",
123
+ "link_info": "",
124
+ "link_blog": "",
125
+ "name": "Admin Login Notification Email",
126
+ "summary": "Send An Notification Email When Administrator Logs In",
127
+ "description": "If you would like to be notified every time an administrator user logs into this WordPress site, enter a notification email address. No email address - No Notification."
128
+ },
129
+ {
130
+ "key": "session_timeout_interval",
131
+ "section": "section_user_session_management",
132
+ "default": 2,
133
+ "min": 0,
134
+ "type": "integer",
135
+ "link_info": "",
136
+ "link_blog": "",
137
+ "name": "Session Timeout",
138
+ "summary": "Specify How Many Days After Login To Automatically Force Re-Login",
139
+ "description": "WordPress default is 2 days, or 14 days if you check the 'Remember Me' box."
140
+ },
141
+ {
142
+ "key": "session_idle_timeout_interval",
143
+ "section": "section_user_session_management",
144
+ "default": 48,
145
+ "min": 0,
146
+ "type": "integer",
147
+ "link_info": "https://support.getshieldsecurity.com/support/solutions/articles/3000070590",
148
+ "link_blog": "",
149
+ "beacon_id": 397,
150
+ "name": "Idle Timeout",
151
+ "summary": "Specify How Many Hours After Inactivity To Automatically Logout User",
152
+ "description": "If the user is inactive for the number of hours specified, they will be forcefully logged out next time they return. Set this to '0' to turn off this option."
153
+ },
154
+ {
155
+ "key": "session_lock_location",
156
+ "section": "section_user_session_management",
157
+ "advanced": true,
158
+ "default": "N",
159
+ "type": "checkbox",
160
+ "link_info": "",
161
+ "link_blog": "",
162
+ "name": "Lock To Location",
163
+ "summary": "Locks A User Session To IP address",
164
+ "description": "When selected, a session is restricted to the same IP address as when the user logged in. If a logged-in user's IP address changes, the session will be invalidated and they'll be forced to re-login to WordPress."
165
+ },
166
+ {
167
+ "key": "session_username_concurrent_limit",
168
+ "section": "section_user_session_management",
169
+ "default": 0,
170
+ "min": 0,
171
+ "type": "integer",
172
+ "link_info": "",
173
+ "link_blog": "",
174
+ "name": "Max Simultaneous Sessions",
175
+ "summary": "Limit Simultaneous Sessions For The Same Username",
176
+ "description": "The number provided here is the maximum number of simultaneous, distinct, sessions allowed for any given username. Use '0' for no limits."
177
+ },
178
+ {
179
+ "key": "reg_email_validate",
180
+ "section": "section_user_reg",
181
+ "premium": true,
182
+ "type": "select",
183
+ "default": "log",
184
+ "value_options": [
185
+ {
186
+ "value_key": "disabled",
187
+ "text": "Disabled"
188
+ },
189
+ {
190
+ "value_key": "log",
191
+ "text": "Log Only"
192
+ },
193
+ {
194
+ "value_key": "offense",
195
+ "text": "Increment Offense Counter"
196
+ },
197
+ {
198
+ "value_key": "block",
199
+ "text": "Immediate Block and Kill"
200
+ }
201
+ ],
202
+ "link_info": "https://shsec.io/gk",
203
+ "link_blog": "https://shsec.io/hh",
204
+ "name": "Validate Email Addresses",
205
+ "summary": "Validate Email Addresses When User Attempts To Register",
206
+ "description": "Validate Email Addresses When User Attempts To Register."
207
+ },
208
+ {
209
+ "key": "email_checks",
210
+ "section": "section_user_reg",
211
+ "type": "multiple_select",
212
+ "default": [
213
+ "syntax",
214
+ "domain"
215
+ ],
216
+ "value_options": [
217
+ {
218
+ "value_key": "syntax",
219
+ "text": "Email Address Syntax"
220
+ },
221
+ {
222
+ "value_key": "domain_registered",
223
+ "text": "Domain Is Registered"
224
+ },
225
+ {
226
+ "value_key": "domain_resolves",
227
+ "text": "Domain Resolve To IP"
228
+ },
229
+ {
230
+ "value_key": "mx",
231
+ "text": "Domain MX"
232
+ },
233
+ {
234
+ "value_key": "nondisposable",
235
+ "text": "Disposable Email Service"
236
+ }
237
+ ],
238
+ "link_info": "https://shsec.io/gk",
239
+ "link_blog": "https://shsec.io/hh",
240
+ "name": "Email Checks",
241
+ "summary": "The Email Address Properties That Will Be Tested",
242
+ "description": "Select which ."
243
+ },
244
+ {
245
+ "key": "enable_password_policies",
246
+ "section": "section_passwords",
247
+ "type": "checkbox",
248
+ "default": "N",
249
+ "link_info": "https://shsec.io/e1",
250
+ "link_blog": "https://shsec.io/c4",
251
+ "beacon_id": 146,
252
+ "name": "Enable Password Policies",
253
+ "summary": "Enable The Password Policies Below",
254
+ "description": "Turn on/off all password policies."
255
+ },
256
+ {
257
+ "key": "pass_prevent_pwned",
258
+ "section": "section_passwords",
259
+ "type": "checkbox",
260
+ "default": "Y",
261
+ "link_info": "https://shsec.io/by",
262
+ "link_blog": "",
263
+ "name": "Prevent Pwned Passwords",
264
+ "summary": "Prevent Use Of Pwned Passwords",
265
+ "description": "Prevents users from using any passwords found on the public available list of pwned passwords."
266
+ },
267
+ {
268
+ "key": "pass_min_length",
269
+ "section": "section_passwords",
270
+ "premium": true,
271
+ "type": "integer",
272
+ "default": "12",
273
+ "link_info": "",
274
+ "link_blog": "",
275
+ "name": "Minimum Length",
276
+ "summary": "Minimum Password Length",
277
+ "description": "All passwords that a user sets must be at least this many characters in length."
278
+ },
279
+ {
280
+ "key": "pass_min_strength",
281
+ "section": "section_passwords",
282
+ "premium": true,
283
+ "type": "select",
284
+ "default": "4",
285
+ "value_options": [
286
+ {
287
+ "value_key": "0",
288
+ "text": "Very Weak"
289
+ },
290
+ {
291
+ "value_key": "1",
292
+ "text": "Weak"
293
+ },
294
+ {
295
+ "value_key": "2",
296
+ "text": "Medium"
297
+ },
298
+ {
299
+ "value_key": "3",
300
+ "text": "Strong"
301
+ },
302
+ {
303
+ "value_key": "4",
304
+ "text": "Very Strong"
305
+ }
306
+ ],
307
+ "link_info": "",
308
+ "link_blog": "",
309
+ "name": "Minimum Strength",
310
+ "summary": "Minimum Password Strength",
311
+ "description": "All passwords that a user sets must meet this minimum strength."
312
+ },
313
+ {
314
+ "key": "pass_force_existing",
315
+ "section": "section_passwords",
316
+ "premium": true,
317
+ "type": "checkbox",
318
+ "default": "N",
319
+ "link_info": "",
320
+ "link_blog": "",
321
+ "name": "Apply To Existing Users",
322
+ "summary": "Apply Password Policies To Existing Users and Their Passwords",
323
+ "description": "Forces existing users to update their passwords if they don't meet requirements, after they next login ."
324
+ },
325
+ {
326
+ "key": "pass_expire",
327
+ "section": "section_passwords",
328
+ "premium": true,
329
+ "type": "integer",
330
+ "default": "60",
331
+ "min": 0,
332
+ "link_info": "",
333
+ "link_blog": "",
334
+ "name": "Password Expiration",
335
+ "summary": "Passwords Expire After This Many Days",
336
+ "description": "Users will be forced to reset their passwords after the number of days specified."
337
+ },
338
+ {
339
+ "key": "manual_suspend",
340
+ "section": "section_suspend",
341
+ "premium": true,
342
+ "type": "checkbox",
343
+ "default": "N",
344
+ "link_info": "https://shsec.io/fq",
345
+ "link_blog": "https://shsec.io/fr",
346
+ "beacon_id": 399,
347
+ "name": "Allow Manual User Suspension",
348
+ "summary": "Manually Suspend User Accounts To Prevent Login",
349
+ "description": "Users may be suspended by administrators to prevent login."
350
+ },
351
+ {
352
+ "key": "auto_password",
353
+ "section": "section_suspend",
354
+ "premium": true,
355
+ "type": "checkbox",
356
+ "default": "Y",
357
+ "link_info": "https://shsec.io/fs",
358
+ "link_blog": "https://shsec.io/fr",
359
+ "beacon_id": 400,
360
+ "name": "Auto-Suspend Expired Passwords",
361
+ "summary": "Automatically Suspend Users With Expired Passwords",
362
+ "description": "Suspend login by users and require password reset to unsuspend."
363
+ },
364
+ {
365
+ "key": "auto_idle_days",
366
+ "section": "section_suspend",
367
+ "premium": true,
368
+ "type": "integer",
369
+ "default": 0,
370
+ "min": 0,
371
+ "link_info": "https://shsec.io/ft",
372
+ "link_blog": "https://shsec.io/fr",
373
+ "beacon_id": 400,
374
+ "name": "Auto-Suspend Idle Users",
375
+ "summary": "Automatically Suspend Idle User Accounts",
376
+ "description": "Prevent login by idle users and require password reset to unsuspend."
377
+ },
378
+ {
379
+ "key": "auto_idle_roles",
380
+ "section": "section_suspend",
381
+ "premium": true,
382
+ "type": "array",
383
+ "default": [
384
+ "administrator",
385
+ "editor",
386
+ "author"
387
+ ],
388
+ "link_info": "https://shsec.io/ft",
389
+ "link_blog": "",
390
+ "beacon_id": 400,
391
+ "name": "Auto-Suspend Idle Users",
392
+ "summary": "Automatically Suspend Idle User Accounts",
393
+ "description": "Prevent login by idle users and require password reset to unsuspend."
394
+ },
395
+ {
396
+ "key": "autoadd_sessions_started_at",
397
+ "section": "section_non_ui",
398
+ "transferable": false,
399
+ "type": "integer",
400
+ "default": 0
401
+ },
402
+ {
403
+ "key": "hard_suspended_userids",
404
+ "section": "section_non_ui",
405
+ "transferable": false,
406
+ "type": "array",
407
+ "default": []
408
+ }
409
+ ],
410
+ "definitions": {
411
+ "pwned_api_url_password_single": "https://api.pwnedpasswords.com/pwnedpassword/",
412
+ "pwned_api_url_password_range": "https://api.pwnedpasswords.com/range/",
413
+ "events": {
414
+ "session_notfound": {
415
+ "audit_params": [
416
+ "user_login"
417
+ ]
418
+ },
419
+ "session_expired": {
420
+ "audit_params": [
421
+ "user_login"
422
+ ]
423
+ },
424
+ "session_idle": {
425
+ "audit_params": [
426
+ "user_login"
427
+ ]
428
+ },
429
+ "session_iplock": {
430
+ "audit_params": [
431
+ "user_login"
432
+ ]
433
+ },
434
+ "password_expired": {
435
+ "audit_params": [
436
+ "user_login"
437
+ ],
438
+ "level": "notice"
439
+ },
440
+ "password_policy_force_change": {
441
+ "audit_params": [
442
+ "user_login"
443
+ ],
444
+ "level": "notice"
445
+ },
446
+ "password_policy_block": {
447
+ "level": "notice"
448
+ },
449
+ "user_hard_suspended": {
450
+ "audit_params": [
451
+ "user_login",
452
+ "admin"
453
+ ],
454
+ "level": "warning"
455
+ },
456
+ "user_hard_unsuspended": {
457
+ "audit_params": [
458
+ "user_login",
459
+ "admin"
460
+ ],
461
+ "level": "warning"
462
+ },
463
+ "reg_email_invalid": {
464
+ "audit_params": [
465
+ "email",
466
+ "reason"
467
+ ],
468
+ "level": "warning",
469
+ "offense": true
470
+ }
471
+ }
472
+ }
473
+ }
icwp-wpsf.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Shield Security
4
  * Plugin URI: https://shsec.io/2f
5
  * Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
6
- * Version: 11.5.5
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages
9
  * Author: Shield Security
3
  * Plugin Name: Shield Security
4
  * Plugin URI: https://shsec.io/2f
5
  * Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
6
+ * Version: 12.0.0
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages
9
  * Author: Shield Security
init.php CHANGED
@@ -17,10 +17,10 @@ class ICWP_WPSF_Shield_Security {
17
  private static $oInstance = null;
18
 
19
  /**
20
- * @param Shield\Controller\Controller $oController
21
  */
22
- private function __construct( Shield\Controller\Controller $oController ) {
23
- $oController->loadAllFeatures();
24
  }
25
 
26
  /**
17
  private static $oInstance = null;
18
 
19
  /**
20
+ * @param Shield\Controller\Controller $controller
21
  */
22
+ private function __construct( Shield\Controller\Controller $controller ) {
23
+ $controller->loadAllFeatures();
24
  }
25
 
26
  /**
plugin-spec.php CHANGED
@@ -1,8 +1,8 @@
1
  {
2
  "properties": {
3
- "version": "11.5.4",
4
- "release_timestamp": 1628069509,
5
- "build": "202108.0401",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
@@ -29,6 +29,7 @@
29
  }
30
  },
31
  "paths": {
 
32
  "source": "src",
33
  "autoload": "lib/vendor/autoload.php",
34
  "assets": "resources",
@@ -53,6 +54,7 @@
53
  "plugin",
54
  "jquery/featherlight",
55
  "introjs",
 
56
  "shield/scanners"
57
  ],
58
  "js": [
@@ -62,6 +64,9 @@
62
  "jquery/fileDownload",
63
  "shield/tours",
64
  "bootstrap-select",
 
 
 
65
  "shield/scanners"
66
  ]
67
  },
@@ -93,11 +98,17 @@
93
  ]
94
  },
95
  "datatables-bootstrap": {
96
- "url": "https://cdn.datatables.net/1.10.25/css/dataTables.bootstrap4.min.css",
97
  "deps": [
98
  "bootstrap"
99
  ]
100
  },
 
 
 
 
 
 
101
  "datatables-select": {
102
  "url": "https://cdn.datatables.net/select/1.3.3/css/select.dataTables.min.css",
103
  "deps": [
@@ -149,6 +160,15 @@
149
  "footer": true
150
  },
151
  "shield/mainwp": {},
 
 
 
 
 
 
 
 
 
152
  "shield/scanners": {
153
  "deps": [
154
  "datatables-select",
@@ -162,61 +182,67 @@
162
  }
163
  },
164
  "js": {
165
- "bootstrap": {
166
  "url": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js",
167
  "deps": [
168
  "wp-jquery"
169
  ]
170
  },
171
- "select2": {
172
  "url": "https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js",
173
  "deps": [
174
  "plugin"
175
  ]
176
  },
177
- "bootstrap-datepicker": {
178
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js",
179
  "deps": [
180
  "bootstrap"
181
  ]
182
  },
183
- "bootstrap-select": {
184
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.18/js/bootstrap-select.min.js",
185
  "deps": [
186
  "bootstrap"
187
  ]
188
  },
189
- "datatables": {
190
- "url": "https://cdn.datatables.net/1.10.25/js/jquery.dataTables.min.js",
191
  "deps": [
192
  "bootstrap",
193
  "wp-jquery"
194
  ]
195
  },
196
- "datatables-bootstrap": {
197
- "url": "https://cdn.datatables.net/1.10.25/js/dataTables.bootstrap4.min.js",
198
  "deps": [
199
  "datatables"
200
  ]
201
  },
202
- "datatables-select": {
 
 
 
 
 
 
203
  "url": "https://cdn.datatables.net/select/1.3.3/js/dataTables.select.min.js",
204
  "deps": [
205
- "datatables"
206
  ]
207
  },
208
- "datatables-buttons": {
209
  "url": "https://cdn.datatables.net/buttons/1.7.1/js/dataTables.buttons.min.js",
210
  "deps": [
211
  "datatables-bootstrap"
212
  ]
213
  },
214
- "global-plugin": {
215
  "deps": [
216
  "wp-jquery"
217
  ]
218
  },
219
- "plugin": {
220
  "deps": [
221
  "bootstrap",
222
  "datatables-bootstrap",
@@ -226,76 +252,95 @@
226
  "lz-string.min"
227
  ]
228
  },
229
- "base64.min": {
230
  "url": "https://cdn.jsdelivr.net/npm/js-base64@2.6.4/base64.min.js"
231
  },
232
- "lz-string.min": {},
233
- "jquery/fileDownload": {},
234
- "jquery/steps": {
235
  "url": "https://cdnjs.cloudflare.com/ajax/libs/jquery-steps/1.1.0/jquery.steps.min.js"
236
  },
237
- "jquery/featherlight": {
238
  "url": "https://cdnjs.cloudflare.com/ajax/libs/featherlight/1.7.13/featherlight.min.js"
239
  },
240
- "chartist": {
241
  "url": "https://cdnjs.cloudflare.com/ajax/libs/chartist/0.11.4/chartist.min.js"
242
  },
243
- "chartist-plugin-legend": {
244
  "deps": [
245
  "chartist"
246
  ]
247
  },
248
- "introjs": {
249
  "url": "https://cdnjs.cloudflare.com/ajax/libs/intro.js/3.3.1/intro.min.js"
250
  },
251
- "shield/charts": {
252
  "deps": [
253
  "chartist",
254
  "chartist-plugin-legend",
255
  "plugin"
256
  ]
257
  },
258
- "shuffle": {
259
  "url": "https://cdnjs.cloudflare.com/ajax/libs/Shuffle/5.3.0/shuffle.min.js"
260
  },
261
- "shield/shuffle": {
262
  "deps": [
263
  "shuffle"
264
  ]
265
  },
266
- "shield/dialog": {
267
  "deps": [
268
  "wp-jquery-ui-dialog"
269
  ]
270
  },
271
- "shield/comments": {
272
  "deps": [
273
  "wp-jquery"
274
  ],
275
  "footer": true
276
  },
277
- "shield/loginbot": {
278
  "deps": [
279
  "wp-jquery"
280
  ]
281
  },
282
- "shield/navigation": {},
283
- "shield/secadmin": {
284
  "deps": [
285
  "wp-jquery"
286
  ]
287
  },
288
- "shield/tables": {
289
  "deps": [
290
  "plugin"
291
  ]
292
  },
293
- "shield/scanners": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  "deps": [
295
  "shield/scantables"
296
  ]
297
  },
298
- "shield/scantables": {
299
  "deps": [
300
  "datatables-select",
301
  "datatables-buttons",
@@ -303,64 +348,64 @@
303
  "tp/highlightjs"
304
  ]
305
  },
306
- "shield/tours": {
307
  "deps": [
308
  "plugin",
309
  "introjs"
310
  ]
311
  },
312
- "shield/notbot": {
313
  },
314
- "shield/scans": {
315
  "deps": [
316
  "shield/tables"
317
  ]
318
  },
319
- "shield/import": {
320
  "deps": [
321
  "plugin"
322
  ]
323
  },
324
- "shield/ipanalyse": {
325
  "deps": [
326
  "plugin"
327
  ]
328
  },
329
- "shield/mainwp": {
330
  "deps": [
331
  "wp-jquery"
332
  ]
333
  },
334
- "shield/userprofile": {
335
  "deps": [
336
  "u2f-bundle",
337
  "shield/dialog"
338
  ],
339
  "footer": true
340
  },
341
- "shield/wizard": {
342
  "deps": [
343
  "bootstrap",
344
  "global-plugin",
345
  "jquery/steps"
346
  ]
347
  },
348
- "u2f-bundle": {},
349
- "tp/grecaptcha": {
350
  "url": "https://www.google.com/recaptcha/api.js",
351
  "attributes": {
352
  "async": "async",
353
  "defer": "defer"
354
  }
355
  },
356
- "tp/hcaptcha": {
357
  "url": "https://hcaptcha.com/1/api.js",
358
  "attributes": {
359
  "async": "async",
360
  "defer": "defer"
361
  }
362
  },
363
- "tp/highlightjs": {
364
  "url": "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/highlight.min.js"
365
  }
366
  }
@@ -398,12 +443,10 @@
398
  }
399
  ],
400
  "version_upgrades": [
401
- "9.1.1",
402
- "9.2.0",
403
- "9.2.2",
404
  "10.1.0",
405
  "10.2.1",
406
- "11.2.0"
 
407
  ],
408
  "action_links": {
409
  "remove": null,
1
  {
2
  "properties": {
3
+ "version": "12.0.0",
4
+ "release_timestamp": 1631783098,
5
+ "build": "202109.1601",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
29
  }
30
  },
31
  "paths": {
32
+ "config": "config",
33
  "source": "src",
34
  "autoload": "lib/vendor/autoload.php",
35
  "assets": "resources",
54
  "plugin",
55
  "jquery/featherlight",
56
  "introjs",
57
+ "shield/datatables",
58
  "shield/scanners"
59
  ],
60
  "js": [
64
  "jquery/fileDownload",
65
  "shield/tours",
66
  "bootstrap-select",
67
+ "shield/datatables",
68
+ "shield/traffic",
69
+ "shield/audit_trail",
70
  "shield/scanners"
71
  ]
72
  },
98
  ]
99
  },
100
  "datatables-bootstrap": {
101
+ "url": "https://cdn.datatables.net/1.11.0/css/dataTables.bootstrap4.min.css",
102
  "deps": [
103
  "bootstrap"
104
  ]
105
  },
106
+ "datatables-searchpanes": {
107
+ "url": "https://cdn.datatables.net/searchpanes/1.4.0/css/searchPanes.dataTables.min.css",
108
+ "deps": [
109
+ "datatables-bootstrap"
110
+ ]
111
+ },
112
  "datatables-select": {
113
  "url": "https://cdn.datatables.net/select/1.3.3/css/select.dataTables.min.css",
114
  "deps": [
160
  "footer": true
161
  },
162
  "shield/mainwp": {},
163
+ "shield/datatables": {
164
+ "deps": [
165
+ "datatables-select",
166
+ "datatables-buttons",
167
+ "datatables-bootstrap",
168
+ "datatables-searchpanes",
169
+ "tp/highlightjs"
170
+ ]
171
+ },
172
  "shield/scanners": {
173
  "deps": [
174
  "datatables-select",
182
  }
183
  },
184
  "js": {
185
+ "bootstrap": {
186
  "url": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js",
187
  "deps": [
188
  "wp-jquery"
189
  ]
190
  },
191
+ "select2": {
192
  "url": "https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js",
193
  "deps": [
194
  "plugin"
195
  ]
196
  },
197
+ "bootstrap-datepicker": {
198
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js",
199
  "deps": [
200
  "bootstrap"
201
  ]
202
  },
203
+ "bootstrap-select": {
204
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.18/js/bootstrap-select.min.js",
205
  "deps": [
206
  "bootstrap"
207
  ]
208
  },
209
+ "datatables": {
210
+ "url": "https://cdn.datatables.net/1.11.0/js/jquery.dataTables.min.js",
211
  "deps": [
212
  "bootstrap",
213
  "wp-jquery"
214
  ]
215
  },
216
+ "datatables-bootstrap": {
217
+ "url": "https://cdn.datatables.net/1.11.0/js/dataTables.bootstrap4.min.js",
218
  "deps": [
219
  "datatables"
220
  ]
221
  },
222
+ "datatables-searchpanes": {
223
+ "url": "https://cdn.datatables.net/searchpanes/1.4.0/js/dataTables.searchPanes.min.js",
224
+ "deps": [
225
+ "datatables-bootstrap"
226
+ ]
227
+ },
228
+ "datatables-select": {
229
  "url": "https://cdn.datatables.net/select/1.3.3/js/dataTables.select.min.js",
230
  "deps": [
231
+ "datatables-bootstrap"
232
  ]
233
  },
234
+ "datatables-buttons": {
235
  "url": "https://cdn.datatables.net/buttons/1.7.1/js/dataTables.buttons.min.js",
236
  "deps": [
237
  "datatables-bootstrap"
238
  ]
239
  },
240
+ "global-plugin": {
241
  "deps": [
242
  "wp-jquery"
243
  ]
244
  },
245
+ "plugin": {
246
  "deps": [
247
  "bootstrap",
248
  "datatables-bootstrap",
252
  "lz-string.min"
253
  ]
254
  },
255
+ "base64.min": {
256
  "url": "https://cdn.jsdelivr.net/npm/js-base64@2.6.4/base64.min.js"
257
  },
258
+ "lz-string.min": {},
259
+ "jquery/fileDownload": {},
260
+ "jquery/steps": {
261
  "url": "https://cdnjs.cloudflare.com/ajax/libs/jquery-steps/1.1.0/jquery.steps.min.js"
262
  },
263
+ "jquery/featherlight": {
264
  "url": "https://cdnjs.cloudflare.com/ajax/libs/featherlight/1.7.13/featherlight.min.js"
265
  },
266
+ "chartist": {
267
  "url": "https://cdnjs.cloudflare.com/ajax/libs/chartist/0.11.4/chartist.min.js"
268
  },
269
+ "chartist-plugin-legend": {
270
  "deps": [
271
  "chartist"
272
  ]
273
  },
274
+ "introjs": {
275
  "url": "https://cdnjs.cloudflare.com/ajax/libs/intro.js/3.3.1/intro.min.js"
276
  },
277
+ "shield/charts": {
278
  "deps": [
279
  "chartist",
280
  "chartist-plugin-legend",
281
  "plugin"
282
  ]
283
  },
284
+ "shuffle": {
285
  "url": "https://cdnjs.cloudflare.com/ajax/libs/Shuffle/5.3.0/shuffle.min.js"
286
  },
287
+ "shield/shuffle": {
288
  "deps": [
289
  "shuffle"
290
  ]
291
  },
292
+ "shield/dialog": {
293
  "deps": [
294
  "wp-jquery-ui-dialog"
295
  ]
296
  },
297
+ "shield/comments": {
298
  "deps": [
299
  "wp-jquery"
300
  ],
301
  "footer": true
302
  },
303
+ "shield/loginbot": {
304
  "deps": [
305
  "wp-jquery"
306
  ]
307
  },
308
+ "shield/navigation": {},
309
+ "shield/secadmin": {
310
  "deps": [
311
  "wp-jquery"
312
  ]
313
  },
314
+ "shield/tables": {
315
  "deps": [
316
  "plugin"
317
  ]
318
  },
319
+ "shield/audit_trail": {
320
+ "deps": [
321
+ "shield/datatables"
322
+ ]
323
+ },
324
+ "shield/traffic": {
325
+ "deps": [
326
+ "shield/datatables"
327
+ ]
328
+ },
329
+ "shield/datatables": {
330
+ "deps": [
331
+ "datatables-select",
332
+ "datatables-buttons",
333
+ "datatables-bootstrap",
334
+ "datatables-searchpanes",
335
+ "tp/highlightjs"
336
+ ]
337
+ },
338
+ "shield/scanners": {
339
  "deps": [
340
  "shield/scantables"
341
  ]
342
  },
343
+ "shield/scantables": {
344
  "deps": [
345
  "datatables-select",
346
  "datatables-buttons",
348
  "tp/highlightjs"
349
  ]
350
  },
351
+ "shield/tours": {
352
  "deps": [
353
  "plugin",
354
  "introjs"
355
  ]
356
  },
357
+ "shield/notbot": {
358
  },
359
+ "shield/scans": {
360
  "deps": [
361
  "shield/tables"
362
  ]
363
  },
364
+ "shield/import": {
365
  "deps": [
366
  "plugin"
367
  ]
368
  },
369
+ "shield/ipanalyse": {
370
  "deps": [
371
  "plugin"
372
  ]
373
  },
374
+ "shield/mainwp-extension": {
375
  "deps": [
376
  "wp-jquery"
377
  ]
378
  },
379
+ "shield/userprofile": {
380
  "deps": [
381
  "u2f-bundle",
382
  "shield/dialog"
383
  ],
384
  "footer": true
385
  },
386
+ "shield/wizard": {
387
  "deps": [
388
  "bootstrap",
389
  "global-plugin",
390
  "jquery/steps"
391
  ]
392
  },
393
+ "u2f-bundle": {},
394
+ "tp/grecaptcha": {
395
  "url": "https://www.google.com/recaptcha/api.js",
396
  "attributes": {
397
  "async": "async",
398
  "defer": "defer"
399
  }
400
  },
401
+ "tp/hcaptcha": {
402
  "url": "https://hcaptcha.com/1/api.js",
403
  "attributes": {
404
  "async": "async",
405
  "defer": "defer"
406
  }
407
  },
408
+ "tp/highlightjs": {
409
  "url": "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/highlight.min.js"
410
  }
411
  }
443
  }
444
  ],
445
  "version_upgrades": [
 
 
 
446
  "10.1.0",
447
  "10.2.1",
448
+ "11.2.0",
449
+ "12.0.0"
450
  ],
451
  "action_links": {
452
  "remove": null,
plugin.json CHANGED
@@ -1,8 +1,8 @@
1
  {
2
  "properties": {
3
- "version": "11.5.5",
4
- "release_timestamp": 1631201669,
5
- "build": "202109.0901",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
@@ -29,6 +29,7 @@
29
  }
30
  },
31
  "paths": {
 
32
  "source": "src",
33
  "autoload": "lib/vendor/autoload.php",
34
  "assets": "resources",
@@ -53,6 +54,7 @@
53
  "plugin",
54
  "jquery/featherlight",
55
  "introjs",
 
56
  "shield/scanners"
57
  ],
58
  "js": [
@@ -62,6 +64,9 @@
62
  "jquery/fileDownload",
63
  "shield/tours",
64
  "bootstrap-select",
 
 
 
65
  "shield/scanners"
66
  ]
67
  },
@@ -93,11 +98,17 @@
93
  ]
94
  },
95
  "datatables-bootstrap": {
96
- "url": "https://cdn.datatables.net/1.10.25/css/dataTables.bootstrap4.min.css",
97
  "deps": [
98
  "bootstrap"
99
  ]
100
  },
 
 
 
 
 
 
101
  "datatables-select": {
102
  "url": "https://cdn.datatables.net/select/1.3.3/css/select.dataTables.min.css",
103
  "deps": [
@@ -149,6 +160,15 @@
149
  "footer": true
150
  },
151
  "shield/mainwp": {},
 
 
 
 
 
 
 
 
 
152
  "shield/scanners": {
153
  "deps": [
154
  "datatables-select",
@@ -162,61 +182,67 @@
162
  }
163
  },
164
  "js": {
165
- "bootstrap": {
166
  "url": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js",
167
  "deps": [
168
  "wp-jquery"
169
  ]
170
  },
171
- "select2": {
172
  "url": "https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js",
173
  "deps": [
174
  "plugin"
175
  ]
176
  },
177
- "bootstrap-datepicker": {
178
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js",
179
  "deps": [
180
  "bootstrap"
181
  ]
182
  },
183
- "bootstrap-select": {
184
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.18/js/bootstrap-select.min.js",
185
  "deps": [
186
  "bootstrap"
187
  ]
188
  },
189
- "datatables": {
190
- "url": "https://cdn.datatables.net/1.10.25/js/jquery.dataTables.min.js",
191
  "deps": [
192
  "bootstrap",
193
  "wp-jquery"
194
  ]
195
  },
196
- "datatables-bootstrap": {
197
- "url": "https://cdn.datatables.net/1.10.25/js/dataTables.bootstrap4.min.js",
198
  "deps": [
199
  "datatables"
200
  ]
201
  },
202
- "datatables-select": {
 
 
 
 
 
 
203
  "url": "https://cdn.datatables.net/select/1.3.3/js/dataTables.select.min.js",
204
  "deps": [
205
- "datatables"
206
  ]
207
  },
208
- "datatables-buttons": {
209
  "url": "https://cdn.datatables.net/buttons/1.7.1/js/dataTables.buttons.min.js",
210
  "deps": [
211
  "datatables-bootstrap"
212
  ]
213
  },
214
- "global-plugin": {
215
  "deps": [
216
  "wp-jquery"
217
  ]
218
  },
219
- "plugin": {
220
  "deps": [
221
  "bootstrap",
222
  "datatables-bootstrap",
@@ -226,76 +252,95 @@
226
  "lz-string.min"
227
  ]
228
  },
229
- "base64.min": {
230
  "url": "https://cdn.jsdelivr.net/npm/js-base64@2.6.4/base64.min.js"
231
  },
232
- "lz-string.min": {},
233
- "jquery/fileDownload": {},
234
- "jquery/steps": {
235
  "url": "https://cdnjs.cloudflare.com/ajax/libs/jquery-steps/1.1.0/jquery.steps.min.js"
236
  },
237
- "jquery/featherlight": {
238
  "url": "https://cdnjs.cloudflare.com/ajax/libs/featherlight/1.7.13/featherlight.min.js"
239
  },
240
- "chartist": {
241
  "url": "https://cdnjs.cloudflare.com/ajax/libs/chartist/0.11.4/chartist.min.js"
242
  },
243
- "chartist-plugin-legend": {
244
  "deps": [
245
  "chartist"
246
  ]
247
  },
248
- "introjs": {
249
  "url": "https://cdnjs.cloudflare.com/ajax/libs/intro.js/3.3.1/intro.min.js"
250
  },
251
- "shield/charts": {
252
  "deps": [
253
  "chartist",
254
  "chartist-plugin-legend",
255
  "plugin"
256
  ]
257
  },
258
- "shuffle": {
259
  "url": "https://cdnjs.cloudflare.com/ajax/libs/Shuffle/5.3.0/shuffle.min.js"
260
  },
261
- "shield/shuffle": {
262
  "deps": [
263
  "shuffle"
264
  ]
265
  },
266
- "shield/dialog": {
267
  "deps": [
268
  "wp-jquery-ui-dialog"
269
  ]
270
  },
271
- "shield/comments": {
272
  "deps": [
273
  "wp-jquery"
274
  ],
275
  "footer": true
276
  },
277
- "shield/loginbot": {
278
  "deps": [
279
  "wp-jquery"
280
  ]
281
  },
282
- "shield/navigation": {},
283
- "shield/secadmin": {
284
  "deps": [
285
  "wp-jquery"
286
  ]
287
  },
288
- "shield/tables": {
289
  "deps": [
290
  "plugin"
291
  ]
292
  },
293
- "shield/scanners": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  "deps": [
295
  "shield/scantables"
296
  ]
297
  },
298
- "shield/scantables": {
299
  "deps": [
300
  "datatables-select",
301
  "datatables-buttons",
@@ -303,64 +348,64 @@
303
  "tp/highlightjs"
304
  ]
305
  },
306
- "shield/tours": {
307
  "deps": [
308
  "plugin",
309
  "introjs"
310
  ]
311
  },
312
- "shield/notbot": {
313
  },
314
- "shield/scans": {
315
  "deps": [
316
  "shield/tables"
317
  ]
318
  },
319
- "shield/import": {
320
  "deps": [
321
  "plugin"
322
  ]
323
  },
324
- "shield/ipanalyse": {
325
  "deps": [
326
  "plugin"
327
  ]
328
  },
329
- "shield/mainwp": {
330
  "deps": [
331
  "wp-jquery"
332
  ]
333
  },
334
- "shield/userprofile": {
335
  "deps": [
336
  "u2f-bundle",
337
  "shield/dialog"
338
  ],
339
  "footer": true
340
  },
341
- "shield/wizard": {
342
  "deps": [
343
  "bootstrap",
344
  "global-plugin",
345
  "jquery/steps"
346
  ]
347
  },
348
- "u2f-bundle": {},
349
- "tp/grecaptcha": {
350
  "url": "https://www.google.com/recaptcha/api.js",
351
  "attributes": {
352
  "async": "async",
353
  "defer": "defer"
354
  }
355
  },
356
- "tp/hcaptcha": {
357
  "url": "https://hcaptcha.com/1/api.js",
358
  "attributes": {
359
  "async": "async",
360
  "defer": "defer"
361
  }
362
  },
363
- "tp/highlightjs": {
364
  "url": "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/highlight.min.js"
365
  }
366
  }
@@ -398,12 +443,10 @@
398
  }
399
  ],
400
  "version_upgrades": [
401
- "9.1.1",
402
- "9.2.0",
403
- "9.2.2",
404
  "10.1.0",
405
  "10.2.1",
406
- "11.2.0"
 
407
  ],
408
  "action_links": {
409
  "remove": null,
1
  {
2
  "properties": {
3
+ "version": "12.0.0",
4
+ "release_timestamp": 1631783098,
5
+ "build": "202109.1601",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
29
  }
30
  },
31
  "paths": {
32
+ "config": "config",
33
  "source": "src",
34
  "autoload": "lib/vendor/autoload.php",
35
  "assets": "resources",
54
  "plugin",
55
  "jquery/featherlight",
56
  "introjs",
57
+ "shield/datatables",
58
  "shield/scanners"
59
  ],
60
  "js": [
64
  "jquery/fileDownload",
65
  "shield/tours",
66
  "bootstrap-select",
67
+ "shield/datatables",
68
+ "shield/traffic",
69
+ "shield/audit_trail",
70
  "shield/scanners"
71
  ]
72
  },
98
  ]
99
  },
100
  "datatables-bootstrap": {
101
+ "url": "https://cdn.datatables.net/1.11.0/css/dataTables.bootstrap4.min.css",
102
  "deps": [
103
  "bootstrap"
104
  ]
105
  },
106
+ "datatables-searchpanes": {
107
+ "url": "https://cdn.datatables.net/searchpanes/1.4.0/css/searchPanes.dataTables.min.css",
108
+ "deps": [
109
+ "datatables-bootstrap"
110
+ ]
111
+ },
112
  "datatables-select": {
113
  "url": "https://cdn.datatables.net/select/1.3.3/css/select.dataTables.min.css",
114
  "deps": [
160
  "footer": true
161
  },
162
  "shield/mainwp": {},
163
+ "shield/datatables": {
164
+ "deps": [
165
+ "datatables-select",
166
+ "datatables-buttons",
167
+ "datatables-bootstrap",
168
+ "datatables-searchpanes",
169
+ "tp/highlightjs"
170
+ ]
171
+ },
172
  "shield/scanners": {
173
  "deps": [
174
  "datatables-select",
182
  }
183
  },
184
  "js": {
185
+ "bootstrap": {
186
  "url": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js",
187
  "deps": [
188
  "wp-jquery"
189
  ]
190
  },
191
+ "select2": {
192
  "url": "https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js",
193
  "deps": [
194
  "plugin"
195
  ]
196
  },
197
+ "bootstrap-datepicker": {
198
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js",
199
  "deps": [
200
  "bootstrap"
201
  ]
202
  },
203
+ "bootstrap-select": {
204
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.18/js/bootstrap-select.min.js",
205
  "deps": [
206
  "bootstrap"
207
  ]
208
  },
209
+ "datatables": {
210
+ "url": "https://cdn.datatables.net/1.11.0/js/jquery.dataTables.min.js",
211
  "deps": [
212
  "bootstrap",
213
  "wp-jquery"
214
  ]
215
  },
216
+ "datatables-bootstrap": {
217
+ "url": "https://cdn.datatables.net/1.11.0/js/dataTables.bootstrap4.min.js",
218
  "deps": [
219
  "datatables"
220
  ]
221
  },
222
+ "datatables-searchpanes": {
223
+ "url": "https://cdn.datatables.net/searchpanes/1.4.0/js/dataTables.searchPanes.min.js",
224
+ "deps": [
225
+ "datatables-bootstrap"
226
+ ]
227
+ },
228
+ "datatables-select": {
229
  "url": "https://cdn.datatables.net/select/1.3.3/js/dataTables.select.min.js",
230
  "deps": [
231
+ "datatables-bootstrap"
232
  ]
233
  },
234
+ "datatables-buttons": {
235
  "url": "https://cdn.datatables.net/buttons/1.7.1/js/dataTables.buttons.min.js",
236
  "deps": [
237
  "datatables-bootstrap"
238
  ]
239
  },
240
+ "global-plugin": {
241
  "deps": [
242
  "wp-jquery"
243
  ]
244
  },
245
+ "plugin": {
246
  "deps": [
247
  "bootstrap",
248
  "datatables-bootstrap",
252
  "lz-string.min"
253
  ]
254
  },
255
+ "base64.min": {
256
  "url": "https://cdn.jsdelivr.net/npm/js-base64@2.6.4/base64.min.js"
257
  },
258
+ "lz-string.min": {},
259
+ "jquery/fileDownload": {},
260
+ "jquery/steps": {
261
  "url": "https://cdnjs.cloudflare.com/ajax/libs/jquery-steps/1.1.0/jquery.steps.min.js"
262
  },
263
+ "jquery/featherlight": {
264
  "url": "https://cdnjs.cloudflare.com/ajax/libs/featherlight/1.7.13/featherlight.min.js"
265
  },
266
+ "chartist": {
267
  "url": "https://cdnjs.cloudflare.com/ajax/libs/chartist/0.11.4/chartist.min.js"
268
  },
269
+ "chartist-plugin-legend": {
270
  "deps": [
271
  "chartist"
272
  ]
273
  },
274
+ "introjs": {
275
  "url": "https://cdnjs.cloudflare.com/ajax/libs/intro.js/3.3.1/intro.min.js"
276
  },
277
+ "shield/charts": {
278
  "deps": [
279
  "chartist",
280
  "chartist-plugin-legend",
281
  "plugin"
282
  ]
283
  },
284
+ "shuffle": {
285
  "url": "https://cdnjs.cloudflare.com/ajax/libs/Shuffle/5.3.0/shuffle.min.js"
286
  },
287
+ "shield/shuffle": {
288
  "deps": [
289
  "shuffle"
290
  ]
291
  },
292
+ "shield/dialog": {
293
  "deps": [
294
  "wp-jquery-ui-dialog"
295
  ]
296
  },
297
+ "shield/comments": {
298
  "deps": [
299
  "wp-jquery"
300
  ],
301
  "footer": true
302
  },
303
+ "shield/loginbot": {
304
  "deps": [
305
  "wp-jquery"
306
  ]
307
  },
308
+ "shield/navigation": {},
309
+ "shield/secadmin": {
310
  "deps": [
311
  "wp-jquery"
312
  ]
313
  },
314
+ "shield/tables": {
315
  "deps": [
316
  "plugin"
317
  ]
318
  },
319
+ "shield/audit_trail": {
320
+ "deps": [
321
+ "shield/datatables"
322
+ ]
323
+ },
324
+ "shield/traffic": {
325
+ "deps": [
326
+ "shield/datatables"
327
+ ]
328
+ },
329
+ "shield/datatables": {
330
+ "deps": [
331
+ "datatables-select",
332
+ "datatables-buttons",
333
+ "datatables-bootstrap",
334
+ "datatables-searchpanes",
335
+ "tp/highlightjs"
336
+ ]
337
+ },
338
+ "shield/scanners": {
339
  "deps": [
340
  "shield/scantables"
341
  ]
342
  },
343
+ "shield/scantables": {
344
  "deps": [
345
  "datatables-select",
346
  "datatables-buttons",
348
  "tp/highlightjs"
349
  ]
350
  },
351
+ "shield/tours": {
352
  "deps": [
353
  "plugin",
354
  "introjs"
355
  ]
356
  },
357
+ "shield/notbot": {
358
  },
359
+ "shield/scans": {
360
  "deps": [
361
  "shield/tables"
362
  ]
363
  },
364
+ "shield/import": {
365
  "deps": [
366
  "plugin"
367
  ]
368
  },
369
+ "shield/ipanalyse": {
370
  "deps": [
371
  "plugin"
372
  ]
373
  },
374
+ "shield/mainwp-extension": {
375
  "deps": [
376
  "wp-jquery"
377
  ]
378
  },
379
+ "shield/userprofile": {
380
  "deps": [
381
  "u2f-bundle",
382
  "shield/dialog"
383
  ],
384
  "footer": true
385
  },
386
+ "shield/wizard": {
387
  "deps": [
388
  "bootstrap",
389
  "global-plugin",
390
  "jquery/steps"
391
  ]
392
  },
393
+ "u2f-bundle": {},
394
+ "tp/grecaptcha": {
395
  "url": "https://www.google.com/recaptcha/api.js",
396
  "attributes": {
397
  "async": "async",
398
  "defer": "defer"
399
  }
400
  },
401
+ "tp/hcaptcha": {
402
  "url": "https://hcaptcha.com/1/api.js",
403
  "attributes": {
404
  "async": "async",
405
  "defer": "defer"
406
  }
407
  },
408
+ "tp/highlightjs": {
409
  "url": "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/highlight.min.js"
410
  }
411
  }
443
  }
444
  ],
445
  "version_upgrades": [
 
 
 
446
  "10.1.0",
447
  "10.2.1",
448
+ "11.2.0",
449
+ "12.0.0"
450
  ],
451
  "action_links": {
452
  "remove": null,
readme.txt CHANGED
@@ -8,7 +8,7 @@ Requires at least: 3.7
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 5.8
11
- Stable tag: 11.5.4
12
 
13
  No-Nonsense Security Hardening that protects WordPress against hackers, malicious bots, and spammers (no captchas!). Now with exclusive ShieldNET Technology.
14
 
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 5.8
11
+ Stable tag: 12.0.0
12
 
13
  No-Nonsense Security Hardening that protects WordPress against hackers, malicious bots, and spammers (no captchas!). Now with exclusive ShieldNET Technology.
14
 
resources/css/plugin.css CHANGED
@@ -709,11 +709,13 @@ input:checked + .icwp-slider:before {
709
  vertical-align: text-bottom;
710
  }
711
  /** TABLE: Audit trail **/
 
712
  td.column-message textarea {
713
  width: 100%;
714
  background: transparent;
715
  box-shadow: none;
716
  border-color: transparent;
 
717
  }
718
  .table-side-filter {
719
  position: sticky;
@@ -728,17 +730,6 @@ td.column-message textarea {
728
  font-size: 0.85rem;
729
  }
730
  /** TABLE: TRAFFIC **/
731
- th.column-visitor {
732
- }
733
- th.column-path {
734
- }
735
- td.column-path code {
736
- color: #4120a2;
737
- display: block;
738
- font-size: 0.9rem;
739
- background-color: transparent; /* rgba(245, 245, 245, 0.8); */
740
- word-break: break-all;
741
- }
742
  th.column-code {
743
  width: 84px;
744
  }
@@ -1229,7 +1220,7 @@ a:focus .gravatar, a:focus, a:focus .media-icon img {
1229
  #SectionIpsWhite .form-inline {
1230
  background-color: transparent !important;
1231
  }
1232
- a[target="_blank"]:not(.option_link_info):not(.card-link)::after {
1233
  content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQElEQVR42qXKwQkAIAxDUUdxtO6/RBQkQZvSi8I/pL4BoGw/XPkh4XigPmsUgh0626AjRsgxHTkUThsG2T/sIlzdTsp52kSS1wAAAABJRU5ErkJggg==);
1234
  margin: 0 3px;
1235
  }
@@ -1552,14 +1543,14 @@ body.folded #FooterBannerGoPro {
1552
  border-color: rgb(177 216 178);
1553
  border-width: 1px 1px 1px 0;
1554
  border-style: solid;
1555
- min-width: 130px;
1556
- width: 130px;
1557
  }
1558
  #NavSideBar a.nav-link:hover {
1559
  }
1560
  #NavSideBar a.nav-link.active {
1561
  font-weight: bolder;
1562
- font-size: 1rem;
1563
  }
1564
  #NavSideBar:hover ul.top-level-nav > .nav-item {
1565
  }
@@ -1568,8 +1559,8 @@ body.folded #FooterBannerGoPro {
1568
  #NavSideBar .nav-item {
1569
  }
1570
  #NavSideBar .nav-item .nav-icon > svg {
1571
- width: 24px;
1572
- height: 24px;
1573
  color: #333333;
1574
  }
1575
  #NavSideBar .nav-item.activesub .nav-icon > svg,
@@ -1586,8 +1577,8 @@ body.folded #FooterBannerGoPro {
1586
  #NavSideBar .nav-item .top-title > svg {
1587
  transform-origin: 50% 50%;
1588
  transform: rotate(60deg);
1589
- height: 8px;
1590
- width: 8px;
1591
  margin-top: -4px;
1592
  margin-left: 2px;
1593
  margin-right: -2px;
@@ -1614,11 +1605,11 @@ body.folded #FooterBannerGoPro {
1614
  /*border-left-width: 0;*/
1615
  position: absolute;
1616
  left: 90%;
1617
- width: 100%;
1618
  z-index: 1000;
1619
  height: auto;
1620
  box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.4);
1621
- margin-top: -60px;
1622
  opacity: 0;
1623
  transition: visibility 0.15s 0s, opacity 0.15s ease-in;
1624
  }
@@ -1636,6 +1627,7 @@ body.folded #FooterBannerGoPro {
1636
  }
1637
  .primary_side_sub_menu a:hover {
1638
  color: #008000;
 
1639
  }
1640
  .primary_side_sub_menu a.active {
1641
  border-left: 3px solid rgba(0, 128, 0, 0.4);
709
  vertical-align: text-bottom;
710
  }
711
  /** TABLE: Audit trail **/
712
+ #table_id-audit_trail textarea,
713
  td.column-message textarea {
714
  width: 100%;
715
  background: transparent;
716
  box-shadow: none;
717
  border-color: transparent;
718
+ font-family: monospace;
719
  }
720
  .table-side-filter {
721
  position: sticky;
730
  font-size: 0.85rem;
731
  }
732
  /** TABLE: TRAFFIC **/
 
 
 
 
 
 
 
 
 
 
 
733
  th.column-code {
734
  width: 84px;
735
  }
1220
  #SectionIpsWhite .form-inline {
1221
  background-color: transparent !important;
1222
  }
1223
+ a[target="_blank"]:not(.option_link_info):not(.card-link):not(.table-link)::after {
1224
  content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQElEQVR42qXKwQkAIAxDUUdxtO6/RBQkQZvSi8I/pL4BoGw/XPkh4XigPmsUgh0626AjRsgxHTkUThsG2T/sIlzdTsp52kSS1wAAAABJRU5ErkJggg==);
1225
  margin: 0 3px;
1226
  }
1543
  border-color: rgb(177 216 178);
1544
  border-width: 1px 1px 1px 0;
1545
  border-style: solid;
1546
+ min-width: 115px;
1547
+ width: 115px;
1548
  }
1549
  #NavSideBar a.nav-link:hover {
1550
  }
1551
  #NavSideBar a.nav-link.active {
1552
  font-weight: bolder;
1553
+ font-size: 0.8rem;
1554
  }
1555
  #NavSideBar:hover ul.top-level-nav > .nav-item {
1556
  }
1559
  #NavSideBar .nav-item {
1560
  }
1561
  #NavSideBar .nav-item .nav-icon > svg {
1562
+ width: 18px;
1563
+ height: 18px;
1564
  color: #333333;
1565
  }
1566
  #NavSideBar .nav-item.activesub .nav-icon > svg,
1577
  #NavSideBar .nav-item .top-title > svg {
1578
  transform-origin: 50% 50%;
1579
  transform: rotate(60deg);
1580
+ height: 6px;
1581
+ width: 6px;
1582
  margin-top: -4px;
1583
  margin-left: 2px;
1584
  margin-right: -2px;
1605
  /*border-left-width: 0;*/
1606
  position: absolute;
1607
  left: 90%;
1608
+ width: 120%;
1609
  z-index: 1000;
1610
  height: auto;
1611
  box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.4);
1612
+ margin-top: -40px;
1613
  opacity: 0;
1614
  transition: visibility 0.15s 0s, opacity 0.15s ease-in;
1615
  }
1627
  }
1628
  .primary_side_sub_menu a:hover {
1629
  color: #008000;
1630
+ text-shadow: 0 1px 1px rgba(0,0,0,0.15);
1631
  }
1632
  .primary_side_sub_menu a.active {
1633
  border-left: 3px solid rgba(0, 128, 0, 0.4);
resources/css/shield/datatables.css ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 
2
+ th.message {
3
+ min-width: 500px !important;
4
+ }
5
+ th.date {
6
+ width: 140px !important;
7
+ max-width: 140px !important;
8
+ }
9
+ td.meta,
10
+ th.meta {
11
+ width: 40px !important;
12
+ max-width: 40px !important;
13
+ }
14
+ td.severity,
15
+ th.severity {
16
+ text-align: center;
17
+ width: 40px !important;
18
+ max-width: 40px !important;
19
+ }
20
+ td.message > .message-header {
21
+ font-weight: bolder;
22
+ display: block;
23
+ }
24
+ td.severity .severity-icon.severity-alert {
25
+ color: #d40000;
26
+ }
27
+ td.severity .severity-icon.severity-warning {
28
+ color: #c18c2a;
29
+ }
30
+ td.severity .severity-icon.severity-notice {
31
+ color: #0303ab;
32
+ }
33
+ td.severity .severity-icon.severity-info {
34
+ color: #039aab;
35
+ }
36
+ td.severity .severity-icon.severity-debug {
37
+ color: grey;
38
+ }
39
+ td.severity .severity-icon > svg {
40
+ margin-top: 10px;
41
+ width: 32px;
42
+ height: 32px;
43
+ }
44
+ .shield-section-datatable .popover .popover-body li {
45
+ margin-bottom: 4px;
46
+ }
47
+ .shield-section-datatable .dtsp-title {
48
+ font-weight: bolder;
49
+ }
50
+ .shield-section-datatable div.dtsp-topRow.dtsp-bordered {
51
+ border: 1px solid black;
52
+ }
53
+
54
+ td.page code {
55
+ color: #4120a2;
56
+ display: block;
57
+ font-size: 0.9rem;
58
+ background-color: transparent; /* rgba(245, 245, 245, 0.8); */
59
+ word-break: break-all;
60
+ }
61
+
62
+ td.user a[target="_blank"]::after,
63
+ td.ip_linked a[target="_blank"]::after {
64
+ content: none !important;
65
+ }
resources/images/bootstrap/exclamation-triangle.svg ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-exclamation-triangle" viewBox="0 0 16 16">
2
+ <path d="M7.938 2.016A.13.13 0 0 1 8.002 2a.13.13 0 0 1 .063.016.146.146 0 0 1 .054.057l6.857 11.667c.036.06.035.124.002.183a.163.163 0 0 1-.054.06.116.116 0 0 1-.066.017H1.146a.115.115 0 0 1-.066-.017.163.163 0 0 1-.054-.06.176.176 0 0 1 .002-.183L7.884 2.073a.147.147 0 0 1 .054-.057zm1.044-.45a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566z"/>
3
+ <path d="M7.002 12a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 5.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995z"/>
4
+ </svg>
resources/images/bootstrap/info-circle.svg ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-info-circle" viewBox="0 0 16 16">
2
+ <path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
3
+ <path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>
4
+ </svg>
resources/images/bootstrap/info-square.svg ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-info-square" viewBox="0 0 16 16">
2
+ <path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
3
+ <path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>
4
+ </svg>
resources/images/bootstrap/question-diamond.svg ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-question-diamond" viewBox="0 0 16 16">
2
+ <path d="M6.95.435c.58-.58 1.52-.58 2.1 0l6.515 6.516c.58.58.58 1.519 0 2.098L9.05 15.565c-.58.58-1.519.58-2.098 0L.435 9.05a1.482 1.482 0 0 1 0-2.098L6.95.435zm1.4.7a.495.495 0 0 0-.7 0L1.134 7.65a.495.495 0 0 0 0 .7l6.516 6.516a.495.495 0 0 0 .7 0l6.516-6.516a.495.495 0 0 0 0-.7L8.35 1.134z"/>
3
+ <path d="M5.255 5.786a.237.237 0 0 0 .241.247h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286zm1.557 5.763c0 .533.425.927 1.01.927.609 0 1.028-.394 1.028-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94z"/>
4
+ </svg>
resources/images/bootstrap/tags.svg ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-tags" viewBox="0 0 16 16">
2
+ <path d="M3 2v4.586l7 7L14.586 9l-7-7H3zM2 2a1 1 0 0 1 1-1h4.586a1 1 0 0 1 .707.293l7 7a1 1 0 0 1 0 1.414l-4.586 4.586a1 1 0 0 1-1.414 0l-7-7A1 1 0 0 1 2 6.586V2z"/>
3
+ <path d="M5.5 5a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0 1a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zM1 7.086a1 1 0 0 0 .293.707L8.75 15.25l-.043.043a1 1 0 0 1-1.414 0l-7-7A1 1 0 0 1 0 7.586V3a1 1 0 0 1 1-1v5.086z"/>
4
+ </svg>
resources/images/bootstrap/x-octagon.svg ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-octagon" viewBox="0 0 16 16">
2
+ <path d="M4.54.146A.5.5 0 0 1 4.893 0h6.214a.5.5 0 0 1 .353.146l4.394 4.394a.5.5 0 0 1 .146.353v6.214a.5.5 0 0 1-.146.353l-4.394 4.394a.5.5 0 0 1-.353.146H4.893a.5.5 0 0 1-.353-.146L.146 11.46A.5.5 0 0 1 0 11.107V4.893a.5.5 0 0 1 .146-.353L4.54.146zM5.1 1 1 5.1v5.8L5.1 15h5.8l4.1-4.1V5.1L10.9 1H5.1z"/>
3
+ <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
4
+ </svg>
resources/js/plugin.js CHANGED
@@ -6,19 +6,19 @@ var iCWP_WPSF_OptionsPages = new function () {
6
 
7
  this.initialise = function () {
8
  jQuery( document ).ready( function () {
9
- jQuery( document ).on( "click", "a.nav-link.module", showWaiting );
10
 
11
  /** Track active tab */
12
- jQuery( document ).on( "click", "#ModuleOptionsNav a.nav-link", function ( e ) {
13
  e.preventDefault();
14
  jQuery( this ).tab( 'show' );
15
  jQuery( 'html,body' ).scrollTop( 0 );
16
  } );
17
- jQuery( document ).on( "shown.bs.tab", "#ModuleOptionsNav a.nav-link", function ( e ) {
18
  window.location.hash = jQuery( e.target ).attr( "href" ).substr( 1 );
19
  } );
20
 
21
- jQuery( document ).on( "odp-optsrender", onOptsTabRender );
22
  } );
23
  };
24
 
6
 
7
  this.initialise = function () {
8
  jQuery( document ).ready( function () {
9
+ jQuery( document ).on( 'click', 'a.nav-link.module', showWaiting );
10
 
11
  /** Track active tab */
12
+ jQuery( document ).on( 'click', '#ModuleOptionsNav a.nav-link', function ( e ) {
13
  e.preventDefault();
14
  jQuery( this ).tab( 'show' );
15
  jQuery( 'html,body' ).scrollTop( 0 );
16
  } );
17
+ jQuery( document ).on( 'shown.bs.tab', '#ModuleOptionsNav a.nav-link', function ( e ) {
18
  window.location.hash = jQuery( e.target ).attr( "href" ).substr( 1 );
19
  } );
20
 
21
+ jQuery( document ).on( 'odp-optsrender', onOptsTabRender );
22
  } );
23
  };
24
 
resources/js/shield/audit_trail.js ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function ( $, window, document, undefined ) {
2
+
3
+ $.fn.icwpWpsfAuditTableActions = function ( runtimeOptions ) {
4
+ return this.each(
5
+ function () {
6
+ new $.icwpWpsfAuditTableActions( this, runtimeOptions )
7
+ }
8
+ );
9
+ };
10
+
11
+ $.icwpWpsfAuditTableActions = function ( el, options ) {
12
+ // To avoid scope issues, use 'base' instead of 'this'
13
+ // to reference this class from internal events and functions.
14
+ const base = this;
15
+
16
+ // Access to jQuery and DOM versions of element
17
+ base.$el = $( el );
18
+ base.el = el;
19
+
20
+ // Add a reverse reference to the DOM object
21
+ base.$el.data( "icwpWpsfAuditTableActions", base );
22
+
23
+ base.init = function () {
24
+ base.options = $.extend( {}, $.icwpWpsfAuditTableActions.defaultOptions, options );
25
+ base.setupDatatable();
26
+ base.bindEvents();
27
+ };
28
+
29
+ base.rowSelectionChanged = function () {
30
+ if ( this.$table.rows( { selected: true } ).count() > 0 ) {
31
+ this.$table.buttons( 'selected-ignore:name, selected-repair:name' ).enable();
32
+ }
33
+ else {
34
+ this.$table.buttons( 'selected-ignore:name, selected-repair:name' ).disable();
35
+ }
36
+ };
37
+
38
+ base.bindEvents = function () {
39
+
40
+ $( 'body' ).popover( {
41
+ trigger: 'click',
42
+ sanitize: false,
43
+ html: true,
44
+ animation: true,
45
+ selector: 'td.meta > button[data-toggle="popover"]',
46
+ container: 'body',
47
+ content: function () {
48
+ let content = 'no meta';
49
+ let reqData = base.getBaseAjaxData();
50
+ reqData.sub_action = 'get_request_meta';
51
+ reqData.rid = $( this ).data( 'rid' );
52
+ reqData.apto_wrap_response = 1;
53
+
54
+ jQuery.ajax( {
55
+ type: "POST",
56
+ url: ajaxurl,
57
+ data: reqData,
58
+ dataType: "text",
59
+ async: false,
60
+ success: function ( raw ) {
61
+ let resp = iCWP_WPSF_ParseAjaxResponse.parseIt( raw );
62
+ content = resp.data.html;
63
+ }
64
+ } ).fail( function () {
65
+ alert( 'Something went wrong with the request - it was either blocked or there was an error.' );
66
+ } ).always( function () {
67
+ iCWP_WPSF_BodyOverlay.hide();
68
+ } );
69
+
70
+ return content;
71
+ },
72
+ } );
73
+
74
+ base.$table.on( 'draw',
75
+ function ( e, dt, type, row_index ) {
76
+ base.rowSelectionChanged.call( base );
77
+ }
78
+ );
79
+
80
+ base.$table.on( 'select',
81
+ function ( e, dt, type, row_index ) {
82
+ base.rowSelectionChanged.call( base );
83
+ }
84
+ );
85
+
86
+ base.$table.on( 'deselect',
87
+ function ( e, dt, type, row_index ) {
88
+ base.rowSelectionChanged.call( base );
89
+ }
90
+ );
91
+
92
+ };
93
+
94
+ base.sendReq = function ( reqData, forceTableReload = false ) {
95
+
96
+ $( 'html' ).css( 'cursor', 'wait' );
97
+
98
+ $.post( ajaxurl, reqData,
99
+ function ( response ) {
100
+
101
+ if ( response.success ) {
102
+ iCWP_WPSF_Toaster.showMessage( response.data.message, response.success );
103
+ if ( response.data.table_reload ) {
104
+ }
105
+ else {
106
+ iCWP_WPSF_Toaster.showMessage( response.data.message, response.success );
107
+ }
108
+ }
109
+ else {
110
+ let msg = 'Communications error with site.';
111
+ if ( response.data.message !== undefined ) {
112
+ msg = response.data.message;
113
+ }
114
+ alert( msg );
115
+ }
116
+
117
+ if ( response.data.table_reload ) {
118
+ base.tableReload();
119
+ }
120
+ }
121
+ ).always( function () {
122
+ $( "html" ).css( "cursor", 'initial' );
123
+ }
124
+ );
125
+ };
126
+
127
+ base.getBaseAjaxData = function () {
128
+ return JSON.parse( JSON.stringify( base.options.ajax[ 'logtable_action' ] ) );
129
+ };
130
+
131
+ base.setupDatatable = function () {
132
+
133
+ this.$table = this.$el.DataTable(
134
+ $.extend( base.options.datatables_init,
135
+ {
136
+ ajax: function ( data, callback, settings ) {
137
+ iCWP_WPSF_BodyOverlay.show();
138
+ let reqData = base.getBaseAjaxData();
139
+ reqData.sub_action = 'retrieve_table_data';
140
+ reqData.type = base.options.type;
141
+ reqData.file = base.options.file;
142
+ $.post( ajaxurl, reqData, function ( response ) {
143
+ if ( response.success ) {
144
+ callback( response.data.vars );
145
+ }
146
+ else {
147
+ let msg = 'Communications error with site.';
148
+ if ( response.data.message !== undefined ) {
149
+ msg = response.data.message;
150
+ }
151
+ alert( msg );
152
+ }
153
+ } )
154
+ .always( function () {
155
+ iCWP_WPSF_BodyOverlay.hide();
156
+ } );
157
+ },
158
+ deferRender: true,
159
+ select: {
160
+ style: 'multi'
161
+ },
162
+ dom: 'PBfrptip',
163
+ searchPanes: {
164
+ cascadePanes: true,
165
+ viewTotal: true,
166
+ initCollapsed: true
167
+ },
168
+ buttons: [
169
+ {
170
+ text: 'Reload',
171
+ name: 'table-reload',
172
+ className: 'action table-refresh',
173
+ action: function ( e, dt, node, config ) {
174
+ base.tableReload.call( base );
175
+ }
176
+ }
177
+ ],
178
+ language: {
179
+ emptyTable: "There are no items to display, or they've been set to be ignored."
180
+ }
181
+ }
182
+ ) );
183
+ };
184
+
185
+ base.tableReload = function ( full = false ) {
186
+ this.$table.ajax.reload( null, full );
187
+ this.rowSelectionChanged();
188
+ };
189
+
190
+ // Run initializer
191
+ base.init();
192
+ }
193
+
194
+ $.icwpWpsfAuditTableActions.defaultOptions = {};
195
+
196
+ })( jQuery );
resources/js/{base64.min.js → shield/datatables.js} RENAMED
File without changes
resources/js/shield/options.js DELETED
@@ -1,304 +0,0 @@
1
- jQuery.fn.icwpWpsfTableWithFilter = function ( aOptions ) {
2
-
3
- var resetFilters = function ( evt ) {
4
- jQuery( 'input[type=text]', $oForm ).each( function () {
5
- jQuery( this ).val( '' );
6
- } );
7
- jQuery( 'select', $oForm ).each( function () {
8
- jQuery( this ).prop( 'selectedIndex', 0 );
9
- } );
10
- jQuery( 'input[type=checkbox]', $oForm ).each( function () {
11
- jQuery( this ).prop( 'checked', false );
12
- } );
13
- aOpts[ 'table' ].renderTableFromForm( $oForm );
14
- };
15
-
16
- var submitFilters = function ( evt ) {
17
- evt.preventDefault();
18
- aOpts[ 'table' ].renderTableFromForm( $oForm );
19
- return false;
20
- };
21
-
22
- var initialise = function () {
23
- jQuery( document ).ready( function () {
24
- $oForm = jQuery( aOpts[ 'selector_filter_form' ] );
25
- $oForm.on( 'submit', submitFilters );
26
- $oForm.on( 'click', 'a#ClearForm', resetFilters );
27
- } );
28
- };
29
-
30
- var $oThis = this;
31
- var aOpts = jQuery.extend( {}, aOptions );
32
- var $oForm;
33
- initialise();
34
-
35
- return this;
36
- };
37
-
38
- /**
39
- * Add ajax actions to table buttons, and automatically refreshes the table.
40
- */
41
- (function ( $, window, document, undefined ) {
42
-
43
- var pluginName = 'icwpWpsfTableActions';
44
-
45
- function Ob_TableActions( element, options ) {
46
- this.element = element;
47
- this._name = pluginName;
48
- this._defaults = $.fn.icwpWpsfTableActions.defaults;
49
- this.options = $.extend(
50
- {
51
- 'forms': {
52
- 'insert': ''
53
- }
54
- },
55
- this._defaults,
56
- options
57
- );
58
- this.init();
59
- }
60
-
61
- $.extend(
62
- Ob_TableActions.prototype,
63
- {
64
- init: function () {
65
- this.buildCache();
66
- this.bindEvents();
67
- },
68
- destroy: function () {
69
- this.unbindEvents();
70
- this.$element.removeData();
71
- },
72
- buildCache: function () {
73
- this.$element = $( this.element );
74
- this.$oFormInsert = this.options[ 'forms' ][ 'insert' ];
75
- },
76
- bindEvents: function () {
77
- var plugin = this;
78
-
79
- plugin.$element.on(
80
- 'click' + '.' + plugin._name,
81
- 'button.action.delete',
82
- function ( evt ) {
83
- evt.preventDefault();
84
- if ( confirm( icwp_wpsf_vars_insights.strings.are_you_sure ) ) {
85
- plugin.options[ 'working_rid' ] = $( this ).data( 'rid' );
86
- plugin.deleteEntry.call( plugin );
87
- }
88
- }
89
- );
90
-
91
- plugin.$element.on(
92
- 'click' + '.' + plugin._name,
93
- 'button.action.ignore',
94
- function ( evt ) {
95
- evt.preventDefault();
96
- plugin.options[ 'working_rid' ] = $( this ).data( 'rid' );
97
- plugin.ignoreEntry.call( plugin );
98
- }
99
- );
100
-
101
- if ( typeof this.$oFormInsert !== 'undefined' && this.$oFormInsert.length ) {
102
- this.$oFormInsert.on(
103
- 'submit' + '.' + plugin._name,
104
- function ( evt ) {
105
- evt.preventDefault();
106
- plugin.insertEntry.call( plugin );
107
- }
108
- );
109
- }
110
-
111
- plugin.$element.on(
112
- 'click' + '.' + plugin._name,
113
- 'button.action.repair',
114
- function ( evt ) {
115
- evt.preventDefault();
116
- plugin.options[ 'working_rid' ] = $( this ).data( 'rid' );
117
- plugin.repairEntry.call( plugin );
118
- }
119
- );
120
-
121
- plugin.$element.on(
122
- 'click' + '.' + plugin._name,
123
- 'button.action.item_action',
124
- function ( evt ) {
125
- evt.preventDefault();
126
- plugin.options[ 'working_rid' ] = $( this ).data( 'rid' );
127
- plugin.options[ 'working_item_action' ] = $( this ).data( 'item_action' );
128
- plugin.itemAction.call( plugin );
129
- }
130
- );
131
-
132
- plugin.$element.on(
133
- 'click' + '.' + plugin._name,
134
- '.tablenav.top input[type=submit].button.action',
135
- function ( evt ) {
136
- evt.preventDefault();
137
- var sAction = $( '#bulk-action-selector-top', plugin.$element ).find( ":selected" ).val();
138
-
139
- if ( sAction === "-1" ) {
140
- alert( icwp_wpsf_vars_insights.strings.select_action );
141
- }
142
- else {
143
- var aCheckedIds = $( "input:checkbox[name=ids]:checked", plugin.$element ).map(
144
- function () {
145
- return $( this ).val()
146
- } ).get();
147
-
148
- if ( aCheckedIds.length < 1 ) {
149
- alert( 'No rows currently selected' );
150
- }
151
- else {
152
- plugin.options[ 'req_params' ][ 'bulk_action' ] = sAction;
153
- plugin.options[ 'req_params' ][ 'ids' ] = aCheckedIds;
154
- plugin.bulkAction.call( plugin );
155
- }
156
- }
157
- return false;
158
- }
159
- );
160
-
161
- plugin.$element.on(
162
- 'click' + '.' + plugin._name,
163
- 'button.action.custom-action',
164
- function ( evt ) {
165
- evt.preventDefault();
166
- var $oButt = $( this );
167
- var sCustomAction = $oButt.data( 'custom-action' );
168
- if ( sCustomAction in plugin.options[ 'custom_actions_ajax' ] ) {
169
- plugin.options[ 'working_custom_action' ] = plugin.options[ 'custom_actions_ajax' ][ sCustomAction ];
170
- plugin.options[ 'working_custom_action' ][ 'rid' ] = $oButt.data( 'rid' );
171
- plugin.customAction.call( plugin );
172
- }
173
- else {
174
- /** This should never be reached live: **/
175
- alert( 'custom action not supported: ' + sCustomAction );
176
- }
177
- }
178
- );
179
-
180
- plugin.$element.on(
181
- 'click' + '.' + plugin._name,
182
- 'button.action.href-download',
183
- function ( evt ) {
184
- evt.preventDefault();
185
- var $oButt = $( this );
186
- var sHref = $oButt.data( 'href-download' );
187
- if ( sHref !== undefined ) {
188
- plugin.options[ 'working_href_download' ] = sHref;
189
- plugin.hrefDownload.call( plugin );
190
- }
191
- }
192
- );
193
-
194
- },
195
- unbindEvents: function () {
196
- /*
197
- Unbind all events in our plugin's namespace that are attached
198
- to "this.$element".
199
- */
200
- this.$element.off( '.' + this._name );
201
- },
202
-
203
- bulkAction: function () {
204
- let aRequestData = this.options[ 'ajax_bulk_action' ];
205
- this.sendReq( aRequestData );
206
- },
207
-
208
- deleteEntry: function () {
209
- let aRequestData = this.options[ 'ajax_item_delete' ];
210
- aRequestData[ 'rid' ] = this.options[ 'working_rid' ];
211
- this.sendReq( aRequestData );
212
- },
213
-
214
- insertEntry: function () {
215
- let requestData = this.options[ 'ajax_item_insert' ];
216
- requestData[ 'form_params' ] = this.$oFormInsert.serialize();
217
- this.sendReq( requestData );
218
- this.$oFormInsert[ 0 ].reset();
219
- },
220
-
221
- ignoreEntry: function () {
222
- let aRequestData = this.options[ 'ajax_item_ignore' ];
223
- aRequestData[ 'rid' ] = this.options[ 'working_rid' ];
224
- this.sendReq( aRequestData );
225
- },
226
-
227
- repairEntry: function () {
228
- let aRequestData = this.options[ 'ajax_item_repair' ];
229
- aRequestData[ 'rid' ] = this.options[ 'working_rid' ];
230
- this.sendReq( aRequestData );
231
- },
232
-
233
- itemAction: function () {
234
- let aRequestData = this.options[ 'ajax_item_action' ];
235
- aRequestData[ 'rid' ] = this.options[ 'working_rid' ];
236
- aRequestData[ 'item_action' ] = this.options[ 'working_item_action' ];
237
- this.sendReq( aRequestData );
238
- },
239
-
240
- customAction: function () {
241
- this.sendReq( this.options[ 'working_custom_action' ] );
242
- },
243
-
244
- hrefDownload: function () {
245
- $.fileDownload( this.options[ 'working_href_download' ], {
246
- preparingMessageHtml: icwp_wpsf_vars_plugin.strings.downloading_file,
247
- failMessageHtml: icwp_wpsf_vars_plugin.strings.downloading_file_problem
248
- } );
249
- return false;
250
- },
251
-
252
- sendReq: function ( aRequestData ) {
253
- iCWP_WPSF_BodyOverlay.show();
254
-
255
- var plugin = this;
256
-
257
- $.post( ajaxurl, $.extend( aRequestData, plugin.options[ 'req_params' ] ),
258
- function ( oResponse ) {
259
-
260
- if ( oResponse.success ) {
261
- iCWP_WPSF_Toaster.showMessage( oResponse.data.message, oResponse.success );
262
- if ( oResponse.data.page_reload ) {
263
- location.reload();
264
- }
265
- else {
266
- plugin.options[ 'table' ].reloadTable();
267
- iCWP_WPSF_Toaster.showMessage( oResponse.data.message, oResponse.success );
268
- iCWP_WPSF_BodyOverlay.hide();
269
- }
270
- }
271
- else {
272
- let sMessage = 'Communications error with site.';
273
- if ( oResponse.data.message !== undefined ) {
274
- sMessage = oResponse.data.message;
275
- }
276
- alert( sMessage );
277
- iCWP_WPSF_BodyOverlay.hide();
278
- }
279
- }
280
- ).always( function () {
281
- }
282
- );
283
- },
284
- callback: function () {
285
- }
286
- }
287
- );
288
-
289
- $.fn.icwpWpsfTableActions = function ( aOptions ) {
290
- return this.each(
291
- function () {
292
- if ( !$.data( this, "plugin_" + pluginName ) ) {
293
- $.data( this, "plugin_" + pluginName, new Ob_TableActions( this, aOptions ) );
294
- }
295
- }
296
- );
297
- };
298
-
299
- $.fn.icwpWpsfTableActions.defaults = {
300
- 'custom_actions_ajax': {},
301
- 'req_params': {}
302
- };
303
-
304
- })( jQuery );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
resources/js/shield/traffic.js ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function ( $, window, document, undefined ) {
2
+
3
+ $.fn.icwpWpsfTrafficTableActions = function ( runtimeOptions ) {
4
+ return this.each(
5
+ function () {
6
+ new $.icwpWpsfTrafficTableActions( this, runtimeOptions )
7
+ }
8
+ );
9
+ };
10
+
11
+ $.icwpWpsfTrafficTableActions = function ( el, options ) {
12
+ // To avoid scope issues, use 'base' instead of 'this'
13
+ // to reference this class from internal events and functions.
14
+ const base = this;
15
+
16
+ // Access to jQuery and DOM versions of element
17
+ base.$el = $( el );
18
+ base.el = el;
19
+
20
+ // Add a reverse reference to the DOM object
21
+ base.$el.data( "icwpWpsfTrafficTableActions", base );
22
+
23
+ base.init = function () {
24
+ base.options = $.extend( {}, $.icwpWpsfTrafficTableActions.defaultOptions, options );
25
+ base.setupDatatable();
26
+ base.bindEvents();
27
+ };
28
+
29
+ base.rowSelectionChanged = function () {
30
+ if ( this.$table.rows( { selected: true } ).count() > 0 ) {
31
+ this.$table.buttons( 'selected-ignore:name, selected-repair:name' ).enable();
32
+ }
33
+ else {
34
+ this.$table.buttons( 'selected-ignore:name, selected-repair:name' ).disable();
35
+ }
36
+ };
37
+
38
+ base.bindEvents = function () {
39
+
40
+ base.$table.on( 'draw',
41
+ function ( e, dt, type, row_index ) {
42
+ base.rowSelectionChanged.call( base );
43
+ }
44
+ );
45
+
46
+ base.$table.on( 'select',
47
+ function ( e, dt, type, row_index ) {
48
+ base.rowSelectionChanged.call( base );
49
+ }
50
+ );
51
+
52
+ base.$table.on( 'deselect',
53
+ function ( e, dt, type, row_index ) {
54
+ base.rowSelectionChanged.call( base );
55
+ }
56
+ );
57
+
58
+ };
59
+
60
+ base.sendReq = function ( reqData, forceTableReload = false ) {
61
+
62
+ $( 'html' ).css( 'cursor', 'wait' );
63
+
64
+ $.post( ajaxurl, reqData,
65
+ function ( response ) {
66
+
67
+ if ( response.success ) {
68
+ iCWP_WPSF_Toaster.showMessage( response.data.message, response.success );
69
+ if ( response.data.table_reload ) {
70
+ }
71
+ else {
72
+ iCWP_WPSF_Toaster.showMessage( response.data.message, response.success );
73
+ }
74
+ }
75
+ else {
76
+ let msg = 'Communications error with site.';
77
+ if ( response.data.message !== undefined ) {
78
+ msg = response.data.message;
79
+ }
80
+ alert( msg );
81
+ }
82
+
83
+ if ( response.data.table_reload ) {
84
+ base.tableReload();
85
+ }
86
+ }
87
+ ).always( function () {
88
+ $( "html" ).css( "cursor", 'initial' );
89
+ }
90
+ );
91
+ };
92
+
93
+ base.getBaseAjaxData = function () {
94
+ return JSON.parse( JSON.stringify( base.options.ajax[ 'logtable_action' ] ) );
95
+ };
96
+
97
+ base.setupDatatable = function () {
98
+
99
+ this.$table = this.$el.DataTable(
100
+ $.extend( base.options.datatables_init,
101
+ {
102
+ ajax: function ( data, callback, settings ) {
103
+ let reqData = base.getBaseAjaxData();
104
+ reqData.sub_action = 'retrieve_table_data';
105
+ reqData.type = base.options.type;
106
+ reqData.file = base.options.file;
107
+ $.post( ajaxurl, reqData, function ( response ) {
108
+ if ( response.success ) {
109
+ callback( response.data.vars );
110
+ }
111
+ else {
112
+ let msg = 'Communications error with site.';
113
+ if ( response.data.message !== undefined ) {
114
+ msg = response.data.message;
115
+ }
116
+ alert( msg );
117
+ }
118
+ } );
119
+ },
120
+ deferRender: true,
121
+ select: {
122
+ style: 'multi'
123
+ },
124
+ dom: 'PBfrptip',
125
+ searchPanes: {
126
+ cascadePanes: true,
127
+ viewTotal: true,
128
+ initCollapsed: true
129
+ },
130
+ buttons: [
131
+ {
132
+ text: 'Reload',
133
+ name: 'table-reload',
134
+ className: 'action table-refresh',
135
+ action: function ( e, dt, node, config ) {
136
+ base.tableReload.call( base );
137
+ }
138
+ }
139
+ ],
140
+ language: {
141
+ emptyTable: "There are no items to display, or they've been set to be ignored."
142
+ }
143
+ }
144
+ ) );
145
+ };
146
+
147
+ base.tableReload = function ( full = false ) {
148
+ this.$table.ajax.reload( null, full );
149
+ this.rowSelectionChanged();
150
+ };
151
+
152
+ // Run initializer
153
+ base.init();
154
+ }
155
+
156
+ $.icwpWpsfTrafficTableActions.defaultOptions = {};
157
+
158
+ })( jQuery );
src/config/feature-audit_trail.php DELETED
@@ -1,271 +0,0 @@
1
- {
2
- "slug": "audit_trail",
3
- "properties": {
4
- "slug": "audit_trail",
5
- "name": "Audit Trail",
6
- "sidebar_name": "Audit Trail",
7
- "show_module_menu_item": false,
8
- "show_module_options": true,
9
- "storage_key": "audit_trail",
10
- "tagline": "Track All Site Activity: Who, What, When and Where",
11
- "show_central": true,
12
- "access_restricted": true,
13
- "premium": false,
14
- "run_if_whitelisted": true,
15
- "run_if_verified_bot": false,
16
- "run_if_wpcli": true,
17
- "order": 110
18
- },
19
- "menu_items": [
20
- {
21
- "title": "Audit Trail",
22
- "slug": "audit-redirect"
23
- }
24
- ],
25
- "custom_redirects": [
26
- {
27
- "source_mod_page": "audit-redirect",
28
- "target_mod_page": "insights",
29
- "query_args": {
30
- "inav": "audit"
31
- }
32
- }
33
- ],
34
- "sections": [
35
- {
36
- "slug": "section_audit_trail_options",
37
- "primary": true,
38
- "title": "Audit Trail Options",
39
- "title_short": "Options",
40
- "beacon_id": 241,
41
- "summary": [
42
- "Purpose - Provides finer control over the audit trail itself.",
43
- "Recommendation - These settings are dependent on your requirements."
44
- ]
45
- },
46
- {
47
- "slug": "section_change_tracking",
48
- "hidden": true,
49
- "title": "Change Tracking",
50
- "title_short": "Change Tracking",
51
- "summary": [
52
- "Purpose - Track significant changes to your site.",
53
- "Recommendation - Keep this Reporting feature turned on."
54
- ]
55
- },
56
- {
57
- "slug": "section_enable_plugin_feature_audit_trail",
58
- "title": "Enable Module: Audit Trail",
59
- "title_short": "Disable Module",
60
- "beacon_id": 241,
61
- "summary": [
62
- "Purpose - The Audit Trail is designed so you can look back on events and analyse what happened and what may have gone wrong.",
63
- "Recommendation - Keep the Audit Trail feature turned on."
64
- ]
65
- },
66
- {
67
- "slug": "section_non_ui",
68
- "hidden": true
69
- }
70
- ],
71
- "options": [
72
- {
73
- "key": "enable_audit_trail",
74
- "section": "section_enable_plugin_feature_audit_trail",
75
- "advanced": true,
76
- "default": "Y",
77
- "type": "checkbox",
78
- "link_info": "https://shsec.io/5p",
79
- "link_blog": "https://shsec.io/a1",
80
- "beacon_id": 241,
81
- "name": "Enable Audit Trail",
82
- "summary": "Enable (or Disable) The Audit Trail module",
83
- "description": "Un-Checking this option will completely disable the Audit Trail module"
84
- },
85
- {
86
- "key": "audit_trail_auto_clean",
87
- "section": "section_audit_trail_options",
88
- "default": 14,
89
- "min": 1,
90
- "type": "integer",
91
- "link_info": "https://shsec.io/a2",
92
- "link_blog": "https://shsec.io/a1",
93
- "beacon_id": 375,
94
- "name": "Auto Clean",
95
- "summary": "Enable Audit Auto Cleaning",
96
- "description": "Events older than the number of days specified will be automatically cleaned from the database"
97
- },
98
- {
99
- "key": "audit_trail_max_entries",
100
- "section": "section_audit_trail_options",
101
- "premium": true,
102
- "default": 1000,
103
- "min": 0,
104
- "type": "integer",
105
- "link_info": "https://shsec.io/hc",
106
- "link_blog": "",
107
- "beacon_id": 128,
108
- "name": "Max Trail Length",
109
- "summary": "Maximum Audit Trail Length To Keep",
110
- "description": "Automatically remove any audit trail entries when this limit is exceeded."
111
- },
112
- {
113
- "key": "enable_change_tracking",
114
- "section": "section_change_tracking",
115
- "default": "disabled",
116
- "type": "select",
117
- "value_options": [
118
- {
119
- "value_key": "disabled",
120
- "text": "Disabled"
121
- },
122
- {
123
- "value_key": "enabled",
124
- "text": "Enabled"
125
- },
126
- {
127
- "value_key": "enabled_with_email",
128
- "text": "Enabled With Email Reports"
129
- }
130
- ],
131
- "link_info": "",
132
- "link_blog": "",
133
- "name": "Enable Change Tracking",
134
- "summary": "Track Major Changes To Your Site",
135
- "description": "Tracking major changes to your site will help you monitor and catch malicious damage."
136
- },
137
- {
138
- "key": "ct_snapshots_per_week",
139
- "section": "section_change_tracking",
140
- "type": "integer",
141
- "default": 7,
142
- "min": 1,
143
- "link_info": "",
144
- "link_blog": "",
145
- "name": "Snapshot Per Week",
146
- "summary": "Number Of Snapshots To Take Per Week",
147
- "description": "The number of snapshots to take per week. For daily snapshots, select 7."
148
- },
149
- {
150
- "key": "ct_max_snapshots",
151
- "section": "section_change_tracking",
152
- "type": "integer",
153
- "default": 28,
154
- "min": 1,
155
- "link_info": "",
156
- "link_blog": "",
157
- "name": "Snapshot Per Week",
158
- "summary": "Number Of Snapshots To Take Per Week",
159
- "description": "The number of snapshots to take per week. For daily snapshots, select 7."
160
- },
161
- {
162
- "key": "ct_last_snapshot_at",
163
- "section": "section_non_ui",
164
- "transferable": false,
165
- "type": "integer",
166
- "default": 0
167
- }
168
- ],
169
- "definitions": {
170
- "db_classes": {
171
- "audit_trail": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\AuditTrail\\Handler"
172
- },
173
- "db_table_audit_trail": {
174
- "slug": "audit_trail",
175
- "has_updated_at": true,
176
- "cols_custom": {
177
- "rid": "varchar(10) NOT NULL DEFAULT '' COMMENT 'Request ID'",
178
- "ip": "varchar(40) NOT NULL DEFAULT 0 COMMENT 'Visitor IP Address'",
179
- "wp_username": "varchar(255) NOT NULL DEFAULT '-' COMMENT 'WP User'",
180
- "context": "varchar(32) NOT NULL DEFAULT 'none' COMMENT 'Audit Context'",
181
- "event": "varchar(50) NOT NULL DEFAULT 'none' COMMENT 'Specific Audit Event'",
182
- "category": "int(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Severity'",
183
- "meta": "text COMMENT 'Audit Event Data'",
184
- "count": "SMALLINT(5) UNSIGNED NOT NULL DEFAULT 1 COMMENT 'Repeat Count'"
185
- }
186
- },
187
- "audit_trail_free_max_entries": 100,
188
- "audit_trail_table_name": "audit_trail",
189
- "events": {
190
- "plugin_activated": {
191
- "context": "plugins",
192
- "audit_multiple": true
193
- },
194
- "plugin_deactivated": {
195
- "context": "plugins",
196
- "audit_multiple": true
197
- },
198
- "plugin_file_edited": {
199
- "context": "plugins"
200
- },
201
- "plugin_upgraded": {
202
- "context": "plugins",
203
- "audit_multiple": true
204
- },
205
- "theme_activated": {
206
- "context": "themes"
207
- },
208
- "theme_file_edited": {
209
- "context": "themes"
210
- },
211
- "theme_upgraded": {
212
- "context": "themes",
213
- "audit_multiple": true
214
- },
215
- "core_updated": {
216
- "context": "wordpress"
217
- },
218
- "permalinks_structure": {
219
- "context": "wordpress"
220
- },
221
- "post_deleted": {
222
- "context": "posts",
223
- "audit_multiple": true
224
- },
225
- "post_trashed": {
226
- "context": "posts",
227
- "audit_multiple": true
228
- },
229
- "post_recovered": {
230
- "context": "posts",
231
- "audit_multiple": true
232
- },
233
- "post_updated": {
234
- "context": "posts",
235
- "audit_multiple": true
236
- },
237
- "post_published": {
238
- "context": "posts",
239
- "audit_multiple": true
240
- },
241
- "post_unpublished": {
242
- "context": "posts",
243
- "audit_multiple": true
244
- },
245
- "user_login": {
246
- "context": "users"
247
- },
248
- "user_login_app": {
249
- "context": "users"
250
- },
251
- "user_registered": {
252
- "context": "users"
253
- },
254
- "user_deleted": {
255
- "context": "users",
256
- "audit_multiple": true
257
- },
258
- "user_deleted_reassigned": {
259
- "context": "users"
260
- },
261
- "email_attempt_send": {
262
- "context": "emails",
263
- "audit_multiple": true
264
- },
265
- "email_send_invalid": {
266
- "context": "emails",
267
- "audit_multiple": true
268
- }
269
- }
270
- }
271
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Controller/Assets/Paths.php CHANGED
@@ -12,6 +12,10 @@ class Paths {
12
  return $this->forPluginItem( $this->getCon()->cfg->paths[ 'assets' ].'/'.ltrim( $asset, '/' ) );
13
  }
14
 
 
 
 
 
15
  public function forFlag( string $flag = '' ) :string {
16
  return $this->forPluginItem( $this->getCon()->cfg->paths[ 'flags' ].'/'.ltrim( $flag, '/' ) );
17
  }
12
  return $this->forPluginItem( $this->getCon()->cfg->paths[ 'assets' ].'/'.ltrim( $asset, '/' ) );
13
  }
14
 
15
+ public function forModuleConfig( string $module, bool $fromJSONFile = false ) :string {
16
+ return $this->forPluginItem( $this->getCon()->cfg->paths[ 'config' ].'/'.$module.( $fromJSONFile ? '.json' : '.php' ) );
17
+ }
18
+
19
  public function forFlag( string $flag = '' ) :string {
20
  return $this->forPluginItem( $this->getCon()->cfg->paths[ 'flags' ].'/'.ltrim( $flag, '/' ) );
21
  }
src/lib/src/Controller/Assets/Urls.php CHANGED
@@ -42,16 +42,6 @@ class Urls {
42
  return add_query_arg( [ 'ver' => $con->getVersion() ], plugins_url( $path, $con->getRootFile() ) );
43
  }
44
 
45
- /**
46
- * @param string $asset
47
- * @param string $type
48
- * @return mixed|null
49
- * @deprecated 11.4
50
- */
51
- protected function isAssetDynamic( string $asset, string $type ) :bool {
52
- return false;
53
- }
54
-
55
  /**
56
  * @param string $asset
57
  * @param string $type
42
  return add_query_arg( [ 'ver' => $con->getVersion() ], plugins_url( $path, $con->getRootFile() ) );
43
  }
44
 
 
 
 
 
 
 
 
 
 
 
45
  /**
46
  * @param string $asset
47
  * @param string $type
src/lib/src/Controller/Controller.php CHANGED
@@ -4,6 +4,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller;
4
 
5
  use FernleafSystems\Utilities\Data\Adapter\DynPropertiesClass;
6
  use FernleafSystems\Wordpress\Plugin\Shield;
 
7
  use FernleafSystems\Wordpress\Services\Services;
8
  use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
9
 
@@ -14,43 +15,34 @@ use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
14
  * @property Shield\Controller\Assets\Urls $urls
15
  * @property Shield\Controller\Assets\Paths $paths
16
  * @property Shield\Controller\Assets\Svgs $svgs
 
17
  * @property bool $is_activating
18
  * @property bool $is_debug
 
19
  * @property bool $modules_loaded
20
- * @property bool $rebuild_options
21
- * @property Shield\Modules\Integrations\Lib\MainWP\Common\MainWPVO $mwpVO
22
  * @property bool $plugin_deactivating
23
  * @property bool $plugin_deleting
24
  * @property bool $plugin_reset
25
- * @property bool $cache_dir_ready
 
26
  * @property false|string $file_forceoff
27
  * @property string $base_file
28
  * @property string $root_file
29
- * @property bool $is_my_upgrade
30
  * @property Shield\Utilities\Nonce\Handler $nonce_handler
31
- * @property bool $user_can_base_permissions
32
  * @property Shield\Modules\Events\Lib\EventsService $service_events
33
- * @property mixed[]|Shield\Modules\Base\ModCon[] $modules
34
  * @property Shield\Crons\HourlyCron $cron_hourly
35
  * @property Shield\Crons\DailyCron $cron_daily
 
36
  */
37
  class Controller extends DynPropertiesClass {
38
 
39
- /**
40
- * @var \stdClass
41
- */
42
- private static $oControllerOptions;
43
-
44
  /**
45
  * @var Controller
46
  */
47
  public static $oInstance;
48
 
49
- /**
50
- * @var array
51
- */
52
- private $aRequirementsMessages;
53
-
54
  /**
55
  * @var string
56
  */
@@ -66,11 +58,6 @@ class Controller extends DynPropertiesClass {
66
  */
67
  protected $sAdminNoticeError = '';
68
 
69
- /**
70
- * @var Shield\Modules\BaseShield\ModCon[]
71
- */
72
- protected $aModules;
73
-
74
  /**
75
  * @var Shield\Utilities\AdminNotices\Controller
76
  */
@@ -81,23 +68,11 @@ class Controller extends DynPropertiesClass {
81
  */
82
  private $oEventsService;
83
 
84
- /**
85
- * @param string $event
86
- * @param array $meta
87
- * @return $this
88
- */
89
- public function fireEvent( string $event, $meta = [] ) :self {
90
- $this->loadEventsService()->fireEvent( $event, is_array( $meta ) ? $meta : [] );
91
  return $this;
92
  }
93
 
94
- /**
95
- * @return array
96
- */
97
- public function getAllEvents() {
98
- return $this->loadEventsService()->getEvents();
99
- }
100
-
101
  /**
102
  * @return Shield\Modules\Events\Lib\EventsService
103
  */
@@ -154,6 +129,18 @@ class Controller extends DynPropertiesClass {
154
 
155
  switch ( $key ) {
156
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  case 'cfg':
158
  if ( !$val instanceof Config\ConfigVO ) {
159
  $val = $this->loadConfig();
@@ -196,6 +183,13 @@ class Controller extends DynPropertiesClass {
196
  }
197
  break;
198
 
 
 
 
 
 
 
 
199
  default:
200
  break;
201
  }
@@ -211,73 +205,59 @@ class Controller extends DynPropertiesClass {
211
  }
212
 
213
  /**
214
- * @param bool $bCheckOnlyFrontEnd
215
  * @throws \Exception
216
  */
217
- private function checkMinimumRequirements( $bCheckOnlyFrontEnd = true ) {
218
- if ( $bCheckOnlyFrontEnd && !is_admin() ) {
219
- return;
220
- }
221
-
222
- $bMeetsRequirements = true;
223
- $aRequirementsMessages = $this->getRequirementsMessages();
224
 
225
- $php = $this->cfg->requirements[ 'php' ];
226
- if ( !empty( $php ) ) {
227
- if ( version_compare( Services::Data()->getPhpVersion(), $php, '<' ) ) {
228
- $aRequirementsMessages[] = sprintf( 'PHP does not meet minimum version. Your version: %s. Required Version: %s.', PHP_VERSION, $php );
229
- $bMeetsRequirements = false;
230
  }
231
- }
232
 
233
- $wp = $this->cfg->requirements[ 'wordpress' ];
234
- if ( !empty( $wp ) ) {
235
- $sWpVersion = Services::WpGeneral()->getVersion( true );
236
- if ( version_compare( $sWpVersion, $wp, '<' ) ) {
237
- $aRequirementsMessages[] = sprintf( 'WordPress does not meet minimum version. Your version: %s. Required Version: %s.', $sWpVersion, $wp );
238
- $bMeetsRequirements = false;
239
  }
240
- }
241
 
242
- if ( !$bMeetsRequirements ) {
243
- $this->aRequirementsMessages = $aRequirementsMessages;
244
- add_action( 'admin_notices', [ $this, 'adminNoticeDoesNotMeetRequirements' ] );
245
- add_action( 'network_admin_notices', [ $this, 'adminNoticeDoesNotMeetRequirements' ] );
246
- throw new \Exception( 'Plugin does not meet minimum requirements' );
 
247
  }
248
  }
249
 
250
  public function adminNoticeDoesNotMeetRequirements() {
251
- $aMessages = $this->getRequirementsMessages();
252
- if ( !empty( $aMessages ) && is_array( $aMessages ) ) {
253
- $aDisplayData = [
254
- 'strings' => [
255
- 'requirements' => $aMessages,
256
- 'summary_title' => sprintf( 'Web Hosting requirements for Plugin "%s" are not met and you should deactivate the plugin.', $this->getHumanName() ),
257
- 'more_information' => 'Click here for more information on requirements'
258
- ],
259
- 'hrefs' => [
260
- 'more_information' => sprintf( 'https://wordpress.org/plugins/%s/faq', $this->getTextDomain() )
261
- ]
262
- ];
263
-
264
  $this->getRenderer()
265
- ->setTemplate( 'notices/does-not-meet-requirements' )
266
- ->setRenderVars( $aDisplayData )
 
 
 
 
 
 
 
 
 
 
267
  ->display();
268
  }
269
  }
270
 
271
  public function adminNoticePluginFailedToLoad() {
272
- $aDisplayData = [
273
- 'strings' => [
274
- 'summary_title' => 'Perhaps due to a failed upgrade, the Shield plugin failed to load certain component(s) - you should remove the plugin and reinstall.',
275
- 'more_information' => $this->sAdminNoticeError
276
- ]
277
- ];
278
  $this->getRenderer()
279
  ->setTemplate( 'notices/plugin-failed-to-load' )
280
- ->setRenderVars( $aDisplayData )
 
 
 
 
 
281
  ->display();
282
  }
283
 
@@ -304,14 +284,26 @@ class Controller extends DynPropertiesClass {
304
  public function onWpDeactivatePlugin() {
305
  do_action( $this->prefix( 'pre_deactivate_plugin' ) );
306
  if ( $this->isPluginAdmin() ) {
307
- do_action( $this->prefix( 'deactivate_plugin' ) );
308
  $this->plugin_deactivating = true;
 
 
 
 
 
 
309
  if ( apply_filters( $this->prefix( 'delete_on_deactivate' ), false ) ) {
310
- $this->plugin_deleting = true;
311
- do_action( $this->prefix( 'delete_plugin' ) );
312
  }
313
  }
314
- $this->deleteCronJobs();
 
 
 
 
 
 
 
315
  }
316
 
317
  public function onWpActivatePlugin() {
@@ -334,15 +326,6 @@ class Controller extends DynPropertiesClass {
334
  return !empty( $this->getPluginCachePath() );
335
  }
336
 
337
- /**
338
- * @deprecated 11.4
339
- */
340
- private function buildPluginCacheDir() :string {
341
- return ( new Shield\Utilities\CacheDir() )
342
- ->setCon( $this )
343
- ->build();
344
- }
345
-
346
  protected function doRegisterHooks() {
347
  register_deactivation_hook( $this->getRootFile(), [ $this, 'onWpDeactivatePlugin' ] );
348
 
@@ -368,13 +351,13 @@ class Controller extends DynPropertiesClass {
368
  add_filter( 'wp_privacy_personal_data_erasers', [ $this, 'onWpPrivacyRegisterEraser' ] );
369
 
370
  /**
371
- * Support for WP-CLI and it marks the cli as complete plugin admin
372
  */
373
- add_filter( $this->prefix( 'bypass_is_plugin_admin' ), function ( $bByPass ) {
374
  if ( Services::WpGeneral()->isWpCli() && $this->isPremiumActive() ) {
375
- $bByPass = true;
376
  }
377
- return $bByPass;
378
  }, PHP_INT_MAX );
379
  }
380
 
@@ -475,17 +458,8 @@ class Controller extends DynPropertiesClass {
475
  return $ID;
476
  }
477
 
478
- /**
479
- * TODO: Use to set ID after license verify where applicable
480
- * @param string $ID
481
- */
482
- public function setSiteInstallID( $ID ) {
483
- if ( !empty( $ID ) && ( \Ramsey\Uuid\Uuid::isValid( $ID ) ) ) {
484
- Services::WpGeneral()->updateOption( $this->prefixOption( 'install_id' ), $ID );
485
- }
486
- }
487
-
488
  public function onWpLoaded() {
 
489
  $this->getAdminNotices();
490
  $this->initCrons();
491
  ( new Shield\Controller\Assets\Enqueue() )
@@ -504,15 +478,13 @@ class Controller extends DynPropertiesClass {
504
 
505
  protected function initCrons() {
506
  $this->cron_hourly = ( new Shield\Crons\HourlyCron() )->setCon( $this );
507
- $this->cron_hourly->run();
508
  $this->cron_daily = ( new Shield\Crons\DailyCron() )->setCon( $this );
509
- $this->cron_daily->run();
510
 
511
- if ( Services::WpGeneral()->isCron() ) {
512
- ( new Shield\Utilities\Htaccess\RootHtaccess() )
513
- ->setCon( $this )
514
- ->execute();
515
- }
516
  }
517
 
518
  /**
@@ -532,8 +504,12 @@ class Controller extends DynPropertiesClass {
532
  /**
533
  * @param string $action
534
  * @return array
 
535
  */
536
- public function getNonceActionData( $action = '' ) {
 
 
 
537
  return [
538
  'action' => $this->prefix(), //wp ajax doesn't work without this.
539
  'exec' => $action,
@@ -559,15 +535,15 @@ class Controller extends DynPropertiesClass {
559
  }
560
 
561
  /**
562
- * @param array $aActionLinks
563
  * @return array
564
  */
565
- public function onWpPluginActionLinks( $aActionLinks ) {
566
 
567
  if ( $this->isValidAdminArea() ) {
568
 
569
- if ( array_key_exists( 'edit', $aActionLinks ) ) {
570
- unset( $aActionLinks[ 'edit' ] );
571
  }
572
 
573
  $links = $this->cfg->action_links[ 'add' ];
@@ -607,14 +583,14 @@ class Controller extends DynPropertiesClass {
607
  $sLink = sprintf( '<span style="font-weight: bold;">%s</span>', $sLink );
608
  }
609
 
610
- $aActionLinks = array_merge(
611
  [ $this->prefix( sanitize_key( $aLink[ 'name' ] ) ) => $sLink ],
612
- $aActionLinks
613
  );
614
  }
615
  }
616
  }
617
- return $aActionLinks;
618
  }
619
 
620
  /**
@@ -772,7 +748,6 @@ class Controller extends DynPropertiesClass {
772
  }
773
 
774
  public function onWpShutdown() {
775
- $this->getSiteInstallationId();
776
  do_action( $this->prefix( 'pre_plugin_shutdown' ) );
777
  do_action( $this->prefix( 'plugin_shutdown' ) );
778
  $this->saveCurrentPluginControllerOptions();
@@ -922,7 +897,7 @@ class Controller extends DynPropertiesClass {
922
  if ( did_action( 'init' ) && !isset( $this->user_can_base_permissions ) ) {
923
  $this->user_can_base_permissions = current_user_can( $this->getBasePermissions() );
924
  }
925
- return (bool)$this->user_can_base_permissions;
926
  }
927
 
928
  public function getOptionStoragePrefix() :string {
@@ -942,23 +917,15 @@ class Controller extends DynPropertiesClass {
942
  return empty( $labels[ 'Name' ] ) ? $this->getCfgProperty( 'human_name' ) : $labels[ 'Name' ];
943
  }
944
 
945
- public function isLoggingEnabled() :bool {
946
- return (bool)$this->getCfgProperty( 'logging_enabled' );
947
- }
948
-
949
  public function getIsPage_PluginAdmin() :bool {
950
  return strpos( Services::WpGeneral()->getCurrentWpAdminPage(), $this->getPluginPrefix() ) === 0;
951
  }
952
 
953
- public function getIsPage_PluginMainDashboard() :bool {
954
- return Services::WpGeneral()->getCurrentWpAdminPage() === $this->getPluginPrefix();
955
- }
956
-
957
  public function getIsResetPlugin() :bool {
958
  if ( !isset( $this->plugin_reset ) ) {
959
- $this->plugin_reset = (bool)Services::WpFs()->isFile( $this->paths->forFlag( 'reset' ) );
960
  }
961
- return (bool)$this->plugin_reset;
962
  }
963
 
964
  public function getIsWpmsNetworkAdminOnly() :bool {
@@ -985,75 +952,21 @@ class Controller extends DynPropertiesClass {
985
  return $this->getModule_Plugin()->getUrl_AdminPage();
986
  }
987
 
988
- public function getPath_Assets( string $asset = '' ) :string {
989
- $base = path_join( $this->getRootDir(), $this->cfg->paths[ 'assets' ] );
990
- return empty( $asset ) ? $base : path_join( $base, ltrim( $asset, '/' ) );
991
- }
992
-
993
- public function getPath_AssetCss( string $asset = '' ) :string {
994
- return $this->getPath_Assets( 'css/'.$asset );
995
- }
996
-
997
- public function getPath_AssetJs( string $asset = '' ) :string {
998
- return $this->getPath_Assets( 'js/'.$asset );
999
- }
1000
-
1001
- public function getPath_AssetImage( string $asset = '' ) :string {
1002
- return $this->getPath_Assets( 'images/'.$asset );
1003
- }
1004
-
1005
  public function getPath_ConfigFile( string $slug ) :string {
1006
- return $this->getPath_SourceFile( sprintf( 'config/feature-%s.php', $slug ) );
1007
  }
1008
 
1009
  public function getPath_Languages() :string {
1010
  return trailingslashit( path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'languages' ) ) );
1011
  }
1012
 
1013
- public function getPath_LibFile( string $libFile ) :string {
1014
- return $this->getPath_SourceFile( 'lib/'.$libFile );
1015
- }
1016
-
1017
- public function getPath_Autoload() :string {
1018
- return $this->getPath_SourceFile( $this->getPluginSpec_Path( 'autoload' ) );
1019
- }
1020
-
1021
- /**
1022
- * @return string
1023
- * @throws \Exception
1024
- */
1025
- public function getPath_PluginCache() :string {
1026
- $cacheSlug = $this->getPluginSpec_Path( 'cache' );
1027
- if ( empty( $cacheSlug ) ) {
1028
- throw new \Exception( 'Cache dir slug was empty' );
1029
- }
1030
- return path_join( WP_CONTENT_DIR, $cacheSlug );
1031
- }
1032
-
1033
- /**
1034
- * @param string $sourceFile
1035
- * @return string
1036
- * @deprecated 10.3
1037
- */
1038
- public function getPath_SourceFile( string $sourceFile ) :string {
1039
- if ( isset( $this->paths ) ) {
1040
- return $this->paths->forSource( $sourceFile );
1041
- }
1042
- $base = path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'source' ) );
1043
- return empty( $sourceFile ) ? $base : path_join( $base, $sourceFile );
1044
- }
1045
-
1046
  public function getPath_Templates() :string {
1047
  return path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'templates' ) ).'/';
1048
  }
1049
 
1050
- public function getPath_TemplatesFile( string $template ) :string {
1051
- if ( isset( $this->paths ) ) {
1052
- return $this->paths->forTemplate( $template );
1053
- }
1054
- return path_join( $this->getPath_Templates(), $template );
1055
- }
1056
-
1057
  private function getPathPluginSpec( bool $asJSON = true ) :string {
1058
  return path_join( $this->getRootDir(), $asJSON ? 'plugin.json' : 'plugin-spec.php' );
1059
  }
@@ -1076,18 +989,10 @@ class Controller extends DynPropertiesClass {
1076
  return $this->root_file;
1077
  }
1078
 
1079
- public function getReleaseTimestamp() :int {
1080
- return $this->getCfgProperty( 'release_timestamp' );
1081
- }
1082
-
1083
  public function getTextDomain() :string {
1084
  return $this->getCfgProperty( 'text_domain' );
1085
  }
1086
 
1087
- public function getBuild() :string {
1088
- return $this->getCfgProperty( 'build' );
1089
- }
1090
-
1091
  public function getVersion() :string {
1092
  return $this->getCfgProperty( 'version' );
1093
  }
@@ -1107,29 +1012,6 @@ class Controller extends DynPropertiesClass {
1107
  return empty( $action ) ? '' : $action;
1108
  }
1109
 
1110
- /**
1111
- * @return \stdClass
1112
- */
1113
- public function getPluginControllerOptions() {
1114
- return self::$oControllerOptions;
1115
- }
1116
-
1117
- protected function deleteCronJobs() {
1118
- $WPCron = Services::WpCron();
1119
- $crons = $WPCron->getCrons();
1120
-
1121
- $pattern = sprintf( '#^(%s|%s)#', $this->getParentSlug(), $this->getPluginSlug() );
1122
- foreach ( $crons as $cron ) {
1123
- if ( is_array( $crons ) ) {
1124
- foreach ( $cron as $key => $cronEntry ) {
1125
- if ( is_string( $key ) && preg_match( $pattern, $key ) ) {
1126
- $WPCron->deleteCronJob( $key );
1127
- }
1128
- }
1129
- }
1130
- }
1131
- }
1132
-
1133
  public function isPremiumExtensionsEnabled() :bool {
1134
  return (bool)$this->getCfgProperty( 'enable_premium' );
1135
  }
@@ -1154,26 +1036,10 @@ class Controller extends DynPropertiesClass {
1154
  remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
1155
  }
1156
 
1157
- /**
1158
- * This should always be used to modify or delete the options as it works within the Admin Access Permission system.
1159
- * @param \stdClass|bool $oOptions
1160
- * @return $this
1161
- */
1162
- protected function setPluginControllerOptions( $oOptions ) {
1163
- self::$oControllerOptions = $oOptions;
1164
- return $this;
1165
- }
1166
-
1167
  private function getConfigStoreKey() :string {
1168
  return 'aptoweb_controller_'.substr( md5( get_class() ), 0, 6 );
1169
  }
1170
 
1171
- public function deactivateSelf() {
1172
- if ( $this->isPluginAdmin() && function_exists( 'deactivate_plugins' ) ) {
1173
- deactivate_plugins( [ $this->base_file ] );
1174
- }
1175
- }
1176
-
1177
  public function clearSession() {
1178
  Services::Response()->cookieDelete( $this->getSessionCookieID() );
1179
  self::$sSessionId = null;
@@ -1223,6 +1089,11 @@ class Controller extends DynPropertiesClass {
1223
  return self::$sSessionId;
1224
  }
1225
 
 
 
 
 
 
1226
  public function getUniqueRequestId( bool $setIfNeeded = false ) :string {
1227
  if ( !isset( self::$sRequestId ) ) {
1228
  self::$sRequestId = md5(
@@ -1232,8 +1103,14 @@ class Controller extends DynPropertiesClass {
1232
  return self::$sRequestId;
1233
  }
1234
 
 
 
 
 
1235
  public function getShortRequestId() :string {
1236
- return substr( $this->getUniqueRequestId(), 0, 10 );
 
 
1237
  }
1238
 
1239
  public function hasSessionId() :bool {
@@ -1334,6 +1211,10 @@ class Controller extends DynPropertiesClass {
1334
  return $this->getModule( 'comms' );
1335
  }
1336
 
 
 
 
 
1337
  public function getModule_Email() :Shield\Modules\Email\ModCon {
1338
  return $this->getModule( 'email' );
1339
  }
@@ -1371,7 +1252,7 @@ class Controller extends DynPropertiesClass {
1371
  }
1372
 
1373
  public function getModule_Plugin() :Shield\Modules\Plugin\ModCon {
1374
- return $this->getModule( 'plugin' );
1375
  }
1376
 
1377
  public function getModule_Reporting() :Shield\Modules\Reporting\ModCon {
@@ -1405,7 +1286,7 @@ class Controller extends DynPropertiesClass {
1405
  */
1406
  public function loadFeatureHandler( array $modProps ) {
1407
  $modSlug = $modProps[ 'slug' ];
1408
- $mod = isset( $this->modules[ $modSlug ] ) ? $this->modules[ $modSlug ] : null;
1409
  if ( $mod instanceof Shield\Modules\Base\ModCon ) {
1410
  return $mod;
1411
  }
@@ -1423,24 +1304,17 @@ class Controller extends DynPropertiesClass {
1423
  }
1424
 
1425
  $modName = $modProps[ 'namespace' ];
1426
- $sOptionsVarName = sprintf( 'oFeatureHandler%s', $modName ); // e.g. oFeatureHandlerPlugin
1427
 
1428
  $className = $this->getModulesNamespace().sprintf( '\\%s\\ModCon', $modName );
1429
- if ( !@class_exists( $className ) ) {
1430
- $className = sprintf( '%s_FeatureHandler_%s', strtoupper( $this->getPluginPrefix( '_' ) ), $modName );
1431
- }
1432
-
1433
- // All this to prevent fatal errors if the plugin doesn't install/upgrade correctly
1434
  if ( !class_exists( $className ) ) {
1435
- $sMessage = sprintf( 'Class "%s" is missing', $className );
1436
- throw new \Exception( $sMessage );
1437
  }
1438
 
1439
- $this->{$sOptionsVarName} = new $className( $this, $modProps );
1440
-
1441
  $modules = $this->modules;
1442
- $modules[ $modSlug ] = $this->{$sOptionsVarName};
1443
  $this->modules = $modules;
 
1444
  return $this->modules[ $modSlug ];
1445
  }
1446
 
@@ -1453,7 +1327,7 @@ class Controller extends DynPropertiesClass {
1453
 
1454
  /**
1455
  * @param \WP_User $user
1456
- * @return Shield\Users\ShieldUserMeta|mixed
1457
  */
1458
  public function getUserMeta( $user ) {
1459
  $meta = null;
@@ -1527,7 +1401,7 @@ class Controller extends DynPropertiesClass {
1527
  * @param int $page
1528
  * @return array
1529
  */
1530
- public function wpPrivacyExport( $email, $page = 1 ) {
1531
 
1532
  $valid = Services::Data()->validEmail( $email )
1533
  && ( Services::WpUsers()->getUserByEmail( $email ) instanceof \WP_User );
4
 
5
  use FernleafSystems\Utilities\Data\Adapter\DynPropertiesClass;
6
  use FernleafSystems\Wordpress\Plugin\Shield;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Controller\Plugin\PluginDeactivate;
8
  use FernleafSystems\Wordpress\Services\Services;
9
  use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
10
 
15
  * @property Shield\Controller\Assets\Urls $urls
16
  * @property Shield\Controller\Assets\Paths $paths
17
  * @property Shield\Controller\Assets\Svgs $svgs
18
+ * @property bool $cache_dir_ready
19
  * @property bool $is_activating
20
  * @property bool $is_debug
21
+ * @property bool $is_my_upgrade
22
  * @property bool $modules_loaded
 
 
23
  * @property bool $plugin_deactivating
24
  * @property bool $plugin_deleting
25
  * @property bool $plugin_reset
26
+ * @property bool $rebuild_options
27
+ * @property bool $user_can_base_permissions
28
  * @property false|string $file_forceoff
29
  * @property string $base_file
30
  * @property string $root_file
31
+ * @property Shield\Modules\Integrations\Lib\MainWP\Common\MainWPVO $mwpVO
32
  * @property Shield\Utilities\Nonce\Handler $nonce_handler
 
33
  * @property Shield\Modules\Events\Lib\EventsService $service_events
34
+ * @property array|Shield\Modules\Base\ModCon[] $modules
35
  * @property Shield\Crons\HourlyCron $cron_hourly
36
  * @property Shield\Crons\DailyCron $cron_daily
37
+ * @property string[] $reqs_not_met
38
  */
39
  class Controller extends DynPropertiesClass {
40
 
 
 
 
 
 
41
  /**
42
  * @var Controller
43
  */
44
  public static $oInstance;
45
 
 
 
 
 
 
46
  /**
47
  * @var string
48
  */
58
  */
59
  protected $sAdminNoticeError = '';
60
 
 
 
 
 
 
61
  /**
62
  * @var Shield\Utilities\AdminNotices\Controller
63
  */
68
  */
69
  private $oEventsService;
70
 
71
+ public function fireEvent( string $event, array $meta = [] ) :self {
72
+ $this->loadEventsService()->fireEvent( $event, $meta );
 
 
 
 
 
73
  return $this;
74
  }
75
 
 
 
 
 
 
 
 
76
  /**
77
  * @return Shield\Modules\Events\Lib\EventsService
78
  */
129
 
130
  switch ( $key ) {
131
 
132
+ case 'cache_dir_ready':
133
+ case 'is_activating':
134
+ case 'is_my_upgrade':
135
+ case 'modules_loaded':
136
+ case 'plugin_deactivating':
137
+ case 'plugin_deleting':
138
+ case 'plugin_reset':
139
+ case 'rebuild_options':
140
+ case 'user_can_base_permissions':
141
+ $val = (bool)$val;
142
+ break;
143
+
144
  case 'cfg':
145
  if ( !$val instanceof Config\ConfigVO ) {
146
  $val = $this->loadConfig();
183
  }
184
  break;
185
 
186
+ case 'reqs_not_met':
187
+ if ( !is_array( $val ) ) {
188
+ $val = [];
189
+ $this->reqs_not_met = $val;
190
+ }
191
+ break;
192
+
193
  default:
194
  break;
195
  }
205
  }
206
 
207
  /**
 
208
  * @throws \Exception
209
  */
210
+ private function checkMinimumRequirements() {
211
+ if ( is_admin() ) {
212
+ $reqsMsg = [];
 
 
 
 
213
 
214
+ $minPHP = $this->cfg->requirements[ 'php' ];
215
+ if ( !empty( $minPHP ) && version_compare( Services::Data()->getPhpVersion(), $minPHP, '<' ) ) {
216
+ $reqsMsg[] = sprintf( 'PHP does not meet minimum version. Your version: %s. Required Version: %s.', PHP_VERSION, $minPHP );
 
 
217
  }
 
218
 
219
+ $wp = $this->cfg->requirements[ 'wordpress' ];
220
+ if ( !empty( $wp ) && version_compare( Services::WpGeneral()->getVersion( true ), $wp, '<' ) ) {
221
+ $reqsMsg[] = sprintf( 'WordPress does not meet minimum version. Required Version: %s.', $wp );
 
 
 
222
  }
 
223
 
224
+ if ( !empty( $reqsMsg ) ) {
225
+ $this->reqs_not_met = $reqsMsg;
226
+ add_action( 'admin_notices', [ $this, 'adminNoticeDoesNotMeetRequirements' ] );
227
+ add_action( 'network_admin_notices', [ $this, 'adminNoticeDoesNotMeetRequirements' ] );
228
+ throw new \Exception( 'Plugin does not meet minimum requirements' );
229
+ }
230
  }
231
  }
232
 
233
  public function adminNoticeDoesNotMeetRequirements() {
234
+ if ( !empty( $this->reqs_not_met ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
235
  $this->getRenderer()
236
+ ->setTemplate( 'notices/does-not-meet-requirements.twig' )
237
+ ->setRenderVars( [
238
+ 'strings' => [
239
+ 'not_met' => 'Shield Security Plugin - minimum site requirements are not met',
240
+ 'requirements' => $this->reqs_not_met,
241
+ 'summary_title' => sprintf( 'Web Hosting requirements for Plugin "%s" are not met and you should deactivate the plugin.', $this->getHumanName() ),
242
+ 'more_information' => 'Click here for more information on requirements'
243
+ ],
244
+ 'hrefs' => [
245
+ 'more_information' => sprintf( 'https://wordpress.org/plugins/%s/faq', $this->getTextDomain() )
246
+ ]
247
+ ] )
248
  ->display();
249
  }
250
  }
251
 
252
  public function adminNoticePluginFailedToLoad() {
 
 
 
 
 
 
253
  $this->getRenderer()
254
  ->setTemplate( 'notices/plugin-failed-to-load' )
255
+ ->setRenderVars( [
256
+ 'strings' => [
257
+ 'summary_title' => 'Perhaps due to a failed upgrade, the Shield plugin failed to load certain component(s) - you should remove the plugin and reinstall.',
258
+ 'more_information' => $this->sAdminNoticeError
259
+ ]
260
+ ] )
261
  ->display();
262
  }
263
 
284
  public function onWpDeactivatePlugin() {
285
  do_action( $this->prefix( 'pre_deactivate_plugin' ) );
286
  if ( $this->isPluginAdmin() ) {
287
+
288
  $this->plugin_deactivating = true;
289
+ do_action( $this->prefix( 'deactivate_plugin' ) );
290
+
291
+ ( new PluginDeactivate() )
292
+ ->setCon( $this )
293
+ ->execute();
294
+
295
  if ( apply_filters( $this->prefix( 'delete_on_deactivate' ), false ) ) {
296
+ $this->deletePlugin();
 
297
  }
298
  }
299
+ }
300
+
301
+ public function deletePlugin() {
302
+ $this->plugin_deleting = true;
303
+ do_action( $this->prefix( 'delete_plugin' ) );
304
+ ( new Plugin\PluginDelete() )
305
+ ->setCon( $this )
306
+ ->execute();
307
  }
308
 
309
  public function onWpActivatePlugin() {
326
  return !empty( $this->getPluginCachePath() );
327
  }
328
 
 
 
 
 
 
 
 
 
 
329
  protected function doRegisterHooks() {
330
  register_deactivation_hook( $this->getRootFile(), [ $this, 'onWpDeactivatePlugin' ] );
331
 
351
  add_filter( 'wp_privacy_personal_data_erasers', [ $this, 'onWpPrivacyRegisterEraser' ] );
352
 
353
  /**
354
+ * Support for WP-CLI and it marks the cli as plugin admin
355
  */
356
+ add_filter( $this->prefix( 'bypass_is_plugin_admin' ), function ( $byPass ) {
357
  if ( Services::WpGeneral()->isWpCli() && $this->isPremiumActive() ) {
358
+ $byPass = true;
359
  }
360
+ return $byPass;
361
  }, PHP_INT_MAX );
362
  }
363
 
458
  return $ID;
459
  }
460
 
 
 
 
 
 
 
 
 
 
 
461
  public function onWpLoaded() {
462
+ $this->getSiteInstallationId();
463
  $this->getAdminNotices();
464
  $this->initCrons();
465
  ( new Shield\Controller\Assets\Enqueue() )
478
 
479
  protected function initCrons() {
480
  $this->cron_hourly = ( new Shield\Crons\HourlyCron() )->setCon( $this );
481
+ $this->cron_hourly->execute();
482
  $this->cron_daily = ( new Shield\Crons\DailyCron() )->setCon( $this );
483
+ $this->cron_daily->execute();
484
 
485
+ ( new Shield\Utilities\Htaccess\RootHtaccess() )
486
+ ->setCon( $this )
487
+ ->execute();
 
 
488
  }
489
 
490
  /**
504
  /**
505
  * @param string $action
506
  * @return array
507
+ * @throws \Exception
508
  */
509
+ public function getNonceActionData( string $action = '' ) :array {
510
+ if ( empty( $action ) ) {
511
+ throw new \Exception( 'Empty actions are not allowed.' );
512
+ }
513
  return [
514
  'action' => $this->prefix(), //wp ajax doesn't work without this.
515
  'exec' => $action,
535
  }
536
 
537
  /**
538
+ * @param array $actionLinks
539
  * @return array
540
  */
541
+ public function onWpPluginActionLinks( $actionLinks ) {
542
 
543
  if ( $this->isValidAdminArea() ) {
544
 
545
+ if ( array_key_exists( 'edit', $actionLinks ) ) {
546
+ unset( $actionLinks[ 'edit' ] );
547
  }
548
 
549
  $links = $this->cfg->action_links[ 'add' ];
583
  $sLink = sprintf( '<span style="font-weight: bold;">%s</span>', $sLink );
584
  }
585
 
586
+ $actionLinks = array_merge(
587
  [ $this->prefix( sanitize_key( $aLink[ 'name' ] ) ) => $sLink ],
588
+ $actionLinks
589
  );
590
  }
591
  }
592
  }
593
+ return $actionLinks;
594
  }
595
 
596
  /**
748
  }
749
 
750
  public function onWpShutdown() {
 
751
  do_action( $this->prefix( 'pre_plugin_shutdown' ) );
752
  do_action( $this->prefix( 'plugin_shutdown' ) );
753
  $this->saveCurrentPluginControllerOptions();
897
  if ( did_action( 'init' ) && !isset( $this->user_can_base_permissions ) ) {
898
  $this->user_can_base_permissions = current_user_can( $this->getBasePermissions() );
899
  }
900
+ return $this->user_can_base_permissions;
901
  }
902
 
903
  public function getOptionStoragePrefix() :string {
917
  return empty( $labels[ 'Name' ] ) ? $this->getCfgProperty( 'human_name' ) : $labels[ 'Name' ];
918
  }
919
 
 
 
 
 
920
  public function getIsPage_PluginAdmin() :bool {
921
  return strpos( Services::WpGeneral()->getCurrentWpAdminPage(), $this->getPluginPrefix() ) === 0;
922
  }
923
 
 
 
 
 
924
  public function getIsResetPlugin() :bool {
925
  if ( !isset( $this->plugin_reset ) ) {
926
+ $this->plugin_reset = Services::WpFs()->isFile( $this->paths->forFlag( 'reset' ) );
927
  }
928
+ return $this->plugin_reset;
929
  }
930
 
931
  public function getIsWpmsNetworkAdminOnly() :bool {
952
  return $this->getModule_Plugin()->getUrl_AdminPage();
953
  }
954
 
955
+ /**
956
+ * @deprecated 12.0
957
+ */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
958
  public function getPath_ConfigFile( string $slug ) :string {
959
+ return $this->paths->forModuleConfig( $slug );
960
  }
961
 
962
  public function getPath_Languages() :string {
963
  return trailingslashit( path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'languages' ) ) );
964
  }
965
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
966
  public function getPath_Templates() :string {
967
  return path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'templates' ) ).'/';
968
  }
969
 
 
 
 
 
 
 
 
970
  private function getPathPluginSpec( bool $asJSON = true ) :string {
971
  return path_join( $this->getRootDir(), $asJSON ? 'plugin.json' : 'plugin-spec.php' );
972
  }
989
  return $this->root_file;
990
  }
991
 
 
 
 
 
992
  public function getTextDomain() :string {
993
  return $this->getCfgProperty( 'text_domain' );
994
  }
995
 
 
 
 
 
996
  public function getVersion() :string {
997
  return $this->getCfgProperty( 'version' );
998
  }
1012
  return empty( $action ) ? '' : $action;
1013
  }
1014
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1015
  public function isPremiumExtensionsEnabled() :bool {
1016
  return (bool)$this->getCfgProperty( 'enable_premium' );
1017
  }
1036
  remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
1037
  }
1038
 
 
 
 
 
 
 
 
 
 
 
1039
  private function getConfigStoreKey() :string {
1040
  return 'aptoweb_controller_'.substr( md5( get_class() ), 0, 6 );
1041
  }
1042
 
 
 
 
 
 
 
1043
  public function clearSession() {
1044
  Services::Response()->cookieDelete( $this->getSessionCookieID() );
1045
  self::$sSessionId = null;
1089
  return self::$sSessionId;
1090
  }
1091
 
1092
+ /**
1093
+ * @param bool $setIfNeeded
1094
+ * @return string
1095
+ * @deprecated 12.0
1096
+ */
1097
  public function getUniqueRequestId( bool $setIfNeeded = false ) :string {
1098
  if ( !isset( self::$sRequestId ) ) {
1099
  self::$sRequestId = md5(
1103
  return self::$sRequestId;
1104
  }
1105
 
1106
+ /**
1107
+ * @return string
1108
+ * @deprecated 12
1109
+ */
1110
  public function getShortRequestId() :string {
1111
+ $req = Services::Request();
1112
+ /** @deprecated 12.0 */
1113
+ return substr( method_exists( $req, 'getID' ) ? $req->getID() : $this->getUniqueRequestId(), 0, 10 );
1114
  }
1115
 
1116
  public function hasSessionId() :bool {
1211
  return $this->getModule( 'comms' );
1212
  }
1213
 
1214
+ public function getModule_Data() :Shield\Modules\Data\ModCon {
1215
+ return $this->getModule( 'data' );
1216
+ }
1217
+
1218
  public function getModule_Email() :Shield\Modules\Email\ModCon {
1219
  return $this->getModule( 'email' );
1220
  }
1252
  }
1253
 
1254
  public function getModule_Plugin() :Shield\Modules\Plugin\ModCon {
1255
+ return $this->loadCorePluginFeatureHandler();
1256
  }
1257
 
1258
  public function getModule_Reporting() :Shield\Modules\Reporting\ModCon {
1286
  */
1287
  public function loadFeatureHandler( array $modProps ) {
1288
  $modSlug = $modProps[ 'slug' ];
1289
+ $mod = $this->modules[ $modSlug ] ?? null;
1290
  if ( $mod instanceof Shield\Modules\Base\ModCon ) {
1291
  return $mod;
1292
  }
1304
  }
1305
 
1306
  $modName = $modProps[ 'namespace' ];
 
1307
 
1308
  $className = $this->getModulesNamespace().sprintf( '\\%s\\ModCon', $modName );
 
 
 
 
 
1309
  if ( !class_exists( $className ) ) {
1310
+ // All this to prevent fatal errors if the plugin doesn't install/upgrade correctly
1311
+ throw new \Exception( sprintf( 'Class "%s" is missing', $className ) );
1312
  }
1313
 
 
 
1314
  $modules = $this->modules;
1315
+ $modules[ $modSlug ] = new $className( $this, $modProps );
1316
  $this->modules = $modules;
1317
+
1318
  return $this->modules[ $modSlug ];
1319
  }
1320
 
1327
 
1328
  /**
1329
  * @param \WP_User $user
1330
+ * @return Shield\Users\ShieldUserMeta|null
1331
  */
1332
  public function getUserMeta( $user ) {
1333
  $meta = null;
1401
  * @param int $page
1402
  * @return array
1403
  */
1404
+ public function wpPrivacyExport( $email, $page = 1 ) :array {
1405
 
1406
  $valid = Services::Data()->validEmail( $email )
1407
  && ( Services::WpUsers()->getUserByEmail( $email ) instanceof \WP_User );
src/lib/src/Controller/Plugin/PluginDeactivate.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Plugin;
4
+
5
+ use FernleafSystems\Utilities\Logic\ExecOnce;
6
+ use FernleafSystems\Wordpress\Plugin\Shield;
7
+ use FernleafSystems\Wordpress\Services\Services;
8
+
9
+ class PluginDeactivate {
10
+
11
+ use Shield\Modules\PluginControllerConsumer;
12
+ use ExecOnce;
13
+
14
+ protected function run() {
15
+ $this->deleteCrons();
16
+ }
17
+
18
+ private function deleteCrons() {
19
+ $con = $this->getCon();
20
+ $WPCron = Services::WpCron();
21
+ $crons = $WPCron->getCrons();
22
+
23
+ $pattern = sprintf( '#^(%s|%s)#', $con->getParentSlug(), $con->getPluginSlug() );
24
+ foreach ( $crons as $cron ) {
25
+ if ( is_array( $crons ) ) {
26
+ foreach ( $cron as $key => $cronEntry ) {
27
+ if ( is_string( $key ) && preg_match( $pattern, $key ) ) {
28
+ $WPCron->deleteCronJob( $key );
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
src/lib/src/Controller/Plugin/PluginDelete.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Plugin;
4
+
5
+ use FernleafSystems\Utilities\Logic\ExecOnce;
6
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base\Handler;
7
+ use FernleafSystems\Wordpress\Plugin\Shield;
8
+ use FernleafSystems\Wordpress\Services\Services;
9
+
10
+ class PluginDelete {
11
+
12
+ use Shield\Modules\PluginControllerConsumer;
13
+ use ExecOnce;
14
+
15
+ protected function run() {
16
+ $this->deleteDatabases();
17
+ $this->deleteTmpDir();
18
+ }
19
+
20
+ private function deleteTmpDir() {
21
+ Services::WpFs()->deleteDir( $this->getCon()->getPluginCachePath() );
22
+ }
23
+
24
+ private function deleteDatabases() {
25
+ $con = $this->getCon();
26
+ $WPDB = Services::WpDb();
27
+
28
+ // Delete all the legacy tables first (i.e. no inter-dependencies)
29
+ array_map(
30
+ function ( $module ) {
31
+ foreach ( $module->getDbHandlers( true ) as $dbh ) {
32
+ $dbh->tableDelete();
33
+ }
34
+ },
35
+ [
36
+ $con->getModule_Plugin(),
37
+ $con->getModule_Events(),
38
+ $con->getModule_HackGuard(),
39
+ $con->getModule_IPs(),
40
+ $con->getModule_Reporting(),
41
+ $con->getModule_Sessions(),
42
+ ]
43
+ );
44
+
45
+ $WPDB->doDropTable(
46
+ implode( '`,`', array_map(
47
+ function ( $dbh ) {
48
+ /** @var $dbh Handler */
49
+ return $dbh->getTableSchema()->table;
50
+ },
51
+ [
52
+ // Order is critical
53
+ $con->getModule_AuditTrail()->getDbH_Meta(),
54
+ $con->getModule_AuditTrail()->getDbH_Logs(),
55
+ $con->getModule_Data()->getDbH_ReqLogs(),
56
+ $con->getModule_Data()->getDbH_IPs(),
57
+ ]
58
+ )
59
+ )
60
+ );
61
+ }
62
+ }
src/lib/src/Crons/BaseCron.php CHANGED
@@ -12,6 +12,9 @@ abstract class BaseCron {
12
  use Shield\Crons\StandardCron;
13
  use PluginControllerConsumer;
14
 
 
 
 
15
  public function run() {
16
  $this->setupCron();
17
  }
12
  use Shield\Crons\StandardCron;
13
  use PluginControllerConsumer;
14
 
15
+ /**
16
+ * @deprecated 12 - switch to protected
17
+ */
18
  public function run() {
19
  $this->setupCron();
20
  }
src/lib/src/Databases/AuditTrail/Handler.php CHANGED
@@ -3,14 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\AuditTrail;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Options;
7
 
8
  class Handler extends Base\Handler {
9
 
10
- public function autoCleanDb() {
11
- /** @var Options $opts */
12
- $opts = $this->getOptions();
13
- $this->tableCleanExpired( $opts->getAutoCleanDays() );
14
- $this->tableTrimExcess( $opts->getMaxEntries() );
15
- }
16
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\AuditTrail;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
 
6
 
7
  class Handler extends Base\Handler {
8
 
 
 
 
 
 
 
9
  }
src/lib/src/Databases/Events/Handler.php CHANGED
@@ -25,10 +25,7 @@ class Handler extends Base\Handler {
25
  /** @var EntryVO $entry */
26
  $entry = $this->getVo();
27
  $entry->event = $evt;
28
- /**
29
- * @deprecated 11.5
30
- */
31
- $entry->count = min( max( 1, $count ), 1627310000 );
32
  $entry->created_at = Services::Request()->ts();
33
  /** @var Insert $QI */
34
  $QI = $this->getQueryInserter();
25
  /** @var EntryVO $entry */
26
  $entry = $this->getVo();
27
  $entry->event = $evt;
28
+ $entry->count = max( 1, $count );
 
 
 
29
  $entry->created_at = Services::Request()->ts();
30
  /** @var Insert $QI */
31
  $QI = $this->getQueryInserter();
src/lib/src/Databases/Events/Select.php CHANGED
@@ -81,16 +81,16 @@ class Select extends Base\Select {
81
  * @return EntryVO[] - keys are event names
82
  */
83
  public function getLatestForAllEvents() {
84
- $aKeyedLatest = [];
85
  $this->setGroupBy( 'event' )
86
  ->setOrderBy( 'created_at', 'DESC' )
87
  ->addWhere( 'id', $this->getMaxIds(), 'IN' )
88
  ->setResultsAsVo( true );
89
- foreach ( $this->query() as $oEntry ) {
90
- /** @var EntryVO $oEntry */
91
- $aKeyedLatest[ $oEntry->event ] = $oEntry;
92
  }
93
- return $aKeyedLatest;
94
  }
95
 
96
  /**
81
  * @return EntryVO[] - keys are event names
82
  */
83
  public function getLatestForAllEvents() {
84
+ $latest = [];
85
  $this->setGroupBy( 'event' )
86
  ->setOrderBy( 'created_at', 'DESC' )
87
  ->addWhere( 'id', $this->getMaxIds(), 'IN' )
88
  ->setResultsAsVo( true );
89
+ foreach ( $this->query() as $entry ) {
90
+ /** @var EntryVO $entry */
91
+ $latest[ $entry->event ] = $entry;
92
  }
93
+ return $latest;
94
  }
95
 
96
  /**
src/lib/src/Databases/GeoIp/BaseGeoIp.php DELETED
@@ -1,30 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\GeoIp;
4
-
5
- trait BaseGeoIp {
6
-
7
- /**
8
- * Will test whether the Binary IP can be converted back before applying filter.
9
- * @param mixed $bBinaryIp - IP has already been converted using inet_pton
10
- * @return $this
11
- */
12
- public function filterByIp( $bBinaryIp ) {
13
- if ( inet_ntop( $bBinaryIp ) !== false ) {
14
- $this->addWhereEquals( 'ip', $bBinaryIp );
15
- }
16
- return $this;
17
- }
18
-
19
- /**
20
- * Will test whether the Binary IP can be converted back before applying filter.
21
- * @param mixed $bBinaryIp - IP has already been converted using inet_pton
22
- * @return $this
23
- */
24
- public function filterByNotIp( $bBinaryIp ) {
25
- if ( inet_ntop( $bBinaryIp ) !== false ) {
26
- $this->addWhere( 'ip', $bBinaryIp, '!=' );
27
- }
28
- return $this;
29
- }
30
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Databases/GeoIp/Delete.php DELETED
@@ -1,10 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\GeoIp;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
6
-
7
- class Delete extends Base\Delete {
8
-
9
- use BaseGeoIp;
10
- }
 
 
 
 
 
 
 
 
 
 
src/lib/src/Databases/GeoIp/EntryVO.php DELETED
@@ -1,88 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\GeoIp;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
6
-
7
- /**
8
- * Class EntryVO
9
- * @property string rid
10
- * @property int uid
11
- * @property string ip
12
- * @property string path
13
- * @property string code
14
- * @property string ua
15
- * @property string verb
16
- * @property bool trans
17
- */
18
- class EntryVO extends Base\EntryVO {
19
-
20
- /**
21
- * @return string
22
- */
23
- public function getCountryCode() {
24
- return $this->meta[ 'countryCode' ] ?? '';
25
- }
26
-
27
- /**
28
- * @return string
29
- */
30
- public function getCountryName() {
31
- return $this->meta[ 'countryName' ] ?? '';
32
- }
33
-
34
- /**
35
- * @return string
36
- */
37
- public function getLatitude() {
38
- return $this->meta[ 'latitude' ] ?? '';
39
- }
40
-
41
- /**
42
- * @return string
43
- */
44
- public function getLongitude() {
45
- return $this->meta[ 'longitude' ] ?? '';
46
- }
47
-
48
- /**
49
- * @return string
50
- */
51
- public function getTimezone() {
52
- return $this->meta[ 'timeZone' ] ?? '';
53
- }
54
-
55
- /**
56
- * @param string $key
57
- * @return mixed
58
- */
59
- public function __get( string $key ) {
60
- switch ( $key ) {
61
- case 'ip':
62
- $value = inet_ntop( parent::__get( $key ) );
63
- break;
64
-
65
- default:
66
- $value = parent::__get( $key );
67
- }
68
- return $value;
69
- }
70
-
71
- /**
72
- * @inheritDoc
73
- */
74
- public function __set( string $key, $value ) {
75
-
76
- switch ( $key ) {
77
-
78
- case 'ip':
79
- $value = inet_pton( $value );
80
- break;
81
-
82
- default:
83
- break;
84
- }
85
-
86
- parent::__set( $key, $value );
87
- }
88
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Databases/GeoIp/Handler.php DELETED
@@ -1,10 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\GeoIp;
4
-
5
- class Handler extends \FernleafSystems\Wordpress\Plugin\Shield\Databases\Base\Handler {
6
-
7
- public function autoCleanDb() {
8
- $this->tableCleanExpired( (int)$this->getTableSchema()->autoexpire );
9
- }
10
- }
 
 
 
 
 
 
 
 
 
 
src/lib/src/Databases/GeoIp/Insert.php DELETED
@@ -1,9 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\GeoIp;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
6
-
7
- class Insert extends Base\Insert {
8
-
9
- }
 
 
 
 
 
 
 
 
 
src/lib/src/Databases/GeoIp/Select.php DELETED
@@ -1,22 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\GeoIp;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Tool\IpListSort;
7
-
8
- class Select extends Base\Select {
9
-
10
- use BaseGeoIp;
11
- use Base\Traits\Select_IPTable;
12
-
13
- /**
14
- * @param string $sIp
15
- * @return EntryVO
16
- */
17
- public function byIp( $sIp ) {
18
- return $this->filterByIp( inet_pton( $sIp ) )
19
- ->setResultsAsVo( true )
20
- ->first();
21
- }
22
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Databases/Scanner/Update.php CHANGED
@@ -7,40 +7,6 @@ use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class Update extends Base\Update {
9
 
10
- /**
11
- * @param string $scan
12
- * @return bool
13
- */
14
- public function clearIgnoredAtForScan( $scan ) {
15
- return $this->setUpdateWheres( [ 'scan' => $scan ] )
16
- ->setUpdateData( [ 'ignored_at' => 0 ] )
17
- ->query() !== false;
18
- }
19
-
20
- /**
21
- * @param string $scan
22
- * @return bool
23
- */
24
- public function clearNotifiedAtForScan( $scan ) {
25
- return $this->setUpdateWheres( [ 'scan' => $scan ] )
26
- ->setUpdateData( [ 'notified_at' => 0 ] )
27
- ->query() !== false;
28
- }
29
-
30
- /**
31
- * @param string $scan
32
- * @return bool
33
- */
34
- public function setAllNotifiedForScan( $scan ) {
35
- return $this
36
- ->setUpdateWheres( [
37
- 'scan' => $scan,
38
- 'ignored_at' => 0,
39
- ] )
40
- ->setUpdateData( [ 'notified_at' => Services::Request()->ts() ] )
41
- ->query() !== false;
42
- }
43
-
44
  /**
45
  * @param EntryVO $entry
46
  * @return bool
7
 
8
  class Update extends Base\Update {
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  /**
11
  * @param EntryVO $entry
12
  * @return bool
src/lib/src/Databases/Session/Select.php CHANGED
@@ -21,7 +21,7 @@ class Select extends Base\Select {
21
  * @param string $ip
22
  * @return $this
23
  */
24
- public function filterByIp( string $ip ) :self {
25
  if ( Services::IP()->isValidIp( $ip ) ) {
26
  $this->addWhereEquals( 'ip', trim( $ip ) );
27
  }
21
  * @param string $ip
22
  * @return $this
23
  */
24
+ public function filterByIp( string $ip ) {
25
  if ( Services::IP()->isValidIp( $ip ) ) {
26
  $this->addWhereEquals( 'ip', trim( $ip ) );
27
  }
src/lib/src/Databases/Traffic/Handler.php CHANGED
@@ -3,14 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Traffic;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Options;
7
 
8
  class Handler extends Base\Handler {
9
 
10
- public function autoCleanDb() {
11
- /** @var Options $opts */
12
- $opts = $this->getOptions();
13
- $this->tableCleanExpired( $opts->getAutoCleanDays() );
14
- $this->tableTrimExcess( $opts->getMaxEntries() );
15
- }
16
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Traffic;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
 
6
 
7
  class Handler extends Base\Handler {
8
 
 
 
 
 
 
 
9
  }
src/lib/src/Logging/Processors/RequestMetaProcessor.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Logging\Processors;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+ use Monolog\Processor\ProcessorInterface;
8
+
9
+ class RequestMetaProcessor implements ProcessorInterface {
10
+
11
+ /**
12
+ * @param array $record
13
+ * @return array
14
+ */
15
+ public function __invoke( array $record ) {
16
+ $isWpCli = Services::WpGeneral()->isWpCli();
17
+
18
+ $req = Services::Request();
19
+ $leadingPath = Services::WpGeneral()->isMultisite_SubdomainInstall() ? $req->getHost() : '';
20
+
21
+ $record[ 'extra' ][ 'meta_request' ] = array_filter( [
22
+ 'ip' => $isWpCli ? '' : (string)Services::IP()->getRequestIp(),
23
+ 'rid' => Services::Request()->getID( true, 10 ),
24
+ 'ts' => microtime( true ),
25
+ 'ua' => $isWpCli ? 'wpcli' : $req->getUserAgent(),
26
+ 'verb' => $isWpCli ? '' : strtoupper( $req->getMethod() ),
27
+ 'path' => $isWpCli ? '' : ( $leadingPath.$req->getPath().( empty( $_GET ) ? '' : '?'.http_build_query( $_GET ) ) ),
28
+ 'code' => $isWpCli ? '' : http_response_code(),
29
+ ] );
30
+
31
+ return $record;
32
+ }
33
+ }
src/lib/src/Logging/Processors/ShieldMetaProcessor.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Logging\Processors;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
6
+ use Monolog\Processor\ProcessorInterface;
7
+
8
+ class ShieldMetaProcessor implements ProcessorInterface {
9
+
10
+ use PluginControllerConsumer;
11
+
12
+ /**
13
+ * @param array $record
14
+ * @return array
15
+ */
16
+ public function __invoke( array $record ) {
17
+ $record[ 'extra' ][ 'meta_shield' ] = array_filter( [
18
+ 'offense' => $this->getCon()
19
+ ->getModule_IPs()
20
+ ->loadOffenseTracker()
21
+ ->getOffenseCount() > 0 ? 1 : 0,
22
+ ] );
23
+ return $record;
24
+ }
25
+ }
src/lib/src/Logging/Processors/UserMetaProcessor.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Logging\Processors;
4
+
5
+ use FernleafSystems\Wordpress\Services\Services;
6
+ use Monolog\Processor\ProcessorInterface;
7
+
8
+ class UserMetaProcessor implements ProcessorInterface {
9
+
10
+ /**
11
+ * @param array $record
12
+ * @return array
13
+ */
14
+ public function __invoke( array $record ) {
15
+ $WP = Services::WpGeneral();
16
+
17
+ $uid = Services::WpUsers()->getCurrentWpUserId();
18
+ if ( empty( $uid ) ) {
19
+ if ( $WP->isWpCli() ) {
20
+ $uid = 'cli';
21
+ }
22
+ elseif ( $WP->isCron() ) {
23
+ $uid = 'cron';
24
+ }
25
+ else {
26
+ $uid = false;
27
+ }
28
+ }
29
+
30
+ $record[ 'extra' ][ 'meta_user' ] = array_filter( [
31
+ 'uid' => $uid,
32
+ ] );
33
+
34
+ return $record;
35
+ }
36
+ }
src/lib/src/Logging/Processors/WpMetaProcessor.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Logging\Processors;
4
+
5
+ use Monolog\Processor\ProcessorInterface;
6
+
7
+ class WpMetaProcessor implements ProcessorInterface {
8
+
9
+ /**
10
+ * @param array $record
11
+ * @return array
12
+ */
13
+ public function __invoke( array $record ) {
14
+ $record[ 'extra' ][ 'meta_wp' ] = array_filter( [
15
+ 'site_id' => \get_current_blog_id(),
16
+ ] );
17
+ return $record;
18
+ }
19
+ }
src/lib/src/Modules/AuditTrail/AdminNotices.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Utilities\AdminNotices\NoticeVO;
7
+ use FernleafSystems\Wordpress\Services\Services;
8
+
9
+ class AdminNotices extends Shield\Modules\Base\AdminNotices {
10
+
11
+ /**
12
+ * @inheritDoc
13
+ */
14
+ protected function processNotice( NoticeVO $notice ) {
15
+
16
+ switch ( $notice->id ) {
17
+
18
+ case 'new-audit-trail':
19
+ $this->buildNotice_NewAuditTrail( $notice );
20
+ break;
21
+
22
+ default:
23
+ parent::processNotice( $notice );
24
+ break;
25
+ }
26
+ }
27
+
28
+ protected function isDisplayNeeded( NoticeVO $notice ) :bool {
29
+ switch ( $notice->id ) {
30
+
31
+ case 'new-audit-trail':
32
+ $needed = $this->getMod()->isPage_InsightsThisModule();
33
+ break;
34
+
35
+ default:
36
+ $needed = parent::isDisplayNeeded( $notice );
37
+ break;
38
+ }
39
+ return $needed;
40
+ }
41
+
42
+ private function buildNotice_NewAuditTrail( NoticeVO $notice ) {
43
+ $notice->render_data = [
44
+ 'notice_attributes' => [],
45
+ 'strings' => [
46
+ 'title' => __( "Your Security Audit Log Is Brand New", 'wp-simple-firewall' ),
47
+ 'lines' => [
48
+ __( "We've completely completely rewritten the security audit log making it more detailed, searchable, faster, and more.", 'wp-simple-firewall' ),
49
+ __( "Every effort has been made to convert old data to the new format, but it wasn't 100% possible.", 'wp-simple-firewall' )
50
+ .' '.__( "Some older events may have missing data, but all new events will populate properly.", 'wp-simple-firewall' ),
51
+ ],
52
+ 'read_more' => __( 'Click here to read more about the changes', 'wp-simple-firewall' )
53
+ ],
54
+ 'hrefs' => [
55
+ 'read_more' => 'https://shsec.io/kf'
56
+ ]
57
+ ];
58
+ }
59
+ }
src/lib/src/Modules/AuditTrail/AjaxHandler.php CHANGED
@@ -10,14 +10,9 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
10
  protected function processAjaxAction( string $action ) :array {
11
 
12
  switch ( $action ) {
13
- case 'render_table_audittrail':
14
- $response = $this->ajaxExec_BuildTableAuditTrail();
15
  break;
16
-
17
- case 'item_addparamwhite':
18
- $response = $this->ajaxExec_AddParamToFirewallWhitelist();
19
- break;
20
-
21
  default:
22
  $response = parent::processAjaxAction( $action );
23
  }
@@ -25,42 +20,18 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
25
  return $response;
26
  }
27
 
28
- protected function ajaxExec_AddParamToFirewallWhitelist() :array {
29
- /** @var ModCon $mod */
30
- $mod = $this->getMod();
31
- $success = false;
32
-
33
- $entryID = Services::Request()->post( 'rid' );
34
- if ( empty( $entryID ) || !is_numeric( $entryID ) || $entryID < 1 ) {
35
- $msg = __( 'Invalid audit entry selected for this action', 'wp-simple-firewall' );
36
  }
37
- else {
38
- try {
39
- $msg = ( new Lib\Utility\AutoWhitelistParamFromAuditEntry() )
40
- ->setMod( $mod )
41
- ->run( (int)$entryID );
42
- $success = true;
43
- }
44
- catch ( \Exception $e ) {
45
- $msg = $e->getMessage();
46
- }
47
  }
48
-
49
- return [
50
- 'success' => $success,
51
- 'message' => $msg
52
- ];
53
- }
54
-
55
- private function ajaxExec_BuildTableAuditTrail() :array {
56
- /** @var ModCon $mod */
57
- $mod = $this->getMod();
58
- return [
59
- 'success' => true,
60
- 'html' => ( new Shield\Tables\Build\AuditTrail() )
61
- ->setMod( $mod )
62
- ->setDbHandler( $mod->getDbHandler_AuditTrail() )
63
- ->render()
64
- ];
65
  }
66
  }
10
  protected function processAjaxAction( string $action ) :array {
11
 
12
  switch ( $action ) {
13
+ case 'logtable_action':
14
+ $response = $this->ajaxExec_AuditTrailTableAction();
15
  break;
 
 
 
 
 
16
  default:
17
  $response = parent::processAjaxAction( $action );
18
  }
20
  return $response;
21
  }
22
 
23
+ private function ajaxExec_AuditTrailTableAction() :array {
24
+ try {
25
+ return ( new Lib\LogTable\DelegateAjaxHandler() )
26
+ ->setMod( $this->getMod() )
27
+ ->processAjaxAction();
 
 
 
28
  }
29
+ catch ( \Exception $e ) {
30
+ return [
31
+ 'success' => false,
32
+ 'page_reload' => true,
33
+ 'message' => $e->getMessage(),
34
+ ];
 
 
 
 
35
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  }
37
  }
src/lib/src/Modules/AuditTrail/Auditors/Emails.php CHANGED
@@ -11,27 +11,27 @@ class Emails extends Base {
11
  }
12
 
13
  /**
14
- * @param array $aEmail
15
  * @return array
16
  */
17
- public function auditEmailSend( $aEmail ) {
18
 
19
- if ( is_array( $aEmail ) ) {
20
 
21
- $sTo = isset( $aEmail[ 'to' ] ) ? $aEmail[ 'to' ] : __( 'not provided', 'wp-simple-firewall' );
22
- if ( is_array( $sTo ) ) {
23
- $sTo = implode( ', ', $sTo );
24
  }
25
 
26
- $aData = [
27
- 'to' => $sTo,
28
- 'subject' => $aEmail[ 'subject' ],
29
  ];
30
 
31
  // Attempt to capture BCC/CC
32
  $aCCs = [];
33
- if ( !empty( $aEmail[ 'headers' ] ) ) {
34
- $aHeaders = $aEmail[ 'headers' ];
35
  if ( is_string( $aHeaders ) ) {
36
  $aHeaders = explode( "\n", $aHeaders );
37
  }
@@ -39,24 +39,18 @@ class Emails extends Base {
39
  $aCCs = $this->extractCcFromHeaders( $aHeaders );
40
  }
41
  }
42
- $aData[ 'cc' ] = empty( $aCCs[ 'cc' ] ) ? '-' : implode( ',', $aCCs[ 'cc' ] );
43
- $aData[ 'bcc' ] = empty( $aCCs[ 'bcc' ] ) ? '-' : implode( ',', $aCCs[ 'bcc' ] );
44
 
45
  // Where was the wp_mail function called from
46
  $aBacktrace = $this->findEmailSenderBacktrace();
47
- $aData[ 'bt_file' ] = empty( $aBacktrace[ 'file' ] ) ? 'unavailable' : str_replace( ABSPATH, '', $aBacktrace[ 'file' ] );
48
- $aData[ 'bt_line' ] = empty( $aBacktrace[ 'line' ] ) ? 'unavailable' : $aBacktrace[ 'line' ];
49
 
50
- $this->getCon()->fireEvent( 'email_attempt_send', [ 'audit' => $aData ] );
51
- }
52
- else {
53
- $this->getCon()->fireEvent(
54
- 'email_send_invalid',
55
- [ 'audit' => [ 'type' => 'array' ] ]
56
- );
57
  }
58
 
59
- return $aEmail;
60
  }
61
 
62
  /**
11
  }
12
 
13
  /**
14
+ * @param array $email
15
  * @return array
16
  */
17
+ public function auditEmailSend( $email ) {
18
 
19
+ if ( is_array( $email ) ) {
20
 
21
+ $to = $email[ 'to' ] ?? __( 'not provided', 'wp-simple-firewall' );
22
+ if ( is_array( $to ) ) {
23
+ $to = implode( ', ', $to );
24
  }
25
 
26
+ $auditData = [
27
+ 'to' => $to,
28
+ 'subject' => $email[ 'subject' ],
29
  ];
30
 
31
  // Attempt to capture BCC/CC
32
  $aCCs = [];
33
+ if ( !empty( $email[ 'headers' ] ) ) {
34
+ $aHeaders = $email[ 'headers' ];
35
  if ( is_string( $aHeaders ) ) {
36
  $aHeaders = explode( "\n", $aHeaders );
37
  }
39
  $aCCs = $this->extractCcFromHeaders( $aHeaders );
40
  }
41
  }
42
+ $auditData[ 'cc' ] = empty( $aCCs[ 'cc' ] ) ? '-' : implode( ',', $aCCs[ 'cc' ] );
43
+ $auditData[ 'bcc' ] = empty( $aCCs[ 'bcc' ] ) ? '-' : implode( ',', $aCCs[ 'bcc' ] );
44
 
45
  // Where was the wp_mail function called from
46
  $aBacktrace = $this->findEmailSenderBacktrace();
47
+ $auditData[ 'bt_file' ] = empty( $aBacktrace[ 'file' ] ) ? 'unavailable' : str_replace( ABSPATH, '', $aBacktrace[ 'file' ] );
48
+ $auditData[ 'bt_line' ] = empty( $aBacktrace[ 'line' ] ) ? 'unavailable' : $aBacktrace[ 'line' ];
49
 
50
+ $this->getCon()->fireEvent( 'email_attempt_send', [ 'audit_params' => $auditData ] );
 
 
 
 
 
 
51
  }
52
 
53
+ return $email;
54
  }
55
 
56
  /**
src/lib/src/Modules/AuditTrail/Auditors/Plugins.php CHANGED
@@ -11,25 +11,25 @@ class Plugins extends Base {
11
  }
12
 
13
  /**
14
- * @param string $sPlugin
15
  */
16
- public function auditActivatedPlugin( $sPlugin ) {
17
- if ( !empty( $sPlugin ) ) {
18
  $this->getCon()->fireEvent(
19
  'plugin_activated',
20
- [ 'audit' => [ 'plugin' => $sPlugin ] ]
21
  );
22
  }
23
  }
24
 
25
  /**
26
- * @param string $sPlugin
27
  */
28
- public function auditDeactivatedPlugin( $sPlugin ) {
29
- if ( !empty( $sPlugin ) ) {
30
  $this->getCon()->fireEvent(
31
  'plugin_deactivated',
32
- [ 'audit' => [ 'plugin' => $sPlugin ] ]
33
  );
34
  }
35
  }
@@ -43,7 +43,7 @@ class Plugins extends Base {
43
  if ( strpos( $sAction, $sStub ) === 0 ) {
44
  $this->getCon()->fireEvent(
45
  'plugin_file_edited',
46
- [ 'audit' => [ 'file' => str_replace( $sStub, '', $sAction ) ] ]
47
  );
48
  }
49
  }
11
  }
12
 
13
  /**
14
+ * @param string $plugin
15
  */
16
+ public function auditActivatedPlugin( $plugin ) {
17
+ if ( !empty( $plugin ) ) {
18
  $this->getCon()->fireEvent(
19
  'plugin_activated',
20
+ [ 'audit_params' => [ 'plugin' => $plugin ] ]
21
  );
22
  }
23
  }
24
 
25
  /**
26
+ * @param string $plugin
27
  */
28
+ public function auditDeactivatedPlugin( $plugin ) {
29
+ if ( !empty( $plugin ) ) {
30
  $this->getCon()->fireEvent(
31
  'plugin_deactivated',
32
+ [ 'audit_params' => [ 'plugin' => $plugin ] ]
33
  );
34
  }
35
  }
43
  if ( strpos( $sAction, $sStub ) === 0 ) {
44
  $this->getCon()->fireEvent(
45
  'plugin_file_edited',
46
+ [ 'audit_params' => [ 'file' => str_replace( $sStub, '', $sAction ) ] ]
47
  );
48
  }
49
  }
src/lib/src/Modules/AuditTrail/Auditors/Posts.php CHANGED
@@ -12,73 +12,69 @@ class Posts extends Base {
12
  }
13
 
14
  /**
15
- * @param string $nPostId
16
  */
17
- public function auditDeletedPost( $nPostId ) {
18
- $oPost = Services::WpPost()->getById( $nPostId );
19
- if ( $oPost instanceof \WP_Post && !$this->isIgnoredPostType( $oPost ) ) {
20
  $this->getCon()->fireEvent(
21
  'post_deleted',
22
- [ 'audit' => [ 'title' => $oPost->post_title ] ]
23
  );
24
  }
25
  }
26
 
27
  /**
28
- * @param string $sNewStatus
29
- * @param string $sOldStatus
30
- * @param \WP_Post $oPost
31
  */
32
- public function auditPostStatus( $sNewStatus, $sOldStatus, $oPost ) {
33
 
34
- if ( !$oPost instanceof \WP_Post || $this->isIgnoredPostType( $oPost )
35
- || in_array( $sNewStatus, [ 'auto-draft', 'inherit' ] ) ) {
36
  return;
37
  }
38
 
39
- if ( $sNewStatus == 'trash' ) {
40
- $sEvent = 'post_trashed';
41
  }
42
- elseif ( $sOldStatus == 'trash' && $sNewStatus != 'trash' ) {
43
- $sEvent = 'post_recovered';
44
  }
45
- elseif ( in_array( $sNewStatus, [ 'publish', 'private' ] ) ) {
46
 
47
- if ( in_array( $sOldStatus, [ 'publish', 'private' ] ) ) {
48
- $sEvent = 'post_updated';
49
  }
50
  else {
51
- $sEvent = 'post_published';
52
  }
53
  }
54
- elseif ( in_array( $sOldStatus, [ 'publish', 'private' ] ) && $sNewStatus == 'draft' ) {
55
- $sEvent = 'post_unpublished';
56
  }
57
  else {
58
- $sEvent = 'post_updated';
59
  }
60
 
61
  $this->getCon()->fireEvent(
62
- $sEvent,
63
  [
64
- 'audit' => [
65
- 'title' => $oPost->post_title,
66
- 'type' => $oPost->post_type,
67
  ]
68
  ]
69
  );
70
  }
71
 
72
- /**
73
- * @param \WP_Post $oPost
74
- * @return bool
75
- */
76
- private function isIgnoredPostType( $oPost ) {
77
  return
78
- ( $oPost->post_status == 'auto-draft' )
79
  ||
80
  in_array(
81
- $oPost->post_type,
82
  [
83
  'revision',
84
  'nav_menu_item',
12
  }
13
 
14
  /**
15
+ * @param string $postID
16
  */
17
+ public function auditDeletedPost( $postID ) {
18
+ $post = Services::WpPost()->getById( $postID );
19
+ if ( $post instanceof \WP_Post && !$this->isIgnoredPostType( $post ) ) {
20
  $this->getCon()->fireEvent(
21
  'post_deleted',
22
+ [ 'audit_params' => [ 'title' => $post->post_title ] ]
23
  );
24
  }
25
  }
26
 
27
  /**
28
+ * @param string $newStatus
29
+ * @param string $oldStatus
30
+ * @param \WP_Post $post
31
  */
32
+ public function auditPostStatus( $newStatus, $oldStatus, $post ) {
33
 
34
+ if ( !$post instanceof \WP_Post || $this->isIgnoredPostType( $post )
35
+ || in_array( $newStatus, [ 'auto-draft', 'inherit' ] ) ) {
36
  return;
37
  }
38
 
39
+ if ( $newStatus == 'trash' ) {
40
+ $event = 'post_trashed';
41
  }
42
+ elseif ( $oldStatus == 'trash' ) {
43
+ $event = 'post_recovered';
44
  }
45
+ elseif ( in_array( $newStatus, [ 'publish', 'private' ] ) ) {
46
 
47
+ if ( in_array( $oldStatus, [ 'publish', 'private' ] ) ) {
48
+ $event = 'post_updated';
49
  }
50
  else {
51
+ $event = 'post_published';
52
  }
53
  }
54
+ elseif ( in_array( $oldStatus, [ 'publish', 'private' ] ) && $newStatus == 'draft' ) {
55
+ $event = 'post_unpublished';
56
  }
57
  else {
58
+ $event = 'post_updated';
59
  }
60
 
61
  $this->getCon()->fireEvent(
62
+ $event,
63
  [
64
+ 'audit_params' => [
65
+ 'title' => $post->post_title,
66
+ 'type' => $post->post_type,
67
  ]
68
  ]
69
  );
70
  }
71
 
72
+ private function isIgnoredPostType( \WP_Post $post ) :bool {
 
 
 
 
73
  return
74
+ ( $post->post_status == 'auto-draft' )
75
  ||
76
  in_array(
77
+ $post->post_type,
78
  [
79
  'revision',
80
  'nav_menu_item',
src/lib/src/Modules/AuditTrail/Auditors/Themes.php CHANGED
@@ -6,31 +6,30 @@ class Themes extends Base {
6
 
7
  protected function run() {
8
  add_action( 'switch_theme', [ $this, 'auditSwitchTheme' ] );
9
- add_action( 'check_admin_referer', [ $this, 'auditEditedThemeFile' ], 10, 2 );
10
  }
11
 
12
  /**
13
- * @param string $sThemeName
14
  */
15
- public function auditSwitchTheme( $sThemeName ) {
16
- if ( !empty( $sThemeName ) ) {
17
  $this->getCon()->fireEvent(
18
  'theme_activated',
19
- [ 'audit' => [ 'theme' => $sThemeName ] ]
20
  );
21
  }
22
  }
23
 
24
  /**
25
- * @param string $sAction
26
- * @param bool $bResult
27
  */
28
- public function auditEditedThemeFile( $sAction, $bResult ) {
29
- $sStub = 'edit-theme_';
30
- if ( strpos( $sAction, $sStub ) === 0 ) {
31
  $this->getCon()->fireEvent(
32
  'theme_file_edited',
33
- [ 'audit' => [ 'file' => str_replace( $sStub, '', $sAction ) ] ]
34
  );
35
  }
36
  }
6
 
7
  protected function run() {
8
  add_action( 'switch_theme', [ $this, 'auditSwitchTheme' ] );
9
+ add_action( 'check_admin_referer', [ $this, 'auditEditedThemeFile' ] );
10
  }
11
 
12
  /**
13
+ * @param string $themeName
14
  */
15
+ public function auditSwitchTheme( $themeName ) {
16
+ if ( !empty( $themeName ) ) {
17
  $this->getCon()->fireEvent(
18
  'theme_activated',
19
+ [ 'audit_params' => [ 'theme' => $themeName ] ]
20
  );
21
  }
22
  }
23
 
24
  /**
25
+ * @param string $action
 
26
  */
27
+ public function auditEditedThemeFile( $action ) {
28
+ $stub = 'edit-theme_';
29
+ if ( strpos( $action, $stub ) === 0 ) {
30
  $this->getCon()->fireEvent(
31
  'theme_file_edited',
32
+ [ 'audit_params' => [ 'file' => str_replace( $stub, '', $action ) ] ]
33
  );
34
  }
35
  }
src/lib/src/Modules/AuditTrail/Auditors/Upgrades.php CHANGED
@@ -55,57 +55,57 @@ class Upgrades extends Base {
55
  if ( $action === 'update' && in_array( $type, [ 'plugin', 'theme' ] ) ) {
56
  if ( !empty( $data[ 'plugins' ] ) && is_array( $data[ 'plugins' ] ) ) {
57
  foreach ( $data[ 'plugins' ] as $item ) {
58
- if ( isset( $this->plugins[ $item ] ) ) {
59
- $this->handlePlugin( $item );
60
- }
61
  }
62
  }
63
  elseif ( !empty( $data[ 'themes' ] ) && is_array( $data[ 'themes' ] ) ) {
64
  foreach ( $data[ 'themes' ] as $item ) {
65
- if ( isset( $this->themes[ $item ] ) ) {
66
- $this->handleTheme( $item );
67
- }
68
  }
69
  }
70
  }
71
  }
72
 
73
  private function handlePlugin( string $item ) {
74
- $WPP = Services::WpPlugins();
75
- $VO = $WPP->getPluginAsVo( $item, true );
76
- if ( !empty( $VO ) ) {
77
- $this->getCon()->fireEvent(
78
- 'plugin_upgraded',
79
- [
80
- 'audit' => [
81
- 'file' => $VO->Name,
82
- 'from' => $this->plugins[ $item ],
83
- 'to' => $VO->Version,
 
 
84
  ]
85
- ]
86
- );
 
87
  }
88
  }
89
 
90
  /**
91
- * Hooked into 'shutdown' to ensure that the latest theme data is avaiable
92
- * so that we can get the "upgraded to" version correctly.
93
- * @param string $item
94
  */
95
  private function handleTheme( string $item ) {
96
- $WPT = Services::WpThemes();
97
- $VO = $WPT->getThemeAsVo( $item, true );
98
- if ( !empty( $VO ) ) {
99
- $this->getCon()->fireEvent(
100
- 'theme_upgraded',
101
- [
102
- 'audit' => [
103
- 'file' => $VO->Name,
104
- 'from' => $this->themes[ $item ],
105
- 'to' => $VO->Version,
 
 
106
  ]
107
- ]
108
- );
 
109
  }
110
  }
111
  }
55
  if ( $action === 'update' && in_array( $type, [ 'plugin', 'theme' ] ) ) {
56
  if ( !empty( $data[ 'plugins' ] ) && is_array( $data[ 'plugins' ] ) ) {
57
  foreach ( $data[ 'plugins' ] as $item ) {
58
+ $this->handlePlugin( $item );
 
 
59
  }
60
  }
61
  elseif ( !empty( $data[ 'themes' ] ) && is_array( $data[ 'themes' ] ) ) {
62
  foreach ( $data[ 'themes' ] as $item ) {
63
+ $this->handleTheme( $item );
 
 
64
  }
65
  }
66
  }
67
  }
68
 
69
  private function handlePlugin( string $item ) {
70
+ if ( isset( $this->plugins[ $item ] ) ) {
71
+ $WPP = Services::WpPlugins();
72
+ $VO = $WPP->getPluginAsVo( $item, true );
73
+ if ( !empty( $VO ) ) {
74
+ $this->getCon()->fireEvent(
75
+ 'plugin_upgraded',
76
+ [
77
+ 'audit_params' => [
78
+ 'plugin' => $VO->file, // was 'file'
79
+ 'from' => $this->plugins[ $item ],
80
+ 'to' => $VO->Version,
81
+ ]
82
  ]
83
+ );
84
+ unset( $this->plugins[ $item ] );
85
+ }
86
  }
87
  }
88
 
89
  /**
90
+ * uses "isset()" to prevent duplicates.
 
 
91
  */
92
  private function handleTheme( string $item ) {
93
+ if ( isset( $this->themes[ $item ] ) ) {
94
+ $WPT = Services::WpThemes();
95
+ $VO = $WPT->getThemeAsVo( $item, true );
96
+ if ( !empty( $VO ) ) {
97
+ $this->getCon()->fireEvent(
98
+ 'theme_upgraded',
99
+ [
100
+ 'audit_params' => [
101
+ 'theme' => $VO->stylesheet, // was 'file'
102
+ 'from' => $this->themes[ $item ],
103
+ 'to' => $VO->Version,
104
+ ]
105
  ]
106
+ );
107
+ unset( $this->themes[ $item ] );
108
+ }
109
  }
110
  }
111
  }
src/lib/src/Modules/AuditTrail/Auditors/Users.php CHANGED
@@ -25,8 +25,8 @@ class Users extends Base {
25
  $this->getCon()->fireEvent(
26
  Services::WpUsers()->isAppPasswordAuth() ? 'user_login_app' : 'user_login',
27
  [
28
- 'audit' => [
29
- 'user' => $user->user_login,
30
  ]
31
  ]
32
  );
@@ -38,9 +38,9 @@ class Users extends Base {
38
  $this->getCon()->fireEvent(
39
  'user_registered',
40
  [
41
- 'audit' => [
42
- 'user' => sanitize_user( $user->user_login ),
43
- 'email' => $user->user_email,
44
  ]
45
  ]
46
  );
@@ -52,28 +52,28 @@ class Users extends Base {
52
  * @param int $nReassigned
53
  */
54
  public function auditDeleteUser( $userID, $nReassigned ) {
55
- $oWpUsers = Services::WpUsers();
56
 
57
- $user = empty( $userID ) ? null : $oWpUsers->getUserById( $userID );
58
  if ( $user instanceof \WP_User ) {
59
  $this->getCon()->fireEvent(
60
  'user_deleted',
61
  [
62
- 'audit' => [
63
- 'user' => sanitize_user( $user->user_login ),
64
- 'email' => $user->user_email,
65
  ]
66
  ]
67
  );
68
  }
69
 
70
- $oReassignedUser = empty( $nReassigned ) ? null : $oWpUsers->getUserById( $nReassigned );
71
- if ( $oReassignedUser instanceof \WP_User ) {
72
  $this->getCon()->fireEvent(
73
  'user_deleted_reassigned',
74
  [
75
- 'audit' => [
76
- 'user' => sanitize_user( $oReassignedUser->user_login ),
77
  ]
78
  ]
79
  );
25
  $this->getCon()->fireEvent(
26
  Services::WpUsers()->isAppPasswordAuth() ? 'user_login_app' : 'user_login',
27
  [
28
+ 'audit_params' => [
29
+ 'user_login' => $user->user_login,
30
  ]
31
  ]
32
  );
38
  $this->getCon()->fireEvent(
39
  'user_registered',
40
  [
41
+ 'audit_params' => [
42
+ 'user_login' => sanitize_user( $user->user_login ),
43
+ 'email' => $user->user_email,
44
  ]
45
  ]
46
  );
52
  * @param int $nReassigned
53
  */
54
  public function auditDeleteUser( $userID, $nReassigned ) {
55
+ $WPU = Services::WpUsers();
56
 
57
+ $user = empty( $userID ) ? null : $WPU->getUserById( $userID );
58
  if ( $user instanceof \WP_User ) {
59
  $this->getCon()->fireEvent(
60
  'user_deleted',
61
  [
62
+ 'audit_params' => [
63
+ 'user_login' => sanitize_user( $user->user_login ),
64
+ 'email' => $user->user_email,
65
  ]
66
  ]
67
  );
68
  }
69
 
70
+ $reassigned = empty( $nReassigned ) ? null : $WPU->getUserById( $nReassigned );
71
+ if ( $reassigned instanceof \WP_User ) {
72
  $this->getCon()->fireEvent(
73
  'user_deleted_reassigned',
74
  [
75
+ 'audit_params' => [
76
+ 'user_login' => sanitize_user( $reassigned->user_login ),
77
  ]
78
  ]
79
  );
src/lib/src/Modules/AuditTrail/Auditors/Wordpress.php CHANGED
@@ -12,31 +12,31 @@ class Wordpress extends Base {
12
  }
13
 
14
  /**
15
- * @param string $sNewCoreVersion
16
  */
17
- public function auditCoreUpdated( $sNewCoreVersion ) {
18
  $this->getCon()->fireEvent(
19
  'core_updated',
20
  [
21
- 'audit' => [
22
- 'old' => Services::WpGeneral()->getVersion(),
23
- 'new' => $sNewCoreVersion,
24
  ]
25
  ]
26
  );
27
  }
28
 
29
  /**
30
- * @param string $sOld
31
- * @param string $sNew
32
  */
33
- public function auditPermalinkStructure( $sOld, $sNew ) {
34
  $this->getCon()->fireEvent(
35
  'permalinks_structure',
36
  [
37
- 'audit' => [
38
- 'old' => $sOld,
39
- 'new' => $sNew,
40
  ]
41
  ]
42
  );
12
  }
13
 
14
  /**
15
+ * @param string $newVersion
16
  */
17
+ public function auditCoreUpdated( $newVersion ) {
18
  $this->getCon()->fireEvent(
19
  'core_updated',
20
  [
21
+ 'audit_params' => [
22
+ 'from' => Services::WpGeneral()->getVersion(),
23
+ 'to' => $newVersion,
24
  ]
25
  ]
26
  );
27
  }
28
 
29
  /**
30
+ * @param string $old
31
+ * @param string $new
32
  */
33
+ public function auditPermalinkStructure( $old, $new ) {
34
  $this->getCon()->fireEvent(
35
  'permalinks_structure',
36
  [
37
+ 'audit_params' => [
38
+ 'from' => $old,
39
+ 'to' => $new,
40
  ]
41
  ]
42
  );
src/lib/src/Modules/AuditTrail/DB/LoadLogs.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\ModCon;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
+ use FernleafSystems\Wordpress\Services\Services;
9
+
10
+ class LoadLogs {
11
+
12
+ use ModConsumer;
13
+ use IpAddressConsumer;
14
+
15
+ /**
16
+ * @return LogRecord[]
17
+ */
18
+ public function run() :array {
19
+ /** @var ModCon $mod */
20
+ $mod = $this->getMod();
21
+ $stdKeys = array_flip( array_unique( array_merge(
22
+ $mod->getDbH_Logs()
23
+ ->getTableSchema()
24
+ ->getColumnNames(),
25
+ $this->getCon()
26
+ ->getModule_Data()
27
+ ->getDbH_IPs()
28
+ ->getTableSchema()
29
+ ->getColumnNames(),
30
+ [
31
+ 'rid'
32
+ ]
33
+ ) ) );
34
+
35
+ $results = [];
36
+
37
+ foreach ( $this->selectRaw() as $raw ) {
38
+ if ( empty( $results[ $raw[ 'id' ] ] ) ) {
39
+ $record = new LogRecord( array_intersect_key( $raw, $stdKeys ) );
40
+ $results[ $raw[ 'id' ] ] = $record;
41
+ }
42
+ else {
43
+ $record = $results[ $raw[ 'id' ] ];
44
+ }
45
+
46
+ if ( !empty( $raw[ 'meta_key' ] ) ) {
47
+ $meta = $record->meta_data ?? [];
48
+ $meta[ $raw[ 'meta_key' ] ] = $raw[ 'meta_value' ];
49
+ $record->meta_data = $meta;
50
+ }
51
+ }
52
+
53
+ return $results;
54
+ }
55
+
56
+ /**
57
+ * https://stackoverflow.com/questions/55347251/cannot-select-where-ip-inet-ptonip
58
+ * We use MySQL built-in IP conversion, not PHPs, as it wasn't working as expected and return 0 results.
59
+ * Note: reverse is INET6_ATON
60
+ * @return array[]
61
+ */
62
+ private function selectRaw() :array {
63
+ /** @var ModCon $mod */
64
+ $mod = $this->getMod();
65
+
66
+ return Services::WpDb()->selectCustom(
67
+ sprintf( 'SELECT log.id, log.site_id, log.event_slug, log.created_at,
68
+ ips.ip,
69
+ meta.meta_key, meta.meta_value,
70
+ req.req_id as rid
71
+ FROM `%s` as log
72
+ INNER JOIN `%s` as req
73
+ ON log.req_ref = req.id
74
+ INNER JOIN `%s` as ips
75
+ ON ips.id = req.ip_ref
76
+ %s
77
+ LEFT JOIN `%s` as `meta`
78
+ ON log.id = `meta`.log_ref
79
+ ORDER BY log.updated_at DESC;',
80
+ $mod->getDbH_Logs()->getTableSchema()->table,
81
+ $this->getCon()->getModule_Data()->getDbH_ReqLogs()->getTableSchema()->table,
82
+ $this->getCon()->getModule_Data()->getDbH_IPs()->getTableSchema()->table,
83
+ empty( $this->getIP() ) ? '' : sprintf( "AND ips.ip=INET6_ATON('%s')", $this->getIP() ),
84
+ $mod->getDbH_Meta()->getTableSchema()->table
85
+ )
86
+ );
87
+ }
88
+ }
src/lib/src/Modules/AuditTrail/DB/LogRecord.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB;
4
+
5
+ /**
6
+ * @property string $rid
7
+ * @property array $meta_data
8
+ */
9
+ class LogRecord extends Logs\Ops\Record {
10
+
11
+ }
src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Common.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Logs\Ops;
4
+
5
+ trait Common {
6
+
7
+ public function filterByEvent( string $event ) {
8
+ return $this->addWhereEquals( 'event_slug', $event );
9
+ }
10
+
11
+ public function filterByRequestRef( int $reqRef ) {
12
+ return $this->addWhereEquals( 'req_ref', $reqRef );
13
+ }
14
+
15
+ public function filterByRequestRefs( array $reqRef ) {
16
+ return $this->addWhereIn( 'req_ref', $reqRef );
17
+ }
18
+ }
src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Delete.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Logs\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Delete extends Base\Delete {
8
+
9
+ use Common;
10
+ }
src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Handler.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Logs\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Handler extends Base\Handler {
8
+
9
+ }
src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Record.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Logs\Ops;
4
+
5
+ /**
6
+ * @property int $req_ref
7
+ * @property int $site_id
8
+ * @property string $event_slug
9
+ * @property string $ip
10
+ */
11
+ class Record extends \FernleafSystems\Wordpress\Plugin\Core\Databases\Base\Record {
12
+
13
+ }
src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Select.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Logs\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Select extends Base\Select {
8
+
9
+ use Common;
10
+ }
src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Common.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Meta\Ops;
4
+
5
+ trait Common {
6
+
7
+ public function filterByMetaKey( string $key ) {
8
+ return $this->addWhereEquals( 'meta_key', $key );
9
+ }
10
+
11
+ public function filterByLogRef( int $logRef ) {
12
+ return $this->filterByLogRefs( [ $logRef ] );
13
+ }
14
+
15
+ public function filterByLogRefs( array $logRefs ) {
16
+ return $this->addWhereIn( 'log_ref', array_map( 'intval', $logRefs ) );
17
+ }
18
+ }
src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Delete.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Meta\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Delete extends Base\Delete {
8
+
9
+ use Common;
10
+ }
src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Handler.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Meta\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Handler extends Base\Handler {
8
+
9
+ }
src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Record.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Meta\Ops;
4
+
5
+ /**
6
+ * @property int $log_ref
7
+ * @property string $meta_key
8
+ * @property mixed $meta_value
9
+ */
10
+ class Record extends \FernleafSystems\Wordpress\Plugin\Core\Databases\Base\Record {
11
+
12
+ }
src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Select.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Meta\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Select extends Base\Select {
8
+
9
+ use Common;
10
+ }
src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Update.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Meta\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Update extends Base\Select {
8
+
9
+ use Common;
10
+ }
src/lib/src/Modules/AuditTrail/Insights/OverviewCards.php CHANGED
@@ -26,7 +26,7 @@ class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
26
  $cards[ 'audit_length' ] = [
27
  'name' => __( 'Audit Trail', 'wp-simple-firewall' ),
28
  'state' => 0,
29
- 'summary' => sprintf( __( 'Maximum Audit Trail entries limited to %s', 'wp-simple-firewall' ), $opts->getMaxEntries() ),
30
  'href' => $mod->getUrl_DirectLinkToOption( 'audit_trail_max_entries' ),
31
  ];
32
  }
26
  $cards[ 'audit_length' ] = [
27
  'name' => __( 'Audit Trail', 'wp-simple-firewall' ),
28
  'state' => 0,
29
+ 'summary' => sprintf( __( 'Audit Trail entries limited to maximum %s days', 'wp-simple-firewall' ), $opts->getAutoCleanDays() ),
30
  'href' => $mod->getUrl_DirectLinkToOption( 'audit_trail_max_entries' ),
31
  ];
32
  }
src/lib/src/Modules/AuditTrail/Lib/AuditLogger.php ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Logging\Processors;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Logs;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\LogHandlers\LocalDbWriter;
8
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\LogHandlers\LogFileHandler;
9
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Options;
10
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Lib\EventsListener;
11
+ use Monolog\Formatter\JsonFormatter;
12
+ use Monolog\Handler\FilterHandler;
13
+ use Monolog\Logger;
14
+
15
+ class AuditLogger extends EventsListener {
16
+
17
+ /**
18
+ * @var array[]
19
+ */
20
+ private $auditLogs = [];
21
+
22
+ /**
23
+ * @var Logger
24
+ */
25
+ private $logger;
26
+
27
+ protected function init() {
28
+ $con = $this->getCon();
29
+ $mod = $con->getModule_AuditTrail();
30
+ /** @var Options $opts */
31
+ $opts = $mod->getOptions();
32
+
33
+ if ( $opts->isLogToDB() ) {
34
+ $this->getLogger()
35
+ ->pushHandler(
36
+ new FilterHandler( ( new LocalDbWriter() )->setMod( $mod ), $opts->getLogLevelsDB() )
37
+ );
38
+ // The Request Logger is required to link up the DB entries.
39
+ $con->getModule_Traffic()->getRequestLogger()->execute();
40
+ }
41
+
42
+ if ( $con->hasCacheDir() && $opts->isLogToFile() ) {
43
+ try {
44
+ $fileHandlerWithFilter = new FilterHandler( new LogFileHandler( $mod ), $opts->getLogLevelsFile() );
45
+ if ( $opts->getOpt( 'log_format_file' ) === 'json' ) {
46
+ $fileHandlerWithFilter->getHandler()->setFormatter( new JsonFormatter() );
47
+ }
48
+ $this->getLogger()->pushHandler( $fileHandlerWithFilter );
49
+ }
50
+ catch ( \Exception $e ) {
51
+ }
52
+ }
53
+ }
54
+
55
+ public function getLogger() :Logger {
56
+ if ( !isset( $this->logger ) ) {
57
+ $this->logger = new Logger( 'audit', [], [
58
+ new Processors\RequestMetaProcessor(),
59
+ new Processors\UserMetaProcessor(),
60
+ new Processors\WpMetaProcessor()
61
+ ] );
62
+ }
63
+ return $this->logger;
64
+ }
65
+
66
+ /**
67
+ * @param string $evt
68
+ * @param array $meta
69
+ * @param array $def
70
+ */
71
+ protected function captureEvent( string $evt, $meta = [], $def = [] ) {
72
+
73
+ $meta = apply_filters( 'shield/audit_event_meta', $meta, $evt );
74
+
75
+ if ( $def[ 'audit' ] && empty( $meta[ 'suppress_audit' ] ) ) {
76
+ $meta[ 'event_slug' ] = $evt;
77
+ $meta[ 'event_def' ] = $def;
78
+ // cater for where certain events may happen more than once in the same request
79
+ if ( !empty( $def[ 'audit_multiple' ] ) ) {
80
+ $this->auditLogs[] = $meta;
81
+ }
82
+ else {
83
+ $this->auditLogs[ $evt ] = $meta;
84
+ }
85
+ }
86
+ }
87
+
88
+ protected function onShutdown() {
89
+ if ( !$this->getCon()->plugin_deleting ) {
90
+ foreach ( $this->auditLogs as $auditLog ) {
91
+ $this->getLogger()->log(
92
+ $auditLog[ 'level' ] ?? $auditLog[ 'event_def' ][ 'level' ],
93
+ AuditMessageBuilder::Build( $auditLog[ 'event_slug' ], $auditLog[ 'audit_params' ] ?? [] ),
94
+ $auditLog
95
+ );
96
+ }
97
+ }
98
+ }
99
+ }
src/lib/src/Modules/AuditTrail/Lib/AuditMessageBuilder.php CHANGED
@@ -2,24 +2,31 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\AuditTrail\EntryVO;
6
 
7
  class AuditMessageBuilder {
8
 
9
- public static function Build( EntryVO $entry, array $msgStructure ) :string {
 
 
10
 
11
- $substitutions = $entry->meta;
12
- $rawString = implode( "\n", $msgStructure );
13
 
14
- // In-case we're working with an older audit message without as much data substitutions
15
- $missingCount = substr_count( $rawString, '%s' ) - count( $substitutions );
16
 
17
- if ( $missingCount > 0 ) {
18
- $substitutions = array_merge(
19
- $substitutions,
20
- array_fill( 0, $missingCount, '[data missing for older audit logs]' )
21
- );
22
  }
23
- return stripslashes( sanitize_textarea_field( vsprintf( $rawString, $substitutions ) ) );
 
 
 
 
 
 
 
 
24
  }
25
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\LogRecord;
6
 
7
  class AuditMessageBuilder {
8
 
9
+ public static function BuildFromLogRecord( LogRecord $log ) :array {
10
+ return explode( "\n", self::Build( $log->event_slug, $log->meta_data ?? [] ) );
11
+ }
12
 
13
+ public static function Build( string $event, array $substitutions = [] ) :string {
14
+ $srvEvents = shield_security_get_plugin()->getController()->loadEventsService();
15
 
16
+ $raw = implode( "\n", $srvEvents->getEventAuditStrings( $event ) );
 
17
 
18
+ $stringSubs = [];
19
+ foreach ( $substitutions as $subKey => $subValue ) {
20
+ $stringSubs[ sprintf( '{{%s}}', $subKey ) ] = $subValue;
 
 
21
  }
22
+
23
+ $log = preg_replace( '#{{[a-z_]+}}#i', 'missing data', strtr( $raw, $stringSubs ) );
24
+
25
+ $auditCount = (int)( $substitutions[ 'audit_count' ] ?? 1 );
26
+ if ( $srvEvents->getEventDef( $event )[ 'audit_countable' ] && $auditCount > 1 ) {
27
+ $log .= "\n".sprintf( __( 'This event repeated %s times in the last 24hrs.', 'wp-simple-firewall' ), $auditCount );
28
+ }
29
+
30
+ return $log;
31
  }
32
  }
src/lib/src/Modules/AuditTrail/Lib/AuditWriter.php CHANGED
@@ -7,6 +7,9 @@ use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base\HandlerConsumer;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\Ops\Commit;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Lib\EventsListener;
9
 
 
 
 
10
  class AuditWriter extends EventsListener {
11
 
12
  use HandlerConsumer;
@@ -30,7 +33,7 @@ class AuditWriter extends EventsListener {
30
  $entry = new AuditTrail\EntryVO();
31
  $entry->rid = $con->getShortRequestId();
32
  $entry->event = $evt;
33
- $entry->category = $def[ 'cat' ];
34
  $entry->context = $def[ 'context' ];
35
  $entry->meta = $meta[ 'audit' ] ?? [];
36
 
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\Ops\Commit;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Lib\EventsListener;
9
 
10
+ /**
11
+ * @deprecated 12.0
12
+ */
13
  class AuditWriter extends EventsListener {
14
 
15
  use HandlerConsumer;
33
  $entry = new AuditTrail\EntryVO();
34
  $entry->rid = $con->getShortRequestId();
35
  $entry->event = $evt;
36
+ $entry->category = 1;
37
  $entry->context = $def[ 'context' ];
38
  $entry->meta = $meta[ 'audit' ] ?? [];
39
 
src/lib/src/Modules/AuditTrail/Lib/LogHandlers/LocalDbWriter.php ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\LogHandlers;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Logs;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Meta;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\ModCon;
8
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs;
9
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
10
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\IPRecords;
11
+ use FernleafSystems\Wordpress\Services\Services;
12
+ use Monolog\Handler\AbstractProcessingHandler;
13
+
14
+ class LocalDbWriter extends AbstractProcessingHandler {
15
+
16
+ use ModConsumer;
17
+
18
+ /**
19
+ * @var array
20
+ */
21
+ private $log;
22
+
23
+ /**
24
+ * @inheritDoc
25
+ */
26
+ protected function write( array $record ) {
27
+ /** @var ModCon $mod */
28
+ $mod = $this->getMod();
29
+
30
+ $this->log = $record;
31
+
32
+ try {
33
+ if ( $record[ 'context' ][ 'event_def' ][ 'audit_countable' ] && $this->updateRecentLogEntry() ) {
34
+ return; // event is countable
35
+ }
36
+
37
+ $log = $this->createPrimaryLogRecord();
38
+
39
+ // anything stored in the primary log record doesn't need stored in meta
40
+ unset( $record[ 'extra' ][ 'meta_wp' ] );
41
+ unset( $record[ 'extra' ][ 'meta_wp' ] );
42
+ unset( $record[ 'extra' ][ 'meta_request' ] );
43
+
44
+ $metas = array_merge(
45
+ $record[ 'context' ][ 'audit_params' ] ?? [],
46
+ $record[ 'extra' ][ 'meta_user' ]
47
+ );
48
+ if ( $record[ 'context' ][ 'event_def' ][ 'audit_countable' ] ?? false ) {
49
+ $metas[ 'audit_count' ] = 1;
50
+ }
51
+
52
+ $metaRecord = new Meta\Ops\Record();
53
+ $metaRecord->log_ref = $log->id;
54
+ foreach ( $metas as $metaKey => $metaValue ) {
55
+ $metaRecord->meta_key = $metaKey;
56
+ $metaRecord->meta_value = $metaValue;
57
+ $mod->getDbH_Meta()
58
+ ->getQueryInserter()
59
+ ->insert( $metaRecord );
60
+ }
61
+ $this->triggerRequestLogger();
62
+ }
63
+ catch ( \Exception $e ) {
64
+ }
65
+ }
66
+
67
+ private function triggerRequestLogger() {
68
+ add_filter( 'shield/is_log_traffic', '__return_true', PHP_INT_MAX );
69
+ }
70
+
71
+ protected function updateRecentLogEntry() :bool {
72
+ /** @var ModCon $mod */
73
+ $mod = $this->getMod();
74
+ $modData = $this->getCon()->getModule_Data();
75
+
76
+ $ipRecordID = ( new IPRecords() )
77
+ ->setMod( $modData )
78
+ ->loadIP( $this->log[ 'extra' ][ 'meta_request' ][ 'ip' ] )
79
+ ->id;
80
+ /** @var ReqLogs\Ops\Select $reqSelector */
81
+ $reqSelector = $modData->getDbH_ReqLogs()->getQuerySelector();
82
+ $reqIDs = array_map( function ( $rawRecord ) {
83
+ return $rawRecord->id;
84
+ }, (array)$reqSelector->filterByIP( $ipRecordID )
85
+ ->setColumnsToSelect( [ 'id' ] )
86
+ ->queryWithResult() );
87
+
88
+ /** @var Logs\Ops\Select $select */
89
+ $select = $mod->getDbH_Logs()->getQuerySelector();
90
+ /** @var Logs\Ops\Record $existingLog */
91
+ $existingLog = $select->filterByEvent( $this->log[ 'context' ][ 'event_slug' ] )
92
+ ->filterByRequestRefs( $reqIDs )
93
+ ->filterByCreatedAt( Services::Request()->carbon()->subDay()->timestamp, '>' )
94
+ ->setOrderBy( 'updated_at', 'DESC', true )
95
+ ->setOrderBy( 'created_at', 'DESC' )
96
+ ->first();
97
+
98
+ if ( !empty( $existingLog ) ) {
99
+ Services::WpDb()->doSql(
100
+ sprintf( "UPDATE `%s` SET `meta_value` = `meta_value`+1
101
+ WHERE `log_ref`=%s
102
+ AND `meta_key`='audit_count'
103
+ ", $mod->getDbH_Meta()->getTableSchema()->table, $existingLog->id )
104
+ );
105
+ // this can fail under load, but doesn't actually matter:
106
+ $mod->getDbH_Logs()
107
+ ->getQueryUpdater()
108
+ ->updateById( $existingLog->id, [ 'updated_at' => Services::Request()->ts() ] );
109
+ }
110
+ return !empty( $existingLog );
111
+ }
112
+
113
+ /**
114
+ * @return Logs\Ops\Record
115
+ * @throws \Exception
116
+ */
117
+ protected function createPrimaryLogRecord() :Logs\Ops\Record {
118
+ /** @var ModCon $mod */
119
+ $mod = $this->getMod();
120
+
121
+ $record = new Logs\Ops\Record();
122
+ $record->event_slug = $this->log[ 'context' ][ 'event_slug' ];
123
+ $record->site_id = $this->log[ 'extra' ][ 'meta_wp' ][ 'site_id' ];
124
+
125
+ $ipRecordID = ( new IPRecords() )
126
+ ->setMod( $this->getCon()->getModule_Data() )
127
+ ->loadIP( $this->log[ 'extra' ][ 'meta_request' ][ 'ip' ] )
128
+ ->id;
129
+ $record->req_ref = ( new ReqLogs\RequestRecords() )
130
+ ->setMod( $this->getCon()->getModule_Data() )
131
+ ->loadReq( $this->log[ 'extra' ][ 'meta_request' ][ 'rid' ], $ipRecordID )
132
+ ->id;
133
+
134
+ $success = $mod->getDbH_Logs()
135
+ ->getQueryInserter()
136
+ ->insert( $record );
137
+ if ( !$success ) {
138
+ throw new \Exception( 'Failed to insert' );
139
+ }
140
+
141
+ /** @var Logs\Ops\Record $log */
142
+ $log = $mod->getDbH_Logs()
143
+ ->getQuerySelector()
144
+ ->byId( Services::WpDb()->getVar( 'SELECT LAST_INSERT_ID()' ) );
145
+ if ( empty( $log ) ) {
146
+ throw new \Exception( 'Could not load log record' );
147
+ }
148
+ return $log;
149
+ }
150
+ }
src/lib/src/Modules/AuditTrail/Lib/LogHandlers/LogFileHandler.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\LogHandlers;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\{
6
+ ModCon,
7
+ Options
8
+ };
9
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
10
+ use Monolog\Handler\StreamHandler;
11
+ use Monolog\Logger;
12
+
13
+ class LogFileHandler extends StreamHandler {
14
+
15
+ use ModConsumer;
16
+
17
+ public function __construct( ModCon $modCon, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false ) {
18
+ $this->setMod( $modCon );
19
+
20
+ parent::__construct( $this->getLogFilePath(), $level, $bubble, $filePermission, $useLocking );
21
+
22
+ $this->rotateLogs();
23
+ }
24
+
25
+ public function getLogFilePath() :string {
26
+ /** @var Options $opts */
27
+ $opts = $this->getOptions();
28
+ return $opts->getLogFilePath();
29
+ }
30
+
31
+ public function getLogFileRotationLimit() :int {
32
+ /** @var Options $opts */
33
+ $opts = $this->getOptions();
34
+ return $opts->getLogFileRotationLimit();
35
+ }
36
+
37
+ private function rotateLogs() {
38
+ if ( apply_filters( 'shield/audit_trail_rotate_log_files', true ) ) {
39
+ try {
40
+ ( new Utility\LogFileRotate( $this->getLogFilePath(), $this->getLogFileRotationLimit() ) )->run();
41
+ }
42
+ catch ( \Exception $e ) {
43
+ }
44
+ }
45
+ }
46
+ }
src/lib/src/Modules/AuditTrail/Lib/LogHandlers/Utility/LogFileDirCreate.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\LogHandlers\Utility;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+
8
+ class LogFileDirCreate {
9
+
10
+ use ModConsumer;
11
+
12
+ public function run() :string {
13
+ $FS = Services::WpFs();
14
+ $baseDir = $this->getCon()->getPluginCachePath();
15
+ if ( empty( $baseDir ) ) {
16
+ throw new \Exception( "Plugin TMP Dir is unavailable." );
17
+ }
18
+
19
+ $theLogsDir = null;
20
+ foreach ( $FS->getAllFilesInDir( $baseDir, true ) as $possibleDir ) {
21
+ $possibleFullPath = path_join( $baseDir, $possibleDir );
22
+ if ( strpos( basename( $possibleDir ), 'logs-' ) === 0 && $FS->isDir( $possibleDir ) ) {
23
+ $theLogsDir = $possibleFullPath;
24
+ break;
25
+ }
26
+ }
27
+
28
+ if ( empty( $theLogsDir ) ) {
29
+ $theLogsDir = path_join( $baseDir, str_replace( '.', '', uniqid( 'logs-', true ) ) );
30
+ $FS->mkdir( $theLogsDir );
31
+ }
32
+
33
+ if ( !$FS->isDir( $theLogsDir ) ) {
34
+ throw new \Exception( "Couldn't create the logs dir." );
35
+ }
36
+
37
+ return $theLogsDir;
38
+ }
39
+ }
src/lib/src/Modules/AuditTrail/Lib/LogHandlers/Utility/LogFileRotate.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\LogHandlers\Utility;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+
8
+ class LogFileRotate extends ExecOnceModConsumer {
9
+
10
+ private $logFile;
11
+
12
+ /**
13
+ * @var int
14
+ */
15
+ private $limit;
16
+
17
+ public function __construct( string $logFile, int $limit = 5 ) {
18
+ $this->logFile = $logFile;
19
+ $this->limit = $limit;
20
+ }
21
+
22
+ /**
23
+ * @throws \Exception
24
+ */
25
+ public function run() {
26
+ $FS = Services::WpFs();
27
+ if ( !$FS->mkdir( dirname( $this->logFile ) ) ) {
28
+ throw new \Exception( 'Could not create logs dir' );
29
+ }
30
+
31
+ $startOfDay = Services::Request()->carbon( true )->startOfDay()->timestamp;
32
+
33
+ if ( $FS->isFile( $this->logFile )
34
+ && $FS->getModifiedTime( $this->logFile ) < $startOfDay ) {
35
+ $this->rotateLogs( (int)apply_filters( 'shield/file_log_rotation_limit', 5 ) );
36
+ }
37
+ }
38
+
39
+ protected function rotateLogs() {
40
+ $FS = Services::WpFs();
41
+ $limit = (int)max( 1, $this->limit );
42
+
43
+ $basePath = $this->logFile;
44
+ for ( $i = $limit ; $i >= 0 ; $i-- ) {
45
+
46
+ $suffix = $i === 0 ? '' : '.'.$i;
47
+ $fileToRotate = $basePath.$suffix;
48
+
49
+ if ( $FS->isFile( $fileToRotate ) ) {
50
+ $FS->move( $fileToRotate, $basePath.'.'.( $i + 1 ) );
51
+ }
52
+ }
53
+
54
+ $excessFile = $basePath.'.'.( $limit + 1 );
55
+ if ( $FS->isFile( $excessFile ) ) {
56
+ $FS->deleteFile( $excessFile );
57
+ }
58
+ }
59
+ }
src/lib/src/Modules/AuditTrail/Lib/LogTable/DelegateAjaxHandler.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\LogTable;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+
8
+ class DelegateAjaxHandler {
9
+
10
+ use Shield\Modules\ModConsumer;
11
+
12
+ /**
13
+ * @return array
14
+ * @throws \Exception
15
+ */
16
+ public function processAjaxAction() :array {
17
+ $action = Services::Request()->post( 'sub_action' );
18
+ switch ( $action ) {
19
+
20
+ case 'retrieve_table_data':
21
+ $response = $this->retrieveTableData();
22
+ break;
23
+
24
+ case 'get_request_meta':
25
+ $response = $this->getRequestMeta();
26
+ break;
27
+
28
+ default:
29
+ throw new \Exception( 'Not a supported Audit Trail table sub_action: '.$action );
30
+ }
31
+ return $response;
32
+ }
33
+
34
+ /**
35
+ * @return array
36
+ * @throws \Exception
37
+ */
38
+ private function retrieveTableData() :array {
39
+ return [
40
+ 'success' => true,
41
+ 'vars' => [
42
+ 'data' => ( new LoadRawTableData() )
43
+ ->setMod( $this->getMod() )
44
+ ->loadForLogs()
45
+ ],
46
+ ];
47
+ }
48
+
49
+ /**
50
+ * @return array
51
+ * @throws \Exception
52
+ */
53
+ private function getRequestMeta() :array {
54
+ return [
55
+ 'success' => true,
56
+ 'html' => ( new Shield\Modules\Data\DB\ReqLogs\GetRequestMeta() )
57
+ ->setMod( $this->getCon()->getModule_Data() )
58
+ ->retrieve( Services::Request()->post( 'rid' ) )
59
+ ];
60
+ }
61
+ }
src/lib/src/Modules/AuditTrail/Lib/LogTable/LoadRawTableData.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\LogTable;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\LoadLogs;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\LogRecord;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\AuditMessageBuilder;
8
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\Ops\ConvertLegacy;
9
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
10
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
11
+ use FernleafSystems\Wordpress\Services\Services;
12
+
13
+ class LoadRawTableData {
14
+
15
+ use ModConsumer;
16
+
17
+ /**
18
+ * @var LogRecord
19
+ */
20
+ private $log;
21
+
22
+ public function loadForLogs() :array {
23
+ ( new Traffic\Lib\Ops\ConvertLegacy() )
24
+ ->setMod( $this->getCon()->getModule_Traffic() )
25
+ ->run();
26
+ ( new ConvertLegacy() )
27
+ ->setMod( $this->getMod() )
28
+ ->run();
29
+
30
+ $srvEvents = $this->getCon()->loadEventsService();
31
+ return array_values( array_map(
32
+ function ( $log ) use ( $srvEvents ) {
33
+ $this->log = $log;
34
+
35
+ $data = $log->getRawData();
36
+
37
+ $data[ 'ip' ] = $this->log->ip;
38
+ $data[ 'rid' ] = $this->log->rid ?? __( 'Unknown', 'wp-simple-firewall' );
39
+ $data[ 'ip_linked' ] = $this->getColumnContent_RequestDetails();
40
+ $data[ 'event' ] = $srvEvents->getEventName( $log->event_slug );
41
+ $data[ 'created_since' ] = $this->getColumnContent_Date();
42
+ $data[ 'message' ] = $this->getColumnContent_Message();
43
+ $data[ 'user' ] = $this->getColumnContent_User();
44
+ $data[ 'user_id' ] = $this->getColumnContent_UserID();
45
+ $data[ 'level' ] = $this->getColumnContent_Level();
46
+ $data[ 'severity' ] = $this->getColumnContent_SeverityIcon();
47
+ $data[ 'meta' ] = $this->getColumnContent_Meta();
48
+ return $data;
49
+ },
50
+ $this->getLogRecords()
51
+ ) );
52
+ }
53
+
54
+ /**
55
+ * @return LogRecord[]
56
+ */
57
+ private function getLogRecords() :array {
58
+ return array_filter(
59
+ ( new LoadLogs() )
60
+ ->setMod( $this->getCon()->getModule_AuditTrail() )
61
+ ->run(),
62
+ function ( $logRecord ) {
63
+ return $this->getCon()->loadEventsService()->eventExists( $logRecord->event_slug );
64
+ }
65
+ );
66
+ }
67
+
68
+ private function getColumnContent_RequestDetails() :string {
69
+ return sprintf( '<h6><a href="%s" target="_blank">%s</a></h6>',
70
+ $this->getCon()->getModule_Insights()->getUrl_IpAnalysis( $this->log->ip ),
71
+ $this->log->ip
72
+ );
73
+ }
74
+
75
+ private function getColumnContent_Date() :string {
76
+ return sprintf( '%s<br /><small>%s</small>',
77
+ Services::Request()
78
+ ->carbon( true )
79
+ ->setTimestamp( $this->log->created_at )
80
+ ->diffForHumans(),
81
+ Services::WpGeneral()->getTimeStringForDisplay( $this->log->created_at )
82
+ );
83
+ }
84
+
85
+ private function getColumnContent_UserID() :string {
86
+ return $this->log->meta_data[ 'uid' ] ?? '-';
87
+ }
88
+
89
+ private function getColumnContent_User() :string {
90
+ $content = '-';
91
+ $uid = $this->log->meta_data[ 'uid' ] ?? '';
92
+ if ( !empty( $uid ) ) {
93
+ if ( is_numeric( $uid ) ) {
94
+ $user = Services::WpUsers()->getUserById( $uid );
95
+ if ( !empty( $user ) ) {
96
+ $content = sprintf( '<a href="%s" target="_blank">%s</a>',
97
+ Services::WpUsers()->getAdminUrl_ProfileEdit( $user ),
98
+ $user->user_login );
99
+ }
100
+ else {
101
+ $content = sprintf( 'Unavailable (ID:%s)', $uid );
102
+ }
103
+ }
104
+ else {
105
+ $content = $uid === 'cron' ? 'WP Cron' : 'WP-CLI';
106
+ }
107
+ }
108
+ return $content;
109
+ }
110
+
111
+ private function getColumnContent_Message() :string {
112
+ $msg = AuditMessageBuilder::BuildFromLogRecord( $this->log );
113
+ return sprintf( '<span class="message-header">%s</span><textarea readonly rows="%s">%s</textarea>',
114
+ $this->getCon()->loadEventsService()->getEventName( $this->log->event_slug ),
115
+ count( $msg ) + 1, sanitize_textarea_field( implode( "\n", $msg ) ) );
116
+ }
117
+
118
+ private function getColumnContent_Meta() :string {
119
+ return sprintf(
120
+ '<button type="button" class="btn btn-link" '.
121
+ 'data-toggle="popover" data-placement="left" '.
122
+ 'data-customClass="audit-meta" '.
123
+ 'data-rid="%s">%s</button>', $this->log->rid,
124
+ sprintf( '<span class="meta-icon">%s</span>',
125
+ $this->getCon()->svgs->raw( 'bootstrap/tags.svg' )
126
+ )
127
+ );
128
+ }
129
+
130
+ private function getColumnContent_Level() :string {
131
+ return $this->getCon()->loadEventsService()->getEventDef( $this->log->event_slug )[ 'level' ];
132
+ }
133
+
134
+ private function getColumnContent_SeverityIcon() :string {
135
+ $level = $this->getColumnContent_Level();
136
+ $levelDetails = [
137
+ 'alert' => [
138
+ 'icon' => 'x-octagon',
139
+ ],
140
+ 'warning' => [
141
+ 'icon' => 'exclamation-triangle',
142
+ ],
143
+ 'notice' => [
144
+ 'icon' => 'info-square',
145
+ ],
146
+ 'info' => [
147
+ 'icon' => 'info-circle',
148
+ ],
149
+ 'debug' => [
150
+ 'icon' => 'question-diamond',
151
+ ],
152
+ ][ $level ];
153
+ return sprintf( '<span class="severity-%s severity-icon">%s</span>', $level,
154
+ $this->getCon()->svgs->raw( sprintf( 'bootstrap/%s.svg', $levelDetails[ 'icon' ] ) )
155
+ );
156
+ }
157
+ }
src/lib/src/Modules/AuditTrail/Lib/Ops/ConvertLegacy.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\AuditTrail;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\ModCon;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\IPRecords;
8
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs\RequestRecords;
9
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\{
10
+ Logs,
11
+ Meta
12
+ };
13
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
14
+ use FernleafSystems\Wordpress\Services\Services;
15
+
16
+ class ConvertLegacy {
17
+
18
+ use ModConsumer;
19
+
20
+ public function run() {
21
+ /** @var ModCon $mod */
22
+ $mod = $this->getMod();
23
+ $opts = $this->getOptions();
24
+
25
+ if ( empty( $opts->getOpt( 'legacy_db_deleted_at' ) ) ) {
26
+ $this->convert();
27
+ $dbh = $mod->getDbHandler_AuditTrail();
28
+ if ( $dbh->getQuerySelector()->count() === 0 ) {
29
+ $opts->setOpt( 'legacy_db_deleted_at', Services::Request()->ts() );
30
+ $dbh->tableDelete();
31
+ }
32
+ }
33
+ }
34
+
35
+ private function convert() {
36
+ /** @var ModCon $mod */
37
+ $mod = $this->getMod();
38
+ $dbh = $mod->getDbHandler_AuditTrail();
39
+
40
+ $metaInserter = $mod->getDbH_Meta()->getQueryInserter();
41
+
42
+ $toDelete = [];
43
+
44
+ /** @var AuditTrail\EntryVO $entry */
45
+ foreach ( $dbh->getIterator() as $entry ) {
46
+
47
+ try {
48
+ $log = $this->createPrimaryLogRecord( $entry );
49
+
50
+ $metaRecord = new Meta\Ops\Record();
51
+ $metaRecord->log_ref = $log->id;
52
+
53
+ $uid = '';
54
+ if ( $entry->wp_username === 'WP Cron' ) {
55
+ $uid = 'cron';
56
+ }
57
+ elseif ( $entry->wp_username === 'WP CLI' ) {
58
+ $uid = 'cli';
59
+ }
60
+ elseif ( $entry->wp_username !== '-' ) {
61
+ $user = Services::WpUsers()->getUserByUsername( $entry->wp_username );
62
+ if ( $user instanceof \WP_User ) {
63
+ $uid = $user->ID;
64
+ }
65
+ }
66
+
67
+ if ( !empty( $uid ) ) {
68
+ $metaRecord->meta_key = 'uid';
69
+ $metaRecord->meta_value = $uid;
70
+ $metaInserter->insert( $metaRecord );
71
+ }
72
+
73
+ foreach ( $entry->meta as $metaKey => $metaValue ) {
74
+ if ( $metaKey == 'user' ) {
75
+ $metaKey = 'user_login';
76
+ }
77
+ $metaRecord->meta_key = $metaKey;
78
+ $metaRecord->meta_value = $metaValue;
79
+ $mod->getDbH_Meta()
80
+ ->getQueryInserter()
81
+ ->insert( $metaRecord );
82
+ }
83
+ }
84
+ catch ( \Exception $e ) {
85
+ }
86
+ finally {
87
+ $toDelete[] = $entry->id;
88
+ }
89
+ }
90
+
91
+ if ( !empty( $toDelete ) ) {
92
+ $dbh->getQueryDeleter()
93
+ ->addWhereIn( 'in', $toDelete )
94
+ ->query();
95
+ }
96
+ // TODO: set hidden marker to say completed and delete table
97
+ }
98
+
99
+ /**
100
+ * @param AuditTrail\EntryVO $entry
101
+ * @return Logs\Ops\Record
102
+ * @throws \Exception
103
+ */
104
+ protected function createPrimaryLogRecord( AuditTrail\EntryVO $entry ) :Logs\Ops\Record {
105
+ /** @var ModCon $mod */
106
+ $mod = $this->getMod();
107
+
108
+ if ( empty( $entry->rid ) || empty( $entry->ip ) ) {
109
+ throw new \Exception( 'No RID or IP' );
110
+ }
111
+
112
+ if ( !$this->getCon()->loadEventsService()->eventExists( (string)$entry->event ) ) {
113
+ throw new \Exception( 'Not a supported event' );
114
+ }
115
+
116
+ $record = new Logs\Ops\Record();
117
+ $record->event_slug = $entry->event;
118
+ $record->site_id = 1;
119
+ $record->created_at = $entry->created_at;
120
+
121
+ $ipID = ( new IPRecords() )
122
+ ->setMod( $this->getCon()->getModule_Data() )
123
+ ->loadIP( $entry->ip )
124
+ ->id;
125
+ $record->req_ref = ( new RequestRecords() )
126
+ ->setMod( $this->getCon()->getModule_Data() )
127
+ ->loadReq( $entry->rid, $ipID )
128
+ ->id;
129
+
130
+ $success = $mod->getDbH_Logs()
131
+ ->getQueryInserter()
132
+ ->insert( $record );
133
+ if ( !$success ) {
134
+ throw new \Exception( 'Failed to insert' );
135
+ }
136
+
137
+ /** @var Logs\Ops\Record $log */
138
+ $log = $mod->getDbH_Logs()
139
+ ->getQuerySelector()
140
+ ->byId( Services::WpDb()->getVar( 'SELECT LAST_INSERT_ID()' ) );
141
+ if ( empty( $log ) ) {
142
+ throw new \Exception( 'Could not load log record' );
143
+ }
144
+ return $log;
145
+ }
146
+ }
src/lib/src/Modules/AuditTrail/Lib/Utility/AutoWhitelistParamFromAuditEntry.php DELETED
@@ -1,55 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\Utility;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\AuditTrail\EntryVO;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\AuditTrail\Select;
8
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\ModCon;
9
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
10
-
11
- class AutoWhitelistParamFromAuditEntry {
12
-
13
- use ModConsumer;
14
-
15
- /**
16
- * @param int $entryID
17
- * @return string
18
- * @throws \Exception
19
- */
20
- public function run( int $entryID ) :string {
21
- /** @var ModCon $mod */
22
- $mod = $this->getMod();
23
-
24
- /** @var Select $selector */
25
- $selector = $mod->getDbHandler_AuditTrail()->getQuerySelector();
26
-
27
- /** @var EntryVO $entry */
28
- $entry = $selector->byId( $entryID );
29
- if ( !$entry instanceof EntryVO ) {
30
- throw new \Exception( __( 'Audit entry could not be loaded.', 'wp-simple-firewall' ) );
31
- }
32
-
33
- $uri = '';
34
- foreach ( $selector->filterByRequestID( (int)$entry->rid )->all() as $entry ) {
35
- $param = $this->extractParameter( $entry );
36
- if ( !empty( $param ) ) {
37
- $uri = $entry->meta[ 'uri' ] ?? '*';
38
- break;
39
- }
40
- }
41
-
42
- if ( empty( $param ) ) {
43
- throw new \Exception( __( 'Parameter associated with this audit entry could not be found.', 'wp-simple-firewall' ) );
44
- }
45
-
46
- /** @var Shield\Modules\Firewall\ModCon $modFW */
47
- $modFW = $this->getCon()->modules[ 'firewall' ];
48
- $modFW->addParamToWhitelist( $param, $uri );
49
- return sprintf( __( 'Parameter "%s" whitelisted successfully', 'wp-simple-firewall' ), $param );
50
- }
51
-
52
- private function extractParameter( EntryVO $entry ) :string {
53
- return $entry->meta[ 'param' ] ?? '';
54
- }
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/AuditTrail/Lib/Utility/GetLogFileContent.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\Utility;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Options;
8
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
9
+
10
+ class GetLogFileContent {
11
+
12
+ use ModConsumer;
13
+
14
+ public function run() :string {
15
+ /** @var Options $opts */
16
+ $opts = $this->getOptions();
17
+ $logFile = $opts->getLogFilePath();
18
+ $FS = Services::WpFs();
19
+ return $FS->isFile( $logFile ) ? (string)$FS->getFileContent( $logFile ) : '';
20
+ }
21
+ }
src/lib/src/Modules/AuditTrail/ModCon.php CHANGED
@@ -9,31 +9,140 @@ use FernleafSystems\Wordpress\Services\Services;
9
 
10
  class ModCon extends BaseShield\ModCon {
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  public function getDbHandler_AuditTrail() :Shield\Databases\AuditTrail\Handler {
13
  return $this->getDbH( 'audit_trail' );
14
  }
15
 
 
 
 
 
 
 
 
16
  protected function handleFileDownload( string $downloadID ) {
17
  switch ( $downloadID ) {
18
- case 'db_audit':
19
- ( new DbTableExport() )
20
- ->setDbHandler( $this->getDbHandler_AuditTrail() )
21
- ->toCSV();
 
 
 
22
  break;
23
  }
24
  }
25
 
26
  /**
27
- * @return bool
28
  * @throws \Exception
29
  */
30
  protected function isReadyToExecute() :bool {
31
- return $this->getDbHandler_AuditTrail()->isReady() && parent::isReadyToExecute();
32
  }
33
 
34
  /**
 
 
 
 
35
  * @return array
36
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  public function getAllContexts() {
38
  return [
39
  'all' => 'All', //special
@@ -48,69 +157,16 @@ class ModCon extends BaseShield\ModCon {
48
  }
49
 
50
  /**
51
- * See plugin controller for the nature of $aData wpPrivacyExport()
52
- *
53
- * @param array $aExportItems
54
- * @param string $sEmail
55
- * @param int $nPage
56
- * @return array
57
  */
58
- public function onWpPrivacyExport( $aExportItems, $sEmail, $nPage = 1 ) {
59
-
60
- $oUser = Services::WpUsers()->getUserByEmail( $sEmail );
61
-
62
- $aExportItem = [
63
- 'group_id' => $this->prefix(),
64
- 'group_label' => sprintf( __( '[%s] Audit Trail Entries', 'wp-simple-firewall' ), $this->getCon()
65
- ->getHumanName() ),
66
- 'item_id' => $this->prefix( 'audit-trail' ),
67
- 'data' => [],
68
- ];
69
-
70
- try {
71
- /** @var Shield\Databases\AuditTrail\Select $oFinder */
72
- $oFinder = $this->getDbHandler_AuditTrail()->getQuerySelector();
73
- $oFinder->filterByUsername( $oUser->user_login );
74
-
75
- $WP = Services::WpGeneral();
76
- /** @var Shield\Databases\AuditTrail\EntryVO $entry */
77
- foreach ( $oFinder->query() as $entry ) {
78
- $aExportItem[ 'data' ][] = [
79
- $sTimeStamp = $WP->getTimeStringForDisplay( $entry->getCreatedAt() ),
80
- 'name' => sprintf( '[%s] Audit Trail Entry', $sTimeStamp ),
81
- 'value' => sprintf( '[IP:%s] %s', $entry->ip, $entry->message )
82
- ];
83
- }
84
-
85
- if ( !empty( $aExportItem[ 'data' ] ) ) {
86
- $aExportItems[] = $aExportItem;
87
- }
88
- }
89
- catch ( \Exception $e ) {
90
- }
91
-
92
- return $aExportItems;
93
  }
94
 
95
  /**
96
- * See plugin controller for the nature of $aData wpPrivacyErase()
97
- *
98
- * @param array $aData
99
- * @param string $sEmail
100
- * @param int $nPage
101
- * @return array
102
  */
103
- public function onWpPrivacyErase( $aData, $sEmail, $nPage = 1 ) {
104
- try {
105
- $oThisUsername = Services::WpUsers()->getUserByEmail( $sEmail )->user_login;
106
- $this->getDbHandler_AuditTrail()
107
- ->getQueryDeleter()
108
- ->addWhereSearch( 'wp_username', $oThisUsername )
109
- ->all();
110
- $aData[ 'messages' ][] = sprintf( '%s Audit Entries deleted', $this->getCon()->getHumanName() );
111
- }
112
- catch ( \Exception $e ) {
113
- }
114
- return $aData;
115
  }
116
  }
9
 
10
  class ModCon extends BaseShield\ModCon {
11
 
12
+ /**
13
+ * @var Lib\AuditLogger
14
+ */
15
+ private $auditLogger;
16
+
17
+ public function getDbH_Logs() :DB\Logs\Ops\Handler {
18
+ $this->getCon()->getModule_Data()->getDbH_ReqLogs();
19
+ return $this->getDbHandler()->loadDbH( 'at_logs' );
20
+ }
21
+
22
+ public function getDbH_Meta() :DB\Meta\Ops\Handler {
23
+ $this->getDbH_Logs();
24
+ return $this->getDbHandler()->loadDbH( 'at_meta' );
25
+ }
26
+
27
+ /**
28
+ * @deprecated 12.0
29
+ */
30
  public function getDbHandler_AuditTrail() :Shield\Databases\AuditTrail\Handler {
31
  return $this->getDbH( 'audit_trail' );
32
  }
33
 
34
+ public function getAuditLogger() :Lib\AuditLogger {
35
+ if ( !isset( $this->auditLogger ) ) {
36
+ $this->auditLogger = new Lib\AuditLogger( $this->getCon() );
37
+ }
38
+ return $this->auditLogger;
39
+ }
40
+
41
  protected function handleFileDownload( string $downloadID ) {
42
  switch ( $downloadID ) {
43
+ case 'db_log':
44
+ Services::Response()->downloadStringAsFile(
45
+ ( new Lib\Utility\GetLogFileContent() )
46
+ ->setMod( $this )
47
+ ->run(),
48
+ sprintf( 'log_file-%s.json', date( 'Ymd_His' ) )
49
+ );
50
  break;
51
  }
52
  }
53
 
54
  /**
 
55
  * @throws \Exception
56
  */
57
  protected function isReadyToExecute() :bool {
58
+ return $this->getDbH_Logs()->isReady() && parent::isReadyToExecute();
59
  }
60
 
61
  /**
62
+ * TODO: This requires some fairly convoluted SQL to pick out records for a specific user in an efficient manner
63
+ * @param array $exportItems
64
+ * @param string $email
65
+ * @param int $page
66
  * @return array
67
  */
68
+ public function onWpPrivacyExport( $exportItems, $email, $page = 1 ) :array {
69
+
70
+ $user = Services::WpUsers()->getUserByEmail( $email );
71
+ if ( !empty( $user ) ) {
72
+
73
+ $WP = Services::WpGeneral();
74
+ $exportData = array_map(
75
+ function ( $log ) use ( $WP ) {
76
+ return [
77
+ 'name' => sprintf( '%s', $WP->getTimeStringForDisplay( $log[ 'created_at' ] ) ),
78
+ 'value' => sprintf( '[IP:%s] %s', $log[ 'ip' ], $log[ 'message' ] )
79
+ ];
80
+ },
81
+ array_filter( // Get all logs entries pertaining to this user:
82
+ ( new Shield\Modules\AuditTrail\Lib\LogTable\LoadRawTableData() )
83
+ ->setMod( $this )
84
+ ->loadForLogs(),
85
+ function ( $log ) use ( $user ) {
86
+ $keep = $log[ 'user_id' ] === $user->ID;
87
+ if ( !$keep ) {
88
+ $userParts = array_map( 'preg_quote', [ $user->user_login, $user->user_email ] );
89
+ $keep = preg_match( sprintf( '/(%s)/i', implode( '|', $userParts ) ), $log[ 'message' ] ) > 0;
90
+ }
91
+ return $keep;
92
+ }
93
+ )
94
+ );
95
+
96
+ if ( !empty( $exportData ) ) {
97
+ $exportItems[] = [
98
+ 'group_id' => $this->getModSlug(),
99
+ 'group_label' => sprintf( __( '[%s] Audit Trail Entries', 'wp-simple-firewall' ),
100
+ $this->getCon()->getHumanName() ),
101
+ 'group_description' => sprintf( __( '[%s] Audit Trail Entries referencing the given user.', 'wp-simple-firewall' ),
102
+ $this->getCon()->getHumanName() ),
103
+ 'item_id' => $this->prefix( 'audit-trail' ),
104
+ 'data' => $exportData,
105
+ ];
106
+ }
107
+ }
108
+
109
+ return is_array( $exportItems ) ? $exportItems : [];
110
+ }
111
+
112
+ /**
113
+ * See plugin controller for the nature of $aData wpPrivacyErase()
114
+ *
115
+ * @param array $data
116
+ * @param string $email
117
+ * @param int $page
118
+ * @return array
119
+ */
120
+ public function onWpPrivacyErase( $data, $email, $page = 1 ) {
121
+ try {
122
+ $user = Services::WpUsers()->getUserByEmail( $email );
123
+ if ( !empty( $user ) ) {
124
+ $deleter = $this->getDbH_Meta()->getQueryDeleter();
125
+ $deleter->addWhereEquals( 'meta_key', 'uid' )
126
+ ->addWhereEquals( 'meta_data', $user->ID )
127
+ ->query();
128
+ $deleter->addWhereEquals( 'meta_key', 'user_login' )
129
+ ->addWhereEquals( 'meta_data', $user->user_login )
130
+ ->query();
131
+ $deleter->addWhereEquals( 'meta_key', 'email' )
132
+ ->addWhereEquals( 'meta_data', $user->user_email )
133
+ ->query();
134
+ $data[ 'messages' ][] = sprintf( '%s Audit Entries deleted', $this->getCon()->getHumanName() );
135
+ }
136
+ }
137
+ catch ( \Exception $e ) {
138
+ }
139
+ return $data;
140
+ }
141
+
142
+ /**
143
+ * @return array
144
+ * @deprecated 12.0 (Shield Central?)
145
+ */
146
  public function getAllContexts() {
147
  return [
148
  'all' => 'All', //special
157
  }
158
 
159
  /**
160
+ * @inheritDoc
161
+ * @deprecated 12.0
 
 
 
 
162
  */
163
+ public function getDbHandlers( $bInitAll = false ) {
164
+ return [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  }
166
 
167
  /**
168
+ * @deprecated 12.0
 
 
 
 
 
169
  */
170
+ protected function cleanupDatabases() {
 
 
 
 
 
 
 
 
 
 
 
171
  }
172
  }
src/lib/src/Modules/AuditTrail/Options.php CHANGED
@@ -2,17 +2,78 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
6
 
7
  class Options extends BaseShield\Options {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  public function getAutoCleanDays() :int {
 
 
 
 
10
  return (int)$this->getOpt( 'audit_trail_auto_clean' );
11
  }
12
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  public function getMaxEntries() :int {
14
- return $this->isPremium() ?
15
- (int)$this->getOpt( 'audit_trail_max_entries' ) :
16
- (int)$this->getDef( 'audit_trail_free_max_entries' );
17
  }
18
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\LogHandlers\Utility\LogFileDirCreate;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
7
 
8
  class Options extends BaseShield\Options {
9
 
10
+ public function getLogFilePath() :string {
11
+ try {
12
+ $dir = ( new LogFileDirCreate() )
13
+ ->setMod( $this->getMod() )
14
+ ->run();
15
+ }
16
+ catch ( \Exception $e ) {
17
+ $dir = '';
18
+ }
19
+
20
+ $path = empty( $dir ) ? '' : path_join( $dir, 'shield.log' );
21
+ return apply_filters( 'shield/audit_trail_log_file_path', $path );
22
+ }
23
+
24
+ public function getLogFileRotationLimit() :int {
25
+ return (int)apply_filters( 'shield/audit_trail_log_file_rotation_limit', 5 );
26
+ }
27
+
28
+ public function getLogLevelsDB() :array {
29
+ $levels = $this->getOpt( 'log_level_db', [] );
30
+ if ( empty( $levels ) || !is_array( $levels ) ) {
31
+ $this->resetOptToDefault( 'log_level_db' );
32
+ }
33
+ elseif ( count( $levels ) > 1 && in_array( 'disabled', $levels ) ) {
34
+ $this->setOpt( 'log_level_db', [ 'disabled' ] );
35
+ }
36
+ return $this->getOpt( 'log_level_db', [] );
37
+ }
38
+
39
+ public function getLogLevelsFile() :array {
40
+ $levels = $this->getOpt( 'log_level_file', [] );
41
+ if ( empty( $levels ) || !is_array( $levels ) ) {
42
+ $this->resetOptToDefault( 'log_level_file' );
43
+ }
44
+ elseif ( count( $levels ) > 1 ) {
45
+ if ( in_array( 'disabled', $levels ) ) {
46
+ $this->setOpt( 'log_level_file', [ 'disabled' ] );
47
+ }
48
+ elseif ( in_array( 'same_as_db', $levels ) ) {
49
+ $this->setOpt( 'log_level_file', [ 'same_as_db' ] );
50
+ }
51
+ }
52
+ $levels = $this->getOpt( 'log_level_file', [] );
53
+ return in_array( 'same_as_db', $levels ) ? $this->getLogLevelsDB() : $levels;
54
+ }
55
+
56
  public function getAutoCleanDays() :int {
57
+ $days = $this->getOpt( 'audit_trail_auto_clean' );
58
+ if ( !$this->isPremium() ) {
59
+ $this->setOpt( 'audit_trail_auto_clean', min( $days, 7 ) );
60
+ }
61
  return (int)$this->getOpt( 'audit_trail_auto_clean' );
62
  }
63
 
64
+ public function isLogToDB() :bool {
65
+ return !in_array( 'disabled', $this->getLogLevelsDB() );
66
+ }
67
+
68
+ public function isLogToFile() :bool {
69
+ return !in_array( 'disabled', $this->getLogLevelsFile() )
70
+ && !empty( $this->getLogFilePath() );
71
+ }
72
+
73
+ /**
74
+ * @deprecated 12.0
75
+ */
76
  public function getMaxEntries() :int {
77
+ return PHP_INT_MAX;
 
 
78
  }
79
  }
src/lib/src/Modules/AuditTrail/Processor.php CHANGED
@@ -20,6 +20,7 @@ class Processor extends BaseShield\Processor {
20
 
21
  /**
22
  * @return Lib\AuditWriter
 
23
  */
24
  private function loadAuditorWriter() :Lib\AuditWriter {
25
  if ( !isset( $this->auditWriter ) ) {
@@ -32,7 +33,9 @@ class Processor extends BaseShield\Processor {
32
  }
33
 
34
  private function initAuditors() {
35
- $this->loadAuditorWriter()->setIfCommit( true );
 
 
36
  foreach ( $this->getAuditors() as $auditor ) {
37
  $auditor->setMod( $this->getMod() )->execute();
38
  }
20
 
21
  /**
22
  * @return Lib\AuditWriter
23
+ * @deprecated 12.0
24
  */
25
  private function loadAuditorWriter() :Lib\AuditWriter {
26
  if ( !isset( $this->auditWriter ) ) {
33
  }
34
 
35
  private function initAuditors() {
36
+ /** @var ModCon $mod */
37
+ $mod = $this->getMod();
38
+ $mod->getAuditLogger()->setIfCommit( true );
39
  foreach ( $this->getAuditors() as $auditor ) {
40
  $auditor->setMod( $this->getMod() )->execute();
41
  }
src/lib/src/Modules/AuditTrail/Strings.php CHANGED
@@ -7,86 +7,146 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
7
  class Strings extends Base\Strings {
8
 
9
  /**
10
- * @return string[][]
11
  */
12
- protected function getAuditMessages() :array {
13
  return [
14
  'plugin_activated' => [
15
- __( 'Plugin "%s" was activated.', 'wp-simple-firewall' )
 
 
 
16
  ],
17
  'plugin_deactivated' => [
18
- __( 'Plugin "%s" was deactivated.', 'wp-simple-firewall' )
19
- ],
20
- 'plugin_file_edited' => [
21
- __( 'An attempt was made to edit the plugin file "%s" directly through the WordPress editor.', 'wp-simple-firewall' )
22
  ],
23
  'plugin_upgraded' => [
24
- __( 'Plugin "%s" was upgraded from version %s to version %s.', 'wp-simple-firewall' )
 
 
 
 
 
 
 
 
 
25
  ],
26
  'theme_activated' => [
27
- __( 'Theme "%s" was activated.', 'wp-simple-firewall' )
 
 
 
28
  ],
29
  'theme_file_edited' => [
30
- __( 'An attempt was made to edit the theme file "%s" directly through the WordPress editor.', 'wp-simple-firewall' )
 
 
 
31
  ],
32
  'theme_upgraded' => [
33
- __( 'Theme "%s" was upgraded from version %s to version %s.', 'wp-simple-firewall' )
 
 
 
34
  ],
35
  'core_updated' => [
36
- __( 'WordPress Core was updated from "%s" to "%s".', 'wp-simple-firewall' )
 
 
 
37
  ],
38
  'permalinks_structure' => [
39
- __( 'WordPress Permalinks Structure was updated from "%s" to "%s".', 'wp-simple-firewall' )
 
 
 
40
  ],
41
  'post_deleted' => [
42
- __( 'WordPress Post entitled "%s" was permanently deleted from trash.', 'wp-simple-firewall' )
 
 
 
43
  ],
44
  'post_trashed' => [
45
- __( 'Post entitled "%s" was trashed.', 'wp-simple-firewall' ),
46
- __( 'Post Type: %s' ),
 
 
 
47
  ],
48
  'post_recovered' => [
49
- __( 'Post entitled "%s" was recoverd from trash.', 'wp-simple-firewall' ),
50
- __( 'Post Type: %s' ),
 
 
 
51
  ],
52
  'post_updated' => [
53
- __( 'Post entitled "%s" was updated.', 'wp-simple-firewall' ),
54
- __( 'Post Type: %s' ),
 
 
 
55
  ],
56
  'post_published' => [
57
- __( 'Post entitled "%s" was published.', 'wp-simple-firewall' ),
58
- __( 'Post Type: %s' ),
 
 
 
59
  ],
60
  'post_unpublished' => [
61
- __( 'Post entitled "%s" was unpublished.', 'wp-simple-firewall' ),
62
- __( 'Post Type: %s' ),
 
 
 
63
  ],
64
  'user_login' => [
65
- __( 'Attempted user login by "%s" was successful.', 'wp-simple-firewall' ),
 
 
 
66
  ],
67
  'user_login_app' => [
68
- __( 'Attempted login by "%s" using application password was successful.', 'wp-simple-firewall' ),
 
 
 
69
  ],
70
  'user_registered' => [
71
- __( 'New WordPress user registered.', 'wp-simple-firewall' )
72
- .' '.__( 'New username is "%s" with email address "%s".', 'wp-simple-firewall' )
 
 
 
73
  ],
74
  'user_deleted' => [
75
- __( 'WordPress user deleted.', 'wp-simple-firewall' )
76
- .' '.__( 'Username was "%s" with email address "%s".', 'wp-simple-firewall' )
 
 
 
77
  ],
78
  'user_deleted_reassigned' => [
79
- __( 'Deleted user posts were reassigned to user "%s".', 'wp-simple-firewall' )
 
 
 
80
  ],
81
  'email_attempt_send' => [
82
- __( 'There was an attempt to send an email using the "wp_mail" function.', 'wp-simple-firewall' ),
83
- __( "This log entry doesn't mean it was sent or received successfully, but only that an attempt was made.", 'wp-simple-firewall' ),
84
- __( 'It was sent to "%s" with the subject "%s".', 'wp-simple-firewall' ),
85
- "CC/BCC Recipients: %s / %s",
86
- __( 'The "wp_mail" function was called from the file "%s" on line %s.', 'wp-simple-firewall' )
87
- ],
88
- 'email_send_invalid' => [
89
- __( 'Attempting to log email, but data was not of the correct type (%s)', 'wp-simple-firewall' ),
90
  ],
91
  ];
92
  }
@@ -123,58 +183,41 @@ class Strings extends Base\Strings {
123
  switch ( $section ) {
124
 
125
  case 'section_enable_plugin_feature_audit_trail' :
126
- $sTitleShort = sprintf( '%s/%s', __( 'On', 'wp-simple-firewall' ), __( 'Off', 'wp-simple-firewall' ) );
127
- $sTitle = sprintf( __( 'Enable Module: %s', 'wp-simple-firewall' ), $this->getMod()
128
- ->getMainFeatureName() );
129
- $aSummary = [
130
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'The Audit Trail is designed so you can look back on events and analyse what happened and what may have gone wrong.', 'wp-simple-firewall' ) ),
131
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), sprintf( __( 'Keep the %s feature turned on.', 'wp-simple-firewall' ), __( 'Audit Trail', 'wp-simple-firewall' ) ) )
132
  ];
133
  break;
134
 
135
- case 'section_audit_trail_options' :
136
- $sTitle = __( 'Audit Trail Options', 'wp-simple-firewall' );
137
- $aSummary = [
138
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Provides finer control over the audit trail itself.', 'wp-simple-firewall' ) ),
139
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'These settings are dependent on your requirements.', 'wp-simple-firewall' ) )
140
  ];
141
- $sTitleShort = __( 'Audit Trail Options', 'wp-simple-firewall' );
142
  break;
143
 
144
  case 'section_enable_audit_contexts' :
145
- $sTitle = __( 'Enable Audit Areas', 'wp-simple-firewall' );
146
- $aSummary = [
147
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Specify which types of actions on your site are logged.', 'wp-simple-firewall' ) ),
148
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'These settings are dependent on your requirements.', 'wp-simple-firewall' ) )
149
  ];
150
- $sTitleShort = __( 'Audit Areas', 'wp-simple-firewall' );
151
  break;
152
 
153
- /*
154
- case 'section_change_tracking' :
155
- $sTitle = __( 'Track All Major Changes To Your Site', 'wp-simple-firewall' );
156
- $sTitleShort = __( 'Change Tracking', 'wp-simple-firewall' );
157
- $aData = ( new Shield\ChangeTrack\Snapshot\Collate() )->run();
158
- $sResult = (int)( strlen( base64_encode( WP_Http_Encoding::compress( json_encode( $aData ) ) ) )/1024 );
159
- $aSummary = [
160
- sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Track significant changes to your site.', 'wp-simple-firewall' ) )
161
- .' '.sprintf( '%s - %s', __( 'Note', 'wp-simple-firewall' ), __( 'This is separate from the Audit Trail.', 'wp-simple-firewall' ) ),
162
- sprintf( '%s - %s', __( 'Considerations', 'wp-simple-firewall' ),
163
- __( 'Change Tracking uses snapshots that may use take up lot of data.', 'wp-simple-firewall' )
164
- .' '.sprintf( 'Each snapshot will consume ~%sKB in your database', $sResult )
165
- ),
166
- ];
167
- break;
168
- */
169
-
170
  default:
171
  return parent::getSectionStrings( $section );
172
  }
173
 
174
  return [
175
- 'title' => $sTitle,
176
- 'title_short' => $sTitleShort,
177
- 'summary' => ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : [],
178
  ];
179
  }
180
 
@@ -187,96 +230,60 @@ class Strings extends Base\Strings {
187
  $con = $this->getCon();
188
  /** @var Options $opts */
189
  $opts = $this->getOptions();
190
- $sModName = $this->getMod()->getMainFeatureName();
191
 
192
  switch ( $key ) {
193
 
194
  case 'enable_audit_trail' :
195
- $sName = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $sModName );
196
- $sSummary = sprintf( __( 'Enable (or Disable) The %s Module', 'wp-simple-firewall' ), $sModName );
197
- $sDescription = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), $sModName );
198
  break;
199
 
200
- case 'audit_trail_max_entries' :
201
- $sName = __( 'Max Trail Length', 'wp-simple-firewall' );
202
- $sSummary = __( 'Maximum Audit Trail Length To Keep', 'wp-simple-firewall' );
203
- $sDescription = [
204
- __( 'Automatically remove any audit trail entries when this limit is exceeded.', 'wp-simple-firewall' ),
 
 
 
 
 
205
  ];
206
- if ( !$con->isPremiumActive() ) {
207
- $sDescription[] = sprintf( __( 'Upgrade to PRO to increase limit above %s.', 'wp-simple-firewall' ),
208
- '<code>'.$opts->getDef( 'audit_trail_free_max_entries' ).'</code>' );
209
- }
210
-
211
  break;
212
 
213
  case 'audit_trail_auto_clean' :
214
- $sName = __( 'Auto Clean', 'wp-simple-firewall' );
215
- $sSummary = __( 'Automatically Purge Audit Log Entries Older Than The Set Number Of Days', 'wp-simple-firewall' );
216
- $sDescription = __( 'Events older than the number of days specified will be automatically cleaned from the database.', 'wp-simple-firewall' );
217
- break;
218
-
219
- case 'enable_audit_context_users' :
220
- $sName = __( 'Users And Logins', 'wp-simple-firewall' );
221
- $sSummary = sprintf( __( 'Enable Audit Context - %s', 'wp-simple-firewall' ), __( 'Users And Logins', 'wp-simple-firewall' ) );
222
- $sDescription = sprintf( __( 'When this context is enabled, the audit trail will track activity relating to: %s', 'wp-simple-firewall' ), __( 'Users And Logins', 'wp-simple-firewall' ) );
223
- break;
224
-
225
- case 'enable_audit_context_plugins' :
226
- $sName = __( 'Plugins', 'wp-simple-firewall' );
227
- $sSummary = sprintf( __( 'Enable Audit Context - %s', 'wp-simple-firewall' ), __( 'Plugins', 'wp-simple-firewall' ) );
228
- $sDescription = sprintf( __( 'When this context is enabled, the audit trail will track activity relating to: %s', 'wp-simple-firewall' ), __( 'WordPress Plugins', 'wp-simple-firewall' ) );
229
- break;
230
-
231
- case 'enable_audit_context_themes' :
232
- $sName = __( 'Themes', 'wp-simple-firewall' );
233
- $sSummary = sprintf( __( 'Enable Audit Context - %s', 'wp-simple-firewall' ), __( 'Themes', 'wp-simple-firewall' ) );
234
- $sDescription = sprintf( __( 'When this context is enabled, the audit trail will track activity relating to: %s', 'wp-simple-firewall' ), __( 'WordPress Themes', 'wp-simple-firewall' ) );
235
- break;
236
-
237
- case 'enable_audit_context_posts' :
238
- $sName = __( 'Posts And Pages', 'wp-simple-firewall' );
239
- $sSummary = sprintf( __( 'Enable Audit Context - %s', 'wp-simple-firewall' ), __( 'Posts And Pages', 'wp-simple-firewall' ) );
240
- $sDescription = sprintf( __( 'When this context is enabled, the audit trail will track activity relating to: %s', 'wp-simple-firewall' ), __( 'Editing and publishing of posts and pages', 'wp-simple-firewall' ) );
241
- break;
242
-
243
- case 'enable_audit_context_wordpress' :
244
- $sName = __( 'WordPress And Settings', 'wp-simple-firewall' );
245
- $sSummary = sprintf( __( 'Enable Audit Context - %s', 'wp-simple-firewall' ), __( 'WordPress And Settings', 'wp-simple-firewall' ) );
246
- $sDescription = sprintf( __( 'When this context is enabled, the audit trail will track activity relating to: %s', 'wp-simple-firewall' ), __( 'WordPress upgrades and changes to particular WordPress settings', 'wp-simple-firewall' ) );
247
- break;
248
-
249
- case 'enable_audit_context_emails' :
250
- $sName = __( 'Emails', 'wp-simple-firewall' );
251
- $sSummary = sprintf( __( 'Enable Audit Context - %s', 'wp-simple-firewall' ), __( 'Emails', 'wp-simple-firewall' ) );
252
- $sDescription = sprintf( __( 'When this context is enabled, the audit trail will track activity relating to: %s', 'wp-simple-firewall' ), __( 'Email Sending', 'wp-simple-firewall' ) );
253
- break;
254
-
255
- case 'enable_audit_context_wpsf' :
256
- $sName = $con->getHumanName();
257
- $sSummary = sprintf( __( 'Enable Audit Context - %s', 'wp-simple-firewall' ), $con->getHumanName() );
258
- $sDescription = sprintf( __( 'When this context is enabled, the audit trail will track activity relating to: %s', 'wp-simple-firewall' ), $con->getHumanName() );
259
- break;
260
-
261
- case 'enable_change_tracking' :
262
- $sName = __( 'Site Change Tracking', 'wp-simple-firewall' );
263
- $sSummary = __( 'Track Major Changes To Your Site', 'wp-simple-firewall' );
264
- $sDescription = __( 'Tracking major changes to your site will help you monitor and catch malicious damage.', 'wp-simple-firewall' );
265
- break;
266
-
267
- case 'ct_snapshots_per_week' :
268
- $sName = __( 'Snapshot Per Week', 'wp-simple-firewall' );
269
- $sSummary = __( 'Number Of Snapshots To Take Per Week', 'wp-simple-firewall' );
270
- $sDescription = __( 'The number of snapshots to take per week. For daily snapshots, select 7.', 'wp-simple-firewall' )
271
- .'<br />'.__( 'Data storage in your database increases with the number of snapshots.', 'wp-simple-firewall' )
272
- .'<br />'.__( 'However, increased snapshots provide more granular information on when major site changes occurred.', 'wp-simple-firewall' );
273
  break;
274
 
275
- case 'ct_max_snapshots' :
276
- $sName = __( 'Max Snapshots', 'wp-simple-firewall' );
277
- $sSummary = __( 'Maximum Number Of Snapshots To Retain', 'wp-simple-firewall' );
278
- $sDescription = __( 'The more snapshots you retain, the further back you can look at changes over your site.', 'wp-simple-firewall' )
279
- .'<br />'.__( 'You will need to consider the implications to database storage requirements.', 'wp-simple-firewall' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  break;
281
 
282
  default:
@@ -284,9 +291,9 @@ class Strings extends Base\Strings {
284
  }
285
 
286
  return [
287
- 'name' => $sName,
288
- 'summary' => $sSummary,
289
- 'description' => $sDescription,
290
  ];
291
  }
292
  }
7
  class Strings extends Base\Strings {
8
 
9
  /**
10
+ * @inheritDoc
11
  */
12
+ public function getEventStrings() :array {
13
  return [
14
  'plugin_activated' => [
15
+ 'name' => __( 'Plugin Activated', 'wp-simple-firewall' ),
16
+ 'audit' => [
17
+ __( 'Plugin "{{plugin}}" was activated.', 'wp-simple-firewall' )
18
+ ],
19
  ],
20
  'plugin_deactivated' => [
21
+ 'name' => __( 'Plugin Deactivated', 'wp-simple-firewall' ),
22
+ 'audit' => [
23
+ __( 'Plugin "{{plugin}}" was deactivated.', 'wp-simple-firewall' )
24
+ ],
25
  ],
26
  'plugin_upgraded' => [
27
+ 'name' => __( 'Plugin Upgraded', 'wp-simple-firewall' ),
28
+ 'audit' => [
29
+ __( 'Plugin "{{plugin}}" was upgraded from version {{from}} to version {{to}}.', 'wp-simple-firewall' )
30
+ ],
31
+ ],
32
+ 'plugin_file_edited' => [
33
+ 'name' => __( 'Plugin File Edited', 'wp-simple-firewall' ),
34
+ 'audit' => [
35
+ __( 'An attempt was made to edit the plugin file "{{file}}" directly through the WordPress editor.', 'wp-simple-firewall' )
36
+ ],
37
  ],
38
  'theme_activated' => [
39
+ 'name' => __( 'Theme Activated', 'wp-simple-firewall' ),
40
+ 'audit' => [
41
+ __( 'Theme "{{theme}}" was activated.', 'wp-simple-firewall' ),
42
+ ],
43
  ],
44
  'theme_file_edited' => [
45
+ 'name' => __( 'Theme File Edited', 'wp-simple-firewall' ),
46
+ 'audit' => [
47
+ __( 'An attempt was made to edit the theme file "{{file}}" directly through the WordPress editor.', 'wp-simple-firewall' ),
48
+ ],
49
  ],
50
  'theme_upgraded' => [
51
+ 'name' => __( 'Theme Upgraded', 'wp-simple-firewall' ),
52
+ 'audit' => [
53
+ __( 'Theme "{{theme}}" was upgraded from version {{from}} to version {{to}}.', 'wp-simple-firewall' ),
54
+ ],
55
  ],
56
  'core_updated' => [
57
+ 'name' => __( 'WP Core Updated', 'wp-simple-firewall' ),
58
+ 'audit' => [
59
+ __( 'WordPress Core was updated from "{{from}}" to "{{to}}".', 'wp-simple-firewall' ),
60
+ ],
61
  ],
62
  'permalinks_structure' => [
63
+ 'name' => __( 'Permalinks Updated', 'wp-simple-firewall' ),
64
+ 'audit' => [
65
+ __( 'WordPress Permalinks Structure was updated from "{{from}}" to "{{to}}".', 'wp-simple-firewall' ),
66
+ ],
67
  ],
68
  'post_deleted' => [
69
+ 'name' => __( 'Post Deleted', 'wp-simple-firewall' ),
70
+ 'audit' => [
71
+ __( 'WordPress Post entitled "{{title}}" was permanently deleted from trash.', 'wp-simple-firewall' )
72
+ ],
73
  ],
74
  'post_trashed' => [
75
+ 'name' => __( 'Post Trashed', 'wp-simple-firewall' ),
76
+ 'audit' => [
77
+ __( 'Post entitled "{{title}}" was trashed.', 'wp-simple-firewall' ),
78
+ __( 'Post Type: {{type}}' ),
79
+ ],
80
  ],
81
  'post_recovered' => [
82
+ 'name' => __( 'Post Recovered', 'wp-simple-firewall' ),
83
+ 'audit' => [
84
+ __( 'Post entitled "{{title}}" was recovered from trash.', 'wp-simple-firewall' ),
85
+ __( 'Post Type: {{type}}' ),
86
+ ],
87
  ],
88
  'post_updated' => [
89
+ 'name' => __( 'Post Updated', 'wp-simple-firewall' ),
90
+ 'audit' => [
91
+ __( 'Post entitled "{{title}}" was updated.', 'wp-simple-firewall' ),
92
+ __( 'Post Type: {{type}}' ),
93
+ ],
94
  ],
95
  'post_published' => [
96
+ 'name' => __( 'Post Published', 'wp-simple-firewall' ),
97
+ 'audit' => [
98
+ __( 'Post entitled "{{title}}" was published.', 'wp-simple-firewall' ),
99
+ __( 'Post Type: {{type}}' ),
100
+ ],
101
  ],
102
  'post_unpublished' => [
103
+ 'name' => __( 'Post Unpublished', 'wp-simple-firewall' ),
104
+ 'audit' => [
105
+ __( 'Post entitled "{{title}}" was unpublished.', 'wp-simple-firewall' ),
106
+ __( 'Post Type: {{type}}' ),
107
+ ],
108
  ],
109
  'user_login' => [
110
+ 'name' => __( 'User Login', 'wp-simple-firewall' ),
111
+ 'audit' => [
112
+ __( 'Attempted user login by "{{user_login}}" was successful.', 'wp-simple-firewall' ),
113
+ ],
114
  ],
115
  'user_login_app' => [
116
+ 'name' => __( 'User Login By App Password', 'wp-simple-firewall' ),
117
+ 'audit' => [
118
+ __( 'Attempted login by "{{user_login}}" using application password was successful.', 'wp-simple-firewall' ),
119
+ ],
120
  ],
121
  'user_registered' => [
122
+ 'name' => __( 'User Registered', 'wp-simple-firewall' ),
123
+ 'audit' => [
124
+ __( 'New WordPress user registered.', 'wp-simple-firewall' ),
125
+ __( 'New username is "{{user_login}}" with email address "{{email}}".', 'wp-simple-firewall' ),
126
+ ],
127
  ],
128
  'user_deleted' => [
129
+ 'name' => __( 'User Deleted', 'wp-simple-firewall' ),
130
+ 'audit' => [
131
+ __( 'WordPress user deleted.', 'wp-simple-firewall' ),
132
+ __( 'Username was "{{user_login}}" with email address "{{email}}".', 'wp-simple-firewall' ),
133
+ ],
134
  ],
135
  'user_deleted_reassigned' => [
136
+ 'name' => __( 'User Deleted And Reassigned', 'wp-simple-firewall' ),
137
+ 'audit' => [
138
+ __( 'Deleted user posts were reassigned to user "{{user_login}}".', 'wp-simple-firewall' )
139
+ ],
140
  ],
141
  'email_attempt_send' => [
142
+ 'name' => __( 'Email Sent', 'wp-simple-firewall' ),
143
+ 'audit' => [
144
+ __( 'There was an attempt to send an email using the "wp_mail" function.', 'wp-simple-firewall' ),
145
+ __( "This log entry doesn't mean it was sent or received successfully, but only that an attempt was made.", 'wp-simple-firewall' ),
146
+ __( 'It was sent to "{{to}}" with the subject "{{subject}}".', 'wp-simple-firewall' ),
147
+ "CC/BCC Recipients: {{cc}} / {{bcc}}",
148
+ __( 'The "wp_mail" function was called from the file "{{bt_file}}" on line {{bt_line}}.', 'wp-simple-firewall' )
149
+ ],
150
  ],
151
  ];
152
  }
183
  switch ( $section ) {
184
 
185
  case 'section_enable_plugin_feature_audit_trail' :
186
+ $titleShort = sprintf( '%s/%s', __( 'On', 'wp-simple-firewall' ), __( 'Off', 'wp-simple-firewall' ) );
187
+ $title = sprintf( __( 'Enable Module: %s', 'wp-simple-firewall' ), $this->getMod()
188
+ ->getMainFeatureName() );
189
+ $summary = [
190
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'The Audit Trail is designed so you can look back on events and analyse what happened and what may have gone wrong.', 'wp-simple-firewall' ) ),
191
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), sprintf( __( 'Keep the %s feature turned on.', 'wp-simple-firewall' ), __( 'Audit Trail', 'wp-simple-firewall' ) ) )
192
  ];
193
  break;
194
 
195
+ case 'section_localdb' :
196
+ $title = __( 'Log To DB', 'wp-simple-firewall' );
197
+ $summary = [
198
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Provides finer control over the audit trail itself.', 'wp-simple-firewall' ) ),
199
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'These settings are dependent on your requirements.', 'wp-simple-firewall' ) )
200
  ];
201
+ $titleShort = __( 'Log To DB', 'wp-simple-firewall' );
202
  break;
203
 
204
  case 'section_enable_audit_contexts' :
205
+ $title = __( 'Enable Audit Areas', 'wp-simple-firewall' );
206
+ $summary = [
207
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Specify which types of actions on your site are logged.', 'wp-simple-firewall' ) ),
208
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'These settings are dependent on your requirements.', 'wp-simple-firewall' ) )
209
  ];
210
+ $titleShort = __( 'Audit Areas', 'wp-simple-firewall' );
211
  break;
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  default:
214
  return parent::getSectionStrings( $section );
215
  }
216
 
217
  return [
218
+ 'title' => $title,
219
+ 'title_short' => $titleShort,
220
+ 'summary' => $summary,
221
  ];
222
  }
223
 
230
  $con = $this->getCon();
231
  /** @var Options $opts */
232
  $opts = $this->getOptions();
233
+ $modName = $this->getMod()->getMainFeatureName();
234
 
235
  switch ( $key ) {
236
 
237
  case 'enable_audit_trail' :
238
+ $name = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $modName );
239
+ $summary = sprintf( __( 'Enable (or Disable) The %s Module', 'wp-simple-firewall' ), $modName );
240
+ $description = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), $modName );
241
  break;
242
 
243
+ case 'log_level_db' :
244
+ $name = __( 'Logging Level', 'wp-simple-firewall' );
245
+ $summary = __( 'Logging Level For DB-Based Logs', 'wp-simple-firewall' );
246
+ $description = [
247
+ __( 'Specify the logging levels when using the local database.', 'wp-simple-firewall' ),
248
+ __( "Debug and Info logging should only be enabled when investigating specific problems.", 'wp-simple-firewall' ),
249
+ sprintf( '<a href="%s" target="_blank">%s</a>',
250
+ $this->getCon()->getModule_Insights()->getUrl_SubInsightsPage( 'docs' ),
251
+ __( 'View all event details and their assigned levels', 'wp-simple-firewall' )
252
+ )
253
  ];
 
 
 
 
 
254
  break;
255
 
256
  case 'audit_trail_auto_clean' :
257
+ $name = __( 'Auto Clean', 'wp-simple-firewall' );
258
+ $summary = __( 'Automatically Purge Audit Log Entries Older Than The Set Number Of Days', 'wp-simple-firewall' );
259
+ $description = [
260
+ __( 'Events older than the number of days specified will be automatically cleaned from the database.', 'wp-simple-firewall' )
261
+ ];
262
+ if ( !$con->isPremiumActive() ) {
263
+ $description[] = sprintf( __( 'Upgrade to PRO to increase limit beyond %s days.', 'wp-simple-firewall' ),
264
+ '<code>'.$opts->getDef( 'max_free_days' ).'</code>' );
265
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  break;
267
 
268
+ case 'log_level_file' :
269
+ $name = __( 'File Logging Level', 'wp-simple-firewall' );
270
+ $summary = __( 'Logging Level For File-Based Logs', 'wp-simple-firewall' );
271
+ $description = [
272
+ __( 'Specify the logging levels when using the local filesystem.', 'wp-simple-firewall' ),
273
+ sprintf( '%s: <code>%s</code>',
274
+ __( 'Log File Location', 'wp-simple-firewall' ),
275
+ $opts->getLogFilePath()
276
+ ),
277
+ sprintf( '<a href="%s" target="_blank">%s</a>',
278
+ $this->getCon()->getModule_Insights()->getUrl_SubInsightsPage( 'docs' ),
279
+ __( 'View all event details and their assigned levels', 'wp-simple-firewall' )
280
+ ),
281
+ sprintf( '%s: %s',
282
+ __( 'Note', 'wp-simple-firewall' ),
283
+ sprintf( __( 'Log files will be rotated daily up to a limit of %s.', 'wp-simple-firewall' ),
284
+ sprintf( '<code>%s</code>', $opts->getLogFileRotationLimit() ) )
285
+ )
286
+ ];
287
  break;
288
 
289
  default:
291
  }
292
 
293
  return [
294
+ 'name' => $name,
295
+ 'summary' => $summary,
296
+ 'description' => $description,
297
  ];
298
  }
299
  }
src/lib/src/Modules/AuditTrail/UI.php CHANGED
@@ -3,51 +3,31 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
 
8
 
9
  class UI extends BaseShield\UI {
10
 
11
  public function renderAuditTrailTable() :string {
12
- $con = $this->getCon();
13
  /** @var ModCon $mod */
14
  $mod = $this->getMod();
15
- /** @var Databases\AuditTrail\Select $dbSel */
16
- $dbSel = $mod->getDbHandler_AuditTrail()->getQuerySelector();
17
-
18
- /** @var Modules\Events\Strings $eventStrings */
19
- $eventStrings = $con->getModule_Events()->getStrings();
20
- $eventsSelect = array_intersect_key( $eventStrings->getEventNames(), array_flip( $dbSel->getDistinctEvents() ) );
21
- asort( $eventsSelect );
22
-
23
- return $this->getMod()
24
- ->renderTemplate(
25
- '/wpadmin_pages/insights/audit/audit_table.twig',
26
- [
27
- 'ajax' => [
28
- 'render_table_audittrail' => $mod->getAjaxActionData( 'render_table_audittrail', true ),
29
- 'item_addparamwhite' => $mod->getAjaxActionData( 'item_addparamwhite', true )
30
- ],
31
- 'flags' => [],
32
- 'strings' => [
33
- 'table_title' => sprintf( '%s: %s', __( 'Logs', 'wp-simple-firewall' ), __( 'Audit Trail', 'wp-simple-firewall' ) ),
34
- 'sub_title' => __( 'Use the Audit Trail Glossary for help interpreting log entries.', 'wp-simple-firewall' ),
35
- 'title_filter_form' => __( 'Audit Trail Filters', 'wp-simple-firewall' ),
36
- 'username_ignores' => __( "Providing a username will cause the 'logged-in' filter to be ignored.", 'wp-simple-firewall' ),
37
- 'exclude_your_ip' => __( 'Exclude Your Current IP', 'wp-simple-firewall' ),
38
- 'exclude_your_ip_tooltip' => __( 'Exclude Your IP From Results', 'wp-simple-firewall' ),
39
- 'context' => __( 'Context', 'wp-simple-firewall' ),
40
- 'event' => __( 'Event', 'wp-simple-firewall' ),
41
- 'show_after' => __( 'show results that occurred after', 'wp-simple-firewall' ),
42
- 'show_before' => __( 'show results that occurred before', 'wp-simple-firewall' ),
43
- ],
44
- 'vars' => [
45
- 'events_for_select' => $eventsSelect,
46
- 'unique_ips' => $dbSel->getDistinctIps(),
47
- 'unique_users' => $dbSel->getDistinctUsernames(),
48
- ],
49
- ],
50
- true
51
- );
52
  }
53
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
 
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\AuditTrail\ForAuditTrail;
8
 
9
  class UI extends BaseShield\UI {
10
 
11
  public function renderAuditTrailTable() :string {
 
12
  /** @var ModCon $mod */
13
  $mod = $this->getMod();
14
+ return $mod->renderTemplate(
15
+ '/wpadmin_pages/insights/audit_trail/audit_table.twig',
16
+ [
17
+ 'ajax' => [
18
+ 'logtable_action' => $mod->getAjaxActionData( 'logtable_action', true ),
19
+ ],
20
+ 'flags' => [],
21
+ 'strings' => [
22
+ 'table_title' => sprintf( '%s: %s', __( 'Logs', 'wp-simple-firewall' ), __( 'Audit Trail', 'wp-simple-firewall' ) ),
23
+ ],
24
+ 'vars' => [
25
+ 'datatables_init' => ( new ForAuditTrail() )
26
+ ->setMod( $this->getMod() )
27
+ ->build()
28
+ ],
29
+ ],
30
+ true
31
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  }
33
  }
src/lib/src/Modules/AuditTrail/Upgrade.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
4
+
5
+ class Upgrade extends \FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Upgrade {
6
+
7
+ protected function upgrade_1200() {
8
+ ( new Lib\Ops\ConvertLegacy() )
9
+ ->setMod( $this->getMod() )
10
+ ->run();
11
+ }
12
+ }
src/lib/src/Modules/AuditTrail/WpCli/Display.php CHANGED
@@ -64,13 +64,8 @@ class Display extends Base\WpCli\BaseWpCliCmd {
64
  * @throws WP_CLI\ExitException
65
  */
66
  public function cmdDisplay( array $null, array $aA ) {
67
- /** @var ModCon $mod */
68
- $mod = $this->getMod();
69
- $oTableBuilder = ( new Tables\Build\AuditTrail() )
70
- ->setMod( $mod )
71
- ->setDbHandler( $mod->getDbHandler_AuditTrail() );
72
  ( new Tables\Render\WpCliTable\AuditTrail() )
73
- ->setDataBuilder( $oTableBuilder )
74
  ->render();
75
  }
76
  }
64
  * @throws WP_CLI\ExitException
65
  */
66
  public function cmdDisplay( array $null, array $aA ) {
 
 
 
 
 
67
  ( new Tables\Render\WpCliTable\AuditTrail() )
68
+ ->setMod( $this->getMod() )
69
  ->render();
70
  }
71
  }
src/lib/src/Modules/Autoupdates/Strings.php CHANGED
@@ -87,21 +87,21 @@ class Strings extends Base\Strings {
87
  switch ( $key ) {
88
 
89
  case 'enable_autoupdates' :
90
- $sName = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $sModName );
91
- $sSummary = sprintf( __( 'Enable (or Disable) The %s Module', 'wp-simple-firewall' ), $sModName );
92
- $sDescription = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), $sModName );
93
  break;
94
 
95
  case 'enable_autoupdate_disable_all' :
96
- $sName = __( 'Disable All', 'wp-simple-firewall' );
97
- $sSummary = __( 'Completely Disable WordPress Automatic Updates', 'wp-simple-firewall' );
98
- $sDescription = __( 'When selected, regardless of any other settings, all WordPress automatic updates on this site will be completely disabled!', 'wp-simple-firewall' );
99
  break;
100
 
101
  case 'autoupdate_plugin_self' :
102
- $sName = __( 'Auto Update Plugin', 'wp-simple-firewall' );
103
- $sSummary = __( 'Always Automatically Update This Plugin', 'wp-simple-firewall' );
104
- $sDescription = [
105
  sprintf(
106
  __( 'Regardless of any other settings, automatically update the "%s" plugin.', 'wp-simple-firewall' ),
107
  $sPlugName
@@ -111,51 +111,51 @@ class Strings extends Base\Strings {
111
  break;
112
 
113
  case 'autoupdate_core' :
114
- $sName = __( 'WordPress Core Updates', 'wp-simple-firewall' );
115
- $sSummary = __( 'Decide how the WordPress Core will automatically update, if at all', 'wp-simple-firewall' );
116
- $sDescription = __( 'At least automatically upgrading minor versions is recommended (and is the WordPress default).', 'wp-simple-firewall' );
117
  break;
118
 
119
  case 'enable_autoupdate_translations' : // REMOVED 8.6.2
120
- $sName = __( 'Translations', 'wp-simple-firewall' );
121
- $sSummary = __( 'Automatically Update Translations', 'wp-simple-firewall' );
122
- $sDescription = __( 'Note: Automatic updates for translations are enabled on WordPress by default.', 'wp-simple-firewall' );
123
  break;
124
 
125
  case 'enable_autoupdate_plugins' :
126
- $sName = __( 'Plugins', 'wp-simple-firewall' );
127
- $sSummary = __( 'Automatically Update All Plugins', 'wp-simple-firewall' );
128
- $sDescription = __( 'Note: Automatic updates for plugins are disabled on WordPress by default.', 'wp-simple-firewall' );
129
  break;
130
 
131
  case 'enable_autoupdate_themes' :
132
- $sName = __( 'Themes', 'wp-simple-firewall' );
133
- $sSummary = __( 'Automatically Update Themes', 'wp-simple-firewall' );
134
- $sDescription = __( 'Note: Automatic updates for themes are disabled on WordPress by default.', 'wp-simple-firewall' );
135
  break;
136
 
137
  case 'enable_autoupdate_ignore_vcs' : // REMOVED 8.6.2
138
- $sName = __( 'Ignore Version Control', 'wp-simple-firewall' );
139
- $sSummary = __( 'Ignore Version Control Systems Such As GIT and SVN', 'wp-simple-firewall' );
140
- $sDescription = __( 'If you use SVN or GIT and WordPress detects it, automatic updates are disabled by default. Check this box to ignore version control systems and allow automatic updates.', 'wp-simple-firewall' );
141
  break;
142
 
143
  case 'enable_upgrade_notification_email' :
144
- $sName = __( 'Send Report Email', 'wp-simple-firewall' );
145
- $sSummary = __( 'Send email notices after automatic updates', 'wp-simple-firewall' );
146
- $sDescription = __( 'You can turn on/off email notices from automatic updates by un/checking this box.', 'wp-simple-firewall' );
147
  break;
148
 
149
  case 'override_email_address' :
150
- $sName = __( 'Report Email Address', 'wp-simple-firewall' );
151
- $sSummary = __( 'Where to send upgrade notification reports', 'wp-simple-firewall' );
152
- $sDescription = __( 'If this is empty, it will default to the Site Admin email address', 'wp-simple-firewall' );
153
  break;
154
 
155
  case 'update_delay' :
156
- $sName = __( 'Update Delay', 'wp-simple-firewall' );
157
- $sSummary = __( 'Delay Automatic Updates For Period Of Stability', 'wp-simple-firewall' );
158
- $sDescription = sprintf( __( '%s will delay upgrades until the new update has been available for the set number of days.', 'wp-simple-firewall' ), $sPlugName )
159
  .'<br />'.__( "This helps ensure updates are more stable before they're automatically applied to your site.", 'wp-simple-firewall' );
160
  break;
161
 
@@ -164,9 +164,9 @@ class Strings extends Base\Strings {
164
  }
165
 
166
  return [
167
- 'name' => $sName,
168
- 'summary' => $sSummary,
169
- 'description' => $sDescription,
170
  ];
171
  }
172
  }
87
  switch ( $key ) {
88
 
89
  case 'enable_autoupdates' :
90
+ $name = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $sModName );
91
+ $summary = sprintf( __( 'Enable (or Disable) The %s Module', 'wp-simple-firewall' ), $sModName );
92
+ $description = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), $sModName );
93
  break;
94
 
95
  case 'enable_autoupdate_disable_all' :
96
+ $name = __( 'Disable All', 'wp-simple-firewall' );
97
+ $summary = __( 'Completely Disable WordPress Automatic Updates', 'wp-simple-firewall' );
98
+ $description = __( 'When selected, regardless of any other settings, all WordPress automatic updates on this site will be completely disabled!', 'wp-simple-firewall' );
99
  break;
100
 
101
  case 'autoupdate_plugin_self' :
102
+ $name = __( 'Auto Update Plugin', 'wp-simple-firewall' );
103
+ $summary = __( 'Always Automatically Update This Plugin', 'wp-simple-firewall' );
104
+ $description = [
105
  sprintf(
106
  __( 'Regardless of any other settings, automatically update the "%s" plugin.', 'wp-simple-firewall' ),
107
  $sPlugName
111
  break;
112
 
113
  case 'autoupdate_core' :
114
+ $name = __( 'WordPress Core Updates', 'wp-simple-firewall' );
115
+ $summary = __( 'Decide how the WordPress Core will automatically update, if at all', 'wp-simple-firewall' );
116
+ $description = __( 'At least automatically upgrading minor versions is recommended (and is the WordPress default).', 'wp-simple-firewall' );
117
  break;
118
 
119
  case 'enable_autoupdate_translations' : // REMOVED 8.6.2
120
+ $name = __( 'Translations', 'wp-simple-firewall' );
121
+ $summary = __( 'Automatically Update Translations', 'wp-simple-firewall' );
122
+ $description = __( 'Note: Automatic updates for translations are enabled on WordPress by default.', 'wp-simple-firewall' );
123
  break;
124
 
125
  case 'enable_autoupdate_plugins' :
126
+ $name = __( 'Plugins', 'wp-simple-firewall' );
127
+ $summary = __( 'Automatically Update All Plugins', 'wp-simple-firewall' );
128
+ $description = __( 'Note: Automatic updates for plugins are disabled on WordPress by default.', 'wp-simple-firewall' );
129
  break;
130
 
131
  case 'enable_autoupdate_themes' :
132
+ $name = __( 'Themes', 'wp-simple-firewall' );
133
+ $summary = __( 'Automatically Update Themes', 'wp-simple-firewall' );
134
+ $description = __( 'Note: Automatic updates for themes are disabled on WordPress by default.', 'wp-simple-firewall' );
135
  break;
136
 
137
  case 'enable_autoupdate_ignore_vcs' : // REMOVED 8.6.2
138
+ $name = __( 'Ignore Version Control', 'wp-simple-firewall' );
139
+ $summary = __( 'Ignore Version Control Systems Such As GIT and SVN', 'wp-simple-firewall' );
140
+ $description = __( 'If you use SVN or GIT and WordPress detects it, automatic updates are disabled by default. Check this box to ignore version control systems and allow automatic updates.', 'wp-simple-firewall' );
141
  break;
142
 
143
  case 'enable_upgrade_notification_email' :
144
+ $name = __( 'Send Report Email', 'wp-simple-firewall' );
145
+ $summary = __( 'Send email notices after automatic updates', 'wp-simple-firewall' );
146
+ $description = __( 'You can turn on/off email notices from automatic updates by un/checking this box.', 'wp-simple-firewall' );
147
  break;
148
 
149
  case 'override_email_address' :
150
+ $name = __( 'Report Email Address', 'wp-simple-firewall' );
151
+ $summary = __( 'Where to send upgrade notification reports', 'wp-simple-firewall' );
152
+ $description = __( 'If this is empty, it will default to the Site Admin email address', 'wp-simple-firewall' );
153
  break;
154
 
155
  case 'update_delay' :
156
+ $name = __( 'Update Delay', 'wp-simple-firewall' );
157
+ $summary = __( 'Delay Automatic Updates For Period Of Stability', 'wp-simple-firewall' );
158
+ $description = sprintf( __( '%s will delay upgrades until the new update has been available for the set number of days.', 'wp-simple-firewall' ), $sPlugName )
159
  .'<br />'.__( "This helps ensure updates are more stable before they're automatically applied to your site.", 'wp-simple-firewall' );
160
  break;
161
 
164
  }
165
 
166
  return [
167
+ 'name' => $name,
168
+ 'summary' => $summary,
169
+ 'description' => $description,
170
  ];
171
  }
172
  }
src/lib/src/Modules/Base/Config/LoadConfig.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Config;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+ use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
8
+
9
+ class LoadConfig {
10
+
11
+ use ModConsumer;
12
+
13
+ private $configSourceFile = '';
14
+
15
+ private $isBuiltFromFile = false;
16
+
17
+ public function getConfigSourceFile() :string {
18
+ return empty( $this->configSourceFile ) ? $this->getPathCfg() : $this->configSourceFile;
19
+ }
20
+
21
+ public function isBuiltFromFile() :bool {
22
+ return $this->isBuiltFromFile;
23
+ }
24
+
25
+ /**
26
+ * @return array
27
+ * @throws \Exception
28
+ */
29
+ public function run() :array {
30
+ try {
31
+ if ( $this->getCon()->cfg->rebuilt ) {
32
+ throw new \Exception( 'Force rebuild from file' );
33
+ }
34
+ $cfg = $this->fromWP();
35
+ $this->isBuiltFromFile = false;
36
+ }
37
+ catch ( \Exception $e ) {
38
+ $cfg = $this->fromFile();
39
+ $this->isBuiltFromFile = true;
40
+ }
41
+ return $cfg;
42
+ }
43
+
44
+ /**
45
+ * @return array
46
+ * @throws \Exception
47
+ */
48
+ public function fromWP() :array {
49
+ $FS = Services::WpFs();
50
+ $cfg = Transient::Get( $this->storeKey() );
51
+
52
+ if ( empty( $cfg ) || !is_array( $cfg ) || ( $FS->getModifiedTime( $this->getConfigSourceFile() ) > $cfg[ 'meta' ][ 'ts_mod' ] ) ) {
53
+ throw new \Exception( 'WP store is expired or non-existent' );
54
+ }
55
+ return $cfg;
56
+ }
57
+
58
+ public function storeKey() :string {
59
+ return 'shield_mod_config_'.$this->getMod()->getSlug();
60
+ }
61
+
62
+ /**
63
+ * @return array
64
+ * @throws \Exception
65
+ */
66
+ public function fromFile() :array {
67
+ $path = $this->getPathCfg();
68
+ try {
69
+ $raw = $this->loadRawFromFile( $path );
70
+ $this->configSourceFile = $path;
71
+ }
72
+ catch ( \Exception $e ) {
73
+ $path = $this->getCon()->paths->forModuleConfig( $this->getMod()->getSlug(), false );
74
+ $raw = $this->loadRawFromFile( $path );
75
+ $this->configSourceFile = $path;
76
+ }
77
+
78
+ $cfg = json_decode( $raw, true );
79
+ if ( empty( $cfg ) || !is_array( $cfg ) ) {
80
+ throw new \Exception( sprintf( "Couldn't part JSON from (%s) file '%s'.", $this->configSourceFile, $path ) );
81
+ }
82
+
83
+ $keyedOptions = [];
84
+ foreach ( $cfg[ 'options' ] as $option ) {
85
+ if ( !empty( $option[ 'key' ] ) ) {
86
+ $keyedOptions[ $option[ 'key' ] ] = $option;
87
+ }
88
+ }
89
+ $cfg[ 'options' ] = $keyedOptions;
90
+
91
+ $cfg[ 'meta' ] = [
92
+ 'ts_mod' => Services::WpFs()->getModifiedTime( $this->getConfigSourceFile() ),
93
+ ];
94
+
95
+ Transient::Set( $this->storeKey(), $cfg, WEEK_IN_SECONDS );
96
+ return $cfg;
97
+ }
98
+
99
+ private function getPathCfg() :string {
100
+ return $this->getCon()->paths->forModuleConfig( $this->getMod()->getSlug(), true );
101
+ }
102
+
103
+ /**
104
+ * @param string $file
105
+ * @return string
106
+ * @throws \Exception
107
+ */
108
+ private function loadRawFromFile( string $file ) :string {
109
+ if ( !Services::WpFs()->exists( $file ) ) {
110
+ throw new \Exception( sprintf( 'Configuration file "%s" does not exist.', $file ) );
111
+ }
112
+ $contents = Services::Data()->readFileWithInclude( $file );
113
+ if ( empty( $contents ) ) {
114
+ throw new \Exception( sprintf( 'Configuration file "%s" contents were empty or could not be read.', $file ) );
115
+ }
116
+ return $contents;
117
+ }
118
+ }
src/lib/src/Modules/Base/Databases.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+ use FernleafSystems\Wordpress\Plugin\Core;
7
+
8
+ class Databases {
9
+
10
+ use ModConsumer;
11
+
12
+ private $dbHandlers = [];
13
+
14
+ /**
15
+ * @return string[]
16
+ */
17
+ protected function getDbHandlerClasses() :array {
18
+ $c = $this->getMod()->getOptions()->getDef( 'db_handler_classes' );
19
+ return is_array( $c ) ? $c : [];
20
+ }
21
+
22
+ /**
23
+ * @return Core\Databases\Base\Handler[]
24
+ * @throws \Exception
25
+ */
26
+ public function loadAllDbHandlers() :array {
27
+ foreach ( array_keys( $this->getDbHandlerClasses() ) as $dbKey ) {
28
+ $this->loadDbH( $dbKey );
29
+ }
30
+ return $this->dbHandlers;
31
+ }
32
+
33
+ /**
34
+ * @param string $dbKey
35
+ * @return Core\Databases\Base\Handler|mixed|null
36
+ * @throws \Exception
37
+ */
38
+ public function loadDbH( string $dbKey ) {
39
+ $dbh = $this->dbHandlers[ $dbKey ] ?? null;
40
+
41
+ if ( empty( $dbh ) ) {
42
+
43
+ $dbDef = $this->getOptions()->getDef( 'db_table_'.$dbKey );
44
+ if ( empty( $dbDef ) ) {
45
+ throw new \Exception( sprintf( 'DB Definition for key (%s) is empty', $dbKey ) );
46
+ }
47
+
48
+ $dbClasses = $this->getDbHandlerClasses();
49
+ if ( !isset( $dbClasses[ $dbKey ] ) ) {
50
+ throw new \Exception( sprintf( 'DB Handler for key (%s) is not valid', $dbKey ) );
51
+ }
52
+
53
+ $dbClass = $dbClasses[ $dbKey ];
54
+ if ( !class_exists( $dbClass ) ) {
55
+ throw new \Exception( sprintf( 'DB Handler Class for key (%s) is not valid', $dbKey ) );
56
+ }
57
+
58
+ $dbDef[ 'table_prefix' ] = $this->getCon()->getPluginPrefix( '_' );
59
+ /** @var Core\Databases\Base\Handler|mixed $dbh */
60
+ $dbh = new $dbClass( $dbDef );
61
+ $dbh->execute();
62
+
63
+ $this->dbHandlers[ $dbKey ] = $dbh;
64
+ }
65
+
66
+ return $dbh;
67
+ }
68
+ }
src/lib/src/Modules/Base/Lib/Components/UiTrack.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Lib\Components;
4
+
5
+ use FernleafSystems\Utilities\Data\Adapter\DynPropertiesClass;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
8
+
9
+ /**
10
+ * @property array $selected_scans
11
+ */
12
+ class UiTrack extends DynPropertiesClass {
13
+
14
+ use PluginControllerConsumer;
15
+
16
+ /**
17
+ * @param string $key
18
+ * @return mixed
19
+ */
20
+ public function __get( string $key ) {
21
+
22
+ $value = parent::__get( $key );
23
+
24
+ switch ( $key ) {
25
+ case 'selected_scans':
26
+ if ( !is_array( $value ) || empty( $value ) ) {
27
+ /** @var HackGuard\Options $opts */
28
+ $opts = $this->getCon()
29
+ ->getModule_HackGuard()
30
+ ->getOptions();
31
+ $value = $opts->getScanSlugs();
32
+ }
33
+ break;
34
+ default:
35
+ break;
36
+ }
37
+
38
+ return $value;
39
+ }
40
+ }
src/lib/src/Modules/Base/ModCon.php CHANGED
@@ -76,6 +76,11 @@ abstract class ModCon {
76
  */
77
  private $aDbHandlers;
78
 
 
 
 
 
 
79
  /**
80
  * @param Shield\Controller\Controller $pluginCon
81
  * @param array $mod
@@ -118,9 +123,6 @@ abstract class ModCon {
118
  add_action( $con->prefix( 'plugin_shutdown' ), [ $this, 'onPluginShutdown' ] );
119
  add_action( $con->prefix( 'deactivate_plugin' ), [ $this, 'onPluginDeactivate' ] );
120
  add_action( $con->prefix( 'delete_plugin' ), [ $this, 'onPluginDelete' ] );
121
- add_filter( $con->prefix( 'aggregate_all_plugin_options' ), [ $this, 'aggregateOptionsValues' ] );
122
-
123
- add_filter( $con->prefix( 'register_admin_notices' ), [ $this, 'fRegisterAdminNotices' ] );
124
 
125
  if ( is_admin() || is_network_admin() ) {
126
  $this->loadAdminNotices();
@@ -165,7 +167,7 @@ abstract class ModCon {
165
  * @param bool $bInitAll
166
  * @return Shield\Databases\Base\Handler[]
167
  */
168
- protected function getDbHandlers( $bInitAll = false ) {
169
  if ( $bInitAll ) {
170
  foreach ( $this->getAllDbClasses() as $dbSlug => $dbClass ) {
171
  $this->getDbH( $dbSlug );
@@ -226,17 +228,6 @@ abstract class ModCon {
226
  return $this->loadModElement( 'Upgrade' );
227
  }
228
 
229
- /**
230
- * @param array $aAdminNotices
231
- * @return array
232
- */
233
- public function fRegisterAdminNotices( $aAdminNotices ) {
234
- if ( !is_array( $aAdminNotices ) ) {
235
- $aAdminNotices = [];
236
- }
237
- return array_merge( $aAdminNotices, $this->getOptions()->getAdminNotices() );
238
- }
239
-
240
  private function verifyModuleMeetRequirements() :bool {
241
  $bMeetsReqs = true;
242
 
@@ -387,7 +378,7 @@ abstract class ModCon {
387
  }
388
 
389
  public function isUpgrading() :bool {
390
- return $this->getCon()->cfg->rebuilt || $this->getOptions()->getRebuildFromFile();
391
  }
392
 
393
  /**
@@ -395,9 +386,8 @@ abstract class ModCon {
395
  */
396
  public function onPluginShutdown() {
397
  if ( !$this->getCon()->plugin_deleting ) {
398
- if ( rand( 1, 40 ) === 2 ) {
399
- // cleanup databases randomly just in-case cron doesn't run.
400
- $this->cleanupDatabases();
401
  }
402
  $this->saveModOptions();
403
  }
@@ -653,15 +643,6 @@ abstract class ModCon {
653
  return (bool)$this->getOptions()->getFeatureProperty( 'show_module_options' );
654
  }
655
 
656
- /**
657
- * Get config 'definition'.
658
- * @param string $key
659
- * @return mixed|null
660
- */
661
- public function getDef( string $key ) {
662
- return $this->getOptions()->getDef( $key );
663
- }
664
-
665
  /**
666
  * @return $this
667
  */
@@ -742,7 +723,7 @@ abstract class ModCon {
742
  * @return array
743
  */
744
  public function getNonceActionData( $action = '' ) {
745
- $data = $this->getCon()->getNonceActionData( $action );
746
  $data[ 'mod_slug' ] = $this->getModSlug();
747
  return $data;
748
  }
@@ -755,20 +736,19 @@ abstract class ModCon {
755
  return is_array( $notices ) ? $notices : [];
756
  }
757
 
758
- /**
759
- * @return string[]
760
- */
761
- public function getUiTrack() :array {
762
  $a = $this->getOptions()->getOpt( 'ui_track' );
763
- return is_array( $a ) ? $a : [];
 
 
764
  }
765
 
766
  public function setDismissedNotices( array $dis ) {
767
  $this->getOptions()->setOpt( 'dismissed_notices', $dis );
768
  }
769
 
770
- public function setUiTrack( array $UI ) {
771
- $this->getOptions()->setOpt( 'ui_track', $UI );
772
  }
773
 
774
  /**
@@ -818,14 +798,6 @@ abstract class ModCon {
818
  remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true', 1000 );
819
  }
820
 
821
- /**
822
- * @param array $aAggregatedOptions
823
- * @return array
824
- */
825
- public function aggregateOptionsValues( $aAggregatedOptions ) {
826
- return array_merge( $aAggregatedOptions, $this->getOptions()->getAllOptionsValues() );
827
- }
828
-
829
  /**
830
  * This is the point where you would want to do any options verification
831
  */
@@ -836,11 +808,6 @@ abstract class ModCon {
836
  }
837
 
838
  public function onPluginDelete() {
839
- foreach ( $this->getDbHandlers( true ) as $dbh ) {
840
- if ( !empty( $dbh ) ) {
841
- $dbh->tableDelete();
842
- }
843
- }
844
  $this->getOptions()->deleteStorage();
845
  }
846
 
@@ -1016,7 +983,7 @@ abstract class ModCon {
1016
 
1017
  public function isPage_InsightsThisModule() :bool {
1018
  return $this->isPage_Insights()
1019
- && Services::Request()->query( 'subnav' ) == $this->getSlug();
1020
  }
1021
 
1022
  protected function isModuleOptionsRequest() :bool {
@@ -1133,7 +1100,8 @@ abstract class ModCon {
1133
  }
1134
 
1135
  public function getWizardDefinitions() :array {
1136
- return is_array( $this->getDef( 'wizards' ) ) ? $this->getDef( 'wizards' ) : [];
 
1137
  }
1138
 
1139
  public function hasWizard() :bool {
@@ -1244,9 +1212,9 @@ abstract class ModCon {
1244
  $data[ 'unique_render_id' ] = 'noticeid-'.substr( md5( mt_rand() ), 0, 5 );
1245
  }
1246
  try {
1247
- $oRndr = $this->getCon()->getRenderer();
1248
  if ( $isTwig || preg_match( '#^.*\.twig$#i', $template ) ) {
1249
- $oRndr->setTemplateEngineTwig();
1250
  }
1251
 
1252
  $data[ 'strings' ] = Services::DataManipulation()
@@ -1255,9 +1223,9 @@ abstract class ModCon {
1255
  $data[ 'strings' ] ?? []
1256
  );
1257
 
1258
- $render = $oRndr->setTemplate( $template )
1259
- ->setRenderVars( $data )
1260
- ->render();
1261
  }
1262
  catch ( \Exception $e ) {
1263
  $render = $e->getMessage();
@@ -1267,18 +1235,6 @@ abstract class ModCon {
1267
  return (string)$render;
1268
  }
1269
 
1270
- /**
1271
- * @param array $aTransferableOptions
1272
- * @return array
1273
- */
1274
- public function exportTransferableOptions( $aTransferableOptions ) {
1275
- if ( !is_array( $aTransferableOptions ) ) {
1276
- $aTransferableOptions = [];
1277
- }
1278
- $aTransferableOptions[ $this->getOptionsStorageKey() ] = $this->getOptions()->getTransferableOptions();
1279
- return $aTransferableOptions;
1280
- }
1281
-
1282
  public function getMainWpData() :array {
1283
  return [
1284
  'options' => $this->getOptions()->getTransferableOptions()
@@ -1287,41 +1243,34 @@ abstract class ModCon {
1287
 
1288
  /**
1289
  * See plugin controller for the nature of $aData wpPrivacyExport()
1290
- * @param array $aExportItems
1291
- * @param string $sEmail
1292
- * @param int $nPage
1293
  * @return array
1294
  */
1295
- public function onWpPrivacyExport( $aExportItems, $sEmail, $nPage = 1 ) {
1296
- return $aExportItems;
1297
  }
1298
 
1299
  /**
1300
  * See plugin controller for the nature of $aData wpPrivacyErase()
1301
- * @param array $aData
1302
- * @param string $sEmail
1303
- * @param int $nPage
1304
  * @return array
1305
  */
1306
- public function onWpPrivacyErase( $aData, $sEmail, $nPage = 1 ) {
1307
- return $aData;
1308
  }
1309
 
1310
  /**
1311
  * @return null|Shield\Modules\Base\Options|mixed
1312
  */
1313
  public function getOptions() {
1314
- $opts = $this->opts ?? $this->oOpts;
1315
- if ( !$opts instanceof Options ) {
1316
- $con = $this->getCon();
1317
  $this->opts = $this->loadModElement( 'Options' );
1318
- $this->opts->setPathToConfig( $con->getPath_ConfigFile( $this->getSlug() ) )
1319
- ->setRebuildFromFile( $con->cfg->rebuilt )
1320
- ->setOptionsStorageKey( $this->getOptionsStorageKey() )
1321
- ->setIfLoadOptionsFromStorage( !$con->getIsResetPlugin() );
1322
- $opts = $this->opts;
1323
  }
1324
- return $opts;
1325
  }
1326
 
1327
  /**
@@ -1407,6 +1356,16 @@ abstract class ModCon {
1407
  }
1408
  }
1409
 
 
 
 
 
 
 
 
 
 
 
1410
  /**
1411
  * @return Shield\Modules\Base\Strings|mixed
1412
  */
@@ -1485,4 +1444,14 @@ abstract class ModCon {
1485
  public function savePluginOptions() {
1486
  $this->saveModOptions();
1487
  }
 
 
 
 
 
 
 
 
 
 
1488
  }
76
  */
77
  private $aDbHandlers;
78
 
79
+ /**
80
+ * @var Databases
81
+ */
82
+ private $dbHandler;
83
+
84
  /**
85
  * @param Shield\Controller\Controller $pluginCon
86
  * @param array $mod
123
  add_action( $con->prefix( 'plugin_shutdown' ), [ $this, 'onPluginShutdown' ] );
124
  add_action( $con->prefix( 'deactivate_plugin' ), [ $this, 'onPluginDeactivate' ] );
125
  add_action( $con->prefix( 'delete_plugin' ), [ $this, 'onPluginDelete' ] );
 
 
 
126
 
127
  if ( is_admin() || is_network_admin() ) {
128
  $this->loadAdminNotices();
167
  * @param bool $bInitAll
168
  * @return Shield\Databases\Base\Handler[]
169
  */
170
+ public function getDbHandlers( $bInitAll = false ) {
171
  if ( $bInitAll ) {
172
  foreach ( $this->getAllDbClasses() as $dbSlug => $dbClass ) {
173
  $this->getDbH( $dbSlug );
228
  return $this->loadModElement( 'Upgrade' );
229
  }
230
 
 
 
 
 
 
 
 
 
 
 
 
231
  private function verifyModuleMeetRequirements() :bool {
232
  $bMeetsReqs = true;
233
 
378
  }
379
 
380
  public function isUpgrading() :bool {
381
+ return $this->getCon()->cfg->rebuilt || $this->getOptions()->getConfigLoader()->isBuiltFromFile();
382
  }
383
 
384
  /**
386
  */
387
  public function onPluginShutdown() {
388
  if ( !$this->getCon()->plugin_deleting ) {
389
+ if ( rand( 1, 100 ) === 2 ) {
390
+ $this->cleanupDatabases(); // cleanup databases randomly just in-case cron doesn't run.
 
391
  }
392
  $this->saveModOptions();
393
  }
643
  return (bool)$this->getOptions()->getFeatureProperty( 'show_module_options' );
644
  }
645
 
 
 
 
 
 
 
 
 
 
646
  /**
647
  * @return $this
648
  */
723
  * @return array
724
  */
725
  public function getNonceActionData( $action = '' ) {
726
+ $data = $this->getCon()->getNonceActionData( (string)$action );
727
  $data[ 'mod_slug' ] = $this->getModSlug();
728
  return $data;
729
  }
736
  return is_array( $notices ) ? $notices : [];
737
  }
738
 
739
+ public function getUiTrack() :Lib\Components\UiTrack {
 
 
 
740
  $a = $this->getOptions()->getOpt( 'ui_track' );
741
+ return ( new Lib\Components\UiTrack() )
742
+ ->setCon( $this->getCon() )
743
+ ->applyFromArray( is_array( $a ) ? $a : [] );
744
  }
745
 
746
  public function setDismissedNotices( array $dis ) {
747
  $this->getOptions()->setOpt( 'dismissed_notices', $dis );
748
  }
749
 
750
+ public function setUiTrack( Lib\Components\UiTrack $UI ) {
751
+ $this->getOptions()->setOpt( 'ui_track', $UI->getRawData() );
752
  }
753
 
754
  /**
798
  remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true', 1000 );
799
  }
800
 
 
 
 
 
 
 
 
 
801
  /**
802
  * This is the point where you would want to do any options verification
803
  */
808
  }
809
 
810
  public function onPluginDelete() {
 
 
 
 
 
811
  $this->getOptions()->deleteStorage();
812
  }
813
 
983
 
984
  public function isPage_InsightsThisModule() :bool {
985
  return $this->isPage_Insights()
986
+ && Services::Request()->query( 'inav' ) == $this->getSlug();
987
  }
988
 
989
  protected function isModuleOptionsRequest() :bool {
1100
  }
1101
 
1102
  public function getWizardDefinitions() :array {
1103
+ $wiz = $this->getOptions()->getDef( 'wizards' );
1104
+ return is_array( $wiz ) ? $wiz : [];
1105
  }
1106
 
1107
  public function hasWizard() :bool {
1212
  $data[ 'unique_render_id' ] = 'noticeid-'.substr( md5( mt_rand() ), 0, 5 );
1213
  }
1214
  try {
1215
+ $rndr = $this->getCon()->getRenderer();
1216
  if ( $isTwig || preg_match( '#^.*\.twig$#i', $template ) ) {
1217
+ $rndr->setTemplateEngineTwig();
1218
  }
1219
 
1220
  $data[ 'strings' ] = Services::DataManipulation()
1223
  $data[ 'strings' ] ?? []
1224
  );
1225
 
1226
+ $render = $rndr->setTemplate( $template )
1227
+ ->setRenderVars( $data )
1228
+ ->render();
1229
  }
1230
  catch ( \Exception $e ) {
1231
  $render = $e->getMessage();
1235
  return (string)$render;
1236
  }
1237
 
 
 
 
 
 
 
 
 
 
 
 
 
1238
  public function getMainWpData() :array {
1239
  return [
1240
  'options' => $this->getOptions()->getTransferableOptions()
1243
 
1244
  /**
1245
  * See plugin controller for the nature of $aData wpPrivacyExport()
1246
+ * @param array $exportItems
1247
+ * @param string $email
1248
+ * @param int $page
1249
  * @return array
1250
  */
1251
+ public function onWpPrivacyExport( $exportItems, $email, $page = 1 ) {
1252
+ return $exportItems;
1253
  }
1254
 
1255
  /**
1256
  * See plugin controller for the nature of $aData wpPrivacyErase()
1257
+ * @param array $data
1258
+ * @param string $email
1259
+ * @param int $page
1260
  * @return array
1261
  */
1262
+ public function onWpPrivacyErase( $data, $email, $page = 1 ) {
1263
+ return $data;
1264
  }
1265
 
1266
  /**
1267
  * @return null|Shield\Modules\Base\Options|mixed
1268
  */
1269
  public function getOptions() {
1270
+ if ( empty( $this->opts ) ) {
 
 
1271
  $this->opts = $this->loadModElement( 'Options' );
 
 
 
 
 
1272
  }
1273
+ return $this->opts;
1274
  }
1275
 
1276
  /**
1356
  }
1357
  }
1358
 
1359
+ /**
1360
+ * @return Shield\Modules\Base\Databases|mixed
1361
+ */
1362
+ protected function getDbHandler() {
1363
+ if ( empty( $this->dbHandler ) ) {
1364
+ $this->dbHandler = $this->loadModElement( 'Databases' );
1365
+ }
1366
+ return $this->dbHandler;
1367
+ }
1368
+
1369
  /**
1370
  * @return Shield\Modules\Base\Strings|mixed
1371
  */
1444
  public function savePluginOptions() {
1445
  $this->saveModOptions();
1446
  }
1447
+
1448
+ /**
1449
+ * Get config 'definition'.
1450
+ * @param string $key
1451
+ * @return mixed|null
1452
+ * @deprecated 12.0
1453
+ */
1454
+ public function getDef( string $key ) {
1455
+ return $this->getOptions()->getDef( $key );
1456
+ }
1457
  }
src/lib/src/Modules/Base/Options.php CHANGED
@@ -5,12 +5,15 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Options\OptValueSanitize;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
  use FernleafSystems\Wordpress\Services\Services;
8
- use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
9
 
10
  class Options {
11
 
12
  use ModConsumer;
13
 
 
 
 
 
14
  /**
15
  * @var array
16
  */
@@ -29,68 +32,49 @@ class Options {
29
  /**
30
  * @var bool
31
  */
32
- protected $bNeedSave;
33
-
34
- /**
35
- * @var bool
36
- */
37
- protected $bRebuildFromFile = false;
38
 
39
  /**
40
  * @var string
41
  */
42
  protected $aOptionsKeys;
43
 
44
- /**
45
- * @var string
46
- */
47
- protected $sOptionsStorageKey;
48
-
49
- /**
50
- * by default we load from saved
51
- * @var string
52
- */
53
- protected $bLoadFromSaved = true;
54
-
55
- /**
56
- * @var string
57
- */
58
- protected $sPathToConfig;
59
-
60
  public function __construct() {
61
  }
62
 
63
  /**
64
- * @param bool $bDeleteFirst Used primarily with plugin reset
65
- * @param bool $bIsPremiumLicensed
66
  * @return bool
67
  */
68
- public function doOptionsSave( $bDeleteFirst = false, $bIsPremiumLicensed = false ) {
69
  if ( !$this->getNeedSave() ) {
70
  return true;
71
  }
72
  $this->cleanOptions();
73
- if ( !$bIsPremiumLicensed ) {
74
  $this->resetPremiumOptsToDefault();
75
  }
76
  $this->setNeedSave( false );
77
- if ( $bDeleteFirst ) {
78
- Services::WpGeneral()->deleteOption( $this->getOptionsStorageKey() );
79
- }
80
- return Services::WpGeneral()->updateOption( $this->getOptionsStorageKey(), $this->getAllOptionsValues() );
81
  }
82
 
83
- /**
84
- * @return bool
85
- */
86
  public function deleteStorage() {
87
- $oWp = Services::WpGeneral();
88
- $oWp->deleteOption( $this->getConfigStorageKey() );
89
- return $oWp->deleteOption( $this->getOptionsStorageKey() );
90
  }
91
 
92
  public function getAllOptionsValues() :array {
93
- return $this->getStoredOptions();
 
 
 
 
 
 
 
 
 
94
  }
95
 
96
  public function getSlug() :string {
@@ -101,15 +85,15 @@ class Options {
101
  * Returns an array of all the transferable options and their values
102
  * @return array
103
  */
104
- public function getTransferableOptions() {
105
- $aTransferable = [];
106
 
107
- foreach ( $this->getRawData_AllOptions() as $nKey => $aOptionData ) {
108
- if ( !isset( $aOptionData[ 'transferable' ] ) || $aOptionData[ 'transferable' ] === true ) {
109
- $aTransferable[ $aOptionData[ 'key' ] ] = $this->getOpt( $aOptionData[ 'key' ] );
110
  }
111
  }
112
- return $aTransferable;
113
  }
114
 
115
  /**
@@ -119,14 +103,14 @@ class Options {
119
  public function getOptionsMaskSensitive() {
120
 
121
  $aOptions = $this->getAllOptionsValues();
122
- foreach ( $this->getOptionsKeys() as $sKey ) {
123
- if ( !isset( $aOptions[ $sKey ] ) ) {
124
- $aOptions[ $sKey ] = $this->getOptDefault( $sKey );
125
  }
126
  }
127
- foreach ( $this->getRawData_AllOptions() as $nKey => $aOptDef ) {
128
- if ( isset( $aOptDef[ 'sensitive' ] ) && $aOptDef[ 'sensitive' ] === true ) {
129
- unset( $aOptions[ $aOptDef[ 'key' ] ] );
130
  }
131
  }
132
  return array_diff_key( $aOptions, array_flip( $this->getVirtualCommonOptions() ) );
@@ -138,10 +122,9 @@ class Options {
138
  public function getOptionsForWpCli() :array {
139
  return array_filter(
140
  $this->getOptionsKeys(),
141
- function ( $sKey ) {
142
- $opt = $this->getRawData_SingleOption( $sKey );
143
- return !empty( $opt[ 'section' ] )
144
- && $opt[ 'section' ] !== 'section_non_ui';
145
  }
146
  );
147
  }
@@ -154,39 +137,37 @@ class Options {
154
  $opts = [];
155
  if ( (bool)$this->getFeatureProperty( 'tracking_exclude' ) === false ) {
156
 
157
- $aOptions = $this->getAllOptionsValues();
158
- foreach ( $this->getOptionsKeys() as $sKey ) {
159
- if ( !isset( $aOptions[ $sKey ] ) ) {
160
- $aOptions[ $sKey ] = $this->getOptDefault( $sKey );
161
  }
162
  }
163
- foreach ( $this->getRawData_AllOptions() as $nKey => $aOptDef ) {
164
- if ( !empty( $aOptDef[ 'sensitive' ] ) || !empty( $aOptDef[ 'tracking_exclude' ] ) ) {
165
- unset( $aOptions[ $aOptDef[ 'key' ] ] );
166
  }
167
  }
168
- $opts = array_diff_key( $aOptions, array_flip( $this->getVirtualCommonOptions() ) );
169
  }
170
  return $opts;
171
  }
172
 
173
  /**
174
- * @param $property
175
- * @return null|mixed
176
  */
177
- public function getFeatureProperty( $property ) {
178
- $raw = $this->getRawData_FullFeatureConfig();
179
- return ( isset( $raw[ 'properties' ] ) && isset( $raw[ 'properties' ][ $property ] ) ) ? $raw[ 'properties' ][ $property ] : null;
180
  }
181
 
182
  public function getWpCliCfg() :array {
183
- $cfg = $this->getRawData_FullFeatureConfig();
184
  return array_merge(
185
  [
186
  'enabled' => true,
187
  'root' => $this->getSlug(),
188
  ],
189
- empty( $cfg[ 'wpcli' ] ) ? [] : $cfg[ 'wpcli' ]
190
  );
191
  }
192
 
@@ -195,25 +176,19 @@ class Options {
195
  * @return mixed|null
196
  */
197
  public function getDef( string $key ) {
198
- $cfg = $this->getRawData_FullFeatureConfig();
199
- return ( isset( $cfg[ 'definitions' ] ) && isset( $cfg[ 'definitions' ][ $key ] ) ) ? $cfg[ 'definitions' ][ $key ] : null;
200
  }
201
 
202
- /**
203
- * @param string $req
204
- * @return null|mixed
205
- */
206
- public function getFeatureRequirement( string $req ) {
207
- $aReqs = $this->getRawData_Requirements();
208
- return ( is_array( $aReqs ) && isset( $aReqs[ $req ] ) ) ? $aReqs[ $req ] : null;
209
  }
210
 
211
- /**
212
- * @return array
213
- */
214
- public function getAdminNotices() {
215
- $cfg = $this->getRawData_FullFeatureConfig();
216
- return ( isset( $cfg[ 'admin_notices' ] ) && is_array( $cfg[ 'admin_notices' ] ) ) ? $cfg[ 'admin_notices' ] : [];
217
  }
218
 
219
  /**
@@ -223,17 +198,22 @@ class Options {
223
  return $this->getFeatureProperty( 'tagline' );
224
  }
225
 
226
- /**
227
- * @return bool
228
- */
229
- public function getIfLoadOptionsFromStorage() {
230
- return $this->bLoadFromSaved;
231
- }
232
-
233
  public function isValidOptionKey( string $key ) :bool {
234
  return in_array( $key, $this->getOptionsKeys() );
235
  }
236
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  /**
238
  * @return array[]
239
  */
@@ -241,7 +221,7 @@ class Options {
241
 
242
  $aOptionsData = [];
243
 
244
- foreach ( $this->getRawData_OptionsSections() as $nPosition => $aRawSection ) {
245
 
246
  // if hidden isn't specified we skip
247
  if ( !isset( $aRawSection[ 'hidden' ] ) || !$aRawSection[ 'hidden' ] ) {
@@ -263,33 +243,32 @@ class Options {
263
  * @return array|null
264
  */
265
  public function getSection( string $section ) {
266
- $sections = $this->getSections();
267
- return $sections[ $section ] ?? null;
268
  }
269
 
270
  /**
271
- * @param bool $bIncludeHidden
272
  * @return array[]
273
  */
274
- public function getSections( $bIncludeHidden = false ) {
275
- $aSections = [];
276
- foreach ( $this->getRawData_OptionsSections() as $aRawSection ) {
277
- if ( $bIncludeHidden || !isset( $aRawSection[ 'hidden' ] ) || !$aRawSection[ 'hidden' ] ) {
278
- $aSections[ $aRawSection[ 'slug' ] ] = $aRawSection;
279
  }
280
  }
281
- return $aSections;
282
  }
283
 
284
  public function getPrimarySection() :array {
285
- $section = [];
286
- foreach ( $this->getSections() as $aS ) {
287
- if ( isset( $aS[ 'primary' ] ) && $aS[ 'primary' ] ) {
288
- $section = $aS;
289
  break;
290
  }
291
  }
292
- return $section;
293
  }
294
 
295
  /**
@@ -297,26 +276,16 @@ class Options {
297
  * @return array
298
  */
299
  public function getSection_Requirements( $slug ) {
300
- $aSection = $this->getSection( $slug );
301
- $aReqs = ( is_array( $aSection ) && isset( $aSection[ 'reqs' ] ) ) ? $aSection[ 'reqs' ] : [];
302
  return array_merge(
303
  [
304
- 'php_min' => '5.2.4',
305
- 'wp_min' => '3.5.0',
306
  ],
307
- $aReqs
308
  );
309
  }
310
 
311
- /**
312
- * @param string $sSlug
313
- * @return array|null
314
- */
315
- public function getSectionHelpVideo( $sSlug ) {
316
- $aSection = $this->getSection( $sSlug );
317
- return ( is_array( $aSection ) && isset( $aSection[ 'help_video' ] ) ) ? $aSection[ 'help_video' ] : null;
318
- }
319
-
320
  /**
321
  * @param string $slug
322
  * @return bool
@@ -327,101 +296,95 @@ class Options {
327
  && Services::WpGeneral()->getWordpressIsAtLeastVersion( $reqs[ 'wp_min' ] );
328
  }
329
 
330
- /**
331
- * @param string $optKey
332
- * @return bool
333
- */
334
- public function isOptReqsMet( $optKey ) :bool {
335
- return $this->isSectionReqsMet( $this->getOptProperty( $optKey, 'section' ) );
336
  }
337
 
338
  /**
339
  * @return string[]
340
  */
341
  public function getVisibleOptionsKeys() :array {
342
- $aKeys = [];
343
 
344
- foreach ( $this->getRawData_AllOptions() as $aOptionDef ) {
345
- if ( isset( $aOptionDef[ 'hidden' ] ) && $aOptionDef[ 'hidden' ] ) {
346
  continue;
347
  }
348
- $aSection = $this->getSection( $aOptionDef[ 'section' ] );
349
- if ( empty( $aSection ) || ( isset( $aSection[ 'hidden' ] ) && $aSection[ 'hidden' ] ) ) {
350
  continue;
351
  }
352
 
353
- $aKeys[] = $aOptionDef[ 'key' ];
354
  }
355
 
356
- return $aKeys;
357
  }
358
 
359
  public function getOptionsForPluginUse() :array {
360
 
361
- $aOptionsData = [];
362
 
363
- foreach ( $this->getRawData_OptionsSections() as $aRawSection ) {
364
 
365
- if ( isset( $aRawSection[ 'hidden' ] ) && $aRawSection[ 'hidden' ] ) {
366
  continue;
367
  }
368
 
369
- $aRawSection = array_merge(
370
  [
371
  'primary' => false,
372
- 'options' => $this->getOptionsForSection( $aRawSection[ 'slug' ] ),
373
  'help_video_id' => ''
374
  ],
375
- $aRawSection
376
  );
377
 
378
- if ( !empty( $aRawSection[ 'options' ] ) ) {
379
- $aOptionsData[] = $aRawSection;
380
  }
381
  }
382
 
383
- return $aOptionsData;
384
  }
385
 
386
- /**
387
- * @param string $slug
388
- * @return array[]
389
- */
390
- protected function getOptionsForSection( $slug ) :array {
391
 
392
- $aAllOptions = [];
393
- foreach ( $this->getRawData_AllOptions() as $aOptionDef ) {
394
 
395
- if ( ( $aOptionDef[ 'section' ] != $slug ) || ( isset( $aOptionDef[ 'hidden' ] ) && $aOptionDef[ 'hidden' ] ) ) {
396
  continue;
397
  }
398
 
399
- if ( isset( $aOptionDef[ 'hidden' ] ) && $aOptionDef[ 'hidden' ] ) {
400
  continue;
401
  }
402
 
403
- $aOptionDef = array_merge(
404
  [
405
  'link_info' => '',
406
  'link_blog' => '',
407
  'help_video_id' => '',
408
- 'value_options' => []
 
 
409
  ],
410
- $aOptionDef
411
  );
412
- $aOptionDef[ 'value' ] = $this->getOpt( $aOptionDef[ 'key' ] );
413
 
414
- if ( in_array( $aOptionDef[ 'type' ], [ 'select', 'multiple_select' ] ) ) {
415
- $aNewValueOptions = [];
416
- foreach ( $aOptionDef[ 'value_options' ] as $aValueOptions ) {
417
- $aNewValueOptions[ $aValueOptions[ 'value_key' ] ] = __( $aValueOptions[ 'text' ], 'wp-simple-firewall' );
418
  }
419
- $aOptionDef[ 'value_options' ] = $aNewValueOptions;
420
  }
421
 
422
- $aAllOptions[] = $aOptionDef;
423
  }
424
- return $aAllOptions;
425
  }
426
 
427
  public function getAdditionalMenuItems() :array {
@@ -429,7 +392,7 @@ class Options {
429
  }
430
 
431
  public function getNeedSave() :bool {
432
- return (bool)$this->bNeedSave;
433
  }
434
 
435
  /**
@@ -446,9 +409,11 @@ class Options {
446
  * @return mixed
447
  */
448
  public function getOpt( string $key, $mDefault = false ) {
449
- $aOptionsValues = $this->getAllOptionsValues();
450
- if ( !isset( $aOptionsValues[ $key ] ) && $this->isValidOptionKey( $key ) ) {
451
- $this->setOpt( $key, $this->getOptDefault( $key, $mDefault ) );
 
 
452
  }
453
  return $this->aOptionsValues[ $key ] ?? $mDefault;
454
  }
@@ -459,27 +424,21 @@ class Options {
459
  * @return mixed|null
460
  */
461
  public function getOptDefault( string $key, $mDefault = null ) {
462
- foreach ( $this->getRawData_AllOptions() as $aOption ) {
463
- if ( $aOption[ 'key' ] == $key ) {
464
- if ( isset( $aOption[ 'default' ] ) ) {
465
- $mDefault = $aOption[ 'default' ];
466
- break;
467
- }
468
- if ( isset( $aOption[ 'value' ] ) ) {
469
- $mDefault = $aOption[ 'value' ];
470
- break;
471
- }
472
- }
473
- }
474
- return $mDefault;
475
  }
476
 
477
  public function getOptDefinition( string $key ) :array {
478
- $def = [];
479
- foreach ( $this->getRawData_AllOptions() as $option ) {
480
- if ( $option[ 'key' ] == $key ) {
481
- $def = $option;
482
- break;
 
 
 
 
 
483
  }
484
  }
485
  return $def;
@@ -492,8 +451,7 @@ class Options {
492
  * @return bool
493
  */
494
  public function isOpt( string $key, $mValueToTest, $strict = false ) :bool {
495
- $mOptionValue = $this->getOpt( $key );
496
- return $strict ? $mOptionValue === $mValueToTest : $mOptionValue == $mValueToTest;
497
  }
498
 
499
  /**
@@ -501,21 +459,13 @@ class Options {
501
  * @return string|null
502
  */
503
  public function getOptionType( $key ) {
504
- $def = $this->getRawData_SingleOption( $key );
505
- if ( !empty( $def ) && isset( $def[ 'type' ] ) ) {
506
- return $def[ 'type' ];
507
- }
508
- return null;
509
  }
510
 
511
  public function getOptionsKeys() :array {
512
  if ( !isset( $this->aOptionsKeys ) ) {
513
- $this->aOptionsKeys = [];
514
- foreach ( $this->getRawData_AllOptions() as $aOption ) {
515
- $this->aOptionsKeys[] = $aOption[ 'key' ];
516
- }
517
  $this->aOptionsKeys = array_merge(
518
- $this->aOptionsKeys,
519
  $this->getCommonStandardOptions(),
520
  $this->getVirtualCommonOptions()
521
  );
@@ -523,113 +473,63 @@ class Options {
523
  return $this->aOptionsKeys;
524
  }
525
 
526
- /**
527
- * @return string
528
- */
529
- public function getPathToConfig() {
530
- return $this->sPathToConfig;
531
- }
532
-
533
- /**
534
- * @return string
535
- */
536
- protected function getConfigModTime() {
537
- return Services::WpFs()->getModifiedTime( $this->getPathToConfig() );
538
- }
539
-
540
- /**
541
- * @return string
542
- */
543
- public function getOptionsStorageKey() {
544
- return $this->sOptionsStorageKey;
545
- }
546
-
547
  /**
548
  * @param string $key
549
  * @param string $prop
550
  * @return mixed|null
551
  */
552
  public function getOptProperty( string $key, string $prop ) {
553
- $opt = $this->getRawData_SingleOption( $key );
554
- return $opt[ $prop ] ?? null;
555
- }
556
-
557
- public function getStoredOptions() :array {
558
- try {
559
- return $this->loadOptionsValuesFromStorage();
560
- }
561
- catch ( \Exception $e ) {
562
- return [];
563
- }
564
  }
565
 
566
  public function getRawData_FullFeatureConfig() :array {
567
  if ( empty( $this->aRawOptionsConfigData ) ) {
568
- $this->aRawOptionsConfigData = $this->readConfiguration();
 
 
 
 
 
569
  }
570
  return $this->aRawOptionsConfigData;
571
  }
572
 
573
  protected function getRawData_AllOptions() :array {
574
- $raw = $this->getRawData_FullFeatureConfig();
575
- return $raw[ 'options' ] ?? [];
576
  }
577
 
578
  protected function getRawData_OptionsSections() :array {
579
- $raw = $this->getRawData_FullFeatureConfig();
580
- return $raw[ 'sections' ] ?? [];
581
  }
582
 
583
  protected function getRawData_Requirements() :array {
584
- $raw = $this->getRawData_FullFeatureConfig();
585
- return $raw[ 'requirements' ] ?? [];
586
- }
587
-
588
- public function getRawData_SingleOption( string $key ) :array {
589
- foreach ( $this->getRawData_AllOptions() as $opt ) {
590
- if ( isset( $opt[ 'key' ] ) && ( $key == $opt[ 'key' ] ) ) {
591
- return $opt;
592
- }
593
- }
594
- return [];
595
- }
596
-
597
- public function getRebuildFromFile() :bool {
598
- return (bool)$this->bRebuildFromFile;
599
  }
600
 
601
- /**
602
- * @param string $key
603
- * @return string
604
- */
605
- public function getSelectOptionValueText( string $key ) {
606
- $sText = '';
607
  foreach ( $this->getOptDefinition( $key )[ 'value_options' ] as $opt ) {
608
  if ( $opt[ 'value_key' ] == $this->getOpt( $key ) ) {
609
- $sText = $opt[ 'text' ];
610
  break;
611
  }
612
  }
613
- return $sText;
614
  }
615
 
616
  public function isAccessRestricted() :bool {
617
  $state = $this->getFeatureProperty( 'access_restricted' );
618
- return is_null( $state ) ? true : (bool)$state;
619
- }
620
-
621
- public function isModulePremium() :bool {
622
- return (bool)$this->getFeatureProperty( 'premium' );
623
  }
624
 
625
  public function isModuleRunIfWhitelisted() :bool {
626
  $state = $this->getFeatureProperty( 'run_if_whitelisted' );
627
- return is_null( $state ) ? true : (bool)$state;
628
  }
629
 
630
  public function isModuleRunUnderWpCli() :bool {
631
  $state = $this->getFeatureProperty( 'run_if_wpcli' );
632
- return is_null( $state ) ? true : (bool)$state;
633
  }
634
 
635
  public function isModuleRunIfVerifiedBot() :bool {
@@ -649,7 +549,7 @@ class Options {
649
  }
650
 
651
  public function optExists( string $key ) :bool {
652
- return !empty( $this->getRawData_SingleOption( $key ) );
653
  }
654
 
655
  public function resetOptToDefault( string $key ) :self {
@@ -660,92 +560,64 @@ class Options {
660
  * Will traverse each premium option and set it to the default.
661
  */
662
  public function resetPremiumOptsToDefault() {
663
- foreach ( $this->getRawData_AllOptions() as $aOption ) {
664
- if ( isset( $aOption[ 'premium' ] ) && $aOption[ 'premium' ] ) {
665
- $this->resetOptToDefault( $aOption[ 'key' ] );
666
  }
667
  }
668
  }
669
 
670
- public function setOptionsStorageKey( string $key ) :self {
671
- $this->sOptionsStorageKey = $key;
672
- return $this;
673
- }
674
-
675
- public function setIfLoadOptionsFromStorage( bool $bLoadFromSaved ) :self {
676
- $this->bLoadFromSaved = $bLoadFromSaved;
677
- return $this;
678
- }
679
-
680
  public function setNeedSave( bool $need ) {
681
  $this->bNeedSave = $need;
682
  }
683
 
684
- /**
685
- * @param bool $bRebuild
686
- * @return $this
687
- */
688
- public function setRebuildFromFile( $bRebuild ) {
689
- $this->bRebuildFromFile = $bRebuild;
690
- return $this;
691
- }
692
-
693
- /**
694
- * @param array $aOptions
695
- */
696
- public function setMultipleOptions( $aOptions ) {
697
- if ( is_array( $aOptions ) ) {
698
- foreach ( $aOptions as $sKey => $mValue ) {
699
- $this->setOpt( $sKey, $mValue );
700
- }
701
  }
702
  }
703
 
704
  /**
705
- * @param string $sOptKey
706
- * @param mixed $mNewValue
707
  * @return $this
708
  */
709
- public function setOpt( $sOptKey, $mNewValue ) :self {
710
 
711
- // NOTE: can't use getOpt() for current as it'll create infinite loop
712
- $aOptVals = $this->getAllOptionsValues();
713
- $mCurrent = isset( $aOptVals[ $sOptKey ] ) ? $aOptVals[ $sOptKey ] : null;
714
 
715
  try {
716
- $mNewValue = ( new OptValueSanitize() )
717
  ->setMod( $this->getMod() )
718
- ->run( $sOptKey, $mNewValue );
719
- $bVerified = true;
720
  }
721
  catch ( \Exception $e ) {
722
- $bVerified = false;
723
  }
724
 
725
- if ( $bVerified ) {
726
  // Here we try to ensure that values that are repeatedly changed properly reflect their changed
727
  // states, as they may be reverted back to their original state and we "think" it's been changed.
728
- $bValueIsDifferent = serialize( $mCurrent ) !== serialize( $mNewValue );
729
  // basically if we're actually resetting back to the original value
730
- $bIsResetting = $bValueIsDifferent && $this->isOptChanged( $sOptKey )
731
- && ( serialize( $this->getOldValue( $sOptKey ) ) === serialize( $mNewValue ) );
732
 
733
- if ( $bValueIsDifferent && $this->verifyCanSet( $sOptKey, $mNewValue ) ) {
734
  $this->setNeedSave( true );
735
 
736
  //Load the config and do some pre-set verification where possible. This will slowly grow.
737
- $aOption = $this->getRawData_SingleOption( $sOptKey );
738
- if ( !empty( $aOption[ 'type' ] ) ) {
739
- if ( $aOption[ 'type' ] == 'boolean' && !is_bool( $mNewValue ) ) {
740
- return $this->resetOptToDefault( $sOptKey );
741
- }
742
  }
743
- $this->setOldOptValue( $sOptKey, $mCurrent )
744
- ->setOptValue( $sOptKey, $mNewValue );
745
  }
746
 
747
  if ( $bIsResetting ) {
748
- unset( $this->aOld[ $sOptKey ] );
749
  }
750
  }
751
 
@@ -753,25 +625,23 @@ class Options {
753
  }
754
 
755
  /**
756
- * @param string $sOpt
757
- * @param int $nAt
758
  * @return $this
759
  */
760
- public function setOptAt( $sOpt, $nAt = null ) {
761
- $nAt = is_null( $nAt ) ? Services::Request()->ts() : max( 0, (int)$nAt );
762
- return $this->setOpt( $sOpt, $nAt );
763
  }
764
 
765
  /**
766
  * Use this to directly set the option value without the risk of any recursion.
767
- * @param string $sOptKey
768
- * @param mixed $mValue
769
  * @return $this
770
  */
771
- public function setOptValue( $sOptKey, $mValue ) {
772
- $aValues = $this->getAllOptionsValues();
773
- $aValues[ $sOptKey ] = $mValue;
774
- $this->aOptionsValues = $aValues;
775
  return $this;
776
  }
777
 
@@ -798,14 +668,17 @@ class Options {
798
  }
799
  break;
800
 
 
 
 
 
801
  case 'select':
802
- $aPossible = array_map(
803
- function ( $aPoss ) {
804
- return $aPoss[ 'value_key' ];
805
  },
806
  $this->getOptProperty( $key, 'value_options' )
807
- );
808
- $valid = in_array( $mPotentialValue, $aPossible );
809
  break;
810
 
811
  case 'email':
@@ -816,16 +689,16 @@ class Options {
816
  }
817
 
818
  /**
819
- * @param string $sOptionKey
820
- * @param mixed $mValue
821
  * @return $this
822
  */
823
- private function setOldOptValue( $sOptionKey, $mValue ) {
824
  if ( !is_array( $this->aOld ) ) {
825
  $this->aOld = [];
826
  }
827
- if ( !isset( $this->aOld[ $sOptionKey ] ) ) {
828
- $this->aOld[ $sOptionKey ] = $mValue;
829
  }
830
  return $this;
831
  }
@@ -845,6 +718,7 @@ class Options {
845
  /**
846
  * @return array
847
  */
 
848
  protected function getCommonStandardOptions() {
849
  return [];
850
  }
@@ -878,110 +752,168 @@ class Options {
878
  }
879
  }
880
 
 
 
 
 
 
 
 
881
  /**
882
- * @param bool $reload
883
- * @return array
884
- * @throws \Exception
885
  */
886
- private function loadOptionsValuesFromStorage( bool $reload = false ) :array {
887
-
888
- if ( $reload || empty( $this->aOptionsValues ) ) {
889
 
890
- if ( $this->getIfLoadOptionsFromStorage() ) {
 
 
 
 
 
891
 
892
- $key = $this->getOptionsStorageKey();
893
- if ( empty( $key ) ) {
894
- throw new \Exception( 'Options Storage Key Is Empty' );
895
- }
896
- $this->aOptionsValues = Services::WpGeneral()->getOption( $key, [] );
897
- }
 
898
  }
899
- if ( !is_array( $this->aOptionsValues ) ) {
900
- $this->aOptionsValues = [];
901
- $this->setNeedSave( true );
902
  }
903
- return $this->aOptionsValues;
904
  }
905
 
906
- private function readConfiguration() :array {
907
- $cfg = Transient::Get( $this->getConfigStorageKey() );
 
 
 
 
 
 
 
908
 
909
- $bRebuild = $this->getRebuildFromFile() || empty( $cfg ) || !is_array( $cfg );
910
- if ( !$bRebuild ) {
911
- if ( !isset( $cfg[ 'meta_modts' ] ) ) {
912
- $cfg[ 'meta_modts' ] = 0;
913
- }
914
- $bRebuild = $this->getConfigModTime() > $cfg[ 'meta_modts' ];
915
  }
916
-
917
- if ( $bRebuild ) {
918
- try {
919
- $cfg = $this->readConfigurationJson();
920
- }
921
- catch ( \Exception $e ) {
922
- if ( Services::WpGeneral()->isDebug() ) {
923
- trigger_error( $e->getMessage() );
924
- }
925
- $cfg = [];
926
- }
927
- $cfg[ 'meta_modts' ] = $this->getConfigModTime();
928
- Transient::Set( $this->getConfigStorageKey(), $cfg );
929
  }
 
930
 
931
- $this->setRebuildFromFile( $bRebuild );
932
- return $cfg;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
933
  }
934
 
935
  /**
936
  * @return array
937
  * @throws \Exception
 
938
  */
939
  private function readConfigurationJson() :array {
940
- $cfg = json_decode( $this->readConfigurationFileContents(), true );
941
- if ( empty( $cfg ) || !is_array( $cfg ) ) {
942
- throw new \Exception( sprintf( 'Reading JSON configuration from file "%s" failed.', $this->getSlug() ) );
943
- }
944
- return $cfg;
945
  }
946
 
947
  /**
948
- * @return string
949
- * @throws \Exception
950
  */
951
- private function readConfigurationFileContents() {
952
- if ( !$this->getConfigFileExists() ) {
953
- throw new \Exception( sprintf( 'Configuration file "%s" does not exist.', $this->getPathToConfig() ) );
954
- }
955
- return Services::Data()->readFileWithInclude( $this->getPathToConfig() );
956
  }
957
 
958
- public function getConfigStorageKey() :string {
959
- return 'shield_mod_config_'.md5(
960
- str_replace( wp_normalize_path( ABSPATH ), '', wp_normalize_path( $this->getPathToConfig() ) )
961
- );
 
962
  }
963
 
964
- private function getConfigFileExists() :bool {
965
- $sPath = $this->getPathToConfig();
966
- return !empty( $sPath ) && Services::WpFs()->isFile( $sPath );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
967
  }
968
 
969
  /**
970
  * @param string $sPathToConfig
971
  * @return $this
 
972
  */
973
  public function setPathToConfig( $sPathToConfig ) {
974
- $this->sPathToConfig = $sPathToConfig;
975
  return $this;
976
  }
977
 
978
  /**
979
- * @param $aValues
980
- * @return $this
981
  */
982
- public function setOptionsValues( array $aValues = [] ) {
983
- $this->aOptionsValues = $aValues;
984
- $this->setNeedSave( true );
 
 
 
 
 
985
  return $this;
986
  }
987
  }
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Options\OptValueSanitize;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
  use FernleafSystems\Wordpress\Services\Services;
 
8
 
9
  class Options {
10
 
11
  use ModConsumer;
12
 
13
+ private $cfgLoader;
14
+
15
+ private $optsStorage;
16
+
17
  /**
18
  * @var array
19
  */
32
  /**
33
  * @var bool
34
  */
35
+ protected $bNeedSave = false;
 
 
 
 
 
36
 
37
  /**
38
  * @var string
39
  */
40
  protected $aOptionsKeys;
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  public function __construct() {
43
  }
44
 
45
  /**
46
+ * @param bool $deleteFirst Used primarily with plugin reset
47
+ * @param bool $isPremium
48
  * @return bool
49
  */
50
+ public function doOptionsSave( $deleteFirst = false, $isPremium = false ) {
51
  if ( !$this->getNeedSave() ) {
52
  return true;
53
  }
54
  $this->cleanOptions();
55
+ if ( !$isPremium ) {
56
  $this->resetPremiumOptsToDefault();
57
  }
58
  $this->setNeedSave( false );
59
+
60
+ return $this->getOptsStorage()->storeOptions( $this->getAllOptionsValues(), $deleteFirst );
 
 
61
  }
62
 
 
 
 
63
  public function deleteStorage() {
64
+ $this->getOptsStorage()->deleteOptions();
 
 
65
  }
66
 
67
  public function getAllOptionsValues() :array {
68
+ if ( !isset( $this->aOptionsValues ) ) {
69
+ try {
70
+ $this->aOptionsValues = $this->getOptsStorage()->loadOptions();
71
+ }
72
+ catch ( \Exception $e ) {
73
+ $this->aOptionsValues = [];
74
+ $this->setNeedSave( true );
75
+ }
76
+ }
77
+ return $this->aOptionsValues;
78
  }
79
 
80
  public function getSlug() :string {
85
  * Returns an array of all the transferable options and their values
86
  * @return array
87
  */
88
+ public function getTransferableOptions() :array {
89
+ $transferable = [];
90
 
91
+ foreach ( $this->getRawData_AllOptions() as $option ) {
92
+ if ( $option[ 'transferable' ] ?? true ) {
93
+ $transferable[ $option[ 'key' ] ] = $this->getOpt( $option[ 'key' ] );
94
  }
95
  }
96
+ return $transferable;
97
  }
98
 
99
  /**
103
  public function getOptionsMaskSensitive() {
104
 
105
  $aOptions = $this->getAllOptionsValues();
106
+ foreach ( $this->getOptionsKeys() as $key ) {
107
+ if ( !isset( $aOptions[ $key ] ) ) {
108
+ $aOptions[ $key ] = $this->getOptDefault( $key );
109
  }
110
  }
111
+ foreach ( $this->getRawData_AllOptions() as $optDef ) {
112
+ if ( isset( $optDef[ 'sensitive' ] ) && $optDef[ 'sensitive' ] === true ) {
113
+ unset( $aOptions[ $optDef[ 'key' ] ] );
114
  }
115
  }
116
  return array_diff_key( $aOptions, array_flip( $this->getVirtualCommonOptions() ) );
122
  public function getOptionsForWpCli() :array {
123
  return array_filter(
124
  $this->getOptionsKeys(),
125
+ function ( $key ) {
126
+ $opt = $this->getOptDefinition( $key );
127
+ return !empty( $opt[ 'section' ] ) && $opt[ 'section' ] !== 'section_non_ui';
 
128
  }
129
  );
130
  }
137
  $opts = [];
138
  if ( (bool)$this->getFeatureProperty( 'tracking_exclude' ) === false ) {
139
 
140
+ $options = $this->getAllOptionsValues();
141
+ foreach ( $this->getOptionsKeys() as $key ) {
142
+ if ( !isset( $options[ $key ] ) ) {
143
+ $options[ $key ] = $this->getOptDefault( $key );
144
  }
145
  }
146
+ foreach ( $this->getRawData_AllOptions() as $optDef ) {
147
+ if ( !empty( $optDef[ 'sensitive' ] ) || !empty( $optDef[ 'tracking_exclude' ] ) ) {
148
+ unset( $options[ $optDef[ 'key' ] ] );
149
  }
150
  }
151
+ $opts = array_diff_key( $options, array_flip( $this->getVirtualCommonOptions() ) );
152
  }
153
  return $opts;
154
  }
155
 
156
  /**
157
+ * @param string $property
158
+ * @return mixed|null
159
  */
160
+ public function getFeatureProperty( string $property ) {
161
+ return ( $this->getRawData_FullFeatureConfig()[ 'properties' ] ?? [] )[ $property ] ?? null;
 
162
  }
163
 
164
  public function getWpCliCfg() :array {
 
165
  return array_merge(
166
  [
167
  'enabled' => true,
168
  'root' => $this->getSlug(),
169
  ],
170
+ $this->getRawData_FullFeatureConfig()[ 'wpcli' ] ?? []
171
  );
172
  }
173
 
176
  * @return mixed|null
177
  */
178
  public function getDef( string $key ) {
179
+ return ( $this->getRawData_FullFeatureConfig()[ 'definitions' ] ?? [] )[ $key ] ?? null;
 
180
  }
181
 
182
+ public function getEvents() :array {
183
+ return is_array( $this->getDef( 'events' ) ) ? $this->getDef( 'events' ) : [];
 
 
 
 
 
184
  }
185
 
186
+ public function getFeatureRequirement( string $req ) :array {
187
+ return $this->getRawData_Requirements()[ $req ] ?? [];
188
+ }
189
+
190
+ public function getAdminNotices() :array {
191
+ return $this->getRawData_FullFeatureConfig()[ 'admin_notices' ] ?? [];
192
  }
193
 
194
  /**
198
  return $this->getFeatureProperty( 'tagline' );
199
  }
200
 
 
 
 
 
 
 
 
201
  public function isValidOptionKey( string $key ) :bool {
202
  return in_array( $key, $this->getOptionsKeys() );
203
  }
204
 
205
+ public function isValidOptionValueType( string $key, $value ) :bool {
206
+ switch ( $this->getOptionType( $key ) ) {
207
+ case 'array':
208
+ $valid = is_array( $value );
209
+ break;
210
+ default:
211
+ $valid = true;
212
+ break;
213
+ }
214
+ return $valid;
215
+ }
216
+
217
  /**
218
  * @return array[]
219
  */
221
 
222
  $aOptionsData = [];
223
 
224
+ foreach ( $this->getRawData_OptionsSections() as $aRawSection ) {
225
 
226
  // if hidden isn't specified we skip
227
  if ( !isset( $aRawSection[ 'hidden' ] ) || !$aRawSection[ 'hidden' ] ) {
243
  * @return array|null
244
  */
245
  public function getSection( string $section ) {
246
+ return $this->getSections()[ $section ] ?? null;
 
247
  }
248
 
249
  /**
250
+ * @param bool $includeHidden
251
  * @return array[]
252
  */
253
+ public function getSections( $includeHidden = false ) {
254
+ $sections = [];
255
+ foreach ( $this->getRawData_OptionsSections() as $section ) {
256
+ if ( $includeHidden || empty( $section[ 'hidden' ] ) ) {
257
+ $sections[ $section[ 'slug' ] ] = $section;
258
  }
259
  }
260
+ return $sections;
261
  }
262
 
263
  public function getPrimarySection() :array {
264
+ $theSection = [];
265
+ foreach ( $this->getSections() as $section ) {
266
+ if ( $section[ 'primary' ] ?? false ) {
267
+ $theSection = $section;
268
  break;
269
  }
270
  }
271
+ return $theSection;
272
  }
273
 
274
  /**
276
  * @return array
277
  */
278
  public function getSection_Requirements( $slug ) {
279
+ $section = $this->getSection( $slug );
 
280
  return array_merge(
281
  [
282
+ 'php_min' => '7.0',
283
+ 'wp_min' => '3.7',
284
  ],
285
+ ( is_array( $section ) && isset( $section[ 'reqs' ] ) ) ? $section[ 'reqs' ] : []
286
  );
287
  }
288
 
 
 
 
 
 
 
 
 
 
289
  /**
290
  * @param string $slug
291
  * @return bool
296
  && Services::WpGeneral()->getWordpressIsAtLeastVersion( $reqs[ 'wp_min' ] );
297
  }
298
 
299
+ public function isOptReqsMet( string $key ) :bool {
300
+ return $this->isSectionReqsMet( $this->getOptProperty( $key, 'section' ) );
 
 
 
 
301
  }
302
 
303
  /**
304
  * @return string[]
305
  */
306
  public function getVisibleOptionsKeys() :array {
307
+ $keys = [];
308
 
309
+ foreach ( $this->getRawData_AllOptions() as $optDef ) {
310
+ if ( $optDef[ 'hidden' ] ?? false ) {
311
  continue;
312
  }
313
+ $section = $this->getSection( $optDef[ 'section' ] );
314
+ if ( empty( $section ) || ( $section[ 'hidden' ] ?? false ) ) {
315
  continue;
316
  }
317
 
318
+ $keys[] = $optDef[ 'key' ];
319
  }
320
 
321
+ return $keys;
322
  }
323
 
324
  public function getOptionsForPluginUse() :array {
325
 
326
+ $optionsData = [];
327
 
328
+ foreach ( $this->getRawData_OptionsSections() as $section ) {
329
 
330
+ if ( isset( $section[ 'hidden' ] ) && $section[ 'hidden' ] ) {
331
  continue;
332
  }
333
 
334
+ $section = array_merge(
335
  [
336
  'primary' => false,
337
+ 'options' => $this->getOptionsForSection( $section[ 'slug' ] ),
338
  'help_video_id' => ''
339
  ],
340
+ $section
341
  );
342
 
343
+ if ( !empty( $section[ 'options' ] ) ) {
344
+ $optionsData[] = $section;
345
  }
346
  }
347
 
348
+ return $optionsData;
349
  }
350
 
351
+ protected function getOptionsForSection( string $slug ) :array {
 
 
 
 
352
 
353
+ $allOptions = [];
354
+ foreach ( $this->getRawData_AllOptions() as $optDef ) {
355
 
356
+ if ( ( $optDef[ 'section' ] != $slug ) || ( isset( $optDef[ 'hidden' ] ) && $optDef[ 'hidden' ] ) ) {
357
  continue;
358
  }
359
 
360
+ if ( isset( $optDef[ 'hidden' ] ) && $optDef[ 'hidden' ] ) {
361
  continue;
362
  }
363
 
364
+ $optDef = array_merge(
365
  [
366
  'link_info' => '',
367
  'link_blog' => '',
368
  'help_video_id' => '',
369
+ 'value_options' => [],
370
+ 'premium' => false,
371
+ 'advanced' => false
372
  ],
373
+ $optDef
374
  );
375
+ $optDef[ 'value' ] = $this->getOpt( $optDef[ 'key' ] );
376
 
377
+ if ( in_array( $optDef[ 'type' ], [ 'select', 'multiple_select' ] ) ) {
378
+ $convertedOptions = [];
379
+ foreach ( $optDef[ 'value_options' ] as $selectValues ) {
380
+ $convertedOptions[ $selectValues[ 'value_key' ] ] = __( $selectValues[ 'text' ], 'wp-simple-firewall' );
381
  }
382
+ $optDef[ 'value_options' ] = $convertedOptions;
383
  }
384
 
385
+ $allOptions[] = $optDef;
386
  }
387
+ return $allOptions;
388
  }
389
 
390
  public function getAdditionalMenuItems() :array {
392
  }
393
 
394
  public function getNeedSave() :bool {
395
+ return $this->bNeedSave;
396
  }
397
 
398
  /**
409
  * @return mixed
410
  */
411
  public function getOpt( string $key, $mDefault = false ) {
412
+ $value = $this->getAllOptionsValues()[ $key ] ?? null;
413
+
414
+ if ( is_null( $value ) || !$this->isValidOptionValueType( $key, $value ) ) {
415
+ $value = $this->getOptDefault( $key, $mDefault );
416
+ $this->setOpt( $key, $value );
417
  }
418
  return $this->aOptionsValues[ $key ] ?? $mDefault;
419
  }
424
  * @return mixed|null
425
  */
426
  public function getOptDefault( string $key, $mDefault = null ) {
427
+ $def = $this->getOptDefinition( $key );
428
+ return $def[ 'default' ] ?? ( $def[ 'value' ] ?? $mDefault );
 
 
 
 
 
 
 
 
 
 
 
429
  }
430
 
431
  public function getOptDefinition( string $key ) :array {
432
+ $def = $this->getRawData_AllOptions()[ $key ] ?? [];
433
+ if ( empty( $def ) ) {
434
+ /**
435
+ * @deprecated 12.0 - this is the fallback before we switched to using keys
436
+ */
437
+ foreach ( $this->getRawData_AllOptions() as $option ) {
438
+ if ( $option[ 'key' ] == $key ) {
439
+ $def = $option;
440
+ break;
441
+ }
442
  }
443
  }
444
  return $def;
451
  * @return bool
452
  */
453
  public function isOpt( string $key, $mValueToTest, $strict = false ) :bool {
454
+ return $strict ? $this->getOpt( $key ) === $mValueToTest : $this->getOpt( $key ) == $mValueToTest;
 
455
  }
456
 
457
  /**
459
  * @return string|null
460
  */
461
  public function getOptionType( $key ) {
462
+ return $this->getOptDefinition( $key )[ 'type' ] ?? null;
 
 
 
 
463
  }
464
 
465
  public function getOptionsKeys() :array {
466
  if ( !isset( $this->aOptionsKeys ) ) {
 
 
 
 
467
  $this->aOptionsKeys = array_merge(
468
+ array_keys( $this->getRawData_AllOptions() ),
469
  $this->getCommonStandardOptions(),
470
  $this->getVirtualCommonOptions()
471
  );
473
  return $this->aOptionsKeys;
474
  }
475
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  /**
477
  * @param string $key
478
  * @param string $prop
479
  * @return mixed|null
480
  */
481
  public function getOptProperty( string $key, string $prop ) {
482
+ return $this->getOptDefinition( $key )[ $prop ] ?? null;
 
 
 
 
 
 
 
 
 
 
483
  }
484
 
485
  public function getRawData_FullFeatureConfig() :array {
486
  if ( empty( $this->aRawOptionsConfigData ) ) {
487
+ try {
488
+ $this->aRawOptionsConfigData = $this->getConfigLoader()->run();
489
+ }
490
+ catch ( \Exception $e ) {
491
+ $this->aRawOptionsConfigData = [];
492
+ }
493
  }
494
  return $this->aRawOptionsConfigData;
495
  }
496
 
497
  protected function getRawData_AllOptions() :array {
498
+ return $this->getRawData_FullFeatureConfig()[ 'options' ] ?? [];
 
499
  }
500
 
501
  protected function getRawData_OptionsSections() :array {
502
+ return $this->getRawData_FullFeatureConfig()[ 'sections' ] ?? [];
 
503
  }
504
 
505
  protected function getRawData_Requirements() :array {
506
+ return $this->getRawData_FullFeatureConfig()[ 'requirements' ] ?? [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
  }
508
 
509
+ public function getSelectOptionValueText( string $key ) :string {
510
+ $text = '';
 
 
 
 
511
  foreach ( $this->getOptDefinition( $key )[ 'value_options' ] as $opt ) {
512
  if ( $opt[ 'value_key' ] == $this->getOpt( $key ) ) {
513
+ $text = $opt[ 'text' ];
514
  break;
515
  }
516
  }
517
+ return $text;
518
  }
519
 
520
  public function isAccessRestricted() :bool {
521
  $state = $this->getFeatureProperty( 'access_restricted' );
522
+ return is_null( $state ) || $state;
 
 
 
 
523
  }
524
 
525
  public function isModuleRunIfWhitelisted() :bool {
526
  $state = $this->getFeatureProperty( 'run_if_whitelisted' );
527
+ return is_null( $state ) || $state;
528
  }
529
 
530
  public function isModuleRunUnderWpCli() :bool {
531
  $state = $this->getFeatureProperty( 'run_if_wpcli' );
532
+ return is_null( $state ) || $state;
533
  }
534
 
535
  public function isModuleRunIfVerifiedBot() :bool {
549
  }
550
 
551
  public function optExists( string $key ) :bool {
552
+ return !empty( $this->getOptDefinition( $key ) );
553
  }
554
 
555
  public function resetOptToDefault( string $key ) :self {
560
  * Will traverse each premium option and set it to the default.
561
  */
562
  public function resetPremiumOptsToDefault() {
563
+ foreach ( $this->getRawData_AllOptions() as $opt ) {
564
+ if ( $opt[ 'premium' ] ?? false ) {
565
+ $this->resetOptToDefault( $opt[ 'key' ] );
566
  }
567
  }
568
  }
569
 
 
 
 
 
 
 
 
 
 
 
570
  public function setNeedSave( bool $need ) {
571
  $this->bNeedSave = $need;
572
  }
573
 
574
+ public function setMultipleOptions( array $options ) {
575
+ foreach ( $options as $key => $value ) {
576
+ $this->setOpt( $key, $value );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
577
  }
578
  }
579
 
580
  /**
581
+ * @param string $key
582
+ * @param mixed $newValue
583
  * @return $this
584
  */
585
+ public function setOpt( $key, $newValue ) :self {
586
 
587
+ // NOTE: can't use getOpt() for current value as it'll create infinite loop
588
+ $mCurrent = $this->getAllOptionsValues()[ $key ] ?? null;
 
589
 
590
  try {
591
+ $newValue = ( new OptValueSanitize() )
592
  ->setMod( $this->getMod() )
593
+ ->run( $key, $newValue );
594
+ $verified = true;
595
  }
596
  catch ( \Exception $e ) {
597
+ $verified = false;
598
  }
599
 
600
+ if ( $verified ) {
601
  // Here we try to ensure that values that are repeatedly changed properly reflect their changed
602
  // states, as they may be reverted back to their original state and we "think" it's been changed.
603
+ $bValueIsDifferent = serialize( $mCurrent ) !== serialize( $newValue );
604
  // basically if we're actually resetting back to the original value
605
+ $bIsResetting = $bValueIsDifferent && $this->isOptChanged( $key )
606
+ && ( serialize( $this->getOldValue( $key ) ) === serialize( $newValue ) );
607
 
608
+ if ( $bValueIsDifferent && $this->verifyCanSet( $key, $newValue ) ) {
609
  $this->setNeedSave( true );
610
 
611
  //Load the config and do some pre-set verification where possible. This will slowly grow.
612
+ if ( $this->getOptionType( $key ) === 'boolean' && !is_bool( $newValue ) ) {
613
+ return $this->resetOptToDefault( $key );
 
 
 
614
  }
615
+ $this->setOldOptValue( $key, $mCurrent )
616
+ ->setOptValue( $key, $newValue );
617
  }
618
 
619
  if ( $bIsResetting ) {
620
+ unset( $this->aOld[ $key ] );
621
  }
622
  }
623
 
625
  }
626
 
627
  /**
628
+ * @param string $key
 
629
  * @return $this
630
  */
631
+ public function setOptAt( string $key ) {
632
+ return $this->setOpt( $key, Services::Request()->ts() );
 
633
  }
634
 
635
  /**
636
  * Use this to directly set the option value without the risk of any recursion.
637
+ * @param string $key
638
+ * @param mixed $value
639
  * @return $this
640
  */
641
+ protected function setOptValue( string $key, $value ) {
642
+ $values = $this->getAllOptionsValues();
643
+ $values[ $key ] = $value;
644
+ $this->aOptionsValues = $values;
645
  return $this;
646
  }
647
 
668
  }
669
  break;
670
 
671
+ case 'array':
672
+ $valid = is_array( $mPotentialValue );
673
+ break;
674
+
675
  case 'select':
676
+ $valid = in_array( $mPotentialValue, array_map(
677
+ function ( $valueOptions ) {
678
+ return $valueOptions[ 'value_key' ];
679
  },
680
  $this->getOptProperty( $key, 'value_options' )
681
+ ) );
 
682
  break;
683
 
684
  case 'email':
689
  }
690
 
691
  /**
692
+ * @param string $key
693
+ * @param mixed $value
694
  * @return $this
695
  */
696
+ private function setOldOptValue( $key, $value ) {
697
  if ( !is_array( $this->aOld ) ) {
698
  $this->aOld = [];
699
  }
700
+ if ( !isset( $this->aOld[ $key ] ) ) {
701
+ $this->aOld[ $key ] = $value;
702
  }
703
  return $this;
704
  }
718
  /**
719
  * @return array
720
  */
721
+
722
  protected function getCommonStandardOptions() {
723
  return [];
724
  }
752
  }
753
  }
754
 
755
+ public function getConfigLoader() :Config\LoadConfig {
756
+ if ( empty( $this->cfgLoader ) ) {
757
+ $this->cfgLoader = ( new Config\LoadConfig() )->setMod( $this->getMod() );
758
+ }
759
+ return $this->cfgLoader;
760
+ }
761
+
762
  /**
763
+ * @deprecated 12.0
 
 
764
  */
765
+ private function loadOptionsValuesFromStorage() :array {
766
+ return $this->getOptsStorage()->loadOptions();
767
+ }
768
 
769
+ private function getOptsStorage() :Options\Storage {
770
+ if ( empty( $this->optsStorage ) ) {
771
+ $this->optsStorage = ( new Options\Storage() )->setMod( $this->getMod() );
772
+ }
773
+ return $this->optsStorage;
774
+ }
775
 
776
+ /**
777
+ * @return array
778
+ * @deprecated 12.0
779
+ */
780
+ private function readConfiguration() :array {
781
+ try {
782
+ $cfg = $this->getConfigLoader()->run();
783
  }
784
+ catch ( \Exception $e ) {
785
+ $cfg = [];
 
786
  }
787
+ return $cfg;
788
  }
789
 
790
+ /**
791
+ * @param $values
792
+ * @return $this
793
+ */
794
+ public function setOptionsValues( array $values = [] ) {
795
+ $this->aOptionsValues = $values;
796
+ $this->setNeedSave( true );
797
+ return $this;
798
+ }
799
 
800
+ /**
801
+ * @deprecated 12.0
802
+ */
803
+ public function getStoredOptions() :array {
804
+ try {
805
+ return $this->loadOptionsValuesFromStorage();
806
  }
807
+ catch ( \Exception $e ) {
808
+ return [];
 
 
 
 
 
 
 
 
 
 
 
809
  }
810
+ }
811
 
812
+ /**
813
+ * @deprecated 12.0
814
+ */
815
+ public function getConfigStorageKey() :string {
816
+ return '';
817
+ }
818
+
819
+ /**
820
+ * @return string
821
+ * @throws \Exception
822
+ * @deprecated 12.0
823
+ */
824
+ private function readConfigurationFileContents() {
825
+ return '';
826
+ }
827
+
828
+ /**
829
+ * @deprecated 12.0
830
+ */
831
+ private function getConfigFileExists() :bool {
832
+ return true;
833
+ }
834
+
835
+ /**
836
+ * @deprecated 12.0
837
+ */
838
+ protected function getConfigModTime() :int {
839
+ return 0;
840
  }
841
 
842
  /**
843
  * @return array
844
  * @throws \Exception
845
+ * @deprecated 12.0
846
  */
847
  private function readConfigurationJson() :array {
848
+ throw new \Exception( sprintf( 'Reading JSON configuration from file "%s" failed.', $this->getSlug() ) );
 
 
 
 
849
  }
850
 
851
  /**
852
+ * @deprecated 12.0
 
853
  */
854
+ public function getPathToConfig() :string {
855
+ return '';
 
 
 
856
  }
857
 
858
+ /**
859
+ * @deprecated 12.0
860
+ */
861
+ public function getRawData_SingleOption( string $key ) :array {
862
+ return $this->getOptDefinition( $key );
863
  }
864
 
865
+ /**
866
+ * @deprecated 12.0
867
+ */
868
+ public function getOptionsStorageKey() :string {
869
+ return $this->getMod()->getOptionsStorageKey();
870
+ }
871
+
872
+ /**
873
+ * @param bool $bLoadFromSaved
874
+ * @return $this
875
+ * @deprecated 12.0
876
+ */
877
+ public function setIfLoadOptionsFromStorage( bool $bLoadFromSaved ) :self {
878
+ return $this;
879
+ }
880
+
881
+ /**
882
+ * @deprecated 12.0
883
+ */
884
+ public function getIfLoadOptionsFromStorage() {
885
+ return true;
886
+ }
887
+
888
+ /**
889
+ * @param string $key
890
+ * @return $this
891
+ * @deprecated 12.0
892
+ */
893
+ public function setOptionsStorageKey( string $key ) :self {
894
+ return $this;
895
  }
896
 
897
  /**
898
  * @param string $sPathToConfig
899
  * @return $this
900
+ * @deprecated 12.0
901
  */
902
  public function setPathToConfig( $sPathToConfig ) {
 
903
  return $this;
904
  }
905
 
906
  /**
907
+ * @deprecated 12.0
 
908
  */
909
+ public function getRebuildFromFile() :bool {
910
+ return false;
911
+ }
912
+
913
+ /**
914
+ * @deprecated 12.0
915
+ */
916
+ public function setRebuildFromFile( $bRebuild ) {
917
  return $this;
918
  }
919
  }
src/lib/src/Modules/Base/Options/OptValueSanitize.php CHANGED
@@ -10,71 +10,69 @@ class OptValueSanitize {
10
  use ModConsumer;
11
 
12
  /**
13
- * @param string $sKey
14
- * @param mixed $mVal
15
  * @return mixed
16
  * @throws \Exception
17
  */
18
- public function run( $sKey, $mVal ) {
19
  $opts = $this->getOptions();
20
- $raw = $opts->getRawData_SingleOption( $sKey );
21
-
22
- if ( !in_array( $sKey, $opts->getOptionsKeys() ) ) {
23
- throw new \Exception( sprintf( 'Not a valid option key for module: %s', $sKey ) );
24
  }
25
 
26
  $validValue = false;
27
- switch ( $opts->getOptionType( $sKey ) ) {
28
 
29
  case 'boolean':
30
- $validValue = is_bool( $mVal );
31
  break;
32
 
33
  case 'integer':
34
- $validValue = is_numeric( $mVal );
35
  if ( $validValue ) {
36
- $mVal = (int)$mVal;
37
  }
38
  break;
39
 
40
  case 'email':
41
- $mVal = trim( (string)$mVal );
42
- $validValue = empty( $mVal ) || Services::Data()->validEmail( $mVal );
43
  break;
44
 
45
  case 'array':
46
- $validValue = is_array( $mVal );
47
  break;
48
 
49
  case 'text':
50
- if ( is_null( $mVal ) || is_scalar( $mVal ) ) {
51
  $validValue = true;
52
- $mVal = (string)$mVal;
53
  }
54
  break;
55
 
56
  case 'select':
57
- $validValue = is_string( $mVal ) && strlen( $mVal ) > 0;
58
  break;
59
 
60
  case 'multiple_select':
61
- if ( is_array( $mVal ) ) {
62
  $validValue = count( array_diff(
63
- $mVal,
64
  array_map(
65
  function ( $aValueOption ) {
66
  return $aValueOption[ 'value_key' ];
67
  },
68
- $raw[ 'value_options' ]
69
  )
70
  ) ) === 0;
71
  }
72
  break;
73
 
74
  case 'checkbox':
75
- if ( is_string( $mVal ) ) {
76
- $mVal = strtoupper( $mVal );
77
- $validValue = in_array( $mVal, [ 'Y', 'N' ] );
78
  }
79
  break;
80
 
@@ -87,6 +85,6 @@ class OptValueSanitize {
87
  throw new \Exception( 'Not a valid value type for option.' );
88
  }
89
 
90
- return $mVal;
91
  }
92
  }
10
  use ModConsumer;
11
 
12
  /**
13
+ * @param string $key
14
+ * @param mixed $value
15
  * @return mixed
16
  * @throws \Exception
17
  */
18
+ public function run( $key, $value ) {
19
  $opts = $this->getOptions();
20
+ if ( !in_array( $key, $opts->getOptionsKeys() ) ) {
21
+ throw new \Exception( sprintf( 'Not a valid option key for module: %s', $key ) );
 
 
22
  }
23
 
24
  $validValue = false;
25
+ switch ( $opts->getOptionType( $key ) ) {
26
 
27
  case 'boolean':
28
+ $validValue = is_bool( $value );
29
  break;
30
 
31
  case 'integer':
32
+ $validValue = is_numeric( $value );
33
  if ( $validValue ) {
34
+ $value = (int)$value;
35
  }
36
  break;
37
 
38
  case 'email':
39
+ $value = trim( (string)$value );
40
+ $validValue = empty( $value ) || Services::Data()->validEmail( $value );
41
  break;
42
 
43
  case 'array':
44
+ $validValue = is_array( $value );
45
  break;
46
 
47
  case 'text':
48
+ if ( is_null( $value ) || is_scalar( $value ) ) {
49
  $validValue = true;
50
+ $value = (string)$value;
51
  }
52
  break;
53
 
54
  case 'select':
55
+ $validValue = is_string( $value ) && strlen( $value ) > 0;
56
  break;
57
 
58
  case 'multiple_select':
59
+ if ( is_array( $value ) ) {
60
  $validValue = count( array_diff(
61
+ $value,
62
  array_map(
63
  function ( $aValueOption ) {
64
  return $aValueOption[ 'value_key' ];
65
  },
66
+ $opts->getOptDefinition( $key )[ 'value_options' ]
67
  )
68
  ) ) === 0;
69
  }
70
  break;
71
 
72
  case 'checkbox':
73
+ if ( is_string( $value ) ) {
74
+ $value = strtoupper( $value );
75
+ $validValue = in_array( $value, [ 'Y', 'N' ] );
76
  }
77
  break;
78
 
85
  throw new \Exception( 'Not a valid value type for option.' );
86
  }
87
 
88
+ return $value;
89
  }
90
  }
src/lib/src/Modules/Base/Options/Storage.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Options;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+
8
+ class Storage {
9
+
10
+ use ModConsumer;
11
+
12
+ public function storeOptions( array $optsValues, bool $preDelete = false ) :bool {
13
+ if ( $preDelete ) {
14
+ $this->deleteOptions();
15
+ }
16
+ return (bool)Services::WpGeneral()->updateOption( $this->getMod()->getOptionsStorageKey(), $optsValues );
17
+ }
18
+
19
+ public function deleteOptions() {
20
+ Services::WpGeneral()->deleteOption( $this->getMod()->getOptionsStorageKey() );
21
+ }
22
+
23
+ /**
24
+ * @return array
25
+ * @throws \Exception
26
+ */
27
+ public function loadOptions() :array {
28
+ if ( $this->getCon()->getIsResetPlugin() ) {
29
+ throw new \Exception( 'Resetting plugin - not loading stored options' );
30
+ }
31
+ return $this->loadFromWP();
32
+ }
33
+
34
+ /**
35
+ * @return array
36
+ * @throws \Exception
37
+ */
38
+ private function loadFromWP() :array {
39
+ $values = Services::WpGeneral()->getOption( $this->getMod()->getOptionsStorageKey(), [] );
40
+ if ( empty( $values ) || !is_array( $values ) ) {
41
+ throw new \Exception( 'no values stored' );
42
+ }
43
+ return $values;
44
+ }
45
+ }
src/lib/src/Modules/Base/Processor.php CHANGED
@@ -16,8 +16,8 @@ abstract class Processor {
16
  */
17
  public function __construct( $mod ) {
18
  $this->setMod( $mod );
19
- add_action( 'init', [ $this, 'onWpInit' ], 9 );
20
- add_action( 'wp_loaded', [ $this, 'onWpLoaded' ] );
21
  add_action( $mod->prefix( 'plugin_shutdown' ), [ $this, 'onModuleShutdown' ] );
22
  $this->setupCronHooks();
23
  }
@@ -30,4 +30,15 @@ abstract class Processor {
30
 
31
  public function onModuleShutdown() {
32
  }
 
 
 
 
 
 
 
 
 
 
 
33
  }
16
  */
17
  public function __construct( $mod ) {
18
  $this->setMod( $mod );
19
+ add_action( 'init', [ $this, 'onWpInit' ], $this->getWpHookPriority( 'init' ) );
20
+ add_action( 'wp_loaded', [ $this, 'onWpLoaded' ], $this->getWpHookPriority( 'wp_loaded' ) );
21
  add_action( $mod->prefix( 'plugin_shutdown' ), [ $this, 'onModuleShutdown' ] );
22
  $this->setupCronHooks();
23
  }
30
 
31
  public function onModuleShutdown() {
32
  }
33
+
34
+ protected function getWpHookPriority( string $hook ) :int {
35
+ switch ( $hook ) {
36
+ case 'init':
37
+ $pri = 9;
38
+ break;
39
+ default:
40
+ $pri = 10;
41
+ }
42
+ return $pri;
43
+ }
44
  }
src/lib/src/Modules/Base/Strings.php CHANGED
@@ -148,19 +148,26 @@ class Strings {
148
  }
149
 
150
  /**
151
- * @return string[][]
 
 
 
 
 
 
 
 
 
152
  */
153
  protected function getAuditMessages() :array {
154
  return [];
155
  }
156
 
157
  /**
158
- * @param string $sKey
159
- * @return string[]
160
  */
161
- public function getAuditMessage( $sKey ) {
162
- $aMsg = $this->getAuditMessages();
163
- return isset( $aMsg[ $sKey ] ) ? $aMsg[ $sKey ] : [];
164
  }
165
 
166
  /**
@@ -190,9 +197,9 @@ class Strings {
190
  switch ( $section ) {
191
 
192
  case 'section_user_messages' :
193
- $sTitle = __( 'User Messages', 'wp-simple-firewall' );
194
- $sTitleShort = __( 'Messages', 'wp-simple-firewall' );
195
- $aSummary = [
196
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Customize the messages displayed to the user.', 'wp-simple-firewall' ) ),
197
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use this section if you need to communicate to the user in a particular manner.', 'wp-simple-firewall' ) ),
198
  sprintf( '%s: %s', __( 'Hint', 'wp-simple-firewall' ), sprintf( __( 'To reset any message to its default, enter the text exactly: %s', 'wp-simple-firewall' ), 'default' ) )
@@ -200,11 +207,11 @@ class Strings {
200
  break;
201
 
202
  default:
203
- $aSect = $this->getOptions()->getSection( $section );
204
- if ( is_array( $aSect ) && !empty( $aSect[ 'title' ] ) && !empty( $aSect[ 'title_short' ] ) ) {
205
- $sTitle = __( $aSect[ 'title' ], 'wp-simple-firewall' );
206
- $sTitleShort = __( $aSect[ 'title_short' ], 'wp-simple-firewall' );
207
- $aSummary = empty( $aSect[ 'summary' ] ) ? [] : $aSect[ 'summary' ];
208
  }
209
  else {
210
  throw new \Exception( sprintf( 'A section slug was defined but with no associated strings. Slug: "%s".', $section ) );
@@ -212,9 +219,9 @@ class Strings {
212
  }
213
 
214
  return [
215
- 'title' => $sTitle,
216
- 'title_short' => $sTitleShort,
217
- 'summary' => ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : [],
218
  ];
219
  }
220
  }
148
  }
149
 
150
  /**
151
+ * @param string $key
152
+ * @return string[]
153
+ * @deprecated 12.0
154
+ */
155
+ public function getAuditMessage( string $key ) :array {
156
+ return [];
157
+ }
158
+
159
+ /**
160
+ * @return string[][]|string[]
161
  */
162
  protected function getAuditMessages() :array {
163
  return [];
164
  }
165
 
166
  /**
167
+ * @return string[][][]|string[][]
 
168
  */
169
+ public function getEventStrings() :array {
170
+ return [];
 
171
  }
172
 
173
  /**
197
  switch ( $section ) {
198
 
199
  case 'section_user_messages' :
200
+ $title = __( 'User Messages', 'wp-simple-firewall' );
201
+ $titleShort = __( 'Messages', 'wp-simple-firewall' );
202
+ $summary = [
203
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Customize the messages displayed to the user.', 'wp-simple-firewall' ) ),
204
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use this section if you need to communicate to the user in a particular manner.', 'wp-simple-firewall' ) ),
205
  sprintf( '%s: %s', __( 'Hint', 'wp-simple-firewall' ), sprintf( __( 'To reset any message to its default, enter the text exactly: %s', 'wp-simple-firewall' ), 'default' ) )
207
  break;
208
 
209
  default:
210
+ $section = $this->getOptions()->getSection( $section );
211
+ if ( is_array( $section ) && !empty( $section[ 'title' ] ) && !empty( $section[ 'title_short' ] ) ) {
212
+ $title = __( $section[ 'title' ], 'wp-simple-firewall' );
213
+ $titleShort = __( $section[ 'title_short' ], 'wp-simple-firewall' );
214
+ $summary = empty( $section[ 'summary' ] ) ? [] : $section[ 'summary' ];
215
  }
216
  else {
217
  throw new \Exception( sprintf( 'A section slug was defined but with no associated strings. Slug: "%s".', $section ) );
219
  }
220
 
221
  return [
222
+ 'title' => $title,
223
+ 'title_short' => $titleShort,
224
+ 'summary' => ( isset( $summary ) && is_array( $summary ) ) ? $summary : [],
225
  ];
226
  }
227
  }
src/lib/src/Modules/Base/UI.php CHANGED
@@ -23,129 +23,127 @@ class UI {
23
  $bShowAdvanced = $con->getModule_Plugin()->isShowAdvanced();
24
 
25
  $opts = $this->getOptions();
26
- $aOptions = $opts->getOptionsForPluginUse();
27
 
28
- foreach ( $aOptions as $nSectionKey => $aSection ) {
29
 
30
- if ( !empty( $aSection[ 'options' ] ) ) {
31
 
32
- foreach ( $aSection[ 'options' ] as $nKey => $aOption ) {
33
- $aOption[ 'is_value_default' ] = ( $aOption[ 'value' ] === $aOption[ 'default' ] );
34
- $bIsPrem = isset( $aOption[ 'premium' ] ) && $aOption[ 'premium' ];
35
- $bIsAdv = isset( $aOption[ 'advanced' ] ) && $aOption[ 'advanced' ];
36
  if ( ( !$bIsPrem || $bPremiumEnabled ) && ( !$bIsAdv || $bShowAdvanced ) ) {
37
- $aSection[ 'options' ][ $nKey ] = $this->buildOptionForUi( $aOption );
38
  }
39
  else {
40
- unset( $aSection[ 'options' ][ $nKey ] );
41
  }
42
  }
43
 
44
- if ( empty( $aSection[ 'options' ] ) ) {
45
- unset( $aOptions[ $nSectionKey ] );
46
  }
47
  else {
48
  try {
49
- $aSection = array_merge(
50
- $aSection,
51
  $this->getMod()
52
  ->getStrings()
53
- ->getSectionStrings( $aSection[ 'slug' ] )
54
  );
55
  }
56
  catch ( \Exception $e ) {
57
  }
58
- $aOptions[ $nSectionKey ] = $aSection;
59
  }
60
 
61
- if ( isset( $aOptions[ $nSectionKey ] ) ) {
62
- $aWarnings = [];
63
- if ( !$opts->isSectionReqsMet( $aSection[ 'slug' ] ) ) {
64
- $aWarnings[] = __( 'Unfortunately your WordPress and/or PHP versions are too old to support this feature.', 'wp-simple-firewall' );
65
  }
66
- $aOptions[ $nSectionKey ][ 'warnings' ] = array_merge(
67
- $aWarnings,
68
- $this->getSectionWarnings( $aSection[ 'slug' ] )
69
  );
70
- $aOptions[ $nSectionKey ][ 'notices' ] = $this->getSectionNotices( $aSection[ 'slug' ] );
71
  }
72
  }
73
  }
74
 
75
- return $aOptions;
76
  }
77
 
78
  /**
79
- * @param array $aOptParams
80
  * @return array
81
  */
82
- protected function buildOptionForUi( $aOptParams ) {
83
 
84
- $mCurrent = $aOptParams[ 'value' ];
85
 
86
- switch ( $aOptParams[ 'type' ] ) {
87
 
88
  case 'password':
89
- if ( !empty( $mCurrent ) ) {
90
- $mCurrent = '';
91
  }
92
  break;
93
 
94
  case 'array':
95
-
96
- if ( empty( $mCurrent ) || !is_array( $mCurrent ) ) {
97
- $mCurrent = [];
98
  }
99
 
100
- $aOptParams[ 'rows' ] = count( $mCurrent ) + 2;
101
- $mCurrent = stripslashes( implode( "\n", $mCurrent ) );
102
 
103
  break;
104
 
105
  case 'comma_separated_lists':
106
 
107
- $aNewValues = [];
108
- if ( !empty( $mCurrent ) && is_array( $mCurrent ) ) {
109
-
110
- foreach ( $mCurrent as $sPage => $aParams ) {
111
- $aNewValues[] = $sPage.', '.implode( ", ", $aParams );
112
  }
113
  }
114
- $aOptParams[ 'rows' ] = count( $aNewValues ) + 1;
115
- $mCurrent = implode( "\n", $aNewValues );
116
 
117
  break;
118
 
119
  case 'multiple_select':
120
- if ( !is_array( $mCurrent ) ) {
121
- $mCurrent = [];
122
  }
123
  break;
124
 
125
  case 'text':
126
- $mCurrent = stripslashes( $this->getMod()->getTextOpt( $aOptParams[ 'key' ] ) );
127
  break;
128
  }
129
 
130
- $aParams = [
131
- 'value' => is_scalar( $mCurrent ) ? esc_attr( $mCurrent ) : $mCurrent,
132
  'disabled' => !$this->getCon()
133
- ->isPremiumActive() && ( isset( $aOptParams[ 'premium' ] ) && $aOptParams[ 'premium' ] ),
134
  ];
135
- $aParams[ 'enabled' ] = !$aParams[ 'disabled' ];
136
- $aOptParams = array_merge( [ 'rows' => 2 ], $aOptParams, $aParams );
137
 
138
  // add strings
139
  try {
140
- $aOptStrings = $this->getMod()->getStrings()->getOptionStrings( $aOptParams[ 'key' ] );
141
  if ( !is_array( $aOptStrings[ 'description' ] ) ) {
142
  $aOptStrings[ 'description' ] = [ $aOptStrings[ 'description' ] ];
143
  }
144
- $aOptParams = Services::DataManipulation()->mergeArraysRecursive( $aOptParams, $aOptStrings );
145
  }
146
  catch ( \Exception $e ) {
147
  }
148
- return $aOptParams;
149
  }
150
 
151
  public function buildSelectData_ModuleSettings() :array {
23
  $bShowAdvanced = $con->getModule_Plugin()->isShowAdvanced();
24
 
25
  $opts = $this->getOptions();
26
+ $options = $opts->getOptionsForPluginUse();
27
 
28
+ foreach ( $options as $sectionKey => $sect ) {
29
 
30
+ if ( !empty( $sect[ 'options' ] ) ) {
31
 
32
+ foreach ( $sect[ 'options' ] as $optKey => $option ) {
33
+ $option[ 'is_value_default' ] = ( $option[ 'value' ] === $option[ 'default' ] );
34
+ $bIsPrem = $option[ 'premium' ] ?? false;
35
+ $bIsAdv = $option[ 'advanced' ] ?? false;
36
  if ( ( !$bIsPrem || $bPremiumEnabled ) && ( !$bIsAdv || $bShowAdvanced ) ) {
37
+ $sect[ 'options' ][ $optKey ] = $this->buildOptionForUi( $option );
38
  }
39
  else {
40
+ unset( $sect[ 'options' ][ $optKey ] );
41
  }
42
  }
43
 
44
+ if ( empty( $sect[ 'options' ] ) ) {
45
+ unset( $options[ $sectionKey ] );
46
  }
47
  else {
48
  try {
49
+ $sect = array_merge(
50
+ $sect,
51
  $this->getMod()
52
  ->getStrings()
53
+ ->getSectionStrings( $sect[ 'slug' ] )
54
  );
55
  }
56
  catch ( \Exception $e ) {
57
  }
58
+ $options[ $sectionKey ] = $sect;
59
  }
60
 
61
+ if ( isset( $options[ $sectionKey ] ) ) {
62
+ $warning = [];
63
+ if ( !$opts->isSectionReqsMet( $sect[ 'slug' ] ) ) {
64
+ $warning[] = __( 'Unfortunately your WordPress and/or PHP versions are too old to support this feature.', 'wp-simple-firewall' );
65
  }
66
+ $options[ $sectionKey ][ 'warnings' ] = array_merge(
67
+ $warning,
68
+ $this->getSectionWarnings( $sect[ 'slug' ] )
69
  );
70
+ $options[ $sectionKey ][ 'notices' ] = $this->getSectionNotices( $sect[ 'slug' ] );
71
  }
72
  }
73
  }
74
 
75
+ return $options;
76
  }
77
 
78
  /**
79
+ * @param array $option
80
  * @return array
81
  */
82
+ protected function buildOptionForUi( $option ) {
83
 
84
+ $value = $option[ 'value' ];
85
 
86
+ switch ( $option[ 'type' ] ) {
87
 
88
  case 'password':
89
+ if ( !empty( $value ) ) {
90
+ $value = '';
91
  }
92
  break;
93
 
94
  case 'array':
95
+ if ( empty( $value ) || !is_array( $value ) ) {
96
+ $value = [];
 
97
  }
98
 
99
+ $option[ 'rows' ] = count( $value ) + 2;
100
+ $value = stripslashes( implode( "\n", $value ) );
101
 
102
  break;
103
 
104
  case 'comma_separated_lists':
105
 
106
+ $converted = [];
107
+ if ( !empty( $value ) && is_array( $value ) ) {
108
+ foreach ( $value as $page => $params ) {
109
+ $converted[] = $page.', '.implode( ", ", $params );
 
110
  }
111
  }
112
+ $option[ 'rows' ] = count( $converted ) + 1;
113
+ $value = implode( "\n", $converted );
114
 
115
  break;
116
 
117
  case 'multiple_select':
118
+ if ( !is_array( $value ) ) {
119
+ $value = [];
120
  }
121
  break;
122
 
123
  case 'text':
124
+ $value = stripslashes( $this->getMod()->getTextOpt( $option[ 'key' ] ) );
125
  break;
126
  }
127
 
128
+ $params = [
129
+ 'value' => is_scalar( $value ) ? esc_attr( $value ) : $value,
130
  'disabled' => !$this->getCon()
131
+ ->isPremiumActive() && ( isset( $option[ 'premium' ] ) && $option[ 'premium' ] ),
132
  ];
133
+ $params[ 'enabled' ] = !$params[ 'disabled' ];
134
+ $option = array_merge( [ 'rows' => 2 ], $option, $params );
135
 
136
  // add strings
137
  try {
138
+ $aOptStrings = $this->getMod()->getStrings()->getOptionStrings( $option[ 'key' ] );
139
  if ( !is_array( $aOptStrings[ 'description' ] ) ) {
140
  $aOptStrings[ 'description' ] = [ $aOptStrings[ 'description' ] ];
141
  }
142
+ $option = Services::DataManipulation()->mergeArraysRecursive( $option, $aOptStrings );
143
  }
144
  catch ( \Exception $e ) {
145
  }
146
+ return $option;
147
  }
148
 
149
  public function buildSelectData_ModuleSettings() :array {
src/lib/src/Modules/Base/WpCli/ModuleStandard.php CHANGED
@@ -124,10 +124,10 @@ class ModuleStandard extends BaseWpCliCmd {
124
  * @param array $args
125
  */
126
  public function cmdOptGet( array $null, array $args ) {
127
- $oOpts = $this->getOptions();
128
 
129
- $mVal = $oOpts->getOpt( $args[ 'key' ], $null );
130
- $aOpt = $oOpts->getRawData_SingleOption( $args[ 'key' ] );
131
  if ( !is_numeric( $mVal ) && empty( $mVal ) ) {
132
  \WP_CLI::log( __( 'No value set.', 'wp-simple-firewall' ) );
133
  }
124
  * @param array $args
125
  */
126
  public function cmdOptGet( array $null, array $args ) {
127
+ $opts = $this->getOptions();
128
 
129
+ $mVal = $opts->getOpt( $args[ 'key' ], $null );
130
+ $aOpt = $opts->getOptDefinition( $args[ 'key' ] );
131
  if ( !is_numeric( $mVal ) && empty( $mVal ) ) {
132
  \WP_CLI::log( __( 'No value set.', 'wp-simple-firewall' ) );
133
  }
src/lib/src/Modules/BaseShield/ModCon.php CHANGED
@@ -20,15 +20,6 @@ class ModCon extends Base\ModCon {
20
  */
21
  private static $bVisitorIsWhitelisted;
22
 
23
- /**
24
- * @deprecated 11.4
25
- */
26
- public function canCacheDirWrite() :bool {
27
- return ( new Shield\Modules\Plugin\Lib\TestCacheDirWrite() )
28
- ->setMod( $this->getCon()->getModule_Plugin() )
29
- ->canWrite();
30
- }
31
-
32
  public function getDbHandler_Sessions() :Shield\Databases\Session\Handler {
33
  return $this->getCon()
34
  ->getModule_Sessions()
20
  */
21
  private static $bVisitorIsWhitelisted;
22
 
 
 
 
 
 
 
 
 
 
23
  public function getDbHandler_Sessions() :Shield\Databases\Session\Handler {
24
  return $this->getCon()
25
  ->getModule_Sessions()
src/lib/src/Modules/CommentsFilter/Scan/Scanner.php CHANGED
@@ -96,7 +96,7 @@ class Scanner extends ExecOnceModConsumer {
96
  $this->getCon()
97
  ->fireEvent(
98
  'spam_block_'.$mResult->get_error_code(),
99
- [ 'audit' => $mResult->get_error_data() ]
100
  );
101
  $this->getCon()->fireEvent( 'comment_spam_block' );
102
 
96
  $this->getCon()
97
  ->fireEvent(
98
  'spam_block_'.$mResult->get_error_code(),
99
+ [ 'audit_params' => $mResult->get_error_data() ]
100
  );
101
  $this->getCon()->fireEvent( 'comment_spam_block' );
102
 
src/lib/src/Modules/CommentsFilter/Strings.php CHANGED
@@ -8,20 +8,52 @@ use FernleafSystems\Wordpress\Services\Services;
8
  class Strings extends Base\Strings {
9
 
10
  /**
11
- * @return string[][]
12
  */
13
- protected function getAuditMessages() :array {
14
  return [
15
- 'spam_block_antibot' => [ __( 'Blocked SPAM comment that failed AntiBot tests.', 'wp-simple-firewall' ) ],
 
 
 
 
 
 
 
 
16
  'spam_block_human' => [
17
- __( 'Blocked human SPAM comment containing suspicious content.', 'wp-simple-firewall' ),
18
- __( 'Human SPAM filter found "%s" in "%s"', 'wp-simple-firewall' )
 
 
 
 
 
 
19
  ],
20
  'spam_block_bot' => [
21
- __( 'Blocked SPAM comment from Bot.', 'wp-simple-firewall' )
 
 
 
 
 
 
22
  ],
23
  'spam_block_recaptcha' => [
24
- __( 'Blocked SPAM comment that failed reCAPTCHA.', 'wp-simple-firewall' )
 
 
 
 
 
 
 
 
 
 
 
 
25
  ],
26
  ];
27
  }
8
  class Strings extends Base\Strings {
9
 
10
  /**
11
+ * @inheritDoc
12
  */
13
+ public function getEventStrings() :array {
14
  return [
15
+ 'spam_block_antibot' => [
16
+ 'name' => sprintf( '%s: %s',
17
+ __( 'SPAM Blocked', 'wp-simple-firewall' ),
18
+ __( 'AntiBot System', 'wp-simple-firewall' )
19
+ ),
20
+ 'audit' => [
21
+ __( 'Blocked SPAM comment that failed AntiBot tests.', 'wp-simple-firewall' )
22
+ ],
23
+ ],
24
  'spam_block_human' => [
25
+ 'name' => sprintf( '%s: %s',
26
+ __( 'SPAM Blocked', 'wp-simple-firewall' ),
27
+ __( 'Human', 'wp-simple-firewall' )
28
+ ),
29
+ 'audit' => [
30
+ __( 'Blocked human SPAM comment containing suspicious content.', 'wp-simple-firewall' ),
31
+ __( 'Human SPAM filter found "{{word}}" in "{{key}}"', 'wp-simple-firewall' ),
32
+ ],
33
  ],
34
  'spam_block_bot' => [
35
+ 'name' => sprintf( '%s: %s',
36
+ __( 'SPAM Blocked', 'wp-simple-firewall' ),
37
+ __( 'Bot', 'wp-simple-firewall' )
38
+ ),
39
+ 'audit' => [
40
+ __( 'Blocked SPAM comment from Bot.', 'wp-simple-firewall' ),
41
+ ],
42
  ],
43
  'spam_block_recaptcha' => [
44
+ 'name' => sprintf( '%s: %s',
45
+ __( 'SPAM Blocked', 'wp-simple-firewall' ),
46
+ __( 'CAPTCHA', 'wp-simple-firewall' )
47
+ ),
48
+ 'audit' => [
49
+ __( 'Blocked SPAM comment that failed reCAPTCHA.', 'wp-simple-firewall' ),
50
+ ],
51
+ ],
52
+ 'comment_spam_block' => [
53
+ 'name' => __( 'Comment SPAM Blocked.', 'wp-simple-firewall' ),
54
+ 'audit' => [
55
+ __( 'Comment SPAM Blocked.', 'wp-simple-firewall' ),
56
+ ],
57
  ],
58
  ];
59
  }
src/lib/src/Modules/CommentsFilter/Upgrade.php CHANGED
@@ -6,36 +6,4 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
 
7
  class Upgrade extends Base\Upgrade {
8
 
9
- protected function upgrade_905() {
10
- /** @var Options $opts */
11
- $opts = $this->getOptions();
12
- $opts->setOpt(
13
- 'comments_default_action_human_spam',
14
- (string)$opts->getOpt( 'comments_default_action_human_spam' )
15
- );
16
- $opts->setOpt(
17
- 'comments_default_action_spam_bot',
18
- (string)$opts->getOpt( 'comments_default_action_spam_bot' )
19
- );
20
- }
21
-
22
- protected function upgrade_900() {
23
- $opts = $this->getOptions();
24
-
25
- if ( $opts->getOpt( 'enable_google_recaptcha_comments' ) === 'N' ) {
26
- $opts->setOpt( 'google_recaptcha_style_comments', 'disabled' );
27
- }
28
-
29
- $map = [
30
- 'comments_cooldown_interval' => 'comments_cooldown',
31
- 'comments_token_expire_interval' => 'comments_expire',
32
- 'enable_comments_human_spam_filter_items' => 'human_spam_items',
33
- ];
34
- foreach ( $map as $from => $to ) {
35
- $val = $opts->getOpt( $from );
36
- if ( $val !== false ) {
37
- $opts->setOpt( $to, $val );
38
- }
39
- }
40
- }
41
  }
6
 
7
  class Upgrade extends Base\Upgrade {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  }
src/lib/src/Modules/Comms/Strings.php CHANGED
@@ -6,6 +6,26 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
 
7
  class Strings extends Base\Strings {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  /**
10
  * @param string $section
11
  * @return array
@@ -59,20 +79,4 @@ class Strings extends Base\Strings {
59
  'description' => $desc,
60
  ];
61
  }
62
-
63
- /**
64
- * @return string[][]
65
- */
66
- protected function getAuditMessages() :array {
67
- return [
68
- 'suresend_success' => [
69
- __( 'Attempt to send email using SureSend: %s', 'wp-simple-firewall' ),
70
- __( 'SureSend email success.', 'wp-simple-firewall' ),
71
- ],
72
- 'suresend_fail' => [
73
- __( 'Attempt to send email using SureSend: %s', 'wp-simple-firewall' ),
74
- __( 'SureSend email failed.', 'wp-simple-firewall' ),
75
- ],
76
- ];
77
- }
78
  }
6
 
7
  class Strings extends Base\Strings {
8
 
9
+ /**
10
+ * @inheritDoc
11
+ */
12
+ public function getEventStrings() :array {
13
+ return [
14
+ 'suresend_fail' => [
15
+ 'name' => __( 'SureSend Fail', 'wp-simple-firewall' ),
16
+ 'audit' => [
17
+ __( 'Failed to send email (type: {{slug}}) to "{{email}}" using SureSend.', 'wp-simple-firewall' ),
18
+ ],
19
+ ],
20
+ 'suresend_success' => [
21
+ 'name' => __( 'SureSend Success', 'wp-simple-firewall' ),
22
+ 'audit' => [
23
+ __( 'Successfully sent email (type: {{slug}}) to "{{email}}" using SureSend.', 'wp-simple-firewall' ),
24
+ ],
25
+ ],
26
+ ];
27
+ }
28
+
29
  /**
30
  * @param string $section
31
  * @return array
79
  'description' => $desc,
80
  ];
81
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  }
src/lib/src/Modules/Data/DB/IPs/IPGeoVO.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs;
4
+
5
+ use FernleafSystems\Utilities\Data\Adapter\DynPropertiesClass;
6
+
7
+ /**
8
+ * @property string $countryCode
9
+ * @property string $countryName
10
+ * @property string $latitude
11
+ * @property string $longitude
12
+ * @property string $timeZone
13
+ * @property int $ts
14
+ */
15
+ class IPGeoVO extends DynPropertiesClass {
16
+
17
+ }
src/lib/src/Modules/Data/DB/IPs/IPRecords.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\ModCon;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
+
8
+ class IPRecords {
9
+
10
+ use ModConsumer;
11
+
12
+ public function loadIP( string $ip, bool $autoCreate = true ) :Ops\Record {
13
+ /** @var ModCon $mod */
14
+ $mod = $this->getMod();
15
+ $dbh = $mod->getDbH_IPs();
16
+ /** @var Ops\Select $select */
17
+ $select = $dbh->getQuerySelector();
18
+ $record = $select->filterByIPHuman( $ip )->first();
19
+
20
+ if ( empty( $record ) && $autoCreate && $this->addIP( $ip ) ) {
21
+ $record = $this->loadIP( $ip, false );
22
+ }
23
+
24
+ if ( empty( $record ) ) {
25
+ throw new \Exception( 'IP Record unavailable' );
26
+ }
27
+
28
+ return $record;
29
+ }
30
+
31
+ public function addIP( string $ip ) :bool {
32
+ /** @var ModCon $mod */
33
+ $mod = $this->getMod();
34
+ $dbh = $mod->getDbH_IPs();
35
+ /** @var Ops\Insert $insert */
36
+ $insert = $dbh->getQueryInserter();
37
+ /** @var Ops\Record $record */
38
+ $record = $dbh->getRecord();
39
+ $record->ip = $ip;
40
+ return $insert->insert( $record );
41
+ }
42
+ }
src/lib/src/Modules/Data/DB/IPs/Ops/Common.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\Ops;
4
+
5
+ trait Common {
6
+
7
+ public function filterByIP( string $ip ) {
8
+ return $this->addWhereEquals( 'ip', $ip );
9
+ }
10
+
11
+ public function filterByIPHuman( string $ip ) :self {
12
+ return $this->filterByIP( inet_pton( $ip ) );
13
+ }
14
+ }
src/lib/src/Modules/Data/DB/IPs/Ops/Delete.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Delete extends Base\Delete {
8
+
9
+ use Common;
10
+ }
src/lib/src/Modules/Data/DB/IPs/Ops/Handler.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Handler extends Base\Handler {
8
+
9
+ }
src/lib/src/Modules/Data/DB/IPs/Ops/Insert.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Insert extends Base\Insert {
8
+
9
+ }
src/lib/src/Modules/Data/DB/IPs/Ops/Record.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\Ops;
4
+
5
+ /**
6
+ * @property string $ip
7
+ * @property array $geo
8
+ */
9
+ class Record extends \FernleafSystems\Wordpress\Plugin\Core\Databases\Base\Record {
10
+
11
+ /**
12
+ * @param string $key
13
+ * @return mixed
14
+ */
15
+ public function __get( string $key ) {
16
+
17
+ $value = parent::__get( $key );
18
+
19
+ switch ( $key ) {
20
+
21
+ case 'geo':
22
+ if ( is_string( $value ) && !empty( $value ) ) {
23
+ $value = base64_decode( $value );
24
+ if ( !empty( $value ) ) {
25
+ $value = @json_decode( $value, true );
26
+ }
27
+ }
28
+
29
+ if ( !is_array( $value ) ) {
30
+ $value = [];
31
+ }
32
+ break;
33
+
34
+ default:
35
+ break;
36
+ }
37
+
38
+ return $value;
39
+ }
40
+
41
+ /**
42
+ * @param string $key
43
+ * @param mixed $value
44
+ */
45
+ public function __set( string $key, $value ) {
46
+
47
+ switch ( $key ) {
48
+
49
+ case 'geo':
50
+ if ( !is_array( $value ) ) {
51
+ $value = [];
52
+ }
53
+ $value = base64_encode( json_encode( $value ) );
54
+ break;
55
+
56
+ default:
57
+ break;
58
+ }
59
+
60
+ parent::__set( $key, $value );
61
+ }
62
+ }
src/lib/src/Modules/Data/DB/IPs/Ops/Select.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base\Traits\Select_IPTable;
7
+
8
+ class Select extends Base\Select {
9
+
10
+ use Common;
11
+ use Select_IPTable;
12
+ }
src/lib/src/Modules/Data/DB/ReqLogs/GetRequestMeta.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\{
6
+ DB\ReqLogs\Ops,
7
+ ModCon
8
+ };
9
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
10
+
11
+ class GetRequestMeta {
12
+
13
+ use ModConsumer;
14
+
15
+ private $reqID;
16
+
17
+ public function retrieve( string $reqID ) :string {
18
+ $this->reqID = sanitize_key( $reqID );
19
+
20
+ $metaDefs = [
21
+ 'rid' => [
22
+ 'name' => __( 'Request ID', 'wp-simple-firewall' ),
23
+ 'formatter' => function ( $metaDatum ) {
24
+ return sprintf( '<code>%s</code>', $metaDatum );
25
+ },
26
+ ],
27
+ 'uid' => [
28
+ 'name' => __( 'User ID', 'wp-simple-firewall' ),
29
+ ],
30
+ 'ts' => [
31
+ 'name' => __( 'Timestamp', 'wp-simple-firewall' ),
32
+ ],
33
+ 'verb' => [
34
+ 'name' => __( 'Method', 'wp-simple-firewall' ),
35
+ 'formatter' => function ( $metaDatum ) {
36
+ return strtoupper( $metaDatum );
37
+ }
38
+ ],
39
+ 'path' => [
40
+ 'name' => __( 'Path', 'wp-simple-firewall' ),
41
+ ],
42
+ 'ua' => [
43
+ 'name' => __( 'User Agent', 'wp-simple-firewall' ),
44
+ ],
45
+ ];
46
+
47
+ $lines = [];
48
+ $meta = $this->getRawMeta();
49
+ $meta[ 'rid' ] = $reqID;
50
+ foreach ( $metaDefs as $metaKey => $metaDef ) {
51
+
52
+ if ( !empty( $meta[ $metaKey ] ) ) {
53
+ $lines[] = sprintf(
54
+ '<li><strong>%s</strong>: <span>%s</span></li>',
55
+ ( $metaDef[ 'name' ] ?? $metaKey ),
56
+ isset( $metaDef[ 'formatter' ] ) ? $metaDef[ 'formatter' ]( $meta[ $metaKey ] ) : $meta[ $metaKey ]
57
+ );
58
+ }
59
+ }
60
+ return empty( $lines ) ? 'No Meta' : sprintf( '<ul>%s</ul>', implode( '', $lines ) );
61
+ }
62
+
63
+ /**
64
+ * @return array[]
65
+ */
66
+ private function getRawMeta() :array {
67
+ /** @var ModCon $mod */
68
+ $mod = $this->getMod();
69
+ /** @var Ops\Select $selector */
70
+ $selector = $mod->getDbH_ReqLogs()->getQuerySelector();
71
+ return $selector->filterByReqID( $this->reqID )->first()->meta;
72
+ }
73
+ }
src/lib/src/Modules/Data/DB/ReqLogs/LoadLogs.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\ModCon;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
+ use FernleafSystems\Wordpress\Services\Services;
9
+
10
+ class LoadLogs {
11
+
12
+ use ModConsumer;
13
+ use IpAddressConsumer;
14
+
15
+ /**
16
+ * @return LogRecord[]
17
+ */
18
+ public function run() :array {
19
+ $results = [];
20
+ foreach ( $this->selectRaw() as $raw ) {
21
+ $record = new LogRecord( $raw );
22
+ $results[ $raw[ 'id' ] ] = $record;
23
+ }
24
+ return $results;
25
+ }
26
+
27
+ /**
28
+ * @return array[]
29
+ */
30
+ private function selectRaw() :array {
31
+ /** @var ModCon $mod */
32
+ $mod = $this->getMod();
33
+ $ip = $this->getIP();
34
+ return Services::WpDb()->selectCustom(
35
+ sprintf( 'SELECT req.id, req.req_id as rid, req.meta, req.created_at,
36
+ ips.ip as ip
37
+ FROM `%s` as `req`
38
+ %s
39
+ INNER JOIN `%s` as `ips`
40
+ ON req.ip_ref = ips.id
41
+ ORDER BY `req`.created_at DESC;',
42
+ $mod->getDbH_ReqLogs()->getTableSchema()->table,
43
+ empty( $ip ) ? '' : sprintf( "WHERE `ips`.ip==INET6_ATON('%s')", $ip ),
44
+ $this->getCon()->getModule_Data()->getDbH_IPs()->getTableSchema()->table
45
+ )
46
+ );
47
+ }
48
+ }
src/lib/src/Modules/Data/DB/ReqLogs/LogRecord.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base\Record;
6
+
7
+ /**
8
+ * @property string $ip
9
+ * @property string $rid
10
+ */
11
+ class LogRecord extends Record {
12
+
13
+ }
src/lib/src/Modules/Data/DB/ReqLogs/Ops/Common.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs\Ops;
4
+
5
+ trait Common {
6
+
7
+ public function filterByIP( int $ipRef ) {
8
+ return $this->addWhereEquals( 'ip_ref', $ipRef );
9
+ }
10
+
11
+ public function filterByReqID( string $reqID ) :self {
12
+ return $this->addWhereEquals( 'req_id', $reqID );
13
+ }
14
+ }
src/lib/src/Modules/Data/DB/ReqLogs/Ops/Delete.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Delete extends Base\Delete {
8
+
9
+ use Common;
10
+ }
src/lib/src/Modules/Data/DB/ReqLogs/Ops/Handler.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Handler extends Base\Handler {
8
+
9
+ }
src/lib/src/Modules/Data/DB/ReqLogs/Ops/Insert.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Insert extends Base\Insert {
8
+
9
+ }
src/lib/src/Modules/Data/DB/ReqLogs/Ops/Record.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs\Ops;
4
+
5
+ /**
6
+ * @property string $req_id
7
+ * @property int $ip_ref
8
+ */
9
+ class Record extends \FernleafSystems\Wordpress\Plugin\Core\Databases\Base\Record {
10
+
11
+ }
src/lib/src/Modules/Data/DB/ReqLogs/Ops/Select.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
+
7
+ class Select extends Base\Select {
8
+
9
+ use Common;
10
+ }
src/lib/src/Modules/Data/DB/ReqLogs/RequestRecords.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\ModCon;
7
+
8
+ class RequestRecords {
9
+
10
+ use ModConsumer;
11
+
12
+ public function loadReq( string $reqID, int $ipRefID, bool $autoCreate = true ) :Ops\Record {
13
+ /** @var ModCon $mod */
14
+ $mod = $this->getMod();
15
+ /** @var Ops\Select $select */
16
+ $select = $mod->getDbH_ReqLogs()->getQuerySelector();
17
+ /** @var Ops\Record|null $record */
18
+ $record = $select->filterByReqID( $reqID )->first();
19
+
20
+ if ( empty( $record ) && $autoCreate && $this->addReq( $reqID, $ipRefID ) ) {
21
+ $record = $this->loadReq( $reqID, $ipRefID, false );
22
+ }
23
+
24
+ return $record;
25
+ }
26
+
27
+ public function addReq( string $reqID, int $ipRef ) :bool {
28
+ /** @var ModCon $mod */
29
+ $mod = $this->getMod();
30
+ $dbh = $mod->getDbH_ReqLogs();
31
+ /** @var Ops\Insert $insert */
32
+ $insert = $dbh->getQueryInserter();
33
+ $record = new Ops\Record();
34
+ $record->req_id = $reqID;
35
+ $record->ip_ref = $ipRef;
36
+ return $insert->insert( $record );
37
+ }
38
+ }
src/lib/src/Modules/Data/ModCon.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\{
6
+ AuditTrail,
7
+ Traffic
8
+ };
9
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
10
+
11
+ class ModCon extends BaseShield\ModCon {
12
+
13
+ public function getDbH_IPs() :DB\IPs\Ops\Handler {
14
+ return $this->getDbHandler()->loadDbH( 'ips' );
15
+ }
16
+
17
+ public function getDbH_ReqLogs() :DB\ReqLogs\Ops\Handler {
18
+ $this->getDbH_IPs();
19
+ return $this->getDbHandler()->loadDbH( 'req_logs' );
20
+ }
21
+
22
+ protected function cleanupDatabases() {
23
+
24
+ // 1. Clean Requests & Audit Trail
25
+ // Deleting Request Logs automatically cascades to Audit Trail and then to Audit Trail Meta.
26
+ /** @var AuditTrail\Options $optsAudit */
27
+ $optsAudit = $this->getCon()->getModule_AuditTrail()->getOptions();
28
+ /** @var Traffic\Options $optsTraffic */
29
+ $optsTraffic = $this->getCon()->getModule_Traffic()->getOptions();
30
+ $this->getDbH_ReqLogs()
31
+ ->tableCleanExpired( max( $optsAudit->getAutoCleanDays(), $optsTraffic->getAutoCleanDays() ) );
32
+
33
+ // 2. Clean Unused IPs.
34
+ $this->getDbH_IPs()
35
+ ->getQueryDeleter()
36
+ ->addWhere( 'id',
37
+ $this->getDbH_ReqLogs()
38
+ ->getQuerySelector()
39
+ ->getDistinctForColumn( 'ip_ref' ),
40
+ 'NOT IN'
41
+ );
42
+ }
43
+ }
src/lib/src/Modules/Events/Lib/EventsListener.php CHANGED
@@ -29,6 +29,11 @@ abstract class EventsListener {
29
  add_action( $con->prefix( 'plugin_shutdown' ), function () {
30
  $this->onShutdown();
31
  }, 100 );
 
 
 
 
 
32
  }
33
 
34
  /**
29
  add_action( $con->prefix( 'plugin_shutdown' ), function () {
30
  $this->onShutdown();
31
  }, 100 );
32
+
33
+ $this->init();
34
+ }
35
+
36
+ protected function init() {
37
  }
38
 
39
  /**
src/lib/src/Modules/Events/Lib/EventsService.php CHANGED
@@ -13,21 +13,45 @@ class EventsService {
13
  */
14
  private $aEvents;
15
 
16
- /**
17
- * @param string $event
18
- * @param array $meta
19
- * @return $this
20
- */
21
  public function fireEvent( string $event, array $meta = [] ) {
22
  if ( $this->isSupportedEvent( $event ) ) {
23
- do_action(
24
- $this->getCon()->prefix( 'event' ),
25
- $event,
26
- $meta,
27
- $this->getEventDef( $event )
28
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  }
30
- return $this;
31
  }
32
 
33
  /**
@@ -37,18 +61,25 @@ class EventsService {
37
  if ( empty( $this->aEvents ) ) {
38
  $events = [];
39
  foreach ( $this->getCon()->modules as $mod ) {
 
40
  $events = array_merge(
41
  $events,
42
  array_map(
43
  function ( $evt ) use ( $mod ) {
 
 
44
  $evt[ 'context' ] = $mod->getSlug();
45
  return $evt;
46
  },
47
- is_array( $mod->getDef( 'events' ) ) ? $mod->getDef( 'events' ) : []
 
48
  )
49
  );
50
  }
51
- $this->aEvents = $this->buildEvents( $events );
 
 
 
52
  }
53
  return $this->aEvents;
54
  }
@@ -58,22 +89,48 @@ class EventsService {
58
  * @return array|null
59
  */
60
  public function getEventDef( string $eventKey ) {
61
- return $this->isSupportedEvent( $eventKey ) ? $this->getEvents()[ $eventKey ] : null;
62
  }
63
 
64
- public function isSupportedEvent( string $eventKey ) :bool {
65
- return in_array( $eventKey, array_keys( $this->getEvents() ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  }
67
 
68
  private function buildEvents( array $events ) :array {
69
  $defaults = [
70
- 'cat' => 1,
 
71
  'stat' => true,
72
  'audit' => true,
73
  'recent' => false, // whether to show in the recent events logs
74
  'offense' => false, // whether to mark offense against IP
75
- 'audit_multiple' => false, // allow multiple audit entries in the same request
76
  'suppress_offense' => false, // events that normally trigger offense can be forcefully suppressed
 
 
 
77
  ];
78
  foreach ( $events as $eventKey => $evt ) {
79
  $events[ $eventKey ] = array_merge( $defaults, $evt );
@@ -81,4 +138,13 @@ class EventsService {
81
  }
82
  return $events;
83
  }
 
 
 
 
 
 
 
 
 
84
  }
13
  */
14
  private $aEvents;
15
 
16
+ public function eventExists( string $eventKey ) :bool {
17
+ return !empty( $this->getEventDef( $eventKey ) );
18
+ }
19
+
 
20
  public function fireEvent( string $event, array $meta = [] ) {
21
  if ( $this->isSupportedEvent( $event ) ) {
22
+ try {
23
+ $this->verifyAuditParams( $event, $meta );
24
+ do_action(
25
+ $this->getCon()->prefix( 'event' ),
26
+ $event,
27
+ $meta,
28
+ $this->getEventDef( $event )
29
+ );
30
+ }
31
+ catch ( \Exception $e ) {
32
+ error_log( $e->getMessage() );
33
+ }
34
+ }
35
+ }
36
+
37
+ /**
38
+ * @throws \Exception
39
+ */
40
+ private function verifyAuditParams( string $event, array $meta ) {
41
+ $def = $this->getEventDef( $event )[ 'audit_params' ] ?? [];
42
+ $metaParams = array_keys( $meta[ 'audit_params' ] ?? [] );
43
+
44
+ if ( empty( $def ) && !empty( $metaParams ) ) {
45
+ error_log( sprintf( 'WARNING: Event (%s) receives params but none are defined.', $event ) );
46
+ }
47
+ elseif ( !empty( $def ) ) {
48
+ if ( array_diff( $def, $metaParams ) ) {
49
+ throw new \Exception( sprintf( "Event (%s) def has audit params that aren't present: %s", $event, implode( ', ', $def ) ) );
50
+ }
51
+ if ( array_diff( $metaParams, $def ) ) {
52
+ throw new \Exception( sprintf( "Event (%s) has audit params that aren't present in def: %s", $event, implode( ', ', $metaParams ) ) );
53
+ }
54
  }
 
55
  }
56
 
57
  /**
61
  if ( empty( $this->aEvents ) ) {
62
  $events = [];
63
  foreach ( $this->getCon()->modules as $mod ) {
64
+ $opts = $mod->getOptions();
65
  $events = array_merge(
66
  $events,
67
  array_map(
68
  function ( $evt ) use ( $mod ) {
69
+ $evt[ 'module' ] = $mod->getSlug();
70
+ /** @deprecated 12.0 */
71
  $evt[ 'context' ] = $mod->getSlug();
72
  return $evt;
73
  },
74
+ /** @deprecated 12.0 - replace with $opts->getEvents() */
75
+ is_array( $opts->getDef( 'events' ) ) ? $opts->getDef( 'events' ) : []
76
  )
77
  );
78
  }
79
+ $this->aEvents = (array)apply_filters( 'shield/events_definitions', $this->buildEvents( $events ) );
80
+ if ( empty( $this->aEvents ) ) {
81
+ error_log( 'Shield events definitions is empty or not the correct format' );
82
+ }
83
  }
84
  return $this->aEvents;
85
  }
89
  * @return array|null
90
  */
91
  public function getEventDef( string $eventKey ) {
92
+ return $this->getEvents()[ $eventKey ] ?? null;
93
  }
94
 
95
+ public function getEventName( string $event ) :string {
96
+ return $this->getEventStrings( $event )[ 'name' ] ?? '';
97
+ }
98
+
99
+ public function getEventAuditStrings( string $event ) :array {
100
+ return $this->getEventStrings( $event )[ 'audit' ] ?? [];
101
+ }
102
+
103
+ public function getEventStrings( string $eventKey ) :array {
104
+ return $this->getCon()
105
+ ->getModule( $this->getEventDef( $eventKey )[ 'module' ] )
106
+ ->getStrings()
107
+ ->getEventStrings()[ $eventKey ] ?? [];
108
+ }
109
+
110
+ /**
111
+ * @return string[]
112
+ */
113
+ public function getEventNames() :array {
114
+ return array_map(
115
+ function ( $event ) {
116
+ return $this->getEventName( $event[ 'key' ] );
117
+ },
118
+ $this->getEvents()
119
+ );
120
  }
121
 
122
  private function buildEvents( array $events ) :array {
123
  $defaults = [
124
+ 'cat' => 0, //@deprecated 12.0
125
+ 'level' => 'notice', // events default at "warning" level
126
  'stat' => true,
127
  'audit' => true,
128
  'recent' => false, // whether to show in the recent events logs
129
  'offense' => false, // whether to mark offense against IP
 
130
  'suppress_offense' => false, // events that normally trigger offense can be forcefully suppressed
131
+ 'audit_multiple' => false, // allow multiple audit entries in the same request
132
+ 'audit_countable' => false, // allow shortcut to audit trail to allow events to be counted
133
+ 'audit_params' => [],
134
  ];
135
  foreach ( $events as $eventKey => $evt ) {
136
  $events[ $eventKey ] = array_merge( $defaults, $evt );
138
  }
139
  return $events;
140
  }
141
+
142
+ /**
143
+ * @param string $eventKey
144
+ * @return bool
145
+ * @deprecated 12.0
146
+ */
147
+ public function isSupportedEvent( string $eventKey ) :bool {
148
+ return array_key_exists( $eventKey, $this->getEvents() );
149
+ }
150
  }
src/lib/src/Modules/Events/Lib/Reports/KeyStats.php CHANGED
@@ -15,8 +15,6 @@ class KeyStats extends BaseReporter {
15
  $mod = $this->getMod();
16
  /** @var DBEvents\Select $selector */
17
  $selector = $mod->getDbHandler_Events()->getQuerySelector();
18
- /** @var Events\Strings $strings */
19
- $strings = $mod->getStrings();
20
 
21
  $eventKeys = [
22
  'ip_offense',
@@ -38,6 +36,7 @@ class KeyStats extends BaseReporter {
38
  $rep = $this->getReport();
39
 
40
  $sums = [];
 
41
  foreach ( $eventKeys as $event ) {
42
  try {
43
  $eventSum = $selector
@@ -46,7 +45,7 @@ class KeyStats extends BaseReporter {
46
  if ( $eventSum > 0 ) {
47
  $sums[ $event ] = [
48
  'count' => $eventSum,
49
- 'name' => $strings->getEventName( $event ),
50
  ];
51
  }
52
  }
15
  $mod = $this->getMod();
16
  /** @var DBEvents\Select $selector */
17
  $selector = $mod->getDbHandler_Events()->getQuerySelector();
 
 
18
 
19
  $eventKeys = [
20
  'ip_offense',
36
  $rep = $this->getReport();
37
 
38
  $sums = [];
39
+ $srvEvents = $this->getCon()->loadEventsService();
40
  foreach ( $eventKeys as $event ) {
41
  try {
42
  $eventSum = $selector
45
  if ( $eventSum > 0 ) {
46
  $sums[ $event ] = [
47
  'count' => $eventSum,
48
+ 'name' => $srvEvents->getEventName( $event ),
49
  ];
50
  }
51
  }
src/lib/src/Modules/Events/Lib/UI/BuildDataForStats.php CHANGED
@@ -27,10 +27,7 @@ class BuildDataForStats {
27
  private function buildStats() :array {
28
  $allStats = [];
29
 
30
- /** @var ModCon $mod */
31
- $mod = $this->getMod();
32
- /** @var Strings $strings */
33
- $strings = $mod->getStrings();
34
  foreach ( $this->getAllEvents() as $eventSection ) {
35
  $stats = [];
36
  foreach ( $eventSection[ 'events' ] as $event ) {
@@ -38,7 +35,7 @@ class BuildDataForStats {
38
  if ( !empty( array_filter( $sums ) ) ) {
39
  $stats[ $event ] = [
40
  'key' => $event,
41
- 'name' => $strings->getEventName( $event ),
42
  'counts' => $this->buildSums( $event ),
43
  ];
44
  }
27
  private function buildStats() :array {
28
  $allStats = [];
29
 
30
+ $srvEvents = $this->getCon()->loadEventsService();
 
 
 
31
  foreach ( $this->getAllEvents() as $eventSection ) {
32
  $stats = [];
33
  foreach ( $eventSection[ 'events' ] as $event ) {
35
  if ( !empty( array_filter( $sums ) ) ) {
36
  $stats[ $event ] = [
37
  'key' => $event,
38
+ 'name' => $srvEvents->getEventName( $event ),
39
  'counts' => $this->buildSums( $event ),
40
  ];
41
  }
src/lib/src/Modules/Events/Strings.php CHANGED
@@ -9,261 +9,52 @@ class Strings extends Base\Strings {
9
  /**
10
  * @param string $eventKey
11
  * @return string
 
12
  */
13
- public function getEventName( $eventKey ) {
14
- return $this->getEventNames()[ $eventKey ] ?? '';
15
  }
16
 
17
  /**
18
  * @param bool $auto
19
  * @return string[]
 
20
  */
21
  public function getEventNames( bool $auto = true ) :array {
22
  $names = [
23
- 'test_cron_run' => __( 'Test Cron Run', 'wp-simple-firewall' ),
24
- 'import_notify_sent' => __( 'Import Notify Sent', 'wp-simple-firewall' ),
25
- 'import_notify_received' => __( 'Import Notify Received', 'wp-simple-firewall' ),
26
- 'options_exported' => __( 'Options Exported', 'wp-simple-firewall' ),
27
- 'options_imported' => __( 'Options Imported', 'wp-simple-firewall' ),
28
- 'whitelist_site_added' => __( 'Whitelist Site Added', 'wp-simple-firewall' ),
29
- 'whitelist_site_removed' => __( 'Whitelist Site Removed', 'wp-simple-firewall' ),
30
- 'master_url_set' => __( 'Master Site URL Set', 'wp-simple-firewall' ),
31
- 'recaptcha_success' => __( 'CAPTCHA Test Success', 'wp-simple-firewall' ),
32
- 'recaptcha_fail' => __( 'CAPTCHA Test Fail', 'wp-simple-firewall' ),
33
- 'key_success' => __( 'Security PIN Authentication Success', 'wp-simple-firewall' ),
34
- 'key_fail' => __( 'Security PIN Authentication Failed', 'wp-simple-firewall' ),
35
- 'custom_offense' => __( 'Custom Offense', 'wp-simple-firewall' ),
36
- 'conn_kill' => __( 'Connection Killed', 'wp-simple-firewall' ),
37
- 'ip_offense' => __( 'Offense Triggered', 'wp-simple-firewall' ),
38
- 'ip_blocked' => __( 'IP Blocked', 'wp-simple-firewall' ),
39
- 'ip_unblock_flag' => __( 'IP Unblocked Using Flag File', 'wp-simple-firewall' ),
40
- 'ip_block_auto' => __( 'IP Block Add Auto', 'wp-simple-firewall' ),
41
- 'ip_block_manual' => __( 'IP Block Add Manual', 'wp-simple-firewall' ),
42
- 'ip_bypass_add' => __( 'IP Bypass Add', 'wp-simple-firewall' ),
43
- 'ip_bypass_remove' => __( 'IP Bypass Remove', 'wp-simple-firewall' ),
44
- 'antibot_fail' => __( 'Fail AntiBot Test', 'wp-simple-firewall' ),
45
- 'antibot_pass' => __( 'Pass AntiBot Test', 'wp-simple-firewall' ),
46
- 'bottrack_404' => sprintf( '%s: %s',
47
- __( 'Bot Detection', 'wp-simple-firewall' ),
48
- '404'
49
- ),
50
- 'bottrack_fakewebcrawler' => sprintf( '%s: %s',
51
- __( 'Bot Detection', 'wp-simple-firewall' ),
52
- __( 'Fake Web Crawler', 'wp-simple-firewall' )
53
- ),
54
- 'bottrack_linkcheese' => sprintf( '%s: %s',
55
- __( 'Bot Detection', 'wp-simple-firewall' ),
56
- __( 'Link Cheese', 'wp-simple-firewall' )
57
- ),
58
- 'bottrack_loginfailed' => sprintf( '%s: %s',
59
- __( 'Bot Detection', 'wp-simple-firewall' ),
60
- __( 'Failed Login', 'wp-simple-firewall' )
61
- ),
62
- 'bottrack_logininvalid' => sprintf( '%s: %s',
63
- __( 'Bot Detection', 'wp-simple-firewall' ),
64
- __( 'Invalid Username Login', 'wp-simple-firewall' )
65
- ),
66
- 'bottrack_useragent' => sprintf( '%s: %s',
67
- __( 'Bot Detection', 'wp-simple-firewall' ),
68
- __( 'Invalid User-Agent', 'wp-simple-firewall' )
69
- ),
70
- 'bottrack_xmlrpc' => sprintf( '%s: %s',
71
- __( 'Bot Detection', 'wp-simple-firewall' ),
72
- 'XML-RPC'
73
- ),
74
- 'bottrack_invalidscript' => sprintf( '%s: %s',
75
- __( 'Bot Detection', 'wp-simple-firewall' ),
76
- __( 'Invalid Script Load', 'wp-simple-firewall' )
77
- ),
78
- 'apc_alert_sent' => sprintf( '%s: %s',
79
- __( 'Alert Sent', 'wp-simple-firewall' ),
80
- __( 'Abandoned Plugin Detected', 'wp-simple-firewall' )
81
- ),
82
- 'mal_alert_sent' => sprintf( '%s: %s',
83
- __( 'Alert Sent', 'wp-simple-firewall' ),
84
- __( 'Malware Detected', 'wp-simple-firewall' )
85
- ),
86
- 'ptg_alert_sent' => sprintf( '%s: %s',
87
- __( 'Alert Sent', 'wp-simple-firewall' ),
88
- __( 'Modified Plugin/Theme Detected', 'wp-simple-firewall' )
89
- ),
90
- 'ufc_alert_sent' => sprintf( '%s: %s',
91
- __( 'Alert Sent', 'wp-simple-firewall' ),
92
- __( 'Unrecognised File Detected', 'wp-simple-firewall' )
93
- ),
94
- 'wcf_alert_sent' => sprintf( '%s: %s',
95
- __( 'Alert Sent', 'wp-simple-firewall' ),
96
- __( 'Modified/Missing WP Core File Detected', 'wp-simple-firewall' )
97
- ),
98
- 'wpv_alert_sent' => sprintf( '%s: %s',
99
- __( 'Alert Sent', 'wp-simple-firewall' ),
100
- __( 'Vulnerable Plugin Detected', 'wp-simple-firewall' )
101
- ),
102
- 'apc_scan_run' => sprintf( '%s: %s',
103
- __( 'Scan Completed', 'wp-simple-firewall' ),
104
- __( 'Abandoned Plugins', 'wp-simple-firewall' )
105
- ),
106
- 'mal_scan_run' => sprintf( '%s: %s',
107
- __( 'Scan Completed', 'wp-simple-firewall' ),
108
- __( 'Malware', 'wp-simple-firewall' )
109
- ),
110
- 'ptg_scan_run' => sprintf( '%s: %s',
111
- __( 'Scan Completed', 'wp-simple-firewall' ),
112
- __( 'Plugin/Theme Guard', 'wp-simple-firewall' )
113
- ),
114
- 'ufc_scan_run' => sprintf( '%s: %s',
115
- __( 'Scan Completed', 'wp-simple-firewall' ),
116
- __( 'Unrecognised Files', 'wp-simple-firewall' )
117
- ),
118
- 'wcf_scan_run' => sprintf( '%s: %s',
119
- __( 'Scan Completed', 'wp-simple-firewall' ),
120
- __( 'WP Core Files', 'wp-simple-firewall' )
121
- ),
122
- 'wpv_scan_run' => sprintf( '%s: %s',
123
- __( 'Scan Completed', 'wp-simple-firewall' ),
124
- __( 'Vulnerabilities', 'wp-simple-firewall' )
125
- ),
126
- 'apc_scan_found' => sprintf( '%s: %s',
127
- __( 'Scan Item Discovered', 'wp-simple-firewall' ),
128
- __( 'Abandoned Plugins', 'wp-simple-firewall' )
129
- ),
130
- 'mal_scan_found' => sprintf( '%s: %s',
131
- __( 'Scan Item Discovered', 'wp-simple-firewall' ),
132
- __( 'Malware', 'wp-simple-firewall' )
133
- ),
134
- 'ptg_scan_found' => sprintf( '%s: %s',
135
- __( 'Scan Item Discovered', 'wp-simple-firewall' ),
136
- __( 'Plugin/Theme Guard', 'wp-simple-firewall' )
137
- ),
138
- 'ufc_scan_found' => sprintf( '%s: %s',
139
- __( 'Scan Item Discovered', 'wp-simple-firewall' ),
140
- __( 'Unrecognised Files', 'wp-simple-firewall' )
141
- ),
142
- 'wcf_scan_found' => sprintf( '%s: %s',
143
- __( 'Scan Item Discovered', 'wp-simple-firewall' ),
144
- __( 'WP Core Files', 'wp-simple-firewall' )
145
- ),
146
- 'wpv_scan_found' => sprintf( '%s: %s',
147
- __( 'Scan Item Discovered', 'wp-simple-firewall' ),
148
- __( 'Vulnerabilities', 'wp-simple-firewall' )
149
- ),
150
- 'scan_item_delete_success' => __( 'Scan Item Delete Success', 'wp-simple-firewall' ),
151
- 'scan_item_repair_success' => __( 'Scan Item Repair Success', 'wp-simple-firewall' ),
152
- 'scan_item_repair_fail' => __( 'Scan Item Repair Failure', 'wp-simple-firewall' ),
153
- '2fa_backupcode_verified' => __( '', 'wp-simple-firewall' ),
154
- '2fa_backupcode_fail' => __( '', 'wp-simple-firewall' ),
155
- '2fa_email_verified' => __( '', 'wp-simple-firewall' ),
156
- '2fa_email_verify_fail' => __( '', 'wp-simple-firewall' ),
157
- '2fa_googleauth_verified' => __( '', 'wp-simple-firewall' ),
158
- '2fa_google_fail' => __( '', 'wp-simple-firewall' ),
159
- '2fa_yubikey_verified' => __( '', 'wp-simple-firewall' ),
160
- '2fa_yubikey_fail' => __( '', 'wp-simple-firewall' ),
161
- '2fa_email_send_success' => __( '', 'wp-simple-firewall' ),
162
- '2fa_email_send_fail' => __( '', 'wp-simple-firewall' ),
163
- 'cooldown_fail' => __( '', 'wp-simple-firewall' ),
164
- 'honeypot_fail' => __( '', 'wp-simple-firewall' ),
165
- 'botbox_fail' => __( '', 'wp-simple-firewall' ),
166
- 'login_block' => __( 'Blocked Login', 'wp-simple-firewall' ),
167
- 'hide_login_url' => __( '', 'wp-simple-firewall' ),
168
- '2fa_success' => __( '', 'wp-simple-firewall' ),
169
- 'check_skip' => __( '', 'wp-simple-firewall' ),
170
- 'fw_email_fail' => __( 'Firewall Block Email Fail', 'wp-simple-firewall' ),
171
- 'fw_email_success' => __( 'Firewall Block Email Success', 'wp-simple-firewall' ),
172
- 'firewall_block' => __( 'Firewall Block', 'wp-simple-firewall' ),
173
- 'blockparam_dirtraversal' => sprintf( '%s: %s',
174
- __( 'Firewall', 'wp-simple-firewall' ),
175
- __( 'Directory Traversal', 'wp-simple-firewall' )
176
- ),
177
- 'blockparam_wpterms' => sprintf( '%s: %s',
178
- __( 'Firewall', 'wp-simple-firewall' ),
179
- __( 'WordPress Terms', 'wp-simple-firewall' )
180
- ),
181
- 'blockparam_fieldtruncation' => sprintf( '%s: %s',
182
- __( 'Firewall', 'wp-simple-firewall' ),
183
- __( 'Field Truncation', 'wp-simple-firewall' )
184
- ),
185
- 'blockparam_sqlqueries' => sprintf( '%s: %s',
186
- __( 'Firewall', 'wp-simple-firewall' ),
187
- __( 'SQL Queries', 'wp-simple-firewall' )
188
- ),
189
- 'blockparam_schema' => sprintf( '%s: %s',
190
- __( 'Firewall', 'wp-simple-firewall' ),
191
- __( 'Leading Schema', 'wp-simple-firewall' )
192
- ),
193
- 'blockparam_aggressive' => sprintf( '%s: %s',
194
- __( 'Firewall', 'wp-simple-firewall' ),
195
- __( 'Aggressive Rules', 'wp-simple-firewall' )
196
- ),
197
- 'blockparam_phpcode' => sprintf( '%s: %s',
198
- __( 'Firewall', 'wp-simple-firewall' ),
199
- __( 'PHP Code', 'wp-simple-firewall' )
200
- ),
201
- 'block_exefile' => sprintf( '%s: %s',
202
- __( 'Firewall', 'wp-simple-firewall' ),
203
- __( 'EXE File Uploads', 'wp-simple-firewall' )
204
- ),
205
- 'session_notfound' => __( 'Session Not Found', 'wp-simple-firewall' ),
206
- 'session_expired' => __( 'Session Expired', 'wp-simple-firewall' ),
207
- 'session_idle' => __( 'Session Idle', 'wp-simple-firewall' ),
208
- 'session_iplock' => __( 'Session Locked To IP', 'wp-simple-firewall' ),
209
- 'session_browserlock' => __( 'Session Locked To Browser', 'wp-simple-firewall' ),
210
- 'session_unverified' => __( 'Session Unverified', 'wp-simple-firewall' ),
211
- 'password_expired' => __( 'Password Expired', 'wp-simple-firewall' ),
212
- 'password_policy_force_change' => __( 'Forced Password Change', 'wp-simple-firewall' ),
213
- 'password_policy_block' => __( 'Password Change Blocked', 'wp-simple-firewall' ),
214
- 'user_hard_suspended' => __( 'User Hard-Suspended', 'wp-simple-firewall' ),
215
- 'user_hard_unsuspended' => __( 'User Hard-Unsuspended', 'wp-simple-firewall' ),
216
- 'spam_block_antibot' => sprintf( '%s: %s',
217
- __( 'SPAM Blocked', 'wp-simple-firewall' ),
218
- __( 'AntiBot System', 'wp-simple-firewall' )
219
- ),
220
- 'spam_block_bot' => sprintf( '%s: %s',
221
- __( 'SPAM Blocked', 'wp-simple-firewall' ),
222
- __( 'Bot', 'wp-simple-firewall' )
223
- ),
224
- 'spam_block_recaptcha' => sprintf( '%s: %s',
225
- __( 'SPAM Blocked', 'wp-simple-firewall' ),
226
- __( 'CAPTCHA', 'wp-simple-firewall' )
227
- ),
228
- 'spam_block_human' => sprintf( '%s: %s',
229
- __( 'SPAM Blocked', 'wp-simple-firewall' ),
230
- __( 'Human', 'wp-simple-firewall' )
231
- ),
232
- 'block_anonymous_restapi' => sprintf( '%s: %s',
233
- __( 'Blocked', 'wp-simple-firewall' ),
234
- __( 'Anonymous REST API' )
235
- ),
236
- 'block_xml' => sprintf( '%s: %s',
237
- __( 'Blocked', 'wp-simple-firewall' ),
238
- __( 'XML-RPC' )
239
- ),
240
- 'session_start' => __( 'Session Started', 'wp-simple-firewall' ),
241
- 'session_terminate' => __( 'Session Terminated', 'wp-simple-firewall' ),
242
- 'plugin_activated' => __( 'Plugin Activated', 'wp-simple-firewall' ),
243
- 'plugin_deactivated' => __( 'Plugin Deactivated', 'wp-simple-firewall' ),
244
- 'plugin_upgraded' => __( 'Plugin Upgraded', 'wp-simple-firewall' ),
245
- 'plugin_file_edited' => __( 'Plugin File Edited', 'wp-simple-firewall' ),
246
- 'theme_activated' => __( 'Theme Activated', 'wp-simple-firewall' ),
247
- 'theme_file_edited' => __( 'Theme File Edited', 'wp-simple-firewall' ),
248
- 'theme_upgraded' => __( 'Theme Upgraded', 'wp-simple-firewall' ),
249
- 'core_updated' => __( 'WP Core Updated', 'wp-simple-firewall' ),
250
- 'permalinks_structure' => __( 'Permalinks Updated', 'wp-simple-firewall' ),
251
- 'post_deleted' => __( 'Post Deleted', 'wp-simple-firewall' ),
252
- 'post_trashed' => __( 'Post Trashed', 'wp-simple-firewall' ),
253
- 'post_recovered' => __( 'Post Recovered', 'wp-simple-firewall' ),
254
- 'post_updated' => __( 'Post Updated', 'wp-simple-firewall' ),
255
- 'post_published' => __( 'Post Published', 'wp-simple-firewall' ),
256
- 'post_unpublished' => __( 'Post Unpublished', 'wp-simple-firewall' ),
257
- 'user_login' => __( 'User Login', 'wp-simple-firewall' ),
258
- 'user_login_app' => __( 'User Login By App Password', 'wp-simple-firewall' ),
259
- 'user_registered' => __( 'User Registered', 'wp-simple-firewall' ),
260
- 'user_deleted' => __( 'User Deleted', 'wp-simple-firewall' ),
261
- 'user_deleted_reassigned' => __( 'User Deleted And Reassigned', 'wp-simple-firewall' ),
262
- 'email_attempt_send' => __( 'Email Sent', 'wp-simple-firewall' ),
263
- 'email_send_invalid' => __( 'Invalid Email Sent', 'wp-simple-firewall' ),
264
- 'lic_check_success' => __( 'License Check Success', 'wp-simple-firewall' ),
265
- 'lic_fail_email' => __( 'License Failure Email', 'wp-simple-firewall' ),
266
- 'lic_fail_deactivate' => __( 'License Deactivated', 'wp-simple-firewall' ),
267
  ];
268
 
269
  if ( $auto ) {
9
  /**
10
  * @param string $eventKey
11
  * @return string
12
+ * @deprecated 12.0
13
  */
14
+ public function getEventName( string $eventKey ) :string {
15
+ return $this->getCon()->loadEventsService()->getEventName( $eventKey );
16
  }
17
 
18
  /**
19
  * @param bool $auto
20
  * @return string[]
21
+ * @deprecated 12.0
22
  */
23
  public function getEventNames( bool $auto = true ) :array {
24
  $names = [
25
+ // 'block_param' => __( 'Firewall Blocked Request Parameter', 'wp-simple-firewall' ),
26
+ // 'blockparam_dirtraversal' => sprintf( '%s: %s',
27
+ // __( 'Firewall', 'wp-simple-firewall' ),
28
+ // __( 'Directory Traversal', 'wp-simple-firewall' )
29
+ // ),
30
+ // 'blockparam_wpterms' => sprintf( '%s: %s',
31
+ // __( 'Firewall', 'wp-simple-firewall' ),
32
+ // __( 'WordPress Terms', 'wp-simple-firewall' )
33
+ // ),
34
+ // 'blockparam_fieldtruncation' => sprintf( '%s: %s',
35
+ // __( 'Firewall', 'wp-simple-firewall' ),
36
+ // __( 'Field Truncation', 'wp-simple-firewall' )
37
+ // ),
38
+ // 'blockparam_sqlqueries' => sprintf( '%s: %s',
39
+ // __( 'Firewall', 'wp-simple-firewall' ),
40
+ // __( 'SQL Queries', 'wp-simple-firewall' )
41
+ // ),
42
+ // 'blockparam_schema' => sprintf( '%s: %s',
43
+ // __( 'Firewall', 'wp-simple-firewall' ),
44
+ // __( 'Leading Schema', 'wp-simple-firewall' )
45
+ // ),
46
+ // 'blockparam_aggressive' => sprintf( '%s: %s',
47
+ // __( 'Firewall', 'wp-simple-firewall' ),
48
+ // __( 'Aggressive Rules', 'wp-simple-firewall' )
49
+ // ),
50
+ // 'blockparam_phpcode' => sprintf( '%s: %s',
51
+ // __( 'Firewall', 'wp-simple-firewall' ),
52
+ // __( 'PHP Code', 'wp-simple-firewall' )
53
+ // ),
54
+ // 'block_exefile' => sprintf( '%s: %s',
55
+ // __( 'Firewall', 'wp-simple-firewall' ),
56
+ // __( 'EXE File Uploads', 'wp-simple-firewall' )
57
+ // ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  ];
59
 
60
  if ( $auto ) {
src/lib/src/Modules/Events/Upgrade.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\{
6
+ Base,
7
+ HackGuard
8
+ };
9
+ use FernleafSystems\Wordpress\Services\Services;
10
+
11
+ class Upgrade extends Base\Upgrade {
12
+
13
+ protected function upgrade_1200() {
14
+ $WPDB = Services::WpDb();
15
+ /** @var ModCon $mod */
16
+ $mod = $this->getMod();
17
+ /** @var HackGuard\Options $optsHG */
18
+ $optsHG = $this->getCon()
19
+ ->getModule_HackGuard()
20
+ ->getOptions();
21
+ $scans = $optsHG->getScanSlugs();
22
+
23
+ $eventTable = $mod->getDbHandler_Events()->getTableSchema()->table;
24
+
25
+ $WPDB->doSql( sprintf( "DELETE FROM `%s` WHERE `event` IN ('%s')",
26
+ $eventTable,
27
+ implode( "','", array_map( function ( $scan ) {
28
+ return $scan.'_alert_sent';
29
+ }, $scans ) )
30
+ ) );
31
+
32
+ $WPDB->doSql(
33
+ sprintf( "UPDATE `%s` SET `event`='scan_run' WHERE `event` IN ('%s')",
34
+ $eventTable,
35
+ implode( "','", array_map( function ( $scan ) {
36
+ return $scan.'_scan_run';
37
+ }, $scans ) )
38
+ )
39
+ );
40
+
41
+ $WPDB->doSql(
42
+ sprintf( "UPDATE `%s` SET `event`='scan_items_found' WHERE `event` IN ('%s')",
43
+ $eventTable,
44
+ implode( "','", array_map( function ( $scan ) {
45
+ return $scan.'_scan_found';
46
+ }, $scans ) )
47
+ )
48
+ );
49
+
50
+ $WPDB->doSql(
51
+ sprintf( "UPDATE `%s` SET `event`='firewall_block' WHERE `event` LIKE ('blockparam_%%')", $eventTable )
52
+ );
53
+ }
54
+ }
src/lib/src/Modules/Firewall/Lib/Scan/CanScan.php CHANGED
@@ -21,7 +21,14 @@ class CanScan {
21
  ->setMod( $this->getMod() )
22
  ->retrieve();
23
  $canScan = count( $paramsToScan ) > 0
24
- && ( !$opts->isIgnoreAdmin() && is_super_admin() );
 
 
 
 
 
 
 
25
  }
26
  return $canScan;
27
  }
21
  ->setMod( $this->getMod() )
22
  ->retrieve();
23
  $canScan = count( $paramsToScan ) > 0
24
+ && ( !is_super_admin() || !$opts->isIgnoreAdmin() );
25
+ if ( !$canScan ) {
26
+ $this->getCon()->fireEvent( 'check_skip', [
27
+ 'audit_params' => [
28
+ 'path' => $req->getPath(),
29
+ ]
30
+ ] );
31
+ }
32
  }
33
  return $canScan;
34
  }
src/lib/src/Modules/Firewall/Lib/Scan/Checks/ExeFiles.php CHANGED
@@ -13,12 +13,12 @@ class ExeFiles extends Base {
13
  */
14
  public function run() {
15
  $found = false;
16
- foreach ( $this->getFileNames() as $param => $file ) {
17
  foreach ( $this->getFirewallPatterns_Regex() as $term ) {
18
  if ( preg_match( $term, $file ) ) {
19
  $found = new \WP_Error( 'shield-firewall', '', [
20
  'term' => $term,
21
- 'param' => $param,
22
  'value' => $file,
23
  'check' => $this->check,
24
  'type' => 'regex',
13
  */
14
  public function run() {
15
  $found = false;
16
+ foreach ( $this->getFileNames() as $file ) {
17
  foreach ( $this->getFirewallPatterns_Regex() as $term ) {
18
  if ( preg_match( $term, $file ) ) {
19
  $found = new \WP_Error( 'shield-firewall', '', [
20
  'term' => $term,
21
+ 'param' => 'file',
22
  'value' => $file,
23
  'check' => $this->check,
24
  'type' => 'regex',
src/lib/src/Modules/Firewall/Lib/Scan/FirewallHandler.php ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\{
7
+ Lib\Scan\Handlers\Base,
8
+ ModCon,
9
+ Options
10
+ };
11
+ use FernleafSystems\Wordpress\Services\Services;
12
+
13
+ class FirewallHandler extends ExecOnceModConsumer {
14
+
15
+ /**
16
+ * @var false|\WP_Error
17
+ */
18
+ private $result = false;
19
+
20
+ protected function canRun() :bool {
21
+ return ( new CanScan() )
22
+ ->setMod( $this->getMod() )
23
+ ->run();
24
+ }
25
+
26
+ public function getResult() :\WP_Error {
27
+ return is_wp_error( $this->result ) ? $this->result : new \WP_Error();
28
+ }
29
+
30
+ protected function run() {
31
+ $this->runScans();
32
+
33
+ $result = $this->getResult();
34
+ $block = (bool)apply_filters( 'shield/do_firewall_block', !empty( $result->get_error_codes() ) );
35
+ if ( $block ) {
36
+ $this->doBlock();
37
+ }
38
+ }
39
+
40
+ private function runScans() {
41
+ $opts = $this->getOptions();
42
+
43
+ $this->result = new \WP_Error();
44
+ foreach ( $this->enumHandlers() as $opt => $handlerInit ) {
45
+ if ( $opts->isOpt( 'block_'.$opt, 'Y' ) ) {
46
+ /** @var Base $handler */
47
+ $handler = $handlerInit();
48
+ $result = $handler->setMod( $this->getMod() )->runCheck();
49
+ if ( !empty( $result->get_error_codes() ) ) {
50
+ $this->result = $result;
51
+ break;
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ private function doBlock() {
58
+ /** @var ModCon $mod */
59
+ $mod = $this->getMod();
60
+
61
+ $this->preBlock();
62
+
63
+ switch ( $mod->getBlockResponse() ) {
64
+ case 'redirect_die':
65
+ Services::WpGeneral()->wpDie( 'Firewall Triggered' );
66
+ break;
67
+ case 'redirect_die_message':
68
+ Services::WpGeneral()->wpDie( implode( ' ', $this->getFirewallDieMessage() ) );
69
+ break;
70
+ case 'redirect_home':
71
+ Services::Response()->redirectToHome();
72
+ break;
73
+ case 'redirect_404':
74
+ header( 'Cache-Control: no-store, no-cache' );
75
+ Services::WpGeneral()->turnOffCache();
76
+ Services::Response()->sendApache404();
77
+ break;
78
+ default:
79
+ break;
80
+ }
81
+ die();
82
+ }
83
+
84
+ private function preBlock() {
85
+ /** @var Options $opts */
86
+ $opts = $this->getOptions();
87
+ if ( $opts->isSendBlockEmail() ) {
88
+ $this->getCon()->fireEvent(
89
+ $this->sendBlockEmail() ? 'fw_email_success' : 'fw_email_fail',
90
+ [ 'audit_params' => [ 'to' => $this->getMod()->getPluginReportEmail() ] ]
91
+ );
92
+ }
93
+ $this->getCon()->fireEvent( 'firewall_block', [ 'audit_params' => $this->getResult()->get_error_data() ] );
94
+ }
95
+
96
+ protected function getFirewallDieMessage() :array {
97
+ $default = __( "Something in the request URL or Form data triggered the firewall.", 'wp-simple-firewall' );
98
+ $customMessage = $this->getMod()->getTextOpt( 'text_firewalldie' );
99
+
100
+ $messages = apply_filters(
101
+ 'shield/firewall_die_message',
102
+ [
103
+ empty( $customMessage ) ? $default : $customMessage,
104
+ ]
105
+ );
106
+ return is_array( $messages ) ? $messages : [ $default ];
107
+ }
108
+
109
+ private function sendBlockEmail() :bool {
110
+ $ip = Services::IP()->getRequestIp();
111
+ $resultData = $this->getResult()->get_error_data( 'shield-firewall' );
112
+
113
+ return $this->getMod()
114
+ ->getEmailProcessor()
115
+ ->sendEmailWithTemplate(
116
+ '/email/firewall_block.twig',
117
+ $this->getMod()->getPluginReportEmail(),
118
+ __( 'Firewall Block Alert', 'wp-simple-firewall' ),
119
+ [
120
+ 'strings' => [
121
+ 'shield_blocked' => sprintf( __( '%s Firewall has blocked a request to your WordPress site.', 'wp-simple-firewall' ),
122
+ $this->getCon()->getHumanName() ),
123
+ 'details_below' => __( 'Details for the request are given below:', 'wp-simple-firewall' ),
124
+ 'details' => __( 'Request Details', 'wp-simple-firewall' ),
125
+ 'ip_lookup' => __( 'IP Address Lookup' ),
126
+ 'this_is_info' => __( 'This is for informational purposes only.' ),
127
+ 'already_blocked' => sprintf( __( '%s has already taken the necessary action of blocking the request.' ),
128
+ $this->getCon()->getHumanName() ),
129
+ ],
130
+ 'hrefs' => [
131
+ 'ip_lookup' => add_query_arg( [ 'ip' => $ip ], 'https://shsec.io/botornot' )
132
+ ],
133
+ 'vars' => [
134
+ 'req_details' => [
135
+ __( 'Visitor IP Address', 'wp-simple-firewall' ) => $ip,
136
+ __( 'Firewall Rule', 'wp-simple-firewall' ) => $resultData[ 'name' ],
137
+ __( 'Request Path', 'wp-simple-firewall' ) => Services::Request()->getPath(),
138
+ __( 'Request Parameter', 'wp-simple-firewall' ) => $resultData[ 'param' ],
139
+ __( 'Request Value', 'wp-simple-firewall' ) => $resultData[ 'value' ],
140
+ ]
141
+ ]
142
+ ]
143
+ );
144
+ }
145
+
146
+ /**
147
+ * @return callable[]
148
+ */
149
+ private function enumHandlers() :array {
150
+ return [
151
+ 'dir_traversal' => function () {
152
+ return new Handlers\DirTraversal();
153
+ },
154
+ 'sql_queries' => function () {
155
+ return new Handlers\SqlQueries();
156
+ },
157
+ 'wordpress_terms' => function () {
158
+ return new Handlers\WpTerms();
159
+ },
160
+ 'field_truncation' => function () {
161
+ return new Handlers\FieldTruncation();
162
+ },
163
+ 'php_code' => function () {
164
+ return new Handlers\PhpCode();
165
+ },
166
+ 'leading_schema' => function () {
167
+ return new Handlers\LeadingSchema();
168
+ },
169
+ 'aggressive' => function () {
170
+ return new Handlers\Aggressive();
171
+ },
172
+ 'exe_file_uploads' => function () {
173
+ return new Handlers\ExeFiles();
174
+ },
175
+ ];
176
+ }
177
+ }
src/lib/src/Modules/Firewall/Lib/Scan/Handlers/Aggressive.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan\Handlers;
4
+
5
+ class Aggressive extends BaseRequestParams {
6
+
7
+ const SLUG = 'aggressive';
8
+
9
+ protected function getScanName() :string {
10
+ return __( 'Aggressive', 'wp-simple-firewall' );
11
+ }
12
+ }
src/lib/src/Modules/Firewall/Lib/Scan/Handlers/Base.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan\Handlers;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+
7
+ abstract class Base {
8
+
9
+ use ModConsumer;
10
+
11
+ const SLUG = '';
12
+ const TYPE = '';
13
+
14
+ public function runCheck() :\WP_Error {
15
+ $checkResult = new \WP_Error();
16
+
17
+ if ( !empty( $this->getFirewallPatterns() ) ) {
18
+ $checkResult = $this->testSimplePatterns();
19
+ if ( !is_wp_error( $checkResult ) ) {
20
+ $checkResult = $this->testRegexPatterns();
21
+ }
22
+ }
23
+
24
+ return $checkResult;
25
+ }
26
+
27
+ protected function getFirewallPatterns() :array {
28
+ return $this->getOptions()->getDef( 'firewall_patterns' )[ static::SLUG ] ?? [];
29
+ }
30
+
31
+ protected function getFirewallPatterns_Regex() :array {
32
+ return array_map(
33
+ function ( $regex ) {
34
+ return '/'.$regex.'/i';
35
+ },
36
+ $this->getFirewallPatterns()[ 'regex' ] ?? []
37
+ );
38
+ }
39
+
40
+ protected function getFirewallPatterns_Simple() :array {
41
+ return $this->getFirewallPatterns()[ 'simple' ] ?? [];
42
+ }
43
+
44
+ protected function getItemsToScan() :array {
45
+ return [];
46
+ }
47
+
48
+ protected function getScanName() :string {
49
+ return '';
50
+ }
51
+
52
+ protected function testRegexPatterns() :\WP_Error {
53
+ $found = new \WP_Error;
54
+ foreach ( $this->getFirewallPatterns_Regex() as $term ) {
55
+ foreach ( $this->getItemsToScan() as $param => $value ) {
56
+ if ( preg_match( $term, $value ) ) {
57
+ $found = new \WP_Error( 'shield-firewall', '', [
58
+ 'name' => $this->getScanName(),
59
+ 'term' => $term,
60
+ 'param' => $param,
61
+ 'value' => $value,
62
+ 'scan' => static::SLUG,
63
+ 'type' => static::TYPE,
64
+ ] );
65
+ break 2;
66
+ }
67
+ }
68
+ }
69
+ return $found;
70
+ }
71
+
72
+ protected function testSimplePatterns() :\WP_Error {
73
+ $found = new \WP_Error;
74
+ foreach ( $this->getFirewallPatterns_Simple() as $term ) {
75
+ foreach ( $this->getItemsToScan() as $param => $value ) {
76
+ if ( stripos( $value, $term ) !== false ) {
77
+ $found = new \WP_Error( 'shield-firewall', '', [
78
+ 'name' => $this->getScanName(),
79
+ 'term' => $term,
80
+ 'param' => $param,
81
+ 'value' => $value,
82
+ 'scan' => static::SLUG,
83
+ 'type' => static::TYPE,
84
+ ] );
85
+ break 2;
86
+ }
87
+ }
88
+ }
89
+ return $found;
90
+ }
91
+ }
src/lib/src/Modules/Firewall/Lib/Scan/Handlers/BaseRequestParams.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan\Handlers;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan\ParametersToScan;
6
+
7
+ abstract class BaseRequestParams extends Base {
8
+
9
+ const TYPE = 'param';
10
+
11
+ protected function getItemsToScan() :array {
12
+ return ( new ParametersToScan() )
13
+ ->setMod( $this->getMod() )
14
+ ->retrieve();
15
+ }
16
+ }
src/lib/src/Modules/Firewall/Lib/Scan/Handlers/DirTraversal.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan\Handlers;
4
+
5
+ class DirTraversal extends BaseRequestParams {
6
+
7
+ const SLUG = 'dirtraversal';
8
+
9
+ protected function getScanName() :string {
10
+ return __( 'Directory Traversal', 'wp-simple-firewall' );
11
+ }
12
+ }
src/lib/src/Modules/Firewall/Lib/Scan/Handlers/ExeFiles.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan\Handlers;
4
+
5
+ class ExeFiles extends Base {
6
+
7
+ const SLUG = 'exefile';
8
+ const TYPE = 'file';
9
+
10
+ protected function getItemsToScan() :array {
11
+ return array_filter( array_map(
12
+ function ( $file ) {
13
+ return $file[ 'name' ] ?? '';
14
+ },
15
+ ( !empty( $_FILES ) && is_array( $_FILES ) ) ? $_FILES : []
16
+ ) );
17
+ }
18
+
19
+ protected function getScanName() :string {
20
+ return __( 'Exe File', 'wp-simple-firewall' );
21
+ }
22
+ }
src/lib/src/Modules/Firewall/Lib/Scan/Handlers/FieldTruncation.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan\Handlers;
4
+
5
+ class FieldTruncation extends BaseRequestParams {
6
+
7
+ const SLUG = 'fieldtruncation';
8
+
9
+ protected function getScanName() :string {
10
+ return __( 'Field Truncation', 'wp-simple-firewall' );
11
+ }
12
+ }
src/lib/src/Modules/Firewall/Lib/Scan/Handlers/LeadingSchema.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan\Handlers;
4
+
5
+ class LeadingSchema extends BaseRequestParams {
6
+
7
+ const SLUG = 'schema';
8
+
9
+ protected function getScanName() :string {
10
+ return __( 'Leading Schema', 'wp-simple-firewall' );
11
+ }
12
+ }
src/lib/src/Modules/Firewall/Lib/Scan/Handlers/PhpCode.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan\Handlers;
4
+
5
+ class PhpCode extends BaseRequestParams {
6
+
7
+ const SLUG = 'phpcode';
8
+
9
+ protected function getScanName() :string {
10
+ return __( 'PHP Code', 'wp-simple-firewall' );
11
+ }
12
+ }
src/lib/src/Modules/Firewall/Lib/Scan/Handlers/SqlQueries.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan\Handlers;
4
+
5
+ class SqlQueries extends BaseRequestParams {
6
+
7
+ const SLUG = 'sqlqueries';
8
+
9
+ protected function getScanName() :string {
10
+ return __( 'SQL Queries', 'wp-simple-firewall' );
11
+ }
12
+ }
src/lib/src/Modules/Firewall/Lib/Scan/Handlers/WpTerms.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan\Handlers;
4
+
5
+ class WpTerms extends BaseRequestParams {
6
+
7
+ const SLUG = 'wpterms';
8
+
9
+ protected function getScanName() :string {
10
+ return __( 'WP Terms', 'wp-simple-firewall' );
11
+ }
12
+ }
src/lib/src/Modules/Firewall/Lib/Scan/ParametersToScan.php CHANGED
@@ -10,41 +10,44 @@ class ParametersToScan {
10
 
11
  use ModConsumer;
12
 
13
- private $params = [];
14
 
15
  public function retrieve() :array {
16
 
17
- // Ensure strings and remove non-scalar entries
18
- $this->params = array_map( 'strval', array_filter(
19
- Services::Request()->getRawRequestParams( false ),
20
- function ( $value ) {
21
- return is_scalar( $value );
22
- }
23
- ) );
24
 
25
- if ( !empty( $this->params ) ) {
26
- $this->removeParamsBasedOnPageName();
27
- }
 
 
 
 
28
 
29
- if ( !empty( $this->params ) ) {
30
- $this->removeAllPageParams();
 
 
 
 
 
31
  }
32
 
33
- return $this->params;
34
  }
35
 
36
  private function removeAllPageParams() {
37
  foreach ( $this->getAllPageWhitelistedParameters() as $listParam ) {
38
 
39
  if ( preg_match( '#^/.+/$#', $listParam ) ) {
40
- foreach ( array_keys( $this->params ) as $param ) {
41
  if ( preg_match( $listParam, $param ) ) {
42
- unset( $this->params[ $param ] );
43
  }
44
  }
45
  }
46
- elseif ( isset( $params[ $listParam ] ) ) {
47
- unset( $params[ $listParam ] );
48
  }
49
  }
50
  }
@@ -59,10 +62,10 @@ class ParametersToScan {
59
 
60
  /**
61
  * If the page has no parameters, then remove all parameters to scan
62
- * Otherwise, remove only those parameter specified
63
  */
64
- $this->params = empty( $pageParams ) ? []
65
- : array_diff_key( $this->params, array_flip( $pageParams ) );
66
  break;
67
  }
68
  }
10
 
11
  use ModConsumer;
12
 
13
+ private static $params;
14
 
15
  public function retrieve() :array {
16
 
17
+ if ( !isset( self::$params ) ) {
 
 
 
 
 
 
18
 
19
+ // Ensure strings and remove non-scalar entries
20
+ self::$params = array_map( 'strval', array_filter(
21
+ Services::Request()->getRawRequestParams( false ),
22
+ function ( $value ) {
23
+ return is_scalar( $value );
24
+ }
25
+ ) );
26
 
27
+ if ( !empty( self::$params ) ) {
28
+ $this->removeParamsBasedOnPageName();
29
+ }
30
+
31
+ if ( !empty( self::$params ) ) {
32
+ $this->removeAllPageParams();
33
+ }
34
  }
35
 
36
+ return self::$params;
37
  }
38
 
39
  private function removeAllPageParams() {
40
  foreach ( $this->getAllPageWhitelistedParameters() as $listParam ) {
41
 
42
  if ( preg_match( '#^/.+/$#', $listParam ) ) {
43
+ foreach ( array_keys( self::$params ) as $param ) {
44
  if ( preg_match( $listParam, $param ) ) {
45
+ unset( self::$params[ $param ] );
46
  }
47
  }
48
  }
49
+ else {
50
+ unset( self::$params[ $listParam ] );
51
  }
52
  }
53
  }
62
 
63
  /**
64
  * If the page has no parameters, then remove all parameters to scan
65
+ * Otherwise, remove only those parameters specified
66
  */
67
+ self::$params = empty( $pageParams ) ? []
68
+ : array_diff_key( self::$params, array_flip( $pageParams ) );
69
  break;
70
  }
71
  }
src/lib/src/Modules/Firewall/Lib/Scan/PerformScan.php DELETED
@@ -1,68 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan;
4
-
5
- use FernleafSystems\Utilities\Logic\ExecOnce;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
-
9
- class PerformScan extends ExecOnceModConsumer {
10
-
11
- /**
12
- * @var false|\WP_Error
13
- */
14
- private $checkResult = false;
15
-
16
- /**
17
- * @return false|\WP_Error
18
- */
19
- public function getCheckResult() {
20
- return $this->checkResult;
21
- }
22
-
23
- protected function canRun() :bool {
24
- return ( new CanScan() )
25
- ->setMod( $this->getMod() )
26
- ->run();
27
- }
28
-
29
- protected function run() {
30
- $opts = $this->getOptions();
31
-
32
- $params = ( new ParametersToScan() )
33
- ->setMod( $this->getMod() )
34
- ->retrieve();
35
-
36
- $standardChecker = ( new Checks\Standard() )
37
- ->setMod( $this->getMod() );
38
- foreach ( $this->getStandardChecks() as $opt => $check ) {
39
- if ( $opts->isOpt( 'block_'.$opt, 'Y' ) ) {
40
- $this->checkResult = $standardChecker
41
- ->setCheck( $check )
42
- ->run( $params );
43
- if ( is_wp_error( $this->checkResult ) ) {
44
- break;
45
- }
46
- }
47
- }
48
-
49
- if ( !is_wp_error( $this->checkResult ) ) {
50
- $this->checkResult = ( new Checks\ExeFiles() )
51
- ->setMod( $this->getMod() )
52
- ->setCheck( 'exefile' )
53
- ->run();
54
- }
55
- }
56
-
57
- private function getStandardChecks() :array {
58
- return [
59
- 'dir_traversal' => 'dirtraversal',
60
- 'sql_queries' => 'sqlqueries',
61
- 'wordpress_terms' => 'wpterms',
62
- 'field_truncation' => 'fieldtruncation',
63
- 'php_code' => 'phpcode',
64
- 'leading_schema' => 'schema',
65
- 'aggressive' => 'aggressive',
66
- ];
67
- }
68
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Firewall/Processor.php CHANGED
@@ -3,432 +3,141 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
6
- use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class Processor extends BaseShield\Processor {
9
 
10
  /**
11
- * @var array
12
  */
13
  private $dieMessage;
14
 
15
  /**
16
- * @var array
17
  */
18
  protected $aPatterns;
19
 
20
  /**
21
- * @var array
22
  */
23
  private $aAuditBlockMessage;
24
 
25
  /**
26
- * After any parameter whitelisting has been accounted for
27
- *
28
- * @var array
29
  */
30
  private $params;
31
 
 
 
32
  protected function run() {
33
- if ( $this->getIfPerformFirewallScan() && $this->getIfDoFirewallBlock() ) {
34
- // Hooked here to ensure "plugins_loaded" has completely finished as some mailers aren't init'd.
35
- add_action( 'init', function () {
36
- $this->doPreFirewallBlock();
37
- $this->doFirewallBlock();
38
- }, 0 );
39
- }
40
  }
41
 
42
- private function getIfDoFirewallBlock() :bool {
43
- return apply_filters( 'icwp_shield_do_firewall_block', !$this->isVisitorRequestPermitted() );
44
  }
45
 
46
- private function getIfPerformFirewallScan() :bool {
47
- $bPerformScan = true;
48
- /** @var Options $opts */
49
- $opts = $this->getOptions();
50
-
51
- $path = Services::Request()->getPath();
52
- if ( count( $this->getRawRequestParams() ) == 0 ) {
53
- $bPerformScan = false;
54
- }
55
- elseif ( empty( $path ) ) {
56
- $this->getCon()->fireEvent( 'firewall_skip' );
57
- $bPerformScan = false;
58
- }
59
- elseif ( count( $this->getParamsToCheck() ) == 0 ) {
60
- $bPerformScan = false;
61
  }
62
- // TODO: are we calling is_super_admin() too early?
63
- elseif ( $opts->isIgnoreAdmin() && is_super_admin() ) {
64
- $bPerformScan = false;
65
- }
66
-
67
- return $bPerformScan;
68
  }
69
 
70
- private function isVisitorRequestPermitted() :bool {
71
- $opts = $this->getOptions();
72
-
73
- $bRequestIsPermitted = true;
74
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_dir_traversal', 'Y' ) ) {
75
- $bRequestIsPermitted = $this->doPassCheck( 'dirtraversal' );
76
- }
77
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_sql_queries', 'Y' ) ) {
78
- $bRequestIsPermitted = $this->doPassCheck( 'sqlqueries' );
79
- }
80
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_wordpress_terms', 'Y' ) ) {
81
- $bRequestIsPermitted = $this->doPassCheck( 'wpterms' );
82
- }
83
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_field_truncation', 'Y' ) ) {
84
- $bRequestIsPermitted = $this->doPassCheck( 'fieldtruncation' );
85
- }
86
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_php_code', 'Y' ) ) {
87
- $bRequestIsPermitted = $this->doPassCheck( 'phpcode' );
88
- }
89
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_leading_schema', 'Y' ) ) {
90
- $bRequestIsPermitted = $this->doPassCheck( 'schema' );
91
- }
92
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_aggressive', 'Y' ) ) {
93
- $bRequestIsPermitted = $this->doPassCheck( 'aggressive' );
94
- }
95
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_exe_file_uploads', 'Y' ) ) {
96
- $bRequestIsPermitted = $this->doPassCheckBlockExeFileUploads();
97
  }
98
- return $bRequestIsPermitted;
99
  }
100
 
101
- protected function doPassCheckBlockExeFileUploads() :bool {
102
- /** @var ModCon $mod */
103
- $mod = $this->getMod();
 
 
 
104
 
105
- $sKey = 'exefile';
106
- $bFAIL = false;
107
- if ( isset( $_FILES ) && !empty( $_FILES ) ) {
108
- $aFileNames = [];
109
- foreach ( $_FILES as $aFile ) {
110
- if ( !empty( $aFile[ 'name' ] ) ) {
111
- $aFileNames[] = $aFile[ 'name' ];
112
- }
113
- }
114
- $aMatchTerms = $this->getFirewallPatterns( 'exefile' );
115
- if ( isset( $aMatchTerms[ 'regex' ] ) && is_array( $aMatchTerms[ 'regex' ] ) ) {
116
 
117
- $aMatchTerms[ 'regex' ] = array_map(
118
- function ( $term ) {
119
- return '/'.$term.'/i';
120
- },
121
- $aMatchTerms[ 'regex' ]
122
- );
123
- foreach ( $aMatchTerms[ 'regex' ] as $sTerm ) {
124
- foreach ( $aFileNames as $sParam => $mValue ) {
125
- if ( is_scalar( $mValue ) && preg_match( $sTerm, (string)$mValue ) ) {
126
- $bFAIL = true;
127
- break 2;
128
- }
129
- }
130
- }
131
- }
132
- if ( $bFAIL ) {
133
- $this->getCon()
134
- ->fireEvent(
135
- 'block_exefile',
136
- [
137
- 'audit' => [
138
- 'blockresponse' => $mod->getBlockResponse(),
139
- 'blockkey' => $sKey,
140
- ]
141
- ]
142
 
143
- );
144
- }
145
- }
146
- return !$bFAIL;
147
  }
148
 
149
  /**
150
- * Returns false when check fails - that is, it should be blocked by the firewall.
151
- *
152
- * @param string $sBlockKey
153
- * @return bool
154
  */
155
- private function doPassCheck( string $sBlockKey ) :bool {
156
- /** @var ModCon $mod */
157
- $mod = $this->getMod();
158
-
159
- $aMatchTerms = $this->getFirewallPatterns( $sBlockKey );
160
- $aParamValues = $this->getParamsToCheck();
161
- if ( empty( $aMatchTerms ) || empty( $aParamValues ) ) {
162
- return true;
163
- }
164
-
165
- $sParam = '';
166
- $mValue = '';
167
-
168
- $bFAIL = false;
169
- if ( isset( $aMatchTerms[ 'simple' ] ) && is_array( $aMatchTerms[ 'simple' ] ) ) {
170
-
171
- foreach ( $aMatchTerms[ 'simple' ] as $sTerm ) {
172
- foreach ( $aParamValues as $sParam => $mValue ) {
173
- if ( is_scalar( $mValue ) && ( stripos( (string)$mValue, $sTerm ) !== false ) ) {
174
- $bFAIL = true;
175
- break 2;
176
- }
177
- }
178
- }
179
- }
180
-
181
- if ( !$bFAIL && isset( $aMatchTerms[ 'regex' ] ) && is_array( $aMatchTerms[ 'regex' ] ) ) {
182
- $aMatchTerms[ 'regex' ] = array_map(
183
- function ( $term ) {
184
- return '/'.$term.'/i';
185
- },
186
- $aMatchTerms[ 'regex' ]
187
- );
188
- foreach ( $aMatchTerms[ 'regex' ] as $sTerm ) {
189
- foreach ( $aParamValues as $sParam => $mValue ) {
190
- if ( is_scalar( $mValue ) && preg_match( $sTerm, (string)$mValue ) ) {
191
- $sParam = sanitize_text_field( $sParam );
192
- $mValue = sanitize_text_field( $mValue );
193
- $bFAIL = true;
194
- break 2;
195
- }
196
- }
197
- }
198
- }
199
-
200
- if ( $bFAIL ) {
201
- $this->addToFirewallDieMessage( __( "Something in the URL, Form or Cookie data wasn't appropriate.", 'wp-simple-firewall' ) );
202
-
203
- $this->aAuditBlockMessage = [
204
- sprintf( __( 'Firewall Trigger: %s.', 'wp-simple-firewall' ), $this->getFirewallBlockKeyName( $sBlockKey ) ),
205
- __( 'Page parameter failed firewall check.', 'wp-simple-firewall' ),
206
- sprintf( __( 'The offending parameter was "%s" with a value of "%s".', 'wp-simple-firewall' ), $sParam, $mValue )
207
- ];
208
-
209
- $this->getCon()
210
- ->fireEvent(
211
- 'blockparam_'.$sBlockKey,
212
- [
213
- 'audit' => [
214
- 'param' => $sParam,
215
- 'val' => $mValue,
216
- 'blockresponse' => $mod->getBlockResponse(),
217
- 'blockkey' => $sBlockKey,
218
- ]
219
- ]
220
- );
221
- }
222
-
223
- return !$bFAIL;
224
  }
225
 
226
  /**
227
- * @param string $sKey
228
- * @return array|null
229
  */
230
- protected function getFirewallPatterns( $sKey = null ) {
231
- if ( !isset( $this->aPatterns ) ) {
232
- $this->aPatterns = $this->getOptions()->getDef( 'firewall_patterns' );
233
- }
234
- if ( !empty( $sKey ) ) {
235
- return isset( $this->aPatterns[ $sKey ] ) ? $this->aPatterns[ $sKey ] : null;
236
- }
237
- return $this->aPatterns;
238
  }
239
 
240
- private function doPreFirewallBlock() {
241
- /** @var Options $opts */
242
- $opts = $this->getOptions();
243
- if ( $opts->isSendBlockEmail() ) {
244
- $recipient = $this->getMod()->getPluginReportEmail();
245
- $this->getCon()->fireEvent(
246
- $this->sendBlockEmail( $recipient ) ? 'fw_email_success' : 'fw_email_fail',
247
- [ 'audit' => [ 'recipient' => $recipient ] ]
248
- );
249
- }
250
- $this->getCon()->fireEvent( 'firewall_block' );
251
  }
252
 
253
- private function doFirewallBlock() {
254
- /** @var ModCon $mod */
255
- $mod = $this->getMod();
256
-
257
- switch ( $mod->getBlockResponse() ) {
258
- case 'redirect_die':
259
- Services::WpGeneral()->wpDie( 'Firewall Triggered' );
260
- break;
261
- case 'redirect_die_message':
262
- Services::WpGeneral()->wpDie( $this->getFirewallDieMessageForDisplay() );
263
- break;
264
- case 'redirect_home':
265
- Services::Response()->redirectToHome();
266
- break;
267
- case 'redirect_404':
268
- header( 'Cache-Control: no-store, no-cache' );
269
- Services::WpGeneral()->turnOffCache();
270
- Services::Response()->sendApache404();
271
- break;
272
- default:
273
- break;
274
- }
275
- die();
276
  }
277
 
278
- protected function getFirewallDieMessage() :array {
279
- if ( !isset( $this->dieMessage ) || !is_array( $this->dieMessage ) ) {
280
- $this->dieMessage = [ $this->getMod()->getTextOpt( 'text_firewalldie' ) ];
281
- }
282
- return $this->dieMessage;
283
  }
284
 
285
- protected function getFirewallDieMessageForDisplay() :string {
286
- $messages = apply_filters(
287
- $this->getCon()->prefix( 'firewall_die_message' ),
288
- $this->getFirewallDieMessage()
289
- );
290
- return implode( ' ', is_array( $messages ) ? $messages : [] );
291
  }
292
 
293
  /**
294
  * @param string $msg
295
  * @return $this
 
296
  */
297
  protected function addToFirewallDieMessage( string $msg ) {
298
- $messages = $this->getFirewallDieMessage();
299
- $messages[] = $msg;
300
- $this->dieMessage = $messages;
301
  return $this;
302
  }
303
 
 
 
 
304
  private function getParamsToCheck() :array {
305
- if ( isset( $this->params ) ) {
306
- return $this->params;
307
- }
308
-
309
- /** @var Options $opts */
310
- $opts = $this->getOptions();
311
-
312
- $this->params = $this->getRawRequestParams();
313
- $aWhitelist = Services::DataManipulation()
314
- ->mergeArraysRecursive( $opts->getDef( 'default_whitelist' ), $opts->getCustomWhitelist() );
315
-
316
- // first we remove globally whitelisted request parameters
317
- if ( !empty( $aWhitelist[ '*' ] ) && is_array( $aWhitelist[ '*' ] ) ) {
318
- foreach ( $aWhitelist[ '*' ] as $sWhitelistParam ) {
319
-
320
- if ( preg_match( '#^/.+/$#', $sWhitelistParam ) ) {
321
- foreach ( array_keys( $this->params ) as $sParamKey ) {
322
- if ( preg_match( $sWhitelistParam, $sParamKey ) ) {
323
- unset( $this->params[ $sParamKey ] );
324
- }
325
- }
326
- }
327
- elseif ( isset( $this->params[ $sWhitelistParam ] ) ) {
328
- unset( $this->params[ $sWhitelistParam ] );
329
- }
330
- }
331
- }
332
-
333
- // If the parameters to check is already empty, we return it to save any further processing.
334
- if ( empty( $this->params ) ) {
335
- return $this->params;
336
- }
337
-
338
- // Now we run through the list of whitelist pages
339
- $sRequestPage = Services::Request()->getPath();
340
- foreach ( $aWhitelist as $sWhitelistPageName => $aWhitelistPageParams ) {
341
-
342
- // if the page is white listed
343
- if ( strpos( $sRequestPage, $sWhitelistPageName ) !== false ) {
344
-
345
- // if the page has no particular parameters specified there is nothing to check since the whole page is white listed.
346
- if ( empty( $aWhitelistPageParams ) ) {
347
- $this->params = [];
348
- }
349
- else {
350
- // Otherwise we run through any whitelisted parameters and remove them.
351
- foreach ( $aWhitelistPageParams as $sWhitelistParam ) {
352
- if ( array_key_exists( $sWhitelistParam, $this->params ) ) {
353
- unset( $this->params[ $sWhitelistParam ] );
354
- }
355
- }
356
- }
357
- break;
358
- }
359
- }
360
-
361
- return $this->params;
362
  }
363
 
 
 
 
364
  private function getRawRequestParams() :array {
365
- return Services::Request()->getRawRequestParams( false );
366
- }
367
-
368
- private function sendBlockEmail( string $recipient ) :bool {
369
- $success = false;
370
- if ( !empty( $this->aAuditBlockMessage ) ) {
371
- $sIp = Services::IP()->getRequestIp();
372
- $message = array_merge(
373
- [
374
- sprintf( __( '%s has blocked a page visit to your site.', 'wp-simple-firewall' ), $this->getCon()
375
- ->getHumanName() ),
376
- __( 'Log details for this visitor are below:', 'wp-simple-firewall' ),
377
- '- '.sprintf( '%s: %s', __( 'IP Address', 'wp-simple-firewall' ), $sIp ),
378
- ],
379
- array_map(
380
- function ( $sLine ) {
381
- return '- '.$sLine;
382
- },
383
- $this->aAuditBlockMessage
384
- ),
385
- [
386
- '',
387
- sprintf( __( 'You can look up the offending IP Address here: %s', 'wp-simple-firewall' ), 'http://ip-lookup.net/?ip='.$sIp )
388
- ]
389
- );
390
-
391
- $success = $this->getMod()
392
- ->getEmailProcessor()
393
- ->sendEmailWithWrap(
394
- $recipient,
395
- __( 'Firewall Block Alert', 'wp-simple-firewall' ),
396
- $message
397
- );
398
- }
399
- return $success;
400
  }
401
 
 
 
 
402
  private function getFirewallBlockKeyName( string $blockKey ) :string {
403
- switch ( $blockKey ) {
404
- case 'dirtraversal':
405
- $name = __( 'Directory Traversal', 'wp-simple-firewall' );
406
- break;
407
- case 'wpterms':
408
- $name = __( 'WordPress Terms', 'wp-simple-firewall' );
409
- break;
410
- case 'fieldtruncation':
411
- $name = __( 'Field Truncation', 'wp-simple-firewall' );
412
- break;
413
- case 'sqlqueries':
414
- $name = __( 'SQL Queries', 'wp-simple-firewall' );
415
- break;
416
- case 'exefile':
417
- $name = __( 'EXE File Uploads', 'wp-simple-firewall' );
418
- break;
419
- case 'schema':
420
- $name = __( 'Leading Schema', 'wp-simple-firewall' );
421
- break;
422
- case 'phpcode':
423
- $name = __( 'PHP Code', 'wp-simple-firewall' );
424
- break;
425
- case 'aggressive':
426
- $name = __( 'Aggressive Rules', 'wp-simple-firewall' );
427
- break;
428
- default:
429
- $name = __( 'Unknown Rules', 'wp-simple-firewall' );
430
- break;
431
- }
432
- return $name;
433
  }
434
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Lib\Scan\FirewallHandler;
7
 
8
  class Processor extends BaseShield\Processor {
9
 
10
  /**
11
+ * @deprecated 12.0
12
  */
13
  private $dieMessage;
14
 
15
  /**
16
+ * @deprecated 12.0
17
  */
18
  protected $aPatterns;
19
 
20
  /**
21
+ * @deprecated 12.0
22
  */
23
  private $aAuditBlockMessage;
24
 
25
  /**
26
+ * @deprecated 12.0
 
 
27
  */
28
  private $params;
29
 
30
+ private $firewallHandler;
31
+
32
  protected function run() {
 
 
 
 
 
 
 
33
  }
34
 
35
+ public function onWpInit() {
36
+ $this->getFirewallHandler()->execute();
37
  }
38
 
39
+ private function getFirewallHandler() :FirewallHandler {
40
+ if ( !isset( $this->firewallHandler ) ) {
41
+ $this->firewallHandler = ( new FirewallHandler() )->setMod( $this->getMod() );
 
 
 
 
 
 
 
 
 
 
 
 
42
  }
43
+ return $this->firewallHandler;
 
 
 
 
 
44
  }
45
 
46
+ protected function getWpHookPriority( string $hook ) :int {
47
+ switch ( $hook ) {
48
+ case 'init':
49
+ $pri = 0;
50
+ break;
51
+ default:
52
+ $pri = parent::getWpHookPriority( $hook );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  }
54
+ return $pri;
55
  }
56
 
57
+ private function getIfDoFirewallBlock() :bool {
58
+ return false;
59
+ }
60
+
61
+ private function doPreFirewallBlock() {
62
+ }
63
 
64
+ private function doFirewallBlock() {
65
+ }
 
 
 
 
 
 
 
 
 
66
 
67
+ protected function getFirewallDieMessageForDisplay() :string {
68
+ return '';
69
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
+ private function sendBlockEmail( string $recipient ) :bool {
72
+ return false;
 
 
73
  }
74
 
75
  /**
76
+ * @deprecated 12.0
 
 
 
77
  */
78
+ private function getIfPerformFirewallScan() :bool {
79
+ return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  }
81
 
82
  /**
83
+ * @deprecated 12.0
 
84
  */
85
+ private function isVisitorRequestPermitted() :bool {
86
+ return true;
 
 
 
 
 
 
87
  }
88
 
89
+ /**
90
+ * @deprecated 12.0
91
+ */
92
+ protected function doPassCheckBlockExeFileUploads() :bool {
93
+ return true;
 
 
 
 
 
 
94
  }
95
 
96
+ /**
97
+ * @deprecated 12.0
98
+ */
99
+ private function doPassCheck( string $blockKey ) :bool {
100
+ return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  }
102
 
103
+ protected function getFirewallPatterns( $key = null ) {
104
+ return [];
 
 
 
105
  }
106
 
107
+ /**
108
+ * @deprecated 12.0
109
+ */
110
+ protected function getFirewallDieMessage() :array {
111
+ return [ $this->getMod()->getTextOpt( 'text_firewalldie' ) ];
 
112
  }
113
 
114
  /**
115
  * @param string $msg
116
  * @return $this
117
+ * @deprecated 12.0
118
  */
119
  protected function addToFirewallDieMessage( string $msg ) {
 
 
 
120
  return $this;
121
  }
122
 
123
+ /**
124
+ * @deprecated 12.0
125
+ */
126
  private function getParamsToCheck() :array {
127
+ return [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  }
129
 
130
+ /**
131
+ * @deprecated 12.0
132
+ */
133
  private function getRawRequestParams() :array {
134
+ return [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  }
136
 
137
+ /**
138
+ * @deprecated 12.0
139
+ */
140
  private function getFirewallBlockKeyName( string $blockKey ) :string {
141
+ return '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  }
143
  }
src/lib/src/Modules/Firewall/Strings.php CHANGED
@@ -180,16 +180,45 @@ class Strings extends Base\Strings {
180
  }
181
 
182
  /**
183
- * @return string[][]
184
  */
185
- protected function getAuditMessages() :array {
186
- /** @var ModCon $mod */
187
- $mod = $this->getMod();
188
-
189
- $aMsgs = [
190
- 'check_skip' => [
191
- sprintf( __( 'Skipping firewall checking for this visit: %s.', 'wp-simple-firewall' ), __( 'Parsing the URI failed', 'wp-simple-firewall' ) )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  'blockparam_dirtraversal' => [
194
  sprintf( __( 'Firewall Trigger: %s.', 'wp-simple-firewall' ), __( 'Directory Traversal', 'wp-simple-firewall' ) )
195
  ],
@@ -214,44 +243,6 @@ class Strings extends Base\Strings {
214
  'block_exefile' => [
215
  sprintf( __( 'Firewall Trigger: %s.', 'wp-simple-firewall' ), __( 'EXE File Uploads', 'wp-simple-firewall' ) )
216
  ],
217
- 'fw_email_success' => [
218
- __( 'Successfully sent Firewall Block email alert to: %s', 'wp-simple-firewall' )
219
- ],
220
- 'fw_email_fail' => [
221
- __( 'Failed to send Firewall Block email alert to: %s', 'wp-simple-firewall' )
222
- ],
223
  ];
224
-
225
- foreach ( $aMsgs as $sKey => &$aMsg ) {
226
-
227
- if ( strpos( $sKey, 'blockparam_' ) === 0 ) {
228
- $aMsg[] = __( 'Page parameter failed firewall check.', 'wp-simple-firewall' );
229
- $aMsg[] = __( 'The offending parameter was "%s" with a value of "%s".', 'wp-simple-firewall' );
230
- }
231
-
232
- if ( strpos( $sKey, 'block' ) === 0 ) {
233
-
234
- switch ( $mod->getBlockResponse() ) {
235
- case 'redirect_die':
236
- $sBlkResp = __( 'Visitor connection was killed with wp_die()', 'wp-simple-firewall' );
237
- break;
238
- case 'redirect_die_message':
239
- $sBlkResp = __( 'Visitor connection was killed with wp_die() and a message', 'wp-simple-firewall' );
240
- break;
241
- case 'redirect_home':
242
- $sBlkResp = __( 'Visitor was sent HOME', 'wp-simple-firewall' );
243
- break;
244
- case 'redirect_404':
245
- $sBlkResp = __( 'Visitor was sent 404', 'wp-simple-firewall' );
246
- break;
247
- default:
248
- $sBlkResp = __( 'Unknown', 'wp-simple-firewall' );
249
- break;
250
- }
251
- $aMsg[] = sprintf( __( 'Firewall Block Response: %s.', 'wp-simple-firewall' ), $sBlkResp );
252
- }
253
- }
254
-
255
- return $aMsgs;
256
  }
257
  }
180
  }
181
 
182
  /**
183
+ * @inheritDoc
184
  */
185
+ public function getEventStrings() :array {
186
+ return [
187
+ 'firewall_block' => [
188
+ 'name' => __( 'Firewall Block', 'wp-simple-firewall' ),
189
+ 'audit' => [
190
+ __( 'Request blocked by firewall rule: {{name}}.', 'wp-simple-firewall' ),
191
+ __( 'Rule pattern detected: "{{term}}".', 'wp-simple-firewall' ),
192
+ __( 'The offending request parameter was "{{param}}" with a value of "{{value}}".', 'wp-simple-firewall' ),
193
+ ],
194
+ ],
195
+ 'check_skip' => [
196
+ 'name' => __( 'Firewall Skip Checking', 'wp-simple-firewall' ),
197
+ 'audit' => [
198
+ __( 'Skipping firewall checking for this visit: {{path}}.', 'wp-simple-firewall' )
199
+ ],
200
+ ],
201
+ 'fw_email_success' => [
202
+ 'name' => __( 'Firewall Block Email Success', 'wp-simple-firewall' ),
203
+ 'audit' => [
204
+ __( 'Successfully sent Firewall Block email alert to: {{to}}', 'wp-simple-firewall' )
205
+ ],
206
  ],
207
+ 'fw_email_fail' => [
208
+ 'name' => __( 'Firewall Block Email Fail', 'wp-simple-firewall' ),
209
+ 'audit' => [
210
+ __( 'Failed to send Firewall Block email alert to: {{to}}', 'wp-simple-firewall' )
211
+ ],
212
+ ],
213
+ ];
214
+ }
215
+
216
+ /**
217
+ * @inheritDoc
218
+ */
219
+ protected function getAuditMessages() :array {
220
+ return [
221
+ 'block_param' => sprintf( __( 'Firewall Block Triggered: %s.', 'wp-simple-firewall' ), __( 'Directory Traversal', 'wp-simple-firewall' ) ),
222
  'blockparam_dirtraversal' => [
223
  sprintf( __( 'Firewall Trigger: %s.', 'wp-simple-firewall' ), __( 'Directory Traversal', 'wp-simple-firewall' ) )
224
  ],
243
  'block_exefile' => [
244
  sprintf( __( 'Firewall Trigger: %s.', 'wp-simple-firewall' ), __( 'EXE File Uploads', 'wp-simple-firewall' ) )
245
  ],
 
 
 
 
 
 
246
  ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  }
248
  }
src/lib/src/Modules/GeoIp/Lookup.php CHANGED
@@ -3,65 +3,59 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\GeoIp;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
 
 
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
 
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class Lookup {
10
 
11
  const URL_REDIRECTLI = 'https://api.redirect.li/v1/ip/';
12
- use Databases\Base\HandlerConsumer;
13
  use IpAddressConsumer;
14
 
15
  private $ips = [];
16
 
17
- /**
18
- * @return Databases\GeoIp\EntryVO|null
19
- */
20
- public function lookupIp() {
21
  $ip = $this->getIP();
22
  // Small optimization so we don't SQL it every time.
23
  if ( isset( $this->ips[ $ip ] ) ) {
24
  return $this->ips[ $ip ];
25
  }
26
 
27
- /** @var Databases\GeoIp\Handler $dbh */
28
- $dbh = $this->getDbHandler();
29
- /** @var Databases\GeoIp\Select $select */
30
- $select = $dbh->getQuerySelector();
31
- $IP = $select->byIp( $ip );
32
-
33
- /**
34
- * We look up the IP and if the request fails, we store it anyway so that we don't repeatedly
35
- * bombard the API. The address will eventually be expired over time and lookup will process
36
- * again at a later date, as required
37
- */
38
- if ( empty( $IP ) ) {
39
- $IP = new Databases\GeoIp\EntryVO();
40
- $IP->ip = $ip;
41
- $IP->meta = $this->redirectliIpLookup();
42
- $dbh->getQueryInserter()->insert( $IP );
 
 
 
 
 
43
  }
44
 
45
- $this->ips[ $ip ] = $IP;
46
- return $IP;
47
  }
48
 
49
  private function redirectliIpLookup() :array {
50
- $oHttp = Services::HttpRequest();
51
- $aIpData = @json_decode( $oHttp->getContent( self::URL_REDIRECTLI.$this->getIP() ), true );
52
- if ( empty( $aIpData ) || !is_array( $aIpData ) ) {
53
- $aIpData = [];
54
- }
55
-
56
- return array_intersect_key(
57
- $aIpData,
58
- [
59
- 'countryCode' => '',
60
- 'countryName' => '',
61
- 'timeZone' => '',
62
- 'latitude' => '',
63
- 'longitude' => '',
64
- ]
65
  );
 
 
 
66
  }
67
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\GeoIp;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\IPGeoVO;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\IPRecords;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
9
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
10
  use FernleafSystems\Wordpress\Services\Services;
11
 
12
  class Lookup {
13
 
14
  const URL_REDIRECTLI = 'https://api.redirect.li/v1/ip/';
15
+ use PluginControllerConsumer;
16
  use IpAddressConsumer;
17
 
18
  private $ips = [];
19
 
20
+ public function lookupIp() :IPGeoVO {
 
 
 
21
  $ip = $this->getIP();
22
  // Small optimization so we don't SQL it every time.
23
  if ( isset( $this->ips[ $ip ] ) ) {
24
  return $this->ips[ $ip ];
25
  }
26
 
27
+ try {
28
+ $ipRecord = ( new IPRecords() )
29
+ ->setMod( $this->getCon()->getModule_Data() )
30
+ ->loadIP( $this->getIP(), true );
31
+
32
+ if ( is_null( $ipRecord->geo )
33
+ || Services::Request()->carbon()->subMonth()->timestamp > @$ipRecord->geo[ 'ts' ] ) {
34
+ $ipRecord->geo = $this->redirectliIpLookup();
35
+ $this->getCon()
36
+ ->getModule_Data()
37
+ ->getDbH_IPs()
38
+ ->getQueryUpdater()
39
+ ->updateById( $ipRecord->id, [
40
+ 'geo' => $ipRecord->getRawData()[ 'geo' ]
41
+ ] );
42
+ }
43
+
44
+ $geoData = $ipRecord->geo ?? [];
45
+ }
46
+ catch ( \Exception $e ) {
47
+ $geoData = [];
48
  }
49
 
50
+ return $this->ips[ $ip ] = ( new IPGeoVO() )->applyFromArray( $geoData );
 
51
  }
52
 
53
  private function redirectliIpLookup() :array {
54
+ $data = @json_decode(
55
+ Services::HttpRequest()->getContent( self::URL_REDIRECTLI.$this->getIP() ), true
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  );
57
+ $data = ( empty( $data ) || !is_array( $data ) ) ? [] : $data;
58
+ $data[ 'ts' ] = Services::Request()->carbon( true )->timestamp;
59
+ return $data;
60
  }
61
  }
src/lib/src/Modules/HackGuard/AjaxHandler.php CHANGED
@@ -13,7 +13,6 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
13
 
14
  protected function processAjaxAction( string $action ) :array {
15
 
16
- $req = Services::Request();
17
  switch ( $action ) {
18
 
19
  case 'scanresults_action':
@@ -28,26 +27,6 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
28
  $response = $this->ajaxExec_CheckScans();
29
  break;
30
 
31
- case 'item_action':
32
- $response = $this->ajaxExec_ScanItemAction( $req->post( 'item_action' ) );
33
- break;
34
-
35
- case 'bulk_action':
36
- $response = $this->ajaxExec_ScanItemAction( $req->post( 'bulk_action' ), true );
37
- break;
38
-
39
- case 'item_asset_deactivate':
40
- case 'item_asset_reinstall':
41
- case 'item_delete':
42
- case 'item_ignore':
43
- case 'item_repair':
44
- $response = $this->ajaxExec_ScanItemAction( str_replace( 'item_', '', $action ) );
45
- break;
46
-
47
- case 'render_table_scan':
48
- $response = $this->ajaxExec_BuildTableScan();
49
- break;
50
-
51
  case 'plugin_reinstall':
52
  $response = $this->ajaxExec_PluginReinstall();
53
  break;
@@ -67,60 +46,6 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
67
  return $response;
68
  }
69
 
70
- private function ajaxExec_BuildTableScan() :array {
71
- /** @var ModCon $mod */
72
- $mod = $this->getMod();
73
-
74
- switch ( Services::Request()->post( 'fScan', '' ) ) {
75
-
76
- case 'aggregate':
77
- $oTableBuilder = new Shield\Tables\Build\ScanAggregate();
78
- break;
79
-
80
- case 'apc':
81
- $oTableBuilder = new Shield\Tables\Build\ScanApc();
82
- break;
83
-
84
- case 'mal':
85
- $oTableBuilder = new Shield\Tables\Build\ScanMal();
86
- break;
87
-
88
- case 'wcf':
89
- $oTableBuilder = new Shield\Tables\Build\ScanWcf();
90
- break;
91
-
92
- case 'ptg':
93
- $oTableBuilder = new Shield\Tables\Build\ScanPtg();
94
- break;
95
-
96
- case 'ufc':
97
- $oTableBuilder = new Shield\Tables\Build\ScanUfc();
98
- break;
99
-
100
- case 'wpv':
101
- $oTableBuilder = new Shield\Tables\Build\ScanWpv();
102
- break;
103
-
104
- default:
105
- break;
106
- }
107
-
108
- if ( empty( $oTableBuilder ) ) {
109
- $sHtml = '<div class="alert alert-danger m-0">SCAN SLUG NOT SUPPORTED</div>';
110
- }
111
- else {
112
- $sHtml = $oTableBuilder
113
- ->setMod( $mod )
114
- ->setDbHandler( $mod->getDbHandler_ScanResults() )
115
- ->render();
116
- }
117
-
118
- return [
119
- 'success' => !empty( $oTableBuilder ),
120
- 'html' => $sHtml
121
- ];
122
- }
123
-
124
  private function ajaxExec_FileLockerShowDiff() :array {
125
  /** @var ModCon $mod */
126
  $mod = $this->getMod();
@@ -278,80 +203,6 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
278
  return [ 'success' => true ];
279
  }
280
 
281
- private function ajaxExec_ScanItemAction( string $action, bool $isBulkAction = false ) :array {
282
- /** @var ModCon $mod */
283
- $mod = $this->getMod();
284
-
285
- $success = false;
286
-
287
- if ( $action == 'download' ) {
288
- // A special case since this action is handled using Javascript
289
- $success = true;
290
- $msg = __( 'File download has started.', 'wp-simple-firewall' );
291
- }
292
- else {
293
- if ( $isBulkAction ) {
294
- $itemIDs = (array)Services::Request()->post( 'ids', [] );
295
- }
296
- else {
297
- $itemIDs = [ Services::Request()->post( 'rid' ) ];
298
- }
299
- /** @var int[] $itemIDs */
300
- $itemIDs = array_filter( array_map( 'intval', $itemIDs ) );
301
-
302
- if ( empty( $itemIDs ) ) {
303
- $msg = __( 'Unsupported item(s) selected', 'wp-simple-firewall' );
304
- }
305
- else {
306
- try {
307
- $scanSlugs = [];
308
- $aSuccessfulItems = [];
309
- foreach ( $itemIDs as $ID ) {
310
- /** @var Shield\Databases\Scanner\EntryVO $entry */
311
- $entry = $mod->getDbHandler_ScanResults()
312
- ->getQuerySelector()
313
- ->byId( $ID );
314
- if ( $entry instanceof Shield\Databases\Scanner\EntryVO ) {
315
- $scanSlugs[] = $entry->scan;
316
- if ( $mod->getScanCon( $entry->scan )->executeItemAction( $ID, $action ) ) {
317
- $aSuccessfulItems[] = $ID;
318
- }
319
- }
320
- }
321
-
322
- if ( count( $aSuccessfulItems ) === count( $itemIDs ) ) {
323
- $success = true;
324
- $msg = __( 'Action successful.' );
325
- }
326
- else {
327
- $msg = __( 'An error occurred.' ).' '.__( 'Some items may not have been processed.' );
328
- }
329
-
330
- // We don't rescan for ignores.
331
- $rescanSlugs = array_diff( $scanSlugs, [ Scan\Controller\Mal::SCAN_SLUG ] );
332
-
333
- if ( empty( $rescanSlugs ) || in_array( $action, [ 'ignore' ] ) ) {
334
- $msg .= ' '.__( 'Reloading', 'wp-simple-firewall' ).' ...';
335
- }
336
- else {
337
- // rescan
338
- $mod->getScanQueueController()->startScans( $rescanSlugs );
339
- $msg .= ' '.__( 'Rescanning', 'wp-simple-firewall' ).' ...';
340
- }
341
- }
342
- catch ( \Exception $e ) {
343
- $msg = $e->getMessage();
344
- }
345
- }
346
- }
347
-
348
- return [
349
- 'success' => $success,
350
- 'page_reload' => !in_array( $action, [ 'download' ] ),
351
- 'message' => $msg,
352
- ];
353
- }
354
-
355
  private function ajaxExec_CheckScans() :array {
356
  /** @var ModCon $mod */
357
  $mod = $this->getMod();
@@ -414,9 +265,9 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
414
  if ( !empty( $formParams ) ) {
415
  $selected = array_keys( $formParams );
416
 
417
- $aUiTrack = $mod->getUiTrack();
418
- $aUiTrack[ 'selected_scans' ] = array_intersect( array_keys( $formParams ), $opts->getScanSlugs() );
419
- $mod->setUiTrack( $aUiTrack );
420
 
421
  $toScan = [];
422
  foreach ( $selected as $slug ) {
13
 
14
  protected function processAjaxAction( string $action ) :array {
15
 
 
16
  switch ( $action ) {
17
 
18
  case 'scanresults_action':
27
  $response = $this->ajaxExec_CheckScans();
28
  break;
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  case 'plugin_reinstall':
31
  $response = $this->ajaxExec_PluginReinstall();
32
  break;
46
  return $response;
47
  }
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  private function ajaxExec_FileLockerShowDiff() :array {
50
  /** @var ModCon $mod */
51
  $mod = $this->getMod();
203
  return [ 'success' => true ];
204
  }
205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  private function ajaxExec_CheckScans() :array {
207
  /** @var ModCon $mod */
208
  $mod = $this->getMod();
265
  if ( !empty( $formParams ) ) {
266
  $selected = array_keys( $formParams );
267
 
268
+ $uiTrack = $mod->getUiTrack();
269
+ $uiTrack->selected_scans = array_intersect( array_keys( $formParams ), $opts->getScanSlugs() );
270
+ $mod->setUiTrack( $uiTrack );
271
 
272
  $toScan = [];
273
  foreach ( $selected as $slug ) {
src/lib/src/Modules/HackGuard/Lib/Reports/ScanAlerts.php CHANGED
@@ -26,7 +26,7 @@ class ScanAlerts extends BaseReporter {
26
  foreach ( $scanCounts as $slug => $count ) {
27
  $scanCounts[ $slug ] = [
28
  'count' => $count,
29
- 'name' => $strings->getScanNames()[ $slug ],
30
  ];
31
  }
32
  $alerts[] = $this->getMod()->renderTemplate(
26
  foreach ( $scanCounts as $slug => $count ) {
27
  $scanCounts[ $slug ] = [
28
  'count' => $count,
29
+ 'name' => $strings->getScanName( $slug ),
30
  ];
31
  }
32
  $alerts[] = $this->getMod()->renderTemplate(
src/lib/src/Modules/HackGuard/Lib/Reports/ScanRepairs.php CHANGED
@@ -2,9 +2,9 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Reports;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\AuditTrail as DBAudit;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events as DBEvents;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
 
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports\BaseReporter;
9
 
10
  class ScanRepairs extends BaseReporter {
@@ -12,14 +12,11 @@ class ScanRepairs extends BaseReporter {
12
  public function build() :array {
13
  $alerts = [];
14
 
15
- $modEvents = $this->getCon()->getModule_Events();
16
  /** @var DBEvents\Select $selectorEvents */
17
  $selectorEvents = $this->getCon()
18
  ->getModule_Events()
19
  ->getDbHandler_Events()
20
  ->getQuerySelector();
21
- /** @var Events\Strings $strings */
22
- $strings = $modEvents->getStrings();
23
 
24
  $report = $this->getReport();
25
 
@@ -31,6 +28,7 @@ class ScanRepairs extends BaseReporter {
31
  ];
32
 
33
  $total = 0;
 
34
  foreach ( $repairEvents as $event ) {
35
  $eventTotal = $selectorEvents
36
  ->filterByBoundary( $report->interval_start_at, $report->interval_end_at )
@@ -38,28 +36,36 @@ class ScanRepairs extends BaseReporter {
38
  $total += $eventTotal;
39
 
40
  if ( $eventTotal > 0 ) {
41
- /** @var DBAudit\Select $auditSelector */
42
- $auditSelector = $this->getCon()
43
- ->getModule_AuditTrail()
44
- ->getDbHandler_AuditTrail()
45
- ->getQuerySelector();
46
- /** @var DBAudit\EntryVO[] $audits */
47
- $audits = $auditSelector->filterByEvent( $event )
48
- ->filterByBoundary( $report->interval_start_at, $report->interval_end_at )
49
- ->setLimit( 10 )
50
- ->query();
 
 
 
 
 
 
 
51
 
52
  $repairs[] = [
53
  'count' => $eventTotal,
54
- 'name' => $strings->getEventName( $event ),
55
- 'repairs' => array_filter( array_map( function ( $entry ) {
56
- // see Base ItemActionHandler for audit event data
57
- $fragment = $entry->meta[ 'path_full' ] ?? ( $entry->meta[ 'fragment' ] ?? false );
58
- if ( !empty( $fragment ) ) {
59
- $fragment = str_replace( wp_normalize_path( ABSPATH ), '', $fragment );
60
- }
61
- return $fragment;
62
- }, $audits ) ),
 
63
  ];
64
  }
65
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Reports;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events as DBEvents;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Logs;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Meta;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports\BaseReporter;
9
 
10
  class ScanRepairs extends BaseReporter {
12
  public function build() :array {
13
  $alerts = [];
14
 
 
15
  /** @var DBEvents\Select $selectorEvents */
16
  $selectorEvents = $this->getCon()
17
  ->getModule_Events()
18
  ->getDbHandler_Events()
19
  ->getQuerySelector();
 
 
20
 
21
  $report = $this->getReport();
22
 
28
  ];
29
 
30
  $total = 0;
31
+ $srvEvents = $this->getCon()->loadEventsService();
32
  foreach ( $repairEvents as $event ) {
33
  $eventTotal = $selectorEvents
34
  ->filterByBoundary( $report->interval_start_at, $report->interval_end_at )
36
  $total += $eventTotal;
37
 
38
  if ( $eventTotal > 0 ) {
39
+ $modAudit = $this->getCon()->getModule_AuditTrail();
40
+
41
+ /** @var Logs\Ops\Select $logSelect */
42
+ $logSelect = $modAudit->getDbH_Logs()->getQuerySelector();
43
+ /** @var Logs\Ops\Record[] $logs */
44
+ $logIDs = array_map(
45
+ function ( $log ) {
46
+ return $log->id;
47
+ },
48
+ $logSelect->filterByEvent( $event )
49
+ ->filterByBoundary( $report->interval_start_at, $report->interval_end_at )
50
+ ->setLimit( $eventTotal )
51
+ ->queryWithResult()
52
+ );
53
+
54
+ /** @var Meta\Ops\Select $metaSelect */
55
+ $metaSelect = $modAudit->getDbH_Meta()->getQuerySelector();
56
 
57
  $repairs[] = [
58
  'count' => $eventTotal,
59
+ 'name' => $srvEvents->getEventName( $event ),
60
+ 'repairs' => array_unique( array_map(
61
+ function ( $meta ) {
62
+ /** @var Meta\Ops\Record $meta */
63
+ return str_replace( ABSPATH, '', $meta->meta_value );
64
+ },
65
+ $metaSelect->filterByLogRefs( $logIDs )
66
+ ->filterByMetaKey( 'path_full' )
67
+ ->queryWithResult()
68
+ ) ),
69
  ];
70
  }
71
  }
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/ScheduleBuildAll.php CHANGED
@@ -33,9 +33,9 @@ class ScheduleBuildAll extends BaseBulk {
33
  if ( empty( $meta[ 'cs_hashes_at' ] ) ) {
34
  $meta[ 'cs_hashes_at' ] = Services::Request()->ts();
35
  if ( $store->setSnapMeta( $meta )->saveMeta() ) {
36
- ( new SubmitHashes() )
37
- ->setMod( $this->getMod() )
38
- ->run( $asset );
39
  }
40
  }
41
  }
33
  if ( empty( $meta[ 'cs_hashes_at' ] ) ) {
34
  $meta[ 'cs_hashes_at' ] = Services::Request()->ts();
35
  if ( $store->setSnapMeta( $meta )->saveMeta() ) {
36
+ // ( new SubmitHashes() )
37
+ // ->setMod( $this->getMod() )
38
+ // ->run( $asset );
39
  }
40
  }
41
  }
src/lib/src/Modules/HackGuard/ModCon.php CHANGED
@@ -194,7 +194,6 @@ class ModCon extends BaseShield\ModCon {
194
  $opts = $this->getOptions();
195
  return $this->isModuleEnabled() && $this->isPremium()
196
  && $opts->isOpt( 'ptg_enable', 'enabled' )
197
- && $opts->isOptReqsMet( 'ptg_enable' )
198
  && $this->getCon()->hasCacheDir()
199
  && !empty( $this->getPtgSnapsBaseDir() );
200
  }
194
  $opts = $this->getOptions();
195
  return $this->isModuleEnabled() && $this->isPremium()
196
  && $opts->isOpt( 'ptg_enable', 'enabled' )
 
197
  && $this->getCon()->hasCacheDir()
198
  && !empty( $this->getPtgSnapsBaseDir() );
199
  }
src/lib/src/Modules/HackGuard/Processor.php CHANGED
@@ -18,16 +18,4 @@ class Processor extends BaseShield\Processor {
18
  $mod->getFileLocker()->execute();
19
  }
20
  }
21
-
22
- public function runHourlyCron() {
23
- }
24
-
25
- public function runDailyCron() {
26
- }
27
-
28
- public function onWpLoaded() {
29
- }
30
-
31
- public function onModuleShutdown() {
32
- }
33
  }
18
  $mod->getFileLocker()->execute();
19
  }
20
  }
 
 
 
 
 
 
 
 
 
 
 
 
21
  }
src/lib/src/Modules/HackGuard/Scan/Controller/Base.php CHANGED
@@ -11,7 +11,6 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
11
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\ResultItem;
12
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\ResultsSet;
13
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseScanActionVO;
14
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Table\BaseEntryFormatter;
15
  use FernleafSystems\Wordpress\Services\Services;
16
 
17
  abstract class Base extends ExecOnceModConsumer {
@@ -77,16 +76,6 @@ abstract class Base extends ExecOnceModConsumer {
77
  return $this->getMod()->createFileDownloadLink( 'scan_file', [ 'rid' => $recordID ] );
78
  }
79
 
80
- public function getLastScanAt() :int {
81
- /** @var Databases\Events\Select $sel */
82
- $sel = $this->getCon()
83
- ->getModule_Events()
84
- ->getDbHandler_Events()
85
- ->getQuerySelector();
86
- $entry = $sel->getLatestForEvent( $this->getSlug().'_scan_run' );
87
- return ( $entry instanceof Databases\Events\EntryVO ) ? (int)$entry->created_at : 0;
88
- }
89
-
90
  public function countScanProblems() :int {
91
  if ( !isset( self::$resultsCounts ) ) {
92
  /** @var ModCon $mod */
@@ -118,24 +107,6 @@ abstract class Base extends ExecOnceModConsumer {
118
  ->process( $action );
119
  }
120
 
121
- public function executeItemAction( int $recordID, string $action ) :bool {
122
- $success = false;
123
-
124
- if ( is_numeric( $recordID ) ) {
125
- /** @var Databases\Scanner\EntryVO $entry */
126
- $entry = $this->getScanResultsDbHandler()
127
- ->getQuerySelector()
128
- ->byId( $recordID );
129
- if ( empty( $entry ) ) {
130
- throw new \Exception( 'Item could not be found.' );
131
- }
132
-
133
- $success = $this->executeEntryAction( $entry, $action );
134
- }
135
-
136
- return $success;
137
- }
138
-
139
  /**
140
  * @return Scans\Base\ResultsSet|mixed
141
  */
@@ -149,15 +120,6 @@ abstract class Base extends ExecOnceModConsumer {
149
  ->fromVOsToResultsSet( $sel->query() );
150
  }
151
 
152
- /**
153
- * @return bool
154
- */
155
- public function updateAllAsNotified() {
156
- /** @var Databases\Scanner\Update $updater */
157
- $updater = $this->getScanResultsDbHandler()->getQueryUpdater();
158
- return $updater->setAllNotifiedForScan( $this->getSlug() );
159
- }
160
-
161
  /**
162
  * @param bool $includeIgnored
163
  * @return Scans\Base\ResultsSet|mixed
@@ -202,7 +164,7 @@ abstract class Base extends ExecOnceModConsumer {
202
  public function getScanName() :string {
203
  /** @var HackGuard\Strings $strings */
204
  $strings = $this->getMod()->getStrings();
205
- return $strings->getScanNames()[ static::SCAN_SLUG ];
206
  }
207
 
208
  public function isCronAutoRepair() :bool {
@@ -229,24 +191,20 @@ abstract class Base extends ExecOnceModConsumer {
229
  return $this->isPremiumOnly() && !$this->getCon()->isPremiumActive();
230
  }
231
 
232
- /**
233
- * @return $this
234
- */
235
- public function resetIgnoreStatus() {
236
- /** @var Databases\Scanner\Update $oUpd */
237
- $oUpd = $this->getScanResultsDbHandler()->getQueryUpdater();
238
- $oUpd->clearIgnoredAtForScan( $this->getSlug() );
239
- return $this;
240
  }
241
 
242
- /**
243
- * @return $this
244
- */
245
- public function resetNotifiedStatus() {
246
- /** @var Databases\Scanner\Update $oUpd */
247
- $oUpd = $this->getScanResultsDbHandler()->getQueryUpdater();
248
- $oUpd->clearNotifiedAtForScan( $this->getSlug() );
249
- return $this;
250
  }
251
 
252
  /**
@@ -310,18 +268,6 @@ abstract class Base extends ExecOnceModConsumer {
310
  return new $class();
311
  }
312
 
313
- /**
314
- * @return BaseEntryFormatter|mixed
315
- */
316
- public function getTableEntryFormatter() {
317
- $class = $this->getScanNamespace().'Table\\EntryFormatter';
318
- /** @var BaseEntryFormatter $formatter */
319
- $formatter = new $class();
320
- return $formatter->setScanController( $this )
321
- ->setMod( $this->getMod() )
322
- ->setScanActionVO( $this->getScanActionVO() );
323
- }
324
-
325
  public function getScanNamespace() :string {
326
  try {
327
  $ns = ( new \ReflectionClass( $this->getScanActionVO() ) )->getNamespaceName();
11
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\ResultItem;
12
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\ResultsSet;
13
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseScanActionVO;
 
14
  use FernleafSystems\Wordpress\Services\Services;
15
 
16
  abstract class Base extends ExecOnceModConsumer {
76
  return $this->getMod()->createFileDownloadLink( 'scan_file', [ 'rid' => $recordID ] );
77
  }
78
 
 
 
 
 
 
 
 
 
 
 
79
  public function countScanProblems() :int {
80
  if ( !isset( self::$resultsCounts ) ) {
81
  /** @var ModCon $mod */
107
  ->process( $action );
108
  }
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  /**
111
  * @return Scans\Base\ResultsSet|mixed
112
  */
120
  ->fromVOsToResultsSet( $sel->query() );
121
  }
122
 
 
 
 
 
 
 
 
 
 
123
  /**
124
  * @param bool $includeIgnored
125
  * @return Scans\Base\ResultsSet|mixed
164
  public function getScanName() :string {
165
  /** @var HackGuard\Strings $strings */
166
  $strings = $this->getMod()->getStrings();
167
+ return $strings->getScanStrings()[ static::SCAN_SLUG ][ 'name' ];
168
  }
169
 
170
  public function isCronAutoRepair() :bool {
191
  return $this->isPremiumOnly() && !$this->getCon()->isPremiumActive();
192
  }
193
 
194
+ public function resetIgnoreStatus() :bool {
195
+ return $this->getScanResultsDbHandler()
196
+ ->getQueryUpdater()
197
+ ->setUpdateWheres( [ 'scan' => $this->getSlug() ] )
198
+ ->setUpdateData( [ 'ignored_at' => 0 ] )
199
+ ->query() !== false;
 
 
200
  }
201
 
202
+ public function resetNotifiedStatus() :bool {
203
+ return $this->getScanResultsDbHandler()
204
+ ->getQueryUpdater()
205
+ ->setUpdateWheres( [ 'scan' => $this->getSlug() ] )
206
+ ->setUpdateData( [ 'notified_at' => 0 ] )
207
+ ->query() !== false;
 
 
208
  }
209
 
210
  /**
268
  return new $class();
269
  }
270
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  public function getScanNamespace() :string {
272
  try {
273
  $ns = ( new \ReflectionClass( $this->getScanActionVO() ) )->getNamespaceName();
src/lib/src/Modules/HackGuard/Scan/Queue/CompleteQueue.php CHANGED
@@ -38,7 +38,7 @@ class CompleteQueue {
38
  ->setDbHandler( $dbh )
39
  ->collate( $scanSlug );
40
 
41
- $con->fireEvent( $scanSlug.'_scan_run' );
42
 
43
  ( new HackGuard\Scan\Results\ResultsUpdate() )
44
  ->setScanController( $scanCon )
@@ -50,16 +50,16 @@ class CompleteQueue {
50
  __( 'Only the first 30 items are shown.', 'wp-simple-firewall' )
51
  : __( 'The following items were discovered.', 'wp-simple-firewall' );
52
 
53
- $items .= ' "'.
54
- implode( '", "', array_map( function ( $item ) {
55
- return $item->getDescriptionForAudit();
56
- }, array_slice( $resultsSet->getItems(), 0, 30 ) ) )
57
- .'"';
58
 
59
  $con->fireEvent(
60
- $scanSlug.'_scan_found',
61
  [
62
- 'audit' => [
 
63
  'items' => $items
64
  ]
65
  ]
38
  ->setDbHandler( $dbh )
39
  ->collate( $scanSlug );
40
 
41
+ $con->fireEvent( 'scan_run', [ 'audit_params' => [ 'scan' => $scanCon->getScanName() ] ] );
42
 
43
  ( new HackGuard\Scan\Results\ResultsUpdate() )
44
  ->setScanController( $scanCon )
50
  __( 'Only the first 30 items are shown.', 'wp-simple-firewall' )
51
  : __( 'The following items were discovered.', 'wp-simple-firewall' );
52
 
53
+ $itemDescriptions = array_slice( array_unique( array_map( function ( $item ) {
54
+ return $item->getDescriptionForAudit();
55
+ }, $resultsSet->getItems() ) ), 0, 30 );
56
+ $items .= ' "'.implode( '", "', $itemDescriptions ).'"';
 
57
 
58
  $con->fireEvent(
59
+ 'scan_items_found',
60
  [
61
+ 'audit_params' => [
62
+ 'scan' => $scanCon->getScanName(),
63
  'items' => $items
64
  ]
65
  ]
src/lib/src/Modules/HackGuard/Strings.php CHANGED
@@ -8,64 +8,98 @@ use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class Strings extends Base\Strings {
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  public function getScanName( string $slug ) :string {
12
- return $this->getScanNames()[ $slug ];
13
  }
14
 
15
  /**
16
  * @return string[]
 
17
  */
18
  public function getScanNames() :array {
19
- return [
20
- 'apc' => __( 'Abandoned Plugins', 'wp-simple-firewall' ),
21
- 'ptg' => __( 'Plugin/Theme Guard', 'wp-simple-firewall' ),
22
- 'mal' => __( 'Malware', 'wp-simple-firewall' ),
23
- 'ufc' => __( 'Unrecognised Files', 'wp-simple-firewall' ),
24
- 'wcf' => __( 'WordPress Core Files', 'wp-simple-firewall' ),
25
- 'wpv' => __( 'Vulnerabilities', 'wp-simple-firewall' ),
26
- ];
27
  }
28
 
29
  /**
30
- * @return string[][]
31
  */
32
- protected function getAuditMessages() :array {
33
- $messages = [];
34
- foreach ( $this->getScanNames() as $slug => $scanName ) {
35
- $messages[ $slug.'_alert_sent' ] = [
36
- sprintf( __( '%s scan alert sent.', 'wp-simple-firewall' ), $scanName )
37
- .' '.__( 'Alert sent to %s via %s.' )
38
- ];
39
- $messages[ $slug.'_scan_found' ] = [
40
- sprintf( __( '%s scan completed and items were discovered.', 'wp-simple-firewall' ), $scanName ),
41
- sprintf( '%s: %s',
42
- __( 'Note', 'wp-simple-firewall' ),
43
- __( "These items wont display in results if you've previously marked them as ignored.", 'wp-simple-firewall' )
44
- ),
45
- '%s'
46
- ];
47
- $messages[ 'scan_item_delete_success' ] = [
48
- __( 'Deleted item found in the scan.', 'wp-simple-firewall' )
49
- .' '.__( 'Item deleted: "%s"', 'wp-simple-firewall' ),
50
- ];
51
- $messages[ 'scan_item_repair_success' ] = [
52
- __( 'Repaired item found in the scan.', 'wp-simple-firewall' )
53
- .' '.__( 'Item repaired: "%s"', 'wp-simple-firewall' ),
54
- ];
55
- $messages[ 'scan_item_repair_fail' ] = [
56
- __( 'Failed to repair scan item.', 'wp-simple-firewall' )
57
- .' '.__( 'Failed item: "%s"', 'wp-simple-firewall' ),
58
- ];
59
- $messages[ $slug.'_item_repair_success' ] = [
60
- sprintf( __( '%s scan repaired a item found in the scan.', 'wp-simple-firewall' ), $scanName )
61
- .' '.__( 'Item repaired: "%s"', 'wp-simple-firewall' ),
62
- ];
63
- $messages[ $slug.'_item_repair_fail' ] = [
64
- sprintf( __( '%s scan could not repair item.', 'wp-simple-firewall' ), $scanName )
65
- .' '.__( 'Failed repair item: "%s"', 'wp-simple-firewall' ),
66
- ];
67
- }
68
- return $messages;
69
  }
70
 
71
  /**
8
 
9
  class Strings extends Base\Strings {
10
 
11
+ /**
12
+ * @inheritDoc
13
+ */
14
+ public function getEventStrings() :array {
15
+ return [
16
+ 'scan_run' => [
17
+ 'name' => __( 'Scan Completed', 'wp-simple-firewall' ),
18
+ 'audit' => [
19
+ sprintf( '%s: {{scan}}', __( 'Scan Completed', 'wp-simple-firewall' ) ),
20
+ ],
21
+ ],
22
+ 'scan_item_delete_success' => [
23
+ 'name' => __( 'Scan Item Delete Success', 'wp-simple-firewall' ),
24
+ 'audit' => [
25
+ __( 'Deleted item found in the scan.', 'wp-simple-firewall' ),
26
+ __( 'Item deleted: "{{path_full}}"', 'wp-simple-firewall' ),
27
+ ],
28
+ ],
29
+ 'scan_item_repair_success' => [
30
+ 'name' => __( 'Scan Item Repair Success', 'wp-simple-firewall' ),
31
+ 'audit' => [
32
+ __( 'Repaired item found in the scan.', 'wp-simple-firewall' ),
33
+ __( 'Item repaired: "{{path_full}}"', 'wp-simple-firewall' ),
34
+ ],
35
+ ],
36
+ 'scan_item_repair_fail' => [
37
+ 'name' => __( 'Scan Item Repair Failure', 'wp-simple-firewall' ),
38
+ 'audit' => [
39
+ __( 'Failed to repair scan item.', 'wp-simple-firewall' ),
40
+ __( 'Failed item: "{{path_full}}"', 'wp-simple-firewall' ),
41
+ ],
42
+ ],
43
+ 'scan_items_found' => [
44
+ 'name' => __( 'Items Found In Scan', 'wp-simple-firewall' ),
45
+ 'audit' => [
46
+ __( '{{scan}}: scan completed and items were discovered.', 'wp-simple-firewall' ),
47
+ sprintf( '%s: %s {{items}}',
48
+ __( 'Note', 'wp-simple-firewall' ),
49
+ __( "These items wont display in results if you've previously marked them as ignored.", 'wp-simple-firewall' )
50
+ ),
51
+ ],
52
+ ],
53
+ ];
54
+ }
55
+
56
  public function getScanName( string $slug ) :string {
57
+ return $this->getScanStrings()[ $slug ][ 'name' ];
58
  }
59
 
60
  /**
61
  * @return string[]
62
+ * @deprecated 12.0
63
  */
64
  public function getScanNames() :array {
65
+ return array_map(
66
+ function ( $strings ) {
67
+ return $strings[ 'name' ];
68
+ },
69
+ $this->getScanStrings()
70
+ );
 
 
71
  }
72
 
73
  /**
74
+ * @return string[]
75
  */
76
+ public function getScanStrings() :array {
77
+ return [
78
+ 'apc' => [
79
+ 'name' => __( 'Abandoned Plugins', 'wp-simple-firewall' ),
80
+ 'subtitle' => __( "Discover plugins that may have been abandoned by their authors", 'wp-simple-firewall' ),
81
+ ],
82
+ 'ptg' => [
83
+ 'name' => __( 'Plugin/Theme Guard', 'wp-simple-firewall' ),
84
+ 'subtitle' => __( "Be alerted to file changes for all your plugins and themes", 'wp-simple-firewall' ),
85
+ ],
86
+ 'mal' => [
87
+ 'name' => __( 'Malware', 'wp-simple-firewall' ),
88
+ 'subtitle' => __( "Detect files that may be infected with malware", 'wp-simple-firewall' ),
89
+ ],
90
+ 'ufc' => [
91
+ 'name' => __( 'Unrecognised Files', 'wp-simple-firewall' ),
92
+ 'subtitle' => __( "Detect files which aren't part of the official WordPress.org distribution", 'wp-simple-firewall' ),
93
+ ],
94
+ 'wcf' => [
95
+ 'name' => __( 'WordPress Core Files', 'wp-simple-firewall' ),
96
+ 'subtitle' => __( "Detect changes to core WordPress files when compared to the official distribution", 'wp-simple-firewall' ),
97
+ ],
98
+ 'wpv' => [
99
+ 'name' => __( 'Vulnerabilities', 'wp-simple-firewall' ),
100
+ 'subtitle' => __( "Be alerted to plugins and themes with known security vulnerabilities", 'wp-simple-firewall' ),
101
+ ],
102
+ ];
 
 
 
 
 
 
 
 
 
 
103
  }
104
 
105
  /**
src/lib/src/Modules/HackGuard/UI.php CHANGED
@@ -8,17 +8,12 @@ use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class UI extends BaseShield\UI {
10
 
11
- public function buildInsightsVars() :array {
12
  /** @var ModCon $mod */
13
  $mod = $this->getMod();
14
  /** @var Options $opts */
15
  $opts = $this->getOptions();
16
 
17
- $uiTrack = $mod->getUiTrack();
18
- if ( empty( $uiTrack[ 'selected_scans' ] ) ) {
19
- $uiTrack[ 'selected_scans' ] = $opts->getScanSlugs();
20
- }
21
-
22
  foreach ( $opts->getScanSlugs() as $scan ) {
23
  $mod->getScanCon( $scan )->cleanStalesResults();
24
  }
@@ -31,28 +26,17 @@ class UI extends BaseShield\UI {
31
 
32
  // Can Scan Checks:
33
  $reasonsCantScan = $mod->getScansCon()->getReasonsScansCantExecute();
34
-
35
- /** @var \FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\Select $selector */
36
- $selector = $mod->getDbHandler_ScanResults()->getQuerySelector();
37
- $data = [
38
- 'ajax' => [
39
- 'scans_start' => $mod->getAjaxActionData( 'scans_start', true ),
40
- 'scans_check' => $mod->getAjaxActionData( 'scans_check', true ),
41
- 'render_table_scan' => $mod->getAjaxActionData( 'render_table_scan', true ),
42
- 'bulk_action' => $mod->getAjaxActionData( 'bulk_action', true ),
43
- 'item_asset_deactivate' => $mod->getAjaxActionData( 'item_asset_deactivate', true ),
44
- 'item_asset_reinstall' => $mod->getAjaxActionData( 'item_asset_reinstall', true ),
45
- 'item_delete' => $mod->getAjaxActionData( 'item_delete', true ),
46
- 'item_ignore' => $mod->getAjaxActionData( 'item_ignore', true ),
47
- 'item_repair' => $mod->getAjaxActionData( 'item_repair', true ),
48
- 'item_action' => $mod->getAjaxActionData( 'item_action', true ),
49
  ],
50
- 'flags' => [
51
  'is_premium' => $this->getCon()->isPremiumActive(),
52
  'can_scan' => count( $reasonsCantScan ) === 0,
53
  'module_disabled' => !$mod->isModOptEnabled(),
54
  ],
55
- 'strings' => [
56
  'never' => __( 'Never', 'wp-simple-firewall' ),
57
  'not_available' => __( 'Sorry, this scan is not available.', 'wp-simple-firewall' ),
58
  'not_enabled' => __( 'This scan is not currently enabled.', 'wp-simple-firewall' ),
@@ -79,7 +63,7 @@ class UI extends BaseShield\UI {
79
  'module_disabled' => __( "Scans can't run because the module that controls them is currently disabled.", 'wp-simple-firewall' ),
80
  'review_scanner_config' => __( "Review Scanner Module configuration", 'wp-simple-firewall' ),
81
  ],
82
- 'vars' => [
83
  'initial_check' => $mod->getScanQueueController()->hasRunningScans(),
84
  'cannot_scan_reasons' => $reasonsCantScan,
85
  'sections' => [
@@ -97,13 +81,13 @@ class UI extends BaseShield\UI {
97
  ],
98
  ]
99
  ],
100
- 'hrefs' => [
101
  'scanner_mod_config' => $mod->getUrl_DirectLinkToSection( 'section_enable_plugin_feature_hack_protection_tools' ),
102
  'scans_results' => $this->getCon()
103
  ->getModule_Insights()
104
  ->getUrl_ScansResults(),
105
  ],
106
- 'content' => [
107
  'section' => [
108
  'plugins' => $sectionBuilderPlugins->render(),
109
  'themes' => $sectionBuilderThemes->render(),
@@ -112,121 +96,123 @@ class UI extends BaseShield\UI {
112
  'logs' => 'logs todo',
113
  ]
114
  ],
115
- 'scan_results' => [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  ],
117
- 'file_locker' => $this->getFileLockerVars(),
118
- 'scans' => [
119
- 'wcf' => [
120
- 'flags' => [
121
- 'has_items' => false,
122
- 'show_table' => false,
123
- ],
124
- 'hrefs' => [],
125
- 'vars' => [],
126
- 'strings' => [
127
- 'subtitle' => __( "Detect changes to core WordPress files when compared to the official distribution", 'wp-simple-firewall' ),
128
- 'explanation' => [
129
- __( 'The files listed below are WordPress Core files - official files that are installed with every WordPress website.', 'wp-simple-firewall' ),
130
- __( 'However, they have either been deleted, or their contents have changed in some way.', 'wp-simple-firewall' ),
131
- __( 'Under normal circumstances this should never happen.', 'wp-simple-firewall' ),
132
- __( 'You should review each file below and repair them. Repair means to replace file with the original.', 'wp-simple-firewall' ),
133
- __( "If you know why a file has been changed and you're happy to keep those changes, you can click to Ignore that file.", 'wp-simple-firewall' ),
134
- ],
135
- ],
136
- ],
137
- 'apc' => [
138
- 'flags' => [
139
- 'has_items' => true,
140
- 'show_table' => false,
141
- ],
142
- 'hrefs' => [],
143
- 'vars' => [],
144
- 'strings' => [
145
- 'subtitle' => __( "Discover plugins that may have been abandoned by their authors", 'wp-simple-firewall' ),
146
- ],
147
- ],
148
- 'ufc' => [
149
- 'flags' => [
150
- 'has_items' => true,
151
- 'show_table' => false,
152
- ],
153
- 'hrefs' => [],
154
- 'vars' => [],
155
- 'strings' => [
156
- 'subtitle' => __( "Detect files which aren't part of the official WordPress.org distribution", 'wp-simple-firewall' )
157
- ],
158
- ],
159
- 'mal' => [
160
- 'flags' => [
161
- 'has_items' => true,
162
- 'show_table' => false,
163
- ],
164
- 'hrefs' => [],
165
- 'vars' => [],
166
- 'strings' => [
167
- 'subtitle' => __( "Detect files that may be infected with malware", 'wp-simple-firewall' )
168
- ],
169
- ],
170
- 'ptg' => $this->getInsightVarsScan_Ptg(),
171
- 'wpv' => [
172
- 'flags' => [
173
- 'has_items' => true,
174
- 'show_table' => false,
175
- ],
176
- 'hrefs' => [],
177
- 'vars' => [],
178
- 'strings' => [
179
- 'subtitle' => __( "Be alerted to plugins and themes with known security vulnerabilities", 'wp-simple-firewall' )
180
- ],
181
- ],
182
  ],
 
 
 
 
 
183
  ];
 
 
 
 
 
184
 
185
  /** @var Strings $strings */
186
  $strings = $mod->getStrings();
187
- $name = $strings->getScanNames();
188
- foreach ( $data[ 'scans' ] as $slug => &$scData ) {
189
- try {
190
- $scon = $mod->getScanCon( $slug );
191
- }
192
- catch ( \Exception $e ) {
193
- continue;
194
- }
195
- $lastScanAt = $scon->getLastScanAt();
196
- $scData[ 'vars' ][ 'slug' ] = $slug;
197
- $scData[ 'count' ] = $selector->countForScan( $slug );
198
- $scData[ 'flags' ][ 'is_available' ] = $scon->isReady();
199
- // $scData[ 'flags' ][ 'show_table' ] = $scData[ 'count' ] > 0;
200
- $scData[ 'flags' ][ 'is_restricted' ] = $scon->isRestricted();
201
- $scData[ 'flags' ][ 'is_enabled' ] = $scon->isEnabled();
202
- $scData[ 'flags' ][ 'is_selected' ] = $scon->isReady() && in_array( $slug, $uiTrack[ 'selected_scans' ] );
203
- $scData[ 'vars' ][ 'last_scan_at_ts' ] = $lastScanAt;
204
- $scData[ 'flags' ][ 'has_last_scan' ] = $lastScanAt > 0;
205
- $scData[ 'vars' ][ 'last_scan_at' ] = sprintf(
206
- __( 'Last Scan: %s', 'wp-simple-firewall' ),
207
- ( $lastScanAt > 0 ) ?
208
- Services::Request()->carbon()->setTimestamp( $lastScanAt )->diffForHumans()
209
- : __( 'Never', 'wp-simple-firewall' )
210
- );
211
- $scData[ 'strings' ][ 'title' ] = $name[ $slug ];
212
- $scData[ 'hrefs' ][ 'options' ] = $mod->getUrl_DirectLinkToSection( 'section_scan_'.$slug );
213
- $scData[ 'hrefs' ][ 'please_enable' ] = $mod->getUrl_DirectLinkToSection( 'section_scan_'.$slug );
214
- $scData[ 'count' ] = $selector->countForScan( $slug );
215
  }
216
 
217
- return $data;
218
  }
219
 
220
  /**
221
- * @param array $aOptParams
222
  * @return array
223
  */
224
- protected function buildOptionForUi( $aOptParams ) {
225
- $aOptParams = parent::buildOptionForUi( $aOptParams );
226
- if ( $aOptParams[ 'key' ] === 'file_locker' && !Services::Data()->isWindows() ) {
227
- $aOptParams[ 'value_options' ][ 'root_webconfig' ] .= sprintf( ' (%s)', __( 'unavailable', 'wp-simple-firewall' ) );
228
  }
229
- return $aOptParams;
230
  }
231
 
232
  protected function getFileLockerVars() :array {
@@ -267,40 +253,6 @@ class UI extends BaseShield\UI {
267
  ];
268
  }
269
 
270
- private function getInsightVarsScan_Ptg() :array {
271
- /** @var ModCon $mod */
272
- $mod = $this->getMod();
273
-
274
- /** @var \FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\Select $oSelector */
275
- $oSelector = $mod->getDbHandler_ScanResults()->getQuerySelector();
276
-
277
- /** @var \FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\EntryVO[] $aPtgResults */
278
- $aPtgResults = $oSelector->filterByNotIgnored()
279
- ->filterByScan( 'ptg' )
280
- ->query();
281
-
282
- return [
283
- 'flags' => [
284
- 'has_items' => $mod->isPtgEnabled() && !empty( $aPtgResults ),
285
- 'has_plugins' => !empty( $aPlugins ),
286
- 'has_themes' => !empty( $aThemes ),
287
- 'show_table' => false,
288
- ],
289
- 'hrefs' => [],
290
- 'vars' => [],
291
- 'strings' => [
292
- 'subtitle' => __( "Detects unauthorized changes to plugins/themes", 'wp-simple-firewall' ),
293
- 'files_with_problems' => __( 'Files with problems', 'wp-simple-firewall' ),
294
- 'root_dir' => __( 'Root directory', 'wp-simple-firewall' ),
295
- 'date_snapshot' => __( 'Snapshot taken', 'wp-simple-firewall' ),
296
- 'reinstall' => __( 'Re-Install', 'wp-simple-firewall' ),
297
- 'deactivate' => __( 'Deactivate and Ignore', 'wp-simple-firewall' ),
298
- 'accept' => __( 'Accept', 'wp-simple-firewall' ),
299
- 'update' => __( 'Upgrade', 'wp-simple-firewall' ),
300
- ]
301
- ];
302
- }
303
-
304
  protected function getSectionWarnings( string $section ) :array {
305
  $warnings = [];
306
 
8
 
9
  class UI extends BaseShield\UI {
10
 
11
+ public function buildInsightsVars_Results() :array {
12
  /** @var ModCon $mod */
13
  $mod = $this->getMod();
14
  /** @var Options $opts */
15
  $opts = $this->getOptions();
16
 
 
 
 
 
 
17
  foreach ( $opts->getScanSlugs() as $scan ) {
18
  $mod->getScanCon( $scan )->cleanStalesResults();
19
  }
26
 
27
  // Can Scan Checks:
28
  $reasonsCantScan = $mod->getScansCon()->getReasonsScansCantExecute();
29
+ return [
30
+ 'ajax' => [
31
+ 'scans_start' => $mod->getAjaxActionData( 'scans_start', true ),
32
+ 'scans_check' => $mod->getAjaxActionData( 'scans_check', true ),
 
 
 
 
 
 
 
 
 
 
 
33
  ],
34
+ 'flags' => [
35
  'is_premium' => $this->getCon()->isPremiumActive(),
36
  'can_scan' => count( $reasonsCantScan ) === 0,
37
  'module_disabled' => !$mod->isModOptEnabled(),
38
  ],
39
+ 'strings' => [
40
  'never' => __( 'Never', 'wp-simple-firewall' ),
41
  'not_available' => __( 'Sorry, this scan is not available.', 'wp-simple-firewall' ),
42
  'not_enabled' => __( 'This scan is not currently enabled.', 'wp-simple-firewall' ),
63
  'module_disabled' => __( "Scans can't run because the module that controls them is currently disabled.", 'wp-simple-firewall' ),
64
  'review_scanner_config' => __( "Review Scanner Module configuration", 'wp-simple-firewall' ),
65
  ],
66
+ 'vars' => [
67
  'initial_check' => $mod->getScanQueueController()->hasRunningScans(),
68
  'cannot_scan_reasons' => $reasonsCantScan,
69
  'sections' => [
81
  ],
82
  ]
83
  ],
84
+ 'hrefs' => [
85
  'scanner_mod_config' => $mod->getUrl_DirectLinkToSection( 'section_enable_plugin_feature_hack_protection_tools' ),
86
  'scans_results' => $this->getCon()
87
  ->getModule_Insights()
88
  ->getUrl_ScansResults(),
89
  ],
90
+ 'content' => [
91
  'section' => [
92
  'plugins' => $sectionBuilderPlugins->render(),
93
  'themes' => $sectionBuilderThemes->render(),
96
  'logs' => 'logs todo',
97
  ]
98
  ],
99
+ 'file_locker' => $this->getFileLockerVars(),
100
+ ];
101
+ }
102
+
103
+ public function buildInsightsVars_Run() :array {
104
+ /** @var ModCon $mod */
105
+ $mod = $this->getMod();
106
+ /** @var Options $opts */
107
+ $opts = $this->getOptions();
108
+
109
+ foreach ( $opts->getScanSlugs() as $scan ) {
110
+ $mod->getScanCon( $scan )->cleanStalesResults();
111
+ }
112
+
113
+ // Can Scan Checks:
114
+ $reasonsCantScan = $mod->getScansCon()->getReasonsScansCantExecute();
115
+ return [
116
+ 'ajax' => [
117
+ 'scans_start' => $mod->getAjaxActionData( 'scans_start', true ),
118
+ 'scans_check' => $mod->getAjaxActionData( 'scans_check', true ),
119
  ],
120
+ 'flags' => [
121
+ 'is_premium' => $this->getCon()->isPremiumActive(),
122
+ 'can_scan' => count( $reasonsCantScan ) === 0,
123
+ 'module_disabled' => !$mod->isModOptEnabled(),
124
+ ],
125
+ 'strings' => [
126
+ 'never' => __( 'Never', 'wp-simple-firewall' ),
127
+ 'not_available' => __( 'Sorry, this scan is not available.', 'wp-simple-firewall' ),
128
+ 'not_enabled' => __( 'This scan is not currently enabled.', 'wp-simple-firewall' ),
129
+ 'please_enable' => __( 'Please turn on this scan in the options.', 'wp-simple-firewall' ),
130
+ 'click_see_results' => __( 'Click a scan to see its results', 'wp-simple-firewall' ),
131
+ 'title_scan_site_now' => __( 'Scan Your Site Now', 'wp-simple-firewall' ),
132
+ 'title_scan_now' => __( 'Scan Your Site Now', 'wp-simple-firewall' ),
133
+ 'subtitle_scan_now' => __( 'Run the selected scans on your site now to get the latest results', 'wp-simple-firewall' ),
134
+ 'more_items_longer' => __( 'The more scans that are selected, the longer the scan may take.', 'wp-simple-firewall' ),
135
+ 'scan_options' => __( 'Scan Options', 'wp-simple-firewall' ),
136
+ 'scanselect' => __( 'Select Scans To Run', 'wp-simple-firewall' ),
137
+ 'scanselect_file_areas' => __( 'Select File Scans To Run', 'wp-simple-firewall' ),
138
+ 'scanselect_assets' => __( 'Select Scans For Plugins and Themes', 'wp-simple-firewall' ),
139
+ 'select_view_results' => __( 'View Scan Results', 'wp-simple-firewall' ),
140
+ 'select_what_to_scan' => __( 'Select Scans To Run', 'wp-simple-firewall' ),
141
+ 'clear_ignore' => __( 'Clear Ignore Flags', 'wp-simple-firewall' ),
142
+ 'clear_ignore_sub' => __( 'Previously ignored results will be revealed (for the selected scans only)', 'wp-simple-firewall' ),
143
+ 'clear_suppression' => __( 'Remove Notification Suppression', 'wp-simple-firewall' ),
144
+ 'clear_suppression_sub' => __( 'Allow notification emails to be resent (for the selected scans only)', 'wp-simple-firewall' ),
145
+ 'run_scans_now' => __( 'Run Scans Now', 'wp-simple-firewall' ),
146
+ 'no_entries_to_display' => __( "The previous scan either didn't detect any items that require your attention or they've already been repaired.", 'wp-simple-firewall' ),
147
+ 'scan_progress' => __( 'Scan Progress', 'wp-simple-firewall' ),
148
+ 'reason_not_call_self' => __( "This site currently can't make HTTP requests to itself.", 'wp-simple-firewall' ),
149
+ 'module_disabled' => __( "Scans can't run because the module that controls them is currently disabled.", 'wp-simple-firewall' ),
150
+ 'review_scanner_config' => __( "Review Scanner Module configuration", 'wp-simple-firewall' ),
151
+ ],
152
+ 'scans' => $this->buildScansVars(),
153
+ 'vars' => [
154
+ 'initial_check' => $mod->getScanQueueController()->hasRunningScans(),
155
+ 'cannot_scan_reasons' => $reasonsCantScan,
156
+ ],
157
+ 'hrefs' => [
158
+ 'scanner_mod_config' => $mod->getUrl_DirectLinkToSection( 'section_enable_plugin_feature_hack_protection_tools' ),
159
+ 'scans_results' => $this->getCon()
160
+ ->getModule_Insights()
161
+ ->getUrl_ScansResults(),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  ],
163
+ 'content' => [
164
+ 'section' => [
165
+ ]
166
+ ],
167
+ 'file_locker' => $this->getFileLockerVars(),
168
  ];
169
+ }
170
+
171
+ private function buildScansVars() :array {
172
+ /** @var ModCon $mod */
173
+ $mod = $this->getMod();
174
 
175
  /** @var Strings $strings */
176
  $strings = $mod->getStrings();
177
+ $scanStrings = $strings->getScanStrings();
178
+ $scans = [];
179
+ foreach ( $mod->getScansCon()->getAllScanCons() as $scanCon ) {
180
+ $slug = $scanCon->getSlug();
181
+ $data = [
182
+ 'flags' => [
183
+ 'is_available' => $scanCon->isReady(),
184
+ 'is_restricted' => $scanCon->isRestricted(),
185
+ 'is_enabled' => $scanCon->isEnabled(),
186
+ 'is_selected' => $scanCon->isReady()
187
+ && in_array( $slug, $mod->getUiTrack()->selected_scans ),
188
+ ],
189
+ 'hrefs' => [
190
+ 'options' => $mod->getUrl_DirectLinkToSection( 'section_scan_'.$slug ),
191
+ ],
192
+ 'strings' => [
193
+ 'title' => $scanStrings[ $slug ][ 'name' ],
194
+ 'subtitle' => $scanStrings[ $slug ][ 'subtitle' ],
195
+ ],
196
+ 'vars' => [
197
+ 'slug' => $scanCon->getSlug(),
198
+ ],
199
+ ];
200
+ $scans[ $slug ] = $data;
 
 
 
 
201
  }
202
 
203
+ return $scans;
204
  }
205
 
206
  /**
207
+ * @param array $option
208
  * @return array
209
  */
210
+ protected function buildOptionForUi( $option ) {
211
+ $option = parent::buildOptionForUi( $option );
212
+ if ( $option[ 'key' ] === 'file_locker' && !Services::Data()->isWindows() ) {
213
+ $option[ 'value_options' ][ 'root_webconfig' ] .= sprintf( ' (%s)', __( 'unavailable', 'wp-simple-firewall' ) );
214
  }
215
+ return $option;
216
  }
217
 
218
  protected function getFileLockerVars() :array {
253
  ];
254
  }
255
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  protected function getSectionWarnings( string $section ) :array {
257
  $warnings = [];
258
 
src/lib/src/Modules/IPs/AjaxHandler.php CHANGED
@@ -81,8 +81,8 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
81
  $list = $formParams[ 'list' ] ?? '';
82
 
83
  $acceptableIP = $srvIP->isValidIp( $ip )
84
- || $srvIP->isValidIp4Range( $ip )
85
- || $srvIP->isValidIp6Range( $ip );
86
 
87
  $isBlackList = $list != $mod::LIST_MANUAL_WHITE;
88
 
@@ -226,11 +226,12 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
226
  ->setMod( $this->getMod() )
227
  ->setIP( $ip )
228
  ->toManualBlacklist() instanceof Shield\Databases\IPs\EntryVO;
 
 
229
  }
230
  catch ( \Exception $e ) {
 
231
  }
232
- $msg = $success ? __( 'IP address blocked.', 'wp-simple-firewall' )
233
- : __( "IP address couldn't be blocked at this time.", 'wp-simple-firewall' );
234
  break;
235
 
236
  case 'unblock':
81
  $list = $formParams[ 'list' ] ?? '';
82
 
83
  $acceptableIP = $srvIP->isValidIp( $ip )
84
+ || $srvIP->isValidIp4Range( $ip )
85
+ || $srvIP->isValidIp6Range( $ip );
86
 
87
  $isBlackList = $list != $mod::LIST_MANUAL_WHITE;
88
 
226
  ->setMod( $this->getMod() )
227
  ->setIP( $ip )
228
  ->toManualBlacklist() instanceof Shield\Databases\IPs\EntryVO;
229
+ $msg = $success ? __( 'IP address blocked.', 'wp-simple-firewall' )
230
+ : __( "IP address couldn't be blocked at this time.", 'wp-simple-firewall' );
231
  }
232
  catch ( \Exception $e ) {
233
+ $msg = $e->getMessage();
234
  }
 
 
235
  break;
236
 
237
  case 'unblock':
src/lib/src/Modules/IPs/BotTrack/Base.php CHANGED
@@ -39,7 +39,7 @@ abstract class Base extends Shield\Modules\Base\Common\ExecOnceModConsumer {
39
  ->fireEvent(
40
  'bot'.static::OPT_KEY,
41
  [
42
- 'audit' => $this->getAuditData(),
43
  'offense_count' => $offenseCount,
44
  'block' => $isBlock,
45
  ]
39
  ->fireEvent(
40
  'bot'.static::OPT_KEY,
41
  [
42
+ 'audit_params' => $this->getAuditData(),
43
  'offense_count' => $offenseCount,
44
  'block' => $isBlock,
45
  ]
src/lib/src/Modules/IPs/BotTrack/TrackFakeWebCrawler.php CHANGED
@@ -5,15 +5,11 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\BotTrack;
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
6
  use FernleafSystems\Wordpress\Services\Services;
7
 
8
- /**
9
- * Class TrackFakeWebCrawler
10
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\BotTrack
11
- */
12
  class TrackFakeWebCrawler extends Base {
13
 
14
  const OPT_KEY = 'track_fakewebcrawler';
15
 
16
- private $agentUsed = '';
17
 
18
  protected function process() {
19
  /** @var ModCon $mod */
@@ -31,7 +27,7 @@ class TrackFakeWebCrawler extends Base {
31
  foreach ( Services::ServiceProviders()->getAllCrawlerUseragents() as $possibleAgent ) {
32
  if ( stripos( $userAgent, $possibleAgent ) !== false ) {
33
  $identifiesAsCrawler = true;
34
- $this->agentUsed = $possibleAgent;
35
  break;
36
  }
37
  }
@@ -42,7 +38,7 @@ class TrackFakeWebCrawler extends Base {
42
 
43
  protected function getAuditData() :array {
44
  return array_merge( parent::getAuditData(), [
45
- 'script' => $this->agentUsed
46
  ] );
47
  }
48
  }
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
6
  use FernleafSystems\Wordpress\Services\Services;
7
 
 
 
 
 
8
  class TrackFakeWebCrawler extends Base {
9
 
10
  const OPT_KEY = 'track_fakewebcrawler';
11
 
12
+ private $crawlerUsed = '';
13
 
14
  protected function process() {
15
  /** @var ModCon $mod */
27
  foreach ( Services::ServiceProviders()->getAllCrawlerUseragents() as $possibleAgent ) {
28
  if ( stripos( $userAgent, $possibleAgent ) !== false ) {
29
  $identifiesAsCrawler = true;
30
+ $this->crawlerUsed = $possibleAgent;
31
  break;
32
  }
33
  }
38
 
39
  protected function getAuditData() :array {
40
  return array_merge( parent::getAuditData(), [
41
+ 'crawler' => $this->crawlerUsed
42
  ] );
43
  }
44
  }
src/lib/src/Modules/IPs/BotTrack/TrackLoginFailed.php CHANGED
@@ -40,7 +40,7 @@ class TrackLoginFailed extends Base {
40
 
41
  protected function getAuditData() :array {
42
  return [
43
- 'login' => $this->user_login
44
  ];
45
  }
46
  }
40
 
41
  protected function getAuditData() :array {
42
  return [
43
+ 'user_login' => $this->user_login
44
  ];
45
  }
46
  }
src/lib/src/Modules/IPs/BotTrack/TrackLoginInvalid.php CHANGED
@@ -40,7 +40,7 @@ class TrackLoginInvalid extends Base {
40
 
41
  protected function getAuditData() :array {
42
  return [
43
- 'login' => $this->user_login
44
  ];
45
  }
46
  }
40
 
41
  protected function getAuditData() :array {
42
  return [
43
+ 'user_login' => $this->user_login
44
  ];
45
  }
46
  }
src/lib/src/Modules/IPs/BotTrack/TrackUserAgent.php DELETED
@@ -1,17 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\BotTrack;
4
-
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- class TrackUserAgent extends Base {
8
-
9
- const OPT_KEY = 'track_useragent';
10
-
11
- protected function process() {
12
- $sAgent = trim( Services::Request()->getUserAgent() );
13
- if ( empty( $sAgent ) || strlen( $sAgent ) < 2 ) {
14
- $this->doTransgression();
15
- }
16
- }
17
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/IPs/Components/ProcessOffense.php CHANGED
@@ -24,32 +24,32 @@ class ProcessOffense {
24
  $opts = $this->getOptions();
25
 
26
  try {
27
- $oIP = ( new IPs\Lib\Ops\AddIp() )
28
  ->setMod( $mod )
29
  ->setIP( $this->getIP() )
30
  ->toAutoBlacklist();
31
  }
32
  catch ( \Exception $e ) {
33
- $oIP = null;
34
  }
35
 
36
- if ( $oIP instanceof Databases\IPs\EntryVO ) {
37
- $nCurrent = $oIP->transgressions;
38
 
39
- $oTracker = $mod->loadOffenseTracker();
40
- $nNewTotal = $oIP->transgressions + $oTracker->getOffenseCount();
41
- $bToBlock = $oTracker->isBlocked() ||
42
- ( $oIP->blocked_at == 0 && ( $nNewTotal >= $opts->getOffenseLimit() ) );
43
 
44
- /** @var Databases\IPs\Update $oUp */
45
- $oUp = $mod->getDbHandler_IPs()->getQueryUpdater();
46
- $oUp->updateTransgressions( $oIP, $nNewTotal );
47
 
48
- $con->fireEvent( $bToBlock ? 'ip_blocked' : 'ip_offense',
49
  [
50
- 'audit' => [
51
- 'from' => $nCurrent,
52
- 'to' => $nNewTotal,
53
  ]
54
  ]
55
  );
@@ -59,9 +59,9 @@ class ProcessOffense {
59
  * want to also audit the offense (only audit the block),
60
  * so we fire ip_offense but suppress the audit
61
  */
62
- if ( $bToBlock ) {
63
- $oUp = $mod->getDbHandler_IPs()->getQueryUpdater();
64
- $oUp->setBlocked( $oIP );
65
  $con->fireEvent( 'ip_offense', [ 'suppress_audit' => true ] );
66
  }
67
  }
24
  $opts = $this->getOptions();
25
 
26
  try {
27
+ $IP = ( new IPs\Lib\Ops\AddIp() )
28
  ->setMod( $mod )
29
  ->setIP( $this->getIP() )
30
  ->toAutoBlacklist();
31
  }
32
  catch ( \Exception $e ) {
33
+ $IP = null;
34
  }
35
 
36
+ if ( !empty( $IP ) ) {
37
+ $currentCount = $IP->transgressions;
38
 
39
+ $offenseTracker = $mod->loadOffenseTracker();
40
+ $newCount = $IP->transgressions + $offenseTracker->getOffenseCount();
41
+ $toBlock = $offenseTracker->isBlocked() ||
42
+ ( $IP->blocked_at == 0 && ( $newCount >= $opts->getOffenseLimit() ) );
43
 
44
+ /** @var Databases\IPs\Update $updater */
45
+ $updater = $mod->getDbHandler_IPs()->getQueryUpdater();
46
+ $updater->updateTransgressions( $IP, $newCount );
47
 
48
+ $con->fireEvent( $toBlock ? 'ip_blocked' : 'ip_offense',
49
  [
50
+ 'audit_params' => [
51
+ 'from' => $currentCount,
52
+ 'to' => $newCount,
53
  ]
54
  ]
55
  );
59
  * want to also audit the offense (only audit the block),
60
  * so we fire ip_offense but suppress the audit
61
  */
62
+ if ( $toBlock ) {
63
+ $updater = $mod->getDbHandler_IPs()->getQueryUpdater();
64
+ $updater->setBlocked( $IP );
65
  $con->fireEvent( 'ip_offense', [ 'suppress_audit' => true ] );
66
  }
67
  }
src/lib/src/Modules/IPs/Components/UnblockIpByFlag.php CHANGED
@@ -26,7 +26,7 @@ class UnblockIpByFlag {
26
  ->setIP( $ip )
27
  ->fromBlacklist();
28
  if ( $removed ) {
29
- $this->getCon()->fireEvent( 'ip_unblock_flag', [ 'audit' => [ 'ip' => $ip ] ] );
30
  }
31
  }
32
  }
26
  ->setIP( $ip )
27
  ->fromBlacklist();
28
  if ( $removed ) {
29
+ $this->getCon()->fireEvent( 'ip_unblock_flag', [ 'audit_params' => [ 'ip' => $ip ] ] );
30
  }
31
  }
32
  }
src/lib/src/Modules/IPs/Lib/BlockRequest.php CHANGED
@@ -10,14 +10,15 @@ use FernleafSystems\Wordpress\Services\Utilities\Obfuscate;
10
  class BlockRequest extends ExecOnceModConsumer {
11
 
12
  protected function run() {
13
- if ( $this->isBlocked() && !$this->isHighReputationIP() ) {
14
 
15
  if ( $this->isAutoUnBlocked() ) {
16
  Services::Response()->redirectToHome();
17
  }
 
 
 
18
  else {
19
- add_filter( 'shield/is_log_traffic', '__return_false' ); // don't log killed requests
20
- $this->getCon()->fireEvent( 'conn_kill' );
21
  $this->renderKillPage();
22
  }
23
  }
10
  class BlockRequest extends ExecOnceModConsumer {
11
 
12
  protected function run() {
13
+ if ( $this->isBlocked() ) {
14
 
15
  if ( $this->isAutoUnBlocked() ) {
16
  Services::Response()->redirectToHome();
17
  }
18
+ elseif ( $this->isHighReputationIP() ) {
19
+ $this->getCon()->fireEvent( 'not_conn_kill_high_rep' );
20
+ }
21
  else {
 
 
22
  $this->renderKillPage();
23
  }
24
  }
src/lib/src/Modules/IPs/Lib/Bots/BotEventListener.php CHANGED
@@ -11,7 +11,7 @@ class BotEventListener extends ExecOnceModConsumer {
11
  $events = $this->getEventsToColumn();
12
 
13
  foreach ( $events as $eventTrigger => $column ) {
14
- if ( $eventTrigger === $event || preg_match( sprintf( '#^%s$#', $eventTrigger ), $event ) ) {
15
  try {
16
  ( new BotSignalsRecord() )
17
  ->setMod( $this->getMod() )
@@ -62,7 +62,7 @@ class BotEventListener extends ExecOnceModConsumer {
62
  'spam_block_human' => 'humanspam',
63
  'comment_markspam' => 'markspam',
64
  'comment_unmarkspam' => 'unmarkspam',
65
- 'blockparam_.*' => 'firewall',
66
  'ip_offense' => 'offense',
67
  'ip_blocked' => 'blocked',
68
  'ip_unblock' => 'unblocked',
11
  $events = $this->getEventsToColumn();
12
 
13
  foreach ( $events as $eventTrigger => $column ) {
14
+ if ( $eventTrigger === $event ) {
15
  try {
16
  ( new BotSignalsRecord() )
17
  ->setMod( $this->getMod() )
62
  'spam_block_human' => 'humanspam',
63
  'comment_markspam' => 'markspam',
64
  'comment_unmarkspam' => 'unmarkspam',
65
+ 'firewall_block' => 'firewall',
66
  'ip_offense' => 'offense',
67
  'ip_blocked' => 'blocked',
68
  'ip_unblock' => 'unblocked',
src/lib/src/Modules/IPs/Lib/Bots/BotSignalsController.php CHANGED
@@ -42,7 +42,7 @@ class BotSignalsController extends ExecOnceModConsumer {
42
  $this->getCon()->fireEvent(
43
  'antibot_'.( $isBot ? 'fail' : 'pass' ),
44
  [
45
- 'audit' => [
46
  'score' => $score,
47
  'minimum' => $botScoreMinimum,
48
  ]
42
  $this->getCon()->fireEvent(
43
  'antibot_'.( $isBot ? 'fail' : 'pass' ),
44
  [
45
+ 'audit_params' => [
46
  'score' => $score,
47
  'minimum' => $botScoreMinimum,
48
  ]
src/lib/src/Modules/IPs/Lib/IpAnalyse/BuildDisplay.php CHANGED
@@ -3,6 +3,8 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\IpAnalyse;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
 
 
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\AuditMessageBuilder;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\GeoIp\Lookup;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
@@ -13,6 +15,8 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\LookupIpOnList;
13
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
14
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Strings;
15
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
 
 
16
  use FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\Reputation\GetIPReputation;
17
  use FernleafSystems\Wordpress\Services\Services;
18
  use FernleafSystems\Wordpress\Services\Utilities\Net\IpID;
@@ -80,10 +84,9 @@ class BuildDisplay {
80
  ->lookup( true );
81
 
82
  $geo = ( new Lookup() )
83
- ->setDbHandler( $con->getModule_Plugin()->getDbHandler_GeoIp() )
84
  ->setIP( $ip )
85
  ->lookupIp();
86
- $validGeo = $geo instanceof Databases\GeoIp\EntryVO;
87
 
88
  $sRDNS = gethostbyaddr( $ip );
89
 
@@ -177,9 +180,9 @@ class BuildDisplay {
177
  'ip' => $ip,
178
  'status' => [
179
  'is_you' => Services::IP()->checkIp( $ip, Services::IP()->getRequestIp() ),
180
- 'offenses' => $blockIP instanceof Databases\IPs\EntryVO ? $blockIP->transgressions : 0,
181
- 'is_blocked' => $blockIP instanceof Databases\IPs\EntryVO ? $blockIP->blocked_at > 0 : false,
182
- 'is_bypass' => $bypassIP instanceof Databases\IPs\EntryVO,
183
  'ip_reputation_score' => $botScore,
184
  'snapi_reputation_score' => is_numeric( $shieldNetScore ) ? $shieldNetScore : 'Unavailable',
185
  'is_bot' => $isBot,
@@ -187,19 +190,19 @@ class BuildDisplay {
187
  'identity' => [
188
  'who_is_it' => $ipName,
189
  'rdns' => $sRDNS === $ip ? __( 'Unavailable', 'wp-simple-firewall' ) : $sRDNS,
190
- 'country_name' => $validGeo ? $geo->getCountryName() : __( 'Unknown', 'wp-simple-firewall' ),
191
- 'timezone' => $validGeo ? $geo->getTimezone() : __( 'Unknown', 'wp-simple-firewall' ),
192
- 'coordinates' => $validGeo ? sprintf( '%s: %s; %s: %s;',
193
- __( 'Latitude', 'wp-simple-firewall' ), $geo->getLatitude(),
194
- __( 'Longitude', 'wp-simple-firewall' ), $geo->getLongitude() )
195
- : 'Unknown'
196
  ],
197
  'extras' => [
198
  'ip_whois' => sprintf( 'https://whois.domaintools.com/%s', $ip ),
199
  ],
200
  ],
201
  'flags' => [
202
- 'has_geo' => $validGeo,
203
  ],
204
  ],
205
  true
@@ -245,23 +248,43 @@ class BuildDisplay {
245
  }
246
 
247
  private function renderForTraffic() :string {
248
- /** @var Databases\Traffic\Select $sel */
249
- $sel = $this->getCon()
250
- ->getModule_Traffic()
251
- ->getDbHandler_Traffic()
252
- ->getQuerySelector();
253
- /** @var Databases\Traffic\EntryVO[] $requests */
254
- $requests = $sel->filterByIp( inet_pton( $this->getIP() ) )
255
- ->query();
 
 
 
 
 
 
 
256
 
257
- foreach ( $requests as $key => $request ) {
258
- $asArray = $request->getRawData();
259
- $asArray[ 'created_at' ] = $this->formatTimestampField( (int)$request->created_at );
260
- if ( strpos( $request->path, '?' ) === false ) {
261
- $request->path .= '?';
 
 
 
 
 
 
 
 
 
 
 
 
262
  }
263
- list( $asArray[ 'path' ], $asArray[ 'query' ] ) = array_map( 'esc_js', explode( '?', $request->path, 2 ) );
264
- $asArray[ 'trans' ] = (bool)$asArray[ 'trans' ];
 
265
  $requests[ $key ] = $asArray;
266
  }
267
 
@@ -362,28 +385,26 @@ class BuildDisplay {
362
  }
363
 
364
  private function renderForAuditTrail() :string {
365
- $con = $this->getCon();
366
- /** @var Databases\AuditTrail\Select $sel */
367
- $sel = $con->getModule_AuditTrail()
368
- ->getDbHandler_AuditTrail()
369
- ->getQuerySelector();
370
- /** @var Databases\AuditTrail\EntryVO[] $logs */
371
- $logs = $sel->filterByIp( $this->getIP() )
372
- ->query();
373
-
374
- foreach ( $logs as $key => $entry ) {
375
- $asArray = $entry->getRawData();
376
-
377
- $module = $con->getModule( $entry->context );
378
- if ( empty( $module ) ) {
379
- $module = $con->getModule_AuditTrail();
380
- }
381
- $strings = $module->getStrings();
382
 
383
- $asArray[ 'event' ] = AuditMessageBuilder::Build( $entry, $strings->getAuditMessage( $entry->event ) );
384
- $asArray[ 'created_at' ] = $this->formatTimestampField( (int)$entry->created_at );
385
 
386
- $logs[ $key ] = $asArray;
 
 
 
 
387
  }
388
 
389
  return $this->getMod()->renderTemplate(
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\IpAnalyse;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\LoadLogs;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\DB\Logs;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\AuditMessageBuilder;
9
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\GeoIp\Lookup;
10
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
15
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
16
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Strings;
17
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
18
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\IPRecords;
19
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs;
20
  use FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\Reputation\GetIPReputation;
21
  use FernleafSystems\Wordpress\Services\Services;
22
  use FernleafSystems\Wordpress\Services\Utilities\Net\IpID;
84
  ->lookup( true );
85
 
86
  $geo = ( new Lookup() )
87
+ ->setCon( $con )
88
  ->setIP( $ip )
89
  ->lookupIp();
 
90
 
91
  $sRDNS = gethostbyaddr( $ip );
92
 
180
  'ip' => $ip,
181
  'status' => [
182
  'is_you' => Services::IP()->checkIp( $ip, Services::IP()->getRequestIp() ),
183
+ 'offenses' => !empty( $blockIP ) ? $blockIP->transgressions : 0,
184
+ 'is_blocked' => !empty( $blockIP ) ? $blockIP->blocked_at > 0 : false,
185
+ 'is_bypass' => !empty( $bypassIP ),
186
  'ip_reputation_score' => $botScore,
187
  'snapi_reputation_score' => is_numeric( $shieldNetScore ) ? $shieldNetScore : 'Unavailable',
188
  'is_bot' => $isBot,
190
  'identity' => [
191
  'who_is_it' => $ipName,
192
  'rdns' => $sRDNS === $ip ? __( 'Unavailable', 'wp-simple-firewall' ) : $sRDNS,
193
+ 'country_name' => $geo->countryName ?? __( 'Unknown', 'wp-simple-firewall' ),
194
+ 'timezone' => $geo->timeZone ?? __( 'Unknown', 'wp-simple-firewall' ),
195
+ 'coordinates' => $geo->latitude ? sprintf( '%s: %s; %s: %s;',
196
+ __( 'Latitude', 'wp-simple-firewall' ), $geo->latitude,
197
+ __( 'Longitude', 'wp-simple-firewall' ), $geo->longitude )
198
+ : __( 'Unknown', 'wp-simple-firewall' )
199
  ],
200
  'extras' => [
201
  'ip_whois' => sprintf( 'https://whois.domaintools.com/%s', $ip ),
202
  ],
203
  ],
204
  'flags' => [
205
+ 'has_geo' => !empty( $geo->getRawData() ),
206
  ],
207
  ],
208
  true
248
  }
249
 
250
  private function renderForTraffic() :string {
251
+ try {
252
+ $ip = ( new IPRecords() )
253
+ ->setMod( $this->getCon()->getModule_Data() )
254
+ ->loadIP( $this->getIP(), false );
255
+ /** @var ReqLogs\Ops\Select $selector */
256
+ $selector = $this->getCon()
257
+ ->getModule_Data()
258
+ ->getDbH_ReqLogs()
259
+ ->getQuerySelector();
260
+ /** @var ReqLogs\Ops\Record[] $requests */
261
+ $requests = $selector->filterByIP( $ip->id )->queryWithResult();
262
+ }
263
+ catch ( \Exception $e ) {
264
+ $requests = [];
265
+ }
266
 
267
+ foreach ( $requests as $key => $req ) {
268
+ $asArray = $req->getRawData();
269
+ $asArray[ 'created_at' ] = $this->formatTimestampField( (int)$req->created_at );
270
+
271
+ $asArray = array_merge(
272
+ [
273
+ 'path' => '',
274
+ 'code' => '-',
275
+ 'verb' => '-',
276
+ 'query' => '',
277
+ 'offense' => false,
278
+ ],
279
+ $asArray,
280
+ $req->meta
281
+ );
282
+ if ( strpos( $asArray[ 'path' ], '?' ) === false ) {
283
+ $asArray[ 'path' ] .= '?';
284
  }
285
+
286
+ list( $asArray[ 'path' ], $asArray[ 'query' ] ) = array_map( 'esc_js', explode( '?', $asArray[ 'path' ], 2 ) );
287
+ $asArray[ 'trans' ] = (bool)$asArray[ 'offense' ];
288
  $requests[ $key ] = $asArray;
289
  }
290
 
385
  }
386
 
387
  private function renderForAuditTrail() :string {
388
+ // TODO: IP Filtering at the SQL query level
389
+ $logRecords = ( new LoadLogs() )
390
+ ->setMod( $this->getCon()->getModule_AuditTrail() )
391
+ ->setIP( $this->getIP() )
392
+ ->run();
393
+
394
+ $logs = [];
395
+ $srvEvents = $this->getCon()->loadEventsService();
396
+ foreach ( $logRecords as $key => $record ) {
397
+ if ( $srvEvents->eventExists( $record->event_slug ) ) {
398
+ $asArray = $record->getRawData();
 
 
 
 
 
 
399
 
400
+ $asArray[ 'event' ] = implode( ' ', AuditMessageBuilder::BuildFromLogRecord( $record ) );
401
+ $asArray[ 'created_at' ] = $this->formatTimestampField( $record->created_at );
402
 
403
+ $user = empty( $record->meta_data[ 'uid' ] ) ? null
404
+ : Services::WpUsers()->getUserById( $record->meta_data[ 'uid' ] );
405
+ $asArray[ 'user' ] = empty( $user ) ? '-' : $user->user_login;
406
+ $logs[ $key ] = $asArray;
407
+ }
408
  }
409
 
410
  return $this->getMod()->renderTemplate(
src/lib/src/Modules/IPs/Lib/IpAnalyse/FindAllPluginIps.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\IpAnalyse;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
 
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Tool\IpListSort;
8
 
@@ -20,17 +21,9 @@ class FindAllPluginIps {
20
  ->getQuerySelector();
21
  $ips = $sel->getDistinctIps();
22
 
23
- // Traffic
24
- /** @var Databases\Traffic\Select $sel */
25
- $sel = $con->getModule_Traffic()
26
- ->getDbHandler_Traffic()
27
- ->getQuerySelector();
28
- $ips = array_merge( $ips, $sel->getDistinctIps() );
29
-
30
- // Audit Trail
31
- /** @var Databases\AuditTrail\Select $sel */
32
- $sel = $con->getModule_AuditTrail()
33
- ->getDbHandler_AuditTrail()
34
  ->getQuerySelector();
35
  $ips = array_merge( $ips, $sel->getDistinctIps() );
36
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\IpAnalyse;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\Ops\Select;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Tool\IpListSort;
9
 
21
  ->getQuerySelector();
22
  $ips = $sel->getDistinctIps();
23
 
24
+ /** @var Select $sel */
25
+ $sel = $con->getModule_Data()
26
+ ->getDbH_IPs()
 
 
 
 
 
 
 
 
27
  ->getQuerySelector();
28
  $ips = array_merge( $ips, $sel->getDistinctIps() );
29
 
src/lib/src/Modules/IPs/Lib/Ops/AddIp.php CHANGED
@@ -41,7 +41,7 @@ class AddIp {
41
  if ( !$IP instanceof Databases\IPs\EntryVO ) {
42
  $IP = $this->add( $mod::LIST_AUTO_BLACK, 'auto', $req->ts() );
43
  if ( !empty( $IP ) ) {
44
- $this->getCon()->fireEvent( 'ip_block_auto', [ 'audit' => [ 'ip' => $this->getIP() ] ] );
45
  }
46
  }
47
 
@@ -76,6 +76,10 @@ class AddIp {
76
  throw new \Exception( "IP address isn't valid." );
77
  }
78
 
 
 
 
 
79
  $IP = null;
80
  if ( !in_array( $ip, $srvIP->getServerPublicIPs() ) ) {
81
 
@@ -92,10 +96,10 @@ class AddIp {
92
  ->setIP( $ip )
93
  ->lookup( false );
94
 
95
- if ( !$IP instanceof Databases\IPs\EntryVO ) {
96
  $IP = $this->add( $mod::LIST_MANUAL_BLACK, $label );
97
  if ( !empty( $IP ) ) {
98
- $this->getCon()->fireEvent( 'ip_block_manual', [ 'audit' => [ 'ip' => $this->getIP() ] ] );
99
  }
100
  }
101
 
@@ -150,7 +154,7 @@ class AddIp {
150
  if ( !$IP instanceof Databases\IPs\EntryVO ) {
151
  $IP = $this->add( $mod::LIST_MANUAL_WHITE, $label );
152
  if ( !empty( $IP ) ) {
153
- $this->getCon()->fireEvent( 'ip_bypass_add', [ 'audit' => [ 'ip' => $this->getIP() ] ] );
154
  }
155
  }
156
 
41
  if ( !$IP instanceof Databases\IPs\EntryVO ) {
42
  $IP = $this->add( $mod::LIST_AUTO_BLACK, 'auto', $req->ts() );
43
  if ( !empty( $IP ) ) {
44
+ $this->getCon()->fireEvent( 'ip_block_auto', [ 'audit_params' => [ 'ip' => $this->getIP() ] ] );
45
  }
46
  }
47
 
76
  throw new \Exception( "IP address isn't valid." );
77
  }
78
 
79
+ if ( !$this->getCon()->isPremiumActive() ) {
80
+ throw new \Exception( __( 'Sorry, this is a PRO-only feature.', 'wp-simple-firewall' ) );
81
+ }
82
+
83
  $IP = null;
84
  if ( !in_array( $ip, $srvIP->getServerPublicIPs() ) ) {
85
 
96
  ->setIP( $ip )
97
  ->lookup( false );
98
 
99
+ if ( empty( $IP ) ) {
100
  $IP = $this->add( $mod::LIST_MANUAL_BLACK, $label );
101
  if ( !empty( $IP ) ) {
102
+ $this->getCon()->fireEvent( 'ip_block_manual', [ 'audit_params' => [ 'ip' => $this->getIP() ] ] );
103
  }
104
  }
105
 
154
  if ( !$IP instanceof Databases\IPs\EntryVO ) {
155
  $IP = $this->add( $mod::LIST_MANUAL_WHITE, $label );
156
  if ( !empty( $IP ) ) {
157
+ $this->getCon()->fireEvent( 'ip_bypass_add', [ 'audit_params' => [ 'ip' => $this->getIP() ] ] );
158
  }
159
  }
160
 
src/lib/src/Modules/IPs/Lib/Ops/DeleteIp.php CHANGED
@@ -19,7 +19,7 @@ class DeleteIp {
19
  }
20
 
21
  public function fromWhiteList() :bool {
22
- $this->getCon()->fireEvent( 'ip_bypass_remove', [ 'audit' => [ 'ip' => $this->getIP() ] ] );
23
  return (bool)$this->getDeleter()
24
  ->filterByWhitelist()
25
  ->query();
19
  }
20
 
21
  public function fromWhiteList() :bool {
22
+ $this->getCon()->fireEvent( 'ip_bypass_remove', [ 'audit_params' => [ 'ip' => $this->getIP() ] ] );
23
  return (bool)$this->getDeleter()
24
  ->filterByWhitelist()
25
  ->query();
src/lib/src/Modules/IPs/Lib/ProcessOffenses.php CHANGED
@@ -19,7 +19,7 @@ class ProcessOffenses extends ExecOnceModConsumer {
19
  $mod->loadOffenseTracker()->setIfCommit( true );
20
 
21
  $con = $this->getCon();
22
- add_filter( $con->prefix( 'firewall_die_message' ), [ $this, 'augmentFirewallDieMessage' ] );
23
  add_action( $con->prefix( 'pre_plugin_shutdown' ), function () {
24
  $this->processOffense();
25
  } );
@@ -41,15 +41,15 @@ class ProcessOffenses extends ExecOnceModConsumer {
41
  }
42
 
43
  /**
44
- * @param array $aMessages
45
  * @return array
46
  */
47
- public function augmentFirewallDieMessage( $aMessages ) {
48
- if ( !is_array( $aMessages ) ) {
49
- $aMessages = [];
50
  }
51
 
52
- $aMessages[] = sprintf( '<p>%s</p>', sprintf(
53
  $this->getMod()->getTextOpt( 'text_remainingtrans' ),
54
  max( 0, ( new IPs\Components\QueryRemainingOffenses() )
55
  ->setMod( $this->getMod() )
@@ -57,27 +57,27 @@ class ProcessOffenses extends ExecOnceModConsumer {
57
  ->run() )
58
  ) );
59
 
60
- return $aMessages;
61
  }
62
 
63
  /**
64
  * Allows 3rd parties to trigger Shield offenses
65
  * @param string $message
66
  * @param int $offenseCount
67
- * @param bool $bIncludeLoggedIn
68
  */
69
- public function processCustomShieldOffense( $message, $offenseCount = 1, $bIncludeLoggedIn = true ) {
70
  if ( $this->getCon()->isPremiumActive() ) {
71
  if ( empty( $message ) ) {
72
  $message = __( 'No custom message provided.', 'wp-simple-firewall' );
73
  }
74
 
75
- if ( $bIncludeLoggedIn || !did_action( 'init' ) || !Services::WpUsers()->isUserLoggedIn() ) {
76
  $this->getCon()
77
  ->fireEvent(
78
  'custom_offense',
79
  [
80
- 'audit' => [ 'message' => $message ],
81
  'offense_count' => (int)$offenseCount
82
  ]
83
  );
19
  $mod->loadOffenseTracker()->setIfCommit( true );
20
 
21
  $con = $this->getCon();
22
+ add_filter( 'shield/firewall_die_message', [ $this, 'augmentFirewallDieMessage' ] );
23
  add_action( $con->prefix( 'pre_plugin_shutdown' ), function () {
24
  $this->processOffense();
25
  } );
41
  }
42
 
43
  /**
44
+ * @param array $msg
45
  * @return array
46
  */
47
+ public function augmentFirewallDieMessage( $msg ) {
48
+ if ( !is_array( $msg ) ) {
49
+ $msg = [];
50
  }
51
 
52
+ $msg[] = sprintf( '<p>%s</p>', sprintf(
53
  $this->getMod()->getTextOpt( 'text_remainingtrans' ),
54
  max( 0, ( new IPs\Components\QueryRemainingOffenses() )
55
  ->setMod( $this->getMod() )
57
  ->run() )
58
  ) );
59
 
60
+ return $msg;
61
  }
62
 
63
  /**
64
  * Allows 3rd parties to trigger Shield offenses
65
  * @param string $message
66
  * @param int $offenseCount
67
+ * @param bool $includedLoggedIn
68
  */
69
+ public function processCustomShieldOffense( $message, $offenseCount = 1, $includedLoggedIn = true ) {
70
  if ( $this->getCon()->isPremiumActive() ) {
71
  if ( empty( $message ) ) {
72
  $message = __( 'No custom message provided.', 'wp-simple-firewall' );
73
  }
74
 
75
+ if ( $includedLoggedIn || !did_action( 'init' ) || !Services::WpUsers()->isUserLoggedIn() ) {
76
  $this->getCon()
77
  ->fireEvent(
78
  'custom_offense',
79
  [
80
+ 'audit_params' => [ 'message' => $message ],
81
  'offense_count' => (int)$offenseCount
82
  ]
83
  );
src/lib/src/Modules/IPs/Strings.php CHANGED
@@ -7,6 +7,156 @@ use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class Strings extends Base\Strings {
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  /**
11
  * @param string $section
12
  * @return array
@@ -87,7 +237,7 @@ class Strings extends Base\Strings {
87
  return [
88
  'title' => $title,
89
  'title_short' => $titleShort,
90
- 'summary' => ( isset( $summary ) && is_array( $summary ) ) ? $summary : [],
91
  ];
92
  }
93
 
@@ -111,7 +261,7 @@ class Strings extends Base\Strings {
111
 
112
  case 'transgression_limit' :
113
  $name = __( 'Offense Limit', 'wp-simple-firewall' );
114
- $summary = __( 'Visitor IP address will be Black Listed after X bad actions on your site', 'wp-simple-firewall' );
115
  $desc = [
116
  sprintf( __( 'An offense is registered against an IP address each time a visitor trips the defenses of the %s plugin.', 'wp-simple-firewall' ), $sPlugName ),
117
  __( 'When the number of these offenses exceeds the limit, they are automatically blocked from accessing the site.', 'wp-simple-firewall' ),
@@ -303,68 +453,4 @@ class Strings extends Base\Strings {
303
  'bypass' => __( 'IP Bypassed', 'wp-simple-firewall' ),
304
  ];
305
  }
306
-
307
- /**
308
- * @return string[][]
309
- */
310
- protected function getAuditMessages() :array {
311
- return [
312
- 'custom_offense' => [
313
- sprintf(
314
- __( 'A custom %s offense was registered on the site.', 'wp-simple-firewall' ),
315
- $this->getCon()->getHumanName()
316
- ),
317
- str_replace( '{{MESSAGE}}', __( 'Message', 'wp-simple-firewall' ), '{{MESSAGE}}: "%s"' ),
318
- ],
319
- 'conn_kill' => [
320
- __( 'Visitor found on the Black List and their connection was killed.', 'wp-simple-firewall' )
321
- ],
322
- 'ip_offense' => [
323
- __( 'Auto Black List offenses counter was incremented from %s to %s.', 'wp-simple-firewall' )
324
- ],
325
- 'ip_blocked' => [
326
- __( 'IP blocked after incrementing offenses from %s to %s.', 'wp-simple-firewall' )
327
- ],
328
- 'ip_block_auto' => [
329
- __( "IP address '%s' automatically added to block list.", 'wp-simple-firewall' )
330
- ],
331
- 'ip_block_manual' => [
332
- __( "IP address '%s' manually added to block list.", 'wp-simple-firewall' )
333
- ],
334
- 'ip_bypass_add' => [
335
- __( "IP address '%s' manually added to bypass list.", 'wp-simple-firewall' )
336
- ],
337
- 'ip_bypass_remove' => [
338
- __( "IP address '%s' manually removed from the bypass list.", 'wp-simple-firewall' )
339
- ],
340
- 'ip_unblock_flag' => [
341
- __( "IP address '%s' removed from blacklist using 'unblock' file flag.", 'wp-simple-firewall' )
342
- ],
343
- 'bottrack_404' => [
344
- __( '404 detected at "%s".', 'wp-simple-firewall' )
345
- ],
346
- 'bottrack_fakewebcrawler' => [
347
- __( 'Fake Web Crawler detected at "%s".', 'wp-simple-firewall' ),
348
- __( 'Fake Crawler misrepresented itself as "%s".', 'wp-simple-firewall' ),
349
- ],
350
- 'bottrack_linkcheese' => [
351
- __( 'Link cheese access detected at "%s".', 'wp-simple-firewall' )
352
- ],
353
- 'bottrack_loginfailed' => [
354
- __( 'Attempted login failed by user "%s".', 'wp-simple-firewall' )
355
- ],
356
- 'bottrack_logininvalid' => [
357
- __( 'Attempted login with invalid user "%s".', 'wp-simple-firewall' )
358
- ],
359
- 'bottrack_useragent' => [
360
- __( 'Empty user agent detected at "%s".', 'wp-simple-firewall' )
361
- ],
362
- 'bottrack_xmlrpc' => [
363
- __( 'Access to XML-RPC detected at "%s".', 'wp-simple-firewall' )
364
- ],
365
- 'bottrack_invalidscript' => [
366
- __( 'Tried to load an invalid WordPress PHP script "%s".', 'wp-simple-firewall' )
367
- ],
368
- ];
369
- }
370
  }
7
 
8
  class Strings extends Base\Strings {
9
 
10
+ /**
11
+ * @inheritDoc
12
+ */
13
+ public function getEventStrings() :array {
14
+ return [
15
+ 'conn_kill' => [
16
+ 'name' => __( 'Connection Killed', 'wp-simple-firewall' ),
17
+ 'audit' => [
18
+ __( 'Visitor found on the Black List and their connection was killed.', 'wp-simple-firewall' ),
19
+ ],
20
+ ],
21
+ 'conn_not_kill_high_rep' => [
22
+ 'name' => __( 'Connection Not Killed', 'wp-simple-firewall' ),
23
+ 'audit' => [
24
+ __( 'IP address has a high reputation so connection allowed.', 'wp-simple-firewall' ),
25
+ ],
26
+ ],
27
+ 'ip_offense' => [
28
+ 'name' => __( 'Offense Triggered', 'wp-simple-firewall' ),
29
+ 'audit' => [
30
+ __( 'Auto Black List offenses counter was incremented from {{from}} to {{to}}.', 'wp-simple-firewall' ),
31
+ ],
32
+ ],
33
+ 'ip_blocked' => [
34
+ 'name' => __( 'IP Blocked', 'wp-simple-firewall' ),
35
+ 'audit' => [
36
+ __( 'IP blocked after incrementing offenses from {{from}} to {{to}}.', 'wp-simple-firewall' ),
37
+ ],
38
+ ],
39
+ 'ip_unblock' => [
40
+ 'name' => __( 'IP Unblocked', 'wp-simple-firewall' ),
41
+ 'audit' => [
42
+ __( 'IP removed from block list.', 'wp-simple-firewall' ),
43
+ ],
44
+ ],
45
+ 'ip_unblock_flag' => [
46
+ 'name' => __( 'IP Unblocked (Flag File)', 'wp-simple-firewall' ),
47
+ 'audit' => [
48
+ __( "IP address '{{ip}}' removed from blacklist using 'unblock' file flag.", 'wp-simple-firewall' ),
49
+ ],
50
+ ],
51
+ 'ip_block_auto' => [
52
+ 'name' => __( 'IP Block List Add (Auto)', 'wp-simple-firewall' ),
53
+ 'audit' => [
54
+ __( "IP address '{{ip}}' automatically added to block list.", 'wp-simple-firewall' ),
55
+ ],
56
+ ],
57
+ 'ip_block_manual' => [
58
+ 'name' => __( 'IP Block List Add (Manual)', 'wp-simple-firewall' ),
59
+ 'audit' => [
60
+ __( "IP address '{{ip}}' manually added to block list.", 'wp-simple-firewall' ),
61
+ ],
62
+ ],
63
+ 'ip_bypass_add' => [
64
+ 'name' => __( 'IP Bypass List Add (Manual)', 'wp-simple-firewall' ),
65
+ 'audit' => [
66
+ __( "IP address '{{ip}}' manually added to bypass list.", 'wp-simple-firewall' ),
67
+ ],
68
+ ],
69
+ 'ip_bypass_remove' => [
70
+ 'name' => __( 'IP Bypass List Removed (Manual)', 'wp-simple-firewall' ),
71
+ 'audit' => [
72
+ __( "IP address '{{ip}}' manually removed from the bypass list.", 'wp-simple-firewall' ),
73
+ ],
74
+ ],
75
+ 'bottrack_notbot' => [
76
+ 'name' => __( 'NotBot Registration', 'wp-simple-firewall' ),
77
+ 'audit' => [
78
+ __( 'Visitor registered using NotBot.', 'wp-simple-firewall' ),
79
+ ],
80
+ ],
81
+ 'bottrack_404' => [
82
+ 'name' => sprintf( '%s: %s', __( 'Bot Detection', 'wp-simple-firewall' ), '404' ),
83
+ 'audit' => [
84
+ __( '404 detected at "{{path}}".', 'wp-simple-firewall' ),
85
+ ],
86
+ ],
87
+ 'bottrack_fakewebcrawler' => [
88
+ 'name' => sprintf( '%s: %s', __( 'Bot Detection', 'wp-simple-firewall' ),
89
+ __( 'Fake Web Crawler', 'wp-simple-firewall' ) ),
90
+ 'audit' => [
91
+ __( 'Fake Web Crawler detected at "{{path}}".', 'wp-simple-firewall' ),
92
+ __( 'Fake Crawler misrepresented itself as "{{crawler}}".', 'wp-simple-firewall' ),
93
+ ],
94
+ ],
95
+ 'bottrack_linkcheese' => [
96
+ 'name' => sprintf( '%s: %s', __( 'Bot Detection', 'wp-simple-firewall' ),
97
+ __( 'Link Cheese', 'wp-simple-firewall' ) ),
98
+ 'audit' => [
99
+ __( 'Link cheese access detected at "{{path}}".', 'wp-simple-firewall' ),
100
+ ],
101
+ ],
102
+ 'bottrack_loginfailed' => [
103
+ 'name' => sprintf( '%s: %s', __( 'Bot Detection', 'wp-simple-firewall' ),
104
+ __( 'Failed Login', 'wp-simple-firewall' ) ),
105
+ 'audit' => [
106
+ __( 'Attempted login failed by user "{{user_login}}".', 'wp-simple-firewall' ),
107
+ ],
108
+ ],
109
+ 'bottrack_logininvalid' => [
110
+ 'name' => sprintf( '%s: %s', __( 'Bot Detection', 'wp-simple-firewall' ),
111
+ __( 'Invalid Username Login', 'wp-simple-firewall' ) ),
112
+ 'audit' => [
113
+ __( 'Attempted login with invalid user "{{user_login}}".', 'wp-simple-firewall' ),
114
+ ],
115
+ ],
116
+ 'bottrack_useragent' => [
117
+ /** TODO **/
118
+ 'name' => sprintf( '%s: %s', __( 'Bot Detection', 'wp-simple-firewall' ),
119
+ __( 'Invalid User-Agent', 'wp-simple-firewall' ) ),
120
+ 'audit' => [
121
+ __( 'Invalid user agent detected at "{{useragent}}".', 'wp-simple-firewall' ),
122
+ ],
123
+ ],
124
+ 'bottrack_xmlrpc' => [
125
+ 'name' => sprintf( '%s: %s', __( 'Bot Detection', 'wp-simple-firewall' ),
126
+ __( 'XML-RPC', 'wp-simple-firewall' ) ),
127
+ 'audit' => [
128
+ __( 'Access to XML-RPC detected at "{{path}}".', 'wp-simple-firewall' ),
129
+ ],
130
+ ],
131
+ 'bottrack_invalidscript' => [
132
+ 'name' => sprintf( '%s: %s', __( 'Bot Detection', 'wp-simple-firewall' ),
133
+ __( 'Invalid Script Load', 'wp-simple-firewall' ) ),
134
+ 'audit' => [
135
+ __( 'Tried to load an invalid WordPress PHP script "{{script}}".', 'wp-simple-firewall' ),
136
+ ],
137
+ ],
138
+ 'comment_markspam' => [
139
+ 'name' => __( 'Mark Comment SPAM (Manual)', 'wp-simple-firewall' ),
140
+ 'audit' => [
141
+ __( 'Comment manually marked as SPAM.', 'wp-simple-firewall' ),
142
+ ],
143
+ ],
144
+ 'comment_unmarkspam' => [
145
+ 'name' => __( 'Mark Comment Not SPAM (Manual)', 'wp-simple-firewall' ),
146
+ 'audit' => [
147
+ __( 'Comment manually marked as not SPAM.', 'wp-simple-firewall' ),
148
+ ],
149
+ ],
150
+ 'custom_offense' => [
151
+ 'name' => __( 'Mark Comment Not SPAM (Manual)', 'wp-simple-firewall' ),
152
+ 'audit' => [
153
+ __( 'A custom offense was registered on the site.', 'wp-simple-firewall' ),
154
+ sprintf( '%s: {{message}}', __( 'Message', 'wp-simple-firewall' ) ),
155
+ ],
156
+ ],
157
+ ];
158
+ }
159
+
160
  /**
161
  * @param string $section
162
  * @return array
237
  return [
238
  'title' => $title,
239
  'title_short' => $titleShort,
240
+ 'summary' => $summary,
241
  ];
242
  }
243
 
261
 
262
  case 'transgression_limit' :
263
  $name = __( 'Offense Limit', 'wp-simple-firewall' );
264
+ $summary = __( 'The number of permitted offenses before an IP address will be blocked', 'wp-simple-firewall' );
265
  $desc = [
266
  sprintf( __( 'An offense is registered against an IP address each time a visitor trips the defenses of the %s plugin.', 'wp-simple-firewall' ), $sPlugName ),
267
  __( 'When the number of these offenses exceeds the limit, they are automatically blocked from accessing the site.', 'wp-simple-firewall' ),
453
  'bypass' => __( 'IP Bypassed', 'wp-simple-firewall' ),
454
  ];
455
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
  }
src/lib/src/Modules/IPs/Upgrade.php CHANGED
@@ -4,7 +4,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\IPs\Delete;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
7
- use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class Upgrade extends Base\Upgrade {
10
 
@@ -15,43 +14,4 @@ class Upgrade extends Base\Upgrade {
15
  $del = $mod->getDbHandler_IPs()->getQueryDeleter();
16
  $del->filterByLabel( 'iControlWP' )->query();
17
  }
18
-
19
- protected function upgrade_905() {
20
- /** @var ModCon $mod */
21
- $mod = $this->getMod();
22
- $schema = $mod->getDbHandler_IPs()->getTableSchema();
23
- Services::WpDb()->doSql(
24
- sprintf( "ALTER TABLE `%s` MODIFY `%s` %s;",
25
- $schema->table, 'ip', $schema->enumerateColumns()[ 'ip' ] )
26
- );
27
- }
28
-
29
- /**
30
- * Support larger transgression counts [smallint(1) => int(10)]
31
- */
32
- protected function upgrade_911() {
33
- /** @var ModCon $mod */
34
- $mod = $this->getMod();
35
- $schema = $mod->getDbHandler_IPs()->getTableSchema();
36
- Services::WpDb()->doSql(
37
- sprintf( "ALTER TABLE `%s` MODIFY `%s` %s;",
38
- $schema->table,
39
- 'transgressions',
40
- $schema->enumerateColumns()[ 'transgressions' ]
41
- )
42
- );
43
- }
44
-
45
- /**
46
- * Support Magic Links for logged-in users.
47
- */
48
- protected function upgrade_920() {
49
- /** @var Options $opts */
50
- $opts = $this->getOptions();
51
- $current = $opts->getOpt( 'user_auto_recover' );
52
- if ( !is_array( $current ) ) {
53
- $current = ( $current === 'gasp' ) ? [ 'gasp' ] : [];
54
- $opts->setOpt( 'user_auto_recover', $current );
55
- }
56
- }
57
  }
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\IPs\Delete;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
 
7
 
8
  class Upgrade extends Base\Upgrade {
9
 
14
  $del = $mod->getDbHandler_IPs()->getQueryDeleter();
15
  $del->filterByLabel( 'iControlWP' )->query();
16
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  }
src/lib/src/Modules/Insights/Lib/SideMenuBuilder.php CHANGED
@@ -127,7 +127,7 @@ class SideMenuBuilder {
127
  /** @var ModCon $mod */
128
  $mod = $this->getMod();
129
 
130
- $slug = 'audit';
131
  $subItems = [
132
  [
133
  'slug' => $slug.'-log',
@@ -135,10 +135,15 @@ class SideMenuBuilder {
135
  'href' => $mod->getUrl_SubInsightsPage( $slug ),
136
  'active' => $this->getInav() === $slug,
137
  ],
 
 
 
 
 
138
  [
139
  'slug' => 'audit-download',
140
- 'title' => sprintf( __( 'Download (%s)', 'wp-simple-firewall' ), 'CSV' ),
141
- 'href' => $con->getModule_AuditTrail()->createFileDownloadLink( 'db_audit' ),
142
  'classes' => [ 'shield_file_download' ],
143
  ],
144
  [
@@ -437,12 +442,6 @@ class SideMenuBuilder {
437
  'title' => __( 'Configure', 'wp-simple-firewall' ),
438
  'href' => $con->getModule_Traffic()->getUrl_DirectLinkToSection( 'section_traffic_options' ),
439
  ],
440
- [
441
- 'slug' => 'traffic-download',
442
- 'href' => $con->getModule_Traffic()->createFileDownloadLink( 'db_traffic' ),
443
- 'classes' => [ 'shield_file_download' ],
444
- 'title' => sprintf( __( 'Download (%s)', 'wp-simple-firewall' ), 'CSV' ),
445
- ],
446
  ];
447
 
448
  return [
127
  /** @var ModCon $mod */
128
  $mod = $this->getMod();
129
 
130
+ $slug = 'audit_trail';
131
  $subItems = [
132
  [
133
  'slug' => $slug.'-log',
135
  'href' => $mod->getUrl_SubInsightsPage( $slug ),
136
  'active' => $this->getInav() === $slug,
137
  ],
138
+ [
139
+ 'slug' => $slug.'-settings',
140
+ 'title' => __( 'Configure', 'wp-simple-firewall' ),
141
+ 'href' => $con->getModule_AuditTrail()->getUrl_AdminPage(),
142
+ ],
143
  [
144
  'slug' => 'audit-download',
145
+ 'title' => sprintf( __( 'Download (%s)', 'wp-simple-firewall' ), 'JSON' ),
146
+ 'href' => $con->getModule_AuditTrail()->createFileDownloadLink( 'db_log' ),
147
  'classes' => [ 'shield_file_download' ],
148
  ],
149
  [
442
  'title' => __( 'Configure', 'wp-simple-firewall' ),
443
  'href' => $con->getModule_Traffic()->getUrl_DirectLinkToSection( 'section_traffic_options' ),
444
  ],
 
 
 
 
 
 
445
  ];
446
 
447
  return [
src/lib/src/Modules/Insights/ModCon.php CHANGED
@@ -142,24 +142,19 @@ class ModCon extends BaseShield\ModCon {
142
  case 'scans_results':
143
  case 'scans_run':
144
  case 'audit':
 
145
  case 'traffic':
146
  case 'ips':
147
  case 'debug':
148
  case 'users':
149
  case 'stats':
150
 
151
- $enq[ Enqueue::JS ][] = 'shield-tables';
152
  if ( in_array( $inav, [ 'scans_results', 'scans_run' ] ) ) {
153
  $enq[ Enqueue::JS ][] = 'shield-scans';
154
  }
155
  elseif ( $inav == 'ips' ) {
156
  $enq[ Enqueue::JS ][] = 'shield/ipanalyse';
157
  }
158
-
159
- if ( in_array( $inav, [ 'audit', 'traffic' ] ) ) {
160
- $enq[ Enqueue::JS ][] = 'bootstrap-datepicker';
161
- $enq[ Enqueue::CSS ][] = 'bootstrap-datepicker';
162
- }
163
  break;
164
  }
165
  }
142
  case 'scans_results':
143
  case 'scans_run':
144
  case 'audit':
145
+ case 'audit_trail':
146
  case 'traffic':
147
  case 'ips':
148
  case 'debug':
149
  case 'users':
150
  case 'stats':
151
 
 
152
  if ( in_array( $inav, [ 'scans_results', 'scans_run' ] ) ) {
153
  $enq[ Enqueue::JS ][] = 'shield-scans';
154
  }
155
  elseif ( $inav == 'ips' ) {
156
  $enq[ Enqueue::JS ][] = 'shield/ipanalyse';
157
  }
 
 
 
 
 
158
  break;
159
  }
160
  }
src/lib/src/Modules/Insights/Strings.php CHANGED
@@ -6,50 +6,6 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
 
7
  class Strings extends Base\Strings {
8
 
9
- /**
10
- * @return string[]
11
- */
12
- public function getInsightStatNames() :array {
13
- return [
14
- 'key_success' => __( 'Successful authentication with Security Admin', 'wp-simple-firewall' ),
15
- 'key_fail' => __( 'Failed authentication with Security Admin', 'wp-simple-firewall' ),
16
- 'test_cron_run' => __( 'Simple Test Cron', 'wp-simple-firewall' ),
17
- 'apc_scan_run' => __( 'Scanned for abandoned plugins', 'wp-simple-firewall' ),
18
- 'mal_scan_run' => __( 'Scanned for malware', 'wp-simple-firewall' ),
19
- 'ptg_scan_run' => __( 'Scanned for altered plugin/theme files', 'wp-simple-firewall' ),
20
- 'ufc_scan_run' => __( 'Scanned for unrecognised files', 'wp-simple-firewall' ),
21
- 'wcf_scan_run' => __( 'Scanned Core files', 'wp-simple-firewall' ),
22
- 'wpv_scan_run' => __( 'Scanned for vulnerabilities', 'wp-simple-firewall' ),
23
- 'wcf_scan_found' => __( 'Found modified core file', 'wp-simple-firewall' ),
24
- 'apc_scan_found' => __( 'Found abandoned plugin', 'wp-simple-firewall' ),
25
- 'mal_scan_found' => __( 'Found malware in file', 'wp-simple-firewall' ),
26
- 'ptg_scan_found' => __( 'Found altered plugin/themes file', 'wp-simple-firewall' ),
27
- 'ufc_scan_found' => __( 'Found unrecognised file', 'wp-simple-firewall' ),
28
- 'wpv_scan_found' => __( 'Found vulnerable item', 'wp-simple-firewall' ),
29
- 'scan_item_repair_success' => __( 'Scan item successfully repaired', 'wp-simple-firewall' ),
30
- 'scan_item_repair_fail' => __( 'Scan item repair failed', 'wp-simple-firewall' ),
31
- 'scan_item_delete_success' => __( 'Scan item successfully deleted', 'wp-simple-firewall' ),
32
- 'session_terminate' => __( 'User session terminated and forced to re-login', 'wp-simple-firewall' ),
33
- 'conn_kill' => __( 'Connection killed for blocked IP address', 'wp-simple-firewall' ),
34
- 'ip_offense' => __( 'Offense registered against IP address', 'wp-simple-firewall' ),
35
- 'ip_blocked' => __( 'IP address blocked after too many offenses', 'wp-simple-firewall' ),
36
- 'spam_block_antibot' => __( 'Detected comment SPAM using AntiBot', 'wp-simple-firewall' ),
37
- 'spam_block_bot' => __( 'Detected comment SPAM from bot', 'wp-simple-firewall' ),
38
- 'spam_block_recaptcha' => __( 'Detected comment SPAM from failed reCAPTCHA', 'wp-simple-firewall' ),
39
- 'spam_block_human' => __( 'Detected human comment SPAM with suspicious content', 'wp-simple-firewall' ),
40
- '2fa_success' => __( 'Successful 2-FA Login', 'wp-simple-firewall' ),
41
- 'login_block' => __( 'Blocked Login', 'wp-simple-firewall' ),
42
- 'password_policy_force_change' => __( 'Forced password update due to policy', 'wp-simple-firewall' ),
43
- 'password_policy_block' => __( 'Prevented password update due to policy', 'wp-simple-firewall' ),
44
- 'firewall_block' => __( 'Firewall Block', 'wp-simple-firewall' ),
45
- 'options_exported' => __( 'Options exported', 'wp-simple-firewall' ),
46
- 'options_imported' => __( 'Options imported', 'wp-simple-firewall' ),
47
- 'block_anonymous_restapi' => __( 'Blocked anonymous Rest API', 'wp-simple-firewall' ),
48
- 'block_xml' => __( 'Blocked XML-RPC', 'wp-simple-firewall' ),
49
- 'user_hard_suspended' => __( 'User account suspended by administrator', 'wp-simple-firewall' ),
50
- ];
51
- }
52
-
53
  /**
54
  * @inheritDoc
55
  */
6
 
7
  class Strings extends Base\Strings {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  /**
10
  * @inheritDoc
11
  */
src/lib/src/Modules/Insights/UI.php CHANGED
@@ -15,6 +15,7 @@ class UI extends BaseShield\UI {
15
  return [
16
  'content' => [
17
  'tab_updates' => $this->renderTabUpdates(),
 
18
  ],
19
  'flags' => [
20
  'is_pro' => $con->isPremiumActive(),
@@ -23,8 +24,9 @@ class UI extends BaseShield\UI {
23
  'free_trial' => 'https://shsec.io/shieldfreetrialinplugin',
24
  ],
25
  'strings' => [
26
- 'tab_freetrial' => __( 'Free Trial', 'wp-simple-firewall' ),
27
  'tab_updates' => __( 'Updates and Changes', 'wp-simple-firewall' ),
 
 
28
  ],
29
  ];
30
  }
@@ -64,6 +66,7 @@ class UI extends BaseShield\UI {
64
  switch ( $inav ) {
65
 
66
  case 'audit':
 
67
  $modAudit = $con->getModule_AuditTrail();
68
  /** @var Shield\Modules\AuditTrail\UI $auditUI */
69
  $auditUI = $modAudit->getUIHandler();
@@ -134,10 +137,15 @@ class UI extends BaseShield\UI {
134
  break;
135
 
136
  case 'scans_results':
 
 
 
 
 
137
  case 'scans_run':
138
  /** @var Shield\Modules\HackGuard\UI $UIHackGuard */
139
  $UIHackGuard = $con->getModule_HackGuard()->getUIHandler();
140
- $data = $UIHackGuard->buildInsightsVars();
141
  break;
142
 
143
  case 'settings':
@@ -189,6 +197,7 @@ class UI extends BaseShield\UI {
189
  'docs' => __( 'Docs', 'wp-simple-firewall' ),
190
  'ips' => __( 'IP Management and Analysis', 'wp-simple-firewall' ),
191
  'audit' => __( 'Audit Trail', 'wp-simple-firewall' ),
 
192
  'traffic' => __( 'Traffic', 'wp-simple-firewall' ),
193
  'notes' => __( 'Admin Notes', 'wp-simple-firewall' ),
194
  'users' => __( 'User Sessions', 'wp-simple-firewall' ),
@@ -265,6 +274,51 @@ class UI extends BaseShield\UI {
265
  );
266
  }
267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  private function renderTabUpdates() :string {
269
  try {
270
  $changelog = ( new Retrieve() )
15
  return [
16
  'content' => [
17
  'tab_updates' => $this->renderTabUpdates(),
18
+ 'tab_events' => $this->renderTabEvents(),
19
  ],
20
  'flags' => [
21
  'is_pro' => $con->isPremiumActive(),
24
  'free_trial' => 'https://shsec.io/shieldfreetrialinplugin',
25
  ],
26
  'strings' => [
 
27
  'tab_updates' => __( 'Updates and Changes', 'wp-simple-firewall' ),
28
+ 'tab_events' => __( 'Event Details', 'wp-simple-firewall' ),
29
+ 'tab_freetrial' => __( 'Free Trial', 'wp-simple-firewall' ),
30
  ],
31
  ];
32
  }
66
  switch ( $inav ) {
67
 
68
  case 'audit':
69
+ case 'audit_trail':
70
  $modAudit = $con->getModule_AuditTrail();
71
  /** @var Shield\Modules\AuditTrail\UI $auditUI */
72
  $auditUI = $modAudit->getUIHandler();
137
  break;
138
 
139
  case 'scans_results':
140
+ /** @var Shield\Modules\HackGuard\UI $UIHackGuard */
141
+ $UIHackGuard = $con->getModule_HackGuard()->getUIHandler();
142
+ $data = $UIHackGuard->buildInsightsVars_Results();
143
+ break;
144
+
145
  case 'scans_run':
146
  /** @var Shield\Modules\HackGuard\UI $UIHackGuard */
147
  $UIHackGuard = $con->getModule_HackGuard()->getUIHandler();
148
+ $data = $UIHackGuard->buildInsightsVars_Run();
149
  break;
150
 
151
  case 'settings':
197
  'docs' => __( 'Docs', 'wp-simple-firewall' ),
198
  'ips' => __( 'IP Management and Analysis', 'wp-simple-firewall' ),
199
  'audit' => __( 'Audit Trail', 'wp-simple-firewall' ),
200
+ 'audit_trail' => __( 'Audit Trail', 'wp-simple-firewall' ),
201
  'traffic' => __( 'Traffic', 'wp-simple-firewall' ),
202
  'notes' => __( 'Admin Notes', 'wp-simple-firewall' ),
203
  'users' => __( 'User Sessions', 'wp-simple-firewall' ),
274
  );
275
  }
276
 
277
+ private function renderTabEvents() :string {
278
+ $con = $this->getCon();
279
+ $srvEvents = $this->getCon()->loadEventsService();
280
+
281
+ $eventsSortedByLevel = [
282
+ 'Alert' => [],
283
+ 'Warning' => [],
284
+ 'Notice' => [],
285
+ 'Info' => [],
286
+ 'Debug' => [],
287
+ ];
288
+ foreach ( $srvEvents->getEvents() as $event ) {
289
+ $level = ucfirst( strtolower( $event[ 'level' ] ) );
290
+ $eventsSortedByLevel[ $level ][ $event[ 'key' ] ] = [
291
+ 'name' => $srvEvents->getEventName( $event[ 'key' ] ),
292
+ 'attr' => [
293
+ 'stat' => sprintf( 'Stat: %s', empty( $event[ 'stat' ] ) ? 'No' : 'Yes' ),
294
+ 'offense' => sprintf( 'Offense: %s', empty( $event[ 'offense' ] ) ? 'No' : 'Yes' ),
295
+ 'module' => sprintf( 'Module: %s', $con->getModule( $event[ 'module' ] )->getMainFeatureName() ),
296
+ ]
297
+ ];
298
+ }
299
+ foreach ( $eventsSortedByLevel as &$events ) {
300
+ ksort( $events );
301
+ }
302
+
303
+ return $this->getMod()->renderTemplate(
304
+ '/wpadmin_pages/insights/docs/events.twig',
305
+ [
306
+ 'vars' => [
307
+ // the keys here must match the changelog item types
308
+ 'event_defs' => $eventsSortedByLevel
309
+ ],
310
+ 'strings' => [
311
+ // the keys here must match the changelog item types
312
+ 'version' => __( 'Version', 'wp-simple-firewall' ),
313
+ 'release_date' => __( 'Release Date', 'wp-simple-firewall' ),
314
+ 'pro_only' => __( 'Pro Only', 'wp-simple-firewall' ),
315
+ 'full_release' => __( 'Full Release Announcement', 'wp-simple-firewall' ),
316
+ ],
317
+ ],
318
+ true
319
+ );
320
+ }
321
+
322
  private function renderTabUpdates() :string {
323
  try {
324
  $changelog = ( new Retrieve() )
src/lib/src/Modules/Integrations/Lib/Bots/Spam/Handlers/Base.php CHANGED
@@ -14,7 +14,7 @@ abstract class Base extends BaseHandler {
14
  $this->getCon()->fireEvent(
15
  sprintf( 'spam_form_%s', $isSpam ? 'fail' : 'pass' ),
16
  [
17
- 'audit' => [
18
  'form_provider' => $this->getProviderName(),
19
  ]
20
  ]
14
  $this->getCon()->fireEvent(
15
  sprintf( 'spam_form_%s', $isSpam ? 'fail' : 'pass' ),
16
  [
17
+ 'audit_params' => [
18
  'form_provider' => $this->getProviderName(),
19
  ]
20
  ]
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/Base.php CHANGED
@@ -78,7 +78,7 @@ abstract class Base extends Integrations\Lib\Bots\Common\BaseHandler {
78
  $this->getCon()->fireEvent(
79
  sprintf( 'user_form_bot_%s', self::$isBot ? 'fail' : 'pass' ),
80
  [
81
- 'audit' => [
82
  'form_provider' => $this->getProviderName(),
83
  'action' => $this->getAuditAction(),
84
  'username' => $this->getAuditUser(),
78
  $this->getCon()->fireEvent(
79
  sprintf( 'user_form_bot_%s', self::$isBot ? 'fail' : 'pass' ),
80
  [
81
+ 'audit_params' => [
82
  'form_provider' => $this->getProviderName(),
83
  'action' => $this->getAuditAction(),
84
  'username' => $this->getAuditUser(),
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/Buddyboss.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers;
4
+
5
+ class Buddyboss extends Base {
6
+
7
+ protected function register() {
8
+ add_action( 'bp_signup_validate', [ $this, 'checkRegister_BP' ] );
9
+ }
10
+
11
+ public function checkRegister_BP() {
12
+ if ( $this->setAuditAction( 'register' )->checkIsBot() ) {
13
+ $bp = \buddypress();
14
+ if ( is_object( $bp->signup ) ) {
15
+ $bp->signup->errors[ 'shield-fail-register' ] = 'Failed AntiBot SPAM Check';
16
+ }
17
+ }
18
+ }
19
+
20
+ protected function getProviderName() :string {
21
+ return 'BuddyBoss';
22
+ }
23
+
24
+ public static function IsProviderInstalled() :bool {
25
+ return @class_exists( '\BuddyPress' )
26
+ && method_exists( '\BuddyPress', 'instance' )
27
+ && function_exists( '\buddypress' )
28
+ && \BuddyPress::instance()->buddyboss === true;
29
+ }
30
+ }
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/Buddypress.php CHANGED
@@ -2,10 +2,6 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers;
4
 
5
- /**
6
- * Class Buddypress
7
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers
8
- */
9
  class Buddypress extends Base {
10
 
11
  protected function register() {
@@ -23,6 +19,6 @@ class Buddypress extends Base {
23
  }
24
 
25
  public static function IsProviderInstalled() :bool {
26
- return @class_exists( 'BuddyPress' );
27
  }
28
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers;
4
 
 
 
 
 
5
  class Buddypress extends Base {
6
 
7
  protected function register() {
19
  }
20
 
21
  public static function IsProviderInstalled() :bool {
22
+ return @class_exists( '\BuddyPress' );
23
  }
24
  }
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/EasyDigitalDownloads.php CHANGED
@@ -2,14 +2,10 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers;
4
 
5
- /**
6
- * Class EasyDigitalDownloads
7
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers
8
- */
9
  class EasyDigitalDownloads extends Base {
10
 
11
  protected function register() {
12
- add_action( 'edd_process_register_form', [ $this, 'checkRegister_EDD' ], 10 );
13
  }
14
 
15
  public function checkRegister_EDD() {
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers;
4
 
 
 
 
 
5
  class EasyDigitalDownloads extends Base {
6
 
7
  protected function register() {
8
+ add_action( 'edd_process_register_form', [ $this, 'checkRegister_EDD' ] );
9
  }
10
 
11
  public function checkRegister_EDD() {
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/LearnPress.php CHANGED
@@ -2,10 +2,6 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers;
4
 
5
- /**
6
- * Class LearnPress
7
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers
8
- */
9
  class LearnPress extends Base {
10
 
11
  protected function login() {
@@ -13,7 +9,7 @@ class LearnPress extends Base {
13
  }
14
 
15
  protected function register() {
16
- add_filter( 'learn-press/register-validate-field', [ $this, 'checkRegister_LP' ], 100, 1 );
17
  }
18
 
19
  /**
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers;
4
 
 
 
 
 
5
  class LearnPress extends Base {
6
 
7
  protected function login() {
9
  }
10
 
11
  protected function register() {
12
+ add_filter( 'learn-press/register-validate-field', [ $this, 'checkRegister_LP' ], 100 );
13
  }
14
 
15
  /**
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/LifterLMS.php CHANGED
@@ -4,8 +4,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\
4
 
5
  /**
6
  * Lost Password is mimicked after WordPress so no separate integration necessary
7
- * Class LifterLMS
8
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers
9
  */
10
  class LifterLMS extends Base {
11
 
4
 
5
  /**
6
  * Lost Password is mimicked after WordPress so no separate integration necessary
 
 
7
  */
8
  class LifterLMS extends Base {
9
 
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/PaidMemberSubscriptions.php CHANGED
@@ -2,10 +2,6 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers;
4
 
5
- /**
6
- * Class PaidMemberSubscriptions
7
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers
8
- */
9
  class PaidMemberSubscriptions extends Base {
10
 
11
  protected function register() {
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers;
4
 
 
 
 
 
5
  class PaidMemberSubscriptions extends Base {
6
 
7
  protected function register() {
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/ProfileBuilder.php CHANGED
@@ -4,9 +4,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\
4
 
5
  /**
6
  * https://wordpress.org/plugins/profile-builder/
7
- *
8
- * Class ProfileBuilder
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers
10
  */
11
  class ProfileBuilder extends Base {
12
 
4
 
5
  /**
6
  * https://wordpress.org/plugins/profile-builder/
 
 
 
7
  */
8
  class ProfileBuilder extends Base {
9
 
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/UltimateMember.php CHANGED
@@ -4,9 +4,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\
4
 
5
  /**
6
  * https://wordpress.org/plugins/ultimate-member/
7
- *
8
- * Class UltimateMember
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers
10
  */
11
  class UltimateMember extends Base {
12
 
4
 
5
  /**
6
  * https://wordpress.org/plugins/ultimate-member/
 
 
 
7
  */
8
  class UltimateMember extends Base {
9
 
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/WPMembers.php CHANGED
@@ -4,9 +4,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\
4
 
5
  /**
6
  * https://wordpress.org/plugins/wp-members/
7
- *
8
- * Class WPMembers
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers
10
  */
11
  class WPMembers extends Base {
12
 
4
 
5
  /**
6
  * https://wordpress.org/plugins/wp-members/
 
 
 
7
  */
8
  class WPMembers extends Base {
9
 
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/WooCommerce.php CHANGED
@@ -2,10 +2,6 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers;
4
 
5
- /**
6
- * Class WooCommerce
7
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers
8
- */
9
  class WooCommerce extends Base {
10
 
11
  protected function login() {
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers;
4
 
 
 
 
 
5
  class WooCommerce extends Base {
6
 
7
  protected function login() {
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/WordPress.php CHANGED
@@ -4,10 +4,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\
4
 
5
  use FernleafSystems\Wordpress\Services\Services;
6
 
7
- /**
8
- * Class WordPress
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers
10
- */
11
  class WordPress extends Base {
12
 
13
  protected function login() {
4
 
5
  use FernleafSystems\Wordpress\Services\Services;
6
 
 
 
 
 
7
  class WordPress extends Base {
8
 
9
  protected function login() {
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/UserFormsController.php CHANGED
@@ -27,6 +27,7 @@ class UserFormsController extends Integrations\Lib\Bots\Common\BaseBotDetectionC
27
  */
28
  public function enumProviders() :array {
29
  return [
 
30
  new Handlers\Buddypress(),
31
  new Handlers\EasyDigitalDownloads(),
32
  new Handlers\LearnPress(),
27
  */
28
  public function enumProviders() :array {
29
  return [
30
+ new Handlers\Buddyboss(),
31
  new Handlers\Buddypress(),
32
  new Handlers\EasyDigitalDownloads(),
33
  new Handlers\LearnPress(),
src/lib/src/Modules/Integrations/Lib/Spam/Handlers/Base.php DELETED
@@ -1,61 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
6
- use FernleafSystems\Wordpress\Services\Services;
7
-
8
- abstract class Base extends ExecOnceModConsumer {
9
-
10
- const SLUG = '';
11
-
12
- protected function canRun() :bool {
13
- return $this->getCon()->isPremiumActive() && $this->isEnabled() && $this->isProviderAvailable();
14
- }
15
-
16
- public function isSpam() :bool {
17
- $isSpam = $this->isSpam_Bot();
18
- $this->getCon()->fireEvent(
19
- sprintf( 'spam_form_%s', $isSpam ? 'fail' : 'pass' ),
20
- [
21
- 'audit' => [
22
- 'form_provider' => $this->getProviderName(),
23
- ]
24
- ]
25
- );
26
- return $isSpam;
27
- }
28
-
29
- protected function isSpam_Bot() :bool {
30
- return $this->getCon()
31
- ->getModule_IPs()
32
- ->getBotSignalsController()
33
- ->isBot( Services::IP()->getRequestIp() );
34
- }
35
-
36
- protected function isSpam_Human() :bool {
37
- return false;
38
- }
39
-
40
- protected function isEnabled() :bool {
41
- return in_array( $this->getProviderSlug(), $this->getOptions()->getOpt( 'form_spam_providers', [] ) );
42
- }
43
-
44
- protected function isProviderAvailable() :bool {
45
- return false;
46
- }
47
-
48
- protected function getProviderName() :string {
49
- return '';
50
- }
51
-
52
- protected function getProviderSlug() :string {
53
- try {
54
- $slug = strtolower( ( new \ReflectionClass( $this ) )->getShortName() );
55
- }
56
- catch ( \Exception $e ) {
57
- $slug = '';
58
- }
59
- return $slug;
60
- }
61
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Integrations/Lib/Spam/Handlers/ContactForm7.php DELETED
@@ -1,20 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers;
4
-
5
- class ContactForm7 extends Base {
6
-
7
- protected function run() {
8
- add_filter( 'wpcf7_spam', function ( $wasSpam, $submission ) {
9
- return $wasSpam || $this->isSpam();
10
- }, 1000, 2 );
11
- }
12
-
13
- protected function getProviderName() :string {
14
- return 'Contact Form 7';
15
- }
16
-
17
- protected function isProviderAvailable() :bool {
18
- return defined( 'WPCF7_TEXT_DOMAIN' ) && WPCF7_TEXT_DOMAIN === 'contact-form-7';
19
- }
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Integrations/Lib/Spam/Handlers/ElementorPro.php DELETED
@@ -1,26 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers;
4
-
5
- class ElementorPro extends Base {
6
-
7
- protected function run() {
8
- add_action( 'elementor_pro/forms/validation', function ( $form, $ajax_handler ) {
9
- /** @var \ElementorPro\Modules\Forms\Classes\Ajax_Handler $ajax_handler */
10
- if ( empty( $ajax_handler->errors ) && $this->isSpam() ) {
11
- $msg = sprintf( __( "This appears to be spam - failed %s AntiBot protection checks.", 'wp-simple-firewall' ),
12
- $this->getCon()->getHumanName() );
13
- $ajax_handler->add_error( 'shield-antibot', $msg );
14
- $ajax_handler->add_error_message( $msg );
15
- }
16
- }, 1000, 2 );
17
- }
18
-
19
- protected function getProviderName() :string {
20
- return 'Elementor Pro';
21
- }
22
-
23
- protected function isProviderAvailable() :bool {
24
- return defined( 'ELEMENTOR_PRO_VERSION' ) && @function_exists( 'elementor_pro_load_plugin' );
25
- }
26
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Integrations/Lib/Spam/Handlers/FluentForms.php DELETED
@@ -1,28 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers;
4
-
5
- /**
6
- * This form only provides a filter on the "Akismet" spam result, not a general spam result.
7
- *
8
- * Luckily the error message within the plugin is non-Akismet specific.
9
- *
10
- * Class FluentForms
11
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers
12
- */
13
- class FluentForms extends Base {
14
-
15
- protected function run() {
16
- add_filter( 'fluentform_akismet_spam_result', function ( $wasSpam ) {
17
- return $wasSpam || $this->isSpam();
18
- }, 1000 );
19
- }
20
-
21
- protected function getProviderName() :string {
22
- return 'Fluent Forms';
23
- }
24
-
25
- protected function isProviderAvailable() :bool {
26
- return defined( 'FLUENTFORM' ) && @class_exists( '\FluentForm\Framework\Foundation\Bootstrap' );
27
- }
28
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Integrations/Lib/Spam/Handlers/FormidableForms.php DELETED
@@ -1,29 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers;
4
-
5
- class FormidableForms extends Base {
6
-
7
- protected function run() {
8
- add_filter( 'frm_validate_entry', function ( $errors ) {
9
- if ( !is_array( $errors ) || empty( $errors[ 'spam' ] ) ) {
10
- if ( $this->isSpam() ) {
11
- if ( !is_array( $errors ) ) {
12
- $errors = [];
13
- }
14
- // string taken from Formidable forms FrmEntryValidate.php
15
- $errors[ 'spam' ] = __( 'Your entry appears to be spam!', 'formidable' );
16
- }
17
- }
18
- return $errors;
19
- }, 1000 );
20
- }
21
-
22
- protected function getProviderName() :string {
23
- return 'Formidable Forms';
24
- }
25
-
26
- protected function isProviderAvailable() :bool {
27
- return function_exists( 'load_formidable_forms' ) && @class_exists( '\FrmHooksController' );
28
- }
29
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Integrations/Lib/Spam/Handlers/Forminator.php DELETED
@@ -1,20 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers;
4
-
5
- class Forminator extends Base {
6
-
7
- protected function run() {
8
- add_filter( 'forminator_spam_protection', function ( $wasSpam ) {
9
- return $wasSpam || $this->isSpam();
10
- }, 1000 );
11
- }
12
-
13
- protected function getProviderName() :string {
14
- return 'Forminator';
15
- }
16
-
17
- protected function isProviderAvailable() :bool {
18
- return defined( 'FORMINATOR_VERSION' ) && @class_exists( '\Forminator' );
19
- }
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Integrations/Lib/Spam/Handlers/GravityForms.php DELETED
@@ -1,22 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers;
4
-
5
- class GravityForms extends Base {
6
-
7
- protected function run() {
8
- add_filter( 'gform_entry_is_spam', function ( $wasSpam ) {
9
- return $wasSpam || $this->isSpam();
10
- }, 1000 );
11
- }
12
-
13
- protected function getProviderName() :string {
14
- return 'Gravity Forms';
15
- }
16
-
17
- protected function isProviderAvailable() :bool {
18
- return @class_exists( '\GFForms' )
19
- && isset( \GFForms::$version )
20
- && version_compare( \GFForms::$version, '2.4.17', '>=' );
21
- }
22
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Integrations/Lib/Spam/Handlers/Helpers/NinjaForms_ShieldSpamAction.php DELETED
@@ -1,47 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers\Helpers;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers\NinjaForms;
6
-
7
- final class NinjaForms_ShieldSpamAction extends \NF_Abstracts_Action {
8
-
9
- /**
10
- * @var string
11
- */
12
- protected $_name = 'shieldantibot';
13
-
14
- /**
15
- * @var NinjaForms
16
- */
17
- private $shieldNinjaFormsHandler;
18
-
19
- /**
20
- * @var array
21
- */
22
- protected $_tags = [ 'spam', 'filtering', 'shield' ];
23
-
24
- public function __construct() {
25
- parent::__construct();
26
- $this->_nicename = esc_html__( 'Shield Anti-Spam', 'ninja-forms' );
27
- }
28
-
29
- /**
30
- * @param NinjaForms $handler
31
- * @return $this
32
- */
33
- public function setHandler( NinjaForms $handler ) {
34
- $this->shieldNinjaFormsHandler = $handler;
35
- return $this;
36
- }
37
-
38
- /**
39
- * @inheritDoc
40
- */
41
- public function process( $action_settings, $form_id, $data ) {
42
- if ( $this->shieldNinjaFormsHandler->isSpam() ) {
43
- $data[ 'errors' ][ 'form' ][ 'spam' ] = esc_html__( 'There was an error trying to send your message. Please try again later', 'ninja-forms' );
44
- }
45
- return $data;
46
- }
47
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Integrations/Lib/Spam/Handlers/KaliForms.php DELETED
@@ -1,27 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers;
4
-
5
- class KaliForms extends Base {
6
-
7
- protected function run() {
8
- add_filter( 'kaliforms_before_form_process', function ( $data ) {
9
- if ( is_array( $data ) && empty( $data[ 'error_bag' ] ) && $this->isSpam() ) {
10
- $data[ 'admin_stop_execution' ] = true;
11
- $data[ 'admin_stop_reason' ] = __( 'Your entry appears to be spam!', 'wp-simple-firewall' );
12
- $data[ 'error_bag' ] = [
13
- __( 'SPAM Bot detected.', 'wp-simple-firewall' )
14
- ];
15
- }
16
- return $data;
17
- }, 1000 );
18
- }
19
-
20
- protected function getProviderName() :string {
21
- return 'Kali Forms';
22
- }
23
-
24
- protected function isProviderAvailable() :bool {
25
- return defined( 'KALIFORMS_PLUGIN_FILE' ) && @class_exists( '\KaliForms\Inc\KaliForms' );
26
- }
27
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Integrations/Lib/Spam/Handlers/NinjaForms.php DELETED
@@ -1,48 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers\Helpers\NinjaForms_ShieldSpamAction;
6
-
7
- /**
8
- * A rather convoluted way to integrate. First you must add your custom "action" to the list
9
- * of actions to be executed on a submission.
10
- *
11
- * Then you must create a Custom Action class which will handle the action and add it to the
12
- * registered action.
13
- *
14
- * Unfortunately the action register is executed early and so hooking to Init breaks it.
15
- *
16
- * Class NinjaForms
17
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers
18
- */
19
- class NinjaForms extends Base {
20
-
21
- protected function run() {
22
-
23
- add_filter( 'ninja_forms_register_actions', function ( $actions ) {
24
- $actions[ 'shieldantibot' ] = ( new NinjaForms_ShieldSpamAction() )
25
- ->setHandler( $this );
26
- return $actions;
27
- }, 1000 );
28
-
29
- add_filter( 'ninja_forms_submission_actions', function ( $actions ) {
30
- $actions[] = [
31
- 'id' => 'shieldantibot',
32
- 'settings' => [
33
- 'active' => true,
34
- 'type' => 'shieldantibot',
35
- ]
36
- ];
37
- return $actions;
38
- }, 1000 );
39
- }
40
-
41
- protected function getProviderName() :string {
42
- return 'Ninja Forms';
43
- }
44
-
45
- protected function isProviderAvailable() :bool {
46
- return @class_exists( '\Ninja_Forms' );
47
- }
48
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Integrations/Lib/Spam/Handlers/WPForms.php DELETED
@@ -1,37 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers;
4
-
5
- class WPForms extends Base {
6
-
7
- private $workingFormID = null;
8
-
9
- protected function run() {
10
- add_filter( 'wpforms_process_before_form_data',
11
- function ( $formData, $formEntry ) {
12
- $this->workingFormID = absint( $formEntry[ 'id' ] );
13
- return $formData;
14
- },
15
- 1000, 2
16
- );
17
-
18
- add_filter( 'wpforms_process_initial_errors', function ( $errors, $formData ) {
19
-
20
- if ( empty( $errors[ $this->workingFormID ] ) && $this->isSpam() ) {
21
- $errors[ $this->workingFormID ] = [
22
- 'header' => __( 'Shield detected this as a SPAM Bot submission.' ),
23
- ];
24
- }
25
-
26
- return $errors;
27
- }, 1000, 2 );
28
- }
29
-
30
- protected function getProviderName() :string {
31
- return 'WP Forms';
32
- }
33
-
34
- protected function isProviderAvailable() :bool {
35
- return defined( 'WPFORMS_VERSION' ) && function_exists( 'wpforms' );
36
- }
37
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Integrations/Lib/Spam/Handlers/WpForo.php DELETED
@@ -1,40 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam\Handlers;
4
-
5
- class WpForo extends Base {
6
-
7
- protected function run() {
8
- foreach ( $this->getFiltersToMonitor() as $filter ) {
9
- add_filter( $filter, function ( array $args = [] ) {
10
-
11
- $status = $args[ 'status' ] ?? null;
12
- if ( $status !== 1 && $this->isSpam() ) {
13
- if ( !empty( WPF()->current_userid ) ) {
14
- WPF()->moderation->ban_for_spam( WPF()->current_userid );
15
- }
16
- $args[ 'status' ] = 1; // 1 signifies not approved
17
- }
18
-
19
- return $args;
20
- }, 1000 );
21
- }
22
- }
23
-
24
- private function getFiltersToMonitor() :array {
25
- return [
26
- 'wpforo_add_topic_data_filter',
27
- 'wpforo_edit_topic_data_filter',
28
- 'wpforo_add_post_data_filter',
29
- 'wpforo_edit_post_data_filter',
30
- ];
31
- }
32
-
33
- protected function getProviderName() :string {
34
- return 'wpForo';
35
- }
36
-
37
- protected function isProviderAvailable() :bool {
38
- return function_exists( 'WPF' ) && @class_exists( 'wpForo' ) && !empty( WPF()->tools_antispam[ 'spam_filter' ] );
39
- }
40
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Integrations/Strings.php CHANGED
@@ -9,19 +9,31 @@ class Strings extends Base\Strings {
9
  /**
10
  * @inheritDoc
11
  */
12
- protected function getAuditMessages() :array {
13
  return [
14
  'spam_form_pass' => [
15
- __( '"%s" submission passed SPAM check.', 'wp-simple-firewall' ),
 
 
 
16
  ],
17
  'spam_form_fail' => [
18
- __( '"%s" submission failed SPAM check.', 'wp-simple-firewall' )
 
 
 
19
  ],
20
  'user_form_bot_pass' => [
21
- __( '"%s" submission for form "%s" with username "%s" passed Bot check.', 'wp-simple-firewall' ),
 
 
 
22
  ],
23
  'user_form_bot_fail' => [
24
- __( '"%s" submission for form "%s" with username "%s" failed Bot check.', 'wp-simple-firewall' ),
 
 
 
25
  ],
26
  ];
27
  }
9
  /**
10
  * @inheritDoc
11
  */
12
+ public function getEventStrings() :array {
13
  return [
14
  'spam_form_pass' => [
15
+ 'name' => __( 'SPAM Check Pass', 'wp-simple-firewall' ),
16
+ 'audit' => [
17
+ __( '"{{form_provider}}" submission passed SPAM check.', 'wp-simple-firewall' ),
18
+ ],
19
  ],
20
  'spam_form_fail' => [
21
+ 'name' => __( 'SPAM Check Fail', 'wp-simple-firewall' ),
22
+ 'audit' => [
23
+ __( '"{{form_provider}}" submission failed SPAM check.', 'wp-simple-firewall' ),
24
+ ],
25
  ],
26
  'user_form_bot_pass' => [
27
+ 'name' => __( 'User Bot Check Pass', 'wp-simple-firewall' ),
28
+ 'audit' => [
29
+ __( '"{{form_provider}}" submission for form "{{action}}" with username "{{username}}" passed Bot check.', 'wp-simple-firewall' ),
30
+ ],
31
  ],
32
  'user_form_bot_fail' => [
33
+ 'name' => __( 'User Bot Check Fail', 'wp-simple-firewall' ),
34
+ 'audit' => [
35
+ __( '"{{form_provider}}" submission for form "{{action}}" with username "{{username}}" failed Bot check.', 'wp-simple-firewall' ),
36
+ ],
37
  ],
38
  ];
39
  }
src/lib/src/Modules/License/ModCon.php CHANGED
@@ -56,10 +56,12 @@ class ModCon extends BaseShield\ModCon {
56
  }
57
 
58
  public function onPluginShutdown() {
59
- try {
60
- $this->getLicenseHandler()->verify( false );
61
- }
62
- catch ( \Exception $e ) {
 
 
63
  }
64
  parent::onPluginShutdown();
65
  }
56
  }
57
 
58
  public function onPluginShutdown() {
59
+ if ( !$this->getCon()->plugin_deleting ) {
60
+ try {
61
+ $this->getLicenseHandler()->verify( false );
62
+ }
63
+ catch ( \Exception $e ) {
64
+ }
65
  }
66
  parent::onPluginShutdown();
67
  }
src/lib/src/Modules/License/Strings.php CHANGED
@@ -6,6 +6,32 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
 
7
  class Strings extends Base\Strings {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  /**
10
  * @inheritDoc
11
  */
@@ -35,21 +61,4 @@ class Strings extends Base\Strings {
35
  'last_errors' => __( 'Error', 'wp-simple-firewall' ),
36
  ];
37
  }
38
-
39
- /**
40
- * @return string[][]
41
- */
42
- protected function getAuditMessages() :array {
43
- return [
44
- 'lic_check_success' => [
45
- __( 'Pro License check succeeded.', 'wp-simple-firewall' )
46
- ],
47
- 'lic_fail_email' => [
48
- __( 'License check failed. Sending Warning Email.', 'wp-simple-firewall' )
49
- ],
50
- 'lic_fail_deactivate' => [
51
- __( 'License check failed. Deactivating Pro.', 'wp-simple-firewall' )
52
- ],
53
- ];
54
- }
55
  }
6
 
7
  class Strings extends Base\Strings {
8
 
9
+ /**
10
+ * @inheritDoc
11
+ */
12
+ public function getEventStrings() :array {
13
+ return [
14
+ 'lic_check_success' => [
15
+ 'name' => __( 'License Check Success', 'wp-simple-firewall' ),
16
+ 'audit' => [
17
+ __( 'License check succeeded.', 'wp-simple-firewall' ),
18
+ ],
19
+ ],
20
+ 'lic_fail_email' => [
21
+ 'name' => __( 'License Failure Email Sent', 'wp-simple-firewall' ),
22
+ 'audit' => [
23
+ __( 'License check failed. Sending Warning Email.', 'wp-simple-firewall' ),
24
+ ],
25
+ ],
26
+ 'lic_fail_deactivate' => [
27
+ 'name' => __( 'License Deactivated', 'wp-simple-firewall' ),
28
+ 'audit' => [
29
+ __( 'License check failed. Deactivating Pro.', 'wp-simple-firewall' ),
30
+ ],
31
+ ],
32
+ ];
33
+ }
34
+
35
  /**
36
  * @inheritDoc
37
  */
61
  'last_errors' => __( 'Error', 'wp-simple-firewall' ),
62
  ];
63
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  }
src/lib/src/Modules/Lockdown/Processor.php CHANGED
@@ -112,9 +112,9 @@ class Processor extends BaseShield\Processor {
112
  $mod = $this->getMod();
113
  $oWpRest = Services::Rest();
114
 
115
- $sNamespace = $oWpRest->getNamespace();
116
- if ( !empty( $sNamespace ) && $mStatus !== true && !is_wp_error( $mStatus )
117
- && !$mod->isPermittedAnonRestApiNamespace( $sNamespace ) ) {
118
 
119
  $mStatus = new \WP_Error(
120
  'shield_block_anon_restapi',
@@ -125,7 +125,7 @@ class Processor extends BaseShield\Processor {
125
  $this->getCon()
126
  ->fireEvent(
127
  'block_anonymous_restapi',
128
- [ 'audit' => [ 'namespace' => $sNamespace ] ]
129
  );
130
  }
131
 
112
  $mod = $this->getMod();
113
  $oWpRest = Services::Rest();
114
 
115
+ $namespace = $oWpRest->getNamespace();
116
+ if ( !empty( $namespace ) && $mStatus !== true && !is_wp_error( $mStatus )
117
+ && !$mod->isPermittedAnonRestApiNamespace( $namespace ) ) {
118
 
119
  $mStatus = new \WP_Error(
120
  'shield_block_anon_restapi',
125
  $this->getCon()
126
  ->fireEvent(
127
  'block_anonymous_restapi',
128
+ [ 'audit_params' => [ 'namespace' => $namespace ] ]
129
  );
130
  }
131
 
src/lib/src/Modules/Lockdown/Strings.php CHANGED
@@ -7,12 +7,21 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
7
  class Strings extends Base\Strings {
8
 
9
  /**
10
- * @return string[][]
11
  */
12
- protected function getAuditMessages() :array {
13
  return [
14
  'block_anonymous_restapi' => [
15
- __( 'Blocked Anonymous API Access through "%s" namespace', 'wp-simple-firewall' )
 
 
 
 
 
 
 
 
 
16
  ],
17
  ];
18
  }
@@ -27,40 +36,40 @@ class Strings extends Base\Strings {
27
  switch ( $section ) {
28
 
29
  case 'section_enable_plugin_feature_wordpress_lockdown' :
30
- $sTitleShort = sprintf( '%s/%s', __( 'On', 'wp-simple-firewall' ), __( 'Off', 'wp-simple-firewall' ) );
31
- $sTitle = sprintf( __( 'Enable Module: %s', 'wp-simple-firewall' ), $this->getMod()
32
- ->getMainFeatureName() );
33
- $aSummary = [
34
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Lockdown helps secure-up certain loosely-controlled WordPress settings on your site.', 'wp-simple-firewall' ) ),
35
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), sprintf( __( 'Keep the %s feature turned on.', 'wp-simple-firewall' ), __( 'Lockdown', 'wp-simple-firewall' ) ) )
36
  ];
37
  break;
38
 
39
  case 'section_apixml' :
40
- $sTitle = __( 'API & XML-RPC', 'wp-simple-firewall' );
41
- $aSummary = [
42
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Lockdown certain core WordPress system features.', 'wp-simple-firewall' ) ),
43
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'This depends on your usage and needs for certain WordPress functions and features.', 'wp-simple-firewall' ) )
44
  ];
45
- $sTitleShort = __( 'API & XML-RPC', 'wp-simple-firewall' );
46
  break;
47
 
48
  case 'section_permission_access_options' :
49
- $sTitle = __( 'Permissions and Access Options', 'wp-simple-firewall' );
50
- $aSummary = [
51
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Provides finer control of certain WordPress permissions.', 'wp-simple-firewall' ) ),
52
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Only enable SSL if you have a valid certificate installed.', 'wp-simple-firewall' ) )
53
  ];
54
- $sTitleShort = __( 'Permissions', 'wp-simple-firewall' );
55
  break;
56
 
57
  case 'section_wordpress_obscurity_options' :
58
- $sTitle = __( 'WordPress Obscurity Options', 'wp-simple-firewall' );
59
- $aSummary = [
60
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Obscures certain WordPress settings from public view.', 'wp-simple-firewall' ) ),
61
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Obscurity is not true security and so these settings are down to your personal tastes.', 'wp-simple-firewall' ) )
62
  ];
63
- $sTitleShort = __( 'Obscurity', 'wp-simple-firewall' );
64
  break;
65
 
66
  default:
@@ -68,9 +77,9 @@ class Strings extends Base\Strings {
68
  }
69
 
70
  return [
71
- 'title' => $sTitle,
72
- 'title_short' => $sTitleShort,
73
- 'summary' => ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : [],
74
  ];
75
  }
76
 
@@ -86,56 +95,56 @@ class Strings extends Base\Strings {
86
  switch ( $key ) {
87
 
88
  case 'enable_lockdown' :
89
- $sName = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $sModName );
90
- $sSummary = sprintf( __( 'Enable (or Disable) The %s Module', 'wp-simple-firewall' ), $sModName );
91
- $sDescription = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), $sModName );
92
  break;
93
 
94
  case 'disable_xmlrpc' :
95
- $sName = sprintf( __( 'Disable %s', 'wp-simple-firewall' ), 'XML-RPC' );
96
- $sSummary = sprintf( __( 'Disable The %s System', 'wp-simple-firewall' ), 'XML-RPC' );
97
- $sDescription = sprintf( __( 'Checking this option will completely turn off the whole %s system.', 'wp-simple-firewall' ), 'XML-RPC' );
98
  break;
99
 
100
  case 'disable_anonymous_restapi' :
101
- $sName = __( 'Anonymous Rest API', 'wp-simple-firewall' );
102
- $sSummary = sprintf( __( 'Disable The %s System', 'wp-simple-firewall' ), __( 'Anonymous Rest API', 'wp-simple-firewall' ) );
103
- $sDescription = [
104
  __( 'You can choose to completely disable anonymous access to the REST API.', 'wp-simple-firewall' ),
105
  sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( 'Enabling this option may break plugins that use the REST API for your site visitors.', 'wp-simple-firewall' ) )
106
  ];
107
  break;
108
 
109
  case 'api_namespace_exclusions' :
110
- $sName = __( 'Rest API Exclusions', 'wp-simple-firewall' );
111
- $sSummary = __( 'Anonymous REST API Exclusions', 'wp-simple-firewall' );
112
- $sDescription = __( 'Any namespaces provided here will be excluded from the Anonymous API restriction.', 'wp-simple-firewall' );
113
  break;
114
 
115
  case 'disable_file_editing' :
116
- $sName = __( 'Disable File Editing', 'wp-simple-firewall' );
117
- $sSummary = __( 'Disable Ability To Edit Files From Within WordPress', 'wp-simple-firewall' );
118
- $sDescription = __( 'Removes the option to directly edit any files from within the WordPress admin area.', 'wp-simple-firewall' )
119
  .'<br />'.__( 'Equivalent to setting "DISALLOW_FILE_EDIT" to TRUE.', 'wp-simple-firewall' );
120
  break;
121
 
122
  case 'force_ssl_admin' :
123
- $sName = __( 'Force SSL Admin', 'wp-simple-firewall' );
124
- $sSummary = __( 'Forces WordPress Admin Dashboard To Be Delivered Over SSL', 'wp-simple-firewall' );
125
- $sDescription = __( 'Please only enable this option if you have a valid SSL certificate installed.', 'wp-simple-firewall' )
126
  .'<br />'.__( 'Equivalent to setting "FORCE_SSL_ADMIN" to TRUE.', 'wp-simple-firewall' );
127
  break;
128
 
129
  case 'hide_wordpress_generator_tag' :
130
- $sName = __( 'WP Generator Tag', 'wp-simple-firewall' );
131
- $sSummary = __( 'Remove WP Generator Meta Tag', 'wp-simple-firewall' );
132
- $sDescription = __( 'Remove a meta tag from your WordPress pages that publicly displays that your site is WordPress and its current version.', 'wp-simple-firewall' );
133
  break;
134
 
135
  case 'clean_wp_rubbish' :
136
- $sName = __( 'Clean WP Files', 'wp-simple-firewall' );
137
- $sSummary = __( 'Automatically Delete Unnecessary WP Files', 'wp-simple-firewall' );
138
- $sDescription = [
139
  __( "Automatically delete WordPress files that aren't necessary.", 'wp-simple-firewall' ),
140
  __( "The cleanup process runs once each day.", 'wp-simple-firewall' ),
141
  sprintf( '%s: <code>%s</code>', __( 'Files Deleted', 'wp-simple-firewall' ),
@@ -144,9 +153,9 @@ class Strings extends Base\Strings {
144
  break;
145
 
146
  case 'block_author_discovery' :
147
- $sName = __( 'Block Username Fishing', 'wp-simple-firewall' );
148
- $sSummary = __( 'Block the ability to discover WordPress usernames based on author IDs', 'wp-simple-firewall' );
149
- $sDescription = sprintf( __( 'When enabled, any URL requests containing "%s" will be killed.', 'wp-simple-firewall' ), 'author=' )
150
  .'<br />'.sprintf( '%s - %s', __( 'Warning', 'wp-simple-firewall' ), __( 'Enabling this option may interfere with expected operations of your site.', 'wp-simple-firewall' ) );
151
  break;
152
 
@@ -155,9 +164,9 @@ class Strings extends Base\Strings {
155
  }
156
 
157
  return [
158
- 'name' => $sName,
159
- 'summary' => $sSummary,
160
- 'description' => $sDescription,
161
  ];
162
  }
163
  }
7
  class Strings extends Base\Strings {
8
 
9
  /**
10
+ * @inheritDoc
11
  */
12
+ public function getEventStrings() :array {
13
  return [
14
  'block_anonymous_restapi' => [
15
+ 'name' => sprintf( '%s: %s', __( 'Blocked', 'wp-simple-firewall' ), __( 'Anonymous REST API' ) ),
16
+ 'audit' => [
17
+ __( 'Blocked Anonymous API Access through "{{namespace}}" namespace.', 'wp-simple-firewall' ),
18
+ ],
19
+ ],
20
+ 'block_xml' => [
21
+ 'name' => sprintf( '%s: %s', __( 'Blocked', 'wp-simple-firewall' ), __( 'XML-RPC' ) ),
22
+ 'audit' => [
23
+ __( 'XML-RPC Request Blocked.', 'wp-simple-firewall' ),
24
+ ],
25
  ],
26
  ];
27
  }
36
  switch ( $section ) {
37
 
38
  case 'section_enable_plugin_feature_wordpress_lockdown' :
39
+ $titleShort = sprintf( '%s/%s', __( 'On', 'wp-simple-firewall' ), __( 'Off', 'wp-simple-firewall' ) );
40
+ $title = sprintf( __( 'Enable Module: %s', 'wp-simple-firewall' ), $this->getMod()
41
+ ->getMainFeatureName() );
42
+ $summary = [
43
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Lockdown helps secure-up certain loosely-controlled WordPress settings on your site.', 'wp-simple-firewall' ) ),
44
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), sprintf( __( 'Keep the %s feature turned on.', 'wp-simple-firewall' ), __( 'Lockdown', 'wp-simple-firewall' ) ) )
45
  ];
46
  break;
47
 
48
  case 'section_apixml' :
49
+ $title = __( 'API & XML-RPC', 'wp-simple-firewall' );
50
+ $summary = [
51
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Lockdown certain core WordPress system features.', 'wp-simple-firewall' ) ),
52
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'This depends on your usage and needs for certain WordPress functions and features.', 'wp-simple-firewall' ) )
53
  ];
54
+ $titleShort = __( 'API & XML-RPC', 'wp-simple-firewall' );
55
  break;
56
 
57
  case 'section_permission_access_options' :
58
+ $title = __( 'Permissions and Access Options', 'wp-simple-firewall' );
59
+ $summary = [
60
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Provides finer control of certain WordPress permissions.', 'wp-simple-firewall' ) ),
61
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Only enable SSL if you have a valid certificate installed.', 'wp-simple-firewall' ) )
62
  ];
63
+ $titleShort = __( 'Permissions', 'wp-simple-firewall' );
64
  break;
65
 
66
  case 'section_wordpress_obscurity_options' :
67
+ $title = __( 'WordPress Obscurity Options', 'wp-simple-firewall' );
68
+ $summary = [
69
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Obscures certain WordPress settings from public view.', 'wp-simple-firewall' ) ),
70
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Obscurity is not true security and so these settings are down to your personal tastes.', 'wp-simple-firewall' ) )
71
  ];
72
+ $titleShort = __( 'Obscurity', 'wp-simple-firewall' );
73
  break;
74
 
75
  default:
77
  }
78
 
79
  return [
80
+ 'title' => $title,
81
+ 'title_short' => $titleShort,
82
+ 'summary' => $summary,
83
  ];
84
  }
85
 
95
  switch ( $key ) {
96
 
97
  case 'enable_lockdown' :
98
+ $name = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $sModName );
99
+ $summary = sprintf( __( 'Enable (or Disable) The %s Module', 'wp-simple-firewall' ), $sModName );
100
+ $description = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), $sModName );
101
  break;
102
 
103
  case 'disable_xmlrpc' :
104
+ $name = sprintf( __( 'Disable %s', 'wp-simple-firewall' ), 'XML-RPC' );
105
+ $summary = sprintf( __( 'Disable The %s System', 'wp-simple-firewall' ), 'XML-RPC' );
106
+ $description = sprintf( __( 'Checking this option will completely turn off the whole %s system.', 'wp-simple-firewall' ), 'XML-RPC' );
107
  break;
108
 
109
  case 'disable_anonymous_restapi' :
110
+ $name = __( 'Anonymous Rest API', 'wp-simple-firewall' );
111
+ $summary = sprintf( __( 'Disable The %s System', 'wp-simple-firewall' ), __( 'Anonymous Rest API', 'wp-simple-firewall' ) );
112
+ $description = [
113
  __( 'You can choose to completely disable anonymous access to the REST API.', 'wp-simple-firewall' ),
114
  sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( 'Enabling this option may break plugins that use the REST API for your site visitors.', 'wp-simple-firewall' ) )
115
  ];
116
  break;
117
 
118
  case 'api_namespace_exclusions' :
119
+ $name = __( 'Rest API Exclusions', 'wp-simple-firewall' );
120
+ $summary = __( 'Anonymous REST API Exclusions', 'wp-simple-firewall' );
121
+ $description = __( 'Any namespaces provided here will be excluded from the Anonymous API restriction.', 'wp-simple-firewall' );
122
  break;
123
 
124
  case 'disable_file_editing' :
125
+ $name = __( 'Disable File Editing', 'wp-simple-firewall' );
126
+ $summary = __( 'Disable Ability To Edit Files From Within WordPress', 'wp-simple-firewall' );
127
+ $description = __( 'Removes the option to directly edit any files from within the WordPress admin area.', 'wp-simple-firewall' )
128
  .'<br />'.__( 'Equivalent to setting "DISALLOW_FILE_EDIT" to TRUE.', 'wp-simple-firewall' );
129
  break;
130
 
131
  case 'force_ssl_admin' :
132
+ $name = __( 'Force SSL Admin', 'wp-simple-firewall' );
133
+ $summary = __( 'Forces WordPress Admin Dashboard To Be Delivered Over SSL', 'wp-simple-firewall' );
134
+ $description = __( 'Please only enable this option if you have a valid SSL certificate installed.', 'wp-simple-firewall' )
135
  .'<br />'.__( 'Equivalent to setting "FORCE_SSL_ADMIN" to TRUE.', 'wp-simple-firewall' );
136
  break;
137
 
138
  case 'hide_wordpress_generator_tag' :
139
+ $name = __( 'WP Generator Tag', 'wp-simple-firewall' );
140
+ $summary = __( 'Remove WP Generator Meta Tag', 'wp-simple-firewall' );
141
+ $description = __( 'Remove a meta tag from your WordPress pages that publicly displays that your site is WordPress and its current version.', 'wp-simple-firewall' );
142
  break;
143
 
144
  case 'clean_wp_rubbish' :
145
+ $name = __( 'Clean WP Files', 'wp-simple-firewall' );
146
+ $summary = __( 'Automatically Delete Unnecessary WP Files', 'wp-simple-firewall' );
147
+ $description = [
148
  __( "Automatically delete WordPress files that aren't necessary.", 'wp-simple-firewall' ),
149
  __( "The cleanup process runs once each day.", 'wp-simple-firewall' ),
150
  sprintf( '%s: <code>%s</code>', __( 'Files Deleted', 'wp-simple-firewall' ),
153
  break;
154
 
155
  case 'block_author_discovery' :
156
+ $name = __( 'Block Username Fishing', 'wp-simple-firewall' );
157
+ $summary = __( 'Block the ability to discover WordPress usernames based on author IDs', 'wp-simple-firewall' );
158
+ $description = sprintf( __( 'When enabled, any URL requests containing "%s" will be killed.', 'wp-simple-firewall' ), 'author=' )
159
  .'<br />'.sprintf( '%s - %s', __( 'Warning', 'wp-simple-firewall' ), __( 'Enabling this option may interfere with expected operations of your site.', 'wp-simple-firewall' ) );
160
  break;
161
 
164
  }
165
 
166
  return [
167
+ 'name' => $name,
168
+ 'summary' => $summary,
169
+ 'description' => $description,
170
  ];
171
  }
172
  }
src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GaspJs.php CHANGED
@@ -80,7 +80,7 @@ class GaspJs extends BaseProtectionProvider {
80
  $this->getCon()->fireEvent(
81
  'botbox_fail',
82
  [
83
- 'audit' => [
84
  'user_login' => $username,
85
  'action' => $action,
86
  ]
@@ -92,7 +92,7 @@ class GaspJs extends BaseProtectionProvider {
92
  $this->getCon()->fireEvent(
93
  'honeypot_fail',
94
  [
95
- 'audit' => [
96
  'user_login' => $username,
97
  'action' => $action,
98
  ]
80
  $this->getCon()->fireEvent(
81
  'botbox_fail',
82
  [
83
+ 'audit_params' => [
84
  'user_login' => $username,
85
  'action' => $action,
86
  ]
92
  $this->getCon()->fireEvent(
93
  'honeypot_fail',
94
  [
95
+ 'audit_params' => [
96
  'user_login' => $username,
97
  'action' => $action,
98
  ]
src/lib/src/Modules/LoginGuard/Lib/Rename/RenameLogin.php CHANGED
@@ -46,13 +46,13 @@ class RenameLogin {
46
  add_action( 'login_init', [ $this, 'aLoginFormAction' ], 0 );
47
 
48
  // ensure that wp-login.php is never used in site urls or redirects
49
- add_filter( 'site_url', [ $this, 'fCheckForLoginPhp' ], 20, 1 );
50
- add_filter( 'network_site_url', [ $this, 'fCheckForLoginPhp' ], 20, 1 );
51
- add_filter( 'wp_redirect', [ $this, 'fCheckForLoginPhp' ], 20, 1 );
52
  if ( !Services::WpUsers()->isUserLoggedIn() ) {
53
- add_filter( 'wp_redirect', [ $this, 'fProtectUnauthorizedLoginRedirect' ], 50, 1 );
54
  }
55
- add_filter( 'register_url', [ $this, 'blockRegisterUrlRedirect' ], 20, 1 );
56
 
57
  add_filter( 'et_anticipate_exceptions', [ $this, 'fAddToEtMaintenanceExceptions' ] );
58
  }
@@ -63,42 +63,41 @@ class RenameLogin {
63
  /** @var LoginGuard\Options $opts */
64
  $opts = $this->getOptions();
65
 
66
- $sMessage = '';
67
- $bConflicted = false;
68
 
69
  $path = $opts->getCustomLoginPath();
70
 
71
  $WP = Services::WpGeneral();
72
  if ( $WP->isMultisite() ) {
73
- $sMessage = __( 'Your login URL is unchanged because the Rename WP Login feature is not currently supported on WPMS.', 'wp-simple-firewall' );
74
- $bConflicted = true;
75
  }
76
  elseif ( class_exists( 'Rename_WP_Login' ) ) {
77
- $sMessage = sprintf( __( 'Can not use the Rename WP Login feature because you have the "%s" plugin installed and it is active.', 'wp-simple-firewall' ), 'Rename WP Login' );
78
- $bConflicted = true;
79
  }
80
  elseif ( class_exists( 'Theme_My_Login' ) ) {
81
- $sMessage = sprintf( __( 'Can not use the Rename WP Login feature because you have the "%s" plugin installed and it is active.', 'wp-simple-firewall' ), 'Theme My Login' );
82
- $bConflicted = true;
83
  }
84
  elseif ( !$WP->isPermalinksEnabled() ) {
85
- $sMessage = sprintf( __( 'Can not use the Rename WP Login feature because you have not enabled %s.', 'wp-simple-firewall' ), __( 'Permalinks' ) );
86
- $bConflicted = true;
87
  }
88
  elseif ( $WP->isPermalinksEnabled() && ( $WP->getDoesWpSlugExist( $path ) || in_array( $path, $WP->getAutoRedirectLocations() ) ) ) {
89
- $sMessage = sprintf( __( 'Can not use the Rename WP Login feature because you have chosen a path ("%s") that is reserved on your WordPress site.', 'wp-simple-firewall' ), $path );
90
- $bConflicted = true;
91
  }
92
 
93
- if ( $bConflicted ) {
94
- $sNoticeMessage = sprintf( '<strong>%s</strong>: %s',
95
  __( 'Warning', 'wp-simple-firewall' ),
96
- $sMessage
97
- );
98
- $mod->setFlashAdminNotice( $sNoticeMessage, true );
99
  }
100
 
101
- return $bConflicted;
102
  }
103
 
104
  private function hasUnsupportedConfiguration() :bool {
@@ -124,11 +123,11 @@ class RenameLogin {
124
  public function doBlockPossibleWpLoginLoad() {
125
 
126
  // To begin, we block if it's an access to the admin area and the user isn't logged in (and it's not ajax)
127
- $bDoBlock = is_admin() && !Services::WpGeneral()->isAjax()
128
- && !Services::WpUsers()->isUserLoggedIn();
129
 
130
  // Next block option is where it's a direct attempt to access the old login URL
131
- if ( !$bDoBlock ) {
132
  $path = trim( Services::Request()->getPath(), '/' );
133
  $possible = [
134
  trim( home_url( 'wp-login.php', 'relative' ), '/' ),
@@ -139,53 +138,54 @@ class RenameLogin {
139
  trim( home_url( 'login', 'relative' ), '/' ),
140
  trim( site_url( 'login', 'relative' ), '/' )
141
  ];
142
- $bDoBlock = !empty( $path )
143
- && ( in_array( $path, $possible ) || preg_match( '/wp-login\.php/i', $path ) );
144
  }
145
 
146
- if ( $bDoBlock ) {
147
  $this->doWpLoginFailedRedirect404();
148
  }
149
  }
150
 
151
  /**
152
- * @param string $sLocation
153
  * @return string
154
  */
155
- public function fCheckForLoginPhp( $sLocation ) {
156
  /** @var LoginGuard\Options $opts */
157
  $opts = $this->getOptions();
158
 
159
- $sRedirectPath = parse_url( $sLocation, PHP_URL_PATH );
160
- if ( strpos( $sRedirectPath, 'wp-login.php' ) !== false ) {
161
 
162
- $sLoginUrl = home_url( $opts->getCustomLoginPath() );
163
- $aQueryArgs = explode( '?', $sLocation );
164
- if ( !empty( $aQueryArgs[ 1 ] ) ) {
165
- parse_str( $aQueryArgs[ 1 ], $aNewQueryArgs );
166
- $sLoginUrl = add_query_arg( $aNewQueryArgs, $sLoginUrl );
167
  }
168
- return $sLoginUrl;
169
  }
170
- return $sLocation;
 
171
  }
172
 
173
  /**
174
- * @param string $sLocation
175
  * @return string
176
  */
177
- public function fProtectUnauthorizedLoginRedirect( $sLocation ) {
178
  /** @var LoginGuard\Options $opts */
179
  $opts = $this->getOptions();
180
 
181
  if ( !Services::WpGeneral()->isLoginUrl() ) {
182
- $sRedirectPath = trim( parse_url( $sLocation, PHP_URL_PATH ), '/' );
183
  $bRedirectIsHiddenUrl = ( $sRedirectPath == $opts->getCustomLoginPath() );
184
  if ( $bRedirectIsHiddenUrl && !Services::WpUsers()->isUserLoggedIn() ) {
185
  $this->doWpLoginFailedRedirect404();
186
  }
187
  }
188
- return $sLocation;
189
  }
190
 
191
  /**
@@ -202,6 +202,8 @@ class RenameLogin {
202
 
203
  public function aLoadWpLogin() {
204
  if ( Services::WpGeneral()->isLoginUrl() ) {
 
 
205
  @require_once( ABSPATH.'wp-login.php' );
206
  die();
207
  }
46
  add_action( 'login_init', [ $this, 'aLoginFormAction' ], 0 );
47
 
48
  // ensure that wp-login.php is never used in site urls or redirects
49
+ add_filter( 'site_url', [ $this, 'fCheckForLoginPhp' ], 20 );
50
+ add_filter( 'network_site_url', [ $this, 'fCheckForLoginPhp' ], 20 );
51
+ add_filter( 'wp_redirect', [ $this, 'fCheckForLoginPhp' ], 20 );
52
  if ( !Services::WpUsers()->isUserLoggedIn() ) {
53
+ add_filter( 'wp_redirect', [ $this, 'fProtectUnauthorizedLoginRedirect' ], 50 );
54
  }
55
+ add_filter( 'register_url', [ $this, 'blockRegisterUrlRedirect' ], 20 );
56
 
57
  add_filter( 'et_anticipate_exceptions', [ $this, 'fAddToEtMaintenanceExceptions' ] );
58
  }
63
  /** @var LoginGuard\Options $opts */
64
  $opts = $this->getOptions();
65
 
66
+ $msg = '';
67
+ $isConflicted = false;
68
 
69
  $path = $opts->getCustomLoginPath();
70
 
71
  $WP = Services::WpGeneral();
72
  if ( $WP->isMultisite() ) {
73
+ $msg = __( 'Your login URL is unchanged because the Rename WP Login feature is not currently supported on WPMS.', 'wp-simple-firewall' );
74
+ $isConflicted = true;
75
  }
76
  elseif ( class_exists( 'Rename_WP_Login' ) ) {
77
+ $msg = sprintf( __( 'Can not use the Rename WP Login feature because you have the "%s" plugin installed and it is active.', 'wp-simple-firewall' ), 'Rename WP Login' );
78
+ $isConflicted = true;
79
  }
80
  elseif ( class_exists( 'Theme_My_Login' ) ) {
81
+ $msg = sprintf( __( 'Can not use the Rename WP Login feature because you have the "%s" plugin installed and it is active.', 'wp-simple-firewall' ), 'Theme My Login' );
82
+ $isConflicted = true;
83
  }
84
  elseif ( !$WP->isPermalinksEnabled() ) {
85
+ $msg = sprintf( __( 'Can not use the Rename WP Login feature because you have not enabled %s.', 'wp-simple-firewall' ), __( 'Permalinks' ) );
86
+ $isConflicted = true;
87
  }
88
  elseif ( $WP->isPermalinksEnabled() && ( $WP->getDoesWpSlugExist( $path ) || in_array( $path, $WP->getAutoRedirectLocations() ) ) ) {
89
+ $msg = sprintf( __( 'Can not use the Rename WP Login feature because you have chosen a path ("%s") that is already used on your WordPress site.', 'wp-simple-firewall' ), $path );
90
+ $isConflicted = true;
91
  }
92
 
93
+ if ( $isConflicted ) {
94
+ $mod->setFlashAdminNotice( sprintf( '<strong>%s</strong>: %s',
95
  __( 'Warning', 'wp-simple-firewall' ),
96
+ $msg
97
+ ), true );
 
98
  }
99
 
100
+ return $isConflicted;
101
  }
102
 
103
  private function hasUnsupportedConfiguration() :bool {
123
  public function doBlockPossibleWpLoginLoad() {
124
 
125
  // To begin, we block if it's an access to the admin area and the user isn't logged in (and it's not ajax)
126
+ $doBlock = is_admin() && !Services::WpGeneral()->isAjax()
127
+ && !Services::WpUsers()->isUserLoggedIn();
128
 
129
  // Next block option is where it's a direct attempt to access the old login URL
130
+ if ( !$doBlock ) {
131
  $path = trim( Services::Request()->getPath(), '/' );
132
  $possible = [
133
  trim( home_url( 'wp-login.php', 'relative' ), '/' ),
138
  trim( home_url( 'login', 'relative' ), '/' ),
139
  trim( site_url( 'login', 'relative' ), '/' )
140
  ];
141
+ $doBlock = !empty( $path )
142
+ && ( in_array( $path, $possible ) || preg_match( '/wp-login\.php/i', $path ) );
143
  }
144
 
145
+ if ( $doBlock ) {
146
  $this->doWpLoginFailedRedirect404();
147
  }
148
  }
149
 
150
  /**
151
+ * @param string $location
152
  * @return string
153
  */
154
+ public function fCheckForLoginPhp( $location ) {
155
  /** @var LoginGuard\Options $opts */
156
  $opts = $this->getOptions();
157
 
158
+ $redirectPath = parse_url( $location, PHP_URL_PATH );
159
+ if ( strpos( $redirectPath, 'wp-login.php' ) !== false ) {
160
 
161
+ $loginUrl = home_url( $opts->getCustomLoginPath() );
162
+ $queryArgs = explode( '?', $location );
163
+ if ( !empty( $queryArgs[ 1 ] ) ) {
164
+ parse_str( $queryArgs[ 1 ], $newQueryArgs );
165
+ $loginUrl = add_query_arg( $newQueryArgs, $loginUrl );
166
  }
167
+ $location = $loginUrl;
168
  }
169
+
170
+ return $location;
171
  }
172
 
173
  /**
174
+ * @param string $location
175
  * @return string
176
  */
177
+ public function fProtectUnauthorizedLoginRedirect( $location ) {
178
  /** @var LoginGuard\Options $opts */
179
  $opts = $this->getOptions();
180
 
181
  if ( !Services::WpGeneral()->isLoginUrl() ) {
182
+ $sRedirectPath = trim( parse_url( $location, PHP_URL_PATH ), '/' );
183
  $bRedirectIsHiddenUrl = ( $sRedirectPath == $opts->getCustomLoginPath() );
184
  if ( $bRedirectIsHiddenUrl && !Services::WpUsers()->isUserLoggedIn() ) {
185
  $this->doWpLoginFailedRedirect404();
186
  }
187
  }
188
+ return $location;
189
  }
190
 
191
  /**
202
 
203
  public function aLoadWpLogin() {
204
  if ( Services::WpGeneral()->isLoginUrl() ) {
205
+ // To prevent PHP warnings about undefined vars
206
+ $user_login = $error = '';
207
  @require_once( ABSPATH.'wp-login.php' );
208
  die();
209
  }
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BackupCodes.php CHANGED
@@ -91,22 +91,6 @@ class BackupCodes extends BaseProvider {
91
  return (bool)wp_check_password( str_replace( '-', '', $sOtpCode ), $this->getSecret( $user ) );
92
  }
93
 
94
- /**
95
- * @param \WP_User $user
96
- * @param bool $bIsSuccess
97
- */
98
- protected function auditLogin( \WP_User $user, bool $bIsSuccess ) {
99
- $this->getCon()->fireEvent(
100
- $bIsSuccess ? '2fa_backupcode_verified' : '2fa_backupcode_fail',
101
- [
102
- 'audit' => [
103
- 'user_login' => $user->user_login,
104
- 'method' => 'Backup Code',
105
- ]
106
- ]
107
- );
108
- }
109
-
110
  /**
111
  * @param \WP_User $user
112
  * @return string
91
  return (bool)wp_check_password( str_replace( '-', '', $sOtpCode ), $this->getSecret( $user ) );
92
  }
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  /**
95
  * @param \WP_User $user
96
  * @return string
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BaseProvider.php CHANGED
@@ -220,7 +220,17 @@ abstract class BaseProvider {
220
  return [];
221
  }
222
 
223
- abstract protected function auditLogin( \WP_User $user, bool $bIsSuccess );
 
 
 
 
 
 
 
 
 
 
224
 
225
  /**
226
  * @param \WP_User $user
220
  return [];
221
  }
222
 
223
+ protected function auditLogin( \WP_User $user, bool $success ) {
224
+ $this->getCon()->fireEvent(
225
+ $success ? '2fa_verify_success' : '2fa_verify_fail',
226
+ [
227
+ 'audit_params' => [
228
+ 'user_login' => $user->user_login,
229
+ 'method' => $this->getProviderName(),
230
+ ]
231
+ ]
232
+ );
233
+ }
234
 
235
  /**
236
  * @param \WP_User $user
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Email.php CHANGED
@@ -24,22 +24,6 @@ class Email extends BaseProvider {
24
  ];
25
  }
26
 
27
- /**
28
- * @param \WP_User $user
29
- * @param bool $bIsSuccess
30
- */
31
- protected function auditLogin( \WP_User $user, bool $bIsSuccess ) {
32
- $this->getCon()->fireEvent(
33
- $bIsSuccess ? 'email_verified' : 'email_fail',
34
- [
35
- 'audit' => [
36
- 'user_login' => $user->user_login,
37
- 'method' => 'Email',
38
- ]
39
- ]
40
- );
41
- }
42
-
43
  /**
44
  * @param \WP_User $user
45
  * @return $this
@@ -179,15 +163,6 @@ class Email extends BaseProvider {
179
  $sendSuccess = false;
180
  }
181
 
182
- $this->getCon()->fireEvent(
183
- $sendSuccess ? '2fa_email_send_success' : '2fa_email_send_fail',
184
- [
185
- 'audit' => [
186
- 'user_login' => $user->user_login,
187
- ]
188
- ]
189
- );
190
-
191
  return $this;
192
  }
193
 
24
  ];
25
  }
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  /**
28
  * @param \WP_User $user
29
  * @return $this
163
  $sendSuccess = false;
164
  }
165
 
 
 
 
 
 
 
 
 
 
166
  return $this;
167
  }
168
 
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/GoogleAuth.php CHANGED
@@ -212,22 +212,6 @@ class GoogleAuth extends BaseProvider {
212
  return $valid;
213
  }
214
 
215
- /**
216
- * @param \WP_User $user
217
- * @param bool $bIsSuccess
218
- */
219
- protected function auditLogin( \WP_User $user, bool $bIsSuccess ) {
220
- $this->getCon()->fireEvent(
221
- $bIsSuccess ? 'googleauth_verified' : 'googleauth_fail',
222
- [
223
- 'audit' => [
224
- 'user_login' => $user->user_login,
225
- 'method' => 'Google Authenticator',
226
- ]
227
- ]
228
- );
229
- }
230
-
231
  /**
232
  * @param \WP_User $user
233
  * @return string
212
  return $valid;
213
  }
214
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  /**
216
  * @param \WP_User $user
217
  * @return string
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/U2F.php CHANGED
@@ -309,22 +309,6 @@ class U2F extends BaseProvider {
309
  return !empty( $oRegistration );
310
  }
311
 
312
- /**
313
- * @param \WP_User $user
314
- * @param bool $bIsSuccess
315
- */
316
- protected function auditLogin( \WP_User $user, bool $bIsSuccess ) {
317
- $this->getCon()->fireEvent(
318
- $bIsSuccess ? '2fa_u2f_verified' : '2fa_u2f_fail',
319
- [
320
- 'audit' => [
321
- 'user_login' => $user->user_login,
322
- 'method' => 'U2F',
323
- ]
324
- ]
325
- );
326
- }
327
-
328
  public function isProviderEnabled() :bool {
329
  /** @var LoginGuard\Options $opts */
330
  $opts = $this->getOptions();
309
  return !empty( $oRegistration );
310
  }
311
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  public function isProviderEnabled() :bool {
313
  /** @var LoginGuard\Options $opts */
314
  $opts = $this->getOptions();
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Yubikey.php CHANGED
@@ -144,7 +144,7 @@ class Yubikey extends BaseProvider {
144
  if ( preg_match( '#^[a-z]{44}$#', $otp ) ) {
145
  $parts = [
146
  'otp' => $otp,
147
- 'nonce' => md5( uniqid( $this->getCon()->getUniqueRequestId() ) ),
148
  'id' => $opts->getYubikeyAppId()
149
  ];
150
 
@@ -216,22 +216,6 @@ class Yubikey extends BaseProvider {
216
  return $this->setSecret( $user, implode( ',', array_unique( array_filter( $IDs ) ) ) );
217
  }
218
 
219
- /**
220
- * @param \WP_User $user
221
- * @param bool $bIsSuccess
222
- */
223
- protected function auditLogin( \WP_User $user, bool $bIsSuccess ) {
224
- $this->getCon()->fireEvent(
225
- $bIsSuccess ? 'yubikey_verified' : 'yubikey_fail',
226
- [
227
- 'audit' => [
228
- 'user_login' => $user->user_login,
229
- 'method' => 'Yubikey',
230
- ]
231
- ]
232
- );
233
- }
234
-
235
  /**
236
  * @return array
237
  */
144
  if ( preg_match( '#^[a-z]{44}$#', $otp ) ) {
145
  $parts = [
146
  'otp' => $otp,
147
+ 'nonce' => wp_create_nonce( 'shield-yubikey-verify-'.$otp ),
148
  'id' => $opts->getYubikeyAppId()
149
  ];
150
 
216
  return $this->setSecret( $user, implode( ',', array_unique( array_filter( $IDs ) ) ) );
217
  }
218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  /**
220
  * @return array
221
  */
src/lib/src/Modules/LoginGuard/Strings.php CHANGED
@@ -7,48 +7,57 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
7
  class Strings extends Base\Strings {
8
 
9
  /**
10
- * @return string[][]
11
  */
12
- protected function getAuditMessages() :array {
13
  return [
14
- 'botbox_fail' => [
15
- __( 'User "%s" attempted "%s" but Bot checkbox was not found.', 'wp-simple-firewall' )
 
 
 
16
  ],
17
- 'cooldown_fail' => [
18
- __( 'Login/Register request triggered cooldown and was blocked.', 'wp-simple-firewall' )
 
 
 
19
  ],
20
- 'honeypot_fail' => [
21
- __( 'User "%s" attempted %s but they were caught by the honeypot.', 'wp-simple-firewall' )
 
 
 
22
  ],
23
- '2fa_backupcode_verified' => [
24
- __( 'User "%s" verified their identity using %s.', 'wp-simple-firewall' )
 
 
 
25
  ],
26
- '2fa_backupcode_fail' => [
27
- __( 'User "%s" failed to verify their identity using %s.', 'wp-simple-firewall' )
 
 
 
28
  ],
29
- '2fa_email_verified' => [
30
- __( 'User "%s" verified their identity using %s.', 'wp-simple-firewall' )
 
 
 
31
  ],
32
- '2fa_email_verify_fail' => [
33
- __( 'User "%s" failed to verify their identity using %s.', 'wp-simple-firewall' )
 
 
 
34
  ],
35
- '2fa_googleauth_verified' => [
36
- __( 'User "%s" verified their identity using %s.', 'wp-simple-firewall' )
37
- ],
38
- '2fa_googleauth_fail' => [
39
- __( 'User "%s" failed to verify their identity using %s.', 'wp-simple-firewall' )
40
- ],
41
- '2fa_yubikey_verified' => [
42
- __( 'User "%s" verified their identity using %s.', 'wp-simple-firewall' )
43
- ],
44
- '2fa_yubikey_fail' => [
45
- __( 'User "%s" failed to verify their identity using %s.', 'wp-simple-firewall' )
46
- ],
47
- '2fa_email_send_success' => [
48
- __( 'User "%s" sent two-factor authentication email to verify identity.', 'wp-simple-firewall' )
49
- ],
50
- '2fa_email_send_fail' => [
51
- __( 'Failed to send user "%s" two-factor authentication email.', 'wp-simple-firewall' )
52
  ],
53
  ];
54
  }
@@ -63,37 +72,37 @@ class Strings extends Base\Strings {
63
  switch ( $section ) {
64
 
65
  case 'section_enable_plugin_feature_login_protection' :
66
- $sTitle = sprintf( __( 'Enable Module: %s', 'wp-simple-firewall' ), $this->getMod()
67
- ->getMainFeatureName() );
68
- $sTitleShort = sprintf( '%s/%s', __( 'On', 'wp-simple-firewall' ), __( 'Off', 'wp-simple-firewall' ) );
69
- $aSummary = [
70
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Login Guard blocks all automated and brute force attempts to log in to your site.', 'wp-simple-firewall' ) ),
71
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), sprintf( __( 'Keep the %s feature turned on.', 'wp-simple-firewall' ), __( 'Login Guard', 'wp-simple-firewall' ) ) )
72
  ];
73
  break;
74
 
75
  case 'section_rename_wplogin' :
76
- $sTitle = __( 'Hide WordPress Login Page', 'wp-simple-firewall' );
77
- $sTitleShort = __( 'Hide Login', 'wp-simple-firewall' );
78
- $aSummary = [
79
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'To hide your wp-login.php page from brute force attacks and hacking attempts - if your login page cannot be found, no-one can login.', 'wp-simple-firewall' ) ),
80
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'This is not required for complete security and if your site has irregular or inconsistent configuration it may not work for you.', 'wp-simple-firewall' ) )
81
  ];
82
  break;
83
 
84
  case 'section_multifactor_authentication' :
85
- $sTitle = __( 'Multi-Factor Authentication', 'wp-simple-firewall' );
86
- $sTitleShort = __( 'Multi-Factor Auth', 'wp-simple-firewall' );
87
- $aSummary = [
88
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Verifies the identity of users who log in to your site - i.e. they are who they say they are.', 'wp-simple-firewall' ) ),
89
  __( 'You may combine multiple authentication factors for increased security.', 'wp-simple-firewall' )
90
  ];
91
  break;
92
 
93
  case 'section_2fa_email' :
94
- $sTitle = __( 'Email Two-Factor Authentication', 'wp-simple-firewall' );
95
- $sTitleShort = __( '2FA Email', 'wp-simple-firewall' );
96
- $aSummary = [
97
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Verifies the identity of users who log in to your site using email-based one-time-passwords.', 'wp-simple-firewall' ) ),
98
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ).' '.__( 'However, if your host blocks email sending you may lock yourself out.', 'wp-simple-firewall' ) ),
99
  sprintf( '%s: %s', __( 'Note', 'wp-simple-firewall' ), __( 'You may combine multiple authentication factors for increased security.', 'wp-simple-firewall' ) )
@@ -101,27 +110,27 @@ class Strings extends Base\Strings {
101
  break;
102
 
103
  case 'section_2fa_ga' :
104
- $sTitle = __( 'Google Authenticator Two-Factor Authentication', 'wp-simple-firewall' );
105
- $sTitleShort = __( 'Google Auth', 'wp-simple-firewall' );
106
- $aSummary = [
107
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Verifies the identity of users who log in to your site using Google Authenticator one-time-passwords.', 'wp-simple-firewall' ) ),
108
  sprintf( '%s: %s', __( 'Note', 'wp-simple-firewall' ), __( 'You may combine multiple authentication factors for increased security.', 'wp-simple-firewall' ) )
109
  ];
110
  break;
111
 
112
  case 'section_brute_force_login_protection' :
113
- $sTitle = __( 'Brute Force Login Protection', 'wp-simple-firewall' );
114
- $sTitleShort = __( 'Bots', 'wp-simple-firewall' );
115
- $aSummary = [
116
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Blocks brute force hacking attacks against your login and registration pages.', 'wp-simple-firewall' ) ),
117
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) )
118
  ];
119
  break;
120
 
121
  case 'section_hardware_authentication' :
122
- $sTitle = __( 'Hardware 2-Factor Authentication', 'wp-simple-firewall' );
123
- $sTitleShort = __( 'Hardware 2FA', 'wp-simple-firewall' );
124
- $aSummary = [
125
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Verifies the identity of users who log in to your site using Yubikey one-time-passwords.', 'wp-simple-firewall' ) ),
126
  sprintf( '%s: %s', __( 'Note', 'wp-simple-firewall' ), __( 'You may combine multiple authentication factors for increased security.', 'wp-simple-firewall' ) )
127
  ];
@@ -132,9 +141,9 @@ class Strings extends Base\Strings {
132
  }
133
 
134
  return [
135
- 'title' => $sTitle,
136
- 'title_short' => $sTitleShort,
137
- 'summary' => ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : [],
138
  ];
139
  }
140
 
7
  class Strings extends Base\Strings {
8
 
9
  /**
10
+ * @inheritDoc
11
  */
12
+ public function getEventStrings() :array {
13
  return [
14
+ 'botbox_fail' => [
15
+ 'name' => __( 'BotBox Fail', 'wp-simple-firewall' ),
16
+ 'audit' => [
17
+ __( 'User "{{user_login}}" attempted "{{action}}" but Bot checkbox was not found.', 'wp-simple-firewall' ),
18
+ ],
19
  ],
20
+ 'cooldown_fail' => [
21
+ 'name' => __( 'Cooldown Fail', 'wp-simple-firewall' ),
22
+ 'audit' => [
23
+ __( 'Login/Register request triggered cooldown and was blocked.', 'wp-simple-firewall' )
24
+ ],
25
  ],
26
+ 'honeypot_fail' => [
27
+ 'name' => __( 'Honeypot Fail', 'wp-simple-firewall' ),
28
+ 'audit' => [
29
+ __( 'User "{{user_login}}" attempted {{action}} but they were caught by the honeypot.', 'wp-simple-firewall' )
30
+ ],
31
  ],
32
+ '2fa_success' => [
33
+ 'name' => __( '2FA Login Success', 'wp-simple-firewall' ),
34
+ 'audit' => [
35
+ __( 'Successful 2FA Login Verification', 'wp-simple-firewall' ),
36
+ ],
37
  ],
38
+ '2fa_verify_success' => [
39
+ 'name' => __( '2FA Verify Success', 'wp-simple-firewall' ),
40
+ 'audit' => [
41
+ __( 'User "{{user_login}}" verified their identity using "{{method}}".', 'wp-simple-firewall' )
42
+ ],
43
  ],
44
+ '2fa_verify_fail' => [
45
+ 'name' => __( '2FA Verify Fail', 'wp-simple-firewall' ),
46
+ 'audit' => [
47
+ __( 'User "{{user_login}}" failed to verify their identity using "{{method}}".', 'wp-simple-firewall' )
48
+ ],
49
  ],
50
+ 'login_block' => [
51
+ 'name' => __( 'Login Blocked', 'wp-simple-firewall' ),
52
+ 'audit' => [
53
+ __( 'Login Blocked.', 'wp-simple-firewall' ),
54
+ ],
55
  ],
56
+ 'hide_login_url' => [
57
+ 'name' => __( 'Hidden Login URL Fail', 'wp-simple-firewall' ),
58
+ 'audit' => [
59
+ __( 'Redirecting wp-login due to hidden login URL', 'wp-simple-firewall' ),
60
+ ],
 
 
 
 
 
 
 
 
 
 
 
 
61
  ],
62
  ];
63
  }
72
  switch ( $section ) {
73
 
74
  case 'section_enable_plugin_feature_login_protection' :
75
+ $title = sprintf( __( 'Enable Module: %s', 'wp-simple-firewall' ), $this->getMod()
76
+ ->getMainFeatureName() );
77
+ $titleShort = sprintf( '%s/%s', __( 'On', 'wp-simple-firewall' ), __( 'Off', 'wp-simple-firewall' ) );
78
+ $summary = [
79
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Login Guard blocks all automated and brute force attempts to log in to your site.', 'wp-simple-firewall' ) ),
80
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), sprintf( __( 'Keep the %s feature turned on.', 'wp-simple-firewall' ), __( 'Login Guard', 'wp-simple-firewall' ) ) )
81
  ];
82
  break;
83
 
84
  case 'section_rename_wplogin' :
85
+ $title = __( 'Hide WordPress Login Page', 'wp-simple-firewall' );
86
+ $titleShort = __( 'Hide Login', 'wp-simple-firewall' );
87
+ $summary = [
88
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'To hide your wp-login.php page from brute force attacks and hacking attempts - if your login page cannot be found, no-one can login.', 'wp-simple-firewall' ) ),
89
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'This is not required for complete security and if your site has irregular or inconsistent configuration it may not work for you.', 'wp-simple-firewall' ) )
90
  ];
91
  break;
92
 
93
  case 'section_multifactor_authentication' :
94
+ $title = __( 'Multi-Factor Authentication', 'wp-simple-firewall' );
95
+ $titleShort = __( 'Multi-Factor Auth', 'wp-simple-firewall' );
96
+ $summary = [
97
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Verifies the identity of users who log in to your site - i.e. they are who they say they are.', 'wp-simple-firewall' ) ),
98
  __( 'You may combine multiple authentication factors for increased security.', 'wp-simple-firewall' )
99
  ];
100
  break;
101
 
102
  case 'section_2fa_email' :
103
+ $title = __( 'Email Two-Factor Authentication', 'wp-simple-firewall' );
104
+ $titleShort = __( '2FA Email', 'wp-simple-firewall' );
105
+ $summary = [
106
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Verifies the identity of users who log in to your site using email-based one-time-passwords.', 'wp-simple-firewall' ) ),
107
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ).' '.__( 'However, if your host blocks email sending you may lock yourself out.', 'wp-simple-firewall' ) ),
108
  sprintf( '%s: %s', __( 'Note', 'wp-simple-firewall' ), __( 'You may combine multiple authentication factors for increased security.', 'wp-simple-firewall' ) )
110
  break;
111
 
112
  case 'section_2fa_ga' :
113
+ $title = __( 'Google Authenticator Two-Factor Authentication', 'wp-simple-firewall' );
114
+ $titleShort = __( 'Google Auth', 'wp-simple-firewall' );
115
+ $summary = [
116
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Verifies the identity of users who log in to your site using Google Authenticator one-time-passwords.', 'wp-simple-firewall' ) ),
117
  sprintf( '%s: %s', __( 'Note', 'wp-simple-firewall' ), __( 'You may combine multiple authentication factors for increased security.', 'wp-simple-firewall' ) )
118
  ];
119
  break;
120
 
121
  case 'section_brute_force_login_protection' :
122
+ $title = __( 'Brute Force Login Protection', 'wp-simple-firewall' );
123
+ $titleShort = __( 'Bots', 'wp-simple-firewall' );
124
+ $summary = [
125
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Blocks brute force hacking attacks against your login and registration pages.', 'wp-simple-firewall' ) ),
126
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) )
127
  ];
128
  break;
129
 
130
  case 'section_hardware_authentication' :
131
+ $title = __( 'Hardware 2-Factor Authentication', 'wp-simple-firewall' );
132
+ $titleShort = __( 'Hardware 2FA', 'wp-simple-firewall' );
133
+ $summary = [
134
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Verifies the identity of users who log in to your site using Yubikey one-time-passwords.', 'wp-simple-firewall' ) ),
135
  sprintf( '%s: %s', __( 'Note', 'wp-simple-firewall' ), __( 'You may combine multiple authentication factors for increased security.', 'wp-simple-firewall' ) )
136
  ];
141
  }
142
 
143
  return [
144
+ 'title' => $title,
145
+ 'title_short' => $titleShort,
146
+ 'summary' => $summary,
147
  ];
148
  }
149
 
src/lib/src/Modules/Plugin/AdminNotices.php CHANGED
@@ -21,10 +21,6 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
21
  $this->buildNotice_PluginTooOld( $notice );
22
  break;
23
 
24
- case 'php7':
25
- $this->buildNotice_Php7( $notice );
26
- break;
27
-
28
  case 'override-forceoff':
29
  $this->buildNotice_OverrideForceoff( $notice );
30
  break;
@@ -111,30 +107,6 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
111
  ];
112
  }
113
 
114
- private function buildNotice_Php7( NoticeVO $notice ) {
115
- $name = $this->getCon()->getHumanName();
116
-
117
- $notice->render_data = [
118
- 'notice_attributes' => [],
119
- 'strings' => [
120
- 'title' => sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ),
121
- sprintf( __( "%s 10+ Wont Be Available For Your Site", 'wp-simple-firewall' ), $name ) ),
122
- 'lines' => [
123
- sprintf(
124
- __( '%s 10 wont support old versions of PHP, including yours (PHP: %s).', 'wp-simple-firewall' ),
125
- $name, Services::Data()->getPhpVersionCleaned( true )
126
- ),
127
- __( "We recommended updating your server's PHP version ASAP.", 'wp-simple-firewall' )
128
- .' '.__( "Your webhost will be able to help guide you in this.", 'wp-simple-firewall' ),
129
- ],
130
- 'read_more' => __( 'Click here to read more about this', 'wp-simple-firewall' )
131
- ],
132
- 'hrefs' => [
133
- 'read_more' => 'https://shsec.io/h3'
134
- ]
135
- ];
136
- }
137
-
138
  private function buildNotice_OverrideForceoff( NoticeVO $notice ) {
139
  $name = $this->getCon()->getHumanName();
140
 
@@ -331,10 +303,6 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
331
  $needed = $con->getIfForceOffActive();
332
  break;
333
 
334
- case 'php7':
335
- $needed = !Services::Data()->getPhpVersionIsAtLeast( '7.0' );
336
- break;
337
-
338
  case 'plugin-disabled':
339
  $needed = $opts->isPluginGloballyDisabled();
340
  break;
21
  $this->buildNotice_PluginTooOld( $notice );
22
  break;
23
 
 
 
 
 
24
  case 'override-forceoff':
25
  $this->buildNotice_OverrideForceoff( $notice );
26
  break;
107
  ];
108
  }
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  private function buildNotice_OverrideForceoff( NoticeVO $notice ) {
111
  $name = $this->getCon()->getHumanName();
112
 
303
  $needed = $con->getIfForceOffActive();
304
  break;
305
 
 
 
 
 
306
  case 'plugin-disabled':
307
  $needed = $opts->isPluginGloballyDisabled();
308
  break;
src/lib/src/Modules/Plugin/AjaxHandler.php CHANGED
@@ -109,7 +109,7 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
109
  $success = false;
110
  $msg = __( 'No items selected.', 'wp-simple-firewall' );
111
  }
112
- elseif ( !in_array( $req->post( 'bulk_action' ), [ 'delete' ] ) ) {
113
  $msg = __( 'Not a supported action.', 'wp-simple-firewall' );
114
  }
115
  else {
109
  $success = false;
110
  $msg = __( 'No items selected.', 'wp-simple-firewall' );
111
  }
112
+ elseif ( $req->post( 'bulk_action' ) != 'delete' ) {
113
  $msg = __( 'Not a supported action.', 'wp-simple-firewall' );
114
  }
115
  else {
src/lib/src/Modules/Plugin/Components/BadgeWidget.php CHANGED
@@ -2,11 +2,12 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Components;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\ModCon;
6
 
7
  class BadgeWidget extends \WP_Widget {
8
 
9
- use \FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
10
 
11
  /**
12
  * BadgeWidget constructor.
@@ -46,12 +47,12 @@ class BadgeWidget extends \WP_Widget {
46
  }
47
 
48
  /**
49
- * @param array $aNewInstance
50
- * @param array $aOldInstance
51
  * @return array
52
  */
53
- public function update( $aNewInstance, $aOldInstance ) {
54
- return parent::update( $aNewInstance, $aOldInstance );
55
  // $aInstance = array(
56
  // 'title' => empty( $aNewInstance['title'] ) ? '' : strip_tags( $aNewInstance['title'] )
57
  // );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Components;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\ModCon;
7
 
8
  class BadgeWidget extends \WP_Widget {
9
 
10
+ use ModConsumer;
11
 
12
  /**
13
  * BadgeWidget constructor.
47
  }
48
 
49
  /**
50
+ * @param array $new_instance
51
+ * @param array $old_instance
52
  * @return array
53
  */
54
+ public function update( $new_instance, $old_instance ) {
55
+ return parent::update( $new_instance, $old_instance );
56
  // $aInstance = array(
57
  // 'title' => empty( $aNewInstance['title'] ) ? '' : strip_tags( $aNewInstance['title'] )
58
  // );
src/lib/src/Modules/Plugin/Components/PluginBadge.php CHANGED
@@ -31,7 +31,7 @@ class PluginBadge extends Modules\Base\Common\ExecOnceModConsumer {
31
  add_action( 'widgets_init', [ $this, 'addPluginBadgeWidget' ] );
32
 
33
  add_shortcode( 'SHIELD_BADGE', function () {
34
- $this->render( false );
35
  } );
36
  }
37
 
@@ -59,11 +59,7 @@ class PluginBadge extends Modules\Base\Common\ExecOnceModConsumer {
59
  echo $this->render( true );
60
  }
61
 
62
- /**
63
- * @param bool $isFloating
64
- * @return string
65
- */
66
- public function render( $isFloating = false ) {
67
  $con = $this->getCon();
68
  $wlCon = $con->getModule_SecAdmin()->getWhiteLabelController();
69
 
@@ -101,34 +97,32 @@ class PluginBadge extends Modules\Base\Common\ExecOnceModConsumer {
101
  $badgeAttrs = apply_filters( 'icwp_shield_plugin_badge_attributes', $badgeAttrs, $isFloating );
102
  }
103
 
104
- $data = [
105
- 'ajax' => [
106
- 'plugin_badge_close' => $this->getMod()->getAjaxActionData( 'plugin_badge_close', true ),
107
- ],
108
- 'content' => [
109
- 'custom_css' => esc_js( $badgeAttrs[ 'custom_css' ] ),
110
- ],
111
- 'flags' => [
112
- 'nofollow' => apply_filters( 'icwp_shield_badge_relnofollow', false ),
113
- 'is_floating' => $isFloating
114
- ],
115
- 'hrefs' => [
116
- 'badge' => $badgeAttrs[ 'url' ],
117
- 'logo' => $badgeAttrs[ 'logo' ],
118
- ],
119
- 'strings' => [
120
- 'protected' => $badgeAttrs[ 'protected_by' ],
121
- 'name' => $badgeAttrs[ 'name' ],
122
- ],
123
- ];
124
-
125
- try {
126
- $render = $this->getMod()->renderTemplate( 'snippets/plugin_badge_widget', $data, true );
127
- }
128
- catch ( \Exception $e ) {
129
- $render = 'Could not generate badge: '.$e->getMessage();
130
- }
131
- return $render;
132
  }
133
 
134
  public function setBadgeStateClosed() :bool {
31
  add_action( 'widgets_init', [ $this, 'addPluginBadgeWidget' ] );
32
 
33
  add_shortcode( 'SHIELD_BADGE', function () {
34
+ $this->render();
35
  } );
36
  }
37
 
59
  echo $this->render( true );
60
  }
61
 
62
+ public function render( bool $isFloating = false ) :string {
 
 
 
 
63
  $con = $this->getCon();
64
  $wlCon = $con->getModule_SecAdmin()->getWhiteLabelController();
65
 
97
  $badgeAttrs = apply_filters( 'icwp_shield_plugin_badge_attributes', $badgeAttrs, $isFloating );
98
  }
99
 
100
+ return $this->getMod()
101
+ ->renderTemplate(
102
+ 'snippets/plugin_badge_widget',
103
+ [
104
+ 'ajax' => [
105
+ 'plugin_badge_close' => $this->getMod()
106
+ ->getAjaxActionData( 'plugin_badge_close', true ),
107
+ ],
108
+ 'content' => [
109
+ 'custom_css' => esc_js( $badgeAttrs[ 'custom_css' ] ),
110
+ ],
111
+ 'flags' => [
112
+ 'nofollow' => apply_filters( 'icwp_shield_badge_relnofollow', false ),
113
+ 'is_floating' => $isFloating
114
+ ],
115
+ 'hrefs' => [
116
+ 'badge' => $badgeAttrs[ 'url' ],
117
+ 'logo' => $badgeAttrs[ 'logo' ],
118
+ ],
119
+ 'strings' => [
120
+ 'protected' => $badgeAttrs[ 'protected_by' ],
121
+ 'name' => $badgeAttrs[ 'name' ],
122
+ ],
123
+ ],
124
+ true
125
+ );
 
 
126
  }
127
 
128
  public function setBadgeStateClosed() :bool {
src/lib/src/Modules/Plugin/Debug.php CHANGED
@@ -3,10 +3,27 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules;
 
6
 
7
  class Debug extends Modules\Base\Debug {
8
 
9
  public function run() {
 
10
  die( 'finish' );
11
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Tests\RunTests;
7
 
8
  class Debug extends Modules\Base\Debug {
9
 
10
  public function run() {
11
+ $this->tests();
12
  die( 'finish' );
13
  }
14
+
15
+ private function getIpRefs() {
16
+ $ipRefs = $this->getCon()
17
+ ->getModule_Data()
18
+ ->getDbH_ReqLogs()
19
+ ->getQuerySelector()
20
+ ->getDistinctForColumn( 'ip_ref' );
21
+ var_dump($ipRefs);
22
+ }
23
+
24
+ private function tests() {
25
+ ( new RunTests() )
26
+ ->setCon( $this->getCon() )
27
+ ->run();
28
+ }
29
  }
src/lib/src/Modules/Plugin/Insights/OverviewCards.php CHANGED
@@ -6,6 +6,7 @@ use FernleafSystems\Wordpress\Plugin\Shield;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
7
  use FernleafSystems\Wordpress\Services\Services;
8
  use FernleafSystems\Wordpress\Services\Utilities\Ssl;
 
9
 
10
  class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
11
 
@@ -67,11 +68,11 @@ class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
67
  $cards = [];
68
 
69
  // db password strength
70
- $bStrong = ( new \ZxcvbnPhp\Zxcvbn() )->passwordStrength( DB_PASSWORD )[ 'score' ] >= 4;
71
  $cards[ 'db_strength' ] = [
72
  'name' => __( 'DB Password', 'wp-simple-firewall' ),
73
- 'state' => $bStrong >= 4 ? 1 : -1,
74
- 'summary' => $bStrong ?
75
  __( 'WP Database password is very strong', 'wp-simple-firewall' )
76
  : __( "WP Database password appears to be weak", 'wp-simple-firewall' ),
77
  'href' => '',
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
7
  use FernleafSystems\Wordpress\Services\Services;
8
  use FernleafSystems\Wordpress\Services\Utilities\Ssl;
9
+ use ZxcvbnPhp\Zxcvbn;
10
 
11
  class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
12
 
68
  $cards = [];
69
 
70
  // db password strength
71
+ $strong = ( new Zxcvbn() )->passwordStrength( DB_PASSWORD )[ 'score' ] >= 4;
72
  $cards[ 'db_strength' ] = [
73
  'name' => __( 'DB Password', 'wp-simple-firewall' ),
74
+ 'state' => $strong >= 4 ? 1 : -1,
75
+ 'summary' => $strong ?
76
  __( 'WP Database password is very strong', 'wp-simple-firewall' )
77
  : __( "WP Database password appears to be weak", 'wp-simple-firewall' ),
78
  'href' => '',
src/lib/src/Modules/Plugin/Lib/Debug/Collate.php CHANGED
@@ -164,13 +164,18 @@ class Collate {
164
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
165
  : 'Missing';
166
 
167
- $dbh = $con->getModule_AuditTrail()->getDbHandler_AuditTrail();
168
  $data[ 'DB Table: Audit Trail' ] = $dbh->isReady() ?
169
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
170
  : 'Missing';
171
 
 
 
 
 
 
172
  $dbh = $con->getModule_IPs()->getDbHandler_IPs();
173
- $data[ 'DB Table: IP' ] = $dbh->isReady() ?
174
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
175
  : 'Missing';
176
 
@@ -180,12 +185,12 @@ class Collate {
180
  : 'Missing';
181
 
182
  $dbh = $con->getModule_HackGuard()->getDbHandler_ScanResults();
183
- $data[ 'DB Table: Scan' ] = $dbh->isReady() ?
184
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
185
  : 'Missing';
186
 
187
- $dbh = $con->getModule_Traffic()->getDbHandler_Traffic();
188
- $data[ 'DB Table: Traffic' ] = $dbh->isReady() ?
189
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
190
  : 'Missing';
191
 
164
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
165
  : 'Missing';
166
 
167
+ $dbh = $con->getModule_AuditTrail()->getDbH_Logs();
168
  $data[ 'DB Table: Audit Trail' ] = $dbh->isReady() ?
169
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
170
  : 'Missing';
171
 
172
+ $dbh = $con->getModule_Data()->getDbH_IPs();
173
+ $data[ 'DB Table: IPs' ] = $dbh->isReady() ?
174
+ sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
175
+ : 'Missing';
176
+
177
  $dbh = $con->getModule_IPs()->getDbHandler_IPs();
178
+ $data[ 'DB Table: IP Lists' ] = $dbh->isReady() ?
179
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
180
  : 'Missing';
181
 
185
  : 'Missing';
186
 
187
  $dbh = $con->getModule_HackGuard()->getDbHandler_ScanResults();
188
+ $data[ 'DB Table: Scan Results' ] = $dbh->isReady() ?
189
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
190
  : 'Missing';
191
 
192
+ $dbh = $con->getModule_Data()->getDbH_ReqLogs();
193
+ $data[ 'DB Table: Traffic/Requests' ] = $dbh->isReady() ?
194
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
195
  : 'Missing';
196
 
src/lib/src/Modules/Plugin/Lib/Debug/RecentEvents.php CHANGED
@@ -3,7 +3,6 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Debug;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Insights\Strings;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
@@ -30,48 +29,47 @@ class RecentEvents {
30
  }
31
 
32
  private function getData() :array {
33
- $con = $this->getCon();
34
 
35
- $aTheStats = array_filter(
36
- $con->loadEventsService()->getEvents(),
37
  function ( $evt ) {
38
- return isset( $evt[ 'recent' ] ) && $evt[ 'recent' ];
39
  }
40
  );
41
 
42
- /** @var Strings $oStrs */
43
- $oStrs = $con->getModule_Insights()->getStrings();
44
- $aNames = $oStrs->getInsightStatNames();
45
-
46
- /** @var Events\Select $oSel */
47
- $oSel = $con->getModule_Events()
48
- ->getDbHandler_Events()
49
- ->getQuerySelector();
50
 
51
- $aRecentStats = array_intersect_key(
52
- array_map(
53
- function ( $oEntryVO ) use ( $aNames ) {
54
- /** @var Events\EntryVO $oEntryVO */
55
- return [
56
- 'name' => isset( $aNames[ $oEntryVO->event ] ) ? $aNames[ $oEntryVO->event ] : '*** '.$oEntryVO->event,
57
- 'val' => Services::WpGeneral()->getTimeStringForDisplay( $oEntryVO->created_at )
58
- ];
 
 
59
  },
60
- $oSel->getLatestForAllEvents()
61
- ),
62
- $aTheStats
63
  );
64
 
65
- $sNotYetRecorded = __( 'Not yet recorded', 'wp-simple-firewall' );
66
- foreach ( array_keys( $aTheStats ) as $sStatKey ) {
67
- if ( !isset( $aRecentStats[ $sStatKey ] ) ) {
68
- $aRecentStats[ $sStatKey ] = [
69
- 'name' => isset( $aNames[ $sStatKey ] ) ? $aNames[ $sStatKey ] : '*** '.$sStatKey,
70
- 'val' => $sNotYetRecorded
71
  ];
72
  }
73
  }
74
 
75
- return $aRecentStats;
76
  }
77
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Debug;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events;
 
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
29
  }
30
 
31
  private function getData() :array {
32
+ $srvEvents = $this->getCon()->loadEventsService();
33
 
34
+ $theStats = array_filter(
35
+ $srvEvents->getEvents(),
36
  function ( $evt ) {
37
+ return !empty( $evt[ 'recent' ] );
38
  }
39
  );
40
 
41
+ /** @var Events\Select $selector */
42
+ $selector = $this->getCon()
43
+ ->getModule_Events()
44
+ ->getDbHandler_Events()
45
+ ->getQuerySelector();
 
 
 
46
 
47
+ $recent = array_intersect_key(
48
+ array_filter( array_map(
49
+ function ( $entry ) use ( $srvEvents ) {
50
+ /** @var Events\EntryVO $entry */
51
+ return $srvEvents->eventExists( $entry->event ) ?
52
+ [
53
+ 'name' => $srvEvents->getEventName( $entry->event ),
54
+ 'val' => Services::WpGeneral()->getTimeStringForDisplay( $entry->created_at )
55
+ ]
56
+ : null;
57
  },
58
+ $selector->getLatestForAllEvents()
59
+ ) ),
60
+ $theStats
61
  );
62
 
63
+ $notYetRecorded = __( 'Not yet recorded', 'wp-simple-firewall' );
64
+ foreach ( array_keys( $theStats ) as $eventKey ) {
65
+ if ( !isset( $recent[ $eventKey ] ) ) {
66
+ $recent[ $eventKey ] = [
67
+ 'name' => $srvEvents->getEventName( $eventKey ),
68
+ 'val' => $notYetRecorded
69
  ];
70
  }
71
  }
72
 
73
+ return $recent;
74
  }
75
  }
src/lib/src/Modules/Plugin/Lib/ImportExport/Export.php CHANGED
@@ -40,16 +40,16 @@ class Export {
40
 
41
  $sNetworkOpt = $req->query( 'network', '' );
42
  $bDoNetwork = !empty( $sNetworkOpt );
43
- $sUrl = Services::Data()->validateSimpleHttpUrl( $req->query( 'url', '' ) );
44
 
45
- if ( !$mod->isImportExportSecretKey( $sSecretKey ) && !$this->isUrlOnWhitelist( $sUrl ) ) {
46
  return; // we show no signs of responding to invalid secret keys or unwhitelisted URLs
47
  }
48
 
49
  $bSuccess = false;
50
  $aData = [];
51
 
52
- if ( !$this->verifyUrlWithHandshake( $sUrl ) ) {
53
  $nCode = 3;
54
  $sMessage = __( 'Handshake verification failed.', 'wp-simple-firewall' );
55
  }
@@ -61,41 +61,40 @@ class Export {
61
 
62
  $this->getCon()->fireEvent(
63
  'options_exported',
64
- [ 'audit' => [ 'site' => $sUrl ] ]
65
  );
66
 
67
  if ( $bDoNetwork ) {
68
  if ( $sNetworkOpt === 'Y' ) {
69
- $mod->addUrlToImportExportWhitelistUrls( $sUrl );
70
  $this->getCon()->fireEvent(
71
  'whitelist_site_added',
72
- [ 'audit' => [ 'site' => $sUrl ] ]
73
  );
74
  }
75
  else {
76
- $mod->removeUrlFromImportExportWhitelistUrls( $sUrl );
77
  $this->getCon()->fireEvent(
78
  'whitelist_site_removed',
79
- [ 'audit' => [ 'site' => $sUrl ] ]
80
  );
81
  }
82
  }
83
  }
84
 
85
- $aResponse = [
86
  'success' => $bSuccess,
87
  'code' => $nCode,
88
  'message' => $sMessage,
89
  'data' => $aData,
90
- ];
91
- echo json_encode( $aResponse );
92
  die();
93
  }
94
 
95
  /**
96
  * @return string[]
97
  */
98
- public function toStandardArray() {
99
  $sExport = json_encode( $this->getExportData() );
100
  return [
101
  '# Site URL: '.Services::WpGeneral()->getHomeUrl(),
@@ -115,26 +114,23 @@ class Export {
115
  );
116
  }
117
 
118
- /**
119
- * @return array
120
- */
121
- private function getExportData() {
122
- $aAll = [];
123
  foreach ( $this->getCon()->modules as $mod ) {
124
  $oOpts = $mod->getOptions();
125
- $aAll[ $mod->getOptionsStorageKey() ] = array_diff_key(
126
  $oOpts->getTransferableOptions(),
127
  array_flip( $oOpts->getXferExcluded() )
128
  );
129
  }
130
- return $aAll;
131
  }
132
 
133
  /**
134
  * @param string $url
135
  * @return bool
136
  */
137
- private function isUrlOnWhitelist( $url ) {
138
  /** @var Plugin\Options $opts */
139
  $opts = $this->getOptions();
140
  return !empty( $url ) && in_array( $url, $opts->getImportExportWhitelist() );
@@ -144,7 +140,7 @@ class Export {
144
  * @param string $url
145
  * @return bool
146
  */
147
- private function verifyUrlWithHandshake( $url ) {
148
  $bVerified = false;
149
 
150
  if ( !empty( $url ) ) {
40
 
41
  $sNetworkOpt = $req->query( 'network', '' );
42
  $bDoNetwork = !empty( $sNetworkOpt );
43
+ $url = Services::Data()->validateSimpleHttpUrl( $req->query( 'url', '' ) );
44
 
45
+ if ( !$mod->isImportExportSecretKey( $sSecretKey ) && !$this->isUrlOnWhitelist( $url ) ) {
46
  return; // we show no signs of responding to invalid secret keys or unwhitelisted URLs
47
  }
48
 
49
  $bSuccess = false;
50
  $aData = [];
51
 
52
+ if ( !$this->verifyUrlWithHandshake( $url ) ) {
53
  $nCode = 3;
54
  $sMessage = __( 'Handshake verification failed.', 'wp-simple-firewall' );
55
  }
61
 
62
  $this->getCon()->fireEvent(
63
  'options_exported',
64
+ [ 'audit_params' => [ 'site' => $url ] ]
65
  );
66
 
67
  if ( $bDoNetwork ) {
68
  if ( $sNetworkOpt === 'Y' ) {
69
+ $mod->addUrlToImportExportWhitelistUrls( $url );
70
  $this->getCon()->fireEvent(
71
  'whitelist_site_added',
72
+ [ 'audit_params' => [ 'site' => $url ] ]
73
  );
74
  }
75
  else {
76
+ $mod->removeUrlFromImportExportWhitelistUrls( $url );
77
  $this->getCon()->fireEvent(
78
  'whitelist_site_removed',
79
+ [ 'audit_params' => [ 'site' => $url ] ]
80
  );
81
  }
82
  }
83
  }
84
 
85
+ echo json_encode( [
86
  'success' => $bSuccess,
87
  'code' => $nCode,
88
  'message' => $sMessage,
89
  'data' => $aData,
90
+ ] );
 
91
  die();
92
  }
93
 
94
  /**
95
  * @return string[]
96
  */
97
+ public function toStandardArray() :array{
98
  $sExport = json_encode( $this->getExportData() );
99
  return [
100
  '# Site URL: '.Services::WpGeneral()->getHomeUrl(),
114
  );
115
  }
116
 
117
+ private function getExportData() :array{
118
+ $all = [];
 
 
 
119
  foreach ( $this->getCon()->modules as $mod ) {
120
  $oOpts = $mod->getOptions();
121
+ $all[ $mod->getOptionsStorageKey() ] = array_diff_key(
122
  $oOpts->getTransferableOptions(),
123
  array_flip( $oOpts->getXferExcluded() )
124
  );
125
  }
126
+ return $all;
127
  }
128
 
129
  /**
130
  * @param string $url
131
  * @return bool
132
  */
133
+ private function isUrlOnWhitelist( $url ) :bool{
134
  /** @var Plugin\Options $opts */
135
  $opts = $this->getOptions();
136
  return !empty( $url ) && in_array( $url, $opts->getImportExportWhitelist() );
140
  * @param string $url
141
  * @return bool
142
  */
143
+ private function verifyUrlWithHandshake( $url ):bool {
144
  $bVerified = false;
145
 
146
  if ( !empty( $url ) ) {
src/lib/src/Modules/Plugin/Lib/ImportExport/Import.php CHANGED
@@ -100,21 +100,21 @@ class Import {
100
  }
101
 
102
  /**
103
- * @param string $sMasterSiteUrl
104
  * @param string $sSecretKey
105
  * @param bool|null $bEnableNetwork
106
  * @return int
107
  * @throws \Exception
108
  */
109
- public function fromSite( $sMasterSiteUrl = '', $sSecretKey = '', $bEnableNetwork = null ) {
110
  /** @var Plugin\Options $opts */
111
  $opts = $this->getOptions();
112
  /** @var Plugin\ModCon $mod */
113
  $mod = $this->getMod();
114
  $DP = Services::Data();
115
 
116
- if ( empty( $sMasterSiteUrl ) ) {
117
- $sMasterSiteUrl = $opts->getImportExportMasterImportUrl();
118
  }
119
 
120
  $sOriginalMasterSiteUrl = $opts->getImportExportMasterImportUrl();
@@ -132,7 +132,7 @@ class Import {
132
  }
133
 
134
  // Ensure we have entries for 'scheme' and 'host'
135
- $aUrlParts = wp_parse_url( $sMasterSiteUrl );
136
  $bHasParts = !empty( $aUrlParts )
137
  && count(
138
  array_filter( array_intersect_key(
@@ -143,8 +143,8 @@ class Import {
143
  if ( !$bHasParts ) {
144
  throw new \Exception( "Couldn't parse the URL into its parts", 4 );
145
  }
146
- $sMasterSiteUrl = $DP->validateSimpleHttpUrl( $sMasterSiteUrl ); // final clean
147
- if ( empty( $sMasterSiteUrl ) ) {
148
  throw new \Exception( "Couldn't validate the URL.", 4 );
149
  }
150
 
@@ -166,7 +166,7 @@ class Import {
166
  }
167
 
168
  { // Make the request
169
- $sFinalUrl = add_query_arg( $data, $sMasterSiteUrl );
170
  $sResponse = Services::HttpRequest()->getContent( $sFinalUrl );
171
  $response = @json_decode( $sResponse, true );
172
 
@@ -189,7 +189,7 @@ class Import {
189
  throw new \Exception( "Response data was empty", 8 );
190
  }
191
 
192
- $this->processDataImport( $response[ 'data' ], $sMasterSiteUrl );
193
 
194
  // Fix for the overwriting of the Master Site URL with an empty string.
195
  // Only do so if we're not turning it off. i.e on or no-change
@@ -199,10 +199,10 @@ class Import {
199
  }
200
  }
201
  elseif ( $bEnableNetwork === true ) {
202
- $mod->setImportExportMasterImportUrl( $sMasterSiteUrl );
203
  $this->getCon()->fireEvent(
204
  'master_url_set',
205
- [ 'audit' => [ 'site' => $sMasterSiteUrl ] ]
206
  );
207
  }
208
  elseif ( $bEnableNetwork === false ) {
@@ -220,7 +220,7 @@ class Import {
220
  $oTheseOpts = $mod->getOptions();
221
  $oTheseOpts->setMultipleOptions(
222
  array_diff_key(
223
- $data[ $mod->getOptionsStorageKey() ],
224
  array_flip( $oTheseOpts->getXferExcluded() )
225
  )
226
  );
@@ -233,7 +233,7 @@ class Import {
233
  if ( $anythingChanged ) {
234
  $this->getCon()->fireEvent(
235
  'options_imported',
236
- [ 'audit' => [ 'site' => $source ] ]
237
  );
238
  }
239
  }
100
  }
101
 
102
  /**
103
+ * @param string $masterSiteURL
104
  * @param string $sSecretKey
105
  * @param bool|null $bEnableNetwork
106
  * @return int
107
  * @throws \Exception
108
  */
109
+ public function fromSite( $masterSiteURL = '', $sSecretKey = '', $bEnableNetwork = null ) {
110
  /** @var Plugin\Options $opts */
111
  $opts = $this->getOptions();
112
  /** @var Plugin\ModCon $mod */
113
  $mod = $this->getMod();
114
  $DP = Services::Data();
115
 
116
+ if ( empty( $masterSiteURL ) ) {
117
+ $masterSiteURL = $opts->getImportExportMasterImportUrl();
118
  }
119
 
120
  $sOriginalMasterSiteUrl = $opts->getImportExportMasterImportUrl();
132
  }
133
 
134
  // Ensure we have entries for 'scheme' and 'host'
135
+ $aUrlParts = wp_parse_url( $masterSiteURL );
136
  $bHasParts = !empty( $aUrlParts )
137
  && count(
138
  array_filter( array_intersect_key(
143
  if ( !$bHasParts ) {
144
  throw new \Exception( "Couldn't parse the URL into its parts", 4 );
145
  }
146
+ $masterSiteURL = $DP->validateSimpleHttpUrl( $masterSiteURL ); // final clean
147
+ if ( empty( $masterSiteURL ) ) {
148
  throw new \Exception( "Couldn't validate the URL.", 4 );
149
  }
150
 
166
  }
167
 
168
  { // Make the request
169
+ $sFinalUrl = add_query_arg( $data, $masterSiteURL );
170
  $sResponse = Services::HttpRequest()->getContent( $sFinalUrl );
171
  $response = @json_decode( $sResponse, true );
172
 
189
  throw new \Exception( "Response data was empty", 8 );
190
  }
191
 
192
+ $this->processDataImport( $response[ 'data' ], $masterSiteURL );
193
 
194
  // Fix for the overwriting of the Master Site URL with an empty string.
195
  // Only do so if we're not turning it off. i.e on or no-change
199
  }
200
  }
201
  elseif ( $bEnableNetwork === true ) {
202
+ $mod->setImportExportMasterImportUrl( $masterSiteURL );
203
  $this->getCon()->fireEvent(
204
  'master_url_set',
205
+ [ 'audit_params' => [ 'site' => $masterSiteURL ] ]
206
  );
207
  }
208
  elseif ( $bEnableNetwork === false ) {
220
  $oTheseOpts = $mod->getOptions();
221
  $oTheseOpts->setMultipleOptions(
222
  array_diff_key(
223
+ $data[ $mod->getOptionsStorageKey() ] ?? [],
224
  array_flip( $oTheseOpts->getXferExcluded() )
225
  )
226
  );
233
  if ( $anythingChanged ) {
234
  $this->getCon()->fireEvent(
235
  'options_imported',
236
+ [ 'audit_params' => [ 'site' => $source ] ]
237
  );
238
  }
239
  }
src/lib/src/Modules/Plugin/Lib/ImportExport/ImportExportController.php CHANGED
@@ -113,7 +113,7 @@ class ImportExportController {
113
 
114
  $this->getCon()->fireEvent(
115
  'import_notify_received',
116
- [ 'audit' => [ 'master_site' => $opts->getImportExportMasterImportUrl() ] ]
117
  );
118
  }
119
  }
@@ -124,15 +124,12 @@ class ImportExportController {
124
  * window for the handshake to complete. We do not explicitly fail.
125
  */
126
  private function confirmExportHandshake() {
127
- /** @var Plugin\Options $oOpts */
128
- $oOpts = $this->getOptions();
129
- if ( Services::Request()->ts() < (int)$oOpts->getOpt( 'importexport_handshake_expires_at' ) ) {
130
  echo json_encode( [ 'success' => true ] );
131
  die();
132
  }
133
- else {
134
- return;
135
- }
136
  }
137
 
138
  public function runDailyCron() {
113
 
114
  $this->getCon()->fireEvent(
115
  'import_notify_received',
116
+ [ 'audit_params' => [ 'master_site' => $opts->getImportExportMasterImportUrl() ] ]
117
  );
118
  }
119
  }
124
  * window for the handshake to complete. We do not explicitly fail.
125
  */
126
  private function confirmExportHandshake() {
127
+ /** @var Plugin\Options $opts */
128
+ $opts = $this->getOptions();
129
+ if ( Services::Request()->ts() < (int)$opts->getOpt( 'importexport_handshake_expires_at' ) ) {
130
  echo json_encode( [ 'success' => true ] );
131
  die();
132
  }
 
 
 
133
  }
134
 
135
  public function runDailyCron() {
src/lib/src/Modules/Plugin/Lib/ImportExport/Options/BuildTransferableOptions.php CHANGED
@@ -9,9 +9,9 @@ class BuildTransferableOptions {
9
  use ModConsumer;
10
 
11
  /**
12
- * @return mixed[]
13
  */
14
- public function build() {
15
  $opts = $this->getOptions();
16
  return array_merge(
17
  array_fill_keys( $opts->getOptionsKeys(), false ),
9
  use ModConsumer;
10
 
11
  /**
12
+ * @return array[]
13
  */
14
+ public function build() :array {
15
  $opts = $this->getOptions();
16
  return array_merge(
17
  array_fill_keys( $opts->getOptionsKeys(), false ),
src/lib/src/Modules/Plugin/ModCon.php CHANGED
@@ -78,9 +78,11 @@ class ModCon extends BaseShield\ModCon {
78
  }
79
 
80
  public function onPluginShutdown() {
81
- $preferred = Services::IP()->getIpDetector()->getLastSuccessfulSource();
82
- if ( !empty( $preferred ) ) {
83
- $this->getOptions()->setOpt( 'last_ip_detect_source', $preferred );
 
 
84
  }
85
  parent::onPluginShutdown();
86
  }
@@ -158,7 +160,7 @@ class ModCon extends BaseShield\ModCon {
158
  }
159
 
160
  public function getActivePluginFeatures() :array {
161
- $features = $this->getDef( 'active_plugin_features' );
162
 
163
  $available = [];
164
  if ( is_array( $features ) ) {
@@ -475,7 +477,7 @@ class ModCon extends BaseShield\ModCon {
475
  * @param string $sId
476
  * @return bool
477
  */
478
- protected function isValidInstallId( $sId ) {
479
  return !empty( $sId ) && is_string( $sId ) && strlen( $sId ) == 40;
480
  }
481
 
@@ -528,10 +530,6 @@ class ModCon extends BaseShield\ModCon {
528
  return $enqs;
529
  }
530
 
531
- public function getDbHandler_GeoIp() :Shield\Databases\GeoIp\Handler {
532
- return $this->getDbH( 'geoip' );
533
- }
534
-
535
  public function getDbHandler_Notes() :Shield\Databases\AdminNotes\Handler {
536
  return $this->getDbH( 'notes' );
537
  }
78
  }
79
 
80
  public function onPluginShutdown() {
81
+ if ( !$this->getCon()->plugin_deleting ) {
82
+ $preferred = Services::IP()->getIpDetector()->getLastSuccessfulSource();
83
+ if ( !empty( $preferred ) ) {
84
+ $this->getOptions()->setOpt( 'last_ip_detect_source', $preferred );
85
+ }
86
  }
87
  parent::onPluginShutdown();
88
  }
160
  }
161
 
162
  public function getActivePluginFeatures() :array {
163
+ $features = $this->getOptions()->getDef( 'active_plugin_features' );
164
 
165
  $available = [];
166
  if ( is_array( $features ) ) {
477
  * @param string $sId
478
  * @return bool
479
  */
480
+ protected function isValidInstallId( $sId ) :bool {
481
  return !empty( $sId ) && is_string( $sId ) && strlen( $sId ) == 40;
482
  }
483
 
530
  return $enqs;
531
  }
532
 
 
 
 
 
533
  public function getDbHandler_Notes() :Shield\Databases\AdminNotes\Handler {
534
  return $this->getDbH( 'notes' );
535
  }
src/lib/src/Modules/Plugin/Options.php CHANGED
@@ -30,11 +30,6 @@ class Options extends BaseShield\Options {
30
  return $this->getOpt( 'visitor_address_source' );
31
  }
32
 
33
- public function getShieldNetApiData() :array {
34
- $d = $this->getOpt( 'snapi_data', [] );
35
- return is_array( $d ) ? $d : [];
36
- }
37
-
38
  /**
39
  * @return bool
40
  */
30
  return $this->getOpt( 'visitor_address_source' );
31
  }
32
 
 
 
 
 
 
33
  /**
34
  * @return bool
35
  */
src/lib/src/Modules/Plugin/Processor.php CHANGED
@@ -32,8 +32,8 @@ class Processor extends BaseShield\Processor {
32
  $mod->getImpExpController()->execute();
33
  }
34
 
35
- add_filter( $con->prefix( 'delete_on_deactivate' ), function ( $isDelete ) use ( $opts ) {
36
- return $isDelete || $opts->isOpt( 'delete_on_deactivate', 'Y' );
37
  } );
38
 
39
  add_action( $con->prefix( 'dashboard_widget_content' ), function () {
32
  $mod->getImpExpController()->execute();
33
  }
34
 
35
+ add_filter( $con->prefix( 'delete_on_deactivate' ), function ( $isDelete ) {
36
+ return $isDelete || $this->getOptions()->isOpt( 'delete_on_deactivate', 'Y' );
37
  } );
38
 
39
  add_action( $con->prefix( 'dashboard_widget_content' ), function () {
src/lib/src/Modules/Plugin/Strings.php CHANGED
@@ -11,60 +11,114 @@ class Strings extends Base\Strings {
11
  /**
12
  * @inheritDoc
13
  */
14
- protected function getAdditionalDisplayStrings() :array {
15
- return [
16
- 'actions_title' => __( 'Plugin Actions', 'wp-simple-firewall' ),
17
- 'actions_summary' => __( 'E.g. Import/Export', 'wp-simple-firewall' ),
18
- ];
19
- }
20
-
21
- /**
22
- * @return string[][]
23
- */
24
- protected function getAuditMessages() :array {
25
  return [
26
- 'suresend_success' => [
27
- __( 'Attempt to send email using SureSend: %s', 'wp-simple-firewall' ),
28
- __( 'SureSend email success.', 'wp-simple-firewall' ),
 
 
29
  ],
30
- 'suresend_fail' => [
31
- __( 'Attempt to send email using SureSend: %s', 'wp-simple-firewall' ),
32
- __( 'SureSend email failed.', 'wp-simple-firewall' ),
 
 
 
33
  ],
34
- 'import_notify_sent' => [
35
- __( 'Sent notifications to whitelisted sites for required options import.', 'wp-simple-firewall' )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  ],
37
- 'import_notify_received' => [
38
- __( 'Received notification that options import required.', 'wp-simple-firewall' ),
39
- __( 'Current master site: %s', 'wp-simple-firewall' )
 
 
40
  ],
41
- 'options_exported' => [
42
- __( 'Options exported to site: %s', 'wp-simple-firewall' ),
 
 
 
 
 
 
 
 
 
 
43
  ],
44
- 'options_imported' => [
45
- __( 'Options imported from site: %s', 'wp-simple-firewall' ),
 
 
 
46
  ],
47
- 'whitelist_site_added' => [
48
- __( 'Site added to export white list: %s', 'wp-simple-firewall' ),
 
 
 
49
  ],
50
- 'whitelist_site_removed' => [
51
- __( 'Site removed from export white list: %s', 'wp-simple-firewall' ),
 
 
 
52
  ],
53
- 'master_url_set' => [
54
- __( 'Master Site URL set: %s', 'wp-simple-firewall' ),
 
 
 
55
  ],
56
- 'recaptcha_fail' => [
57
- __( 'CAPTCHA Test Fail', 'wp-simple-firewall' )
 
 
 
58
  ],
59
- 'antibot_pass' => [
60
- __( 'Request passed the AntiBot Test with a Visitor Score of "%s" (minimum score: %s).', 'wp-simple-firewall' ),
 
 
 
61
  ],
62
- 'antibot_fail' => [
63
- __( 'Request failed the AntiBot Test with a Visitor Score of "%s" (minimum score: %s).', 'wp-simple-firewall' ),
 
 
 
64
  ],
65
  ];
66
  }
67
 
 
 
 
 
 
 
 
 
 
 
68
  /**
69
  * @param string $section
70
  * @return array
11
  /**
12
  * @inheritDoc
13
  */
14
+ public function getEventStrings() :array {
 
 
 
 
 
 
 
 
 
 
15
  return [
16
+ 'debug_log' => [
17
+ 'name' => __( 'Custom Debug', 'wp-simple-firewall' ),
18
+ 'audit' => [
19
+ '{{message}}',
20
+ ],
21
  ],
22
+ 'frontpage_load' => [
23
+ 'name' => sprintf( '%s: %s', __( 'Loaded', 'wp-simple-firewall' ),
24
+ __( 'Front Page', 'wp-simple-firewall' ) ),
25
+ 'audit' => [
26
+ __( 'Front page loaded', 'wp-simple-firewall' ),
27
+ ],
28
  ],
29
+ 'loginpage_load' => [
30
+ 'name' => sprintf( '%s: %s', __( 'Loaded', 'wp-simple-firewall' ),
31
+ __( 'Login Page', 'wp-simple-firewall' ) ),
32
+ 'audit' => [
33
+ __( 'Login page loaded', 'wp-simple-firewall' ),
34
+ ],
35
+ ],
36
+ 'recaptcha_success' => [
37
+ 'name' => __( 'CAPTCHA Pass', 'wp-simple-firewall' ),
38
+ 'audit' => [
39
+ __( 'CAPTCHA test successful.', 'wp-simple-firewall' ),
40
+ ],
41
+ ],
42
+ 'recaptcha_fail' => [
43
+ 'name' => __( 'CAPTCHA Fail', 'wp-simple-firewall' ),
44
+ 'audit' => [
45
+ __( 'CAPTCHA test failed.', 'wp-simple-firewall' ),
46
+ ],
47
  ],
48
+ 'test_cron_run' => [
49
+ 'name' => __( 'Test Cron Run', 'wp-simple-firewall' ),
50
+ 'audit' => [
51
+ __( 'Test WP Cron ran successfully.', 'wp-simple-firewall' ),
52
+ ],
53
  ],
54
+ 'import_notify_sent' => [
55
+ 'name' => __( 'Import Notify Sent', 'wp-simple-firewall' ),
56
+ 'audit' => [
57
+ __( 'Sent notifications to whitelisted sites for required options import.', 'wp-simple-firewall' ),
58
+ ],
59
+ ],
60
+ 'import_notify_received' => [
61
+ 'name' => __( 'Import Notify Received', 'wp-simple-firewall' ),
62
+ 'audit' => [
63
+ __( 'Received notification that options import required.', 'wp-simple-firewall' ),
64
+ __( 'Current master site: {{master_site}}', 'wp-simple-firewall' ),
65
+ ],
66
  ],
67
+ 'options_exported' => [
68
+ 'name' => __( 'Options Exported', 'wp-simple-firewall' ),
69
+ 'audit' => [
70
+ __( 'Options exported to site: {{site}}', 'wp-simple-firewall' ),
71
+ ],
72
  ],
73
+ 'options_imported' => [
74
+ 'name' => __( 'Options Imported', 'wp-simple-firewall' ),
75
+ 'audit' => [
76
+ __( 'Options exported from site: {{site}}', 'wp-simple-firewall' ),
77
+ ],
78
  ],
79
+ 'whitelist_site_added' => [
80
+ 'name' => __( 'Whitelist Site Added', 'wp-simple-firewall' ),
81
+ 'audit' => [
82
+ __( 'Site added to export white list: {{site}}', 'wp-simple-firewall' ),
83
+ ],
84
  ],
85
+ 'whitelist_site_removed' => [
86
+ 'name' => __( 'Whitelist Site Removed', 'wp-simple-firewall' ),
87
+ 'audit' => [
88
+ __( 'Site removed from export white list: {{site}}', 'wp-simple-firewall' ),
89
+ ],
90
  ],
91
+ 'master_url_set' => [
92
+ 'name' => __( 'Whitelist Site Removed', 'wp-simple-firewall' ),
93
+ 'audit' => [
94
+ __( 'Master Site URL set: {{site}}', 'wp-simple-firewall' ),
95
+ ],
96
  ],
97
+ 'antibot_pass' => [
98
+ 'name' => __( 'AntiBot Pass', 'wp-simple-firewall' ),
99
+ 'audit' => [
100
+ __( 'Request passed the AntiBot Test with a Visitor Score of "{{score}}" (minimum score: {{minimum}}).', 'wp-simple-firewall' ),
101
+ ],
102
  ],
103
+ 'antibot_fail' => [
104
+ 'name' => __( 'AntiBot Fail', 'wp-simple-firewall' ),
105
+ 'audit' => [
106
+ __( 'Request failed the AntiBot Test with a Visitor Score of "{{score}}" (minimum score: {{minimum}}).', 'wp-simple-firewall' ),
107
+ ],
108
  ],
109
  ];
110
  }
111
 
112
+ /**
113
+ * @inheritDoc
114
+ */
115
+ protected function getAdditionalDisplayStrings() :array {
116
+ return [
117
+ 'actions_title' => __( 'Plugin Actions', 'wp-simple-firewall' ),
118
+ 'actions_summary' => __( 'E.g. Import/Export', 'wp-simple-firewall' ),
119
+ ];
120
+ }
121
+
122
  /**
123
  * @param string $section
124
  * @return array
src/lib/src/Modules/Plugin/UI.php CHANGED
@@ -66,15 +66,15 @@ class UI extends BaseShield\UI {
66
  }
67
 
68
  /**
69
- * @param array $aOptParams
70
  * @return array
71
  */
72
- protected function buildOptionForUi( $aOptParams ) {
73
- $aOptParams = parent::buildOptionForUi( $aOptParams );
74
- if ( $aOptParams[ 'key' ] === 'visitor_address_source' ) {
75
  $newOptions = [];
76
  $ipDetector = Services::IP()->getIpDetector();
77
- foreach ( $aOptParams[ 'value_options' ] as $valKey => $source ) {
78
  if ( $valKey == 'AUTO_DETECT_IP' ) {
79
  $newOptions[ $valKey ] = $source;
80
  }
@@ -85,9 +85,9 @@ class UI extends BaseShield\UI {
85
  }
86
  }
87
  }
88
- $aOptParams[ 'value_options' ] = $newOptions;
89
  }
90
- return $aOptParams;
91
  }
92
 
93
  protected function getSectionWarnings( string $section ) :array {
66
  }
67
 
68
  /**
69
+ * @param array $option
70
  * @return array
71
  */
72
+ protected function buildOptionForUi( $option ) {
73
+ $option = parent::buildOptionForUi( $option );
74
+ if ( $option[ 'key' ] === 'visitor_address_source' ) {
75
  $newOptions = [];
76
  $ipDetector = Services::IP()->getIpDetector();
77
+ foreach ( $option[ 'value_options' ] as $valKey => $source ) {
78
  if ( $valKey == 'AUTO_DETECT_IP' ) {
79
  $newOptions[ $valKey ] = $source;
80
  }
85
  }
86
  }
87
  }
88
+ $option[ 'value_options' ] = $newOptions;
89
  }
90
+ return $option;
91
  }
92
 
93
  protected function getSectionWarnings( string $section ) :array {
src/lib/src/Modules/Plugin/Upgrade.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
 
6
 
7
  class Upgrade extends Base\Upgrade {
8
 
@@ -11,4 +12,22 @@ class Upgrade extends Base\Upgrade {
11
  $mod = $this->getMod();
12
  $mod->deleteAllPluginCrons();
13
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class Upgrade extends Base\Upgrade {
9
 
12
  $mod = $this->getMod();
13
  $mod->deleteAllPluginCrons();
14
  }
15
+
16
+ protected function upgrade_1200() {
17
+ // remove old tables that have somehow been missed in the past.
18
+ $tables = [
19
+ 'geoip',
20
+ 'reporting',
21
+ 'spambot_comments_filter',
22
+ 'statistics',
23
+ ];
24
+
25
+ $WPDB = Services::WpDb();
26
+ foreach ( $tables as $table ) {
27
+ $table = sprintf( '%s%s%s', $WPDB->getPrefix(), $this->getCon()->getOptionStoragePrefix(), $table );
28
+ if ( $WPDB->getIfTableExists( $table ) ) {
29
+ $WPDB->doDropTable( $table );
30
+ }
31
+ }
32
+ }
33
  }
src/lib/src/Modules/Plugin/WpCli/ForceOff.php CHANGED
@@ -32,11 +32,16 @@ class ForceOff extends BaseWpCliCmd {
32
  ] ) );
33
  }
34
 
35
- public function cmdForceOff( $null, $aA ) {
 
 
 
 
 
36
  $FS = Services::WpFs();
37
  $path = path_join( $this->getCon()->getRootDir(), 'forceoff' );
38
 
39
- switch ( $aA[ 'action' ] ) {
40
  case 'query':
41
  if ( $FS->exists( $path ) ) {
42
  WP_CLI::log( '`forceoff` file is present.' );
32
  ] ) );
33
  }
34
 
35
+ /**
36
+ * @param $null
37
+ * @param $args
38
+ * @throws WP_CLI\ExitException
39
+ */
40
+ public function cmdForceOff( $null, $args ) {
41
  $FS = Services::WpFs();
42
  $path = path_join( $this->getCon()->getRootDir(), 'forceoff' );
43
 
44
+ switch ( $args[ 'action' ] ) {
45
  case 'query':
46
  if ( $FS->exists( $path ) ) {
47
  WP_CLI::log( '`forceoff` file is present.' );
src/lib/src/Modules/Plugin/WpCli/ToggleDebug.php CHANGED
@@ -32,6 +32,11 @@ class ToggleDebug extends BaseWpCliCmd {
32
  ] ) );
33
  }
34
 
 
 
 
 
 
35
  public function cmdDebugMode( $null, $args ) {
36
  $debugMode = ( new DebugMode() )->setCon( $this->getCon() );
37
 
32
  ] ) );
33
  }
34
 
35
+ /**
36
+ * @param $null
37
+ * @param array $args
38
+ * @throws WP_CLI\ExitException
39
+ */
40
  public function cmdDebugMode( $null, $args ) {
41
  $debugMode = ( new DebugMode() )->setCon( $this->getCon() );
42
 
src/lib/src/Modules/Reporting/Charts/BaseBuildChartData.php CHANGED
@@ -50,10 +50,9 @@ class BaseBuildChartData {
50
  $req = $this->getChartRequest();
51
  $legend = [];
52
  if ( !$req->combine_events ) {
53
- /** @var Strings $strings */
54
- $strings = $this->getCon()->getModule_Events()->getStrings();
55
  foreach ( $req->events as $event ) {
56
- $legend[] = $strings->getEventName( $event );
57
  }
58
  }
59
  return $legend;
50
  $req = $this->getChartRequest();
51
  $legend = [];
52
  if ( !$req->combine_events ) {
53
+ $srvEvents = $this->getCon()->loadEventsService();
 
54
  foreach ( $req->events as $event ) {
55
+ $legend[] = $srvEvents->getEventName( $event );
56
  }
57
  }
58
  return $legend;
src/lib/src/Modules/Reporting/Debug.php CHANGED
@@ -10,5 +10,6 @@ class Debug extends Modules\Base\Debug {
10
  /** @var ModCon $mod */
11
  $mod = $this->getMod();
12
  $mod->getReportingController()->runHourlyCron();
 
13
  }
14
  }
10
  /** @var ModCon $mod */
11
  $mod = $this->getMod();
12
  $mod->getReportingController()->runHourlyCron();
13
+ die( 'finish' );
14
  }
15
  }
src/lib/src/Modules/Reporting/Lib/ReportingController.php CHANGED
@@ -2,17 +2,14 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib;
4
 
5
- use FernleafSystems\Utilities\Logic\ExecOnce;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Crons\PluginCronsConsumer;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Reports as DBReports;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules;
9
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports\Build;
10
  use FernleafSystems\Wordpress\Services\Services;
11
 
12
- class ReportingController {
13
 
14
- use Modules\ModConsumer;
15
- use ExecOnce;
16
  use PluginCronsConsumer;
17
 
18
  protected function canRun() :bool {
@@ -37,10 +34,16 @@ class ReportingController {
37
 
38
  if ( $opts->getFrequencyAlert() !== 'disabled' ) {
39
  try {
40
- $alertReport = $this->buildReportAlerts();
41
- if ( !empty( $alertReport->content ) ) {
42
- $this->storeReportRecord( $alertReport );
43
- $reports[] = $alertReport;
 
 
 
 
 
 
44
  }
45
  }
46
  catch ( \Exception $e ) {
@@ -49,10 +52,16 @@ class ReportingController {
49
 
50
  if ( $opts->getFrequencyInfo() !== 'disabled' ) {
51
  try {
52
- $infoReport = $this->buildReportInfo();
53
- if ( !empty( $infoReport->content ) ) {
54
- $this->storeReportRecord( $infoReport );
55
- $reports[] = $infoReport;
 
 
 
 
 
 
56
  }
57
  }
58
  catch ( \Exception $e ) {
@@ -151,6 +160,12 @@ class ReportingController {
151
  ]
152
  ]
153
  );
 
 
 
 
 
 
154
  }
155
  catch ( \Exception $e ) {
156
  error_log( $e->getMessage() );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Crons\PluginCronsConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Reports as DBReports;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports\Build;
9
  use FernleafSystems\Wordpress\Services\Services;
10
 
11
+ class ReportingController extends Modules\Base\Common\ExecOnceModConsumer {
12
 
 
 
13
  use PluginCronsConsumer;
14
 
15
  protected function canRun() :bool {
34
 
35
  if ( $opts->getFrequencyAlert() !== 'disabled' ) {
36
  try {
37
+ $report = $this->buildReportAlerts();
38
+ if ( !empty( $report->content ) ) {
39
+ $this->storeReportRecord( $report );
40
+ $reports[] = $report;
41
+ $this->getCon()->fireEvent( 'report_generated', [
42
+ 'audit_params' => [
43
+ 'type' => 'alert',
44
+ 'interval' => $report->interval,
45
+ ]
46
+ ] );
47
  }
48
  }
49
  catch ( \Exception $e ) {
52
 
53
  if ( $opts->getFrequencyInfo() !== 'disabled' ) {
54
  try {
55
+ $report = $this->buildReportInfo();
56
+ if ( !empty( $report->content ) ) {
57
+ $this->storeReportRecord( $report );
58
+ $reports[] = $report;
59
+ $this->getCon()->fireEvent( 'report_generated', [
60
+ 'audit_params' => [
61
+ 'type' => 'info',
62
+ 'interval' => $report->interval,
63
+ ]
64
+ ] );
65
  }
66
  }
67
  catch ( \Exception $e ) {
160
  ]
161
  ]
162
  );
163
+
164
+ $this->getCon()->fireEvent( 'report_sent', [
165
+ 'audit_params' => [
166
+ 'medium' => 'email',
167
+ ]
168
+ ] );
169
  }
170
  catch ( \Exception $e ) {
171
  error_log( $e->getMessage() );
src/lib/src/Modules/Reporting/Strings.php CHANGED
@@ -7,18 +7,22 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
7
  class Strings extends Base\Strings {
8
 
9
  /**
10
- * @return string[][]
11
  */
12
- protected function getAuditMessages() :array {
13
  return [
14
- 'lic_check_success' => [
15
- __( 'Pro License check succeeded.', 'wp-simple-firewall' )
 
 
 
 
16
  ],
17
- 'lic_fail_email' => [
18
- __( 'License check failed. Sending Warning Email.', 'wp-simple-firewall' )
19
- ],
20
- 'lic_fail_deactivate' => [
21
- __( 'License check failed. Deactivating Pro.', 'wp-simple-firewall' )
22
  ],
23
  ];
24
  }
@@ -29,33 +33,33 @@ class Strings extends Base\Strings {
29
  * @throws \Exception
30
  */
31
  public function getOptionStrings( string $key ) :array {
32
- $oCon = $this->getCon();
33
 
34
  switch ( $key ) {
35
 
36
  case 'frequency_alert' :
37
- $sName = __( 'Alert Frequency', 'wp-simple-firewall' );
38
- $sSummary = __( 'How Often Important Alerts Will Be Sent To You', 'wp-simple-firewall' );
39
- $sDescription = [
40
  __( 'Choose when you should be sent important and critical alerts about your site security.', 'wp-simple-firewall' ),
41
  __( 'Critical alerts are typically results from your most recent site scans.', 'wp-simple-firewall' )
42
  ];
43
- if ( !$oCon->isPremiumActive() ) {
44
- $sDescription[] = __( 'If you wish to receive alerts more quickly, please consider upgrading to ShieldPRO.', 'wp-simple-firewall' );
45
- $sDescription[] = sprintf( '<a href="%s" target="_blank">%s</a>', 'https://shsec.io/shieldgoprofeature', __( 'Upgrade to ShieldPRO', 'wp-simple-firewall' ) );
46
  }
47
  break;
48
 
49
  case 'frequency_info' :
50
- $sName = __( 'Info Frequency', 'wp-simple-firewall' );
51
- $sSummary = __( 'How Often Informational Reports Will Be Sent To You', 'wp-simple-firewall' );
52
- $sDescription = [
53
  __( 'Choose when you should be sent non-critical information and reports about your site security.', 'wp-simple-firewall' ),
54
  __( 'Information and reports are typically statistics.', 'wp-simple-firewall' )
55
  ];
56
- if ( !$oCon->isPremiumActive() ) {
57
- $sDescription[] = __( 'If you wish to receive reports more often, please consider upgrading to ShieldPRO.', 'wp-simple-firewall' );
58
- $sDescription[] = sprintf( '<a href="%s" target="_blank">%s</a>', 'https://shsec.io/shieldgoprofeature', __( 'Upgrade to ShieldPRO', 'wp-simple-firewall' ) );
59
  }
60
  break;
61
 
@@ -64,9 +68,9 @@ class Strings extends Base\Strings {
64
  }
65
 
66
  return [
67
- 'name' => $sName,
68
- 'summary' => $sSummary,
69
- 'description' => $sDescription,
70
  ];
71
  }
72
 
7
  class Strings extends Base\Strings {
8
 
9
  /**
10
+ * @inheritDoc
11
  */
12
+ public function getEventStrings() :array {
13
  return [
14
+ 'report_generated' => [
15
+ 'name' => __( 'Report Generated', 'wp-simple-firewall' ),
16
+ 'audit' => [
17
+ __( 'Report Generated.', 'wp-simple-firewall' ),
18
+ __( 'Type: {{type}}; Interval: {{interval}};', 'wp-simple-firewall' ),
19
+ ],
20
  ],
21
+ 'report_sent' => [
22
+ 'name' => __( 'Report Sent', 'wp-simple-firewall' ),
23
+ 'audit' => [
24
+ __( 'Report Sent (via {{medium}}).', 'wp-simple-firewall' ),
25
+ ],
26
  ],
27
  ];
28
  }
33
  * @throws \Exception
34
  */
35
  public function getOptionStrings( string $key ) :array {
36
+ $con = $this->getCon();
37
 
38
  switch ( $key ) {
39
 
40
  case 'frequency_alert' :
41
+ $name = __( 'Alert Frequency', 'wp-simple-firewall' );
42
+ $summary = __( 'How Often Important Alerts Will Be Sent To You', 'wp-simple-firewall' );
43
+ $description = [
44
  __( 'Choose when you should be sent important and critical alerts about your site security.', 'wp-simple-firewall' ),
45
  __( 'Critical alerts are typically results from your most recent site scans.', 'wp-simple-firewall' )
46
  ];
47
+ if ( !$con->isPremiumActive() ) {
48
+ $description[] = __( 'If you wish to receive alerts more quickly, please consider upgrading to ShieldPRO.', 'wp-simple-firewall' );
49
+ $description[] = sprintf( '<a href="%s" target="_blank">%s</a>', 'https://shsec.io/shieldgoprofeature', __( 'Upgrade to ShieldPRO', 'wp-simple-firewall' ) );
50
  }
51
  break;
52
 
53
  case 'frequency_info' :
54
+ $name = __( 'Info Frequency', 'wp-simple-firewall' );
55
+ $summary = __( 'How Often Informational Reports Will Be Sent To You', 'wp-simple-firewall' );
56
+ $description = [
57
  __( 'Choose when you should be sent non-critical information and reports about your site security.', 'wp-simple-firewall' ),
58
  __( 'Information and reports are typically statistics.', 'wp-simple-firewall' )
59
  ];
60
+ if ( !$con->isPremiumActive() ) {
61
+ $description[] = __( 'If you wish to receive reports more often, please consider upgrading to ShieldPRO.', 'wp-simple-firewall' );
62
+ $description[] = sprintf( '<a href="%s" target="_blank">%s</a>', 'https://shsec.io/shieldgoprofeature', __( 'Upgrade to ShieldPRO', 'wp-simple-firewall' ) );
63
  }
64
  break;
65
 
68
  }
69
 
70
  return [
71
+ 'name' => $name,
72
+ 'summary' => $summary,
73
+ 'description' => $description,
74
  ];
75
  }
76
 
src/lib/src/Modules/Reporting/UI.php CHANGED
@@ -44,14 +44,13 @@ class UI extends BaseShield\UI {
44
  * @return array
45
  */
46
  private function buildPossibleEvents() :array {
47
- $eventsMod = $this->getCon()->getModule_Events();
48
- /** @var Events\Strings $strings */
49
- $strings = $eventsMod->getStrings();
50
  return array_intersect_key(
51
- $strings->getEventNames(),
52
- array_flip( $eventsMod->getDbHandler_Events()
53
- ->getQuerySelector()
54
- ->getDistinctForColumn( 'event' ) )
 
 
55
  );
56
  }
57
 
44
  * @return array
45
  */
46
  private function buildPossibleEvents() :array {
 
 
 
47
  return array_intersect_key(
48
+ $this->getCon()->loadEventsService()->getEventNames(),
49
+ array_flip( $this->getCon()
50
+ ->getModule_Events()
51
+ ->getDbHandler_Events()
52
+ ->getQuerySelector()
53
+ ->getDistinctForColumn( 'event' ) )
54
  );
55
  }
56
 
src/lib/src/Modules/SecurityAdmin/Strings.php CHANGED
@@ -7,15 +7,21 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
7
  class Strings extends Base\Strings {
8
 
9
  /**
10
- * @return string[][]
11
  */
12
- protected function getAuditMessages() :array {
13
  return [
14
- 'key_success' => [
15
- __( 'Successful authentication using Security Admin PIN.', 'wp-simple-firewall' ),
 
 
 
16
  ],
17
  'key_fail' => [
18
- __( 'Failed authentication using Security Admin PIN.', 'wp-simple-firewall' ),
 
 
 
19
  ],
20
  ];
21
  }
@@ -31,9 +37,9 @@ class Strings extends Base\Strings {
31
  switch ( $section ) {
32
 
33
  case 'section_enable_plugin_feature_admin_access_restriction' :
34
- $sTitleShort = sprintf( '%s/%s', __( 'On', 'wp-simple-firewall' ), __( 'Off', 'wp-simple-firewall' ) );
35
- $sTitle = sprintf( __( 'Enable Module: %s', 'wp-simple-firewall' ), __( 'Security Admin', 'wp-simple-firewall' ) );
36
- $aSummary = [
37
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Restricts access to this plugin preventing unauthorized changes to your security settings.', 'wp-simple-firewall' ) ),
38
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), sprintf( __( 'Keep the %s feature turned on.', 'wp-simple-firewall' ), __( 'Security Admin', 'wp-simple-firewall' ) ) ),
39
  __( 'You need to also enter a new Security PIN to enable this feature.', 'wp-simple-firewall' ),
@@ -41,26 +47,26 @@ class Strings extends Base\Strings {
41
  break;
42
 
43
  case 'section_security_admin_settings' :
44
- $sTitle = __( 'Security Admin Restriction Settings', 'wp-simple-firewall' );
45
- $aSummary = [
46
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Restricts access to this plugin preventing unauthorized changes to your security settings.', 'wp-simple-firewall' ) ),
47
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) ),
48
  ];
49
- $sTitleShort = __( 'Security Admin Settings', 'wp-simple-firewall' );
50
  break;
51
 
52
  case 'section_admin_access_restriction_areas' :
53
- $sTitle = __( 'Security Admin Restriction Zones', 'wp-simple-firewall' );
54
- $aSummary = [
55
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Restricts access to key WordPress areas for all users not authenticated with the Security Admin Access system.', 'wp-simple-firewall' ) ),
56
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) ),
57
  ];
58
- $sTitleShort = __( 'Access Restriction Zones', 'wp-simple-firewall' );
59
  break;
60
 
61
  case 'section_whitelabel' :
62
- $sTitle = __( 'White Label', 'wp-simple-firewall' );
63
- $aSummary = [
64
  sprintf( '%s - %s',
65
  __( 'Purpose', 'wp-simple-firewall' ),
66
  sprintf( __( 'Rename and re-brand the %s plugin for your client site installations.', 'wp-simple-firewall' ),
@@ -72,7 +78,7 @@ class Strings extends Base\Strings {
72
  $sPlugName )
73
  )
74
  ];
75
- $sTitleShort = __( 'White Label', 'wp-simple-firewall' );
76
  break;
77
 
78
  default:
@@ -80,9 +86,9 @@ class Strings extends Base\Strings {
80
  }
81
 
82
  return [
83
- 'title' => $sTitle,
84
- 'title_short' => $sTitleShort,
85
- 'summary' => ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : [],
86
  ];
87
  }
88
 
7
  class Strings extends Base\Strings {
8
 
9
  /**
10
+ * @inheritDoc
11
  */
12
+ public function getEventStrings() :array {
13
  return [
14
+ 'key_success' => [
15
+ 'name' => __( 'Security PIN Pass', 'wp-simple-firewall' ),
16
+ 'audit' => [
17
+ __( 'Security PIN authentication successful.', 'wp-simple-firewall' ),
18
+ ],
19
  ],
20
  'key_fail' => [
21
+ 'name' => __( 'Security PIN Fail', 'wp-simple-firewall' ),
22
+ 'audit' => [
23
+ __( 'Security PIN authentication failed.', 'wp-simple-firewall' ),
24
+ ],
25
  ],
26
  ];
27
  }
37
  switch ( $section ) {
38
 
39
  case 'section_enable_plugin_feature_admin_access_restriction' :
40
+ $titleShort = sprintf( '%s/%s', __( 'On', 'wp-simple-firewall' ), __( 'Off', 'wp-simple-firewall' ) );
41
+ $title = sprintf( __( 'Enable Module: %s', 'wp-simple-firewall' ), __( 'Security Admin', 'wp-simple-firewall' ) );
42
+ $summary = [
43
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Restricts access to this plugin preventing unauthorized changes to your security settings.', 'wp-simple-firewall' ) ),
44
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), sprintf( __( 'Keep the %s feature turned on.', 'wp-simple-firewall' ), __( 'Security Admin', 'wp-simple-firewall' ) ) ),
45
  __( 'You need to also enter a new Security PIN to enable this feature.', 'wp-simple-firewall' ),
47
  break;
48
 
49
  case 'section_security_admin_settings' :
50
+ $title = __( 'Security Admin Restriction Settings', 'wp-simple-firewall' );
51
+ $summary = [
52
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Restricts access to this plugin preventing unauthorized changes to your security settings.', 'wp-simple-firewall' ) ),
53
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) ),
54
  ];
55
+ $titleShort = __( 'Security Admin Settings', 'wp-simple-firewall' );
56
  break;
57
 
58
  case 'section_admin_access_restriction_areas' :
59
+ $title = __( 'Security Admin Restriction Zones', 'wp-simple-firewall' );
60
+ $summary = [
61
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Restricts access to key WordPress areas for all users not authenticated with the Security Admin Access system.', 'wp-simple-firewall' ) ),
62
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) ),
63
  ];
64
+ $titleShort = __( 'Access Restriction Zones', 'wp-simple-firewall' );
65
  break;
66
 
67
  case 'section_whitelabel' :
68
+ $title = __( 'White Label', 'wp-simple-firewall' );
69
+ $summary = [
70
  sprintf( '%s - %s',
71
  __( 'Purpose', 'wp-simple-firewall' ),
72
  sprintf( __( 'Rename and re-brand the %s plugin for your client site installations.', 'wp-simple-firewall' ),
78
  $sPlugName )
79
  )
80
  ];
81
+ $titleShort = __( 'White Label', 'wp-simple-firewall' );
82
  break;
83
 
84
  default:
86
  }
87
 
88
  return [
89
+ 'title' => $title,
90
+ 'title_short' => $titleShort,
91
+ 'summary' => $summary,
92
  ];
93
  }
94
 
src/lib/src/Modules/Sessions/Lib/SessionController.php CHANGED
@@ -50,11 +50,20 @@ class SessionController {
50
  public function terminateCurrentSession() :bool {
51
  $current = $this->getCurrent();
52
 
53
- $success = $current instanceof Session\EntryVO
54
  && ( new Ops\Terminate() )
55
  ->setMod( $this->getMod() )
56
  ->byRecordId( $current->id );
57
 
 
 
 
 
 
 
 
 
 
58
  $this->current = null;
59
  $this->getCon()->clearSession();
60
 
@@ -82,7 +91,12 @@ class SessionController {
82
  $insert = $mod->getDbHandler_Sessions()->getQueryInserter();
83
  $success = $insert->create( $sessionID, $user->user_login );
84
 
85
- $this->getCon()->fireEvent( 'session_start' );
 
 
 
 
 
86
  }
87
  return $success;
88
  }
@@ -101,20 +115,6 @@ class SessionController {
101
  return (string)$this->sessionID;
102
  }
103
 
104
- public function queryCreateSession( string $sessionID, \WP_User $user ) :bool {
105
- /** @var ModCon $mod */
106
- $mod = $this->getMod();
107
-
108
- $success = false;
109
- if ( !empty( $sessionID ) && !empty( $user->user_login ) ) {
110
- $this->getCon()->fireEvent( 'session_start' );
111
- /** @var Session\Insert $insert */
112
- $insert = $mod->getDbHandler_Sessions()->getQueryInserter();
113
- $success = $insert->create( $sessionID, $user->user_login );
114
- }
115
- return $success;
116
- }
117
-
118
  /**
119
  * @param string $username
120
  * @param string $sessionID
50
  public function terminateCurrentSession() :bool {
51
  $current = $this->getCurrent();
52
 
53
+ $success = !empty( $current )
54
  && ( new Ops\Terminate() )
55
  ->setMod( $this->getMod() )
56
  ->byRecordId( $current->id );
57
 
58
+ if ( $success ) {
59
+ $this->getCon()->fireEvent( 'session_terminate_current', [
60
+ 'audit_params' => [
61
+ 'user_login' => $current->wp_username,
62
+ 'session_id' => $current->session_id,
63
+ ]
64
+ ] );
65
+ }
66
+
67
  $this->current = null;
68
  $this->getCon()->clearSession();
69
 
91
  $insert = $mod->getDbHandler_Sessions()->getQueryInserter();
92
  $success = $insert->create( $sessionID, $user->user_login );
93
 
94
+ $this->getCon()->fireEvent( 'session_start', [
95
+ 'audit_params' => [
96
+ 'user_login' => $user->user_login,
97
+ 'session_id' => $sessionID
98
+ ]
99
+ ] );
100
  }
101
  return $success;
102
  }
115
  return (string)$this->sessionID;
116
  }
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  /**
119
  * @param string $username
120
  * @param string $sessionID
src/lib/src/Modules/Sessions/Strings.php CHANGED
@@ -6,6 +6,38 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
 
7
  class Strings extends Base\Strings {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  /**
10
  * @param string $section
11
  * @return array
6
 
7
  class Strings extends Base\Strings {
8
 
9
+ /**
10
+ * @inheritDoc
11
+ */
12
+ public function getEventStrings() :array {
13
+ return [
14
+ 'session_start' => [
15
+ 'name' => __( 'Session Started', 'wp-simple-firewall' ),
16
+ 'audit' => [
17
+ __( 'Session started for user ({{user_login}}) with session ID {{session_id}}.', 'wp-simple-firewall' ),
18
+ ],
19
+ ],
20
+ 'session_terminate' => [
21
+ 'name' => __( 'Session Terminated', 'wp-simple-firewall' ),
22
+ 'audit' => [
23
+ __( 'Session terminated.', 'wp-simple-firewall' ),
24
+ ],
25
+ ],
26
+ 'session_terminate_current' => [
27
+ 'name' => __( 'Current Session Terminated', 'wp-simple-firewall' ),
28
+ 'audit' => [
29
+ __( 'Current session terminated for user ({{user_login}}) with session ID {{session_id}}.', 'wp-simple-firewall' ),
30
+ ],
31
+ ],
32
+ 'login_success' => [
33
+ 'name' => __( 'Login Success', 'wp-simple-firewall' ),
34
+ 'audit' => [
35
+ __( 'Login successful.', 'wp-simple-firewall' ),
36
+ ],
37
+ ],
38
+ ];
39
+ }
40
+
41
  /**
42
  * @param string $section
43
  * @return array
src/lib/src/Modules/Sessions/Upgrade.php CHANGED
@@ -3,20 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Sessions;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
- use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class Upgrade extends Base\Upgrade {
9
 
10
- /**
11
- * Support full-length IPv6 addresses
12
- */
13
- protected function upgrade_922() {
14
- /** @var ModCon $mod */
15
- $mod = $this->getMod();
16
- $schema = $mod->getDbHandler_Sessions()->getTableSchema();
17
- Services::WpDb()->doSql(
18
- sprintf( "ALTER TABLE `%s` MODIFY `%s` %s;",
19
- $schema->table, 'ip', $schema->enumerateColumns()[ 'ip' ] )
20
- );
21
- }
22
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Sessions;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
 
6
 
7
  class Upgrade extends Base\Upgrade {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  }
src/lib/src/Modules/Statistics/Options.php DELETED
@@ -1,15 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Statistics;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
6
-
7
- class Options extends BaseShield\Options {
8
-
9
- /**
10
- * @return string
11
- */
12
- public function getDbTable_Tallys() {
13
- return $this->getCon()->prefixOption( $this->getDef( 'statistics_table_name' ) );
14
- }
15
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Statistics/Strings.php DELETED
@@ -1,58 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Statistics;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
-
7
- class Strings extends Base\Strings {
8
-
9
- /**
10
- * @param string $section
11
- * @return array
12
- * @throws \Exception
13
- */
14
- public function getSectionStrings( string $section ) :array {
15
- $sModName = $this->getMod()->getMainFeatureName();
16
-
17
- switch ( $section ) {
18
-
19
- case 'section_enable_plugin_feature_statistics' :
20
- $sTitleShort = sprintf( '%s/%s', __( 'On', 'wp-simple-firewall' ), __( 'Off', 'wp-simple-firewall' ) );
21
- $sTitle = sprintf( __( 'Enable Module: %s', 'wp-simple-firewall' ), $sModName );
22
- $aSummary = [
23
- sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Helps you see at a glance how effective the plugin has been.', 'wp-simple-firewall' ) ),
24
- sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), sprintf( __( 'Keep the %s feature turned on.', 'wp-simple-firewall' ), $sModName ) )
25
- ];
26
- break;
27
-
28
- case 'section_enable_plugin_feature_reporting' :
29
- $sTitleShort = sprintf( '%s/%s', __( 'On', 'wp-simple-firewall' ), __( 'Off', 'wp-simple-firewall' ) );
30
- $sTitle = sprintf( __( 'Enable Module: %s', 'wp-simple-firewall' ), $sModName );
31
- $aSummary = [
32
- sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'To track stats and issue reports.', 'wp-simple-firewall' ) ),
33
- sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), sprintf( __( 'Keep the %s feature turned on.', 'wp-simple-firewall' ), $sModName ) )
34
- ];
35
- break;
36
-
37
- case 'section_stats_sharing' :
38
- $sTitle = __( 'Statistics Sharing', 'wp-simple-firewall' );
39
- $aSummary = [
40
- sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Help us to provide globally accessible statistics on the effectiveness of the plugin.', 'wp-simple-firewall' ) ),
41
- sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Enabling this option helps us improve our plugin over time.', 'wp-simple-firewall' ) )
42
- .__( 'All statistics data collection is 100% anonymous.', 'wp-simple-firewall' ).__( 'Neither we nor anyone else will be able to trace the data back to the originating site.', 'wp-simple-firewall' )
43
-
44
- ];
45
- $sTitleShort = __( 'Sharing', 'wp-simple-firewall' );
46
- break;
47
-
48
- default:
49
- return parent::getSectionStrings( $section );
50
- }
51
-
52
- return [
53
- 'title' => $sTitle,
54
- 'title_short' => $sTitleShort,
55
- 'summary' => ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : [],
56
- ];
57
- }
58
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Traffic/AjaxHandler.php CHANGED
@@ -9,8 +9,9 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
9
  protected function processAjaxAction( string $action ) :array {
10
 
11
  switch ( $action ) {
12
- case 'render_table_traffic':
13
- $response = $this->ajaxExec_BuildTableTraffic();
 
14
  break;
15
 
16
  default:
@@ -20,15 +21,18 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
20
  return $response;
21
  }
22
 
23
- private function ajaxExec_BuildTableTraffic() :array {
24
- /** @var ModCon $mod */
25
- $mod = $this->getMod();
26
- return [
27
- 'success' => true,
28
- 'html' => ( new Shield\Tables\Build\Traffic() )
29
- ->setMod( $mod )
30
- ->setDbHandler( $mod->getDbHandler_Traffic() )
31
- ->render()
32
- ];
 
 
 
33
  }
34
  }
9
  protected function processAjaxAction( string $action ) :array {
10
 
11
  switch ( $action ) {
12
+
13
+ case 'traffictable_action':
14
+ $response = $this->ajaxExec_TrafficTableAction();
15
  break;
16
 
17
  default:
21
  return $response;
22
  }
23
 
24
+ private function ajaxExec_TrafficTableAction() :array {
25
+ try {
26
+ return ( new Lib\TrafficTable\DelegateAjaxHandler() )
27
+ ->setMod( $this->getMod() )
28
+ ->processAjaxAction();
29
+ }
30
+ catch ( \Exception $e ) {
31
+ return [
32
+ 'success' => false,
33
+ 'page_reload' => true,
34
+ 'message' => $e->getMessage(),
35
+ ];
36
+ }
37
  }
38
  }
src/lib/src/Modules/Traffic/Lib/Limit/Limiter.php CHANGED
@@ -2,44 +2,45 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib\Limit;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
- /**
10
- * Class Limiter
11
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib\Limit
12
- */
13
- class Limiter {
14
 
15
- use ModConsumer;
 
 
 
 
16
 
17
- public function run() {
18
- /** @var Traffic\Options $oOpts */
19
- $oOpts = $this->getOptions();
20
- if ( $oOpts->isTrafficLimitEnabled() ) {
21
- add_action( 'init', [ $this, 'limit' ] );
22
- }
23
  }
24
 
25
  public function limit() {
26
  try {
27
- ( new TestIp() )
28
  ->setMod( $this->getMod() )
29
- ->runTest( Services::IP()->getRequestIp() );
 
30
  }
31
- catch ( \Exception $e ) {
32
  /** @var Traffic\Options $opts */
33
  $opts = $this->getOptions();
34
  $this->getCon()->fireEvent(
35
  'request_limit_exceeded',
36
  [
37
- 'audit' => [
38
- 'count' => $opts->getLimitRequestCount(),
39
- 'span' => $opts->getLimitTimeSpan(),
 
40
  ]
41
  ]
42
  );
43
  }
 
 
 
44
  }
45
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib\Limit;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
+ class Limiter extends ExecOnceModConsumer {
 
 
 
 
10
 
11
+ protected function canRun() :bool {
12
+ /** @var Traffic\Options $opts */
13
+ $opts = $this->getOptions();
14
+ return $opts->isTrafficLimitEnabled();
15
+ }
16
 
17
+ protected function run() {
18
+ add_action( 'init', [ $this, 'limit' ] );
 
 
 
 
19
  }
20
 
21
  public function limit() {
22
  try {
23
+ ( new TestIpLimit() )
24
  ->setMod( $this->getMod() )
25
+ ->setIP( Services::IP()->getRequestIp() )
26
+ ->run();
27
  }
28
+ catch ( RateLimitExceededException $rle ) {
29
  /** @var Traffic\Options $opts */
30
  $opts = $this->getOptions();
31
  $this->getCon()->fireEvent(
32
  'request_limit_exceeded',
33
  [
34
+ 'audit_params' => [
35
+ 'requests' => $rle->getCount(),
36
+ 'count' => $opts->getLimitRequestCount(),
37
+ 'span' => $opts->getLimitTimeSpan(),
38
  ]
39
  ]
40
  );
41
  }
42
+ catch ( \Exception $e ) {
43
+ error_log( $e->getMessage() );
44
+ }
45
  }
46
  }
src/lib/src/Modules/Traffic/Lib/Limit/RateLimitExceededException.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib\Limit;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield;
6
+
7
+ class RateLimitExceededException extends \Exception {
8
+
9
+ private $requestCount = 0;
10
+
11
+ public function __construct( string $message = "", int $requestCount = 0 ) {
12
+ $this->requestCount = $requestCount;
13
+ parent::__construct( $message );
14
+ }
15
+
16
+ public function getCount() :int {
17
+ return $this->requestCount;
18
+ }
19
+ }
src/lib/src/Modules/Traffic/Lib/Limit/TestIp.php DELETED
@@ -1,43 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib\Limit;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Traffic;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\ModCon;
8
- use FernleafSystems\Wordpress\Services\Services;
9
-
10
- /**
11
- * Class TestIp
12
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib\Limit
13
- */
14
- class TestIp {
15
-
16
- use Shield\Modules\ModConsumer;
17
-
18
- /**
19
- * @param string $sHumanIp
20
- * @return bool - true if request is allowed (i.e. request limit has not been exceeded)
21
- * @throws \Exception
22
- */
23
- public function runTest( $sHumanIp ) {
24
- /** @var ModCon $mod */
25
- $mod = $this->getMod();
26
- /** @var Shield\Modules\Traffic\Options $opts */
27
- $opts = $this->getOptions();
28
-
29
- $oNow = Services::Request()->carbon();
30
-
31
- /** @var Traffic\Select $oSel */
32
- $oSel = $mod->getDbHandler_Traffic()->getQuerySelector();
33
- $count = $oSel->filterByIp( inet_pton( $sHumanIp ) )
34
- ->filterByCreatedAt( $oNow->subSeconds( $opts->getLimitTimeSpan() )->timestamp, '>' )
35
- ->count();
36
-
37
- if ( $count > $opts->getLimitRequestCount() ) {
38
- throw new \Exception( 'Requests from IP have exceeded allowable limit.' );
39
- }
40
-
41
- return true;
42
- }
43
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Traffic/Lib/Limit/TestIpLimit.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib\Limit;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\IPRecords;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs;
8
+ use FernleafSystems\Wordpress\Services\Services;
9
+
10
+ class TestIpLimit {
11
+
12
+ use Shield\Modules\ModConsumer;
13
+ use Shield\Modules\IPs\Components\IpAddressConsumer;
14
+
15
+ /**
16
+ * @return bool - true if request is allowed (i.e. request limit has not been exceeded)
17
+ * @throws \Exception|RateLimitExceededException
18
+ */
19
+ public function run() :bool {
20
+ /** @var Shield\Modules\Traffic\Options $opts */
21
+ $opts = $this->getOptions();
22
+
23
+ if ( !empty( $this->getIP() ) ) {
24
+ $ip = ( new IPRecords() )
25
+ ->setMod( $this->getCon()->getModule_Data() )
26
+ ->loadIP( $this->getIP(), false );
27
+ $now = Services::Request()->carbon();
28
+ /** @var ReqLogs\Ops\Select $selector */
29
+ $selector = $this->getCon()
30
+ ->getModule_Data()
31
+ ->getDbH_ReqLogs()
32
+ ->getQuerySelector();
33
+ $count = $selector->filterByIP( $ip->id )
34
+ ->filterByCreatedAt( $now->subSeconds( $opts->getLimitTimeSpan() )->timestamp, '>' )
35
+ ->count();
36
+ if ( $count > $opts->getLimitRequestCount() ) {
37
+ throw new RateLimitExceededException( 'Rate limit triggered.', $count );
38
+ }
39
+ }
40
+
41
+ return true;
42
+ }
43
+ }
src/lib/src/Modules/Traffic/Lib/LogHandlers/LocalDbWriter.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib\LogHandlers;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\IPRecords;
8
+ use Monolog\Handler\AbstractProcessingHandler;
9
+
10
+ class LocalDbWriter extends AbstractProcessingHandler {
11
+
12
+ use ModConsumer;
13
+
14
+ /**
15
+ * @inheritDoc
16
+ */
17
+ protected function write( array $record ) {
18
+ try {
19
+ $this->createPrimaryLogRecord( $record );
20
+ }
21
+ catch ( \Exception $e ) {
22
+ }
23
+ }
24
+
25
+ protected function createPrimaryLogRecord( array $logData ) :bool {
26
+ $modData = $this->getCon()->getModule_Data();
27
+
28
+ $ipRecord = ( new IPRecords() )
29
+ ->setMod( $modData )
30
+ ->loadIP( $logData[ 'extra' ][ 'meta_request' ][ 'ip' ] );
31
+
32
+ $reqRecord = ( new ReqLogs\RequestRecords() )
33
+ ->setMod( $modData )
34
+ ->loadReq( $logData[ 'extra' ][ 'meta_request' ][ 'rid' ], $ipRecord->id );
35
+
36
+ // anything stored in the primary log record doesn't need stored in meta
37
+ unset( $logData[ 'extra' ][ 'meta_request' ][ 'ip' ] );
38
+ unset( $logData[ 'extra' ][ 'meta_request' ][ 'rid' ] );
39
+
40
+ $success = $modData->getDbH_ReqLogs()
41
+ ->getQueryUpdater()
42
+ ->updateById( $reqRecord->id, [
43
+ 'meta' => base64_encode( json_encode( array_merge(
44
+ $logData[ 'extra' ][ 'meta_shield' ],
45
+ $logData[ 'extra' ][ 'meta_request' ],
46
+ $logData[ 'extra' ][ 'meta_user' ],
47
+ $logData[ 'extra' ][ 'meta_wp' ]
48
+ ) ) )
49
+ ] );
50
+
51
+ if ( !$success ) {
52
+ throw new \Exception( 'Failed to insert' );
53
+ }
54
+ return true;
55
+ }
56
+ }
src/lib/src/Modules/Traffic/Lib/Logger.php CHANGED
@@ -7,6 +7,9 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
 
 
 
10
  class Logger {
11
 
12
  use ModConsumer;
@@ -25,9 +28,9 @@ class Logger {
25
 
26
  private function isRequestToBeLogged() :bool {
27
  return !$this->getCon()->plugin_deleting
28
- && apply_filters( 'shield/is_log_traffic', true )
29
- && ( !$this->isCustomExcluded() )
30
- && ( !$this->isRequestTypeExcluded() );
31
  }
32
 
33
  private function isRequestTypeExcluded() :bool {
@@ -77,31 +80,5 @@ class Logger {
77
  }
78
 
79
  private function logTraffic() {
80
- /** @var Traffic\ModCon $mod */
81
- $mod = $this->getMod();
82
- $dbh = $mod->getDbHandler_Traffic();
83
-
84
- $req = Services::Request();
85
-
86
- // For multisites that are separated by sub-domains we also show the host.
87
- $sLeadingPath = Services::WpGeneral()->isMultisite_SubdomainInstall() ? $req->getHost() : '';
88
-
89
- /** @var EntryVO $entry */
90
- $entry = $dbh->getVo();
91
-
92
- $entry->rid = $this->getCon()->getShortRequestId();
93
- $entry->uid = Services::WpUsers()->getCurrentWpUserId();
94
- $entry->ip = Services::IP()->getRequestIp();
95
- $entry->verb = $req->getMethod();
96
- $entry->path = $sLeadingPath.$req->getPath().( empty( $_GET ) ? '' : '?'.http_build_query( $_GET ) );
97
- $entry->code = http_response_code();
98
- $entry->ua = $req->getUserAgent();
99
- $entry->trans = $this->getCon()
100
- ->getModule_IPs()
101
- ->loadOffenseTracker()
102
- ->getOffenseCount() > 0 ? 1 : 0;
103
-
104
- $dbh->getQueryInserter()
105
- ->insert( $entry );
106
  }
107
  }
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
10
+ /**
11
+ * @deprecated 12.0
12
+ */
13
  class Logger {
14
 
15
  use ModConsumer;
28
 
29
  private function isRequestToBeLogged() :bool {
30
  return !$this->getCon()->plugin_deleting
31
+ && apply_filters( 'shield/is_log_traffic',
32
+ !$this->isCustomExcluded() && !$this->isRequestTypeExcluded()
33
+ );
34
  }
35
 
36
  private function isRequestTypeExcluded() :bool {
80
  }
81
 
82
  private function logTraffic() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  }
84
  }
src/lib/src/Modules/Traffic/Lib/Ops/ConvertLegacy.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib\Ops;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\Traffic;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs\RequestRecords;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\ModCon;
8
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\IPRecords;
9
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
10
+ use FernleafSystems\Wordpress\Services\Services;
11
+
12
+ class ConvertLegacy {
13
+
14
+ use ModConsumer;
15
+
16
+ public function run() {
17
+ /** @var ModCon $mod */
18
+ $mod = $this->getMod();
19
+ $opts = $this->getOptions();
20
+
21
+ if ( empty( $opts->getOpt( 'legacy_db_deleted_at' ) ) ) {
22
+ $this->convert();
23
+ $dbh = $mod->getDbHandler_Traffic();
24
+ if ( $dbh->getQuerySelector()->count() === 0 ) {
25
+ $opts->setOpt( 'legacy_db_deleted_at', Services::Request()->ts() );
26
+ $dbh->tableDelete();
27
+ }
28
+ }
29
+ }
30
+
31
+ private function convert() {
32
+ /** @var ModCon $mod */
33
+ $mod = $this->getMod();
34
+ $dbh = $mod->getDbHandler_Traffic();
35
+
36
+ $toDelete = [];
37
+
38
+ /** @var Traffic\EntryVO $entry */
39
+ foreach ( $dbh->getIterator() as $entry ) {
40
+
41
+ try {
42
+ $this->createPrimaryLogRecord( $entry );
43
+ }
44
+ catch ( \Exception $e ) {
45
+ }
46
+ finally {
47
+ $toDelete[] = $entry->id;
48
+ }
49
+ }
50
+
51
+ if ( !empty( $toDelete ) ) {
52
+ $dbh->getQueryDeleter()
53
+ ->addWhereIn( 'in', $toDelete )
54
+ ->query();
55
+ }
56
+ }
57
+
58
+ protected function createPrimaryLogRecord( Traffic\EntryVO $entry ) :bool {
59
+ $modData = $this->getCon()->getModule_Data();
60
+
61
+ if ( empty( $entry->rid ) || empty( $entry->ip ) ) {
62
+ throw new \Exception( 'No RID or IP' );
63
+ }
64
+
65
+ $meta = [];
66
+ foreach ( [ 'uid', 'path', 'code', 'verb', 'ua', 'trans', ] as $metaKey ) {
67
+ if ( !empty( $entry->{$metaKey} ) ) {
68
+ $meta[ ( $metaKey === 'trans' ) ? 'offense' : $metaKey ] = $entry->{$metaKey};
69
+ }
70
+ }
71
+
72
+ $ipID = ( new IPRecords() )
73
+ ->setMod( $modData )
74
+ ->loadIP( $entry->ip )
75
+ ->id;
76
+ $record = ( new RequestRecords() )
77
+ ->setMod( $modData )
78
+ ->loadReq( $entry->rid, $ipID );
79
+ $record->meta = $meta;
80
+
81
+ return $modData->getDbH_ReqLogs()
82
+ ->getQueryUpdater()
83
+ ->updateById( $record->id, [
84
+ 'meta' => $record->getRawData()[ 'meta' ],
85
+ 'created_at' => $entry->created_at
86
+ ] );
87
+ }
88
+ }
src/lib/src/Modules/Traffic/Lib/RequestLogger.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Logging\Processors;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
8
+ use FernleafSystems\Wordpress\Services\Services;
9
+ use FernleafSystems\Wordpress\Services\Utilities\Net\IpID;
10
+ use Monolog\Logger;
11
+
12
+ class RequestLogger extends ExecOnceModConsumer {
13
+
14
+ /**
15
+ * @var Logger
16
+ */
17
+ private $logger;
18
+
19
+ protected function run() {
20
+ $this->getLogger()
21
+ ->pushHandler( ( new LogHandlers\LocalDbWriter() )->setMod( $this->getMod() ) );
22
+
23
+ add_action( $this->getCon()->prefix( 'plugin_shutdown' ), function () {
24
+ if ( $this->isRequestToBeLogged() ) {
25
+ $this->getLogger()->log( 'debug', 'log request' );
26
+ }
27
+ }, 1000 ); // high enough to come after audit trail
28
+ }
29
+
30
+ private function isRequestToBeLogged() :bool {
31
+ /** @var Traffic\Options $opts */
32
+ $opts = $this->getOptions();
33
+ return !$this->getCon()->plugin_deleting
34
+ && apply_filters( 'shield/is_log_traffic',
35
+ $opts->isTrafficLoggerEnabled() && !$this->isCustomExcluded() && !$this->isRequestTypeExcluded()
36
+ );
37
+ }
38
+
39
+ public function getLogger() :Logger {
40
+ if ( !isset( $this->logger ) ) {
41
+ $this->logger = new Logger( 'request', [], [
42
+ ( new Processors\ShieldMetaProcessor() )->setCon( $this->getCon() ),
43
+ new Processors\RequestMetaProcessor(),
44
+ new Processors\UserMetaProcessor(),
45
+ new Processors\WpMetaProcessor()
46
+ ] );
47
+ }
48
+ return $this->logger;
49
+ }
50
+
51
+ private function isRequestTypeExcluded() :bool {
52
+ /** @var Traffic\Options $opts */
53
+ $opts = $this->getOptions();
54
+ $excl = $opts->getReqTypeExclusions();
55
+ $isLoggedIn = Services::WpUsers()->isUserLoggedIn();
56
+
57
+ $exclude = ( in_array( 'simple', $excl ) && count( Services::Request()->getRawRequestParams( false ) ) == 0 )
58
+ || ( in_array( 'logged_in', $excl ) && $isLoggedIn )
59
+ || ( in_array( 'ajax', $excl ) && Services::WpGeneral()->isAjax() )
60
+ || ( in_array( 'cron', $excl ) && Services::WpGeneral()->isCron() )
61
+ || ( in_array( 'server', $excl ) && $this->isThisServer() );
62
+
63
+ if ( !$exclude && !$isLoggedIn ) {
64
+ $exclude = ( in_array( 'search', $excl ) && $this->isServiceIp_Search() )
65
+ || ( in_array( 'uptime', $excl ) && $this->isServiceIp_Uptime() );
66
+ }
67
+
68
+ return $exclude;
69
+ }
70
+
71
+ private function isCustomExcluded() :bool {
72
+ /** @var Traffic\Options $opts */
73
+ $opts = $this->getOptions();
74
+ $req = Services::Request();
75
+
76
+ $agent = $req->getUserAgent();
77
+ $path = $req->getPath().( empty( $_GET ) ? '' : '?'.http_build_query( $_GET ) );
78
+
79
+ $exclude = false;
80
+ foreach ( $opts->getCustomExclusions() as $excl ) {
81
+ if ( stripos( $agent, $excl ) !== false || stripos( $path, $excl ) !== false ) {
82
+ $exclude = true;
83
+ }
84
+ }
85
+ return $exclude;
86
+ }
87
+
88
+ private function isServiceIp_Search() :bool {
89
+ return in_array( Services::IP()->getIpDetector()->getIPIdentity(),
90
+ Services::ServiceProviders()->getSearchProviders() );
91
+ }
92
+
93
+ private function isServiceIp_Uptime() :bool {
94
+ return in_array( Services::IP()->getIpDetector()->getIPIdentity(),
95
+ Services::ServiceProviders()->getUptimeProviders() );
96
+ }
97
+
98
+ private function isThisServer() :bool {
99
+ return Services::IP()->getIpDetector()->getIPIdentity() === IpID::THIS_SERVER;
100
+ }
101
+ }
src/lib/src/Modules/Traffic/Lib/TrafficTable/DelegateAjaxHandler.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib\TrafficTable;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+
8
+ class DelegateAjaxHandler {
9
+
10
+ use Shield\Modules\ModConsumer;
11
+
12
+ /**
13
+ * @return array
14
+ * @throws \Exception
15
+ */
16
+ public function processAjaxAction() :array {
17
+ $action = Services::Request()->post( 'sub_action' );
18
+ switch ( $action ) {
19
+
20
+ case 'retrieve_table_data':
21
+ $response = $this->retrieveTableData();
22
+ break;
23
+
24
+ default:
25
+ throw new \Exception( 'Not a supported Audit Trail table sub_action: '.$action );
26
+ }
27
+ return $response;
28
+ }
29
+
30
+ /**
31
+ * @return array
32
+ * @throws \Exception
33
+ */
34
+ private function retrieveTableData() :array {
35
+ return [
36
+ 'success' => true,
37
+ 'vars' => [
38
+ 'data' => ( new LoadRawTableData() )
39
+ ->setMod( $this->getMod() )
40
+ ->loadForLogs()
41
+ ],
42
+ ];
43
+ }
44
+ }
src/lib/src/Modules/Traffic/Lib/TrafficTable/LoadRawTableData.php ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib\TrafficTable;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\GeoIp\Lookup;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\LookupIpOnList;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
8
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs\LoadLogs;
9
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs\LogRecord;
10
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib\Ops\ConvertLegacy;
11
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
12
+ use FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\LoadData\BaseLoadTableData;
13
+ use FernleafSystems\Wordpress\Services\Services;
14
+
15
+ class LoadRawTableData extends BaseLoadTableData {
16
+
17
+ use ModConsumer;
18
+
19
+ /**
20
+ * @var LogRecord
21
+ */
22
+ private $log;
23
+
24
+ /**
25
+ * @var Lookup
26
+ */
27
+ private $geoLookup;
28
+
29
+ private $users = [];
30
+
31
+ private $ipInfo = [];
32
+
33
+ public function loadForLogs() :array {
34
+ ( new ConvertLegacy() )
35
+ ->setMod( $this->getMod() )
36
+ ->run();
37
+
38
+ $this->users = [ 0 => __( 'No', 'wp-simple-firewall' ) ];
39
+
40
+ return array_values( array_filter( array_map(
41
+ function ( $log ) {
42
+ /**
43
+ * @deprecated 12.0 - this just removes dud entries from the conversion.
44
+ */
45
+ if ( empty( @$log->meta[ 'path' ] ) ) {
46
+ return null;
47
+ }
48
+
49
+ $WPU = Services::WpUsers();
50
+
51
+ $log->meta = array_merge(
52
+ [
53
+ 'path' => '',
54
+ 'code' => '200',
55
+ 'ua' => 'Unknown',
56
+ 'verb' => 'Unknown',
57
+ 'offense' => false,
58
+ 'uid' => 0
59
+ ],
60
+ $log->meta
61
+ );
62
+
63
+ $this->log = $log;
64
+
65
+ $data = $log->getRawData();
66
+
67
+ $data[ 'ip' ] = $this->log->ip;
68
+ $data[ 'page' ] = $this->log->ip;
69
+ $data[ 'code' ] = $this->log->meta[ 'code' ];
70
+ $data[ 'offense' ] = $this->log->meta[ 'offense' ] ? 'Offense' : 'Not Offense';
71
+ $data[ 'rid' ] = $this->log->rid ?? __( 'Unknown', 'wp-simple-firewall' );
72
+ $data[ 'path' ] = empty( $this->log->meta[ 'path' ] ) ? '-'
73
+ : explode( '?', $this->log->meta[ 'path' ], 2 )[ 0 ];
74
+
75
+ $geo = $this->getCountryIP( $this->log->ip );
76
+ $data[ 'country' ] = empty( $geo->countryCode ) ?
77
+ __( 'Unknown', 'wp-simple-firewall' ) : $geo->countryName;
78
+
79
+ $userID = $this->log->meta[ 'uid' ] ?? 0;
80
+ if ( $userID > 0 ) {
81
+ if ( !isset( $users[ $userID ] ) ) {
82
+ $user = $WPU->getUserById( $userID );
83
+ $this->users[ $userID ] = empty( $user ) ? __( 'Unknown', 'wp-simple-firewall' ) :
84
+ sprintf( '<a href="%s" target="_blank" title="Go To Profile">%s</a>',
85
+ $WPU->getAdminUrl_ProfileEdit( $user ), $user->user_login );
86
+ }
87
+ }
88
+
89
+ $data[ 'page' ] = $this->getColumnContent_Page();
90
+ $data[ 'details' ] = $this->getColumnContent_Details();
91
+ $data[ 'response' ] = $this->getColumnContent_Response();
92
+ $data[ 'created_since' ] = $this->getColumnContent_Date( $this->log->created_at );
93
+ return $data;
94
+ },
95
+ $this->getLogRecords()
96
+ ) ) );
97
+ }
98
+
99
+ /**
100
+ * @return LogRecord[]
101
+ */
102
+ private function getLogRecords() :array {
103
+ return ( new LoadLogs() )
104
+ ->setMod( $this->getCon()->getModule_Data() )
105
+ ->run();
106
+ }
107
+
108
+ private function getColumnContent_Details() :string {
109
+ $geo = $this->getCountryIP( $this->log->ip );
110
+ if ( empty( $geo->countryCode ) ) {
111
+ $country = __( 'Unknown', 'wp-simple-firewall' );
112
+ }
113
+ else {
114
+ $country = sprintf(
115
+ '<img class="icon-flag" src="%s" alt="%s" width="24px"/> %s',
116
+ sprintf( 'https://api.aptoweb.com/api/v1/country/flag/%s.svg', strtolower( $geo->countryCode ) ),
117
+ $geo->countryCode,
118
+ $geo->countryName
119
+ );
120
+ }
121
+
122
+ return sprintf( '<div>%s</div>', implode( '</div><div>', [
123
+ sprintf( '%s: %s', __( 'IP', 'wp-simple-firewall' ), $this->getIpAnalysisLink( $this->log->ip ) ),
124
+ sprintf( '%s: %s', __( 'IP Status', 'wp-simple-firewall' ), $this->getIpInfo( $this->log->ip ) ),
125
+ sprintf( '%s: %s', __( 'Logged-In', 'wp-simple-firewall' ), $this->users[ $this->log->meta[ 'uid' ] ] ),
126
+ sprintf( '%s: %s', __( 'Location', 'wp-simple-firewall' ), $country ),
127
+ esc_html( esc_js( sprintf( '%s - %s', __( 'User Agent', 'wp-simple-firewall' ), $this->log->meta[ 'ua' ] ) ) ),
128
+ ] ) );
129
+ }
130
+
131
+ private function getColumnContent_Response() :string {
132
+ if ( $this->log->meta[ 'code' ] >= 400 ) {
133
+ $codeType = 'danger';
134
+ }
135
+ elseif ( $this->log->meta[ 'code' ] >= 300 ) {
136
+ $codeType = 'warning';
137
+ }
138
+ else {
139
+ $codeType = 'success';
140
+ }
141
+
142
+ return sprintf( '<div>%s</div>', implode( '</div><div>', [
143
+ sprintf( '%s: %s', __( 'Response', 'wp-simple-firewall' ),
144
+ sprintf( '<span class="badge badge-%s">%s</span>', $codeType, $this->log->meta[ 'code' ] ) ),
145
+ sprintf( '%s: %s', __( 'Offense', 'wp-simple-firewall' ),
146
+ sprintf(
147
+ '<span class="badge badge-%s">%s</span>',
148
+ @$this->log->meta[ 'offense' ] ? 'danger' : 'info',
149
+ @$this->log->meta[ 'offense' ] ? __( 'Yes', 'wp-simple-firewall' ) : __( 'No', 'wp-simple-firewall' )
150
+ )
151
+ ),
152
+ ] ) );
153
+ }
154
+
155
+ private function getColumnContent_Page() :string {
156
+ list( $preQuery, $query ) = explode( '?', $this->log->meta[ 'path' ].'?', 2 );
157
+ return strtoupper( $this->log->meta[ 'verb' ] ).': <code>'.$preQuery
158
+ .( empty( $query ) ? '' : '?<br/>'.$query ).'</code>';
159
+ }
160
+
161
+ private function getIpInfo( string $ip ) {
162
+
163
+ if ( !isset( $this->ipInfo[ $ip ] ) ) {
164
+
165
+ if ( empty( $ip ) ) {
166
+ $this->ipInfo[ '' ] = 'n/a';
167
+ }
168
+ else {
169
+ $badgeTemplate = '<span class="badge badge-%s">%s</span>';
170
+ $status = __( 'No Record', 'wp-simple-firewall' );
171
+
172
+ $record = ( new LookupIpOnList() )
173
+ ->setDbHandler( $this->getCon()->getModule_IPs()->getDbHandler_IPs() )
174
+ ->setIP( $ip )
175
+ ->lookup();
176
+
177
+ if ( empty( $record ) ) {
178
+ $status = __( 'No Record', 'wp-simple-firewall' );
179
+ }
180
+ elseif ( $record->blocked_at > 0 || $record->list === ModCon::LIST_MANUAL_BLACK ) {
181
+ $status = sprintf( $badgeTemplate, 'danger', __( 'Blocked', 'wp-simple-firewall' ) );
182
+ }
183
+ elseif ( $record->list === ModCon::LIST_AUTO_BLACK ) {
184
+ $status = sprintf( $badgeTemplate,
185
+ 'warning',
186
+ sprintf( _n( '%s offense', '%s offenses', $record->transgressions, 'wp-simple-firewall' ), $record->transgressions )
187
+ );
188
+ }
189
+ elseif ( $record->list === ModCon::LIST_MANUAL_WHITE ) {
190
+ $status = sprintf( $badgeTemplate,
191
+ 'success',
192
+ __( 'Bypass', 'wp-simple-firewall' )
193
+ );
194
+ }
195
+ $this->ipInfo[ $ip ] = $status;
196
+ }
197
+ }
198
+
199
+ return $this->ipInfo[ $ip ];
200
+ }
201
+
202
+ private function getCountryIP( string $ip ) {
203
+ if ( empty( $this->geoLookup ) ) {
204
+ $this->geoLookup = ( new Lookup() )->setCon( $this->getCon() );
205
+ }
206
+ return $this->geoLookup
207
+ ->setIP( $ip )
208
+ ->lookupIp();
209
+ }
210
+ }
src/lib/src/Modules/Traffic/ModCon.php CHANGED
@@ -2,25 +2,23 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Tool\DbTableExport;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
10
  class ModCon extends BaseShield\ModCon {
11
 
12
- public function getDbHandler_Traffic() :Databases\Traffic\Handler {
13
- return $this->getDbH( 'traffic' );
14
- }
 
15
 
16
- protected function handleFileDownload( string $downloadID ) {
17
- switch ( $downloadID ) {
18
- case 'db_traffic':
19
- ( new DbTableExport() )
20
- ->setDbHandler( $this->getDbHandler_Traffic() )
21
- ->toCSV();
22
- break;
23
  }
 
24
  }
25
 
26
  protected function preProcessOptions() {
@@ -41,8 +39,28 @@ class ModCon extends BaseShield\ModCon {
41
  protected function isReadyToExecute() :bool {
42
  $IP = Services::IP();
43
  return $IP->isValidIp_PublicRange( $IP->getRequestIp() )
44
- && ( $this->getDbHandler_Traffic() instanceof Databases\Traffic\Handler )
45
- && $this->getDbHandler_Traffic()->isReady()
46
  && parent::isReadyToExecute();
47
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
 
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
10
  class ModCon extends BaseShield\ModCon {
11
 
12
+ /**
13
+ * @var Lib\RequestLogger
14
+ */
15
+ private $requestLogger;
16
 
17
+ public function getRequestLogger() :Lib\RequestLogger {
18
+ if ( !isset( $this->requestLogger ) ) {
19
+ $this->requestLogger = ( new Lib\RequestLogger() )->setMod( $this );
 
 
 
 
20
  }
21
+ return $this->requestLogger;
22
  }
23
 
24
  protected function preProcessOptions() {
39
  protected function isReadyToExecute() :bool {
40
  $IP = Services::IP();
41
  return $IP->isValidIp_PublicRange( $IP->getRequestIp() )
42
+ && $this->getCon()->getModule_Data()->getDbH_ReqLogs()->isReady()
 
43
  && parent::isReadyToExecute();
44
  }
45
+
46
+ /**
47
+ * @deprecated 12.0
48
+ */
49
+ protected function cleanupDatabases() {
50
+ }
51
+
52
+ /**
53
+ * @inheritDoc
54
+ * @deprecated 12.0
55
+ */
56
+ public function getDbHandlers( $bInitAll = false ) {
57
+ return [];
58
+ }
59
+
60
+ /**
61
+ * @deprecated 12.0
62
+ */
63
+ public function getDbHandler_Traffic() :Databases\Traffic\Handler {
64
+ return $this->getDbH( 'traffic' );
65
+ }
66
  }
src/lib/src/Modules/Traffic/Options.php CHANGED
@@ -7,6 +7,10 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
7
  class Options extends BaseShield\Options {
8
 
9
  public function getAutoCleanDays() :int {
 
 
 
 
10
  return (int)$this->getOpt( 'auto_clean' );
11
  }
12
 
@@ -23,10 +27,6 @@ class Options extends BaseShield\Options {
23
  return (int)$this->getOpt( 'limit_time_span' );
24
  }
25
 
26
- public function getMaxEntries() :int {
27
- return (int)$this->getOpt( 'max_entries' );
28
- }
29
-
30
  public function getReqTypeExclusions() :array {
31
  $ex = $this->getOpt( 'type_exclusions' );
32
  return is_array( $ex ) ? $ex : [];
@@ -34,11 +34,18 @@ class Options extends BaseShield\Options {
34
 
35
  public function isTrafficLoggerEnabled() :bool {
36
  return $this->isOpt( 'enable_traffic', 'Y' ) && $this->isOpt( 'enable_logger', 'Y' )
37
- && $this->getMaxEntries() > 0 && $this->getAutoCleanDays() > 0;
38
  }
39
 
40
  public function isTrafficLimitEnabled() :bool {
41
- return $this->isTrafficLoggerEnabled() && $this->isOpt( 'enable_limiter', 'Y' )
42
  && ( $this->getLimitTimeSpan() > 0 ) && ( $this->getLimitRequestCount() > 0 );
43
  }
 
 
 
 
 
 
 
44
  }
7
  class Options extends BaseShield\Options {
8
 
9
  public function getAutoCleanDays() :int {
10
+ $days = $this->getOpt( 'auto_clean' );
11
+ if ( !$this->isPremium() ) {
12
+ $this->setOpt( 'auto_clean', min( $days, 7 ) );
13
+ }
14
  return (int)$this->getOpt( 'auto_clean' );
15
  }
16
 
27
  return (int)$this->getOpt( 'limit_time_span' );
28
  }
29
 
 
 
 
 
30
  public function getReqTypeExclusions() :array {
31
  $ex = $this->getOpt( 'type_exclusions' );
32
  return is_array( $ex ) ? $ex : [];
34
 
35
  public function isTrafficLoggerEnabled() :bool {
36
  return $this->isOpt( 'enable_traffic', 'Y' ) && $this->isOpt( 'enable_logger', 'Y' )
37
+ && $this->getAutoCleanDays() > 0;
38
  }
39
 
40
  public function isTrafficLimitEnabled() :bool {
41
+ return $this->isPremium() && $this->isTrafficLoggerEnabled() && $this->isOpt( 'enable_limiter', 'Y' )
42
  && ( $this->getLimitTimeSpan() > 0 ) && ( $this->getLimitRequestCount() > 0 );
43
  }
44
+
45
+ /**
46
+ * @deprecated 12.0
47
+ */
48
+ public function getMaxEntries() :int {
49
+ return PHP_INT_MAX;
50
+ }
51
  }
src/lib/src/Modules/Traffic/Processor.php CHANGED
@@ -7,15 +7,12 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules;
7
  class Processor extends Modules\BaseShield\Processor {
8
 
9
  protected function run() {
10
- /** @var Options $opts */
11
- $opts = $this->getOptions();
12
- if ( $opts->isTrafficLoggerEnabled() ) {
13
- ( new Lib\Logger() )
14
- ->setMod( $this->getMod() )
15
- ->run();
16
- ( new Lib\Limit\Limiter() )
17
- ->setMod( $this->getMod() )
18
- ->run();
19
- }
20
  }
21
  }
7
  class Processor extends Modules\BaseShield\Processor {
8
 
9
  protected function run() {
10
+ /** @var ModCon $mod */
11
+ $mod = $this->getMod();
12
+ $mod->getRequestLogger()->execute();
13
+
14
+ ( new Lib\Limit\Limiter() )
15
+ ->setMod( $this->getMod() )
16
+ ->execute();
 
 
 
17
  }
18
  }
src/lib/src/Modules/Traffic/Strings.php CHANGED
@@ -7,12 +7,15 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
7
  class Strings extends Base\Strings {
8
 
9
  /**
10
- * @return string[][]
11
  */
12
- protected function getAuditMessages() :array {
13
  return [
14
  'request_limit_exceeded' => [
15
- __( 'Visitor exceeded the maximum allowable requests (%s) within %s seconds.', 'wp-simple-firewall' ),
 
 
 
16
  ],
17
  ];
18
  }
@@ -117,12 +120,6 @@ class Strings extends Base\Strings {
117
  $desc = __( 'DB cleanup will delete logs older than this maximum value (in days).', 'wp-simple-firewall' );
118
  break;
119
 
120
- case 'max_entries' :
121
- $name = __( 'Max Log Length', 'wp-simple-firewall' );
122
- $summary = __( 'Maximum Traffic Log Length To Keep', 'wp-simple-firewall' );
123
- $desc = __( 'DB cleanup will delete logs to maintain this maximum number of records.', 'wp-simple-firewall' );
124
- break;
125
-
126
  case 'enable_limiter' :
127
  $name = __( 'Enable Rate Limiting', 'wp-simple-firewall' );
128
  $summary = __( 'Turn On The Rate Limiting Feature', 'wp-simple-firewall' );
7
  class Strings extends Base\Strings {
8
 
9
  /**
10
+ * @inheritDoc
11
  */
12
+ public function getEventStrings() :array {
13
  return [
14
  'request_limit_exceeded' => [
15
+ 'name' => __( 'Rate Limit Exceeded', 'wp-simple-firewall' ),
16
+ 'audit' => [
17
+ __( 'Rate limit ({{count}}) was exceeded with {{requests}} requests within {{span}} seconds.', 'wp-simple-firewall' ),
18
+ ],
19
  ],
20
  ];
21
  }
120
  $desc = __( 'DB cleanup will delete logs older than this maximum value (in days).', 'wp-simple-firewall' );
121
  break;
122
 
 
 
 
 
 
 
123
  case 'enable_limiter' :
124
  $name = __( 'Enable Rate Limiting', 'wp-simple-firewall' );
125
  $summary = __( 'Turn On The Rate Limiting Feature', 'wp-simple-firewall' );
src/lib/src/Modules/Traffic/UI.php CHANGED
@@ -4,6 +4,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Traffic\Select;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
 
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class UI extends BaseShield\UI {
@@ -13,14 +14,16 @@ class UI extends BaseShield\UI {
13
  $mod = $this->getMod();
14
  /** @var Options $opts */
15
  $opts = $this->getOptions();
16
- /** @var Select $dbSel */
17
- $dbSel = $mod->getDbHandler_Traffic()->getQuerySelector();
 
 
18
 
19
  return $mod->renderTemplate(
20
  '/wpadmin_pages/insights/traffic/traffic_table.twig',
21
  [
22
  'ajax' => [
23
- 'render_table_traffic' => $mod->getAjaxActionData( 'render_table_traffic', true )
24
  ],
25
  'flags' => [
26
  'is_enabled' => $opts->isTrafficLoggerEnabled(),
@@ -29,19 +32,11 @@ class UI extends BaseShield\UI {
29
  'please_enable' => $mod->getUrl_DirectLinkToOption( 'enable_logger' ),
30
  ],
31
  'strings' => [
32
- 'title_filter_form' => __( 'Traffic Table Filters', 'wp-simple-firewall' ),
33
- 'traffic_title' => __( 'Traffic Watch', 'wp-simple-firewall' ),
34
- 'traffic_subtitle' => __( 'Watch and review requests to your site', 'wp-simple-firewall' ),
35
- 'response' => __( 'Response', 'wp-simple-firewall' ),
36
- 'path_contains' => __( 'Page/Path Contains', 'wp-simple-firewall' ),
37
- 'exclude_your_ip' => __( 'Exclude Your Current IP', 'wp-simple-firewall' ),
38
- 'exclude_your_ip_tooltip' => __( 'Exclude Your IP From Results', 'wp-simple-firewall' ),
39
- 'username_ignores' => __( "Providing a username will cause the 'logged-in' filter to be ignored.", 'wp-simple-firewall' ),
40
  ],
41
  'vars' => [
42
- 'unique_ips' => $dbSel->getDistinctIps(),
43
- 'unique_responses' => $dbSel->getDistinctCodes(),
44
- 'unique_users' => $dbSel->getDistinctUsernames(),
45
  ],
46
  ],
47
  true
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Traffic\Select;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Traffic\ForTraffic;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
10
  class UI extends BaseShield\UI {
14
  $mod = $this->getMod();
15
  /** @var Options $opts */
16
  $opts = $this->getOptions();
17
+
18
+ ( new Lib\Ops\ConvertLegacy() )
19
+ ->setMod( $this->getMod() )
20
+ ->run();
21
 
22
  return $mod->renderTemplate(
23
  '/wpadmin_pages/insights/traffic/traffic_table.twig',
24
  [
25
  'ajax' => [
26
+ 'traffictable_action' => $mod->getAjaxActionData( 'traffictable_action', true ),
27
  ],
28
  'flags' => [
29
  'is_enabled' => $opts->isTrafficLoggerEnabled(),
32
  'please_enable' => $mod->getUrl_DirectLinkToOption( 'enable_logger' ),
33
  ],
34
  'strings' => [
 
 
 
 
 
 
 
 
35
  ],
36
  'vars' => [
37
+ 'datatables_init' => ( new ForTraffic() )
38
+ ->setMod( $this->getMod() )
39
+ ->build()
40
  ],
41
  ],
42
  true
src/lib/src/Modules/Traffic/Upgrade.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
4
+
5
+ class Upgrade extends \FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Upgrade {
6
+
7
+ protected function upgrade_1200() {
8
+ ( new Lib\Ops\ConvertLegacy() )
9
+ ->setMod( $this->getMod() )
10
+ ->run();
11
+
12
+ // Add "This Server" as a default exclusion.
13
+ /** @var Options $opts */
14
+ $opts = $this->getOptions();
15
+ $excl = $opts->getReqTypeExclusions();
16
+ $excl[] = 'server';
17
+ $opts->setOpt( 'type_exclusions', $excl );
18
+ }
19
+ }
src/lib/src/Modules/UserManagement/AjaxHandler.php CHANGED
@@ -66,7 +66,7 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
66
  $success = false;
67
  $msg = __( 'No items selected.', 'wp-simple-firewall' );
68
  }
69
- elseif ( !in_array( $req->post( 'bulk_action' ), [ 'delete' ] ) ) {
70
  $msg = __( 'Not a supported action.', 'wp-simple-firewall' );
71
  }
72
  else {
@@ -103,25 +103,25 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
103
  $con = $this->getCon();
104
  /** @var ModCon $mod */
105
  $mod = $this->getMod();
106
- $bSuccess = false;
107
  $nId = Services::Request()->post( 'rid', -1 );
108
  if ( !is_numeric( $nId ) || $nId < 0 ) {
109
- $sMessage = __( 'Invalid session selected', 'wp-simple-firewall' );
110
  }
111
  elseif ( $mod->getSession()->id === $nId ) {
112
- $sMessage = __( 'Please logout if you want to delete your own session.', 'wp-simple-firewall' );
113
  }
114
  elseif ( $con->getModule_Sessions()->getDbHandler_Sessions()->getQueryDeleter()->deleteById( $nId ) ) {
115
- $sMessage = __( 'User session deleted', 'wp-simple-firewall' );
116
- $bSuccess = true;
117
  }
118
  else {
119
- $sMessage = __( "User session wasn't deleted", 'wp-simple-firewall' );
120
  }
121
 
122
  return [
123
- 'success' => $bSuccess,
124
- 'message' => $sMessage,
125
  ];
126
  }
127
  }
66
  $success = false;
67
  $msg = __( 'No items selected.', 'wp-simple-firewall' );
68
  }
69
+ elseif ( $req->post( 'bulk_action' ) != 'delete' ) {
70
  $msg = __( 'Not a supported action.', 'wp-simple-firewall' );
71
  }
72
  else {
103
  $con = $this->getCon();
104
  /** @var ModCon $mod */
105
  $mod = $this->getMod();
106
+ $success = false;
107
  $nId = Services::Request()->post( 'rid', -1 );
108
  if ( !is_numeric( $nId ) || $nId < 0 ) {
109
+ $msg = __( 'Invalid session selected', 'wp-simple-firewall' );
110
  }
111
  elseif ( $mod->getSession()->id === $nId ) {
112
+ $msg = __( 'Please logout if you want to delete your own session.', 'wp-simple-firewall' );
113
  }
114
  elseif ( $con->getModule_Sessions()->getDbHandler_Sessions()->getQueryDeleter()->deleteById( $nId ) ) {
115
+ $msg = __( 'User session deleted', 'wp-simple-firewall' );
116
+ $success = true;
117
  }
118
  else {
119
+ $msg = __( "User session wasn't deleted", 'wp-simple-firewall' );
120
  }
121
 
122
  return [
123
+ 'success' => $success,
124
+ 'message' => $msg,
125
  ];
126
  }
127
  }
src/lib/src/Modules/UserManagement/Lib/Password/UserPasswordHandler.php CHANGED
@@ -6,6 +6,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsu
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Consumer\WpLoginCapture;
8
  use FernleafSystems\Wordpress\Services\Services;
 
9
 
10
  /**
11
  * Referenced some of https://github.com/BenjaminNelan/PwnedPasswordChecker
@@ -29,7 +30,7 @@ class UserPasswordHandler extends ExecOnceModConsumer {
29
  if ( $user instanceof \WP_User ) {
30
  $this->onPasswordReset( $user );
31
  }
32
- }, 100, 1 );
33
  add_filter( 'registration_errors', [ $this, 'checkPassword' ], 100, 3 );
34
  add_action( 'user_profile_update_errors', [ $this, 'checkPassword' ], 100, 3 );
35
  add_action( 'validate_password_reset', [ $this, 'checkPassword' ], 100, 3 );
@@ -70,14 +71,16 @@ class UserPasswordHandler extends ExecOnceModConsumer {
70
  /** @var UserManagement\Options $opts */
71
  $opts = $this->getOptions();
72
  if ( $opts->isPassExpirationEnabled() ) {
73
- $passStartedAt = (int)$this->getCon()->getCurrentUserMeta()->pass_started_at;
74
- if ( $passStartedAt > 0 ) {
75
- if ( Services::Request()->ts() - $passStartedAt > $opts->getPassExpireTimeout() ) {
76
- $this->getCon()->fireEvent( 'pass_expired' );
77
- $this->redirectToResetPassword(
78
- sprintf( __( 'Your password has expired (after %s days).', 'wp-simple-firewall' ), $opts->getPassExpireDays() )
79
- );
80
- }
 
 
81
  }
82
  }
83
  }
@@ -103,24 +106,27 @@ class UserPasswordHandler extends ExecOnceModConsumer {
103
  * @uses wp_redirect()
104
  */
105
  private function redirectToResetPassword( string $msg ) {
106
- $nNow = Services::Request()->ts();
107
 
108
- $oMeta = $this->getCon()->getCurrentUserMeta();
109
- $nLastRedirect = (int)$oMeta->pass_reset_last_redirect_at;
110
- if ( $nNow - $nLastRedirect > MINUTE_IN_SECONDS*2 ) {
111
 
112
- $oMeta->pass_reset_last_redirect_at = $nNow;
113
 
114
- $oWpUsers = Services::WpUsers();
115
- $sAction = Services::Request()->query( 'action' );
116
- $oUser = $oWpUsers->getCurrentWpUser();
117
- if ( $oUser && ( !Services::WpGeneral()->isLoginUrl() || !in_array( $sAction, [ 'rp', 'resetpass' ] ) ) ) {
118
 
119
  $msg .= ' '.__( 'For your security, please use the password section below to update your password.', 'wp-simple-firewall' );
120
  $this->getMod()
121
  ->setFlashAdminNotice( $msg, true, true );
122
- $this->getCon()->fireEvent( 'password_policy_force_change' );
123
- Services::Response()->redirect( $oWpUsers->getPasswordResetUrl( $oUser ) );
 
 
 
 
124
  }
125
  }
126
  }
@@ -137,14 +143,14 @@ class UserPasswordHandler extends ExecOnceModConsumer {
137
  $failureMsg = '';
138
  try {
139
  $this->applyPasswordChecks( $password );
140
- $bChecksPassed = true;
141
  }
142
  catch ( \Exception $e ) {
143
- $bChecksPassed = ( $e->getCode() === 999 );
144
  $failureMsg = $e->getMessage();
145
  }
146
 
147
- if ( $bChecksPassed ) {
148
  if ( Services::WpUsers()->isUserLoggedIn() ) {
149
  $this->getCon()->getCurrentUserMeta()->pass_check_failed_at = 0;
150
  }
@@ -152,7 +158,7 @@ class UserPasswordHandler extends ExecOnceModConsumer {
152
  else {
153
  $msg = __( 'Your security administrator has imposed requirements for password quality.', 'wp-simple-firewall' );
154
  if ( !empty( $failureMsg ) ) {
155
- $msg .= '<br/>'.__( 'Reason', 'wp-simple-firewall' ).': '.$failureMsg;
156
  }
157
  $wpErrors->add( 'shield_password_policy', $msg );
158
  $this->getCon()->fireEvent( 'password_policy_block' );
@@ -203,15 +209,15 @@ class UserPasswordHandler extends ExecOnceModConsumer {
203
  * @throws \Exception
204
  */
205
  private function testPasswordMeetsMinimumStrength( string $password, int $min ) {
206
- $aResults = ( new \ZxcvbnPhp\Zxcvbn() )->passwordStrength( $password );
207
-
208
- $nScore = $aResults[ 'score' ];
209
 
210
- if ( $nScore < $min ) {
211
  /** @var UserManagement\ModCon $mod */
212
  $mod = $this->getMod();
213
  throw new \Exception( sprintf( "Password strength (%s) doesn't meet the minimum required strength (%s).",
214
- $mod->getPassStrengthName( $nScore ), $mod->getPassStrengthName( $min ) ) );
 
 
215
  }
216
  return true;
217
  }
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Consumer\WpLoginCapture;
8
  use FernleafSystems\Wordpress\Services\Services;
9
+ use ZxcvbnPhp\Zxcvbn;
10
 
11
  /**
12
  * Referenced some of https://github.com/BenjaminNelan/PwnedPasswordChecker
30
  if ( $user instanceof \WP_User ) {
31
  $this->onPasswordReset( $user );
32
  }
33
+ }, 100 );
34
  add_filter( 'registration_errors', [ $this, 'checkPassword' ], 100, 3 );
35
  add_action( 'user_profile_update_errors', [ $this, 'checkPassword' ], 100, 3 );
36
  add_action( 'validate_password_reset', [ $this, 'checkPassword' ], 100, 3 );
71
  /** @var UserManagement\Options $opts */
72
  $opts = $this->getOptions();
73
  if ( $opts->isPassExpirationEnabled() ) {
74
+ $startedAt = (int)$this->getCon()->getCurrentUserMeta()->pass_started_at;
75
+ if ( $startedAt > 0 && ( Services::Request()->ts() - $startedAt > $opts->getPassExpireTimeout() ) ) {
76
+ $this->getCon()->fireEvent( 'password_expired', [
77
+ 'audit_params' => [
78
+ 'user_login' => Services::WpUsers()->getCurrentWpUsername()
79
+ ]
80
+ ] );
81
+ $this->redirectToResetPassword(
82
+ sprintf( __( 'Your password has expired (after %s days).', 'wp-simple-firewall' ), $opts->getPassExpireDays() )
83
+ );
84
  }
85
  }
86
  }
106
  * @uses wp_redirect()
107
  */
108
  private function redirectToResetPassword( string $msg ) {
109
+ $now = Services::Request()->ts();
110
 
111
+ $meta = $this->getCon()->getCurrentUserMeta();
112
+ if ( $now - $meta->pass_reset_last_redirect_at > MINUTE_IN_SECONDS*2 ) {
 
113
 
114
+ $meta->pass_reset_last_redirect_at = $now;
115
 
116
+ $WPU = Services::WpUsers();
117
+ $action = Services::Request()->query( 'action' );
118
+ $user = $WPU->getCurrentWpUser();
119
+ if ( $user && ( !Services::WpGeneral()->isLoginUrl() || !in_array( $action, [ 'rp', 'resetpass' ] ) ) ) {
120
 
121
  $msg .= ' '.__( 'For your security, please use the password section below to update your password.', 'wp-simple-firewall' );
122
  $this->getMod()
123
  ->setFlashAdminNotice( $msg, true, true );
124
+ $this->getCon()->fireEvent( 'password_policy_force_change', [
125
+ 'audit_params' => [
126
+ 'user_login' => $user->user_login
127
+ ]
128
+ ] );
129
+ Services::Response()->redirect( $WPU->getPasswordResetUrl( $user ) );
130
  }
131
  }
132
  }
143
  $failureMsg = '';
144
  try {
145
  $this->applyPasswordChecks( $password );
146
+ $checksPassed = true;
147
  }
148
  catch ( \Exception $e ) {
149
+ $checksPassed = ( $e->getCode() === 999 );
150
  $failureMsg = $e->getMessage();
151
  }
152
 
153
+ if ( $checksPassed ) {
154
  if ( Services::WpUsers()->isUserLoggedIn() ) {
155
  $this->getCon()->getCurrentUserMeta()->pass_check_failed_at = 0;
156
  }
158
  else {
159
  $msg = __( 'Your security administrator has imposed requirements for password quality.', 'wp-simple-firewall' );
160
  if ( !empty( $failureMsg ) ) {
161
+ $msg .= sprintf( '<br/>%s: %s', __( 'Reason', 'wp-simple-firewall' ), $failureMsg );
162
  }
163
  $wpErrors->add( 'shield_password_policy', $msg );
164
  $this->getCon()->fireEvent( 'password_policy_block' );
209
  * @throws \Exception
210
  */
211
  private function testPasswordMeetsMinimumStrength( string $password, int $min ) {
212
+ $score = ( new Zxcvbn() )->passwordStrength( $password )[ 'score' ];
 
 
213
 
214
+ if ( $score < $min ) {
215
  /** @var UserManagement\ModCon $mod */
216
  $mod = $this->getMod();
217
  throw new \Exception( sprintf( "Password strength (%s) doesn't meet the minimum required strength (%s).",
218
+ $mod->getPassStrengthName( $score ),
219
+ $mod->getPassStrengthName( $min )
220
+ ) );
221
  }
222
  return true;
223
  }
src/lib/src/Modules/UserManagement/Lib/Registration/EmailValidate.php CHANGED
@@ -37,9 +37,9 @@ class EmailValidate {
37
  $this->track[] = $email;
38
 
39
  $opts = $this->getOptions();
40
- $sInvalidBecause = null;
41
  if ( !is_email( $email ) ) {
42
- $sInvalidBecause = 'syntax';
43
  }
44
  else {
45
  $apiToken = $this->getCon()
@@ -52,7 +52,7 @@ class EmailValidate {
52
  if ( is_array( $aVerifys ) ) {
53
  foreach ( $aVerifys as $sVerifyKey => $bIsValid ) {
54
  if ( !$bIsValid && in_array( $sVerifyKey, $aChecks ) ) {
55
- $sInvalidBecause = $sVerifyKey;
56
  break;
57
  }
58
  }
@@ -60,14 +60,14 @@ class EmailValidate {
60
  }
61
  }
62
 
63
- if ( !empty( $sInvalidBecause ) ) {
64
  $opt = $opts->getValidateEmailOnRegistration();
65
  $this->getCon()->fireEvent(
66
  'reg_email_invalid',
67
  [
68
- 'audit' => [
69
  'email' => sanitize_email( $email ),
70
- 'reason' => sanitize_key( $sInvalidBecause ),
71
  ],
72
  'offense_count' => $opt == 'log' ? 0 : 1,
73
  'block' => $opt == 'block',
37
  $this->track[] = $email;
38
 
39
  $opts = $this->getOptions();
40
+ $invalidBecause = null;
41
  if ( !is_email( $email ) ) {
42
+ $invalidBecause = 'syntax';
43
  }
44
  else {
45
  $apiToken = $this->getCon()
52
  if ( is_array( $aVerifys ) ) {
53
  foreach ( $aVerifys as $sVerifyKey => $bIsValid ) {
54
  if ( !$bIsValid && in_array( $sVerifyKey, $aChecks ) ) {
55
+ $invalidBecause = $sVerifyKey;
56
  break;
57
  }
58
  }
60
  }
61
  }
62
 
63
+ if ( !empty( $invalidBecause ) ) {
64
  $opt = $opts->getValidateEmailOnRegistration();
65
  $this->getCon()->fireEvent(
66
  'reg_email_invalid',
67
  [
68
+ 'audit_params' => [
69
  'email' => sanitize_email( $email ),
70
+ 'reason' => sanitize_key( $invalidBecause ),
71
  ],
72
  'offense_count' => $opt == 'log' ? 0 : 1,
73
  'block' => $opt == 'block',
src/lib/src/Modules/UserManagement/Lib/Session/UserSessionHandler.php CHANGED
@@ -40,6 +40,7 @@ class UserSessionHandler extends ExecOnceModConsumer {
40
  $con = $this->getCon();
41
  $srvIP = Services::IP();
42
 
 
43
  try {
44
  if ( !empty( $srvIP->isValidIp( $srvIP->getRequestIp() ) ) ) {
45
  $this->assessSession();
@@ -49,7 +50,13 @@ class UserSessionHandler extends ExecOnceModConsumer {
49
  $srvIP->getServerPublicIPs( true );
50
  if ( !$srvIP->isLoopback() ) {
51
  $event = $e->getMessage();
52
- $con->fireEvent( $event );
 
 
 
 
 
 
53
  $con->getModule_Sessions()
54
  ->getSessionCon()
55
  ->terminateCurrentSession();
@@ -70,7 +77,7 @@ class UserSessionHandler extends ExecOnceModConsumer {
70
  ->getModule_Sessions()
71
  ->getSessionCon()
72
  ->getCurrent();
73
- if ( !$sess instanceof EntryVO ) {
74
  throw new \Exception( 'session_notfound' );
75
  }
76
 
@@ -88,7 +95,6 @@ class UserSessionHandler extends ExecOnceModConsumer {
88
  if ( $opts->isLockToIp() && $srvIP->getRequestIp() != $sess->ip ) {
89
  throw new \Exception( 'session_iplock' );
90
  }
91
- // TODO: 'session_browserlock';
92
  }
93
 
94
  /**
@@ -134,36 +140,31 @@ class UserSessionHandler extends ExecOnceModConsumer {
134
 
135
  switch ( $forceLogoutParam ) {
136
  case 'session_expired':
137
- $sMessage = __( 'Your session has expired.', 'wp-simple-firewall' );
138
  break;
139
 
140
  case 'session_idle':
141
- $sMessage = __( 'Your session was idle for too long.', 'wp-simple-firewall' );
142
  break;
143
 
144
  case 'session_iplock':
145
- $sMessage = __( 'Your session was locked to another IP Address.', 'wp-simple-firewall' );
146
  break;
147
 
148
  case 'session_notfound':
149
- $sMessage = sprintf(
150
  __( 'You do not currently have a %s user session.', 'wp-simple-firewall' ),
151
  $this->getCon()->getHumanName()
152
  );
153
  break;
154
 
155
- case 'session_browserlock':
156
- $sMessage = __( 'Your browser appears to have changed for this session.', 'wp-simple-firewall' );
157
- break;
158
-
159
- case 'session_unverified':
160
  default:
161
- $sMessage = __( 'Your session was terminated.', 'wp-simple-firewall' );
162
  break;
163
  }
164
 
165
- $sMessage .= '<br />'.__( 'Please login again.', 'wp-simple-firewall' );
166
- $error->add( 'shield-forcelogout', $sMessage );
167
  }
168
  return $error;
169
  }
40
  $con = $this->getCon();
41
  $srvIP = Services::IP();
42
 
43
+ $user = Services::WpUsers()->getCurrentWpUser();
44
  try {
45
  if ( !empty( $srvIP->isValidIp( $srvIP->getRequestIp() ) ) ) {
46
  $this->assessSession();
50
  $srvIP->getServerPublicIPs( true );
51
  if ( !$srvIP->isLoopback() ) {
52
  $event = $e->getMessage();
53
+
54
+ $con->fireEvent( $event, [
55
+ 'audit_params' => [
56
+ 'user_login' => $user->user_login
57
+ ]
58
+ ] );
59
+
60
  $con->getModule_Sessions()
61
  ->getSessionCon()
62
  ->terminateCurrentSession();
77
  ->getModule_Sessions()
78
  ->getSessionCon()
79
  ->getCurrent();
80
+ if ( empty( $sess ) ) {
81
  throw new \Exception( 'session_notfound' );
82
  }
83
 
95
  if ( $opts->isLockToIp() && $srvIP->getRequestIp() != $sess->ip ) {
96
  throw new \Exception( 'session_iplock' );
97
  }
 
98
  }
99
 
100
  /**
140
 
141
  switch ( $forceLogoutParam ) {
142
  case 'session_expired':
143
+ $msg = __( 'Your session has expired.', 'wp-simple-firewall' );
144
  break;
145
 
146
  case 'session_idle':
147
+ $msg = __( 'Your session was idle for too long.', 'wp-simple-firewall' );
148
  break;
149
 
150
  case 'session_iplock':
151
+ $msg = __( 'Your session was locked to another IP Address.', 'wp-simple-firewall' );
152
  break;
153
 
154
  case 'session_notfound':
155
+ $msg = sprintf(
156
  __( 'You do not currently have a %s user session.', 'wp-simple-firewall' ),
157
  $this->getCon()->getHumanName()
158
  );
159
  break;
160
 
 
 
 
 
 
161
  default:
162
+ $msg = __( 'Your session was terminated.', 'wp-simple-firewall' );
163
  break;
164
  }
165
 
166
+ $msg .= '<br />'.__( 'Please login again.', 'wp-simple-firewall' );
167
+ $error->add( 'shield-forcelogout', $msg );
168
  }
169
  return $error;
170
  }
src/lib/src/Modules/UserManagement/Lib/Suspend/UserSuspendController.php CHANGED
@@ -57,7 +57,7 @@ class UserSuspendController extends ExecOnceModConsumer {
57
  $opts = $this->getOptions();
58
 
59
  // User profile UI
60
- add_filter( 'edit_user_profile', [ $this, 'addUserBlockOption' ], 1, 1 );
61
  add_action( 'edit_user_profile_update', [ $this, 'handleUserSuspendOptionSubmit' ] );
62
 
63
  // Display suspended on the user list table
@@ -150,20 +150,20 @@ class UserSuspendController extends ExecOnceModConsumer {
150
 
151
  public function handleUserSuspendOptionSubmit( int $uid ) {
152
  $con = $this->getCon();
153
- $oWpUsers = Services::WpUsers();
154
 
155
- $oEditedUser = $oWpUsers->getUserById( $uid );
156
 
157
- if ( !$oWpUsers->isUserAdmin( $oEditedUser ) || $con->isPluginAdmin() ) {
158
  $isSuspend = Services::Request()->post( 'shield_suspend_user' ) === 'Y';
159
  /** @var UserManagement\ModCon $mod */
160
  $mod = $this->getMod();
161
- $mod->addRemoveHardSuspendUserId( $uid, $isSuspend );
162
 
163
  if ( $isSuspend ) { // Delete any existing user sessions
164
  ( new Terminate() )
165
  ->setMod( $con->getModule_Sessions() )
166
- ->byUsername( $oEditedUser->user_login );
167
  }
168
  }
169
  }
57
  $opts = $this->getOptions();
58
 
59
  // User profile UI
60
+ add_filter( 'edit_user_profile', [ $this, 'addUserBlockOption' ], 1 );
61
  add_action( 'edit_user_profile_update', [ $this, 'handleUserSuspendOptionSubmit' ] );
62
 
63
  // Display suspended on the user list table
150
 
151
  public function handleUserSuspendOptionSubmit( int $uid ) {
152
  $con = $this->getCon();
153
+ $WPU = Services::WpUsers();
154
 
155
+ $user = $WPU->getUserById( $uid );
156
 
157
+ if ( $user instanceof \WP_User && ( !$WPU->isUserAdmin( $user ) || $con->isPluginAdmin() ) ) {
158
  $isSuspend = Services::Request()->post( 'shield_suspend_user' ) === 'Y';
159
  /** @var UserManagement\ModCon $mod */
160
  $mod = $this->getMod();
161
+ $mod->addRemoveHardSuspendUser( $user, $isSuspend );
162
 
163
  if ( $isSuspend ) { // Delete any existing user sessions
164
  ( new Terminate() )
165
  ->setMod( $con->getModule_Sessions() )
166
+ ->byUsername( $user->user_login );
167
  }
168
  }
169
  }
src/lib/src/Modules/UserManagement/ModCon.php CHANGED
@@ -70,56 +70,55 @@ class ModCon extends BaseShield\ModCon {
70
  * @return int
71
  */
72
  public function getPassStrengthName( $nStrength ) {
73
- $aMap = [
74
- __( 'Very Weak', 'wp-simple-firewall' ),
75
- __( 'Weak', 'wp-simple-firewall' ),
76
- __( 'Medium', 'wp-simple-firewall' ),
77
- __( 'Strong', 'wp-simple-firewall' ),
78
- __( 'Very Strong', 'wp-simple-firewall' ),
79
- ];
80
- return $aMap[ max( 0, min( 4, $nStrength ) ) ];
81
  }
82
 
83
  /**
84
- * @param int $nUserId
85
- * @param bool $bAdd - set true to add, false to remove
86
  */
87
- public function addRemoveHardSuspendUserId( $nUserId, $bAdd = true ) {
88
  /** @var Options $opts */
89
  $opts = $this->getOptions();
90
 
91
- $aIds = $opts->getSuspendHardUserIds();
92
 
93
- $oMeta = $this->getCon()->getUserMeta( Services::WpUsers()->getUserById( $nUserId ) );
94
- $bIdSuspended = isset( $aIds[ $nUserId ] ) || $oMeta->hard_suspended_at > 0;
95
 
96
- if ( $bAdd && !$bIdSuspended ) {
97
- $oMeta->hard_suspended_at = Services::Request()->ts();
98
- $aIds[ $nUserId ] = $oMeta->hard_suspended_at;
99
  $this->getCon()->fireEvent(
100
  'user_hard_suspended',
101
  [
102
- 'audit' => [
103
- 'user_id' => $nUserId,
104
- 'admin' => Services::WpUsers()->getCurrentWpUsername(),
105
  ]
106
  ]
107
  );
108
  }
109
- elseif ( !$bAdd && $bIdSuspended ) {
110
- $oMeta->hard_suspended_at = 0;
111
- unset( $aIds[ $nUserId ] );
112
  $this->getCon()->fireEvent(
113
  'user_hard_unsuspended',
114
  [
115
- 'audit' => [
116
- 'user_id' => $nUserId,
117
- 'admin' => Services::WpUsers()->getCurrentWpUsername(),
118
  ]
119
  ]
120
  );
121
  }
122
 
123
- $opts->setOpt( 'hard_suspended_userids', $aIds );
124
  }
125
  }
70
  * @return int
71
  */
72
  public function getPassStrengthName( $nStrength ) {
73
+ return [
74
+ __( 'Very Weak', 'wp-simple-firewall' ),
75
+ __( 'Weak', 'wp-simple-firewall' ),
76
+ __( 'Medium', 'wp-simple-firewall' ),
77
+ __( 'Strong', 'wp-simple-firewall' ),
78
+ __( 'Very Strong', 'wp-simple-firewall' ),
79
+ ][ max( 0, min( 4, $nStrength ) ) ];
 
80
  }
81
 
82
  /**
83
+ * @param int $userID
84
+ * @param bool $add - set true to add, false to remove
85
  */
86
+ public function addRemoveHardSuspendUser( \WP_User $user, bool $add = true ) {
87
  /** @var Options $opts */
88
  $opts = $this->getOptions();
89
 
90
+ $IDs = $opts->getSuspendHardUserIds();
91
 
92
+ $meta = $this->getCon()->getUserMeta( $user );
93
+ $isSuspended = isset( $IDs[ $user->ID ] ) || $meta->hard_suspended_at > 0;
94
 
95
+ if ( $add && !$isSuspended ) {
96
+ $meta->hard_suspended_at = Services::Request()->ts();
97
+ $IDs[ $user->ID ] = $meta->hard_suspended_at;
98
  $this->getCon()->fireEvent(
99
  'user_hard_suspended',
100
  [
101
+ 'audit_params' => [
102
+ 'user_login' => $user->user_login,
103
+ 'admin' => Services::WpUsers()->getCurrentWpUsername(),
104
  ]
105
  ]
106
  );
107
  }
108
+ elseif ( !$add && $isSuspended ) {
109
+ $meta->hard_suspended_at = 0;
110
+ unset( $IDs[ $user->ID ] );
111
  $this->getCon()->fireEvent(
112
  'user_hard_unsuspended',
113
  [
114
+ 'audit_params' => [
115
+ 'user_login' => $user->user_login,
116
+ 'admin' => Services::WpUsers()->getCurrentWpUsername(),
117
  ]
118
  ]
119
  );
120
  }
121
 
122
+ $opts->setOpt( 'hard_suspended_userids', $IDs );
123
  }
124
  }
src/lib/src/Modules/UserManagement/Processor.php CHANGED
@@ -163,11 +163,7 @@ class Processor extends BaseShield\Processor {
163
  return $aColumns;
164
  }
165
 
166
- /**
167
- * @param \WP_User $oUser
168
- * @return bool
169
- */
170
- private function sendAdminLoginEmailNotification( $oUser ) {
171
  /** @var ModCon $mod */
172
  $mod = $this->getMod();
173
  $con = $this->getCon();
@@ -188,80 +184,69 @@ class Processor extends BaseShield\Processor {
188
  }
189
  $sHumanName = ucwords( str_replace( '_', ' ', $sRoleToCheck ) ).'+';
190
 
191
- $bIsUserSignificantEnough = false;
192
  foreach ( $aUserCapToRolesMap as $sRole => $sCap ) {
193
- if ( isset( $oUser->allcaps[ $sCap ] ) && $oUser->allcaps[ $sCap ] ) {
194
- $bIsUserSignificantEnough = true;
195
  }
196
  if ( $sRoleToCheck == $sRole ) {
197
  break; // we've hit our role limit.
198
  }
199
  }
200
- if ( !$bIsUserSignificantEnough ) {
201
- return false;
202
- }
203
-
204
- $sHomeUrl = Services::WpGeneral()->getHomeUrl();
205
-
206
- $aMessage = [
207
- sprintf( __( 'As requested, %s is notifying you of a successful %s login to a WordPress site that you manage.', 'wp-simple-firewall' ),
208
- $con->getHumanName(),
209
- $sHumanName
210
- ),
211
- '',
212
- sprintf( __( 'Important: %s', 'wp-simple-firewall' ), __( 'This user may now be subject to additional Two-Factor Authentication before completing their login.', 'wp-simple-firewall' ) ),
213
- '',
214
- __( 'Details for this user are below:', 'wp-simple-firewall' ),
215
- '- '.sprintf( '%s: %s', __( 'Site URL', 'wp-simple-firewall' ), $sHomeUrl ),
216
- '- '.sprintf( '%s: %s', __( 'Username', 'wp-simple-firewall' ), $oUser->user_login ),
217
- '- '.sprintf( '%s: %s', __( 'Email', 'wp-simple-firewall' ), $oUser->user_email ),
218
- '- '.sprintf( '%s: %s', __( 'IP Address', 'wp-simple-firewall' ), Services::IP()->getRequestIp() ),
219
- '',
220
- __( 'Thanks.', 'wp-simple-firewall' )
221
- ];
222
-
223
- $oEmailer = $this->getMod()
224
- ->getEmailProcessor();
225
- foreach ( $mod->getAdminLoginNotificationEmails() as $to ) {
226
- $oEmailer->sendEmailWithWrap(
227
- $to,
228
- sprintf( '%s - %s', __( 'Notice', 'wp-simple-firewall' ), sprintf( __( '%s Just Logged Into %s', 'wp-simple-firewall' ), $sHumanName, $sHomeUrl ) ),
229
- $aMessage
230
- );
231
  }
232
-
233
- return true;
234
  }
235
 
236
- /**
237
- * @param \WP_User $oUser
238
- * @return bool
239
- */
240
- private function sendUserLoginEmailNotification( $oUser ) {
241
- $oWp = Services::WpGeneral();
242
- $aMessage = [
243
- sprintf( __( '%s is notifying you of a successful login to your WordPress account.', 'wp-simple-firewall' ), $this->getCon()
244
- ->getHumanName() ),
245
- '',
246
- __( 'Details for this login are below:', 'wp-simple-firewall' ),
247
- '- '.sprintf( '%s: %s', __( 'Site URL', 'wp-simple-firewall' ), $oWp->getHomeUrl() ),
248
- '- '.sprintf( '%s: %s', __( 'Username', 'wp-simple-firewall' ), $oUser->user_login ),
249
- '- '.sprintf( '%s: %s', __( 'IP Address', 'wp-simple-firewall' ), Services::IP()->getRequestIp() ),
250
- '- '.sprintf( '%s: %s', __( 'Time', 'wp-simple-firewall' ), $oWp->getTimeStampForDisplay() ),
251
- '',
252
- __( 'If this is unexpected or suspicious, please contact your site administrator immediately.', 'wp-simple-firewall' ),
253
- '',
254
- __( 'Thanks.', 'wp-simple-firewall' )
255
- ];
256
-
257
- return $this
258
- ->getMod()
259
- ->getEmailProcessor()
260
- ->sendEmailWithWrap(
261
- $oUser->user_email,
262
- sprintf( '%s - %s', __( 'Notice', 'wp-simple-firewall' ), __( 'A login to your WordPress account just occurred', 'wp-simple-firewall' ) ),
263
- $aMessage
264
- );
265
  }
266
 
267
  public function runDailyCron() {
163
  return $aColumns;
164
  }
165
 
166
+ private function sendAdminLoginEmailNotification( \WP_User $user ) {
 
 
 
 
167
  /** @var ModCon $mod */
168
  $mod = $this->getMod();
169
  $con = $this->getCon();
184
  }
185
  $sHumanName = ucwords( str_replace( '_', ' ', $sRoleToCheck ) ).'+';
186
 
187
+ $isUserSignificantEnough = false;
188
  foreach ( $aUserCapToRolesMap as $sRole => $sCap ) {
189
+ if ( isset( $user->allcaps[ $sCap ] ) && $user->allcaps[ $sCap ] ) {
190
+ $isUserSignificantEnough = true;
191
  }
192
  if ( $sRoleToCheck == $sRole ) {
193
  break; // we've hit our role limit.
194
  }
195
  }
196
+ if ( $isUserSignificantEnough ) {
197
+
198
+ $sHomeUrl = Services::WpGeneral()->getHomeUrl();
199
+
200
+ $emailer = $this->getMod()
201
+ ->getEmailProcessor();
202
+ foreach ( $mod->getAdminLoginNotificationEmails() as $to ) {
203
+ $emailer->sendEmailWithWrap(
204
+ $to,
205
+ sprintf( '%s - %s', __( 'Notice', 'wp-simple-firewall' ), sprintf( __( '%s Just Logged Into %s', 'wp-simple-firewall' ), $sHumanName, $sHomeUrl ) ),
206
+ [
207
+ sprintf( __( 'As requested, %s is notifying you of a successful %s login to a WordPress site that you manage.', 'wp-simple-firewall' ),
208
+ $con->getHumanName(),
209
+ $sHumanName
210
+ ),
211
+ '',
212
+ sprintf( __( 'Important: %s', 'wp-simple-firewall' ), __( 'This user may now be subject to additional Two-Factor Authentication before completing their login.', 'wp-simple-firewall' ) ),
213
+ '',
214
+ __( 'Details for this user are below:', 'wp-simple-firewall' ),
215
+ '- '.sprintf( '%s: %s', __( 'Site URL', 'wp-simple-firewall' ), $sHomeUrl ),
216
+ '- '.sprintf( '%s: %s', __( 'Username', 'wp-simple-firewall' ), $user->user_login ),
217
+ '- '.sprintf( '%s: %s', __( 'Email', 'wp-simple-firewall' ), $user->user_email ),
218
+ '- '.sprintf( '%s: %s', __( 'IP Address', 'wp-simple-firewall' ), Services::IP()
219
+ ->getRequestIp() ),
220
+ '',
221
+ __( 'Thanks.', 'wp-simple-firewall' )
222
+ ]
223
+ );
224
+ }
 
 
225
  }
 
 
226
  }
227
 
228
+ private function sendUserLoginEmailNotification( \WP_User $user ) {
229
+ $WP = Services::WpGeneral();
230
+ $this->getMod()
231
+ ->getEmailProcessor()
232
+ ->sendEmailWithWrap(
233
+ $user->user_email,
234
+ sprintf( '%s - %s', __( 'Notice', 'wp-simple-firewall' ), __( 'A login to your WordPress account just occurred', 'wp-simple-firewall' ) ),
235
+ [
236
+ sprintf( __( '%s is notifying you of a successful login to your WordPress account.', 'wp-simple-firewall' ), $this->getCon()
237
+ ->getHumanName() ),
238
+ '',
239
+ __( 'Details for this login are below:', 'wp-simple-firewall' ),
240
+ '- '.sprintf( '%s: %s', __( 'Site URL', 'wp-simple-firewall' ), $WP->getHomeUrl() ),
241
+ '- '.sprintf( '%s: %s', __( 'Username', 'wp-simple-firewall' ), $user->user_login ),
242
+ '- '.sprintf( '%s: %s', __( 'IP Address', 'wp-simple-firewall' ), Services::IP()->getRequestIp() ),
243
+ '- '.sprintf( '%s: %s', __( 'Time', 'wp-simple-firewall' ), $WP->getTimeStampForDisplay() ),
244
+ '',
245
+ __( 'If this is unexpected or suspicious, please contact your site administrator immediately.', 'wp-simple-firewall' ),
246
+ '',
247
+ __( 'Thanks.', 'wp-simple-firewall' )
248
+ ]
249
+ );
 
 
 
 
 
 
 
250
  }
251
 
252
  public function runDailyCron() {
src/lib/src/Modules/UserManagement/Strings.php CHANGED
@@ -7,6 +7,79 @@ use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class Strings extends Base\Strings {
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  /**
11
  * @inheritDoc
12
  */
@@ -33,18 +106,18 @@ class Strings extends Base\Strings {
33
  switch ( $section ) {
34
 
35
  case 'section_enable_plugin_feature_user_accounts_management' :
36
- $sTitleShort = sprintf( '%s/%s', __( 'On', 'wp-simple-firewall' ), __( 'Off', 'wp-simple-firewall' ) );
37
- $sTitle = sprintf( __( 'Enable Module: %s', 'wp-simple-firewall' ), $sModName );
38
- $aSummary = [
39
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'User Management offers real user sessions, finer control over user session time-out, and ensures users have logged-in in a correct manner.', 'wp-simple-firewall' ) ),
40
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), sprintf( __( 'Keep the %s feature turned on.', 'wp-simple-firewall' ), __( 'User Management', 'wp-simple-firewall' ) ) )
41
  ];
42
  break;
43
 
44
  case 'section_passwords' :
45
- $sTitle = __( 'Password Policies', 'wp-simple-firewall' );
46
- $sTitleShort = __( 'Password Policies', 'wp-simple-firewall' );
47
- $aSummary = [
48
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Have full control over passwords used by users on the site.', 'wp-simple-firewall' ) ),
49
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) ),
50
  sprintf( '%s - %s', __( 'Requirements', 'wp-simple-firewall' ), sprintf( 'WordPress v%s+', '4.4.0' ) ),
@@ -52,36 +125,36 @@ class Strings extends Base\Strings {
52
  break;
53
 
54
  case 'section_admin_login_notification' :
55
- $sTitle = __( 'Admin Login Notification', 'wp-simple-firewall' );
56
- $aSummary = [
57
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'So you can be made aware of when a WordPress administrator has logged into your site when you are not expecting it.', 'wp-simple-firewall' ) ),
58
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) )
59
  ];
60
- $sTitleShort = __( 'Notifications', 'wp-simple-firewall' );
61
  break;
62
 
63
  case 'section_multifactor_authentication' :
64
- $sTitle = __( 'Multi-Factor User Authentication', 'wp-simple-firewall' );
65
- $aSummary = [
66
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Verifies the identity of users who log in to your site - i.e. they are who they say they are.', 'wp-simple-firewall' ) ),
67
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ).' '.__( 'However, if your host blocks email sending you may lock yourself out.', 'wp-simple-firewall' ) )
68
  ];
69
- $sTitleShort = __( 'Multi-Factor Authentication', 'wp-simple-firewall' );
70
  break;
71
 
72
  case 'section_user_session_management' :
73
- $sTitle = __( 'User Session Management', 'wp-simple-firewall' );
74
- $aSummary = [
75
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Allows you to better control user sessions on your site and expire idle sessions and prevent account sharing.', 'wp-simple-firewall' ) ),
76
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) )
77
  ];
78
- $sTitleShort = __( 'Session Options', 'wp-simple-firewall' );
79
  break;
80
 
81
  case 'section_suspend' :
82
- $sTitleShort = __( 'User Suspension', 'wp-simple-firewall' );
83
- $sTitle = __( 'Automatic And Manual User Suspension', 'wp-simple-firewall' );
84
- $aSummary = [
85
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Automatically suspends accounts to prevent login by certain users.', 'wp-simple-firewall' ) ),
86
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) )
87
  ];
@@ -92,9 +165,9 @@ class Strings extends Base\Strings {
92
  }
93
 
94
  return [
95
- 'title' => $sTitle,
96
- 'title_short' => $sTitleShort,
97
- 'summary' => ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : [],
98
  ];
99
  }
100
 
@@ -110,142 +183,142 @@ class Strings extends Base\Strings {
110
  switch ( $key ) {
111
 
112
  case 'enable_user_management' :
113
- $sName = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $sModName );
114
- $sSummary = sprintf( __( 'Enable (or Disable) The %s Module', 'wp-simple-firewall' ), $sModName );
115
- $sDesc = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), $sModName );
116
  break;
117
 
118
  case 'enable_admin_login_email_notification' :
119
- $sName = __( 'Admin Login Notification Email', 'wp-simple-firewall' );
120
- $sSummary = __( 'Send An Notification Email When Administrator Logs In', 'wp-simple-firewall' );
121
- $sDesc = __( 'If you would like to be notified every time an administrator user logs into this WordPress site, enter a notification email address.', 'wp-simple-firewall' )
122
- .'<br />'.__( 'No email address - No Notification.', 'wp-simple-firewall' )
123
- .'<br />'.__( 'Pro customers may provide multiple email address, separated by commas.', 'wp-simple-firewall' );
124
  break;
125
 
126
  case 'enable_user_login_email_notification' :
127
- $sName = __( 'User Login Notification Email', 'wp-simple-firewall' );
128
- $sSummary = __( 'Send Email Notification To Each User Upon Successful Login', 'wp-simple-firewall' );
129
- $sDesc = __( 'A notification is sent to each user when a successful login occurs for their account.', 'wp-simple-firewall' );
130
  break;
131
 
132
  case 'session_timeout_interval' :
133
- $sName = __( 'Session Timeout', 'wp-simple-firewall' );
134
- $sSummary = __( 'Specify How Many Days After Login To Automatically Force Re-Login', 'wp-simple-firewall' );
135
- $sDesc = __( 'WordPress default is 2 days, or 14 days if you check the "Remember Me" box.', 'wp-simple-firewall' )
136
- .'<br />'.__( 'Think of this as an absolute maximum possible session length.', 'wp-simple-firewall' )
137
- .'<br />'.sprintf( __( 'This cannot be less than %s.', 'wp-simple-firewall' ), '<strong>1</strong>' )
138
- .' '.sprintf( '%s: %s', __( 'Default', 'wp-simple-firewall' ), '<strong>'.$oOpts->getOptDefault( 'session_timeout_interval' ).'</strong>' );
139
  break;
140
 
141
  case 'session_idle_timeout_interval' :
142
- $sName = __( 'Idle Timeout', 'wp-simple-firewall' );
143
- $sSummary = __( 'Specify How Many Hours After Inactivity To Automatically Logout User', 'wp-simple-firewall' );
144
- $sDesc = __( 'If the user is inactive for the number of hours specified, they will be forcefully logged out next time they return.', 'wp-simple-firewall' )
145
- .'<br />'.sprintf( __( 'Set to %s to turn off this option.', 'wp-simple-firewall' ), '"<strong>0</strong>"' );
146
  break;
147
 
148
  case 'session_lock_location' :
149
- $sName = __( 'Lock To Location', 'wp-simple-firewall' );
150
- $sSummary = __( 'Locks A User Session To IP address', 'wp-simple-firewall' );
151
- $sDesc = __( 'When selected, a session is restricted to the same IP address as when the user logged in.', 'wp-simple-firewall' )
152
- .' '.__( "If a logged-in user's IP address changes, the session will be invalidated and they'll be forced to re-login to WordPress.", 'wp-simple-firewall' );
153
  break;
154
 
155
  case 'session_username_concurrent_limit' :
156
- $sName = __( 'Max Simultaneous Sessions', 'wp-simple-firewall' );
157
- $sSummary = __( 'Limit Simultaneous Sessions For The Same Username', 'wp-simple-firewall' );
158
- $sDesc = __( 'The number provided here is the maximum number of simultaneous, distinct, sessions allowed for any given username.', 'wp-simple-firewall' )
159
- .'<br />'.__( "Zero (0) will allow unlimited simultaneous sessions.", 'wp-simple-firewall' );
160
  break;
161
 
162
  case 'reg_email_validate' :
163
- $sName = __( 'Validate Email Addresses', 'wp-simple-firewall' );
164
- $sSummary = __( 'Validate Email Addresses When User Attempts To Register', 'wp-simple-firewall' );
165
- $sDesc = [
166
  __( 'Validate Email Addresses When User Attempts To Register.', 'wp-simple-firewall' ),
167
  __( 'To validate an email your site sends a request to the WPHashes API and may cause a small delay during the user registration request.', 'wp-simple-firewall' ),
168
  ];
169
  break;
170
 
171
  case 'email_checks' :
172
- $sName = __( 'Email Validation Checks', 'wp-simple-firewall' );
173
- $sSummary = __( 'The Email Address Properties That Will Be Tested', 'wp-simple-firewall' );
174
- $sDesc = __( 'Select the properties that should be tested during email address validation.', 'wp-simple-firewall' );
175
  break;
176
 
177
  case 'enable_password_policies' :
178
- $sName = __( 'Enable Password Policies', 'wp-simple-firewall' );
179
- $sSummary = __( 'Enable The Password Policies Detailed Below', 'wp-simple-firewall' );
180
- $sDesc = __( 'Turn on/off all password policy settings.', 'wp-simple-firewall' );
181
  break;
182
 
183
  case 'pass_prevent_pwned' :
184
- $sName = __( 'Prevent Pwned Passwords', 'wp-simple-firewall' );
185
- $sSummary = __( 'Prevent Use Of "Pwned" Passwords', 'wp-simple-firewall' );
186
- $sDesc = __( 'Prevents users from using any passwords found on the public available list of "pwned" passwords.', 'wp-simple-firewall' );
187
  break;
188
 
189
  case 'pass_min_length' :
190
- $sName = __( 'Minimum Length', 'wp-simple-firewall' );
191
- $sSummary = __( 'Minimum Password Length', 'wp-simple-firewall' );
192
- $sDesc = __( 'All passwords that a user sets must be at least this many characters in length.', 'wp-simple-firewall' )
193
- .'<br/>'.__( 'Set to Zero(0) to disable.', 'wp-simple-firewall' );
194
  break;
195
 
196
  case 'pass_min_strength' :
197
- $sName = __( 'Minimum Strength', 'wp-simple-firewall' );
198
- $sSummary = __( 'Minimum Password Strength', 'wp-simple-firewall' );
199
- $sDesc = __( 'All passwords that a user sets must meet this minimum strength.', 'wp-simple-firewall' );
200
  break;
201
 
202
  case 'pass_force_existing' :
203
- $sName = __( 'Apply To Existing Users', 'wp-simple-firewall' );
204
- $sSummary = __( 'Apply Password Policies To Existing Users and Their Passwords', 'wp-simple-firewall' );
205
- $sDesc = __( "Forces existing users to update their passwords if they don't meet requirements, after they next login.", 'wp-simple-firewall' )
206
- .'<br/>'.__( 'Note: You may want to warn users prior to enabling this option.', 'wp-simple-firewall' );
207
  break;
208
 
209
  case 'pass_expire' :
210
- $sName = __( 'Password Expiration', 'wp-simple-firewall' );
211
- $sSummary = __( 'Passwords Expire After This Many Days', 'wp-simple-firewall' );
212
- $sDesc = __( 'Users will be forced to reset their passwords after the number of days specified.', 'wp-simple-firewall' )
213
- .'<br/>'.__( 'Set to Zero(0) to disable.', 'wp-simple-firewall' );
214
  break;
215
 
216
  case 'manual_suspend' :
217
- $sName = __( 'Allow Manual User Suspension', 'wp-simple-firewall' );
218
- $sSummary = __( 'Manually Suspend User Accounts To Prevent Login', 'wp-simple-firewall' );
219
- $sDesc = __( 'Users may be suspended by administrators to prevent future login.', 'wp-simple-firewall' );
220
  break;
221
 
222
  case 'auto_password' :
223
- $sName = __( 'Auto-Suspend Expired Passwords', 'wp-simple-firewall' );
224
- $sSummary = __( 'Automatically Suspend Users With Expired Passwords', 'wp-simple-firewall' );
225
- $sDesc = __( 'Automatically suspends login by users and requires password reset to unsuspend.', 'wp-simple-firewall' )
226
- .'<br/>'.sprintf(
227
- '<strong>%s</strong> - %s',
228
- __( 'Important', 'wp-simple-firewall' ),
229
- __( 'Requires password expiration policy to be set.', 'wp-simple-firewall' )
230
- );
231
  break;
232
 
233
  case 'auto_idle_days' :
234
- $sName = __( 'Auto-Suspend Idle Users', 'wp-simple-firewall' );
235
- $sSummary = __( 'Automatically Suspend Idle User Accounts', 'wp-simple-firewall' );
236
- $sDesc = __( 'Automatically suspends login for idle accounts and requires password reset to unsuspend.', 'wp-simple-firewall' )
237
- .'<br/>'.__( 'Specify the number of days since last login to consider a user as idle.', 'wp-simple-firewall' )
238
- .'<br/>'.__( 'Set to Zero(0) to disable.', 'wp-simple-firewall' );
239
  break;
240
 
241
  case 'auto_idle_roles' :
242
- $sName = __( 'Auto-Suspend Idle User Roles', 'wp-simple-firewall' );
243
- $sSummary = __( 'Apply Automatic Suspension To Accounts With These Roles', 'wp-simple-firewall' );
244
- $sDesc = __( 'Automatic suspension for idle accounts applies only to the roles you specify.', 'wp-simple-firewall' )
245
- .'<br/>'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( 'Take a new line for each user role.', 'wp-simple-firewall' ) )
246
- .'<br/>'.sprintf( '%s: %s', __( 'Available Roles', 'wp-simple-firewall' ), implode( ', ', Services::WpUsers()
247
- ->getAvailableUserRoles() ) )
248
- .'<br/>'.sprintf( '%s: %s', __( 'Default', 'wp-simple-firewall' ), implode( ', ', $oOpts->getOptDefault( 'auto_idle_roles' ) ) );
249
  break;
250
 
251
  default:
@@ -253,60 +326,9 @@ class Strings extends Base\Strings {
253
  }
254
 
255
  return [
256
- 'name' => $sName,
257
- 'summary' => $sSummary,
258
- 'description' => $sDesc,
259
- ];
260
- }
261
-
262
- /**
263
- * @return string[][]
264
- */
265
- protected function getAuditMessages() :array {
266
- return [
267
- 'reg_email_invalid' => [
268
- __( 'Detected user registration with invalid email address (%s).', 'wp-simple-firewall' ),
269
- __( 'Email verification test that failed: %s' )
270
- ],
271
- 'pass_expired' => [
272
- __( 'Forcing user to update expired password.', 'wp-simple-firewall' ),
273
- ],
274
- 'password_policy_force_change' => [
275
- __( 'Forcing user to update password that fails to meet policies.', 'wp-simple-firewall' ),
276
- ],
277
- 'password_policy_block' => [
278
- __( 'Blocked attempted password update that failed policy requirements.', 'wp-simple-firewall' ),
279
- ],
280
- 'session_notfound' => [
281
- __( 'Valid user session could not be found.', 'wp-simple-firewall' ),
282
- __( 'Logging out.', 'wp-simple-firewall' )
283
- ],
284
- 'session_expired' => [
285
- __( 'User session has expired.', 'wp-simple-firewall' ),
286
- __( 'Logging out.', 'wp-simple-firewall' )
287
- ],
288
- 'session_idle' => [
289
- __( 'User session has expired due to inactivity.', 'wp-simple-firewall' ),
290
- __( 'Logging out.', 'wp-simple-firewall' )
291
- ],
292
- 'session_iplock' => [
293
- __( 'Access to an established user session from a different IP address.', 'wp-simple-firewall' ),
294
- __( 'Logging out.', 'wp-simple-firewall' )
295
- ],
296
- 'session_browserlock' => [
297
- __( 'Browser signature has changed for this user session.', 'wp-simple-firewall' ),
298
- __( 'Logging out.', 'wp-simple-firewall' )
299
- ],
300
- 'session_unverified' => [
301
- __( 'Unable to verify the current User Session. Forcefully logging out session.', 'wp-simple-firewall' ),
302
- __( 'Logging out.', 'wp-simple-firewall' )
303
- ],
304
- 'user_hard_suspended' => [
305
- __( 'User ID %s suspended by admin (%s)', 'wp-simple-firewall' ),
306
- ],
307
- 'user_hard_unsuspended' => [
308
- __( 'User ID %s unsuspended by admin (%s)', 'wp-simple-firewall' ),
309
- ],
310
  ];
311
  }
312
  }
7
 
8
  class Strings extends Base\Strings {
9
 
10
+ /**
11
+ * @inheritDoc
12
+ */
13
+ public function getEventStrings() :array {
14
+ return [
15
+ 'reg_email_invalid' => [
16
+ 'name' => __( 'Invalid User Email Registration', 'wp-simple-firewall' ),
17
+ 'audit' => [
18
+ __( 'Detected user registration with invalid email address ({{email}}).', 'wp-simple-firewall' ),
19
+ __( 'Email verification test that failed: {{reason}}' ),
20
+ ],
21
+ ],
22
+ 'password_expired' => [
23
+ 'name' => __( 'Password Expired', 'wp-simple-firewall' ),
24
+ 'audit' => [
25
+ __( 'Forcing user ({{user_login}}) to update expired password.', 'wp-simple-firewall' ),
26
+ ],
27
+ ],
28
+ 'password_policy_force_change' => [
29
+ 'name' => __( 'Forced Password Change', 'wp-simple-firewall' ),
30
+ 'audit' => [
31
+ __( 'Forcing user ({{user_login}}) to update password that fails to meet policies.', 'wp-simple-firewall' ),
32
+ ],
33
+ ],
34
+ 'password_policy_block' => [
35
+ 'name' => __( 'Password Change Blocked', 'wp-simple-firewall' ),
36
+ 'audit' => [
37
+ __( 'Blocked attempted password update that failed policy requirements.', 'wp-simple-firewall' ),
38
+ ],
39
+ ],
40
+ 'session_notfound' => [
41
+ 'name' => __( 'Session Not Found', 'wp-simple-firewall' ),
42
+ 'audit' => [
43
+ __( 'Valid user session could not be found ({{user_login}}).', 'wp-simple-firewall' ),
44
+ __( 'Logging out.', 'wp-simple-firewall' )
45
+ ],
46
+ ],
47
+ 'session_expired' => [
48
+ 'name' => __( 'Session Expired', 'wp-simple-firewall' ),
49
+ 'audit' => [
50
+ __( 'User session has expired ({{user_login}}).', 'wp-simple-firewall' ),
51
+ __( 'Logging out.', 'wp-simple-firewall' )
52
+ ],
53
+ ],
54
+ 'session_idle' => [
55
+ 'name' => __( 'Session Idle', 'wp-simple-firewall' ),
56
+ 'audit' => [
57
+ __( 'User session has expired due to inactivity ({{user_login}}).', 'wp-simple-firewall' ),
58
+ __( 'Logging out.', 'wp-simple-firewall' )
59
+ ],
60
+ ],
61
+ 'session_iplock' => [
62
+ 'name' => __( 'Session Locked To IP', 'wp-simple-firewall' ),
63
+ 'audit' => [
64
+ __( 'Access to an established user session ({{user_login}}) from a different IP address.', 'wp-simple-firewall' ),
65
+ __( 'Logging out.', 'wp-simple-firewall' )
66
+ ],
67
+ ],
68
+ 'user_hard_suspended' => [
69
+ 'name' => __( 'User Hard-Suspended', 'wp-simple-firewall' ),
70
+ 'audit' => [
71
+ __( 'User "{{user_login}}" suspended by admin ({{admin}})', 'wp-simple-firewall' ),
72
+ ],
73
+ ],
74
+ 'user_hard_unsuspended' => [
75
+ 'name' => __( 'User Hard-Unsuspended', 'wp-simple-firewall' ),
76
+ 'audit' => [
77
+ __( 'User "{{user_login}}" unsuspended by admin ({{admin}})', 'wp-simple-firewall' ),
78
+ ],
79
+ ],
80
+ ];
81
+ }
82
+
83
  /**
84
  * @inheritDoc
85
  */
106
  switch ( $section ) {
107
 
108
  case 'section_enable_plugin_feature_user_accounts_management' :
109
+ $titleShort = sprintf( '%s/%s', __( 'On', 'wp-simple-firewall' ), __( 'Off', 'wp-simple-firewall' ) );
110
+ $title = sprintf( __( 'Enable Module: %s', 'wp-simple-firewall' ), $sModName );
111
+ $summary = [
112
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'User Management offers real user sessions, finer control over user session time-out, and ensures users have logged-in in a correct manner.', 'wp-simple-firewall' ) ),
113
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), sprintf( __( 'Keep the %s feature turned on.', 'wp-simple-firewall' ), __( 'User Management', 'wp-simple-firewall' ) ) )
114
  ];
115
  break;
116
 
117
  case 'section_passwords' :
118
+ $title = __( 'Password Policies', 'wp-simple-firewall' );
119
+ $titleShort = __( 'Password Policies', 'wp-simple-firewall' );
120
+ $summary = [
121
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Have full control over passwords used by users on the site.', 'wp-simple-firewall' ) ),
122
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) ),
123
  sprintf( '%s - %s', __( 'Requirements', 'wp-simple-firewall' ), sprintf( 'WordPress v%s+', '4.4.0' ) ),
125
  break;
126
 
127
  case 'section_admin_login_notification' :
128
+ $title = __( 'Admin Login Notification', 'wp-simple-firewall' );
129
+ $summary = [
130
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'So you can be made aware of when a WordPress administrator has logged into your site when you are not expecting it.', 'wp-simple-firewall' ) ),
131
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) )
132
  ];
133
+ $titleShort = __( 'Notifications', 'wp-simple-firewall' );
134
  break;
135
 
136
  case 'section_multifactor_authentication' :
137
+ $title = __( 'Multi-Factor User Authentication', 'wp-simple-firewall' );
138
+ $summary = [
139
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Verifies the identity of users who log in to your site - i.e. they are who they say they are.', 'wp-simple-firewall' ) ),
140
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ).' '.__( 'However, if your host blocks email sending you may lock yourself out.', 'wp-simple-firewall' ) )
141
  ];
142
+ $titleShort = __( 'Multi-Factor Authentication', 'wp-simple-firewall' );
143
  break;
144
 
145
  case 'section_user_session_management' :
146
+ $title = __( 'User Session Management', 'wp-simple-firewall' );
147
+ $summary = [
148
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Allows you to better control user sessions on your site and expire idle sessions and prevent account sharing.', 'wp-simple-firewall' ) ),
149
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) )
150
  ];
151
+ $titleShort = __( 'Session Options', 'wp-simple-firewall' );
152
  break;
153
 
154
  case 'section_suspend' :
155
+ $titleShort = __( 'User Suspension', 'wp-simple-firewall' );
156
+ $title = __( 'Automatic And Manual User Suspension', 'wp-simple-firewall' );
157
+ $summary = [
158
  sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Automatically suspends accounts to prevent login by certain users.', 'wp-simple-firewall' ) ),
159
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Use of this feature is highly recommend.', 'wp-simple-firewall' ) )
160
  ];
165
  }
166
 
167
  return [
168
+ 'title' => $title,
169
+ 'title_short' => $titleShort,
170
+ 'summary' => $summary,
171
  ];
172
  }
173
 
183
  switch ( $key ) {
184
 
185
  case 'enable_user_management' :
186
+ $name = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $sModName );
187
+ $summary = sprintf( __( 'Enable (or Disable) The %s Module', 'wp-simple-firewall' ), $sModName );
188
+ $description = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), $sModName );
189
  break;
190
 
191
  case 'enable_admin_login_email_notification' :
192
+ $name = __( 'Admin Login Notification Email', 'wp-simple-firewall' );
193
+ $summary = __( 'Send An Notification Email When Administrator Logs In', 'wp-simple-firewall' );
194
+ $description = __( 'If you would like to be notified every time an administrator user logs into this WordPress site, enter a notification email address.', 'wp-simple-firewall' )
195
+ .'<br />'.__( 'No email address - No Notification.', 'wp-simple-firewall' )
196
+ .'<br />'.__( 'Pro customers may provide multiple email address, separated by commas.', 'wp-simple-firewall' );
197
  break;
198
 
199
  case 'enable_user_login_email_notification' :
200
+ $name = __( 'User Login Notification Email', 'wp-simple-firewall' );
201
+ $summary = __( 'Send Email Notification To Each User Upon Successful Login', 'wp-simple-firewall' );
202
+ $description = __( 'A notification is sent to each user when a successful login occurs for their account.', 'wp-simple-firewall' );
203
  break;
204
 
205
  case 'session_timeout_interval' :
206
+ $name = __( 'Session Timeout', 'wp-simple-firewall' );
207
+ $summary = __( 'Specify How Many Days After Login To Automatically Force Re-Login', 'wp-simple-firewall' );
208
+ $description = __( 'WordPress default is 2 days, or 14 days if you check the "Remember Me" box.', 'wp-simple-firewall' )
209
+ .'<br />'.__( 'Think of this as an absolute maximum possible session length.', 'wp-simple-firewall' )
210
+ .'<br />'.sprintf( __( 'This cannot be less than %s.', 'wp-simple-firewall' ), '<strong>1</strong>' )
211
+ .' '.sprintf( '%s: %s', __( 'Default', 'wp-simple-firewall' ), '<strong>'.$oOpts->getOptDefault( 'session_timeout_interval' ).'</strong>' );
212
  break;
213
 
214
  case 'session_idle_timeout_interval' :
215
+ $name = __( 'Idle Timeout', 'wp-simple-firewall' );
216
+ $summary = __( 'Specify How Many Hours After Inactivity To Automatically Logout User', 'wp-simple-firewall' );
217
+ $description = __( 'If the user is inactive for the number of hours specified, they will be forcefully logged out next time they return.', 'wp-simple-firewall' )
218
+ .'<br />'.sprintf( __( 'Set to %s to turn off this option.', 'wp-simple-firewall' ), '"<strong>0</strong>"' );
219
  break;
220
 
221
  case 'session_lock_location' :
222
+ $name = __( 'Lock To Location', 'wp-simple-firewall' );
223
+ $summary = __( 'Locks A User Session To IP address', 'wp-simple-firewall' );
224
+ $description = __( 'When selected, a session is restricted to the same IP address as when the user logged in.', 'wp-simple-firewall' )
225
+ .' '.__( "If a logged-in user's IP address changes, the session will be invalidated and they'll be forced to re-login to WordPress.", 'wp-simple-firewall' );
226
  break;
227
 
228
  case 'session_username_concurrent_limit' :
229
+ $name = __( 'Max Simultaneous Sessions', 'wp-simple-firewall' );
230
+ $summary = __( 'Limit Simultaneous Sessions For The Same Username', 'wp-simple-firewall' );
231
+ $description = __( 'The number provided here is the maximum number of simultaneous, distinct, sessions allowed for any given username.', 'wp-simple-firewall' )
232
+ .'<br />'.__( "Zero (0) will allow unlimited simultaneous sessions.", 'wp-simple-firewall' );
233
  break;
234
 
235
  case 'reg_email_validate' :
236
+ $name = __( 'Validate Email Addresses', 'wp-simple-firewall' );
237
+ $summary = __( 'Validate Email Addresses When User Attempts To Register', 'wp-simple-firewall' );
238
+ $description = [
239
  __( 'Validate Email Addresses When User Attempts To Register.', 'wp-simple-firewall' ),
240
  __( 'To validate an email your site sends a request to the WPHashes API and may cause a small delay during the user registration request.', 'wp-simple-firewall' ),
241
  ];
242
  break;
243
 
244
  case 'email_checks' :
245
+ $name = __( 'Email Validation Checks', 'wp-simple-firewall' );
246
+ $summary = __( 'The Email Address Properties That Will Be Tested', 'wp-simple-firewall' );
247
+ $description = __( 'Select the properties that should be tested during email address validation.', 'wp-simple-firewall' );
248
  break;
249
 
250
  case 'enable_password_policies' :
251
+ $name = __( 'Enable Password Policies', 'wp-simple-firewall' );
252
+ $summary = __( 'Enable The Password Policies Detailed Below', 'wp-simple-firewall' );
253
+ $description = __( 'Turn on/off all password policy settings.', 'wp-simple-firewall' );
254
  break;
255
 
256
  case 'pass_prevent_pwned' :
257
+ $name = __( 'Prevent Pwned Passwords', 'wp-simple-firewall' );
258
+ $summary = __( 'Prevent Use Of "Pwned" Passwords', 'wp-simple-firewall' );
259
+ $description = __( 'Prevents users from using any passwords found on the public available list of "pwned" passwords.', 'wp-simple-firewall' );
260
  break;
261
 
262
  case 'pass_min_length' :
263
+ $name = __( 'Minimum Length', 'wp-simple-firewall' );
264
+ $summary = __( 'Minimum Password Length', 'wp-simple-firewall' );
265
+ $description = __( 'All passwords that a user sets must be at least this many characters in length.', 'wp-simple-firewall' )
266
+ .'<br/>'.__( 'Set to Zero(0) to disable.', 'wp-simple-firewall' );
267
  break;
268
 
269
  case 'pass_min_strength' :
270
+ $name = __( 'Minimum Strength', 'wp-simple-firewall' );
271
+ $summary = __( 'Minimum Password Strength', 'wp-simple-firewall' );
272
+ $description = __( 'All passwords that a user sets must meet this minimum strength.', 'wp-simple-firewall' );
273
  break;
274
 
275
  case 'pass_force_existing' :
276
+ $name = __( 'Apply To Existing Users', 'wp-simple-firewall' );
277
+ $summary = __( 'Apply Password Policies To Existing Users and Their Passwords', 'wp-simple-firewall' );
278
+ $description = __( "Forces existing users to update their passwords if they don't meet requirements, after they next login.", 'wp-simple-firewall' )
279
+ .'<br/>'.__( 'Note: You may want to warn users prior to enabling this option.', 'wp-simple-firewall' );
280
  break;
281
 
282
  case 'pass_expire' :
283
+ $name = __( 'Password Expiration', 'wp-simple-firewall' );
284
+ $summary = __( 'Passwords Expire After This Many Days', 'wp-simple-firewall' );
285
+ $description = __( 'Users will be forced to reset their passwords after the number of days specified.', 'wp-simple-firewall' )
286
+ .'<br/>'.__( 'Set to Zero(0) to disable.', 'wp-simple-firewall' );
287
  break;
288
 
289
  case 'manual_suspend' :
290
+ $name = __( 'Allow Manual User Suspension', 'wp-simple-firewall' );
291
+ $summary = __( 'Manually Suspend User Accounts To Prevent Login', 'wp-simple-firewall' );
292
+ $description = __( 'Users may be suspended by administrators to prevent future login.', 'wp-simple-firewall' );
293
  break;
294
 
295
  case 'auto_password' :
296
+ $name = __( 'Auto-Suspend Expired Passwords', 'wp-simple-firewall' );
297
+ $summary = __( 'Automatically Suspend Users With Expired Passwords', 'wp-simple-firewall' );
298
+ $description = __( 'Automatically suspends login by users and requires password reset to unsuspend.', 'wp-simple-firewall' )
299
+ .'<br/>'.sprintf(
300
+ '<strong>%s</strong> - %s',
301
+ __( 'Important', 'wp-simple-firewall' ),
302
+ __( 'Requires password expiration policy to be set.', 'wp-simple-firewall' )
303
+ );
304
  break;
305
 
306
  case 'auto_idle_days' :
307
+ $name = __( 'Auto-Suspend Idle Users', 'wp-simple-firewall' );
308
+ $summary = __( 'Automatically Suspend Idle User Accounts', 'wp-simple-firewall' );
309
+ $description = __( 'Automatically suspends login for idle accounts and requires password reset to unsuspend.', 'wp-simple-firewall' )
310
+ .'<br/>'.__( 'Specify the number of days since last login to consider a user as idle.', 'wp-simple-firewall' )
311
+ .'<br/>'.__( 'Set to Zero(0) to disable.', 'wp-simple-firewall' );
312
  break;
313
 
314
  case 'auto_idle_roles' :
315
+ $name = __( 'Auto-Suspend Idle User Roles', 'wp-simple-firewall' );
316
+ $summary = __( 'Apply Automatic Suspension To Accounts With These Roles', 'wp-simple-firewall' );
317
+ $description = __( 'Automatic suspension for idle accounts applies only to the roles you specify.', 'wp-simple-firewall' )
318
+ .'<br/>'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( 'Take a new line for each user role.', 'wp-simple-firewall' ) )
319
+ .'<br/>'.sprintf( '%s: %s', __( 'Available Roles', 'wp-simple-firewall' ), implode( ', ', Services::WpUsers()
320
+ ->getAvailableUserRoles() ) )
321
+ .'<br/>'.sprintf( '%s: %s', __( 'Default', 'wp-simple-firewall' ), implode( ', ', $oOpts->getOptDefault( 'auto_idle_roles' ) ) );
322
  break;
323
 
324
  default:
326
  }
327
 
328
  return [
329
+ 'name' => $name,
330
+ 'summary' => $summary,
331
+ 'description' => $description,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  ];
333
  }
334
  }
src/lib/src/Modules/UserManagement/Suspend/Base.php DELETED
@@ -1,38 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement\Suspend;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Users\ShieldUserMeta;
7
-
8
- abstract class Base {
9
-
10
- use ModConsumer;
11
-
12
- const HOOK_PRIORITY = 1000; // so only authenticated user is notified of account state.
13
-
14
- public function run() {
15
- add_filter( 'authenticate', [ $this, 'checkUser' ], static::HOOK_PRIORITY );
16
- }
17
-
18
- /**
19
- * Should be a filter added to WordPress's "authenticate" filter, but before WordPress performs
20
- * it's own authentication (theirs is priority 30, so we could go in at around 20).
21
- * @param null|\WP_User|\WP_Error $oUser
22
- * @return \WP_User|\WP_Error
23
- */
24
- public function checkUser( $oUser ) {
25
- if ( $oUser instanceof \WP_User ) {
26
- $oUser = $this->processUser( $oUser, $this->getCon()->getUserMeta( $oUser ) );
27
- }
28
- return $oUser;
29
- }
30
-
31
- /**
32
- * Test the User and its Meta and if it fails return \WP_Error; Always return Error or User
33
- * @param \WP_User $oUser
34
- * @param ShieldUserMeta $oMeta
35
- * @return \WP_Error|\WP_User
36
- */
37
- abstract protected function processUser( $oUser, $oMeta );
38
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/UserManagement/Suspend/Idle.php DELETED
@@ -1,47 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement\Suspend;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Users\ShieldUserMeta;
7
- use FernleafSystems\Wordpress\Services\Services;
8
-
9
- class Idle extends Base {
10
-
11
- /**
12
- * @param \WP_User $oUser
13
- * @param ShieldUserMeta $oMeta
14
- * @return \WP_Error|\WP_User
15
- */
16
- protected function processUser( $oUser, $oMeta ) {
17
- /** @var UserManagement\Options $oOpts */
18
- $oOpts = $this->getOptions();
19
-
20
- $aRoles = array_intersect( $oOpts->getSuspendAutoIdleUserRoles(), array_map( 'strtolower', $oUser->roles ) );
21
-
22
- if ( count( $aRoles ) > 0 && $this->isLastVerifiedAtExpired( $oMeta ) ) {
23
- $oUser = new \WP_Error(
24
- $this->getCon()->prefix( 'pass-expired' ),
25
- implode( ' ', [
26
- __( 'Sorry, this account is suspended because of inactivity.', 'wp-simple-firewall' ),
27
- __( 'Please reset your password to regain access.', 'wp-simple-firewall' ),
28
- sprintf( '<a href="%s">%s &rarr;</a>',
29
- Services::WpGeneral()->getLostPasswordUrl(),
30
- __( 'Reset', 'wp-simple-firewall' )
31
- ),
32
- ] )
33
- );
34
- }
35
- return $oUser;
36
- }
37
-
38
- /**
39
- * @param ShieldUserMeta $oMeta
40
- * @return bool
41
- */
42
- protected function isLastVerifiedAtExpired( $oMeta ) {
43
- /** @var UserManagement\Options $oOpts */
44
- $oOpts = $this->getOptions();
45
- return ( Services::Request()->ts() - $oMeta->getLastVerifiedAt() > $oOpts->getSuspendAutoIdleTime() );
46
- }
47
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/UserManagement/Suspend/PasswordExpiry.php DELETED
@@ -1,71 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement\Suspend;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Users\ShieldUserMeta;
7
- use FernleafSystems\Wordpress\Services\Services;
8
-
9
- /**
10
- * Class PasswordExpiry
11
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement\Suspend
12
- */
13
- class PasswordExpiry extends Base {
14
-
15
- /**
16
- * @var int
17
- */
18
- private $nMaxPasswordAge;
19
-
20
- /**
21
- * @param \WP_User $oUser
22
- * @param ShieldUserMeta $oMeta
23
- * @return \WP_Error|\WP_User
24
- */
25
- protected function processUser( $oUser, $oMeta ) {
26
- if ( $this->isPassExpired( $oMeta ) ) {
27
-
28
- $oUser = new \WP_Error(
29
- $this->getCon()->prefix( 'pass-expired' ),
30
- implode( ' ', [
31
- __( 'Sorry, this account is suspended because the password has expired.', 'wp-simple-firewall' ),
32
- __( 'Please reset your password to regain access.', 'wp-simple-firewall' ),
33
- sprintf( '<a href="%s">%s &rarr;</a>',
34
- Services::WpGeneral()->getLostPasswordUrl(),
35
- __( 'Reset', 'wp-simple-firewall' )
36
- ),
37
- ] )
38
- );
39
- }
40
- return $oUser;
41
- }
42
-
43
- /**
44
- * @param ShieldUserMeta $oMeta
45
- * @return bool
46
- */
47
- private function isPassExpired( $oMeta ) {
48
- /** @var UserManagement\Options $oOpts */
49
- $oOpts = $this->getOptions();
50
- if ( empty( $oMeta->pass_started_at ) ) {
51
- $oMeta->pass_started_at = $oMeta->first_seen_at;
52
- }
53
- return ( Services::Request()->ts() - $oMeta->pass_started_at > $oOpts->getPassExpireTimeout() );
54
- }
55
-
56
- /**
57
- * @return int
58
- */
59
- public function getMaxPasswordAge() {
60
- return (int)$this->nMaxPasswordAge;
61
- }
62
-
63
- /**
64
- * @param int $nMaxPasswordAge
65
- * @return $this
66
- */
67
- public function setMaxPasswordAge( $nMaxPasswordAge ) {
68
- $this->nMaxPasswordAge = $nMaxPasswordAge;
69
- return $this;
70
- }
71
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/UserManagement/Suspend/Suspended.php DELETED
@@ -1,28 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement\Suspend;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Users\ShieldUserMeta;
6
-
7
- class Suspended extends Base {
8
-
9
- const HOOK_PRIORITY = 999; // we process hard suspension before all others.
10
-
11
- /**
12
- * @param \WP_User $oUser
13
- * @param ShieldUserMeta $oMeta
14
- * @return \WP_Error|\WP_User
15
- */
16
- protected function processUser( $oUser, $oMeta ) {
17
- if ( $oMeta->hard_suspended_at > 0 ) {
18
- $oUser = new \WP_Error(
19
- $this->getCon()->prefix( 'hard-suspended' ),
20
- implode( ' ', [
21
- __( 'Sorry, this account is suspended.', 'wp-simple-firewall' ),
22
- __( 'Please contact your website administrator.', 'wp-simple-firewall' ),
23
- ] )
24
- );
25
- }
26
- return $oUser;
27
- }
28
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/UserManagement/Upgrade.php CHANGED
@@ -6,14 +6,4 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
 
7
  class Upgrade extends Base\Upgrade {
8
 
9
- protected function upgrade_903() {
10
- /** @var Options $opts */
11
- $opts = $this->getOptions();
12
- $aChecks = $opts->getEmailValidationChecks();
13
- if ( in_array( 'domain', $aChecks ) ) {
14
- $aChecks[] = 'domain_registered';
15
- unset( $aChecks[ array_search( 'domain', $aChecks ) ] );
16
- $opts->setOpt( 'email_checks', $aChecks );
17
- }
18
- }
19
  }
6
 
7
  class Upgrade extends Base\Upgrade {
8
 
 
 
 
 
 
 
 
 
 
 
9
  }
src/lib/src/Scans/Base/BaseFileScanActionVO.php CHANGED
@@ -2,9 +2,6 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
 
5
- use FernleafSystems\Utilities\Data\Adapter\DynProperties;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Table\BaseEntryFormatter;
7
-
8
  /**
9
  * Class BaseFileScanActionVO
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Base
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
 
 
 
 
5
  /**
6
  * Class BaseFileScanActionVO
7
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Base
src/lib/src/Scans/Base/BaseScanActionVO.php CHANGED
@@ -3,7 +3,6 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
 
5
  use FernleafSystems\Utilities\Data\Adapter\DynProperties;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Table\BaseEntryFormatter;
7
 
8
  /**
9
  * Class ScanActionVO
@@ -41,16 +40,6 @@ abstract class BaseScanActionVO {
41
  return new $class();
42
  }
43
 
44
- /**
45
- * @return BaseEntryFormatter|mixed
46
- */
47
- public function getTableEntryFormatter() {
48
- $class = $this->getScanNamespace().'Table\\EntryFormatter';
49
- /** @var BaseEntryFormatter $formatter */
50
- $formatter = new $class();
51
- return $formatter->setScanActionVO( $this );
52
- }
53
-
54
  public function getScanNamespace() :string {
55
  try {
56
  $namespace = ( new \ReflectionClass( $this ) )->getNamespaceName();
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
 
5
  use FernleafSystems\Utilities\Data\Adapter\DynProperties;
 
6
 
7
  /**
8
  * Class ScanActionVO
40
  return new $class();
41
  }
42
 
 
 
 
 
 
 
 
 
 
 
43
  public function getScanNamespace() :string {
44
  try {
45
  $namespace = ( new \ReflectionClass( $this ) )->getNamespaceName();
src/lib/src/Scans/Base/Table/BaseEntryFormatter.php DELETED
@@ -1,82 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Table;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
9
- use FernleafSystems\Wordpress\Services\Services;
10
-
11
- abstract class BaseEntryFormatter {
12
-
13
- use Databases\Base\EntryVoConsumer;
14
- use Scans\Common\ScanActionConsumer;
15
- use Scan\Controller\ScanControllerConsumer;
16
- use ModConsumer;
17
-
18
- abstract public function format() :array;
19
-
20
- /**
21
- * @return string[]
22
- */
23
- protected function getSupportedActions() :array {
24
- return [
25
- 'ignore'
26
- ];
27
- }
28
-
29
- /**
30
- * @return array[]
31
- */
32
- protected function getActionDefinitions() :array {
33
- return [
34
- 'ignore' => [
35
- 'text' => __( 'Ignore', 'wp-simple-firewall' ),
36
- 'classes' => [ 'ignore' ],
37
- 'data' => []
38
- ],
39
- 'delete' => [
40
- 'text' => __( 'Delete', 'wp-simple-firewall' ),
41
- 'classes' => [ 'delete', 'text-danger' ],
42
- 'data' => []
43
- ],
44
- 'repair' => [
45
- 'text' => __( 'Repair', 'wp-simple-firewall' ),
46
- 'classes' => [ 'repair', 'text-success' ],
47
- 'data' => []
48
- ],
49
- 'download' => [
50
- 'text' => __( 'Download', 'wp-simple-firewall' ),
51
- 'classes' => [ 'href-download', 'text-info' ],
52
- 'data' => [ 'href-download' => $this->getScanController()->createFileDownloadLink( $this->getEntryVO()->id ) ]
53
- ],
54
- ];
55
- }
56
-
57
- protected function getBaseData() :array {
58
- return $this->getEntryVO()->getRawData();
59
- }
60
-
61
- /**
62
- * @return Scans\Base\ResultItem|mixed
63
- */
64
- protected function getResultItem() {
65
- return ( new Scan\Results\ConvertBetweenTypes() )
66
- ->setScanController( $this->getScanController() )
67
- ->convertVoToResultItem( $this->getEntryVO() );
68
- }
69
-
70
- /**
71
- * @param int $ts
72
- * @return string
73
- */
74
- protected function formatTimestampField( $ts ) {
75
- return Services::Request()
76
- ->carbon()
77
- ->setTimestamp( $ts )
78
- ->diffForHumans()
79
- .'<br/><span class="timestamp-small">'
80
- .Services::WpGeneral()->getTimeStringForDisplay( $ts ).'</span>';
81
- }
82
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Scans/Base/Table/BaseFileEntryFormatter.php DELETED
@@ -1,77 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Table;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
7
- use FernleafSystems\Wordpress\Services\Services;
8
-
9
- abstract class BaseFileEntryFormatter extends BaseEntryFormatter {
10
-
11
- protected function getBaseData() :array {
12
- $data = parent::getBaseData();
13
- $item = $this->getResultItem();
14
- $data[ 'explanation' ] = $this->getExplanation();
15
- $data[ 'path' ] = $item->path_fragment;
16
- $data[ 'path_relabs' ] = Services::WpFs()->getPathRelativeToAbsPath( $item->path_full );
17
- $data[ 'path_details' ] = [];
18
- $data[ 'created_at' ] = $this->formatTimestampField( $this->getEntryVO()->created_at );
19
- $data[ 'custom_row' ] = false;
20
-
21
- $actionDefs = array_intersect_key(
22
- $this->getActionDefinitions(),
23
- array_flip( array_unique( $this->getSupportedActions() ) )
24
- );
25
- foreach ( $actionDefs as $key => $actionDef ) {
26
- $actionDefs[ $key ][ 'data' ] = array_merge(
27
- $actionDef[ 'data' ],
28
- [
29
- 'rid' => $this->getEntryVO()->id,
30
- 'item_action' => $key,
31
- ]
32
- );
33
- $actionDefs[ $key ][ 'classes' ] = array_merge(
34
- $actionDef[ 'classes' ],
35
- [ 'action', 'item_action' ]
36
- );
37
- }
38
- $data[ 'actions' ] = $actionDefs;
39
-
40
- return $data;
41
- }
42
-
43
- /**
44
- * @return array[]
45
- */
46
- protected function getActionDefinitions() :array {
47
- return [
48
- 'ignore' => [
49
- 'text' => sprintf( __( 'Ignore %s', 'wp-simple-firewall' ), __( 'File', 'wp-simple-firewall' ) ),
50
- 'classes' => [],
51
- 'data' => []
52
- ],
53
- 'delete' => [
54
- 'text' => sprintf( __( 'Delete %s', 'wp-simple-firewall' ), __( 'File', 'wp-simple-firewall' ) ),
55
- 'classes' => [ 'text-danger' ],
56
- 'data' => []
57
- ],
58
- 'repair' => [
59
- 'text' => sprintf( __( 'Repair %s', 'wp-simple-firewall' ), __( 'File', 'wp-simple-firewall' ) ),
60
- 'classes' => [ 'text-success' ],
61
- 'data' => []
62
- ],
63
- 'download' => [
64
- 'text' => sprintf( __( 'Download %s', 'wp-simple-firewall' ), __( 'File', 'wp-simple-firewall' ) ),
65
- 'classes' => [ 'href-download', 'text-info' ],
66
- 'data' => [ 'href-download' => $this->getScanController()->createFileDownloadLink( $this->getEntryVO()->id ) ]
67
- ],
68
- ];
69
- }
70
-
71
- /**
72
- * @return string[]
73
- */
74
- protected function getExplanation() :array {
75
- return [];
76
- }
77
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Scans/Base/Utilities/ItemActionHandler.php CHANGED
@@ -134,7 +134,7 @@ abstract class ItemActionHandler {
134
  if ( !empty( $item->path_full ) && !empty( $item->repair_event_status ) ) {
135
  $this->getCon()->fireEvent(
136
  sprintf( 'scan_item_%s', $item->repair_event_status ),
137
- [ 'audit' => [ 'path_full' => $item->path_full ] ]
138
  );
139
  }
140
  }
134
  if ( !empty( $item->path_full ) && !empty( $item->repair_event_status ) ) {
135
  $this->getCon()->fireEvent(
136
  sprintf( 'scan_item_%s', $item->repair_event_status ),
137
+ [ 'audit_params' => [ 'path_full' => $item->path_full ] ]
138
  );
139
  }
140
  }
src/lib/src/Scans/Mal/BuildFileMap.php CHANGED
@@ -3,7 +3,6 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseBuildFileMap;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Common\ScanActionConsumer;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers\StandardDirectoryIterator;
8
 
9
  class BuildFileMap extends BaseBuildFileMap {
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseBuildFileMap;
 
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers\StandardDirectoryIterator;
7
 
8
  class BuildFileMap extends BaseBuildFileMap {
src/lib/src/Scans/Mal/BuildScanAction.php CHANGED
@@ -3,7 +3,6 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
7
 
8
  class BuildScanAction extends Shield\Scans\Base\BaseBuildScanAction {
9
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
 
6
 
7
  class BuildScanAction extends Shield\Scans\Base\BaseBuildScanAction {
8
 
src/lib/src/Scans/Mal/Scan.php CHANGED
@@ -5,10 +5,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
7
 
8
- /**
9
- * Class Scan
10
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal
11
- */
12
  class Scan extends Shield\Scans\Base\Files\BaseFileMapScan {
13
 
14
  /**
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
7
 
 
 
 
 
8
  class Scan extends Shield\Scans\Base\Files\BaseFileMapScan {
9
 
10
  /**
src/lib/src/Scans/Mal/ScanFromFileMap.php CHANGED
@@ -4,10 +4,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Files\BaseScanFromFileMap;
6
 
7
- /**
8
- * Class ScanFromFileMap
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal
10
- */
11
  class ScanFromFileMap extends BaseScanFromFileMap {
12
 
13
  /**
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Files\BaseScanFromFileMap;
6
 
 
 
 
 
7
  class ScanFromFileMap extends BaseScanFromFileMap {
8
 
9
  /**
src/lib/src/Scans/Mal/Table/EntryFormatter.php DELETED
@@ -1,84 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal\Table;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Table\BaseFileEntryFormatter;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
8
-
9
- class EntryFormatter extends BaseFileEntryFormatter {
10
-
11
- public function format() :array {
12
- $e = $this->getBaseData();
13
- $e[ 'status' ] = __( 'Potential Malware Detected', 'wp-simple-firewall' );
14
- if ( !array_key_exists( 'repair', $e[ 'actions' ] ) ) {
15
- $e[ 'explanation' ][] = __( 'Repair Unavailable', 'wp-simple-firewall' );
16
- }
17
- return $e;
18
- }
19
-
20
- /**
21
- * @return string[]
22
- */
23
- protected function getExplanation() :array {
24
- /** @var Mal\ResultItem $item */
25
- $item = $this->getResultItem();
26
-
27
- $exp = [
28
- sprintf( '%s: %s', __( 'Pattern Detected' ), $this->getPatternForDisplay( base64_decode( $item->mal_sig ) ) ),
29
- sprintf( '%s: %s', __( 'Affected line numbers' ),
30
- implode( ', ', array_map(
31
- function ( $nLineNumber ) {
32
- return $nLineNumber + 1;
33
- },
34
- $item->file_lines // because lines start at ZERO
35
- ) )
36
- ),
37
- ];
38
-
39
- /** @var HackGuard\Options $opts */
40
- $opts = $this->getOptions();
41
- if ( $opts->isMalUseNetworkIntelligence() ) {
42
- $exp[] = sprintf( '%s: %s/100 [%s]',
43
- __( 'Likelihood That This Is A False Positive' ),
44
- sprintf( '<strong>%s</strong>', (int)$item->fp_confidence ),
45
- sprintf( '<a href="%s" target="_blank">%s</a>', 'https://shsec.io/isthismalware', __( 'more info', 'wp-simple-firewall' ) )
46
- );
47
- }
48
-
49
- return $exp;
50
- }
51
-
52
- /**
53
- * @param string $text
54
- * @return string
55
- */
56
- private function getPatternForDisplay( $text ) :string {
57
- return sprintf( '<code style="white-space: nowrap">%s</code>', esc_html( $text ) );
58
- }
59
-
60
- /**
61
- * @inheritDoc
62
- */
63
- protected function getSupportedActions() :array {
64
- $actions = parent::getSupportedActions();
65
-
66
- /** @var Mal\ResultItem $item */
67
- $item = $this->getResultItem();
68
-
69
- try {
70
- $bCanRepair = ( new Mal\Utilities\Repair() )
71
- ->setMod( $this->getMod() )
72
- ->setScanItem( $item )
73
- ->canRepair();
74
- }
75
- catch ( \Exception $e ) {
76
- $bCanRepair = false;
77
- }
78
-
79
- $actions[] = $bCanRepair ? 'repair' : 'delete';
80
- $actions[] = 'download';
81
-
82
- return $actions;
83
- }
84
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Scans/Ptg/Table/EntryFormatter.php DELETED
@@ -1,151 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg\Table;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Table\BaseFileEntryFormatter;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg;
7
- use FernleafSystems\Wordpress\Services\Services;
8
-
9
- class EntryFormatter extends BaseFileEntryFormatter {
10
-
11
- protected function getBaseData() :array {
12
- $data = parent::getBaseData();
13
- /** @var Ptg\ResultItem $item */
14
- $item = $this->getResultItem();
15
-
16
- if ( $item->context == 'plugins' ) {
17
- $asset = Services::WpPlugins()->getPluginAsVo( $item->slug );
18
- if ( !empty( $asset ) ) {
19
- $data[ 'path_details' ][] = sprintf( '%s: %s v%s',
20
- __( 'Plugin', 'wp-simple-firewall' ), $asset->Name, $asset->version );
21
- }
22
- }
23
- else {
24
- $asset = Services::WpThemes()->getThemeAsVo( $item->slug );
25
- if ( !empty( $asset ) ) {
26
- $data[ 'path_details' ][] = sprintf( '%s: %s v%s',
27
- __( 'Theme', 'wp-simple-firewall' ), $asset->wp_theme->get( 'Name' ), $asset->version );
28
- }
29
- }
30
-
31
- return $data;
32
- }
33
-
34
- public function format() :array {
35
- /** @var Ptg\ResultItem $item */
36
- $item = $this->getResultItem();
37
-
38
- $e = $this->getBaseData();
39
- $e[ 'status' ] = $item->is_different ? __( 'Modified', 'wp-simple-firewall' )
40
- : ( $item->is_missing ? __( 'Missing', 'wp-simple-firewall' ) : __( 'Unrecognised', 'wp-simple-firewall' ) );
41
- return $e;
42
- }
43
-
44
- /**
45
- * @inheritDoc
46
- */
47
- protected function getActionDefinitions() :array {
48
- /** @var Ptg\ResultItem $item */
49
- $item = $this->getResultItem();
50
- $assetType = ( $item->context == 'plugins' ? __( 'Plugin', 'wp-simple-firewall' ) : __( 'Theme', 'wp-simple-firewall' ) );
51
- return array_merge(
52
- parent::getActionDefinitions(),
53
- [
54
- 'asset_accept' => [
55
- 'text' => sprintf( __( 'Accept %s', 'wp-simple-firewall' ), $assetType ),
56
- 'title' => sprintf( __( 'Accept all current scan results for this %s.' ), $assetType ),
57
- 'classes' => [ 'asset_accept' ],
58
- 'data' => [],
59
- ],
60
- 'asset_reinstall' => [
61
- 'text' => sprintf( __( 'Re-Install %s', 'wp-simple-firewall' ), $assetType ),
62
- 'classes' => [ 'asset_reinstall' ],
63
- 'data' => []
64
- ],
65
- ]
66
- );
67
- }
68
-
69
- /**
70
- * @return string[]
71
- */
72
- protected function getExplanation() :array {
73
- /** @var Ptg\ResultItem $item */
74
- $item = $this->getResultItem();
75
-
76
- if ( $item->is_different ) {
77
- $expl = [
78
- __( "This file appears to have been modified from its original content.", 'wp-simple-firewall' )
79
- .' '.__( "This may be okay if you're editing files directly on your site.", 'wp-simple-firewall' ),
80
- __( "You may want to download it to ensure that the contents are as you expect.", 'wp-simple-firewall' )
81
- .' '.sprintf( __( "You can then click to '%s' the file.", 'wp-simple-firewall' ),
82
- __( 'Ignore', 'wp-simple-firewall' ) ),
83
- ];
84
- }
85
- elseif ( $item->is_missing ) {
86
- $expl = [
87
- __( "This file appears to have been removed from your site.", 'wp-simple-firewall' )
88
- .' '.__( "This may be okay if you're editing files directly on your site.", 'wp-simple-firewall' ),
89
- __( "If you're unsure, you should check whether this is okay.", 'wp-simple-firewall' )
90
- .' '.sprintf( __( "You can then click to '%s' the file.", 'wp-simple-firewall' ),
91
- __( 'Ignore', 'wp-simple-firewall' ) ),
92
- ];
93
- }
94
- else {
95
- $expl = [
96
- __( "This file appears to have been added to your site.", 'wp-simple-firewall' ),
97
- __( "This is not normal in the vast majority of cases.", 'wp-simple-firewall' ),
98
- __( "You may want to download it to ensure that the contents are what you expect.", 'wp-simple-firewall' )
99
- .' '.sprintf( __( "You can then click to '%s' or '%s' the file.", 'wp-simple-firewall' ),
100
- __( 'Ignore', 'wp-simple-firewall' ), __( 'Delete', 'wp-simple-firewall' ) ),
101
- ];
102
- }
103
-
104
- return $expl;
105
- }
106
-
107
- /**
108
- * @inheritDoc
109
- */
110
- protected function getSupportedActions() :array {
111
- /** @var Ptg\ResultItem $item */
112
- $item = $this->getResultItem();
113
-
114
- $extras = [
115
- 'asset_accept'
116
- ];
117
-
118
- if ( $item->context == 'plugins' ) {
119
- $asset = Services::WpPlugins()->getPluginAsVo( $item->slug );
120
- }
121
- else {
122
- $asset = Services::WpThemes()->getThemeAsVo( $item->slug );
123
- }
124
-
125
- $canRepair = ( new Ptg\Utilities\Repair() )
126
- ->setScanItem( $item )
127
- ->canRepair();
128
- $hasUpdate = $asset->hasUpdate();
129
-
130
- if ( $hasUpdate ) {
131
- $extras[] = 'update';
132
- }
133
-
134
- if ( $item->is_unrecognised ) {
135
- $extras[] = 'delete';
136
- }
137
- elseif ( $canRepair ) {
138
- $extras[] = 'repair';
139
- }
140
-
141
- if ( $canRepair && !$hasUpdate ) {
142
- $extras[] = 'asset_reinstall';
143
- }
144
-
145
- if ( !$item->is_missing ) {
146
- $extras[] = 'download';
147
- }
148
-
149
- return array_merge( parent::getSupportedActions(), $extras );
150
- }
151
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Scans/Ufc/Table/EntryFormatter.php DELETED
@@ -1,34 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc\Table;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Table\BaseFileEntryFormatter;
6
-
7
- class EntryFormatter extends BaseFileEntryFormatter {
8
-
9
- public function format() :array {
10
- $e = $this->getBaseData();
11
- $e[ 'status' ] = __( 'Unrecognised', 'wp-simple-firewall' );
12
- return $e;
13
- }
14
-
15
- /**
16
- * @return string[]
17
- */
18
- protected function getExplanation() :array {
19
- return [
20
- __( 'This file was discovered within one of your core WordPress directories.', 'wp-simple-firewall' ),
21
- __( "But it isn't part of the official WordPress distribution for this version.", 'wp-simple-firewall' ),
22
- __( "You may want to download it to ensure that the contents are what you expect.", 'wp-simple-firewall' )
23
- .' '.sprintf( __( "You can then click to '%s' or '%s' the file.", 'wp-simple-firewall' ),
24
- __( 'Ignore', 'wp-simple-firewall' ), __( 'Delete', 'wp-simple-firewall' ) ),
25
- ];
26
- }
27
-
28
- /**
29
- * @inheritDoc
30
- */
31
- protected function getSupportedActions() :array {
32
- return array_merge( parent::getSupportedActions(), [ 'delete', 'download' ] );
33
- }
34
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Scans/Wcf/Table/EntryFormatter.php DELETED
@@ -1,89 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf\Table;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Table\BaseFileEntryFormatter;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf\ResultItem;
7
-
8
- class EntryFormatter extends BaseFileEntryFormatter {
9
-
10
- public function format() :array {
11
- /** @var ResultItem $item */
12
- $item = $this->getResultItem();
13
-
14
- $e = $this->getBaseData();
15
-
16
- $e[ 'status' ] = $item->is_checksumfail ? __( 'Modified', 'wp-simple-firewall' )
17
- : ( $item->is_missing ? __( 'Missing', 'wp-simple-firewall' ) : __( 'Unknown', 'wp-simple-firewall' ) );
18
-
19
- if ( $item->is_checksumfail ) {
20
- $e[ 'explanation' ] = [
21
- __( 'This file is an official WordPress core file.', 'wp-simple-firewall' ),
22
- __( "But, it appears to have been modified when compared to the official WordPress distribution.", 'wp-simple-firewall' )
23
- .' '.__( "This is not normal in the vast majority of cases.", 'wp-simple-firewall' ),
24
- __( "You may want to download it to ensure that the contents are what you expect.", 'wp-simple-firewall' )
25
- .' '.sprintf( __( "You can then click to '%s' or '%s' the file.", 'wp-simple-firewall' ),
26
- __( 'Ignore', 'wp-simple-firewall' ), __( 'Repair', 'wp-simple-firewall' ) ),
27
- ];
28
- }
29
- elseif ( $item->is_missing ) {
30
- $e[ 'explanation' ] = [
31
- __( 'This file is an official WordPress core file.', 'wp-simple-firewall' ),
32
- __( "But, it appears to be missing from your site.", 'wp-simple-firewall' ),
33
- __( "You may want to check why this might be missing.", 'wp-simple-firewall' )
34
- .' '.sprintf( __( "You can then click to '%s' or '%s' the file.", 'wp-simple-firewall' ),
35
- __( 'Ignore', 'wp-simple-firewall' ), __( 'Repair', 'wp-simple-firewall' ) ),
36
- ];
37
- }
38
-
39
- return $e;
40
- }
41
-
42
- /**
43
- * @return string[]
44
- */
45
- protected function getExplanation() :array {
46
- /** @var ResultItem $oIt */
47
- $oIt = $this->getResultItem();
48
-
49
- if ( $oIt->is_checksumfail ) {
50
- $expl = [
51
- __( 'This file is an official WordPress core file.', 'wp-simple-firewall' ),
52
- __( "But, it appears to have been modified when compared to the official WordPress distribution.", 'wp-simple-firewall' )
53
- .' '.__( "This is not normal in the vast majority of cases.", 'wp-simple-firewall' ),
54
- __( "You may want to download it to ensure that the contents are what you expect.", 'wp-simple-firewall' )
55
- .' '.sprintf( __( "You can then click to '%s' or '%s' the file.", 'wp-simple-firewall' ),
56
- __( 'Ignore', 'wp-simple-firewall' ), __( 'Repair', 'wp-simple-firewall' ) ),
57
- ];
58
- }
59
- elseif ( $oIt->is_missing ) {
60
- $expl = [
61
- __( 'This file is an official WordPress core file.', 'wp-simple-firewall' ),
62
- __( "But, it appears to be missing from your site.", 'wp-simple-firewall' ),
63
- __( "You may want to check why this might be missing.", 'wp-simple-firewall' )
64
- .' '.sprintf( __( "You can then click to '%s' or '%s' the file.", 'wp-simple-firewall' ),
65
- __( 'Ignore', 'wp-simple-firewall' ), __( 'Repair', 'wp-simple-firewall' ) ),
66
- ];
67
- }
68
- else {
69
- $expl = [];
70
- }
71
-
72
- return $expl;
73
- }
74
-
75
- /**
76
- * @inheritDoc
77
- */
78
- protected function getSupportedActions() :array {
79
- $extras = [ 'repair' ];
80
-
81
- /** @var ResultItem $oIt */
82
- $oIt = $this->getResultItem();
83
- if ( $oIt->is_checksumfail ) {
84
- $extras[] = 'download';
85
- }
86
-
87
- return array_merge( parent::getSupportedActions(), $extras );
88
- }
89
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Scans/Wpv/Scan.php CHANGED
@@ -20,13 +20,13 @@ class Scan extends Shield\Scans\Base\BaseScan {
20
  $copier->copyTo( $results, $tmpResults );
21
  }
22
  }
23
- $items = [];
24
- if ( $tmpResults->hasItems() ) {
25
- foreach ( $tmpResults->getAllItems() as $item ) {
26
- $items[] = $item->getRawData();
27
- }
28
- }
29
- $action->results = $items;
30
  }
31
 
32
  /**
20
  $copier->copyTo( $results, $tmpResults );
21
  }
22
  }
23
+
24
+ $action->results = array_map(
25
+ function ( $item ) {
26
+ return $item->getRawData();
27
+ },
28
+ $tmpResults->getAllItems()
29
+ );
30
  }
31
 
32
  /**
src/lib/src/ShieldNetApi/SureSend/SendEmail.php CHANGED
@@ -40,9 +40,9 @@ class SendEmail extends BaseShieldNetApi {
40
  $this->getCon()->fireEvent(
41
  $success ? 'suresend_success' : 'suresend_fail',
42
  [
43
- 'audit' => [
44
- 'user_login' => $to,
45
- 'slug' => $slug,
46
  ]
47
  ]
48
  );
40
  $this->getCon()->fireEvent(
41
  $success ? 'suresend_success' : 'suresend_fail',
42
  [
43
+ 'audit_params' => [
44
+ 'email' => $to,
45
+ 'slug' => $slug,
46
  ]
47
  ]
48
  );
src/lib/src/Tables/Build/AuditTrail.php CHANGED
@@ -86,24 +86,11 @@ class AuditTrail extends BaseBuild {
86
 
87
  $srvIP = Services::IP();
88
  $you = $srvIP->getRequestIp();
89
- $con = $this->getCon();
90
- foreach ( $this->getEntriesRaw() as $key => $entry ) {
91
  /** @var Shield\Databases\AuditTrail\EntryVO $entry */
92
 
93
- $msg = 'Audit message could not be retrieved';
94
  if ( empty( $entry->message ) ) {
95
- /**
96
- * To cater for the contexts that don't refer to a module, but rather a context
97
- * with the Audit Trail module
98
- */
99
- $mod = $con->getModule( $entry->context );
100
- if ( empty( $mod ) ) {
101
- $mod = $con->getModule_AuditTrail();
102
- }
103
-
104
- $msg = Shield\Modules\AuditTrail\Lib\AuditMessageBuilder::Build(
105
- $entry, $mod->getStrings()->getAuditMessage( $entry->event )
106
- );
107
  }
108
  else {
109
  $msg = $entry->message;
86
 
87
  $srvIP = Services::IP();
88
  $you = $srvIP->getRequestIp();
89
+ foreach ( $this->getEntriesRaw() as $entry ) {
 
90
  /** @var Shield\Databases\AuditTrail\EntryVO $entry */
91
 
 
92
  if ( empty( $entry->message ) ) {
93
+ $msg = Shield\Modules\AuditTrail\Lib\AuditMessageBuilder::Build( $entry->event, $entry->meta );
 
 
 
 
 
 
 
 
 
 
 
94
  }
95
  else {
96
  $msg = $entry->message;
src/lib/src/Tables/Build/BaseBuild.php CHANGED
@@ -92,11 +92,11 @@ class BaseBuild {
92
 
93
  /**
94
  * Override this to filter entries that cannot be filtered using SQL WHERE
95
- * @param array[] $aEntries
96
  * @return array[]
97
  */
98
- protected function postSelectEntriesFilter( $aEntries ) {
99
- return $aEntries;
100
  }
101
 
102
  /**
92
 
93
  /**
94
  * Override this to filter entries that cannot be filtered using SQL WHERE
95
+ * @param array[] $entries
96
  * @return array[]
97
  */
98
+ protected function postSelectEntriesFilter( $entries ) {
99
+ return $entries;
100
  }
101
 
102
  /**
src/lib/src/Tables/Build/ScanAggregate.php DELETED
@@ -1,131 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Build;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\EntryVO;
8
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
9
-
10
- class ScanAggregate extends ScanBase {
11
-
12
- /**
13
- * @return $this
14
- */
15
- protected function preBuildTable() {
16
- /** @var HackGuard\ModCon $mod */
17
- $mod = $this->getMod();
18
-
19
- foreach ( $this->getIncludedScanSlugs() as $scan ) {
20
- $mod->getScanCon( $scan )->cleanStalesResults();
21
- }
22
-
23
- return $this;
24
- }
25
-
26
- /**
27
- * @return array[]
28
- */
29
- public function getEntriesFormatted() :array {
30
- // first filter out PTG results as we process them a bit separately.
31
- $ptgScanEntries = [];
32
- /** @var Scanner\EntryVO[] $raw */
33
- $raw = $this->getEntriesRaw();
34
- foreach ( $raw as $key => $entry ) {
35
- if ( $entry->scan == 'ptg' ) {
36
- unset( $raw[ $key ] );
37
- $ptgScanEntries[ $key ] = $entry;
38
- }
39
- }
40
-
41
- $aEntries = $this->processEntriesGroup( $raw );
42
-
43
- // Group all PTG entries together
44
- usort( $ptgScanEntries, function ( $oE1, $oE2 ) {
45
- /** @var $oE1 EntryVO */
46
- /** @var $oE2 EntryVO */
47
- return strcasecmp( $oE1->meta[ 'path_full' ], $oE2->meta[ 'path_full' ] );
48
- } );
49
-
50
- return array_merge(
51
- $aEntries,
52
- $this->processEntriesGroup( $ptgScanEntries )
53
- );
54
- }
55
-
56
- /**
57
- * @param Scanner\EntryVO[] $entries
58
- * @return array[]
59
- */
60
- private function processEntriesGroup( array $entries ) {
61
- $processed = [];
62
-
63
- /** @var HackGuard\ModCon $mod */
64
- $mod = $this->getMod();
65
- /** @var HackGuard\Strings $strings */
66
- $strings = $mod->getStrings();
67
- $scanNames = $strings->getScanNames();
68
-
69
- $aScanRowTracker = [];
70
- foreach ( $entries as $key => $entry ) {
71
- if ( empty( $aScanRowTracker[ $entry->scan ] ) ) {
72
- $aScanRowTracker[ $entry->scan ] = $entry->scan;
73
- $processed[ $entry->scan ] = [
74
- 'custom_row' => true,
75
- 'title' => $scanNames[ $entry->scan ],
76
- ];
77
- }
78
- $processed[ $key ] = $mod
79
- ->getScanCon( $entry->scan )
80
- ->getTableEntryFormatter()
81
- ->setMod( $this->getMod() )
82
- ->setEntryVO( $entry )
83
- ->format();
84
- }
85
-
86
- return $processed;
87
- }
88
-
89
- protected function getParamDefaults() :array {
90
- return array_merge(
91
- parent::getParamDefaults(),
92
- [ 'orderby' => 'scan', ]
93
- );
94
- }
95
-
96
- /**
97
- * Override this to apply table-specific query filters.
98
- * @return $this
99
- */
100
- protected function applyCustomQueryFilters() {
101
- $aParams = $this->getParams();
102
- /** @var Scanner\Select $oSelector */
103
- $oSelector = $this->getWorkingSelector();
104
-
105
- if ( empty( $aParams[ 'fIgnored' ] ) || $aParams[ 'fIgnored' ] !== 'Y' ) {
106
- $oSelector->filterByNotIgnored();
107
- }
108
-
109
- $oSelector->filterByScans( $this->getIncludedScanSlugs() );
110
-
111
- return $this;
112
- }
113
-
114
- /**
115
- * @return string[]
116
- */
117
- private function getIncludedScanSlugs() :array {
118
- return [ 'mal' ];
119
- }
120
-
121
- protected function getCustomParams() :array {
122
- return [];
123
- }
124
-
125
- /**
126
- * @return Shield\Tables\Render\WpListTable\ScanAggregate
127
- */
128
- protected function getTableRenderer() {
129
- return new Shield\Tables\Render\WpListTable\ScanAggregate();
130
- }
131
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Build/ScanApc.php DELETED
@@ -1,55 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Build;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan;
7
- use FernleafSystems\Wordpress\Services\Services;
8
-
9
- /**
10
- * Class ScanApc
11
- * @package FernleafSystems\Wordpress\Plugin\Shield\Tables\Build
12
- */
13
- class ScanApc extends ScanBase {
14
-
15
- /**
16
- * @return array[]
17
- */
18
- public function getEntriesFormatted() :array {
19
- $aEntries = [];
20
-
21
- /** @var Shield\Modules\HackGuard\ModCon $mod */
22
- $mod = $this->getMod();
23
-
24
- $oCarbon = Services::Request()->carbon();
25
-
26
- $oConverter = new Scan\Results\ConvertBetweenTypes();
27
-
28
- $oWpPlugins = Services::WpPlugins();
29
- foreach ( $this->getEntriesRaw() as $nKey => $entry ) {
30
- /** @var Shield\Databases\Scanner\EntryVO $entry */
31
- /** @var Shield\Scans\Apc\ResultItem $item */
32
- $item = $oConverter
33
- ->setScanController( $mod->getScanCon( $entry->scan ) )
34
- ->convertVoToResultItem( $entry );
35
- $oPlugin = $oWpPlugins->getPluginAsVo( $item->slug );
36
- $aE = $entry->getRawData();
37
- $aE[ 'plugin' ] = sprintf( '%s (%s)', $oPlugin->Name, $oPlugin->Version );
38
- $aE[ 'status' ] = sprintf( '%s: %s',
39
- __( 'Abandoned', 'wp-simple-firewall' ), $oCarbon->setTimestamp( $item->last_updated_at )
40
- ->diffForHumans() );
41
- $aE[ 'ignored' ] = $this->formatIsIgnored( $entry );
42
- $aE[ 'created_at' ] = $this->formatTimestampField( $entry->created_at );
43
- $aEntries[ $nKey ] = $aE;
44
- }
45
-
46
- return $aEntries;
47
- }
48
-
49
- /**
50
- * @return Shield\Tables\Render\WpListTable\ScanApc
51
- */
52
- protected function getTableRenderer() {
53
- return new Shield\Tables\Render\WpListTable\ScanApc();
54
- }
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Build/ScanBase.php DELETED
@@ -1,81 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Build;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Tables;
8
- use FernleafSystems\Wordpress\Services\Services;
9
-
10
- /**
11
- * Class ScanBase
12
- * @package FernleafSystems\Wordpress\Plugin\Shield\Tables\Build
13
- */
14
- class ScanBase extends BaseBuild {
15
-
16
- protected function buildEmpty() :string {
17
- return sprintf( '<div class="alert alert-success m-0">%s</div>',
18
- __( "The previous scan either didn't detect any items that require your attention or they've already been repaired.", 'wp-simple-firewall' ) );
19
- }
20
-
21
- /**
22
- * @return array[]
23
- */
24
- public function getEntriesFormatted() :array {
25
- $entries = [];
26
-
27
- /** @var ModCon $mod */
28
- $mod = $this->getMod();
29
- foreach ( $this->getEntriesRaw() as $key => $entry ) {
30
- /** @var Scanner\EntryVO $entry */
31
- $entries[ $key ] = $mod->getScanCon( $entry->scan )
32
- ->getTableEntryFormatter()
33
- ->setEntryVO( $entry )
34
- ->format();
35
- }
36
-
37
- return $entries;
38
- }
39
-
40
- /**
41
- * Override this to apply table-specific query filters.
42
- * @return $this
43
- */
44
- protected function applyCustomQueryFilters() {
45
- $aParams = $this->getParams();
46
- /** @var Scanner\Select $oSelector */
47
- $oSelector = $this->getWorkingSelector();
48
-
49
- $oSelector->filterByScan( $aParams[ 'fScan' ] );
50
-
51
- if ( $aParams[ 'fIgnored' ] !== 'Y' ) {
52
- $oSelector->filterByNotIgnored();
53
- }
54
-
55
- return $this;
56
- }
57
-
58
- protected function getCustomParams() :array {
59
- return [
60
- 'fScan' => 'wcf',
61
- 'fSlug' => '',
62
- 'fIgnored' => 'N',
63
- ];
64
- }
65
-
66
- protected function getParamDefaults() :array {
67
- return array_merge(
68
- parent::getParamDefaults(),
69
- [ 'limit' => PHP_INT_MAX ]
70
- );
71
- }
72
-
73
- /**
74
- * @param Scanner\EntryVO $entry
75
- * @return string
76
- */
77
- protected function formatIsIgnored( $entry ) {
78
- return ( $entry->ignored_at > 0 && Services::Request()->ts() > $entry->ignored_at ) ?
79
- __( 'Yes' ) : __( 'No' );
80
- }
81
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Build/ScanMal.php DELETED
@@ -1,19 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Build;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield;
6
-
7
- /**
8
- * Class ScanMal
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Tables\Build
10
- */
11
- class ScanMal extends ScanBase {
12
-
13
- /**
14
- * @return Shield\Tables\Render\WpListTable\ScanMal
15
- */
16
- protected function getTableRenderer() {
17
- return new Shield\Tables\Render\WpListTable\ScanMal();
18
- }
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Build/ScanPtg.php DELETED
@@ -1,49 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Build;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan;
8
-
9
- /**
10
- * Class ScanPtg
11
- * @package FernleafSystems\Wordpress\Plugin\Shield\Tables\Build
12
- */
13
- class ScanPtg extends ScanBase {
14
-
15
- /**
16
- * Since we can't select items by slug directly from the scan results DB
17
- * we have to post-filter the results.
18
- * @param Shield\Databases\Scanner\EntryVO[] $aEntries
19
- * @return Shield\Databases\Scanner\EntryVO[]
20
- */
21
- protected function postSelectEntriesFilter( $aEntries ) {
22
- $params = $this->getParams();
23
-
24
- /** @var ModCon $mod */
25
- $mod = $this->getMod();
26
-
27
- if ( !empty( $params[ 'fSlug' ] ) ) {
28
-
29
-
30
- /** @var Shield\Scans\Ptg\ResultsSet $oSlugResults */
31
- $oSlugResults = ( new Scan\Results\ConvertBetweenTypes() )
32
- ->setScanController( $mod->getScanCon( $params[ 'fSlug' ] ) )
33
- ->fromVOsToResultsSet( $aEntries );
34
- $oSlugResults = $oSlugResults->getResultsSetForSlug( $params[ 'fSlug' ] );
35
-
36
- foreach ( $aEntries as $key => $oVo ) {
37
- if ( !$oSlugResults->getItemExists( $oVo->hash ) ) {
38
- unset( $aEntries[ $key ] );
39
- }
40
- }
41
- }
42
-
43
- return array_values( $aEntries );
44
- }
45
-
46
- protected function getTableRenderer() :Shield\Tables\Render\WpListTable\ScanPtg {
47
- return new Shield\Tables\Render\WpListTable\ScanPtg();
48
- }
49
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Build/ScanUfc.php DELETED
@@ -1,19 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Build;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield;
6
-
7
- /**
8
- * Class ScanUfc
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Tables\Build
10
- */
11
- class ScanUfc extends ScanBase {
12
-
13
- /**
14
- * @return Shield\Tables\Render\WpListTable\ScanUfc
15
- */
16
- protected function getTableRenderer() {
17
- return new Shield\Tables\Render\WpListTable\ScanUfc();
18
- }
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Build/ScanWcf.php DELETED
@@ -1,19 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Build;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield;
6
-
7
- /**
8
- * Class ScanWcf
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Tables\Build
10
- */
11
- class ScanWcf extends ScanBase {
12
-
13
- /**
14
- * @return Shield\Tables\Render\WpListTable\ScanWcf
15
- */
16
- protected function getTableRenderer() {
17
- return new Shield\Tables\Render\WpListTable\ScanWcf();
18
- }
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Build/ScanWpv.php DELETED
@@ -1,72 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Build;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan;
8
- use FernleafSystems\Wordpress\Services\Services;
9
-
10
- /**
11
- * Class ScanWpv
12
- * @package FernleafSystems\Wordpress\Plugin\Shield\Tables\Build
13
- */
14
- class ScanWpv extends ScanBase {
15
-
16
- /**
17
- * @return array[]
18
- */
19
- public function getEntriesFormatted() :array {
20
- $entries = [];
21
-
22
- /** @var ModCon $mod */
23
- $mod = $this->getMod();
24
-
25
- $WPP = Services::WpPlugins();
26
- $WPT = Services::WpThemes();
27
-
28
- // so that any available update will show
29
- $WPP->getUpdates( true );
30
- $WPT->getUpdates( true );
31
-
32
- $oConverter = new Scan\Results\ConvertBetweenTypes();
33
- foreach ( $this->getEntriesRaw() as $key => $entry ) {
34
- /** @var Shield\Databases\Scanner\EntryVO $entry */
35
- /** @var Shield\Scans\Wpv\ResultItem $item */
36
- $item = $oConverter
37
- ->setScanController( $mod->getScanCon( $entry->scan ) )
38
- ->convertVoToResultItem( $entry );
39
- $e = $entry->getRawData();
40
- if ( $item->context == 'plugins' ) {
41
- $asset = $WPP->getPluginAsVo( $item->slug );
42
- $e[ 'asset' ] = $asset;
43
- $e[ 'asset_name' ] = $asset->Name;
44
- $e[ 'asset_version' ] = $asset->Version;
45
- $e[ 'can_deactivate' ] = $WPP->isActive( $item->slug );
46
- $e[ 'has_update' ] = $WPP->isUpdateAvailable( $item->slug );
47
- }
48
- else {
49
- $asset = $WPT->getTheme( $item->slug );
50
- $e[ 'asset' ] = $asset;
51
- $e[ 'asset_name' ] = $asset->get( 'Name' );
52
- $e[ 'asset_version' ] = $asset->get( 'Version' );
53
- $e[ 'can_deactivate' ] = false;
54
- $e[ 'has_update' ] = $WPT->isUpdateAvailable( $item->slug );
55
- }
56
- $e[ 'slug' ] = $item->slug;
57
- $e[ 'wpvuln_vo' ] = $item->getVulnVo();
58
- $e[ 'ignored' ] = $this->formatIsIgnored( $entry );
59
- $e[ 'created_at' ] = $this->formatTimestampField( $entry->created_at );
60
- $entries[ $key ] = $e;
61
- }
62
-
63
- return $entries;
64
- }
65
-
66
- /**
67
- * @return Shield\Tables\Render\WpListTable\ScanWpv
68
- */
69
- protected function getTableRenderer() {
70
- return new Shield\Tables\Render\WpListTable\ScanWpv();
71
- }
72
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Build/Traffic.php DELETED
@@ -1,208 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Build;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\GeoIp\Lookup;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\LookupIpOnList;
8
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
9
- use FernleafSystems\Wordpress\Plugin\Shield\Tables;
10
- use FernleafSystems\Wordpress\Services\Services;
11
-
12
- class Traffic extends BaseBuild {
13
-
14
- /**
15
- * Override this to apply table-specific query filters.
16
- * @return $this
17
- */
18
- protected function applyCustomQueryFilters() {
19
- $params = $this->getParams();
20
- /** @var Databases\Traffic\Select $select */
21
- $select = $this->getWorkingSelector();
22
-
23
- $srvIP = Services::IP();
24
- // If an IP is specified, it takes priority
25
- if ( $srvIP->isValidIp( $params[ 'fIp' ] ) ) {
26
- $select->filterByIp( inet_pton( $params[ 'fIp' ] ) );
27
- }
28
- elseif ( $params[ 'fExcludeYou' ] == 'Y' ) {
29
- $select->filterByNotIp( inet_pton( $srvIP->getRequestIp() ) );
30
- }
31
-
32
- // if username is provided, this takes priority over "logged-in" (even if it's invalid)
33
- if ( !empty( $params[ 'fUsername' ] ) ) {
34
- $oUser = Services::WpUsers()->getUserByUsername( $params[ 'fUsername' ] );
35
- if ( !empty( $oUser ) ) {
36
- $select->filterByUserId( $oUser->ID );
37
- }
38
- }
39
- elseif ( $params[ 'fLoggedIn' ] >= 0 ) {
40
- $select->filterByIsLoggedIn( $params[ 'fLoggedIn' ] );
41
- }
42
-
43
- if ( $params[ 'fOffense' ] >= 0 ) {
44
- $select->filterByIsTransgression( $params[ 'fOffense' ] );
45
- }
46
-
47
- $select->filterByPathContains( $params[ 'fPath' ] );
48
- $select->filterByResponseCode( $params[ 'fResponse' ] );
49
-
50
- return $this;
51
- }
52
-
53
- protected function buildEmpty() :string {
54
- return sprintf( '<div class="alert alert-success m-0">%s</div>',
55
- __( "No requests have been logged.", 'wp-simple-firewall' ) );
56
- }
57
-
58
- protected function getCustomParams() :array {
59
- return [
60
- 'fIp' => '',
61
- 'fUsername' => '',
62
- 'fLoggedIn' => -1,
63
- 'fPath' => '',
64
- 'fOffense' => -1,
65
- 'fResponse' => '',
66
- 'fExcludeYou' => '',
67
- ];
68
- }
69
-
70
- /**
71
- * @return array[]
72
- */
73
- public function getEntriesFormatted() :array {
74
- $entries = [];
75
-
76
- $oWpUsers = Services::WpUsers();
77
- $oGeoIpLookup = ( new Lookup() )->setDbHandler( $this->getCon()
78
- ->getModule_Plugin()
79
- ->getDbHandler_GeoIp() );
80
- $srvIP = Services::IP();
81
- $you = $srvIP->getRequestIp();
82
-
83
- $users = [ 0 => __( 'No', 'wp-simple-firewall' ) ];
84
- $ipInfos = [];
85
- foreach ( $this->getEntriesRaw() as $key => $record ) {
86
- /** @var Databases\Traffic\EntryVO $record */
87
- $ip = $record->ip;
88
-
89
- list( $preQuery, $query ) = explode( '?', $record->path.'?', 2 );
90
- $query = trim( $query, '?' );
91
- $sPath = strtoupper( $record->verb ).': <code>'.$preQuery
92
- .( empty( $query ) ? '' : '?<br/>'.$query ).'</code>';
93
-
94
- $sCodeType = 'success';
95
- if ( $record->code >= 400 ) {
96
- $sCodeType = 'danger';
97
- }
98
- elseif ( $record->code >= 300 ) {
99
- $sCodeType = 'warning';
100
- }
101
-
102
- $e = $record->getRawData();
103
- $e[ 'path' ] = $sPath;
104
- $e[ 'code' ] = sprintf( '<span class="badge badge-%s">%s</span>', $sCodeType, $record->code );
105
- $e[ 'trans' ] = sprintf(
106
- '<span class="badge badge-%s">%s</span>',
107
- $record->trans ? 'danger' : 'info',
108
- $record->trans ? __( 'Yes', 'wp-simple-firewall' ) : __( 'No', 'wp-simple-firewall' )
109
- );
110
- $e[ 'ip' ] = $ip;
111
- $e[ 'created_at' ] = $this->formatTimestampField( $record->created_at );
112
-
113
- try {
114
- $e[ 'is_you' ] = $srvIP->checkIp( $you, $record->ip );
115
- }
116
- catch ( \Exception $e ) {
117
- $e[ 'is_you' ] = false;
118
- }
119
- $ipLink = sprintf( '%s%s',
120
- $this->getIpAnalysisLink( $record->ip ),
121
- $e[ 'is_you' ] ? ' <small>('.__( 'This Is You', 'wp-simple-firewall' ).')</small>' : ''
122
- );
123
-
124
- if ( $record->uid > 0 ) {
125
- if ( !isset( $users[ $record->uid ] ) ) {
126
- $user = $oWpUsers->getUserById( $record->uid );
127
- $users[ $record->uid ] = empty( $user ) ? __( 'Unknown', 'wp-simple-firewall' ) :
128
- sprintf( '<a href="%s" target="_blank" title="Go To Profile">%s</a>',
129
- $oWpUsers->getAdminUrl_ProfileEdit( $user ), $user->user_login );
130
- }
131
- }
132
-
133
- if ( !empty( $record->ip ) ) {
134
- if ( !isset( $ipInfos[ $record->ip ] ) ) {
135
- $ipInfos[ $record->ip ] = $this->getIpInfo( $record->ip );
136
- }
137
- }
138
-
139
- $geoIP = $oGeoIpLookup
140
- ->setIP( $ip )
141
- ->lookupIp();
142
- $countryISO = $geoIP->getCountryCode();
143
- if ( empty( $countryISO ) ) {
144
- $country = __( 'Unknown', 'wp-simple-firewall' );
145
- }
146
- else {
147
- $country = sprintf(
148
- '<img class="icon-flag" src="%s" alt="%s" width="24px"/> %s',
149
- sprintf( 'https://api.aptoweb.com/api/v1/country/flag/%s.svg', strtolower( $countryISO ) ),
150
- $countryISO,
151
- $geoIP->getCountryName()
152
- );
153
- }
154
-
155
- $e[ 'visitor' ] = sprintf( '<div>%s</div>', implode( '</div><div>', [
156
- sprintf( '%s: %s', __( 'IP', 'wp-simple-firewall' ), $ipLink ),
157
- sprintf( '%s: %s', __( 'IP Status', 'wp-simple-firewall' ), $ipInfos[ $record->ip ] ?? 'n/a' ),
158
- sprintf( '%s: %s', __( 'Logged-In', 'wp-simple-firewall' ), $users[ $record->uid ] ),
159
- sprintf( '%s: %s', __( 'Location', 'wp-simple-firewall' ), $country ),
160
- esc_html( esc_js( sprintf( '%s - %s', __( 'User Agent', 'wp-simple-firewall' ), $record->ua ) ) ),
161
- ] ) );
162
-
163
- $e[ 'request_info' ] = sprintf( '<div>%s</div>', implode( '</div><div>', [
164
- sprintf( '%s: %s', __( 'Response', 'wp-simple-firewall' ), $e[ 'code' ] ),
165
- sprintf( '%s: %s', __( 'Offense', 'wp-simple-firewall' ), $e[ 'trans' ] ),
166
- ] ) );
167
-
168
- $entries[ $key ] = $e;
169
- }
170
- return $entries;
171
- }
172
-
173
- private function getIpInfo( string $ip ) :string {
174
- $record = ( new LookupIpOnList() )
175
- ->setDbHandler( $this->getCon()->getModule_IPs()->getDbHandler_IPs() )
176
- ->setIP( $ip )
177
- ->lookup();
178
-
179
- $badgeTemplate = '<span class="badge badge-%s">%s</span>';
180
- $status = __( 'No Record', 'wp-simple-firewall' );
181
- if ( !$record instanceof Databases\IPs\EntryVO ) {
182
- $status = __( 'No Record', 'wp-simple-firewall' );
183
- }
184
- elseif ( $record->blocked_at > 0 || $record->list === ModCon::LIST_MANUAL_BLACK ) {
185
- $status = sprintf( $badgeTemplate, 'danger', __( 'Blocked', 'wp-simple-firewall' ) );
186
- }
187
- elseif ( $record->list === ModCon::LIST_AUTO_BLACK ) {
188
- $status = sprintf( $badgeTemplate,
189
- 'warning',
190
- sprintf( _n( '%s offense', '%s offenses', $record->transgressions, 'wp-simple-firewall' ), $record->transgressions )
191
- );
192
- }
193
- elseif ( $record->list === ModCon::LIST_MANUAL_WHITE ) {
194
- $status = sprintf( $badgeTemplate,
195
- 'success',
196
- __( 'Bypass', 'wp-simple-firewall' )
197
- );
198
- }
199
- return $status;
200
- }
201
-
202
- /**
203
- * @return Tables\Render\WpListTable\Traffic
204
- */
205
- protected function getTableRenderer() {
206
- return new Tables\Render\WpListTable\Traffic();
207
- }
208
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/DataTables/Build/AuditTrail/ForAuditTrail.php CHANGED
@@ -2,109 +2,162 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\AuditTrail;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Base;
7
 
8
  class ForAuditTrail extends Base {
9
 
10
  protected function getOrderColumnSlug() :string {
11
- return 'detected';
12
  }
13
 
14
  protected function getColumnsToDisplay() :array {
15
  return [
 
 
 
 
 
 
 
 
16
  'rid',
17
- 'file_as_href',
18
- 'status',
19
- 'file_type',
20
- 'detected',
21
- 'actions',
22
  ];
23
  }
24
 
25
  protected function getColumnDefs() :array {
26
  return [
27
- 'rid' => [
28
- 'data' => 'rid',
29
- 'title' => 'ID',
30
- 'orderable' => true,
31
- 'searchable' => false,
32
- 'visible' => false,
 
 
 
 
33
  ],
34
- 'file' => [
35
- 'data' => 'file',
36
- 'title' => __( 'File' ),
37
- 'className' => 'file',
38
- 'orderable' => true,
39
- 'searchable' => true,
40
- 'visible' => true,
 
 
 
41
  ],
42
- 'file_as_href' => [
43
- 'data' => 'file_as_href',
44
- 'title' => __( 'File' ),
45
- 'className' => 'file_as_href',
46
- 'orderable' => true,
47
- 'searchable' => true,
48
- 'visible' => true,
 
 
 
49
  ],
50
- 'file_type' => [
51
- 'data' => 'file_type',
52
- 'title' => __( 'Type' ),
53
- 'className' => 'file_type',
54
- 'orderable' => true,
55
- 'searchable' => true,
56
- 'visible' => true,
 
 
 
57
  ],
58
- 'status' => [
59
- 'data' => 'status',
60
- 'title' => __( 'Status' ),
61
- 'className' => 'status',
62
- 'orderable' => true,
63
- 'searchable' => false,
64
- 'visible' => true,
 
 
 
65
  ],
66
- 'detected' => [
67
- 'data' => [
68
- '_' => 'detected_since',
69
- 'sort' => 'detected_at',
 
 
 
 
 
70
  ],
71
- 'title' => __( 'Detected' ),
72
- 'className' => 'detected',
73
- 'orderable' => true,
74
- 'searchable' => false,
75
- 'visible' => true,
76
  ],
77
- 'actions' => [
78
- 'data' => 'actions',
79
- 'title' => __( 'Actions' ),
80
- 'className' => 'actions',
81
- 'orderable' => false,
82
- 'searchable' => false,
83
- 'visible' => true,
 
 
 
84
  ],
85
- 'fp_confidence' => [
86
- 'data' => 'fp_confidence',
87
- 'title' => __( 'False Positive Confidence' ),
88
- 'className' => 'fp_confidence',
89
  'orderable' => true,
90
  'searchable' => false,
91
- 'visible' => true,
92
  ],
93
- 'line_numbers' => [
94
- 'data' => 'line_numbers',
95
- 'title' => __( 'Line Numbers' ),
96
- 'className' => 'line_numbers',
97
- 'orderable' => false,
98
- 'searchable' => false,
99
- 'visible' => true,
 
 
 
100
  ],
101
- 'mal_sig' => [
102
- 'data' => 'mal_sig',
103
- 'title' => __( 'Pattern Detected' ),
104
- 'className' => 'mal_sig',
105
- 'orderable' => false,
106
- 'searchable' => true,
107
- 'visible' => true,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  ],
109
  ];
110
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\AuditTrail;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Base;
6
 
7
  class ForAuditTrail extends Base {
8
 
9
  protected function getOrderColumnSlug() :string {
10
+ return 'date';
11
  }
12
 
13
  protected function getColumnsToDisplay() :array {
14
  return [
15
+ 'severity',
16
+ 'ip_linked',
17
+ 'ip',
18
+ 'event',
19
+ 'level',
20
+ 'user',
21
+ 'message',
22
+ 'date',
23
  'rid',
24
+ 'meta',
 
 
 
 
25
  ];
26
  }
27
 
28
  protected function getColumnDefs() :array {
29
  return [
30
+ 'rid' => [
31
+ 'data' => 'rid',
32
+ 'title' => __( 'Request ID' ),
33
+ 'className' => 'rid',
34
+ 'orderable' => true,
35
+ 'searchable' => true,
36
+ 'visible' => false,
37
+ 'searchPanes' => [
38
+ 'show' => true,
39
+ ],
40
  ],
41
+ 'event' => [
42
+ 'data' => 'event',
43
+ 'title' => __( 'Event' ),
44
+ 'className' => 'event',
45
+ 'orderable' => true,
46
+ 'searchable' => true,
47
+ 'visible' => false,
48
+ 'searchPanes' => [
49
+ 'show' => true
50
+ ],
51
  ],
52
+ 'event_slug' => [
53
+ 'data' => 'event',
54
+ 'title' => __( 'Event Slug' ),
55
+ 'className' => 'event',
56
+ 'orderable' => true,
57
+ 'searchable' => false,
58
+ 'visible' => false,
59
+ 'searchPanes' => [
60
+ 'show' => false
61
+ ],
62
  ],
63
+ 'severity' => [
64
+ 'data' => 'severity',
65
+ 'title' => __( 'Severity' ),
66
+ 'className' => 'severity',
67
+ 'orderable' => false,
68
+ 'searchable' => false,
69
+ 'visible' => true,
70
+ 'searchPanes' => [
71
+ 'show' => false
72
+ ],
73
  ],
74
+ 'level' => [
75
+ 'data' => 'level',
76
+ 'title' => __( 'Severity' ),
77
+ 'className' => 'level',
78
+ 'orderable' => false,
79
+ 'searchable' => true,
80
+ 'visible' => false,
81
+ 'searchPanes' => [
82
+ 'show' => true
83
+ ],
84
  ],
85
+ 'ip' => [
86
+ 'data' => 'ip',
87
+ 'title' => __( 'IP Address' ),
88
+ 'className' => 'ip',
89
+ 'orderable' => true,
90
+ 'searchable' => true,
91
+ 'visible' => false,
92
+ 'searchPanes' => [
93
+ 'show' => true,
94
  ],
 
 
 
 
 
95
  ],
96
+ 'ip_linked' => [
97
+ 'data' => 'ip_linked',
98
+ 'title' => __( 'IP' ),
99
+ 'className' => 'ip_linked',
100
+ 'orderable' => true,
101
+ 'searchable' => true,
102
+ 'visible' => true,
103
+ 'searchPanes' => [
104
+ 'show' => false,
105
+ ],
106
  ],
107
+ 'uid' => [
108
+ 'data' => 'uid',
109
+ 'title' => __( 'User ID' ),
110
+ 'className' => 'uid',
111
  'orderable' => true,
112
  'searchable' => false,
113
+ 'visible' => false,
114
  ],
115
+ 'user' => [
116
+ 'data' => 'user',
117
+ 'title' => __( 'User' ),
118
+ 'className' => 'user',
119
+ 'orderable' => true,
120
+ 'searchable' => true,
121
+ 'visible' => true,
122
+ 'searchPanes' => [
123
+ 'show' => true
124
+ ],
125
  ],
126
+ 'message' => [
127
+ 'data' => 'message',
128
+ 'title' => __( 'Message' ),
129
+ 'className' => 'message',
130
+ 'orderable' => false,
131
+ 'searchable' => true,
132
+ 'visible' => true,
133
+ 'searchPanes' => [
134
+ 'show' => false
135
+ ],
136
+ ],
137
+ 'date' => [
138
+ 'data' => [
139
+ '_' => 'created_since',
140
+ 'sort' => 'created_at',
141
+ ],
142
+ 'title' => __( 'Date' ),
143
+ 'className' => 'date',
144
+ 'orderable' => true,
145
+ 'searchable' => false,
146
+ 'visible' => true,
147
+ 'searchPanes' => [
148
+ 'show' => false
149
+ ],
150
+ ],
151
+ 'meta' => [
152
+ 'data' => 'meta',
153
+ 'title' => __( 'Meta' ),
154
+ 'className' => 'meta',
155
+ 'orderable' => false,
156
+ 'searchable' => false,
157
+ 'visible' => true,
158
+ 'searchPanes' => [
159
+ 'show' => false
160
+ ],
161
  ],
162
  ];
163
  }
src/lib/src/Tables/DataTables/Build/Base.php CHANGED
@@ -29,7 +29,7 @@ abstract class Base {
29
  */
30
  public function getInitialOrdering() :array {
31
  $thePosition = 0;
32
- foreach ( $this->getColumnsForDisplay() as $position => $columnDef ) {
33
  if ( $columnDef === $this->getOrderColumnSlug() ) {
34
  $thePosition = $position;
35
  break;
29
  */
30
  public function getInitialOrdering() :array {
31
  $thePosition = 0;
32
+ foreach ( $this->getColumnsToDisplay() as $position => $columnDef ) {
33
  if ( $columnDef === $this->getOrderColumnSlug() ) {
34
  $thePosition = $position;
35
  break;
src/lib/src/Tables/DataTables/Build/Traffic/ForTraffic.php ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Traffic;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Base;
6
+
7
+ class ForTraffic extends Base {
8
+
9
+ protected function getOrderColumnSlug() :string {
10
+ return 'date';
11
+ }
12
+
13
+ protected function getColumnsToDisplay() :array {
14
+ return [
15
+ 'ip',
16
+ 'page',
17
+ 'details',
18
+ 'response',
19
+ 'date',
20
+ 'path',
21
+ 'code',
22
+ 'offense',
23
+ 'country',
24
+ ];
25
+ }
26
+
27
+ protected function getColumnDefs() :array {
28
+ return [
29
+ 'rid' => [
30
+ 'data' => 'rid',
31
+ 'title' => __( 'Request ID' ),
32
+ 'className' => 'rid',
33
+ 'orderable' => true,
34
+ 'searchable' => true,
35
+ 'visible' => false,
36
+ 'searchPanes' => [
37
+ 'show' => true,
38
+ ],
39
+ ],
40
+ 'page' => [
41
+ 'data' => 'page',
42
+ 'title' => __( 'Page' ),
43
+ 'className' => 'page',
44
+ 'orderable' => true,
45
+ 'searchable' => true,
46
+ 'visible' => true,
47
+ 'searchPanes' => [
48
+ 'show' => false
49
+ ],
50
+ ],
51
+ 'details' => [
52
+ 'data' => 'details',
53
+ 'title' => __( 'Details' ),
54
+ 'className' => 'details',
55
+ 'orderable' => false,
56
+ 'searchable' => true,
57
+ 'visible' => true,
58
+ 'searchPanes' => [
59
+ 'show' => false
60
+ ],
61
+ ],
62
+ 'response' => [
63
+ 'data' => 'response',
64
+ 'title' => __( 'Response' ),
65
+ 'className' => 'response',
66
+ 'orderable' => false,
67
+ 'searchable' => true,
68
+ 'visible' => true,
69
+ 'searchPanes' => [
70
+ 'show' => false
71
+ ],
72
+ ],
73
+ 'ip' => [
74
+ 'data' => 'ip',
75
+ 'title' => __( 'IP Address' ),
76
+ 'className' => 'ip',
77
+ 'orderable' => true,
78
+ 'searchable' => true,
79
+ 'visible' => false,
80
+ 'searchPanes' => [
81
+ 'show' => true,
82
+ ],
83
+ ],
84
+ 'code' => [
85
+ 'data' => 'code',
86
+ 'title' => __( 'Response Code' ),
87
+ 'className' => 'code',
88
+ 'orderable' => true,
89
+ 'searchable' => true,
90
+ 'visible' => false,
91
+ 'searchPanes' => [
92
+ 'show' => true,
93
+ ],
94
+ ],
95
+ 'country' => [
96
+ 'data' => 'country',
97
+ 'title' => __( 'Country' ),
98
+ 'className' => 'country',
99
+ 'orderable' => true,
100
+ 'searchable' => true,
101
+ 'visible' => false,
102
+ 'searchPanes' => [
103
+ 'show' => true,
104
+ ],
105
+ ],
106
+ 'offense' => [
107
+ 'data' => 'offense',
108
+ 'title' => __( 'Is Offense' ),
109
+ 'className' => 'offense',
110
+ 'orderable' => false,
111
+ 'searchable' => true,
112
+ 'visible' => false,
113
+ 'searchPanes' => [
114
+ 'show' => true,
115
+ ],
116
+ ],
117
+ 'path' => [
118
+ 'data' => 'path',
119
+ 'title' => __( 'Path' ),
120
+ 'className' => 'path',
121
+ 'orderable' => false,
122
+ 'searchable' => true,
123
+ 'visible' => false,
124
+ 'searchPanes' => [
125
+ 'show' => true,
126
+ ],
127
+ ],
128
+ 'uid' => [
129
+ 'data' => 'uid',
130
+ 'title' => __( 'User ID' ),
131
+ 'className' => 'uid',
132
+ 'orderable' => true,
133
+ 'searchable' => false,
134
+ 'visible' => false,
135
+ ],
136
+ 'date' => [
137
+ 'data' => [
138
+ '_' => 'created_since',
139
+ 'sort' => 'created_at',
140
+ ],
141
+ 'title' => __( 'Date' ),
142
+ 'className' => 'date',
143
+ 'orderable' => true,
144
+ 'searchable' => false,
145
+ 'visible' => true,
146
+ 'searchPanes' => [
147
+ 'show' => false
148
+ ],
149
+ ],
150
+ ];
151
+ }
152
+ }
src/lib/src/Tables/DataTables/LoadData/BaseLoadTableData.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\LoadData;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+
8
+ class BaseLoadTableData {
9
+
10
+ use ModConsumer;
11
+
12
+ protected function getColumnContent_Date( int $ts ) :string {
13
+ return sprintf( '%s<br /><small>%s</small>',
14
+ Services::Request()
15
+ ->carbon( true )
16
+ ->setTimestamp( $ts )
17
+ ->diffForHumans(),
18
+ Services::WpGeneral()->getTimeStringForDisplay( $ts )
19
+ );
20
+ }
21
+
22
+ protected function getIpAnalysisLink( string $ip ) :string {
23
+ $srvIP = Services::IP();
24
+ return sprintf( '<a href="%s" target="_blank" title="%s" class="ip-whois">%s</a>',
25
+ $srvIP->isValidIpRange( $ip ) ? $srvIP->getIpWhoisLookup( $ip ) :
26
+ $this->getCon()->getModule_Insights()->getUrl_IpAnalysis( $ip ),
27
+ __( 'IP Analysis' ),
28
+ $ip
29
+ );
30
+ }
31
+ }
src/lib/src/Tables/Render/WpCliTable/AuditTrail.php CHANGED
@@ -2,18 +2,24 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\WpCliTable;
4
 
5
- class AuditTrail extends Base {
 
 
 
 
 
6
 
7
  public function render() {
8
- $aRows = $this->getDataBuilder()
9
- ->getEntriesFormatted();
 
10
 
11
  \WP_CLI\Utils\format_items(
12
  'table',
13
- $aRows,
14
  [
15
  'ip',
16
- 'wp_username',
17
  'message',
18
  'created_at',
19
  ]
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\WpCliTable;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\LogTable\LoadRawTableData;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
+
8
+ class AuditTrail {
9
+
10
+ use ModConsumer;
11
 
12
  public function render() {
13
+ $rows = ( new LoadRawTableData() )
14
+ ->setMod( $this->getMod() )
15
+ ->loadForLogs();
16
 
17
  \WP_CLI\Utils\format_items(
18
  'table',
19
+ $rows,
20
  [
21
  'ip',
22
+ 'user_id',
23
  'message',
24
  'created_at',
25
  ]
src/lib/src/Tables/Render/WpCliTable/Base.php DELETED
@@ -1,29 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\WpCliTable;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Tables\Build\BaseBuild;
6
-
7
- class Base {
8
-
9
- /**
10
- * @var BaseBuild
11
- */
12
- private $oDataBuilder;
13
-
14
- /**
15
- * @return BaseBuild|mixed
16
- */
17
- public function getDataBuilder() {
18
- return $this->oDataBuilder;
19
- }
20
-
21
- /**
22
- * @param BaseBuild $oDataBuilder
23
- * @return $this
24
- */
25
- public function setDataBuilder( $oDataBuilder ) {
26
- $this->oDataBuilder = $oDataBuilder;
27
- return $this;
28
- }
29
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Render/WpListTable/AuditTrail.php CHANGED
@@ -17,22 +17,6 @@ class AuditTrail extends Base {
17
  echo '</tr>';
18
  }
19
 
20
- /**
21
- * @param int $id
22
- * @return string
23
- */
24
- protected function getActionButton_AddParam( $id ) {
25
- return $this->buildActionButton_Custom(
26
- __( 'Whitelist Param', 'wp-simple-firewall' ),
27
- [ 'custom-action' ],
28
- [
29
- 'rid' => $id,
30
- 'custom-action' => 'item_addparamwhite'
31
- ],
32
- __( 'Add Parameter To Whitelist', 'wp-simple-firewall' )
33
- );
34
- }
35
-
36
  /**
37
  * @param array $aItem
38
  * @return string
@@ -49,11 +33,7 @@ class AuditTrail extends Base {
49
  * @return string
50
  */
51
  public function column_user( $item ) {
52
- $content = $item[ 'wp_username' ];
53
- if ( isset( $item[ 'meta' ][ 'param' ] ) ) {
54
- $content .= $this->buildActions( [ $this->getActionButton_AddParam( $item[ 'id' ] ) ] );
55
- }
56
- return $content;
57
  }
58
 
59
  /**
17
  echo '</tr>';
18
  }
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  /**
21
  * @param array $aItem
22
  * @return string
33
  * @return string
34
  */
35
  public function column_user( $item ) {
36
+ return $item[ 'wp_username' ];
 
 
 
 
37
  }
38
 
39
  /**
src/lib/src/Tables/Render/WpListTable/ScanAggregate.php DELETED
@@ -1,74 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\WpListTable;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
-
7
- class ScanAggregate extends ScanBase {
8
-
9
- /**
10
- * @param array $item
11
- * @return string
12
- */
13
- public function column_path( $item ) {
14
-
15
- $sContent = parent::column_path( $item );
16
-
17
- if ( !empty( $item[ 'actions' ] ) ) {
18
- $sContent .= $this->buildActions(
19
- array_map(
20
- function ( $aActionDef ) {
21
- return $this->buildActionButton_CustomArray( $aActionDef );
22
- },
23
- $item[ 'actions' ]
24
- )
25
- );
26
- }
27
-
28
- return $sContent;
29
- }
30
-
31
- /**
32
- * @param array $item
33
- * @return string
34
- */
35
- public function column_status( $item ) {
36
- $status = sprintf( '<strong>%s</strong>', $item[ 'status' ] );
37
- if ( !empty( $item[ 'explanation' ] ) ) {
38
- $status .= '<ul><li>'.implode( '</li><li>', $item[ 'explanation' ] ).'</li></ul>';
39
- }
40
- return $status;
41
- }
42
-
43
- /**
44
- * @return array
45
- */
46
- protected function get_bulk_actions() {
47
- return [
48
- 'repair' => __( 'Repair', 'wp-simple-firewall' ),
49
- 'ignore' => __( 'Ignore', 'wp-simple-firewall' ),
50
- ];
51
- }
52
-
53
- /**
54
- * @return array
55
- */
56
- public function get_columns() {
57
- return array_merge(
58
- [ 'cb' => '&nbsp;' ],
59
- parent::get_columns()
60
- );
61
- }
62
-
63
- /**
64
- * override this in order to display a custom row
65
- * @param array $aItem
66
- */
67
- public function single_row_custom( $aItem ) {
68
- $sRowContent = sprintf( '%s: %s', __( 'Scan Area', 'wp-simple-firewall' ), $aItem[ 'title' ] );
69
- echo sprintf( '<tr class="row-sticky"><td colspan=%s><h5>%s</h5></td></tr>',
70
- count( $this->get_columns() ),
71
- $sRowContent
72
- );
73
- }
74
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Render/WpListTable/ScanApc.php DELETED
@@ -1,40 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\WpListTable;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
-
7
- class ScanApc extends ScanBase {
8
-
9
- /**
10
- * @param array $aItem
11
- * @return string
12
- */
13
- public function column_plugin( $aItem ) {
14
- $aButtons = [
15
- $this->getActionButton_Ignore( $aItem[ 'id' ] ),
16
- ];
17
- return $aItem[ 'plugin' ].$this->buildActions( $aButtons );
18
- }
19
-
20
- /**
21
- * @return array
22
- */
23
- protected function get_bulk_actions() {
24
- return [
25
- 'ignore' => __( 'Ignore', 'wp-simple-firewall' ),
26
- ];
27
- }
28
-
29
- /**
30
- * @return array
31
- */
32
- public function get_columns() {
33
- return [
34
- 'cb' => '&nbsp;',
35
- 'plugin' => __( 'Item', 'wp-simple-firewall' ),
36
- 'status' => __( 'Status', 'wp-simple-firewall' ),
37
- 'created_at' => __( 'Discovered', 'wp-simple-firewall' ),
38
- ];
39
- }
40
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Render/WpListTable/ScanBase.php DELETED
@@ -1,58 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\WpListTable;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
-
7
- class ScanBase extends Base {
8
-
9
- /**
10
- * @param array $item
11
- * @return string
12
- */
13
- public function column_path( $item ) {
14
- $output = sprintf( '<code><span class="font-weight-bolder text-dark" style="font-size: larger">%s</span></code><code>[%s]</code>',
15
- $item[ 'path' ],
16
- sprintf( '%s: %s', __( 'Path', 'wp-simple-firewall' ), trailingslashit( dirname( $item[ 'path_relabs' ] ) ) )
17
- );
18
- if ( !empty( $item[ 'path_details' ] ) ) {
19
- $output .= '<p class="mb-0">'.implode( '; ', $item[ 'path_details' ] ).'</p>';
20
- }
21
- return $output;
22
- }
23
-
24
- //.implode( '; ', $aItem[ 'asset_description' ] )
25
- protected function extra_tablenav( $which ) {
26
- echo '';
27
- }
28
-
29
- /**
30
- * @return string[]
31
- */
32
- protected function get_table_classes() {
33
- return array_merge( parent::get_table_classes(), [ 'scan-table' ] );
34
- }
35
-
36
- /**
37
- * @param string $sHref
38
- * @return string
39
- */
40
- protected function getActionButton_DownloadFile( $sHref ) {
41
- return $this->buildActionButton_Custom(
42
- __( 'Download', 'wp-simple-firewall' ),
43
- [ 'href-download', 'text-info' ],
44
- [ 'href-download' => $sHref ]
45
- );
46
- }
47
-
48
- /**
49
- * @return array
50
- */
51
- public function get_columns() {
52
- return [
53
- 'path' => __( 'File', 'wp-simple-firewall' ),
54
- 'status' => __( 'Status', 'wp-simple-firewall' ),
55
- 'created_at' => __( 'Discovered', 'wp-simple-firewall' ),
56
- ];
57
- }
58
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Render/WpListTable/ScanMal.php DELETED
@@ -1,49 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\WpListTable;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
-
7
- class ScanMal extends ScanBase {
8
-
9
- /**
10
- * @param array $item
11
- * @return string
12
- */
13
- public function column_path( $item ) {
14
- $aButtons = [
15
- $this->getActionButton_Ignore( $item[ 'id' ] ),
16
- ];
17
- if ( $item[ 'can_repair' ] ) {
18
- $aButtons[] = $this->getActionButton_Repair( $item[ 'id' ] );
19
- }
20
- else {
21
- $aButtons[] = $this->getActionButton_Delete( $item[ 'id' ] );
22
- }
23
- if ( !empty( $item[ 'href_download' ] ) ) {
24
- $aButtons[] = $this->getActionButton_DownloadFile( $item[ 'href_download' ] );
25
- }
26
- return parent::column_path( $item ).$this->buildActions( $aButtons );
27
- }
28
-
29
- /**
30
- * @return array
31
- */
32
- protected function get_bulk_actions() {
33
- return [
34
- 'ignore' => __( 'Ignore', 'wp-simple-firewall' ),
35
- 'delete' => __( 'Delete', 'wp-simple-firewall' ),
36
- 'repair' => __( 'Repair', 'wp-simple-firewall' ),
37
- ];
38
- }
39
-
40
- /**
41
- * @return array
42
- */
43
- public function get_columns() {
44
- return array_merge(
45
- [ 'cb' => '&nbsp;' ],
46
- parent::get_columns()
47
- );
48
- }
49
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Render/WpListTable/ScanPtg.php DELETED
@@ -1,31 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\WpListTable;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
-
7
- class ScanPtg extends ScanBase {
8
-
9
- /**
10
- * @param array $item
11
- * @return string
12
- */
13
- public function column_path( $item ) {
14
- $aButtons = [];
15
- if ( !empty( $item[ 'href_download' ] ) ) {
16
- $aButtons[] = $this->getActionButton_DownloadFile( $item[ 'href_download' ] );
17
- }
18
- return parent::column_path( $item ).$this->buildActions( $aButtons );
19
- }
20
-
21
- /**
22
- * @return array
23
- */
24
- public function get_columns() {
25
- return [
26
- 'path' => __( 'File', 'wp-simple-firewall' ),
27
- 'status' => __( 'Status', 'wp-simple-firewall' ),
28
- 'created_at' => __( 'Discovered', 'wp-simple-firewall' ),
29
- ];
30
- }
31
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Render/WpListTable/ScanUfc.php DELETED
@@ -1,43 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\WpListTable;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
-
7
- class ScanUfc extends ScanBase {
8
-
9
- /**
10
- * @param array $item
11
- * @return string
12
- */
13
- public function column_path( $item ) {
14
- $aButtons = [
15
- $this->getActionButton_Ignore( $item[ 'id' ] ),
16
- $this->getActionButton_Delete( $item[ 'id' ] ),
17
- ];
18
- if ( !empty( $item[ 'href_download' ] ) ) {
19
- $aButtons[] = $this->getActionButton_DownloadFile( $item[ 'href_download' ] );
20
- }
21
- return parent::column_path( $item ).$this->buildActions( $aButtons );
22
- }
23
-
24
- /**
25
- * @return array
26
- */
27
- protected function get_bulk_actions() {
28
- return [
29
- 'delete' => __( 'Delete', 'wp-simple-firewall' ),
30
- 'ignore' => __( 'Ignore', 'wp-simple-firewall' ),
31
- ];
32
- }
33
-
34
- /**
35
- * @return array
36
- */
37
- public function get_columns() {
38
- return array_merge(
39
- [ 'cb' => '&nbsp;' ],
40
- parent::get_columns()
41
- );
42
- }
43
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Render/WpListTable/ScanWcf.php DELETED
@@ -1,43 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\WpListTable;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
-
7
- class ScanWcf extends ScanBase {
8
-
9
- /**
10
- * @param array $item
11
- * @return string
12
- */
13
- public function column_path( $item ) {
14
- $aButtons = [
15
- $this->getActionButton_Ignore( $item[ 'id' ] ),
16
- $this->getActionButton_Repair( $item[ 'id' ] ),
17
- ];
18
- if ( !empty( $item[ 'href_download' ] ) ) {
19
- $aButtons[] = $this->getActionButton_DownloadFile( $item[ 'href_download' ] );
20
- }
21
- return parent::column_path( $item ).$this->buildActions( $aButtons );
22
- }
23
-
24
- /**
25
- * @return array
26
- */
27
- protected function get_bulk_actions() {
28
- return [
29
- 'repair' => __( 'Repair', 'wp-simple-firewall' ),
30
- 'ignore' => __( 'Ignore', 'wp-simple-firewall' ),
31
- ];
32
- }
33
-
34
- /**
35
- * @return array
36
- */
37
- public function get_columns() {
38
- return array_merge(
39
- [ 'cb' => '&nbsp;' ],
40
- parent::get_columns()
41
- );
42
- }
43
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Render/WpListTable/ScanWpv.php DELETED
@@ -1,94 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\WpListTable;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
-
7
- class ScanWpv extends ScanBase {
8
-
9
- /**
10
- * @param array $item
11
- * @return string
12
- */
13
- public function column_asset( $item ) {
14
- $content = sprintf( '<span class="asset-title font-weight-bold">%s</span> v%s',
15
- $item[ 'asset_name' ], ltrim( $item[ 'asset_version' ], 'v' ) );
16
-
17
- $buttons = [];
18
-
19
- $hasUpdate = $item[ 'has_update' ];
20
- $buttons[] = $this->buildActionButton_Custom(
21
- $hasUpdate ? __( 'Apply Update', 'wp-simple-firewall' ) : __( 'No Update Available', 'wp-simple-firewall' ),
22
- [ ( $hasUpdate ? 'custom-action text-success' : 'disabled' ) ],
23
- [
24
- 'rid' => $item[ 'id' ],
25
- 'custom-action' => 'item_repair'
26
- ]
27
- );
28
-
29
- if ( $item[ 'can_deactivate' ] ) {
30
- $buttons[] = $this->buildActionButton_Custom(
31
- __( 'Deactivate', 'wp-simple-firewall' ),
32
- [ 'custom-action' ],
33
- [
34
- 'rid' => $item[ 'id' ],
35
- 'custom-action' => 'item_asset_deactivate'
36
- ]
37
- );
38
- }
39
-
40
- return $content.$this->buildActions( $buttons );
41
- }
42
-
43
- /**
44
- * @param array $item
45
- * @return string
46
- */
47
- public function column_vulnerability( $item ) {
48
- /** @var Scans\Wpv\WpVulnDb\VulnVO $vul */
49
- $vul = $item[ 'wpvuln_vo' ];
50
- $content = sprintf( '<span class="vuln-title">%s</span>', $vul->title );
51
-
52
- if ( $vul->provider === 'patchstack' ) {
53
- $url = $vul->references[ 0 ];
54
- }
55
- elseif ( $vul->provider === 'wpscan' ) {
56
- if ( empty( $vul->references[ 'url' ] ) ) {
57
- $url = sprintf( 'https://wpscan.com/vulnerability/%s', $vul->id );
58
- }
59
- else {
60
- $url = $vul->references[ 'url' ][ 0 ];
61
- }
62
- }
63
- else {
64
- $url = '';
65
- }
66
- $buttons = [
67
- $this->getActionButton_Ignore( $item[ 'id' ] ),
68
- sprintf( '<a href="%s" class="btn btn-sm btn-link text-info" target="_blank">%s</a>',
69
- $url, __( 'More Info', 'wp-simple-firewall' ) ),
70
- ];
71
- return $content.$this->buildActions( $buttons );
72
- }
73
-
74
- /**
75
- * @return array
76
- */
77
- protected function get_bulk_actions() {
78
- return [
79
- 'ignore' => __( 'Ignore', 'wp-simple-firewall' ),
80
- ];
81
- }
82
-
83
- /**
84
- * @return array
85
- */
86
- public function get_columns() {
87
- return [
88
- 'cb' => '&nbsp;',
89
- 'vulnerability' => __( 'Vulnerability', 'wp-simple-firewall' ),
90
- 'asset' => __( 'Asset Details', 'wp-simple-firewall' ),
91
- 'created_at' => __( 'Discovered', 'wp-simple-firewall' ),
92
- ];
93
- }
94
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tests/RunTests.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Tests;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
6
+
7
+ class RunTests {
8
+
9
+ use PluginControllerConsumer;
10
+
11
+ public function run() {
12
+ array_map(
13
+ fn( $test ) => $test->setCon( $this->getCon() )->run(), $this->enumPluginTests()
14
+ );
15
+ die( 'end test' );
16
+ }
17
+
18
+ /**
19
+ * @return PluginControllerConsumer[]
20
+ */
21
+ private function enumPluginTests() :array {
22
+ return [
23
+ new VerifyEvents(),
24
+ new VerifyUniqueEvents(),
25
+ ];
26
+ }
27
+ }
src/lib/src/Tests/VerifyEvents.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Tests;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
6
+
7
+ class VerifyEvents {
8
+
9
+ use PluginControllerConsumer;
10
+
11
+ public function run() {
12
+ $con = $this->getCon();
13
+
14
+ $NoKey = [];
15
+ $NotEnoughSubstitutions = [];
16
+ $SubstitutionsMismatch = [];
17
+ $OldStyleSprintfSubstitutions = [];
18
+ $NotEnoughParams = [];
19
+ $NoMsgs = [];
20
+ $srvEvents = $con->loadEventsService();
21
+ foreach ( $srvEvents->getEvents() as $evt ) {
22
+ $key = $evt[ 'key' ] ?? '';
23
+ if ( empty( $key ) ) {
24
+ $NoKey[] = $key;
25
+ }
26
+ else {
27
+ $msg = implode( '', $srvEvents->getEventAuditStrings( $key ) );
28
+
29
+ if ( empty( $msg ) ) {
30
+ $NoMsgs[] = $key;
31
+ }
32
+ else {
33
+ $paramCount = count( $evt[ 'audit_params' ] );
34
+
35
+ if ( substr_count( $msg, '%s' ) > 0 ) {
36
+ $OldStyleSprintfSubstitutions[] = $key;
37
+ }
38
+ else {
39
+ preg_match_all( '#{{[a-z_]+}}#i', $msg, $matches );
40
+ $substitutionPlaceholders = $matches[ 0 ];
41
+ if ( $paramCount < count( $substitutionPlaceholders ) ) {
42
+ $NotEnoughParams[] = $key;
43
+ }
44
+ elseif ( count( $substitutionPlaceholders ) > $paramCount ) {
45
+ $NotEnoughSubstitutions[] = $key;
46
+ }
47
+ elseif ( !empty( $substitutionPlaceholders ) ) {
48
+ $substitutionPlaceholders = array_map(
49
+ fn( $placeholder ) => trim( $placeholder, '{}' ),
50
+ $substitutionPlaceholders
51
+ );
52
+
53
+ // audit_params that aren't present in the string is ok, but not the other way around.
54
+ if ( !empty( array_diff( $substitutionPlaceholders, $evt[ 'audit_params' ] ) ) ) {
55
+ $SubstitutionsMismatch[] = $key;
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ if ( !empty( $NoKey ) ) {
64
+ var_dump( 'No Key' );
65
+ var_dump( $NoKey );
66
+ }
67
+ if ( !empty( $NoMsgs ) ) {
68
+ var_dump( 'No Msg' );
69
+ var_dump( $NoMsgs );
70
+ }
71
+ if ( !empty( $NotEnoughSubstitutions ) ) {
72
+ var_dump( 'Not enough substitutions in the audit message' );
73
+ var_dump( $NotEnoughSubstitutions );
74
+ }
75
+ if ( !empty( $NotEnoughParams ) ) {
76
+ var_dump( 'Not enough parameters in the event def' );
77
+ var_dump( $NotEnoughParams );
78
+ }
79
+ if ( !empty( $SubstitutionsMismatch ) ) {
80
+ var_dump( 'There is a mismatch between substitutions placeholders and audit params' );
81
+ var_dump( $SubstitutionsMismatch );
82
+ }
83
+ }
84
+ }
src/lib/src/Tests/VerifyUniqueEvents.php CHANGED
@@ -16,19 +16,13 @@ class VerifyUniqueEvents {
16
  public function run() {
17
  $con = $this->getCon();
18
 
19
- $aAllKeys = [];
20
  foreach ( $con->modules as $mod ) {
21
- $aKeys = array_map(
22
- function ( $aEvt ) {
23
- return $aEvt[ 'key' ];
24
- },
25
- array_values( $mod->getOptions()->getDef( 'events' ) )
26
- );
27
- $aAllKeys = array_merge( $aAllKeys, $aKeys );
28
  }
29
- if ( count( $aA
16
  public function run() {
17
  $con = $this->getCon();
18
 
19
+ $all = [];
20
  foreach ( $con->modules as $mod ) {
21
+ $all = array_merge( $all, array_keys( $mod->getOptions()->getEvents() ) );
 
 
 
 
 
 
22
  }