Shield Security for WordPress - Version 11.5.6

Version Description

Download this release

Release Info

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

Code changes from version 12.0.5 to 11.5.6

Files changed (360) hide show
  1. cl.json +0 -132
  2. config/audit_trail.json +0 -433
  3. config/comments_filter.json +0 -353
  4. config/data.json +0 -73
  5. config/deprecated/admin_access_restriction.php +0 -449
  6. config/deprecated/audit_trail.php +0 -433
  7. config/deprecated/autoupdates.php +0 -211
  8. config/deprecated/comms.php +0 -74
  9. config/deprecated/data.php +0 -73
  10. config/deprecated/hack_protect.php +0 -596
  11. config/deprecated/integrations.php +0 -241
  12. config/deprecated/ips.php +0 -912
  13. config/deprecated/lockdown.php +0 -200
  14. config/deprecated/plugin.php +0 -785
  15. config/deprecated/reporting.php +0 -174
  16. config/deprecated/sessions.php +0 -103
  17. config/email.json +0 -24
  18. config/events.json +0 -61
  19. config/firewall.json +0 -442
  20. config/headers.json +0 -198
  21. config/insights.json +0 -32
  22. config/license.json +0 -167
  23. config/login_protect.json +0 -551
  24. config/traffic.json +0 -252
  25. config/user_management.json +0 -473
  26. icwp-wpsf.php +1 -1
  27. init.php +3 -3
  28. plugin-spec.php +52 -96
  29. plugin.json +51 -95
  30. readme.txt +2 -2
  31. resources/css/plugin.css +21 -13
  32. resources/css/shield/datatables.css +0 -65
  33. resources/images/bootstrap/exclamation-triangle.svg +0 -4
  34. resources/images/bootstrap/info-circle.svg +0 -4
  35. resources/images/bootstrap/info-square.svg +0 -4
  36. resources/images/bootstrap/question-diamond.svg +0 -4
  37. resources/images/bootstrap/tags.svg +0 -4
  38. resources/images/bootstrap/x-octagon.svg +0 -4
  39. resources/js/{shield/datatables.js → base64.min.js} +0 -0
  40. resources/js/plugin.js +4 -4
  41. resources/js/shield/audit_trail.js +0 -196
  42. resources/js/shield/options.js +304 -0
  43. resources/js/shield/traffic.js +0 -158
  44. config/admin_access_restriction.json → src/config/feature-admin_access_restriction.php +6 -7
  45. src/config/feature-audit_trail.php +271 -0
  46. config/autoupdates.json → src/config/feature-autoupdates.php +0 -0
  47. config/deprecated/comments_filter.php → src/config/feature-comments_filter.php +1 -5
  48. config/comms.json → src/config/feature-comms.php +0 -10
  49. config/deprecated/email.php → src/config/feature-email.php +1 -1
  50. config/deprecated/events.php → src/config/feature-events.php +0 -0
  51. config/deprecated/firewall.php → src/config/feature-firewall.php +49 -30
  52. config/hack_protect.json → src/config/feature-hack_protect.php +64 -24
  53. config/deprecated/headers.php → src/config/feature-headers.php +0 -0
  54. config/deprecated/insights.php → src/config/feature-insights.php +5 -5
  55. config/integrations.json → src/config/feature-integrations.php +12 -36
  56. config/ips.json → src/config/feature-ips.php +42 -220
  57. config/deprecated/license.php → src/config/feature-license.php +4 -6
  58. config/lockdown.json → src/config/feature-lockdown.php +2 -6
  59. config/deprecated/login_protect.php → src/config/feature-login_protect.php +31 -33
  60. config/plugin.json → src/config/feature-plugin.php +37 -65
  61. config/reporting.json → src/config/feature-reporting.php +0 -15
  62. config/sessions.json → src/config/feature-sessions.php +10 -21
  63. config/deprecated/traffic.php → src/config/feature-traffic.php +17 -20
  64. config/deprecated/user_management.php → src/config/feature-user_management.php +8 -37
  65. src/lib/src/Controller/Assets/Paths.php +0 -4
  66. src/lib/src/Controller/Assets/Urls.php +10 -0
  67. src/lib/src/Controller/Controller.php +219 -98
  68. src/lib/src/Controller/Plugin/PluginDeactivate.php +0 -34
  69. src/lib/src/Controller/Plugin/PluginDelete.php +0 -62
  70. src/lib/src/Controller/Utilities/Upgrade.php +1 -16
  71. src/lib/src/Crons/BaseCron.php +0 -3
  72. src/lib/src/Databases/AuditTrail/Handler.php +7 -0
  73. src/lib/src/Databases/Base/Traits/Select_IPTable.php +3 -3
  74. src/lib/src/Databases/Base/Update.php +6 -11
  75. src/lib/src/Databases/BotSignals/Common.php +9 -15
  76. src/lib/src/Databases/BotSignals/Handler.php +3 -0
  77. src/lib/src/Databases/BotSignals/Select.php +10 -0
  78. src/lib/src/Databases/Events/Handler.php +4 -1
  79. src/lib/src/Databases/Events/Select.php +5 -5
  80. src/lib/src/Databases/GeoIp/BaseGeoIp.php +30 -0
  81. src/lib/src/Databases/GeoIp/Delete.php +10 -0
  82. src/lib/src/Databases/GeoIp/EntryVO.php +88 -0
  83. src/lib/src/Databases/GeoIp/Handler.php +10 -0
  84. src/lib/src/Databases/GeoIp/Insert.php +9 -0
  85. src/lib/src/Databases/GeoIp/Select.php +22 -0
  86. src/lib/src/Databases/IPs/Delete.php +1 -1
  87. src/lib/src/Databases/Scanner/Common.php +7 -14
  88. src/lib/src/Databases/Scanner/Update.php +34 -0
  89. src/lib/src/Databases/Session/Select.php +1 -1
  90. src/lib/src/Databases/Traffic/Handler.php +7 -0
  91. src/lib/src/Logging/Processors/RequestMetaProcessor.php +0 -33
  92. src/lib/src/Logging/Processors/ShieldMetaProcessor.php +0 -25
  93. src/lib/src/Logging/Processors/UserMetaProcessor.php +0 -36
  94. src/lib/src/Logging/Processors/WpMetaProcessor.php +0 -19
  95. src/lib/src/Modules/AuditTrail/AdminNotices.php +0 -59
  96. src/lib/src/Modules/AuditTrail/AjaxHandler.php +42 -13
  97. src/lib/src/Modules/AuditTrail/Auditors/Emails.php +23 -17
  98. src/lib/src/Modules/AuditTrail/Auditors/Plugins.php +9 -9
  99. src/lib/src/Modules/AuditTrail/Auditors/Posts.php +33 -29
  100. src/lib/src/Modules/AuditTrail/Auditors/Themes.php +11 -10
  101. src/lib/src/Modules/AuditTrail/Auditors/Upgrades.php +33 -33
  102. src/lib/src/Modules/AuditTrail/Auditors/Users.php +14 -14
  103. src/lib/src/Modules/AuditTrail/Auditors/Wordpress.php +11 -11
  104. src/lib/src/Modules/AuditTrail/DB/LoadLogs.php +0 -99
  105. src/lib/src/Modules/AuditTrail/DB/LogRecord.php +0 -11
  106. src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Common.php +0 -18
  107. src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Delete.php +0 -10
  108. src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Handler.php +0 -9
  109. src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Record.php +0 -13
  110. src/lib/src/Modules/AuditTrail/DB/Logs/Ops/Select.php +0 -10
  111. src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Common.php +0 -18
  112. src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Delete.php +0 -10
  113. src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Handler.php +0 -9
  114. src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Record.php +0 -12
  115. src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Select.php +0 -10
  116. src/lib/src/Modules/AuditTrail/DB/Meta/Ops/Update.php +0 -10
  117. src/lib/src/Modules/AuditTrail/Insights/OverviewCards.php +1 -1
  118. src/lib/src/Modules/AuditTrail/Lib/AuditLogger.php +0 -99
  119. src/lib/src/Modules/AuditTrail/Lib/AuditMessageBuilder.php +12 -19
  120. src/lib/src/Modules/AuditTrail/Lib/AuditWriter.php +7 -4
  121. src/lib/src/Modules/AuditTrail/Lib/LogHandlers/LocalDbWriter.php +0 -153
  122. src/lib/src/Modules/AuditTrail/Lib/LogHandlers/LogFileHandler.php +0 -46
  123. src/lib/src/Modules/AuditTrail/Lib/LogHandlers/Utility/LogFileDirCreate.php +0 -39
  124. src/lib/src/Modules/AuditTrail/Lib/LogHandlers/Utility/LogFileRotate.php +0 -59
  125. src/lib/src/Modules/AuditTrail/Lib/LogTable/DelegateAjaxHandler.php +0 -61
  126. src/lib/src/Modules/AuditTrail/Lib/LogTable/LoadRawTableData.php +0 -143
  127. src/lib/src/Modules/AuditTrail/Lib/Ops/ConvertLegacy.php +0 -146
  128. src/lib/src/Modules/AuditTrail/Lib/Utility/AutoWhitelistParamFromAuditEntry.php +55 -0
  129. src/lib/src/Modules/AuditTrail/Lib/Utility/GetLogFileContent.php +0 -21
  130. src/lib/src/Modules/AuditTrail/ModCon.php +65 -121
  131. src/lib/src/Modules/AuditTrail/Options.php +3 -64
  132. src/lib/src/Modules/AuditTrail/Processor.php +1 -4
  133. src/lib/src/Modules/AuditTrail/Strings.php +151 -158
  134. src/lib/src/Modules/AuditTrail/UI.php +39 -19
  135. src/lib/src/Modules/AuditTrail/Upgrade.php +0 -12
  136. src/lib/src/Modules/AuditTrail/WpCli/Display.php +6 -1
  137. src/lib/src/Modules/Autoupdates/Strings.php +36 -36
  138. src/lib/src/Modules/Base/Config/LoadConfig.php +0 -118
  139. src/lib/src/Modules/Base/Databases.php +0 -68
  140. src/lib/src/Modules/Base/Lib/Components/UiTrack.php +0 -40
  141. src/lib/src/Modules/Base/ModCon.php +87 -56
  142. src/lib/src/Modules/Base/Options.php +403 -335
  143. src/lib/src/Modules/Base/Options/OptValueSanitize.php +24 -22
  144. src/lib/src/Modules/Base/Options/Storage.php +0 -45
  145. src/lib/src/Modules/Base/Processor.php +3 -13
  146. src/lib/src/Modules/Base/Strings.php +17 -24
  147. src/lib/src/Modules/Base/UI.php +53 -51
  148. src/lib/src/Modules/Base/WpCli/ModuleStandard.php +3 -3
  149. src/lib/src/Modules/BaseShield/ModCon.php +9 -0
  150. src/lib/src/Modules/CommentsFilter/Scan/Scanner.php +1 -1
  151. src/lib/src/Modules/CommentsFilter/Strings.php +7 -39
  152. src/lib/src/Modules/CommentsFilter/Upgrade.php +32 -0
  153. src/lib/src/Modules/Comms/Strings.php +16 -20
  154. src/lib/src/Modules/Data/DB/IPs/IPGeoVO.php +0 -17
  155. src/lib/src/Modules/Data/DB/IPs/IPRecords.php +0 -52
  156. src/lib/src/Modules/Data/DB/IPs/Ops/Common.php +0 -10
  157. src/lib/src/Modules/Data/DB/IPs/Ops/Delete.php +0 -10
  158. src/lib/src/Modules/Data/DB/IPs/Ops/Handler.php +0 -9
  159. src/lib/src/Modules/Data/DB/IPs/Ops/Insert.php +0 -23
  160. src/lib/src/Modules/Data/DB/IPs/Ops/Record.php +0 -62
  161. src/lib/src/Modules/Data/DB/IPs/Ops/Select.php +0 -12
  162. src/lib/src/Modules/Data/DB/ReqLogs/GetRequestMeta.php +0 -73
  163. src/lib/src/Modules/Data/DB/ReqLogs/LoadLogs.php +0 -48
  164. src/lib/src/Modules/Data/DB/ReqLogs/LogRecord.php +0 -13
  165. src/lib/src/Modules/Data/DB/ReqLogs/Ops/Common.php +0 -14
  166. src/lib/src/Modules/Data/DB/ReqLogs/Ops/Delete.php +0 -10
  167. src/lib/src/Modules/Data/DB/ReqLogs/Ops/Handler.php +0 -9
  168. src/lib/src/Modules/Data/DB/ReqLogs/Ops/Insert.php +0 -9
  169. src/lib/src/Modules/Data/DB/ReqLogs/Ops/Record.php +0 -11
  170. src/lib/src/Modules/Data/DB/ReqLogs/Ops/Select.php +0 -10
  171. src/lib/src/Modules/Data/DB/ReqLogs/RequestRecords.php +0 -38
  172. src/lib/src/Modules/Data/Lib/GeoIP/Lookup.php +0 -77
  173. src/lib/src/Modules/Data/ModCon.php +0 -49
  174. src/lib/src/Modules/Events/Lib/EventsListener.php +0 -5
  175. src/lib/src/Modules/Events/Lib/EventsService.php +19 -85
  176. src/lib/src/Modules/Events/Lib/Reports/KeyStats.php +3 -2
  177. src/lib/src/Modules/Events/Lib/UI/BuildDataForStats.php +5 -2
  178. src/lib/src/Modules/Events/Strings.php +246 -37
  179. src/lib/src/Modules/Events/Upgrade.php +0 -54
  180. src/lib/src/Modules/Firewall/Lib/Scan/CanScan.php +1 -8
  181. src/lib/src/Modules/Firewall/Lib/Scan/Checks/ExeFiles.php +2 -2
  182. src/lib/src/Modules/Firewall/Lib/Scan/FirewallHandler.php +0 -177
  183. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/Aggressive.php +0 -12
  184. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/Base.php +0 -91
  185. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/BaseRequestParams.php +0 -16
  186. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/DirTraversal.php +0 -12
  187. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/ExeFiles.php +0 -22
  188. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/FieldTruncation.php +0 -12
  189. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/LeadingSchema.php +0 -12
  190. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/PhpCode.php +0 -12
  191. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/SqlQueries.php +0 -12
  192. src/lib/src/Modules/Firewall/Lib/Scan/Handlers/WpTerms.php +0 -12
  193. src/lib/src/Modules/Firewall/Lib/Scan/ParametersToScan.php +21 -24
  194. src/lib/src/Modules/Firewall/Lib/Scan/PerformScan.php +68 -0
  195. src/lib/src/Modules/Firewall/Processor.php +361 -70
  196. src/lib/src/Modules/Firewall/Strings.php +46 -37
  197. src/lib/src/Modules/GeoIp/Lookup.php +67 -0
  198. src/lib/src/Modules/HackGuard/AjaxHandler.php +152 -3
  199. src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Diff.php +7 -9
  200. src/lib/src/Modules/HackGuard/Lib/Reports/ScanAlerts.php +1 -1
  201. src/lib/src/Modules/HackGuard/Lib/Reports/ScanRepairs.php +25 -31
  202. src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/ScheduleBuildAll.php +3 -3
  203. src/lib/src/Modules/HackGuard/ModCon.php +1 -0
  204. src/lib/src/Modules/HackGuard/Processor.php +12 -0
  205. src/lib/src/Modules/HackGuard/Scan/Controller/Base.php +70 -32
  206. src/lib/src/Modules/HackGuard/Scan/Queue/CompleteQueue.php +8 -8
  207. src/lib/src/Modules/HackGuard/Scan/Results/ResultsDelete.php +23 -49
  208. src/lib/src/Modules/HackGuard/Scan/Results/ResultsStore.php +3 -39
  209. src/lib/src/Modules/HackGuard/Scan/Results/ResultsUpdate.php +18 -26
  210. src/lib/src/Modules/HackGuard/Strings.php +47 -81
  211. src/lib/src/Modules/HackGuard/UI.php +161 -113
  212. src/lib/src/Modules/IPs/AjaxHandler.php +4 -5
  213. src/lib/src/Modules/IPs/BotTrack/Base.php +1 -1
  214. src/lib/src/Modules/IPs/BotTrack/TrackFakeWebCrawler.php +7 -3
  215. src/lib/src/Modules/IPs/BotTrack/TrackLoginFailed.php +1 -1
  216. src/lib/src/Modules/IPs/BotTrack/TrackLoginInvalid.php +1 -1
  217. src/lib/src/Modules/IPs/BotTrack/TrackUserAgent.php +17 -0
  218. src/lib/src/Modules/IPs/Components/ProcessOffense.php +18 -18
  219. src/lib/src/Modules/IPs/Components/UnblockIpByFlag.php +1 -1
  220. src/lib/src/Modules/IPs/DB/BotSignal/BotSignalRecord.php +0 -11
  221. src/lib/src/Modules/IPs/DB/BotSignal/LoadBotSignalRecords.php +0 -41
  222. src/lib/src/Modules/IPs/DB/BotSignal/Ops/Common.php +0 -10
  223. src/lib/src/Modules/IPs/DB/BotSignal/Ops/Delete.php +0 -10
  224. src/lib/src/Modules/IPs/DB/BotSignal/Ops/Handler.php +0 -9
  225. src/lib/src/Modules/IPs/DB/BotSignal/Ops/Insert.php +0 -9
  226. src/lib/src/Modules/IPs/DB/BotSignal/Ops/Record.php +0 -35
  227. src/lib/src/Modules/IPs/DB/BotSignal/Ops/Select.php +0 -11
  228. src/lib/src/Modules/IPs/Lib/BlockRequest.php +3 -4
  229. src/lib/src/Modules/IPs/Lib/Bots/BotEventListener.php +4 -11
  230. src/lib/src/Modules/IPs/Lib/Bots/BotSignalsController.php +1 -1
  231. src/lib/src/Modules/IPs/Lib/Bots/BotSignalsRecord.php +44 -77
  232. src/lib/src/Modules/IPs/Lib/Bots/Calculator/BaseBuildScores.php +11 -7
  233. src/lib/src/Modules/IPs/Lib/Bots/Calculator/CalculateVisitorBotScores.php +4 -5
  234. src/lib/src/Modules/IPs/Lib/Bots/NotBot/InsertNotBotJs.php +1 -24
  235. src/lib/src/Modules/IPs/Lib/Bots/ShieldNET/BuildData.php +16 -35
  236. src/lib/src/Modules/IPs/Lib/IpAnalyse/BuildDisplay.php +48 -76
  237. src/lib/src/Modules/IPs/Lib/IpAnalyse/FindAllPluginIps.php +18 -4
  238. src/lib/src/Modules/IPs/Lib/OffenseTracker.php +5 -2
  239. src/lib/src/Modules/IPs/Lib/Ops/AddIp.php +4 -8
  240. src/lib/src/Modules/IPs/Lib/Ops/ConvertLegacy.php +0 -97
  241. src/lib/src/Modules/IPs/Lib/Ops/DeleteIp.php +1 -1
  242. src/lib/src/Modules/IPs/Lib/ProcessOffenses.php +11 -11
  243. src/lib/src/Modules/IPs/ModCon.php +2 -27
  244. src/lib/src/Modules/IPs/Strings.php +72 -172
  245. src/lib/src/Modules/IPs/Upgrade.php +40 -6
  246. src/lib/src/Modules/Insights/Lib/SideMenuBuilder.php +9 -8
  247. src/lib/src/Modules/Insights/ModCon.php +7 -3
  248. src/lib/src/Modules/Insights/Strings.php +44 -0
  249. src/lib/src/Modules/Insights/UI.php +2 -56
  250. src/lib/src/Modules/Integrations/Lib/Bots/Spam/Handlers/Base.php +1 -1
  251. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/Base.php +1 -1
  252. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/Buddyboss.php +0 -30
  253. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/Buddypress.php +5 -1
  254. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/EasyDigitalDownloads.php +5 -1
  255. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/LearnPress.php +5 -1
  256. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/LifterLMS.php +2 -0
  257. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/PaidMemberSubscriptions.php +4 -0
  258. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/ProfileBuilder.php +3 -0
  259. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/UltimateMember.php +3 -0
  260. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/WPMembers.php +3 -0
  261. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/WooCommerce.php +4 -0
  262. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/WordPress.php +4 -0
  263. src/lib/src/Modules/Integrations/Lib/Bots/UserForms/UserFormsController.php +0 -1
  264. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/Base.php +61 -0
  265. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/ContactForm7.php +20 -0
  266. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/ElementorPro.php +26 -0
  267. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/FluentForms.php +28 -0
  268. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/FormidableForms.php +29 -0
  269. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/Forminator.php +20 -0
  270. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/GravityForms.php +22 -0
  271. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/Helpers/NinjaForms_ShieldSpamAction.php +47 -0
  272. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/KaliForms.php +27 -0
  273. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/NinjaForms.php +48 -0
  274. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/WPForms.php +37 -0
  275. src/lib/src/Modules/Integrations/Lib/Spam/Handlers/WpForo.php +40 -0
  276. src/lib/src/Modules/Integrations/Strings.php +5 -17
  277. src/lib/src/Modules/License/ModCon.php +4 -6
  278. src/lib/src/Modules/License/Strings.php +17 -26
  279. src/lib/src/Modules/Lockdown/Processor.php +13 -18
  280. src/lib/src/Modules/Lockdown/Strings.php +49 -58
  281. src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GaspJs.php +2 -2
  282. src/lib/src/Modules/LoginGuard/Lib/Rename/RenameLogin.php +44 -46
  283. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php +1 -1
  284. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BackupCodes.php +19 -4
  285. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BaseProvider.php +11 -25
  286. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Email.php +25 -0
  287. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/GoogleAuth.php +16 -0
  288. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/U2F.php +16 -1
  289. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Yubikey.php +17 -1
  290. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/ValidateLoginIntentRequest.php +22 -22
  291. src/lib/src/Modules/LoginGuard/Strings.php +58 -67
  292. src/lib/src/Modules/Plugin/AdminNotices.php +32 -0
  293. src/lib/src/Modules/Plugin/AjaxHandler.php +1 -1
  294. src/lib/src/Modules/Plugin/Components/BadgeWidget.php +5 -6
  295. src/lib/src/Modules/Plugin/Components/PluginBadge.php +34 -28
  296. src/lib/src/Modules/Plugin/Debug.php +0 -25
  297. src/lib/src/Modules/Plugin/Insights/DashboardCards.php +1 -1
  298. src/lib/src/Modules/Plugin/Insights/OverviewCards.php +3 -4
  299. src/lib/src/Modules/Plugin/Lib/Debug/Collate.php +6 -11
  300. src/lib/src/Modules/Plugin/Lib/Debug/RecentEvents.php +31 -29
  301. src/lib/src/Modules/Plugin/Lib/ImportExport/Export.php +21 -17
  302. src/lib/src/Modules/Plugin/Lib/ImportExport/Import.php +13 -13
  303. src/lib/src/Modules/Plugin/Lib/ImportExport/ImportExportController.php +7 -4
  304. src/lib/src/Modules/Plugin/Lib/ImportExport/Options/BuildTransferableOptions.php +2 -2
  305. src/lib/src/Modules/Plugin/ModCon.php +9 -7
  306. src/lib/src/Modules/Plugin/Options.php +5 -0
  307. src/lib/src/Modules/Plugin/Processor.php +2 -2
  308. src/lib/src/Modules/Plugin/Strings.php +38 -92
  309. src/lib/src/Modules/Plugin/UI.php +7 -7
  310. src/lib/src/Modules/Plugin/Upgrade.php +0 -19
  311. src/lib/src/Modules/Plugin/WpCli/ForceOff.php +2 -7
  312. src/lib/src/Modules/Plugin/WpCli/ToggleDebug.php +0 -5
  313. src/lib/src/Modules/Reporting/Charts/BaseBuildChartData.php +3 -2
  314. src/lib/src/Modules/Reporting/Debug.php +0 -1
  315. src/lib/src/Modules/Reporting/Lib/ReportingController.php +12 -27
  316. src/lib/src/Modules/Reporting/Strings.php +25 -29
  317. src/lib/src/Modules/Reporting/UI.php +7 -6
  318. src/lib/src/Modules/SecurityAdmin/Strings.php +20 -26
  319. src/lib/src/Modules/Sessions/Lib/SessionController.php +16 -16
  320. src/lib/src/Modules/Sessions/Processor.php +14 -14
  321. src/lib/src/Modules/Sessions/Strings.php +0 -32
  322. src/lib/src/Modules/Sessions/Upgrade.php +13 -0
  323. src/lib/src/Modules/Statistics/Options.php +15 -0
  324. src/lib/src/Modules/Statistics/Strings.php +58 -0
  325. src/lib/src/Modules/Traffic/AjaxHandler.php +12 -16
  326. src/lib/src/Modules/Traffic/Lib/Limit/Limiter.php +19 -20
  327. src/lib/src/Modules/Traffic/Lib/Limit/RateLimitExceededException.php +0 -19
  328. src/lib/src/Modules/Traffic/Lib/Limit/TestIp.php +43 -0
  329. src/lib/src/Modules/Traffic/Lib/Limit/TestIpLimit.php +0 -43
  330. src/lib/src/Modules/Traffic/Lib/LogHandlers/LocalDbWriter.php +0 -56
  331. src/lib/src/Modules/Traffic/Lib/Logger.php +29 -6
  332. src/lib/src/Modules/Traffic/Lib/Ops/ConvertLegacy.php +0 -88
  333. src/lib/src/Modules/Traffic/Lib/RequestLogger.php +0 -101
  334. src/lib/src/Modules/Traffic/Lib/TrafficTable/DelegateAjaxHandler.php +0 -44
  335. src/lib/src/Modules/Traffic/Lib/TrafficTable/LoadRawTableData.php +0 -211
  336. src/lib/src/Modules/Traffic/ModCon.php +13 -31
  337. src/lib/src/Modules/Traffic/Options.php +6 -13
  338. src/lib/src/Modules/Traffic/Processor.php +10 -7
  339. src/lib/src/Modules/Traffic/Strings.php +9 -6
  340. src/lib/src/Modules/Traffic/UI.php +14 -9
  341. src/lib/src/Modules/Traffic/Upgrade.php +0 -19
  342. src/lib/src/Modules/UserManagement/AjaxHandler.php +9 -9
  343. src/lib/src/Modules/UserManagement/Lib/Password/UserPasswordHandler.php +29 -35
  344. src/lib/src/Modules/UserManagement/Lib/Registration/EmailValidate.php +6 -6
  345. src/lib/src/Modules/UserManagement/Lib/Session/UserSessionHandler.php +15 -16
  346. src/lib/src/Modules/UserManagement/Lib/Suspend/UserSuspendController.php +6 -6
  347. src/lib/src/Modules/UserManagement/ModCon.php +27 -26
  348. src/lib/src/Modules/UserManagement/Processor.php +70 -55
  349. src/lib/src/Modules/UserManagement/Strings.php +154 -176
  350. src/lib/src/Modules/UserManagement/Suspend/Base.php +38 -0
  351. src/lib/src/Modules/UserManagement/Suspend/Idle.php +47 -0
  352. src/lib/src/Modules/UserManagement/Suspend/PasswordExpiry.php +71 -0
  353. src/lib/src/Modules/UserManagement/Suspend/Suspended.php +28 -0
  354. src/lib/src/Modules/UserManagement/Upgrade.php +10 -0
  355. src/lib/src/Scans/Base/BaseFileScanActionVO.php +3 -0
  356. src/lib/src/Scans/Base/BaseScanActionVO.php +11 -0
  357. src/lib/src/Scans/Base/DiffResultForStorage.php +10 -10
  358. src/lib/src/Scans/Base/FileResultItem.php +0 -4
  359. src/lib/src/Scans/Base/Table/BaseEntryFormatter.php +82 -0
  360. src/lib/src/Scans/Base/Table/BaseFileEntryFormatter.php +38 -0
cl.json CHANGED
@@ -1,136 +1,4 @@
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": "improved",
83
- "pro_only": false,
84
- "title": "Scanning Improvements and Fixes",
85
- "description": [
86
- "Based on customer feedback we've made some adjustments and fixes to the scans and results processing."
87
- ]
88
- },
89
- {
90
- "type": "changed",
91
- "pro_only": false,
92
- "title": "Traffic Log Limits",
93
- "description": [
94
- "Traffic logs are no longer limited by amount.",
95
- "They are instead limited by age (in days). Updated configuration options are available."
96
- ]
97
- },
98
- {
99
- "type": "changed",
100
- "pro_only": false,
101
- "title": "NotBot JS Is Always Loaded By Default",
102
- "description": [
103
- "Since many customers are using caching and optimisation plugins that interfere with NotBot JS, it is now loaded for all visitors by default.",
104
- "An option within the plugin has been provided to revert to the normal optimised loading of the NotBot JS."
105
- ]
106
- },
107
- {
108
- "type": "changed",
109
- "pro_only": true,
110
- "title": "U2F 2-Factor Authentication Bypasses MFA",
111
- "description": [
112
- "U2F is a strong 2FA mechanism and so it doesn't really need to be used in conjunction with other factors.",
113
- "When the Chained/MFA option is enabled, when U2F is supplied, this can be done alone without the need for other factors."
114
- ]
115
- },
116
- {
117
- "type": "changed",
118
- "pro_only": false,
119
- "title": "Minimum Required MySQL Version",
120
- "description": [
121
- "Shield processed IPv4 and IPv6 addresses and stores them in the MySQL database.",
122
- "With this upgrade, the minimum required MySQL database engine is moving to 5.6."
123
- ],
124
- "href": "https://shsec.io/shieldsystemrequirements"
125
- },
126
- {
127
- "type": "fixed",
128
- "title": "Prevent PHP exception being thrown in certain cases.",
129
- "description": [],
130
- "patch": "12.0.4"
131
- }
132
- ]
133
- },
134
  "11.5": {
135
  "version": "11.5",
136
  "released_at": 1626779164,
1
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  "11.5": {
3
  "version": "11.5",
4
  "released_at": 1626779164,
config/audit_trail.json DELETED
@@ -1,433 +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_trail"
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/comments_filter.json DELETED
@@ -1,353 +0,0 @@
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/data.json DELETED
@@ -1,73 +0,0 @@
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 DELETED
@@ -1,449 +0,0 @@
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 DELETED
@@ -1,433 +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_trail"
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 DELETED
@@ -1,211 +0,0 @@
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/comms.php DELETED
@@ -1,74 +0,0 @@
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 DELETED
@@ -1,73 +0,0 @@
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/hack_protect.php DELETED
@@ -1,596 +0,0 @@
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
- "attempt_repair_at": "Attempted Repair At"
514
- }
515
- },
516
- "db_table_scanq": {
517
- "slug": "scanq",
518
- "cols_custom": {
519
- "scan": "varchar(3) NOT NULL DEFAULT '' COMMENT 'Scan Slug'",
520
- "items": "text COMMENT 'Array of scan items'",
521
- "results": "text COMMENT 'Array of results'",
522
- "meta": "text COMMENT 'Meta Data'"
523
- },
524
- "cols_timestamps": {
525
- "started_at": "Scan Started",
526
- "finished_at": "Scan Completed"
527
- }
528
- },
529
- "table_name_filelocker": "filelocker",
530
- "url_mal_sigs_simple": "https://raw.githubusercontent.com/scr34m/php-malware-scanner/master/definitions/patterns_raw.txt",
531
- "url_mal_sigs_regex": "https://raw.githubusercontent.com/scr34m/php-malware-scanner/master/definitions/patterns_re.txt",
532
- "default_whitelist_paths": [
533
- "wp-content/cache/*",
534
- "wp-content/shield/*",
535
- "wp-content/icwp/rollback/*",
536
- "wp-content/plugins-before-restore/*",
537
- "wp-content/themes-before-restore/*",
538
- "wp-content/uploads/bb-plugin/cache/*",
539
- "wp-content/uploads/cache/wpml/twig/*",
540
- "wp-content/cache/*",
541
- "*/error_log",
542
- "*/php_error_log",
543
- "*/mail.log",
544
- "*/php_mail.log"
545
- ],
546
- "cron_all_scans": "all-scans",
547
- "wcf_exclusions": [
548
- "readme.html",
549
- "license.txt",
550
- "licens-sv_SE.txt",
551
- "wp-config-sample.php",
552
- "wp-content/"
553
- ],
554
- "wcf_exclusions_missing_only": [
555
- "wp-admin/install.php",
556
- "xmlrpc.php"
557
- ],
558
- "events": {
559
- "scan_run": {
560
- "audit_params": [
561
- "scan"
562
- ],
563
- "level": "debug",
564
- "audit_multiple": true
565
- },
566
- "scan_items_found": {
567
- "audit_params": [
568
- "scan",
569
- "items"
570
- ],
571
- "level": "alert",
572
- "audit_multiple": true,
573
- "recent": true
574
- },
575
- "scan_item_repair_success": {
576
- "audit_params": [
577
- "path_full"
578
- ],
579
- "audit_multiple": true,
580
- "recent": true
581
- },
582
- "scan_item_repair_fail": {
583
- "audit_params": [
584
- "path_full"
585
- ],
586
- "audit_multiple": true
587
- },
588
- "scan_item_delete_success": {
589
- "audit_params": [
590
- "path_full"
591
- ],
592
- "audit_multiple": true
593
- }
594
- }
595
- }
596
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
config/deprecated/integrations.php DELETED
@@ -1,241 +0,0 @@
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/deprecated/ips.php DELETED
@@ -1,912 +0,0 @@
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": "force_notbot",
169
- "section": "section_antibot",
170
- "advanced": true,
171
- "default": "Y",
172
- "type": "checkbox",
173
- "link_info": "https://shsec.io/jy",
174
- "link_blog": "https://shsec.io/jz",
175
- "beacon_id": 448,
176
- "name": "Force NotBot JS",
177
- "summary": "Force Loading Of NotBot JS",
178
- "description": "Use this option if you're using an aggressive caching plugin or system to ensure NotBot JS is loaded for visitors."
179
- },
180
- {
181
- "key": "transgression_limit",
182
- "section": "section_auto_black_list",
183
- "default": 10,
184
- "type": "integer",
185
- "link_info": "https://shsec.io/wpsf24",
186
- "link_blog": "https://shsec.io/wpsf26",
187
- "beacon_id": 207,
188
- "name": "Offense Limit",
189
- "summary": "Visitor IP address will be Black Listed after X bad actions on your site",
190
- "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."
191
- },
192
- {
193
- "key": "auto_expire",
194
- "section": "section_auto_black_list",
195
- "advanced": true,
196
- "default": "day",
197
- "type": "select",
198
- "value_options": [
199
- {
200
- "value_key": "minute",
201
- "text": "Minute"
202
- },
203
- {
204
- "value_key": "hour",
205
- "text": "Hour"
206
- },
207
- {
208
- "value_key": "day",
209
- "text": "Day"
210
- },
211
- {
212
- "value_key": "week",
213
- "text": "Week"
214
- },
215
- {
216
- "value_key": "month",
217
- "text": "Month"
218
- }
219
- ],
220
- "link_info": "https://shsec.io/wpsf25",
221
- "link_blog": "https://shsec.io/wpsf26",
222
- "beacon_id": 210,
223
- "name": "Auto Block Expiration",
224
- "summary": "After 1 'X' a black listed IP will be removed from the black list",
225
- "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."
226
- },
227
- {
228
- "key": "user_auto_recover",
229
- "section": "section_auto_black_list",
230
- "advanced": true,
231
- "premium": true,
232
- "default": [],
233
- "type": "multiple_select",
234
- "value_options": [
235
- {
236
- "value_key": "gasp",
237
- "text": "With Shield Bot Protection"
238
- },
239
- {
240
- "value_key": "email",
241
- "text": "Magic Email Links To Unblock Logged-In Users"
242
- }
243
- ],
244
- "link_info": "https://shsec.io/f8",
245
- "link_blog": "",
246
- "beacon_id": 125,
247
- "name": "User Auto Unblock",
248
- "summary": "Allow Visitors To Unblock Their IP",
249
- "description": "Allow visitors blocked by the plugin to automatically unblock themselves."
250
- },
251
- {
252
- "key": "request_whitelist",
253
- "section": "section_auto_black_list",
254
- "advanced": true,
255
- "premium": true,
256
- "default": [],
257
- "type": "array",
258
- "link_info": "https://shsec.io/gd",
259
- "link_blog": "",
260
- "beacon_id": 126,
261
- "name": "Request Path Whitelist",
262
- "summary": "Request Path Whitelist",
263
- "description": "Request Path Whitelist."
264
- },
265
- {
266
- "key": "text_loginfailed",
267
- "section": "section_user_messages",
268
- "sensitive": true,
269
- "premium": true,
270
- "default": "default",
271
- "type": "text",
272
- "link_info": "https://shsec.io/e8",
273
- "link_blog": "",
274
- "beacon_id": 139,
275
- "name": "Login Failed",
276
- "summary": "Visitor Triggers The IP Offenses System Through A Failed Login",
277
- "description": "This message is displayed if the visitor fails a login attempt."
278
- },
279
- {
280
- "key": "track_404",
281
- "section": "section_probes",
282
- "premium": true,
283
- "default": "log",
284
- "type": "select",
285
- "value_options": [
286
- {
287
- "value_key": "disabled",
288
- "text": "Disabled"
289
- },
290
- {
291
- "value_key": "log",
292
- "text": "Audit Log Only"
293
- },
294
- {
295
- "value_key": "transgression-single",
296
- "text": "Increment Offense Counter"
297
- },
298
- {
299
- "value_key": "transgression-double",
300
- "text": "Double-Increment Offense Counter"
301
- },
302
- {
303
- "value_key": "block",
304
- "text": "Immediate Block"
305
- }
306
- ],
307
- "link_info": "https://shsec.io/fo",
308
- "link_blog": "https://shsec.io/f7",
309
- "beacon_id": 123,
310
- "name": "404 Detect",
311
- "summary": "Identify A Bot When It Hits A 404",
312
- "description": "Detect When A Visitor Browses To A Non-Existent Page."
313
- },
314
- {
315
- "key": "track_linkcheese",
316
- "section": "section_probes",
317
- "premium": true,
318
- "default": "disabled",
319
- "type": "select",
320
- "value_options": [
321
- {
322
- "value_key": "disabled",
323
- "text": "Disabled"
324
- },
325
- {
326
- "value_key": "log",
327
- "text": "Audit Log Only"
328
- },
329
- {
330
- "value_key": "transgression-single",
331
- "text": "Increment Offense Counter"
332
- },
333
- {
334
- "value_key": "transgression-double",
335
- "text": "Double-Increment Offense Counter"
336
- },
337
- {
338
- "value_key": "block",
339
- "text": "Immediate Block"
340
- }
341
- ],
342
- "link_info": "https://shsec.io/fo",
343
- "link_blog": "https://shsec.io/f6",
344
- "beacon_id": 123,
345
- "name": "Link Cheese",
346
- "summary": "Tempt A Bot With A Fake Link To Follow",
347
- "description": "Detect A Bot That Follows A 'no-follow' Link."
348
- },
349
- {
350
- "key": "track_xmlrpc",
351
- "section": "section_probes",
352
- "premium": true,
353
- "default": "log",
354
- "type": "select",
355
- "value_options": [
356
- {
357
- "value_key": "disabled",
358
- "text": "Disabled"
359
- },
360
- {
361
- "value_key": "log",
362
- "text": "Audit Log Only"
363
- },
364
- {
365
- "value_key": "transgression-single",
366
- "text": "Increment Offense Counter"
367
- },
368
- {
369
- "value_key": "transgression-double",
370
- "text": "Double-Increment Offense Counter"
371
- },
372
- {
373
- "value_key": "block",
374
- "text": "Immediate Block"
375
- }
376
- ],
377
- "link_info": "https://shsec.io/fo",
378
- "link_blog": "https://shsec.io/f7",
379
- "beacon_id": 123,
380
- "name": "XML-RPC Access",
381
- "summary": "Identify A Bot When It Accesses XML-RPC",
382
- "description": "If you don't use XML-RPC, why would anyone access it?"
383
- },
384
- {
385
- "key": "track_invalidscript",
386
- "section": "section_probes",
387
- "premium": true,
388
- "default": "log",
389
- "type": "select",
390
- "value_options": [
391
- {
392
- "value_key": "disabled",
393
- "text": "Disabled"
394
- },
395
- {
396
- "value_key": "log",
397
- "text": "Audit Log Only"
398
- },
399
- {
400
- "value_key": "transgression-single",
401
- "text": "Increment Offense Counter"
402
- },
403
- {
404
- "value_key": "transgression-double",
405
- "text": "Double-Increment Offense Counter"
406
- },
407
- {
408
- "value_key": "block",
409
- "text": "Immediate Block"
410
- }
411
- ],
412
- "link_info": "https://shsec.io/fo",
413
- "link_blog": "https://shsec.io/f7",
414
- "beacon_id": 123,
415
- "name": "Invalid Script Load",
416
- "summary": "Identify A Bot Attempts To Load WordPress In A Non-Standard Way",
417
- "description": "WordPress should only be loaded in a limited number of ways."
418
- },
419
- {
420
- "key": "track_loginfailed",
421
- "section": "section_logins",
422
- "default": "transgression-single",
423
- "type": "select",
424
- "value_options": [
425
- {
426
- "value_key": "disabled",
427
- "text": "Disabled"
428
- },
429
- {
430
- "value_key": "log",
431
- "text": "Audit Log Only"
432
- },
433
- {
434
- "value_key": "transgression-single",
435
- "text": "Increment Offense Counter"
436
- },
437
- {
438
- "value_key": "transgression-double",
439
- "text": "Double-Increment Offense Counter"
440
- },
441
- {
442
- "value_key": "block",
443
- "text": "Immediate Block"
444
- }
445
- ],
446
- "link_info": "https://shsec.io/fn",
447
- "link_blog": "https://shsec.io/f7",
448
- "beacon_id": 122,
449
- "name": "Failed Login",
450
- "summary": "Detect Failed Login Attempts By Valid Usernames",
451
- "description": "Penalise a visitor who fails to login using a valid username."
452
- },
453
- {
454
- "key": "track_logininvalid",
455
- "section": "section_logins",
456
- "premium": true,
457
- "default": "log",
458
- "type": "select",
459
- "value_options": [
460
- {
461
- "value_key": "disabled",
462
- "text": "Disabled"
463
- },
464
- {
465
- "value_key": "log",
466
- "text": "Audit Log Only"
467
- },
468
- {
469
- "value_key": "transgression-single",
470
- "text": "Increment Offense Counter"
471
- },
472
- {
473
- "value_key": "transgression-double",
474
- "text": "Double-Increment Offense Counter"
475
- },
476
- {
477
- "value_key": "block",
478
- "text": "Immediate Block"
479
- }
480
- ],
481
- "link_info": "https://shsec.io/fn",
482
- "link_blog": "https://shsec.io/f7",
483
- "beacon_id": 122,
484
- "name": "Invalid Usernames",
485
- "summary": "Detect Invalid Username Logins",
486
- "description": "Identify A Bot When It Tries To Login With A Non-Existent Username."
487
- },
488
- {
489
- "key": "track_fakewebcrawler",
490
- "section": "section_behaviours",
491
- "premium": true,
492
- "default": "log",
493
- "type": "select",
494
- "value_options": [
495
- {
496
- "value_key": "disabled",
497
- "text": "Disabled"
498
- },
499
- {
500
- "value_key": "log",
501
- "text": "Audit Log Only"
502
- },
503
- {
504
- "value_key": "transgression-single",
505
- "text": "Increment Offense Counter"
506
- },
507
- {
508
- "value_key": "transgression-double",
509
- "text": "Double-Increment Offense Counter"
510
- },
511
- {
512
- "value_key": "block",
513
- "text": "Immediate Block"
514
- }
515
- ],
516
- "link_info": "https://shsec.io/f5",
517
- "link_blog": "https://shsec.io/f7",
518
- "beacon_id": 206,
519
- "name": "Fake Web Crawler",
520
- "summary": "Detect Fake Search Engine Crawlers",
521
- "description": "Identify a Bot when it presents as an official web crawler, but analysis shows it's fake."
522
- },
523
- {
524
- "key": "track_useragent",
525
- "section": "section_behaviours",
526
- "premium": true,
527
- "default": "log",
528
- "type": "select",
529
- "value_options": [
530
- {
531
- "value_key": "disabled",
532
- "text": "Disabled"
533
- },
534
- {
535
- "value_key": "log",
536
- "text": "Audit Log Only"
537
- },
538
- {
539
- "value_key": "transgression-single",
540
- "text": "Increment Offense Counter"
541
- },
542
- {
543
- "value_key": "transgression-double",
544
- "text": "Double-Increment Offense Counter"
545
- },
546
- {
547
- "value_key": "block",
548
- "text": "Immediate Block"
549
- }
550
- ],
551
- "link_info": "https://shsec.io/fi",
552
- "link_blog": "https://shsec.io/f7",
553
- "beacon_id": 124,
554
- "name": "Empty User Agents",
555
- "summary": "Detect Requests With Empty User Agents",
556
- "description": "Identify a request as a bot if the user agent is not provided."
557
- },
558
- {
559
- "key": "text_remainingtrans",
560
- "section": "section_user_messages",
561
- "sensitive": true,
562
- "premium": true,
563
- "default": "default",
564
- "type": "text",
565
- "link_info": "https://shsec.io/e9",
566
- "link_blog": "",
567
- "beacon_id": 139,
568
- "name": "Remaining Offenses",
569
- "summary": "Visitor Triggers The IP Offenses System Through A Firewall Block",
570
- "description": "This message is displayed if the visitor triggered the IP Offenses system and reports how many offenses remain before being blocked."
571
- },
572
- {
573
- "key": "autounblock_ips",
574
- "section": "section_non_ui",
575
- "transferable": false,
576
- "type": "array",
577
- "default": []
578
- },
579
- {
580
- "key": "autounblock_emailids",
581
- "section": "section_non_ui",
582
- "transferable": false,
583
- "type": "array",
584
- "default": []
585
- },
586
- {
587
- "key": "legacy_db_deleted_at",
588
- "section": "section_non_ui",
589
- "transferable": false,
590
- "type": "integer",
591
- "default": ""
592
- }
593
- ],
594
- "definitions": {
595
- "allowable_ext_404s": [
596
- "js",
597
- "css",
598
- "gif",
599
- "jpg",
600
- "jpeg",
601
- "png",
602
- "map",
603
- "ttf",
604
- "woff",
605
- "woff2"
606
- ],
607
- "db_handler_classes": {
608
- "botsignal": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\DB\\BotSignal\\Ops\\Handler"
609
- },
610
- "db_table_botsignal": {
611
- "autoexpire": 0,
612
- "slug": "botsignal",
613
- "has_updated_at": true,
614
- "col_older_than": "updated_at",
615
- "cols_custom": {
616
- "ip_ref": {
617
- "macro_type": "foreign_key_id",
618
- "foreign_key": {
619
- "ref_table": "icwp_wpsf_ips"
620
- }
621
- },
622
- "notbot_at": {
623
- "macro_type": "timestamp",
624
- "comment": "NotBot"
625
- },
626
- "frontpage_at": {
627
- "macro_type": "timestamp",
628
- "comment": "Any Frontend Page Loaded"
629
- },
630
- "loginpage_at": {
631
- "macro_type": "timestamp",
632
- "comment": "Login Page Loaded"
633
- },
634
- "bt404_at": {
635
- "macro_type": "timestamp",
636
- "comment": "BotTrack 404"
637
- },
638
- "btfake_at": {
639
- "macro_type": "timestamp",
640
- "comment": "BotTrack FakeWebCrawler"
641
- },
642
- "btcheese_at": {
643
- "macro_type": "timestamp",
644
- "comment": "BotTrack LinkCheese"
645
- },
646
- "btloginfail_at": {
647
- "macro_type": "timestamp",
648
- "comment": "BotTrack LoginFailed"
649
- },
650
- "btua_at": {
651
- "macro_type": "timestamp",
652
- "comment": "BotTrack Useragent Fail"
653
- },
654
- "btxml_at": {
655
- "macro_type": "timestamp",
656
- "comment": "BotTrack XMLRPC Access"
657
- },
658
- "btlogininvalid_at": {
659
- "macro_type": "timestamp",
660
- "comment": "BotTrack LoginInvalid"
661
- },
662
- "btinvalidscript_at": {
663
- "macro_type": "timestamp",
664
- "comment": "BotTrack InvalidScript"
665
- },
666
- "cooldown_at": {
667
- "macro_type": "timestamp",
668
- "comment": "Cooldown Triggered"
669
- },
670
- "humanspam_at": {
671
- "macro_type": "timestamp",
672
- "comment": "Comment Marked As Human SPAM"
673
- },
674
- "markspam_at": {
675
- "macro_type": "timestamp",
676
- "comment": "Mark Comment As SPAM"
677
- },
678
- "unmarkspam_at": {
679
- "macro_type": "timestamp",
680
- "comment": "Unmark Comment As SPAM"
681
- },
682
- "captchapass_at": {
683
- "macro_type": "timestamp",
684
- "comment": "Captcha Passed"
685
- },
686
- "captchafail_at": {
687
- "macro_type": "timestamp",
688
- "comment": "Captcha Failed"
689
- },
690
- "auth_at": {
691
- "macro_type": "timestamp",
692
- "comment": "Successful Login"
693
- },
694
- "firewall_at": {
695
- "macro_type": "timestamp",
696
- "comment": "Triggered Firewall"
697
- },
698
- "ratelimit_at": {
699
- "macro_type": "timestamp",
700
- "comment": "Rate Limit Exceeded"
701
- },
702
- "offense_at": {
703
- "macro_type": "timestamp",
704
- "comment": "Last Offense"
705
- },
706
- "blocked_at": {
707
- "macro_type": "timestamp",
708
- "comment": "Last Block"
709
- },
710
- "unblocked_at": {
711
- "macro_type": "timestamp",
712
- "comment": "Unblocked"
713
- },
714
- "bypass_at": {
715
- "macro_type": "timestamp",
716
- "comment": "Bypass"
717
- },
718
- "snsent_at": {
719
- "macro_type": "timestamp",
720
- "comment": "Sent To ShieldNET"
721
- }
722
- }
723
- },
724
- "db_classes": {
725
- "botsignals": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\BotSignals\\Handler",
726
- "ip_lists": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\IPs\\Handler"
727
- },
728
- "ip_lists_table_name": "ip_lists",
729
- "db_table_ip_lists": {
730
- "slug": "ip_lists",
731
- "cols_custom": {
732
- "ip": "varchar(60) NOT NULL DEFAULT '' COMMENT 'Human readable IP address or range'",
733
- "label": "varchar(255) NOT NULL DEFAULT '' COMMENT 'Description'",
734
- "list": "varchar(4) NOT NULL DEFAULT '' COMMENT 'Block or Bypass'",
735
- "ip6": "tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Is IPv6'",
736
- "is_range": "tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Is Range'",
737
- "transgressions": "int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Total Offenses'"
738
- },
739
- "cols_timestamps": {
740
- "last_access_at": "Last Access By IP",
741
- "blocked_at": "IP Blocked"
742
- }
743
- },
744
- "db_table_botsignals": {
745
- "autoexpire": 3,
746
- "slug": "botsignals",
747
- "col_older_than": "updated_at",
748
- "has_updated_at": true,
749
- "cols_custom": {
750
- "ip": "varbinary(16) DEFAULT NULL COMMENT 'IP Address'"
751
- },
752
- "cols_timestamps": {
753
- "notbot_at": "NotBot",
754
- "frontpage_at": "Any Frontend Page Loaded",
755
- "loginpage_at": "Login Page Loaded",
756
- "bt404_at": "BotTrack 404",
757
- "btfake_at": "BotTrack FakeWebCrawler",
758
- "btcheese_at": "BotTrack LinkCheese",
759
- "btloginfail_at": "BotTrack LoginFailed",
760
- "btua_at": "BotTrack Useragent Fail",
761
- "btxml_at": "BotTrack XMLRPC Access",
762
- "btlogininvalid_at": "BotTrack LoginInvalid",
763
- "btinvalidscript_at": "BotTrack InvalidScript",
764
- "cooldown_at": "Triggered Cooldown",
765
- "humanspam_at": "Comment Marked As Human SPAM",
766
- "markspam_at": "Mark Comment As SPAM",
767
- "unmarkspam_at": "Unmark Comment As SPAM",
768
- "captchapass_at": "Captcha Passed",
769
- "captchafail_at": "Captcha Failed",
770
- "auth_at": "Successful Login",
771
- "firewall_at": "Triggered Firewall",
772
- "ratelimit_at": "Rate Limit Exceeded",
773
- "offense_at": "Last Offense",
774
- "blocked_at": "Last Block",
775
- "unblocked_at": "Unblocked",
776
- "bypass_at": "Bypass",
777
- "snsent_at": "Sent To ShieldNET"
778
- }
779
- },
780
- "events": {
781
- "custom_offense": {
782
- "audit_params": [
783
- "message"
784
- ],
785
- "offense": true
786
- },
787
- "conn_kill": {
788
- "level": "warning",
789
- "audit_countable": true
790
- },
791
- "conn_not_kill_high_rep": {
792
- "level": "debug"
793
- },
794
- "ip_offense": {
795
- "level": "warning",
796
- "audit_params": [
797
- "from",
798
- "to"
799
- ]
800
- },
801
- "ip_blocked": {
802
- "audit_params": [
803
- "from",
804
- "to"
805
- ],
806
- "level": "alert"
807
- },
808
- "ip_unblock": {
809
- "level": "notice",
810
- "offense": false,
811
- "stat": false
812
- },
813
- "ip_block_auto": {
814
- "audit_params": [
815
- "ip"
816
- ],
817
- "level": "alert",
818
- "offense": false,
819
- "stat": false
820
- },
821
- "ip_block_manual": {
822
- "audit_params": [
823
- "ip"
824
- ],
825
- "level": "alert",
826
- "offense": false,
827
- "stat": false
828
- },
829
- "ip_bypass_add": {
830
- "audit_params": [
831
- "ip"
832
- ],
833
- "level": "alert",
834
- "offense": false,
835
- "stat": false
836
- },
837
- "ip_bypass_remove": {
838
- "audit_params": [
839
- "ip"
840
- ],
841
- "level": "alert",
842
- "offense": false,
843
- "stat": false
844
- },
845
- "ip_unblock_flag": {
846
- "audit_params": [
847
- "ip"
848
- ],
849
- "level": "alert"
850
- },
851
- "bottrack_notbot": {
852
- "offense": false,
853
- "stat": false,
854
- "level": "debug"
855
- },
856
- "bottrack_404": {
857
- "audit_params": [
858
- "path"
859
- ],
860
- "offense": true
861
- },
862
- "bottrack_fakewebcrawler": {
863
- "audit_params": [
864
- "path",
865
- "crawler"
866
- ],
867
- "offense": true
868
- },
869
- "bottrack_linkcheese": {
870
- "audit_params": [
871
- "path"
872
- ],
873
- "offense": true
874
- },
875
- "bottrack_loginfailed": {
876
- "audit_params": [
877
- "user_login"
878
- ],
879
- "level": "alert",
880
- "offense": true
881
- },
882
- "bottrack_logininvalid": {
883
- "audit_params": [
884
- "user_login"
885
- ],
886
- "level": "alert",
887
- "offense": true
888
- },
889
- "bottrack_xmlrpc": {
890
- "audit_params": [
891
- "path"
892
- ],
893
- "offense": true
894
- },
895
- "bottrack_invalidscript": {
896
- "audit_params": [
897
- "script"
898
- ],
899
- "offense": true
900
- },
901
- "comment_markspam": {
902
- "level": "notice",
903
- "offense": true
904
- },
905
- "comment_unmarkspam": {
906
- "level": "info",
907
- "offense": false,
908
- "stat": false
909
- }
910
- }
911
- }
912
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
config/deprecated/lockdown.php DELETED
@@ -1,200 +0,0 @@
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/deprecated/plugin.php DELETED
@@ -1,785 +0,0 @@
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/deprecated/reporting.php DELETED
@@ -1,174 +0,0 @@
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/deprecated/sessions.php DELETED
@@ -1,103 +0,0 @@
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/email.json DELETED
@@ -1,24 +0,0 @@
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 DELETED
@@ -1,61 +0,0 @@
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 DELETED
@@ -1,442 +0,0 @@
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/headers.json DELETED
@@ -1,198 +0,0 @@
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 DELETED
@@ -1,32 +0,0 @@
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/license.json DELETED
@@ -1,167 +0,0 @@
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/login_protect.json DELETED
@@ -1,551 +0,0 @@
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/traffic.json DELETED
@@ -1,252 +0,0 @@
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 DELETED
@@ -1,473 +0,0 @@
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: 12.0.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: 11.5.6
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 $controller
21
  */
22
- private function __construct( Shield\Controller\Controller $controller ) {
23
- $controller->loadAllFeatures();
24
  }
25
 
26
  /**
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
  /**
plugin-spec.php CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "properties": {
3
- "version": "12.0.5",
4
- "release_timestamp": 1632387382,
5
  "build": "202109.2301",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
@@ -21,7 +21,7 @@
21
  "requirements": {
22
  "php": "7.0",
23
  "wordpress": "3.7",
24
- "mysql": "5.6"
25
  },
26
  "upgrade_reqs": {
27
  "10.0": {
@@ -36,7 +36,6 @@
36
  }
37
  },
38
  "paths": {
39
- "config": "config",
40
  "source": "src",
41
  "autoload": "lib/vendor/autoload.php",
42
  "assets": "resources",
@@ -61,7 +60,6 @@
61
  "plugin",
62
  "jquery/featherlight",
63
  "introjs",
64
- "shield/datatables",
65
  "shield/scanners"
66
  ],
67
  "js": [
@@ -71,9 +69,6 @@
71
  "jquery/fileDownload",
72
  "shield/tours",
73
  "bootstrap-select",
74
- "shield/datatables",
75
- "shield/traffic",
76
- "shield/audit_trail",
77
  "shield/scanners"
78
  ]
79
  },
@@ -105,17 +100,11 @@
105
  ]
106
  },
107
  "datatables-bootstrap": {
108
- "url": "https://cdn.datatables.net/1.11.0/css/dataTables.bootstrap4.min.css",
109
  "deps": [
110
  "bootstrap"
111
  ]
112
  },
113
- "datatables-searchpanes": {
114
- "url": "https://cdn.datatables.net/searchpanes/1.4.0/css/searchPanes.dataTables.min.css",
115
- "deps": [
116
- "datatables-bootstrap"
117
- ]
118
- },
119
  "datatables-select": {
120
  "url": "https://cdn.datatables.net/select/1.3.3/css/select.dataTables.min.css",
121
  "deps": [
@@ -167,15 +156,6 @@
167
  "footer": true
168
  },
169
  "shield/mainwp": {},
170
- "shield/datatables": {
171
- "deps": [
172
- "datatables-select",
173
- "datatables-buttons",
174
- "datatables-bootstrap",
175
- "datatables-searchpanes",
176
- "tp/highlightjs"
177
- ]
178
- },
179
  "shield/scanners": {
180
  "deps": [
181
  "datatables-select",
@@ -189,67 +169,61 @@
189
  }
190
  },
191
  "js": {
192
- "bootstrap": {
193
  "url": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js",
194
  "deps": [
195
  "wp-jquery"
196
  ]
197
  },
198
- "select2": {
199
  "url": "https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js",
200
  "deps": [
201
  "plugin"
202
  ]
203
  },
204
- "bootstrap-datepicker": {
205
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js",
206
  "deps": [
207
  "bootstrap"
208
  ]
209
  },
210
- "bootstrap-select": {
211
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.18/js/bootstrap-select.min.js",
212
  "deps": [
213
  "bootstrap"
214
  ]
215
  },
216
- "datatables": {
217
- "url": "https://cdn.datatables.net/1.11.0/js/jquery.dataTables.min.js",
218
  "deps": [
219
  "bootstrap",
220
  "wp-jquery"
221
  ]
222
  },
223
- "datatables-bootstrap": {
224
- "url": "https://cdn.datatables.net/1.11.0/js/dataTables.bootstrap4.min.js",
225
  "deps": [
226
  "datatables"
227
  ]
228
  },
229
- "datatables-searchpanes": {
230
- "url": "https://cdn.datatables.net/searchpanes/1.4.0/js/dataTables.searchPanes.min.js",
231
- "deps": [
232
- "datatables-bootstrap"
233
- ]
234
- },
235
- "datatables-select": {
236
  "url": "https://cdn.datatables.net/select/1.3.3/js/dataTables.select.min.js",
237
  "deps": [
238
- "datatables-bootstrap"
239
  ]
240
  },
241
- "datatables-buttons": {
242
  "url": "https://cdn.datatables.net/buttons/1.7.1/js/dataTables.buttons.min.js",
243
  "deps": [
244
  "datatables-bootstrap"
245
  ]
246
  },
247
- "global-plugin": {
248
  "deps": [
249
  "wp-jquery"
250
  ]
251
  },
252
- "plugin": {
253
  "deps": [
254
  "bootstrap",
255
  "datatables-bootstrap",
@@ -259,95 +233,76 @@
259
  "lz-string.min"
260
  ]
261
  },
262
- "base64.min": {
263
  "url": "https://cdn.jsdelivr.net/npm/js-base64@2.6.4/base64.min.js"
264
  },
265
- "lz-string.min": {},
266
- "jquery/fileDownload": {},
267
- "jquery/steps": {
268
  "url": "https://cdnjs.cloudflare.com/ajax/libs/jquery-steps/1.1.0/jquery.steps.min.js"
269
  },
270
- "jquery/featherlight": {
271
  "url": "https://cdnjs.cloudflare.com/ajax/libs/featherlight/1.7.13/featherlight.min.js"
272
  },
273
- "chartist": {
274
  "url": "https://cdnjs.cloudflare.com/ajax/libs/chartist/0.11.4/chartist.min.js"
275
  },
276
- "chartist-plugin-legend": {
277
  "deps": [
278
  "chartist"
279
  ]
280
  },
281
- "introjs": {
282
  "url": "https://cdnjs.cloudflare.com/ajax/libs/intro.js/3.3.1/intro.min.js"
283
  },
284
- "shield/charts": {
285
  "deps": [
286
  "chartist",
287
  "chartist-plugin-legend",
288
  "plugin"
289
  ]
290
  },
291
- "shuffle": {
292
  "url": "https://cdnjs.cloudflare.com/ajax/libs/Shuffle/5.3.0/shuffle.min.js"
293
  },
294
- "shield/shuffle": {
295
  "deps": [
296
  "shuffle"
297
  ]
298
  },
299
- "shield/dialog": {
300
  "deps": [
301
  "wp-jquery-ui-dialog"
302
  ]
303
  },
304
- "shield/comments": {
305
  "deps": [
306
  "wp-jquery"
307
  ],
308
  "footer": true
309
  },
310
- "shield/loginbot": {
311
  "deps": [
312
  "wp-jquery"
313
  ]
314
  },
315
- "shield/navigation": {},
316
- "shield/secadmin": {
317
  "deps": [
318
  "wp-jquery"
319
  ]
320
  },
321
- "shield/tables": {
322
  "deps": [
323
  "plugin"
324
  ]
325
  },
326
- "shield/audit_trail": {
327
- "deps": [
328
- "shield/datatables"
329
- ]
330
- },
331
- "shield/traffic": {
332
- "deps": [
333
- "shield/datatables"
334
- ]
335
- },
336
- "shield/datatables": {
337
- "deps": [
338
- "datatables-select",
339
- "datatables-buttons",
340
- "datatables-bootstrap",
341
- "datatables-searchpanes",
342
- "tp/highlightjs"
343
- ]
344
- },
345
- "shield/scanners": {
346
  "deps": [
347
  "shield/scantables"
348
  ]
349
  },
350
- "shield/scantables": {
351
  "deps": [
352
  "datatables-select",
353
  "datatables-buttons",
@@ -355,64 +310,64 @@
355
  "tp/highlightjs"
356
  ]
357
  },
358
- "shield/tours": {
359
  "deps": [
360
  "plugin",
361
  "introjs"
362
  ]
363
  },
364
- "shield/notbot": {
365
  },
366
- "shield/scans": {
367
  "deps": [
368
  "shield/tables"
369
  ]
370
  },
371
- "shield/import": {
372
  "deps": [
373
  "plugin"
374
  ]
375
  },
376
- "shield/ipanalyse": {
377
  "deps": [
378
  "plugin"
379
  ]
380
  },
381
- "shield/mainwp-extension": {
382
  "deps": [
383
  "wp-jquery"
384
  ]
385
  },
386
- "shield/userprofile": {
387
  "deps": [
388
  "u2f-bundle",
389
  "shield/dialog"
390
  ],
391
  "footer": true
392
  },
393
- "shield/wizard": {
394
  "deps": [
395
  "bootstrap",
396
  "global-plugin",
397
  "jquery/steps"
398
  ]
399
  },
400
- "u2f-bundle": {},
401
- "tp/grecaptcha": {
402
  "url": "https://www.google.com/recaptcha/api.js",
403
  "attributes": {
404
  "async": "async",
405
  "defer": "defer"
406
  }
407
  },
408
- "tp/hcaptcha": {
409
  "url": "https://hcaptcha.com/1/api.js",
410
  "attributes": {
411
  "async": "async",
412
  "defer": "defer"
413
  }
414
  },
415
- "tp/highlightjs": {
416
  "url": "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/highlight.min.js"
417
  }
418
  }
@@ -450,11 +405,12 @@
450
  }
451
  ],
452
  "version_upgrades": [
 
 
 
453
  "10.1.0",
454
  "10.2.1",
455
- "11.2.0",
456
- "12.0.0",
457
- "12.0.1"
458
  ],
459
  "action_links": {
460
  "remove": null,
1
  {
2
  "properties": {
3
+ "version": "11.5.6",
4
+ "release_timestamp": 1632389662,
5
  "build": "202109.2301",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
21
  "requirements": {
22
  "php": "7.0",
23
  "wordpress": "3.7",
24
+ "mysql": "5.0"
25
  },
26
  "upgrade_reqs": {
27
  "10.0": {
36
  }
37
  },
38
  "paths": {
 
39
  "source": "src",
40
  "autoload": "lib/vendor/autoload.php",
41
  "assets": "resources",
60
  "plugin",
61
  "jquery/featherlight",
62
  "introjs",
 
63
  "shield/scanners"
64
  ],
65
  "js": [
69
  "jquery/fileDownload",
70
  "shield/tours",
71
  "bootstrap-select",
 
 
 
72
  "shield/scanners"
73
  ]
74
  },
100
  ]
101
  },
102
  "datatables-bootstrap": {
103
+ "url": "https://cdn.datatables.net/1.10.25/css/dataTables.bootstrap4.min.css",
104
  "deps": [
105
  "bootstrap"
106
  ]
107
  },
 
 
 
 
 
 
108
  "datatables-select": {
109
  "url": "https://cdn.datatables.net/select/1.3.3/css/select.dataTables.min.css",
110
  "deps": [
156
  "footer": true
157
  },
158
  "shield/mainwp": {},
 
 
 
 
 
 
 
 
 
159
  "shield/scanners": {
160
  "deps": [
161
  "datatables-select",
169
  }
170
  },
171
  "js": {
172
+ "bootstrap": {
173
  "url": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js",
174
  "deps": [
175
  "wp-jquery"
176
  ]
177
  },
178
+ "select2": {
179
  "url": "https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js",
180
  "deps": [
181
  "plugin"
182
  ]
183
  },
184
+ "bootstrap-datepicker": {
185
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js",
186
  "deps": [
187
  "bootstrap"
188
  ]
189
  },
190
+ "bootstrap-select": {
191
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.18/js/bootstrap-select.min.js",
192
  "deps": [
193
  "bootstrap"
194
  ]
195
  },
196
+ "datatables": {
197
+ "url": "https://cdn.datatables.net/1.10.25/js/jquery.dataTables.min.js",
198
  "deps": [
199
  "bootstrap",
200
  "wp-jquery"
201
  ]
202
  },
203
+ "datatables-bootstrap": {
204
+ "url": "https://cdn.datatables.net/1.10.25/js/dataTables.bootstrap4.min.js",
205
  "deps": [
206
  "datatables"
207
  ]
208
  },
209
+ "datatables-select": {
 
 
 
 
 
 
210
  "url": "https://cdn.datatables.net/select/1.3.3/js/dataTables.select.min.js",
211
  "deps": [
212
+ "datatables"
213
  ]
214
  },
215
+ "datatables-buttons": {
216
  "url": "https://cdn.datatables.net/buttons/1.7.1/js/dataTables.buttons.min.js",
217
  "deps": [
218
  "datatables-bootstrap"
219
  ]
220
  },
221
+ "global-plugin": {
222
  "deps": [
223
  "wp-jquery"
224
  ]
225
  },
226
+ "plugin": {
227
  "deps": [
228
  "bootstrap",
229
  "datatables-bootstrap",
233
  "lz-string.min"
234
  ]
235
  },
236
+ "base64.min": {
237
  "url": "https://cdn.jsdelivr.net/npm/js-base64@2.6.4/base64.min.js"
238
  },
239
+ "lz-string.min": {},
240
+ "jquery/fileDownload": {},
241
+ "jquery/steps": {
242
  "url": "https://cdnjs.cloudflare.com/ajax/libs/jquery-steps/1.1.0/jquery.steps.min.js"
243
  },
244
+ "jquery/featherlight": {
245
  "url": "https://cdnjs.cloudflare.com/ajax/libs/featherlight/1.7.13/featherlight.min.js"
246
  },
247
+ "chartist": {
248
  "url": "https://cdnjs.cloudflare.com/ajax/libs/chartist/0.11.4/chartist.min.js"
249
  },
250
+ "chartist-plugin-legend": {
251
  "deps": [
252
  "chartist"
253
  ]
254
  },
255
+ "introjs": {
256
  "url": "https://cdnjs.cloudflare.com/ajax/libs/intro.js/3.3.1/intro.min.js"
257
  },
258
+ "shield/charts": {
259
  "deps": [
260
  "chartist",
261
  "chartist-plugin-legend",
262
  "plugin"
263
  ]
264
  },
265
+ "shuffle": {
266
  "url": "https://cdnjs.cloudflare.com/ajax/libs/Shuffle/5.3.0/shuffle.min.js"
267
  },
268
+ "shield/shuffle": {
269
  "deps": [
270
  "shuffle"
271
  ]
272
  },
273
+ "shield/dialog": {
274
  "deps": [
275
  "wp-jquery-ui-dialog"
276
  ]
277
  },
278
+ "shield/comments": {
279
  "deps": [
280
  "wp-jquery"
281
  ],
282
  "footer": true
283
  },
284
+ "shield/loginbot": {
285
  "deps": [
286
  "wp-jquery"
287
  ]
288
  },
289
+ "shield/navigation": {},
290
+ "shield/secadmin": {
291
  "deps": [
292
  "wp-jquery"
293
  ]
294
  },
295
+ "shield/tables": {
296
  "deps": [
297
  "plugin"
298
  ]
299
  },
300
+ "shield/scanners": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  "deps": [
302
  "shield/scantables"
303
  ]
304
  },
305
+ "shield/scantables": {
306
  "deps": [
307
  "datatables-select",
308
  "datatables-buttons",
310
  "tp/highlightjs"
311
  ]
312
  },
313
+ "shield/tours": {
314
  "deps": [
315
  "plugin",
316
  "introjs"
317
  ]
318
  },
319
+ "shield/notbot": {
320
  },
321
+ "shield/scans": {
322
  "deps": [
323
  "shield/tables"
324
  ]
325
  },
326
+ "shield/import": {
327
  "deps": [
328
  "plugin"
329
  ]
330
  },
331
+ "shield/ipanalyse": {
332
  "deps": [
333
  "plugin"
334
  ]
335
  },
336
+ "shield/mainwp": {
337
  "deps": [
338
  "wp-jquery"
339
  ]
340
  },
341
+ "shield/userprofile": {
342
  "deps": [
343
  "u2f-bundle",
344
  "shield/dialog"
345
  ],
346
  "footer": true
347
  },
348
+ "shield/wizard": {
349
  "deps": [
350
  "bootstrap",
351
  "global-plugin",
352
  "jquery/steps"
353
  ]
354
  },
355
+ "u2f-bundle": {},
356
+ "tp/grecaptcha": {
357
  "url": "https://www.google.com/recaptcha/api.js",
358
  "attributes": {
359
  "async": "async",
360
  "defer": "defer"
361
  }
362
  },
363
+ "tp/hcaptcha": {
364
  "url": "https://hcaptcha.com/1/api.js",
365
  "attributes": {
366
  "async": "async",
367
  "defer": "defer"
368
  }
369
  },
370
+ "tp/highlightjs": {
371
  "url": "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/highlight.min.js"
372
  }
373
  }
405
  }
406
  ],
407
  "version_upgrades": [
408
+ "9.1.1",
409
+ "9.2.0",
410
+ "9.2.2",
411
  "10.1.0",
412
  "10.2.1",
413
+ "11.2.0"
 
 
414
  ],
415
  "action_links": {
416
  "remove": null,
plugin.json CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "properties": {
3
- "version": "12.0.5",
4
- "release_timestamp": 1632387382,
5
  "build": "202109.2301",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
@@ -36,7 +36,6 @@
36
  }
37
  },
38
  "paths": {
39
- "config": "config",
40
  "source": "src",
41
  "autoload": "lib/vendor/autoload.php",
42
  "assets": "resources",
@@ -61,7 +60,6 @@
61
  "plugin",
62
  "jquery/featherlight",
63
  "introjs",
64
- "shield/datatables",
65
  "shield/scanners"
66
  ],
67
  "js": [
@@ -71,9 +69,6 @@
71
  "jquery/fileDownload",
72
  "shield/tours",
73
  "bootstrap-select",
74
- "shield/datatables",
75
- "shield/traffic",
76
- "shield/audit_trail",
77
  "shield/scanners"
78
  ]
79
  },
@@ -105,17 +100,11 @@
105
  ]
106
  },
107
  "datatables-bootstrap": {
108
- "url": "https://cdn.datatables.net/1.11.0/css/dataTables.bootstrap4.min.css",
109
  "deps": [
110
  "bootstrap"
111
  ]
112
  },
113
- "datatables-searchpanes": {
114
- "url": "https://cdn.datatables.net/searchpanes/1.4.0/css/searchPanes.dataTables.min.css",
115
- "deps": [
116
- "datatables-bootstrap"
117
- ]
118
- },
119
  "datatables-select": {
120
  "url": "https://cdn.datatables.net/select/1.3.3/css/select.dataTables.min.css",
121
  "deps": [
@@ -167,15 +156,6 @@
167
  "footer": true
168
  },
169
  "shield/mainwp": {},
170
- "shield/datatables": {
171
- "deps": [
172
- "datatables-select",
173
- "datatables-buttons",
174
- "datatables-bootstrap",
175
- "datatables-searchpanes",
176
- "tp/highlightjs"
177
- ]
178
- },
179
  "shield/scanners": {
180
  "deps": [
181
  "datatables-select",
@@ -189,67 +169,61 @@
189
  }
190
  },
191
  "js": {
192
- "bootstrap": {
193
  "url": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js",
194
  "deps": [
195
  "wp-jquery"
196
  ]
197
  },
198
- "select2": {
199
  "url": "https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js",
200
  "deps": [
201
  "plugin"
202
  ]
203
  },
204
- "bootstrap-datepicker": {
205
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js",
206
  "deps": [
207
  "bootstrap"
208
  ]
209
  },
210
- "bootstrap-select": {
211
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.18/js/bootstrap-select.min.js",
212
  "deps": [
213
  "bootstrap"
214
  ]
215
  },
216
- "datatables": {
217
- "url": "https://cdn.datatables.net/1.11.0/js/jquery.dataTables.min.js",
218
  "deps": [
219
  "bootstrap",
220
  "wp-jquery"
221
  ]
222
  },
223
- "datatables-bootstrap": {
224
- "url": "https://cdn.datatables.net/1.11.0/js/dataTables.bootstrap4.min.js",
225
  "deps": [
226
  "datatables"
227
  ]
228
  },
229
- "datatables-searchpanes": {
230
- "url": "https://cdn.datatables.net/searchpanes/1.4.0/js/dataTables.searchPanes.min.js",
231
- "deps": [
232
- "datatables-bootstrap"
233
- ]
234
- },
235
- "datatables-select": {
236
  "url": "https://cdn.datatables.net/select/1.3.3/js/dataTables.select.min.js",
237
  "deps": [
238
- "datatables-bootstrap"
239
  ]
240
  },
241
- "datatables-buttons": {
242
  "url": "https://cdn.datatables.net/buttons/1.7.1/js/dataTables.buttons.min.js",
243
  "deps": [
244
  "datatables-bootstrap"
245
  ]
246
  },
247
- "global-plugin": {
248
  "deps": [
249
  "wp-jquery"
250
  ]
251
  },
252
- "plugin": {
253
  "deps": [
254
  "bootstrap",
255
  "datatables-bootstrap",
@@ -259,95 +233,76 @@
259
  "lz-string.min"
260
  ]
261
  },
262
- "base64.min": {
263
  "url": "https://cdn.jsdelivr.net/npm/js-base64@2.6.4/base64.min.js"
264
  },
265
- "lz-string.min": {},
266
- "jquery/fileDownload": {},
267
- "jquery/steps": {
268
  "url": "https://cdnjs.cloudflare.com/ajax/libs/jquery-steps/1.1.0/jquery.steps.min.js"
269
  },
270
- "jquery/featherlight": {
271
  "url": "https://cdnjs.cloudflare.com/ajax/libs/featherlight/1.7.13/featherlight.min.js"
272
  },
273
- "chartist": {
274
  "url": "https://cdnjs.cloudflare.com/ajax/libs/chartist/0.11.4/chartist.min.js"
275
  },
276
- "chartist-plugin-legend": {
277
  "deps": [
278
  "chartist"
279
  ]
280
  },
281
- "introjs": {
282
  "url": "https://cdnjs.cloudflare.com/ajax/libs/intro.js/3.3.1/intro.min.js"
283
  },
284
- "shield/charts": {
285
  "deps": [
286
  "chartist",
287
  "chartist-plugin-legend",
288
  "plugin"
289
  ]
290
  },
291
- "shuffle": {
292
  "url": "https://cdnjs.cloudflare.com/ajax/libs/Shuffle/5.3.0/shuffle.min.js"
293
  },
294
- "shield/shuffle": {
295
  "deps": [
296
  "shuffle"
297
  ]
298
  },
299
- "shield/dialog": {
300
  "deps": [
301
  "wp-jquery-ui-dialog"
302
  ]
303
  },
304
- "shield/comments": {
305
  "deps": [
306
  "wp-jquery"
307
  ],
308
  "footer": true
309
  },
310
- "shield/loginbot": {
311
  "deps": [
312
  "wp-jquery"
313
  ]
314
  },
315
- "shield/navigation": {},
316
- "shield/secadmin": {
317
  "deps": [
318
  "wp-jquery"
319
  ]
320
  },
321
- "shield/tables": {
322
  "deps": [
323
  "plugin"
324
  ]
325
  },
326
- "shield/audit_trail": {
327
- "deps": [
328
- "shield/datatables"
329
- ]
330
- },
331
- "shield/traffic": {
332
- "deps": [
333
- "shield/datatables"
334
- ]
335
- },
336
- "shield/datatables": {
337
- "deps": [
338
- "datatables-select",
339
- "datatables-buttons",
340
- "datatables-bootstrap",
341
- "datatables-searchpanes",
342
- "tp/highlightjs"
343
- ]
344
- },
345
- "shield/scanners": {
346
  "deps": [
347
  "shield/scantables"
348
  ]
349
  },
350
- "shield/scantables": {
351
  "deps": [
352
  "datatables-select",
353
  "datatables-buttons",
@@ -355,64 +310,64 @@
355
  "tp/highlightjs"
356
  ]
357
  },
358
- "shield/tours": {
359
  "deps": [
360
  "plugin",
361
  "introjs"
362
  ]
363
  },
364
- "shield/notbot": {
365
  },
366
- "shield/scans": {
367
  "deps": [
368
  "shield/tables"
369
  ]
370
  },
371
- "shield/import": {
372
  "deps": [
373
  "plugin"
374
  ]
375
  },
376
- "shield/ipanalyse": {
377
  "deps": [
378
  "plugin"
379
  ]
380
  },
381
- "shield/mainwp-extension": {
382
  "deps": [
383
  "wp-jquery"
384
  ]
385
  },
386
- "shield/userprofile": {
387
  "deps": [
388
  "u2f-bundle",
389
  "shield/dialog"
390
  ],
391
  "footer": true
392
  },
393
- "shield/wizard": {
394
  "deps": [
395
  "bootstrap",
396
  "global-plugin",
397
  "jquery/steps"
398
  ]
399
  },
400
- "u2f-bundle": {},
401
- "tp/grecaptcha": {
402
  "url": "https://www.google.com/recaptcha/api.js",
403
  "attributes": {
404
  "async": "async",
405
  "defer": "defer"
406
  }
407
  },
408
- "tp/hcaptcha": {
409
  "url": "https://hcaptcha.com/1/api.js",
410
  "attributes": {
411
  "async": "async",
412
  "defer": "defer"
413
  }
414
  },
415
- "tp/highlightjs": {
416
  "url": "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/highlight.min.js"
417
  }
418
  }
@@ -450,11 +405,12 @@
450
  }
451
  ],
452
  "version_upgrades": [
 
 
 
453
  "10.1.0",
454
  "10.2.1",
455
- "11.2.0",
456
- "12.0.0",
457
- "12.0.1"
458
  ],
459
  "action_links": {
460
  "remove": null,
1
  {
2
  "properties": {
3
+ "version": "11.5.6",
4
+ "release_timestamp": 1632389662,
5
  "build": "202109.2301",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
36
  }
37
  },
38
  "paths": {
 
39
  "source": "src",
40
  "autoload": "lib/vendor/autoload.php",
41
  "assets": "resources",
60
  "plugin",
61
  "jquery/featherlight",
62
  "introjs",
 
63
  "shield/scanners"
64
  ],
65
  "js": [
69
  "jquery/fileDownload",
70
  "shield/tours",
71
  "bootstrap-select",
 
 
 
72
  "shield/scanners"
73
  ]
74
  },
100
  ]
101
  },
102
  "datatables-bootstrap": {
103
+ "url": "https://cdn.datatables.net/1.10.25/css/dataTables.bootstrap4.min.css",
104
  "deps": [
105
  "bootstrap"
106
  ]
107
  },
 
 
 
 
 
 
108
  "datatables-select": {
109
  "url": "https://cdn.datatables.net/select/1.3.3/css/select.dataTables.min.css",
110
  "deps": [
156
  "footer": true
157
  },
158
  "shield/mainwp": {},
 
 
 
 
 
 
 
 
 
159
  "shield/scanners": {
160
  "deps": [
161
  "datatables-select",
169
  }
170
  },
171
  "js": {
172
+ "bootstrap": {
173
  "url": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js",
174
  "deps": [
175
  "wp-jquery"
176
  ]
177
  },
178
+ "select2": {
179
  "url": "https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js",
180
  "deps": [
181
  "plugin"
182
  ]
183
  },
184
+ "bootstrap-datepicker": {
185
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js",
186
  "deps": [
187
  "bootstrap"
188
  ]
189
  },
190
+ "bootstrap-select": {
191
  "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.18/js/bootstrap-select.min.js",
192
  "deps": [
193
  "bootstrap"
194
  ]
195
  },
196
+ "datatables": {
197
+ "url": "https://cdn.datatables.net/1.10.25/js/jquery.dataTables.min.js",
198
  "deps": [
199
  "bootstrap",
200
  "wp-jquery"
201
  ]
202
  },
203
+ "datatables-bootstrap": {
204
+ "url": "https://cdn.datatables.net/1.10.25/js/dataTables.bootstrap4.min.js",
205
  "deps": [
206
  "datatables"
207
  ]
208
  },
209
+ "datatables-select": {
 
 
 
 
 
 
210
  "url": "https://cdn.datatables.net/select/1.3.3/js/dataTables.select.min.js",
211
  "deps": [
212
+ "datatables"
213
  ]
214
  },
215
+ "datatables-buttons": {
216
  "url": "https://cdn.datatables.net/buttons/1.7.1/js/dataTables.buttons.min.js",
217
  "deps": [
218
  "datatables-bootstrap"
219
  ]
220
  },
221
+ "global-plugin": {
222
  "deps": [
223
  "wp-jquery"
224
  ]
225
  },
226
+ "plugin": {
227
  "deps": [
228
  "bootstrap",
229
  "datatables-bootstrap",
233
  "lz-string.min"
234
  ]
235
  },
236
+ "base64.min": {
237
  "url": "https://cdn.jsdelivr.net/npm/js-base64@2.6.4/base64.min.js"
238
  },
239
+ "lz-string.min": {},
240
+ "jquery/fileDownload": {},
241
+ "jquery/steps": {
242
  "url": "https://cdnjs.cloudflare.com/ajax/libs/jquery-steps/1.1.0/jquery.steps.min.js"
243
  },
244
+ "jquery/featherlight": {
245
  "url": "https://cdnjs.cloudflare.com/ajax/libs/featherlight/1.7.13/featherlight.min.js"
246
  },
247
+ "chartist": {
248
  "url": "https://cdnjs.cloudflare.com/ajax/libs/chartist/0.11.4/chartist.min.js"
249
  },
250
+ "chartist-plugin-legend": {
251
  "deps": [
252
  "chartist"
253
  ]
254
  },
255
+ "introjs": {
256
  "url": "https://cdnjs.cloudflare.com/ajax/libs/intro.js/3.3.1/intro.min.js"
257
  },
258
+ "shield/charts": {
259
  "deps": [
260
  "chartist",
261
  "chartist-plugin-legend",
262
  "plugin"
263
  ]
264
  },
265
+ "shuffle": {
266
  "url": "https://cdnjs.cloudflare.com/ajax/libs/Shuffle/5.3.0/shuffle.min.js"
267
  },
268
+ "shield/shuffle": {
269
  "deps": [
270
  "shuffle"
271
  ]
272
  },
273
+ "shield/dialog": {
274
  "deps": [
275
  "wp-jquery-ui-dialog"
276
  ]
277
  },
278
+ "shield/comments": {
279
  "deps": [
280
  "wp-jquery"
281
  ],
282
  "footer": true
283
  },
284
+ "shield/loginbot": {
285
  "deps": [
286
  "wp-jquery"
287
  ]
288
  },
289
+ "shield/navigation": {},
290
+ "shield/secadmin": {
291
  "deps": [
292
  "wp-jquery"
293
  ]
294
  },
295
+ "shield/tables": {
296
  "deps": [
297
  "plugin"
298
  ]
299
  },
300
+ "shield/scanners": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  "deps": [
302
  "shield/scantables"
303
  ]
304
  },
305
+ "shield/scantables": {
306
  "deps": [
307
  "datatables-select",
308
  "datatables-buttons",
310
  "tp/highlightjs"
311
  ]
312
  },
313
+ "shield/tours": {
314
  "deps": [
315
  "plugin",
316
  "introjs"
317
  ]
318
  },
319
+ "shield/notbot": {
320
  },
321
+ "shield/scans": {
322
  "deps": [
323
  "shield/tables"
324
  ]
325
  },
326
+ "shield/import": {
327
  "deps": [
328
  "plugin"
329
  ]
330
  },
331
+ "shield/ipanalyse": {
332
  "deps": [
333
  "plugin"
334
  ]
335
  },
336
+ "shield/mainwp": {
337
  "deps": [
338
  "wp-jquery"
339
  ]
340
  },
341
+ "shield/userprofile": {
342
  "deps": [
343
  "u2f-bundle",
344
  "shield/dialog"
345
  ],
346
  "footer": true
347
  },
348
+ "shield/wizard": {
349
  "deps": [
350
  "bootstrap",
351
  "global-plugin",
352
  "jquery/steps"
353
  ]
354
  },
355
+ "u2f-bundle": {},
356
+ "tp/grecaptcha": {
357
  "url": "https://www.google.com/recaptcha/api.js",
358
  "attributes": {
359
  "async": "async",
360
  "defer": "defer"
361
  }
362
  },
363
+ "tp/hcaptcha": {
364
  "url": "https://hcaptcha.com/1/api.js",
365
  "attributes": {
366
  "async": "async",
367
  "defer": "defer"
368
  }
369
  },
370
+ "tp/highlightjs": {
371
  "url": "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/highlight.min.js"
372
  }
373
  }
405
  }
406
  ],
407
  "version_upgrades": [
408
+ "9.1.1",
409
+ "9.2.0",
410
+ "9.2.2",
411
  "10.1.0",
412
  "10.2.1",
413
+ "11.2.0"
 
 
414
  ],
415
  "action_links": {
416
  "remove": null,
readme.txt CHANGED
@@ -8,13 +8,13 @@ Requires at least: 3.7
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 5.8
11
- Stable tag: 12.0.5
12
 
13
  No-Nonsense Security Hardening that protects WordPress against hackers, malicious bots, and spammers (no captchas!). Now with exclusive ShieldNET Technology.
14
 
15
  == Description ==
16
 
17
- **No-Nonsense, No-Hype. Just Good Security Protection**. Shield is the only NO-nonsense security solution that defends and protects your WordPress sites against hackers and malicious bots, of all types. With our exclusive, *no-need-for-captcha* security technology you can limit login attempts, block brute force attacks and prevent 100% bot comment SPAM.
18
 
19
  **Performance is critical**. Shield Security automatically blocks bad IP addresses while optimising performance so your WordPress site never slows down because of bloated security, with large IP lookup tables .
20
 
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 5.8
11
+ Stable tag: 11.5.6
12
 
13
  No-Nonsense Security Hardening that protects WordPress against hackers, malicious bots, and spammers (no captchas!). Now with exclusive ShieldNET Technology.
14
 
15
  == Description ==
16
 
17
+ **No-Nonsense, No-Hype. Just Security Protection**. Shield is the only NO-nonsense security solution that defends and protects your WordPress sites against hackers and malicious bots, of all types. With our exclusive, *no-need-for-captcha* security technology you can limit login attempts, block brute force attacks and prevent 100% bot comment SPAM.
18
 
19
  **Performance is critical**. Shield Security automatically blocks bad IP addresses while optimising performance so your WordPress site never slows down because of bloated security, with large IP lookup tables .
20
 
resources/css/plugin.css CHANGED
@@ -709,13 +709,11 @@ input:checked + .icwp-slider:before {
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,6 +728,17 @@ td.column-message textarea {
730
  font-size: 0.85rem;
731
  }
732
  /** TABLE: TRAFFIC **/
 
 
 
 
 
 
 
 
 
 
 
733
  th.column-code {
734
  width: 84px;
735
  }
@@ -1220,7 +1229,7 @@ a:focus .gravatar, a:focus, a:focus .media-icon img {
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,14 +1552,14 @@ body.folded #FooterBannerGoPro {
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,8 +1568,8 @@ body.folded #FooterBannerGoPro {
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,8 +1586,8 @@ body.folded #FooterBannerGoPro {
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,11 +1614,11 @@ body.folded #FooterBannerGoPro {
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,7 +1636,6 @@ body.folded #FooterBannerGoPro {
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);
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
  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
  #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
  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
  #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
  #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
  /*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
  }
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);
resources/css/shield/datatables.css DELETED
@@ -1,65 +0,0 @@
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 DELETED
@@ -1,4 +0,0 @@
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 DELETED
@@ -1,4 +0,0 @@
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 DELETED
@@ -1,4 +0,0 @@
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 DELETED
@@ -1,4 +0,0 @@
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 DELETED
@@ -1,4 +0,0 @@
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 DELETED
@@ -1,4 +0,0 @@
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/{shield/datatables.js → base64.min.js} RENAMED
File without changes
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 DELETED
@@ -1,196 +0,0 @@
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."
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/shield/options.js ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 DELETED
@@ -1,158 +0,0 @@
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."
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 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
config/admin_access_restriction.json → src/config/feature-admin_access_restriction.php 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,11 +408,10 @@
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
  }
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
  "recent": true
412
  },
413
  "key_fail": {
414
+ "cat": 3,
415
  "recent": true,
416
  "offense": true
417
  }
src/config/feature-audit_trail.php ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ }
config/autoupdates.json → src/config/feature-autoupdates.php RENAMED
File without changes
config/deprecated/comments_filter.php → src/config/feature-comments_filter.php 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
- "level": "notice",
336
  "stat": false,
337
  "offense": true
338
  },
@@ -343,10 +343,6 @@
343
  "spam_block_recaptcha": {
344
  },
345
  "spam_block_human": {
346
- "audit_params": [
347
- "word",
348
- "key"
349
- ]
350
  }
351
  }
352
  }
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
  "spam_block_recaptcha": {
344
  },
345
  "spam_block_human": {
 
 
 
 
346
  }
347
  }
348
  }
config/comms.json → src/config/feature-comms.php RENAMED
@@ -56,18 +56,8 @@
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
  }
56
  "definitions": {
57
  "events": {
58
  "suresend_success": {
 
 
 
 
 
59
  },
60
  "suresend_fail": {
 
 
 
 
 
61
  }
62
  }
63
  }
config/deprecated/email.php → src/config/feature-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": [
config/deprecated/events.php → src/config/feature-events.php RENAMED
File without changes
config/deprecated/firewall.php → src/config/feature-firewall.php RENAMED
@@ -88,6 +88,19 @@
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,39 +416,45 @@
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
  }
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
  }
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
  }
config/hack_protect.json → src/config/feature-hack_protect.php RENAMED
@@ -508,9 +508,8 @@
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
- "attempt_repair_at": "Attempted Repair At"
514
  }
515
  },
516
  "db_table_scanq": {
@@ -556,39 +555,80 @@
556
  "xmlrpc.php"
557
  ],
558
  "events": {
559
- "scan_run": {
560
- "audit_params": [
561
- "scan"
562
- ],
563
- "level": "debug",
564
- "audit_multiple": true
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
565
  },
566
- "scan_items_found": {
567
- "audit_params": [
568
- "scan",
569
- "items"
570
- ],
571
- "level": "alert",
572
  "audit_multiple": true,
573
  "recent": true
574
  },
575
  "scan_item_repair_success": {
576
- "audit_params": [
577
- "path_full"
578
- ],
579
  "audit_multiple": true,
580
  "recent": true
581
  },
582
  "scan_item_repair_fail": {
583
- "audit_params": [
584
- "path_full"
585
- ],
586
  "audit_multiple": true
587
  },
588
  "scan_item_delete_success": {
589
- "audit_params": [
590
- "path_full"
591
- ],
592
  "audit_multiple": true
593
  }
594
  }
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": {
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
  }
config/deprecated/headers.php → src/config/feature-headers.php RENAMED
File without changes
config/deprecated/insights.php → src/config/feature-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
  }
config/integrations.json → src/config/feature-integrations.php RENAMED
@@ -137,10 +137,6 @@
137
  "wordpress"
138
  ],
139
  "value_options": [
140
- {
141
- "value_key": "buddyboss",
142
- "text": "BuddyBoss"
143
- },
144
  {
145
  "value_key": "buddypress",
146
  "text": "BuddyPress"
@@ -197,44 +193,24 @@
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
  }
137
  "wordpress"
138
  ],
139
  "value_options": [
 
 
 
 
140
  {
141
  "value_key": "buddypress",
142
  "text": "BuddyPress"
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
  }
config/ips.json → src/config/feature-ips.php RENAMED
@@ -164,19 +164,6 @@
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": "force_notbot",
169
- "section": "section_antibot",
170
- "advanced": true,
171
- "default": "Y",
172
- "type": "checkbox",
173
- "link_info": "https://shsec.io/jy",
174
- "link_blog": "https://shsec.io/jz",
175
- "beacon_id": 448,
176
- "name": "Force NotBot JS",
177
- "summary": "Force Loading Of NotBot JS",
178
- "description": "Use this option if you're using an aggressive caching plugin or system to ensure NotBot JS is loaded for visitors."
179
- },
180
  {
181
  "key": "transgression_limit",
182
  "section": "section_auto_black_list",
@@ -582,13 +569,6 @@
582
  "transferable": false,
583
  "type": "array",
584
  "default": []
585
- },
586
- {
587
- "key": "legacy_db_deleted_at",
588
- "section": "section_non_ui",
589
- "transferable": false,
590
- "type": "integer",
591
- "default": ""
592
  }
593
  ],
594
  "definitions": {
@@ -604,123 +584,6 @@
604
  "woff",
605
  "woff2"
606
  ],
607
- "db_handler_classes": {
608
- "botsignal": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\DB\\BotSignal\\Ops\\Handler"
609
- },
610
- "db_table_botsignal": {
611
- "autoexpire": 0,
612
- "slug": "botsignal",
613
- "has_updated_at": true,
614
- "col_older_than": "updated_at",
615
- "cols_custom": {
616
- "ip_ref": {
617
- "macro_type": "foreign_key_id",
618
- "foreign_key": {
619
- "ref_table": "icwp_wpsf_ips"
620
- }
621
- },
622
- "notbot_at": {
623
- "macro_type": "timestamp",
624
- "comment": "NotBot"
625
- },
626
- "frontpage_at": {
627
- "macro_type": "timestamp",
628
- "comment": "Any Frontend Page Loaded"
629
- },
630
- "loginpage_at": {
631
- "macro_type": "timestamp",
632
- "comment": "Login Page Loaded"
633
- },
634
- "bt404_at": {
635
- "macro_type": "timestamp",
636
- "comment": "BotTrack 404"
637
- },
638
- "btfake_at": {
639
- "macro_type": "timestamp",
640
- "comment": "BotTrack FakeWebCrawler"
641
- },
642
- "btcheese_at": {
643
- "macro_type": "timestamp",
644
- "comment": "BotTrack LinkCheese"
645
- },
646
- "btloginfail_at": {
647
- "macro_type": "timestamp",
648
- "comment": "BotTrack LoginFailed"
649
- },
650
- "btua_at": {
651
- "macro_type": "timestamp",
652
- "comment": "BotTrack Useragent Fail"
653
- },
654
- "btxml_at": {
655
- "macro_type": "timestamp",
656
- "comment": "BotTrack XMLRPC Access"
657
- },
658
- "btlogininvalid_at": {
659
- "macro_type": "timestamp",
660
- "comment": "BotTrack LoginInvalid"
661
- },
662
- "btinvalidscript_at": {
663
- "macro_type": "timestamp",
664
- "comment": "BotTrack InvalidScript"
665
- },
666
- "cooldown_at": {
667
- "macro_type": "timestamp",
668
- "comment": "Cooldown Triggered"
669
- },
670
- "humanspam_at": {
671
- "macro_type": "timestamp",
672
- "comment": "Comment Marked As Human SPAM"
673
- },
674
- "markspam_at": {
675
- "macro_type": "timestamp",
676
- "comment": "Mark Comment As SPAM"
677
- },
678
- "unmarkspam_at": {
679
- "macro_type": "timestamp",
680
- "comment": "Unmark Comment As SPAM"
681
- },
682
- "captchapass_at": {
683
- "macro_type": "timestamp",
684
- "comment": "Captcha Passed"
685
- },
686
- "captchafail_at": {
687
- "macro_type": "timestamp",
688
- "comment": "Captcha Failed"
689
- },
690
- "auth_at": {
691
- "macro_type": "timestamp",
692
- "comment": "Successful Login"
693
- },
694
- "firewall_at": {
695
- "macro_type": "timestamp",
696
- "comment": "Triggered Firewall"
697
- },
698
- "ratelimit_at": {
699
- "macro_type": "timestamp",
700
- "comment": "Rate Limit Exceeded"
701
- },
702
- "offense_at": {
703
- "macro_type": "timestamp",
704
- "comment": "Last Offense"
705
- },
706
- "blocked_at": {
707
- "macro_type": "timestamp",
708
- "comment": "Last Block"
709
- },
710
- "unblocked_at": {
711
- "macro_type": "timestamp",
712
- "comment": "Unblocked"
713
- },
714
- "bypass_at": {
715
- "macro_type": "timestamp",
716
- "comment": "Bypass"
717
- },
718
- "snsent_at": {
719
- "macro_type": "timestamp",
720
- "comment": "Sent To ShieldNET"
721
- }
722
- }
723
- },
724
  "db_classes": {
725
  "botsignals": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\BotSignals\\Handler",
726
  "ip_lists": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\IPs\\Handler"
@@ -779,131 +642,90 @@
779
  },
780
  "events": {
781
  "custom_offense": {
782
- "audit_params": [
783
- "message"
784
- ],
785
- "offense": true
786
  },
787
  "conn_kill": {
788
- "level": "warning",
789
- "audit_countable": true
790
- },
791
- "conn_not_kill_high_rep": {
792
- "level": "debug"
793
  },
794
  "ip_offense": {
795
- "level": "warning",
796
- "audit_params": [
797
- "from",
798
- "to"
799
- ]
800
  },
801
  "ip_blocked": {
802
- "audit_params": [
803
- "from",
804
- "to"
805
- ],
806
- "level": "alert"
807
  },
808
  "ip_unblock": {
809
- "level": "notice",
810
  "offense": false,
 
811
  "stat": false
812
  },
813
  "ip_block_auto": {
814
- "audit_params": [
815
- "ip"
816
- ],
817
- "level": "alert",
818
- "offense": false,
819
- "stat": false
820
  },
821
  "ip_block_manual": {
822
- "audit_params": [
823
- "ip"
824
- ],
825
- "level": "alert",
826
- "offense": false,
827
- "stat": false
828
  },
829
  "ip_bypass_add": {
830
- "audit_params": [
831
- "ip"
832
- ],
833
- "level": "alert",
834
- "offense": false,
835
- "stat": false
836
  },
837
  "ip_bypass_remove": {
838
- "audit_params": [
839
- "ip"
840
- ],
841
- "level": "alert",
842
- "offense": false,
843
- "stat": false
844
  },
845
  "ip_unblock_flag": {
846
- "audit_params": [
847
- "ip"
848
- ],
849
- "level": "alert"
850
  },
851
  "bottrack_notbot": {
 
852
  "offense": false,
853
- "stat": false,
854
- "level": "debug"
855
  },
856
  "bottrack_404": {
857
- "audit_params": [
858
- "path"
859
- ],
860
- "offense": true
861
  },
862
  "bottrack_fakewebcrawler": {
863
- "audit_params": [
864
- "path",
865
- "crawler"
866
- ],
867
- "offense": true
868
  },
869
  "bottrack_linkcheese": {
870
- "audit_params": [
871
- "path"
872
- ],
873
- "offense": true
874
  },
875
  "bottrack_loginfailed": {
876
- "audit_params": [
877
- "user_login"
878
- ],
879
- "level": "alert",
880
- "offense": true
881
  },
882
  "bottrack_logininvalid": {
883
- "audit_params": [
884
- "user_login"
885
- ],
886
- "level": "alert",
887
- "offense": true
 
888
  },
889
  "bottrack_xmlrpc": {
890
- "audit_params": [
891
- "path"
892
- ],
893
- "offense": true
894
  },
895
  "bottrack_invalidscript": {
896
- "audit_params": [
897
- "script"
898
- ],
899
- "offense": true
900
  },
901
  "comment_markspam": {
902
- "level": "notice",
903
  "offense": true
904
  },
905
  "comment_unmarkspam": {
906
- "level": "info",
907
  "offense": false,
908
  "stat": false
909
  }
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",
569
  "transferable": false,
570
  "type": "array",
571
  "default": []
 
 
 
 
 
 
 
572
  }
573
  ],
574
  "definitions": {
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"
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
  }
config/deprecated/license.php → src/config/feature-license.php RENAMED
@@ -151,16 +151,14 @@
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
  }
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
  }
config/lockdown.json → src/config/feature-lockdown.php RENAMED
@@ -184,14 +184,10 @@
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
  }
184
  ],
185
  "events": {
186
  "block_anonymous_restapi": {
187
+ "recent": true
 
 
 
 
188
  },
189
  "block_xml": {
190
+ "audit": false,
191
  "recent": true,
192
  "offense": true
193
  }
config/deprecated/login_protect.php → src/config/feature-login_protect.php RENAMED
@@ -502,48 +502,46 @@
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
  }
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
  }
config/plugin.json → src/config/feature-plugin.php RENAMED
@@ -50,6 +50,14 @@
50
  "can_dismiss": false,
51
  "type": "error"
52
  },
 
 
 
 
 
 
 
 
53
  "compat-sgoptimize": {
54
  "id": "compat-sgoptimize",
55
  "schedule": "conditions",
@@ -545,6 +553,7 @@
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,14 +564,18 @@
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,97 +654,56 @@
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": {
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
  "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
  "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
  }
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": {
config/reporting.json → src/config/feature-reporting.php RENAMED
@@ -154,21 +154,6 @@
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
  }
154
  "interval_end_at": "Reporting Interval End",
155
  "sent_at": "Report Sent"
156
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  }
158
  }
159
  }
config/sessions.json → src/config/feature-sessions.php RENAMED
@@ -69,33 +69,22 @@
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
  }
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
  }
config/deprecated/traffic.php → src/config/feature-traffic.php RENAMED
@@ -101,7 +101,6 @@
101
  "advanced": true,
102
  "default": [
103
  "logged_in",
104
- "server",
105
  "cron",
106
  "search",
107
  "uptime"
@@ -127,10 +126,6 @@
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,7 +160,7 @@
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,6 +169,20 @@
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,13 +219,6 @@
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,13 +241,8 @@
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
  }
101
  "advanced": true,
102
  "default": [
103
  "logged_in",
 
104
  "cron",
105
  "search",
106
  "uptime"
126
  "value_key": "cron",
127
  "text": "WP CRON"
128
  },
 
 
 
 
129
  {
130
  "value_key": "search",
131
  "text": "Search Engines"
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
  "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
  "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
  "traffic_table_name": "traffic",
242
  "events": {
243
  "request_limit_exceeded": {
244
+ "cat": 3,
245
+ "offense": true
 
 
 
 
 
246
  }
247
  }
248
  }
config/deprecated/user_management.php → src/config/feature-user_management.php RENAMED
@@ -412,61 +412,32 @@
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
  }
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
  }
src/lib/src/Controller/Assets/Paths.php CHANGED
@@ -12,10 +12,6 @@ class Paths {
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
  }
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
  }
src/lib/src/Controller/Assets/Urls.php CHANGED
@@ -42,6 +42,16 @@ 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
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
src/lib/src/Controller/Controller.php CHANGED
@@ -4,7 +4,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller;
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,34 +14,44 @@ use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
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,6 +67,11 @@ class Controller extends DynPropertiesClass {
58
  */
59
  protected $sAdminNoticeError = '';
60
 
 
 
 
 
 
61
  /**
62
  * @var Shield\Utilities\AdminNotices\Controller
63
  */
@@ -68,11 +82,23 @@ class Controller extends DynPropertiesClass {
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,18 +155,6 @@ class Controller extends DynPropertiesClass {
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();
@@ -257,14 +271,15 @@ class Controller extends DynPropertiesClass {
257
  }
258
 
259
  public function adminNoticePluginFailedToLoad() {
 
 
 
 
 
 
260
  $this->getRenderer()
261
  ->setTemplate( 'notices/plugin-failed-to-load' )
262
- ->setRenderVars( [
263
- 'strings' => [
264
- 'summary_title' => 'Perhaps due to a failed upgrade, the Shield plugin failed to load certain component(s) - you should remove the plugin and reinstall.',
265
- 'more_information' => $this->sAdminNoticeError
266
- ]
267
- ] )
268
  ->display();
269
  }
270
 
@@ -291,26 +306,14 @@ class Controller extends DynPropertiesClass {
291
  public function onWpDeactivatePlugin() {
292
  do_action( $this->prefix( 'pre_deactivate_plugin' ) );
293
  if ( $this->isPluginAdmin() ) {
294
-
295
- $this->plugin_deactivating = true;
296
  do_action( $this->prefix( 'deactivate_plugin' ) );
297
-
298
- ( new PluginDeactivate() )
299
- ->setCon( $this )
300
- ->execute();
301
-
302
  if ( apply_filters( $this->prefix( 'delete_on_deactivate' ), false ) ) {
303
- $this->deletePlugin();
 
304
  }
305
  }
306
- }
307
-
308
- public function deletePlugin() {
309
- $this->plugin_deleting = true;
310
- do_action( $this->prefix( 'delete_plugin' ) );
311
- ( new Plugin\PluginDelete() )
312
- ->setCon( $this )
313
- ->execute();
314
  }
315
 
316
  public function onWpActivatePlugin() {
@@ -333,6 +336,15 @@ class Controller extends DynPropertiesClass {
333
  return !empty( $this->getPluginCachePath() );
334
  }
335
 
 
 
 
 
 
 
 
 
 
336
  protected function doRegisterHooks() {
337
  register_deactivation_hook( $this->getRootFile(), [ $this, 'onWpDeactivatePlugin' ] );
338
 
@@ -358,13 +370,13 @@ class Controller extends DynPropertiesClass {
358
  add_filter( 'wp_privacy_personal_data_erasers', [ $this, 'onWpPrivacyRegisterEraser' ] );
359
 
360
  /**
361
- * Support for WP-CLI and it marks the cli as plugin admin
362
  */
363
- add_filter( $this->prefix( 'bypass_is_plugin_admin' ), function ( $byPass ) {
364
  if ( Services::WpGeneral()->isWpCli() && $this->isPremiumActive() ) {
365
- $byPass = true;
366
  }
367
- return $byPass;
368
  }, PHP_INT_MAX );
369
  }
370
 
@@ -465,8 +477,17 @@ class Controller extends DynPropertiesClass {
465
  return $ID;
466
  }
467
 
 
 
 
 
 
 
 
 
 
 
468
  public function onWpLoaded() {
469
- $this->getSiteInstallationId();
470
  $this->getAdminNotices();
471
  $this->initCrons();
472
  ( new Shield\Controller\Assets\Enqueue() )
@@ -485,13 +506,15 @@ class Controller extends DynPropertiesClass {
485
 
486
  protected function initCrons() {
487
  $this->cron_hourly = ( new Shield\Crons\HourlyCron() )->setCon( $this );
488
- $this->cron_hourly->execute();
489
  $this->cron_daily = ( new Shield\Crons\DailyCron() )->setCon( $this );
490
- $this->cron_daily->execute();
491
 
492
- ( new Shield\Utilities\Htaccess\RootHtaccess() )
493
- ->setCon( $this )
494
- ->execute();
 
 
495
  }
496
 
497
  /**
@@ -511,12 +534,8 @@ class Controller extends DynPropertiesClass {
511
  /**
512
  * @param string $action
513
  * @return array
514
- * @throws \Exception
515
  */
516
- public function getNonceActionData( string $action = '' ) :array {
517
- if ( empty( $action ) ) {
518
- throw new \Exception( 'Empty actions are not allowed.' );
519
- }
520
  return [
521
  'action' => $this->prefix(), //wp ajax doesn't work without this.
522
  'exec' => $action,
@@ -542,15 +561,15 @@ class Controller extends DynPropertiesClass {
542
  }
543
 
544
  /**
545
- * @param array $actionLinks
546
  * @return array
547
  */
548
- public function onWpPluginActionLinks( $actionLinks ) {
549
 
550
  if ( $this->isValidAdminArea() ) {
551
 
552
- if ( array_key_exists( 'edit', $actionLinks ) ) {
553
- unset( $actionLinks[ 'edit' ] );
554
  }
555
 
556
  $links = $this->cfg->action_links[ 'add' ];
@@ -590,14 +609,14 @@ class Controller extends DynPropertiesClass {
590
  $sLink = sprintf( '<span style="font-weight: bold;">%s</span>', $sLink );
591
  }
592
 
593
- $actionLinks = array_merge(
594
  [ $this->prefix( sanitize_key( $aLink[ 'name' ] ) ) => $sLink ],
595
- $actionLinks
596
  );
597
  }
598
  }
599
  }
600
- return $actionLinks;
601
  }
602
 
603
  /**
@@ -624,12 +643,12 @@ class Controller extends DynPropertiesClass {
624
  $DB = Services::WpDb();
625
  foreach ( $reqs as $shieldVer => $verReqs ) {
626
  $toHide = version_compare( $updates->response[ $file ]->new_version, $shieldVer, '>=' )
627
- && (
628
  !Services::Data()->getPhpVersionIsAtLeast( $verReqs[ 'php' ] )
629
  || !Services::WpGeneral()->getWordpressIsAtLeastVersion( $verReqs[ 'wp' ] )
630
  || ( !empty( $verReqs[ 'mysql' ] ) &&
631
  version_compare( $DB->loadWpdb()->db_version(), $verReqs[ 'mysql' ], '<' ) )
632
- );
633
  if ( $toHide ) {
634
  unset( $updates->response[ $file ] );
635
  break;
@@ -758,6 +777,7 @@ class Controller extends DynPropertiesClass {
758
  }
759
 
760
  public function onWpShutdown() {
 
761
  do_action( $this->prefix( 'pre_plugin_shutdown' ) );
762
  do_action( $this->prefix( 'plugin_shutdown' ) );
763
  $this->saveCurrentPluginControllerOptions();
@@ -907,7 +927,7 @@ class Controller extends DynPropertiesClass {
907
  if ( did_action( 'init' ) && !isset( $this->user_can_base_permissions ) ) {
908
  $this->user_can_base_permissions = current_user_can( $this->getBasePermissions() );
909
  }
910
- return $this->user_can_base_permissions;
911
  }
912
 
913
  public function getOptionStoragePrefix() :string {
@@ -927,15 +947,23 @@ class Controller extends DynPropertiesClass {
927
  return empty( $labels[ 'Name' ] ) ? $this->getCfgProperty( 'human_name' ) : $labels[ 'Name' ];
928
  }
929
 
 
 
 
 
930
  public function getIsPage_PluginAdmin() :bool {
931
  return strpos( Services::WpGeneral()->getCurrentWpAdminPage(), $this->getPluginPrefix() ) === 0;
932
  }
933
 
 
 
 
 
934
  public function getIsResetPlugin() :bool {
935
  if ( !isset( $this->plugin_reset ) ) {
936
- $this->plugin_reset = Services::WpFs()->isFile( $this->paths->forFlag( 'reset' ) );
937
  }
938
- return $this->plugin_reset;
939
  }
940
 
941
  public function getIsWpmsNetworkAdminOnly() :bool {
@@ -962,21 +990,75 @@ class Controller extends DynPropertiesClass {
962
  return $this->getModule_Plugin()->getUrl_AdminPage();
963
  }
964
 
965
- /**
966
- * @deprecated 12.0
967
- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
968
  public function getPath_ConfigFile( string $slug ) :string {
969
- return $this->paths->forModuleConfig( $slug );
970
  }
971
 
972
  public function getPath_Languages() :string {
973
  return trailingslashit( path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'languages' ) ) );
974
  }
975
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
976
  public function getPath_Templates() :string {
977
  return path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'templates' ) ).'/';
978
  }
979
 
 
 
 
 
 
 
 
980
  private function getPathPluginSpec( bool $asJSON = true ) :string {
981
  return path_join( $this->getRootDir(), $asJSON ? 'plugin.json' : 'plugin-spec.php' );
982
  }
@@ -999,10 +1081,18 @@ class Controller extends DynPropertiesClass {
999
  return $this->root_file;
1000
  }
1001
 
 
 
 
 
1002
  public function getTextDomain() :string {
1003
  return $this->getCfgProperty( 'text_domain' );
1004
  }
1005
 
 
 
 
 
1006
  public function getVersion() :string {
1007
  return $this->getCfgProperty( 'version' );
1008
  }
@@ -1022,6 +1112,29 @@ class Controller extends DynPropertiesClass {
1022
  return empty( $action ) ? '' : $action;
1023
  }
1024
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1025
  public function isPremiumExtensionsEnabled() :bool {
1026
  return (bool)$this->getCfgProperty( 'enable_premium' );
1027
  }
@@ -1046,10 +1159,26 @@ class Controller extends DynPropertiesClass {
1046
  remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
1047
  }
1048
 
 
 
 
 
 
 
 
 
 
 
1049
  private function getConfigStoreKey() :string {
1050
  return 'aptoweb_controller_'.substr( md5( get_class() ), 0, 6 );
1051
  }
1052
 
 
 
 
 
 
 
1053
  public function clearSession() {
1054
  Services::Response()->cookieDelete( $this->getSessionCookieID() );
1055
  self::$sSessionId = null;
@@ -1099,11 +1228,6 @@ class Controller extends DynPropertiesClass {
1099
  return self::$sSessionId;
1100
  }
1101
 
1102
- /**
1103
- * @param bool $setIfNeeded
1104
- * @return string
1105
- * @deprecated 12.0
1106
- */
1107
  public function getUniqueRequestId( bool $setIfNeeded = false ) :string {
1108
  if ( !isset( self::$sRequestId ) ) {
1109
  self::$sRequestId = md5(
@@ -1113,14 +1237,8 @@ class Controller extends DynPropertiesClass {
1113
  return self::$sRequestId;
1114
  }
1115
 
1116
- /**
1117
- * @return string
1118
- * @deprecated 12
1119
- */
1120
  public function getShortRequestId() :string {
1121
- $req = Services::Request();
1122
- /** @deprecated 12.0 */
1123
- return substr( method_exists( $req, 'getID' ) ? $req->getID() : $this->getUniqueRequestId(), 0, 10 );
1124
  }
1125
 
1126
  public function hasSessionId() :bool {
@@ -1221,10 +1339,6 @@ class Controller extends DynPropertiesClass {
1221
  return $this->getModule( 'comms' );
1222
  }
1223
 
1224
- public function getModule_Data() :Shield\Modules\Data\ModCon {
1225
- return $this->getModule( 'data' );
1226
- }
1227
-
1228
  public function getModule_Email() :Shield\Modules\Email\ModCon {
1229
  return $this->getModule( 'email' );
1230
  }
@@ -1262,7 +1376,7 @@ class Controller extends DynPropertiesClass {
1262
  }
1263
 
1264
  public function getModule_Plugin() :Shield\Modules\Plugin\ModCon {
1265
- return $this->loadCorePluginFeatureHandler();
1266
  }
1267
 
1268
  public function getModule_Reporting() :Shield\Modules\Reporting\ModCon {
@@ -1296,7 +1410,7 @@ class Controller extends DynPropertiesClass {
1296
  */
1297
  public function loadFeatureHandler( array $modProps ) {
1298
  $modSlug = $modProps[ 'slug' ];
1299
- $mod = $this->modules[ $modSlug ] ?? null;
1300
  if ( $mod instanceof Shield\Modules\Base\ModCon ) {
1301
  return $mod;
1302
  }
@@ -1314,17 +1428,24 @@ class Controller extends DynPropertiesClass {
1314
  }
1315
 
1316
  $modName = $modProps[ 'namespace' ];
 
1317
 
1318
  $className = $this->getModulesNamespace().sprintf( '\\%s\\ModCon', $modName );
 
 
 
 
 
1319
  if ( !class_exists( $className ) ) {
1320
- // All this to prevent fatal errors if the plugin doesn't install/upgrade correctly
1321
- throw new \Exception( sprintf( 'Class "%s" is missing', $className ) );
1322
  }
1323
 
 
 
1324
  $modules = $this->modules;
1325
- $modules[ $modSlug ] = new $className( $this, $modProps );
1326
  $this->modules = $modules;
1327
-
1328
  return $this->modules[ $modSlug ];
1329
  }
1330
 
@@ -1337,7 +1458,7 @@ class Controller extends DynPropertiesClass {
1337
 
1338
  /**
1339
  * @param \WP_User $user
1340
- * @return Shield\Users\ShieldUserMeta|null
1341
  */
1342
  public function getUserMeta( $user ) {
1343
  $meta = null;
@@ -1411,7 +1532,7 @@ class Controller extends DynPropertiesClass {
1411
  * @param int $page
1412
  * @return array
1413
  */
1414
- public function wpPrivacyExport( $email, $page = 1 ) :array {
1415
 
1416
  $valid = Services::Data()->validEmail( $email )
1417
  && ( 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\Services\Services;
8
  use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
9
 
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
  * @property string[] $reqs_not_met
37
  */
38
  class Controller extends DynPropertiesClass {
39
 
40
+ /**
41
+ * @var \stdClass
42
+ */
43
+ private static $oControllerOptions;
44
+
45
  /**
46
  * @var Controller
47
  */
48
  public static $oInstance;
49
 
50
+ /**
51
+ * @var array
52
+ */
53
+ private $aRequirementsMessages;
54
+
55
  /**
56
  * @var string
57
  */
67
  */
68
  protected $sAdminNoticeError = '';
69
 
70
+ /**
71
+ * @var Shield\Modules\BaseShield\ModCon[]
72
+ */
73
+ protected $aModules;
74
+
75
  /**
76
  * @var Shield\Utilities\AdminNotices\Controller
77
  */
82
  */
83
  private $oEventsService;
84
 
85
+ /**
86
+ * @param string $event
87
+ * @param array $meta
88
+ * @return $this
89
+ */
90
+ public function fireEvent( string $event, $meta = [] ) :self {
91
+ $this->loadEventsService()->fireEvent( $event, is_array( $meta ) ? $meta : [] );
92
  return $this;
93
  }
94
 
95
+ /**
96
+ * @return array
97
+ */
98
+ public function getAllEvents() {
99
+ return $this->loadEventsService()->getEvents();
100
+ }
101
+
102
  /**
103
  * @return Shield\Modules\Events\Lib\EventsService
104
  */
155
 
156
  switch ( $key ) {
157
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  case 'cfg':
159
  if ( !$val instanceof Config\ConfigVO ) {
160
  $val = $this->loadConfig();
271
  }
272
 
273
  public function adminNoticePluginFailedToLoad() {
274
+ $aDisplayData = [
275
+ 'strings' => [
276
+ 'summary_title' => 'Perhaps due to a failed upgrade, the Shield plugin failed to load certain component(s) - you should remove the plugin and reinstall.',
277
+ 'more_information' => $this->sAdminNoticeError
278
+ ]
279
+ ];
280
  $this->getRenderer()
281
  ->setTemplate( 'notices/plugin-failed-to-load' )
282
+ ->setRenderVars( $aDisplayData )
 
 
 
 
 
283
  ->display();
284
  }
285
 
306
  public function onWpDeactivatePlugin() {
307
  do_action( $this->prefix( 'pre_deactivate_plugin' ) );
308
  if ( $this->isPluginAdmin() ) {
 
 
309
  do_action( $this->prefix( 'deactivate_plugin' ) );
310
+ $this->plugin_deactivating = true;
 
 
 
 
311
  if ( apply_filters( $this->prefix( 'delete_on_deactivate' ), false ) ) {
312
+ $this->plugin_deleting = true;
313
+ do_action( $this->prefix( 'delete_plugin' ) );
314
  }
315
  }
316
+ $this->deleteCronJobs();
 
 
 
 
 
 
 
317
  }
318
 
319
  public function onWpActivatePlugin() {
336
  return !empty( $this->getPluginCachePath() );
337
  }
338
 
339
+ /**
340
+ * @deprecated 11.4
341
+ */
342
+ private function buildPluginCacheDir() :string {
343
+ return ( new Shield\Utilities\CacheDir() )
344
+ ->setCon( $this )
345
+ ->build();
346
+ }
347
+
348
  protected function doRegisterHooks() {
349
  register_deactivation_hook( $this->getRootFile(), [ $this, 'onWpDeactivatePlugin' ] );
350
 
370
  add_filter( 'wp_privacy_personal_data_erasers', [ $this, 'onWpPrivacyRegisterEraser' ] );
371
 
372
  /**
373
+ * Support for WP-CLI and it marks the cli as complete plugin admin
374
  */
375
+ add_filter( $this->prefix( 'bypass_is_plugin_admin' ), function ( $bByPass ) {
376
  if ( Services::WpGeneral()->isWpCli() && $this->isPremiumActive() ) {
377
+ $bByPass = true;
378
  }
379
+ return $bByPass;
380
  }, PHP_INT_MAX );
381
  }
382
 
477
  return $ID;
478
  }
479
 
480
+ /**
481
+ * TODO: Use to set ID after license verify where applicable
482
+ * @param string $ID
483
+ */
484
+ public function setSiteInstallID( $ID ) {
485
+ if ( !empty( $ID ) && ( \Ramsey\Uuid\Uuid::isValid( $ID ) ) ) {
486
+ Services::WpGeneral()->updateOption( $this->prefixOption( 'install_id' ), $ID );
487
+ }
488
+ }
489
+
490
  public function onWpLoaded() {
 
491
  $this->getAdminNotices();
492
  $this->initCrons();
493
  ( new Shield\Controller\Assets\Enqueue() )
506
 
507
  protected function initCrons() {
508
  $this->cron_hourly = ( new Shield\Crons\HourlyCron() )->setCon( $this );
509
+ $this->cron_hourly->run();
510
  $this->cron_daily = ( new Shield\Crons\DailyCron() )->setCon( $this );
511
+ $this->cron_daily->run();
512
 
513
+ if ( Services::WpGeneral()->isCron() ) {
514
+ ( new Shield\Utilities\Htaccess\RootHtaccess() )
515
+ ->setCon( $this )
516
+ ->execute();
517
+ }
518
  }
519
 
520
  /**
534
  /**
535
  * @param string $action
536
  * @return array
 
537
  */
538
+ public function getNonceActionData( $action = '' ) {
 
 
 
539
  return [
540
  'action' => $this->prefix(), //wp ajax doesn't work without this.
541
  'exec' => $action,
561
  }
562
 
563
  /**
564
+ * @param array $aActionLinks
565
  * @return array
566
  */
567
+ public function onWpPluginActionLinks( $aActionLinks ) {
568
 
569
  if ( $this->isValidAdminArea() ) {
570
 
571
+ if ( array_key_exists( 'edit', $aActionLinks ) ) {
572
+ unset( $aActionLinks[ 'edit' ] );
573
  }
574
 
575
  $links = $this->cfg->action_links[ 'add' ];
609
  $sLink = sprintf( '<span style="font-weight: bold;">%s</span>', $sLink );
610
  }
611
 
612
+ $aActionLinks = array_merge(
613
  [ $this->prefix( sanitize_key( $aLink[ 'name' ] ) ) => $sLink ],
614
+ $aActionLinks
615
  );
616
  }
617
  }
618
  }
619
+ return $aActionLinks;
620
  }
621
 
622
  /**
643
  $DB = Services::WpDb();
644
  foreach ( $reqs as $shieldVer => $verReqs ) {
645
  $toHide = version_compare( $updates->response[ $file ]->new_version, $shieldVer, '>=' )
646
+ && (
647
  !Services::Data()->getPhpVersionIsAtLeast( $verReqs[ 'php' ] )
648
  || !Services::WpGeneral()->getWordpressIsAtLeastVersion( $verReqs[ 'wp' ] )
649
  || ( !empty( $verReqs[ 'mysql' ] ) &&
650
  version_compare( $DB->loadWpdb()->db_version(), $verReqs[ 'mysql' ], '<' ) )
651
+ );
652
  if ( $toHide ) {
653
  unset( $updates->response[ $file ] );
654
  break;
777
  }
778
 
779
  public function onWpShutdown() {
780
+ $this->getSiteInstallationId();
781
  do_action( $this->prefix( 'pre_plugin_shutdown' ) );
782
  do_action( $this->prefix( 'plugin_shutdown' ) );
783
  $this->saveCurrentPluginControllerOptions();
927
  if ( did_action( 'init' ) && !isset( $this->user_can_base_permissions ) ) {
928
  $this->user_can_base_permissions = current_user_can( $this->getBasePermissions() );
929
  }
930
+ return (bool)$this->user_can_base_permissions;
931
  }
932
 
933
  public function getOptionStoragePrefix() :string {
947
  return empty( $labels[ 'Name' ] ) ? $this->getCfgProperty( 'human_name' ) : $labels[ 'Name' ];
948
  }
949
 
950
+ public function isLoggingEnabled() :bool {
951
+ return (bool)$this->getCfgProperty( 'logging_enabled' );
952
+ }
953
+
954
  public function getIsPage_PluginAdmin() :bool {
955
  return strpos( Services::WpGeneral()->getCurrentWpAdminPage(), $this->getPluginPrefix() ) === 0;
956
  }
957
 
958
+ public function getIsPage_PluginMainDashboard() :bool {
959
+ return Services::WpGeneral()->getCurrentWpAdminPage() === $this->getPluginPrefix();
960
+ }
961
+
962
  public function getIsResetPlugin() :bool {
963
  if ( !isset( $this->plugin_reset ) ) {
964
+ $this->plugin_reset = (bool)Services::WpFs()->isFile( $this->paths->forFlag( 'reset' ) );
965
  }
966
+ return (bool)$this->plugin_reset;
967
  }
968
 
969
  public function getIsWpmsNetworkAdminOnly() :bool {
990
  return $this->getModule_Plugin()->getUrl_AdminPage();
991
  }
992
 
993
+ public function getPath_Assets( string $asset = '' ) :string {
994
+ $base = path_join( $this->getRootDir(), $this->cfg->paths[ 'assets' ] );
995
+ return empty( $asset ) ? $base : path_join( $base, ltrim( $asset, '/' ) );
996
+ }
997
+
998
+ public function getPath_AssetCss( string $asset = '' ) :string {
999
+ return $this->getPath_Assets( 'css/'.$asset );
1000
+ }
1001
+
1002
+ public function getPath_AssetJs( string $asset = '' ) :string {
1003
+ return $this->getPath_Assets( 'js/'.$asset );
1004
+ }
1005
+
1006
+ public function getPath_AssetImage( string $asset = '' ) :string {
1007
+ return $this->getPath_Assets( 'images/'.$asset );
1008
+ }
1009
+
1010
  public function getPath_ConfigFile( string $slug ) :string {
1011
+ return $this->getPath_SourceFile( sprintf( 'config/feature-%s.php', $slug ) );
1012
  }
1013
 
1014
  public function getPath_Languages() :string {
1015
  return trailingslashit( path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'languages' ) ) );
1016
  }
1017
 
1018
+ public function getPath_LibFile( string $libFile ) :string {
1019
+ return $this->getPath_SourceFile( 'lib/'.$libFile );
1020
+ }
1021
+
1022
+ public function getPath_Autoload() :string {
1023
+ return $this->getPath_SourceFile( $this->getPluginSpec_Path( 'autoload' ) );
1024
+ }
1025
+
1026
+ /**
1027
+ * @return string
1028
+ * @throws \Exception
1029
+ */
1030
+ public function getPath_PluginCache() :string {
1031
+ $cacheSlug = $this->getPluginSpec_Path( 'cache' );
1032
+ if ( empty( $cacheSlug ) ) {
1033
+ throw new \Exception( 'Cache dir slug was empty' );
1034
+ }
1035
+ return path_join( WP_CONTENT_DIR, $cacheSlug );
1036
+ }
1037
+
1038
+ /**
1039
+ * @param string $sourceFile
1040
+ * @return string
1041
+ * @deprecated 10.3
1042
+ */
1043
+ public function getPath_SourceFile( string $sourceFile ) :string {
1044
+ if ( isset( $this->paths ) ) {
1045
+ return $this->paths->forSource( $sourceFile );
1046
+ }
1047
+ $base = path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'source' ) );
1048
+ return empty( $sourceFile ) ? $base : path_join( $base, $sourceFile );
1049
+ }
1050
+
1051
  public function getPath_Templates() :string {
1052
  return path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'templates' ) ).'/';
1053
  }
1054
 
1055
+ public function getPath_TemplatesFile( string $template ) :string {
1056
+ if ( isset( $this->paths ) ) {
1057
+ return $this->paths->forTemplate( $template );
1058
+ }
1059
+ return path_join( $this->getPath_Templates(), $template );
1060
+ }
1061
+
1062
  private function getPathPluginSpec( bool $asJSON = true ) :string {
1063
  return path_join( $this->getRootDir(), $asJSON ? 'plugin.json' : 'plugin-spec.php' );
1064
  }
1081
  return $this->root_file;
1082
  }
1083
 
1084
+ public function getReleaseTimestamp() :int {
1085
+ return $this->getCfgProperty( 'release_timestamp' );
1086
+ }
1087
+
1088
  public function getTextDomain() :string {
1089
  return $this->getCfgProperty( 'text_domain' );
1090
  }
1091
 
1092
+ public function getBuild() :string {
1093
+ return $this->getCfgProperty( 'build' );
1094
+ }
1095
+
1096
  public function getVersion() :string {
1097
  return $this->getCfgProperty( 'version' );
1098
  }
1112
  return empty( $action ) ? '' : $action;
1113
  }
1114
 
1115
+ /**
1116
+ * @return \stdClass
1117
+ */
1118
+ public function getPluginControllerOptions() {
1119
+ return self::$oControllerOptions;
1120
+ }
1121
+
1122
+ protected function deleteCronJobs() {
1123
+ $WPCron = Services::WpCron();
1124
+ $crons = $WPCron->getCrons();
1125
+
1126
+ $pattern = sprintf( '#^(%s|%s)#', $this->getParentSlug(), $this->getPluginSlug() );
1127
+ foreach ( $crons as $cron ) {
1128
+ if ( is_array( $crons ) ) {
1129
+ foreach ( $cron as $key => $cronEntry ) {
1130
+ if ( is_string( $key ) && preg_match( $pattern, $key ) ) {
1131
+ $WPCron->deleteCronJob( $key );
1132
+ }
1133
+ }
1134
+ }
1135
+ }
1136
+ }
1137
+
1138
  public function isPremiumExtensionsEnabled() :bool {
1139
  return (bool)$this->getCfgProperty( 'enable_premium' );
1140
  }
1159
  remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
1160
  }
1161
 
1162
+ /**
1163
+ * This should always be used to modify or delete the options as it works within the Admin Access Permission system.
1164
+ * @param \stdClass|bool $oOptions
1165
+ * @return $this
1166
+ */
1167
+ protected function setPluginControllerOptions( $oOptions ) {
1168
+ self::$oControllerOptions = $oOptions;
1169
+ return $this;
1170
+ }
1171
+
1172
  private function getConfigStoreKey() :string {
1173
  return 'aptoweb_controller_'.substr( md5( get_class() ), 0, 6 );
1174
  }
1175
 
1176
+ public function deactivateSelf() {
1177
+ if ( $this->isPluginAdmin() && function_exists( 'deactivate_plugins' ) ) {
1178
+ deactivate_plugins( [ $this->base_file ] );
1179
+ }
1180
+ }
1181
+
1182
  public function clearSession() {
1183
  Services::Response()->cookieDelete( $this->getSessionCookieID() );
1184
  self::$sSessionId = null;
1228
  return self::$sSessionId;
1229
  }
1230
 
 
 
 
 
 
1231
  public function getUniqueRequestId( bool $setIfNeeded = false ) :string {
1232
  if ( !isset( self::$sRequestId ) ) {
1233
  self::$sRequestId = md5(
1237
  return self::$sRequestId;
1238
  }
1239
 
 
 
 
 
1240
  public function getShortRequestId() :string {
1241
+ return substr( $this->getUniqueRequestId(), 0, 10 );
 
 
1242
  }
1243
 
1244
  public function hasSessionId() :bool {
1339
  return $this->getModule( 'comms' );
1340
  }
1341
 
 
 
 
 
1342
  public function getModule_Email() :Shield\Modules\Email\ModCon {
1343
  return $this->getModule( 'email' );
1344
  }
1376
  }
1377
 
1378
  public function getModule_Plugin() :Shield\Modules\Plugin\ModCon {
1379
+ return $this->getModule( 'plugin' );
1380
  }
1381
 
1382
  public function getModule_Reporting() :Shield\Modules\Reporting\ModCon {
1410
  */
1411
  public function loadFeatureHandler( array $modProps ) {
1412
  $modSlug = $modProps[ 'slug' ];
1413
+ $mod = isset( $this->modules[ $modSlug ] ) ? $this->modules[ $modSlug ] : null;
1414
  if ( $mod instanceof Shield\Modules\Base\ModCon ) {
1415
  return $mod;
1416
  }
1428
  }
1429
 
1430
  $modName = $modProps[ 'namespace' ];
1431
+ $sOptionsVarName = sprintf( 'oFeatureHandler%s', $modName ); // e.g. oFeatureHandlerPlugin
1432
 
1433
  $className = $this->getModulesNamespace().sprintf( '\\%s\\ModCon', $modName );
1434
+ if ( !@class_exists( $className ) ) {
1435
+ $className = sprintf( '%s_FeatureHandler_%s', strtoupper( $this->getPluginPrefix( '_' ) ), $modName );
1436
+ }
1437
+
1438
+ // All this to prevent fatal errors if the plugin doesn't install/upgrade correctly
1439
  if ( !class_exists( $className ) ) {
1440
+ $sMessage = sprintf( 'Class "%s" is missing', $className );
1441
+ throw new \Exception( $sMessage );
1442
  }
1443
 
1444
+ $this->{$sOptionsVarName} = new $className( $this, $modProps );
1445
+
1446
  $modules = $this->modules;
1447
+ $modules[ $modSlug ] = $this->{$sOptionsVarName};
1448
  $this->modules = $modules;
 
1449
  return $this->modules[ $modSlug ];
1450
  }
1451
 
1458
 
1459
  /**
1460
  * @param \WP_User $user
1461
+ * @return Shield\Users\ShieldUserMeta|mixed
1462
  */
1463
  public function getUserMeta( $user ) {
1464
  $meta = null;
1532
  * @param int $page
1533
  * @return array
1534
  */
1535
+ public function wpPrivacyExport( $email, $page = 1 ) {
1536
 
1537
  $valid = Services::Data()->validEmail( $email )
1538
  && ( Services::WpUsers()->getUserByEmail( $email ) instanceof \WP_User );
src/lib/src/Controller/Plugin/PluginDeactivate.php DELETED
@@ -1,34 +0,0 @@
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 DELETED
@@ -1,62 +0,0 @@
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/Controller/Utilities/Upgrade.php CHANGED
@@ -13,33 +13,18 @@ class Upgrade {
13
 
14
  protected function canRun() :bool {
15
  $con = $this->getCon();
16
- return $con->cfg->previous_version !== $con->getVersion()
17
- && !$this->isAlreadyUpgrading();
18
- }
19
-
20
- protected function isAlreadyUpgrading() :bool {
21
- $FS = Services::WpFs();
22
- $upgradeFlag = $this->getCon()->getPluginCachePath( 'upgrading.flag' );
23
- return !empty( $upgradeFlag )
24
- && $FS->isFile( $upgradeFlag )
25
- && ( Services::Request()->ts() - 600 ) < $FS->getModifiedTime( $upgradeFlag );
26
  }
27
 
28
  protected function run() {
29
  $con = $this->getCon();
30
 
31
- Services::WpFs()->touch( $con->getPluginCachePath( 'upgrading.flag' ), Services::Request()->ts() );
32
-
33
  $this->upgradeModules();
34
  do_action( $con->prefix( 'plugin_shutdown' ), function () {
35
  $this->deleteOldModConfigs();
36
  } );
37
 
38
  $con->cfg->previous_version = $con->getVersion();
39
-
40
- add_action( $con->prefix( 'plugin_shutdown' ), function () {
41
- Services::WpFs()->deleteFile( $this->getCon()->getPluginCachePath( 'upgrading.flag' ) );
42
- } );
43
  }
44
 
45
  private function deleteOldModConfigs() {
13
 
14
  protected function canRun() :bool {
15
  $con = $this->getCon();
16
+ return $con->cfg->previous_version !== $con->getVersion();
 
 
 
 
 
 
 
 
 
17
  }
18
 
19
  protected function run() {
20
  $con = $this->getCon();
21
 
 
 
22
  $this->upgradeModules();
23
  do_action( $con->prefix( 'plugin_shutdown' ), function () {
24
  $this->deleteOldModConfigs();
25
  } );
26
 
27
  $con->cfg->previous_version = $con->getVersion();
 
 
 
 
28
  }
29
 
30
  private function deleteOldModConfigs() {
src/lib/src/Crons/BaseCron.php CHANGED
@@ -12,9 +12,6 @@ abstract class BaseCron {
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
  }
12
  use Shield\Crons\StandardCron;
13
  use PluginControllerConsumer;
14
 
 
 
 
15
  public function run() {
16
  $this->setupCron();
17
  }
src/lib/src/Databases/AuditTrail/Handler.php CHANGED
@@ -3,7 +3,14 @@
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
  }
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
  }
src/lib/src/Databases/Base/Traits/Select_IPTable.php CHANGED
@@ -12,12 +12,12 @@ trait Select_IPTable {
12
  public function getDistinctIps() :array {
13
  $ips = $this->getDistinctForColumn( 'ip' );
14
  if ( $this->getDbH()->getTableSchema()->is_ip_binary ) {
15
- $ips = array_filter( array_map(
16
  function ( $binaryIP ) {
17
- return empty( $binaryIP ) ? '' : inet_ntop( $binaryIP );
18
  },
19
  $ips
20
- ) );
21
  }
22
  return IpListSort::Sort( $ips );
23
  }
12
  public function getDistinctIps() :array {
13
  $ips = $this->getDistinctForColumn( 'ip' );
14
  if ( $this->getDbH()->getTableSchema()->is_ip_binary ) {
15
+ $ips = array_map(
16
  function ( $binaryIP ) {
17
+ return inet_ntop( $binaryIP );
18
  },
19
  $ips
20
+ );
21
  }
22
  return IpListSort::Sort( $ips );
23
  }
src/lib/src/Databases/Base/Update.php CHANGED
@@ -25,10 +25,6 @@ class Update extends Insert {
25
  return is_array( $this->aUpdateWheres ) ? $this->aUpdateWheres : [];
26
  }
27
 
28
- public function setSoftDeleted() {
29
- return $this->setUpdateData( [ 'deleted_at' => Services::Request()->ts() ] );
30
- }
31
-
32
  /**
33
  * @param array $data
34
  * @return $this
@@ -75,8 +71,7 @@ class Update extends Insert {
75
  $success = true;
76
  }
77
  else {
78
- if ( $this->getDbH()->getTableSchema()->hasColumn( 'updated_at' )
79
- && !isset( $updateData[ 'updated_at' ] ) ) {
80
  $updateData[ 'updated_at' ] = Services::Request()->ts();
81
  }
82
  if ( $this->updateById( $entry->id, $updateData ) ) {
@@ -107,10 +102,10 @@ class Update extends Insert {
107
 
108
  public function query() {
109
  return (bool)Services::WpDb()
110
- ->updateRowsFromTableWhere(
111
- $this->getDbH()->getTable(),
112
- $this->getUpdateData(),
113
- $this->getUpdateWheres()
114
- );
115
  }
116
  }
25
  return is_array( $this->aUpdateWheres ) ? $this->aUpdateWheres : [];
26
  }
27
 
 
 
 
 
28
  /**
29
  * @param array $data
30
  * @return $this
71
  $success = true;
72
  }
73
  else {
74
+ if ( $this->getDbH()->getTableSchema()->hasColumn( 'updated_at' ) && !isset( $updateData[ 'updated_at' ] ) ) {
 
75
  $updateData[ 'updated_at' ] = Services::Request()->ts();
76
  }
77
  if ( $this->updateById( $entry->id, $updateData ) ) {
102
 
103
  public function query() {
104
  return (bool)Services::WpDb()
105
+ ->updateRowsFromTableWhere(
106
+ $this->getDbH()->getTable(),
107
+ $this->getUpdateData(),
108
+ $this->getUpdateWheres()
109
+ );
110
  }
111
  }
src/lib/src/Databases/BotSignals/Common.php CHANGED
@@ -2,30 +2,24 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\BotSignals;
4
 
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
  trait Common {
8
 
9
  /**
10
- * COPIED FROM PLUGIN CORE
11
- * @param string $ip
12
  * @return $this
13
  */
14
- public function filterByIPHuman( string $ip ) {
15
- $rightSide = null;
16
- if ( empty( $ip ) ) {
17
- $rightSide = "''";
18
- }
19
- elseif ( Services::IP()->isValidIp( $ip ) || Services::IP()->isValidIp( inet_ntop( $ip ) ) ) {
20
- $rightSide = sprintf( "INET6_ATON('%s')", Services::IP()->isValidIp( $ip ) ? $ip : inet_ntop( $ip ) );
21
- }
22
-
23
- if ( !empty( $rightSide ) ) {
24
- $this->addRawWhere( [ 'ip', '=', $rightSide ] );
25
  }
26
  return $this;
27
  }
28
 
 
 
 
 
29
  /**
30
  * Will test whether the Binary IP can be converted back before applying filter.
31
  * @param mixed $bBinaryIp - IP has already been converted using inet_pton
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\BotSignals;
4
 
 
 
5
  trait Common {
6
 
7
  /**
8
+ * Will test whether the Binary IP can be converted back before applying filter.
9
+ * @param mixed $binaryIp - IP has already been converted using inet_pton
10
  * @return $this
11
  */
12
+ public function filterByIP( $binaryIp ) {
13
+ if ( inet_ntop( $binaryIp ) !== false ) {
14
+ $this->addWhereEquals( 'ip', $binaryIp );
 
 
 
 
 
 
 
 
15
  }
16
  return $this;
17
  }
18
 
19
+ public function filterByIPHuman( string $ip ) {
20
+ return $this->filterByIP( inet_pton( $ip ) );
21
+ }
22
+
23
  /**
24
  * Will test whether the Binary IP can be converted back before applying filter.
25
  * @param mixed $bBinaryIp - IP has already been converted using inet_pton
src/lib/src/Databases/BotSignals/Handler.php CHANGED
@@ -4,4 +4,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\BotSignals;
4
 
5
  class Handler extends \FernleafSystems\Wordpress\Plugin\Shield\Databases\Base\Handler {
6
 
 
 
 
7
  }
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/BotSignals/Select.php CHANGED
@@ -8,4 +8,14 @@ class Select extends Base\Select {
8
 
9
  use Common;
10
  use Base\Traits\Select_IPTable;
 
 
 
 
 
 
 
 
 
 
11
  }
8
 
9
  use Common;
10
  use Base\Traits\Select_IPTable;
11
+
12
+ /**
13
+ * @param string $ip
14
+ * @return EntryVO
15
+ */
16
+ public function byIp( string $ip ) {
17
+ return $this->filterByIP( inet_pton( $ip ) )
18
+ ->setResultsAsVo( true )
19
+ ->first();
20
+ }
21
  }
src/lib/src/Databases/Events/Handler.php CHANGED
@@ -25,7 +25,10 @@ class Handler extends Base\Handler {
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();
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();
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
- $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
  /**
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
  /**
src/lib/src/Databases/GeoIp/BaseGeoIp.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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/IPs/Delete.php CHANGED
@@ -33,6 +33,6 @@ class Delete extends Base\Delete {
33
  $this->filterByIp( $sIp )
34
  ->filterByBlacklist();
35
  }
36
- return $this->hasWheres() && $this->query();
37
  }
38
  }
33
  $this->filterByIp( $sIp )
34
  ->filterByBlacklist();
35
  }
36
+ return $this->hasWheres() ? $this->query() : false;
37
  }
38
  }
src/lib/src/Databases/Scanner/Common.php CHANGED
@@ -7,22 +7,22 @@ use FernleafSystems\Wordpress\Services\Services;
7
  trait Common {
8
 
9
  /**
10
- * @param string $hash
11
  * @return $this
12
  */
13
- public function filterByHash( string $hash ) {
14
- if ( !empty( $hash ) ) {
15
- $this->addWhereEquals( 'hash', $hash );
16
  }
17
  return $this;
18
  }
19
 
20
  /**
21
- * @param string[] $hashes
22
  * @return $this
23
  */
24
- public function filterByHashes( $hashes ) {
25
- return $this->addWhereIn( 'hash', $hashes );
26
  }
27
 
28
  /**
@@ -39,13 +39,6 @@ trait Common {
39
  return $this->addWhereEquals( 'ignored_at', 0 );
40
  }
41
 
42
- /**
43
- * @return $this
44
- */
45
- public function filterByNoRepairAttempted() {
46
- return $this->addWhereEquals( 'attempt_repair_at', 0 );
47
- }
48
-
49
  /**
50
  * @return $this
51
  */
7
  trait Common {
8
 
9
  /**
10
+ * @param string $sHash
11
  * @return $this
12
  */
13
+ public function filterByHash( $sHash ) {
14
+ if ( !empty( $sHash ) ) {
15
+ $this->filterByHashes( [ $sHash ] );
16
  }
17
  return $this;
18
  }
19
 
20
  /**
21
+ * @param string[] $aHashes
22
  * @return $this
23
  */
24
+ public function filterByHashes( $aHashes ) {
25
+ return $this->addWhereIn( 'hash', $aHashes );
26
  }
27
 
28
  /**
39
  return $this->addWhereEquals( 'ignored_at', 0 );
40
  }
41
 
 
 
 
 
 
 
 
42
  /**
43
  * @return $this
44
  */
src/lib/src/Databases/Scanner/Update.php CHANGED
@@ -7,6 +7,40 @@ use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class Update extends Base\Update {
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  /**
11
  * @param EntryVO $entry
12
  * @return bool
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
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 ) {
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 ) :self {
25
  if ( Services::IP()->isValidIp( $ip ) ) {
26
  $this->addWhereEquals( 'ip', trim( $ip ) );
27
  }
src/lib/src/Databases/Traffic/Handler.php CHANGED
@@ -3,7 +3,14 @@
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
  }
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
  }
src/lib/src/Logging/Processors/RequestMetaProcessor.php DELETED
@@ -1,33 +0,0 @@
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' ] = [
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 DELETED
@@ -1,25 +0,0 @@
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 DELETED
@@ -1,36 +0,0 @@
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 DELETED
@@ -1,19 +0,0 @@
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 DELETED
@@ -1,59 +0,0 @@
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,9 +10,14 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
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,18 +25,42 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
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
  }
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
  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
  }
src/lib/src/Modules/AuditTrail/Auditors/Emails.php CHANGED
@@ -11,27 +11,27 @@ class Emails extends Base {
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,18 +39,24 @@ class Emails extends Base {
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
  /**
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
  $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
  /**
src/lib/src/Modules/AuditTrail/Auditors/Plugins.php CHANGED
@@ -11,25 +11,25 @@ class Plugins extends Base {
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,7 +43,7 @@ class Plugins extends Base {
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
  }
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
  if ( strpos( $sAction, $sStub ) === 0 ) {
44
  $this->getCon()->fireEvent(
45
  'plugin_file_edited',
46
+ [ 'audit' => [ 'file' => str_replace( $sStub, '', $sAction ) ] ]
47
  );
48
  }
49
  }
src/lib/src/Modules/AuditTrail/Auditors/Posts.php CHANGED
@@ -12,69 +12,73 @@ class Posts extends Base {
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',
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',
src/lib/src/Modules/AuditTrail/Auditors/Themes.php CHANGED
@@ -6,30 +6,31 @@ 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
  }
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
  }
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
  }
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
- $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
  }
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
  }
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_params' => [
29
- 'user_login' => $user->user_login,
30
  ]
31
  ]
32
  );
@@ -38,9 +38,9 @@ class Users extends Base {
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,28 +52,28 @@ class Users extends Base {
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
  );
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
  $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
  * @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
  );
src/lib/src/Modules/AuditTrail/Auditors/Wordpress.php CHANGED
@@ -12,31 +12,31 @@ class Wordpress extends Base {
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
  );
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
  );
src/lib/src/Modules/AuditTrail/DB/LoadLogs.php DELETED
@@ -1,99 +0,0 @@
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
- const DEFAULT_LIMIT = 10000;
13
- use ModConsumer;
14
- use IpAddressConsumer;
15
-
16
- private $limit = null;
17
-
18
- /**
19
- * @return LogRecord[]
20
- */
21
- public function run() :array {
22
- /** @var ModCon $mod */
23
- $mod = $this->getMod();
24
- $stdKeys = array_flip( array_unique( array_merge(
25
- $mod->getDbH_Logs()
26
- ->getTableSchema()
27
- ->getColumnNames(),
28
- $this->getCon()
29
- ->getModule_Data()
30
- ->getDbH_IPs()
31
- ->getTableSchema()
32
- ->getColumnNames(),
33
- [
34
- 'rid'
35
- ]
36
- ) ) );
37
-
38
- $results = [];
39
-
40
- foreach ( $this->selectRaw() as $raw ) {
41
- if ( empty( $results[ $raw[ 'id' ] ] ) ) {
42
- $record = new LogRecord( array_intersect_key( $raw, $stdKeys ) );
43
- $results[ $raw[ 'id' ] ] = $record;
44
- }
45
- else {
46
- $record = $results[ $raw[ 'id' ] ];
47
- }
48
-
49
- if ( !empty( $raw[ 'meta_key' ] ) ) {
50
- $meta = $record->meta_data ?? [];
51
- $meta[ $raw[ 'meta_key' ] ] = $raw[ 'meta_value' ];
52
- $record->meta_data = $meta;
53
- }
54
- }
55
-
56
- return $results;
57
- }
58
-
59
- /**
60
- * https://stackoverflow.com/questions/55347251/cannot-select-where-ip-inet-ptonip
61
- * We use MySQL built-in IP conversion, not PHPs, as it wasn't working as expected and return 0 results.
62
- * Note: reverse is INET6_ATON
63
- * @return array[]
64
- */
65
- private function selectRaw() :array {
66
- /** @var ModCon $mod */
67
- $mod = $this->getMod();
68
-
69
- return Services::WpDb()->selectCustom(
70
- sprintf( 'SELECT log.id, log.site_id, log.event_slug, log.created_at,
71
- ips.ip,
72
- meta.meta_key, meta.meta_value,
73
- req.req_id as rid
74
- FROM `%s` as log
75
- INNER JOIN `%s` as req
76
- ON log.req_ref = req.id
77
- INNER JOIN `%s` as ips
78
- ON ips.id = req.ip_ref
79
- %s
80
- LEFT JOIN `%s` as `meta`
81
- ON log.id = `meta`.log_ref
82
- ORDER BY log.updated_at DESC
83
- %s
84
- ',
85
- $mod->getDbH_Logs()->getTableSchema()->table,
86
- $this->getCon()->getModule_Data()->getDbH_ReqLogs()->getTableSchema()->table,
87
- $this->getCon()->getModule_Data()->getDbH_IPs()->getTableSchema()->table,
88
- empty( $this->getIP() ) ? '' : sprintf( "AND ips.ip=INET6_ATON('%s')", $this->getIP() ),
89
- $mod->getDbH_Meta()->getTableSchema()->table,
90
- ( $this->limit === 0 ) ? '' : sprintf( 'LIMIT %s', is_null( $this->limit ) ? self::DEFAULT_LIMIT : $this->limit )
91
- )
92
- );
93
- }
94
-
95
- public function setLimit( int $limit ) {
96
- $this->limit = $limit;
97
- return $this;
98
- }
99
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/AuditTrail/DB/LogRecord.php DELETED
@@ -1,11 +0,0 @@
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 DELETED
@@ -1,18 +0,0 @@
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 DELETED
@@ -1,10 +0,0 @@
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 DELETED
@@ -1,9 +0,0 @@
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 DELETED
@@ -1,13 +0,0 @@
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 DELETED
@@ -1,10 +0,0 @@
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 DELETED
@@ -1,18 +0,0 @@
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 DELETED
@@ -1,10 +0,0 @@
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 DELETED
@@ -1,9 +0,0 @@
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 DELETED
@@ -1,12 +0,0 @@
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 DELETED
@@ -1,10 +0,0 @@
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 DELETED
@@ -1,10 +0,0 @@
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( __( 'Audit Trail entries limited to maximum %s days', 'wp-simple-firewall' ), $opts->getAutoCleanDays() ),
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( __( 'Maximum Audit Trail entries limited to %s', 'wp-simple-firewall' ), $opts->getMaxEntries() ),
30
  'href' => $mod->getUrl_DirectLinkToOption( 'audit_trail_max_entries' ),
31
  ];
32
  }
src/lib/src/Modules/AuditTrail/Lib/AuditLogger.php DELETED
@@ -1,99 +0,0 @@
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,31 +2,24 @@
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
  }
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
  }
src/lib/src/Modules/AuditTrail/Lib/AuditWriter.php CHANGED
@@ -7,9 +7,6 @@ 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
- /**
11
- * @deprecated 12.0
12
- */
13
  class AuditWriter extends EventsListener {
14
 
15
  use HandlerConsumer;
@@ -33,7 +30,7 @@ class AuditWriter extends EventsListener {
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
 
@@ -52,6 +49,12 @@ class AuditWriter extends EventsListener {
52
  }
53
 
54
  protected function onShutdown() {
 
 
 
 
 
 
55
  }
56
 
57
  /**
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
  $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
 
49
  }
50
 
51
  protected function onShutdown() {
52
+ if ( !$this->getCon()->plugin_deleting ) {
53
+ ( new Commit() )
54
+ ->setDbHandler( $this->getDbHandler() )
55
+ ->commitAudits( $this->getLogs() );
56
+ $this->setLogs();
57
+ }
58
  }
59
 
60
  /**
src/lib/src/Modules/AuditTrail/Lib/LogHandlers/LocalDbWriter.php DELETED
@@ -1,153 +0,0 @@
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(
83
- function ( $rawRecord ) {
84
- return $rawRecord->id;
85
- },
86
- (array)$reqSelector->filterByIP( $ipRecordID )
87
- ->setColumnsToSelect( [ 'id' ] )
88
- ->queryWithResult()
89
- );
90
-
91
- /** @var Logs\Ops\Select $select */
92
- $select = $mod->getDbH_Logs()->getQuerySelector();
93
- /** @var Logs\Ops\Record $existingLog */
94
- $existingLog = $select->filterByEvent( $this->log[ 'context' ][ 'event_slug' ] )
95
- ->filterByRequestRefs( $reqIDs )
96
- ->filterByCreatedAt( Services::Request()->carbon()->subDay()->timestamp, '>' )
97
- ->setOrderBy( 'updated_at', 'DESC', true )
98
- ->setOrderBy( 'created_at', 'DESC' )
99
- ->first();
100
-
101
- if ( !empty( $existingLog ) ) {
102
- Services::WpDb()->doSql(
103
- sprintf( "UPDATE `%s` SET `meta_value` = `meta_value`+1
104
- WHERE `log_ref`=%s
105
- AND `meta_key`='audit_count'
106
- ", $mod->getDbH_Meta()->getTableSchema()->table, $existingLog->id )
107
- );
108
- // this can fail under load, but doesn't actually matter:
109
- $mod->getDbH_Logs()
110
- ->getQueryUpdater()
111
- ->updateById( $existingLog->id, [ 'updated_at' => Services::Request()->ts() ] );
112
- }
113
- return !empty( $existingLog );
114
- }
115
-
116
- /**
117
- * @return Logs\Ops\Record
118
- * @throws \Exception
119
- */
120
- protected function createPrimaryLogRecord() :Logs\Ops\Record {
121
- /** @var ModCon $mod */
122
- $mod = $this->getMod();
123
-
124
- $record = new Logs\Ops\Record();
125
- $record->event_slug = $this->log[ 'context' ][ 'event_slug' ];
126
- $record->site_id = $this->log[ 'extra' ][ 'meta_wp' ][ 'site_id' ];
127
-
128
- $ipRecordID = ( new IPRecords() )
129
- ->setMod( $this->getCon()->getModule_Data() )
130
- ->loadIP( $this->log[ 'extra' ][ 'meta_request' ][ 'ip' ] ?? '' )
131
- ->id;
132
- $record->req_ref = ( new ReqLogs\RequestRecords() )
133
- ->setMod( $this->getCon()->getModule_Data() )
134
- ->loadReq( $this->log[ 'extra' ][ 'meta_request' ][ 'rid' ], $ipRecordID )
135
- ->id;
136
-
137
- $success = $mod->getDbH_Logs()
138
- ->getQueryInserter()
139
- ->insert( $record );
140
- if ( !$success ) {
141
- throw new \Exception( 'Failed to insert' );
142
- }
143
-
144
- /** @var Logs\Ops\Record $log */
145
- $log = $mod->getDbH_Logs()
146
- ->getQuerySelector()
147
- ->byId( Services::WpDb()->getVar( 'SELECT LAST_INSERT_ID()' ) );
148
- if ( empty( $log ) ) {
149
- throw new \Exception( 'Could not load log record' );
150
- }
151
- return $log;
152
- }
153
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/AuditTrail/Lib/LogHandlers/LogFileHandler.php DELETED
@@ -1,46 +0,0 @@
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 DELETED
@@ -1,39 +0,0 @@
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 DELETED
@@ -1,59 +0,0 @@
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 DELETED
@@ -1,61 +0,0 @@
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 DELETED
@@ -1,143 +0,0 @@
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\Tables\DataTables\LoadData\BaseLoadTableData;
11
- use FernleafSystems\Wordpress\Services\Services;
12
-
13
- class LoadRawTableData extends BaseLoadTableData {
14
-
15
- /**
16
- * @var LogRecord
17
- */
18
- private $log;
19
-
20
- public function loadForLogs() :array {
21
- ( new Traffic\Lib\Ops\ConvertLegacy() )
22
- ->setMod( $this->getCon()->getModule_Traffic() )
23
- ->run();
24
- ( new ConvertLegacy() )
25
- ->setMod( $this->getMod() )
26
- ->run();
27
-
28
- $srvEvents = $this->getCon()->loadEventsService();
29
- return array_values( array_map(
30
- function ( $log ) use ( $srvEvents ) {
31
- $this->log = $log;
32
-
33
- $data = $log->getRawData();
34
-
35
- $data[ 'ip' ] = $this->log->ip;
36
- $data[ 'rid' ] = $this->log->rid ?? __( 'Unknown', 'wp-simple-firewall' );
37
- $data[ 'ip_linked' ] = $this->getColumnContent_RequestDetails();
38
- $data[ 'event' ] = $srvEvents->getEventName( $log->event_slug );
39
- $data[ 'created_since' ] = $this->getColumnContent_Date( $this->log->created_at );
40
- $data[ 'message' ] = $this->getColumnContent_Message();
41
- $data[ 'user' ] = $this->getColumnContent_User();
42
- $data[ 'user_id' ] = $this->getColumnContent_UserID();
43
- $data[ 'level' ] = $this->getColumnContent_Level();
44
- $data[ 'severity' ] = $this->getColumnContent_SeverityIcon();
45
- $data[ 'meta' ] = $this->getColumnContent_Meta();
46
- return $data;
47
- },
48
- $this->getLogRecords()
49
- ) );
50
- }
51
-
52
- /**
53
- * @return LogRecord[]
54
- */
55
- private function getLogRecords() :array {
56
- return array_filter(
57
- ( new LoadLogs() )
58
- ->setMod( $this->getCon()->getModule_AuditTrail() )
59
- ->setLimit( (int)Services::Request()->post( 'record_limit', 10000 ) )
60
- ->run(),
61
- function ( $logRecord ) {
62
- return $this->getCon()->loadEventsService()->eventExists( $logRecord->event_slug );
63
- }
64
- );
65
- }
66
-
67
- private function getColumnContent_RequestDetails() :string {
68
- return sprintf( '<h6>%s</h6>', $this->getIpAnalysisLink( (string)$this->log->ip ) );
69
- }
70
-
71
- private function getColumnContent_UserID() :string {
72
- return $this->log->meta_data[ 'uid' ] ?? '-';
73
- }
74
-
75
- private function getColumnContent_User() :string {
76
- $content = '-';
77
- $uid = $this->log->meta_data[ 'uid' ] ?? '';
78
- if ( !empty( $uid ) ) {
79
- if ( is_numeric( $uid ) ) {
80
- $user = Services::WpUsers()->getUserById( $uid );
81
- if ( !empty( $user ) ) {
82
- $content = sprintf( '<a href="%s" target="_blank">%s</a>',
83
- Services::WpUsers()->getAdminUrl_ProfileEdit( $user ),
84
- $user->user_login );
85
- }
86
- else {
87
- $content = sprintf( 'Unavailable (ID:%s)', $uid );
88
- }
89
- }
90
- else {
91
- $content = $uid === 'cron' ? 'WP Cron' : 'WP-CLI';
92
- }
93
- }
94
- return $content;
95
- }
96
-
97
- private function getColumnContent_Message() :string {
98
- $msg = AuditMessageBuilder::BuildFromLogRecord( $this->log );
99
- return sprintf( '<span class="message-header">%s</span><textarea readonly rows="%s">%s</textarea>',
100
- $this->getCon()->loadEventsService()->getEventName( $this->log->event_slug ),
101
- count( $msg ) + 1, sanitize_textarea_field( implode( "\n", $msg ) ) );
102
- }
103
-
104
- private function getColumnContent_Meta() :string {
105
- return sprintf(
106
- '<button type="button" class="btn btn-link" '.
107
- 'data-toggle="popover" data-placement="left" '.
108
- 'data-customClass="audit-meta" '.
109
- 'data-rid="%s">%s</button>', $this->log->rid,
110
- sprintf( '<span class="meta-icon">%s</span>',
111
- $this->getCon()->svgs->raw( 'bootstrap/tags.svg' )
112
- )
113
- );
114
- }
115
-
116
- private function getColumnContent_Level() :string {
117
- return $this->getCon()->loadEventsService()->getEventDef( $this->log->event_slug )[ 'level' ];
118
- }
119
-
120
- private function getColumnContent_SeverityIcon() :string {
121
- $level = $this->getColumnContent_Level();
122
- $levelDetails = [
123
- 'alert' => [
124
- 'icon' => 'x-octagon',
125
- ],
126
- 'warning' => [
127
- 'icon' => 'exclamation-triangle',
128
- ],
129
- 'notice' => [
130
- 'icon' => 'info-square',
131
- ],
132
- 'info' => [
133
- 'icon' => 'info-circle',
134
- ],
135
- 'debug' => [
136
- 'icon' => 'question-diamond',
137
- ],
138
- ][ $level ];
139
- return sprintf( '<span class="severity-%s severity-icon">%s</span>', $level,
140
- $this->getCon()->svgs->raw( sprintf( 'bootstrap/%s.svg', $levelDetails[ 'icon' ] ) )
141
- );
142
- }
143
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/AuditTrail/Lib/Ops/ConvertLegacy.php DELETED
@@ -1,146 +0,0 @@
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 ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 DELETED
@@ -1,21 +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\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,140 +9,31 @@ use FernleafSystems\Wordpress\Services\Services;
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,16 +48,69 @@ class ModCon extends BaseShield\ModCon {
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
  }
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
  }
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
  }
src/lib/src/Modules/AuditTrail/Options.php CHANGED
@@ -2,78 +2,17 @@
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
  }
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
  }
src/lib/src/Modules/AuditTrail/Processor.php CHANGED
@@ -20,7 +20,6 @@ class Processor extends BaseShield\Processor {
20
 
21
  /**
22
  * @return Lib\AuditWriter
23
- * @deprecated 12.0
24
  */
25
  private function loadAuditorWriter() :Lib\AuditWriter {
26
  if ( !isset( $this->auditWriter ) ) {
@@ -33,9 +32,7 @@ class Processor extends BaseShield\Processor {
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
  }
20
 
21
  /**
22
  * @return Lib\AuditWriter
 
23
  */
24
  private function loadAuditorWriter() :Lib\AuditWriter {
25
  if ( !isset( $this->auditWriter ) ) {
32
  }
33
 
34
  private function initAuditors() {
35
+ $this->loadAuditorWriter()->setIfCommit( true );
 
 
36
  foreach ( $this->getAuditors() as $auditor ) {
37
  $auditor->setMod( $this->getMod() )->execute();
38
  }
src/lib/src/Modules/AuditTrail/Strings.php CHANGED
@@ -7,146 +7,86 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
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,41 +123,58 @@ class Strings extends Base\Strings {
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,60 +187,96 @@ class Strings extends Base\Strings {
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,9 +284,9 @@ class Strings extends Base\Strings {
291
  }
292
 
293
  return [
294
- 'name' => $name,
295
- 'summary' => $summary,
296
- 'description' => $description,
297
  ];
298
  }
299
  }
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
  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
  $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
  }
285
 
286
  return [
287
+ 'name' => $sName,
288
+ 'summary' => $sSummary,
289
+ 'description' => $sDescription,
290
  ];
291
  }
292
  }
src/lib/src/Modules/AuditTrail/UI.php CHANGED
@@ -3,31 +3,51 @@
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
  }
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
  }
src/lib/src/Modules/AuditTrail/Upgrade.php DELETED
@@ -1,12 +0,0 @@
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,8 +64,13 @@ class Display extends Base\WpCli\BaseWpCliCmd {
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
  }
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
  }
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
- $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,51 +111,51 @@ class Strings extends Base\Strings {
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,9 +164,9 @@ class Strings extends Base\Strings {
164
  }
165
 
166
  return [
167
- 'name' => $name,
168
- 'summary' => $summary,
169
- 'description' => $description,
170
  ];
171
  }
172
  }
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
  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
  }
165
 
166
  return [
167
+ 'name' => $sName,
168
+ 'summary' => $sSummary,
169
+ 'description' => $sDescription,
170
  ];
171
  }
172
  }
src/lib/src/Modules/Base/Config/LoadConfig.php DELETED
@@ -1,118 +0,0 @@
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 DELETED
@@ -1,68 +0,0 @@
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 DELETED
@@ -1,40 +0,0 @@
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,11 +76,6 @@ abstract class ModCon {
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,6 +118,9 @@ abstract class ModCon {
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,7 +165,7 @@ abstract class ModCon {
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,6 +226,17 @@ abstract class ModCon {
228
  return $this->loadModElement( 'Upgrade' );
229
  }
230
 
 
 
 
 
 
 
 
 
 
 
 
231
  private function verifyModuleMeetRequirements() :bool {
232
  $bMeetsReqs = true;
233
 
@@ -378,7 +387,7 @@ abstract class ModCon {
378
  }
379
 
380
  public function isUpgrading() :bool {
381
- return $this->getCon()->cfg->rebuilt || $this->getOptions()->getConfigLoader()->isBuiltFromFile();
382
  }
383
 
384
  /**
@@ -386,8 +395,9 @@ abstract class ModCon {
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,6 +653,15 @@ abstract class ModCon {
643
  return (bool)$this->getOptions()->getFeatureProperty( 'show_module_options' );
644
  }
645
 
 
 
 
 
 
 
 
 
 
646
  /**
647
  * @return $this
648
  */
@@ -723,7 +742,7 @@ abstract class ModCon {
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,19 +755,20 @@ abstract class ModCon {
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,6 +818,14 @@ abstract class ModCon {
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,6 +836,11 @@ abstract class ModCon {
808
  }
809
 
810
  public function onPluginDelete() {
 
 
 
 
 
811
  $this->getOptions()->deleteStorage();
812
  }
813
 
@@ -983,7 +1016,7 @@ abstract class ModCon {
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,8 +1133,7 @@ abstract class ModCon {
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,9 +1244,9 @@ abstract class ModCon {
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,9 +1255,9 @@ abstract class ModCon {
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,6 +1267,18 @@ abstract class ModCon {
1235
  return (string)$render;
1236
  }
1237
 
 
 
 
 
 
 
 
 
 
 
 
 
1238
  public function getMainWpData() :array {
1239
  return [
1240
  'options' => $this->getOptions()->getTransferableOptions()
@@ -1243,34 +1287,41 @@ abstract class ModCon {
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,16 +1407,6 @@ abstract class ModCon {
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,14 +1485,4 @@ abstract class ModCon {
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
  }
76
  */
77
  private $aDbHandlers;
78
 
 
 
 
 
 
79
  /**
80
  * @param Shield\Controller\Controller $pluginCon
81
  * @param array $mod
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
  * @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
  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
  }
388
 
389
  public function isUpgrading() :bool {
390
+ return $this->getCon()->cfg->rebuilt || $this->getOptions()->getRebuildFromFile();
391
  }
392
 
393
  /**
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
  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
  * @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
  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
  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
  }
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
 
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
  }
1134
 
1135
  public function getWizardDefinitions() :array {
1136
+ return is_array( $this->getDef( 'wizards' ) ) ? $this->getDef( 'wizards' ) : [];
 
1137
  }
1138
 
1139
  public function hasWizard() :bool {
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
  $data[ 'strings' ] ?? []
1256
  );
1257
 
1258
+ $render = $oRndr->setTemplate( $template )
1259
+ ->setRenderVars( $data )
1260
+ ->render();
1261
  }
1262
  catch ( \Exception $e ) {
1263
  $render = $e->getMessage();
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
 
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
  }
1408
  }
1409
 
 
 
 
 
 
 
 
 
 
 
1410
  /**
1411
  * @return Shield\Modules\Base\Strings|mixed
1412
  */
1485
  public function savePluginOptions() {
1486
  $this->saveModOptions();
1487
  }
 
 
 
 
 
 
 
 
 
 
1488
  }
src/lib/src/Modules/Base/Options.php CHANGED
@@ -5,15 +5,12 @@ 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
 
9
  class Options {
10
 
11
  use ModConsumer;
12
 
13
- private $cfgLoader;
14
-
15
- private $optsStorage;
16
-
17
  /**
18
  * @var array
19
  */
@@ -32,49 +29,68 @@ class Options {
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,15 +101,15 @@ class Options {
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,14 +119,14 @@ class Options {
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,9 +138,10 @@ class Options {
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,37 +154,39 @@ class Options {
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,19 +195,25 @@ class Options {
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,20 +223,15 @@ class Options {
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
  /**
@@ -221,7 +241,7 @@ class Options {
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,32 +263,33 @@ class Options {
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,16 +297,26 @@ class Options {
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,95 +327,101 @@ class Options {
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,7 +429,7 @@ class Options {
392
  }
393
 
394
  public function getNeedSave() :bool {
395
- return $this->bNeedSave;
396
  }
397
 
398
  /**
@@ -409,11 +446,9 @@ class Options {
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,21 +459,27 @@ class Options {
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,7 +492,8 @@ class Options {
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,13 +501,21 @@ class Options {
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,63 +523,113 @@ class Options {
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,7 +649,7 @@ class Options {
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,64 +660,92 @@ class Options {
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,23 +753,25 @@ class Options {
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,17 +798,14 @@ class Options {
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,16 +816,16 @@ class Options {
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,7 +845,6 @@ class Options {
718
  /**
719
  * @return array
720
  */
721
-
722
  protected function getCommonStandardOptions() {
723
  return [];
724
  }
@@ -752,168 +878,110 @@ class Options {
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
  }
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
  /**
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
  * 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
  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
  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
  $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
  * @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
  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
  /**
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
  * @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
  * @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
  && 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
  }
430
 
431
  public function getNeedSave() :bool {
432
+ return (bool)$this->bNeedSave;
433
  }
434
 
435
  /**
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
  * @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
  * @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
  * @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
  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
  }
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
  * 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
  }
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
  }
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
  }
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
  /**
846
  * @return array
847
  */
 
848
  protected function getCommonStandardOptions() {
849
  return [];
850
  }
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
  }
src/lib/src/Modules/Base/Options/OptValueSanitize.php CHANGED
@@ -10,69 +10,71 @@ class OptValueSanitize {
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,6 +87,6 @@ class OptValueSanitize {
85
  throw new \Exception( 'Not a valid value type for option.' );
86
  }
87
 
88
- return $value;
89
  }
90
  }
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
  throw new \Exception( 'Not a valid value type for option.' );
88
  }
89
 
90
+ return $mVal;
91
  }
92
  }
src/lib/src/Modules/Base/Options/Storage.php DELETED
@@ -1,45 +0,0 @@
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,9 @@ abstract class Processor {
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
  $this->setupCronHooks();
22
  }
23
 
@@ -29,15 +30,4 @@ abstract class Processor {
29
 
30
  public function onModuleShutdown() {
31
  }
32
-
33
- protected function getWpHookPriority( string $hook ) :int {
34
- switch ( $hook ) {
35
- case 'init':
36
- $pri = 9;
37
- break;
38
- default:
39
- $pri = 10;
40
- }
41
- return $pri;
42
- }
43
  }
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
  }
24
 
30
 
31
  public function onModuleShutdown() {
32
  }
 
 
 
 
 
 
 
 
 
 
 
33
  }
src/lib/src/Modules/Base/Strings.php CHANGED
@@ -148,26 +148,19 @@ class Strings {
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,9 +190,9 @@ class Strings {
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,11 +200,11 @@ class Strings {
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,9 +212,9 @@ class Strings {
219
  }
220
 
221
  return [
222
- 'title' => $title,
223
- 'title_short' => $titleShort,
224
- 'summary' => ( isset( $summary ) && is_array( $summary ) ) ? $summary : [],
225
  ];
226
  }
227
  }
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
  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
  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
  }
213
 
214
  return [
215
+ 'title' => $sTitle,
216
+ 'title_short' => $sTitleShort,
217
+ 'summary' => ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : [],
218
  ];
219
  }
220
  }
src/lib/src/Modules/Base/UI.php CHANGED
@@ -23,127 +23,129 @@ class UI {
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 {
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 {
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
- $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
  }
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
  }
src/lib/src/Modules/BaseShield/ModCon.php CHANGED
@@ -20,6 +20,15 @@ class ModCon extends Base\ModCon {
20
  */
21
  private static $bVisitorIsWhitelisted;
22
 
 
 
 
 
 
 
 
 
 
23
  public function getDbHandler_Sessions() :Shield\Databases\Session\Handler {
24
  return $this->getCon()
25
  ->getModule_Sessions()
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()
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_params' => $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' => $mResult->get_error_data() ]
100
  );
101
  $this->getCon()->fireEvent( 'comment_spam_block' );
102
 
src/lib/src/Modules/CommentsFilter/Strings.php CHANGED
@@ -8,52 +8,20 @@ use FernleafSystems\Wordpress\Services\Services;
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
  }
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
  }
src/lib/src/Modules/CommentsFilter/Upgrade.php CHANGED
@@ -6,4 +6,36 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
 
7
  class Upgrade extends Base\Upgrade {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  }
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
  }
src/lib/src/Modules/Comms/Strings.php CHANGED
@@ -6,26 +6,6 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
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,4 +59,20 @@ class Strings extends Base\Strings {
79
  'description' => $desc,
80
  ];
81
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  }
6
 
7
  class Strings extends Base\Strings {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  /**
10
  * @param string $section
11
  * @return array
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
  }
src/lib/src/Modules/Data/DB/IPs/IPGeoVO.php DELETED
@@ -1,17 +0,0 @@
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 DELETED
@@ -1,52 +0,0 @@
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
- private static $ips = [];
13
-
14
- public function loadIP( string $ip, bool $autoCreate = true ) :Ops\Record {
15
-
16
- if ( !empty( self::$ips[ $ip ] ) ) {
17
- $record = self::$ips[ $ip ];
18
- }
19
- else {
20
- /** @var ModCon $mod */
21
- $mod = $this->getMod();
22
- $dbh = $mod->getDbH_IPs();
23
- /** @var Ops\Select $select */
24
- $select = $dbh->getQuerySelector();
25
- $record = $select->filterByIPHuman( $ip )->first();
26
-
27
- if ( empty( $record ) && $autoCreate && $this->addIP( $ip ) ) {
28
- $record = $this->loadIP( $ip, false );
29
- }
30
-
31
- if ( empty( $record ) ) {
32
- throw new \Exception( 'IP Record unavailable: '.$ip );
33
- }
34
-
35
- self::$ips[ $ip ] = $record;
36
- }
37
-
38
- return $record;
39
- }
40
-
41
- public function addIP( string $ip ) :bool {
42
- /** @var ModCon $mod */
43
- $mod = $this->getMod();
44
- $dbh = $mod->getDbH_IPs();
45
- /** @var Ops\Insert $insert */
46
- $insert = $dbh->getQueryInserter();
47
- /** @var Ops\Record $record */
48
- $record = $dbh->getRecord();
49
- $record->ip = $ip;
50
- return $insert->insert( $record );
51
- }
52
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Data/DB/IPs/Ops/Common.php DELETED
@@ -1,10 +0,0 @@
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\Traits\FilterByIP;
6
-
7
- trait Common {
8
-
9
- use FilterByIP;
10
- }
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Data/DB/IPs/Ops/Delete.php DELETED
@@ -1,10 +0,0 @@
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 DELETED
@@ -1,9 +0,0 @@
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 DELETED
@@ -1,23 +0,0 @@
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\Services\Services;
7
-
8
- class Insert extends Base\Insert {
9
-
10
- /**
11
- * @param Record $record
12
- * @return bool
13
- */
14
- public function insert( $record ) :bool {
15
- return (bool)Services::WpDb()->doSql( sprintf(
16
- "INSERT INTO `%s` (`%s`,`created_at`) VALUES (INET6_ATON('%s'), %s)",
17
- $this->getDbH()->getTableSchema()->table,
18
- 'ip',
19
- $record->ip,
20
- Services::Request()->ts()
21
- ) );
22
- }
23
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Data/DB/IPs/Ops/Record.php DELETED
@@ -1,62 +0,0 @@
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 DELETED
@@ -1,12 +0,0 @@
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 DELETED
@@ -1,73 +0,0 @@
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 DELETED
@@ -1,48 +0,0 @@
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 DELETED
@@ -1,13 +0,0 @@
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 DELETED
@@ -1,14 +0,0 @@
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 DELETED
@@ -1,10 +0,0 @@
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 DELETED
@@ -1,9 +0,0 @@
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 DELETED
@@ -1,9 +0,0 @@
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 DELETED
@@ -1,11 +0,0 @@
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 DELETED
@@ -1,10 +0,0 @@
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 DELETED
@@ -1,38 +0,0 @@
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/Lib/GeoIP/Lookup.php DELETED
@@ -1,77 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\Lib\GeoIP;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\{
7
- DB\IPs\IPGeoVO,
8
- DB\IPs\IPRecords
9
- };
10
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
11
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
12
- use FernleafSystems\Wordpress\Services\Services;
13
-
14
- class Lookup {
15
-
16
- const URL_REDIRECTLI = 'https://api.redirect.li/v1/ip/';
17
- use PluginControllerConsumer;
18
- use IpAddressConsumer;
19
-
20
- private $ips = [];
21
-
22
- private $reqCount = 0;
23
-
24
- public function lookupIp() :IPGeoVO {
25
- $ip = $this->getIP();
26
- // Small optimization so we don't SQL it every time.
27
- if ( isset( $this->ips[ $ip ] ) ) {
28
- return $this->ips[ $ip ];
29
- }
30
-
31
- try {
32
- if ( empty( $ip ) || !Services::IP()->isValidIp_PublicRemote( $ip ) ) {
33
- throw new \Exception( 'Not a valid public IP address' );
34
- }
35
-
36
- $ipRecord = ( new IPRecords() )
37
- ->setMod( $this->getCon()->getModule_Data() )
38
- ->loadIP( $this->getIP(), true );
39
-
40
- if ( is_null( $ipRecord->geo )
41
- || Services::Request()->carbon()->subMonth()->timestamp > @$ipRecord->geo[ 'ts' ] ) {
42
-
43
- if ( $this->reqCount++ > 30 ) {
44
- throw new \Exception( 'Lookup limit reached.' );
45
- }
46
-
47
- $ipRecord->geo = $this->redirectliIpLookup();
48
- $this->getCon()
49
- ->getModule_Data()
50
- ->getDbH_IPs()
51
- ->getQueryUpdater()
52
- ->updateById( $ipRecord->id, [
53
- 'geo' => $ipRecord->getRawData()[ 'geo' ]
54
- ] );
55
- }
56
-
57
- $geoData = $ipRecord->geo ?? [];
58
- }
59
- catch ( \Exception $e ) {
60
- $geoData = [];
61
- }
62
-
63
- return $this->ips[ $ip ] = ( new IPGeoVO() )->applyFromArray( $geoData );
64
- }
65
-
66
- /**
67
- * @throws \Exception
68
- */
69
- private function redirectliIpLookup() :array {
70
- $data = @json_decode(
71
- Services::HttpRequest()->getContent( self::URL_REDIRECTLI.$this->getIP() ), true
72
- );
73
- $data = ( empty( $data ) || !is_array( $data ) ) ? [] : $data;
74
- $data[ 'ts' ] = Services::Request()->carbon( true )->timestamp;
75
- return $data;
76
- }
77
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Data/ModCon.php DELETED
@@ -1,49 +0,0 @@
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
- $con = $this->getCon();
24
-
25
- // 1. Clean Requests & Audit Trail
26
- // Deleting Request Logs automatically cascades to Audit Trail and then to Audit Trail Meta.
27
- /** @var AuditTrail\Options $optsAudit */
28
- $optsAudit = $con->getModule_AuditTrail()->getOptions();
29
- /** @var Traffic\Options $optsTraffic */
30
- $optsTraffic = $con->getModule_Traffic()->getOptions();
31
- $this->getDbH_ReqLogs()
32
- ->tableCleanExpired( max( $optsAudit->getAutoCleanDays(), $optsTraffic->getAutoCleanDays() ) );
33
-
34
- // 2. Clean Unused IPs.
35
- $this->getDbH_IPs()
36
- ->getQueryDeleter()
37
- ->addWhereNotIn( 'id',
38
- array_unique( array_merge(
39
- $this->getDbH_ReqLogs()
40
- ->getQuerySelector()
41
- ->getDistinctForColumn( 'ip_ref' ),
42
- $con->getModule_IPs()
43
- ->getDbH_BotSignal()
44
- ->getQuerySelector()
45
- ->getDistinctForColumn( 'ip_ref' )
46
- ) )
47
- );
48
- }
49
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Events/Lib/EventsListener.php CHANGED
@@ -29,11 +29,6 @@ abstract class EventsListener {
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
  /**
29
  add_action( $con->prefix( 'plugin_shutdown' ), function () {
30
  $this->onShutdown();
31
  }, 100 );
 
 
 
 
 
32
  }
33
 
34
  /**
src/lib/src/Modules/Events/Lib/EventsService.php CHANGED
@@ -13,45 +13,21 @@ class EventsService {
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,25 +37,18 @@ class EventsService {
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,48 +58,22 @@ class EventsService {
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,13 +81,4 @@ class EventsService {
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
  }
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
  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
  * @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
  }
82
  return $events;
83
  }
 
 
 
 
 
 
 
 
 
84
  }
src/lib/src/Modules/Events/Lib/Reports/KeyStats.php CHANGED
@@ -15,6 +15,8 @@ class KeyStats extends BaseReporter {
15
  $mod = $this->getMod();
16
  /** @var DBEvents\Select $selector */
17
  $selector = $mod->getDbHandler_Events()->getQuerySelector();
 
 
18
 
19
  $eventKeys = [
20
  'ip_offense',
@@ -36,7 +38,6 @@ class KeyStats extends BaseReporter {
36
  $rep = $this->getReport();
37
 
38
  $sums = [];
39
- $srvEvents = $this->getCon()->loadEventsService();
40
  foreach ( $eventKeys as $event ) {
41
  try {
42
  $eventSum = $selector
@@ -45,7 +46,7 @@ class KeyStats extends BaseReporter {
45
  if ( $eventSum > 0 ) {
46
  $sums[ $event ] = [
47
  'count' => $eventSum,
48
- 'name' => $srvEvents->getEventName( $event ),
49
  ];
50
  }
51
  }
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
  $rep = $this->getReport();
39
 
40
  $sums = [];
 
41
  foreach ( $eventKeys as $event ) {
42
  try {
43
  $eventSum = $selector
46
  if ( $eventSum > 0 ) {
47
  $sums[ $event ] = [
48
  'count' => $eventSum,
49
+ 'name' => $strings->getEventName( $event ),
50
  ];
51
  }
52
  }
src/lib/src/Modules/Events/Lib/UI/BuildDataForStats.php CHANGED
@@ -27,7 +27,10 @@ class BuildDataForStats {
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,7 +38,7 @@ class BuildDataForStats {
35
  if ( !empty( array_filter( $sums ) ) ) {
36
  $stats[ $event ] = [
37
  'key' => $event,
38
- 'name' => $srvEvents->getEventName( $event ),
39
  'counts' => $this->buildSums( $event ),
40
  ];
41
  }
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
  if ( !empty( array_filter( $sums ) ) ) {
39
  $stats[ $event ] = [
40
  'key' => $event,
41
+ 'name' => $strings->getEventName( $event ),
42
  'counts' => $this->buildSums( $event ),
43
  ];
44
  }
src/lib/src/Modules/Events/Strings.php CHANGED
@@ -9,52 +9,261 @@ class Strings extends Base\Strings {
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 ) {
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 ) {
src/lib/src/Modules/Events/Upgrade.php DELETED
@@ -1,54 +0,0 @@
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,14 +21,7 @@ class CanScan {
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
  }
21
  ->setMod( $this->getMod() )
22
  ->retrieve();
23
  $canScan = count( $paramsToScan ) > 0
24
+ && ( !$opts->isIgnoreAdmin() && is_super_admin() );
 
 
 
 
 
 
 
25
  }
26
  return $canScan;
27
  }
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 $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',
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',
src/lib/src/Modules/Firewall/Lib/Scan/FirewallHandler.php DELETED
@@ -1,177 +0,0 @@
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 DELETED
@@ -1,12 +0,0 @@
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 DELETED
@@ -1,91 +0,0 @@
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 DELETED
@@ -1,16 +0,0 @@
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 DELETED
@@ -1,12 +0,0 @@
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 DELETED
@@ -1,22 +0,0 @@
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 DELETED
@@ -1,12 +0,0 @@
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 DELETED
@@ -1,12 +0,0 @@
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 DELETED
@@ -1,12 +0,0 @@
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 DELETED
@@ -1,12 +0,0 @@
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 DELETED
@@ -1,12 +0,0 @@
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,44 +10,41 @@ class ParametersToScan {
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,10 +59,10 @@ class ParametersToScan {
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
  }
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
 
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
  }
src/lib/src/Modules/Firewall/Lib/Scan/PerformScan.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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,141 +3,432 @@
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
  }
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
  }
src/lib/src/Modules/Firewall/Strings.php CHANGED
@@ -180,45 +180,16 @@ class Strings extends Base\Strings {
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,6 +214,44 @@ class Strings extends Base\Strings {
243
  'block_exefile' => [
244
  sprintf( __( 'Firewall Trigger: %s.', 'wp-simple-firewall' ), __( 'EXE File Uploads', 'wp-simple-firewall' ) )
245
  ],
 
 
 
 
 
 
246
  ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  }
248
  }
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
  '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
  }
src/lib/src/Modules/GeoIp/Lookup.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
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
+ }
src/lib/src/Modules/HackGuard/AjaxHandler.php CHANGED
@@ -13,6 +13,7 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
13
 
14
  protected function processAjaxAction( string $action ) :array {
15
 
 
16
  switch ( $action ) {
17
 
18
  case 'scanresults_action':
@@ -27,6 +28,26 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
27
  $response = $this->ajaxExec_CheckScans();
28
  break;
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  case 'plugin_reinstall':
31
  $response = $this->ajaxExec_PluginReinstall();
32
  break;
@@ -46,6 +67,60 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
46
  return $response;
47
  }
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  private function ajaxExec_FileLockerShowDiff() :array {
50
  /** @var ModCon $mod */
51
  $mod = $this->getMod();
@@ -203,6 +278,80 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
203
  return [ 'success' => true ];
204
  }
205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  private function ajaxExec_CheckScans() :array {
207
  /** @var ModCon $mod */
208
  $mod = $this->getMod();
@@ -265,9 +414,9 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
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 ) {
13
 
14
  protected function processAjaxAction( string $action ) :array {
15
 
16
+ $req = Services::Request();
17
  switch ( $action ) {
18
 
19
  case 'scanresults_action':
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
  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
  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
  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 ) {
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Diff.php CHANGED
@@ -35,17 +35,15 @@ class Diff extends BaseOps {
35
 
36
  /**
37
  * The WP Diff is empty if the only difference is white space
38
- * @since 10.3 - always use WP Hashes DIFF
39
- * @since 12.0 - use WPHashes and fallback to WP Diff
 
 
 
 
40
  */
41
- try {
42
- $diff = $this->useWpHashes( $original, $current );
43
- }
44
- catch ( \Exception $e ) {
45
- $diff = $this->useWpDiff( $original, $current );
46
- }
47
 
48
- return $diff;
49
  }
50
 
51
  /**
35
 
36
  /**
37
  * The WP Diff is empty if the only difference is white space
38
+ * @since v10.3 always use WP Hashes DIFF
39
+ *
40
+ * $diff = $this->useWpDiff( $original, $current );
41
+ * if ( empty( $diff ) ) {
42
+ * $this->useWpHashes( $original, $current );
43
+ * }
44
  */
 
 
 
 
 
 
45
 
46
+ return $this->useWpHashes( $original, $current );
47
  }
48
 
49
  /**
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->getScanName( $slug ),
30
  ];
31
  }
32
  $alerts[] = $this->getMod()->renderTemplate(
26
  foreach ( $scanCounts as $slug => $count ) {
27
  $scanCounts[ $slug ] = [
28
  'count' => $count,
29
+ 'name' => $strings->getScanNames()[ $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\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,11 +12,14 @@ 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,7 +31,6 @@ class ScanRepairs extends BaseReporter {
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,36 +38,28 @@ class ScanRepairs extends BaseReporter {
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
  }
@@ -85,7 +79,7 @@ class ScanRepairs extends BaseReporter {
85
  'hrefs' => [
86
  'audit_trail' => $this->getCon()
87
  ->getModule_Insights()
88
- ->getUrl_SubInsightsPage( 'audit_trail' ),
89
  ],
90
  ]
91
  );
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
  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
  ];
32
 
33
  $total = 0;
 
34
  foreach ( $repairEvents as $event ) {
35
  $eventTotal = $selectorEvents
36
  ->filterByBoundary( $report->interval_start_at, $report->interval_end_at )
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
  }
79
  'hrefs' => [
80
  'audit_trail' => $this->getCon()
81
  ->getModule_Insights()
82
+ ->getUrl_SubInsightsPage( 'audit' ),
83
  ],
84
  ]
85
  );
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,6 +194,7 @@ class ModCon extends BaseShield\ModCon {
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
  }
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
  }
src/lib/src/Modules/HackGuard/Processor.php CHANGED
@@ -18,4 +18,16 @@ class Processor extends BaseShield\Processor {
18
  $mod->getFileLocker()->execute();
19
  }
20
  }
 
 
 
 
 
 
 
 
 
 
 
 
21
  }
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
  }
src/lib/src/Modules/HackGuard/Scan/Controller/Base.php CHANGED
@@ -11,6 +11,7 @@ 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\Services\Services;
15
 
16
  abstract class Base extends ExecOnceModConsumer {
@@ -67,30 +68,25 @@ abstract class Base extends ExecOnceModConsumer {
67
  $results->removeItemByHash( $item->hash );
68
  }
69
  }
70
- try {
71
- ( new HackGuard\Scan\Results\ResultsDelete() )
72
- ->setScanController( $this )
73
- ->delete( $results, true );
74
- }
75
- catch ( \Exception $e ) {
76
- }
77
-
78
- $this->cleanStalesDeletedResults();
79
- }
80
-
81
- public function cleanStalesDeletedResults() {
82
- /** @var Databases\Scanner\Delete $deleter */
83
- $deleter = $this->getScanResultsDbHandler()->getQueryDeleter();
84
- $deleter->addWhere( 'deleted_at', 0, '>' )
85
- ->addWhereOlderThan( Services::Request()->carbon()->subMonths( 1 )->timestamp, 'deleted_at' )
86
- ->filterByScan( $this->getSlug() )
87
- ->query();
88
  }
89
 
90
  public function createFileDownloadLink( int $recordID ) :string {
91
  return $this->getMod()->createFileDownloadLink( 'scan_file', [ 'rid' => $recordID ] );
92
  }
93
 
 
 
 
 
 
 
 
 
 
 
94
  public function countScanProblems() :int {
95
  if ( !isset( self::$resultsCounts ) ) {
96
  /** @var ModCon $mod */
@@ -122,6 +118,24 @@ abstract class Base extends ExecOnceModConsumer {
122
  ->process( $action );
123
  }
124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  /**
126
  * @return Scans\Base\ResultsSet|mixed
127
  */
@@ -129,13 +143,21 @@ abstract class Base extends ExecOnceModConsumer {
129
  /** @var Databases\Scanner\Select $sel */
130
  $sel = $this->getScanResultsDbHandler()->getQuerySelector();
131
  $sel->filterByScan( $this->getSlug() )
132
- ->filterByNoRepairAttempted()
133
  ->filterByNotIgnored();
134
  return ( new HackGuard\Scan\Results\ConvertBetweenTypes() )
135
  ->setScanController( $this )
136
  ->fromVOsToResultsSet( $sel->query() );
137
  }
138
 
 
 
 
 
 
 
 
 
 
139
  /**
140
  * @param bool $includeIgnored
141
  * @return Scans\Base\ResultsSet|mixed
@@ -180,7 +202,7 @@ abstract class Base extends ExecOnceModConsumer {
180
  public function getScanName() :string {
181
  /** @var HackGuard\Strings $strings */
182
  $strings = $this->getMod()->getStrings();
183
- return $strings->getScanStrings()[ static::SCAN_SLUG ][ 'name' ];
184
  }
185
 
186
  public function isCronAutoRepair() :bool {
@@ -207,20 +229,24 @@ abstract class Base extends ExecOnceModConsumer {
207
  return $this->isPremiumOnly() && !$this->getCon()->isPremiumActive();
208
  }
209
 
210
- public function resetIgnoreStatus() :bool {
211
- return $this->getScanResultsDbHandler()
212
- ->getQueryUpdater()
213
- ->setUpdateWheres( [ 'scan' => $this->getSlug() ] )
214
- ->setUpdateData( [ 'ignored_at' => 0 ] )
215
- ->query() !== false;
 
 
216
  }
217
 
218
- public function resetNotifiedStatus() :bool {
219
- return $this->getScanResultsDbHandler()
220
- ->getQueryUpdater()
221
- ->setUpdateWheres( [ 'scan' => $this->getSlug() ] )
222
- ->setUpdateData( [ 'notified_at' => 0 ] )
223
- ->query() !== false;
 
 
224
  }
225
 
226
  /**
@@ -284,6 +310,18 @@ abstract class Base extends ExecOnceModConsumer {
284
  return new $class();
285
  }
286
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  public function getScanNamespace() :string {
288
  try {
289
  $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\Plugin\Shield\Scans\Base\Table\BaseEntryFormatter;
15
  use FernleafSystems\Wordpress\Services\Services;
16
 
17
  abstract class Base extends ExecOnceModConsumer {
68
  $results->removeItemByHash( $item->hash );
69
  }
70
  }
71
+ ( new HackGuard\Scan\Results\ResultsDelete() )
72
+ ->setScanController( $this )
73
+ ->delete( $results );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  }
75
 
76
  public function createFileDownloadLink( int $recordID ) :string {
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
  ->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
  */
143
  /** @var Databases\Scanner\Select $sel */
144
  $sel = $this->getScanResultsDbHandler()->getQuerySelector();
145
  $sel->filterByScan( $this->getSlug() )
 
146
  ->filterByNotIgnored();
147
  return ( new HackGuard\Scan\Results\ConvertBetweenTypes() )
148
  ->setScanController( $this )
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
  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
  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
  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();
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( 'scan_run', [ 'audit_params' => [ 'scan' => $scanCon->getScanName() ] ] );
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
- $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
  ]
38
  ->setDbHandler( $dbh )
39
  ->collate( $scanSlug );
40
 
41
+ $con->fireEvent( $scanSlug.'_scan_run' );
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
+ $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
  ]
src/lib/src/Modules/HackGuard/Scan/Results/ResultsDelete.php CHANGED
@@ -15,65 +15,39 @@ class ResultsDelete {
15
  use ScanControllerConsumer;
16
 
17
  /**
18
- * @param Scans\Base\ResultsSet $resultsToDelete
19
- * @param bool $softDelete
20
  * @return bool
21
- * @throws \Exception
22
  */
23
- public function delete( $resultsToDelete, bool $softDelete = true ) {
24
- if ( !$resultsToDelete->hasItems() ) {
25
- throw new \Exception( 'No items' );
26
- }
27
-
28
- $hashes = array_filter( array_map(
29
- function ( $item ) {
30
- return (string)$item->hash;
31
  },
32
- $resultsToDelete->getAllItems()
33
- ) );
34
-
35
- $success = true;
36
- if ( !empty( $hashes ) ) {
37
- if ( $softDelete ) {
38
- $success = $this->softDelete( $hashes );
39
- }
40
- else {
41
- /** @var Databases\Scanner\Delete $deleter */
42
- $deleter = $this->getScanController()
43
- ->getScanResultsDbHandler()
44
- ->getQueryDeleter();
45
- $success = $deleter->filterByHashes( $hashes )->query();
46
- }
47
  }
48
- return $success;
49
- }
50
-
51
- protected function softDelete( array $hashes ) :bool {
52
- /** @var Databases\Scanner\Update $updater */
53
- $updater = $this->getScanController()
54
- ->getScanResultsDbHandler()
55
- ->getQueryUpdater();
56
- // This is an inefficient hack for multiple soft-deletes until we rewrite a custom SQL updater
57
- foreach ( $hashes as $hash ) {
58
- $updater->setSoftDeleted()
59
- ->setUpdateWheres( [
60
- 'hash' => $hash
61
- ] )
62
- ->query();
63
- }
64
-
65
- return true;
66
  }
67
 
68
  /**
69
  * @return $this
70
  */
71
  public function deleteAllForScan() {
72
- /** @var Databases\Scanner\Delete $deleter */
73
- $deleter = $this->getScanController()
74
- ->getScanResultsDbHandler()
75
- ->getQueryDeleter();
76
- $deleter->forScan( $this->getScanController()->getSlug() );
77
  return $this;
78
  }
79
  }
15
  use ScanControllerConsumer;
16
 
17
  /**
18
+ * @param Scans\Base\ResultsSet $oResultsToDelete
 
19
  * @return bool
 
20
  */
21
+ public function delete( $oResultsToDelete ) {
22
+ $aHashes = array_map(
23
+ function ( $oItem ) {
24
+ /** @var Scans\Base\ResultItem $oItem */
25
+ return $oItem->hash;
 
 
 
26
  },
27
+ $oResultsToDelete->getAllItems()
28
+ );
29
+
30
+ $bSuccess = true;
31
+ if ( !empty( $aHashes ) ) {
32
+ /** @var Databases\Scanner\Delete $oDel */
33
+ $oDel = $this->getScanController()
34
+ ->getScanResultsDbHandler()
35
+ ->getQueryDeleter();
36
+ $bSuccess = $oDel->filterByHashes( $aHashes )
37
+ ->query();
 
 
 
 
38
  }
39
+ return $bSuccess;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  }
41
 
42
  /**
43
  * @return $this
44
  */
45
  public function deleteAllForScan() {
46
+ /** @var Databases\Scanner\Delete $oDel */
47
+ $oDel = $this->getScanController()
48
+ ->getScanResultsDbHandler()
49
+ ->getQueryDeleter();
50
+ $oDel->forScan( $this->getScanController()->getSlug() );
51
  return $this;
52
  }
53
  }
src/lib/src/Modules/HackGuard/Scan/Results/ResultsStore.php CHANGED
@@ -16,53 +16,17 @@ class ResultsStore {
16
 
17
  /**
18
  * @param Scans\Base\ResultsSet $resultsToStore
19
- * @throws \Exception
20
  */
21
  public function store( $resultsToStore ) {
22
- if ( !$resultsToStore->hasItems() ) {
23
- throw new \Exception( 'No items' );
24
- }
25
-
26
  $scanCon = $this->getScanController();
27
- $dbh = $scanCon->getScanResultsDbHandler();
28
-
29
  $VOs = ( new ConvertBetweenTypes() )
30
  ->setScanController( $scanCon )
31
  ->fromResultsToVOs( $resultsToStore );
32
 
33
- // Try to find all older, but deleted results.
34
- /** @var Databases\Scanner\Select $selector */
35
- $selector = $dbh->getQuerySelector();
36
- /** @var Databases\Scanner\EntryVO[] $existing */
37
- $existing = $selector->filterByScan( $scanCon->getSlug() )
38
- ->filterByHashes( array_keys( $VOs ) )
39
- ->setIncludeSoftDeleted( true )
40
- ->query();
41
-
42
- if ( !empty( $existing ) ) {
43
- foreach ( $existing as $existingRecord ) {
44
- foreach ( $VOs as $vo ) {
45
- if ( $existingRecord->hash === $vo->hash ) {
46
-
47
- $updateData = $vo->getRawData();
48
- $updateData[ 'deleted_at' ] = 0;
49
-
50
- $dbh->getQueryUpdater()
51
- ->setUpdateWheres( [
52
- 'scan' => $scanCon->getSlug(),
53
- 'hash' => $existingRecord->hash,
54
- ] )
55
- ->setUpdateData( $updateData )
56
- ->query();
57
- unset( $VOs[ $vo->hash ] );
58
- break;
59
- }
60
- }
61
- }
62
- }
63
-
64
  foreach ( $VOs as $vo ) {
65
- $dbh->getQueryInserter()->insert( $vo );
66
  }
67
  }
68
  }
16
 
17
  /**
18
  * @param Scans\Base\ResultsSet $resultsToStore
 
19
  */
20
  public function store( $resultsToStore ) {
 
 
 
 
21
  $scanCon = $this->getScanController();
22
+ $inserter = $scanCon->getScanResultsDbHandler()
23
+ ->getQueryInserter();
24
  $VOs = ( new ConvertBetweenTypes() )
25
  ->setScanController( $scanCon )
26
  ->fromResultsToVOs( $resultsToStore );
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  foreach ( $VOs as $vo ) {
29
+ $inserter->insert( $vo );
30
  }
31
  }
32
  }
src/lib/src/Modules/HackGuard/Scan/Results/ResultsUpdate.php CHANGED
@@ -27,35 +27,27 @@ class ResultsUpdate {
27
 
28
  $itemsToDelete = ( new Scans\Base\DiffResultForStorage() )->diff( $existing, $newCopy );
29
 
30
- try {
31
- ( new ResultsDelete() )
32
- ->setScanController( $scanCon )
33
- ->delete( $itemsToDelete );
34
- }
35
- catch ( \Exception $e ) {
36
- }
37
-
38
- try {
39
- ( new ResultsStore() )
40
- ->setScanController( $scanCon )
41
- ->store( $newCopy );
42
- }
43
- catch ( \Exception $e ) {
44
- }
45
 
46
- if ( $existing->hasItems() ) {
47
- $updater = $scanCon->getScanResultsDbHandler()->getQueryUpdater();
48
- /** @var Databases\Scanner\EntryVO $vo */
49
- $converter = ( new ConvertBetweenTypes() )->setScanController( $scanCon );
50
- foreach ( $converter->fromResultsToVOs( $existing ) as $vo ) {
51
- $updater->reset()
52
- ->setUpdateWheres( [
 
 
 
 
 
53
  'scan' => $scanCon->getSlug(),
54
  'hash' => $vo->hash,
55
- ] )
56
- ->setUpdateData( $vo->getRawData() )
57
- ->query();
58
- }
59
  }
60
  }
61
  }
27
 
28
  $itemsToDelete = ( new Scans\Base\DiffResultForStorage() )->diff( $existing, $newCopy );
29
 
30
+ ( new ResultsDelete() )
31
+ ->setScanController( $scanCon )
32
+ ->delete( $itemsToDelete );
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ ( new ResultsStore() )
35
+ ->setScanController( $scanCon )
36
+ ->store( $newCopy );
37
+
38
+ $updater = $scanCon->getScanResultsDbHandler()->getQueryUpdater();
39
+ /** @var Databases\Scanner\EntryVO $vo */
40
+ $converter = ( new ConvertBetweenTypes() )->setScanController( $scanCon );
41
+ foreach ( $converter->fromResultsToVOs( $existing ) as $vo ) {
42
+ $updater->reset()
43
+ ->setUpdateData( $vo->getRawData() )
44
+ ->setUpdateWheres(
45
+ [
46
  'scan' => $scanCon->getSlug(),
47
  'hash' => $vo->hash,
48
+ ]
49
+ )
50
+ ->query();
 
51
  }
52
  }
53
  }
src/lib/src/Modules/HackGuard/Strings.php CHANGED
@@ -8,98 +8,64 @@ use FernleafSystems\Wordpress\Services\Services;
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
  /**
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
  /**
src/lib/src/Modules/HackGuard/UI.php CHANGED
@@ -8,12 +8,17 @@ use FernleafSystems\Wordpress\Services\Services;
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,17 +31,28 @@ class UI extends BaseShield\UI {
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,7 +79,7 @@ class UI extends BaseShield\UI {
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,13 +97,13 @@ class UI extends BaseShield\UI {
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,123 +112,121 @@ class UI extends BaseShield\UI {
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,6 +267,40 @@ class UI extends BaseShield\UI {
253
  ];
254
  }
255
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  protected function getSectionWarnings( string $section ) :array {
257
  $warnings = [];
258
 
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
 
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
  '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
  ],
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
  '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
  ];
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
 
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,12 +226,11 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
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':
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
  }
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':
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_params' => $this->getAuditData(),
43
  'offense_count' => $offenseCount,
44
  'block' => $isBlock,
45
  ]
39
  ->fireEvent(
40
  'bot'.static::OPT_KEY,
41
  [
42
+ 'audit' => $this->getAuditData(),
43
  'offense_count' => $offenseCount,
44
  'block' => $isBlock,
45
  ]
src/lib/src/Modules/IPs/BotTrack/TrackFakeWebCrawler.php CHANGED
@@ -5,11 +5,15 @@ 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
  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,7 +31,7 @@ class TrackFakeWebCrawler extends Base {
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,7 +42,7 @@ class TrackFakeWebCrawler extends Base {
38
 
39
  protected function getAuditData() :array {
40
  return array_merge( parent::getAuditData(), [
41
- 'crawler' => $this->crawlerUsed
42
  ] );
43
  }
44
  }
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
  foreach ( Services::ServiceProviders()->getAllCrawlerUseragents() as $possibleAgent ) {
32
  if ( stripos( $userAgent, $possibleAgent ) !== false ) {
33
  $identifiesAsCrawler = true;
34
+ $this->agentUsed = $possibleAgent;
35
  break;
36
  }
37
  }
42
 
43
  protected function getAuditData() :array {
44
  return array_merge( parent::getAuditData(), [
45
+ 'script' => $this->agentUsed
46
  ] );
47
  }
48
  }
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
- 'user_login' => $this->user_login
44
  ];
45
  }
46
  }
40
 
41
  protected function getAuditData() :array {
42
  return [
43
+ '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
- 'user_login' => $this->user_login
44
  ];
45
  }
46
  }
40
 
41
  protected function getAuditData() :array {
42
  return [
43
+ 'login' => $this->user_login
44
  ];
45
  }
46
  }
src/lib/src/Modules/IPs/BotTrack/TrackUserAgent.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- $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,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 ( $toBlock ) {
63
- $updater = $mod->getDbHandler_IPs()->getQueryUpdater();
64
- $updater->setBlocked( $IP );
65
  $con->fireEvent( 'ip_offense', [ 'suppress_audit' => true ] );
66
  }
67
  }
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
  * 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
  }
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_params' => [ 'ip' => $ip ] ] );
30
  }
31
  }
32
  }
26
  ->setIP( $ip )
27
  ->fromBlacklist();
28
  if ( $removed ) {
29
+ $this->getCon()->fireEvent( 'ip_unblock_flag', [ 'audit' => [ 'ip' => $ip ] ] );
30
  }
31
  }
32
  }
src/lib/src/Modules/IPs/DB/BotSignal/BotSignalRecord.php DELETED
@@ -1,11 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\DB\BotSignal;
4
-
5
- /**
6
- * NOT a true DB Model
7
- * @property string $ip
8
- */
9
- class BotSignalRecord extends Ops\Record {
10
-
11
- }
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/IPs/DB/BotSignal/LoadBotSignalRecords.php DELETED
@@ -1,41 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\DB\BotSignal;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
8
- use FernleafSystems\Wordpress\Services\Services;
9
-
10
- class LoadBotSignalRecords {
11
-
12
- use ModConsumer;
13
- use IpAddressConsumer;
14
-
15
- public function loadRecord() :BotSignalRecord {
16
- $raw = $this->selectRaw();
17
- if ( empty( $raw ) ) {
18
- throw new \Exception( 'No record' );
19
- }
20
- return ( new BotSignalRecord() )->applyFromArray( $raw );
21
- }
22
-
23
- private function selectRaw() :array {
24
- /** @var ModCon $mod */
25
- $mod = $this->getMod();
26
- $raw = Services::WpDb()->selectRow(
27
- sprintf( "SELECT ips.ip, bs.*
28
- FROM `%s` as bs
29
- INNER JOIN `%s` as ips
30
- ON `ips`.id = `bs`.ip_ref
31
- AND `ips`.`ip`=INET6_ATON('%s')
32
- ORDER BY `bs`.updated_at DESC
33
- LIMIT 1;",
34
- $mod->getDbH_BotSignal()->getTableSchema()->table,
35
- $this->getCon()->getModule_Data()->getDbH_IPs()->getTableSchema()->table,
36
- $this->getIP()
37
- )
38
- );
39
- return is_array( $raw ) ? $raw : [];
40
- }
41
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/IPs/DB/BotSignal/Ops/Common.php DELETED
@@ -1,10 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\DB\BotSignal\Ops;
4
-
5
- trait Common {
6
-
7
- public function filterByIP( int $ipRef ) {
8
- return $this->addWhereEquals( 'ip_ref', $ipRef );
9
- }
10
- }
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/IPs/DB/BotSignal/Ops/Delete.php DELETED
@@ -1,10 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\DB\BotSignal\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/IPs/DB/BotSignal/Ops/Handler.php DELETED
@@ -1,9 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\DB\BotSignal\Ops;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
-
7
- class Handler extends Base\Handler {
8
-
9
- }
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/IPs/DB/BotSignal/Ops/Insert.php DELETED
@@ -1,9 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\DB\BotSignal\Ops;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
6
-
7
- class Insert extends Base\Insert {
8
-
9
- }
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/IPs/DB/BotSignal/Ops/Record.php DELETED
@@ -1,35 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\DB\BotSignal\Ops;
4
-
5
- /**
6
- * @property int $ip_ref
7
- * @property int $notbot_at
8
- * @property int $frontpage_at
9
- * @property int $loginpage_at
10
- * @property int $bt404_at
11
- * @property int $btcheese_at
12
- * @property int $btfake_at
13
- * @property int $btinvalidscript_at
14
- * @property int $btloginfail_at
15
- * @property int $btlogininvalid_at
16
- * @property int $btua_at
17
- * @property int $btxml_at
18
- * @property int $cooldown_at
19
- * @property int $auth_at
20
- * @property int $offense_at
21
- * @property int $blocked_at
22
- * @property int $unblocked_at
23
- * @property int $bypass_at
24
- * @property int $humanspam_at
25
- * @property int $markspam_at
26
- * @property int $unmarkspam_at
27
- * @property int $captchapass_at
28
- * @property int $captchafail_at
29
- * @property int $ratelimit_at
30
- * @property int $updated_at
31
- * @property int $snsent_at
32
- */
33
- class Record extends \FernleafSystems\Wordpress\Plugin\Core\Databases\Base\Record {
34
-
35
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/IPs/DB/BotSignal/Ops/Select.php DELETED
@@ -1,11 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\DB\BotSignal\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
- }
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/IPs/Lib/BlockRequest.php CHANGED
@@ -10,15 +10,14 @@ use FernleafSystems\Wordpress\Services\Utilities\Obfuscate;
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
  }
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
  }
src/lib/src/Modules/IPs/Lib/Bots/BotEventListener.php CHANGED
@@ -3,7 +3,6 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class BotEventListener extends ExecOnceModConsumer {
@@ -12,7 +11,7 @@ class BotEventListener extends ExecOnceModConsumer {
12
  $events = $this->getEventsToColumn();
13
 
14
  foreach ( $events as $eventTrigger => $column ) {
15
- if ( $eventTrigger === $event ) {
16
  try {
17
  ( new BotSignalsRecord() )
18
  ->setMod( $this->getMod() )
@@ -20,20 +19,14 @@ class BotEventListener extends ExecOnceModConsumer {
20
  ->updateSignalField( $column );
21
  }
22
  catch ( \LogicException $e ) {
23
- error_log( 'Error updating bot signal with column problem: '.$e->getMessage() );
24
- }
25
- catch ( \Exception $e ) {
26
- // error_log( 'Error updating bot signal: '.$e->getMessage() );
27
  }
28
  }
29
  }
30
  }
31
 
32
  protected function canRun() :bool {
33
- /** @var ModCon $mod */
34
- $mod = $this->getMod();
35
- return !$mod->isTrustedVerifiedBot()
36
- && $mod->getDbH_BotSignal()->isReady();
37
  }
38
 
39
  protected function run() {
@@ -69,7 +62,7 @@ class BotEventListener extends ExecOnceModConsumer {
69
  'spam_block_human' => 'humanspam',
70
  'comment_markspam' => 'markspam',
71
  'comment_unmarkspam' => 'unmarkspam',
72
- 'firewall_block' => 'firewall',
73
  'ip_offense' => 'offense',
74
  'ip_blocked' => 'blocked',
75
  'ip_unblock' => 'unblocked',
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
 
6
  use FernleafSystems\Wordpress\Services\Services;
7
 
8
  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() )
19
  ->updateSignalField( $column );
20
  }
21
  catch ( \LogicException $e ) {
22
+ error_log( 'Error updating bot signal: '.$e->getMessage() );
 
 
 
23
  }
24
  }
25
  }
26
  }
27
 
28
  protected function canRun() :bool {
29
+ return !$this->getMod()->isTrustedVerifiedBot();
 
 
 
30
  }
31
 
32
  protected function run() {
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',
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_params' => [
46
  'score' => $score,
47
  'minimum' => $botScoreMinimum,
48
  ]
42
  $this->getCon()->fireEvent(
43
  'antibot_'.( $isBot ? 'fail' : 'pass' ),
44
  [
45
+ 'audit' => [
46
  'score' => $score,
47
  'minimum' => $botScoreMinimum,
48
  ]
src/lib/src/Modules/IPs/Lib/Bots/BotSignalsRecord.php CHANGED
@@ -2,16 +2,12 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots;
4
 
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Session;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\LookupIpOnList;
9
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\{
10
- DB\BotSignal\BotSignalRecord,
11
- DB\BotSignal\LoadBotSignalRecords,
12
- ModCon,
13
- DB\BotSignal
14
- };
15
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
16
  use FernleafSystems\Wordpress\Services\Services;
17
 
@@ -23,61 +19,43 @@ class BotSignalsRecord {
23
  public function delete() :bool {
24
  /** @var ModCon $mod */
25
  $mod = $this->getMod();
26
- /** @var BotSignal\Ops\Select $select */
27
- $select = $mod->getDbH_BotSignal()->getQueryDeleter();
28
- return $select->filterByIP( $this->getIPRecord()->id )->query();
29
  }
30
 
31
- public function retrieveNotBotAt() :int {
32
- /** @var ModCon $mod */
33
- $mod = $this->getMod();
34
- return (int)Services::WpDb()->getVar(
35
- sprintf( "SELECT bs.notbot_at
36
- FROM `%s` as bs
37
- INNER JOIN `%s` as ips
38
- ON `ips`.id = `bs`.ip_ref
39
- AND `ips`.`ip`=INET6_ATON('%s')
40
- ORDER BY `bs`.updated_at DESC
41
- LIMIT 1;",
42
- $mod->getDbH_BotSignal()->getTableSchema()->table,
43
- $this->getCon()->getModule_Data()->getDbH_IPs()->getTableSchema()->table,
44
- $this->getIP()
45
- )
46
- );
47
- }
48
-
49
- public function retrieve( bool $storeOnLoad = true ) :BotSignalRecord {
50
  /** @var ModCon $mod */
51
  $mod = $this->getMod();
52
 
53
- $r = $this->dbLoad();
54
- if ( empty( $r ) ) {
55
- $r = new BotSignalRecord();
56
- $r->ip_ref = $this->getIPRecord()->id;
57
  }
58
 
59
  $ipOnList = ( new LookupIpOnList() )
60
  ->setDbHandler( $mod->getDbHandler_IPs() )
61
- ->setIP( $this->getIP() )
62
  ->lookupIp();
63
 
64
  if ( !empty( $ipOnList ) ) {
65
- if ( empty( $r->bypass_at ) && $ipOnList->list === $mod::LIST_MANUAL_WHITE ) {
66
- $r->bypass_at = $ipOnList->created_at;
67
  }
68
- if ( empty( $r->offense_at ) && $ipOnList->list === $mod::LIST_AUTO_BLACK ) {
69
- $r->offense_at = $ipOnList->last_access_at;
70
  }
71
- $r->blocked_at = $ipOnList->blocked_at;
72
  }
73
 
74
- if ( empty( $r->notbot_at ) && Services::IP()->getRequestIp() === $this->getIP() ) {
75
- $r->notbot_at = $mod->getBotSignalsController()
76
  ->getHandlerNotBot()
77
  ->hasCookie() ? Services::Request()->ts() : 0;
78
  }
79
 
80
- if ( empty( $r->auth_at ) ) {
81
  $dbhSessions = $this->getCon()
82
  ->getModule_Sessions()
83
  ->getDbHandler_Sessions();
@@ -86,48 +64,43 @@ class BotSignalsRecord {
86
  $session = $selector->setIncludeSoftDeleted( true )
87
  ->filterByIp( $this->getIP() )
88
  ->first();
89
- $r->auth_at = empty( $session ) ? 0 : $session->created_at;
90
  }
91
 
92
  if ( $storeOnLoad ) {
93
- $this->store( $r );
94
  }
95
 
96
- return $r;
97
  }
98
 
99
  /**
100
- * @return BotSignal\BotSignalRecord|null
101
  */
102
  private function dbLoad() {
103
- try {
104
- $record = ( new LoadBotSignalRecords() )
105
- ->setMod( $this->getMod() )
106
- ->setIP( $this->getIP() )
107
- ->loadRecord();
108
- }
109
- catch ( \Exception $e ) {
110
- $record = null;
111
- }
112
-
113
- return $record;
114
  }
115
 
116
- public function store( BotSignalRecord $record ) :bool {
117
  /** @var ModCon $mod */
118
  $mod = $this->getMod();
119
 
120
- if ( empty( $record->id ) ) {
121
- $success = $mod->getDbH_BotSignal()
122
  ->getQueryInserter()
123
- ->insert( $record );
124
  }
125
  else {
126
- $data = $record->getRawData();
127
  $data[ 'updated_at' ] = Services::Request()->ts();
128
- $success = $mod->getDbH_BotSignal()
129
  ->getQueryUpdater()
130
- ->updateById( $record->id, $data );
131
  }
132
  return $success;
133
  }
@@ -135,28 +108,22 @@ class BotSignalsRecord {
135
  /**
136
  * @param string $field
137
  * @param int|null $ts
138
- * @return BotSignalRecord
139
  * @throws \LogicException
140
  */
141
- public function updateSignalField( string $field, $ts = null ) :BotSignalRecord {
142
  /** @var ModCon $mod */
143
  $mod = $this->getMod();
144
 
145
- if ( !$mod->getDbH_BotSignal()->getTableSchema()->hasColumn( $field ) ) {
146
  throw new \LogicException( sprintf( '"%s" is not a valid column on Bot Signals', $field ) );
147
  }
148
 
149
- $record = $this->retrieve( false ); // false as we're going to store it anyway
150
- $record->{$field} = is_null( $ts ) ? Services::Request()->ts() : $ts;
151
 
152
- $this->store( $record );
153
-
154
- return $record;
155
- }
156
 
157
- private function getIPRecord() :IPs\Ops\Record {
158
- return ( new IPs\IPRecords() )
159
- ->setMod( $this->getCon()->getModule_Data() )
160
- ->loadIP( $this->getIP(), true );
161
  }
162
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\BotSignals\EntryVO;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\BotSignals\Select;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Session;
 
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
9
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\LookupIpOnList;
10
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
 
 
 
 
 
11
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
12
  use FernleafSystems\Wordpress\Services\Services;
13
 
19
  public function delete() :bool {
20
  /** @var ModCon $mod */
21
  $mod = $this->getMod();
22
+ /** @var Select $select */
23
+ $select = $mod->getDbHandler_BotSignals()->getQueryDeleter();
24
+ return $select->filterByIPHuman( $this->getIP() )->query();
25
  }
26
 
27
+ public function retrieve( bool $storeOnLoad = true ) :EntryVO {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  /** @var ModCon $mod */
29
  $mod = $this->getMod();
30
 
31
+ $e = $this->dbLoad();
32
+ if ( !$e instanceof EntryVO ) {
33
+ $e = new EntryVO();
34
+ $e->ip = $this->getIP();
35
  }
36
 
37
  $ipOnList = ( new LookupIpOnList() )
38
  ->setDbHandler( $mod->getDbHandler_IPs() )
39
+ ->setIP( $e->ip )
40
  ->lookupIp();
41
 
42
  if ( !empty( $ipOnList ) ) {
43
+ if ( empty( $e->bypass_at ) && $ipOnList->list === $mod::LIST_MANUAL_WHITE ) {
44
+ $e->bypass_at = $ipOnList->created_at;
45
  }
46
+ if ( empty( $e->offense_at ) && $ipOnList->list === $mod::LIST_AUTO_BLACK ) {
47
+ $e->offense_at = $ipOnList->last_access_at;
48
  }
49
+ $e->blocked_at = $ipOnList->blocked_at;
50
  }
51
 
52
+ if ( empty( $e->notbot_at ) && Services::IP()->getRequestIp() === $this->getIP() ) {
53
+ $e->notbot_at = $mod->getBotSignalsController()
54
  ->getHandlerNotBot()
55
  ->hasCookie() ? Services::Request()->ts() : 0;
56
  }
57
 
58
+ if ( empty( $e->auth_at ) ) {
59
  $dbhSessions = $this->getCon()
60
  ->getModule_Sessions()
61
  ->getDbHandler_Sessions();
64
  $session = $selector->setIncludeSoftDeleted( true )
65
  ->filterByIp( $this->getIP() )
66
  ->first();
67
+ $e->auth_at = empty( $session ) ? 0 : $session->created_at;
68
  }
69
 
70
  if ( $storeOnLoad ) {
71
+ $this->store( $e );
72
  }
73
 
74
+ return $e;
75
  }
76
 
77
  /**
78
+ * @return EntryVO|null
79
  */
80
  private function dbLoad() {
81
+ /** @var ModCon $mod */
82
+ $mod = $this->getMod();
83
+ /** @var Select $select */
84
+ $select = $mod->getDbHandler_BotSignals()->getQuerySelector();
85
+ /** @var EntryVO $record */
86
+ return $select->filterByIPHuman( $this->getIP() )->first();
 
 
 
 
 
87
  }
88
 
89
+ public function store( EntryVO $entry ) :bool {
90
  /** @var ModCon $mod */
91
  $mod = $this->getMod();
92
 
93
+ if ( empty( $entry->id ) ) {
94
+ $success = $mod->getDbHandler_BotSignals()
95
  ->getQueryInserter()
96
+ ->insert( $entry );
97
  }
98
  else {
99
+ $data = $entry->getRawData();
100
  $data[ 'updated_at' ] = Services::Request()->ts();
101
+ $success = $mod->getDbHandler_BotSignals()
102
  ->getQueryUpdater()
103
+ ->updateById( $entry->id, $data );
104
  }
105
  return $success;
106
  }
108
  /**
109
  * @param string $field
110
  * @param int|null $ts
111
+ * @return EntryVO
112
  * @throws \LogicException
113
  */
114
+ public function updateSignalField( string $field, $ts = null ) :EntryVO {
115
  /** @var ModCon $mod */
116
  $mod = $this->getMod();
117
 
118
+ if ( !$mod->getDbHandler_BotSignals()->getTableSchema()->hasColumn( $field ) ) {
119
  throw new \LogicException( sprintf( '"%s" is not a valid column on Bot Signals', $field ) );
120
  }
121
 
122
+ $entry = $this->retrieve( false ); // false as we're going to store it anyway
123
+ $entry->{$field} = is_null( $ts ) ? Services::Request()->ts() : $ts;
124
 
125
+ $this->store( $entry );
 
 
 
126
 
127
+ return $entry;
 
 
 
128
  }
129
  }
src/lib/src/Modules/IPs/Lib/Bots/Calculator/BaseBuildScores.php CHANGED
@@ -2,14 +2,15 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Core\Databases\Common\RecordConsumer;
 
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
  use FernleafSystems\Wordpress\Services\Services;
8
  use FernleafSystems\Wordpress\Services\Utilities\Net\IpID;
9
 
10
  abstract class BaseBuildScores {
11
 
12
- use RecordConsumer;
13
  use ModConsumer;
14
 
15
  abstract public function build() :array;
@@ -36,16 +37,15 @@ abstract class BaseBuildScores {
36
  }
37
 
38
  protected function getAllFields( $filterForMethods = false ) :array {
 
 
 
39
  $fields = array_map(
40
  function ( $col ) {
41
  return str_replace( '_at', '', $col );
42
  },
43
  array_filter(
44
- $this->getCon()
45
- ->getModule_IPs()
46
- ->getDbH_BotSignal()
47
- ->getTableSchema()
48
- ->getColumnNames(),
49
  function ( $col ) {
50
  return preg_match( '#_at$#', $col ) &&
51
  !in_array( $col, [ 'snsent_at', 'updated_at', 'deleted_at' ] );
@@ -61,4 +61,8 @@ abstract class BaseBuildScores {
61
 
62
  return $fields;
63
  }
 
 
 
 
64
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base\EntryVoConsumer;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\BotSignals\EntryVO;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
  use FernleafSystems\Wordpress\Services\Services;
9
  use FernleafSystems\Wordpress\Services\Utilities\Net\IpID;
10
 
11
  abstract class BaseBuildScores {
12
 
13
+ use EntryVoConsumer;
14
  use ModConsumer;
15
 
16
  abstract public function build() :array;
37
  }
38
 
39
  protected function getAllFields( $filterForMethods = false ) :array {
40
+ $botSignalDBH = shield_security_get_plugin()->getController()
41
+ ->getModule_IPs()
42
+ ->getDbHandler_BotSignals();
43
  $fields = array_map(
44
  function ( $col ) {
45
  return str_replace( '_at', '', $col );
46
  },
47
  array_filter(
48
+ $botSignalDBH->getTableSchema()->getColumnNames(),
 
 
 
 
49
  function ( $col ) {
50
  return preg_match( '#_at$#', $col ) &&
51
  !in_array( $col, [ 'snsent_at', 'updated_at', 'deleted_at' ] );
61
 
62
  return $fields;
63
  }
64
+
65
+ protected function getRecord() :EntryVO {
66
+ return $this->getEntryVO();
67
+ }
68
  }
src/lib/src/Modules/IPs/Lib/Bots/Calculator/CalculateVisitorBotScores.php CHANGED
@@ -2,8 +2,8 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\DB\BotSignal\BotSignalRecord;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\BotSignalsRecord;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
9
  use FernleafSystems\Wordpress\Services\Services;
@@ -17,7 +17,7 @@ class CalculateVisitorBotScores {
17
 
18
  public function scores() :array {
19
  $this->scores = ( new BuildScores() )
20
- ->setRecord( $this->loadRecord() )
21
  ->setMod( $this->getMod() )
22
  ->build();
23
  return $this->getActiveScores();
@@ -40,12 +40,11 @@ class CalculateVisitorBotScores {
40
  );
41
  }
42
 
43
- private function loadRecord() :BotSignalRecord {
44
  $ip = $this->getIP();
45
  if ( empty( $ip ) ) {
46
  $ip = Services::IP()->getRequestIp();
47
  }
48
-
49
  try {
50
  $entry = ( new BotSignalsRecord() )
51
  ->setMod( $this->getMod() )
@@ -53,7 +52,7 @@ class CalculateVisitorBotScores {
53
  ->retrieve();
54
  }
55
  catch ( \Exception $e ) {
56
- $entry = new BotSignalRecord();
57
  $entry->ip = $ip;
58
  }
59
 
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\BotSignals\EntryVO;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
 
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\BotSignalsRecord;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
9
  use FernleafSystems\Wordpress\Services\Services;
17
 
18
  public function scores() :array {
19
  $this->scores = ( new BuildScores() )
20
+ ->setEntryVO( $this->loadEntry() )
21
  ->setMod( $this->getMod() )
22
  ->build();
23
  return $this->getActiveScores();
40
  );
41
  }
42
 
43
+ private function loadEntry() :EntryVO {
44
  $ip = $this->getIP();
45
  if ( empty( $ip ) ) {
46
  $ip = Services::IP()->getRequestIp();
47
  }
 
48
  try {
49
  $entry = ( new BotSignalsRecord() )
50
  ->setMod( $this->getMod() )
52
  ->retrieve();
53
  }
54
  catch ( \Exception $e ) {
55
+ $entry = new EntryVO();
56
  $entry->ip = $ip;
57
  }
58
 
src/lib/src/Modules/IPs/Lib/Bots/NotBot/InsertNotBotJs.php CHANGED
@@ -12,33 +12,10 @@ class InsertNotBotJs extends ExecOnceModConsumer {
12
  protected function canRun() :bool {
13
  $req = Services::Request();
14
  return $req->query( 'force_notbot' ) == 1
15
- || $this->isForcedForOptimisationPlugins()
16
  || ( $req->ts() - ( new BotSignalsRecord() )
17
  ->setMod( $this->getMod() )
18
  ->setIP( Services::IP()->getRequestIp() )
19
- ->retrieveNotBotAt() ) > MINUTE_IN_SECONDS*45;
20
- }
21
-
22
- /**
23
- * Looks for the presence of certain caching plugins and forces notbot to load.
24
- */
25
- private function isForcedForOptimisationPlugins() :bool {
26
- return (bool)apply_filters(
27
- 'shield/notbot_force_load',
28
- $this->getOptions()->isOpt( 'force_notbot', 'Y' )
29
- ||
30
- !empty( array_intersect(
31
- array_map( 'basename', Services::WpPlugins()->getActivePlugins() ),
32
- [
33
- 'breeze.php',
34
- 'wpFastestCache.php',
35
- 'wp-cache.php', // Super Cache
36
- 'wp-hummingbird.php',
37
- 'sg-cachepress.php',
38
- 'autoptimize.php',
39
- ]
40
- ) ) > 0
41
- );
42
  }
43
 
44
  protected function run() {
12
  protected function canRun() :bool {
13
  $req = Services::Request();
14
  return $req->query( 'force_notbot' ) == 1
 
15
  || ( $req->ts() - ( new BotSignalsRecord() )
16
  ->setMod( $this->getMod() )
17
  ->setIP( Services::IP()->getRequestIp() )
18
+ ->retrieve()->notbot_at ) > MINUTE_IN_SECONDS*45;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  }
20
 
21
  protected function run() {
src/lib/src/Modules/IPs/Lib/Bots/ShieldNET/BuildData.php CHANGED
@@ -2,7 +2,8 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\ShieldNET;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\DB\BotSignal;
 
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
  use FernleafSystems\Wordpress\Services\Services;
@@ -14,9 +15,9 @@ class BuildData {
14
 
15
  public function build( bool $quiet = false ) :array {
16
 
17
- $records = $this->getRecords();
18
  if ( !$quiet ) {
19
- $this->markRecordsAsSent( $records );
20
  }
21
 
22
  $records = array_filter( array_map(
@@ -47,7 +48,7 @@ class BuildData {
47
 
48
  return $record;
49
  },
50
- $records
51
  ) );
52
 
53
  // We order with preference towards IPs with more signals.
@@ -79,7 +80,7 @@ class BuildData {
79
  }
80
 
81
  /**
82
- * @param BotSignal\BotSignalRecord[] $records
83
  */
84
  private function markRecordsAsSent( array $records ) {
85
  if ( !empty( $records ) ) {
@@ -88,7 +89,7 @@ class BuildData {
88
  Services::WpDb()
89
  ->doSql(
90
  sprintf( 'UPDATE `%s` SET `snsent_at`=%s WHERE `id` in (%s);',
91
- $mod->getDbH_BotSignal()->getTableSchema()->table,
92
  Services::Request()->ts(),
93
  implode( ',', array_map( function ( $record ) {
94
  return $record->id;
@@ -100,38 +101,18 @@ class BuildData {
100
 
101
  /**
102
  * Optimised to ensure that only signals are sent if they've been updated since the last SNAPI-Send
103
- * @return BotSignal\BotSignalRecord[]
104
  */
105
  private function getRecords() :array {
106
  /** @var ModCon $mod */
107
  $mod = $this->getMod();
108
-
109
- $serverIPs = array_map(
110
- function ( $ip ) {
111
- return sprintf( "INET6_ATON('%s')", $ip );
112
- },
113
- is_array( Services::IP()->getServerPublicIPs() ) ? Services::IP()->getServerPublicIPs() : []
114
- );
115
-
116
- $records = Services::WpDb()->selectCustom(
117
- sprintf( "SELECT ips.ip, bs.*
118
- FROM `%s` as bs
119
- INNER JOIN `%s` as ips
120
- ON `ips`.id = `bs`.ip_ref
121
- %s
122
- ORDER BY `bs`.`updated_at` DESC
123
- LIMIT 200;",
124
- $mod->getDbH_BotSignal()->getTableSchema()->table,
125
- $this->getCon()->getModule_Data()->getDbH_IPs()->getTableSchema()->table,
126
- empty( $serverIPs ) ? '' : sprintf( "AND `ips`.`ip` NOT IN (%s)", implode( ",", $serverIPs ) )
127
- )
128
- );
129
-
130
- return array_map(
131
- function ( $record ) {
132
- return ( new BotSignal\BotSignalRecord() )->applyFromArray( $record );
133
- },
134
- is_array( $records ) ? $records : []
135
- );
136
  }
137
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\ShieldNET;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\BotSignals\EntryVO;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\BotSignals\Select;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
9
  use FernleafSystems\Wordpress\Services\Services;
15
 
16
  public function build( bool $quiet = false ) :array {
17
 
18
+ $recordsToSend = $this->getRecords();
19
  if ( !$quiet ) {
20
+ $this->markRecordsAsSent( $recordsToSend );
21
  }
22
 
23
  $records = array_filter( array_map(
48
 
49
  return $record;
50
  },
51
+ $recordsToSend
52
  ) );
53
 
54
  // We order with preference towards IPs with more signals.
80
  }
81
 
82
  /**
83
+ * @param EntryVO[] $records
84
  */
85
  private function markRecordsAsSent( array $records ) {
86
  if ( !empty( $records ) ) {
89
  Services::WpDb()
90
  ->doSql(
91
  sprintf( 'UPDATE `%s` SET `snsent_at`=%s WHERE `id` in (%s);',
92
+ $mod->getDbHandler_BotSignals()->getTableSchema()->table,
93
  Services::Request()->ts(),
94
  implode( ',', array_map( function ( $record ) {
95
  return $record->id;
101
 
102
  /**
103
  * Optimised to ensure that only signals are sent if they've been updated since the last SNAPI-Send
104
+ * @return EntryVO[]
105
  */
106
  private function getRecords() :array {
107
  /** @var ModCon $mod */
108
  $mod = $this->getMod();
109
+ /** @var Select $select */
110
+ $select = $mod->getDbHandler_BotSignals()->getQuerySelector();
111
+ $records = $select->setLimit( 200 )
112
+ ->setOrderBy( 'updated_at', 'DESC' )
113
+ ->addWhereNotIn( 'ip', array_map( 'inet_pton', Services::IP()->getServerPublicIPs() ) )
114
+ ->addWhereCompareColumns( 'updated_at', 'snsent_at', '>' )
115
+ ->query();
116
+ return is_array( $records ) ? $records : [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  }
118
  }
src/lib/src/Modules/IPs/Lib/IpAnalyse/BuildDisplay.php CHANGED
@@ -3,10 +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\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\Data\Lib\GeoIP\Lookup;
10
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
11
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\BotSignalsRecord;
12
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator\CalculateVisitorBotScores;
@@ -15,8 +13,6 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\LookupIpOnList;
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,9 +80,10 @@ class BuildDisplay {
84
  ->lookup( true );
85
 
86
  $geo = ( new Lookup() )
87
- ->setCon( $con )
88
  ->setIP( $ip )
89
  ->lookupIp();
 
90
 
91
  $sRDNS = gethostbyaddr( $ip );
92
 
@@ -180,9 +177,9 @@ class BuildDisplay {
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,19 +187,19 @@ class BuildDisplay {
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,53 +245,26 @@ class BuildDisplay {
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
 
289
- if ( empty( $asArray[ 'path' ] ) ) {
290
- $asArray = null;
 
 
 
291
  }
 
 
292
  $requests[ $key ] = $asArray;
293
  }
294
 
295
- // remove duds after conversion @12.0
296
- $requests = array_filter( $requests );
297
-
298
  return $this->getMod()->renderTemplate(
299
  '/wpadmin_pages/insights/ips/ip_analyse/ip_traffic.twig',
300
  [
@@ -392,26 +362,28 @@ class BuildDisplay {
392
  }
393
 
394
  private function renderForAuditTrail() :string {
395
- // TODO: IP Filtering at the SQL query level
396
- $logRecords = ( new LoadLogs() )
397
- ->setMod( $this->getCon()->getModule_AuditTrail() )
398
- ->setIP( $this->getIP() )
399
- ->run();
400
-
401
- $logs = [];
402
- $srvEvents = $this->getCon()->loadEventsService();
403
- foreach ( $logRecords as $key => $record ) {
404
- if ( $srvEvents->eventExists( $record->event_slug ) ) {
405
- $asArray = $record->getRawData();
 
 
 
 
 
 
406
 
407
- $asArray[ 'event' ] = implode( ' ', AuditMessageBuilder::BuildFromLogRecord( $record ) );
408
- $asArray[ 'created_at' ] = $this->formatTimestampField( $record->created_at );
409
 
410
- $user = empty( $record->meta_data[ 'uid' ] ) ? null
411
- : Services::WpUsers()->getUserById( $record->meta_data[ 'uid' ] );
412
- $asArray[ 'user' ] = empty( $user ) ? '-' : $user->user_login;
413
- $logs[ $key ] = $asArray;
414
- }
415
  }
416
 
417
  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\Lib\AuditMessageBuilder;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\GeoIp\Lookup;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
9
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\BotSignalsRecord;
10
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator\CalculateVisitorBotScores;
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
  ->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
  '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
  '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
  }
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
 
 
 
 
268
  return $this->getMod()->renderTemplate(
269
  '/wpadmin_pages/insights/ips/ip_analyse/ip_traffic.twig',
270
  [
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(
src/lib/src/Modules/IPs/Lib/IpAnalyse/FindAllPluginIps.php CHANGED
@@ -3,7 +3,6 @@
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,9 +20,17 @@ class FindAllPluginIps {
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
 
@@ -34,6 +41,13 @@ class FindAllPluginIps {
34
  ->getQuerySelector();
35
  $ips = array_merge( $ips, $sel->getDistinctForColumn( 'ip' ) );
36
 
 
 
 
 
 
 
 
37
  return IpListSort::Sort( array_unique( $ips ) );
38
  }
39
  }
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
  ->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
 
41
  ->getQuerySelector();
42
  $ips = array_merge( $ips, $sel->getDistinctForColumn( 'ip' ) );
43
 
44
+ // Bot Signal
45
+ /** @var Databases\BotSignals\Select $sel */
46
+ $sel = $con->getModule_IPs()
47
+ ->getDbHandler_BotSignals()
48
+ ->getQuerySelector();
49
+ $ips = array_merge( $ips, $sel->getDistinctIps() );
50
+
51
  return IpListSort::Sort( array_unique( $ips ) );
52
  }
53
  }
src/lib/src/Modules/IPs/Lib/OffenseTracker.php CHANGED
@@ -23,14 +23,17 @@ class OffenseTracker extends EventsListener {
23
  */
24
  protected function captureEvent( string $evt, $meta = [], $def = [] ) {
25
  if ( !empty( $def[ 'offense' ] ) && empty( $meta[ 'suppress_offense' ] ) ) {
26
- $this->incrementCount( (int)( $meta[ 'offense_count' ] ?? 1 ) );
27
  if ( !empty( $meta[ 'block' ] ) ) {
28
  $this->setIsBlocked( true );
29
  }
30
  }
31
  }
32
 
33
- public function hasVisitorOffended() :bool {
 
 
 
34
  return $this->isBlocked() || $this->getOffenseCount() > 0;
35
  }
36
 
23
  */
24
  protected function captureEvent( string $evt, $meta = [], $def = [] ) {
25
  if ( !empty( $def[ 'offense' ] ) && empty( $meta[ 'suppress_offense' ] ) ) {
26
+ $this->incrementCount( (int)( $meta[ 'offense_count' ] ?? 1) );
27
  if ( !empty( $meta[ 'block' ] ) ) {
28
  $this->setIsBlocked( true );
29
  }
30
  }
31
  }
32
 
33
+ /**
34
+ * @return bool
35
+ */
36
+ public function hasVisitorOffended() {
37
  return $this->isBlocked() || $this->getOffenseCount() > 0;
38
  }
39
 
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_params' => [ 'ip' => $this->getIP() ] ] );
45
  }
46
  }
47
 
@@ -76,10 +76,6 @@ class AddIp {
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,10 +92,10 @@ class AddIp {
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,7 +150,7 @@ class AddIp {
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
 
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
  throw new \Exception( "IP address isn't valid." );
77
  }
78
 
 
 
 
 
79
  $IP = null;
80
  if ( !in_array( $ip, $srvIP->getServerPublicIPs() ) ) {
81
 
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
  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
 
src/lib/src/Modules/IPs/Lib/Ops/ConvertLegacy.php DELETED
@@ -1,97 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\BotSignals;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\IPRecords;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
8
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\DB\BotSignal;
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_BotSignals();
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_BotSignals();
35
-
36
- $toDelete = [];
37
-
38
- /** @var BotSignals\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
- /**
59
- * @param BotSignals\EntryVO $entry
60
- * @return bool
61
- * @throws \Exception
62
- */
63
- protected function createPrimaryLogRecord( BotSignals\EntryVO $entry ) :bool {
64
- /** @var ModCon $mod */
65
- $mod = $this->getMod();
66
- $dbh = $mod->getDbH_BotSignal();
67
-
68
- if ( empty( $entry->ip ) ) {
69
- throw new \Exception( 'No IP' );
70
- }
71
-
72
- $ipRecord = ( new IPRecords() )
73
- ->setMod( $this->getCon()->getModule_Data() )
74
- ->loadIP( $entry->ip );
75
- unset( $entry->ip );
76
-
77
- /** @var BotSignal\Ops\Select $selector */
78
- $selector = $dbh->getQuerySelector();
79
- if ( $selector->filterByIP( $ipRecord->id )->count() > 0 ) {
80
- throw new \Exception( 'Record already exists' );
81
- }
82
-
83
- /** @var BotSignal\Ops\Record $record */
84
- $record = $dbh->getRecord()->applyFromArray( $entry->getRawData() );
85
- unset( $record->id );
86
- $record->ip_ref = $ipRecord->id;
87
-
88
- $success = $mod->getDbH_BotSignal()
89
- ->getQueryInserter()
90
- ->insert( $record );
91
- if ( !$success ) {
92
- throw new \Exception( 'Failed to insert' );
93
- }
94
-
95
- return true;
96
- }
97
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_params' => [ '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' => [ '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( 'shield/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 $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,27 +57,27 @@ class ProcessOffenses extends ExecOnceModConsumer {
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
  );
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
  }
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
  ->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
  );
src/lib/src/Modules/IPs/ModCon.php CHANGED
@@ -43,9 +43,8 @@ class ModCon extends BaseShield\ModCon {
43
  return $this->oBlacklistHandler;
44
  }
45
 
46
- public function getDbH_BotSignal() :DB\BotSignal\Ops\Handler {
47
- $this->getCon()->getModule_Data()->getDbH_IPs();
48
- return $this->getDbHandler()->loadDbH( 'botsignal' );
49
  }
50
 
51
  public function getDbHandler_IPs() :Shield\Databases\IPs\Handler {
@@ -155,28 +154,4 @@ class ModCon extends BaseShield\ModCon {
155
  }
156
  return $text;
157
  }
158
-
159
- /**
160
- * @deprecated 12.0
161
- */
162
- protected function cleanupDatabases() {
163
- $dbhIPs = $this->getDbHandler_IPs();
164
- if ( $dbhIPs->isReady() ) {
165
- $dbhIPs->autoCleanDb();
166
- }
167
- $this->getDbH_BotSignal()
168
- ->getQueryDeleter()
169
- ->addWhereOlderThan(
170
- Services::Request()->carbon()->subWeeks( 1 )->timestamp,
171
- 'updated_at'
172
- )
173
- ->query();
174
- }
175
-
176
- /**
177
- * @deprecated 12.0
178
- */
179
- public function getDbHandler_BotSignals() :Shield\Databases\BotSignals\Handler {
180
- return $this->getDbH( 'botsignals' );
181
- }
182
  }
43
  return $this->oBlacklistHandler;
44
  }
45
 
46
+ public function getDbHandler_BotSignals() :Shield\Databases\BotSignals\Handler {
47
+ return $this->getDbH( 'botsignals' );
 
48
  }
49
 
50
  public function getDbHandler_IPs() :Shield\Databases\IPs\Handler {
154
  }
155
  return $text;
156
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  }
src/lib/src/Modules/IPs/Strings.php CHANGED
@@ -7,156 +7,6 @@ use FernleafSystems\Wordpress\Services\Services;
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,7 +87,7 @@ class Strings extends Base\Strings {
237
  return [
238
  'title' => $title,
239
  'title_short' => $titleShort,
240
- 'summary' => $summary,
241
  ];
242
  }
243
 
@@ -248,22 +98,22 @@ class Strings extends Base\Strings {
248
  */
249
  public function getOptionStrings( string $key ) :array {
250
 
251
- $pluginName = $this->getCon()->getHumanName();
252
- $modName = $this->getMod()->getMainFeatureName();
253
 
254
  switch ( $key ) {
255
 
256
  case 'enable_ips' :
257
- $name = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $modName );
258
- $summary = sprintf( __( 'Enable (or Disable) The %s Module', 'wp-simple-firewall' ), $modName );
259
- $desc = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), $modName );
260
  break;
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' ), $pluginName ),
267
  __( 'When the number of these offenses exceeds the limit, they are automatically blocked from accessing the site.', 'wp-simple-firewall' ),
268
  sprintf( __( 'Set this to "0" to turn off the %s feature.', 'wp-simple-firewall' ), __( 'Automatic IP Black List', 'wp-simple-firewall' ) )
269
  ];
@@ -320,20 +170,6 @@ class Strings extends Base\Strings {
320
  ];
321
  break;
322
 
323
- case 'force_notbot' :
324
- $name = __( 'Force NotBot JS', 'wp-simple-firewall' );
325
- $summary = __( 'Force Loading Of NotBot JS', 'wp-simple-firewall' );
326
- $desc = [
327
- sprintf( __( '%s uses Javascript to help identify bots versus legitimate visitors.', 'wp-simple-firewall' ),
328
- $pluginName )
329
- .' '.__( "However, caching plugins often interfere, preventing it loading for your visitors.", 'wp-simple-firewall' ),
330
- __( "This may cause some of your legitimate users to be identified as bots, when they're not.", 'wp-simple-firewall' ),
331
- __( "Turn this option on if you're using an aggressive caching system, to ensure NotBot JS is loaded for all visitors.", 'wp-simple-firewall' ),
332
- __( "When this option is disabled we'll automatically optimise loading of the Javascript so it's only loaded where it's required.", 'wp-simple-firewall' )
333
- .' '.__( "You should test your site and keep a lookout for user login issues after disabling this option.", 'wp-simple-firewall' )
334
- ];
335
- break;
336
-
337
  case 'text_loginfailed' :
338
  $name = __( 'Login Failed', 'wp-simple-firewall' );
339
  $summary = __( 'Visitor Triggers The IP Offense System Through A Failed Login', 'wp-simple-firewall' );
@@ -467,4 +303,68 @@ class Strings extends Base\Strings {
467
  'bypass' => __( 'IP Bypassed', 'wp-simple-firewall' ),
468
  ];
469
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
470
  }
7
 
8
  class Strings extends Base\Strings {
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  /**
11
  * @param string $section
12
  * @return array
87
  return [
88
  'title' => $title,
89
  'title_short' => $titleShort,
90
+ 'summary' => ( isset( $summary ) && is_array( $summary ) ) ? $summary : [],
91
  ];
92
  }
93
 
98
  */
99
  public function getOptionStrings( string $key ) :array {
100
 
101
+ $sPlugName = $this->getCon()->getHumanName();
102
+ $sModName = $this->getMod()->getMainFeatureName();
103
 
104
  switch ( $key ) {
105
 
106
  case 'enable_ips' :
107
+ $name = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $sModName );
108
+ $summary = sprintf( __( 'Enable (or Disable) The %s Module', 'wp-simple-firewall' ), $sModName );
109
+ $desc = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), $sModName );
110
  break;
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' ),
118
  sprintf( __( 'Set this to "0" to turn off the %s feature.', 'wp-simple-firewall' ), __( 'Automatic IP Black List', 'wp-simple-firewall' ) )
119
  ];
170
  ];
171
  break;
172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  case 'text_loginfailed' :
174
  $name = __( 'Login Failed', 'wp-simple-firewall' );
175
  $summary = __( 'Visitor Triggers The IP Offense System Through A Failed Login', 'wp-simple-firewall' );
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
  }
src/lib/src/Modules/IPs/Upgrade.php CHANGED
@@ -4,15 +4,10 @@ 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
 
8
  class Upgrade extends Base\Upgrade {
9
 
10
- protected function upgrade_1201() {
11
- ( new Lib\Ops\ConvertLegacy() )
12
- ->setMod( $this->getMod() )
13
- ->run();
14
- }
15
-
16
  protected function upgrade_1010() {
17
  /** @var ModCon $mod */
18
  $mod = $this->getMod();
@@ -20,4 +15,43 @@ class Upgrade extends Base\Upgrade {
20
  $del = $mod->getDbHandler_IPs()->getQueryDeleter();
21
  $del->filterByLabel( 'iControlWP' )->query();
22
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
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
 
 
 
 
 
 
 
11
  protected function upgrade_1010() {
12
  /** @var ModCon $mod */
13
  $mod = $this->getMod();
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
  }
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_trail';
131
  $subItems = [
132
  [
133
  'slug' => $slug.'-log',
@@ -135,15 +135,10 @@ class SideMenuBuilder {
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,6 +437,12 @@ class SideMenuBuilder {
442
  'title' => __( 'Configure', 'wp-simple-firewall' ),
443
  'href' => $con->getModule_Traffic()->getUrl_DirectLinkToSection( 'section_traffic_options' ),
444
  ],
 
 
 
 
 
 
445
  ];
446
 
447
  return [
127
  /** @var ModCon $mod */
128
  $mod = $this->getMod();
129
 
130
+ $slug = 'audit';
131
  $subItems = [
132
  [
133
  'slug' => $slug.'-log',
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
  '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 [
src/lib/src/Modules/Insights/ModCon.php CHANGED
@@ -142,20 +142,24 @@ class ModCon extends BaseShield\ModCon {
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
- $enq[ Enqueue::JS ][] = 'shield/tables';
153
  if ( in_array( $inav, [ 'scans_results', 'scans_run' ] ) ) {
154
- $enq[ Enqueue::JS ][] = 'shield/scans';
155
  }
156
  elseif ( $inav == 'ips' ) {
157
  $enq[ Enqueue::JS ][] = 'shield/ipanalyse';
158
  }
 
 
 
 
 
159
  break;
160
  }
161
  }
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
  }
src/lib/src/Modules/Insights/Strings.php CHANGED
@@ -6,6 +6,50 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
 
7
  class Strings extends Base\Strings {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  /**
10
  * @inheritDoc
11
  */
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
  */
src/lib/src/Modules/Insights/UI.php CHANGED
@@ -15,7 +15,6 @@ class UI extends BaseShield\UI {
15
  return [
16
  'content' => [
17
  'tab_updates' => $this->renderTabUpdates(),
18
- 'tab_events' => $this->renderTabEvents(),
19
  ],
20
  'flags' => [
21
  'is_pro' => $con->isPremiumActive(),
@@ -24,9 +23,8 @@ class UI extends BaseShield\UI {
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,7 +64,6 @@ class UI extends BaseShield\UI {
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,15 +134,10 @@ class UI extends BaseShield\UI {
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,7 +189,6 @@ class UI extends BaseShield\UI {
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,51 +265,6 @@ class UI extends BaseShield\UI {
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() )
15
  return [
16
  'content' => [
17
  'tab_updates' => $this->renderTabUpdates(),
 
18
  ],
19
  'flags' => [
20
  'is_pro' => $con->isPremiumActive(),
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
  switch ( $inav ) {
65
 
66
  case 'audit':
 
67
  $modAudit = $con->getModule_AuditTrail();
68
  /** @var Shield\Modules\AuditTrail\UI $auditUI */
69
  $auditUI = $modAudit->getUIHandler();
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
  '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
  );
266
  }
267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  private function renderTabUpdates() :string {
269
  try {
270
  $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_params' => [
18
  'form_provider' => $this->getProviderName(),
19
  ]
20
  ]
14
  $this->getCon()->fireEvent(
15
  sprintf( 'spam_form_%s', $isSpam ? 'fail' : 'pass' ),
16
  [
17
+ 'audit' => [
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_params' => [
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' => [
82
  'form_provider' => $this->getProviderName(),
83
  'action' => $this->getAuditAction(),
84
  'username' => $this->getAuditUser(),
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/Buddyboss.php DELETED
@@ -1,30 +0,0 @@
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,6 +2,10 @@
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,6 +23,6 @@ class Buddypress extends Base {
19
  }
20
 
21
  public static function IsProviderInstalled() :bool {
22
- return @class_exists( '\BuddyPress' );
23
  }
24
  }
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
  }
24
 
25
  public static function IsProviderInstalled() :bool {
26
+ return @class_exists( 'BuddyPress' );
27
  }
28
  }
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/EasyDigitalDownloads.php CHANGED
@@ -2,10 +2,14 @@
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() {
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() {
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/LearnPress.php CHANGED
@@ -2,6 +2,10 @@
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,7 +13,7 @@ class LearnPress extends Base {
9
  }
10
 
11
  protected function register() {
12
- add_filter( 'learn-press/register-validate-field', [ $this, 'checkRegister_LP' ], 100 );
13
  }
14
 
15
  /**
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
  }
14
 
15
  protected function register() {
16
+ add_filter( 'learn-press/register-validate-field', [ $this, 'checkRegister_LP' ], 100, 1 );
17
  }
18
 
19
  /**
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/LifterLMS.php CHANGED
@@ -4,6 +4,8 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\
4
 
5
  /**
6
  * Lost Password is mimicked after WordPress so no separate integration necessary
 
 
7
  */
8
  class LifterLMS extends Base {
9
 
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
 
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/PaidMemberSubscriptions.php CHANGED
@@ -2,6 +2,10 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers;
4
 
 
 
 
 
5
  class PaidMemberSubscriptions extends Base {
6
 
7
  protected function register() {
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() {
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/ProfileBuilder.php CHANGED
@@ -4,6 +4,9 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\
4
 
5
  /**
6
  * https://wordpress.org/plugins/profile-builder/
 
 
 
7
  */
8
  class ProfileBuilder extends Base {
9
 
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
 
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/UltimateMember.php CHANGED
@@ -4,6 +4,9 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\
4
 
5
  /**
6
  * https://wordpress.org/plugins/ultimate-member/
 
 
 
7
  */
8
  class UltimateMember extends Base {
9
 
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
 
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/WPMembers.php CHANGED
@@ -4,6 +4,9 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\
4
 
5
  /**
6
  * https://wordpress.org/plugins/wp-members/
 
 
 
7
  */
8
  class WPMembers extends Base {
9
 
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
 
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/WooCommerce.php CHANGED
@@ -2,6 +2,10 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\UserForms\Handlers;
4
 
 
 
 
 
5
  class WooCommerce extends Base {
6
 
7
  protected function login() {
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() {
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/Handlers/WordPress.php CHANGED
@@ -4,6 +4,10 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\
4
 
5
  use FernleafSystems\Wordpress\Services\Services;
6
 
 
 
 
 
7
  class WordPress extends Base {
8
 
9
  protected function login() {
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() {
src/lib/src/Modules/Integrations/Lib/Bots/UserForms/UserFormsController.php CHANGED
@@ -27,7 +27,6 @@ class UserFormsController extends Integrations\Lib\Bots\Common\BaseBotDetectionC
27
  */
28
  public function enumProviders() :array {
29
  return [
30
- new Handlers\Buddyboss(),
31
  new Handlers\Buddypress(),
32
  new Handlers\EasyDigitalDownloads(),
33
  new Handlers\LearnPress(),
27
  */
28
  public function enumProviders() :array {
29
  return [
 
30
  new Handlers\Buddypress(),
31
  new Handlers\EasyDigitalDownloads(),
32
  new Handlers\LearnPress(),
src/lib/src/Modules/Integrations/Lib/Spam/Handlers/Base.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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,31 +9,19 @@ class Strings extends Base\Strings {
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
  }
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
  }
src/lib/src/Modules/License/ModCon.php CHANGED
@@ -56,12 +56,10 @@ class ModCon extends BaseShield\ModCon {
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
  }
56
  }
57
 
58
  public function onPluginShutdown() {
59
+ try {
60
+ $this->getLicenseHandler()->verify( false );
61
+ }
62
+ catch ( \Exception $e ) {
 
 
63
  }
64
  parent::onPluginShutdown();
65
  }
src/lib/src/Modules/License/Strings.php CHANGED
@@ -6,32 +6,6 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
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,4 +35,21 @@ class Strings extends Base\Strings {
61
  'last_errors' => __( 'Error', 'wp-simple-firewall' ),
62
  ];
63
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  }
6
 
7
  class Strings extends Base\Strings {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  /**
10
  * @inheritDoc
11
  */
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
  }
src/lib/src/Modules/Lockdown/Processor.php CHANGED
@@ -7,8 +7,6 @@ use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class Processor extends BaseShield\Processor {
9
 
10
- private $xmlProcessed = false;
11
-
12
  protected function run() {
13
  /** @var Options $opts */
14
  $opts = $this->getOptions();
@@ -47,17 +45,17 @@ class Processor extends BaseShield\Processor {
47
 
48
  add_filter( 'user_has_cap',
49
  /**
50
- * @param array $allCaps
51
  * @param array $cap
52
- * @param array $args
53
  * @return array
54
  */
55
- function ( $allCaps, $cap, $args ) {
56
- $requestedCapability = $args[ 0 ];
57
- if ( in_array( $requestedCapability, [ 'edit_themes', 'edit_plugins', 'edit_files' ] ) ) {
58
- $allCaps[ $requestedCapability ] = false;
59
  }
60
- return $allCaps;
61
  },
62
  PHP_INT_MAX, 3
63
  );
@@ -79,10 +77,7 @@ class Processor extends BaseShield\Processor {
79
  * @return array|false
80
  */
81
  public function disableXmlrpc() {
82
- if ( !$this->xmlProcessed ) {
83
- $this->xmlProcessed = true;
84
- $this->getCon()->fireEvent( 'block_xml' );
85
- }
86
  return ( current_filter() == 'xmlrpc_enabled' ) ? false : [];
87
  }
88
 
@@ -115,11 +110,11 @@ class Processor extends BaseShield\Processor {
115
  public function disableAnonymousRestApi( $mStatus ) {
116
  /** @var ModCon $mod */
117
  $mod = $this->getMod();
118
- $WPRest = Services::Rest();
119
 
120
- $namespace = $WPRest->getNamespace();
121
- if ( !empty( $namespace ) && $mStatus !== true && !is_wp_error( $mStatus )
122
- && !$mod->isPermittedAnonRestApiNamespace( $namespace ) ) {
123
 
124
  $mStatus = new \WP_Error(
125
  'shield_block_anon_restapi',
@@ -130,7 +125,7 @@ class Processor extends BaseShield\Processor {
130
  $this->getCon()
131
  ->fireEvent(
132
  'block_anonymous_restapi',
133
- [ 'audit_params' => [ 'namespace' => $namespace ] ]
134
  );
135
  }
136
 
7
 
8
  class Processor extends BaseShield\Processor {
9
 
 
 
10
  protected function run() {
11
  /** @var Options $opts */
12
  $opts = $this->getOptions();
45
 
46
  add_filter( 'user_has_cap',
47
  /**
48
+ * @param array $aAllCaps
49
  * @param array $cap
50
+ * @param array $aArgs
51
  * @return array
52
  */
53
+ function ( $aAllCaps, $cap, $aArgs ) {
54
+ $sRequestedCapability = $aArgs[ 0 ];
55
+ if ( in_array( $sRequestedCapability, [ 'edit_themes', 'edit_plugins', 'edit_files' ] ) ) {
56
+ $aAllCaps[ $sRequestedCapability ] = false;
57
  }
58
+ return $aAllCaps;
59
  },
60
  PHP_INT_MAX, 3
61
  );
77
  * @return array|false
78
  */
79
  public function disableXmlrpc() {
80
+ $this->getCon()->fireEvent( 'block_xml' );
 
 
 
81
  return ( current_filter() == 'xmlrpc_enabled' ) ? false : [];
82
  }
83
 
110
  public function disableAnonymousRestApi( $mStatus ) {
111
  /** @var ModCon $mod */
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
  $this->getCon()
126
  ->fireEvent(
127
  'block_anonymous_restapi',
128
+ [ 'audit' => [ 'namespace' => $sNamespace ] ]
129
  );
130
  }
131
 
src/lib/src/Modules/Lockdown/Strings.php CHANGED
@@ -7,21 +7,12 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
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,40 +27,40 @@ class Strings extends Base\Strings {
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,9 +68,9 @@ class Strings extends Base\Strings {
77
  }
78
 
79
  return [
80
- 'title' => $title,
81
- 'title_short' => $titleShort,
82
- 'summary' => $summary,
83
  ];
84
  }
85
 
@@ -95,56 +86,56 @@ class Strings extends Base\Strings {
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,9 +144,9 @@ class Strings extends Base\Strings {
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,9 +155,9 @@ class Strings extends Base\Strings {
164
  }
165
 
166
  return [
167
- 'name' => $name,
168
- 'summary' => $summary,
169
- 'description' => $description,
170
  ];
171
  }
172
  }
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
  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
  }
69
 
70
  return [
71
+ 'title' => $sTitle,
72
+ 'title_short' => $sTitleShort,
73
+ 'summary' => ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : [],
74
  ];
75
  }
76
 
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
  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
  }
156
 
157
  return [
158
+ 'name' => $sName,
159
+ 'summary' => $sSummary,
160
+ 'description' => $sDescription,
161
  ];
162
  }
163
  }
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_params' => [
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_params' => [
96
  'user_login' => $username,
97
  'action' => $action,
98
  ]
80
  $this->getCon()->fireEvent(
81
  'botbox_fail',
82
  [
83
+ 'audit' => [
84
  'user_login' => $username,
85
  'action' => $action,
86
  ]
92
  $this->getCon()->fireEvent(
93
  'honeypot_fail',
94
  [
95
+ 'audit' => [
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 );
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,41 +63,42 @@ class RenameLogin {
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,11 +124,11 @@ class RenameLogin {
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,54 +139,53 @@ class RenameLogin {
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,8 +202,6 @@ class RenameLogin {
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
  }
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
  /** @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
  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
  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
 
203
  public function aLoadWpLogin() {
204
  if ( Services::WpGeneral()->isLoginUrl() ) {
 
 
205
  @require_once( ABSPATH.'wp-login.php' );
206
  die();
207
  }
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php CHANGED
@@ -185,7 +185,7 @@ class MfaController {
185
  }
186
  );
187
 
188
- // BackupCode should NEVER be the only 1 provider available.
189
  if ( count( $Ps ) === 1 ) {
190
  /** @var Provider\BaseProvider $first */
191
  $first = reset( $Ps );
185
  }
186
  );
187
 
188
+ // Neither BackupCode NOR U2F should EVER be the only 1 provider available.
189
  if ( count( $Ps ) === 1 ) {
190
  /** @var Provider\BaseProvider $first */
191
  $first = reset( $Ps );
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BackupCodes.php CHANGED
@@ -8,7 +8,6 @@ use FernleafSystems\Wordpress\Services\Services;
8
  class BackupCodes extends BaseProvider {
9
 
10
  const SLUG = 'backupcode';
11
- const BYPASS_MFA = true;
12
  const STANDALONE = false;
13
 
14
  public function getProviderName() :string {
@@ -85,11 +84,27 @@ class BackupCodes extends BaseProvider {
85
 
86
  /**
87
  * @param \WP_User $user
88
- * @param string $OTP
89
  * @return bool
90
  */
91
- private function validateBackupCode( \WP_User $user, $OTP ) :bool {
92
- return (bool)wp_check_password( str_replace( '-', '', $OTP ), $this->getSecret( $user ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  }
94
 
95
  /**
8
  class BackupCodes extends BaseProvider {
9
 
10
  const SLUG = 'backupcode';
 
11
  const STANDALONE = false;
12
 
13
  public function getProviderName() :string {
84
 
85
  /**
86
  * @param \WP_User $user
87
+ * @param string $sOtpCode
88
  * @return bool
89
  */
90
+ private function validateBackupCode( \WP_User $user, $sOtpCode ) :bool {
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
  /**
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BaseProvider.php CHANGED
@@ -10,15 +10,9 @@ abstract class BaseProvider {
10
  use Modules\ModConsumer;
11
 
12
  const SLUG = '';
13
-
14
- /**
15
- * Set to true if this provider can be used to validate 2FA even if MFA is active.
16
- */
17
- const BYPASS_MFA = false;
18
-
19
  /**
20
  * Set to true if this provider can be used in isolation. False if there
21
- * must be at least 1 other 2FA provider active alongside it.
22
  */
23
  const STANDALONE = true;
24
  /**
@@ -37,15 +31,17 @@ abstract class BaseProvider {
37
 
38
  /**
39
  * Assumes this is only called on active profiles
 
 
40
  */
41
- public function validateLoginIntent( \WP_User $user ) :bool {
42
- $otpSuccess = false;
43
- $OTP = $this->fetchCodeFromRequest();
44
- if ( !empty( $OTP ) ) {
45
- $otpSuccess = $this->processOtp( $user, $OTP );
46
- $this->postOtpProcessAction( $user, $otpSuccess );
47
  }
48
- return $otpSuccess;
49
  }
50
 
51
  /**
@@ -224,17 +220,7 @@ abstract class BaseProvider {
224
  return [];
225
  }
226
 
227
- protected function auditLogin( \WP_User $user, bool $success ) {
228
- $this->getCon()->fireEvent(
229
- $success ? '2fa_verify_success' : '2fa_verify_fail',
230
- [
231
- 'audit_params' => [
232
- 'user_login' => $user->user_login,
233
- 'method' => $this->getProviderName(),
234
- ]
235
- ]
236
- );
237
- }
238
 
239
  /**
240
  * @param \WP_User $user
10
  use Modules\ModConsumer;
11
 
12
  const SLUG = '';
 
 
 
 
 
 
13
  /**
14
  * Set to true if this provider can be used in isolation. False if there
15
+ * must be at least 1 other 2FA provider active.
16
  */
17
  const STANDALONE = true;
18
  /**
31
 
32
  /**
33
  * Assumes this is only called on active profiles
34
+ * @param \WP_User $user
35
+ * @return bool
36
  */
37
+ public function validateLoginIntent( \WP_User $user ) {
38
+ $bOtpSuccess = false;
39
+ $sReqOtpCode = $this->fetchCodeFromRequest();
40
+ if ( !empty( $sReqOtpCode ) ) {
41
+ $bOtpSuccess = $this->processOtp( $user, $sReqOtpCode );
42
+ $this->postOtpProcessAction( $user, $bOtpSuccess );
43
  }
44
+ return $bOtpSuccess;
45
  }
46
 
47
  /**
220
  return [];
221
  }
222
 
223
+ abstract protected function auditLogin( \WP_User $user, bool $bIsSuccess );
 
 
 
 
 
 
 
 
 
 
224
 
225
  /**
226
  * @param \WP_User $user
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Email.php CHANGED
@@ -24,6 +24,22 @@ class Email extends BaseProvider {
24
  ];
25
  }
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  /**
28
  * @param \WP_User $user
29
  * @return $this
@@ -163,6 +179,15 @@ class Email extends BaseProvider {
163
  $sendSuccess = false;
164
  }
165
 
 
 
 
 
 
 
 
 
 
166
  return $this;
167
  }
168
 
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
  $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
 
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/GoogleAuth.php CHANGED
@@ -212,6 +212,22 @@ class GoogleAuth extends BaseProvider {
212
  return $valid;
213
  }
214
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  /**
216
  * @param \WP_User $user
217
  * @return string
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
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/U2F.php CHANGED
@@ -12,7 +12,6 @@ use u2flib_server\SignRequest;
12
  class U2F extends BaseProvider {
13
 
14
  const SLUG = 'u2f';
15
- const BYPASS_MFA = true;
16
  const DEFAULT_SECRET = '[]';
17
 
18
  public function isProfileActive( \WP_User $user ) :bool {
@@ -310,6 +309,22 @@ class U2F extends BaseProvider {
310
  return !empty( $oRegistration );
311
  }
312
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  public function isProviderEnabled() :bool {
314
  /** @var LoginGuard\Options $opts */
315
  $opts = $this->getOptions();
12
  class U2F extends BaseProvider {
13
 
14
  const SLUG = 'u2f';
 
15
  const DEFAULT_SECRET = '[]';
16
 
17
  public function isProfileActive( \WP_User $user ) :bool {
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();
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' => wp_create_nonce( 'shield-yubikey-verify-'.$otp ),
148
  'id' => $opts->getYubikeyAppId()
149
  ];
150
 
@@ -216,6 +216,22 @@ class Yubikey extends BaseProvider {
216
  return $this->setSecret( $user, implode( ',', array_unique( array_filter( $IDs ) ) ) );
217
  }
218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  /**
220
  * @return array
221
  */
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
  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
  */
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/ValidateLoginIntentRequest.php CHANGED
@@ -4,6 +4,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFact
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
 
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class ValidateLoginIntentRequest {
@@ -15,49 +16,48 @@ class ValidateLoginIntentRequest {
15
  * @throws \Exception
16
  */
17
  public function run() :bool {
18
- $mfaCon = $this->getMfaCon();
19
  /** @var LoginGuard\Options $opts */
20
- $opts = $mfaCon->getOptions();
21
 
22
  $user = Services::WpUsers()->getCurrentWpUser();
23
  if ( !$user instanceof \WP_User ) {
24
  throw new \Exception( 'No user logged-in.' );
25
  }
26
-
27
- $providers = $mfaCon->getProvidersForUser( $user, true );
28
  if ( empty( $providers ) ) {
29
  throw new \Exception( 'No valid providers' );
30
  }
31
 
32
- $providerStates = [];
33
- $successfulProviders = [];
34
- foreach ( $providers as $slug => $provider ) {
35
- $providerStates[ $slug ] = $provider->validateLoginIntent( $user );
36
- if ( $providerStates[ $slug ] ) {
37
- $successfulProviders[ $slug ] = $provider;
38
- }
39
- }
40
 
41
  $validated = false;
42
-
43
- foreach ( $providers as $slug => $provider ) {
44
- if ( $provider::BYPASS_MFA ) {
45
- if ( $providerStates[ $slug ] ) {
46
  $validated = true;
 
47
  }
48
- unset( $providers[ $slug ] );
49
- unset( $providerStates[ $slug ] );
50
  }
51
  }
52
 
53
- if ( !$validated ) {
54
- $countSuccessful = count( array_filter( $providerStates ) );
55
- $validated = $opts->isChainedAuth() ? $countSuccessful == count( $providers ) : $countSuccessful > 0;
 
 
 
 
 
 
 
 
56
  }
57
 
58
  if ( $validated ) {
59
  // Some cleanup can only run if login is completely tested and completely valid.
60
- foreach ( $successfulProviders as $provider ) {
61
  $provider->postSuccessActions( $user );
62
  }
63
  }
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor\Provider;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
10
  class ValidateLoginIntentRequest {
16
  * @throws \Exception
17
  */
18
  public function run() :bool {
19
+ $oMfaCon = $this->getMfaCon();
20
  /** @var LoginGuard\Options $opts */
21
+ $opts = $oMfaCon->getOptions();
22
 
23
  $user = Services::WpUsers()->getCurrentWpUser();
24
  if ( !$user instanceof \WP_User ) {
25
  throw new \Exception( 'No user logged-in.' );
26
  }
27
+ $providers = $oMfaCon->getProvidersForUser( $user, true );
 
28
  if ( empty( $providers ) ) {
29
  throw new \Exception( 'No valid providers' );
30
  }
31
 
32
+ $aSuccessfulProviders = [];
 
 
 
 
 
 
 
33
 
34
  $validated = false;
35
+ { // Backup code is special case
36
+ if ( isset( $providers[ Provider\BackupCodes::SLUG ] ) ) {
37
+ if ( $providers[ Provider\BackupCodes::SLUG ]->validateLoginIntent( $user ) ) {
 
38
  $validated = true;
39
+ $aSuccessfulProviders[] = $providers[ Provider\BackupCodes::SLUG ];
40
  }
41
+ unset( $providers[ Provider\BackupCodes::SLUG ] );
 
42
  }
43
  }
44
 
45
+ if ( !$validated ) {
46
+ $aStates = [];
47
+ foreach ( $providers as $sSlug => $provider ) {
48
+ $aStates[ $sSlug ] = $provider->validateLoginIntent( $user );
49
+ if ( $aStates[ $sSlug ] ) {
50
+ $aSuccessfulProviders[] = $provider;
51
+ }
52
+ }
53
+
54
+ $nSuccessful = count( array_filter( $aStates ) );
55
+ $validated = $opts->isChainedAuth() ? $nSuccessful == count( $providers ) : $nSuccessful > 0;
56
  }
57
 
58
  if ( $validated ) {
59
  // Some cleanup can only run if login is completely tested and completely valid.
60
+ foreach ( $aSuccessfulProviders as $provider ) {
61
  $provider->postSuccessActions( $user );
62
  }
63
  }
src/lib/src/Modules/LoginGuard/Strings.php CHANGED
@@ -7,57 +7,48 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
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,37 +63,37 @@ class Strings extends Base\Strings {
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,27 +101,27 @@ class Strings extends Base\Strings {
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,9 +132,9 @@ class Strings extends Base\Strings {
141
  }
142
 
143
  return [
144
- 'title' => $title,
145
- 'title_short' => $titleShort,
146
- 'summary' => $summary,
147
  ];
148
  }
149
 
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
  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
  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
  }
133
 
134
  return [
135
+ 'title' => $sTitle,
136
+ 'title_short' => $sTitleShort,
137
+ 'summary' => ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : [],
138
  ];
139
  }
140
 
src/lib/src/Modules/Plugin/AdminNotices.php CHANGED
@@ -21,6 +21,10 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
21
  $this->buildNotice_PluginTooOld( $notice );
22
  break;
23
 
 
 
 
 
24
  case 'override-forceoff':
25
  $this->buildNotice_OverrideForceoff( $notice );
26
  break;
@@ -107,6 +111,30 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
107
  ];
108
  }
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  private function buildNotice_OverrideForceoff( NoticeVO $notice ) {
111
  $name = $this->getCon()->getHumanName();
112
 
@@ -303,6 +331,10 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
303
  $needed = $con->getIfForceOffActive();
304
  break;
305
 
 
 
 
 
306
  case 'plugin-disabled':
307
  $needed = $opts->isPluginGloballyDisabled();
308
  break;
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
  ];
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
  $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;
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 ( $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 ( !in_array( $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,12 +2,11 @@
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,12 +46,12 @@ class BadgeWidget extends \WP_Widget {
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
  // );
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
  }
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
  // );
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();
35
  } );
36
  }
37
 
@@ -59,7 +59,11 @@ class PluginBadge extends Modules\Base\Common\ExecOnceModConsumer {
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,32 +101,34 @@ class PluginBadge extends Modules\Base\Common\ExecOnceModConsumer {
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 {
31
  add_action( 'widgets_init', [ $this, 'addPluginBadgeWidget' ] );
32
 
33
  add_shortcode( 'SHIELD_BADGE', function () {
34
+ $this->render( false );
35
  } );
36
  }
37
 
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
  $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 {
src/lib/src/Modules/Plugin/Debug.php CHANGED
@@ -3,35 +3,10 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\ShieldNET\BuildData;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Tests\RunTests;
8
 
9
  class Debug extends Modules\Base\Debug {
10
 
11
  public function run() {
12
- $this->tests();
13
  die( 'finish' );
14
  }
15
-
16
- private function testbotsignals() {
17
- $r = ( new BuildData() )
18
- ->setMod( $this->getCon()->getModule_IPs() )
19
- ->build( true );
20
- var_dump( $r );
21
- }
22
-
23
- private function getIpRefs() {
24
- $ipRefs = $this->getCon()
25
- ->getModule_Data()
26
- ->getDbH_ReqLogs()
27
- ->getQuerySelector()
28
- ->getDistinctForColumn( 'ip_ref' );
29
- var_dump( $ipRefs );
30
- }
31
-
32
- private function tests() {
33
- ( new RunTests() )
34
- ->setCon( $this->getCon() )
35
- ->run();
36
- }
37
  }
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
  }
src/lib/src/Modules/Plugin/Insights/DashboardCards.php CHANGED
@@ -217,7 +217,7 @@ class DashboardCards {
217
  'actions' => [
218
  [
219
  'text' => __( "View Audit Log", 'wp-simple-firewall' ),
220
- 'href' => $modInsights->getUrl_SubInsightsPage( 'audit_trail' ),
221
  ],
222
  [
223
  'text' => __( "Audit Trail Settings", 'wp-simple-firewall' ),
217
  'actions' => [
218
  [
219
  'text' => __( "View Audit Log", 'wp-simple-firewall' ),
220
+ 'href' => $modInsights->getUrl_SubInsightsPage( 'audit' ),
221
  ],
222
  [
223
  'text' => __( "Audit Trail Settings", 'wp-simple-firewall' ),
src/lib/src/Modules/Plugin/Insights/OverviewCards.php CHANGED
@@ -6,7 +6,6 @@ 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
- use ZxcvbnPhp\Zxcvbn;
10
 
11
  class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
12
 
@@ -68,11 +67,11 @@ class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
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' => '',
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
  $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' => '',
src/lib/src/Modules/Plugin/Lib/Debug/Collate.php CHANGED
@@ -165,33 +165,28 @@ class Collate {
165
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
166
  : 'Missing';
167
 
168
- $dbh = $con->getModule_AuditTrail()->getDbH_Logs();
169
  $data[ 'DB Table: Audit Trail' ] = $dbh->isReady() ?
170
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
171
  : 'Missing';
172
 
173
- $dbh = $con->getModule_Data()->getDbH_IPs();
174
- $data[ 'DB Table: IPs' ] = $dbh->isReady() ?
175
- sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
176
- : 'Missing';
177
-
178
  $dbh = $con->getModule_IPs()->getDbHandler_IPs();
179
- $data[ 'DB Table: IP Lists' ] = $dbh->isReady() ?
180
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
181
  : 'Missing';
182
 
183
- $dbh = $con->getModule_IPs()->getDbH_BotSignal();
184
  $data[ 'DB Table: Bot Signals' ] = $dbh->isReady() ?
185
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
186
  : 'Missing';
187
 
188
  $dbh = $con->getModule_HackGuard()->getDbHandler_ScanResults();
189
- $data[ 'DB Table: Scan Results' ] = $dbh->isReady() ?
190
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
191
  : 'Missing';
192
 
193
- $dbh = $con->getModule_Data()->getDbH_ReqLogs();
194
- $data[ 'DB Table: Traffic/Requests' ] = $dbh->isReady() ?
195
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
196
  : 'Missing';
197
 
165
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
166
  : 'Missing';
167
 
168
+ $dbh = $con->getModule_AuditTrail()->getDbHandler_AuditTrail();
169
  $data[ 'DB Table: Audit Trail' ] = $dbh->isReady() ?
170
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
171
  : 'Missing';
172
 
 
 
 
 
 
173
  $dbh = $con->getModule_IPs()->getDbHandler_IPs();
174
+ $data[ 'DB Table: IP' ] = $dbh->isReady() ?
175
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
176
  : 'Missing';
177
 
178
+ $dbh = $con->getModule_IPs()->getDbHandler_BotSignals();
179
  $data[ 'DB Table: Bot Signals' ] = $dbh->isReady() ?
180
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
181
  : 'Missing';
182
 
183
  $dbh = $con->getModule_HackGuard()->getDbHandler_ScanResults();
184
+ $data[ 'DB Table: Scan' ] = $dbh->isReady() ?
185
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
186
  : 'Missing';
187
 
188
+ $dbh = $con->getModule_Traffic()->getDbHandler_Traffic();
189
+ $data[ 'DB Table: Traffic' ] = $dbh->isReady() ?
190
  sprintf( '%s (rows: ~%s)', 'Ready', $dbh->getQuerySelector()->count() )
191
  : 'Missing';
192
 
src/lib/src/Modules/Plugin/Lib/Debug/RecentEvents.php CHANGED
@@ -3,6 +3,7 @@
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,47 +30,48 @@ class RecentEvents {
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
  }
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
  }
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
  }
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
- $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,40 +61,41 @@ class Export {
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,23 +115,26 @@ class Export {
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,7 +144,7 @@ class Export {
140
  * @param string $url
141
  * @return bool
142
  */
143
- private function verifyUrlWithHandshake( $url ):bool {
144
  $bVerified = false;
145
 
146
  if ( !empty( $url ) ) {
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
 
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
  );
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
  * @param string $url
145
  * @return bool
146
  */
147
+ private function verifyUrlWithHandshake( $url ) {
148
  $bVerified = false;
149
 
150
  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 $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,7 +132,7 @@ class Import {
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,8 +143,8 @@ class Import {
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,7 +166,7 @@ class Import {
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,7 +189,7 @@ class Import {
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,10 +199,10 @@ class Import {
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,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_params' => [ 'site' => $source ] ]
237
  );
238
  }
239
  }
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
  }
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
  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
  }
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
  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
  }
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
  $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' => [ '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_params' => [ 'master_site' => $opts->getImportExportMasterImportUrl() ] ]
117
  );
118
  }
119
  }
@@ -124,12 +124,15 @@ class ImportExportController {
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() {
113
 
114
  $this->getCon()->fireEvent(
115
  'import_notify_received',
116
+ [ 'audit' => [ '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 $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() {
src/lib/src/Modules/Plugin/Lib/ImportExport/Options/BuildTransferableOptions.php CHANGED
@@ -9,9 +9,9 @@ class BuildTransferableOptions {
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 ),
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 ),
src/lib/src/Modules/Plugin/ModCon.php CHANGED
@@ -78,11 +78,9 @@ class ModCon extends BaseShield\ModCon {
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,7 +158,7 @@ class ModCon extends BaseShield\ModCon {
160
  }
161
 
162
  public function getActivePluginFeatures() :array {
163
- $features = $this->getOptions()->getDef( 'active_plugin_features' );
164
 
165
  $available = [];
166
  if ( is_array( $features ) ) {
@@ -477,7 +475,7 @@ class ModCon extends BaseShield\ModCon {
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,6 +528,10 @@ class ModCon extends BaseShield\ModCon {
530
  return $enqs;
531
  }
532
 
 
 
 
 
533
  public function getDbHandler_Notes() :Shield\Databases\AdminNotes\Handler {
534
  return $this->getDbH( 'notes' );
535
  }
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
  }
159
 
160
  public function getActivePluginFeatures() :array {
161
+ $features = $this->getDef( 'active_plugin_features' );
162
 
163
  $available = [];
164
  if ( is_array( $features ) ) {
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
  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
  }
src/lib/src/Modules/Plugin/Options.php CHANGED
@@ -30,6 +30,11 @@ class Options extends BaseShield\Options {
30
  return $this->getOpt( 'visitor_address_source' );
31
  }
32
 
 
 
 
 
 
33
  /**
34
  * @return bool
35
  */
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
  */
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 ) {
36
- return $isDelete || $this->getOptions()->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 ) use ( $opts ) {
36
+ return $isDelete || $opts->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,111 +11,57 @@ class Strings extends Base\Strings {
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
 
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
 
src/lib/src/Modules/Plugin/UI.php CHANGED
@@ -66,15 +66,15 @@ class UI extends BaseShield\UI {
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,9 +85,9 @@ class UI extends BaseShield\UI {
85
  }
86
  }
87
  }
88
- $option[ 'value_options' ] = $newOptions;
89
  }
90
- return $option;
91
  }
92
 
93
  protected function getSectionWarnings( string $section ) :array {
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
  }
86
  }
87
  }
88
+ $aOptParams[ 'value_options' ] = $newOptions;
89
  }
90
+ return $aOptParams;
91
  }
92
 
93
  protected function getSectionWarnings( string $section ) :array {
src/lib/src/Modules/Plugin/Upgrade.php CHANGED
@@ -3,7 +3,6 @@
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,22 +11,4 @@ class Upgrade extends Base\Upgrade {
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
  }
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
  $mod = $this->getMod();
12
  $mod->deleteAllPluginCrons();
13
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  }
src/lib/src/Modules/Plugin/WpCli/ForceOff.php CHANGED
@@ -32,16 +32,11 @@ class ForceOff extends BaseWpCliCmd {
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.' );
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.' );
src/lib/src/Modules/Plugin/WpCli/ToggleDebug.php CHANGED
@@ -32,11 +32,6 @@ class ToggleDebug extends BaseWpCliCmd {
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
 
32
  ] ) );
33
  }
34
 
 
 
 
 
 
35
  public function cmdDebugMode( $null, $args ) {
36
  $debugMode = ( new DebugMode() )->setCon( $this->getCon() );
37
 
src/lib/src/Modules/Reporting/Charts/BaseBuildChartData.php CHANGED
@@ -50,9 +50,10 @@ class BaseBuildChartData {
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;
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;
src/lib/src/Modules/Reporting/Debug.php CHANGED
@@ -10,6 +10,5 @@ class Debug extends Modules\Base\Debug {
10
  /** @var ModCon $mod */
11
  $mod = $this->getMod();
12
  $mod->getReportingController()->runHourlyCron();
13
- die( 'finish' );
14
  }
15
  }
10
  /** @var ModCon $mod */
11
  $mod = $this->getMod();
12
  $mod->getReportingController()->runHourlyCron();
 
13
  }
14
  }
src/lib/src/Modules/Reporting/Lib/ReportingController.php CHANGED
@@ -2,14 +2,17 @@
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,16 +37,10 @@ class ReportingController extends Modules\Base\Common\ExecOnceModConsumer {
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,16 +49,10 @@ class ReportingController extends Modules\Base\Common\ExecOnceModConsumer {
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,12 +151,6 @@ class ReportingController extends Modules\Base\Common\ExecOnceModConsumer {
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() );
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
 
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
 
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
  ]
152
  ]
153
  );
 
 
 
 
 
 
154
  }
155
  catch ( \Exception $e ) {
156
  error_log( $e->getMessage() );
src/lib/src/Modules/Reporting/Strings.php CHANGED
@@ -7,22 +7,18 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
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,33 +29,33 @@ class Strings extends Base\Strings {
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,9 +64,9 @@ class Strings extends Base\Strings {
68
  }
69
 
70
  return [
71
- 'name' => $name,
72
- 'summary' => $summary,
73
- 'description' => $description,
74
  ];
75
  }
76
 
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
  * @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
  }
65
 
66
  return [
67
+ 'name' => $sName,
68
+ 'summary' => $sSummary,
69
+ 'description' => $sDescription,
70
  ];
71
  }
72
 
src/lib/src/Modules/Reporting/UI.php CHANGED
@@ -44,13 +44,14 @@ class UI extends BaseShield\UI {
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
 
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
 
src/lib/src/Modules/SecurityAdmin/Strings.php CHANGED
@@ -7,21 +7,15 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
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,9 +31,9 @@ class Strings extends Base\Strings {
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,26 +41,26 @@ class Strings extends Base\Strings {
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,7 +72,7 @@ class Strings extends Base\Strings {
78
  $sPlugName )
79
  )
80
  ];
81
- $titleShort = __( 'White Label', 'wp-simple-firewall' );
82
  break;
83
 
84
  default:
@@ -86,9 +80,9 @@ class Strings extends Base\Strings {
86
  }
87
 
88
  return [
89
- 'title' => $title,
90
- 'title_short' => $titleShort,
91
- 'summary' => $summary,
92
  ];
93
  }
94
 
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
  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
  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
  $sPlugName )
73
  )
74
  ];
75
+ $sTitleShort = __( 'White Label', 'wp-simple-firewall' );
76
  break;
77
 
78
  default:
80
  }
81
 
82
  return [
83
+ 'title' => $sTitle,
84
+ 'title_short' => $sTitleShort,
85
+ 'summary' => ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : [],
86
  ];
87
  }
88
 
src/lib/src/Modules/Sessions/Lib/SessionController.php CHANGED
@@ -50,20 +50,11 @@ class SessionController {
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,12 +82,7 @@ class SessionController {
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,6 +101,20 @@ class SessionController {
115
  return (string)$this->sessionID;
116
  }
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  /**
119
  * @param string $username
120
  * @param string $sessionID
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
  $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
  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
src/lib/src/Modules/Sessions/Processor.php CHANGED
@@ -42,18 +42,25 @@ class Processor extends BaseShield\Processor {
42
  }
43
 
44
  public function onWpLoaded() {
45
- /** @var ModCon $mod */
46
- $mod = $this->getMod();
47
-
48
  if ( Services::WpUsers()->isUserLoggedIn() && !Services::Rest()->isRest() ) {
49
  $this->autoAddSession();
50
  }
 
51
 
52
- if ( $mod->getSessionCon()->hasSession() ) {
53
- /** @var Session\Update $update */
54
- $update = $mod->getDbHandler_Sessions()->getQueryUpdater();
55
- $update->updateLastActivity( $mod->getSessionCon()->getCurrent() );
 
 
 
 
 
 
 
56
  }
 
 
57
  }
58
 
59
  private function autoAddSession() {
@@ -94,11 +101,4 @@ class Processor extends BaseShield\Processor {
94
  protected function getHookPriority() :int {
95
  return 100;
96
  }
97
-
98
- /**
99
- * @deprecated 12.0
100
- */
101
- public function onModuleShutdown() {
102
- parent::onModuleShutdown();
103
- }
104
  }
42
  }
43
 
44
  public function onWpLoaded() {
 
 
 
45
  if ( Services::WpUsers()->isUserLoggedIn() && !Services::Rest()->isRest() ) {
46
  $this->autoAddSession();
47
  }
48
+ }
49
 
50
+ public function onModuleShutdown() {
51
+ /** @var ModCon $mod */
52
+ $mod = $this->getMod();
53
+
54
+ if ( !Services::Rest()->isRest() && !$this->getCon()->plugin_deleting ) {
55
+ $session = $mod->getSessionCon()->getCurrent();
56
+ if ( $session instanceof Session\EntryVO ) {
57
+ /** @var Session\Update $update */
58
+ $update = $mod->getDbHandler_Sessions()->getQueryUpdater();
59
+ $update->updateLastActivity( $session );
60
+ }
61
  }
62
+
63
+ parent::onModuleShutdown();
64
  }
65
 
66
  private function autoAddSession() {
101
  protected function getHookPriority() :int {
102
  return 100;
103
  }
 
 
 
 
 
 
 
104
  }
src/lib/src/Modules/Sessions/Strings.php CHANGED
@@ -6,38 +6,6 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
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
6
 
7
  class Strings extends Base\Strings {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  /**
10
  * @param string $section
11
  * @return array
src/lib/src/Modules/Sessions/Upgrade.php CHANGED
@@ -3,7 +3,20 @@
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
  }
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
  }
src/lib/src/Modules/Statistics/Options.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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,9 +9,8 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
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,18 +20,15 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
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
  }
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
  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
  }
src/lib/src/Modules/Traffic/Lib/Limit/Limiter.php CHANGED
@@ -2,45 +2,44 @@
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
  }
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
  }
src/lib/src/Modules/Traffic/Lib/Limit/RateLimitExceededException.php DELETED
@@ -1,19 +0,0 @@
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 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\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 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\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 DELETED
@@ -1,56 +0,0 @@
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,9 +7,6 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
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,9 +25,9 @@ class Logger {
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,5 +77,31 @@ class Logger {
80
  }
81
 
82
  private function logTraffic() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  }
84
  }
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
 
 
 
10
  class Logger {
11
 
12
  use ModConsumer;
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
  }
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
  }
src/lib/src/Modules/Traffic/Lib/Ops/ConvertLegacy.php DELETED
@@ -1,88 +0,0 @@
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 DELETED
@@ -1,101 +0,0 @@
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 DELETED
@@ -1,44 +0,0 @@
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 DELETED
@@ -1,211 +0,0 @@
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\Data\DB\IPs\IPGeoVO;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\Lib\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\Modules\Data\DB\ReqLogs\LoadLogs;
10
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\ReqLogs\LogRecord;
11
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib\Ops\ConvertLegacy;
12
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
13
- use FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\LoadData\BaseLoadTableData;
14
- use FernleafSystems\Wordpress\Services\Services;
15
-
16
- class LoadRawTableData extends BaseLoadTableData {
17
-
18
- use ModConsumer;
19
-
20
- /**
21
- * @var LogRecord
22
- */
23
- private $log;
24
-
25
- /**
26
- * @var Lookup
27
- */
28
- private $geoLookup;
29
-
30
- private $users = [];
31
-
32
- private $ipInfo = [];
33
-
34
- public function loadForLogs() :array {
35
- ( new ConvertLegacy() )
36
- ->setMod( $this->getMod() )
37
- ->run();
38
-
39
- $this->users = [ 0 => __( 'No', 'wp-simple-firewall' ) ];
40
-
41
- return array_values( array_filter( array_map(
42
- function ( $log ) {
43
- /**
44
- * @deprecated 12.0 - this just removes dud entries from after the conversion.
45
- */
46
- if ( empty( @$log->meta[ 'path' ] ) ) {
47
- return null;
48
- }
49
-
50
- $WPU = Services::WpUsers();
51
-
52
- $log->meta = array_merge(
53
- [
54
- 'path' => '',
55
- 'code' => '200',
56
- 'ua' => 'Unknown',
57
- 'verb' => 'Unknown',
58
- 'offense' => false,
59
- 'uid' => 0
60
- ],
61
- $log->meta
62
- );
63
-
64
- $this->log = $log;
65
-
66
- $data = $log->getRawData();
67
-
68
- $data[ 'ip' ] = $this->log->ip;
69
- $data[ 'page' ] = $this->log->ip;
70
- $data[ 'code' ] = $this->log->meta[ 'code' ];
71
- $data[ 'offense' ] = $this->log->meta[ 'offense' ] ? 'Offense' : 'Not Offense';
72
- $data[ 'rid' ] = $this->log->rid ?? __( 'Unknown', 'wp-simple-firewall' );
73
- $data[ 'path' ] = empty( $this->log->meta[ 'path' ] ) ? '-'
74
- : explode( '?', $this->log->meta[ 'path' ], 2 )[ 0 ];
75
-
76
- $geo = $this->getCountryIP( $this->log->ip );
77
- $data[ 'country' ] = empty( $geo->countryCode ) ?
78
- __( 'Unknown', 'wp-simple-firewall' ) : $geo->countryName;
79
-
80
- $userID = $this->log->meta[ 'uid' ] ?? 0;
81
- if ( $userID > 0 ) {
82
- if ( !isset( $users[ $userID ] ) ) {
83
- $user = $WPU->getUserById( $userID );
84
- $this->users[ $userID ] = empty( $user ) ? __( 'Unknown', 'wp-simple-firewall' ) :
85
- sprintf( '<a href="%s" target="_blank" title="Go To Profile">%s</a>',
86
- $WPU->getAdminUrl_ProfileEdit( $user ), $user->user_login );
87
- }
88
- }
89
-
90
- $data[ 'page' ] = $this->getColumnContent_Page();
91
- $data[ 'details' ] = $this->getColumnContent_Details();
92
- $data[ 'response' ] = $this->getColumnContent_Response();
93
- $data[ 'created_since' ] = $this->getColumnContent_Date( $this->log->created_at );
94
- return $data;
95
- },
96
- $this->getLogRecords()
97
- ) ) );
98
- }
99
-
100
- /**
101
- * @return LogRecord[]
102
- */
103
- private function getLogRecords() :array {
104
- return ( new LoadLogs() )
105
- ->setMod( $this->getCon()->getModule_Data() )
106
- ->run();
107
- }
108
-
109
- private function getColumnContent_Details() :string {
110
- $geo = $this->getCountryIP( $this->log->ip );
111
- if ( empty( $geo->countryCode ) ) {
112
- $country = __( 'Unknown', 'wp-simple-firewall' );
113
- }
114
- else {
115
- $country = sprintf(
116
- '<img class="icon-flag" src="%s" alt="%s" width="24px"/> %s',
117
- sprintf( 'https://api.aptoweb.com/api/v1/country/flag/%s.svg', strtolower( $geo->countryCode ) ),
118
- $geo->countryCode,
119
- $geo->countryName
120
- );
121
- }
122
-
123
- return sprintf( '<div>%s</div>', implode( '</div><div>', [
124
- sprintf( '%s: %s', __( 'IP', 'wp-simple-firewall' ), $this->getIpAnalysisLink( $this->log->ip ) ),
125
- sprintf( '%s: %s', __( 'IP Status', 'wp-simple-firewall' ), $this->getIpInfo( $this->log->ip ) ),
126
- sprintf( '%s: %s', __( 'Logged-In', 'wp-simple-firewall' ), $this->users[ $this->log->meta[ 'uid' ] ] ),
127
- sprintf( '%s: %s', __( 'Location', 'wp-simple-firewall' ), $country ),
128
- esc_html( esc_js( sprintf( '%s - %s', __( 'User Agent', 'wp-simple-firewall' ), $this->log->meta[ 'ua' ] ) ) ),
129
- ] ) );
130
- }
131
-
132
- private function getColumnContent_Response() :string {
133
- if ( $this->log->meta[ 'code' ] >= 400 ) {
134
- $codeType = 'danger';
135
- }
136
- elseif ( $this->log->meta[ 'code' ] >= 300 ) {
137
- $codeType = 'warning';
138
- }
139
- else {
140
- $codeType = 'success';
141
- }
142
-
143
- return sprintf( '<div>%s</div>', implode( '</div><div>', [
144
- sprintf( '%s: %s', __( 'Response', 'wp-simple-firewall' ),
145
- sprintf( '<span class="badge badge-%s">%s</span>', $codeType, $this->log->meta[ 'code' ] ) ),
146
- sprintf( '%s: %s', __( 'Offense', 'wp-simple-firewall' ),
147
- sprintf(
148
- '<span class="badge badge-%s">%s</span>',
149
- @$this->log->meta[ 'offense' ] ? 'danger' : 'info',
150
- @$this->log->meta[ 'offense' ] ? __( 'Yes', 'wp-simple-firewall' ) : __( 'No', 'wp-simple-firewall' )
151
- )
152
- ),
153
- ] ) );
154
- }
155
-
156
- private function getColumnContent_Page() :string {
157
- list( $preQuery, $query ) = explode( '?', $this->log->meta[ 'path' ].'?', 2 );
158
- return strtoupper( $this->log->meta[ 'verb' ] ).': <code>'.$preQuery
159
- .( empty( $query ) ? '' : '?<br/>'.rtrim( $query, '?' ) ).'</code>';
160
- }
161
-
162
- private function getIpInfo( string $ip ) {
163
-
164
- if ( !isset( $this->ipInfo[ $ip ] ) ) {
165
-
166
- if ( empty( $ip ) ) {
167
- $this->ipInfo[ '' ] = 'n/a';
168
- }
169
- else {
170
- $badgeTemplate = '<span class="badge badge-%s">%s</span>';
171
- $status = __( 'No Record', 'wp-simple-firewall' );
172
-
173
- $record = ( new LookupIpOnList() )
174
- ->setDbHandler( $this->getCon()->getModule_IPs()->getDbHandler_IPs() )
175
- ->setIP( $ip )
176
- ->lookup();
177
-
178
- if ( empty( $record ) ) {
179
- $status = __( 'No Record', 'wp-simple-firewall' );
180
- }
181
- elseif ( $record->blocked_at > 0 || $record->list === ModCon::LIST_MANUAL_BLACK ) {
182
- $status = sprintf( $badgeTemplate, 'danger', __( 'Blocked', 'wp-simple-firewall' ) );
183
- }
184
- elseif ( $record->list === ModCon::LIST_AUTO_BLACK ) {
185
- $status = sprintf( $badgeTemplate,
186
- 'warning',
187
- sprintf( _n( '%s offense', '%s offenses', $record->transgressions, 'wp-simple-firewall' ), $record->transgressions )
188
- );
189
- }
190
- elseif ( $record->list === ModCon::LIST_MANUAL_WHITE ) {
191
- $status = sprintf( $badgeTemplate,
192
- 'success',
193
- __( 'Bypass', 'wp-simple-firewall' )
194
- );
195
- }
196
- $this->ipInfo[ $ip ] = $status;
197
- }
198
- }
199
-
200
- return $this->ipInfo[ $ip ];
201
- }
202
-
203
- private function getCountryIP( string $ip ) :IPGeoVO {
204
- if ( empty( $this->geoLookup ) ) {
205
- $this->geoLookup = ( new Lookup() )->setCon( $this->getCon() );
206
- }
207
- return $this->geoLookup
208
- ->setIP( $ip )
209
- ->lookupIp();
210
- }
211
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Traffic/ModCon.php CHANGED
@@ -2,23 +2,25 @@
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,28 +41,8 @@ class ModCon extends BaseShield\ModCon {
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
  }
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
  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
  }
src/lib/src/Modules/Traffic/Options.php CHANGED
@@ -7,10 +7,6 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
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,6 +23,10 @@ class Options extends BaseShield\Options {
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,18 +34,11 @@ 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->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
  }
7
  class Options extends BaseShield\Options {
8
 
9
  public function getAutoCleanDays() :int {
 
 
 
 
10
  return (int)$this->getOpt( 'auto_clean' );
11
  }
12
 
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
 
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
  }
src/lib/src/Modules/Traffic/Processor.php CHANGED
@@ -7,12 +7,15 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules;
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
  }
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
  }
src/lib/src/Modules/Traffic/Strings.php CHANGED
@@ -7,15 +7,12 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
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,6 +117,12 @@ class Strings extends Base\Strings {
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' );
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
  $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' );
src/lib/src/Modules/Traffic/UI.php CHANGED
@@ -4,7 +4,6 @@ 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\Plugin\Shield\Tables\DataTables\Build\Traffic\ForTraffic;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
10
  class UI extends BaseShield\UI {
@@ -14,16 +13,14 @@ 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,11 +29,19 @@ class UI extends BaseShield\UI {
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
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
  $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
  '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
src/lib/src/Modules/Traffic/Upgrade.php DELETED
@@ -1,19 +0,0 @@
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 ( $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
- $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
  }
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
  $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
  }
src/lib/src/Modules/UserManagement/Lib/Password/UserPasswordHandler.php CHANGED
@@ -6,7 +6,6 @@ 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
- use ZxcvbnPhp\Zxcvbn;
10
 
11
  /**
12
  * Referenced some of https://github.com/BenjaminNelan/PwnedPasswordChecker
@@ -30,7 +29,7 @@ class UserPasswordHandler extends ExecOnceModConsumer {
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,16 +70,14 @@ class UserPasswordHandler extends ExecOnceModConsumer {
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,27 +103,24 @@ class UserPasswordHandler extends ExecOnceModConsumer {
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,14 +137,14 @@ class UserPasswordHandler extends ExecOnceModConsumer {
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,7 +152,7 @@ class UserPasswordHandler extends ExecOnceModConsumer {
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,15 +203,15 @@ class UserPasswordHandler extends ExecOnceModConsumer {
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
  }
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
  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
  /** @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
  * @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
  $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
  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
  * @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
  }
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
- $invalidBecause = null;
41
  if ( !is_email( $email ) ) {
42
- $invalidBecause = '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
- $invalidBecause = $sVerifyKey;
56
  break;
57
  }
58
  }
@@ -60,14 +60,14 @@ class EmailValidate {
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',
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
  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
  }
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',
src/lib/src/Modules/UserManagement/Lib/Session/UserSessionHandler.php CHANGED
@@ -40,7 +40,6 @@ class UserSessionHandler extends ExecOnceModConsumer {
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,13 +49,7 @@ class UserSessionHandler extends ExecOnceModConsumer {
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,7 +70,7 @@ class UserSessionHandler extends ExecOnceModConsumer {
77
  ->getModule_Sessions()
78
  ->getSessionCon()
79
  ->getCurrent();
80
- if ( empty( $sess ) ) {
81
  throw new \Exception( 'session_notfound' );
82
  }
83
 
@@ -95,6 +88,7 @@ class UserSessionHandler extends ExecOnceModConsumer {
95
  if ( $opts->isLockToIp() && $srvIP->getRequestIp() != $sess->ip ) {
96
  throw new \Exception( 'session_iplock' );
97
  }
 
98
  }
99
 
100
  /**
@@ -140,31 +134,36 @@ class UserSessionHandler extends ExecOnceModConsumer {
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
  }
40
  $con = $this->getCon();
41
  $srvIP = Services::IP();
42
 
 
43
  try {
44
  if ( !empty( $srvIP->isValidIp( $srvIP->getRequestIp() ) ) ) {
45
  $this->assessSession();
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
  ->getModule_Sessions()
71
  ->getSessionCon()
72
  ->getCurrent();
73
+ if ( !$sess instanceof EntryVO ) {
74
  throw new \Exception( 'session_notfound' );
75
  }
76
 
88
  if ( $opts->isLockToIp() && $srvIP->getRequestIp() != $sess->ip ) {
89
  throw new \Exception( 'session_iplock' );
90
  }
91
+ // TODO: 'session_browserlock';
92
  }
93
 
94
  /**
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
  }
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 );
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
- $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
  }
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
 
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
  }
src/lib/src/Modules/UserManagement/ModCon.php CHANGED
@@ -70,55 +70,56 @@ class ModCon extends BaseShield\ModCon {
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
  }
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
  }
src/lib/src/Modules/UserManagement/Processor.php CHANGED
@@ -163,7 +163,11 @@ class Processor extends BaseShield\Processor {
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,69 +188,80 @@ class Processor extends BaseShield\Processor {
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() {
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
  }
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() {
src/lib/src/Modules/UserManagement/Strings.php CHANGED
@@ -7,79 +7,6 @@ use FernleafSystems\Wordpress\Services\Services;
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,18 +33,18 @@ class Strings extends Base\Strings {
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,36 +52,36 @@ class Strings extends Base\Strings {
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,9 +92,9 @@ class Strings extends Base\Strings {
165
  }
166
 
167
  return [
168
- 'title' => $title,
169
- 'title_short' => $titleShort,
170
- 'summary' => $summary,
171
  ];
172
  }
173
 
@@ -183,142 +110,142 @@ class Strings extends Base\Strings {
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,9 +253,60 @@ class Strings extends Base\Strings {
326
  }
327
 
328
  return [
329
- 'name' => $name,
330
- 'summary' => $summary,
331
- 'description' => $description,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  ];
333
  }
334
  }
7
 
8
  class Strings extends Base\Strings {
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  /**
11
  * @inheritDoc
12
  */
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
  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
  }
93
 
94
  return [
95
+ 'title' => $sTitle,
96
+ 'title_short' => $sTitleShort,
97
+ 'summary' => ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : [],
98
  ];
99
  }
100
 
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
  }
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
  }
src/lib/src/Modules/UserManagement/Suspend/Base.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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,4 +6,14 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
 
7
  class Upgrade extends Base\Upgrade {
8
 
 
 
 
 
 
 
 
 
 
 
9
  }
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
  }
src/lib/src/Scans/Base/BaseFileScanActionVO.php CHANGED
@@ -2,6 +2,9 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
 
 
 
 
5
  /**
6
  * Class BaseFileScanActionVO
7
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Base
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
src/lib/src/Scans/Base/BaseScanActionVO.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
 
5
  use FernleafSystems\Utilities\Data\Adapter\DynProperties;
 
6
 
7
  /**
8
  * Class ScanActionVO
@@ -40,6 +41,16 @@ abstract class BaseScanActionVO {
40
  return new $class();
41
  }
42
 
 
 
 
 
 
 
 
 
 
 
43
  public function getScanNamespace() :string {
44
  try {
45
  $namespace = ( new \ReflectionClass( $this ) )->getNamespaceName();
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
  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();
src/lib/src/Scans/Base/DiffResultForStorage.php CHANGED
@@ -12,28 +12,28 @@ class DiffResultForStorage {
12
 
13
  /**
14
  * The Existing set will be updated to reflect the new current status of the scan
15
- * @param ResultsSet $existingResults - will be updated with all items to DB Update
16
- * @param ResultsSet $newResults - will be adjusted with all item to DB Insert
17
  * @return ResultsSet - A results set of all out-of-date records that need to be deleted.
18
  */
19
- public function diff( $existingResults, $newResults ) {
20
 
21
  $toDelete = new ResultsSet();
22
  $merger = new Scans\Base\BaseMergeItems();
23
 
24
  // 1 Remove items in EXISTING that are not in NEW
25
- foreach ( $existingResults->getAllItems() as $oExistItem ) {
26
- if ( !$newResults->getItemExists( $oExistItem->hash ) ) {
27
- $existingResults->removeItemByHash( $oExistItem->hash );
28
  $toDelete->addItem( $oExistItem );
29
  }
30
  }
31
 
32
  // 2 Merge NEW items into Existing items
33
- foreach ( $newResults->getAllItems() as $oNewItem ) {
34
- if ( $existingResults->getItemExists( $oNewItem->hash ) ) {
35
- $merger->mergeItemTo( $existingResults->getItemByHash( $oNewItem->hash ), $oNewItem );
36
- $newResults->removeItemByHash( $oNewItem->hash );
37
  }
38
  }
39
 
12
 
13
  /**
14
  * The Existing set will be updated to reflect the new current status of the scan
15
+ * @param ResultsSet $oExistingRes - will be updated with all items to DB Update
16
+ * @param ResultsSet $oNewResults - will be adjusted with all item to DB Insert
17
  * @return ResultsSet - A results set of all out-of-date records that need to be deleted.
18
  */
19
+ public function diff( $oExistingRes, $oNewResults ) {
20
 
21
  $toDelete = new ResultsSet();
22
  $merger = new Scans\Base\BaseMergeItems();
23
 
24
  // 1 Remove items in EXISTING that are not in NEW
25
+ foreach ( $oExistingRes->getAllItems() as $oExistItem ) {
26
+ if ( !$oNewResults->getItemExists( $oExistItem->hash ) ) {
27
+ $oExistingRes->removeItemByHash( $oExistItem->hash );
28
  $toDelete->addItem( $oExistItem );
29
  }
30
  }
31
 
32
  // 2 Merge NEW items into Existing items
33
+ foreach ( $oNewResults->getAllItems() as $oNewItem ) {
34
+ if ( $oExistingRes->getItemExists( $oNewItem->hash ) ) {
35
+ $merger->mergeItemTo( $oExistingRes->getItemByHash( $oNewItem->hash ), $oNewItem );
36
+ $oNewResults->removeItemByHash( $oNewItem->hash );
37
  }
38
  }
39
 
src/lib/src/Scans/Base/FileResultItem.php CHANGED
@@ -10,10 +10,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
10
  */
11
  class FileResultItem extends ResultItem {
12
 
13
- public function generateHash() :string {
14
- return md5( $this->path_full );
15
- }
16
-
17
  public function getDescriptionForAudit() :string {
18
  return $this->path_full;
19
  }
10
  */
11
  class FileResultItem extends ResultItem {
12
 
 
 
 
 
13
  public function getDescriptionForAudit() :string {
14
  return $this->path_full;
15
  }
src/lib/src/Scans/Base/Table/BaseEntryFormatter.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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;