Shield Security for WordPress - Version 11.5.0

Version Description

Download this release

Release Info

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

Code changes from version 11.4.5 to 11.5.0

Files changed (238) hide show
  1. cl.json +108 -0
  2. icwp-wpsf.php +1 -1
  3. plugin-spec.php +68 -11
  4. readme.txt +1 -1
  5. resources/css/plugin.css +35 -2
  6. resources/css/shield/scanners.css +0 -0
  7. resources/images/bootstrap/download.svg +4 -0
  8. resources/images/bootstrap/eye-slash-fill.svg +4 -0
  9. resources/images/bootstrap/file-excel.svg +4 -0
  10. resources/images/bootstrap/tools.svg +2 -2
  11. resources/images/bootstrap/x-square.svg +4 -0
  12. resources/js/global-plugin.js +2 -0
  13. resources/js/shield/scanners.js +90 -0
  14. resources/js/shield/scans.js +8 -8
  15. resources/js/shield/scantables.js +294 -0
  16. src/config/feature-hack_protect.php +90 -56
  17. src/config/feature-integrations.php +4 -0
  18. src/config/feature-ips.php +18 -3
  19. src/lib/src/Controller/Assets/Paths.php +9 -1
  20. src/lib/src/Controller/Assets/Urls.php +3 -11
  21. src/lib/src/Controller/Controller.php +10 -58
  22. src/lib/src/Databases/Base/BaseQuery.php +5 -5
  23. src/lib/src/Databases/Base/Select.php +5 -5
  24. src/lib/src/Databases/Events/Handler.php +15 -17
  25. src/lib/src/Databases/FileLocker/EntryVO.php +2 -2
  26. src/lib/src/Databases/ScanQueue/Select.php +46 -23
  27. src/lib/src/Databases/Scanner/EntryVO.php +1 -1
  28. src/lib/src/Databases/Scanner/Insert.php +2 -4
  29. src/lib/src/Databases/Scanner/Select.php +17 -0
  30. src/lib/src/Modules/AuditTrail/Lib/AuditWriter.php +3 -0
  31. src/lib/src/Modules/Autoupdates/Processor.php +6 -6
  32. src/lib/src/Modules/Base/Lib/Rest/Utility/RestLocker.php +1 -2
  33. src/lib/src/Modules/Base/ModCon.php +2 -2
  34. src/lib/src/Modules/Base/Options/WildCardOptions.php +123 -0
  35. src/lib/src/Modules/Base/UI.php +1 -0
  36. src/lib/src/Modules/BaseShield/ModCon.php +3 -1
  37. src/lib/src/Modules/CommentsFilter/ModCon.php +2 -5
  38. src/lib/src/Modules/Email/Processor.php +15 -5
  39. src/lib/src/Modules/Events/Lib/Reports/KeyStats.php +1 -4
  40. src/lib/src/Modules/Events/Lib/Reports/ScanRepairs.php +0 -67
  41. src/lib/src/Modules/Events/Lib/StatsWriter.php +8 -6
  42. src/lib/src/Modules/Events/Reporting.php +0 -9
  43. src/lib/src/Modules/Events/Strings.php +10 -15
  44. src/lib/src/Modules/HackGuard/AjaxHandler.php +42 -16
  45. src/lib/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php +0 -6
  46. src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Accept.php +1 -3
  47. src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Diff.php +12 -13
  48. src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Restore.php +6 -10
  49. src/lib/src/Modules/HackGuard/Lib/Reports/FileLockerAlerts.php +16 -19
  50. src/lib/src/Modules/HackGuard/Lib/Reports/ScanAlerts.php +4 -7
  51. src/lib/src/Modules/HackGuard/Lib/Reports/ScanRepairs.php +90 -0
  52. src/lib/src/Modules/HackGuard/Lib/ScanTables/DelegateAjaxHandler.php +152 -0
  53. src/lib/src/Modules/HackGuard/Lib/ScanTables/LoadRawTableData.php +328 -0
  54. src/lib/src/Modules/HackGuard/Lib/ScanTables/RetrieveFileContents.php +63 -0
  55. src/lib/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesForCrowdSource.php +5 -19
  56. src/lib/src/Modules/HackGuard/Lib/Snapshots/CrowdSourced/SubmitHashes.php +5 -1
  57. src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/CleanAll.php +6 -6
  58. src/lib/src/Modules/HackGuard/Lib/Utility/VerifyFileByHash.php +95 -0
  59. src/lib/src/Modules/HackGuard/ModCon.php +37 -13
  60. src/lib/src/Modules/HackGuard/Options.php +36 -25
  61. src/lib/src/Modules/HackGuard/Processor.php +0 -12
  62. src/lib/src/Modules/HackGuard/Render/ScanResults/SectionBase.php +59 -0
  63. src/lib/src/Modules/HackGuard/Render/ScanResults/SectionLogs.php +65 -0
  64. src/lib/src/Modules/HackGuard/Render/ScanResults/SectionMalware.php +66 -0
  65. src/lib/src/Modules/HackGuard/Render/ScanResults/SectionPluginThemesBase.php +63 -0
  66. src/lib/src/Modules/HackGuard/Render/ScanResults/SectionPlugins.php +144 -0
  67. src/lib/src/Modules/HackGuard/Render/ScanResults/SectionThemes.php +149 -0
  68. src/lib/src/Modules/HackGuard/Render/ScanResults/SectionWordpress.php +64 -0
  69. src/lib/src/Modules/HackGuard/Reporting.php +2 -1
  70. src/lib/src/Modules/HackGuard/Scan/Controller/Base.php +66 -42
  71. src/lib/src/Modules/HackGuard/Scan/Controller/BaseForAssets.php +2 -2
  72. src/lib/src/Modules/HackGuard/Scan/Controller/Ptg.php +48 -9
  73. src/lib/src/Modules/HackGuard/Scan/Controller/Ufc.php +7 -3
  74. src/lib/src/Modules/HackGuard/Scan/Queue/CollateResults.php +22 -22
  75. src/lib/src/Modules/HackGuard/Scan/Queue/CompleteQueue.php +9 -9
  76. src/lib/src/Modules/HackGuard/Scan/Queue/Controller.php +25 -25
  77. src/lib/src/Modules/HackGuard/Scan/Queue/ConvertBetweenTypes.php +7 -7
  78. src/lib/src/Modules/HackGuard/Scan/Queue/QueueProcessor.php +9 -9
  79. src/lib/src/Modules/HackGuard/Scan/Queue/ScanEnqueue.php +10 -10
  80. src/lib/src/Modules/HackGuard/Scan/Queue/ScanExecute.php +11 -12
  81. src/lib/src/Modules/HackGuard/Scan/Results/ConvertBetweenTypes.php +27 -25
  82. src/lib/src/Modules/HackGuard/Scan/Results/ResultsDelete.php +2 -2
  83. src/lib/src/Modules/HackGuard/Scan/Results/ResultsRetrieve.php +1 -1
  84. src/lib/src/Modules/HackGuard/Scan/Results/ResultsStore.php +11 -10
  85. src/lib/src/Modules/HackGuard/Scan/Results/ResultsUpdate.php +26 -25
  86. src/lib/src/Modules/HackGuard/Scan/ScansController.php +4 -4
  87. src/lib/src/Modules/HackGuard/Scan/Utilities/WpvAddPluginRows.php +1 -1
  88. src/lib/src/Modules/HackGuard/Strings.php +38 -68
  89. src/lib/src/Modules/HackGuard/UI.php +53 -36
  90. src/lib/src/Modules/HackGuard/Upgrade.php +0 -44
  91. src/lib/src/Modules/IPs/AjaxHandler.php +17 -17
  92. src/lib/src/Modules/IPs/Components/UnblockIpByFlag.php +3 -3
  93. src/lib/src/Modules/IPs/Lib/BlacklistHandler.php +0 -6
  94. src/lib/src/Modules/IPs/Lib/Bots/BotEventListener.php +1 -1
  95. src/lib/src/Modules/IPs/Lib/Ops/AddIp.php +43 -35
  96. src/lib/src/Modules/IPs/Lib/Ops/DeleteIp.php +1 -0
  97. src/lib/src/Modules/IPs/ModCon.php +24 -26
  98. src/lib/src/Modules/IPs/Options.php +12 -25
  99. src/lib/src/Modules/IPs/Strings.php +12 -0
  100. src/lib/src/Modules/IPs/WpCli/Add.php +8 -8
  101. src/lib/src/Modules/IPs/WpCli/Remove.php +8 -8
  102. src/lib/src/Modules/Insights/Lib/SideMenuBuilder.php +1 -1
  103. src/lib/src/Modules/Insights/ModCon.php +3 -2
  104. src/lib/src/Modules/Insights/Strings.php +3 -6
  105. src/lib/src/Modules/Integrations/Lib/Bots/Spam/Handlers/SupportCandy.php +27 -0
  106. src/lib/src/Modules/Integrations/Lib/Bots/Spam/SpamController.php +1 -0
  107. src/lib/src/Modules/LoginGuard/Lib/AntiBot/AntibotSetup.php +2 -2
  108. src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/AntiBot.php +1 -1
  109. src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/BaseProtectionProvider.php +2 -2
  110. src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/CoolDown.php +5 -5
  111. src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GaspJs.php +3 -3
  112. src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GoogleRecaptcha.php +1 -1
  113. src/lib/src/Modules/LoginGuard/Lib/CooldownFlagFile.php +8 -14
  114. src/lib/src/Modules/LoginGuard/Lib/CooldownRedirect.php +0 -63
  115. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php +1 -1
  116. src/lib/src/Modules/LoginGuard/UI.php +7 -1
  117. src/lib/src/Modules/Plugin/Lib/TestCacheDirWrite.php +15 -17
  118. src/lib/src/Modules/Plugin/Options.php +0 -8
  119. src/lib/src/Modules/Plugin/WpCli/Export.php +19 -19
  120. src/lib/src/Modules/Plugin/WpCli/ForceOff.php +8 -8
  121. src/lib/src/Modules/Reporting/Lib/Reports/BaseReporter.php +1 -4
  122. src/lib/src/Modules/Reporting/Lib/Reports/Build/BaseBuilder.php +1 -1
  123. src/lib/src/Modules/Reporting/Lib/Reports/Build/BuilderAlerts.php +2 -2
  124. src/lib/src/Modules/Reporting/Lib/Reports/Build/BuilderInfo.php +2 -2
  125. src/lib/src/Modules/SecurityAdmin/Options.php +0 -22
  126. src/lib/src/Modules/Sessions/Lib/Ops/Retrieve.php +0 -38
  127. src/lib/src/Scans/Apc/ConvertVosToResults.php +0 -32
  128. src/lib/src/Scans/Apc/ResultItem.php +1 -1
  129. src/lib/src/Scans/Apc/ResultsSet.php +12 -105
  130. src/lib/src/Scans/Apc/Scan.php +1 -1
  131. src/lib/src/Scans/Apc/Utilities/ItemActionHandler.php +0 -6
  132. src/lib/src/Scans/Apc/Utilities/Repair.php +1 -1
  133. src/lib/src/Scans/Base/BaseBuildFileMap.php +37 -0
  134. src/lib/src/Scans/Base/BaseBuildScanAction.php +20 -12
  135. src/lib/src/Scans/Base/BaseConvertVosToResults.php +0 -30
  136. src/lib/src/Scans/Base/BaseFileScanActionVO.php +15 -0
  137. src/lib/src/Scans/Base/BaseMergeItems.php +3 -3
  138. src/lib/src/Scans/Base/BaseScan.php +6 -6
  139. src/lib/src/Scans/Base/BaseScanActionVO.php +2 -2
  140. src/lib/src/Scans/Base/DiffResultForStorage.php +3 -4
  141. src/lib/src/Scans/Base/FileResultItem.php +13 -0
  142. src/lib/src/Scans/Base/Files/BaseFileScanner.php +1 -1
  143. src/lib/src/Scans/Base/Files/BaseScanFromFileMap.php +33 -4
  144. src/lib/src/Scans/Base/{BaseResultItem.php → ResultItem.php} +8 -1
  145. src/lib/src/Scans/Base/{BaseResultsSet.php → ResultsSet.php} +14 -18
  146. src/lib/src/Scans/Base/Table/BaseEntryFormatter.php +2 -2
  147. src/lib/src/Scans/Base/Table/BaseFileEntryFormatter.php +1 -1
  148. src/lib/src/Scans/Base/Utilities/BaseRepair.php +7 -19
  149. src/lib/src/Scans/Base/Utilities/ItemActionHandler.php +50 -36
  150. src/lib/src/Scans/Base/Utilities/ItemActionHandlerAssets.php +3 -3
  151. src/lib/src/Scans/Common/ScanActionConsumer.php +3 -3
  152. src/lib/src/Scans/Common/ScanItemConsumer.php +8 -8
  153. src/lib/src/Scans/Helpers/CopyResultsSets.php +3 -3
  154. src/lib/src/Scans/Helpers/MergeResultsSets.php +4 -4
  155. src/lib/src/Scans/Helpers/WpCoreFile.php +7 -7
  156. src/lib/src/Scans/Mal/BuildFileMap.php +5 -24
  157. src/lib/src/Scans/Mal/BuildScanAction.php +8 -11
  158. src/lib/src/Scans/Mal/FileScanner.php +15 -18
  159. src/lib/src/Scans/Mal/ResultItem.php +1 -3
  160. src/lib/src/Scans/Mal/ResultsSet.php +1 -1
  161. src/lib/src/Scans/Mal/ScanActionVO.php +1 -1
  162. src/lib/src/Scans/Mal/Utilities/ItemActionHandler.php +10 -13
  163. src/lib/src/Scans/Mal/Utilities/Patterns.php +6 -4
  164. src/lib/src/Scans/Mal/Utilities/Repair.php +60 -78
  165. src/lib/src/Scans/Ptg/BuildFileMap.php +27 -35
  166. src/lib/src/Scans/Ptg/BuildScanAction.php +12 -17
  167. src/lib/src/Scans/Ptg/FileScanner.php +73 -27
  168. src/lib/src/Scans/Ptg/ResultItem.php +1 -3
  169. src/lib/src/Scans/Ptg/ResultsSet.php +9 -9
  170. src/lib/src/Scans/Ptg/ScanFromFileMap.php +4 -4
  171. src/lib/src/Scans/Ptg/Utilities/ItemActionHandler.php +15 -18
  172. src/lib/src/Scans/Ptg/Utilities/Repair.php +19 -37
  173. src/lib/src/Scans/Ufc/BuildFileMap.php +18 -22
  174. src/lib/src/Scans/Ufc/BuildScanAction.php +5 -4
  175. src/lib/src/Scans/Ufc/ConvertVosToResults.php +0 -33
  176. src/lib/src/Scans/Ufc/ResultItem.php +1 -7
  177. src/lib/src/Scans/Ufc/ResultsSet.php +1 -1
  178. src/lib/src/Scans/Ufc/Utilities/ItemActionHandler.php +4 -15
  179. src/lib/src/Scans/Ufc/Utilities/Repair.php +18 -20
  180. src/lib/src/Scans/Wcf/BuildFileMap.php +13 -12
  181. src/lib/src/Scans/Wcf/BuildScanAction.php +11 -10
  182. src/lib/src/Scans/Wcf/ConvertVosToResults.php +0 -33
  183. src/lib/src/Scans/Wcf/FileScanner.php +13 -13
  184. src/lib/src/Scans/Wcf/ResultItem.php +1 -3
  185. src/lib/src/Scans/Wcf/ResultsSet.php +6 -7
  186. src/lib/src/Scans/Wcf/Utilities/ItemActionHandler.php +4 -8
  187. src/lib/src/Scans/Wcf/Utilities/Repair.php +9 -12
  188. src/lib/src/Scans/Wpv/ConvertVosToResults.php +0 -32
  189. src/lib/src/Scans/Wpv/ResultItem.php +1 -1
  190. src/lib/src/Scans/Wpv/ResultsSet.php +1 -1
  191. src/lib/src/Scans/Wpv/Scan.php +13 -14
  192. src/lib/src/Scans/Wpv/Utilities/ItemActionHandler.php +1 -19
  193. src/lib/src/Scans/Wpv/Utilities/Repair.php +8 -11
  194. src/lib/src/Scans/Wpv/WpVulnDb/WpVulnVO.php +0 -43
  195. src/lib/src/ShieldNetApi/FileLocker/DecryptFile.php +7 -7
  196. src/lib/src/Tables/Build/ScanAggregate.php +4 -8
  197. src/lib/src/Tables/Build/ScanApc.php +4 -4
  198. src/lib/src/Tables/DataTables/Build/AuditTrail/ForAuditTrail.php +111 -0
  199. src/lib/src/Tables/DataTables/Build/Base.php +77 -0
  200. src/lib/src/Tables/DataTables/Build/Scans/BaseForScan.php +111 -0
  201. src/lib/src/Tables/DataTables/Build/Scans/ForMalware.php +50 -0
  202. src/lib/src/Tables/DataTables/Build/Scans/ForPluginTheme.php +9 -0
  203. src/lib/src/Tables/DataTables/Build/Scans/ForWordpress.php +9 -0
  204. src/lib/src/Tables/Render/DataTable/Base.php +0 -10
  205. src/lib/src/Tables/Render/DataTable/ScanBase.php +0 -9
  206. src/lib/src/Tables/Render/DataTable/ScanWcf.php +0 -9
  207. src/lib/src/Utilities/CacheDir.php +100 -0
  208. src/lib/src/Utilities/HumanSpam/TestContent.php +4 -4
  209. src/lib/src/Utilities/Resources/Dynamic.php +5 -0
  210. src/lib/src/Utilities/Tool/TmpFileStore.php +16 -7
  211. src/lib/vendor/composer/autoload_classmap.php +28 -12
  212. src/lib/vendor/composer/autoload_static.php +28 -12
  213. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Fs.php +27 -32
  214. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/Assets/WpBaseVo.php +3 -3
  215. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/Assets/WpPluginVo.php +6 -0
  216. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/Assets/WpThemeVo.php +12 -1
  217. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Assets/DetectInstallationDate.php +29 -0
  218. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Code/AssessPhpFile.php +93 -0
  219. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Data.php +4 -4
  220. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Encrypt/OpenSslEncrypt.php +96 -77
  221. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Encrypt/OpenSslEncryptVo.php +14 -6
  222. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/LocateStrInFile.php +23 -9
  223. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/ReadDataFromFileEncrypted.php +15 -15
  224. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/WriteDataToFileEncrypted.php +12 -12
  225. templates/twig/components/reports/mod/events/alert_scanrepairs.twig +0 -8
  226. templates/twig/components/reports/mod/hack_protect/alert_scanrepairs.twig +19 -0
  227. templates/twig/components/reports/mod/hack_protect/alert_scanresults.twig +4 -1
  228. templates/twig/wpadmin_pages/components/page/nav_sidebar.twig +4 -4
  229. templates/twig/wpadmin_pages/insights/scans/modal/code_block.twig +1 -0
  230. templates/twig/wpadmin_pages/insights/scans/modal/code_render.twig +20 -0
  231. templates/twig/wpadmin_pages/insights/scans/results/index.twig +1 -0
  232. templates/twig/wpadmin_pages/insights/scans/results/scan_results.twig +93 -39
  233. templates/twig/wpadmin_pages/insights/scans/results/section/malware/index.twig +64 -0
  234. templates/twig/wpadmin_pages/insights/scans/results/section/plugins/index.twig +39 -0
  235. templates/twig/wpadmin_pages/insights/scans/results/section/plugins/plugin_panel.twig +109 -0
  236. templates/twig/wpadmin_pages/insights/scans/results/section/themes/index.twig +38 -0
  237. templates/twig/wpadmin_pages/insights/scans/results/section/themes/theme_panel.twig +104 -0
  238. templates/twig/wpadmin_pages/insights/scans/results/section/wordpress/index.twig +66 -0
cl.json CHANGED
@@ -1,4 +1,112 @@
1
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  "11.4": {
3
  "version": "11.4",
4
  "released_at": 1625560514,
1
  {
2
+ "11.5": {
3
+ "version": "11.5",
4
+ "released_at": 1626779164,
5
+ "hrefs": {
6
+ "release": "https://shsec.io/shieldrelease115",
7
+ "upgrade": "https://shsec.io/shieldupgradeguide115"
8
+ },
9
+ "title": "Revamped Scan Results",
10
+ "description": [
11
+ ],
12
+ "items": [
13
+ {
14
+ "type": "new",
15
+ "pro_only": false,
16
+ "title": "Brand New Arrangements of Scan Results",
17
+ "description": [
18
+ "To-date scan results have been presented in tabular format, by listing affected files or assets.",
19
+ "This release sees a major reorganisation to display results grouped into logical sections and areas, such as by plugin, theme, WordPress etc."
20
+ ]
21
+ },
22
+ {
23
+ "type": "new",
24
+ "pro_only": false,
25
+ "title": "View Scan File Contents In Browser",
26
+ "description": [
27
+ "We've added the ability to view the contents of any file shown in file results directly within your web browser.",
28
+ "There's no longer any need to download the files, though you still can do this of course."
29
+ ]
30
+ },
31
+ {
32
+ "type": "new",
33
+ "pro_only": false,
34
+ "title": "Remove 'Empty' PHP Files From Results",
35
+ "description": [
36
+ "A common problem is where a PHP file that has no executable code in it gets flagged in certain scans.",
37
+ "It isn't trivial to detect whether a PHP file has executable code, but we've added detection for this scenario."
38
+ ]
39
+ },
40
+ {
41
+ "type": "new",
42
+ "pro_only": false,
43
+ "title": "Scan File and Folder Exclusions",
44
+ "description": [
45
+ "You can specify files and folder which will be excluded from all file scans.",
46
+ "Files can be excluded in bulk using the asterisk (*) wildcard.",
47
+ "This option is designed to completely replace the exclusions option under the Unrecognised Files Scanner."
48
+ ]
49
+ },
50
+ {
51
+ "type": "improved",
52
+ "pro_only": false,
53
+ "title": "Scan Results Management",
54
+ "description": [
55
+ "We've scrapped the 'WordPress Tables' approach to display results and instead use the powerful DataTables JS plugin.",
56
+ "This makes display, pagination, refresh and actions far smoother and completely seamless."
57
+ ]
58
+ },
59
+ {
60
+ "type": "improved",
61
+ "pro_only": true,
62
+ "title": "Switch To Crowd-Sourced Plugin and Theme Hashes.",
63
+ "description": [
64
+ "When scanning plugin and theme files for modification, Shield now uses its ShieldNET crowd-source hashes system.",
65
+ "This results in more accurate and adaptive hashes accounting for edge-cases better resulting in fewer false positives in scan results."
66
+ ]
67
+ },
68
+ {
69
+ "type": "improved",
70
+ "pro_only": true,
71
+ "title": "Malware Scanner Uses Crowd-Sourced Hashing Data",
72
+ "description": [
73
+ "False Positives in malware results are frustrating, so the more we can reduce them, the better.",
74
+ "Shield already removes 99% of false positives automatically from results, before you even see them.",
75
+ "To improve this, ShieldNET now draws upon Crowd-Source Hashes to eliminate false positives even further."
76
+ ]
77
+ },
78
+ {
79
+ "type": "improved",
80
+ "title": "Reporting alert email now lists some repaired/deleted files.",
81
+ "description": []
82
+ },
83
+ {
84
+ "type": "improved",
85
+ "title": "WP Admin warning when 2FA by email verification isn't complete.",
86
+ "description": []
87
+ },
88
+ {
89
+ "type": "new",
90
+ "title": "Audit Trail entries for IP addresses are added and removed manually.",
91
+ "description": []
92
+ },
93
+ {
94
+ "type": "new",
95
+ "title": "Audit Trail WordPress filter to allow customisation of event logging.",
96
+ "description": []
97
+ },
98
+ {
99
+ "type": "improved",
100
+ "title": "Improved support and fixes for PHP 8 and WordPress 5.8.",
101
+ "description": []
102
+ },
103
+ {
104
+ "type": "fixed",
105
+ "title": "Sidebar navigation bugs.",
106
+ "description": []
107
+ }
108
+ ]
109
+ },
110
  "11.4": {
111
  "version": "11.4",
112
  "released_at": 1625560514,
icwp-wpsf.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Shield Security
4
  * Plugin URI: https://shsec.io/2f
5
  * Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
6
- * Version: 11.4.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.0
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages
9
  * Author: Shield Security
plugin-spec.php CHANGED
@@ -1,8 +1,8 @@
1
  {
2
  "properties": {
3
- "version": "11.4.5",
4
- "release_timestamp": 1626947243,
5
- "build": "202107.2201",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
@@ -53,7 +53,7 @@
53
  "plugin",
54
  "jquery/featherlight",
55
  "introjs",
56
- "bootstrap-select"
57
  ],
58
  "js": [
59
  "select2",
@@ -61,7 +61,8 @@
61
  "jquery/featherlight",
62
  "jquery/fileDownload",
63
  "shield/tours",
64
- "bootstrap-select"
 
65
  ]
66
  },
67
  "frontend": {
@@ -91,16 +92,27 @@
91
  "plugin"
92
  ]
93
  },
94
- "datatables": {
95
- "url": "https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.21/css/dataTables.bootstrap4.min.css",
96
  "deps": [
97
  "bootstrap"
98
  ]
99
  },
 
 
 
 
 
 
 
 
 
 
 
 
100
  "global-plugin": {},
101
  "plugin": {
102
  "deps": [
103
- "datatables",
104
  "bootstrap",
105
  "global-plugin"
106
  ]
@@ -136,7 +148,18 @@
136
  ],
137
  "footer": true
138
  },
139
- "shield/mainwp": {}
 
 
 
 
 
 
 
 
 
 
 
140
  },
141
  "js": {
142
  "bootstrap": {
@@ -164,12 +187,30 @@
164
  ]
165
  },
166
  "datatables": {
167
- "url": "https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.21/js/jquery.dataTables.min.js",
168
  "deps": [
169
  "bootstrap",
170
  "wp-jquery"
171
  ]
172
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  "global-plugin": {
174
  "deps": [
175
  "wp-jquery"
@@ -178,7 +219,7 @@
178
  "plugin": {
179
  "deps": [
180
  "bootstrap",
181
- "datatables",
182
  "global-plugin",
183
  "shield/navigation",
184
  "base64.min",
@@ -249,6 +290,19 @@
249
  "plugin"
250
  ]
251
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  "shield/tours": {
253
  "deps": [
254
  "plugin",
@@ -305,6 +359,9 @@
305
  "async": "async",
306
  "defer": "defer"
307
  }
 
 
 
308
  }
309
  }
310
  }
1
  {
2
  "properties": {
3
+ "version": "11.5.0",
4
+ "release_timestamp": 1627376940,
5
+ "build": "202107.2701",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
53
  "plugin",
54
  "jquery/featherlight",
55
  "introjs",
56
+ "shield/scanners"
57
  ],
58
  "js": [
59
  "select2",
61
  "jquery/featherlight",
62
  "jquery/fileDownload",
63
  "shield/tours",
64
+ "bootstrap-select",
65
+ "shield/scanners"
66
  ]
67
  },
68
  "frontend": {
92
  "plugin"
93
  ]
94
  },
95
+ "datatables-bootstrap": {
96
+ "url": "https://cdn.datatables.net/1.10.25/css/dataTables.bootstrap4.min.css",
97
  "deps": [
98
  "bootstrap"
99
  ]
100
  },
101
+ "datatables-select": {
102
+ "url": "https://cdn.datatables.net/select/1.3.3/css/select.dataTables.min.css",
103
+ "deps": [
104
+ "datatables-bootstrap"
105
+ ]
106
+ },
107
+ "datatables-buttons": {
108
+ "url": "https://cdn.datatables.net/buttons/1.7.1/css/buttons.dataTables.min.css",
109
+ "deps": [
110
+ "datatables-bootstrap"
111
+ ]
112
+ },
113
  "global-plugin": {},
114
  "plugin": {
115
  "deps": [
 
116
  "bootstrap",
117
  "global-plugin"
118
  ]
148
  ],
149
  "footer": true
150
  },
151
+ "shield/mainwp": {},
152
+ "shield/scanners": {
153
+ "deps": [
154
+ "datatables-select",
155
+ "datatables-buttons",
156
+ "datatables-bootstrap",
157
+ "tp/highlightjs"
158
+ ]
159
+ },
160
+ "tp/highlightjs": {
161
+ "url": "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/styles/default.min.css"
162
+ }
163
  },
164
  "js": {
165
  "bootstrap": {
187
  ]
188
  },
189
  "datatables": {
190
+ "url": "https://cdn.datatables.net/1.10.25/js/jquery.dataTables.min.js",
191
  "deps": [
192
  "bootstrap",
193
  "wp-jquery"
194
  ]
195
  },
196
+ "datatables-bootstrap": {
197
+ "url": "https://cdn.datatables.net/1.10.25/js/dataTables.bootstrap4.min.js",
198
+ "deps": [
199
+ "datatables"
200
+ ]
201
+ },
202
+ "datatables-select": {
203
+ "url": "https://cdn.datatables.net/select/1.3.3/js/dataTables.select.min.js",
204
+ "deps": [
205
+ "datatables"
206
+ ]
207
+ },
208
+ "datatables-buttons": {
209
+ "url": "https://cdn.datatables.net/buttons/1.7.1/js/dataTables.buttons.min.js",
210
+ "deps": [
211
+ "datatables-bootstrap"
212
+ ]
213
+ },
214
  "global-plugin": {
215
  "deps": [
216
  "wp-jquery"
219
  "plugin": {
220
  "deps": [
221
  "bootstrap",
222
+ "datatables-bootstrap",
223
  "global-plugin",
224
  "shield/navigation",
225
  "base64.min",
290
  "plugin"
291
  ]
292
  },
293
+ "shield/scanners": {
294
+ "deps": [
295
+ "shield/scantables"
296
+ ]
297
+ },
298
+ "shield/scantables": {
299
+ "deps": [
300
+ "datatables-select",
301
+ "datatables-buttons",
302
+ "datatables-bootstrap",
303
+ "tp/highlightjs"
304
+ ]
305
+ },
306
  "shield/tours": {
307
  "deps": [
308
  "plugin",
359
  "async": "async",
360
  "defer": "defer"
361
  }
362
+ },
363
+ "tp/highlightjs": {
364
+ "url": "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/highlight.min.js"
365
  }
366
  }
367
  }
readme.txt CHANGED
@@ -8,7 +8,7 @@ Requires at least: 3.7
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 5.8
11
- Stable tag: 11.4.5
12
 
13
  No-Nonsense Security Hardening that protects WordPress against hackers, malicious bots, and spammers (no captchas!). Now with exclusive ShieldNET Technology.
14
 
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 5.8
11
+ Stable tag: 11.5.0
12
 
13
  No-Nonsense Security Hardening that protects WordPress against hackers, malicious bots, and spammers (no captchas!). Now with exclusive ShieldNET Technology.
14
 
resources/css/plugin.css CHANGED
@@ -1105,7 +1105,6 @@ input[type=checkbox].form-check-input {
1105
  .icwpOptionsForm .option_link_info > .dashicons {
1106
  font-size: 16px;
1107
  text-decoration: none;
1108
- width: 1px;
1109
  }
1110
  .tooltip .arrow {
1111
  display: none !important;
@@ -1553,7 +1552,8 @@ body.folded #FooterBannerGoPro {
1553
  border-color: rgb(177 216 178);
1554
  border-width: 1px 1px 1px 0;
1555
  border-style: solid;
1556
- min-width: 150px;
 
1557
  }
1558
  #NavSideBar a.nav-link:hover {
1559
  }
@@ -1719,4 +1719,37 @@ body.folded #FooterBannerGoPro {
1719
  background: -webkit-linear-gradient(top, #ffeded 0%, #fcd6d6 12%, #f9acac 88%); /* Chrome10-25,Safari5.1-6 */
1720
  background: linear-gradient(to bottom, #ffeded 0%, #fcd6d6 12%, #f9acac 88%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
1721
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffeded', endColorstr='#f9acac', GradientType=0); /* IE6-9 */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1722
  }
1105
  .icwpOptionsForm .option_link_info > .dashicons {
1106
  font-size: 16px;
1107
  text-decoration: none;
 
1108
  }
1109
  .tooltip .arrow {
1110
  display: none !important;
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
  }
1719
  background: -webkit-linear-gradient(top, #ffeded 0%, #fcd6d6 12%, #f9acac 88%); /* Chrome10-25,Safari5.1-6 */
1720
  background: linear-gradient(to bottom, #ffeded 0%, #fcd6d6 12%, #f9acac 88%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
1721
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffeded', endColorstr='#f9acac', GradientType=0); /* IE6-9 */
1722
+ }
1723
+ #ScanResultsPlugins li.nav-item a.nav-link {
1724
+ /*background-color: white;*/
1725
+ border: 1px solid #c2c2c2;
1726
+ border-radius: 3px;
1727
+ margin-right: 0;
1728
+ }
1729
+ #ScanResultsTabs table td.actions {
1730
+ white-space: nowrap;
1731
+ }
1732
+ #ScanResultsTabs table td.file {
1733
+ }
1734
+ button.btn.action {
1735
+ padding: 3px 6px;
1736
+ line-height: 16px;
1737
+ }
1738
+
1739
+ #CodeRenderModal pre.icwp-code-render {
1740
+ counter-reset: line;
1741
+ }
1742
+ #CodeRenderModal pre.icwp-code-render > code {
1743
+ counter-increment: line;
1744
+ background-color: transparent;
1745
+ padding: 0;
1746
+ display: block;
1747
+ }
1748
+ #CodeRenderModal pre.icwp-code-render > code:before {
1749
+ content: counter(line);
1750
+ width: 32px;
1751
+ display: inline-block;
1752
+ }
1753
+ #CodeRenderModal pre.icwp-code-render > code:before {
1754
+ -webkit-user-select: none;
1755
  }
resources/css/shield/scanners.css ADDED
File without changes
resources/images/bootstrap/download.svg ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-download" viewBox="0 0 16 16">
2
+ <path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
3
+ <path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
4
+ </svg>
resources/images/bootstrap/eye-slash-fill.svg ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye-slash-fill" viewBox="0 0 16 16">
2
+ <path d="m10.79 12.912-1.614-1.615a3.5 3.5 0 0 1-4.474-4.474l-2.06-2.06C.938 6.278 0 8 0 8s3 5.5 8 5.5a7.029 7.029 0 0 0 2.79-.588zM5.21 3.088A7.028 7.028 0 0 1 8 2.5c5 0 8 5.5 8 5.5s-.939 1.721-2.641 3.238l-2.062-2.062a3.5 3.5 0 0 0-4.474-4.474L5.21 3.089z"/>
3
+ <path d="M5.525 7.646a2.5 2.5 0 0 0 2.829 2.829l-2.83-2.829zm4.95.708-2.829-2.83a2.5 2.5 0 0 1 2.829 2.829zm3.171 6-12-12 .708-.708 12 12-.708.708z"/>
4
+ </svg>
resources/images/bootstrap/file-excel.svg ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-excel" viewBox="0 0 16 16">
2
+ <path d="M5.18 4.616a.5.5 0 0 1 .704.064L8 7.219l2.116-2.54a.5.5 0 1 1 .768.641L8.651 8l2.233 2.68a.5.5 0 0 1-.768.64L8 8.781l-2.116 2.54a.5.5 0 0 1-.768-.641L7.349 8 5.116 5.32a.5.5 0 0 1 .064-.704z"/>
3
+ <path d="M4 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H4zm0 1h8a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1z"/>
4
+ </svg>
resources/images/bootstrap/tools.svg CHANGED
@@ -1,3 +1,3 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-tools" viewBox="0 0 16 16">
2
- <path d="M1 0 0 1l2.2 3.081a1 1 0 0 0 .815.419h.07a1 1 0 0 1 .708.293l2.675 2.675-2.617 2.654A3.003 3.003 0 0 0 0 13a3 3 0 1 0 5.878-.851l2.654-2.617.968.968-.305.914a1 1 0 0 0 .242 1.023l3.356 3.356a1 1 0 0 0 1.414 0l1.586-1.586a1 1 0 0 0 0-1.414l-3.356-3.356a1 1 0 0 0-1.023-.242L10.5 9.5l-.96-.96 2.68-2.643A3.005 3.005 0 0 0 16 3c0-.269-.035-.53-.102-.777l-2.14 2.141L12 4l-.364-1.757L13.777.102a3 3 0 0 0-3.675 3.68L7.462 6.46 4.793 3.793a1 1 0 0 1-.293-.707v-.071a1 1 0 0 0-.419-.814L1 0zm9.646 10.646a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708zM3 11l.471.242.529.026.287.445.445.287.026.529L5 13l-.242.471-.026.529-.445.287-.287.445-.529.026L3 15l-.471-.242L2 14.732l-.287-.445L1.268 14l-.026-.529L1 13l.242-.471.026-.529.445-.287.287-.445.529-.026L3 11z"/>
3
  </svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-tools" viewBox="0 0 16 16">
2
+ <path d="M1 0 0 1l2.2 3.081a1 1 0 0 0 .815.419h.07a1 1 0 0 1 .708.293l2.675 2.675-2.617 2.654A3.003 3.003 0 0 0 0 13a3 3 0 1 0 5.878-.851l2.654-2.617.968.968-.305.914a1 1 0 0 0 .242 1.023l3.356 3.356a1 1 0 0 0 1.414 0l1.586-1.586a1 1 0 0 0 0-1.414l-3.356-3.356a1 1 0 0 0-1.023-.242L10.5 9.5l-.96-.96 2.68-2.643A3.005 3.005 0 0 0 16 3c0-.269-.035-.53-.102-.777l-2.14 2.141L12 4l-.364-1.757L13.777.102a3 3 0 0 0-3.675 3.68L7.462 6.46 4.793 3.793a1 1 0 0 1-.293-.707v-.071a1 1 0 0 0-.419-.814L1 0zm9.646 10.646a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708zM3 11l.471.242.529.026.287.445.445.287.026.529L5 13l-.242.471-.026.529-.445.287-.287.445-.529.026L3 15l-.471-.242L2 14.732l-.287-.445L1.268 14l-.026-.529L1 13l.242-.471.026-.529.445-.287.287-.445.529-.026L3 11z"/>
3
  </svg>
resources/images/bootstrap/x-square.svg ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-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="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/global-plugin.js CHANGED
@@ -255,6 +255,7 @@ var iCWP_WPSF_BodyOverlay = new function () {
255
  this.show = function () {
256
  nOverlays++;
257
  jQuery( 'div#icwp-fade-wrapper' ).fadeIn( 1000 );
 
258
  };
259
 
260
  this.hide = function () {
@@ -263,6 +264,7 @@ var iCWP_WPSF_BodyOverlay = new function () {
263
  nOverlays = 0;
264
  jQuery( 'div#icwp-fade-wrapper' ).stop().fadeOut();
265
  }
 
266
  };
267
 
268
  this.initialise = function () {
255
  this.show = function () {
256
  nOverlays++;
257
  jQuery( 'div#icwp-fade-wrapper' ).fadeIn( 1000 );
258
+ jQuery( 'html' ).css( 'cursor', 'wait' );
259
  };
260
 
261
  this.hide = function () {
264
  nOverlays = 0;
265
  jQuery( 'div#icwp-fade-wrapper' ).stop().fadeOut();
266
  }
267
+ jQuery( 'html' ).css( 'cursor', 'initial' );
268
  };
269
 
270
  this.initialise = function () {
resources/js/shield/scanners.js ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * https://css-tricks.com/snippets/jquery/jquery-plugin-template/
3
+ */
4
+ (function ( $, window, document, undefined ) {
5
+
6
+ $.fn.icwpWpsfScanResultsActions = function ( runtimeOptions ) {
7
+ return this.each(
8
+ function () {
9
+ new $.icwpWpsfScanResultsActions( this, runtimeOptions )
10
+ }
11
+ );
12
+ };
13
+
14
+ $.icwpWpsfScanResultsActions = function ( el, options ) {
15
+ // To avoid scope issues, use 'base' instead of 'this'
16
+ // to reference this class from internal events and functions.
17
+ var base = this;
18
+
19
+ // Access to jQuery and DOM versions of element
20
+ base.$el = $( el );
21
+ base.el = el;
22
+
23
+ // Add a reverse reference to the DOM object
24
+ base.$el.data( "icwpWpsfScanResultsActions", base );
25
+
26
+ base.init = function () {
27
+ base.options = $.extend( {}, $.icwpWpsfScanResultsActions.defaultOptions, options );
28
+ base.bindEvents();
29
+ };
30
+
31
+ base.bindEvents = function () {
32
+
33
+ base.$el.on(
34
+ 'click' + '.' + base._name,
35
+ 'button.action.standalone-action.ignore',
36
+ function ( evt ) {
37
+ evt.preventDefault();
38
+ base.bulkAction.call( base, 'ignore', [ $( this ).data( 'rid' ) ] );
39
+ }
40
+ );
41
+
42
+ };
43
+
44
+ base.bulkAction = function ( action, RIDs ) {
45
+ let reqData = this.getBaseAjaxData();
46
+ reqData[ 'sub_action' ] = action;
47
+ reqData[ 'rids' ] = RIDs;
48
+ this.sendReq( reqData );
49
+ };
50
+
51
+ base.sendReq = function ( reqData, ) {
52
+
53
+ $( 'html' ).css( 'cursor', 'wait' );
54
+
55
+ $.post( ajaxurl, reqData,
56
+ function ( response ) {
57
+
58
+ if ( response.success ) {
59
+ iCWP_WPSF_Toaster.showMessage( response.data.message, response.success );
60
+ if ( response.data.table_reload ) {
61
+ }
62
+ else {
63
+ iCWP_WPSF_Toaster.showMessage( response.data.message, response.success );
64
+ }
65
+ }
66
+ else {
67
+ let msg = 'Communications error with site.';
68
+ if ( response.data.message !== undefined ) {
69
+ msg = response.data.message;
70
+ }
71
+ alert( msg );
72
+ }
73
+ }
74
+ ).always( function () {
75
+ $( "html" ).css( "cursor", 'initial' );
76
+ }
77
+ );
78
+ };
79
+
80
+ base.getBaseAjaxData = function () {
81
+ return JSON.parse( JSON.stringify( base.options.ajax[ 'scanresults_action' ] ) );
82
+ };
83
+
84
+ // Run initializer
85
+ base.init();
86
+ }
87
+
88
+ $.icwpWpsfScanResultsActions.defaultOptions = {};
89
+
90
+ })( jQuery );
resources/js/shield/scans.js CHANGED
@@ -75,28 +75,28 @@ jQuery.fn.icwpWpsfScansCheck = function ( aOptions ) {
75
 
76
  let aReqData = aOpts[ 'ajax_scans_check' ];
77
  jQuery.post( ajaxurl, jQuery.extend( aReqData, aParams ),
78
- function ( oResp ) {
79
 
80
  bCurrentlyRunning = false;
81
  nRunningCount = 0;
82
- if ( oResp.data.running !== undefined ) {
83
- for ( const scankey of Object.keys( oResp.data.running ) ) {
84
- if ( oResp.data.running[ scankey ] ) {
85
  nRunningCount++;
86
  bFoundRunning = true;
87
  bCurrentlyRunning = true;
88
  }
89
  }
90
  }
91
- let $oModal = jQuery( '#ScanProgressModal' );
92
- jQuery( '.modal-body', $oModal ).html( oResp.data.vars.progress_html );
93
- $oModal.modal( 'show' );
94
  }
95
  ).always( function () {
96
  if ( bCurrentlyRunning ) {
97
  setTimeout( function () {
98
  sendReq();
99
- }, 5000 );
100
  }
101
  else {
102
  setTimeout( function () {
75
 
76
  let aReqData = aOpts[ 'ajax_scans_check' ];
77
  jQuery.post( ajaxurl, jQuery.extend( aReqData, aParams ),
78
+ function ( response ) {
79
 
80
  bCurrentlyRunning = false;
81
  nRunningCount = 0;
82
+ if ( response.data.running !== undefined ) {
83
+ for ( const scankey of Object.keys( response.data.running ) ) {
84
+ if ( response.data.running[ scankey ] ) {
85
  nRunningCount++;
86
  bFoundRunning = true;
87
  bCurrentlyRunning = true;
88
  }
89
  }
90
  }
91
+ let modal = jQuery( '#ScanProgressModal' );
92
+ jQuery( '.modal-body', modal ).html( response.data.vars.progress_html );
93
+ modal.modal( 'show' );
94
  }
95
  ).always( function () {
96
  if ( bCurrentlyRunning ) {
97
  setTimeout( function () {
98
  sendReq();
99
+ }, 3000 );
100
  }
101
  else {
102
  setTimeout( function () {
resources/js/shield/scantables.js ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * https://css-tricks.com/snippets/jquery/jquery-plugin-template/
3
+ */
4
+ (function ( $, window, document, undefined ) {
5
+
6
+ $.fn.icwpWpsfScanTableActions = function ( runtimeOptions ) {
7
+ return this.each(
8
+ function () {
9
+ new $.icwpWpsfScanTableActions( this, runtimeOptions )
10
+ }
11
+ );
12
+ };
13
+
14
+ $.icwpWpsfScanTableActions = function ( el, options ) {
15
+ // To avoid scope issues, use 'base' instead of 'this'
16
+ // to reference this class from internal events and functions.
17
+ const base = this;
18
+
19
+ // Access to jQuery and DOM versions of element
20
+ base.$el = $( el );
21
+ base.el = el;
22
+
23
+ // Add a reverse reference to the DOM object
24
+ base.$el.data( "icwpWpsfScanTableActions", base );
25
+
26
+ base.init = function () {
27
+ base.options = $.extend( {}, $.icwpWpsfScanTableActions.defaultOptions, options );
28
+ base.setupDatatable();
29
+ base.bindEvents();
30
+ };
31
+
32
+ base.rowSelectionChanged = function () {
33
+ if ( this.$table.rows( { selected: true } ).count() > 0 ) {
34
+ this.$table.buttons( 'selected-ignore:name, selected-repair:name' ).enable();
35
+ }
36
+ else {
37
+ this.$table.buttons( 'selected-ignore:name, selected-repair:name' ).disable();
38
+ }
39
+ };
40
+
41
+ base.bindEvents = function () {
42
+
43
+ base.$table.on( 'draw',
44
+ function ( e, dt, type, row_index ) {
45
+ base.rowSelectionChanged.call( base );
46
+ }
47
+ );
48
+
49
+ base.$table.on( 'select',
50
+ function ( e, dt, type, row_index ) {
51
+ base.rowSelectionChanged.call( base );
52
+ }
53
+ );
54
+
55
+ base.$table.on( 'deselect',
56
+ function ( e, dt, type, row_index ) {
57
+ base.rowSelectionChanged.call( base );
58
+ }
59
+ );
60
+
61
+ base.$el.on(
62
+ 'click' + '.' + base._name,
63
+ 'button.action.delete',
64
+ function ( evt ) {
65
+ evt.preventDefault();
66
+ if ( confirm( icwp_wpsf_vars_insights.strings.are_you_sure ) ) {
67
+ base.bulkAction.call( base, 'delete', [ $( this ).data( 'rid' ) ] );
68
+ }
69
+ }
70
+ );
71
+
72
+ base.$el.on(
73
+ 'click' + '.' + base._name,
74
+ 'button.action.ignore',
75
+ function ( evt ) {
76
+ evt.preventDefault();
77
+ base.bulkAction.call( base, 'ignore', [ $( this ).data( 'rid' ) ] );
78
+ }
79
+ );
80
+
81
+ base.$el.on(
82
+ 'click' + '.' + base._name,
83
+ 'button.action.repair',
84
+ function ( evt ) {
85
+ evt.preventDefault();
86
+ base.$table.rows().deselect();
87
+ base.bulkAction.call( base, 'repair', [ $( this ).data( 'rid' ) ] );
88
+ }
89
+ );
90
+
91
+ base.$el.on(
92
+ 'click' + '.' + base._name,
93
+ 'a.action.view-file',
94
+ function ( evt ) {
95
+ evt.preventDefault();
96
+ let reqData = base.getBaseAjaxData();
97
+ reqData.sub_action = 'view_file';
98
+ reqData.rid = $( this ).data( 'rid' );
99
+ $.post( ajaxurl, reqData, function ( response ) {
100
+ if ( response.success ) {
101
+ let $codeModal = jQuery( '#CodeRenderModal' );
102
+ jQuery( '.modal-title', $codeModal ).html( response.data.vars.path );
103
+ jQuery( '.modal-body', $codeModal ).html( response.data.vars.contents );
104
+ $codeModal.modal( 'show' );
105
+ $codeModal[ 0 ].querySelectorAll( 'pre.icwp-code-render code' ).forEach( ( el ) => {
106
+ hljs.highlightElement( el );
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
+ }
118
+ );
119
+
120
+ base.$el.on(
121
+ 'click' + '.' + base._name,
122
+ 'button.action.href-download',
123
+ function ( evt ) {
124
+ evt.preventDefault();
125
+ var button = $( this );
126
+ var href = button.data( 'href-download' );
127
+ if ( href !== undefined ) {
128
+ base.hrefDownload.call( base, href );
129
+ }
130
+ }
131
+ );
132
+
133
+ };
134
+
135
+ base.bulkAction = function ( action, RIDs = [] ) {
136
+
137
+ if ( RIDs.length === 0 ) {
138
+ this.$table.rows( { selected: true } ).every( function ( rowIdx, tableLoop, rowLoop ) {
139
+ RIDs.push( this.data()[ 'rid' ] );
140
+ } );
141
+ }
142
+
143
+ if ( RIDs.length > 0 ) {
144
+ let reqData = this.getBaseAjaxData();
145
+ reqData[ 'sub_action' ] = action;
146
+ reqData[ 'rids' ] = RIDs;
147
+ this.sendReq( reqData );
148
+ }
149
+ };
150
+
151
+ base.hrefDownload = function ( href ) {
152
+ $.fileDownload( href, {
153
+ preparingMessageHtml: icwp_wpsf_vars_plugin.strings.downloading_file,
154
+ failMessageHtml: icwp_wpsf_vars_plugin.strings.downloading_file_problem
155
+ } );
156
+ return false;
157
+ };
158
+
159
+ base.sendReq = function ( reqData, forceTableReload = false ) {
160
+
161
+ $( 'html' ).css( 'cursor', 'wait' );
162
+
163
+ $.post( ajaxurl, reqData,
164
+ function ( response ) {
165
+
166
+ if ( response.success ) {
167
+ iCWP_WPSF_Toaster.showMessage( response.data.message, response.success );
168
+ if ( response.data.table_reload ) {
169
+ }
170
+ else {
171
+ iCWP_WPSF_Toaster.showMessage( response.data.message, response.success );
172
+ }
173
+ }
174
+ else {
175
+ let msg = 'Communications error with site.';
176
+ if ( response.data.message !== undefined ) {
177
+ msg = response.data.message;
178
+ }
179
+ alert( msg );
180
+ }
181
+
182
+ if ( response.data.table_reload ) {
183
+ base.tableReload();
184
+ }
185
+ }
186
+ ).always( function () {
187
+ $( "html" ).css( "cursor", 'initial' );
188
+ }
189
+ );
190
+ };
191
+
192
+ base.getBaseAjaxData = function () {
193
+ return JSON.parse( JSON.stringify( base.options.ajax[ 'scanresults_action' ] ) );
194
+ };
195
+
196
+ base.setupDatatable = function () {
197
+
198
+ this.$table = this.$el.DataTable(
199
+ $.extend( base.options.datatables_init,
200
+ {
201
+ ajax: function ( data, callback, settings ) {
202
+ let reqData = base.getBaseAjaxData();
203
+ reqData.sub_action = 'retrieve_table_data';
204
+ reqData.type = base.options.type;
205
+ reqData.file = base.options.file;
206
+ $.post( ajaxurl, reqData, function ( response ) {
207
+ if ( response.success ) {
208
+ callback( response.data.vars );
209
+ }
210
+ else {
211
+ let msg = 'Communications error with site.';
212
+ if ( response.data.message !== undefined ) {
213
+ msg = response.data.message;
214
+ }
215
+ alert( msg );
216
+ }
217
+ } );
218
+ },
219
+ deferRender: true,
220
+ select: {
221
+ style: 'multi'
222
+ },
223
+ dom: 'Bfrtip',
224
+ buttons: [
225
+ {
226
+ text: 'Reload',
227
+ name: 'table-reload',
228
+ className: 'action table-refresh',
229
+ action: function ( e, dt, node, config ) {
230
+ base.tableReload.call( base );
231
+ }
232
+ },
233
+ {
234
+ text: 'De/Select All',
235
+ name: 'all-select',
236
+ className: 'select-all',
237
+ action: function ( e, dt, node, config ) {
238
+ let total = base.$table.rows().count()
239
+ if ( base.$table.rows( { selected: true } ).count() < total ) {
240
+ base.$table.rows().select();
241
+ }
242
+ else {
243
+ base.$table.rows().deselect();
244
+ }
245
+ }
246
+ },
247
+ {
248
+ text: 'Ignore Selected',
249
+ name: 'selected-ignore',
250
+ className: 'action selected-action ignore',
251
+ action: function ( e, dt, node, config ) {
252
+ if ( confirm( icwp_wpsf_vars_insights.strings.are_you_sure ) ) {
253
+ base.bulkAction.call( base, 'ignore' );
254
+ }
255
+ }
256
+ },
257
+ {
258
+ text: 'Delete/Repair Selected',
259
+ name: 'selected-repair',
260
+ className: 'action selected-action repair',
261
+ action: function ( e, dt, node, config ) {
262
+
263
+ if ( base.$table.rows().count() > 20 ) {
264
+ alert( "Sorry, this tool isn't designed for such large repairs. We recommend completely removing and reinstalling the item." )
265
+ }
266
+ else if ( confirm( icwp_wpsf_vars_insights.strings.absolutely_sure ) ) {
267
+ base.bulkAction.call( base, 'repair-delete' );
268
+ }
269
+ }
270
+ }
271
+ ],
272
+ language: {
273
+ emptyTable: "There are no items to display, or they've been set to be ignored."
274
+ }
275
+ }
276
+ ) );
277
+
278
+ $( '#ScanResultsPlugins a[data-toggle="tab"]' ).on( 'shown.bs.tab', function ( e ) {
279
+ $.fn.dataTable.tables( { visible: true, api: true } ).columns.adjust();
280
+ } );
281
+ };
282
+
283
+ base.tableReload = function ( full = false ) {
284
+ this.$table.ajax.reload( null, full );
285
+ this.rowSelectionChanged();
286
+ };
287
+
288
+ // Run initializer
289
+ base.init();
290
+ }
291
+
292
+ $.icwpWpsfScanTableActions.defaultOptions = {};
293
+
294
+ })( jQuery );
src/config/feature-hack_protect.php CHANGED
@@ -385,6 +385,41 @@
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": "snapshot_users",
390
  "section": "section_non_ui",
@@ -427,6 +462,27 @@
427
  }
428
  ],
429
  "definitions": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  "db_classes": {
431
  "filelocker": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\FileLocker\\Handler",
432
  "scanner": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Scanner\\Handler",
@@ -474,21 +530,22 @@
474
  "finished_at": "Scan Completed"
475
  }
476
  },
477
- "all_scan_slugs": [
478
- "apc",
479
- "mal",
480
- "ptg",
481
- "wpv",
482
- "wcf",
483
- "ufc"
484
- ],
485
  "table_name_filelocker": "filelocker",
486
  "url_mal_sigs_simple": "https://raw.githubusercontent.com/scr34m/php-malware-scanner/master/definitions/patterns_raw.txt",
487
  "url_mal_sigs_regex": "https://raw.githubusercontent.com/scr34m/php-malware-scanner/master/definitions/patterns_re.txt",
488
- "malware_whitelist_paths": [
489
- "wp-content/wflogs/",
490
- "wp-content/cache/",
491
- "wp-content/icwp/rollback/"
 
 
 
 
 
 
 
 
 
492
  ],
493
  "cron_all_scans": "all-scans",
494
  "wcf_exclusions": [
@@ -503,104 +560,81 @@
503
  "xmlrpc.php"
504
  ],
505
  "events": {
506
- "apc_alert_sent": {
507
  },
508
- "mal_alert_sent": {
509
  },
510
- "ptg_alert_sent": {
511
  },
512
- "ufc_alert_sent": {
513
  },
514
- "wcf_alert_sent": {
515
  },
516
- "wpv_alert_sent": {
517
  },
518
- "apc_scan_run": {
519
  "audit": false,
520
  "recent": true
521
  },
522
- "mal_scan_run": {
523
  "audit": false,
524
  "recent": true
525
  },
526
- "ptg_scan_run": {
527
  "audit": false,
528
  "recent": true
529
  },
530
- "ufc_scan_run": {
531
  "audit": false,
532
  "recent": true
533
  },
534
- "wcf_scan_run": {
535
  "audit": false,
536
  "recent": true
537
  },
538
- "wpv_scan_run": {
539
  "audit": false,
540
  "recent": true
541
  },
542
- "apc_scan_found": {
543
  "cat": 2,
544
  "audit_multiple": true,
545
  "recent": true
546
  },
547
- "mal_scan_found": {
548
  "cat": 3,
549
  "audit_multiple": true,
550
  "recent": true
551
  },
552
- "ptg_scan_found": {
553
  "cat": 3,
554
  "audit_multiple": true,
555
  "recent": true
556
  },
557
- "ufc_scan_found": {
558
  "cat": 3,
559
  "audit_multiple": true,
560
  "recent": true
561
  },
562
- "wcf_scan_found": {
563
  "cat": 3,
564
  "audit_multiple": true,
565
  "recent": true
566
  },
567
- "wpv_scan_found": {
568
  "cat": 3,
569
  "audit_multiple": true,
570
  "recent": true
571
  },
572
- "apc_item_repair_success": {
573
- "audit_multiple": true
574
- },
575
- "apc_item_repair_fail": {
576
- },
577
- "mal_item_repair_success": {
578
  "audit_multiple": true,
579
  "recent": true
580
  },
581
- "mal_item_repair_fail": {
582
- },
583
- "ptg_item_repair_success": {
584
  "audit_multiple": true
585
  },
586
- "ptg_item_repair_fail": {
587
- },
588
- "ufc_item_repair_success": {
589
- "audit_multiple": true,
590
- "recent": true
591
- },
592
- "ufc_item_repair_fail": {
593
- },
594
- "wcf_item_repair_success": {
595
- "audit_multiple": true,
596
- "recent": true
597
- },
598
- "wcf_item_repair_fail": {
599
- },
600
- "wpv_item_repair_success": {
601
  "audit_multiple": true
602
- },
603
- "wpv_item_repair_fail": {
604
  }
605
  }
606
  }
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
+ ],
415
+ "type": "array",
416
+ "link_info": "",
417
+ "link_blog": "",
418
+ "beacon_id": 441,
419
+ "name": "Scan Exclusions",
420
+ "summary": "Scan File and Folder Exclusions",
421
+ "description": "Scan File and Folder Exclusions."
422
+ },
423
  {
424
  "key": "snapshot_users",
425
  "section": "section_non_ui",
462
  }
463
  ],
464
  "definitions": {
465
+ "all_scan_slugs": [
466
+ "apc",
467
+ "mal",
468
+ "ptg",
469
+ "wpv",
470
+ "wcf",
471
+ "ufc"
472
+ ],
473
+ "file_scan_extensions": [
474
+ "php",
475
+ "php5",
476
+ "php7",
477
+ "js",
478
+ "json",
479
+ "css",
480
+ "htm",
481
+ "html",
482
+ "svg",
483
+ "twig",
484
+ "hbs"
485
+ ],
486
  "db_classes": {
487
  "filelocker": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\FileLocker\\Handler",
488
  "scanner": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Scanner\\Handler",
530
  "finished_at": "Scan Completed"
531
  }
532
  },
 
 
 
 
 
 
 
 
533
  "table_name_filelocker": "filelocker",
534
  "url_mal_sigs_simple": "https://raw.githubusercontent.com/scr34m/php-malware-scanner/master/definitions/patterns_raw.txt",
535
  "url_mal_sigs_regex": "https://raw.githubusercontent.com/scr34m/php-malware-scanner/master/definitions/patterns_re.txt",
536
+ "default_whitelist_paths": [
537
+ "wp-content/cache/*",
538
+ "wp-content/shield/*",
539
+ "wp-content/icwp/rollback/*",
540
+ "wp-content/plugins-before-restore/*",
541
+ "wp-content/themes-before-restore/*",
542
+ "wp-content/uploads/bb-plugin/cache/*",
543
+ "wp-content/uploads/cache/wpml/twig/*",
544
+ "wp-content/cache/*",
545
+ "*/error_log",
546
+ "*/php_error_log",
547
+ "*/mail.log",
548
+ "*/php_mail.log"
549
  ],
550
  "cron_all_scans": "all-scans",
551
  "wcf_exclusions": [
560
  "xmlrpc.php"
561
  ],
562
  "events": {
563
+ "apc_alert_sent": {
564
  },
565
+ "mal_alert_sent": {
566
  },
567
+ "ptg_alert_sent": {
568
  },
569
+ "ufc_alert_sent": {
570
  },
571
+ "wcf_alert_sent": {
572
  },
573
+ "wpv_alert_sent": {
574
  },
575
+ "apc_scan_run": {
576
  "audit": false,
577
  "recent": true
578
  },
579
+ "mal_scan_run": {
580
  "audit": false,
581
  "recent": true
582
  },
583
+ "ptg_scan_run": {
584
  "audit": false,
585
  "recent": true
586
  },
587
+ "ufc_scan_run": {
588
  "audit": false,
589
  "recent": true
590
  },
591
+ "wcf_scan_run": {
592
  "audit": false,
593
  "recent": true
594
  },
595
+ "wpv_scan_run": {
596
  "audit": false,
597
  "recent": true
598
  },
599
+ "apc_scan_found": {
600
  "cat": 2,
601
  "audit_multiple": true,
602
  "recent": true
603
  },
604
+ "mal_scan_found": {
605
  "cat": 3,
606
  "audit_multiple": true,
607
  "recent": true
608
  },
609
+ "ptg_scan_found": {
610
  "cat": 3,
611
  "audit_multiple": true,
612
  "recent": true
613
  },
614
+ "ufc_scan_found": {
615
  "cat": 3,
616
  "audit_multiple": true,
617
  "recent": true
618
  },
619
+ "wcf_scan_found": {
620
  "cat": 3,
621
  "audit_multiple": true,
622
  "recent": true
623
  },
624
+ "wpv_scan_found": {
625
  "cat": 3,
626
  "audit_multiple": true,
627
  "recent": true
628
  },
629
+ "scan_item_repair_success": {
 
 
 
 
 
630
  "audit_multiple": true,
631
  "recent": true
632
  },
633
+ "scan_item_repair_fail": {
 
 
634
  "audit_multiple": true
635
  },
636
+ "scan_item_delete_success": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
637
  "audit_multiple": true
 
 
638
  }
639
  }
640
  }
src/config/feature-integrations.php CHANGED
@@ -107,6 +107,10 @@
107
  "value_key": "superforms",
108
  "text": "Super Forms"
109
  },
 
 
 
 
110
  {
111
  "value_key": "wpforo",
112
  "text": "wpForo"
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"
src/config/feature-ips.php CHANGED
@@ -659,10 +659,25 @@
659
  "audit": false,
660
  "stat": false
661
  },
662
- "ip_bypass": {
663
  "offense": false,
664
- "audit": false,
665
- "stat": false
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
666
  },
667
  "ip_unblock_flag": {
668
  "cat": 1
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
src/lib/src/Controller/Assets/Paths.php CHANGED
@@ -35,4 +35,12 @@ class Paths {
35
  public function forTemplate( string $item = '' ) :string {
36
  return $this->forPluginItem( $this->getCon()->cfg->paths[ 'templates' ].'/'.ltrim( $item, '/' ) );
37
  }
38
- }
 
 
 
 
 
 
 
 
35
  public function forTemplate( string $item = '' ) :string {
36
  return $this->forPluginItem( $this->getCon()->cfg->paths[ 'templates' ].'/'.ltrim( $item, '/' ) );
37
  }
38
+
39
+ public function cacheDir() :string {
40
+ return path_join( WP_CONTENT_DIR, $this->getCon()->cfg->paths[ 'cache' ] );
41
+ }
42
+
43
+ public function forCacheItem( string $item = '' ) :string {
44
+ return path_join( $this->cacheDir(), $item );
45
+ }
46
+ }
src/lib/src/Controller/Assets/Urls.php CHANGED
@@ -3,7 +3,6 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Resources\Dynamic;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class Urls {
@@ -24,14 +23,7 @@ class Urls {
24
  public function forJs( string $asset ) :string {
25
  $url = $this->lookupAssetUrlInSpec( $asset, 'js' );
26
  if ( empty( $url ) ) {
27
- if ( $this->isAssetDynamic( $asset, 'js' ) ) {
28
- $url = ( new Dynamic() )
29
- ->setCon( $this->getCon() )
30
- ->getResourceUrl( Services::Data()->addExtensionToFilePath( $asset, 'js' ) );
31
- }
32
- else {
33
- $url = $this->forAsset( 'js/'.Services::Data()->addExtensionToFilePath( $asset, 'js' ) );
34
- }
35
  }
36
  return $url;
37
  }
@@ -54,10 +46,10 @@ class Urls {
54
  * @param string $asset
55
  * @param string $type
56
  * @return mixed|null
 
57
  */
58
  protected function isAssetDynamic( string $asset, string $type ) :bool {
59
- $asset = $this->lookupAssetInSpec( $asset, $type );
60
- return !empty( $asset[ 'dynamic' ] );
61
  }
62
 
63
  /**
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
 
6
  use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class Urls {
23
  public function forJs( string $asset ) :string {
24
  $url = $this->lookupAssetUrlInSpec( $asset, 'js' );
25
  if ( empty( $url ) ) {
26
+ $url = $this->forAsset( 'js/'.Services::Data()->addExtensionToFilePath( $asset, 'js' ) );
 
 
 
 
 
 
 
27
  }
28
  return $url;
29
  }
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
  /**
src/lib/src/Controller/Controller.php CHANGED
@@ -321,72 +321,24 @@ class Controller extends DynPropertiesClass {
321
  }
322
  }
323
 
324
- /**
325
- * @param string $cachePath
326
- * @return string|false
327
- */
328
- public function getPluginCachePath( $cachePath = '' ) {
329
- $path = false;
330
- if ( $this->hasCacheDir() ) {
331
- try {
332
- // Never throws an exception if "hasCacheDir" == true
333
- $path = $this->buildPluginCacheDir();
334
- if ( !empty( $cachePath ) ) {
335
- $path = path_join( $path, $cachePath );
336
- }
337
- }
338
- catch ( \Exception $e ) {
339
- }
340
- }
341
- return $path;
342
  }
343
 
344
  public function hasCacheDir() :bool {
345
- try {
346
- $this->buildPluginCacheDir();
347
- }
348
- catch ( \Exception $e ) {
349
- }
350
- return $this->cache_dir_ready;
351
  }
352
 
353
  /**
354
- * @return string
355
- * @throws \Exception
356
  */
357
  private function buildPluginCacheDir() :string {
358
- $FS = Services::WpFs();
359
-
360
- $cacheDirBasename = $this->cfg->paths[ 'cache' ];
361
- if ( empty( $cacheDirBasename ) ) {
362
- $this->cache_dir_ready = false;
363
- throw new \Exception( 'No slug for cache dir' );
364
- }
365
-
366
- $cacheDir = path_join( WP_CONTENT_DIR, $cacheDirBasename );
367
- if ( empty( $this->cache_dir_ready ) && $FS->mkdir( $cacheDir ) ) {
368
- $htFile = path_join( $cacheDir, '.htaccess' );
369
- $htContent = implode( "\n", [
370
- "# BEGIN SHIELD",
371
- "Options -Indexes",
372
- "Order allow,deny",
373
- "Deny from all",
374
- '<FilesMatch "^.*\.(css|js)$">',
375
- " Allow from all",
376
- '</FilesMatch>',
377
- "# END SHIELD"
378
- ] );
379
- if ( !$FS->exists( $htFile ) || ( md5_file( $htFile ) !== md5( $htContent ) ) ) {
380
- $FS->putFileContent( $htFile, $htContent );
381
- }
382
- $index = path_join( $cacheDir, 'index.php' );
383
- $indexContent = "<?php\nhttp_response_code(404);";
384
- if ( !$FS->exists( $index ) || ( md5_file( $index ) !== md5( $indexContent ) ) ) {
385
- $FS->putFileContent( $index, $indexContent );
386
- }
387
- $this->cache_dir_ready = true;
388
- }
389
- return $cacheDir;
390
  }
391
 
392
  protected function doRegisterHooks() {
321
  }
322
  }
323
 
324
+ public function getPluginCachePath( $cachePath = '' ) :string {
325
+ $cacheDir = ( new Shield\Utilities\CacheDir() )
326
+ ->setCon( $this )
327
+ ->build();
328
+ return empty( $cacheDir ) ? '' : path_join( $cacheDir, $cachePath );
 
 
 
 
 
 
 
 
 
 
 
 
 
329
  }
330
 
331
  public function hasCacheDir() :bool {
332
+ return !empty( $this->getPluginCachePath() );
 
 
 
 
 
333
  }
334
 
335
  /**
336
+ * @deprecated 11.4
 
337
  */
338
  private function buildPluginCacheDir() :string {
339
+ return ( new Shield\Utilities\CacheDir() )
340
+ ->setCon( $this )
341
+ ->build();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  }
343
 
344
  protected function doRegisterHooks() {
src/lib/src/Databases/Base/BaseQuery.php CHANGED
@@ -263,13 +263,13 @@ abstract class BaseQuery {
263
  }
264
 
265
  /**
266
- * @param int $nStartTS
267
- * @param int $nEndTS
268
  * @return $this
269
  */
270
- public function filterByBoundary( $nStartTS, $nEndTS ) {
271
- return $this->filterByCreatedAt( $nEndTS, '<=' )
272
- ->filterByCreatedAt( $nStartTS, '>=' );
273
  }
274
 
275
  /**
263
  }
264
 
265
  /**
266
+ * @param int $startTS
267
+ * @param int $endTS
268
  * @return $this
269
  */
270
+ public function filterByBoundary( $startTS, $endTS ) {
271
+ return $this->filterByCreatedAt( $endTS, '<=' )
272
+ ->filterByCreatedAt( $startTS, '>=' );
273
  }
274
 
275
  /**
src/lib/src/Databases/Base/Select.php CHANGED
@@ -63,10 +63,10 @@ class Select extends BaseQuery {
63
  * @return \stdClass
64
  */
65
  public function byId( $nId ) {
66
- $aItems = $this->reset()
67
- ->addWhereEquals( 'id', $nId )
68
- ->query();
69
- return array_shift( $aItems );
70
  }
71
 
72
  /**
@@ -188,7 +188,7 @@ class Select extends BaseQuery {
188
 
189
  /**
190
  * Handle COUNT, DISTINCT, & normal SELECT
191
- * @return int|string[]|array[]|EntryVO[]|mixed
192
  */
193
  public function query() {
194
  if ( $this->isCount() || $this->isSum() ) {
63
  * @return \stdClass
64
  */
65
  public function byId( $nId ) {
66
+ $items = $this->reset()
67
+ ->addWhereEquals( 'id', $nId )
68
+ ->query();
69
+ return array_shift( $items );
70
  }
71
 
72
  /**
188
 
189
  /**
190
  * Handle COUNT, DISTINCT, & normal SELECT
191
+ * @return int|string[]|array[]|EntryVO[]|\stdClass[]|mixed
192
  */
193
  public function query() {
194
  if ( $this->isCount() || $this->isSum() ) {
src/lib/src/Databases/Events/Handler.php CHANGED
@@ -8,32 +8,30 @@ use FernleafSystems\Wordpress\Services\Services;
8
  class Handler extends Base\Handler {
9
 
10
  /**
11
- * @param $aEvents - array of events: key event slug, value created_at timestamp
12
  */
13
- public function commitEvents( $aEvents ) {
14
- foreach ( $aEvents as $sEvent => $nTs ) {
15
- $this->commitEvent( $sEvent, 1, $nTs );
16
  }
17
  }
18
 
19
  /**
20
  * @param string $evt
21
- * @param null $nTs
22
- * @param int $nCount
23
  * @return bool
24
  */
25
- public function commitEvent( string $evt, $nCount = 1, $nTs = null ) {
26
- if ( empty( $nTs ) || !is_numeric( $nTs ) ) {
27
- $nTs = Services::Request()->ts();
28
- }
29
-
30
- /** @var EntryVO $oEvt */
31
- $oEvt = $this->getVo();
32
- $oEvt->event = $evt;
33
- $oEvt->count = max( 1, (int)$nCount );
34
- $oEvt->created_at = max( 1, $nTs );
35
  /** @var Insert $QI */
36
  $QI = $this->getQueryInserter();
37
- return $QI->insert( $oEvt );
38
  }
39
  }
8
  class Handler extends Base\Handler {
9
 
10
  /**
11
+ * @param $events - array of events: key event slug, value created_at timestamp
12
  */
13
+ public function commitEvents( array $events ) {
14
+ foreach ( $events as $event => $count ) {
15
+ $this->commitEvent( $event, $count );
16
  }
17
  }
18
 
19
  /**
20
  * @param string $evt
21
+ * @param int $count
 
22
  * @return bool
23
  */
24
+ public function commitEvent( string $evt, int $count = 1 ) {
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();
35
+ return $QI->insert( $entry );
36
  }
37
  }
src/lib/src/Databases/FileLocker/EntryVO.php CHANGED
@@ -26,7 +26,7 @@ class EntryVO extends Base\EntryVO {
26
  switch ( $key ) {
27
  case 'content':
28
  case 'file':
29
- $value = base64_decode( $value );
30
  break;
31
 
32
  default:
@@ -42,7 +42,7 @@ class EntryVO extends Base\EntryVO {
42
  switch ( $key ) {
43
  case 'content':
44
  case 'file':
45
- $value = base64_encode( $value);
46
  break;
47
 
48
  default:
26
  switch ( $key ) {
27
  case 'content':
28
  case 'file':
29
+ $value = (string)base64_decode( $value );
30
  break;
31
 
32
  default:
42
  switch ( $key ) {
43
  case 'content':
44
  case 'file':
45
+ $value = base64_encode( $value );
46
  break;
47
 
48
  default:
src/lib/src/Databases/ScanQueue/Select.php CHANGED
@@ -8,16 +8,48 @@ class Select extends Base\Select {
8
 
9
  use Common;
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  /**
12
- * @return string|null
13
  */
14
- public function getCurrentScan() {
15
- return $this->reset()
16
- ->setResultsAsVo( true )
17
- ->setColumnsToSelect( [ 'scan' ] )
18
- ->filterByStarted()
19
- ->filterByNotFinished()
20
- ->queryVar();
21
  }
22
 
23
  /**
@@ -27,21 +59,12 @@ class Select extends Base\Select {
27
  return $this->getDistinctForColumn( 'scan' );
28
  }
29
 
30
- /**
31
- * @return array[]
32
- */
33
- public function getUnfinishedScans() {
34
- $aResults = $this->reset()
35
- ->setResultsAsVo( true )
36
- ->setColumnsToSelect( [ 'scan' ] )
37
- ->filterByNotFinished()
38
- ->query();
39
- $scans = [];
40
- /** @var EntryVO $entry */
41
- foreach ( $aResults as $entry ) {
42
- $scans[ $entry->scan ] = 1;
43
- }
44
- return array_keys( $scans );
45
  }
46
 
47
  public function countForScan( string $scan ) :int {
8
 
9
  use Common;
10
 
11
+ public function countAllForEachScan() :array {
12
+ /** @var array[] $res */
13
+ $res = $this->setCustomSelect( '`scan`,COUNT(*) as count' )
14
+ ->setGroupBy( 'scan' )
15
+ ->setResultsAsVo( false )
16
+ ->setSelectResultsFormat( ARRAY_A )
17
+ ->query();
18
+ $counts = [];
19
+ if ( is_array( $res ) ) {
20
+ foreach ( $res as $entry ) {
21
+ $counts[ $entry[ 'scan' ] ] = $entry[ 'count' ];
22
+ }
23
+ }
24
+ return $counts;
25
+ }
26
+
27
+ public function countUnfinishedForEachScan() :array {
28
+ /** @var array[] $res */
29
+ $res = $this->setCustomSelect( '`scan`,COUNT(*) as count' )
30
+ ->filterByNotFinished()
31
+ ->setGroupBy( 'scan' )
32
+ ->setResultsAsVo( false )
33
+ ->setSelectResultsFormat( ARRAY_A )
34
+ ->query();
35
+ $counts = [];
36
+ if ( is_array( $res ) ) {
37
+ foreach ( $res as $entry ) {
38
+ $counts[ $entry[ 'scan' ] ] = $entry[ 'count' ];
39
+ }
40
+ }
41
+ return $counts;
42
+ }
43
+
44
  /**
45
+ * Not quite right. it'll only get the latest finished_at, not the currently processing item
46
  */
47
+ public function getCurrentScan() :string {
48
+ return (string)$this->reset()
49
+ ->setColumnsToSelect( [ 'scan' ] )
50
+ ->setOrderBy( 'finished_at', 'desc' )
51
+ ->setLimit( 1 )
52
+ ->queryVar();
 
53
  }
54
 
55
  /**
59
  return $this->getDistinctForColumn( 'scan' );
60
  }
61
 
62
+ public function getUnfinishedScans() :array {
63
+ return $this->reset()
64
+ ->filterByNotFinished()
65
+ ->addColumnToSelect( 'scan' )
66
+ ->setIsDistinct( true )
67
+ ->query();
 
 
 
 
 
 
 
 
 
68
  }
69
 
70
  public function countForScan( string $scan ) :int {
src/lib/src/Databases/Scanner/EntryVO.php CHANGED
@@ -1,4 +1,4 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner;
4
 
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner;
4
 
src/lib/src/Databases/Scanner/Insert.php CHANGED
@@ -1,9 +1,7 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
6
-
7
- class Insert extends Base\Insert {
8
 
9
  }
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner;
4
 
5
+ class Insert extends \FernleafSystems\Wordpress\Plugin\Shield\Databases\Base\Insert {
 
 
6
 
7
  }
src/lib/src/Databases/Scanner/Select.php CHANGED
@@ -8,6 +8,23 @@ class Select extends Base\Select {
8
 
9
  use Common;
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  public function countForScan( string $scan ) :int {
12
  return $this->reset()
13
  ->filterByNotIgnored()
8
 
9
  use Common;
10
 
11
+ public function countForEachScan() :array {
12
+ /** @var array[] $res */
13
+ $res = $this->setCustomSelect( '`scan`,COUNT(*) as count' )
14
+ ->setGroupBy( 'scan' )
15
+ ->setResultsAsVo( false )
16
+ ->setSelectResultsFormat( ARRAY_A )
17
+ ->filterByNotIgnored()
18
+ ->query();
19
+ $counts = [];
20
+ if ( is_array( $res ) ) {
21
+ foreach ( $res as $entry ) {
22
+ $counts[ $entry[ 'scan' ] ] = $entry[ 'count' ];
23
+ }
24
+ }
25
+ return $counts;
26
+ }
27
+
28
  public function countForScan( string $scan ) :int {
29
  return $this->reset()
30
  ->filterByNotIgnored()
src/lib/src/Modules/AuditTrail/Lib/AuditWriter.php CHANGED
@@ -23,6 +23,9 @@ class AuditWriter extends EventsListener {
23
  */
24
  protected function captureEvent( string $evt, $meta = [], $def = [] ) {
25
  $con = $this->getCon();
 
 
 
26
  if ( $def[ 'audit' ] && empty( $meta[ 'suppress_audit' ] ) ) { // only audit if it's an auditable event
27
  $entry = new AuditTrail\EntryVO();
28
  $entry->rid = $con->getShortRequestId();
23
  */
24
  protected function captureEvent( string $evt, $meta = [], $def = [] ) {
25
  $con = $this->getCon();
26
+
27
+ $meta = apply_filters( 'shield/audit_event_meta', $meta, $evt );
28
+
29
  if ( $def[ 'audit' ] && empty( $meta[ 'suppress_audit' ] ) ) { // only audit if it's an auditable event
30
  $entry = new AuditTrail\EntryVO();
31
  $entry->rid = $con->getShortRequestId();
src/lib/src/Modules/Autoupdates/Processor.php CHANGED
@@ -71,13 +71,13 @@ class Processor extends BaseShield\Processor {
71
  private function trackAssetsVersions() {
72
  $aAssVers = $this->getTrackedAssetsVersions();
73
 
74
- $oWpPlugins = Services::WpPlugins();
75
- foreach ( array_keys( $oWpPlugins->getUpdates() ) as $sFile ) {
76
- $aAssVers[ 'plugins' ][ $sFile ] = $oWpPlugins->getPluginAsVo( $sFile )->Version;
77
  }
78
- $oWpThemes = Services::WpThemes();
79
- foreach ( array_keys( $oWpThemes->getUpdates() ) as $sFile ) {
80
- $aAssVers[ 'themes' ][ $sFile ] = $oWpThemes->getTheme( $sFile )->get( 'Version' );
81
  }
82
  $this->assetsVersions = $aAssVers;
83
  }
71
  private function trackAssetsVersions() {
72
  $aAssVers = $this->getTrackedAssetsVersions();
73
 
74
+ $WPP = Services::WpPlugins();
75
+ foreach ( array_keys( $WPP->getUpdates() ) as $file ) {
76
+ $aAssVers[ 'plugins' ][ $file ] = $WPP->getPluginAsVo( $file )->Version;
77
  }
78
+ $WPT = Services::WpThemes();
79
+ foreach ( array_keys( $WPT->getUpdates() ) as $file ) {
80
+ $aAssVers[ 'themes' ][ $file ] = $WPT->getTheme( $file )->get( 'Version' );
81
  }
82
  $this->assetsVersions = $aAssVers;
83
  }
src/lib/src/Modules/Base/Lib/Rest/Utility/RestLocker.php CHANGED
@@ -54,8 +54,7 @@ class RestLocker {
54
  */
55
  private function getLockFile() {
56
  try {
57
- $sBase = $this->getRestRoute()->getWorkingDir();
58
- $file = path_join( $sBase, 'rest_process.lock' );
59
  }
60
  catch ( \Exception $e ) {
61
  $file = false;
54
  */
55
  private function getLockFile() {
56
  try {
57
+ $file = path_join( $this->getRestRoute()->getWorkingDir(), 'rest_process.lock' );
 
58
  }
59
  catch ( \Exception $e ) {
60
  $file = false;
src/lib/src/Modules/Base/ModCon.php CHANGED
@@ -270,8 +270,8 @@ abstract class ModCon {
270
  $this->loadProcessor();
271
  }
272
  try {
273
- $bSkip = (bool)$opts->getFeatureProperty( 'skip_processor' );
274
- if ( !$bSkip && !$this->isUpgrading() && $this->isModuleEnabled() && $this->isReadyToExecute() ) {
275
  $this->doExecuteProcessor();
276
  }
277
  }
270
  $this->loadProcessor();
271
  }
272
  try {
273
+ $skip = (bool)$opts->getFeatureProperty( 'skip_processor' );
274
+ if ( !$skip && !$this->isUpgrading() && $this->isModuleEnabled() && $this->isReadyToExecute() ) {
275
  $this->doExecuteProcessor();
276
  }
277
  }
src/lib/src/Modules/Base/Options/WildCardOptions.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Options;
4
+
5
+ use function path_join;
6
+
7
+ class WildCardOptions {
8
+
9
+ const FILE_PATH_REL = 0;
10
+ const URL_PATH = 1;
11
+
12
+ public function clean( array $optValues, array $checks, int $dataType ) :array {
13
+
14
+ $optValues = $this->basicCleanValues( $optValues, $dataType );
15
+ $checks = $this->preProcessChecks( $checks, $dataType );
16
+
17
+ $cleanedValues = [];
18
+ foreach ( $optValues as $value ) {
19
+
20
+ $cleanedValues[ $value ] = $value;
21
+
22
+ $valueRegEx = $this->buildFullRegexValue( $value, $dataType );
23
+ foreach ( $checks as $check ) {
24
+ if ( preg_match( $valueRegEx, $check ) ) {
25
+ $cleanedValues[ $value ] = false;
26
+ break;
27
+ }
28
+ }
29
+ }
30
+
31
+ return array_values( array_filter( $cleanedValues ) );
32
+ }
33
+
34
+ protected function preProcessChecks( array $checks, int $type ) :array {
35
+
36
+ switch ( $type ) {
37
+
38
+ case self::FILE_PATH_REL:
39
+ $checks = array_merge( $checks, array_map( 'untrailingslashit', $checks ) );
40
+ break;
41
+
42
+ case self::URL_PATH:
43
+ $checks = array_map( function ( $path ) {
44
+ $path = '/'.ltrim( $path, '/' );
45
+ return '/'.trim( $path, '/' );
46
+ }, $checks );
47
+ $checks = array_merge( $checks, array_map( 'trailingslashit', $checks ) );
48
+ break;
49
+
50
+ default:
51
+ break;
52
+ }
53
+
54
+ return array_unique( $checks );
55
+ }
56
+
57
+ protected function basicCleanValues( array $optValues, int $type ) :array {
58
+
59
+ $optValues = array_filter( array_map( function ( $value ) {
60
+ return strtolower( trim( $value ) );
61
+ }, $optValues ) );
62
+
63
+ switch ( $type ) {
64
+
65
+ case self::FILE_PATH_REL:
66
+ $optValues = array_map( function ( string $relPath ) {
67
+ $relPath = wp_normalize_path( $relPath );
68
+ if ( strpos( $relPath, wp_normalize_path( ABSPATH ) ) === 0 ) {
69
+ $relPath = str_replace( wp_normalize_path( ABSPATH ), '', $relPath );
70
+ }
71
+ return ltrim( $relPath, '/' );
72
+ }, $optValues );
73
+ break;
74
+
75
+ case self::URL_PATH:
76
+ $optValues = array_map( function ( $path ) {
77
+ if ( strpos( $path, '*' ) !== 0 ) {
78
+ $path = '/'.ltrim( $path, '/' );
79
+ }
80
+ return $path;
81
+ }, $optValues );
82
+ break;
83
+
84
+ default:
85
+ break;
86
+ }
87
+
88
+ return array_unique( $optValues );
89
+ }
90
+
91
+ public function buildFullRegexValue( string $value, int $type ) :string {
92
+ $valueRegEx = $this->convertValueToRegEx( $value, $type );
93
+
94
+ switch ( $type ) {
95
+ case self::FILE_PATH_REL:
96
+ $fullValue = path_join( ABSPATH, $valueRegEx );
97
+ break;
98
+
99
+ case self::URL_PATH:
100
+ default:
101
+ $fullValue = $valueRegEx;
102
+ break;
103
+ }
104
+
105
+ return sprintf( '#^%s$#i', $fullValue );
106
+ }
107
+
108
+ protected function convertValueToRegEx( string $value, int $type ) :string {
109
+
110
+ switch ( $type ) {
111
+ case self::FILE_PATH_REL:
112
+ if ( preg_match( '#/$#', $value ) ) {
113
+ $value .= '*';
114
+ }
115
+ break;
116
+
117
+ case self::URL_PATH:
118
+ default:
119
+ break;
120
+ }
121
+ return str_replace( 'WILDCARDSTAR', '.*', preg_quote( str_replace( '*', 'WILDCARDSTAR', $value ), '#' ) );
122
+ }
123
+ }
src/lib/src/Modules/Base/UI.php CHANGED
@@ -251,6 +251,7 @@ class UI {
251
  ],
252
  'imgs' => [
253
  'svgs' => [
 
254
  'triangle' => $con->svgs->raw( 'bootstrap/triangle-fill.svg' ),
255
  ],
256
  'favicon' => $urlBuilder->forImage( 'pluginlogo_24x24.png' ),
251
  ],
252
  'imgs' => [
253
  'svgs' => [
254
+ 'ignore' => $con->svgs->raw( 'bootstrap/eye-slash-fill.svg' ),
255
  'triangle' => $con->svgs->raw( 'bootstrap/triangle-fill.svg' ),
256
  ],
257
  'favicon' => $urlBuilder->forImage( 'pluginlogo_24x24.png' ),
src/lib/src/Modules/BaseShield/ModCon.php CHANGED
@@ -5,7 +5,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
8
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
9
  use FernleafSystems\Wordpress\Services\Services;
10
  use FernleafSystems\Wordpress\Services\Utilities;
11
 
@@ -21,6 +20,9 @@ class ModCon extends Base\ModCon {
21
  */
22
  private static $bVisitorIsWhitelisted;
23
 
 
 
 
24
  public function canCacheDirWrite() :bool {
25
  return ( new Shield\Modules\Plugin\Lib\TestCacheDirWrite() )
26
  ->setMod( $this->getCon()->getModule_Plugin() )
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
 
8
  use FernleafSystems\Wordpress\Services\Services;
9
  use FernleafSystems\Wordpress\Services\Utilities;
10
 
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() )
src/lib/src/Modules/CommentsFilter/ModCon.php CHANGED
@@ -91,10 +91,7 @@ class ModCon extends BaseShield\ModCon {
91
  $this->getOptions()->setOpt( 'enable_antibot_check', $enabled ? 'Y' : 'N' );
92
  }
93
 
94
- /**
95
- * @return string
96
- */
97
- public function getSpamBlacklistFile() {
98
- return $this->getCon()->getPluginCachePath( 'spamblacklist.txt' );
99
  }
100
  }
91
  $this->getOptions()->setOpt( 'enable_antibot_check', $enabled ? 'Y' : 'N' );
92
  }
93
 
94
+ public function getSpamBlacklistFile() :string {
95
+ return $this->getCon()->paths->forCacheItem( 'spamblacklist.txt' );
 
 
 
96
  }
97
  }
src/lib/src/Modules/Email/Processor.php CHANGED
@@ -153,16 +153,22 @@ class Processor extends BaseShield\Processor {
153
  */
154
  public function setMailFrom( $from ) {
155
  $DP = Services::Data();
156
- $proposed = apply_filters( 'icwp_shield_from_email', '' );
 
 
 
 
 
157
  if ( $DP->validEmail( $proposed ) ) {
158
  $from = $proposed;
159
  }
 
160
  // We help out by trying to correct any funky "from" addresses
161
  // So, at the very least, we don't fail on this for our emails.
162
  if ( !$DP->validEmail( $from ) ) {
163
- $urlParts = @parse_url( Services::WpGeneral()->getWpUrl() );
164
- if ( !empty( $urlParts[ 'host' ] ) ) {
165
- $proposed = 'wordpress@'.$urlParts[ 'host' ];
166
  if ( $DP->validEmail( $proposed ) ) {
167
  $from = $proposed;
168
  }
@@ -176,7 +182,11 @@ class Processor extends BaseShield\Processor {
176
  * @return string
177
  */
178
  public function setMailFromName( $name ) :string {
179
- $proposed = apply_filters( 'icwp_shield_from_email_name', '' );
 
 
 
 
180
  if ( !empty( $proposed ) ) {
181
  $name = $proposed;
182
  }
153
  */
154
  public function setMailFrom( $from ) {
155
  $DP = Services::Data();
156
+
157
+ $proposed = trim( (string)apply_filters(
158
+ 'shield/email_from',
159
+ apply_filters( 'icwp_shield_from_email', $from )
160
+ ) );
161
+
162
  if ( $DP->validEmail( $proposed ) ) {
163
  $from = $proposed;
164
  }
165
+
166
  // We help out by trying to correct any funky "from" addresses
167
  // So, at the very least, we don't fail on this for our emails.
168
  if ( !$DP->validEmail( $from ) ) {
169
+ $host = @parse_url( Services::WpGeneral()->getWpUrl(), PHP_URL_HOST );
170
+ if ( !empty( $host ) ) {
171
+ $proposed = 'wordpress@'.$host;
172
  if ( $DP->validEmail( $proposed ) ) {
173
  $from = $proposed;
174
  }
182
  * @return string
183
  */
184
  public function setMailFromName( $name ) :string {
185
+ $proposed = apply_filters(
186
+ 'shield/email_from_name',
187
+ apply_filters( 'icwp_shield_from_email_name', '' )
188
+ );
189
+
190
  if ( !empty( $proposed ) ) {
191
  $name = $proposed;
192
  }
src/lib/src/Modules/Events/Lib/Reports/KeyStats.php CHANGED
@@ -8,10 +8,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports\BaseRe
8
 
9
  class KeyStats extends BaseReporter {
10
 
11
- /**
12
- * @inheritDoc
13
- */
14
- public function build() {
15
  $alerts = [];
16
 
17
  /** @var Events\ModCon $mod */
8
 
9
  class KeyStats extends BaseReporter {
10
 
11
+ public function build() :array {
 
 
 
12
  $alerts = [];
13
 
14
  /** @var Events\ModCon $mod */
src/lib/src/Modules/Events/Lib/Reports/ScanRepairs.php DELETED
@@ -1,67 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Lib\Reports;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events as DBEvents;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Options;
8
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports\BaseReporter;
9
-
10
- class ScanRepairs extends BaseReporter {
11
-
12
- /**
13
- * @inheritDoc
14
- */
15
- public function build() {
16
- $aAlerts = [];
17
-
18
- /** @var Events\ModCon $mod */
19
- $mod = $this->getMod();
20
- /** @var DBEvents\Select $selectorEvents */
21
- $selectorEvents = $mod->getDbHandler_Events()->getQuerySelector();
22
- /** @var Events\Strings $strings */
23
- $strings = $mod->getStrings();
24
-
25
- $report = $this->getReport();
26
-
27
- $counts = [];
28
-
29
- /** @var Options $oHGOptions */
30
- $oHGOptions = $this->getCon()->getModule_HackGuard()->getOptions();
31
- foreach ( $oHGOptions->getScanSlugs() as $scan ) {
32
- try {
33
- $event = $scan.'_item_repair_success';
34
- $count = $selectorEvents
35
- ->filterByEvent( $event )
36
- ->filterByBoundary( $report->interval_start_at, $report->interval_end_at )
37
- ->count();
38
- if ( $count > 0 ) {
39
- $counts[ $scan ] = [
40
- 'count' => $count,
41
- 'name' => $strings->getEventName( $event ),
42
- ];
43
- }
44
- }
45
- catch ( \Exception $e ) {
46
- }
47
- }
48
-
49
- if ( count( $counts ) > 0 ) {
50
- $aAlerts[] = $this->getMod()->renderTemplate(
51
- '/components/reports/mod/events/info_keystats.twig',
52
- [
53
- 'vars' => [
54
- 'counts' => $counts
55
- ],
56
- 'strings' => [
57
- 'title' => __( 'Scanner Repairs', 'wp-simple-firewall' ),
58
- ],
59
- 'hrefs' => [
60
- ],
61
- ]
62
- );
63
- }
64
-
65
- return $aAlerts;
66
- }
67
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Events/Lib/StatsWriter.php CHANGED
@@ -4,7 +4,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Lib;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base\HandlerConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events\Handler;
7
- use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class StatsWriter extends EventsListener {
10
 
@@ -23,7 +22,10 @@ class StatsWriter extends EventsListener {
23
  protected function captureEvent( string $evt, $meta = [], $def = [] ) {
24
  if ( !empty( $def[ 'stat' ] ) ) {
25
  $stats = $this->getEventStats();
26
- $stats[ $evt ] = $meta[ 'ts' ] ?? Services::Request()->ts();
 
 
 
27
  $this->setEventStats( $stats );
28
  }
29
  }
@@ -40,16 +42,16 @@ class StatsWriter extends EventsListener {
40
  /**
41
  * @return int[]
42
  */
43
- public function getEventStats() {
44
  return is_array( $this->aEventStats ) ? $this->aEventStats : [];
45
  }
46
 
47
  /**
48
- * @param int[] $aStats
49
  * @return $this
50
  */
51
- public function setEventStats( $aStats = [] ) {
52
- $this->aEventStats = $aStats;
53
  return $this;
54
  }
55
  }
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base\HandlerConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events\Handler;
 
7
 
8
  class StatsWriter extends EventsListener {
9
 
22
  protected function captureEvent( string $evt, $meta = [], $def = [] ) {
23
  if ( !empty( $def[ 'stat' ] ) ) {
24
  $stats = $this->getEventStats();
25
+ if ( !isset( $stats[ $evt ] ) ) {
26
+ $stats[ $evt ] = 0;
27
+ }
28
+ $stats[ $evt ]++;
29
  $this->setEventStats( $stats );
30
  }
31
  }
42
  /**
43
  * @return int[]
44
  */
45
+ public function getEventStats() :array {
46
  return is_array( $this->aEventStats ) ? $this->aEventStats : [];
47
  }
48
 
49
  /**
50
+ * @param int[] $stats
51
  * @return $this
52
  */
53
+ public function setEventStats( array $stats = [] ) {
54
+ $this->aEventStats = $stats;
55
  return $this;
56
  }
57
  }
src/lib/src/Modules/Events/Reporting.php CHANGED
@@ -7,15 +7,6 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Lib\Reports;
7
 
8
  class Reporting extends Base\Reporting {
9
 
10
- /**
11
- * @inheritDoc
12
- */
13
- protected function enumAlertReporters() :array {
14
- return [
15
- new Reports\ScanRepairs(),
16
- ];
17
- }
18
-
19
  /**
20
  * @inheritDoc
21
  */
7
 
8
  class Reporting extends Base\Reporting {
9
 
 
 
 
 
 
 
 
 
 
10
  /**
11
  * @inheritDoc
12
  */
src/lib/src/Modules/Events/Strings.php CHANGED
@@ -15,10 +15,10 @@ class Strings extends Base\Strings {
15
  }
16
 
17
  /**
18
- * @param bool $bAuto
19
  * @return string[]
20
  */
21
- public function getEventNames( $bAuto = true ) {
22
  $names = [
23
  'test_cron_run' => __( 'Test Cron Run', 'wp-simple-firewall' ),
24
  'import_notify_sent' => __( 'Import Notify Sent', 'wp-simple-firewall' ),
@@ -37,6 +37,10 @@ class Strings extends Base\Strings {
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
  'antibot_fail' => __( 'Fail AntiBot Test', 'wp-simple-firewall' ),
41
  'antibot_pass' => __( 'Pass AntiBot Test', 'wp-simple-firewall' ),
42
  'bottrack_404' => sprintf( '%s: %s',
@@ -143,18 +147,9 @@ class Strings extends Base\Strings {
143
  __( 'Scan Item Discovered', 'wp-simple-firewall' ),
144
  __( 'Vulnerabilities', 'wp-simple-firewall' )
145
  ),
146
- 'apc_item_repair_success' => __( 'Abandoned Plugin Repair Success', 'wp-simple-firewall' ),
147
- 'apc_item_repair_fail' => __( 'Abandoned Plugin Repair Failure', 'wp-simple-firewall' ),
148
- 'mal_item_repair_success' => __( 'Malware File Repair Success', 'wp-simple-firewall' ),
149
- 'mal_item_repair_fail' => __( 'Malware File Repair Failure', 'wp-simple-firewall' ),
150
- 'ptg_item_repair_success' => __( 'Plugin/Theme File Repair Success', 'wp-simple-firewall' ),
151
- 'ptg_item_repair_fail' => __( 'Plugin/Theme File Repair Failure', 'wp-simple-firewall' ),
152
- 'ufc_item_repair_success' => __( 'Unrecognised File Deleted Success', 'wp-simple-firewall' ),
153
- 'ufc_item_repair_fail' => __( 'Unrecognised File Deleted Failure', 'wp-simple-firewall' ),
154
- 'wcf_item_repair_success' => __( 'WordPress Core File Repair Success', 'wp-simple-firewall' ),
155
- 'wcf_item_repair_fail' => __( 'WordPress Core File Repair Failure', 'wp-simple-firewall' ),
156
- 'wpv_item_repair_success' => __( 'Vulnerable WordPress Plugin Repair Success', 'wp-simple-firewall' ),
157
- 'wpv_item_repair_fail' => __( 'Vulnerable WordPress Plugin Repair Failure', 'wp-simple-firewall' ),
158
  '2fa_backupcode_verified' => __( '', 'wp-simple-firewall' ),
159
  '2fa_backupcode_fail' => __( '', 'wp-simple-firewall' ),
160
  '2fa_email_verified' => __( '', 'wp-simple-firewall' ),
@@ -271,7 +266,7 @@ class Strings extends Base\Strings {
271
  'lic_fail_deactivate' => __( 'License Deactivated', 'wp-simple-firewall' ),
272
  ];
273
 
274
- if ( $bAuto ) {
275
  foreach ( $names as $key => $name ) {
276
  if ( empty( $name ) ) {
277
  $names[ $key ] = ucwords( str_replace( '_', ' ', $key ) );
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' ),
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',
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' ),
266
  'lic_fail_deactivate' => __( 'License Deactivated', 'wp-simple-firewall' ),
267
  ];
268
 
269
+ if ( $auto ) {
270
  foreach ( $names as $key => $name ) {
271
  if ( empty( $name ) ) {
272
  $names[ $key ] = ucwords( str_replace( '_', ' ', $key ) );
src/lib/src/Modules/HackGuard/AjaxHandler.php CHANGED
@@ -16,6 +16,10 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
16
  $req = Services::Request();
17
  switch ( $action ) {
18
 
 
 
 
 
19
  case 'scans_start':
20
  $response = $this->ajaxExec_StartScans();
21
  break;
@@ -351,33 +355,40 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
351
  private function ajaxExec_CheckScans() :array {
352
  /** @var ModCon $mod */
353
  $mod = $this->getMod();
354
- /** @var Strings $oStrings */
355
- $oStrings = $mod->getStrings();
356
- /** @var Shield\Databases\ScanQueue\Select $oSel */
357
- $oSel = $mod->getDbHandler_ScanQueue()->getQuerySelector();
358
-
359
- $oQueCon = $mod->getScanQueueController();
360
- $sCurrent = $oSel->getCurrentScan();
361
- $bHasCurrent = !empty( $sCurrent );
362
- if ( $bHasCurrent ) {
363
- $sCurrentScan = $oStrings->getScanName( $sCurrent );
 
 
 
 
 
 
 
364
  }
365
  else {
366
- $sCurrentScan = __( 'No scan running.', 'wp-simple-firewall' );
 
367
  }
368
 
369
  return [
370
  'success' => true,
371
- 'running' => $oQueCon->getScansRunningStates(),
372
  'vars' => [
373
  'progress_html' => $mod->renderTemplate(
374
  '/wpadmin_pages/insights/scans/modal/progress_snippet.twig',
375
  [
376
  'current_scan' => __( 'Current Scan', 'wp-simple-firewall' ),
377
- 'scan' => $sCurrentScan,
378
- 'remaining_scans' => sprintf( __( '%s of %s scans remaining.', 'wp-simple-firewall' ),
379
- count( $oSel->getUnfinishedScans() ), count( $oSel->getInitiatedScans() ) ),
380
- 'progress' => 100*$oQueCon->getScanJobProgress(),
381
  'patience_1' => __( 'Please be patient.', 'wp-simple-firewall' ),
382
  'patience_2' => __( 'Some scans can take quite a while to complete.', 'wp-simple-firewall' ),
383
  'completed' => __( 'Scans completed.', 'wp-simple-firewall' ).' '.__( 'Reloading page', 'wp-simple-firewall' ).'...'
@@ -442,4 +453,19 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
442
  'message' => $msg,
443
  ];
444
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
445
  }
16
  $req = Services::Request();
17
  switch ( $action ) {
18
 
19
+ case 'scanresults_action':
20
+ $response = $this->ajaxExec_ScanTableAction();
21
+ break;
22
+
23
  case 'scans_start':
24
  $response = $this->ajaxExec_StartScans();
25
  break;
355
  private function ajaxExec_CheckScans() :array {
356
  /** @var ModCon $mod */
357
  $mod = $this->getMod();
358
+ /** @var Strings $strings */
359
+ $strings = $mod->getStrings();
360
+ /** @var Shield\Databases\ScanQueue\Select $selector */
361
+ $selector = $mod->getDbHandler_ScanQueue()->getQuerySelector();
362
+
363
+ $queueCon = $mod->getScanQueueController();
364
+ $current = $selector->getCurrentScan();
365
+ $hasCurrent = !empty( $current );
366
+ if ( $hasCurrent ) {
367
+ $currentScan = $strings->getScanName( $current );
368
+ }
369
+ else {
370
+ $currentScan = __( 'No scan running.', 'wp-simple-firewall' );
371
+ }
372
+
373
+ if ( count( $selector->getInitiatedScans() ) === 0 ) {
374
+ $remainingScans = __( 'No scans remaining.', 'wp-simple-firewall' );
375
  }
376
  else {
377
+ $remainingScans = sprintf( __( '%s of %s scans remaining.', 'wp-simple-firewall' ),
378
+ count( $selector->getUnfinishedScans() ), count( $selector->getInitiatedScans() ) );
379
  }
380
 
381
  return [
382
  'success' => true,
383
+ 'running' => $queueCon->getScansRunningStates(),
384
  'vars' => [
385
  'progress_html' => $mod->renderTemplate(
386
  '/wpadmin_pages/insights/scans/modal/progress_snippet.twig',
387
  [
388
  'current_scan' => __( 'Current Scan', 'wp-simple-firewall' ),
389
+ 'scan' => $currentScan,
390
+ 'remaining_scans' => $remainingScans,
391
+ 'progress' => 100*$queueCon->getScanJobProgress(),
 
392
  'patience_1' => __( 'Please be patient.', 'wp-simple-firewall' ),
393
  'patience_2' => __( 'Some scans can take quite a while to complete.', 'wp-simple-firewall' ),
394
  'completed' => __( 'Scans completed.', 'wp-simple-firewall' ).' '.__( 'Reloading page', 'wp-simple-firewall' ).'...'
453
  'message' => $msg,
454
  ];
455
  }
456
+
457
+ private function ajaxExec_ScanTableAction() :array {
458
+ try {
459
+ return ( new Lib\ScanTables\DelegateAjaxHandler() )
460
+ ->setMod( $this->getMod() )
461
+ ->processAjaxAction();
462
+ }
463
+ catch ( \Exception $e ) {
464
+ return [
465
+ 'success' => false,
466
+ 'page_reload' => true,
467
+ 'message' => $e->getMessage(),
468
+ ];
469
+ }
470
+ }
471
  }
src/lib/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php CHANGED
@@ -244,10 +244,4 @@ class FileLockerController {
244
  protected function setState( array $state ) {
245
  $this->getOptions()->setOpt( 'filelocker_state', $state );
246
  }
247
-
248
- /**
249
- * @deprecated 11.4
250
- */
251
- public function processFileLocks() {
252
- }
253
  }
244
  protected function setState( array $state ) {
245
  $this->getOptions()->setOpt( 'filelocker_state', $state );
246
  }
 
 
 
 
 
 
247
  }
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Accept.php CHANGED
@@ -13,11 +13,9 @@ use FernleafSystems\Wordpress\Services\Services;
13
  class Accept extends BaseOps {
14
 
15
  /**
16
- * @param FileLocker\EntryVO $lock
17
- * @return bool
18
  * @throws \ErrorException
19
  */
20
- public function run( $lock ) {
21
  /** @var ModCon $mod */
22
  $mod = $this->getMod();
23
 
13
  class Accept extends BaseOps {
14
 
15
  /**
 
 
16
  * @throws \ErrorException
17
  */
18
+ public function run( FileLocker\EntryVO $lock ) :bool {
19
  /** @var ModCon $mod */
20
  $mod = $this->getMod();
21
 
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Diff.php CHANGED
@@ -14,18 +14,17 @@ class Diff extends BaseOps {
14
 
15
  /**
16
  * @param FileLocker\EntryVO $lock
17
- * @return bool
18
  * @throws \Exception
19
  */
20
- public function run( $lock ) {
21
-
22
- $oFS = Services::WpFs();
23
 
24
- if ( !$oFS->isFile( $lock->file ) ) {
25
  throw new \Exception( __( 'File is missing or could not be read.', 'wp-simple-firewall' ) );
26
  }
27
 
28
- $current = Services::WpFs()->getFileContent( $lock->file );
29
  if ( empty( $current ) ) {
30
  throw new \Exception( __( 'File is empty or could not be read.', 'wp-simple-firewall' ) );
31
  }
@@ -48,21 +47,21 @@ class Diff extends BaseOps {
48
  }
49
 
50
  /**
51
- * @param string $sOriginal
52
- * @param string $sCurrent
53
  * @return string
54
  * @throws \Exception
55
  */
56
- private function useWpHashes( $sOriginal, $sCurrent ) {
57
- $aRes = ( new WpHashes\Util\Diff() )->getDiff( $sOriginal, $sCurrent );
58
- if ( !is_array( $aRes ) || empty( $aRes[ 'html' ] ) ) {
59
  throw new \Exception( 'Could not get a valid diff for this file.' );
60
  }
61
  return sprintf( '<style>%s</style>%s',
62
  'table.diff.diff-wrapper tbody tr td:nth-child(2){ width:auto;}'.
63
  'table.diff.diff-wrapper { table-layout: auto;}'.
64
- base64_decode( $aRes[ 'html' ][ 'css_default' ] ),
65
- base64_decode( $aRes[ 'html' ][ 'content' ] )
66
  );
67
  }
68
 
14
 
15
  /**
16
  * @param FileLocker\EntryVO $lock
17
+ * @return string
18
  * @throws \Exception
19
  */
20
+ public function run( FileLocker\EntryVO $lock ) {
21
+ $FS = Services::WpFs();
 
22
 
23
+ if ( !$FS->isFile( $lock->file ) ) {
24
  throw new \Exception( __( 'File is missing or could not be read.', 'wp-simple-firewall' ) );
25
  }
26
 
27
+ $current = $FS->getFileContent( $lock->file );
28
  if ( empty( $current ) ) {
29
  throw new \Exception( __( 'File is empty or could not be read.', 'wp-simple-firewall' ) );
30
  }
47
  }
48
 
49
  /**
50
+ * @param string $original
51
+ * @param string $current
52
  * @return string
53
  * @throws \Exception
54
  */
55
+ private function useWpHashes( $original, $current ) :string {
56
+ $res = ( new WpHashes\Util\Diff() )->getDiff( $original, $current );
57
+ if ( !is_array( $res ) || empty( $res[ 'html' ] ) ) {
58
  throw new \Exception( 'Could not get a valid diff for this file.' );
59
  }
60
  return sprintf( '<style>%s</style>%s',
61
  'table.diff.diff-wrapper tbody tr td:nth-child(2){ width:auto;}'.
62
  'table.diff.diff-wrapper { table-layout: auto;}'.
63
+ base64_decode( $res[ 'html' ][ 'css_default' ] ),
64
+ base64_decode( $res[ 'html' ][ 'content' ] )
65
  );
66
  }
67
 
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Restore.php CHANGED
@@ -12,23 +12,19 @@ use FernleafSystems\Wordpress\Services\Services;
12
  */
13
  class Restore extends BaseOps {
14
 
15
- /**
16
- * @param Databases\FileLocker\EntryVO $oRecord
17
- * @return mixed
18
- */
19
- public function run( $oRecord ) {
20
  $bReverted = Services::WpFs()->putFileContent(
21
- $oRecord->file,
22
  ( new ReadOriginalFileContent() )
23
  ->setMod( $this->getMod() )
24
- ->run( $oRecord )
25
  );
26
  if ( $bReverted ) {
27
  /** @var ModCon $mod */
28
  $mod = $this->getMod();
29
- /** @var Databases\FileLocker\Update $oUpd */
30
- $oUpd = $mod->getDbHandler_FileLocker()->getQueryUpdater();
31
- $oUpd->markReverted( $oRecord );
32
  $this->clearFileLocksCache();
33
  }
34
  return $bReverted;
12
  */
13
  class Restore extends BaseOps {
14
 
15
+ public function run( Databases\FileLocker\EntryVO $record ) :bool {
 
 
 
 
16
  $bReverted = Services::WpFs()->putFileContent(
17
+ $record->file,
18
  ( new ReadOriginalFileContent() )
19
  ->setMod( $this->getMod() )
20
+ ->run( $record )
21
  );
22
  if ( $bReverted ) {
23
  /** @var ModCon $mod */
24
  $mod = $this->getMod();
25
+ /** @var Databases\FileLocker\Update $update */
26
+ $update = $mod->getDbHandler_FileLocker()->getQueryUpdater();
27
+ $update->markReverted( $record );
28
  $this->clearFileLocksCache();
29
  }
30
  return $bReverted;
src/lib/src/Modules/HackGuard/Lib/Reports/FileLockerAlerts.php CHANGED
@@ -8,21 +8,18 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports\BaseRe
8
 
9
  class FileLockerAlerts extends BaseReporter {
10
 
11
- /**
12
- * @inheritDoc
13
- */
14
- public function build() {
15
- $aAlerts = [];
16
 
17
  /** @var HackGuard\ModCon $mod */
18
  $mod = $this->getMod();
19
 
20
- $oLockOps = ( new HackGuard\Lib\FileLocker\Ops\LoadFileLocks() )
21
  ->setMod( $this->getMod() );
22
- $aNotNotified = $oLockOps->withProblemsNotNotified();
23
 
24
- if ( count( $aNotNotified ) > 0 ) {
25
- $aAlerts[] = $this->getMod()->renderTemplate(
26
  '/components/reports/mod/hack_protect/alert_filelocker.twig',
27
  [
28
  'vars' => [
@@ -31,7 +28,7 @@ class FileLockerAlerts extends BaseReporter {
31
  'strings' => [
32
  'title' => __( 'File Locker Changes Detected', 'wp-simple-firewall' ),
33
  'file_changed' => __( 'Changes have been detected in the contents of critical files.', 'wp-simple-firewall' ),
34
- 'total_files' => sprintf( '%s: %s', __( 'Total Changed Files', 'wp-simple-firewall' ), count( $aNotNotified ) ),
35
  'view_results' => __( 'Click Here To View File Locker Results', 'wp-simple-firewall' ),
36
  ],
37
  'hrefs' => [
@@ -39,23 +36,23 @@ class FileLockerAlerts extends BaseReporter {
39
  ],
40
  ]
41
  );
42
- $this->markAlertsAsNotified( $aNotNotified );
43
- $oLockOps->clearLocksCache();
44
  }
45
 
46
- return $aAlerts;
47
  }
48
 
49
  /**
50
- * @param FileLocker\EntryVO[] $aNotNotified
51
  */
52
- private function markAlertsAsNotified( $aNotNotified ) {
53
  /** @var HackGuard\ModCon $mod */
54
  $mod = $this->getMod();
55
- /** @var FileLocker\Update $oUpdater */
56
- $oUpdater = $mod->getDbHandler_FileLocker()->getQueryUpdater();
57
- foreach ( $aNotNotified as $oEntry ) {
58
- $oUpdater->markNotified( $oEntry );
59
  }
60
  }
61
  }
8
 
9
  class FileLockerAlerts extends BaseReporter {
10
 
11
+ public function build() :array {
12
+ $alerts = [];
 
 
 
13
 
14
  /** @var HackGuard\ModCon $mod */
15
  $mod = $this->getMod();
16
 
17
+ $lockOps = ( new HackGuard\Lib\FileLocker\Ops\LoadFileLocks() )
18
  ->setMod( $this->getMod() );
19
+ $notNotified = $lockOps->withProblemsNotNotified();
20
 
21
+ if ( count( $notNotified ) > 0 ) {
22
+ $alerts[] = $this->getMod()->renderTemplate(
23
  '/components/reports/mod/hack_protect/alert_filelocker.twig',
24
  [
25
  'vars' => [
28
  'strings' => [
29
  'title' => __( 'File Locker Changes Detected', 'wp-simple-firewall' ),
30
  'file_changed' => __( 'Changes have been detected in the contents of critical files.', 'wp-simple-firewall' ),
31
+ 'total_files' => sprintf( '%s: %s', __( 'Total Changed Files', 'wp-simple-firewall' ), count( $notNotified ) ),
32
  'view_results' => __( 'Click Here To View File Locker Results', 'wp-simple-firewall' ),
33
  ],
34
  'hrefs' => [
36
  ],
37
  ]
38
  );
39
+ $this->markAlertsAsNotified( $notNotified );
40
+ $lockOps->clearLocksCache();
41
  }
42
 
43
+ return $alerts;
44
  }
45
 
46
  /**
47
+ * @param FileLocker\EntryVO[] $setNotified
48
  */
49
+ private function markAlertsAsNotified( $setNotified ) {
50
  /** @var HackGuard\ModCon $mod */
51
  $mod = $this->getMod();
52
+ /** @var FileLocker\Update $updater */
53
+ $updater = $mod->getDbHandler_FileLocker()->getQueryUpdater();
54
+ foreach ( $setNotified as $entry ) {
55
+ $updater->markNotified( $entry );
56
  }
57
  }
58
  }
src/lib/src/Modules/HackGuard/Lib/Reports/ScanAlerts.php CHANGED
@@ -9,13 +9,7 @@ use FernleafSystems\Wordpress\Services\Services;
9
 
10
  class ScanAlerts extends BaseReporter {
11
 
12
- /**
13
- * @inheritDoc
14
- */
15
- public function build() {
16
- /** @var HackGuard\ModCon $mod */
17
- $mod = $this->getMod();
18
-
19
  $alerts = [];
20
 
21
  /** @var HackGuard\Strings $strings */
@@ -44,6 +38,9 @@ class ScanAlerts extends BaseReporter {
44
  'strings' => [
45
  'title' => __( 'New Scan Results', 'wp-simple-firewall' ),
46
  'view_results' => __( 'Click Here To View Scan Results Details', 'wp-simple-firewall' ),
 
 
 
47
  ],
48
  'hrefs' => [
49
  'view_results' => $this->getCon()
9
 
10
  class ScanAlerts extends BaseReporter {
11
 
12
+ public function build() :array {
 
 
 
 
 
 
13
  $alerts = [];
14
 
15
  /** @var HackGuard\Strings $strings */
38
  'strings' => [
39
  'title' => __( 'New Scan Results', 'wp-simple-firewall' ),
40
  'view_results' => __( 'Click Here To View Scan Results Details', 'wp-simple-firewall' ),
41
+ 'note_changes' => sprintf( '%s: %s', __( 'Note', 'wp-simple-firewall' ),
42
+ __( 'Depending on previous actions taken on the site or file system changes, these results may no longer be available to view.', 'wp-simple-firewall' ) ),
43
+
44
  ],
45
  'hrefs' => [
46
  'view_results' => $this->getCon()
src/lib/src/Modules/HackGuard/Lib/Reports/ScanRepairs.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
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 {
11
+
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
+
26
+ $repairs = [];
27
+ $repairEvents = [
28
+ 'scan_item_repair_success',
29
+ 'scan_item_repair_fail',
30
+ 'scan_item_delete_success',
31
+ ];
32
+
33
+ $total = 0;
34
+ foreach ( $repairEvents as $event ) {
35
+ $eventTotal = $selectorEvents
36
+ ->filterByBoundary( $report->interval_start_at, $report->interval_end_at )
37
+ ->sumEvent( $event );
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
+ }
66
+
67
+ if ( !empty( $repairs ) ) {
68
+ $alerts[] = $this->getMod()->renderTemplate(
69
+ '/components/reports/mod/hack_protect/alert_scanrepairs.twig',
70
+ [
71
+ 'vars' => [
72
+ 'total' => $total,
73
+ 'repairs' => $repairs,
74
+ ],
75
+ 'strings' => [
76
+ 'title' => \__( 'Scanner Repairs', 'wp-simple-firewall' ),
77
+ 'audit_trail' => \__( 'View all repairs and file deletions in the Audit Trail', 'wp-simple-firewall' ),
78
+ ],
79
+ 'hrefs' => [
80
+ 'audit_trail' => $this->getCon()
81
+ ->getModule_Insights()
82
+ ->getUrl_SubInsightsPage( 'audit' ),
83
+ ],
84
+ ]
85
+ );
86
+ }
87
+
88
+ return $alerts;
89
+ }
90
+ }
src/lib/src/Modules/HackGuard/Lib/ScanTables/DelegateAjaxHandler.php ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\ScanTables;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
8
+ use FernleafSystems\Wordpress\Services\Services;
9
+
10
+ class DelegateAjaxHandler {
11
+
12
+ use Shield\Modules\ModConsumer;
13
+
14
+ /**
15
+ * @return array
16
+ * @throws \Exception
17
+ */
18
+ public function processAjaxAction() :array {
19
+ $action = Services::Request()->post( 'sub_action' );
20
+ switch ( $action ) {
21
+
22
+ case 'retrieve_table_data':
23
+ $response = $this->retrieveTableData();
24
+ break;
25
+
26
+ case 'delete':
27
+ case 'ignore':
28
+ case 'repair':
29
+ case 'repair-delete':
30
+ $response = $this->doAction( $action );
31
+ break;
32
+
33
+ case 'view_file':
34
+ $response = $this->viewFile();
35
+ break;
36
+
37
+ default:
38
+ throw new \Exception( 'Not a supported scan tables sub_action: '.$action );
39
+ }
40
+ return $response;
41
+ }
42
+
43
+ /**
44
+ * @param string $action
45
+ * @return array
46
+ * @throws \Exception
47
+ */
48
+ private function doAction( string $action ) :array {
49
+ /** @var HackGuard\ModCon $mod */
50
+ $mod = $this->getMod();
51
+
52
+ $success = false;
53
+
54
+ $items = $this->getItemIDs();
55
+
56
+ $resultIT = $mod->getDbHandler_ScanResults()->getIterator();
57
+ $resultIT->setSelector(
58
+ $resultIT->getSelector()->addWhereIn( 'id', $items )
59
+ );
60
+
61
+ $scanSlugs = [];
62
+ $successfulItems = [];
63
+
64
+ /** @var Databases\Scanner\EntryVO $entry */
65
+ foreach ( $resultIT as $entry ) {
66
+ $scanSlugs[ $entry->scan ] = 1;
67
+ if ( $mod->getScanCon( $entry->scan )->executeEntryAction( $entry, $action ) ) {
68
+ $successfulItems[] = $entry->id;
69
+ }
70
+ }
71
+
72
+ $scanSlugs = array_keys( $scanSlugs );
73
+
74
+ foreach ( $scanSlugs as $slug ) {
75
+ $mod->getScanCon( $slug )->cleanStalesResults();
76
+ }
77
+
78
+ if ( count( $successfulItems ) === count( $items ) ) {
79
+ $success = true;
80
+ $msg = __( 'Action successful.' );
81
+ }
82
+ else {
83
+ $msg = __( 'An error occurred.' ).' '.__( 'Some items may not have been processed.' );
84
+ }
85
+
86
+ // We don't rescan for ignores or malware
87
+ $rescanSlugs = array_diff( $scanSlugs, [ HackGuard\Scan\Controller\Mal::SCAN_SLUG ] );
88
+ if ( !empty( $rescanSlugs ) && !in_array( $action, [ 'ignore' ] ) ) {
89
+ $mod->getScanQueueController()->startScans( $rescanSlugs );
90
+ }
91
+
92
+ return [
93
+ 'success' => $success,
94
+ 'page_reload' => false,
95
+ 'table_reload' => in_array( $action, [ 'ignore', 'repair', 'delete', 'repair-delete' ] ),
96
+ 'message' => $msg,
97
+ ];
98
+ }
99
+
100
+ private function getItemIDs() :array {
101
+ $items = Services::Request()->post( 'rids' );
102
+ if ( empty( $items ) || !is_array( $items ) ) {
103
+ throw new \Exception( 'No items selected.' );
104
+ }
105
+ return array_filter(
106
+ array_map(
107
+ function ( $rid ) {
108
+ return is_numeric( $rid ) ? intval( $rid ) : null;
109
+ },
110
+ $items
111
+ ),
112
+ function ( $rid ) {
113
+ return !is_null( $rid );
114
+ }
115
+ );
116
+ }
117
+
118
+ /**
119
+ * @return array
120
+ * @throws \Exception
121
+ */
122
+ private function viewFile() :array {
123
+ $req = Services::Request();
124
+ $rid = $req->post( 'rid' );
125
+ if ( !is_numeric( $rid ) ) {
126
+ throw new \Exception( 'Not a valid file to view' );
127
+ }
128
+
129
+ return [
130
+ 'success' => true,
131
+ 'vars' => ( new RetrieveFileContents() )
132
+ ->setMod( $this->getMod() )
133
+ ->retrieve( (int)$rid ),
134
+ ];
135
+ }
136
+
137
+ /**
138
+ * @return array
139
+ * @throws \Exception
140
+ */
141
+ private function retrieveTableData() :array {
142
+ $req = Services::Request();
143
+ return [
144
+ 'success' => true,
145
+ 'vars' => [
146
+ 'data' => ( new LoadRawTableData() )
147
+ ->setMod( $this->getMod() )
148
+ ->loadFor( $req->post( 'type' ), $req->post( 'file' ) )
149
+ ],
150
+ ];
151
+ }
152
+ }
src/lib/src/Modules/HackGuard/Lib/ScanTables/LoadRawTableData.php ADDED
@@ -0,0 +1,328 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\ScanTables;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Tool\FormatBytes;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller\{
8
+ Mal,
9
+ Ptg,
10
+ Ufc,
11
+ Wcf
12
+ };
13
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
14
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
15
+ use FernleafSystems\Wordpress\Services\Core\VOs\Assets\{
16
+ WpPluginVo,
17
+ WpThemeVo
18
+ };
19
+ use FernleafSystems\Wordpress\Services\Services;
20
+
21
+ class LoadRawTableData {
22
+
23
+ use ModConsumer;
24
+
25
+ private static $GuardFiles;
26
+
27
+ /**
28
+ * @param string $type
29
+ * @param string $file
30
+ * @return array
31
+ * @throws \Exception
32
+ */
33
+ public function loadFor( string $type, string $file ) :array {
34
+
35
+ switch ( $type ) {
36
+
37
+ case 'plugin':
38
+ $item = Services::WpPlugins()->getPluginAsVo( $file );
39
+ if ( empty( $item ) ) {
40
+ throw new \Exception( '[LoadRawTableData] Unsupported slug: '.$file );
41
+ }
42
+ $data = $this->loadForPlugin( $item );
43
+ break;
44
+
45
+ case 'theme':
46
+ $item = Services::WpThemes()->getThemeAsVo( $file );
47
+ if ( empty( $item ) ) {
48
+ throw new \Exception( '[LoadRawTableData] Unsupported slug: '.$file );
49
+ }
50
+ $data = $this->loadForTheme( $item );
51
+ break;
52
+
53
+ case 'wordpress':
54
+ $data = $this->loadForWordPress();
55
+ break;
56
+
57
+ case 'malware':
58
+ $data = $this->loadForMalware();
59
+ break;
60
+
61
+ default:
62
+ throw new \Exception( '[LoadRawTableData] Unsupported type: '.$type );
63
+ }
64
+
65
+ return $data;
66
+ }
67
+
68
+ public function loadForMalware() :array {
69
+ /** @var ModCon $mod */
70
+ $mod = $this->getMod();
71
+ try {
72
+ $files = array_map(
73
+ function ( $item ) {
74
+ /** @var Scans\Mal\ResultItem $item */
75
+ $data = $item->getRawData();
76
+
77
+ $data[ 'rid' ] = $item->VO->id;
78
+ $data[ 'file' ] = $item->path_fragment;
79
+ $data[ 'detected_at' ] = $item->VO->created_at;
80
+ $data[ 'detected_since' ] = Services::Request()
81
+ ->carbon( true )
82
+ ->setTimestamp( $item->VO->created_at )
83
+ ->diffForHumans();
84
+
85
+ $data[ 'file_as_href' ] = $this->getColumnContent_File( $item );
86
+
87
+ $data[ 'status_slug' ] = 'malware';
88
+ $data[ 'status' ] = $this->getColumnContent_FileStatus( $item, __( 'Malware', 'wp-simple-firewall' ) );
89
+
90
+ $data[ 'line_numbers' ] = implode( ', ', array_map(
91
+ function ( $line ) {
92
+ return $line + 1;
93
+ },
94
+ $item->file_lines // because lines start at ZERO
95
+ ) );
96
+ $data[ 'mal_sig' ] = sprintf( '<code style="white-space: nowrap">%s</code>', esc_html( base64_decode( $item->mal_sig ) ) );
97
+
98
+ $data[ 'file_type' ] = strtoupper( Services::Data()->getExtension( $item->path_full ) );
99
+ $data[ 'actions' ] = implode( ' ', $this->getActions( $data[ 'status_slug' ], $item ) );
100
+ return $data;
101
+ },
102
+ array_merge(
103
+ $mod->getScanCon( Mal::SCAN_SLUG )->getAllResults()->getItems()
104
+ )
105
+ );
106
+ }
107
+ catch ( \Exception $e ) {
108
+ $files = [];
109
+ }
110
+
111
+ return $files;
112
+ }
113
+
114
+ public function loadForPlugin( WpPluginVo $plugin ) :array {
115
+ return $this->getGuardFilesDataFor( $plugin );
116
+ }
117
+
118
+ public function loadForTheme( WpThemeVo $theme ) :array {
119
+ return $this->getGuardFilesDataFor( $theme );
120
+ }
121
+
122
+ public function loadForWordPress() :array {
123
+ /** @var ModCon $mod */
124
+ $mod = $this->getMod();
125
+ try {
126
+ $files = array_map(
127
+ function ( $item ) {
128
+ /** @var Scans\Wcf\ResultItem|Scans\Ufc\ResultItem $item */
129
+ $data = $item->getRawData();
130
+ $data[ 'rid' ] = $item->VO->id;
131
+ $data[ 'file' ] = $item->path_fragment;
132
+ $data[ 'detected_at' ] = $item->VO->created_at;
133
+ $data[ 'detected_since' ] = Services::Request()
134
+ ->carbon( true )
135
+ ->setTimestamp( $item->VO->created_at )
136
+ ->diffForHumans();
137
+
138
+ if ( !$item->is_missing ) {
139
+ $data[ 'file_as_href' ] = $this->getColumnContent_File( $item );
140
+ }
141
+ else {
142
+ $data[ 'file_as_href' ] = $item->path_fragment;
143
+ }
144
+
145
+ if ( $item->is_checksumfail ) {
146
+ $data[ 'status_slug' ] = 'modified';
147
+ $data[ 'status' ] = __( 'Modified', 'wp-simple-firewall' );
148
+ }
149
+ elseif ( $item->is_missing ) {
150
+ $data[ 'status_slug' ] = 'missing';
151
+ $data[ 'status' ] = __( 'Missing', 'wp-simple-firewall' );
152
+ }
153
+ else {
154
+ $data[ 'status_slug' ] = 'unrecognised';
155
+ $data[ 'status' ] = __( 'Unrecognised', 'wp-simple-firewall' );
156
+ }
157
+ $data[ 'status' ] = $this->getColumnContent_FileStatus( $item, $data[ 'status' ] );
158
+
159
+ $data[ 'file_type' ] = strtoupper( Services::Data()->getExtension( $item->path_full ) );
160
+ $data[ 'actions' ] = implode( ' ', $this->getActions( $data[ 'status_slug' ], $item ) );
161
+ return $data;
162
+ },
163
+ array_merge(
164
+ $mod->getScanCon( Wcf::SCAN_SLUG )->getAllResults()->getItems(),
165
+ $mod->getScanCon( Ufc::SCAN_SLUG )->getAllResults()->getItems()
166
+ )
167
+ );
168
+ }
169
+ catch ( \Exception $e ) {
170
+ $files = [];
171
+ }
172
+
173
+ return $files;
174
+ }
175
+
176
+ /**
177
+ * @param WpPluginVo|WpThemeVo $item
178
+ * @return array
179
+ */
180
+ private function getGuardFilesDataFor( $item ) :array {
181
+ return array_map(
182
+ function ( $item ) {
183
+
184
+ $data = $item->getRawData();
185
+ $data[ 'rid' ] = $item->VO->id;
186
+ $data[ 'file' ] = $item->path_fragment;
187
+ $data[ 'detected_at' ] = $item->VO->created_at;
188
+ $data[ 'detected_since' ] = Services::Request()
189
+ ->carbon( true )
190
+ ->setTimestamp( $item->VO->created_at )
191
+ ->diffForHumans();
192
+
193
+ if ( $item->is_different ) {
194
+ $data[ 'status_slug' ] = 'modified';
195
+ $data[ 'status' ] = __( 'Modified', 'wp-simple-firewall' );
196
+ }
197
+ elseif ( $item->is_missing ) {
198
+ $data[ 'status_slug' ] = 'missing';
199
+ $data[ 'status' ] = __( 'Missing', 'wp-simple-firewall' );
200
+ }
201
+ else {
202
+ $data[ 'status_slug' ] = 'unrecognised';
203
+ $data[ 'status' ] = __( 'Unrecognised', 'wp-simple-firewall' );
204
+ }
205
+ $data[ 'status' ] = $this->getColumnContent_FileStatus( $item, $data[ 'status' ] );
206
+
207
+ if ( !$item->is_missing ) {
208
+ $data[ 'file_as_href' ] = $this->getColumnContent_File( $item );
209
+ }
210
+ else {
211
+ $data[ 'file_as_href' ] = $item->path_fragment;
212
+ }
213
+
214
+ $data[ 'file_type' ] = strtoupper( Services::Data()->getExtension( $item->path_full ) );
215
+ $data[ 'actions' ] = implode( ' ', $this->getActions( $data[ 'status_slug' ], $item ) );
216
+ return $data;
217
+ },
218
+ $this->getGuardFiles()->getItemsForSlug( $item->asset_type === 'plugin' ? $item->file : $item->stylesheet )
219
+ );
220
+ }
221
+
222
+ /**
223
+ * @param string $status
224
+ * @param Scans\Base\ResultItem $item
225
+ * @return array
226
+ */
227
+ private function getActions( string $status, $item ) :array {
228
+ $con = $this->getCon();
229
+ /** @var ModCon $mod */
230
+ $mod = $this->getMod();
231
+ $itemActionHandler = $mod->getScanCon( $item->VO->scan )
232
+ ->getItemActionHandler()
233
+ ->setScanItem( $item );
234
+
235
+ $actions = [];
236
+
237
+ $defaultButtonClasses = [
238
+ 'btn',
239
+ 'action',
240
+ ];
241
+
242
+ if ( in_array( $status, [ 'unrecognised', 'malware' ] ) ) {
243
+ $actions[] = sprintf( '<button class="btn-danger delete %s" title="%s" data-rid="%s">%s</button>',
244
+ implode( ' ', $defaultButtonClasses ),
245
+ __( 'Delete', 'wp-simple-firewall' ),
246
+ $item->VO->id,
247
+ $con->svgs->raw( 'bootstrap/x-square.svg' )
248
+ );
249
+ }
250
+
251
+ if ( in_array( $status, [ 'modified', 'missing', 'malware' ] ) && $itemActionHandler->getRepairer()
252
+ ->canRepair() ) {
253
+ $actions[] = sprintf( '<button class="btn-warning repair %s" title="%s" data-rid="%s">%s</button>',
254
+ implode( ' ', $defaultButtonClasses ),
255
+ __( 'Repair', 'wp-simple-firewall' ),
256
+ $item->VO->id,
257
+ $con->svgs->raw( 'bootstrap/tools.svg' )
258
+ );
259
+ }
260
+
261
+ if ( in_array( $status, [ 'modified', 'unrecognised', 'malware' ] ) ) {
262
+ $actions[] = sprintf( '<button class="btn-dark href-download %s" title="%s" data-href-download="%s">%s</button>',
263
+ implode( ' ', $defaultButtonClasses ),
264
+ __( 'Download', 'wp-simple-firewall' ),
265
+ $mod->getScanCon( $item->VO->scan )->createFileDownloadLink( $item->VO->id ),
266
+ $con->svgs->raw( 'bootstrap/download.svg' )
267
+ );
268
+ }
269
+
270
+ $actions[] = sprintf( '<button class="btn-light ignore %s" title="%s" data-rid="%s">%s</button>',
271
+ implode( ' ', $defaultButtonClasses ),
272
+ __( 'Ignore', 'wp-simple-firewall' ),
273
+ $item->VO->id,
274
+ $con->svgs->raw( 'bootstrap/eye-slash-fill.svg' )
275
+ );
276
+
277
+ return $actions;
278
+ }
279
+
280
+ private function getGuardFiles() :Scans\Ptg\ResultsSet {
281
+ /** @var ModCon $mod */
282
+ $mod = $this->getMod();
283
+ if ( !isset( self::$GuardFiles ) ) {
284
+ try {
285
+ self::$GuardFiles = $mod->getScanCon( Ptg::SCAN_SLUG )->getAllResults();
286
+ }
287
+ catch ( \Exception $e ) {
288
+ self::$GuardFiles = new Scans\Ptg\ResultsSet();
289
+ }
290
+ }
291
+ return self::$GuardFiles;
292
+ }
293
+
294
+ private function getColumnContent_File( Scans\Base\FileResultItem $item ) :string {
295
+ return sprintf( '<div>%s</div>', $this->getColumnContent_FileAsHref( $item ) );
296
+ }
297
+
298
+ private function getColumnContent_FileStatus( Scans\Base\FileResultItem $item, string $status ) :string {
299
+ $content = $status;
300
+
301
+ $FS = Services::WpFs();
302
+ if ( $FS->isFile( $item->path_full ) ) {
303
+ $carbon = Services::Request()->carbon( true );
304
+ $content = sprintf( '%s<ul style="list-style: square inside"><li>%s</li></ul>',
305
+ $status,
306
+ implode( '</li><li>', [
307
+ sprintf( '%s: %s', __( 'Modified', 'wp-simple-firewall' ),
308
+ $carbon->setTimestamp( $FS->getModifiedTime( $item->path_full ) )
309
+ ->diffForHumans()
310
+ ),
311
+ sprintf( '%s: %s', __( 'Size', 'wp-simple-firewall' ),
312
+ FormatBytes::Format( $FS->getFileSize( $item->path_full ) )
313
+ )
314
+ ] )
315
+ );
316
+ }
317
+
318
+ return $content;
319
+ }
320
+
321
+ private function getColumnContent_FileAsHref( Scans\Base\FileResultItem $item ) :string {
322
+ return sprintf( '<a href="#" title="%s" class="action view-file" data-rid="%s">%s</a>',
323
+ __( 'View File Contents', 'wp-simple-firewall' ),
324
+ $item->VO->id,
325
+ $item->path_fragment
326
+ );
327
+ }
328
+ }
src/lib/src/Modules/HackGuard/Lib/ScanTables/RetrieveFileContents.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\ScanTables;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\EntryVO;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Results\ConvertBetweenTypes;
8
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
9
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
10
+ use FernleafSystems\Wordpress\Services\Services;
11
+
12
+ class RetrieveFileContents {
13
+
14
+ use ModConsumer;
15
+
16
+ /**
17
+ * @param int $rid
18
+ * @param bool $raw
19
+ * @return array
20
+ * @throws \Exception
21
+ */
22
+ public function retrieve( int $rid, bool $raw = false ) :array {
23
+ /** @var ModCon $mod */
24
+ $mod = $this->getMod();
25
+
26
+ /** @var EntryVO $record */
27
+ $record = $mod->getDbHandler_ScanResults()
28
+ ->getQuerySelector()
29
+ ->byId( $rid );
30
+ if ( empty( $record ) ) {
31
+ throw new \Exception( 'Not a valid file record' );
32
+ }
33
+ $item = ( new ConvertBetweenTypes() )
34
+ ->setScanController( $mod->getScanCon( $record->scan ) )
35
+ ->convertVoToResultItem( $record );
36
+ $path = $item->path_full;
37
+ if ( empty( $path ) ) {
38
+ throw new \Exception( 'There is no path associated with this record' );
39
+ }
40
+ $FS = Services::WpFs();
41
+ if ( !$FS->isFile( $path ) ) {
42
+ throw new \Exception( 'File does not exist.' );
43
+ }
44
+ $contents = $FS->getFileContent( $path );
45
+ if ( empty( $contents ) ) {
46
+ throw new \Exception( 'File is empty or could not be read.' );
47
+ }
48
+ if ( !$raw ) {
49
+ $modContents = Services::DataManipulation()->convertLineEndingsDosToLinux( $path );
50
+ $contents = $this->getMod()
51
+ ->renderTemplate(
52
+ '/wpadmin_pages/insights/scans/modal/code_block.twig',
53
+ [
54
+ 'lines' => explode( "\n", str_replace( "\t", " ", $modContents ) ),
55
+ ]
56
+ );
57
+ }
58
+ return [
59
+ 'contents' => $contents,
60
+ 'path' => esc_html( $item->path_fragment ),
61
+ ];
62
+ }
63
+ }
src/lib/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesForCrowdSource.php CHANGED
@@ -16,17 +16,19 @@ class BuildHashesForCrowdSource {
16
  * @param WpPluginVo|WpThemeVo $asset
17
  * @return string[]
18
  */
19
- public function build( $asset ) :array {
20
  $hashes = [];
21
  $DM = Services::DataManipulation();
22
  $dir = wp_normalize_path( $asset->getInstallDir() );
23
  try {
24
- $exts = $this->getExtensions();
 
 
25
  foreach ( StandardDirectoryIterator::create( $dir, 0, [] ) as $file ) {
26
  /** @var \SplFileInfo $file */
27
  if ( in_array( strtolower( $file->getExtension() ), $exts ) ) {
28
  $fullPath = $file->getPathname();
29
- $key = str_replace( $dir, '', wp_normalize_path( $fullPath ) );
30
  $hashes[ $key ] = hash( 'sha1', $DM->convertLineEndingsDosToLinux( $fullPath ) );
31
  }
32
  }
@@ -37,20 +39,4 @@ class BuildHashesForCrowdSource {
37
  }
38
  return $hashes;
39
  }
40
-
41
- private function getExtensions() :array {
42
- return [
43
- 'php',
44
- 'php5',
45
- 'php7',
46
- 'js',
47
- 'json',
48
- 'css',
49
- 'htm',
50
- 'html',
51
- 'svg',
52
- 'twig',
53
- 'hbs',
54
- ];
55
- }
56
  }
16
  * @param WpPluginVo|WpThemeVo $asset
17
  * @return string[]
18
  */
19
+ public function build( $asset, array $exts ) :array {
20
  $hashes = [];
21
  $DM = Services::DataManipulation();
22
  $dir = wp_normalize_path( $asset->getInstallDir() );
23
  try {
24
+ if ( empty( $exts ) ) {
25
+ throw new \Exception( 'File extensions are empty' );
26
+ }
27
  foreach ( StandardDirectoryIterator::create( $dir, 0, [] ) as $file ) {
28
  /** @var \SplFileInfo $file */
29
  if ( in_array( strtolower( $file->getExtension() ), $exts ) ) {
30
  $fullPath = $file->getPathname();
31
+ $key = strtolower( str_replace( $dir, '', wp_normalize_path( $fullPath ) ) );
32
  $hashes[ $key ] = hash( 'sha1', $DM->convertLineEndingsDosToLinux( $fullPath ) );
33
  }
34
  }
39
  }
40
  return $hashes;
41
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  }
src/lib/src/Modules/HackGuard/Lib/Snapshots/CrowdSourced/SubmitHashes.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots\CrowdSourced;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots\Build;
 
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
  use FernleafSystems\Wordpress\Services\Core\VOs\Assets\{
8
  WpPluginVo,
@@ -32,11 +33,14 @@ class SubmitHashes {
32
  * @param WpPluginVo|WpThemeVo $asset
33
  */
34
  public function run( $asset ) {
 
 
 
35
  $this->asset = $asset;
36
 
37
  if ( $this->canSubmitAsset() ) {
38
  $this->hashes = ( new Build\BuildHashesForCrowdSource() )
39
- ->build( $asset );
40
 
41
  if ( !empty( $this->hashes ) && $this->isSubmitRequired() ) {
42
  $this->submit();
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots\CrowdSourced;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots\Build;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Options;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
  use FernleafSystems\Wordpress\Services\Core\VOs\Assets\{
9
  WpPluginVo,
33
  * @param WpPluginVo|WpThemeVo $asset
34
  */
35
  public function run( $asset ) {
36
+ /** @var Options $opts */
37
+ $opts = $this->getOptions();
38
+
39
  $this->asset = $asset;
40
 
41
  if ( $this->canSubmitAsset() ) {
42
  $this->hashes = ( new Build\BuildHashesForCrowdSource() )
43
+ ->build( $asset, $opts->getDef( 'file_scan_extensions' ) );
44
 
45
  if ( !empty( $this->hashes ) && $this->isSubmitRequired() ) {
46
  $this->submit();
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/CleanAll.php CHANGED
@@ -12,14 +12,14 @@ class CleanAll extends BaseBulk {
12
  /** @var ModCon $mod */
13
  $mod = $this->getMod();
14
  try {
15
- $nBoundary = Services::Request()
16
  ->carbon()
17
  ->subDay()->timestamp;
18
- $oDirIt = StandardDirectoryIterator::create( $mod->getPtgSnapsBaseDir() );
19
- foreach ( $oDirIt as $oFile ) {
20
- /** @var \SplFileInfo $oFile */
21
- if ( $nBoundary > $oFile->getMTime() ) {
22
- Services::WpFs()->deleteFile( $oFile->getPathname() );
23
  }
24
  }
25
  }
12
  /** @var ModCon $mod */
13
  $mod = $this->getMod();
14
  try {
15
+ $boundary = Services::Request()
16
  ->carbon()
17
  ->subDay()->timestamp;
18
+ $IT = StandardDirectoryIterator::create( $mod->getPtgSnapsBaseDir() );
19
+ foreach ( $IT as $file ) {
20
+ /** @var \SplFileInfo $file */
21
+ if ( $boundary > $file->getMTime() ) {
22
+ Services::WpFs()->deleteFile( $file->getPathname() );
23
  }
24
  }
25
  }
src/lib/src/Modules/HackGuard/Lib/Utility/VerifyFileByHash.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Utility;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+ use FernleafSystems\Wordpress\Services\Core\CoreFileHashes;
7
+ use FernleafSystems\Wordpress\Services\Core\VOs\Assets\WpPluginVo;
8
+ use FernleafSystems\Wordpress\Services\Core\VOs\Assets\WpThemeVo;
9
+ use FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes\CrowdSourcedHashes\Query;
10
+ use FernleafSystems\Wordpress\Services\Utilities\WpOrg\{
11
+ Plugin,
12
+ Theme
13
+ };
14
+
15
+ class VerifyFileByHash {
16
+
17
+ use ModConsumer;
18
+
19
+ private $fullPath;
20
+
21
+ private $coreFileHashes;
22
+
23
+ private $tmpItem;
24
+
25
+ private $tmpItemHashes;
26
+
27
+ /**
28
+ * TODO
29
+ */
30
+ public function verify( string $fullPath ) :bool {
31
+ $this->fullPath = wp_normalize_path( $fullPath );
32
+
33
+ return $this->isValidCoreFile();
34
+
35
+ /*
36
+ $verified = false;
37
+
38
+ if ( $this->isValidCoreFile() ) {
39
+ $verified = true;
40
+ }
41
+ elseif ( $this->isInCoreDir() ) {
42
+ $verified = false;
43
+ }
44
+ elseif ( false && $this->isInPluginsDir() ) {
45
+ }
46
+ elseif ( false && $this->isInThemesDir() ) {
47
+ }
48
+
49
+ return $verified;
50
+ */
51
+ }
52
+
53
+ /**
54
+ * @param WpPluginVo|WpThemeVo $asset
55
+ */
56
+ private function getAssetHashes( $asset ) :array {
57
+ if ( empty( $this->tmpItem ) || $this->tmpItem !== $asset->unique_id ) {
58
+ $this->tmpItem = null;
59
+ $this->tmpItemHashes = null;
60
+ }
61
+
62
+ if ( !is_array( $this->tmpItemHashes ) ) {
63
+ $hashes = ( $asset->asset_type === 'plugin' ? new Query\Plugin() : new Query\Theme() )
64
+ ->getHashesFromVO( $asset );
65
+ $this->tmpItem = $asset->unique_id;
66
+ $this->tmpItemHashes = is_array( $hashes ) ? $hashes : [];
67
+ }
68
+
69
+ return $this->tmpItemHashes;
70
+ }
71
+
72
+ private function isValidCoreFile() :bool {
73
+ return $this->getCoreFileHashes()->isCoreFileHashValid( $this->fullPath );
74
+ }
75
+
76
+ private function isInCoreDir() :bool {
77
+ return strpos( $this->fullPath, wp_normalize_path( path_join( ABSPATH, 'wp-admin' ) ) ) === 0
78
+ || strpos( $this->fullPath, wp_normalize_path( path_join( ABSPATH, 'wp-includes' ) ) ) === 0;
79
+ }
80
+
81
+ private function isInPluginsDir() :bool {
82
+ return strpos( $this->fullPath, wp_normalize_path( WP_PLUGIN_DIR ) ) === 0;
83
+ }
84
+
85
+ private function isInThemesDir() :bool {
86
+ return strpos( $this->fullPath, wp_normalize_path( path_join( WP_CONTENT_DIR, 'themes' ) ) ) === 0;
87
+ }
88
+
89
+ private function getCoreFileHashes() :CoreFileHashes {
90
+ if ( empty( $this->coreFileHashes ) ) {
91
+ $this->coreFileHashes = new CoreFileHashes();
92
+ }
93
+ return $this->coreFileHashes;
94
+ }
95
+ }
src/lib/src/Modules/HackGuard/ModCon.php CHANGED
@@ -5,6 +5,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
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 {
@@ -116,6 +117,31 @@ class ModCon extends BaseShield\ModCon {
116
  $con->purge();
117
  }
118
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  }
120
 
121
  /**
@@ -169,26 +195,24 @@ class ModCon extends BaseShield\ModCon {
169
  return $this->isModuleEnabled() && $this->isPremium()
170
  && $opts->isOpt( 'ptg_enable', 'enabled' )
171
  && $opts->isOptReqsMet( 'ptg_enable' )
172
- && $this->canCacheDirWrite();
 
173
  }
174
 
175
- /**
176
- * @return string|false
177
- */
178
- public function getPtgSnapsBaseDir() {
179
- return $this->getCon()->getPluginCachePath( 'ptguard/' );
180
  }
181
 
182
  public function hasWizard() :bool {
183
  return false;
184
  }
185
 
186
- /**
187
- * @return string
188
- */
189
- public function getTempDir() {
190
- $dir = $this->getCon()->getPluginCachePath( 'scans' );
191
- return Services::WpFs()->mkdir( $dir ) ? $dir : false;
192
  }
193
 
194
  public function getDbHandler_FileLocker() :Databases\FileLocker\Handler {
@@ -211,7 +235,7 @@ class ModCon extends BaseShield\ModCon {
211
  return ( $this->getDbHandler_ScanQueue() instanceof Databases\ScanQueue\Handler )
212
  && $this->getDbHandler_ScanQueue()->isReady()
213
  && ( $this->getDbHandler_ScanResults() instanceof Databases\Scanner\Handler )
214
- && $this->getDbHandler_ScanQueue()->isReady()
215
  && parent::isReadyToExecute();
216
  }
217
 
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\Plugin\Shield\Utilities\CacheDir;
9
  use FernleafSystems\Wordpress\Services\Services;
10
 
11
  class ModCon extends BaseShield\ModCon {
117
  $con->purge();
118
  }
119
  }
120
+
121
+ $this->cleanScanExclusions();
122
+ }
123
+
124
+ private function cleanScanExclusions() {
125
+ /** @var Options $opts */
126
+ $opts = $this->getOptions();
127
+
128
+ $specialDirs = array_map( 'trailingslashit', [
129
+ ABSPATH,
130
+ path_join( ABSPATH, 'wp-admin' ),
131
+ path_join( ABSPATH, 'wp-includes' ),
132
+ untrailingslashit( WP_CONTENT_DIR ),
133
+ path_join( WP_CONTENT_DIR, 'plugins' ),
134
+ path_join( WP_CONTENT_DIR, 'themes' ),
135
+ ] );
136
+
137
+ $values = $opts->getOpt( 'scan_path_exclusions', [] );
138
+ $opts->setOpt( 'scan_path_exclusions',
139
+ ( new Shield\Modules\Base\Options\WildCardOptions() )->clean(
140
+ is_array( $values ) ? $values : [],
141
+ $specialDirs,
142
+ Shield\Modules\Base\Options\WildCardOptions::FILE_PATH_REL
143
+ )
144
+ );
145
  }
146
 
147
  /**
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
  }
201
 
202
+ public function getPtgSnapsBaseDir() :string {
203
+ return ( new CacheDir() )
204
+ ->setCon( $this->getCon() )
205
+ ->buildSubDir( 'ptguard' );
 
206
  }
207
 
208
  public function hasWizard() :bool {
209
  return false;
210
  }
211
 
212
+ public function getScansTempDir() :string {
213
+ return ( new CacheDir() )
214
+ ->setCon( $this->getCon() )
215
+ ->buildSubDir( 'scans' );
 
 
216
  }
217
 
218
  public function getDbHandler_FileLocker() :Databases\FileLocker\Handler {
235
  return ( $this->getDbHandler_ScanQueue() instanceof Databases\ScanQueue\Handler )
236
  && $this->getDbHandler_ScanQueue()->isReady()
237
  && ( $this->getDbHandler_ScanResults() instanceof Databases\Scanner\Handler )
238
+ && $this->getDbHandler_ScanResults()->isReady()
239
  && parent::isReadyToExecute();
240
  }
241
 
src/lib/src/Modules/HackGuard/Options.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
 
6
  use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class Options extends BaseShield\Options {
@@ -16,6 +17,28 @@ class Options extends BaseShield\Options {
16
  return is_array( $this->getOpt( 'file_repair_areas' ) ) ? $this->getOpt( 'file_repair_areas' ) : [];
17
  }
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  /**
20
  * @return int[] - keys are the unique report hash
21
  */
@@ -32,23 +55,6 @@ class Options extends BaseShield\Options {
32
  return (int)apply_filters( 'shield/fp_confidence_boundary', 65 );
33
  }
34
 
35
- /**
36
- * We do some WP Content dir replacement as there may be custom wp-content dir defines
37
- * @return string[]
38
- */
39
- public function getMalWhitelistPaths() {
40
- return array_map(
41
- function ( $sFragment ) {
42
- return str_replace(
43
- wp_normalize_path( ABSPATH.'wp-content' ),
44
- rtrim( wp_normalize_path( WP_CONTENT_DIR ), '/' ),
45
- wp_normalize_path( path_join( ABSPATH, ltrim( $sFragment, '/' ) ) )
46
- );
47
- },
48
- apply_filters( 'icwp_shield_malware_whitelist_paths', $this->getDef( 'malware_whitelist_paths' ) )
49
- );
50
- }
51
-
52
  /**
53
  * @return int
54
  */
@@ -59,14 +65,14 @@ class Options extends BaseShield\Options {
59
  /**
60
  * @return string[]
61
  */
62
- public function getMalSignaturesSimple() {
63
  return $this->getMalSignatures( 'malsigs_simple.txt', $this->getDef( 'url_mal_sigs_simple' ) );
64
  }
65
 
66
  /**
67
  * @return string[]
68
  */
69
- public function getMalSignaturesRegex() {
70
  return $this->getMalSignatures( 'malsigs_regex.txt', $this->getDef( 'url_mal_sigs_regex' ) );
71
  }
72
 
@@ -75,10 +81,10 @@ class Options extends BaseShield\Options {
75
  * @param string $url
76
  * @return string[]
77
  */
78
- public function getMalSignatures( string $fileName, string $url ) {
79
  $FS = Services::WpFs();
80
- $file = $this->getCon()->getPluginCachePath( $fileName );
81
- if ( $FS->exists( $file ) ) {
82
  $sigs = explode( "\n", $FS->getFileContent( $file, true ) );
83
  }
84
  else {
@@ -87,15 +93,16 @@ class Options extends BaseShield\Options {
87
  explode( "\n", Services::HttpRequest()->getContent( $url ) )
88
  ),
89
  function ( $line ) {
90
- return ( ( strpos( $line, '#' ) !== 0 ) && strlen( $line ) > 0 );
91
  }
92
  );
93
 
94
- if ( !empty( $sigs ) ) {
95
  $FS->putFileContent( $file, implode( "\n", $sigs ), true );
96
  }
97
  }
98
- return $sigs;
 
99
  }
100
 
101
  public function isMalAutoRepairSurgical() :bool {
@@ -106,6 +113,10 @@ class Options extends BaseShield\Options {
106
  return $this->getMalConfidenceBoundary() > 0;
107
  }
108
 
 
 
 
 
109
  public function isPtgReinstallLinks() :bool {
110
  return $this->isOpt( 'ptg_reinstall_links', 'Y' ) && $this->isPremium();
111
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Options\WildCardOptions;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class Options extends BaseShield\Options {
17
  return is_array( $this->getOpt( 'file_repair_areas' ) ) ? $this->getOpt( 'file_repair_areas' ) : [];
18
  }
19
 
20
+ /**
21
+ * @return string[] - precise REGEX patterns to match against PATH.
22
+ */
23
+ public function getWhitelistedPathsAsRegex() :array {
24
+ if ( $this->isPremium() ) {
25
+ $paths = array_merge(
26
+ $this->getOpt( 'scan_path_exclusions', [] ),
27
+ $this->getDef( 'default_whitelist_paths' )
28
+ );
29
+ }
30
+ else {
31
+ $paths = [];
32
+ }
33
+
34
+ return array_map(
35
+ function ( $value ) {
36
+ return ( new WildCardOptions() )->buildFullRegexValue( $value, WildCardOptions::FILE_PATH_REL );
37
+ },
38
+ is_array( $paths ) ? $paths : []
39
+ );
40
+ }
41
+
42
  /**
43
  * @return int[] - keys are the unique report hash
44
  */
55
  return (int)apply_filters( 'shield/fp_confidence_boundary', 65 );
56
  }
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  /**
59
  * @return int
60
  */
65
  /**
66
  * @return string[]
67
  */
68
+ public function getMalSignaturesSimple() :array {
69
  return $this->getMalSignatures( 'malsigs_simple.txt', $this->getDef( 'url_mal_sigs_simple' ) );
70
  }
71
 
72
  /**
73
  * @return string[]
74
  */
75
+ public function getMalSignaturesRegex() :array {
76
  return $this->getMalSignatures( 'malsigs_regex.txt', $this->getDef( 'url_mal_sigs_regex' ) );
77
  }
78
 
81
  * @param string $url
82
  * @return string[]
83
  */
84
+ private function getMalSignatures( string $fileName, string $url ) :array {
85
  $FS = Services::WpFs();
86
+ $file = $this->getCon()->paths->forCacheItem( $fileName );
87
+ if ( !empty( $file ) && $FS->exists( $file ) ) {
88
  $sigs = explode( "\n", $FS->getFileContent( $file, true ) );
89
  }
90
  else {
93
  explode( "\n", Services::HttpRequest()->getContent( $url ) )
94
  ),
95
  function ( $line ) {
96
+ return ( strpos( $line, '#' ) !== 0 ) && strlen( $line ) > 0;
97
  }
98
  );
99
 
100
+ if ( !empty( $file ) && !empty( $sigs ) ) {
101
  $FS->putFileContent( $file, implode( "\n", $sigs ), true );
102
  }
103
  }
104
+
105
+ return is_array( $sigs ) ? $sigs : [];
106
  }
107
 
108
  public function isMalAutoRepairSurgical() :bool {
113
  return $this->getMalConfidenceBoundary() > 0;
114
  }
115
 
116
+ public function isAutoFilterResults() :bool {
117
+ return $this->isOpt( 'auto_filter_results', 'Y' );
118
+ }
119
+
120
  public function isPtgReinstallLinks() :bool {
121
  return $this->isOpt( 'ptg_reinstall_links', 'Y' ) && $this->isPremium();
122
  }
src/lib/src/Modules/HackGuard/Processor.php CHANGED
@@ -20,26 +20,14 @@ class Processor extends BaseShield\Processor {
20
  }
21
 
22
  public function runHourlyCron() {
23
- ( new Lib\Snapshots\StoreAction\TouchAll() )
24
- ->setMod( $this->getMod() )
25
- ->run();
26
  }
27
 
28
  public function runDailyCron() {
29
- ( new Lib\Snapshots\StoreAction\CleanAll() )
30
- ->setMod( $this->getMod() )
31
- ->run();
32
  }
33
 
34
  public function onWpLoaded() {
35
- ( new Lib\Snapshots\StoreAction\ScheduleBuildAll() )
36
- ->setMod( $this->getMod() )
37
- ->hookBuild();
38
  }
39
 
40
  public function onModuleShutdown() {
41
- ( new Lib\Snapshots\StoreAction\ScheduleBuildAll() )
42
- ->setMod( $this->getMod() )
43
- ->schedule();
44
  }
45
  }
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/Render/ScanResults/SectionBase.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Render\ScanResults;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+
8
+ class SectionBase {
9
+
10
+ use ModConsumer;
11
+
12
+ protected $renderData = [];
13
+
14
+ protected function buildRenderData() :array {
15
+ return [];
16
+ }
17
+
18
+ public function getRenderData() :array {
19
+ if ( empty( $this->renderData ) ) {
20
+ $this->renderData = $this->buildRenderData();
21
+ }
22
+ return $this->renderData;
23
+ }
24
+
25
+ protected function getCommonRenderData() :array {
26
+ return Services::DataManipulation()
27
+ ->mergeArraysRecursive(
28
+ $this->getMod()->getUIHandler()->getBaseDisplayData(),
29
+ [
30
+ 'ajax' => [
31
+ 'scanresults_action' => $this->getMod()
32
+ ->getAjaxActionData( 'scanresults_action', true ),
33
+ ],
34
+ 'strings' => [
35
+ 'author' => __( 'Author' ),
36
+ 'version' => __( 'Version' ),
37
+ 'name' => __( 'Name' ),
38
+ 'install_dir' => __( 'Installation Directory', 'wp-simple-firewall' ),
39
+ 'file_integrity' => __( 'File Integrity', 'wp-simple-firewall' ),
40
+ 'status_good' => __( 'Good', 'wp-simple-firewall' ),
41
+ 'status_warning' => __( 'Warning', 'wp-simple-firewall' ),
42
+ 'abandoned' => __( 'Abandoned', 'wp-simple-firewall' ),
43
+ 'vulnerable' => __( 'Vulnerable', 'wp-simple-firewall' ),
44
+ 'vulnerable_known' => __( 'Known vulnerabilities discovered.', 'wp-simple-firewall' ),
45
+ 'vulnerable_update' => __( "You should upgrade to the latest version or remove it if no updates are available.", 'wp-simple-firewall' ),
46
+ 'update_available' => __( 'Update Available', 'wp-simple-firewall' ),
47
+ 'installed_at' => __( 'Installed', 'wp-simple-firewall' ),
48
+ 'estimated' => __( 'estimated', 'wp-simple-firewall' ),
49
+ 'rel_to_abspath' => __( 'relative to WordPress install dir', 'wp-simple-firewall' ),
50
+ 'child_theme' => __( 'Linked To Child Theme', 'wp-simple-firewall' ),
51
+ 'parent_theme' => __( 'Linked To Parent Theme', 'wp-simple-firewall' ),
52
+ ],
53
+ 'hrefs' => [
54
+ 'upgrade' => Services::WpGeneral()->getAdminUrl_Updates()
55
+ ],
56
+ ]
57
+ );
58
+ }
59
+ }
src/lib/src/Modules/HackGuard/Render/ScanResults/SectionLogs.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Render\ScanResults;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\{
6
+ Lib\ScanTables\LoadRawTableData,
7
+ ModCon,
8
+ Scan\Controller\Mal
9
+ };
10
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
11
+ use FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Scans\ForMalware;
12
+ use FernleafSystems\Wordpress\Services\Services;
13
+
14
+ class SectionLogs extends SectionBase {
15
+
16
+ public function render() :string {
17
+ return $this->getMod()
18
+ ->renderTemplate(
19
+ '/wpadmin_pages/insights/scans/results/section/logs/index.twig',
20
+ $this->buildRenderData()
21
+ );
22
+ }
23
+
24
+ protected function buildRenderData() :array {
25
+ /** @var ModCon $mod */
26
+ $mod = $this->getMod();
27
+ $data = $this->buildMalwareData();
28
+ return Services::DataManipulation()
29
+ ->mergeArraysRecursive( $this->getCommonRenderData(), [
30
+ 'strings' => [
31
+ 'no_files' => __( "Previous scans didn't detect any files suspected of being malware.", 'wp-simple-firewall' ),
32
+ 'files_found' => __( "Previous scans detected 1 or more files suspected of being malware.", 'wp-simple-firewall' ),
33
+ 'mal_restricted' => __( 'The Malware File Scanner is only available with ShieldPRO.', 'wp-simple-firewall' ),
34
+ ],
35
+ 'flags' => [
36
+ 'mal_is_restricted' => $mod->getScanCon( Mal::SCAN_SLUG )->isRestricted(),
37
+ ],
38
+ 'vars' => [
39
+ 'count_items' => $data[ 'vars' ][ 'count_items' ],
40
+ 'malware' => $data,
41
+ 'datatables_init' => ( new ForMalware() )
42
+ ->setMod( $this->getMod() )
43
+ ->build()
44
+ ]
45
+ ] );
46
+ }
47
+
48
+ private function buildMalwareData() :array {
49
+
50
+ $filesData = ( new LoadRawTableData() )
51
+ ->setMod( $this->getMod() )
52
+ ->loadForMalware();
53
+
54
+ $data = [
55
+ 'flags' => [
56
+ 'has_malware' => !empty( $filesData ),
57
+ ],
58
+ 'vars' => [
59
+ 'count_items' => count( $filesData )
60
+ ]
61
+ ];
62
+ $data[ 'flags' ][ 'has_issue' ] = $data[ 'flags' ][ 'has_malware' ];
63
+ return $data;
64
+ }
65
+ }
src/lib/src/Modules/HackGuard/Render/ScanResults/SectionMalware.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Render\ScanResults;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\{
6
+ Lib\ScanTables\LoadRawTableData,
7
+ ModCon,
8
+ Scan\Controller\Mal
9
+ };
10
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
11
+ use FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Scans\ForMalware;
12
+ use FernleafSystems\Wordpress\Services\Services;
13
+
14
+ class SectionMalware extends SectionBase {
15
+
16
+ public function render() :string {
17
+ return $this->getMod()
18
+ ->renderTemplate(
19
+ '/wpadmin_pages/insights/scans/results/section/malware/index.twig',
20
+ $this->buildRenderData()
21
+ );
22
+ }
23
+
24
+ protected function buildRenderData() :array {
25
+ /** @var ModCon $mod */
26
+ $mod = $this->getMod();
27
+ $data = $this->buildMalwareData();
28
+ return Services::DataManipulation()
29
+ ->mergeArraysRecursive( $this->getCommonRenderData(), [
30
+ 'strings' => [
31
+ 'no_files' => __( "Previous scans didn't detect any files suspected of being malware.", 'wp-simple-firewall' ),
32
+ 'files_found' => __( "Previous scans detected 1 or more files suspected of being malware.", 'wp-simple-firewall' ),
33
+ 'mal_restricted' => __( 'The Malware File Scanner is only available with ShieldPRO.', 'wp-simple-firewall' ),
34
+ ],
35
+ 'flags' => [
36
+ 'mal_is_restricted' => $mod->getScanCon( Mal::SCAN_SLUG )->isRestricted(),
37
+ ],
38
+ 'vars' => [
39
+ 'count_items' => $data[ 'vars' ][ 'count_items' ],
40
+ 'malware' => $data,
41
+ 'datatables_init' => ( new ForMalware() )
42
+ ->setMod( $this->getMod() )
43
+ ->build(),
44
+ 'beacon_help_id' => 443
45
+ ]
46
+ ] );
47
+ }
48
+
49
+ private function buildMalwareData() :array {
50
+
51
+ $filesData = ( new LoadRawTableData() )
52
+ ->setMod( $this->getMod() )
53
+ ->loadForMalware();
54
+
55
+ $data = [
56
+ 'flags' => [
57
+ 'has_malware' => !empty( $filesData ),
58
+ ],
59
+ 'vars' => [
60
+ 'count_items' => count( $filesData )
61
+ ]
62
+ ];
63
+ $data[ 'flags' ][ 'has_issue' ] = $data[ 'flags' ][ 'has_malware' ];
64
+ return $data;
65
+ }
66
+ }
src/lib/src/Modules/HackGuard/Render/ScanResults/SectionPluginThemesBase.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Render\ScanResults;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller\Apc;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller\Ptg;
8
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller\Wpv;
9
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
10
+ use FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Scans\ForPluginTheme;
11
+ use FernleafSystems\Wordpress\Services\Services;
12
+
13
+ class SectionPluginThemesBase extends SectionBase {
14
+
15
+ protected function getCommonRenderData() :array {
16
+ /** @var ModCon $mod */
17
+ $mod = $this->getMod();
18
+
19
+ return Services::DataManipulation()
20
+ ->mergeArraysRecursive( parent::getCommonRenderData(), [
21
+ 'strings' => [
22
+ 'ptg_name' => __( 'Plugin/Theme Guard', 'wp-simple-firewall' ),
23
+ 'ptg_not_available' => __( 'The Plugin & Theme File Guard Scanner is only available with ShieldPRO.', 'wp-simple-firewall' ),
24
+ ],
25
+ 'flags' => [
26
+ 'ptg_is_restricted' => $mod->getScanCon( Ptg::SCAN_SLUG )->isRestricted(),
27
+ ],
28
+ 'vars' => [
29
+ 'datatables_init' => ( new ForPluginTheme() )
30
+ ->setMod( $this->getMod() )
31
+ ->build()
32
+ ]
33
+ ] );
34
+ }
35
+
36
+ protected function getVulnerabilities() :Scans\Wpv\ResultsSet {
37
+ if ( !isset( $this->vulnerable ) ) {
38
+ /** @var ModCon $mod */
39
+ $mod = $this->getMod();
40
+ try {
41
+ $this->vulnerable = $mod->getScanCon( Wpv::SCAN_SLUG )->getAllResults();
42
+ }
43
+ catch ( \Exception $e ) {
44
+ $this->vulnerable = new Scans\Wpv\ResultsSet();
45
+ }
46
+ }
47
+ return $this->vulnerable;
48
+ }
49
+
50
+ protected function getAbandoned() :Scans\Apc\ResultsSet {
51
+ if ( !isset( $this->abandoned ) ) {
52
+ /** @var ModCon $mod */
53
+ $mod = $this->getMod();
54
+ try {
55
+ $this->abandoned = $mod->getScanCon( Apc::SCAN_SLUG )->getAllResults();
56
+ }
57
+ catch ( \Exception $e ) {
58
+ $this->abandoned = new Scans\Apc\ResultsSet();
59
+ }
60
+ }
61
+ return $this->abandoned;
62
+ }
63
+ }
src/lib/src/Modules/HackGuard/Render/ScanResults/SectionPlugins.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Render\ScanResults;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\ScanTables\LoadRawTableData;
6
+ use FernleafSystems\Wordpress\Services\Core\VOs\Assets\WpPluginVo;
7
+ use FernleafSystems\Wordpress\Services\Services;
8
+ use FernleafSystems\Wordpress\Services\Utilities\Assets\DetectInstallationDate;
9
+
10
+ class SectionPlugins extends SectionPluginThemesBase {
11
+
12
+ public function render() :string {
13
+ return $this->getMod()
14
+ ->renderTemplate(
15
+ '/wpadmin_pages/insights/scans/results/section/plugins/index.twig',
16
+ $this->buildRenderData()
17
+ );
18
+ }
19
+
20
+ protected function buildRenderData() :array {
21
+ $items = $this->buildPluginsData();
22
+ ksort( $items );
23
+
24
+ $hashes = [];
25
+ $abandoned = [];
26
+ $vulnerable = [];
27
+ $problems = [];
28
+ $inactive = [];
29
+ $warning = [];
30
+ foreach ( $items as $key => $item ) {
31
+ if ( $item[ 'flags' ][ 'has_guard_files' ] ) {
32
+ unset( $items[ $key ] );
33
+ $hashes[] = $item;
34
+ }
35
+ elseif ( $item[ 'flags' ][ 'is_vulnerable' ] ) {
36
+ unset( $items[ $key ] );
37
+ $vulnerable[] = $item;
38
+ }
39
+ elseif ( $item[ 'flags' ][ 'is_abandoned' ] ) {
40
+ unset( $items[ $key ] );
41
+ $abandoned[] = $item;
42
+ }
43
+ elseif ( $item[ 'flags' ][ 'has_issue' ] ) {
44
+ unset( $items[ $key ] );
45
+ $problems[] = $item;
46
+ }
47
+ elseif ( $item[ 'flags' ][ 'has_warning' ] ) {
48
+ unset( $items[ $key ] );
49
+ $warning[] = $item;
50
+ }
51
+ }
52
+
53
+ $items = array_merge( $vulnerable, $hashes, $abandoned, $problems, $warning, $inactive, $items );
54
+
55
+ return Services::DataManipulation()
56
+ ->mergeArraysRecursive( $this->getCommonRenderData(), [
57
+ 'strings' => [
58
+ 'no_items' => __( "Previous scans didn't detect any modified or missing files in any plugin directories.", 'wp-simple-firewall' ),
59
+ 'no_files' => __( "Previous scans didn't detect any modified or missing files in the plugin directory.", 'wp-simple-firewall' ),
60
+ 'files_found' => __( "Previous scans detected 1 or more modified or missing files in the plugin directory.", 'wp-simple-firewall' ),
61
+ 'not_active' => __( "This plugin isn't active and should be uninstalled.", 'wp-simple-firewall' ),
62
+ 'go_to_plugins' => sprintf( __( 'Go To %s', 'wp-simple-firewall' ), __( 'Plugins' ) ),
63
+ ],
64
+ 'hrefs' => [
65
+ 'page_plugins' => Services::WpGeneral()->getAdminUrl_Plugins()
66
+ ],
67
+ 'vars' => [
68
+ 'count_items' => count( $vulnerable ) + count( $hashes )
69
+ + count( $abandoned ) + count( $problems ),
70
+ 'plugins' => array_values( $items ),
71
+ ]
72
+ ] );
73
+ }
74
+
75
+ private function buildPluginsData() :array {
76
+ return array_map(
77
+ function ( $plugin ) {
78
+ return $this->buildPluginData( $plugin );
79
+ },
80
+ Services::WpPlugins()->getPluginsAsVo()
81
+ );
82
+ }
83
+
84
+ private function buildPluginData( WpPluginVo $plugin ) :array {
85
+ $carbon = Services::Request()->carbon();
86
+
87
+ $abandoned = $this->getAbandoned()->getItemForSlug( $plugin->file );
88
+ $guardFilesData = ( new LoadRawTableData() )
89
+ ->setMod( $this->getMod() )
90
+ ->loadForPlugin( $plugin );
91
+
92
+ $vulnerabilities = $this->getVulnerabilities()->getItemsForSlug( $plugin->file );
93
+
94
+ $data = [
95
+ 'info' => [
96
+ 'type' => 'plugin',
97
+ 'name' => $plugin->Title,
98
+ 'slug' => $plugin->slug,
99
+ 'description' => $plugin->Description,
100
+ 'version' => $plugin->Version,
101
+ 'author' => $plugin->AuthorName,
102
+ 'author_url' => $plugin->AuthorURI,
103
+ 'file' => $plugin->file,
104
+ 'installed_at' => $carbon->setTimestamp( ( new DetectInstallationDate() )->plugin( $plugin ) )
105
+ ->diffForHumans(),
106
+ 'dir' => '/'.str_replace( wp_normalize_path( ABSPATH ), '', wp_normalize_path( $plugin->getInstallDir() ) ),
107
+ 'abandoned_at' => empty( $abandoned ) ? 0
108
+ : $carbon->setTimestamp( $abandoned->last_updated_at )->diffForHumans(),
109
+ ],
110
+ 'hrefs' => [
111
+ 'vul_info' => add_query_arg(
112
+ [
113
+ 'type' => $plugin->asset_type,
114
+ 'slug' => $plugin->slug,
115
+ 'version' => $plugin->Version,
116
+ ],
117
+ 'https://shsec.io/shieldvulnerabilitylookup'
118
+ ),
119
+ ],
120
+ 'flags' => [
121
+ 'has_update' => $plugin->hasUpdate(),
122
+ 'has_guard_files' => !empty( $guardFilesData ),
123
+ 'is_abandoned' => !empty( $abandoned ),
124
+ 'is_active' => $plugin->active,
125
+ 'is_vulnerable' => !empty( $vulnerabilities ),
126
+ 'is_wporg' => $plugin->isWpOrg(),
127
+ ],
128
+ 'vars' => [
129
+ 'abandoned_rid' => empty( $abandoned ) ? -1 : $abandoned->VO->id,
130
+ 'count_items' => count( $guardFilesData ) + count( $vulnerabilities )
131
+ + ( empty( $abandoned ) ? 0 : 1 )
132
+ ],
133
+ ];
134
+ $data[ 'flags' ][ 'has_issue' ] = $data[ 'flags' ][ 'is_abandoned' ]
135
+ || $data[ 'flags' ][ 'has_guard_files' ]
136
+ || $data[ 'flags' ][ 'is_vulnerable' ];
137
+ $data[ 'flags' ][ 'has_warning' ] = !$data[ 'flags' ][ 'has_issue' ]
138
+ && (
139
+ !$data[ 'flags' ][ 'is_active' ]
140
+ || $data[ 'flags' ][ 'has_update' ]
141
+ );
142
+ return $data;
143
+ }
144
+ }
src/lib/src/Modules/HackGuard/Render/ScanResults/SectionThemes.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Render\ScanResults;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\ScanTables\LoadRawTableData;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
7
+ use FernleafSystems\Wordpress\Services\Core\VOs\Assets\WpThemeVo;
8
+ use FernleafSystems\Wordpress\Services\Services;
9
+ use FernleafSystems\Wordpress\Services\Utilities\Assets\DetectInstallationDate;
10
+
11
+ class SectionThemes extends SectionPluginThemesBase {
12
+
13
+ public function render() :string {
14
+ $renderData = $this->buildRenderData();
15
+ return $this->getMod()
16
+ ->renderTemplate(
17
+ '/wpadmin_pages/insights/scans/results/section/themes/index.twig',
18
+ $renderData
19
+ );
20
+ }
21
+
22
+ protected function buildRenderData() :array {
23
+ $items = $this->buildThemesData();
24
+ ksort( $items );
25
+
26
+ $hashes = [];
27
+ $abandoned = [];
28
+ $vulnerable = [];
29
+ $problems = [];
30
+ $inactive = [];
31
+ $warning = [];
32
+ foreach ( $items as $key => $item ) {
33
+ if ( $item[ 'flags' ][ 'has_guard_files' ] ) {
34
+ unset( $items[ $key ] );
35
+ $hashes[] = $item;
36
+ }
37
+ elseif ( $item[ 'flags' ][ 'is_vulnerable' ] ) {
38
+ unset( $items[ $key ] );
39
+ $vulnerable[] = $item;
40
+ }
41
+ elseif ( $item[ 'flags' ][ 'is_abandoned' ] ) {
42
+ unset( $items[ $key ] );
43
+ $abandoned[] = $item;
44
+ }
45
+ elseif ( $item[ 'flags' ][ 'has_issue' ] ) {
46
+ unset( $items[ $key ] );
47
+ $problems[] = $item;
48
+ }
49
+ elseif ( $item[ 'flags' ][ 'has_warning' ] ) {
50
+ unset( $items[ $key ] );
51
+ $warning[] = $item;
52
+ }
53
+ }
54
+
55
+ $items = array_merge( $vulnerable, $hashes, $abandoned, $problems, $warning, $inactive, $items );
56
+
57
+ return Services::DataManipulation()
58
+ ->mergeArraysRecursive( $this->getCommonRenderData(), [
59
+ 'strings' => [
60
+ 'no_items' => __( "Previous scans didn't detect any modified or missing files in any Theme directories.", 'wp-simple-firewall' ),
61
+ 'no_files' => __( "Previous scans didn't detect any modified or missing files in the Theme directories.", 'wp-simple-firewall' ),
62
+ 'files_found' => __( "Previous scans detected 1 or more modified or missing files in the theme directory.", 'wp-simple-firewall' ),
63
+ 'not_active' => __( "This theme isn't active and should be uninstalled.", 'wp-simple-firewall' ),
64
+ 'go_to_themes' => sprintf( __( 'Go To %s', 'wp-simple-firewall' ), __( 'Themes' ) ),
65
+ ],
66
+ 'hrefs' => [
67
+ 'page_themes' => Services::WpGeneral()->getAdminUrl_Themes()
68
+ ],
69
+ 'vars' => [
70
+ 'count_items' => count( $vulnerable ) + count( $hashes )
71
+ + count( $abandoned ) + count( $problems ),
72
+ 'themes' => array_values( $items ),
73
+ ]
74
+ ] );
75
+ }
76
+
77
+ private function buildThemesData() :array {
78
+ return array_map(
79
+ function ( $item ) {
80
+ return $this->buildThemeData( $item );
81
+ },
82
+ Services::WpThemes()->getThemesAsVo()
83
+ );
84
+ }
85
+
86
+ private function buildThemeData( WpThemeVo $theme ) :array {
87
+ $carbon = Services::Request()->carbon();
88
+
89
+ $abandoned = $this->getAbandoned()->getItemForSlug( $theme->stylesheet );
90
+ $guardFilesData = ( new LoadRawTableData() )
91
+ ->setMod( $this->getMod() )
92
+ ->loadForTheme( $theme );
93
+
94
+ $vulnerabilities = $this->getVulnerabilities()->getItemsForSlug( $theme->file );
95
+
96
+ $data = [
97
+ 'info' => [
98
+ 'type' => 'theme',
99
+ 'name' => $theme->Name,
100
+ 'slug' => $theme->slug,
101
+ 'description' => $theme->Description,
102
+ 'version' => $theme->Version,
103
+ 'author' => $theme->Author,
104
+ 'author_url' => $theme->AuthorURI,
105
+ 'file' => $theme->stylesheet,
106
+ 'dir' => '/'.str_replace( wp_normalize_path( ABSPATH ), '', wp_normalize_path( $theme->getInstallDir() ) ),
107
+ 'abandoned_at' => empty( $abandoned ) ? 0
108
+ : $carbon->setTimestamp( $abandoned->last_updated_at )->diffForHumans(),
109
+ 'installed_at' => $carbon->setTimestamp( ( new DetectInstallationDate() )->theme( $theme ) )
110
+ ->diffForHumans(),
111
+ 'child_theme' => $theme->is_parent ? $theme->child_theme->Name : '',
112
+ 'parent_theme' => $theme->is_child ? $theme->parent_theme->Name : '',
113
+ ],
114
+ 'hrefs' => [
115
+ 'vul_info' => add_query_arg(
116
+ [
117
+ 'type' => $theme->asset_type,
118
+ 'slug' => $theme->stylesheet,
119
+ 'version' => $theme->Version,
120
+ ],
121
+ 'https://shsec.io/shieldvulnerabilitylookup'
122
+ ),
123
+ ],
124
+ 'flags' => [
125
+ 'has_update' => $theme->hasUpdate(),
126
+ 'is_abandoned' => !empty( $abandoned ),
127
+ 'has_guard_files' => !empty( $guardFilesData ),
128
+ 'is_active' => $theme->active || $theme->is_parent,
129
+ 'is_vulnerable' => !empty( $vulnerabilities ),
130
+ 'is_wporg' => $theme->isWpOrg(),
131
+ 'is_child' => $theme->is_child,
132
+ 'is_parent' => $theme->is_parent,
133
+ ],
134
+ 'vars' => [
135
+ 'count_items' => count( $guardFilesData ) + count( $vulnerabilities )
136
+ + ( empty( $abandoned ) ? 0 : 1 )
137
+ ],
138
+ ];
139
+ $data[ 'flags' ][ 'has_issue' ] = $data[ 'flags' ][ 'is_abandoned' ]
140
+ || $data[ 'flags' ][ 'has_guard_files' ]
141
+ || $data[ 'flags' ][ 'is_vulnerable' ];
142
+ $data[ 'flags' ][ 'has_warning' ] = !$data[ 'flags' ][ 'has_issue' ]
143
+ && (
144
+ !$data[ 'flags' ][ 'is_active' ]
145
+ || $data[ 'flags' ][ 'has_update' ]
146
+ );
147
+ return $data;
148
+ }
149
+ }
src/lib/src/Modules/HackGuard/Render/ScanResults/SectionWordpress.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Render\ScanResults;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\ScanTables\LoadRawTableData;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Scans\ForWordpress;
8
+ use FernleafSystems\Wordpress\Services\Services;
9
+
10
+ class SectionWordpress extends SectionBase {
11
+
12
+ public function render() :string {
13
+ return $this->getMod()
14
+ ->renderTemplate(
15
+ '/wpadmin_pages/insights/scans/results/section/wordpress/index.twig',
16
+ $this->buildRenderData()
17
+ );
18
+ }
19
+
20
+ protected function buildRenderData() :array {
21
+ $wpData = $this->buildWordpressData();
22
+ return Services::DataManipulation()
23
+ ->mergeArraysRecursive( $this->getCommonRenderData(), [
24
+ 'strings' => [
25
+ 'no_files' => __( "Previous scans didn't detect any modified, missing or unrecognised files in the WordPress core directories.", 'wp-simple-firewall' ),
26
+ 'files_found' => __( "Previous scans detected 1 or more modified, missing or unrecognised files in the WordPress core directories.", 'wp-simple-firewall' ),
27
+ ],
28
+ 'vars' => [
29
+ 'count_items' => $wpData[ 'vars' ][ 'count_items' ],
30
+ 'wordpress' => $wpData,
31
+ 'datatables_init' => ( new ForWordpress() )
32
+ ->setMod( $this->getMod() )
33
+ ->build()
34
+ ]
35
+ ] );
36
+ }
37
+
38
+ private function buildWordpressData() :array {
39
+
40
+ $coreFilesData = ( new LoadRawTableData() )
41
+ ->setMod( $this->getMod() )
42
+ ->loadForWordPress();
43
+
44
+ $WP = Services::WpGeneral();
45
+ $data = [
46
+ 'info' => [
47
+ 'type' => 'theme',
48
+ 'version' => $WP->getVersion(),
49
+ 'dir' => wp_normalize_path( ABSPATH ),
50
+ ],
51
+ 'flags' => [
52
+ 'has_update' => $WP->hasCoreUpdate(),
53
+ 'has_core_files' => !empty( $coreFilesData ),
54
+ 'is_vulnerable' => false,
55
+ ],
56
+ 'vars' => [
57
+ 'count_items' => count( $coreFilesData )
58
+ ]
59
+ ];
60
+ $data[ 'flags' ][ 'has_issue' ] = $data[ 'flags' ][ 'has_core_files' ]
61
+ || $data[ 'flags' ][ 'is_vulnerable' ];
62
+ return $data;
63
+ }
64
+ }
src/lib/src/Modules/HackGuard/Reporting.php CHANGED
@@ -1,4 +1,4 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
4
 
@@ -13,6 +13,7 @@ class Reporting extends Base\Reporting {
13
  protected function enumAlertReporters() :array {
14
  return [
15
  new Reports\ScanAlerts(),
 
16
  new Reports\FileLockerAlerts(),
17
  ];
18
  }
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
4
 
13
  protected function enumAlertReporters() :array {
14
  return [
15
  new Reports\ScanAlerts(),
16
+ new Reports\ScanRepairs(),
17
  new Reports\FileLockerAlerts(),
18
  ];
19
  }
src/lib/src/Modules/HackGuard/Scan/Controller/Base.php CHANGED
@@ -2,13 +2,14 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
9
  use FernleafSystems\Wordpress\Plugin\Shield\Scans;
10
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultItem;
11
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultsSet;
12
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseScanActionVO;
13
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Table\BaseEntryFormatter;
14
  use FernleafSystems\Wordpress\Services\Services;
@@ -16,16 +17,15 @@ use FernleafSystems\Wordpress\Services\Services;
16
  abstract class Base extends ExecOnceModConsumer {
17
 
18
  const SCAN_SLUG = '';
 
19
 
20
  /**
21
  * @var BaseScanActionVO
22
  */
23
  private $scanActionVO;
24
 
25
- /**
26
- * Base constructor.
27
- * see dynamic constructors: features/hack_protect.php
28
- */
29
  public function __construct() {
30
  }
31
 
@@ -42,6 +42,21 @@ abstract class Base extends ExecOnceModConsumer {
42
  $mod->getScanQueueController()->startScans( [ $this->getSlug() ] );
43
  }
44
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }
46
 
47
  public function cleanStalesResults() {
@@ -58,8 +73,8 @@ abstract class Base extends ExecOnceModConsumer {
58
  ->delete( $results );
59
  }
60
 
61
- public function createFileDownloadLink( Databases\Scanner\EntryVO $entry ) :string {
62
- return $this->getMod()->createFileDownloadLink( 'scan_file', [ 'rid' => $entry->id ] );
63
  }
64
 
65
  public function getLastScanAt() :int {
@@ -72,53 +87,57 @@ abstract class Base extends ExecOnceModConsumer {
72
  return ( $entry instanceof Databases\Events\EntryVO ) ? (int)$entry->created_at : 0;
73
  }
74
 
 
 
 
 
 
 
 
 
 
 
 
75
  public function getScanHasProblem() :bool {
76
- /** @var ModCon $mod */
77
- $mod = $this->getMod();
78
- /** @var Databases\Scanner\Select $sel */
79
- $sel = $mod->getDbHandler_ScanResults()->getQuerySelector();
80
- return $sel->filterByNotIgnored()
81
- ->filterByScan( $this->getSlug() )
82
- ->count() > 0;
83
  }
84
 
85
  /**
86
- * @param BaseResultItem|mixed $item
87
  * @return bool
88
  */
89
  abstract protected function isResultItemStale( $item ) :bool;
90
 
91
- /**
92
- * @param int|string $itemID
93
- * @param string $action
94
- * @return bool
95
- */
96
- public function executeItemAction( int $itemID, string $action ) :bool {
 
 
 
 
 
97
  $success = false;
98
 
99
- if ( is_numeric( $itemID ) ) {
100
  /** @var Databases\Scanner\EntryVO $entry */
101
  $entry = $this->getScanResultsDbHandler()
102
  ->getQuerySelector()
103
- ->byId( $itemID );
104
  if ( empty( $entry ) ) {
105
  throw new \Exception( 'Item could not be found.' );
106
  }
107
 
108
- $entry = ( new HackGuard\Scan\Results\ConvertBetweenTypes() )
109
- ->setScanController( $this )
110
- ->convertVoToResultItem( $entry );
111
-
112
- $success = $this->getItemActionHandler()
113
- ->setScanItem( $entry )
114
- ->process( $action );
115
  }
116
 
117
  return $success;
118
  }
119
 
120
  /**
121
- * @return Scans\Base\BaseResultsSet|mixed
122
  */
123
  protected function getItemsToAutoRepair() {
124
  /** @var Databases\Scanner\Select $sel */
@@ -140,25 +159,26 @@ abstract class Base extends ExecOnceModConsumer {
140
  }
141
 
142
  /**
143
- * @param bool $bIncludeIgnored
144
- * @return Scans\Base\BaseResultsSet|mixed
145
  */
146
- public function getAllResults( $bIncludeIgnored = false ) {
147
  /** @var Databases\Scanner\Select $sel */
148
  $sel = $this->getScanResultsDbHandler()->getQuerySelector();
149
  $sel->filterByScan( $this->getSlug() );
150
- if ( !$bIncludeIgnored ) {
151
  $sel->filterByNotIgnored();
152
  }
 
153
  return ( new HackGuard\Scan\Results\ConvertBetweenTypes() )
154
  ->setScanController( $this )
155
- ->fromVOsToResultsSet( $sel->query() );
156
  }
157
 
158
  /**
159
  * @return Scans\Base\Utilities\ItemActionHandler
160
  */
161
- protected function getItemActionHandler() {
162
  return $this->newItemActionHandler()
163
  ->setMod( $this->getMod() )
164
  ->setScanController( $this );
@@ -189,6 +209,10 @@ abstract class Base extends ExecOnceModConsumer {
189
  return false;
190
  }
191
 
 
 
 
 
192
  abstract public function isEnabled() :bool;
193
 
194
  protected function isPremiumOnly() :bool {
@@ -235,7 +259,7 @@ abstract class Base extends ExecOnceModConsumer {
235
  try {
236
  $this->getItemActionHandler()
237
  ->setScanItem( $item )
238
- ->repair();
239
  }
240
  catch ( \Exception $e ) {
241
  }
@@ -264,14 +288,14 @@ abstract class Base extends ExecOnceModConsumer {
264
  try {
265
  $slug = strtolower( ( new \ReflectionClass( $this ) )->getShortName() );
266
  }
267
- catch ( \ReflectionException $e ) {
268
  $slug = '';
269
  }
270
  return $slug;
271
  }
272
 
273
  /**
274
- * @return BaseResultItem|mixed
275
  */
276
  public function getNewResultItem() {
277
  $class = $this->getScanNamespace().'ResultItem';
@@ -279,7 +303,7 @@ abstract class Base extends ExecOnceModConsumer {
279
  }
280
 
281
  /**
282
- * @return BaseResultsSet|mixed
283
  */
284
  public function getNewResultsSet() {
285
  $class = $this->getScanNamespace().'ResultsSet';
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Crons\PluginCronsConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
9
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
10
  use FernleafSystems\Wordpress\Plugin\Shield\Scans;
11
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\ResultItem;
12
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\ResultsSet;
13
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseScanActionVO;
14
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Table\BaseEntryFormatter;
15
  use FernleafSystems\Wordpress\Services\Services;
17
  abstract class Base extends ExecOnceModConsumer {
18
 
19
  const SCAN_SLUG = '';
20
+ use PluginCronsConsumer;
21
 
22
  /**
23
  * @var BaseScanActionVO
24
  */
25
  private $scanActionVO;
26
 
27
+ private static $resultsCounts;
28
+
 
 
29
  public function __construct() {
30
  }
31
 
42
  $mod->getScanQueueController()->startScans( [ $this->getSlug() ] );
43
  }
44
  );
45
+ add_filter( $this->getCon()->prefix( 'admin_bar_menu_items' ), [ $this, 'addAdminMenuBarItem' ], 100 );
46
+ }
47
+
48
+ public function addAdminMenuBarItem( array $items ) :array {
49
+ $problems = $this->countScanProblems();
50
+ if ( $problems > 0 ) {
51
+ $items[] = [
52
+ 'id' => $this->getCon()->prefix( 'problems-'.$this->getSlug() ),
53
+ 'title' => $this->getScanName()
54
+ .sprintf( '<div class="wp-core-ui wp-ui-notification shield-counter"><span aria-hidden="true">%s</span></div>', $problems ),
55
+ 'href' => $this->getCon()->getModule_Insights()->getUrl_ScansResults(),
56
+ 'warnings' => $problems
57
+ ];
58
+ }
59
+ return $items;
60
  }
61
 
62
  public function cleanStalesResults() {
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 {
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 */
93
+ $mod = $this->getMod();
94
+ /** @var Databases\Scanner\Select $sel */
95
+ $sel = $mod->getDbHandler_ScanResults()->getQuerySelector();
96
+ self::$resultsCounts = $sel->countForEachScan();
97
+ }
98
+ return self::$resultsCounts[ static::SCAN_SLUG ] ?? 0;
99
+ }
100
+
101
  public function getScanHasProblem() :bool {
102
+ return $this->countScanProblems() > 0;
 
 
 
 
 
 
103
  }
104
 
105
  /**
106
+ * @param ResultItem|mixed $item
107
  * @return bool
108
  */
109
  abstract protected function isResultItemStale( $item ) :bool;
110
 
111
+ public function executeEntryAction( Databases\Scanner\EntryVO $entry, string $action ) :bool {
112
+ $item = ( new HackGuard\Scan\Results\ConvertBetweenTypes() )
113
+ ->setScanController( $this )
114
+ ->convertVoToResultItem( $entry );
115
+
116
+ return $this->getItemActionHandler()
117
+ ->setScanItem( $item )
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
  */
142
  protected function getItemsToAutoRepair() {
143
  /** @var Databases\Scanner\Select $sel */
159
  }
160
 
161
  /**
162
+ * @param bool $includeIgnored
163
+ * @return Scans\Base\ResultsSet|mixed
164
  */
165
+ public function getAllResults( $includeIgnored = false ) {
166
  /** @var Databases\Scanner\Select $sel */
167
  $sel = $this->getScanResultsDbHandler()->getQuerySelector();
168
  $sel->filterByScan( $this->getSlug() );
169
+ if ( !$includeIgnored ) {
170
  $sel->filterByNotIgnored();
171
  }
172
+ $raw = $this->isRestricted() ? [] : $sel->query();
173
  return ( new HackGuard\Scan\Results\ConvertBetweenTypes() )
174
  ->setScanController( $this )
175
+ ->fromVOsToResultsSet( $raw );
176
  }
177
 
178
  /**
179
  * @return Scans\Base\Utilities\ItemActionHandler
180
  */
181
+ public function getItemActionHandler() {
182
  return $this->newItemActionHandler()
183
  ->setMod( $this->getMod() )
184
  ->setScanController( $this );
209
  return false;
210
  }
211
 
212
+ public function canCronAutoDelete() :bool {
213
+ return false;
214
+ }
215
+
216
  abstract public function isEnabled() :bool;
217
 
218
  protected function isPremiumOnly() :bool {
259
  try {
260
  $this->getItemActionHandler()
261
  ->setScanItem( $item )
262
+ ->repair( $this->canCronAutoDelete() );
263
  }
264
  catch ( \Exception $e ) {
265
  }
288
  try {
289
  $slug = strtolower( ( new \ReflectionClass( $this ) )->getShortName() );
290
  }
291
+ catch ( \Exception $e ) {
292
  $slug = '';
293
  }
294
  return $slug;
295
  }
296
 
297
  /**
298
+ * @return ResultItem|mixed
299
  */
300
  public function getNewResultItem() {
301
  $class = $this->getScanNamespace().'ResultItem';
303
  }
304
 
305
  /**
306
+ * @return ResultsSet|mixed
307
  */
308
  public function getNewResultsSet() {
309
  $class = $this->getScanNamespace().'ResultsSet';
src/lib/src/Modules/HackGuard/Scan/Controller/BaseForAssets.php CHANGED
@@ -14,11 +14,11 @@ abstract class BaseForAssets extends Base {
14
  protected function isResultItemStale( $item ) :bool {
15
  if ( $item->context == 'plugins' ) {
16
  $asset = Services::WpPlugins()->getPluginAsVo( $item->slug );
17
- $stale = empty( $asset ) || !$asset->active;
18
  }
19
  else {
20
  $asset = Services::WpThemes()->getThemeAsVo( $item->slug );
21
- $stale = empty( $asset ) || ( !$asset->active && !$asset->is_parent );
22
  }
23
  return $stale;
24
  }
14
  protected function isResultItemStale( $item ) :bool {
15
  if ( $item->context == 'plugins' ) {
16
  $asset = Services::WpPlugins()->getPluginAsVo( $item->slug );
17
+ $stale = empty( $asset );
18
  }
19
  else {
20
  $asset = Services::WpThemes()->getThemeAsVo( $item->slug );
21
+ $stale = empty( $asset );
22
  }
23
  return $stale;
24
  }
src/lib/src/Modules/HackGuard/Scan/Controller/Ptg.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
 
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans;
7
  use FernleafSystems\Wordpress\Services\Services;
8
  use FernleafSystems\Wordpress\Services\Utilities\WpOrg;
@@ -16,6 +17,31 @@ class Ptg extends BaseForAssets {
16
  ( new HackGuard\Scan\Utilities\PtgAddReinstallLinks() )
17
  ->setScanController( $this )
18
  ->execute();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  }
20
 
21
  /**
@@ -35,6 +61,13 @@ class Ptg extends BaseForAssets {
35
  elseif ( $opts->isRepairFilePlugin() ) {
36
  $results = $results->getResultsForPluginsContext();
37
  }
 
 
 
 
 
 
 
38
  }
39
 
40
  return $results;
@@ -47,15 +80,23 @@ class Ptg extends BaseForAssets {
47
  }
48
 
49
  /**
50
- * @param Scans\Mal\ResultItem $item
51
  * @return bool
52
  */
53
  protected function isResultItemStale( $item ) :bool {
54
- $asset = ( new WpOrg\Plugin\Files() )->findPluginFromFile( $item->path_full );
55
- if ( empty( $asset ) ) {
56
- $asset = ( new WpOrg\Theme\Files() )->findThemeFromFile( $item->path_full );
 
 
 
 
 
 
 
57
  }
58
- return empty( $asset );
 
59
  }
60
 
61
  /**
@@ -84,13 +125,11 @@ class Ptg extends BaseForAssets {
84
  }
85
 
86
  public function isEnabled() :bool {
87
- return $this->getOptions()->isOpt( 'ptg_enable', 'Y' ) && $this->getOptions()->isOptReqsMet( 'ptg_enable' );
88
  }
89
 
90
  public function isReady() :bool {
91
- return parent::isReady()
92
- && $this->getOptions()->isOptReqsMet( 'ptg_enable' )
93
- && $this->getMod()->canCacheDirWrite();
94
  }
95
 
96
  /**
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots\StoreAction;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Scans;
8
  use FernleafSystems\Wordpress\Services\Services;
9
  use FernleafSystems\Wordpress\Services\Utilities\WpOrg;
17
  ( new HackGuard\Scan\Utilities\PtgAddReinstallLinks() )
18
  ->setScanController( $this )
19
  ->execute();
20
+
21
+ $this->setupCronHooks();
22
+ add_action( 'wp_loaded', [ $this, 'onWpLoaded' ] );
23
+ add_action( $this->getCon()->prefix( 'plugin_shutdown' ), [ $this, 'onModuleShutdown' ] );
24
+ }
25
+
26
+ public function onWpLoaded() {
27
+ ( new StoreAction\ScheduleBuildAll() )
28
+ ->setMod( $this->getMod() )
29
+ ->hookBuild();
30
+ }
31
+
32
+ public function onModuleShutdown() {
33
+ ( new StoreAction\ScheduleBuildAll() )
34
+ ->setMod( $this->getMod() )
35
+ ->schedule();
36
+ }
37
+
38
+ public function runHourlyCron() {
39
+ ( new StoreAction\TouchAll() )
40
+ ->setMod( $this->getMod() )
41
+ ->run();
42
+ ( new StoreAction\CleanAll() )
43
+ ->setMod( $this->getMod() )
44
+ ->run();
45
  }
46
 
47
  /**
61
  elseif ( $opts->isRepairFilePlugin() ) {
62
  $results = $results->getResultsForPluginsContext();
63
  }
64
+
65
+ /** @var Scans\Ptg\ResultItem $item */
66
+ foreach ( $results->getItems() as $item ) {
67
+ if ( $item->is_unrecognised ) {
68
+ $results->removeItem( $item );
69
+ }
70
+ }
71
  }
72
 
73
  return $results;
80
  }
81
 
82
  /**
83
+ * @param Scans\Ptg\ResultItem $item
84
  * @return bool
85
  */
86
  protected function isResultItemStale( $item ) :bool {
87
+ $FS = Services::WPFS();
88
+ $stale = parent::isResultItemStale( $item )
89
+ || ( ( $item->is_unrecognised || $item->is_different ) && !$FS->isFile( $item->path_full ) );
90
+
91
+ if ( !$stale ) {
92
+ $asset = ( new WpOrg\Plugin\Files() )->findPluginFromFile( $item->path_full );
93
+ if ( empty( $asset ) ) {
94
+ $asset = ( new WpOrg\Theme\Files() )->findThemeFromFile( $item->path_full );
95
+ }
96
+ $stale = empty( $asset );
97
  }
98
+
99
+ return $stale;
100
  }
101
 
102
  /**
125
  }
126
 
127
  public function isEnabled() :bool {
128
+ return $this->getOptions()->isOpt( 'ptg_enable', 'Y' );
129
  }
130
 
131
  public function isReady() :bool {
132
+ return parent::isReady() && $this->getCon()->hasCacheDir();
 
 
133
  }
134
 
135
  /**
src/lib/src/Modules/HackGuard/Scan/Controller/Ufc.php CHANGED
@@ -17,6 +17,10 @@ class Ufc extends Base {
17
  return new Scans\Ufc\Utilities\ItemActionHandler();
18
  }
19
 
 
 
 
 
20
  /**
21
  * @param Scans\Mal\ResultItem $item
22
  * @return bool
@@ -26,9 +30,9 @@ class Ufc extends Base {
26
  }
27
 
28
  public function isCronAutoRepair() :bool {
29
- /** @var HackGuard\Options $oOpts */
30
- $oOpts = $this->getOptions();
31
- return $oOpts->isUfsDeleteFiles();
32
  }
33
 
34
  public function isEnabled() :bool {
17
  return new Scans\Ufc\Utilities\ItemActionHandler();
18
  }
19
 
20
+ public function canCronAutoDelete() :bool {
21
+ return $this->isCronAutoRepair();
22
+ }
23
+
24
  /**
25
  * @param Scans\Mal\ResultItem $item
26
  * @return bool
30
  }
31
 
32
  public function isCronAutoRepair() :bool {
33
+ /** @var HackGuard\Options $opts */
34
+ $opts = $this->getOptions();
35
+ return $opts->isUfsDeleteFiles();
36
  }
37
 
38
  public function isEnabled() :bool {
src/lib/src/Modules/HackGuard/Scan/Queue/CollateResults.php CHANGED
@@ -17,35 +17,35 @@ class CollateResults {
17
 
18
  /**
19
  * @param string $sScanSlug
20
- * @return Scans\Base\BaseResultsSet|mixed|null
21
  */
22
  public function collate( $sScanSlug ) {
23
- /** @var Databases\ScanQueue\Handler $oDbH */
24
- $oDbH = $this->getDbHandler();
25
- /** @var Databases\ScanQueue\Select $oSel */
26
- $oSel = $oDbH->getQuerySelector();
27
- $oSel->filterByScan( $sScanSlug )
28
- ->setResultsAsVo( true );
29
- $oSCon = $this->getScanController();
30
-
31
- $oResultsSet = null;
32
- /** @var Databases\ScanQueue\EntryVO $oEntry */
33
- foreach ( $oSel->query() as $oEntry ) {
34
- $oAction = ( new ConvertBetweenTypes() )
35
- ->setDbHandler( $oDbH )
36
- ->fromDbEntryToAction( $oEntry );
37
-
38
- if ( empty( $oResultsSet ) ) {
39
- $oResultsSet = $oSCon->getNewResultsSet();
40
  }
41
 
42
- foreach ( $oAction->results as $aResItemData ) {
43
- $oResultsSet->addItem(
44
- $oAction->getNewResultItem()->applyFromArray( $aResItemData )
45
  );
46
  }
47
  }
48
 
49
- return $oResultsSet;
50
  }
51
  }
17
 
18
  /**
19
  * @param string $sScanSlug
20
+ * @return Scans\Base\ResultsSet|mixed|null
21
  */
22
  public function collate( $sScanSlug ) {
23
+ /** @var Databases\ScanQueue\Handler $dbh */
24
+ $dbh = $this->getDbHandler();
25
+ /** @var Databases\ScanQueue\Select $selector */
26
+ $selector = $dbh->getQuerySelector();
27
+ $selector->filterByScan( $sScanSlug )
28
+ ->setResultsAsVo( true );
29
+ $scanCon = $this->getScanController();
30
+
31
+ $resultsSet = null;
32
+ /** @var Databases\ScanQueue\EntryVO $entry */
33
+ foreach ( $selector->query() as $entry ) {
34
+ $action = ( new ConvertBetweenTypes() )
35
+ ->setDbHandler( $dbh )
36
+ ->fromDbEntryToAction( $entry );
37
+
38
+ if ( empty( $resultsSet ) ) {
39
+ $resultsSet = $scanCon->getNewResultsSet();
40
  }
41
 
42
+ foreach ( $action->results as $aResItemData ) {
43
+ $resultsSet->addItem(
44
+ $action->getNewResultItem()->applyFromArray( $aResItemData )
45
  );
46
  }
47
  }
48
 
49
+ return $resultsSet;
50
  }
51
  }
src/lib/src/Modules/HackGuard/Scan/Queue/CompleteQueue.php CHANGED
@@ -27,25 +27,25 @@ class CompleteQueue {
27
  $con = $this->getCon();
28
  /** @var Databases\ScanQueue\Handler $dbh */
29
  $dbh = $this->getDbHandler();
30
- $oSel = $dbh->getQuerySelector();
31
 
32
- foreach ( $oSel->getDistinctForColumn( 'scan' ) as $scanSlug ) {
33
 
34
- $oScanCon = $mod->getScanCon( $scanSlug );
35
 
36
- $oResultsSet = ( new CollateResults() )
37
- ->setScanController( $oScanCon )
38
  ->setDbHandler( $dbh )
39
  ->collate( $scanSlug );
40
 
41
  $con->fireEvent( $scanSlug.'_scan_run' );
42
 
43
- if ( $oResultsSet instanceof Scans\Base\BaseResultsSet ) {
44
  ( new HackGuard\Scan\Results\ResultsUpdate() )
45
- ->setScanController( $oScanCon )
46
- ->update( $oResultsSet );
47
 
48
- if ( $oResultsSet->countItems() > 0 ) {
49
  $con->fireEvent( $scanSlug.'_scan_found' );
50
  }
51
  }
27
  $con = $this->getCon();
28
  /** @var Databases\ScanQueue\Handler $dbh */
29
  $dbh = $this->getDbHandler();
30
+ $selector = $dbh->getQuerySelector();
31
 
32
+ foreach ( $selector->getDistinctForColumn( 'scan' ) as $scanSlug ) {
33
 
34
+ $scanCon = $mod->getScanCon( $scanSlug );
35
 
36
+ $resultsSet = ( new CollateResults() )
37
+ ->setScanController( $scanCon )
38
  ->setDbHandler( $dbh )
39
  ->collate( $scanSlug );
40
 
41
  $con->fireEvent( $scanSlug.'_scan_run' );
42
 
43
+ if ( $resultsSet instanceof Scans\Base\ResultsSet ) {
44
  ( new HackGuard\Scan\Results\ResultsUpdate() )
45
+ ->setScanController( $scanCon )
46
+ ->update( $resultsSet );
47
 
48
+ if ( $resultsSet->countItems() > 0 ) {
49
  $con->fireEvent( $scanSlug.'_scan_found' );
50
  }
51
  }
src/lib/src/Modules/HackGuard/Scan/Queue/Controller.php CHANGED
@@ -37,7 +37,7 @@ class Controller {
37
  /**
38
  * @return bool[]
39
  */
40
- public function getScansRunningStates() {
41
  /** @var HackGuard\ModCon $mod */
42
  $mod = $this->getMod();
43
  /** @var HackGuard\Options $opts */
@@ -48,11 +48,11 @@ class Controller {
48
  // First clean the queue:
49
  $this->cleanExpiredFromQueue();
50
 
51
- $aScans = array_fill_keys( $opts->getScanSlugs(), false );
52
  foreach ( $sel->getInitiatedScans() as $sInitScan ) {
53
- $aScans[ $sInitScan ] = true;
54
  }
55
- return $aScans;
56
  }
57
 
58
  /**
@@ -66,16 +66,16 @@ class Controller {
66
  $nExpiredBoundary = Services::Request()
67
  ->carbon()
68
  ->subSeconds( $opts->getMalQueueExpirationInterval() )->timestamp;
69
- /** @var ScanQueue\Delete $oDel */
70
- $oDel = $mod->getDbHandler_ScanQueue()->getQueryDeleter();
71
- return $oDel->addWhereOlderThan( $nExpiredBoundary )
72
- ->query();
73
  }
74
 
75
  /**
76
  * @return string[]
77
  */
78
- public function getRunningScans() {
79
  return array_keys( array_filter( $this->getScansRunningStates() ) );
80
  }
81
 
@@ -88,27 +88,27 @@ class Controller {
88
  /** @var ScanQueue\Select $sel */
89
  $sel = $mod->getDbHandler_ScanQueue()->getQuerySelector();
90
 
91
- $aUnfinished = $sel->getUnfinishedScans();
92
- $nProgress = 1;
93
- if ( $sel->getUnfinishedScans() > 0 ) {
94
- $nInitiated = count( $sel->getInitiatedScans() );
95
- if ( $nInitiated > 0 ) {
96
- $nProgress = 1 - ( count( $aUnfinished )/count( $sel->getInitiatedScans() ) );
97
- }
98
  }
99
  else {
100
- $nProgress = 1;
 
 
 
 
101
  }
102
- return $nProgress;
 
103
  }
104
 
105
- /**
106
- * @return bool
107
- */
108
- public function hasRunningScans() {
109
- /** @var HackGuard\Options $oOpts */
110
- $oOpts = $this->getOptions();
111
- return count( $this->getRunningScans() ) > 0 || count( $oOpts->getScansToBuild() ) > 0;
112
  }
113
 
114
  /**
37
  /**
38
  * @return bool[]
39
  */
40
+ public function getScansRunningStates() :array {
41
  /** @var HackGuard\ModCon $mod */
42
  $mod = $this->getMod();
43
  /** @var HackGuard\Options $opts */
48
  // First clean the queue:
49
  $this->cleanExpiredFromQueue();
50
 
51
+ $scans = array_fill_keys( $opts->getScanSlugs(), false );
52
  foreach ( $sel->getInitiatedScans() as $sInitScan ) {
53
+ $scans[ $sInitScan ] = true;
54
  }
55
+ return $scans;
56
  }
57
 
58
  /**
66
  $nExpiredBoundary = Services::Request()
67
  ->carbon()
68
  ->subSeconds( $opts->getMalQueueExpirationInterval() )->timestamp;
69
+ /** @var ScanQueue\Delete $deleter */
70
+ $deleter = $mod->getDbHandler_ScanQueue()->getQueryDeleter();
71
+ return $deleter->addWhereOlderThan( $nExpiredBoundary )
72
+ ->query();
73
  }
74
 
75
  /**
76
  * @return string[]
77
  */
78
+ public function getRunningScans() :array {
79
  return array_keys( array_filter( $this->getScansRunningStates() ) );
80
  }
81
 
88
  /** @var ScanQueue\Select $sel */
89
  $sel = $mod->getDbHandler_ScanQueue()->getQuerySelector();
90
 
91
+ $countsAll = $sel->countAllForEachScan();
92
+ $countsUnfinished = $sel->countUnfinishedForEachScan();
93
+
94
+ if ( empty( $countsAll ) || empty( $countsUnfinished ) ) {
95
+ $progress = 1;
 
 
96
  }
97
  else {
98
+ $progress = 0;
99
+ $eachScanWeight = 1/count( $countsAll );
100
+ foreach ( array_keys( $countsAll ) as $scan ) {
101
+ $progress += $eachScanWeight*( 1 - ( ( $countsUnfinished[ $scan ] ?? 0 )/$countsAll[ $scan ] ) );
102
+ }
103
  }
104
+
105
+ return $progress;
106
  }
107
 
108
+ public function hasRunningScans() :bool {
109
+ /** @var HackGuard\Options $opts */
110
+ $opts = $this->getOptions();
111
+ return count( $this->getRunningScans() ) > 0 || count( $opts->getScansToBuild() ) > 0;
 
 
 
112
  }
113
 
114
  /**
src/lib/src/Modules/HackGuard/Scan/Queue/ConvertBetweenTypes.php CHANGED
@@ -15,15 +15,15 @@ class ConvertBetweenTypes {
15
  use Databases\Base\HandlerConsumer;
16
 
17
  /**
18
- * @param Databases\ScanQueue\EntryVO $oEntry
19
  * @return Scans\Base\BaseScanActionVO|mixed
20
  */
21
- public function fromDbEntryToAction( $oEntry ) {
22
- $oScanAction = ScanActionFromSlug::GetAction( $oEntry->scan );
23
- $oScanAction->applyFromArray( $oEntry->meta );
24
- $oScanAction->items = $oEntry->items;
25
- $oScanAction->results = $oEntry->results;
26
- return $oScanAction;
27
  }
28
 
29
  /**
15
  use Databases\Base\HandlerConsumer;
16
 
17
  /**
18
+ * @param Databases\ScanQueue\EntryVO $entry
19
  * @return Scans\Base\BaseScanActionVO|mixed
20
  */
21
+ public function fromDbEntryToAction( $entry ) {
22
+ $action = ScanActionFromSlug::GetAction( $entry->scan );
23
+ $action->applyFromArray( $entry->meta );
24
+ $action->items = $entry->items;
25
+ $action->results = $entry->results;
26
+ return $action;
27
  }
28
 
29
  /**
src/lib/src/Modules/HackGuard/Scan/Queue/QueueProcessor.php CHANGED
@@ -40,26 +40,26 @@ class QueueProcessor extends Utilities\BackgroundProcessing\BackgroundProcess {
40
  * in the next pass through. Or, return false to remove the
41
  * item from the queue.
42
  *
43
- * @param ScanQueue\EntryVO $oEntry Queue item to iterate over.
44
  * @return mixed
45
  */
46
- protected function task( $oEntry ) {
47
- $oEntry->started_at = Services::Request()->ts();
48
- /** @var ScanQueue\Update $oUpd */
49
- $oUpd = $this->getDbHandler()->getQueryUpdater();
50
- $oUpd->setStarted( $oEntry );
51
 
52
  try {
53
  ( new ScanExecute() )
54
  ->setMod( $this->getMod() )
55
- ->execute( $oEntry );
56
  }
57
  catch ( \Exception $e ) {
58
  // error_log( $e->getMessage() );
59
  }
60
 
61
- $oUpd->setFinished( $oEntry );
62
- return $oEntry;
63
  }
64
 
65
  /**
40
  * in the next pass through. Or, return false to remove the
41
  * item from the queue.
42
  *
43
+ * @param ScanQueue\EntryVO $entry Queue item to iterate over.
44
  * @return mixed
45
  */
46
+ protected function task( $entry ) {
47
+ $entry->started_at = Services::Request()->ts();
48
+ /** @var ScanQueue\Update $updater */
49
+ $updater = $this->getDbHandler()->getQueryUpdater();
50
+ $updater->setStarted( $entry );
51
 
52
  try {
53
  ( new ScanExecute() )
54
  ->setMod( $this->getMod() )
55
+ ->execute( $entry );
56
  }
57
  catch ( \Exception $e ) {
58
  // error_log( $e->getMessage() );
59
  }
60
 
61
+ $updater->setFinished( $entry );
62
+ return $entry;
63
  }
64
 
65
  /**
src/lib/src/Modules/HackGuard/Scan/Queue/ScanEnqueue.php CHANGED
@@ -19,14 +19,14 @@ class ScanEnqueue {
19
  * @throws \Exception
20
  */
21
  public function enqueue() {
22
- $oAction = $this->getScanActionVO();
23
- $aAllItems = (array)$oAction->items;
24
- unset( $oAction->items );
25
 
26
- $nSliceSize = $oAction::QUEUE_GROUP_SIZE_LIMIT;
27
 
28
  do {
29
- $oCurrent = clone $oAction;
30
  $oCurrent->items = array_slice( $aAllItems, 0, $nSliceSize );
31
  $this->pushActionToQueue( $oCurrent );
32
  $aAllItems = array_slice( $aAllItems, $nSliceSize );
@@ -36,12 +36,12 @@ class ScanEnqueue {
36
  }
37
 
38
  /**
39
- * @param Scans\Base\BaseScanActionVO $oAction
40
  */
41
- protected function pushActionToQueue( $oAction ) {
42
- $oEntry = ( new ConvertBetweenTypes() )
43
  ->setDbHandler( $this->getDbHandler() )
44
- ->fromActionToDbEntry( $oAction );
45
- $this->getQueueProcessor()->push_to_queue( $oEntry );
46
  }
47
  }
19
  * @throws \Exception
20
  */
21
  public function enqueue() {
22
+ $action = $this->getScanActionVO();
23
+ $aAllItems = (array)$action->items;
24
+ unset( $action->items );
25
 
26
+ $nSliceSize = $action::QUEUE_GROUP_SIZE_LIMIT;
27
 
28
  do {
29
+ $oCurrent = clone $action;
30
  $oCurrent->items = array_slice( $aAllItems, 0, $nSliceSize );
31
  $this->pushActionToQueue( $oCurrent );
32
  $aAllItems = array_slice( $aAllItems, $nSliceSize );
36
  }
37
 
38
  /**
39
+ * @param Scans\Base\BaseScanActionVO $action
40
  */
41
+ protected function pushActionToQueue( $action ) {
42
+ $entry = ( new ConvertBetweenTypes() )
43
  ->setDbHandler( $this->getDbHandler() )
44
+ ->fromActionToDbEntry( $action );
45
+ $this->getQueueProcessor()->push_to_queue( $entry );
46
  }
47
  }
src/lib/src/Modules/HackGuard/Scan/Queue/ScanExecute.php CHANGED
@@ -14,30 +14,29 @@ class ScanExecute {
14
  use Shield\Modules\ModConsumer;
15
 
16
  /**
17
- * @param ScanQueue\EntryVO $oEntry
18
  * @return ScanQueue\EntryVO
19
  * @throws \Exception
20
  */
21
- public function execute( $oEntry ) {
22
  /** @var Shield\Modules\HackGuard\ModCon $mod */
23
  $mod = $this->getMod();
24
- $oDbH = $mod->getDbHandler_ScanQueue();
25
- $oTypeConverter = ( new ConvertBetweenTypes() )->setDbHandler( $oDbH );
26
 
27
- $oAction = $oTypeConverter->fromDbEntryToAction( $oEntry );
 
 
28
 
29
- $this->getScanner( $oAction )
30
- ->setScanActionVO( $oAction )
31
  ->setMod( $mod )
32
  ->run();
33
 
34
- if ( $oAction->usleep > 0 ) {
35
- usleep( $oAction->usleep );
36
  }
37
 
38
- $oEntry->results = $oAction->results;
39
-
40
- return $oEntry;
41
  }
42
 
43
  /**
14
  use Shield\Modules\ModConsumer;
15
 
16
  /**
17
+ * @param ScanQueue\EntryVO $entry
18
  * @return ScanQueue\EntryVO
19
  * @throws \Exception
20
  */
21
+ public function execute( $entry ) {
22
  /** @var Shield\Modules\HackGuard\ModCon $mod */
23
  $mod = $this->getMod();
 
 
24
 
25
+ $action = ( new ConvertBetweenTypes() )
26
+ ->setDbHandler( $mod->getDbHandler_ScanQueue() )
27
+ ->fromDbEntryToAction( $entry );
28
 
29
+ $this->getScanner( $action )
30
+ ->setScanActionVO( $action )
31
  ->setMod( $mod )
32
  ->run();
33
 
34
+ if ( $action->usleep > 0 ) {
35
+ usleep( $action->usleep );
36
  }
37
 
38
+ $entry->results = $action->results;
39
+ return $entry;
 
40
  }
41
 
42
  /**
src/lib/src/Modules/HackGuard/Scan/Results/ConvertBetweenTypes.php CHANGED
@@ -15,49 +15,51 @@ class ConvertBetweenTypes {
15
  use ScanControllerConsumer;
16
 
17
  /**
18
- * @param Scans\Base\BaseResultsSet $oResultsSet
19
  * @return Databases\Scanner\EntryVO[]|mixed
20
  */
21
- public function fromResultsToVOs( $oResultsSet ) {
22
  $vos = [];
23
- foreach ( $oResultsSet->getAllItems() as $item ) {
24
- /** @var Scans\Base\BaseResultItem $item */
25
  $vos[ $item->generateHash() ] = $this->convertResultItemToVO( $item );
26
  }
27
  return $vos;
28
  }
29
 
30
  /**
31
- * @param Databases\Scanner\EntryVO[] $aVOs
32
- * @return Scans\Base\BaseResultsSet|mixed
33
  */
34
- public function fromVOsToResultsSet( $aVOs ) {
35
- $oRes = $this->getScanController()->getNewResultsSet();
36
- foreach ( $aVOs as $oVo ) {
37
- $oRes->addItem( $this->convertVoToResultItem( $oVo ) );
38
  }
39
- return $oRes;
40
  }
41
 
42
  /**
43
- * @param Databases\Scanner\EntryVO $oVo
44
- * @return Scans\Base\BaseResultItem
45
  */
46
- public function convertVoToResultItem( $oVo ) {
47
- return $this->getScanController()
48
- ->getNewResultItem()
49
- ->applyFromArray( $oVo->meta );
 
 
50
  }
51
 
52
  /**
53
- * @param Scans\Base\BaseResultItem $oIt
54
  * @return Databases\Scanner\EntryVO
55
  */
56
- private function convertResultItemToVO( $oIt ) {
57
- $oVo = new Databases\Scanner\EntryVO();
58
- $oVo->hash = $oIt->hash;
59
- $oVo->meta = $oIt->getData();
60
- $oVo->scan = $this->getScanController()->getSlug();
61
- return $oVo;
62
  }
63
- }
15
  use ScanControllerConsumer;
16
 
17
  /**
18
+ * @param Scans\Base\ResultsSet $resultsSet
19
  * @return Databases\Scanner\EntryVO[]|mixed
20
  */
21
+ public function fromResultsToVOs( $resultsSet ) {
22
  $vos = [];
23
+ foreach ( $resultsSet->getAllItems() as $item ) {
24
+ /** @var Scans\Base\ResultItem $item */
25
  $vos[ $item->generateHash() ] = $this->convertResultItemToVO( $item );
26
  }
27
  return $vos;
28
  }
29
 
30
  /**
31
+ * @param Databases\Scanner\EntryVO[] $VOs
32
+ * @return Scans\Base\ResultsSet|mixed
33
  */
34
+ public function fromVOsToResultsSet( $VOs ) {
35
+ $results = $this->getScanController()->getNewResultsSet();
36
+ foreach ( $VOs as $VO ) {
37
+ $results->addItem( $this->convertVoToResultItem( $VO ) );
38
  }
39
+ return $results;
40
  }
41
 
42
  /**
43
+ * @param Databases\Scanner\EntryVO $VO
44
+ * @return Scans\Base\ResultItem
45
  */
46
+ public function convertVoToResultItem( Databases\Scanner\EntryVO $VO ) {
47
+ $item = $this->getScanController()
48
+ ->getNewResultItem()
49
+ ->applyFromArray( $VO->meta );
50
+ $item->VO = $VO;
51
+ return $item;
52
  }
53
 
54
  /**
55
+ * @param Scans\Base\ResultItem $item
56
  * @return Databases\Scanner\EntryVO
57
  */
58
+ private function convertResultItemToVO( $item ) {
59
+ $vo = new Databases\Scanner\EntryVO();
60
+ $vo->hash = $item->hash;
61
+ $vo->meta = $item->getData();
62
+ $vo->scan = $this->getScanController()->getSlug();
63
+ return $vo;
64
  }
65
+ }
src/lib/src/Modules/HackGuard/Scan/Results/ResultsDelete.php CHANGED
@@ -15,13 +15,13 @@ class ResultsDelete {
15
  use ScanControllerConsumer;
16
 
17
  /**
18
- * @param Scans\Base\BaseResultsSet $oResultsToDelete
19
  * @return bool
20
  */
21
  public function delete( $oResultsToDelete ) {
22
  $aHashes = array_map(
23
  function ( $oItem ) {
24
- /** @var Scans\Base\BaseResultItem $oItem */
25
  return $oItem->hash;
26
  },
27
  $oResultsToDelete->getAllItems()
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()
src/lib/src/Modules/HackGuard/Scan/Results/ResultsRetrieve.php CHANGED
@@ -15,7 +15,7 @@ class ResultsRetrieve {
15
  use ScanControllerConsumer;
16
 
17
  /**
18
- * @return Scans\Base\BaseResultsSet|mixed
19
  */
20
  public function retrieve() {
21
  $oSCon = $this->getScanController();
15
  use ScanControllerConsumer;
16
 
17
  /**
18
+ * @return Scans\Base\ResultsSet|mixed
19
  */
20
  public function retrieve() {
21
  $oSCon = $this->getScanController();
src/lib/src/Modules/HackGuard/Scan/Results/ResultsStore.php CHANGED
@@ -15,17 +15,18 @@ class ResultsStore {
15
  use ScanControllerConsumer;
16
 
17
  /**
18
- * @param Scans\Base\BaseResultsSet $oToStore
19
  */
20
- public function store( $oToStore ) {
21
- $oSCon = $this->getScanController();
22
- $oInsert = $oSCon->getScanResultsDbHandler()
23
- ->getQueryInserter();
24
- $aVOs = ( new ConvertBetweenTypes() )
25
- ->setScanController( $oSCon )
26
- ->fromResultsToVOs( $oToStore );
27
- foreach ( $aVOs as $oVo ) {
28
- $oInsert->insert( $oVo );
 
29
  }
30
  }
31
  }
15
  use ScanControllerConsumer;
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
@@ -15,38 +15,39 @@ class ResultsUpdate {
15
  use ScanControllerConsumer;
16
 
17
  /**
18
- * @param Scans\Base\BaseResultsSet $oNewResults
19
  */
20
- public function update( $oNewResults ) {
21
- $oSCon = $this->getScanController();
22
- $oNewCopy = clone $oNewResults; // so we don't modify these for later use.
23
 
24
- $oExisting = ( new ResultsRetrieve() )
25
- ->setScanController( $oSCon )
26
  ->retrieve();
27
 
28
- $oItemsToDelete = ( new Scans\Base\DiffResultForStorage() )->diff( $oExisting, $oNewCopy );
 
29
  ( new ResultsDelete() )
30
- ->setScanController( $oSCon )
31
- ->delete( $oItemsToDelete );
32
 
33
  ( new ResultsStore() )
34
- ->setScanController( $oSCon )
35
- ->store( $oNewCopy );
36
-
37
- $oUp = $oSCon->getScanResultsDbHandler()->getQueryUpdater();
38
- /** @var Databases\Scanner\EntryVO $oVo */
39
- $oConverter = ( new ConvertBetweenTypes() )->setScanController( $oSCon );
40
- foreach ( $oConverter->fromResultsToVOs( $oExisting ) as $oVo ) {
41
- $oUp->reset()
42
- ->setUpdateData( $oVo->getRawData() )
43
- ->setUpdateWheres(
44
- [
45
- 'scan' => $oSCon->getSlug(),
46
- 'hash' => $oVo->hash,
47
- ]
48
- )
49
- ->query();
50
  }
51
  }
52
  }
15
  use ScanControllerConsumer;
16
 
17
  /**
18
+ * @param Scans\Base\ResultsSet $newResults
19
  */
20
+ public function update( $newResults ) {
21
+ $scanCon = $this->getScanController();
22
+ $newCopy = clone $newResults; // so we don't modify these for later use.
23
 
24
+ $existing = ( new ResultsRetrieve() )
25
+ ->setScanController( $scanCon )
26
  ->retrieve();
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/Scan/ScansController.php CHANGED
@@ -74,10 +74,10 @@ class ScansController extends ExecOnceModConsumer {
74
  $mod = $this->getMod();
75
  /** @var HackGuard\Options $opts */
76
  $opts = $this->getOptions();
77
- foreach ( $opts->getScanSlugs() as $sSlug ) {
78
- $oScanCon = $mod->getScanCon( $sSlug );
79
- if ( $oScanCon->isCronAutoRepair() ) {
80
- $oScanCon->runCronAutoRepair();
81
  }
82
  }
83
  }
74
  $mod = $this->getMod();
75
  /** @var HackGuard\Options $opts */
76
  $opts = $this->getOptions();
77
+ foreach ( $opts->getScanSlugs() as $slug ) {
78
+ $scanCon = $mod->getScanCon( $slug );
79
+ if ( $scanCon->isCronAutoRepair() ) {
80
+ $scanCon->runCronAutoRepair();
81
  }
82
  }
83
  }
src/lib/src/Modules/HackGuard/Scan/Utilities/WpvAddPluginRows.php CHANGED
@@ -33,7 +33,7 @@ class WpvAddPluginRows {
33
  private function isWpvulnPluginsHighlightEnabled() :bool {
34
  $scanCon = $this->getScanController();
35
  if ( $scanCon->isEnabled() ) {
36
- $opt = apply_filters( 'icwp_shield_wpvuln_scan_display', 'securityadmin' );
37
  }
38
  else {
39
  $opt = 'disabled';
33
  private function isWpvulnPluginsHighlightEnabled() :bool {
34
  $scanCon = $this->getScanController();
35
  if ( $scanCon->isEnabled() ) {
36
+ $opt = apply_filters( 'shield/wpvuln_scan_display', 'securityadmin' );
37
  }
38
  else {
39
  $opt = 'disabled';
src/lib/src/Modules/HackGuard/Strings.php CHANGED
@@ -43,6 +43,18 @@ class Strings extends Base\Strings {
43
  __( "These items wont display in results if you've previously marked them as ignored.", 'wp-simple-firewall' )
44
  )
45
  ];
 
 
 
 
 
 
 
 
 
 
 
 
46
  $messages[ $slug.'_item_repair_success' ] = [
47
  sprintf( __( '%s scan repaired a item found in the scan.', 'wp-simple-firewall' ), $scanName )
48
  .' '.__( 'Item repaired: "%s"', 'wp-simple-firewall' ),
@@ -242,7 +254,7 @@ class Strings extends Base\Strings {
242
  sprintf( '%s - %s', __( 'Important', 'wp-simple-firewall' ), __( "Doesn't currently detect missing files.", 'wp-simple-firewall' ) ),
243
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Keep this feature turned on, at all times.', 'wp-simple-firewall' ) )
244
  ];
245
- if ( !$mod->canCacheDirWrite() ) {
246
  $desc[] = sprintf( __( 'Sorry, this feature is not available because we cannot write to disk at this location: %s', 'wp-simple-firewall' ),
247
  '<code>'.$mod->getPtgSnapsBaseDir().'</code>' );
248
  }
@@ -331,22 +343,35 @@ class Strings extends Base\Strings {
331
  $desc = __( "Show links to re-install plugins and offer re-install when activating plugins.", 'wp-simple-firewall' );
332
  break;
333
 
334
- case 'enabled_scan_apc' :
335
- $name = __( 'Abandoned Plugin Scanner', 'wp-simple-firewall' );
336
- $summary = __( 'Enable The Abandoned Plugin Scanner', 'wp-simple-firewall' );
337
- $desc = __( "Scan your WordPress.org assets for whether they've been abandoned.", 'wp-simple-firewall' );
 
 
 
338
  break;
339
 
340
- case 'display_apc' :
341
- $name = __( 'Highlight Plugins', 'wp-simple-firewall' );
342
- $summary = __( 'Highlight Abandoned Plugins', 'wp-simple-firewall' );
343
- $desc = __( "Abandoned plugins will be highlighted on the main plugins page.", 'wp-simple-firewall' );
 
 
 
 
 
 
 
 
 
 
344
  break;
345
 
346
- case 'mal_autorepair_core' :
347
- $name = __( 'Auto-Repair WP Core', 'wp-simple-firewall' );
348
- $summary = __( 'Automatically Repair WordPress Core Files', 'wp-simple-firewall' );
349
- $desc = __( "Automatically reinstall any core files found to have potential malware.", 'wp-simple-firewall' );
350
  break;
351
 
352
  case 'mal_autorepair_surgical' :
@@ -357,61 +382,6 @@ class Strings extends Base\Strings {
357
  .'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( "Only applies to files that don't fall under the other categories for automatic repair.", 'wp-simple-firewall' ) );
358
  break;
359
 
360
- // REMOVED:
361
- case 'mal_autorepair_plugins' :
362
- $name = __( 'Auto-Repair WP Plugins', 'wp-simple-firewall' );
363
- $summary = __( 'Automatically Repair WordPress.org Plugins', 'wp-simple-firewall' );
364
- $desc = __( "Automatically repair any plugin files found to have potential malware.", 'wp-simple-firewall' )
365
- .'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( 'Only compatible with plugins installed from WordPress.org.', 'wp-simple-firewall' ) )
366
- .'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( "Also deletes suspected files if they weren't originally distributed with the plugin.", 'wp-simple-firewall' ) );
367
- break;
368
- case 'autorepair_themes' :
369
- $name = __( 'Auto-Repair WP Themes', 'wp-simple-firewall' );
370
- $summary = __( 'Automatically Repair WordPress.org Themes', 'wp-simple-firewall' );
371
- $desc = __( "Automatically repair any theme files found to have potential malware.", 'wp-simple-firewall' )
372
- .'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( 'Only compatible with themes installed from WordPress.org.', 'wp-simple-firewall' ) )
373
- .'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( "Also deletes suspected files if they weren't originally distributed with the theme.", 'wp-simple-firewall' ) );
374
- break;
375
- case 'wpvuln_scan_display' :
376
- $name = __( 'Highlight Plugins', 'wp-simple-firewall' );
377
- $summary = __( 'Highlight Vulnerable Plugins Upon Display', 'wp-simple-firewall' );
378
- $desc = __( 'Vulnerable plugins will be highlighted on the main plugins page.', 'wp-simple-firewall' );
379
- break;
380
- case 'email_files_list' :
381
- $name = __( 'Email Files List', 'wp-simple-firewall' );
382
- $summary = __( 'Scan Notification Emails Should Include Full Listing Of Files', 'wp-simple-firewall' );
383
- $desc = __( 'Scanner notification emails will include a summary list of all affected files.', 'wp-simple-firewall' );
384
- break;
385
- case 'mal_fp_confidence' :
386
- $name = __( 'Ignore False Positives Threshold', 'wp-simple-firewall' );
387
- $summary = __( 'Ignore False Positives In Scan Results Automatically', 'wp-simple-firewall' );
388
- $desc = __( "You can choose to ignore files with potential malware, depending on whether the confidence that it's a 'false positive' meets your minimum threshold.", 'wp-simple-firewall' )
389
- .'<br />'.__( "A false positive happens when a file appears to contain malware and shows up in scan results, but it's actually clean.", 'wp-simple-firewall' )
390
- .' ('.__( "A false positive is similar to when an anti-virus alerts to a file that doesnt have a virus.", 'wp-simple-firewall' ).')'
391
- .'<br />'.__( "The higher the confidence level, the more likely a result is a false positive.", 'wp-simple-firewall' )
392
- .' '.__( "A low level means it's less likely to be a false positive.", 'wp-simple-firewall' )
393
- .'<br />'.__( "The scan will automatically ignore results whose 'false positive' confidence level is greater than your chosen threshold.", 'wp-simple-firewall' )
394
- .'<br />'.__( "The higher the confidence threshold you select, the more likely that 'false positives' will appears in your scan results.", 'wp-simple-firewall' )
395
- .'<br />'.__( "Disabling network intelligence turns off 'false positive confidence' levels.", 'wp-simple-firewall' )
396
- .' '.__( 'You will no longer benefit from the intelligence gathered from the entire network.', 'wp-simple-firewall' )
397
- .' '.__( 'All data shared is completely anonymous.', 'wp-simple-firewall' )
398
- .' '.' [<a href="https://shsec.io/moreinfomalnetwork">'.__( 'More Info', 'wp-simple-firewall' ).'</a>]'
399
- .'<br />'.__( 'The more sites that share this information, the stronger and smarter the network becomes.', 'wp-simple-firewall' );
400
- break;
401
- case 'notification_interval' :
402
- $name = __( 'Repeat Notifications', 'wp-simple-firewall' );
403
- $summary = __( 'Item Repeat Notifications Suppression Interval', 'wp-simple-firewall' );
404
- $desc = __( 'How long the automated scans should wait before repeating a notification about an item.', 'wp-simple-firewall' )
405
- .'<br/>'.__( 'Specify the number of days to suppress repeat notifications.', 'wp-simple-firewall' )
406
- .'<br/>'.sprintf( '%s: %s', __( 'Note', 'wp-simple-firewall' ), __( 'This is per discovered item or file, not per scan.', 'wp-simple-firewall' ) );
407
- break;
408
- case 'ptg_extensions' :
409
- $name = __( 'Included File Types', 'wp-simple-firewall' );
410
- $summary = __( 'The File Types (by File Extension) Included In The Scan', 'wp-simple-firewall' );
411
- $desc = __( 'Take a new line for each file extension.', 'wp-simple-firewall' )
412
- .'<br/>'.__( 'No commas(,) or periods(.) necessary.', 'wp-simple-firewall' )
413
- .'<br/>'.__( 'Remove all extensions to scan all file type (not recommended).', 'wp-simple-firewall' );
414
- break;
415
  default:
416
  return parent::getOptionStrings( $key );
417
  }
43
  __( "These items wont display in results if you've previously marked them as ignored.", 'wp-simple-firewall' )
44
  )
45
  ];
46
+ $messages[ 'scan_item_delete_success' ] = [
47
+ __( 'Deleted item found in the scan.', 'wp-simple-firewall' )
48
+ .' '.__( 'Item deleted: "%s"', 'wp-simple-firewall' ),
49
+ ];
50
+ $messages[ 'scan_item_repair_success' ] = [
51
+ __( 'Repaired item found in the scan.', 'wp-simple-firewall' )
52
+ .' '.__( 'Item repaired: "%s"', 'wp-simple-firewall' ),
53
+ ];
54
+ $messages[ 'scan_item_repair_fail' ] = [
55
+ __( 'Failed to repair scan item.', 'wp-simple-firewall' )
56
+ .' '.__( 'Failed item: "%s"', 'wp-simple-firewall' ),
57
+ ];
58
  $messages[ $slug.'_item_repair_success' ] = [
59
  sprintf( __( '%s scan repaired a item found in the scan.', 'wp-simple-firewall' ), $scanName )
60
  .' '.__( 'Item repaired: "%s"', 'wp-simple-firewall' ),
254
  sprintf( '%s - %s', __( 'Important', 'wp-simple-firewall' ), __( "Doesn't currently detect missing files.", 'wp-simple-firewall' ) ),
255
  sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Keep this feature turned on, at all times.', 'wp-simple-firewall' ) )
256
  ];
257
+ if ( !$this->getCon()->hasCacheDir() ) {
258
  $desc[] = sprintf( __( 'Sorry, this feature is not available because we cannot write to disk at this location: %s', 'wp-simple-firewall' ),
259
  '<code>'.$mod->getPtgSnapsBaseDir().'</code>' );
260
  }
343
  $desc = __( "Show links to re-install plugins and offer re-install when activating plugins.", 'wp-simple-firewall' );
344
  break;
345
 
346
+ case 'auto_filter_results' :
347
+ $name = __( 'Auto-Filter Results', 'wp-simple-firewall' );
348
+ $summary = __( 'Automatically Filter Results Of Irrelevant Items', 'wp-simple-firewall' );
349
+ $desc = [
350
+ __( 'Automatically remove items from results that are irrelevant.', 'wp-simple-firewall' ),
351
+ __( "An example of this is filtering out results for PHP files that don't have any executable code.", 'wp-simple-firewall' ),
352
+ ];
353
  break;
354
 
355
+ case 'scan_path_exclusions' :
356
+ $name = __( 'Scan Exclusions', 'wp-simple-firewall' );
357
+ $summary = __( 'Scan File And Folder Exclusions', 'wp-simple-firewall' );
358
+ $desc = [
359
+ __( 'A list of file/folder paths that will never be scanned.', 'wp-simple-firewall' ),
360
+ __( 'All paths are relative to your WordPress installation directory.', 'wp-simple-firewall' ),
361
+ __( 'This is an advanced option and should be used with great care.', 'wp-simple-firewall' ),
362
+ __( 'Take a new line for each whitelisted path.', 'wp-simple-firewall' ),
363
+ __( 'All characters will be treated as case-insensitive.', 'wp-simple-firewall' ),
364
+ __( 'Directories should be provided with a trailing slash (/).', 'wp-simple-firewall' ),
365
+ __( "If a path matches any core WordPress directories, it'll be removed automatically.", 'wp-simple-firewall' ),
366
+ __( "These aren't regular expression, but you can use asterisk (*) as a wildcard.", 'wp-simple-firewall' ),
367
+ sprintf( '%s: <code>%s</code>', __( 'WordPress Installation Directory', 'wp-simple-firewall' ), ABSPATH ),
368
+ ];
369
  break;
370
 
371
+ case 'enabled_scan_apc' :
372
+ $name = __( 'Abandoned Plugin Scanner', 'wp-simple-firewall' );
373
+ $summary = __( 'Enable The Abandoned Plugin Scanner', 'wp-simple-firewall' );
374
+ $desc = __( "Scan your WordPress.org assets for whether they've been abandoned.", 'wp-simple-firewall' );
375
  break;
376
 
377
  case 'mal_autorepair_surgical' :
382
  .'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( "Only applies to files that don't fall under the other categories for automatic repair.", 'wp-simple-firewall' ) );
383
  break;
384
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
  default:
386
  return parent::getOptionStrings( $key );
387
  }
src/lib/src/Modules/HackGuard/UI.php CHANGED
@@ -19,6 +19,16 @@ class UI extends BaseShield\UI {
19
  $uiTrack[ 'selected_scans' ] = $opts->getScanSlugs();
20
  }
21
 
 
 
 
 
 
 
 
 
 
 
22
  // Can Scan Checks:
23
  $reasonsCantScan = $mod->getScansCon()->getReasonsScansCantExecute();
24
 
@@ -72,31 +82,37 @@ class UI extends BaseShield\UI {
72
  'vars' => [
73
  'initial_check' => $mod->getScanQueueController()->hasRunningScans(),
74
  'cannot_scan_reasons' => $reasonsCantScan,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  ],
76
  'hrefs' => [
77
- 'scanner_mod_config' => $mod->getUrl_DirectLinkToSection('section_enable_plugin_feature_hack_protection_tools'),
78
  'scans_results' => $this->getCon()
79
  ->getModule_Insights()
80
  ->getUrl_ScansResults(),
81
  ],
82
- 'scan_results' => [
 
 
 
 
 
 
 
83
  ],
84
- 'aggregate' => [
85
- 'flags' => [
86
- 'has_items' => true,
87
- ],
88
- 'hrefs' => [
89
- 'options' => $mod->getUrl_DirectLinkToSection( 'section_scan_options' )
90
- ],
91
- 'vars' => [
92
- ],
93
- 'strings' => [
94
- 'title' => __( 'File Scan', 'wp-simple-firewall' ),
95
- 'subtitle' => __( "Results of all file scans", 'wp-simple-firewall' )
96
- ],
97
- 'count' => $selector->filterByScans( [ 'ptg', 'mal', 'wcf', 'ufc' ] )
98
- ->filterByNotIgnored()
99
- ->count()
100
  ],
101
  'file_locker' => $this->getFileLockerVars(),
102
  'scans' => [
@@ -121,7 +137,7 @@ class UI extends BaseShield\UI {
121
  'apc' => [
122
  'flags' => [
123
  'has_items' => true,
124
- 'show_table' => true,
125
  ],
126
  'hrefs' => [],
127
  'vars' => [],
@@ -155,7 +171,7 @@ class UI extends BaseShield\UI {
155
  'wpv' => [
156
  'flags' => [
157
  'has_items' => true,
158
- 'show_table' => true,
159
  ],
160
  'hrefs' => [],
161
  'vars' => [],
@@ -213,17 +229,14 @@ class UI extends BaseShield\UI {
213
  return $aOptParams;
214
  }
215
 
216
- /**
217
- * @return array
218
- */
219
- protected function getFileLockerVars() {
220
  /** @var ModCon $mod */
221
  $mod = $this->getMod();
222
 
223
- $oLockCon = $mod->getFileLocker();
224
- $oLockLoader = ( new Lib\FileLocker\Ops\LoadFileLocks() )->setMod( $mod );
225
- $aProblemLocks = $oLockLoader->withProblems();
226
- $aGoodLocks = $oLockLoader->withoutProblems();
227
 
228
  return [
229
  'ajax' => [
@@ -231,7 +244,7 @@ class UI extends BaseShield\UI {
231
  'filelocker_fileaction' => $mod->getAjaxActionData( 'filelocker_fileaction', true ),
232
  ],
233
  'flags' => [
234
- 'is_enabled' => $oLockCon->isEnabled(),
235
  'is_restricted' => !$this->getCon()->isPremiumActive(),
236
  ],
237
  'hrefs' => [
@@ -240,8 +253,9 @@ class UI extends BaseShield\UI {
240
  ],
241
  'vars' => [
242
  'file_locks' => [
243
- 'good' => $aGoodLocks,
244
- 'bad' => $aProblemLocks,
 
245
  ],
246
  ],
247
  'strings' => [
@@ -249,7 +263,7 @@ class UI extends BaseShield\UI {
249
  'subtitle' => __( 'Results of file locker monitoring', 'wp-simple-firewall' ),
250
  'please_select' => __( 'Please select a file to review.', 'wp-simple-firewall' ),
251
  ],
252
- 'count' => count( $aProblemLocks )
253
  ];
254
  }
255
 
@@ -267,7 +281,7 @@ class UI extends BaseShield\UI {
267
 
268
  return [
269
  'flags' => [
270
- 'has_items' => $mod->isPtgEnabled() ? !empty( $aPtgResults ) : false,
271
  'has_plugins' => !empty( $aPlugins ),
272
  'has_themes' => !empty( $aThemes ),
273
  'show_table' => false,
@@ -294,12 +308,15 @@ class UI extends BaseShield\UI {
294
 
295
  case 'section_realtime':
296
  $canHandshake = $this->getCon()
297
- ->getModule_Plugin()
298
- ->getShieldNetApiController()
299
- ->canHandshake();
300
  if ( !$canHandshake ) {
301
  $warnings[] = sprintf( __( 'Not available as your site cannot handshake with ShieldNET API.', 'wp-simple-firewall' ), 'OpenSSL' );
302
  }
 
 
 
303
  // if ( !Services::Encrypt()->isSupportedOpenSslDataEncryption() ) {
304
  // $warnings[] = sprintf( __( 'Not available because the %s extension is not available.', 'wp-simple-firewall' ), 'OpenSSL' );
305
  // }
19
  $uiTrack[ 'selected_scans' ] = $opts->getScanSlugs();
20
  }
21
 
22
+ foreach ( $opts->getScanSlugs() as $scan ) {
23
+ $mod->getScanCon( $scan )->cleanStalesResults();
24
+ }
25
+
26
+ $sectionBuilderPlugins = ( new Render\ScanResults\SectionPlugins() )->setMod( $this->getMod() );
27
+ $sectionBuilderThemes = ( new Render\ScanResults\SectionThemes() )->setMod( $this->getMod() );
28
+ $sectionBuilderWordpress = ( new Render\ScanResults\SectionWordpress() )->setMod( $this->getMod() );
29
+ $sectionBuilderMalware = ( new Render\ScanResults\SectionMalware() )->setMod( $this->getMod() );
30
+ // $sectionBuilderLog = ( new Render\ScanResults\SectionMalware() )->setMod( $this->getMod() );
31
+
32
  // Can Scan Checks:
33
  $reasonsCantScan = $mod->getScansCon()->getReasonsScansCantExecute();
34
 
82
  'vars' => [
83
  'initial_check' => $mod->getScanQueueController()->hasRunningScans(),
84
  'cannot_scan_reasons' => $reasonsCantScan,
85
+ 'sections' => [
86
+ 'plugins' => [
87
+ 'count' => $sectionBuilderPlugins->getRenderData()[ 'vars' ][ 'count_items' ]
88
+ ],
89
+ 'themes' => [
90
+ 'count' => $sectionBuilderThemes->getRenderData()[ 'vars' ][ 'count_items' ]
91
+ ],
92
+ 'wordpress' => [
93
+ 'count' => $sectionBuilderWordpress->getRenderData()[ 'vars' ][ 'count_items' ]
94
+ ],
95
+ 'malware' => [
96
+ 'count' => $sectionBuilderMalware->getRenderData()[ 'vars' ][ 'count_items' ]
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(),
110
+ 'wordpress' => $sectionBuilderWordpress->render(),
111
+ 'malware' => $sectionBuilderMalware->render(),
112
+ 'logs' => 'logs todo',
113
+ ]
114
  ],
115
+ 'scan_results' => [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  ],
117
  'file_locker' => $this->getFileLockerVars(),
118
  'scans' => [
137
  'apc' => [
138
  'flags' => [
139
  'has_items' => true,
140
+ 'show_table' => false,
141
  ],
142
  'hrefs' => [],
143
  'vars' => [],
171
  'wpv' => [
172
  'flags' => [
173
  'has_items' => true,
174
+ 'show_table' => false,
175
  ],
176
  'hrefs' => [],
177
  'vars' => [],
229
  return $aOptParams;
230
  }
231
 
232
+ protected function getFileLockerVars() :array {
 
 
 
233
  /** @var ModCon $mod */
234
  $mod = $this->getMod();
235
 
236
+ $lockerCon = $mod->getFileLocker();
237
+ $lockLoader = ( new Lib\FileLocker\Ops\LoadFileLocks() )->setMod( $mod );
238
+ $problemLocks = $lockLoader->withProblems();
239
+ $goodLocks = $lockLoader->withoutProblems();
240
 
241
  return [
242
  'ajax' => [
244
  'filelocker_fileaction' => $mod->getAjaxActionData( 'filelocker_fileaction', true ),
245
  ],
246
  'flags' => [
247
+ 'is_enabled' => $lockerCon->isEnabled(),
248
  'is_restricted' => !$this->getCon()->isPremiumActive(),
249
  ],
250
  'hrefs' => [
253
  ],
254
  'vars' => [
255
  'file_locks' => [
256
+ 'good' => $goodLocks,
257
+ 'bad' => $problemLocks,
258
+ 'count_items' => count( $problemLocks ),
259
  ],
260
  ],
261
  'strings' => [
263
  'subtitle' => __( 'Results of file locker monitoring', 'wp-simple-firewall' ),
264
  'please_select' => __( 'Please select a file to review.', 'wp-simple-firewall' ),
265
  ],
266
+ 'count' => count( $problemLocks )
267
  ];
268
  }
269
 
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,
308
 
309
  case 'section_realtime':
310
  $canHandshake = $this->getCon()
311
+ ->getModule_Plugin()
312
+ ->getShieldNetApiController()
313
+ ->canHandshake();
314
  if ( !$canHandshake ) {
315
  $warnings[] = sprintf( __( 'Not available as your site cannot handshake with ShieldNET API.', 'wp-simple-firewall' ), 'OpenSSL' );
316
  }
317
+ if ( !$this->getCon()->hasCacheDir() ) {
318
+ $warnings[] = __( "Certain scanners are unavailable because we couldn't create a temporary directory to store files.", 'wp-simple-firewall' );
319
+ }
320
  // if ( !Services::Encrypt()->isSupportedOpenSslDataEncryption() ) {
321
  // $warnings[] = sprintf( __( 'Not available because the %s extension is not available.', 'wp-simple-firewall' ), 'OpenSSL' );
322
  // }
src/lib/src/Modules/HackGuard/Upgrade.php CHANGED
@@ -16,48 +16,4 @@ class Upgrade extends Base\Upgrade {
16
  $schema->table, 'content', $schema->enumerateColumns()[ 'content' ] )
17
  );
18
  }
19
-
20
- protected function upgrade_900() {
21
- /** @var Options $opts */
22
- $opts = $this->getOptions();
23
- if ( $opts->getOpt( 'ptg_enable' ) === 'enabled' ) {
24
- $opts->setOpt( 'ptg_enable', 'Y' );
25
- }
26
- elseif ( $opts->getOpt( 'ptg_enable' ) === 'disabled' ) {
27
- $opts->setOpt( 'ptg_enable', 'N' );
28
- }
29
-
30
- $aRepairAreas = $opts->getRepairAreas();
31
- $aMap = [
32
- 'attempt_auto_file_repair' => 'wp',
33
- 'mal_autorepair_plugins' => 'plugin',
34
- ];
35
- foreach ( $aMap as $sOld => $sNew ) {
36
- if ( $opts->getOpt( $sOld ) !== false ) {
37
- $bWasEnabled = $opts->isOpt( $sOld, 'Y' );
38
- $nIsEnabled = array_search( $sNew, $aRepairAreas );
39
- if ( $bWasEnabled && ( $nIsEnabled === false ) ) {
40
- $aRepairAreas[] = $sNew;
41
- }
42
- elseif ( !$bWasEnabled && ( $nIsEnabled !== false ) ) {
43
- unset( $aRepairAreas[ $nIsEnabled ] );
44
- }
45
- }
46
- }
47
- $opts->setOpt( 'file_repair_areas', $aRepairAreas );
48
-
49
- { // migrate old scan options
50
- if ( $opts->getOpt( 'enable_unrecognised_file_cleaner_scan' ) == 'enabled_delete_report' ) {
51
- $opts->setOpt( 'enable_unrecognised_file_cleaner_scan', 'enabled_delete_only' );
52
- }
53
- $sApcOpt = $opts->getOpt( 'enabled_scan_apc' );
54
- if ( strlen( $sApcOpt ) > 1 ) {
55
- $opts->setOpt( 'enabled_scan_apc', $sApcOpt == 'disabled' ? 'N' : 'Y' );
56
- }
57
- $sWpvOpt = $opts->getOpt( 'enable_wpvuln_scan' );
58
- if ( strlen( $sWpvOpt ) > 1 ) {
59
- $opts->setOpt( 'enable_wpvuln_scan', $sWpvOpt == 'disabled' ? 'N' : 'Y' );
60
- }
61
- }
62
- }
63
  }
16
  $schema->table, 'content', $schema->enumerateColumns()[ 'content' ] )
17
  );
18
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  }
src/lib/src/Modules/IPs/AjaxHandler.php CHANGED
@@ -70,48 +70,48 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
70
  private function ajaxExec_AddIp() :array {
71
  /** @var ModCon $mod */
72
  $mod = $this->getMod();
73
- $oIpServ = Services::IP();
74
 
75
  $formParams = FormParams::Retrieve();
76
 
77
  $success = false;
78
  $msg = __( "IP address wasn't added to the list", 'wp-simple-firewall' );
79
 
80
- $ip = preg_replace( '#[^/:.a-f\d]#i', '', ( isset( $formParams[ 'ip' ] ) ? $formParams[ 'ip' ] : '' ) );
81
- $sList = isset( $formParams[ 'list' ] ) ? $formParams[ 'list' ] : '';
82
 
83
- $bAcceptableIp = $oIpServ->isValidIp( $ip )
84
- || $oIpServ->isValidIp4Range( $ip )
85
- || $oIpServ->isValidIp6Range( $ip );
86
 
87
- $bIsBlackList = $sList != $mod::LIST_MANUAL_WHITE;
88
 
89
  // TODO: Bring this IP verification out of here and make it more accessible
90
  if ( empty( $ip ) ) {
91
  $msg = __( "IP address not provided", 'wp-simple-firewall' );
92
  }
93
- elseif ( empty( $sList ) ) {
94
  $msg = __( "IP list not provided", 'wp-simple-firewall' );
95
  }
96
- elseif ( !$bAcceptableIp ) {
97
  $msg = __( "IP address isn't either a valid IP or a CIDR range", 'wp-simple-firewall' );
98
  }
99
- elseif ( $bIsBlackList && !$mod->isPremium() ) {
100
  $msg = __( "Please upgrade to Pro if you'd like to add IPs to the black list manually.", 'wp-simple-firewall' );
101
  }
102
- elseif ( $bIsBlackList && $oIpServ->checkIp( $oIpServ->getRequestIp(), $ip ) ) {
103
  $msg = __( "Manually black listing your current IP address is not supported.", 'wp-simple-firewall' );
104
  }
105
- elseif ( $bIsBlackList && in_array( $ip, Services::IP()->getServerPublicIPs() ) ) {
106
  $msg = __( "This IP is reserved and can't be blacklisted.", 'wp-simple-firewall' );
107
  }
108
  else {
109
  $label = $formParams[ 'label' ] ?? '';
110
- $oIP = null;
111
- switch ( $sList ) {
112
  case $mod::LIST_MANUAL_WHITE:
113
  try {
114
- $oIP = ( new Shield\Modules\IPs\Lib\Ops\AddIp() )
115
  ->setMod( $mod )
116
  ->setIP( $ip )
117
  ->toManualWhitelist( (string)$label );
@@ -122,7 +122,7 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
122
 
123
  case $mod::LIST_MANUAL_BLACK:
124
  try {
125
- $oIP = ( new Shield\Modules\IPs\Lib\Ops\AddIp() )
126
  ->setMod( $mod )
127
  ->setIP( $ip )
128
  ->toManualBlacklist( (string)$label );
@@ -135,7 +135,7 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
135
  break;
136
  }
137
 
138
- if ( !empty( $oIP ) ) {
139
  $msg = __( 'IP address added successfully', 'wp-simple-firewall' );
140
  $success = true;
141
  }
70
  private function ajaxExec_AddIp() :array {
71
  /** @var ModCon $mod */
72
  $mod = $this->getMod();
73
+ $srvIP = Services::IP();
74
 
75
  $formParams = FormParams::Retrieve();
76
 
77
  $success = false;
78
  $msg = __( "IP address wasn't added to the list", 'wp-simple-firewall' );
79
 
80
+ $ip = preg_replace( '#[^/:.a-f\d]#i', '', ( $formParams[ 'ip' ] ?? '' ) );
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
 
89
  // TODO: Bring this IP verification out of here and make it more accessible
90
  if ( empty( $ip ) ) {
91
  $msg = __( "IP address not provided", 'wp-simple-firewall' );
92
  }
93
+ elseif ( empty( $list ) ) {
94
  $msg = __( "IP list not provided", 'wp-simple-firewall' );
95
  }
96
+ elseif ( !$acceptableIP ) {
97
  $msg = __( "IP address isn't either a valid IP or a CIDR range", 'wp-simple-firewall' );
98
  }
99
+ elseif ( $isBlackList && !$mod->isPremium() ) {
100
  $msg = __( "Please upgrade to Pro if you'd like to add IPs to the black list manually.", 'wp-simple-firewall' );
101
  }
102
+ elseif ( $isBlackList && $srvIP->checkIp( $srvIP->getRequestIp(), $ip ) ) {
103
  $msg = __( "Manually black listing your current IP address is not supported.", 'wp-simple-firewall' );
104
  }
105
+ elseif ( $isBlackList && in_array( $ip, Services::IP()->getServerPublicIPs() ) ) {
106
  $msg = __( "This IP is reserved and can't be blacklisted.", 'wp-simple-firewall' );
107
  }
108
  else {
109
  $label = $formParams[ 'label' ] ?? '';
110
+ $IP = null;
111
+ switch ( $list ) {
112
  case $mod::LIST_MANUAL_WHITE:
113
  try {
114
+ $IP = ( new Shield\Modules\IPs\Lib\Ops\AddIp() )
115
  ->setMod( $mod )
116
  ->setIP( $ip )
117
  ->toManualWhitelist( (string)$label );
122
 
123
  case $mod::LIST_MANUAL_BLACK:
124
  try {
125
+ $IP = ( new Shield\Modules\IPs\Lib\Ops\AddIp() )
126
  ->setMod( $mod )
127
  ->setIP( $ip )
128
  ->toManualBlacklist( (string)$label );
135
  break;
136
  }
137
 
138
+ if ( !empty( $IP ) ) {
139
  $msg = __( 'IP address added successfully', 'wp-simple-firewall' );
140
  $success = true;
141
  }
src/lib/src/Modules/IPs/Components/UnblockIpByFlag.php CHANGED
@@ -20,13 +20,13 @@ class UnblockIpByFlag {
20
  $content = $FS->getFileContent( $path );
21
  if ( !empty( $content ) ) {
22
 
23
- foreach ( array_map( 'trim', explode( "\n", $content ) ) as $sIp ) {
24
  $removed = ( new IPs\Lib\Ops\DeleteIp() )
25
  ->setMod( $mod )
26
- ->setIP( $sIp )
27
  ->fromBlacklist();
28
  if ( $removed ) {
29
- $this->getCon()->fireEvent( 'ip_unblock_flag', [ 'audit' => [ 'ip' => $sIp ] ] );
30
  }
31
  }
32
  }
20
  $content = $FS->getFileContent( $path );
21
  if ( !empty( $content ) ) {
22
 
23
+ foreach ( array_map( 'trim', explode( "\n", $content ) ) as $ip ) {
24
  $removed = ( new IPs\Lib\Ops\DeleteIp() )
25
  ->setMod( $mod )
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/Lib/BlacklistHandler.php CHANGED
@@ -44,12 +44,6 @@ class BlacklistHandler extends Modules\Base\Common\ExecOnceModConsumer {
44
  }
45
  }
46
 
47
- /**
48
- * @deprecated 11.3
49
- */
50
- public function loadBotDetectors() {
51
- }
52
-
53
  private function isRequestWhitelisted() :bool {
54
  /** @var IPs\Options $opts */
55
  $opts = $this->getOptions();
44
  }
45
  }
46
 
 
 
 
 
 
 
47
  private function isRequestWhitelisted() :bool {
48
  /** @var IPs\Options $opts */
49
  $opts = $this->getOptions();
src/lib/src/Modules/IPs/Lib/Bots/BotEventListener.php CHANGED
@@ -66,7 +66,7 @@ class BotEventListener extends ExecOnceModConsumer {
66
  'ip_offense' => 'offense',
67
  'ip_blocked' => 'blocked',
68
  'ip_unblock' => 'unblocked',
69
- 'ip_bypass' => 'bypass',
70
  'login_success' => 'auth',
71
  ]
72
  );
66
  'ip_offense' => 'offense',
67
  'ip_blocked' => 'blocked',
68
  'ip_unblock' => 'unblocked',
69
+ 'ip_bypass_add' => 'bypass',
70
  'login_success' => 'auth',
71
  ]
72
  );
src/lib/src/Modules/IPs/Lib/Ops/AddIp.php CHANGED
@@ -40,6 +40,9 @@ class AddIp {
40
  ->lookup( false );
41
  if ( !$IP instanceof Databases\IPs\EntryVO ) {
42
  $IP = $this->add( $mod::LIST_AUTO_BLACK, 'auto', $req->ts() );
 
 
 
43
  }
44
 
45
  // Edge-case: the IP is on the list but the last access long-enough passed
@@ -59,38 +62,41 @@ class AddIp {
59
  }
60
 
61
  /**
62
- * @param string $sLabel
63
  * @return Databases\IPs\EntryVO|null
64
  * @throws \Exception
65
  */
66
- public function toManualBlacklist( $sLabel = '' ) {
67
  /** @var ModCon $mod */
68
  $mod = $this->getMod();
69
- $oIpServ = Services::IP();
70
 
71
- $sIP = $this->getIP();
72
- if ( !$oIpServ->isValidIp( $sIP ) && !$oIpServ->isValidIpRange( $sIP ) ) {
73
  throw new \Exception( "IP address isn't valid." );
74
  }
75
 
76
  $IP = null;
77
- if ( !in_array( $sIP, $oIpServ->getServerPublicIPs() ) ) {
78
 
79
- if ( $oIpServ->isValidIpRange( $sIP ) ) {
80
  ( new DeleteIp() )
81
  ->setMod( $mod )
82
- ->setIP( $sIP )
83
  ->fromBlacklist();
84
  }
85
 
86
  $IP = ( new LookupIpOnList() )
87
  ->setDbHandler( $mod->getDbHandler_IPs() )
88
  ->setListTypeBlock()
89
- ->setIP( $sIP )
90
  ->lookup( false );
91
 
92
  if ( !$IP instanceof Databases\IPs\EntryVO ) {
93
- $IP = $this->add( $mod::LIST_MANUAL_BLACK, $sLabel );
 
 
 
94
  }
95
 
96
  $updateData = [
@@ -100,8 +106,8 @@ class AddIp {
100
  if ( $IP->list != $mod::LIST_MANUAL_BLACK ) {
101
  $updateData[ 'list' ] = $mod::LIST_MANUAL_BLACK;
102
  }
103
- if ( $IP->label != $sLabel ) {
104
- $updateData[ 'label' ] = $sLabel;
105
  }
106
  if ( $IP->blocked_at == 0 ) {
107
  $updateData[ 'blocked_at' ] = Services::Request()->ts();
@@ -123,14 +129,14 @@ class AddIp {
123
  public function toManualWhitelist( $label = '' ) {
124
  /** @var ModCon $mod */
125
  $mod = $this->getMod();
126
- $oIpServ = Services::IP();
127
 
128
  $ip = $this->getIP();
129
- if ( !$oIpServ->isValidIp( $ip ) && !$oIpServ->isValidIpRange( $ip ) ) {
130
  throw new \Exception( "IP address isn't valid." );
131
  }
132
 
133
- if ( $oIpServ->isValidIpRange( $ip ) ) {
134
  ( new DeleteIp() )
135
  ->setMod( $mod )
136
  ->setIP( $ip )
@@ -142,8 +148,10 @@ class AddIp {
142
  ->setIP( $this->getIP() )
143
  ->lookup( false );
144
  if ( !$IP instanceof Databases\IPs\EntryVO ) {
145
- $this->getCon()->fireEvent( 'ip_bypass' );
146
  $IP = $this->add( $mod::LIST_MANUAL_WHITE, $label );
 
 
 
147
  }
148
 
149
  $updateData = [];
@@ -171,40 +179,40 @@ class AddIp {
171
 
172
  /**
173
  * @param string $list
174
- * @param string $sLabel
175
- * @param int|null $nLastAccessAt
176
  * @return Databases\IPs\EntryVO|null
177
  * @throws \Exception
178
  */
179
- private function add( string $list, $sLabel = '', $nLastAccessAt = null ) {
180
- $oIP = null;
181
 
182
  /** @var ModCon $mod */
183
  $mod = $this->getMod();
184
 
185
  // Never add a reserved IP to any black list
186
- $oDbh = $mod->getDbHandler_IPs();
187
 
188
- /** @var Databases\IPs\EntryVO $oTempIp */
189
- $oTempIp = $oDbh->getVo();
190
- $oTempIp->ip = $this->getIP();
191
- $oTempIp->list = $list;
192
- $oTempIp->label = empty( $sLabel ) ? __( 'No Label', 'wp-simple-firewall' ) : trim( $sLabel );
193
- if ( is_numeric( $nLastAccessAt ) && $nLastAccessAt > 0 ) {
194
- $oTempIp->last_access_at = $nLastAccessAt;
195
  }
196
 
197
- if ( $oDbh->getQueryInserter()->insert( $oTempIp ) ) {
198
- /** @var Databases\IPs\EntryVO $oIP */
199
- $oIP = $oDbh->getQuerySelector()
200
- ->setWheresFromVo( $oTempIp )
201
- ->first();
202
  }
203
 
204
- if ( !$oIP instanceof Databases\IPs\EntryVO ) {
205
  throw new \Exception( "IP couldn't be added to the database." );
206
  }
207
 
208
- return $oIP;
209
  }
210
  }
40
  ->lookup( false );
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
 
48
  // Edge-case: the IP is on the list but the last access long-enough passed
62
  }
63
 
64
  /**
65
+ * @param string $label
66
  * @return Databases\IPs\EntryVO|null
67
  * @throws \Exception
68
  */
69
+ public function toManualBlacklist( $label = '' ) {
70
  /** @var ModCon $mod */
71
  $mod = $this->getMod();
72
+ $srvIP = Services::IP();
73
 
74
+ $ip = $this->getIP();
75
+ if ( !$srvIP->isValidIp( $ip ) && !$srvIP->isValidIpRange( $ip ) ) {
76
  throw new \Exception( "IP address isn't valid." );
77
  }
78
 
79
  $IP = null;
80
+ if ( !in_array( $ip, $srvIP->getServerPublicIPs() ) ) {
81
 
82
+ if ( $srvIP->isValidIpRange( $ip ) ) {
83
  ( new DeleteIp() )
84
  ->setMod( $mod )
85
+ ->setIP( $ip )
86
  ->fromBlacklist();
87
  }
88
 
89
  $IP = ( new LookupIpOnList() )
90
  ->setDbHandler( $mod->getDbHandler_IPs() )
91
  ->setListTypeBlock()
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
 
102
  $updateData = [
106
  if ( $IP->list != $mod::LIST_MANUAL_BLACK ) {
107
  $updateData[ 'list' ] = $mod::LIST_MANUAL_BLACK;
108
  }
109
+ if ( $IP->label != $label ) {
110
+ $updateData[ 'label' ] = $label;
111
  }
112
  if ( $IP->blocked_at == 0 ) {
113
  $updateData[ 'blocked_at' ] = Services::Request()->ts();
129
  public function toManualWhitelist( $label = '' ) {
130
  /** @var ModCon $mod */
131
  $mod = $this->getMod();
132
+ $srvIP = Services::IP();
133
 
134
  $ip = $this->getIP();
135
+ if ( !$srvIP->isValidIp( $ip ) && !$srvIP->isValidIpRange( $ip ) ) {
136
  throw new \Exception( "IP address isn't valid." );
137
  }
138
 
139
+ if ( $srvIP->isValidIpRange( $ip ) ) {
140
  ( new DeleteIp() )
141
  ->setMod( $mod )
142
  ->setIP( $ip )
148
  ->setIP( $this->getIP() )
149
  ->lookup( false );
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
 
157
  $updateData = [];
179
 
180
  /**
181
  * @param string $list
182
+ * @param string $label
183
+ * @param int|null $lastAccess
184
  * @return Databases\IPs\EntryVO|null
185
  * @throws \Exception
186
  */
187
+ private function add( string $list, $label = '', $lastAccess = null ) {
188
+ $IP = null;
189
 
190
  /** @var ModCon $mod */
191
  $mod = $this->getMod();
192
 
193
  // Never add a reserved IP to any black list
194
+ $dbh = $mod->getDbHandler_IPs();
195
 
196
+ /** @var Databases\IPs\EntryVO $tmp */
197
+ $tmp = $dbh->getVo();
198
+ $tmp->ip = $this->getIP();
199
+ $tmp->list = $list;
200
+ $tmp->label = empty( $label ) ? __( 'No Label', 'wp-simple-firewall' ) : trim( $label );
201
+ if ( is_numeric( $lastAccess ) && $lastAccess > 0 ) {
202
+ $tmp->last_access_at = $lastAccess;
203
  }
204
 
205
+ if ( $dbh->getQueryInserter()->insert( $tmp ) ) {
206
+ /** @var Databases\IPs\EntryVO $IP */
207
+ $IP = $dbh->getQuerySelector()
208
+ ->setWheresFromVo( $tmp )
209
+ ->first();
210
  }
211
 
212
+ if ( !$IP instanceof Databases\IPs\EntryVO ) {
213
  throw new \Exception( "IP couldn't be added to the database." );
214
  }
215
 
216
+ return $IP;
217
  }
218
  }
src/lib/src/Modules/IPs/Lib/Ops/DeleteIp.php CHANGED
@@ -19,6 +19,7 @@ class DeleteIp {
19
  }
20
 
21
  public function fromWhiteList() :bool {
 
22
  return (bool)$this->getDeleter()
23
  ->filterByWhitelist()
24
  ->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/ModCon.php CHANGED
@@ -88,6 +88,30 @@ class ModCon extends BaseShield\ModCon {
88
  $this->cleanPathWhitelist();
89
  }
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  public function canLinkCheese() :bool {
92
  $FS = Services::WpFs();
93
  $WP = Services::WpGeneral();
@@ -97,32 +121,6 @@ class ModCon extends BaseShield\ModCon {
97
  && ( !$isSplit || !$FS->exists( path_join( dirname( ABSPATH ), 'robots.txt' ) ) );
98
  }
99
 
100
- private function cleanPathWhitelist() {
101
- /** @var Options $opts */
102
- $opts = $this->getOptions();
103
- $opts->setOpt( 'request_whitelist', array_unique( array_filter( array_map(
104
- function ( $rule ) {
105
- $rule = strtolower( trim( $rule ) );
106
- if ( !empty( $rule ) ) {
107
- $toCheck = array_unique( [
108
- (string)parse_url( Services::WpGeneral()->getHomeUrl(), PHP_URL_PATH ),
109
- (string)parse_url( Services::WpGeneral()->getWpUrl(), PHP_URL_PATH ),
110
- ] );
111
- $regEx = sprintf( '#^%s$#i', str_replace( 'STAR', '.*', preg_quote( str_replace( '*', 'STAR', $rule ), '#' ) ) );
112
- foreach ( $toCheck as $path ) {
113
- $slashPath = rtrim( $path, '/' ).'/';
114
- if ( preg_match( $regEx, $path ) || preg_match( $regEx, $slashPath ) ) {
115
- $rule = false;
116
- break;
117
- }
118
- }
119
- }
120
- return $rule;
121
- },
122
- $opts->getOpt( 'request_whitelist', [] ) // do not use Options getter as it formats into regex
123
- ) ) ) );
124
- }
125
-
126
  public function loadOffenseTracker() :Lib\OffenseTracker {
127
  if ( !isset( $this->oOffenseTracker ) ) {
128
  $this->oOffenseTracker = new Lib\OffenseTracker( $this->getCon() );
88
  $this->cleanPathWhitelist();
89
  }
90
 
91
+ private function cleanPathWhitelist() {
92
+ /** @var Options $opts */
93
+ $opts = $this->getOptions();
94
+
95
+ $specialPaths = array_map(
96
+ function ( $url ) {
97
+ return (string)parse_url( $url, PHP_URL_PATH );
98
+ },
99
+ [
100
+ Services::WpGeneral()->getHomeUrl(),
101
+ Services::WpGeneral()->getWpUrl(),
102
+ ]
103
+ );
104
+
105
+ $values = $opts->getOpt( 'request_whitelist', [] );
106
+ $opts->setOpt( 'request_whitelist',
107
+ ( new Shield\Modules\Base\Options\WildCardOptions() )->clean(
108
+ is_array( $values ) ? $values : [],
109
+ $specialPaths,
110
+ Shield\Modules\Base\Options\WildCardOptions::URL_PATH
111
+ )
112
+ );
113
+ }
114
+
115
  public function canLinkCheese() :bool {
116
  $FS = Services::WpFs();
117
  $WP = Services::WpGeneral();
121
  && ( !$isSplit || !$FS->exists( path_join( dirname( ABSPATH ), 'robots.txt' ) ) );
122
  }
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  public function loadOffenseTracker() :Lib\OffenseTracker {
125
  if ( !isset( $this->oOffenseTracker ) ) {
126
  $this->oOffenseTracker = new Lib\OffenseTracker( $this->getCon() );
src/lib/src/Modules/IPs/Options.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
 
6
  use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class Options extends BaseShield\Options {
@@ -27,13 +28,13 @@ class Options extends BaseShield\Options {
27
  public function getCanIpRequestAutoUnblock( string $ip ) :bool {
28
  $existing = $this->getAutoUnblockIps();
29
  return !array_key_exists( $ip, $existing )
30
- || ( Services::Request()->carbon()->subHour( 1 )->timestamp > $existing[ $ip ] );
31
  }
32
 
33
  public function getCanRequestAutoUnblockEmailLink( \WP_User $user ) :bool {
34
  $existing = $this->getAutoUnblockEmailIDs();
35
  return !array_key_exists( $user->ID, $existing )
36
- || ( Services::Request()->carbon()->subHour( 1 )->timestamp > $existing[ $user->ID ] );
37
  }
38
 
39
  public function getOffenseLimit() :int {
@@ -43,12 +44,13 @@ class Options extends BaseShield\Options {
43
  /**
44
  * @return string[] - precise REGEX patterns to match against PATH.
45
  */
46
- public function getRequestWhitelistAsRegex() {
 
47
  return array_map(
48
- function ( $rule ) {
49
- return sprintf( '#^%s$#i', str_replace( 'STAR', '.*', preg_quote( str_replace( '*', 'STAR', $rule ), '#' ) ) );
50
  },
51
- $this->isPremium() ? $this->getOpt( 'request_whitelist', [] ) : []
52
  );
53
  }
54
 
@@ -100,34 +102,19 @@ class Options extends BaseShield\Options {
100
  return $this->isSelectOptionEnabled( 'track_linkcheese' );
101
  }
102
 
103
- /**
104
- * @return bool
105
- */
106
- public function isEnabledTrackXmlRpc() {
107
  return $this->isSelectOptionEnabled( 'track_xmlrpc' );
108
  }
109
 
110
- /**
111
- * @param string $key
112
- * @return bool
113
- */
114
- public function isTrackOptTransgression( $key ) {
115
  return strpos( $this->getOpt( $key ), 'transgression' ) !== false;
116
  }
117
 
118
- /**
119
- * @param string $key
120
- * @return bool
121
- */
122
- public function isTrackOptDoubleTransgression( $key ) {
123
  return $this->isOpt( $key, 'transgression-double' );
124
  }
125
 
126
- /**
127
- * @param string $key
128
- * @return bool
129
- */
130
- public function isTrackOptImmediateBlock( $key ) :bool {
131
  return $this->isOpt( $key, 'block' );
132
  }
133
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Options\WildCardOptions;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class Options extends BaseShield\Options {
28
  public function getCanIpRequestAutoUnblock( string $ip ) :bool {
29
  $existing = $this->getAutoUnblockIps();
30
  return !array_key_exists( $ip, $existing )
31
+ || ( Services::Request()->carbon()->subHours( 1 )->timestamp > $existing[ $ip ] );
32
  }
33
 
34
  public function getCanRequestAutoUnblockEmailLink( \WP_User $user ) :bool {
35
  $existing = $this->getAutoUnblockEmailIDs();
36
  return !array_key_exists( $user->ID, $existing )
37
+ || ( Services::Request()->carbon()->subHours( 1 )->timestamp > $existing[ $user->ID ] );
38
  }
39
 
40
  public function getOffenseLimit() :int {
44
  /**
45
  * @return string[] - precise REGEX patterns to match against PATH.
46
  */
47
+ public function getRequestWhitelistAsRegex() :array {
48
+ $paths = $this->isPremium() ? $this->getOpt( 'request_whitelist', [] ) : [];
49
  return array_map(
50
+ function ( $value ) {
51
+ return ( new WildCardOptions() )->buildFullRegexValue( $value, WildCardOptions::URL_PATH );
52
  },
53
+ is_array( $paths ) ? $paths : []
54
  );
55
  }
56
 
102
  return $this->isSelectOptionEnabled( 'track_linkcheese' );
103
  }
104
 
105
+ public function isEnabledTrackXmlRpc() :bool {
 
 
 
106
  return $this->isSelectOptionEnabled( 'track_xmlrpc' );
107
  }
108
 
109
+ public function isTrackOptTransgression( string $key ) :bool {
 
 
 
 
110
  return strpos( $this->getOpt( $key ), 'transgression' ) !== false;
111
  }
112
 
113
+ public function isTrackOptDoubleTransgression( string $key ) :bool {
 
 
 
 
114
  return $this->isOpt( $key, 'transgression-double' );
115
  }
116
 
117
+ public function isTrackOptImmediateBlock( string $key ) :bool {
 
 
 
 
118
  return $this->isOpt( $key, 'block' );
119
  }
120
 
src/lib/src/Modules/IPs/Strings.php CHANGED
@@ -325,6 +325,18 @@ class Strings extends Base\Strings {
325
  'ip_blocked' => [
326
  __( 'IP blocked after incrementing offenses from %s to %s.', 'wp-simple-firewall' )
327
  ],
 
 
 
 
 
 
 
 
 
 
 
 
328
  'ip_unblock_flag' => [
329
  __( "IP address '%s' removed from blacklist using 'unblock' file flag.", 'wp-simple-firewall' )
330
  ],
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
  ],
src/lib/src/Modules/IPs/WpCli/Add.php CHANGED
@@ -29,22 +29,22 @@ class Add extends BaseAddRemove {
29
 
30
  /**
31
  * @param array $null
32
- * @param array $aA
33
  * @throws WP_CLI\ExitException
34
  */
35
- public function cmdIpAdd( array $null, array $aA ) {
36
 
37
- $label = $aA[ 'label' ] ?? 'none';
38
 
39
- $oAdder = ( new Ops\AddIp() )
40
  ->setMod( $this->getMod() )
41
- ->setIP( $aA[ 'ip' ] );
42
  try {
43
- if ( $aA[ 'list' ] === 'white' ) {
44
- $oAdder->toManualWhitelist( $label );
45
  }
46
  else {
47
- $oAdder->toManualBlacklist( $label );
48
  }
49
  }
50
  catch ( \Exception $e ) {
29
 
30
  /**
31
  * @param array $null
32
+ * @param array $args
33
  * @throws WP_CLI\ExitException
34
  */
35
+ public function cmdIpAdd( array $null, array $args ) {
36
 
37
+ $label = $args[ 'label' ] ?? 'none';
38
 
39
+ $adder = ( new Ops\AddIp() )
40
  ->setMod( $this->getMod() )
41
+ ->setIP( $args[ 'ip' ] );
42
  try {
43
+ if ( $args[ 'list' ] === 'white' ) {
44
+ $adder->toManualWhitelist( $label );
45
  }
46
  else {
47
+ $adder->toManualBlacklist( $label );
48
  }
49
  }
50
  catch ( \Exception $e ) {
src/lib/src/Modules/IPs/WpCli/Remove.php CHANGED
@@ -21,24 +21,24 @@ class Remove extends BaseAddRemove {
21
 
22
  /**
23
  * @param array $null
24
- * @param array $aA
25
  * @throws WP_CLI\ExitException
26
  */
27
- public function cmdIpRemove( array $null, array $aA ) {
28
  /** @var IPs\ModCon $mod */
29
  $mod = $this->getMod();
30
 
31
- $oDel = ( new IPs\Lib\Ops\DeleteIp() )
32
  ->setMod( $mod )
33
- ->setIP( $aA[ 'ip' ] );
34
- if ( $aA[ 'list' ] === 'white' ) {
35
- $bSuccess = $oDel->fromWhiteList();
36
  }
37
  else {
38
- $bSuccess = $oDel->fromBlacklist();
39
  }
40
 
41
- $bSuccess ?
42
  WP_CLI::success( __( 'IP address removed successfully.', 'wp-simple-firewall' ) )
43
  : WP_CLI::error( __( "IP address couldn't be removed. (It may not be on this list)", 'wp-simple-firewall' ) );
44
  }
21
 
22
  /**
23
  * @param array $null
24
+ * @param array $args
25
  * @throws WP_CLI\ExitException
26
  */
27
+ public function cmdIpRemove( array $null, array $args ) {
28
  /** @var IPs\ModCon $mod */
29
  $mod = $this->getMod();
30
 
31
+ $deleter = ( new IPs\Lib\Ops\DeleteIp() )
32
  ->setMod( $mod )
33
+ ->setIP( $args[ 'ip' ] );
34
+ if ( $args[ 'list' ] === 'white' ) {
35
+ $success = $deleter->fromWhiteList();
36
  }
37
  else {
38
+ $success = $deleter->fromBlacklist();
39
  }
40
 
41
+ $success ?
42
  WP_CLI::success( __( 'IP address removed successfully.', 'wp-simple-firewall' ) )
43
  : WP_CLI::error( __( "IP address couldn't be removed. (It may not be on this list)", 'wp-simple-firewall' ) );
44
  }
src/lib/src/Modules/Insights/Lib/SideMenuBuilder.php CHANGED
@@ -31,7 +31,7 @@ class SideMenuBuilder {
31
  $item = Services::DataManipulation()->mergeArraysRecursive( [
32
  'slug' => 'no-slug',
33
  'title' => __( 'NO TITLE', 'wp-simple-firewall' ),
34
- 'href' => '#',
35
  'classes' => [],
36
  'id' => '',
37
  'active' => $this->getInav() === $item[ 'slug' ],
31
  $item = Services::DataManipulation()->mergeArraysRecursive( [
32
  'slug' => 'no-slug',
33
  'title' => __( 'NO TITLE', 'wp-simple-firewall' ),
34
+ 'href' => 'javascript:{}',
35
  'classes' => [],
36
  'id' => '',
37
  'active' => $this->getInav() === $item[ 'slug' ],
src/lib/src/Modules/Insights/ModCon.php CHANGED
@@ -67,8 +67,9 @@ class ModCon extends BaseShield\ModCon {
67
  'icwp_wpsf_vars_insights',
68
  [
69
  'strings' => [
70
- 'select_action' => __( 'Please select an action to perform.', 'wp-simple-firewall' ),
71
- 'are_you_sure' => __( 'Are you sure?', 'wp-simple-firewall' ),
 
72
  ],
73
  ]
74
  ];
67
  'icwp_wpsf_vars_insights',
68
  [
69
  'strings' => [
70
+ 'select_action' => __( 'Please select an action to perform.', 'wp-simple-firewall' ),
71
+ 'are_you_sure' => __( 'Are you sure?', 'wp-simple-firewall' ),
72
+ 'absolutely_sure' => __( 'Are you absolutely sure?', 'wp-simple-firewall' ),
73
  ],
74
  ]
75
  ];
src/lib/src/Modules/Insights/Strings.php CHANGED
@@ -26,12 +26,9 @@ class Strings extends Base\Strings {
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
- 'apc_item_repair_success' => __( 'Repaired abandoned plugin', 'wp-simple-firewall' ),
30
- 'mal_item_repair_success' => __( 'Repaired file with malware', 'wp-simple-firewall' ),
31
- 'ptg_item_repair_success' => __( 'Repaired plugin/theme file', 'wp-simple-firewall' ),
32
- 'ufc_item_repair_success' => __( 'Repaired/Deleted unrecognised file', 'wp-simple-firewall' ),
33
- 'wcf_item_repair_success' => __( 'Repaired Core file', 'wp-simple-firewall' ),
34
- 'wpv_item_repair_success' => __( 'Repaired vulnerable item', 'wp-simple-firewall' ),
35
  'session_terminate' => __( 'User session terminated and forced to re-login', 'wp-simple-firewall' ),
36
  'conn_kill' => __( 'Connection killed for blocked IP address', 'wp-simple-firewall' ),
37
  'ip_offense' => __( 'Offense registered against IP address', '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' ),
src/lib/src/Modules/Integrations/Lib/Bots/Spam/Handlers/SupportCandy.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Bots\Spam\Handlers;
4
+
5
+ use FernleafSystems\Wordpress\Services\Services;
6
+
7
+ class SupportCandy extends Base {
8
+
9
+ protected function run() {
10
+ add_filter( 'wpsc_before_create_ticket_args', function ( $args ) {
11
+ if ( $this->isSpam() ) {
12
+ Services::WpGeneral()->wpDie( sprintf( "Sorry, your request failed %s Bot checking.",
13
+ $this->getCon()->getHumanName() ) );
14
+ }
15
+ return $args;
16
+ }, 1000 );
17
+ }
18
+
19
+ protected function getProviderName() :string {
20
+ return 'SupportCandy';
21
+ }
22
+
23
+ public static function IsProviderInstalled() :bool {
24
+ return @class_exists( 'Support_Candy' )
25
+ && defined( 'WPSC_VERSION' ) && version_compare( WPSC_VERSION, '2.2.3', '>=' );
26
+ }
27
+ }
src/lib/src/Modules/Integrations/Lib/Bots/Spam/SpamController.php CHANGED
@@ -25,6 +25,7 @@ class SpamController extends BaseBotDetectionController {
25
  new Handlers\KaliForms(),
26
  new Handlers\NinjaForms(),
27
  new Handlers\SuperForms(),
 
28
  new Handlers\WPForms(),
29
  new Handlers\WpForo(),
30
  ];
25
  new Handlers\KaliForms(),
26
  new Handlers\NinjaForms(),
27
  new Handlers\SuperForms(),
28
+ new Handlers\SupportCandy(),
29
  new Handlers\WPForms(),
30
  new Handlers\WpForo(),
31
  ];
src/lib/src/Modules/LoginGuard/Lib/AntiBot/AntibotSetup.php CHANGED
@@ -21,7 +21,7 @@ class AntibotSetup extends ExecOnceModConsumer {
21
  $opts = $this->getOptions();
22
 
23
  $providers = [];
24
- if ( $opts->isEnabledCooldown() && $mod->canCacheDirWrite() ) {
25
  $providers[] = ( new AntiBot\ProtectionProviders\CoolDown() )
26
  ->setMod( $mod );
27
  }
@@ -91,4 +91,4 @@ class AntibotSetup extends ExecOnceModConsumer {
91
  }
92
  }
93
  }
94
- }
21
  $opts = $this->getOptions();
22
 
23
  $providers = [];
24
+ if ( $opts->isEnabledCooldown() && $this->getCon()->hasCacheDir() ) {
25
  $providers[] = ( new AntiBot\ProtectionProviders\CoolDown() )
26
  ->setMod( $mod );
27
  }
91
  }
92
  }
93
  }
94
+ }
src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/AntiBot.php CHANGED
@@ -9,7 +9,7 @@ class AntiBot extends BaseProtectionProvider {
9
  /**
10
  * @inheritDoc
11
  */
12
- public function performCheck( $oForm ) {
13
  if ( $this->isFactorTested() ) {
14
  return;
15
  }
9
  /**
10
  * @inheritDoc
11
  */
12
+ public function performCheck( $form ) {
13
  if ( $this->isFactorTested() ) {
14
  return;
15
  }
src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/BaseProtectionProvider.php CHANGED
@@ -42,10 +42,10 @@ abstract class BaseProtectionProvider {
42
  }
43
 
44
  /**
45
- * @param LoginGuard\Lib\AntiBot\FormProviders\BaseFormProvider $oForm
46
  * @throws \Exception
47
  */
48
- abstract public function performCheck( $oForm );
49
 
50
  /**
51
  * @param bool $tested
42
  }
43
 
44
  /**
45
+ * @param LoginGuard\Lib\AntiBot\FormProviders\BaseFormProvider $form
46
  * @throws \Exception
47
  */
48
+ abstract public function performCheck( $form );
49
 
50
  /**
51
  * @param bool $tested
src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/CoolDown.php CHANGED
@@ -9,19 +9,19 @@ class CoolDown extends BaseProtectionProvider {
9
  /**
10
  * @inheritDoc
11
  */
12
- public function performCheck( $oForm ) {
13
  if ( !$this->isFactorTested() ) {
14
  $this->setFactorTested( true );
15
 
16
  // At this point someone has attempted to login within the previous login wait interval
17
  // So we remove WordPress's authentication filter and our own user check authentication
18
  // And finally return a WP_Error which will be reflected back to the user.
19
- $oCooldownFlag = ( new CooldownFlagFile() )->setMod( $this->getMod() );
20
- if ( $oCooldownFlag->isWithinCooldownPeriod() ) {
21
  $sErrorString = __( "Request Cooldown in effect.", 'wp-simple-firewall' ).' '
22
  .sprintf(
23
  __( "You must wait %s seconds before attempting this action again.", 'wp-simple-firewall' ),
24
- $oCooldownFlag->getCooldownRemaining()
25
  );
26
 
27
  $this->getCon()->fireEvent( 'cooldown_fail' );
@@ -29,7 +29,7 @@ class CoolDown extends BaseProtectionProvider {
29
  throw new \Exception( $sErrorString );
30
  }
31
 
32
- $oCooldownFlag->updateCooldownFlag();
33
  }
34
  }
35
 
9
  /**
10
  * @inheritDoc
11
  */
12
+ public function performCheck( $form ) {
13
  if ( !$this->isFactorTested() ) {
14
  $this->setFactorTested( true );
15
 
16
  // At this point someone has attempted to login within the previous login wait interval
17
  // So we remove WordPress's authentication filter and our own user check authentication
18
  // And finally return a WP_Error which will be reflected back to the user.
19
+ $cooldown = ( new CooldownFlagFile() )->setMod( $this->getMod() );
20
+ if ( $cooldown->isWithinCooldownPeriod() ) {
21
  $sErrorString = __( "Request Cooldown in effect.", 'wp-simple-firewall' ).' '
22
  .sprintf(
23
  __( "You must wait %s seconds before attempting this action again.", 'wp-simple-firewall' ),
24
+ $cooldown->getCooldownRemaining()
25
  );
26
 
27
  $this->getCon()->fireEvent( 'cooldown_fail' );
29
  throw new \Exception( $sErrorString );
30
  }
31
 
32
+ $cooldown->updateCooldownFlag();
33
  }
34
  }
35
 
src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GaspJs.php CHANGED
@@ -58,7 +58,7 @@ class GaspJs extends BaseProtectionProvider {
58
  /**
59
  * @inheritDoc
60
  */
61
- public function performCheck( $oForm ) {
62
  if ( $this->isFactorTested() ) {
63
  return;
64
  }
@@ -71,8 +71,8 @@ class GaspJs extends BaseProtectionProvider {
71
 
72
  $gasp = $req->post( $mod->getGaspKey() );
73
 
74
- $username = $oForm->getUserToAudit();
75
- $action = $oForm->getActionToAudit();
76
 
77
  $valid = false;
78
  $errorMsg = '';
58
  /**
59
  * @inheritDoc
60
  */
61
+ public function performCheck( $form ) {
62
  if ( $this->isFactorTested() ) {
63
  return;
64
  }
71
 
72
  $gasp = $req->post( $mod->getGaspKey() );
73
 
74
+ $username = $form->getUserToAudit();
75
+ $action = $form->getActionToAudit();
76
 
77
  $valid = false;
78
  $errorMsg = '';
src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GoogleRecaptcha.php CHANGED
@@ -18,7 +18,7 @@ class GoogleRecaptcha extends BaseProtectionProvider {
18
  /**
19
  * @inheritDoc
20
  */
21
- public function performCheck( $oForm ) {
22
  if ( !$this->isFactorTested() ) {
23
  $this->setFactorTested( true );
24
  try {
18
  /**
19
  * @inheritDoc
20
  */
21
+ public function performCheck( $form ) {
22
  if ( !$this->isFactorTested() ) {
23
  $this->setFactorTested( true );
24
  try {
src/lib/src/Modules/LoginGuard/Lib/CooldownFlagFile.php CHANGED
@@ -9,10 +9,7 @@ class CooldownFlagFile {
9
 
10
  use Modules\ModConsumer;
11
 
12
- /**
13
- * @return bool
14
- */
15
- public function isWithinCooldownPeriod() {
16
  return $this->getCooldownRemaining() > 0;
17
  }
18
 
@@ -20,16 +17,13 @@ class CooldownFlagFile {
20
  * @return int
21
  */
22
  public function getCooldownRemaining() {
23
- /** @var Modules\LoginGuard\Options $oOpts */
24
- $oOpts = $this->getOptions();
25
- return max( 0, $oOpts->getCooldownInterval() - $this->getSecondsSinceLastLogin() );
26
  }
27
 
28
- /**
29
- * @return string
30
- */
31
- public function getFlagFilePath() {
32
- return $this->getCon()->getPluginCachePath( 'mode.login_throttled' );
33
  }
34
 
35
  /**
@@ -38,8 +32,8 @@ class CooldownFlagFile {
38
  public function getSecondsSinceLastLogin() {
39
  $FS = Services::WpFs();
40
  $file = $this->getFlagFilePath();
41
- $nLastLogin = $FS->exists( $file ) ? $FS->getModifiedTime( $file ) : 0;
42
- return ( Services::Request()->ts() - $nLastLogin );
43
  }
44
 
45
  /**
9
 
10
  use Modules\ModConsumer;
11
 
12
+ public function isWithinCooldownPeriod() :bool {
 
 
 
13
  return $this->getCooldownRemaining() > 0;
14
  }
15
 
17
  * @return int
18
  */
19
  public function getCooldownRemaining() {
20
+ /** @var Modules\LoginGuard\Options $opts */
21
+ $opts = $this->getOptions();
22
+ return max( 0, $opts->getCooldownInterval() - $this->getSecondsSinceLastLogin() );
23
  }
24
 
25
+ public function getFlagFilePath() :string {
26
+ return $this->getCon()->paths->forCacheItem( 'mode.login_throttled' );
 
 
 
27
  }
28
 
29
  /**
32
  public function getSecondsSinceLastLogin() {
33
  $FS = Services::WpFs();
34
  $file = $this->getFlagFilePath();
35
+ $lastLogin = $FS->exists( $file ) ? $FS->getModifiedTime( $file ) : 0;
36
+ return Services::Request()->ts() - $lastLogin;
37
  }
38
 
39
  /**
src/lib/src/Modules/LoginGuard/Lib/CooldownRedirect.php DELETED
@@ -1,63 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
8
- use FernleafSystems\Wordpress\Services\Services;
9
-
10
- /**
11
- * Class CooldownRedirect
12
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard
13
- */
14
- class CooldownRedirect {
15
-
16
- use Modules\ModConsumer;
17
-
18
- public function run() {
19
- add_action( 'wp_loaded', [ $this, 'onWpLoaded' ] );
20
- }
21
-
22
- public function onWpLoaded() {
23
- $oReq = Services::Request();
24
- $oCooldownFile = ( new LoginGuard\Lib\CooldownFlagFile() )->setMod( $this->getMod() );
25
- if ( !$oReq->isPost() && Services::WpGeneral()->isLoginUrl()
26
- && $oCooldownFile->isWithinCooldownPeriod()
27
- && !Services::WpUsers()->isUserLoggedIn()
28
- && $oReq->query( 'cooldown_bypass' ) != 1 ) {
29
- $this->renderCooldownPage();
30
- }
31
- }
32
-
33
- private function renderCooldownPage() {
34
- /** @var ModCon $mod */
35
- $mod = $this->getMod();
36
- $nTimeRemaining = ( new LoginGuard\Lib\CooldownFlagFile() )
37
- ->setMod( $mod )
38
- ->getCooldownRemaining();
39
- $aData = [
40
- 'strings' => [
41
- 'title' => __( "The login page is protected against too many login attempts.", 'wp-simple-firewall' ),
42
- 'lines' => [
43
- __( 'If you attempt to login again too quickly you may be blocked from accessing this site entirely.', 'wp-simple-firewall' ),
44
- __( 'If you share this website with others, you may also block their access to the site.', 'wp-simple-firewall' ),
45
- __( 'To ignore this message and return to the login page, please check the box and click continue.', 'wp-simple-firewall' ),
46
- ],
47
- 'understand' => __( 'I understand I may block my access to the site.', 'wp-simple-firewall' ),
48
- 'time_remaining' => __( 'Seconds remaining', 'wp-simple-firewall' ),
49
- 'button' => __( 'Proceed To Login Page', 'wp-simple-firewall' ),
50
- ],
51
- 'vars' => [
52
- 'remaining' => $nTimeRemaining,
53
- 'login_url' => Services::WpGeneral()->getLoginUrl(),
54
- ],
55
- 'flags' => [
56
- ],
57
- ];
58
- Services::WpGeneral()
59
- ->wpDie(
60
- $mod->renderTemplate( '/snippets/cooldown_login_block.twig', $aData, true )
61
- );
62
- }
63
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php CHANGED
@@ -276,7 +276,7 @@ class MfaController {
276
  if ( !$canSkip && $this->getCon()->isPremiumActive() && @class_exists( 'WC_Social_Login' ) ) {
277
  // custom support for WooCommerce Social login
278
  $meta = $this->getCon()->getUserMeta( $user );
279
- $canSkip = isset( $meta->wc_social_login_valid ) ? $meta->wc_social_login_valid : false;
280
  }
281
 
282
  return (bool)apply_filters( 'icwp_shield_2fa_skip',
276
  if ( !$canSkip && $this->getCon()->isPremiumActive() && @class_exists( 'WC_Social_Login' ) ) {
277
  // custom support for WooCommerce Social login
278
  $meta = $this->getCon()->getUserMeta( $user );
279
+ $canSkip = $meta->wc_social_login_valid ?? false;
280
  }
281
 
282
  return (bool)apply_filters( 'icwp_shield_2fa_skip',
src/lib/src/Modules/LoginGuard/UI.php CHANGED
@@ -51,10 +51,16 @@ class UI extends BaseShield\UI {
51
  }
52
 
53
  if ( $section == 'section_2fa_email' ) {
 
 
 
 
 
 
54
  $warnings[] =
55
  __( '2FA by email demands that your WP site is properly configured to send email.', 'wp-simple-firewall' )
56
  .'<br/>'.__( 'This is a common problem and you may get locked out in the future if you ignore this.', 'wp-simple-firewall' )
57
- .' '.sprintf( '<a href="%s" target="_blank" class="alert-link">%s</a>', 'https://shsec.io/dd', __( 'Learn More.', 'wp-simple-firewall' ) );
58
  }
59
 
60
  return $warnings;
51
  }
52
 
53
  if ( $section == 'section_2fa_email' ) {
54
+
55
+ if ( $opts->isEnabledEmailAuth() && !$opts->getIfCanSendEmailVerified() ) {
56
+ $warnings[] = __( "The ability of this site to send email hasn't been verified.", 'wp-simple-firewall' )
57
+ .'<br/>'.__( 'Please click to re-save your settings to trigger another verification email.', 'wp-simple-firewall' );
58
+ }
59
+
60
  $warnings[] =
61
  __( '2FA by email demands that your WP site is properly configured to send email.', 'wp-simple-firewall' )
62
  .'<br/>'.__( 'This is a common problem and you may get locked out in the future if you ignore this.', 'wp-simple-firewall' )
63
+ .' '.sprintf( '<a href="%s" target="_blank" class="alert-link">%s</a>', 'https://shsec.io/dd', trim( __( 'Learn More.', 'wp-simple-firewall' ), '.' ) );
64
  }
65
 
66
  return $warnings;
src/lib/src/Modules/Plugin/Lib/TestCacheDirWrite.php CHANGED
@@ -20,11 +20,12 @@ class TestCacheDirWrite {
20
  $data = $this->getTestData();
21
  $now = Services::Request()->ts();
22
 
23
- if ( ( $data[ 'last_success_at' ] === 0 || $now - WEEK_IN_SECONDS > $data[ 'last_success_at' ] )
24
  && ( $now - HOUR_IN_SECONDS > $data[ 'last_test_at' ] ) ) {
25
 
26
- $rootDir = $this->getCon()->getPluginCachePath();
27
- $canWrite = !empty( $rootDir )
 
28
  && $this->canCreateWriteDeleteFile()
29
  && $this->canCreateWriteDeleteDir();
30
 
@@ -40,13 +41,14 @@ class TestCacheDirWrite {
40
 
41
  $FS = Services::WpFs();
42
 
43
- $testDir = $this->getCon()->getPluginCachePath( uniqid() );
44
  $FS->mkdir( $testDir );
45
  if ( $FS->isDir( $testDir ) ) {
46
- $sFile = path_join( $testDir, uniqid() );
47
- $FS->touch( $sFile );
 
48
  $FS->deleteDir( $testDir );
49
- $canWrite = !$FS->isDir( $testDir );
50
  }
51
  return $canWrite;
52
  }
@@ -56,16 +58,12 @@ class TestCacheDirWrite {
56
 
57
  $FS = Services::WpFs();
58
 
59
- $testFile = $this->getCon()->getPluginCachePath( 'test_write_file.txt' );
60
- $FS->touch( $testFile );
61
-
62
- if ( $FS->exists( $testFile ) ) {
63
- $uniq = uniqid();
64
- $FS->putFileContent( $testFile, $uniq );
65
- if ( $FS->getFileContent( $testFile ) == $uniq ) {
66
- $FS->deleteFile( $testFile );
67
- $canWrite = !$FS->exists( $testFile );
68
- }
69
  }
70
  return $canWrite;
71
  }
20
  $data = $this->getTestData();
21
  $now = Services::Request()->ts();
22
 
23
+ if ( ( $data[ 'last_success_at' ] === 0 || $now - HOUR_IN_SECONDS > $data[ 'last_success_at' ] )
24
  && ( $now - HOUR_IN_SECONDS > $data[ 'last_test_at' ] ) ) {
25
 
26
+ // Use simple cachdir lookup, not the controller getCachePath to prevent infinite loops
27
+ $cacheDir = $this->getCon()->paths->cacheDir();
28
+ $canWrite = !empty( $cacheDir )
29
  && $this->canCreateWriteDeleteFile()
30
  && $this->canCreateWriteDeleteDir();
31
 
41
 
42
  $FS = Services::WpFs();
43
 
44
+ $testDir = $this->getCon()->paths->forCacheItem( uniqid() );
45
  $FS->mkdir( $testDir );
46
  if ( $FS->isDir( $testDir ) ) {
47
+ $file = path_join( $testDir, uniqid() );
48
+ $FS->touch( $file );
49
+ $canTouchFile = $FS->isFile( $file );
50
  $FS->deleteDir( $testDir );
51
+ $canWrite = $canTouchFile && !$FS->isDir( $testDir );
52
  }
53
  return $canWrite;
54
  }
58
 
59
  $FS = Services::WpFs();
60
 
61
+ $testFile = $this->getCon()->paths->forCacheItem( 'test_write_file.txt' );
62
+ $uniq = uniqid();
63
+ $FS->putFileContent( $testFile, $uniq );
64
+ if ( $FS->getFileContent( $testFile ) == $uniq ) {
65
+ $FS->deleteFile( $testFile );
66
+ $canWrite = !$FS->exists( $testFile );
 
 
 
 
67
  }
68
  return $canWrite;
69
  }
src/lib/src/Modules/Plugin/Options.php CHANGED
@@ -63,14 +63,6 @@ class Options extends BaseShield\Options {
63
  return !$this->isOpt( 'tracking_permission_set_at', 0 );
64
  }
65
 
66
- /**
67
- * @return bool
68
- * @deprecated 11.4
69
- */
70
- public function isImportExportPermitted() :bool {
71
- return $this->isOpt( 'importexport_enable', 'Y' );
72
- }
73
-
74
  /**
75
  * @return string[]
76
  */
63
  return !$this->isOpt( 'tracking_permission_set_at', 0 );
64
  }
65
 
 
 
 
 
 
 
 
 
66
  /**
67
  * @return string[]
68
  */
src/lib/src/Modules/Plugin/WpCli/Export.php CHANGED
@@ -36,51 +36,51 @@ class Export extends Base\WpCli\BaseWpCliCmd {
36
 
37
  /**
38
  * @param array $null
39
- * @param array $aA
40
  * @throws WP_CLI\ExitException
41
  */
42
- public function cmdExport( array $null, array $aA ) {
43
- $oFS = Services::WpFs();
44
 
45
- $sFile = $aA[ 'file' ];
46
- $bForce = $this->isForceFlag( $aA );
47
- if ( !path_is_absolute( $sFile ) ) {
48
- $sFile = path_join( ABSPATH, $sFile );
49
- WP_CLI::log( __( "The file specified wasn't an absolute path, so we're using the following path to the export file:" ) );
50
  }
51
- WP_CLI::log( sprintf( '%s: %s', __( 'Export file path', 'wp-simple-firewall' ), $sFile ) );
52
 
53
- if ( $oFS->isDir( $sFile ) ) {
54
  WP_CLI::error( __( "The file specified is an existing directory.", 'wp-simple-firewall' ) );
55
  }
56
 
57
- $dir = dirname( $sFile );
58
- if ( !$oFS->isDir( $dir ) ) {
59
  if ( !$bForce ) {
60
  WP_CLI::confirm( "The directory for the export file doesn't exist. Create it?" );
61
  }
62
- $oFS->mkdir( $sFile );
63
- if ( $oFS->mkdir( $sFile ) && !$oFS->isDir( $dir ) ) {
64
  WP_CLI::error( sprintf( __( "Couldn't create the directory: %s", 'wp-simple-firewall' ), $dir ) );
65
  }
66
  }
67
 
68
- if ( $oFS->isFile( $sFile ) && !$bForce ) {
69
  WP_CLI::confirm( "The export file already exists. Overwrite?" );
70
  }
71
 
72
- $oFS->touch( $sFile );
73
- if ( !$oFS->isFile( $sFile ) ) {
74
  WP_CLI::error( __( "Couldn't create the export file.", 'wp-simple-firewall' ) );
75
  }
76
- if ( !is_writable( $sFile ) ) {
77
  WP_CLI::error( __( "The system reports that this file path isn't writable.", 'wp-simple-firewall' ) );
78
  }
79
 
80
  $aData = ( new Lib\ImportExport\Export() )
81
  ->setMod( $this->getMod() )
82
  ->toStandardArray();
83
- if ( !$oFS->putFileContent( $sFile, implode( "\n", $aData ) ) ) {
84
  WP_CLI::error( __( "The system reports that writing the export file failed.", 'wp-simple-firewall' ) );
85
  }
86
 
36
 
37
  /**
38
  * @param array $null
39
+ * @param array $args
40
  * @throws WP_CLI\ExitException
41
  */
42
+ public function cmdExport( array $null, array $args ) {
43
+ $FS = Services::WpFs();
44
 
45
+ $file = $args[ 'file' ];
46
+ $bForce = $this->isForceFlag( $args );
47
+ if ( !path_is_absolute( $file ) ) {
48
+ $file = path_join( ABSPATH, $file );
49
+ WP_CLI::log( __( "File provied wasn't an absolute path, so we're using the following path to the export file" ) );
50
  }
51
+ WP_CLI::log( sprintf( '%s: %s', __( 'Export file path', 'wp-simple-firewall' ), $file ) );
52
 
53
+ if ( $FS->isDir( $file ) ) {
54
  WP_CLI::error( __( "The file specified is an existing directory.", 'wp-simple-firewall' ) );
55
  }
56
 
57
+ $dir = dirname( $file );
58
+ if ( !$FS->isDir( $dir ) ) {
59
  if ( !$bForce ) {
60
  WP_CLI::confirm( "The directory for the export file doesn't exist. Create it?" );
61
  }
62
+ $FS->mkdir( $file );
63
+ if ( $FS->mkdir( $file ) && !$FS->isDir( $dir ) ) {
64
  WP_CLI::error( sprintf( __( "Couldn't create the directory: %s", 'wp-simple-firewall' ), $dir ) );
65
  }
66
  }
67
 
68
+ if ( $FS->isFile( $file ) && !$bForce ) {
69
  WP_CLI::confirm( "The export file already exists. Overwrite?" );
70
  }
71
 
72
+ $FS->touch( $file );
73
+ if ( !$FS->isFile( $file ) ) {
74
  WP_CLI::error( __( "Couldn't create the export file.", 'wp-simple-firewall' ) );
75
  }
76
+ if ( !is_writable( $file ) ) {
77
  WP_CLI::error( __( "The system reports that this file path isn't writable.", 'wp-simple-firewall' ) );
78
  }
79
 
80
  $aData = ( new Lib\ImportExport\Export() )
81
  ->setMod( $this->getMod() )
82
  ->toStandardArray();
83
+ if ( !$FS->putFileContent( $file, implode( "\n", $aData ) ) ) {
84
  WP_CLI::error( __( "The system reports that writing the export file failed.", 'wp-simple-firewall' ) );
85
  }
86
 
src/lib/src/Modules/Plugin/WpCli/ForceOff.php CHANGED
@@ -33,12 +33,12 @@ class ForceOff extends BaseWpCliCmd {
33
  }
34
 
35
  public function cmdForceOff( $null, $aA ) {
36
- $oFS = Services::WpFs();
37
- $sPath = path_join( $this->getCon()->getRootDir(), 'forceoff' );
38
 
39
  switch ( $aA[ 'action' ] ) {
40
  case 'query':
41
- if ( $oFS->exists( $sPath ) ) {
42
  WP_CLI::log( '`forceoff` file is present.' );
43
  }
44
  else {
@@ -47,8 +47,8 @@ class ForceOff extends BaseWpCliCmd {
47
  break;
48
 
49
  case 'create':
50
- $oFS->touch( $sPath );
51
- if ( $oFS->exists( $sPath ) ) {
52
  WP_CLI::success( '`forceoff` file created successfully.' );
53
  }
54
  else {
@@ -57,12 +57,12 @@ class ForceOff extends BaseWpCliCmd {
57
  break;
58
 
59
  case 'delete':
60
- if ( !$oFS->exists( $sPath ) ) {
61
  WP_CLI::success( "`forceoff` doesn't exist." );
62
  }
63
  else {
64
- $oFS->deleteFile( $sPath );
65
- if ( $oFS->exists( $sPath ) ) {
66
  WP_CLI::error( "`forceoff` file couldn't be deleted." );
67
  }
68
  else {
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.' );
43
  }
44
  else {
47
  break;
48
 
49
  case 'create':
50
+ $FS->touch( $path );
51
+ if ( $FS->exists( $path ) ) {
52
  WP_CLI::success( '`forceoff` file created successfully.' );
53
  }
54
  else {
57
  break;
58
 
59
  case 'delete':
60
+ if ( !$FS->exists( $path ) ) {
61
  WP_CLI::success( "`forceoff` doesn't exist." );
62
  }
63
  else {
64
+ $FS->deleteFile( $path );
65
+ if ( $FS->exists( $path ) ) {
66
  WP_CLI::error( "`forceoff` file couldn't be deleted." );
67
  }
68
  else {
src/lib/src/Modules/Reporting/Lib/Reports/BaseReporter.php CHANGED
@@ -13,10 +13,7 @@ abstract class BaseReporter {
13
  */
14
  private $rep;
15
 
16
- /**
17
- * @return array
18
- */
19
- public function build() {
20
  return [];
21
  }
22
 
13
  */
14
  private $rep;
15
 
16
+ public function build() :array {
 
 
 
17
  return [];
18
  }
19
 
src/lib/src/Modules/Reporting/Lib/Reports/Build/BaseBuilder.php CHANGED
@@ -43,7 +43,7 @@ abstract class BaseBuilder {
43
  */
44
  abstract protected function gather() :array;
45
 
46
- abstract protected function render( array $aGatheredData ) :string;
47
 
48
  /**
49
  * When displaying, we must take into account the GMT offset of the site.
43
  */
44
  abstract protected function gather() :array;
45
 
46
+ abstract protected function render( array $gathered ) :string;
47
 
48
  /**
49
  * When displaying, we must take into account the GMT offset of the site.
src/lib/src/Modules/Reporting/Lib/Reports/Build/BuilderAlerts.php CHANGED
@@ -26,12 +26,12 @@ class BuilderAlerts extends BaseBuilder {
26
  return $reports;
27
  }
28
 
29
- protected function render( array $aGatheredData ) :string {
30
  return $this->getMod()->renderTemplate(
31
  '/components/reports/alert_body.twig',
32
  [
33
  'vars' => [
34
- 'alerts' => $aGatheredData
35
  ],
36
  'strings' => [
37
  'title' => __( 'Important Alerts', 'wp-simple-firewall' ),
26
  return $reports;
27
  }
28
 
29
+ protected function render( array $gathered ) :string {
30
  return $this->getMod()->renderTemplate(
31
  '/components/reports/alert_body.twig',
32
  [
33
  'vars' => [
34
+ 'alerts' => $gathered
35
  ],
36
  'strings' => [
37
  'title' => __( 'Important Alerts', 'wp-simple-firewall' ),
src/lib/src/Modules/Reporting/Lib/Reports/Build/BuilderInfo.php CHANGED
@@ -25,12 +25,12 @@ class BuilderInfo extends BaseBuilder {
25
  return $reports;
26
  }
27
 
28
- protected function render( array $aGatheredData ) :string {
29
  return $this->getMod()->renderTemplate(
30
  '/components/reports/info_body.twig',
31
  [
32
  'vars' => [
33
- 'alerts' => $aGatheredData
34
  ],
35
  'strings' => [
36
  'title' => __( 'Site Information Report', 'wp-simple-firewall' ),
25
  return $reports;
26
  }
27
 
28
+ protected function render( array $gathered ) :string {
29
  return $this->getMod()->renderTemplate(
30
  '/components/reports/info_body.twig',
31
  [
32
  'vars' => [
33
+ 'alerts' => $gathered
34
  ],
35
  'strings' => [
36
  'title' => __( 'Site Information Report', 'wp-simple-firewall' ),
src/lib/src/Modules/SecurityAdmin/Options.php CHANGED
@@ -15,18 +15,6 @@ class Options extends BaseShield\Options {
15
  return $this->isOpt( 'admin_access_restrict_options', 'Y' );
16
  }
17
 
18
- public function getAdminAccessArea_Plugins() :array {
19
- return $this->getAdminAccessArea( 'plugins' );
20
- }
21
-
22
- public function getAdminAccessArea_Themes() :array {
23
- return $this->getAdminAccessArea( 'themes' );
24
- }
25
-
26
- public function getAdminAccessArea_Posts() :array {
27
- return $this->getAdminAccessArea( 'posts' );
28
- }
29
-
30
  /**
31
  * @param string $area one of plugins, themes
32
  * @return array
@@ -37,16 +25,6 @@ class Options extends BaseShield\Options {
37
  return is_array( $d ) ? $d : [];
38
  }
39
 
40
- /**
41
- * @param string $sArea one of plugins, themes
42
- * @return array
43
- * @deprecated 11.1
44
- */
45
- private function getAdminAccessArea( $sArea = 'plugins' ) :array {
46
- $d = $this->getOpt( 'admin_access_restrict_'.$sArea, [] );
47
- return is_array( $d ) ? $d : [];
48
- }
49
-
50
  private function getRestrictedOptions() :array {
51
  $options = $this->getDef( 'options_to_restrict' );
52
  return is_array( $options ) ? $options : [];
15
  return $this->isOpt( 'admin_access_restrict_options', 'Y' );
16
  }
17
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  /**
19
  * @param string $area one of plugins, themes
20
  * @return array
25
  return is_array( $d ) ? $d : [];
26
  }
27
 
 
 
 
 
 
 
 
 
 
 
28
  private function getRestrictedOptions() :array {
29
  $options = $this->getDef( 'options_to_restrict' );
30
  return is_array( $options ) ? $options : [];
src/lib/src/Modules/Sessions/Lib/Ops/Retrieve.php DELETED
@@ -1,38 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Sessions\Lib\Ops;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Session\{
6
- EntryVO,
7
- Select
8
- };
9
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
10
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Sessions\ModCon;
11
-
12
- /**
13
- * Class Retrieve
14
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Sessions\Lib\Ops
15
- * @deprecated 11.2
16
- */
17
- class Retrieve {
18
-
19
- use ModConsumer;
20
-
21
- /**
22
- * @param string $ip
23
- * @return EntryVO|null
24
- */
25
- public function byIP( string $ip ) {
26
- return $this->getSelector()->filterByIp( $ip )->first();
27
- }
28
-
29
- public function byUsername( string $username ) :bool {
30
- return $this->getSelector()->filterByUsername( $username )->first();
31
- }
32
-
33
- private function getSelector() :Select {
34
- /** @var ModCon $mod */
35
- $mod = $this->getMod();
36
- return $mod->getDbHandler_Sessions()->getQuerySelector();
37
- }
38
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Scans/Apc/ConvertVosToResults.php DELETED
@@ -1,32 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Apc;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield;
6
-
7
- /**
8
- * Class ConvertVosToResults
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Apc
10
- */
11
- class ConvertVosToResults extends Shield\Scans\Base\BaseConvertVosToResults {
12
-
13
- /**
14
- * @param Shield\Databases\Scanner\EntryVO[] $VOs
15
- * @return ResultsSet
16
- */
17
- public function convert( $VOs ) {
18
- $oRes = new ResultsSet();
19
- foreach ( $VOs as $oVo ) {
20
- $oRes->addItem( $this->convertItem( $oVo ) );
21
- }
22
- return $oRes;
23
- }
24
-
25
- /**
26
- * @param Shield\Databases\Scanner\EntryVO $VO
27
- * @return ResultItem
28
- */
29
- public function convertItem( $VO ) {
30
- return ( new ResultItem() )->applyFromArray( $VO->meta );
31
- }
32
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Scans/Apc/ResultItem.php CHANGED
@@ -9,6 +9,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Apc;
9
  * @property string $context
10
  * @property int $last_updated_at
11
  */
12
- class ResultItem extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultItem {
13
 
14
  }
9
  * @property string $context
10
  * @property int $last_updated_at
11
  */
12
+ class ResultItem extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\ResultItem {
13
 
14
  }
src/lib/src/Scans/Apc/ResultsSet.php CHANGED
@@ -1,4 +1,4 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Apc;
4
 
@@ -9,114 +9,21 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
9
  * @property ResultItem[] $items
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Apc
11
  */
12
- class ResultsSet extends Base\BaseResultsSet {
13
 
14
  /**
15
- * @return int
 
16
  */
17
- public function countUniqueSlugs() {
18
- return count( $this->getAllResultsSetsForUniqueSlugs() );
19
- }
20
-
21
- /**
22
- * @return int
23
- */
24
- public function countUniqueSlugsForPluginsContext() {
25
- return count( $this->getAllResultsSetsForPluginsContext() );
26
- }
27
-
28
- /**
29
- * @return int
30
- */
31
- public function countUniqueSlugsForThemesContext() {
32
- return count( $this->getAllResultsSetsForThemesContext() );
33
- }
34
-
35
- /**
36
- * Provides a collection of ResultsSets for Plugins.
37
- * @return ResultsSet[]
38
- */
39
- public function getAllResultsSetsForPluginsContext() {
40
- return $this->getAllResultsSetsForContext( 'plugins' );
41
- }
42
-
43
- /**
44
- * Provides a collection of ResultsSets for Themes.
45
- * @return ResultsSet[]
46
- */
47
- public function getAllResultsSetsForThemesContext() {
48
- return $this->getAllResultsSetsForContext( 'themes' );
49
- }
50
-
51
- /**
52
- * @param string $sContext
53
- * @return ResultsSet[]
54
- */
55
- public function getAllResultsSetsForContext( $sContext ) {
56
- $aCollection = [];
57
- foreach ( $this->getAllResultsSetsForUniqueSlugs() as $sSlug => $oRS ) {
58
- if ( $oRS->getItems()[ 0 ]->context == $sContext ) {
59
- $aCollection[ $sSlug ] = $oRS;
60
  }
61
  }
62
- return $aCollection;
63
- }
64
-
65
- /**
66
- * @return ResultsSet[]
67
- */
68
- public function getAllResultsSetsForUniqueSlugs() {
69
- $aCollection = [];
70
- foreach ( $this->getUniqueSlugs() as $sSlug ) {
71
- $oRS = $this->getResultsSetForSlug( $sSlug );
72
- if ( $oRS->hasItems() ) {
73
- $aCollection[ $sSlug ] = $oRS;
74
- }
75
- }
76
- ksort( $aCollection, SORT_NATURAL );
77
- return $aCollection;
78
- }
79
-
80
- /**
81
- * @param string $sSlug
82
- * @return ResultItem[]
83
- */
84
- public function getItemsForSlug( $sSlug ) {
85
- return array_values( array_filter(
86
- $this->getItems(),
87
- function ( $oItem ) use ( $sSlug ) {
88
- /** @var ResultItem $oItem */
89
- return $oItem->slug == $sSlug;
90
- }
91
- ) );
92
- }
93
-
94
- /**
95
- * @param string $sSlug
96
- * @return ResultsSet
97
- */
98
- public function getResultsSetForSlug( $sSlug ) {
99
- $oRes = new ResultsSet();
100
- array_map(
101
- function ( $oItem ) use ( $oRes ) {
102
- /** @var ResultItem $oItem */
103
- $oRes->addItem( $oItem );
104
- },
105
- $this->getItemsForSlug( $sSlug )
106
- );
107
- return $oRes;
108
- }
109
-
110
- /**
111
- * @return string[]
112
- */
113
- public function getUniqueSlugs() {
114
- return array_unique( array_map(
115
- function ( $oItem ) {
116
- /** @var ResultItem $oItem */
117
- return $oItem->slug;
118
- },
119
- $this->getItems()
120
- ) );
121
  }
122
  }
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Apc;
4
 
9
  * @property ResultItem[] $items
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Apc
11
  */
12
+ class ResultsSet extends Base\ResultsSet {
13
 
14
  /**
15
+ * @param string $slug
16
+ * @return ResultItem|null
17
  */
18
+ public function getItemForSlug( string $slug ) {
19
+ $theItem = null;
20
+ /** @var ResultItem $item */
21
+ foreach ( $this->getItems() as $item ) {
22
+ if ( $item->slug === $slug ) {
23
+ $theItem = $item;
24
+ break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  }
26
  }
27
+ return $theItem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  }
29
  }
src/lib/src/Scans/Apc/Scan.php CHANGED
@@ -14,7 +14,7 @@ class Scan extends Shield\Scans\Base\BaseScan {
14
 
15
  foreach ( $oAction->items as $nKey => $sItem ) {
16
  $oItem = $this->getItemScanner()->scan( $sItem );
17
- if ( $oItem instanceof Shield\Scans\Base\BaseResultItem ) {
18
  $oTempRs->addItem( $oItem );
19
  }
20
  }
14
 
15
  foreach ( $oAction->items as $nKey => $sItem ) {
16
  $oItem = $this->getItemScanner()->scan( $sItem );
17
+ if ( $oItem instanceof Shield\Scans\Base\ResultItem ) {
18
  $oTempRs->addItem( $oItem );
19
  }
20
  }
src/lib/src/Scans/Apc/Utilities/ItemActionHandler.php CHANGED
@@ -12,10 +12,4 @@ class ItemActionHandler extends Base\Utilities\ItemActionHandler {
12
  public function getRepairer() {
13
  return ( new Repair() )->setScanItem( $this->getScanItem() );
14
  }
15
-
16
- /**
17
- * @param bool $success
18
- */
19
- protected function fireRepairEvent( $success ) {
20
- }
21
  }
12
  public function getRepairer() {
13
  return ( new Repair() )->setScanItem( $this->getScanItem() );
14
  }
 
 
 
 
 
 
15
  }
src/lib/src/Scans/Apc/Utilities/Repair.php CHANGED
@@ -14,7 +14,7 @@ class Repair extends Scans\Base\Utilities\BaseRepair {
14
  * @return bool
15
  * @throws \Exception
16
  */
17
- public function repairItem() {
18
  throw new \Exception( 'Repair action is not supported' );
19
  }
20
  }
14
  * @return bool
15
  * @throws \Exception
16
  */
17
+ public function repairItem() :bool {
18
  throw new \Exception( 'Repair action is not supported' );
19
  }
20
  }
src/lib/src/Scans/Base/BaseBuildFileMap.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Options;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Common\ScanActionConsumer;
8
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal\ScanActionVO;
9
+
10
+ abstract class BaseBuildFileMap {
11
+
12
+ use Shield\Modules\ModConsumer;
13
+ use ScanActionConsumer;
14
+
15
+ abstract public function build() :array;
16
+
17
+ protected function isAutoFilterFile( \SplFileInfo $file ) :bool {
18
+ /** @var Options $opts */
19
+ $opts = $this->getOptions();
20
+ return $opts->isAutoFilterResults()
21
+ && $file->getSize() === 0;
22
+ }
23
+
24
+ protected function isWhitelistedPath( string $path ) :bool {
25
+ $whitelisted = false;
26
+
27
+ /** @var ScanActionVO $action */
28
+ $action = $this->getScanActionVO();
29
+ foreach ( $action->paths_whitelisted as $wlPathRegEx ) {
30
+ if ( preg_match( $wlPathRegEx, $path ) ) {
31
+ $whitelisted = true;
32
+ break;
33
+ }
34
+ }
35
+ return $whitelisted;
36
+ }
37
+ }
src/lib/src/Scans/Base/BaseBuildScanAction.php CHANGED
@@ -14,14 +14,15 @@ abstract class BaseBuildScanAction {
14
  * @throws \Exception
15
  */
16
  public function build() {
17
- $oAction = $this->getScanActionVO();
18
- if ( !$oAction instanceof BaseScanActionVO ) {
19
  throw new \Exception( 'Scan Action VO not provided.' );
20
  }
21
- if ( empty( $oAction->scan ) ) {
22
  throw new \Exception( 'Scan Slug not provided.' );
23
  }
24
 
 
25
  $this->setCustomFields();
26
  $this->buildScanItems();
27
  $this->setStandardFields();
@@ -31,26 +32,33 @@ abstract class BaseBuildScanAction {
31
  * @throws \Exception
32
  */
33
  protected function buildScanItems() {
34
- $oAction = $this->getScanActionVO();
35
  $this->buildItems();
36
- $oAction->total_items = count( $oAction->items );
37
  }
38
 
39
  abstract protected function buildItems();
40
 
41
  protected function setStandardFields() {
42
- $oAction = $this->getScanActionVO();
43
- if ( empty( $oAction->created_at ) ) {
44
- $oAction->created_at = Services::Request()->ts();
45
- $oAction->started_at = 0;
46
- $oAction->finished_at = 0;
47
- $oAction->usleep = (int)( 1000000*max( 0, apply_filters(
48
  $this->getCon()->prefix( 'scan_block_sleep' ),
49
- $oAction::DEFAULT_SLEEP_SECONDS, $oAction->scan
50
  ) ) );
51
  }
52
  }
53
 
54
  protected function setCustomFields() {
55
  }
 
 
 
 
 
 
 
56
  }
14
  * @throws \Exception
15
  */
16
  public function build() {
17
+ $action = $this->getScanActionVO();
18
+ if ( !$action instanceof BaseScanActionVO ) {
19
  throw new \Exception( 'Scan Action VO not provided.' );
20
  }
21
+ if ( empty( $action->scan ) ) {
22
  throw new \Exception( 'Scan Slug not provided.' );
23
  }
24
 
25
+ $this->setWhitelists();
26
  $this->setCustomFields();
27
  $this->buildScanItems();
28
  $this->setStandardFields();
32
  * @throws \Exception
33
  */
34
  protected function buildScanItems() {
35
+ $action = $this->getScanActionVO();
36
  $this->buildItems();
37
+ $action->total_items = count( $action->items );
38
  }
39
 
40
  abstract protected function buildItems();
41
 
42
  protected function setStandardFields() {
43
+ $action = $this->getScanActionVO();
44
+ if ( empty( $action->created_at ) ) {
45
+ $action->created_at = Services::Request()->ts();
46
+ $action->started_at = 0;
47
+ $action->finished_at = 0;
48
+ $action->usleep = (int)( 1000000*max( 0, apply_filters(
49
  $this->getCon()->prefix( 'scan_block_sleep' ),
50
+ $action::DEFAULT_SLEEP_SECONDS, $action->scan
51
  ) ) );
52
  }
53
  }
54
 
55
  protected function setCustomFields() {
56
  }
57
+
58
+ protected function setWhitelists() {
59
+ /** @var Shield\Modules\HackGuard\Options $opts */
60
+ $opts = $this->getOptions();
61
+ $action = $this->getScanActionVO();
62
+ $action->paths_whitelisted = $opts->getWhitelistedPathsAsRegex();
63
+ }
64
  }
src/lib/src/Scans/Base/BaseConvertVosToResults.php DELETED
@@ -1,30 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\EntryVO;
6
-
7
- /**
8
- * Class BaseConvertVosToResults
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Base
10
- */
11
- abstract class BaseConvertVosToResults {
12
-
13
- /**
14
- * @param EntryVO[] $VOs
15
- * @return BaseResultsSet
16
- */
17
- public function convert( $VOs ) {
18
- $oRes = new BaseResultsSet();
19
- foreach ( $VOs as $oVo ) {
20
- $oRes->addItem( $this->convertItem( $oVo ) );
21
- }
22
- return $oRes;
23
- }
24
-
25
- /**
26
- * @param EntryVO $VO
27
- * @return BaseResultItem
28
- */
29
- abstract public function convertItem( $VO );
30
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Scans/Base/BaseFileScanActionVO.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
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
11
+ * @property string[] $paths_whitelisted
12
+ */
13
+ abstract class BaseFileScanActionVO extends BaseScanActionVO {
14
+
15
+ }
src/lib/src/Scans/Base/BaseMergeItems.php CHANGED
@@ -10,9 +10,9 @@ class BaseMergeItems {
10
 
11
  /**
12
  * Merges any data from $oMergeItem into $oBaseItem, overwriting Base Item data
13
- * @param BaseResultItem $oBaseItem
14
- * @param BaseResultItem $oMergeItem
15
- * @return BaseResultItem
16
  */
17
  public function mergeItemTo( $oBaseItem, $oMergeItem ) {
18
  foreach ( $oMergeItem->getRawData() as $sKey => $mVal ) {
10
 
11
  /**
12
  * Merges any data from $oMergeItem into $oBaseItem, overwriting Base Item data
13
+ * @param ResultItem $oBaseItem
14
+ * @param ResultItem $oMergeItem
15
+ * @return ResultItem
16
  */
17
  public function mergeItemTo( $oBaseItem, $oMergeItem ) {
18
  foreach ( $oMergeItem->getRawData() as $sKey => $mVal ) {
src/lib/src/Scans/Base/BaseScan.php CHANGED
@@ -35,19 +35,19 @@ abstract class BaseScan {
35
  }
36
 
37
  protected function scan() {
38
- $oAction = $this->getScanActionVO();
39
 
40
- if ( empty( $oAction->items ) ) {
41
- $oAction->finished_at = Services::Request()->ts();
42
  }
43
  else {
44
  $this->scanSlice();
45
- if ( empty( $oAction->items ) ) {
46
- $oAction->finished_at = Services::Request()->ts();
47
  }
48
  }
49
 
50
- return $oAction;
51
  }
52
 
53
  /**
35
  }
36
 
37
  protected function scan() {
38
+ $action = $this->getScanActionVO();
39
 
40
+ if ( empty( $action->items ) ) {
41
+ $action->finished_at = Services::Request()->ts();
42
  }
43
  else {
44
  $this->scanSlice();
45
+ if ( empty( $action->items ) ) {
46
+ $action->finished_at = Services::Request()->ts();
47
  }
48
  }
49
 
50
+ return $action;
51
  }
52
 
53
  /**
src/lib/src/Scans/Base/BaseScanActionVO.php CHANGED
@@ -26,7 +26,7 @@ abstract class BaseScanActionVO {
26
  const DEFAULT_SLEEP_SECONDS = 0;
27
 
28
  /**
29
- * @return BaseResultItem|mixed
30
  */
31
  public function getNewResultItem() {
32
  $class = $this->getScanNamespace().'ResultItem';
@@ -34,7 +34,7 @@ abstract class BaseScanActionVO {
34
  }
35
 
36
  /**
37
- * @return BaseResultsSet|mixed
38
  */
39
  public function getNewResultsSet() {
40
  $class = $this->getScanNamespace().'ResultsSet';
26
  const DEFAULT_SLEEP_SECONDS = 0;
27
 
28
  /**
29
+ * @return ResultItem|mixed
30
  */
31
  public function getNewResultItem() {
32
  $class = $this->getScanNamespace().'ResultItem';
34
  }
35
 
36
  /**
37
+ * @return ResultsSet|mixed
38
  */
39
  public function getNewResultsSet() {
40
  $class = $this->getScanNamespace().'ResultsSet';
src/lib/src/Scans/Base/DiffResultForStorage.php CHANGED
@@ -3,7 +3,6 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf\ResultsSet;
7
 
8
  /**
9
  * Class DiffResultForStorage
@@ -13,9 +12,9 @@ class DiffResultForStorage {
13
 
14
  /**
15
  * The Existing set will be updated to reflect the new current status of the scan
16
- * @param BaseResultsSet $oExistingRes - will be updated with all items to DB Update
17
- * @param BaseResultsSet $oNewResults - will be adjusted with all item to DB Insert
18
- * @return BaseResultsSet - A results set of all out-of-date records that need to be deleted.
19
  */
20
  public function diff( $oExistingRes, $oNewResults ) {
21
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans;
 
6
 
7
  /**
8
  * Class DiffResultForStorage
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
 
src/lib/src/Scans/Base/FileResultItem.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
+
5
+ /**
6
+ * Class FileResultItem
7
+ * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Base
8
+ * @property string $path_full
9
+ * @property string $path_fragment - relative to ABSPATH
10
+ */
11
+ class FileResultItem extends ResultItem {
12
+
13
+ }
src/lib/src/Scans/Base/Files/BaseFileScanner.php CHANGED
@@ -15,7 +15,7 @@ abstract class BaseFileScanner {
15
 
16
  /**
17
  * @param string $fullPath
18
- * @return Shield\Scans\Base\BaseResultItem|null
19
  */
20
  abstract public function scan( string $fullPath );
21
  }
15
 
16
  /**
17
  * @param string $fullPath
18
+ * @return Shield\Scans\Base\ResultItem|null
19
  */
20
  abstract public function scan( string $fullPath );
21
  }
src/lib/src/Scans/Base/Files/BaseScanFromFileMap.php CHANGED
@@ -2,8 +2,11 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Files;
4
 
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans;
 
7
 
8
  /**
9
  * Class BaseScanFromFileMap
@@ -15,17 +18,30 @@ abstract class BaseScanFromFileMap {
15
  use Scans\Common\ScanActionConsumer;
16
 
17
  /**
18
- * @return Scans\Base\BaseResultsSet
19
  */
20
  public function run() {
 
 
 
21
  $action = $this->getScanActionVO();
22
  $results = $action->getNewResultsSet();
23
 
 
 
24
  if ( is_array( $action->items ) ) {
 
25
  foreach ( $action->items as $key => $fullPath ) {
26
- $item = $this->getFileScanner()->scan( $fullPath );
27
- if ( $item instanceof Scans\Base\BaseResultItem ) {
28
- $results->addItem( $item );
 
 
 
 
 
 
 
29
  }
30
  }
31
  }
@@ -37,4 +53,17 @@ abstract class BaseScanFromFileMap {
37
  * @return BaseFileScanner
38
  */
39
  abstract protected function getFileScanner();
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Files;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Utility\VerifyFileByHash;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Options;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Scans;
9
+ use FernleafSystems\Wordpress\Services\Utilities\Code\AssessPhpFile;
10
 
11
  /**
12
  * Class BaseScanFromFileMap
18
  use Scans\Common\ScanActionConsumer;
19
 
20
  /**
21
+ * @return Scans\Base\ResultsSet
22
  */
23
  public function run() {
24
+ /** @var Options $opts */
25
+ $opts = $this->getOptions();
26
+
27
  $action = $this->getScanActionVO();
28
  $results = $action->getNewResultsSet();
29
 
30
+ $isAutoFilter = $opts->isAutoFilterResults();
31
+
32
  if ( is_array( $action->items ) ) {
33
+ $hashVerifier = ( new VerifyFileByHash() )->setMod( $this->getMod() );
34
  foreach ( $action->items as $key => $fullPath ) {
35
+
36
+ if ( !$isAutoFilter || !$this->isEmptyOfCode( $fullPath ) ) {
37
+
38
+ if ( !$hashVerifier->verify( $fullPath ) ) {
39
+ $item = $this->getFileScanner()->scan( $fullPath );
40
+ // We can exclude files that are empty of relevant code
41
+ if ( $item instanceof Scans\Base\ResultItem ) {
42
+ $results->addItem( $item );
43
+ }
44
+ }
45
  }
46
  }
47
  }
53
  * @return BaseFileScanner
54
  */
55
  abstract protected function getFileScanner();
56
+
57
+ protected function isEmptyOfCode( string $path ) :bool {
58
+ try {
59
+ if ( strpos( $path, wp_normalize_path( ABSPATH ) ) === false ) {
60
+ $path = path_join( ABSPATH, $path );
61
+ }
62
+ $isEmpty = ( new AssessPhpFile() )->isEmptyOfCode( $path );
63
+ }
64
+ catch ( \Exception $e ) {
65
+ $isEmpty = false;
66
+ }
67
+ return $isEmpty;
68
+ }
69
  }
src/lib/src/Scans/Base/{BaseResultItem.php → ResultItem.php} RENAMED
@@ -3,6 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
 
5
  use FernleafSystems\Utilities\Data\Adapter\DynProperties;
 
6
 
7
  /**
8
  * Class BaseResultItem
@@ -11,11 +12,17 @@ use FernleafSystems\Utilities\Data\Adapter\DynProperties;
11
  * @property bool $is_excluded
12
  * @property string $scan
13
  * @property bool $repaired
 
14
  */
15
- class BaseResultItem {
16
 
17
  use DynProperties;
18
 
 
 
 
 
 
19
  public function isReady() :bool {
20
  return false;
21
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
 
5
  use FernleafSystems\Utilities\Data\Adapter\DynProperties;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\EntryVO;
7
 
8
  /**
9
  * Class BaseResultItem
12
  * @property bool $is_excluded
13
  * @property string $scan
14
  * @property bool $repaired
15
+ * @property string $repair_event_status
16
  */
17
+ class ResultItem {
18
 
19
  use DynProperties;
20
 
21
+ /**
22
+ * @var EntryVO
23
+ */
24
+ public $VO;
25
+
26
  public function isReady() :bool {
27
  return false;
28
  }
src/lib/src/Scans/Base/{BaseResultsSet.php → ResultsSet.php} RENAMED
@@ -2,14 +2,10 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
 
5
- /**
6
- * Class ResultsSet
7
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Base
8
- */
9
- class BaseResultsSet {
10
 
11
  /**
12
- * @var BaseResultItem[]
13
  */
14
  protected $items;
15
 
@@ -19,22 +15,22 @@ class BaseResultsSet {
19
  protected $bFilterExcluded = true;
20
 
21
  /**
22
- * @param BaseResultItem $oItem
23
  * @return $this
24
  */
25
- public function addItem( $oItem ) {
26
- $aI = $this->getAllItems();
27
- if ( !isset( $oItem->hash ) ) {
28
- $oItem->hash = $oItem->generateHash();
29
  }
30
- $aI[ $oItem->hash ] = $oItem;
31
- $this->items = $aI;
32
  return $this;
33
  }
34
 
35
  /**
36
  * @param string $hash
37
- * @return BaseResultItem|null
38
  */
39
  public function getItemByHash( $hash ) {
40
  return $this->getItemExists( $hash ) ? $this->getAllItems()[ $hash ] : null;
@@ -50,7 +46,7 @@ class BaseResultsSet {
50
 
51
  /**
52
  * Ignores the "is_excluded" property on the items
53
- * @return BaseResultItem[]
54
  */
55
  public function getAllItems() :array {
56
  if ( !is_array( $this->items ) ) {
@@ -60,7 +56,7 @@ class BaseResultsSet {
60
  }
61
 
62
  /**
63
- * @return BaseResultItem[]
64
  */
65
  public function getExcludedItems() :array {
66
  return array_values( array_filter(
@@ -73,7 +69,7 @@ class BaseResultsSet {
73
 
74
  /**
75
  * Honours the exclusion flags
76
- * @return BaseResultItem[]
77
  */
78
  public function getItems() :array {
79
  return array_values( array_filter(
@@ -100,7 +96,7 @@ class BaseResultsSet {
100
  }
101
 
102
  /**
103
- * @param BaseResultItem $item
104
  * @return $this
105
  */
106
  public function removeItem( $item ) {
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
4
 
5
+ class ResultsSet {
 
 
 
 
6
 
7
  /**
8
+ * @var ResultItem[]
9
  */
10
  protected $items;
11
 
15
  protected $bFilterExcluded = true;
16
 
17
  /**
18
+ * @param ResultItem $item
19
  * @return $this
20
  */
21
+ public function addItem( $item ) {
22
+ $all = $this->getAllItems();
23
+ if ( !isset( $item->hash ) ) {
24
+ $item->hash = $item->generateHash();
25
  }
26
+ $all[ $item->hash ] = $item;
27
+ $this->items = $all;
28
  return $this;
29
  }
30
 
31
  /**
32
  * @param string $hash
33
+ * @return ResultItem|null
34
  */
35
  public function getItemByHash( $hash ) {
36
  return $this->getItemExists( $hash ) ? $this->getAllItems()[ $hash ] : null;
46
 
47
  /**
48
  * Ignores the "is_excluded" property on the items
49
+ * @return ResultItem[]
50
  */
51
  public function getAllItems() :array {
52
  if ( !is_array( $this->items ) ) {
56
  }
57
 
58
  /**
59
+ * @return ResultItem[]
60
  */
61
  public function getExcludedItems() :array {
62
  return array_values( array_filter(
69
 
70
  /**
71
  * Honours the exclusion flags
72
+ * @return ResultItem[]
73
  */
74
  public function getItems() :array {
75
  return array_values( array_filter(
96
  }
97
 
98
  /**
99
+ * @param ResultItem $item
100
  * @return $this
101
  */
102
  public function removeItem( $item ) {
src/lib/src/Scans/Base/Table/BaseEntryFormatter.php CHANGED
@@ -49,7 +49,7 @@ abstract class BaseEntryFormatter {
49
  'download' => [
50
  'text' => __( 'Download', 'wp-simple-firewall' ),
51
  'classes' => [ 'href-download', 'text-info' ],
52
- 'data' => [ 'href-download' => $this->getScanController()->createFileDownloadLink( $this->getEntryVO() ) ]
53
  ],
54
  ];
55
  }
@@ -59,7 +59,7 @@ abstract class BaseEntryFormatter {
59
  }
60
 
61
  /**
62
- * @return Scans\Base\BaseResultItem|mixed
63
  */
64
  protected function getResultItem() {
65
  return ( new Scan\Results\ConvertBetweenTypes() )
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
  }
59
  }
60
 
61
  /**
62
+ * @return Scans\Base\ResultItem|mixed
63
  */
64
  protected function getResultItem() {
65
  return ( new Scan\Results\ConvertBetweenTypes() )
src/lib/src/Scans/Base/Table/BaseFileEntryFormatter.php CHANGED
@@ -63,7 +63,7 @@ abstract class BaseFileEntryFormatter extends BaseEntryFormatter {
63
  'download' => [
64
  'text' => sprintf( __( 'Download %s', 'wp-simple-firewall' ), __( 'File', 'wp-simple-firewall' ) ),
65
  'classes' => [ 'href-download', 'text-info' ],
66
- 'data' => [ 'href-download' => $this->getScanController()->createFileDownloadLink( $this->getEntryVO() ) ]
67
  ],
68
  ];
69
  }
63
  'download' => [
64
  'text' => sprintf( __( 'Download %s', 'wp-simple-firewall' ), __( 'File', 'wp-simple-firewall' ) ),
65
  'classes' => [ 'href-download', 'text-info' ],
66
+ 'data' => [ 'href-download' => $this->getScanController()->createFileDownloadLink( $this->getEntryVO()->id ) ]
67
  ],
68
  ];
69
  }
src/lib/src/Scans/Base/Utilities/BaseRepair.php CHANGED
@@ -2,6 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Utilities;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Common\ScanItemConsumer;
6
 
7
  /**
@@ -12,38 +13,25 @@ abstract class BaseRepair {
12
 
13
  use ScanItemConsumer;
14
 
15
- /**
16
- * @var bool
17
- */
18
- private $bAllowDelete = false;
19
-
20
  /**
21
  * @return bool
 
22
  */
23
- public function isAllowDelete() {
24
- return (bool)$this->bAllowDelete;
25
- }
26
-
27
- /**
28
- * @param bool $bAllowDelete
29
- * @return $this
30
- */
31
- public function setAllowDelete( $bAllowDelete ) {
32
- $this->bAllowDelete = $bAllowDelete;
33
- return $this;
34
- }
35
 
36
  /**
37
  * @return bool
38
  * @throws \Exception
39
  */
40
- abstract public function repairItem();
 
 
41
 
42
  /**
43
  * @return bool
44
  * @throws \Exception
45
  */
46
- public function canRepair() {
47
  return false;
48
  }
49
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Utilities;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\ResultItem;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Common\ScanItemConsumer;
7
 
8
  /**
13
 
14
  use ScanItemConsumer;
15
 
 
 
 
 
 
16
  /**
17
  * @return bool
18
+ * @throws \Exception
19
  */
20
+ abstract public function repairItem() :bool;
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  /**
23
  * @return bool
24
  * @throws \Exception
25
  */
26
+ public function deleteItem() :bool {
27
+ throw new \Exception( 'Delete is not supported' );
28
+ }
29
 
30
  /**
31
  * @return bool
32
  * @throws \Exception
33
  */
34
+ public function canRepair() :bool {
35
  return false;
36
  }
37
  }
src/lib/src/Scans/Base/Utilities/ItemActionHandler.php CHANGED
@@ -5,6 +5,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Utilities;
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
 
8
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Common\ScanItemConsumer;
9
 
10
  abstract class ItemActionHandler {
@@ -32,22 +33,28 @@ abstract class ItemActionHandler {
32
  $success = $this->repair();
33
  break;
34
 
 
 
 
 
35
  default:
36
  throw new \Exception( 'Unsupported Scan Item Action' );
37
- break;
38
  }
39
  return $success;
40
  }
41
 
42
  /**
43
- * TODO: Determine if "delete" is always the same as a "repair" - see UFC override
44
  * @return bool
45
  * @throws \Exception
46
  */
47
  public function delete() {
48
- return $this->getRepairer()
49
- ->setAllowDelete( true )
50
- ->repairItem();
 
 
 
 
51
  }
52
 
53
  /**
@@ -55,9 +62,7 @@ abstract class ItemActionHandler {
55
  * @throws \Exception
56
  */
57
  public function ignore() {
58
- /** @var Scanner\EntryVO $oEntry */
59
- $oEntry = $this->getEntryVO();
60
- if ( empty( $oEntry ) ) {
61
  throw new \Exception( 'Item could not be found to ignore.' );
62
  }
63
 
@@ -65,13 +70,21 @@ abstract class ItemActionHandler {
65
  $mod = $this->getMod();
66
  /** @var Scanner\Update $updater */
67
  $updater = $mod->getDbHandler_ScanResults()->getQueryUpdater();
68
- if ( !$updater->setIgnored( $oEntry ) ) {
69
  throw new \Exception( 'Item could not be ignored at this time.' );
70
  }
71
 
72
  return true;
73
  }
74
 
 
 
 
 
 
 
 
 
75
  /**
76
  * @param bool $allowDelete
77
  * @return bool
@@ -79,49 +92,50 @@ abstract class ItemActionHandler {
79
  */
80
  public function repair( bool $allowDelete = false ) {
81
  $repairer = $this->getRepairer();
82
- if ( !$repairer->canRepair() ) {
83
- throw new \Exception( 'This item cannot be automatically repaired.' );
 
 
 
84
  }
 
 
 
 
85
 
86
- $repairer->setAllowDelete( $allowDelete );
 
 
 
87
 
88
- $item = $this->getScanItem();
89
- $item->repaired = $repairer->repairItem();
90
- $this->fireRepairEvent( $item->repaired );
91
 
92
  if ( $item->repaired ) {
93
  /** @var HackGuard\ModCon $mod */
94
  $mod = $this->getMod();
95
  /** @var Scanner\Delete $deleter */
96
- $deleter = $mod->getDbHandler_ScanResults()->getQueryDeleter();
97
- $deleter->filterByHash( $item->hash )
98
- ->filterByScan( $item->scan )
99
- ->query();
100
  }
101
 
102
  return $item->repaired;
103
  }
104
 
105
- /**
106
- * @return Scanner\EntryVO|null
107
- */
108
- protected function getEntryVO() {
109
- /** @var HackGuard\ModCon $mod */
110
- $mod = $this->getMod();
111
- /** @var Scanner\Select $selector */
112
- $selector = $mod->getDbHandler_ScanResults()->getQuerySelector();
113
- return $selector->filterByHash( $this->getScanItem()->hash )
114
- ->filterByScan( $this->getScanController()->getSlug() )
115
- ->first();
116
- }
117
-
118
  /**
119
  * @return BaseRepair|mixed
120
  */
121
  abstract public function getRepairer();
122
 
123
- /**
124
- * @param bool $success
125
- */
126
- abstract protected function fireRepairEvent( $success );
 
 
 
 
 
 
 
127
  }
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\ResultItem;
9
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Common\ScanItemConsumer;
10
 
11
  abstract class ItemActionHandler {
33
  $success = $this->repair();
34
  break;
35
 
36
+ case 'repair-delete':
37
+ $success = $this->repairDelete();
38
+ break;
39
+
40
  default:
41
  throw new \Exception( 'Unsupported Scan Item Action' );
 
42
  }
43
  return $success;
44
  }
45
 
46
  /**
 
47
  * @return bool
48
  * @throws \Exception
49
  */
50
  public function delete() {
51
+ $item = $this->getScanItem();
52
+ if ( $this->getRepairer()->deleteItem() ) {
53
+ $item->repaired = true;
54
+ $item->repair_event_status = 'delete_success';
55
+ }
56
+ $this->fireRepairEvent();
57
+ return $item->repaired;
58
  }
59
 
60
  /**
62
  * @throws \Exception
63
  */
64
  public function ignore() {
65
+ if ( empty( $this->getScanItem()->VO ) ) {
 
 
66
  throw new \Exception( 'Item could not be found to ignore.' );
67
  }
68
 
70
  $mod = $this->getMod();
71
  /** @var Scanner\Update $updater */
72
  $updater = $mod->getDbHandler_ScanResults()->getQueryUpdater();
73
+ if ( !$updater->setIgnored( $this->getScanItem()->VO ) ) {
74
  throw new \Exception( 'Item could not be ignored at this time.' );
75
  }
76
 
77
  return true;
78
  }
79
 
80
+ /**
81
+ * @return bool
82
+ * @throws \Exception
83
+ */
84
+ public function repairDelete() :bool {
85
+ throw new \Exception( 'Certain items cannot be automatically bulk repaired / deleted.' );
86
+ }
87
+
88
  /**
89
  * @param bool $allowDelete
90
  * @return bool
92
  */
93
  public function repair( bool $allowDelete = false ) {
94
  $repairer = $this->getRepairer();
95
+
96
+ $item = $this->getScanItem();
97
+
98
+ try {
99
+ $item->repaired = $repairer->repairItem();
100
  }
101
+ catch ( \Exception $e ) {
102
+ $item->repaired = false;
103
+ }
104
+ $item->repair_event_status = $item->repaired ? 'repair_success' : 'repair_fail';
105
 
106
+ if ( $allowDelete && !$item->repaired && $repairer->deleteItem() ) {
107
+ $item->repaired = true;
108
+ $item->repair_event_status = 'delete_success';
109
+ }
110
 
111
+ $this->fireRepairEvent();
 
 
112
 
113
  if ( $item->repaired ) {
114
  /** @var HackGuard\ModCon $mod */
115
  $mod = $this->getMod();
116
  /** @var Scanner\Delete $deleter */
117
+ $mod->getDbHandler_ScanResults()
118
+ ->getQueryDeleter()
119
+ ->deleteById( $item->VO->id );
 
120
  }
121
 
122
  return $item->repaired;
123
  }
124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  /**
126
  * @return BaseRepair|mixed
127
  */
128
  abstract public function getRepairer();
129
 
130
+ protected function fireRepairEvent() {
131
+ /** @var ResultItem $item */
132
+ $item = $this->getScanItem();
133
+
134
+ if ( !empty( $item->path_full ) && !empty( $item->repair_event_status ) ) {
135
+ $this->getCon()->fireEvent(
136
+ sprintf( 'scan_item_%s', $item->repair_event_status ),
137
+ [ 'audit' => [ 'path_full' => $item->path_full ] ]
138
+ );
139
+ }
140
+ }
141
  }
src/lib/src/Scans/Base/Utilities/ItemActionHandlerAssets.php CHANGED
@@ -21,15 +21,15 @@ abstract class ItemActionHandlerAssets extends ItemActionHandler {
21
  switch ( $action ) {
22
 
23
  case 'asset_deactivate':
24
- $bSuccess = $this->assetDeactivate();
25
  break;
26
 
27
  default:
28
- $bSuccess = parent::process( $action );
29
  break;
30
  }
31
 
32
- return $bSuccess;
33
  }
34
 
35
  /**
21
  switch ( $action ) {
22
 
23
  case 'asset_deactivate':
24
+ $success = $this->assetDeactivate();
25
  break;
26
 
27
  default:
28
+ $success = parent::process( $action );
29
  break;
30
  }
31
 
32
+ return $success;
33
  }
34
 
35
  /**
src/lib/src/Scans/Common/ScanActionConsumer.php CHANGED
@@ -19,11 +19,11 @@ trait ScanActionConsumer {
19
  }
20
 
21
  /**
22
- * @param BaseScanActionVO $oScanActionVO
23
  * @return $this
24
  */
25
- public function setScanActionVO( $oScanActionVO ) {
26
- $this->oScanActionVO = $oScanActionVO;
27
  return $this;
28
  }
29
  }
19
  }
20
 
21
  /**
22
+ * @param BaseScanActionVO $action
23
  * @return $this
24
  */
25
+ public function setScanActionVO( $action ) {
26
+ $this->oScanActionVO = $action;
27
  return $this;
28
  }
29
  }
src/lib/src/Scans/Common/ScanItemConsumer.php CHANGED
@@ -2,28 +2,28 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Common;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultItem;
6
 
7
  trait ScanItemConsumer {
8
 
9
  /**
10
- * @var BaseResultItem
11
  */
12
- private $oScanItem;
13
 
14
  /**
15
- * @return BaseResultItem|mixed
16
  */
17
  public function getScanItem() {
18
- return $this->oScanItem;
19
  }
20
 
21
  /**
22
- * @param BaseResultItem|mixed $oItem
23
  * @return $this
24
  */
25
- public function setScanItem( $oItem ) {
26
- $this->oScanItem = $oItem;
27
  return $this;
28
  }
29
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Common;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\ResultItem;
6
 
7
  trait ScanItemConsumer {
8
 
9
  /**
10
+ * @var ResultItem
11
  */
12
+ private $scanItem;
13
 
14
  /**
15
+ * @return ResultItem|mixed
16
  */
17
  public function getScanItem() {
18
+ return $this->scanItem;
19
  }
20
 
21
  /**
22
+ * @param ResultItem|mixed $item
23
  * @return $this
24
  */
25
+ public function setScanItem( $item ) {
26
+ $this->scanItem = $item;
27
  return $this;
28
  }
29
  }
src/lib/src/Scans/Helpers/CopyResultsSets.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultsSet;
6
 
7
  /**
8
  * Class CopyResultsSets
@@ -11,8 +11,8 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultsSet;
11
  class CopyResultsSets {
12
 
13
  /**
14
- * @param BaseResultsSet $oFromRS1
15
- * @param BaseResultsSet $oToRS2
16
  */
17
  public function copyTo( $oFromRS1, $oToRS2 ) {
18
  foreach ( $oFromRS1->getAllItems() as $oIt ) {
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\ResultsSet;
6
 
7
  /**
8
  * Class CopyResultsSets
11
  class CopyResultsSets {
12
 
13
  /**
14
+ * @param ResultsSet $oFromRS1
15
+ * @param ResultsSet $oToRS2
16
  */
17
  public function copyTo( $oFromRS1, $oToRS2 ) {
18
  foreach ( $oFromRS1->getAllItems() as $oIt ) {
src/lib/src/Scans/Helpers/MergeResultsSets.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultsSet;
6
 
7
  /**
8
  * Class MergeResultsSets
@@ -11,9 +11,9 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultsSet;
11
  class MergeResultsSets {
12
 
13
  /**
14
- * @param BaseResultsSet $oRs1
15
- * @param BaseResultsSet $oRs2
16
- * @return BaseResultsSet
17
  */
18
  public function merge( $oRs1, $oRs2 ) {
19
  $oNewRs = clone $oRs1;
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\ResultsSet;
6
 
7
  /**
8
  * Class MergeResultsSets
11
  class MergeResultsSets {
12
 
13
  /**
14
+ * @param ResultsSet $oRs1
15
+ * @param ResultsSet $oRs2
16
+ * @return ResultsSet
17
  */
18
  public function merge( $oRs1, $oRs2 ) {
19
  $oNewRs = clone $oRs1;
src/lib/src/Scans/Helpers/WpCoreFile.php CHANGED
@@ -22,21 +22,21 @@ class WpCoreFile {
22
  protected $sVersion;
23
 
24
  /**
25
- * @param string $sPath
26
  * @return bool
27
  */
28
- public function replace( $sPath ) {
29
- $bSuccess = false;
30
- if ( Services::CoreFileHashes()->isCoreFile( $sPath ) ) {
31
  $oFiles = Services::WpGeneral()->isClassicPress() ? new WpOrg\Cp\Files() : new WpOrg\Wp\Files();
32
  try {
33
- $oFiles->replaceFileFromVcs( $sPath );
34
- $bSuccess = true;
35
  }
36
  catch ( \InvalidArgumentException $e ) {
37
  }
38
  }
39
- return $bSuccess;
40
  }
41
 
42
  /**
22
  protected $sVersion;
23
 
24
  /**
25
+ * @param string $path
26
  * @return bool
27
  */
28
+ public function replace( $path ) :bool {
29
+ $success = false;
30
+ if ( Services::CoreFileHashes()->isCoreFile( $path ) ) {
31
  $oFiles = Services::WpGeneral()->isClassicPress() ? new WpOrg\Cp\Files() : new WpOrg\Wp\Files();
32
  try {
33
+ $oFiles->replaceFileFromVcs( $path );
34
+ $success = true;
35
  }
36
  catch ( \InvalidArgumentException $e ) {
37
  }
38
  }
39
+ return $success;
40
  }
41
 
42
  /**
src/lib/src/Scans/Mal/BuildFileMap.php CHANGED
@@ -2,16 +2,11 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Common\ScanActionConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers\StandardDirectoryIterator;
7
 
8
- /**
9
- * Class BuildFileMap
10
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal
11
- */
12
- class BuildFileMap {
13
-
14
- use ScanActionConsumer;
15
 
16
  /**
17
  * @return string[]
@@ -27,10 +22,10 @@ class BuildFileMap {
27
  try {
28
  foreach ( StandardDirectoryIterator::create( $scanDir, (int)$depth, $action->file_exts, false ) as $item ) {
29
  /** @var \SplFileInfo $item */
30
- $fullPath = wp_normalize_path( $item->getPathname() );
31
  try {
32
- if ( !$this->isWhitelistedPath( $fullPath ) && $item->getSize() > 0 ) {
33
- $files[] = $fullPath;
34
  }
35
  }
36
  catch ( \Exception $e ) {
@@ -66,18 +61,4 @@ class BuildFileMap {
66
  $action->paths_whitelisted = [];
67
  }
68
  }
69
-
70
- private function isWhitelistedPath( string $path ) :bool {
71
- $whitelisted = false;
72
-
73
- /** @var ScanActionVO $oAction */
74
- $oAction = $this->getScanActionVO();
75
- foreach ( $oAction->paths_whitelisted as $sWlPath ) {
76
- if ( stripos( $path, $sWlPath ) === 0 ) {
77
- $whitelisted = true;
78
- break;
79
- }
80
- }
81
- return $whitelisted;
82
- }
83
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseBuildFileMap;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Common\ScanActionConsumer;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers\StandardDirectoryIterator;
8
 
9
+ class BuildFileMap extends BaseBuildFileMap {
 
 
 
 
 
 
10
 
11
  /**
12
  * @return string[]
22
  try {
23
  foreach ( StandardDirectoryIterator::create( $scanDir, (int)$depth, $action->file_exts, false ) as $item ) {
24
  /** @var \SplFileInfo $item */
25
+ $path = wp_normalize_path( $item->getPathname() );
26
  try {
27
+ if ( !$this->isWhitelistedPath( $path ) && !$this->isAutoFilterFile( $item ) ) {
28
+ $files[] = $path;
29
  }
30
  }
31
  catch ( \Exception $e ) {
61
  $action->paths_whitelisted = [];
62
  }
63
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  }
src/lib/src/Scans/Mal/BuildScanAction.php CHANGED
@@ -8,20 +8,17 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
8
  class BuildScanAction extends Shield\Scans\Base\BaseBuildScanAction {
9
 
10
  protected function buildItems() {
11
- /** @var ScanActionVO $oAction */
12
- $oAction = $this->getScanActionVO();
13
- $oAction->items = ( new BuildFileMap() )
14
- ->setScanActionVO( $oAction )
15
  ->build();
 
 
16
  }
17
 
18
  protected function setCustomFields() {
19
- /** @var ScanActionVO $oAction */
20
- $oAction = $this->getScanActionVO();
21
- /** @var HackGuard\Options $oOpts */
22
- $oOpts = $this->getOptions();
23
-
24
- $oAction->paths_whitelisted = $oOpts->getMalWhitelistPaths();
25
- $oAction->file_exts = [ 'php', 'php5', 'php7' ];
26
  }
27
  }
8
  class BuildScanAction extends Shield\Scans\Base\BaseBuildScanAction {
9
 
10
  protected function buildItems() {
11
+ $items = ( new BuildFileMap() )
12
+ ->setMod( $this->getMod() )
13
+ ->setScanActionVO( $this->getScanActionVO() )
 
14
  ->build();
15
+ asort( $items );
16
+ $this->getScanActionVO()->items = $items;
17
  }
18
 
19
  protected function setCustomFields() {
20
+ /** @var ScanActionVO $action */
21
+ $action = $this->getScanActionVO();
22
+ $action->file_exts = [ 'php', 'php5', 'php7' ];
 
 
 
 
23
  }
24
  }
src/lib/src/Scans/Mal/FileScanner.php CHANGED
@@ -11,10 +11,6 @@ use FernleafSystems\Wordpress\Services\Core\VOs\Assets\{
11
  use FernleafSystems\Wordpress\Services\Services;
12
  use FernleafSystems\Wordpress\Services\Utilities;
13
 
14
- /**
15
- * Class FileScanner
16
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal
17
- */
18
  class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
19
 
20
  /**
@@ -43,27 +39,28 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
43
  foreach ( $action->patterns_simple as $signature ) {
44
  $item = $this->scanForSig( $signature );
45
  if ( $item instanceof ResultItem ) {
46
- return $item;
47
  }
48
  }
49
  }
50
 
51
- // RegEx Patterns
52
- if ( empty( $action->patterns_fullregex ) ) {
53
  $this->locator->setIsRegEx( true );
54
- foreach ( $action->patterns_regex as $signature ) {
55
- $item = $this->scanForSig( $signature );
56
- if ( $item instanceof ResultItem ) {
57
- return $item;
 
 
58
  }
59
  }
60
- }
61
- else { // Full regex patterns
62
- $this->locator->setIsRegEx( true );
63
- foreach ( $action->patterns_fullregex as $signature ) {
64
- $item = $this->scanForSig( $signature );
65
- if ( $item instanceof ResultItem ) {
66
- return $item;
67
  }
68
  }
69
  }
11
  use FernleafSystems\Wordpress\Services\Services;
12
  use FernleafSystems\Wordpress\Services\Utilities;
13
 
 
 
 
 
14
  class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
15
 
16
  /**
39
  foreach ( $action->patterns_simple as $signature ) {
40
  $item = $this->scanForSig( $signature );
41
  if ( $item instanceof ResultItem ) {
42
+ break;
43
  }
44
  }
45
  }
46
 
47
+ if ( !$item instanceof ResultItem ) {
48
+ // RegEx Patterns
49
  $this->locator->setIsRegEx( true );
50
+ if ( empty( $action->patterns_fullregex ) ) {
51
+ foreach ( $action->patterns_regex as $signature ) {
52
+ $item = $this->scanForSig( $signature );
53
+ if ( $item instanceof ResultItem ) {
54
+ break;
55
+ }
56
  }
57
  }
58
+ else { // Full regex patterns
59
+ foreach ( $action->patterns_fullregex as $signature ) {
60
+ $item = $this->scanForSig( $signature );
61
+ if ( $item instanceof ResultItem ) {
62
+ break;
63
+ }
 
64
  }
65
  }
66
  }
src/lib/src/Scans/Mal/ResultItem.php CHANGED
@@ -5,14 +5,12 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
5
  /**
6
  * Class ResultItem
7
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal
8
- * @property string $path_full
9
- * @property string $path_fragment - relative to ABSPATH
10
  * @property bool $is_mal
11
  * @property string $mal_sig
12
  * @property int[] $file_lines
13
  * @property int $fp_confidence - false positive confidence level
14
  */
15
- class ResultItem extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultItem {
16
 
17
  public function generateHash() :string {
18
  return md5( $this->path_full );
5
  /**
6
  * Class ResultItem
7
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal
 
 
8
  * @property bool $is_mal
9
  * @property string $mal_sig
10
  * @property int[] $file_lines
11
  * @property int $fp_confidence - false positive confidence level
12
  */
13
+ class ResultItem extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\FileResultItem {
14
 
15
  public function generateHash() :string {
16
  return md5( $this->path_full );
src/lib/src/Scans/Mal/ResultsSet.php CHANGED
@@ -9,7 +9,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
9
  * @property ResultItem[] $items
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal
11
  */
12
- class ResultsSet extends Base\BaseResultsSet {
13
 
14
  /**
15
  * @param ResultItem[] $aItems
9
  * @property ResultItem[] $items
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal
11
  */
12
+ class ResultsSet extends Base\ResultsSet {
13
 
14
  /**
15
  * @param ResultItem[] $aItems
src/lib/src/Scans/Mal/ScanActionVO.php CHANGED
@@ -16,5 +16,5 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
16
  class ScanActionVO extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseScanActionVO {
17
 
18
  const QUEUE_GROUP_SIZE_LIMIT = 50;
19
- const DEFAULT_SLEEP_SECONDS = 1;
20
  }
16
  class ScanActionVO extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseScanActionVO {
17
 
18
  const QUEUE_GROUP_SIZE_LIMIT = 50;
19
+ const DEFAULT_SLEEP_SECONDS = 0.1;
20
  }
src/lib/src/Scans/Mal/Utilities/ItemActionHandler.php CHANGED
@@ -1,9 +1,10 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal\Utilities;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
 
7
 
8
  class ItemActionHandler extends Base\Utilities\ItemActionHandler {
9
 
@@ -21,6 +22,14 @@ class ItemActionHandler extends Base\Utilities\ItemActionHandler {
21
  return true;
22
  }
23
 
 
 
 
 
 
 
 
 
24
  /**
25
  * @return Repair
26
  */
@@ -29,16 +38,4 @@ class ItemActionHandler extends Base\Utilities\ItemActionHandler {
29
  ->setScanItem( $this->getScanItem() )
30
  ->setMod( $this->getMod() );
31
  }
32
-
33
- /**
34
- * @param bool $success
35
- */
36
- protected function fireRepairEvent( $success ) {
37
- /** @var Mal\ResultItem $oItem */
38
- $oItem = $this->getScanItem();
39
- $this->getCon()->fireEvent(
40
- $this->getScanController()->getSlug().'_item_repair_'.( $success ? 'success' : 'fail' ),
41
- [ 'audit' => [ 'fragment' => $oItem->path_full ] ]
42
- );
43
- }
44
  }
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal\Utilities;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
7
+ use FernleafSystems\Wordpress\Services\Utilities\WpOrg;
8
 
9
  class ItemActionHandler extends Base\Utilities\ItemActionHandler {
10
 
22
  return true;
23
  }
24
 
25
+ /**
26
+ * @return bool
27
+ * @throws \Exception
28
+ */
29
+ public function repairDelete() :bool {
30
+ return $this->repair( true );
31
+ }
32
+
33
  /**
34
  * @return Repair
35
  */
38
  ->setScanItem( $this->getScanItem() )
39
  ->setMod( $this->getMod() );
40
  }
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
src/lib/src/Scans/Mal/Utilities/Patterns.php CHANGED
@@ -22,7 +22,7 @@ class Patterns {
22
  $mod = $this->getMod();
23
 
24
  $cacher = new Cache\CacheDefVO();
25
- $cacher->dir = $mod->getTempDir();
26
  if ( !empty( $cacher->dir ) ) {
27
  $cacher->file_fragment = 'cache_patterns.txt';
28
  $cacher->expiration = HOUR_IN_SECONDS;
@@ -50,9 +50,11 @@ class Patterns {
50
  }
51
 
52
  $cacher->data = $patterns;
53
- ( new Cache\StoreToCache() )
54
- ->setCacheDef( $cacher )
55
- ->store();
 
 
56
  }
57
 
58
  return $cacher->data;
22
  $mod = $this->getMod();
23
 
24
  $cacher = new Cache\CacheDefVO();
25
+ $cacher->dir = $mod->getScansTempDir();
26
  if ( !empty( $cacher->dir ) ) {
27
  $cacher->file_fragment = 'cache_patterns.txt';
28
  $cacher->expiration = HOUR_IN_SECONDS;
50
  }
51
 
52
  $cacher->data = $patterns;
53
+ if ( !empty( $cacher->dir ) ) {
54
+ ( new Cache\StoreToCache() )
55
+ ->setCacheDef( $cacher )
56
+ ->store();
57
+ }
58
  }
59
 
60
  return $cacher->data;
src/lib/src/Scans/Mal/Utilities/Repair.php CHANGED
@@ -15,10 +15,7 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
15
 
16
  use Shield\Modules\ModConsumer;
17
 
18
- /**
19
- * @return bool
20
- */
21
- public function repairItem() {
22
  /** @var ResultItem $item */
23
  $item = $this->getScanItem();
24
  /** @var Shield\Modules\HackGuard\Options $opts */
@@ -35,32 +32,28 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
35
  if ( $canRepair ) {
36
 
37
  if ( Services\Services::CoreFileHashes()->isCoreFile( $item->path_fragment ) ) {
38
- $success = $this->repairCoreItem( $item );
39
  }
40
  else {
41
  $plugin = ( new WpOrg\Plugin\Files() )->findPluginFromFile( $item->path_full );
42
  if ( $plugin instanceof Services\Core\VOs\Assets\WpPluginVo && $plugin->isWpOrg() ) {
43
 
44
- $success = $this->repairItemInPlugin( $item );
45
  }
46
  else {
47
  $theme = ( new WpOrg\Theme\Files() )->findThemeFromFile( $item->path_full );
48
  if ( $theme instanceof Services\Core\VOs\Assets\WpThemeVo && $theme->isWpOrg() ) {
49
 
50
- $success = $this->repairItemInTheme( $item );
51
  }
52
  elseif ( $opts->isMalAutoRepairSurgical() ) {
53
- $success = $this->repairSurgicalItem( $item );
54
  }
55
  }
56
  }
57
  }
58
- elseif ( $this->isAllowDelete() ) {
59
- $success = $this->repairItemByDelete( $item );
60
- }
61
 
62
  if ( $success ) {
63
- // 1) Report the file as being malware.
64
  ( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
65
  ->setMod( $this->getMod() )
66
  ->reportResultItem( $item, false );
@@ -69,59 +62,58 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
69
  return $success;
70
  }
71
 
72
- /**
73
- * @param ResultItem $item
74
- * @return bool
75
- */
76
- private function repairItemByDelete( $item ) {
77
- return Services\Services::WpFs()->deleteFile( $item->path_full );
78
  }
79
 
80
  /**
81
  * @return bool
82
  * @throws \Exception
83
  */
84
- public function canRepair() {
85
  return $this->canAutoRepairFromSource( $this->getScanItem() );
86
  }
87
 
88
  /**
89
  * Can only repair a WP Core file, or a plugin that is WP.org, has no update available
90
  * and the latest version uses SVN tags.
91
- * @param ResultItem $oItem
92
  * @return bool
93
  * @throws \Exception
94
  */
95
- public function canAutoRepairFromSource( $oItem ) {
96
- $bCanRepair = Services\Services::CoreFileHashes()->isCoreFile( $oItem->path_fragment );
97
- if ( !$bCanRepair ) {
98
 
99
- $oPlugin = ( new WpOrg\Plugin\Files() )->findPluginFromFile( $oItem->path_full );
100
- if ( $oPlugin instanceof Services\Core\VOs\Assets\WpPluginVo ) {
101
- if ( !$oPlugin->isWpOrg() ) {
 
 
 
102
  throw new \Exception( sprintf(
103
  __( "%s not installed from WordPress.org.", 'wp-simple-firewall' ),
104
  __( 'Plugin', 'wp-simple-firewall' )
105
  )
106
  );
107
  }
108
- if ( !$oPlugin->svn_uses_tags ) {
109
  throw new \Exception( __( "Plugin developer doesn't use SVN tags for official releases.", 'wp-simple-firewall' ) );
110
  }
111
 
112
- $bCanRepair = true;
113
  }
114
  else {
115
- $oTheme = ( new WpOrg\Theme\Files() )->findThemeFromFile( $oItem->path_full );
116
- if ( $oTheme instanceof Services\Core\VOs\Assets\WpThemeVo ) {
117
- if ( $oTheme->is_child ) {
118
  throw new \Exception( sprintf(
119
  __( "%s is a child of another theme.", 'wp-simple-firewall' ),
120
  __( 'Theme', 'wp-simple-firewall' )
121
  )
122
  );
123
  }
124
- if ( !$oTheme->isWpOrg() ) {
125
  throw new \Exception( sprintf(
126
  __( "%s not installed from WordPress.org.", 'wp-simple-firewall' ),
127
  __( 'Theme', 'wp-simple-firewall' )
@@ -129,67 +121,61 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
129
  );
130
  }
131
  if ( !( new WpOrg\Theme\Versions() )
132
- ->setWorkingSlug( $oTheme->stylesheet )
133
- ->exists( $oTheme->version, true ) ) {
134
  throw new \Exception( __( "Theme version doesn't appear to exist.", 'wp-simple-firewall' ) );
135
  }
136
 
137
- $bCanRepair = true;
138
  }
139
  }
140
  }
141
 
142
- return $bCanRepair;
143
  }
144
 
145
- /**
146
- * @param ResultItem $oItem
147
- * @return bool
148
- */
149
- private function repairCoreItem( $oItem ) {
150
- $oFiles = Services\Services::WpGeneral()->isClassicPress() ? new WpOrg\Cp\Files() : new WpOrg\Wp\Files();
151
  try {
152
- $bSuccess = $oFiles->replaceFileFromVcs( $oItem->path_fragment );
153
  }
154
  catch ( \InvalidArgumentException $e ) {
155
- $bSuccess = false;
156
  }
157
- return $bSuccess;
158
  }
159
 
160
- /**
161
- * @param ResultItem $oItem
162
- * @return bool
163
- */
164
- private function repairSurgicalItem( $oItem ) {
165
- $bSuccess = false;
166
- foreach ( $oItem->file_lines as $nLine ) {
167
  try {
168
- ( new Services\Utilities\File\RemoveLineFromFile() )->run( $oItem->path_full, $nLine );
169
- $bSuccess = true;
170
  }
171
  catch ( \Exception $e ) {
172
- $bSuccess = false;
173
  break;
174
  }
175
  }
176
- return $bSuccess;
177
  }
178
 
179
- /**
180
- * @param ResultItem $oItem
181
- * @return bool
182
- */
183
- private function repairItemInPlugin( $oItem ) {
184
  $success = false;
185
 
186
- $oFiles = new WpOrg\Plugin\Files();
187
  try {
188
- if ( $oFiles->isValidFileFromPlugin( $oItem->path_full ) ) {
189
- $success = $oFiles->replaceFileFromVcs( $oItem->path_full );
190
- }
191
- elseif ( $this->isAllowDelete() ) {
192
- $success = (bool)Services\Services::WpFs()->deleteFile( $oItem->path_full );
193
  }
194
  }
195
  catch ( \InvalidArgumentException $e ) {
@@ -198,20 +184,16 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
198
  return $success;
199
  }
200
 
201
- /**
202
- * @param ResultItem $oItem
203
- * @return bool
204
- */
205
- private function repairItemInTheme( $oItem ) {
206
  $success = false;
207
 
208
- $oFiles = new WpOrg\Theme\Files();
209
  try {
210
- if ( $oFiles->isValidFileFromTheme( $oItem->path_full ) ) {
211
- $success = $oFiles->replaceFileFromVcs( $oItem->path_full );
212
- }
213
- elseif ( $this->isAllowDelete() ) {
214
- $success = (bool)Services\Services::WpFs()->deleteFile( $oItem->path_full );
215
  }
216
  }
217
  catch ( \InvalidArgumentException $e ) {
15
 
16
  use Shield\Modules\ModConsumer;
17
 
18
+ public function repairItem() :bool {
 
 
 
19
  /** @var ResultItem $item */
20
  $item = $this->getScanItem();
21
  /** @var Shield\Modules\HackGuard\Options $opts */
32
  if ( $canRepair ) {
33
 
34
  if ( Services\Services::CoreFileHashes()->isCoreFile( $item->path_fragment ) ) {
35
+ $success = $this->repairCoreItem();
36
  }
37
  else {
38
  $plugin = ( new WpOrg\Plugin\Files() )->findPluginFromFile( $item->path_full );
39
  if ( $plugin instanceof Services\Core\VOs\Assets\WpPluginVo && $plugin->isWpOrg() ) {
40
 
41
+ $success = $this->repairItemInPlugin();
42
  }
43
  else {
44
  $theme = ( new WpOrg\Theme\Files() )->findThemeFromFile( $item->path_full );
45
  if ( $theme instanceof Services\Core\VOs\Assets\WpThemeVo && $theme->isWpOrg() ) {
46
 
47
+ $success = $this->repairItemInTheme();
48
  }
49
  elseif ( $opts->isMalAutoRepairSurgical() ) {
50
+ $success = $this->repairSurgicalItem();
51
  }
52
  }
53
  }
54
  }
 
 
 
55
 
56
  if ( $success ) {
 
57
  ( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
58
  ->setMod( $this->getMod() )
59
  ->reportResultItem( $item, false );
62
  return $success;
63
  }
64
 
65
+ public function deleteItem() :bool {
66
+ /** @var ResultItem $item */
67
+ $item = $this->getScanItem();
68
+ return (bool)Services\Services::WpFs()->deleteFile( $item->path_full );
 
 
69
  }
70
 
71
  /**
72
  * @return bool
73
  * @throws \Exception
74
  */
75
+ public function canRepair() :bool {
76
  return $this->canAutoRepairFromSource( $this->getScanItem() );
77
  }
78
 
79
  /**
80
  * Can only repair a WP Core file, or a plugin that is WP.org, has no update available
81
  * and the latest version uses SVN tags.
82
+ * @param ResultItem $item
83
  * @return bool
84
  * @throws \Exception
85
  */
86
+ public function canAutoRepairFromSource( ResultItem $item ) :bool {
 
 
87
 
88
+ $canRepair = Services\Services::CoreFileHashes()->isCoreFile( $item->path_fragment );
89
+ if ( !$canRepair ) {
90
+
91
+ $plugin = ( new WpOrg\Plugin\Files() )->findPluginFromFile( $item->path_full );
92
+ if ( !empty( $plugin ) ) {
93
+ if ( !$plugin->isWpOrg() ) {
94
  throw new \Exception( sprintf(
95
  __( "%s not installed from WordPress.org.", 'wp-simple-firewall' ),
96
  __( 'Plugin', 'wp-simple-firewall' )
97
  )
98
  );
99
  }
100
+ if ( !$plugin->svn_uses_tags ) {
101
  throw new \Exception( __( "Plugin developer doesn't use SVN tags for official releases.", 'wp-simple-firewall' ) );
102
  }
103
 
104
+ $canRepair = true;
105
  }
106
  else {
107
+ $theme = ( new WpOrg\Theme\Files() )->findThemeFromFile( $item->path_full );
108
+ if ( !empty( $theme ) ) {
109
+ if ( $theme->is_child ) {
110
  throw new \Exception( sprintf(
111
  __( "%s is a child of another theme.", 'wp-simple-firewall' ),
112
  __( 'Theme', 'wp-simple-firewall' )
113
  )
114
  );
115
  }
116
+ if ( !$theme->isWpOrg() ) {
117
  throw new \Exception( sprintf(
118
  __( "%s not installed from WordPress.org.", 'wp-simple-firewall' ),
119
  __( 'Theme', 'wp-simple-firewall' )
121
  );
122
  }
123
  if ( !( new WpOrg\Theme\Versions() )
124
+ ->setWorkingSlug( $theme->stylesheet )
125
+ ->exists( $theme->version, true ) ) {
126
  throw new \Exception( __( "Theme version doesn't appear to exist.", 'wp-simple-firewall' ) );
127
  }
128
 
129
+ $canRepair = true;
130
  }
131
  }
132
  }
133
 
134
+ return $canRepair;
135
  }
136
 
137
+ private function repairCoreItem() :bool {
138
+ /** @var ResultItem $item */
139
+ $item = $this->getScanItem();
140
+
141
+ $files = Services\Services::WpGeneral()->isClassicPress() ? new WpOrg\Cp\Files() : new WpOrg\Wp\Files();
 
142
  try {
143
+ $success = $files->replaceFileFromVcs( $item->path_fragment );
144
  }
145
  catch ( \InvalidArgumentException $e ) {
146
+ $success = false;
147
  }
148
+ return $success;
149
  }
150
 
151
+ private function repairSurgicalItem() :bool {
152
+ /** @var ResultItem $item */
153
+ $item = $this->getScanItem();
154
+
155
+ $success = false;
156
+ foreach ( $item->file_lines as $nLine ) {
 
157
  try {
158
+ ( new Services\Utilities\File\RemoveLineFromFile() )->run( $item->path_full, $nLine );
159
+ $success = true;
160
  }
161
  catch ( \Exception $e ) {
162
+ $success = false;
163
  break;
164
  }
165
  }
166
+ return $success;
167
  }
168
 
169
+ private function repairItemInPlugin() :bool {
170
+ /** @var ResultItem $item */
171
+ $item = $this->getScanItem();
172
+
 
173
  $success = false;
174
 
175
+ $files = new WpOrg\Plugin\Files();
176
  try {
177
+ if ( $files->isValidFileFromPlugin( $item->path_full ) ) {
178
+ $success = $files->replaceFileFromVcs( $item->path_full );
 
 
 
179
  }
180
  }
181
  catch ( \InvalidArgumentException $e ) {
184
  return $success;
185
  }
186
 
187
+ private function repairItemInTheme() :bool {
188
+ /** @var ResultItem $item */
189
+ $item = $this->getScanItem();
190
+
 
191
  $success = false;
192
 
193
+ $files = new WpOrg\Theme\Files();
194
  try {
195
+ if ( $files->isValidFileFromTheme( $item->path_full ) ) {
196
+ $success = $files->replaceFileFromVcs( $item->path_full );
 
 
 
197
  }
198
  }
199
  catch ( \InvalidArgumentException $e ) {
src/lib/src/Scans/Ptg/BuildFileMap.php CHANGED
@@ -2,35 +2,30 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Common\ScanActionConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers\StandardDirectoryIterator;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
- /**
10
- * Class BuildFileMap
11
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg
12
- */
13
- class BuildFileMap {
14
-
15
- use ScanActionConsumer;
16
 
17
  /**
18
  * @return string[]
19
  */
20
- public function build() {
21
- $aFiles = [];
22
 
23
- /** @var ScanActionVO $oAction */
24
- $oAction = $this->getScanActionVO();
25
 
26
- $sAbsPath = wp_normalize_path( ABSPATH );
27
- foreach ( $this->getScanRoots() as $sScanDir ) {
28
  try {
29
- foreach ( StandardDirectoryIterator::create( $sScanDir, 0, $oAction->file_exts ) as $oFsItem ) {
30
- /** @var \SplFileInfo $oFsItem */
 
31
  try {
32
- if ( $oFsItem->getSize() > 0 ) {
33
- $aFiles[] = str_replace( $sAbsPath, '', wp_normalize_path( $oFsItem->getPathname() ) );
34
  }
35
  }
36
  catch ( \Exception $e ) {
@@ -40,34 +35,31 @@ class BuildFileMap {
40
  catch ( \Exception $e ) {
41
  error_log(
42
  sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
43
- $oAction->scan, $sScanDir, $e->getMessage() )
44
  );
45
  }
46
  }
47
 
48
- return $aFiles;
49
  }
50
 
51
- /**
52
- * @return string[]
53
- */
54
- private function getScanRoots() {
55
- $aRoots = [];
56
 
57
- $oWpP = Services::WpPlugins();
58
- foreach ( $oWpP->getPluginsAsVo() as $oPlugin ) {
59
- if ( $oPlugin->active ) {
60
- $aRoots[] = $oPlugin->getInstallDir();
61
  }
62
  }
63
 
64
- $oWpT = Services::WpThemes();
65
- $oCurrent = $oWpT->getCurrent();
66
- $aRoots[] = $oCurrent->get_stylesheet_directory();
67
- if ( $oWpT->isActiveThemeAChild() ) {
68
- $aRoots[] = $oCurrent->get_template_directory();
69
  }
70
 
71
- return $aRoots;
72
  }
73
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseBuildFileMap;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers\StandardDirectoryIterator;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
+ class BuildFileMap extends BaseBuildFileMap {
 
 
 
 
 
 
10
 
11
  /**
12
  * @return string[]
13
  */
14
+ public function build() :array {
15
+ $files = [];
16
 
17
+ /** @var ScanActionVO $action */
18
+ $action = $this->getScanActionVO();
19
 
20
+ $abspath = wp_normalize_path( ABSPATH );
21
+ foreach ( $this->getScanRoots() as $dir ) {
22
  try {
23
+ foreach ( StandardDirectoryIterator::create( $dir, 0, $action->file_exts ) as $item ) {
24
+ /** @var \SplFileInfo $item */
25
+ $path = wp_normalize_path( $item->getPathname() );
26
  try {
27
+ if ( !$this->isWhitelistedPath( $path ) && !$this->isAutoFilterFile( $item ) ) {
28
+ $files[] = str_replace( $abspath, '', $path );
29
  }
30
  }
31
  catch ( \Exception $e ) {
35
  catch ( \Exception $e ) {
36
  error_log(
37
  sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
38
+ $action->scan, $dir, $e->getMessage() )
39
  );
40
  }
41
  }
42
 
43
+ return $files;
44
  }
45
 
46
+ private function getScanRoots() :array {
47
+ $roots = [];
 
 
 
48
 
49
+ $WPP = Services::WpPlugins();
50
+ foreach ( $WPP->getPluginsAsVo() as $plugin ) {
51
+ if ( $plugin->active ) {
52
+ $roots[] = $plugin->getInstallDir();
53
  }
54
  }
55
 
56
+ $WPT = Services::WpThemes();
57
+ $current = $WPT->getCurrent();
58
+ $roots[] = $current->get_stylesheet_directory();
59
+ if ( $WPT->isActiveThemeAChild() ) {
60
+ $roots[] = $current->get_template_directory();
61
  }
62
 
63
+ return $roots;
64
  }
65
  }
src/lib/src/Scans/Ptg/BuildScanAction.php CHANGED
@@ -1,4 +1,4 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg;
4
 
@@ -7,27 +7,22 @@ use FernleafSystems\Wordpress\Plugin\Shield;
7
  class BuildScanAction extends Shield\Scans\Base\BaseBuildScanAction {
8
 
9
  protected function buildItems() {
10
- /** @var ScanActionVO $oAction */
11
- $oAction = $this->getScanActionVO();
12
- $oAction->items = ( new BuildFileMap() )
13
- ->setScanActionVO( $oAction )
 
14
  ->build();
15
  }
16
 
17
  protected function setCustomFields() {
18
- /** @var ScanActionVO $oAction */
19
- $oAction = $this->getScanActionVO();
20
- $oAction->file_exts = $this->getFileExts();
21
  }
22
 
23
- /**
24
- * @return array
25
- */
26
- private function getFileExts() {
27
- $aFileExts = apply_filters(
28
- $this->getCon()->prefix( 'scan_ptg_file_exts' ),
29
- [ 'js', 'json', 'otf', 'svg', 'ttf', 'eot', 'woff', 'woff2', 'php', 'php5', 'php7', 'phtml' ]
30
- );
31
- return is_array( $aFileExts ) ? $aFileExts : [];
32
  }
33
  }
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg;
4
 
7
  class BuildScanAction extends Shield\Scans\Base\BaseBuildScanAction {
8
 
9
  protected function buildItems() {
10
+ /** @var ScanActionVO $action */
11
+ $action = $this->getScanActionVO();
12
+ $action->items = ( new BuildFileMap() )
13
+ ->setMod( $this->getMod() )
14
+ ->setScanActionVO( $action )
15
  ->build();
16
  }
17
 
18
  protected function setCustomFields() {
19
+ /** @var ScanActionVO $action */
20
+ $action = $this->getScanActionVO();
21
+ $action->file_exts = $this->getFileExts();
22
  }
23
 
24
+ private function getFileExts() :array {
25
+ $ext = apply_filters( 'shield/scan_ptg_file_exts', $this->getOptions()->getDef( 'file_scan_extensions' ) );
26
+ return is_array( $ext ) ? $ext : $this->getOptions()->getDef( 'file_scan_extensions' );
 
 
 
 
 
 
27
  }
28
  }
src/lib/src/Scans/Ptg/FileScanner.php CHANGED
@@ -6,6 +6,7 @@ use FernleafSystems\Wordpress\Plugin\Shield;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib;
7
  use FernleafSystems\Wordpress\Services\Core\VOs\Assets;
8
  use FernleafSystems\Wordpress\Services\Utilities\File\Compare\CompareHash;
 
9
  use FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes\CrowdSourcedHashes\Query;
10
  use FernleafSystems\Wordpress\Services\Utilities\WpOrg\Plugin;
11
  use FernleafSystems\Wordpress\Services\Utilities\WpOrg\Theme;
@@ -21,13 +22,14 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
21
  */
22
  private $assetStore;
23
 
 
 
24
  /**
25
  * @param string $fullPath - in this case it's relative to ABSPATH
26
  * @return ResultItem|null
27
  */
28
  public function scan( string $fullPath ) {
29
  $item = null;
30
- // file paths are stored in the queue relatives to ABSPATH
31
  $fullPath = path_join( wp_normalize_path( ABSPATH ), $fullPath );
32
  try {
33
  $asset = ( new Plugin\Files() )->findPluginFromFile( $fullPath );
@@ -38,7 +40,12 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
38
  throw new \Exception( sprintf( 'Could not load asset for: %s', $fullPath ) );
39
  }
40
 
41
- $item = $this->scanWithStore( $fullPath, $asset );
 
 
 
 
 
42
  }
43
  catch ( \Exception $e ) {
44
  error_log( $e->getMessage() );
@@ -55,6 +62,39 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
55
  */
56
  private function scanWithStore( string $fullPath, $asset ) {
57
  $assetHashes = $this->getStore( $asset )->getSnapData();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  $pathFragment = str_replace( $asset->getInstallDir(), '', $fullPath );
59
  if ( empty( $assetHashes[ $pathFragment ] ) ) {
60
  $item = $this->getNewItem( $asset, $fullPath );
@@ -73,6 +113,8 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
73
  }
74
 
75
  /**
 
 
76
  * @param string $fullPath
77
  * @param Assets\WpPluginVo|Assets\WpThemeVo $asset
78
  * @return ResultItem|null
@@ -80,17 +122,24 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
80
  */
81
  private function scanWithCsHashes( string $fullPath, $asset ) {
82
  $assetHashes = $this->loadCsHashes( $asset );
83
- $pathFragment = str_replace( $asset->getInstallDir(), '', $fullPath );
 
 
84
 
85
  $item = null;
86
- if ( empty( $assetHashes[ $pathFragment ] ) ) {
 
 
 
 
 
87
  $item = $this->getNewItem( $asset, $fullPath );
88
- $item->path_fragment = $pathFragment;
89
  $item->is_unrecognised = true;
90
  }
91
  else {
92
  $found = false;
93
- foreach ( $assetHashes[ $pathFragment ] as $hash ) {
94
  if ( ( new CompareHash() )->isEqualFileSha1( $fullPath, $hash ) ) {
95
  $found = true;
96
  break;
@@ -99,37 +148,34 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
99
 
100
  if ( !$found ) {
101
  $item = $this->getNewItem( $asset, $fullPath );
102
- $item->path_fragment = $pathFragment;
103
  $item->is_different = true;
104
  }
105
  }
 
106
  return $item;
107
  }
108
 
109
- private function loadCsHashes( $asset ) {
110
- $uniqueId = md5( ( $asset instanceof Assets\WpPluginVo ) ? $asset->file : $asset->stylesheet );
111
- $tmpFileHandler = ( new Shield\Utilities\Tool\TmpFileStore() )
112
- ->setCon( $this->getCon() );
 
 
 
 
 
 
113
 
114
- $hashes = $tmpFileHandler->load( $uniqueId );
115
- if ( empty( $hashes ) ) {
116
- $hashesResponse = ( $asset instanceof Assets\WpPluginVo ? new Query\Plugin() : new Query\Theme() )
117
  ->getHashesFromVO( $asset );
118
- if ( !empty( $hashesResponse[ 'hashes' ] ) ) {
119
- $hashes = [ 'hashes' ];
120
- $tmpFileHandler->store( $uniqueId, $hashes );
121
- }
122
  }
123
- return $hashes;
124
- }
125
 
126
- /**
127
- * @param Assets\WpPluginVo|Assets\WpThemeVo $asset
128
- * @return string[]
129
- * @throws \Exception
130
- */
131
- private function getCSHashes( $asset ) {
132
- return $this->getStore( $asset )->getSnapData();
133
  }
134
 
135
  /**
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib;
7
  use FernleafSystems\Wordpress\Services\Core\VOs\Assets;
8
  use FernleafSystems\Wordpress\Services\Utilities\File\Compare\CompareHash;
9
+ use FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes;
10
  use FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes\CrowdSourcedHashes\Query;
11
  use FernleafSystems\Wordpress\Services\Utilities\WpOrg\Plugin;
12
  use FernleafSystems\Wordpress\Services\Utilities\WpOrg\Theme;
22
  */
23
  private $assetStore;
24
 
25
+ private $csHashes;
26
+
27
  /**
28
  * @param string $fullPath - in this case it's relative to ABSPATH
29
  * @return ResultItem|null
30
  */
31
  public function scan( string $fullPath ) {
32
  $item = null;
 
33
  $fullPath = path_join( wp_normalize_path( ABSPATH ), $fullPath );
34
  try {
35
  $asset = ( new Plugin\Files() )->findPluginFromFile( $fullPath );
40
  throw new \Exception( sprintf( 'Could not load asset for: %s', $fullPath ) );
41
  }
42
 
43
+ try {
44
+ $item = $this->scanWithCsHashes( $fullPath, $asset );
45
+ }
46
+ catch ( \Exception $eScan ) {
47
+ $item = $this->scanWithStore( $fullPath, $asset );
48
+ }
49
  }
50
  catch ( \Exception $e ) {
51
  error_log( $e->getMessage() );
62
  */
63
  private function scanWithStore( string $fullPath, $asset ) {
64
  $assetHashes = $this->getStore( $asset )->getSnapData();
65
+ if ( empty( $assetHashes ) ) {
66
+ throw new \Exception( 'File hashes from store is empty' );
67
+ }
68
+ $pathFragment = str_replace( strtolower( $asset->getInstallDir() ), '', $fullPath );
69
+ if ( empty( $assetHashes[ $pathFragment ] ) ) {
70
+ $item = $this->getNewItem( $asset, $fullPath );
71
+ $item->path_fragment = $pathFragment;
72
+ $item->is_unrecognised = true;
73
+ }
74
+ elseif ( !( new CompareHash() )->isEqualFileMd5( $fullPath, $assetHashes[ $pathFragment ] ) ) {
75
+ $item = $this->getNewItem( $asset, $fullPath );
76
+ $item->path_fragment = $pathFragment;
77
+ $item->is_different = true;
78
+ }
79
+ else {
80
+ $item = null;
81
+ }
82
+ return $item;
83
+ }
84
+
85
+ /**
86
+ * @param string $fullPath
87
+ * @param Assets\WpPluginVo|Assets\WpThemeVo $asset
88
+ * @return ResultItem|null
89
+ * @throws \InvalidArgumentException|\Exception
90
+ */
91
+ private function scanWithHashes( string $fullPath, $asset ) {
92
+ $assetHashes = ( $asset->asset_type === 'plugin' ) ?
93
+ ( new WpHashes\Hashes\Plugin() )->getPluginHashes( $asset )
94
+ : ( new WpHashes\Hashes\Theme() )->getThemeHashes( $asset );
95
+ if ( empty( $assetHashes ) ) {
96
+ throw new \Exception( 'File hashes from WPHashes is empty' );
97
+ }
98
  $pathFragment = str_replace( $asset->getInstallDir(), '', $fullPath );
99
  if ( empty( $assetHashes[ $pathFragment ] ) ) {
100
  $item = $this->getNewItem( $asset, $fullPath );
113
  }
114
 
115
  /**
116
+ * CSHashes path fragments are all lower-case
117
+ * We need to main the true, unchanged fullpath to the file, while looking up with the lower-case path fragment.
118
  * @param string $fullPath
119
  * @param Assets\WpPluginVo|Assets\WpThemeVo $asset
120
  * @return ResultItem|null
122
  */
123
  private function scanWithCsHashes( string $fullPath, $asset ) {
124
  $assetHashes = $this->loadCsHashes( $asset );
125
+ if ( empty( $assetHashes ) ) {
126
+ throw new \Exception( 'Could not retrieve CS Hashes' );
127
+ }
128
 
129
  $item = null;
130
+
131
+ $trueFragment = str_replace( $asset->getInstallDir(), '', $fullPath );
132
+
133
+ // we must use a lower-case key for "scan", but can't use this anywhere else.
134
+ $scanFragment = strtolower( $trueFragment );
135
+ if ( empty( $assetHashes[ $scanFragment ] ) ) {
136
  $item = $this->getNewItem( $asset, $fullPath );
137
+ $item->path_fragment = $trueFragment;
138
  $item->is_unrecognised = true;
139
  }
140
  else {
141
  $found = false;
142
+ foreach ( $assetHashes[ $scanFragment ] as $hash ) {
143
  if ( ( new CompareHash() )->isEqualFileSha1( $fullPath, $hash ) ) {
144
  $found = true;
145
  break;
148
 
149
  if ( !$found ) {
150
  $item = $this->getNewItem( $asset, $fullPath );
151
+ $item->path_fragment = $trueFragment;
152
  $item->is_different = true;
153
  }
154
  }
155
+
156
  return $item;
157
  }
158
 
159
+ /**
160
+ * We "cache" the hashes temporarily in this current load
161
+ * @param Assets\WpPluginVo|Assets\WpThemeVo $asset
162
+ * @return string[][]
163
+ */
164
+ private function loadCsHashes( $asset ) :array {
165
+
166
+ if ( is_array( $this->csHashes ) && $asset->unique_id !== $this->csHashes[ 0 ] ) {
167
+ unset( $this->csHashes );
168
+ }
169
 
170
+ if ( empty( $this->csHashes ) ) {
171
+
172
+ $hashes = ( $asset->asset_type === 'plugin' ? new Query\Plugin() : new Query\Theme() )
173
  ->getHashesFromVO( $asset );
174
+
175
+ $this->csHashes = [ $asset->unique_id, is_array( $hashes ) ? $hashes : [] ];
 
 
176
  }
 
 
177
 
178
+ return $this->csHashes[ 1 ];
 
 
 
 
 
 
179
  }
180
 
181
  /**
src/lib/src/Scans/Ptg/ResultItem.php CHANGED
@@ -5,15 +5,13 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg;
5
  /**
6
  * Class ResultItem
7
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg
8
- * @property string $path_full
9
- * @property string $path_fragment
10
  * @property string $slug
11
  * @property string $context
12
  * @property string $is_unrecognised
13
  * @property string $is_different
14
  * @property string $is_missing
15
  */
16
- class ResultItem extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultItem {
17
 
18
  public function generateHash() :string {
19
  return md5( $this->path_full );
5
  /**
6
  * Class ResultItem
7
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg
 
 
8
  * @property string $slug
9
  * @property string $context
10
  * @property string $is_unrecognised
11
  * @property string $is_different
12
  * @property string $is_missing
13
  */
14
+ class ResultItem extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\FileResultItem {
15
 
16
  public function generateHash() :string {
17
  return md5( $this->path_full );
src/lib/src/Scans/Ptg/ResultsSet.php CHANGED
@@ -9,7 +9,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
9
  * @property ResultItem[] $items
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg
11
  */
12
- class ResultsSet extends Base\BaseResultsSet {
13
 
14
  /**
15
  * @var string
@@ -93,18 +93,18 @@ class ResultsSet extends Base\BaseResultsSet {
93
  }
94
 
95
  /**
96
- * @param string $sContext
97
  * @return ResultsSet
98
  */
99
- public function getResultsForContext( $sContext ) {
100
- $oRs = new ResultsSet();
101
- foreach ( $this->getAllItems() as $oItem ) {
102
- /** @var ResultItem $oItem */
103
- if ( $oItem->context == $sContext ) {
104
- $oRs->addItem( $oItem );
105
  }
106
  }
107
- return $oRs;
108
  }
109
 
110
  /**
9
  * @property ResultItem[] $items
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg
11
  */
12
+ class ResultsSet extends Base\ResultsSet {
13
 
14
  /**
15
  * @var string
93
  }
94
 
95
  /**
96
+ * @param string $context
97
  * @return ResultsSet
98
  */
99
+ public function getResultsForContext( $context ) {
100
+ $results = new ResultsSet();
101
+ foreach ( $this->getAllItems() as $item ) {
102
+ /** @var ResultItem $item */
103
+ if ( $item->context == $context ) {
104
+ $results->addItem( $item );
105
  }
106
  }
107
+ return $results;
108
  }
109
 
110
  /**
src/lib/src/Scans/Ptg/ScanFromFileMap.php CHANGED
@@ -13,17 +13,17 @@ class ScanFromFileMap extends BaseScanFromFileMap {
13
  /**
14
  * @var FileScanner
15
  */
16
- private $oFileScanner;
17
 
18
  /**
19
  * @return FileScanner
20
  */
21
  protected function getFileScanner() {
22
- if ( empty( $this->oFileScanner ) ) {
23
- $this->oFileScanner = ( new FileScanner() )
24
  ->setMod( $this->getMod() )
25
  ->setScanActionVO( $this->getScanActionVO() );
26
  }
27
- return $this->oFileScanner;
28
  }
29
  }
13
  /**
14
  * @var FileScanner
15
  */
16
+ private $fileScanner;
17
 
18
  /**
19
  * @return FileScanner
20
  */
21
  protected function getFileScanner() {
22
+ if ( empty( $this->fileScanner ) ) {
23
+ $this->fileScanner = ( new FileScanner() )
24
  ->setMod( $this->getMod() )
25
  ->setScanActionVO( $this->getScanActionVO() );
26
  }
27
+ return $this->fileScanner;
28
  }
29
  }
src/lib/src/Scans/Ptg/Utilities/ItemActionHandler.php CHANGED
@@ -19,19 +19,19 @@ class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
19
  switch ( $action ) {
20
 
21
  case 'asset_accept':
22
- $bSuccess = $this->assetAccept();
23
  break;
24
 
25
  case 'asset_reinstall':
26
- $bSuccess = $this->assetReinstall();
27
  break;
28
 
29
  default:
30
- $bSuccess = parent::process( $action );
31
  break;
32
  }
33
 
34
- return $bSuccess;
35
  }
36
 
37
  /**
@@ -45,10 +45,10 @@ class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
45
  /** @var Ptg\ResultItem $item */
46
  $item = $this->getScanItem();
47
 
48
- foreach ( $results->getItemsForSlug( $item->slug ) as $oItem ) {
49
  $tmpHandler = clone $this;
50
- $tmpHandler->setScanItem( $oItem )
51
- ->ignore();
52
  }
53
 
54
  ( new Snapshots\StoreAction\Build() )
@@ -99,21 +99,18 @@ class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
99
  }
100
 
101
  /**
102
- * @return Repair
 
 
103
  */
104
- public function getRepairer() {
105
- return ( new Repair() )->setScanItem( $this->getScanItem() );
106
  }
107
 
108
  /**
109
- * @param bool $success
110
  */
111
- protected function fireRepairEvent( $success ) {
112
- /** @var Ptg\ResultItem $oItem */
113
- $oItem = $this->getScanItem();
114
- $this->getCon()->fireEvent(
115
- $this->getScanController()->getSlug().'_item_repair_'.( $success ? 'success' : 'fail' ),
116
- [ 'audit' => [ 'fragment' => $oItem->path_full ] ]
117
- );
118
  }
119
  }
19
  switch ( $action ) {
20
 
21
  case 'asset_accept':
22
+ $success = $this->assetAccept();
23
  break;
24
 
25
  case 'asset_reinstall':
26
+ $success = $this->assetReinstall();
27
  break;
28
 
29
  default:
30
+ $success = parent::process( $action );
31
  break;
32
  }
33
 
34
+ return $success;
35
  }
36
 
37
  /**
45
  /** @var Ptg\ResultItem $item */
46
  $item = $this->getScanItem();
47
 
48
+ foreach ( $results->getItemsForSlug( $item->slug ) as $item ) {
49
  $tmpHandler = clone $this;
50
+ $tmpHandler->setScanItem( $item )
51
+ ->ignore();
52
  }
53
 
54
  ( new Snapshots\StoreAction\Build() )
99
  }
100
 
101
  /**
102
+ * Repair PTG item if it's repairable, or it's unrecognised (i.e. delete)
103
+ * @return bool
104
+ * @throws \Exception
105
  */
106
+ public function repairDelete() :bool {
107
+ return $this->repair( true );
108
  }
109
 
110
  /**
111
+ * @return Repair
112
  */
113
+ public function getRepairer() {
114
+ return ( new Repair() )->setScanItem( $this->getScanItem() );
 
 
 
 
 
115
  }
116
  }
src/lib/src/Scans/Ptg/Utilities/Repair.php CHANGED
@@ -4,10 +4,6 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg\Utilities;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg;
7
- use FernleafSystems\Wordpress\Services\Core\VOs\Assets\{
8
- WpPluginVo,
9
- WpThemeVo
10
- };
11
  use FernleafSystems\Wordpress\Services\Services;
12
  use FernleafSystems\Wordpress\Services\Utilities\WpOrg;
13
 
@@ -17,79 +13,65 @@ use FernleafSystems\Wordpress\Services\Utilities\WpOrg;
17
  */
18
  class Repair extends Scans\Base\Utilities\BaseRepair {
19
 
 
 
 
 
 
 
20
  /**
21
  * @return bool
22
  * @throws \Exception
23
  */
24
- public function repairItem() {
25
  /** @var Ptg\ResultItem $item */
26
  $item = $this->getScanItem();
27
 
28
  if ( $this->canRepair() ) {
29
- if ( $item->context == 'plugins' ) {
30
- $bSuccess = $this->repairPluginFile( $item->path_full );
31
- }
32
- else {
33
- $bSuccess = $this->repairThemeFile( $item->path_full );
34
- }
35
  }
36
  else {
37
- $bSuccess = false;
38
  }
39
 
40
- return $bSuccess;
41
  }
42
 
43
- /**
44
- * @param string $sPath
45
- * @return bool
46
- */
47
- private function repairPluginFile( $sPath ) {
48
  $success = false;
49
  $files = new WpOrg\Plugin\Files();
50
  try {
51
- if ( $files->isValidFileFromPlugin( $sPath ) ) {
52
- $success = $files->replaceFileFromVcs( $sPath );
53
- }
54
- elseif ( $this->isAllowDelete() ) {
55
- $success = (bool)Services::WpFs()->deleteFile( $sPath );
56
  }
57
  }
58
  catch ( \InvalidArgumentException $e ) {
59
  }
60
- return (bool)$success;
61
  }
62
 
63
- /**
64
- * @param string $path
65
- * @return bool
66
- */
67
- private function repairThemeFile( $path ) {
68
  $success = false;
69
  $files = new WpOrg\Theme\Files();
70
  try {
71
  if ( $files->isValidFileFromTheme( $path ) ) {
72
  $success = $files->replaceFileFromVcs( $path );
73
  }
74
- elseif ( $this->isAllowDelete() ) {
75
- $success = (bool)Services::WpFs()->deleteFile( $path );
76
- }
77
  }
78
  catch ( \InvalidArgumentException $e ) {
79
  }
80
  return $success;
81
  }
82
 
83
- /**
84
- * @return bool
85
- */
86
- public function canRepair() {
87
  /** @var Ptg\ResultItem $item */
88
  $item = $this->getScanItem();
89
  if ( $item->context == 'plugins' ) {
90
  $asset = Services::WpPlugins()->getPluginAsVo( $item->slug );
91
  $canRepair = $asset->asset_type === 'plugin'
92
- && $asset->isWpOrg() && $asset->svn_uses_tags;
93
  }
94
  else {
95
  $asset = Services::WpThemes()->getThemeAsVo( $item->slug );
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg;
 
 
 
 
7
  use FernleafSystems\Wordpress\Services\Services;
8
  use FernleafSystems\Wordpress\Services\Utilities\WpOrg;
9
 
13
  */
14
  class Repair extends Scans\Base\Utilities\BaseRepair {
15
 
16
+ public function deleteItem() :bool {
17
+ /** @var Ptg\ResultItem $item */
18
+ $item = $this->getScanItem();
19
+ return $item->is_unrecognised && (bool)Services::WpFs()->deleteFile( $item->path_full );
20
+ }
21
+
22
  /**
23
  * @return bool
24
  * @throws \Exception
25
  */
26
+ public function repairItem() :bool {
27
  /** @var Ptg\ResultItem $item */
28
  $item = $this->getScanItem();
29
 
30
  if ( $this->canRepair() ) {
31
+ $success = ( $item->context == 'plugins' ) ?
32
+ $this->repairPluginFile( $item->path_full )
33
+ : $this->repairThemeFile( $item->path_full );
 
 
 
34
  }
35
  else {
36
+ $success = false;
37
  }
38
 
39
+ return $success;
40
  }
41
 
42
+ private function repairPluginFile( string $path ) :bool {
 
 
 
 
43
  $success = false;
44
  $files = new WpOrg\Plugin\Files();
45
  try {
46
+ if ( $files->isValidFileFromPlugin( $path ) ) {
47
+ $success = $files->replaceFileFromVcs( $path );
 
 
 
48
  }
49
  }
50
  catch ( \InvalidArgumentException $e ) {
51
  }
52
+ return $success;
53
  }
54
 
55
+ private function repairThemeFile( string $path ) :bool {
 
 
 
 
56
  $success = false;
57
  $files = new WpOrg\Theme\Files();
58
  try {
59
  if ( $files->isValidFileFromTheme( $path ) ) {
60
  $success = $files->replaceFileFromVcs( $path );
61
  }
 
 
 
62
  }
63
  catch ( \InvalidArgumentException $e ) {
64
  }
65
  return $success;
66
  }
67
 
68
+ public function canRepair() :bool {
 
 
 
69
  /** @var Ptg\ResultItem $item */
70
  $item = $this->getScanItem();
71
  if ( $item->context == 'plugins' ) {
72
  $asset = Services::WpPlugins()->getPluginAsVo( $item->slug );
73
  $canRepair = $asset->asset_type === 'plugin'
74
+ && $asset->isWpOrg() && $asset->svn_uses_tags;
75
  }
76
  else {
77
  $asset = Services::WpThemes()->getThemeAsVo( $item->slug );
src/lib/src/Scans/Ufc/BuildFileMap.php CHANGED
@@ -2,32 +2,28 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc;
4
 
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Common\ScanActionConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers\StandardDirectoryIterator;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
- /**
10
- * Class BuildFileMap
11
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc
12
- */
13
- class BuildFileMap {
14
-
15
- use ScanActionConsumer;
16
 
17
  /**
18
  * @return string[]
19
  */
20
- public function build() {
21
- $aFiles = [];
22
- $oHashes = Services::CoreFileHashes();
23
- if ( !$oHashes->isReady() ) {
24
- return $aFiles;
25
  }
26
 
27
- /** @var ScanActionVO $oAction */
28
- $oAction = $this->getScanActionVO();
29
 
30
- foreach ( $oAction->scan_dirs as $sScanDir => $aFileExts ) {
31
  try {
32
  /**
33
  * The filter handles the bulk of the file inclusions and exclusions
@@ -36,21 +32,21 @@ class BuildFileMap {
36
  * The filter will also be responsible (in this case) for filtering out
37
  * WP Core files from the collection of files to be assessed
38
  */
39
- foreach ( StandardDirectoryIterator::create( $sScanDir, 0, $aFileExts, true ) as $oFsItem ) {
40
- /** @var \SplFileInfo $oFsItem */
41
- $sFullPath = $oFsItem->getPathname();
42
- if ( !$oHashes->isCoreFile( $sFullPath ) ) {
43
- $aFiles[] = wp_normalize_path( $sFullPath );
44
  }
45
  }
46
  }
47
  catch ( \Exception $e ) {
48
  error_log(
49
  sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
50
- $oAction->scan, $sScanDir, $e->getMessage() )
51
  );
52
  }
53
  }
54
- return $aFiles;
55
  }
56
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Options;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseBuildFileMap;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Common\ScanActionConsumer;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers\StandardDirectoryIterator;
9
  use FernleafSystems\Wordpress\Services\Services;
10
 
11
+ class BuildFileMap extends BaseBuildFileMap {
 
 
 
 
 
 
12
 
13
  /**
14
  * @return string[]
15
  */
16
+ public function build() :array {
17
+ $files = [];
18
+ $coreHashes = Services::CoreFileHashes();
19
+ if ( !$coreHashes->isReady() ) {
20
+ return $files;
21
  }
22
 
23
+ /** @var ScanActionVO $action */
24
+ $action = $this->getScanActionVO();
25
 
26
+ foreach ( $action->scan_dirs as $dir => $fileExts ) {
27
  try {
28
  /**
29
  * The filter handles the bulk of the file inclusions and exclusions
32
  * The filter will also be responsible (in this case) for filtering out
33
  * WP Core files from the collection of files to be assessed
34
  */
35
+ foreach ( StandardDirectoryIterator::create( $dir, 0, $fileExts, true ) as $file ) {
36
+ /** @var \SplFileInfo $file */
37
+ $path = wp_normalize_path( $file->getPathname() );
38
+ if ( !$coreHashes->isCoreFile( $path ) && !$this->isWhitelistedPath( $path ) && !$this->isAutoFilterFile( $file ) ) {
39
+ $files[] = wp_normalize_path( $path );
40
  }
41
  }
42
  }
43
  catch ( \Exception $e ) {
44
  error_log(
45
  sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
46
+ $action->scan, $dir, $e->getMessage() )
47
  );
48
  }
49
  }
50
+ return $files;
51
  }
52
  }
src/lib/src/Scans/Ufc/BuildScanAction.php CHANGED
@@ -11,10 +11,11 @@ use FernleafSystems\Wordpress\Plugin\Shield;
11
  class BuildScanAction extends Shield\Scans\Base\BaseBuildScanAction {
12
 
13
  protected function buildItems() {
14
- /** @var ScanActionVO $oAction */
15
- $oAction = $this->getScanActionVO();
16
- $oAction->items = ( new Shield\Scans\Ufc\BuildFileMap() )
17
- ->setScanActionVO( $oAction )
 
18
  ->build();
19
  }
20
 
11
  class BuildScanAction extends Shield\Scans\Base\BaseBuildScanAction {
12
 
13
  protected function buildItems() {
14
+ /** @var ScanActionVO $action */
15
+ $action = $this->getScanActionVO();
16
+ $action->items = ( new Shield\Scans\Ufc\BuildFileMap() )
17
+ ->setMod( $this->getMod() )
18
+ ->setScanActionVO( $action )
19
  ->build();
20
  }
21
 
src/lib/src/Scans/Ufc/ConvertVosToResults.php DELETED
@@ -1,33 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\EntryVO;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
7
-
8
- /**
9
- * Class ConvertVosToResults
10
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc
11
- */
12
- class ConvertVosToResults extends Scans\Base\BaseConvertVosToResults {
13
-
14
- /**
15
- * @param EntryVO[] $VOs
16
- * @return ResultsSet
17
- */
18
- public function convert( $VOs ) {
19
- $oRes = new ResultsSet();
20
- foreach ( $VOs as $oVo ) {
21
- $oRes->addItem( $this->convertItem( $oVo ) );
22
- }
23
- return $oRes;
24
- }
25
-
26
- /**
27
- * @param EntryVO $VO
28
- * @return ResultItem
29
- */
30
- public function convertItem( $VO ) {
31
- return ( new ResultItem() )->applyFromArray( $VO->meta );
32
- }
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Scans/Ufc/ResultItem.php CHANGED
@@ -2,13 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc;
4
 
5
- /**
6
- * Class ResultItem
7
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc
8
- * @property string $path_full
9
- * @property string $path_fragment
10
- */
11
- class ResultItem extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultItem {
12
 
13
  public function generateHash() :string {
14
  return md5( $this->path_full );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc;
4
 
5
+ class ResultItem extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\FileResultItem {
 
 
 
 
 
 
6
 
7
  public function generateHash() :string {
8
  return md5( $this->path_full );
src/lib/src/Scans/Ufc/ResultsSet.php CHANGED
@@ -9,7 +9,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
9
  * @property ResultItem[] $items
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc
11
  */
12
- class ResultsSet extends Base\BaseResultsSet {
13
 
14
  /**
15
  * @return string[]
9
  * @property ResultItem[] $items
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc
11
  */
12
+ class ResultsSet extends Base\ResultsSet {
13
 
14
  /**
15
  * @return string[]
src/lib/src/Scans/Ufc/Utilities/ItemActionHandler.php CHANGED
@@ -8,10 +8,11 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc;
8
  class ItemActionHandler extends Base\Utilities\ItemActionHandler {
9
 
10
  /**
11
- * @inheritDoc
 
12
  */
13
- public function delete() {
14
- return $this->repair( true );
15
  }
16
 
17
  /**
@@ -20,16 +21,4 @@ class ItemActionHandler extends Base\Utilities\ItemActionHandler {
20
  public function getRepairer() {
21
  return ( new Repair() )->setScanItem( $this->getScanItem() );
22
  }
23
-
24
- /**
25
- * @param bool $success
26
- */
27
- protected function fireRepairEvent( $success ) {
28
- /** @var Ufc\ResultItem $item */
29
- $item = $this->getScanItem();
30
- $this->getCon()->fireEvent(
31
- $this->getScanController()->getSlug().'_item_repair_'.( $success ? 'success' : 'fail' ),
32
- [ 'audit' => [ 'fragment' => $item->path_full ] ]
33
- );
34
- }
35
  }
8
  class ItemActionHandler extends Base\Utilities\ItemActionHandler {
9
 
10
  /**
11
+ * @return bool
12
+ * @throws \Exception
13
  */
14
+ public function repairDelete() :bool {
15
+ return $this->delete();
16
  }
17
 
18
  /**
21
  public function getRepairer() {
22
  return ( new Repair() )->setScanItem( $this->getScanItem() );
23
  }
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
src/lib/src/Scans/Ufc/Utilities/Repair.php CHANGED
@@ -2,6 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc\Utilities;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc;
7
  use FernleafSystems\Wordpress\Services\Services;
@@ -13,35 +14,32 @@ use FernleafSystems\Wordpress\Services\Services;
13
  class Repair extends Scans\Base\Utilities\BaseRepair {
14
 
15
  /**
16
- * @return bool
17
- * @throws \Exception
18
  */
19
- public function repairItem() {
20
- /** @var Ufc\ResultItem $oItem */
21
- $oItem = $this->getScanItem();
22
- $bSuccess = true;
23
-
24
- $oHashes = Services::CoreFileHashes();
25
- if ( $oHashes->isCoreFile( $oItem->path_fragment ) ) {
26
- throw new \Exception( sprintf( 'File "%s" is an official WordPress core file.', $oItem->path_fragment ) );
27
- }
28
 
29
- $oFs = Services::WpFs();
30
- if ( $oFs->deleteFile( $oItem->path_full ) ) {
31
- clearstatcache();
32
- $bSuccess = !$oFs->exists( $oItem->path_full );
33
  }
34
 
35
- return $bSuccess;
 
36
  }
37
 
38
  /**
39
  * @return bool
40
  * @throws \Exception
41
  */
42
- public function canRepair() {
43
- /** @var Ufc\ResultItem $oItem */
44
- $oItem = $this->getScanItem();
45
- return Services::WpFs()->exists( $oItem->path_full );
46
  }
47
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc\Utilities;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Reports\ScanRepairs;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Scans;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Ufc;
8
  use FernleafSystems\Wordpress\Services\Services;
14
  class Repair extends Scans\Base\Utilities\BaseRepair {
15
 
16
  /**
17
+ * @inheritDoc
 
18
  */
19
+ public function repairItem() :bool {
20
+ throw new \Exception( 'Repair action is not supported' );
21
+ }
22
+
23
+ public function deleteItem() :bool {
24
+ /** @var Ufc\ResultItem $item */
25
+ $item = $this->getScanItem();
 
 
26
 
27
+ $coreHashes = Services::CoreFileHashes();
28
+ if ( $coreHashes->isCoreFile( $item->path_fragment ) ) {
29
+ throw new \Exception( sprintf( 'File "%s" is an official WordPress core file.', $item->path_fragment ) );
 
30
  }
31
 
32
+ $FS = Services::WpFs();
33
+ return !$FS->isFile( $item->path_full ) || (bool)$FS->deleteFile( $item->path_full );
34
  }
35
 
36
  /**
37
  * @return bool
38
  * @throws \Exception
39
  */
40
+ public function canRepair() :bool {
41
+ /** @var Ufc\ResultItem $item */
42
+ $item = $this->getScanItem();
43
+ return (bool)Services::WpFs()->exists( $item->path_full );
44
  }
45
  }
src/lib/src/Scans/Wcf/BuildFileMap.php CHANGED
@@ -2,33 +2,34 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Common\ScanActionConsumer;
6
  use FernleafSystems\Wordpress\Services\Services;
7
 
8
  /**
9
  * Class BuildFileMap
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf
11
  */
12
- class BuildFileMap {
13
-
14
- use ScanActionConsumer;
15
 
16
  /**
17
  * @return string[]
18
  */
19
- public function build() {
20
- $aFiles = [];
21
 
22
- $oHashes = Services::CoreFileHashes();
23
- if ( $oHashes->isReady() ) {
24
- foreach ( array_keys( $oHashes->getHashes() ) as $sFragment ) {
25
  // To reduce noise, we exclude plugins and themes (by default)
26
- if ( strpos( $sFragment, 'wp-content/' ) === 0 ) {
27
  continue;
28
  }
29
- $aFiles[] = wp_normalize_path( path_join( ABSPATH, $sFragment ) );
 
 
 
30
  }
31
  }
32
- return $aFiles;
33
  }
34
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseBuildFileMap;
6
  use FernleafSystems\Wordpress\Services\Services;
7
 
8
  /**
9
  * Class BuildFileMap
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf
11
  */
12
+ class BuildFileMap extends BaseBuildFileMap {
 
 
13
 
14
  /**
15
  * @return string[]
16
  */
17
+ public function build() :array {
18
+ $files = [];
19
 
20
+ $coreHashes = Services::CoreFileHashes();
21
+ if ( $coreHashes->isReady() ) {
22
+ foreach ( array_keys( $coreHashes->getHashes() ) as $fragment ) {
23
  // To reduce noise, we exclude plugins and themes (by default)
24
+ if ( strpos( $fragment, 'wp-content/' ) === 0 ) {
25
  continue;
26
  }
27
+ $fullPath = wp_normalize_path( path_join( ABSPATH, $fragment ) );
28
+ if ( !$this->isWhitelistedPath( $fullPath ) ) {
29
+ $files[] = $fullPath;
30
+ }
31
  }
32
  }
33
+ return $files;
34
  }
35
  }
src/lib/src/Scans/Wcf/BuildScanAction.php CHANGED
@@ -7,20 +7,21 @@ use FernleafSystems\Wordpress\Plugin\Shield;
7
  class BuildScanAction extends Shield\Scans\Base\BaseBuildScanAction {
8
 
9
  protected function buildItems() {
10
- /** @var ScanActionVO $oAction */
11
- $oAction = $this->getScanActionVO();
12
- $oAction->items = ( new Shield\Scans\Wcf\BuildFileMap() )
13
- ->setScanActionVO( $oAction )
 
14
  ->build();
15
  }
16
 
17
  protected function setCustomFields() {
18
- /** @var ScanActionVO $oAction */
19
- $oAction = $this->getScanActionVO();
20
- /** @var Shield\Modules\HackGuard\Options $oOpts */
21
- $oOpts = $this->getOptions();
22
 
23
- $oAction->exclusions_missing_regex = $oOpts->getWcfMissingExclusions();
24
- $oAction->exclusions_files_regex = $oOpts->getWcfFileExclusions();
25
  }
26
  }
7
  class BuildScanAction extends Shield\Scans\Base\BaseBuildScanAction {
8
 
9
  protected function buildItems() {
10
+ /** @var ScanActionVO $action */
11
+ $action = $this->getScanActionVO();
12
+ $action->items = ( new Shield\Scans\Wcf\BuildFileMap() )
13
+ ->setMod( $this->getMod() )
14
+ ->setScanActionVO( $action )
15
  ->build();
16
  }
17
 
18
  protected function setCustomFields() {
19
+ /** @var ScanActionVO $action */
20
+ $action = $this->getScanActionVO();
21
+ /** @var Shield\Modules\HackGuard\Options $opts */
22
+ $opts = $this->getOptions();
23
 
24
+ $action->exclusions_missing_regex = $opts->getWcfMissingExclusions();
25
+ $action->exclusions_files_regex = $opts->getWcfFileExclusions();
26
  }
27
  }
src/lib/src/Scans/Wcf/ConvertVosToResults.php DELETED
@@ -1,33 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\EntryVO;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
7
-
8
- /**
9
- * Class ConvertVosToResults
10
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf
11
- */
12
- class ConvertVosToResults extends Scans\Base\BaseConvertVosToResults {
13
-
14
- /**
15
- * @param EntryVO[] $VOs
16
- * @return ResultsSet
17
- */
18
- public function convert( $VOs ) {
19
- $oRes = new ResultsSet();
20
- foreach ( $VOs as $oVo ) {
21
- $oRes->addItem( $this->convertItem( $oVo ) );
22
- }
23
- return $oRes;
24
- }
25
-
26
- /**
27
- * @param EntryVO $VO
28
- * @return ResultItem
29
- */
30
- public function convertItem( $VO ) {
31
- return ( new ResultItem() )->applyFromArray( $VO->meta );
32
- }
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Scans/Wcf/FileScanner.php CHANGED
@@ -17,24 +17,24 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
17
  * @return ResultItem|null
18
  */
19
  public function scan( string $fullPath ) {
20
- $oResult = null;
21
  $oHashes = Services::CoreFileHashes();
22
 
23
- /** @var ResultItem $oRes */
24
- $oRes = $this->getScanActionVO()->getNewResultItem();
25
- $oRes->path_full = $fullPath;
26
- $oRes->path_fragment = $oHashes->getFileFragment( $fullPath );
27
- $oRes->md5_file_wp = $oHashes->getFileHash( $oRes->path_fragment );
28
- $oRes->is_missing = !Services::WpFs()->exists( $oRes->path_full );
29
- $oRes->is_checksumfail = !$oRes->is_missing && $this->isChecksumFail( $oRes );
30
- $oRes->is_excluded = $this->isExcluded( $oRes->path_fragment )
31
- || ( $oRes->is_missing && $this->isExcludedMissing( $oRes->path_fragment ) );
32
 
33
- if ( !$oRes->is_excluded && ( $oRes->is_missing || $oRes->is_checksumfail ) ) {
34
- $oResult = $oRes;
35
  }
36
 
37
- return $oResult;
38
  }
39
 
40
  /**
17
  * @return ResultItem|null
18
  */
19
  public function scan( string $fullPath ) {
20
+ $results = null;
21
  $oHashes = Services::CoreFileHashes();
22
 
23
+ /** @var ResultItem $item */
24
+ $item = $this->getScanActionVO()->getNewResultItem();
25
+ $item->path_full = $fullPath;
26
+ $item->path_fragment = $oHashes->getFileFragment( $fullPath );
27
+ $item->md5_file_wp = $oHashes->getFileHash( $item->path_fragment );
28
+ $item->is_missing = !Services::WpFs()->exists( $item->path_full );
29
+ $item->is_checksumfail = !$item->is_missing && $this->isChecksumFail( $item );
30
+ $item->is_excluded = $this->isExcluded( $item->path_fragment )
31
+ || ( $item->is_missing && $this->isExcludedMissing( $item->path_fragment ) );
32
 
33
+ if ( !$item->is_excluded && ( $item->is_missing || $item->is_checksumfail ) ) {
34
+ $results = $item;
35
  }
36
 
37
+ return $results;
38
  }
39
 
40
  /**
src/lib/src/Scans/Wcf/ResultItem.php CHANGED
@@ -5,13 +5,11 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf;
5
  /**
6
  * Class ResultItem
7
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf
8
- * @property string $path_full
9
- * @property string $path_fragment
10
  * @property string $md5_file_wp
11
  * @property bool $is_checksumfail
12
  * @property bool $is_missing
13
  */
14
- class ResultItem extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultItem {
15
 
16
  public function generateHash() :string {
17
  return md5( $this->path_full );
5
  /**
6
  * Class ResultItem
7
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf
 
 
8
  * @property string $md5_file_wp
9
  * @property bool $is_checksumfail
10
  * @property bool $is_missing
11
  */
12
+ class ResultItem extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\FileResultItem {
13
 
14
  public function generateHash() :string {
15
  return md5( $this->path_full );
src/lib/src/Scans/Wcf/ResultsSet.php CHANGED
@@ -9,19 +9,18 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
9
  * @property ResultItem[] $items
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf
11
  */
12
- class ResultsSet extends Base\BaseResultsSet {
13
 
14
  /**
15
- * @param ResultItem[] $aItems
16
  * @return string[]
17
  */
18
- public function filterItemsForPaths( $aItems ) {
19
  return array_map(
20
- function ( $oItem ) {
21
- /** @var ResultItem $oItem */
22
- return $oItem->path_fragment;
23
  },
24
- $aItems
25
  );
26
  }
27
 
9
  * @property ResultItem[] $items
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wcf
11
  */
12
+ class ResultsSet extends Base\ResultsSet {
13
 
14
  /**
15
+ * @param ResultItem[] $items
16
  * @return string[]
17
  */
18
+ public function filterItemsForPaths( $items ) {
19
  return array_map(
20
+ function ( $item ) {
21
+ return $item->path_fragment;
 
22
  },
23
+ $items
24
  );
25
  }
26
 
src/lib/src/Scans/Wcf/Utilities/ItemActionHandler.php CHANGED
@@ -15,14 +15,10 @@ class ItemActionHandler extends Base\Utilities\ItemActionHandler {
15
  }
16
 
17
  /**
18
- * @param bool $success
 
19
  */
20
- protected function fireRepairEvent( $success ) {
21
- /** @var Wcf\ResultItem $oItem */
22
- $oItem = $this->getScanItem();
23
- $this->getCon()->fireEvent(
24
- $this->getScanController()->getSlug().'_item_repair_'.( $success ? 'success' : 'fail' ),
25
- [ 'audit' => [ 'fragment' => $oItem->path_full ] ]
26
- );
27
  }
28
  }
15
  }
16
 
17
  /**
18
+ * @return bool
19
+ * @throws \Exception
20
  */
21
+ public function repairDelete() :bool {
22
+ return $this->repair();
 
 
 
 
 
23
  }
24
  }
src/lib/src/Scans/Wcf/Utilities/Repair.php CHANGED
@@ -12,23 +12,20 @@ use FernleafSystems\Wordpress\Services\Services;
12
  */
13
  class Repair extends Scans\Base\Utilities\BaseRepair {
14
 
15
- /**
16
- * @return bool
17
- */
18
- public function repairItem() {
19
- /** @var Wcf\ResultItem $oItem */
20
- $oItem = $this->getScanItem();
21
- $sPath = trim( wp_normalize_path( $oItem->path_fragment ), '/' );
22
- return ( new Scans\Helpers\WpCoreFile() )->replace( $sPath );
23
  }
24
 
25
  /**
26
  * @return bool
27
  * @throws \Exception
28
  */
29
- public function canRepair() {
30
- /** @var Wcf\ResultItem $oItem */
31
- $oItem = $this->getScanItem();
32
- return Services::CoreFileHashes()->isCoreFile( $oItem->path_full );
33
  }
34
  }
12
  */
13
  class Repair extends Scans\Base\Utilities\BaseRepair {
14
 
15
+ public function repairItem() :bool {
16
+ /** @var Wcf\ResultItem $item */
17
+ $item = $this->getScanItem();
18
+ $path = trim( wp_normalize_path( $item->path_fragment ), '/' );
19
+ return ( new Scans\Helpers\WpCoreFile() )->replace( $path );
 
 
 
20
  }
21
 
22
  /**
23
  * @return bool
24
  * @throws \Exception
25
  */
26
+ public function canRepair() :bool {
27
+ /** @var Wcf\ResultItem $item */
28
+ $item = $this->getScanItem();
29
+ return Services::CoreFileHashes()->isCoreFile( $item->path_full );
30
  }
31
  }
src/lib/src/Scans/Wpv/ConvertVosToResults.php DELETED
@@ -1,32 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield;
6
-
7
- /**
8
- * Class ConvertVosToResults
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv
10
- */
11
- class ConvertVosToResults extends Shield\Scans\Base\BaseConvertVosToResults {
12
-
13
- /**
14
- * @param Shield\Databases\Scanner\EntryVO[] $VOs
15
- * @return ResultsSet
16
- */
17
- public function convert( $VOs ) {
18
- $results = new ResultsSet();
19
- foreach ( $VOs as $vo ) {
20
- $results->addItem( $this->convertItem( $vo ) );
21
- }
22
- return $results;
23
- }
24
-
25
- /**
26
- * @param Shield\Databases\Scanner\EntryVO $VO
27
- * @return ResultItem
28
- */
29
- public function convertItem( $VO ) {
30
- return ( new ResultItem() )->applyFromArray( $VO->meta );
31
- }
32
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Scans/Wpv/ResultItem.php CHANGED
@@ -12,7 +12,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb\VulnVO;
12
  * @property int $wpvuln_id
13
  * @property array $wpvuln_vo
14
  */
15
- class ResultItem extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultItem {
16
 
17
  public function generateHash() :string {
18
  return md5( $this->slug.$this->wpvuln_id );
12
  * @property int $wpvuln_id
13
  * @property array $wpvuln_vo
14
  */
15
+ class ResultItem extends \FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\ResultItem {
16
 
17
  public function generateHash() :string {
18
  return md5( $this->slug.$this->wpvuln_id );
src/lib/src/Scans/Wpv/ResultsSet.php CHANGED
@@ -9,7 +9,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
9
  * @property ResultItem[] $items
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv
11
  */
12
- class ResultsSet extends Base\BaseResultsSet {
13
 
14
  /**
15
  * @return int
9
  * @property ResultItem[] $items
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv
11
  */
12
+ class ResultsSet extends Base\ResultsSet {
13
 
14
  /**
15
  * @return int
src/lib/src/Scans/Wpv/Scan.php CHANGED
@@ -9,25 +9,24 @@ use FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes\Vulnerabi
9
  class Scan extends Shield\Scans\Base\BaseScan {
10
 
11
  protected function scanSlice() {
12
- /** @var ScanActionVO $oAction */
13
- $oAction = $this->getScanActionVO();
14
- $oTempRs = $oAction->getNewResultsSet();
15
 
16
- $oCopier = new Shield\Scans\Helpers\CopyResultsSets();
17
- foreach ( $oAction->items as $sFile => $sContext ) {
18
- $oNewRes = $this->scanItem( $sContext, $sFile );
19
- if ( $oNewRes instanceof Shield\Scans\Base\BaseResultsSet ) {
20
- $oCopier->copyTo( $oNewRes, $oTempRs );
21
  }
22
  }
23
-
24
- $aNewItems = [];
25
- if ( $oTempRs->hasItems() ) {
26
- foreach ( $oTempRs->getAllItems() as $item ) {
27
- $aNewItems[] = $item->getRawData();
28
  }
29
  }
30
- $oAction->results = $aNewItems;
31
  }
32
 
33
  /**
9
  class Scan extends Shield\Scans\Base\BaseScan {
10
 
11
  protected function scanSlice() {
12
+ /** @var ScanActionVO $action */
13
+ $action = $this->getScanActionVO();
14
+ $tmpResults = $action->getNewResultsSet();
15
 
16
+ $copier = new Shield\Scans\Helpers\CopyResultsSets();
17
+ foreach ( $action->items as $file => $context ) {
18
+ $results = $this->scanItem( $context, $file );
19
+ if ( $results instanceof Shield\Scans\Base\ResultsSet ) {
20
+ $copier->copyTo( $results, $tmpResults );
21
  }
22
  }
23
+ $items = [];
24
+ if ( $tmpResults->hasItems() ) {
25
+ foreach ( $tmpResults->getAllItems() as $item ) {
26
+ $items[] = $item->getRawData();
 
27
  }
28
  }
29
+ $action->results = $items;
30
  }
31
 
32
  /**
src/lib/src/Scans/Wpv/Utilities/ItemActionHandler.php CHANGED
@@ -1,10 +1,8 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\Utilities;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv;
7
- use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
10
 
@@ -14,20 +12,4 @@ class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
14
  public function getRepairer() {
15
  return ( new Repair() )->setScanItem( $this->getScanItem() );
16
  }
17
-
18
- /**
19
- * @param bool $success
20
- */
21
- protected function fireRepairEvent( $success ) {
22
- /** @var Wpv\ResultItem $oItem */
23
- $oItem = $this->getScanItem();
24
- $this->getCon()->fireEvent(
25
- $this->getScanController()->getSlug().'_item_repair_'.( $success ? 'success' : 'fail' ),
26
- [
27
- 'audit' => [
28
- 'name' => Services::WpPlugins()->getPluginAsVo( $oItem->slug )->Name
29
- ]
30
- ]
31
- );
32
- }
33
  }
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\Utilities;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base;
 
 
6
 
7
  class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
8
 
12
  public function getRepairer() {
13
  return ( new Repair() )->setScanItem( $this->getScanItem() );
14
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  }
src/lib/src/Scans/Wpv/Utilities/Repair.php CHANGED
@@ -17,25 +17,22 @@ class Repair extends Scans\Base\Utilities\BaseRepair {
17
  * @return bool
18
  * @throws \Exception
19
  */
20
- public function repairItem() {
21
  if ( !$this->canRepair() ) {
22
  throw new \Exception( 'An update is not currently available.' );
23
  }
24
 
25
- /** @var Wpv\ResultItem $oItem */
26
- $oItem = $this->getScanItem();
27
 
28
- $aData = ( $oItem->context == 'plugins' ) ?
29
- Services::WpPlugins()->update( $oItem->slug )
30
- : Services::WpThemes()->update( $oItem->slug );
31
 
32
- return $aData[ 'successful' ];
33
  }
34
 
35
- /**
36
- * @return bool
37
- */
38
- public function canRepair() {
39
  /** @var Wpv\ResultItem $item */
40
  $item = $this->getScanItem();
41
 
17
  * @return bool
18
  * @throws \Exception
19
  */
20
+ public function repairItem() :bool {
21
  if ( !$this->canRepair() ) {
22
  throw new \Exception( 'An update is not currently available.' );
23
  }
24
 
25
+ /** @var Wpv\ResultItem $item */
26
+ $item = $this->getScanItem();
27
 
28
+ $data = ( $item->context == 'plugins' ) ?
29
+ Services::WpPlugins()->update( $item->slug )
30
+ : Services::WpThemes()->update( $item->slug );
31
 
32
+ return (bool)$data[ 'successful' ];
33
  }
34
 
35
+ public function canRepair() :bool {
 
 
 
36
  /** @var Wpv\ResultItem $item */
37
  $item = $this->getScanItem();
38
 
src/lib/src/Scans/Wpv/WpVulnDb/WpVulnVO.php DELETED
@@ -1,43 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb;
4
-
5
- use FernleafSystems\Utilities\Data\Adapter\DynPropertiesClass;
6
-
7
- /**
8
- * Class WpVulnVO
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb
10
- * @property int $id
11
- * @property string $url
12
- * @property string $title
13
- * @property string $vuln_type
14
- * @property string $fixed_in
15
- * @property string $references
16
- * @property int $updated_at
17
- * @property int $created_at
18
- * @property int $published_date
19
- */
20
- class WpVulnVO extends DynPropertiesClass {
21
-
22
- const URL_BASE = 'https://wpscan.com/vulnerability/%s';
23
-
24
- /**
25
- * @inheritDoc
26
- */
27
- public function __get( string $key ) {
28
- $val = parent::__get( $key );
29
- switch ( $key ) {
30
-
31
- case 'url':
32
- if ( empty( $val ) ) {
33
- $val = sprintf( self::URL_BASE, $this->id );
34
- }
35
- break;
36
-
37
- default:
38
- break;
39
- }
40
-
41
- return $val;
42
- }
43
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/ShieldNetApi/FileLocker/DecryptFile.php CHANGED
@@ -2,7 +2,6 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\FileLocker;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\Common\BaseShieldNetApi;
7
  use FernleafSystems\Wordpress\Services\Utilities\Encrypt\OpenSslEncryptVo;
8
 
@@ -11,18 +10,19 @@ class DecryptFile extends BaseShieldNetApi {
11
  const API_ACTION = 'filelocker/decrypt';
12
 
13
  /**
14
- * @param OpenSslEncryptVo $oOpenSslVO
15
- * @param int $nPublicKeyId
16
  * @return string|null
17
  */
18
- public function retrieve( OpenSslEncryptVo $oOpenSslVO, $nPublicKeyId ) {
19
  $content = null;
20
 
21
  $this->request_method = 'post';
22
  $this->params_body = [
23
- 'key_id' => $nPublicKeyId,
24
- 'sealed_data' => $oOpenSslVO->sealed_data,
25
- 'sealed_pass' => $oOpenSslVO->sealed_password,
 
26
  ];
27
 
28
  $raw = $this->sendReq();
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\FileLocker;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\Common\BaseShieldNetApi;
6
  use FernleafSystems\Wordpress\Services\Utilities\Encrypt\OpenSslEncryptVo;
7
 
10
  const API_ACTION = 'filelocker/decrypt';
11
 
12
  /**
13
+ * @param OpenSslEncryptVo $openSslVO
14
+ * @param int $publicKeyId
15
  * @return string|null
16
  */
17
+ public function retrieve( OpenSslEncryptVo $openSslVO, $publicKeyId ) {
18
  $content = null;
19
 
20
  $this->request_method = 'post';
21
  $this->params_body = [
22
+ 'key_id' => $publicKeyId,
23
+ 'sealed_data' => $openSslVO->sealed_data,
24
+ 'sealed_pass' => $openSslVO->sealed_password,
25
+ 'cipher' => $openSslVO->cipher,
26
  ];
27
 
28
  $raw = $this->sendReq();
src/lib/src/Tables/Build/ScanAggregate.php CHANGED
@@ -7,10 +7,6 @@ use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\EntryVO;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
9
 
10
- /**
11
- * Class ScanAggregate
12
- * @package FernleafSystems\Wordpress\Plugin\Shield\Tables\Build
13
- */
14
  class ScanAggregate extends ScanBase {
15
 
16
  /**
@@ -20,8 +16,8 @@ class ScanAggregate extends ScanBase {
20
  /** @var HackGuard\ModCon $mod */
21
  $mod = $this->getMod();
22
 
23
- foreach ( $this->getIncludedScanSlugs() as $sScan ) {
24
- $mod->getScanCon( $sScan )->cleanStalesResults();
25
  }
26
 
27
  return $this;
@@ -118,8 +114,8 @@ class ScanAggregate extends ScanBase {
118
  /**
119
  * @return string[]
120
  */
121
- private function getIncludedScanSlugs() {
122
- return [ 'mal', 'wcf', 'ufc', 'ptg' ];
123
  }
124
 
125
  protected function getCustomParams() :array {
7
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\EntryVO;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
9
 
 
 
 
 
10
  class ScanAggregate extends ScanBase {
11
 
12
  /**
16
  /** @var HackGuard\ModCon $mod */
17
  $mod = $this->getMod();
18
 
19
+ foreach ( $this->getIncludedScanSlugs() as $scan ) {
20
+ $mod->getScanCon( $scan )->cleanStalesResults();
21
  }
22
 
23
  return $this;
114
  /**
115
  * @return string[]
116
  */
117
+ private function getIncludedScanSlugs() :array {
118
+ return [ 'mal' ];
119
  }
120
 
121
  protected function getCustomParams() :array {
src/lib/src/Tables/Build/ScanApc.php CHANGED
@@ -28,15 +28,15 @@ class ScanApc extends ScanBase {
28
  $oWpPlugins = Services::WpPlugins();
29
  foreach ( $this->getEntriesRaw() as $nKey => $entry ) {
30
  /** @var Shield\Databases\Scanner\EntryVO $entry */
31
- /** @var Shield\Scans\Apc\ResultItem $oIt */
32
- $oIt = $oConverter
33
  ->setScanController( $mod->getScanCon( $entry->scan ) )
34
  ->convertVoToResultItem( $entry );
35
- $oPlugin = $oWpPlugins->getPluginAsVo( $oIt->slug );
36
  $aE = $entry->getRawData();
37
  $aE[ 'plugin' ] = sprintf( '%s (%s)', $oPlugin->Name, $oPlugin->Version );
38
  $aE[ 'status' ] = sprintf( '%s: %s',
39
- __( 'Abandoned', 'wp-simple-firewall' ), $oCarbon->setTimestamp( $oIt->last_updated_at )
40
  ->diffForHumans() );
41
  $aE[ 'ignored' ] = $this->formatIsIgnored( $entry );
42
  $aE[ 'created_at' ] = $this->formatTimestampField( $entry->created_at );
28
  $oWpPlugins = Services::WpPlugins();
29
  foreach ( $this->getEntriesRaw() as $nKey => $entry ) {
30
  /** @var Shield\Databases\Scanner\EntryVO $entry */
31
+ /** @var Shield\Scans\Apc\ResultItem $item */
32
+ $item = $oConverter
33
  ->setScanController( $mod->getScanCon( $entry->scan ) )
34
  ->convertVoToResultItem( $entry );
35
+ $oPlugin = $oWpPlugins->getPluginAsVo( $item->slug );
36
  $aE = $entry->getRawData();
37
  $aE[ 'plugin' ] = sprintf( '%s (%s)', $oPlugin->Name, $oPlugin->Version );
38
  $aE[ 'status' ] = sprintf( '%s: %s',
39
+ __( 'Abandoned', 'wp-simple-firewall' ), $oCarbon->setTimestamp( $item->last_updated_at )
40
  ->diffForHumans() );
41
  $aE[ 'ignored' ] = $this->formatIsIgnored( $entry );
42
  $aE[ 'created_at' ] = $this->formatTimestampField( $entry->created_at );
src/lib/src/Tables/DataTables/Build/AuditTrail/ForAuditTrail.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\AuditTrail;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Base;
7
+
8
+ class ForAuditTrail extends Base {
9
+
10
+ protected function getOrderColumnSlug() :string {
11
+ return 'detected';
12
+ }
13
+
14
+ protected function getColumnsToDisplay() :array {
15
+ return [
16
+ 'rid',
17
+ 'file_as_href',
18
+ 'status',
19
+ 'file_type',
20
+ 'detected',
21
+ 'actions',
22
+ ];
23
+ }
24
+
25
+ protected function getColumnDefs() :array {
26
+ return [
27
+ 'rid' => [
28
+ 'data' => 'rid',
29
+ 'title' => 'ID',
30
+ 'orderable' => true,
31
+ 'searchable' => false,
32
+ 'visible' => false,
33
+ ],
34
+ 'file' => [
35
+ 'data' => 'file',
36
+ 'title' => __( 'File' ),
37
+ 'className' => 'file',
38
+ 'orderable' => true,
39
+ 'searchable' => true,
40
+ 'visible' => true,
41
+ ],
42
+ 'file_as_href' => [
43
+ 'data' => 'file_as_href',
44
+ 'title' => __( 'File' ),
45
+ 'className' => 'file_as_href',
46
+ 'orderable' => true,
47
+ 'searchable' => true,
48
+ 'visible' => true,
49
+ ],
50
+ 'file_type' => [
51
+ 'data' => 'file_type',
52
+ 'title' => __( 'Type' ),
53
+ 'className' => 'file_type',
54
+ 'orderable' => true,
55
+ 'searchable' => true,
56
+ 'visible' => true,
57
+ ],
58
+ 'status' => [
59
+ 'data' => 'status',
60
+ 'title' => __( 'Status' ),
61
+ 'className' => 'status',
62
+ 'orderable' => true,
63
+ 'searchable' => false,
64
+ 'visible' => true,
65
+ ],
66
+ 'detected' => [
67
+ 'data' => [
68
+ '_' => 'detected_since',
69
+ 'sort' => 'detected_at',
70
+ ],
71
+ 'title' => __( 'Detected' ),
72
+ 'className' => 'detected',
73
+ 'orderable' => true,
74
+ 'searchable' => false,
75
+ 'visible' => true,
76
+ ],
77
+ 'actions' => [
78
+ 'data' => 'actions',
79
+ 'title' => __( 'Actions' ),
80
+ 'className' => 'actions',
81
+ 'orderable' => false,
82
+ 'searchable' => false,
83
+ 'visible' => true,
84
+ ],
85
+ 'fp_confidence' => [
86
+ 'data' => 'fp_confidence',
87
+ 'title' => __( 'False Positive Confidence' ),
88
+ 'className' => 'fp_confidence',
89
+ 'orderable' => true,
90
+ 'searchable' => false,
91
+ 'visible' => true,
92
+ ],
93
+ 'line_numbers' => [
94
+ 'data' => 'line_numbers',
95
+ 'title' => __( 'Line Numbers' ),
96
+ 'className' => 'line_numbers',
97
+ 'orderable' => false,
98
+ 'searchable' => false,
99
+ 'visible' => true,
100
+ ],
101
+ 'mal_sig' => [
102
+ 'data' => 'mal_sig',
103
+ 'title' => __( 'Pattern Detected' ),
104
+ 'className' => 'mal_sig',
105
+ 'orderable' => false,
106
+ 'searchable' => true,
107
+ 'visible' => true,
108
+ ],
109
+ ];
110
+ }
111
+ }
src/lib/src/Tables/DataTables/Build/Base.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
7
+
8
+ abstract class Base {
9
+
10
+ use ModConsumer;
11
+
12
+ abstract protected function getColumnDefs() :array;
13
+
14
+ abstract protected function getColumnsToDisplay() :array;
15
+
16
+ abstract protected function getOrderColumnSlug() :string;
17
+
18
+ public function build() :string {
19
+ return json_encode( [
20
+ // array_values() to ensure data of the correct format
21
+ 'columns' => array_values( $this->getColumnsForDisplay() ),
22
+ 'order' => $this->getInitialOrdering()
23
+ ] );
24
+ }
25
+
26
+ /**
27
+ * @return array
28
+ * @throws \Exception
29
+ */
30
+ public function getInitialOrdering() :array {
31
+ $thePosition = 0;
32
+ foreach ( $this->getColumnsForDisplay() as $position => $columnDef ) {
33
+ if ( $columnDef === $this->getOrderColumnSlug() ) {
34
+ $thePosition = $position;
35
+ break;
36
+ }
37
+ }
38
+ return [
39
+ [ $thePosition, $this->getOrderMethod() ]
40
+ ];
41
+ }
42
+
43
+ protected function getOrderMethod() :string {
44
+ return 'desc';
45
+ }
46
+
47
+ /**
48
+ * @return array
49
+ * @throws \Exception
50
+ */
51
+ public function getColumnsForDisplay() :array {
52
+ $columns = [];
53
+ foreach ( $this->getColumnsToDisplay() as $colSlug ) {
54
+ $columns[ $colSlug ] = $this->pluckColumn( $colSlug );
55
+ }
56
+ return $columns;
57
+ }
58
+
59
+ /**
60
+ * @param string $columnSlug
61
+ * @return array
62
+ * @throws \Exception
63
+ */
64
+ protected function pluckColumn( string $columnSlug ) :array {
65
+ $col = null;
66
+ foreach ( $this->getColumnDefs() as $slug => $columnDef ) {
67
+ if ( $slug === $columnSlug ) {
68
+ $col = $columnDef;
69
+ break;
70
+ }
71
+ }
72
+ if ( empty( $col ) ) {
73
+ throw new \Exception( 'Column Definition does not exist for slug: '.$columnSlug );
74
+ }
75
+ return $col;
76
+ }
77
+ }
src/lib/src/Tables/DataTables/Build/Scans/BaseForScan.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Scans;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Base;
7
+
8
+ class BaseForScan extends Base {
9
+
10
+ protected function getOrderColumnSlug() :string {
11
+ return 'detected';
12
+ }
13
+
14
+ protected function getColumnsToDisplay() :array {
15
+ return [
16
+ 'rid',
17
+ 'file_as_href',
18
+ 'status',
19
+ 'file_type',
20
+ 'detected',
21
+ 'actions',
22
+ ];
23
+ }
24
+
25
+ protected function getColumnDefs() :array {
26
+ return [
27
+ 'rid' => [
28
+ 'data' => 'rid',
29
+ 'title' => 'ID',
30
+ 'orderable' => true,
31
+ 'searchable' => false,
32
+ 'visible' => false,
33
+ ],
34
+ 'file' => [
35
+ 'data' => 'file',
36
+ 'title' => __( 'File' ),
37
+ 'className' => 'file',
38
+ 'orderable' => true,
39
+ 'searchable' => true,
40
+ 'visible' => true,
41
+ ],
42
+ 'file_as_href' => [
43
+ 'data' => 'file_as_href',
44
+ 'title' => __( 'File' ),
45
+ 'className' => 'file_as_href',
46
+ 'orderable' => true,
47
+ 'searchable' => true,
48
+ 'visible' => true,
49
+ ],
50
+ 'file_type' => [
51
+ 'data' => 'file_type',
52
+ 'title' => __( 'Type' ),
53
+ 'className' => 'file_type',
54
+ 'orderable' => true,
55
+ 'searchable' => true,
56
+ 'visible' => true,
57
+ ],
58
+ 'status' => [
59
+ 'data' => 'status',
60
+ 'title' => __( 'Status' ),
61
+ 'className' => 'status',
62
+ 'orderable' => true,
63
+ 'searchable' => false,
64
+ 'visible' => true,
65
+ ],
66
+ 'detected' => [
67
+ 'data' => [
68
+ '_' => 'detected_since',
69
+ 'sort' => 'detected_at',
70
+ ],
71
+ 'title' => __( 'Detected' ),
72
+ 'className' => 'detected',
73
+ 'orderable' => true,
74
+ 'searchable' => false,
75
+ 'visible' => true,
76
+ ],
77
+ 'actions' => [
78
+ 'data' => 'actions',
79
+ 'title' => __( 'Actions' ),
80
+ 'className' => 'actions',
81
+ 'orderable' => false,
82
+ 'searchable' => false,
83
+ 'visible' => true,
84
+ ],
85
+ 'fp_confidence' => [
86
+ 'data' => 'fp_confidence',
87
+ 'title' => __( 'False Positive Confidence' ),
88
+ 'className' => 'fp_confidence',
89
+ 'orderable' => true,
90
+ 'searchable' => false,
91
+ 'visible' => true,
92
+ ],
93
+ 'line_numbers' => [
94
+ 'data' => 'line_numbers',
95
+ 'title' => __( 'Line Numbers' ),
96
+ 'className' => 'line_numbers',
97
+ 'orderable' => false,
98
+ 'searchable' => false,
99
+ 'visible' => true,
100
+ ],
101
+ 'mal_sig' => [
102
+ 'data' => 'mal_sig',
103
+ 'title' => __( 'Pattern Detected' ),
104
+ 'className' => 'mal_sig',
105
+ 'orderable' => false,
106
+ 'searchable' => true,
107
+ 'visible' => true,
108
+ ],
109
+ ];
110
+ }
111
+ }
src/lib/src/Tables/DataTables/Build/Scans/ForMalware.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Scans;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
+
7
+ class ForMalware extends BaseForScan {
8
+
9
+ protected function getColumnsToDisplay() :array {
10
+ return [
11
+ 'rid',
12
+ 'file_as_href',
13
+ 'status',
14
+ 'fp_confidence',
15
+ 'line_numbers',
16
+ 'mal_sig',
17
+ 'detected',
18
+ 'actions',
19
+ ];
20
+ }
21
+
22
+ protected function getColumnDefs() :array {
23
+ $colDefs = parent::getColumnDefs();
24
+ $colDefs[ 'fp_confidence' ] = [
25
+ 'data' => 'fp_confidence',
26
+ 'title' => __( 'False Positive Confidence' ),
27
+ 'className' => 'fp_confidence',
28
+ 'orderable' => true,
29
+ 'searchable' => false,
30
+ 'visible' => true,
31
+ ];
32
+ $colDefs[ 'line_numbers' ] = [
33
+ 'data' => 'line_numbers',
34
+ 'title' => __( 'Line Numbers' ),
35
+ 'className' => 'line_numbers',
36
+ 'orderable' => false,
37
+ 'searchable' => false,
38
+ 'visible' => true,
39
+ ];
40
+ $colDefs[ 'mal_sig' ] = [
41
+ 'data' => 'mal_sig',
42
+ 'title' => __( 'Pattern Detected' ),
43
+ 'className' => 'mal_sig',
44
+ 'orderable' => false,
45
+ 'searchable' => true,
46
+ 'visible' => true,
47
+ ];
48
+ return $colDefs;
49
+ }
50
+ }
src/lib/src/Tables/DataTables/Build/Scans/ForPluginTheme.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Scans;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
+
7
+ class ForPluginTheme extends BaseForScan {
8
+
9
+ }
src/lib/src/Tables/DataTables/Build/Scans/ForWordpress.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\DataTables\Build\Scans;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
+
7
+ class ForWordpress extends BaseForScan {
8
+
9
+ }
src/lib/src/Tables/Render/DataTable/Base.php DELETED
@@ -1,10 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\DataTable;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\Common\BaseTable;
7
-
8
- class Base extends BaseTable {
9
-
10
- }
 
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Render/DataTable/ScanBase.php DELETED
@@ -1,9 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\DataTable;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
-
7
- class ScanBase extends Base {
8
-
9
- }
 
 
 
 
 
 
 
 
 
src/lib/src/Tables/Render/DataTable/ScanWcf.php DELETED
@@ -1,9 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Render\DataTable;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Scans;
6
-
7
- class ScanWcf extends ScanBase {
8
-
9
- }
 
 
 
 
 
 
 
 
 
src/lib/src/Utilities/CacheDir.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Utilities;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\TestCacheDirWrite;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
7
+ use FernleafSystems\Wordpress\Services\Services;
8
+
9
+ class CacheDir {
10
+
11
+ use PluginControllerConsumer;
12
+
13
+ public function build() :string {
14
+ $con = $this->getCon();
15
+
16
+ $dir = '';
17
+ try {
18
+ $maybeDir = $this->getDir();
19
+ if ( !isset( $con->cache_dir_ready ) ) {
20
+ if ( !Services::WpFs()->mkdir( $maybeDir ) ) {
21
+ throw new \Exception( 'Failed to mkdir cache dir' );
22
+ }
23
+ $this->testWrite();
24
+ $this->addProtections();
25
+ $con->cache_dir_ready = true;
26
+ }
27
+ if ( $con->cache_dir_ready ) {
28
+ $dir = $maybeDir;
29
+ }
30
+ }
31
+ catch ( \Exception $e ) {
32
+ $con->cache_dir_ready = false;
33
+ }
34
+ return $dir;
35
+ }
36
+
37
+ public function buildSubDir( string $subDir ) :string {
38
+ $finalDir = '';
39
+ $baseDir = $this->build();
40
+ if ( !empty( $baseDir ) ) {
41
+ $FS = Services::WpFs();
42
+ $finalDir = path_join( $baseDir, $subDir );
43
+ if ( !$FS->mkdir( $finalDir ) ) {
44
+ $finalDir = '';
45
+ }
46
+ }
47
+ return $finalDir;
48
+ }
49
+
50
+ /**
51
+ * @return bool
52
+ * @throws \Exception
53
+ */
54
+ private function testWrite() :bool {
55
+ $tester = ( new TestCacheDirWrite() )->setMod( $this->getCon()->getModule_Plugin() );
56
+ if ( !$tester->canWrite() ) {
57
+ throw new \Exception( 'Failed Test-Write' );
58
+ }
59
+ return true;
60
+ }
61
+
62
+ private function addProtections() :bool {
63
+ $FS = Services::WpFs();
64
+ $cacheDir = $this->getDir();
65
+
66
+ $htFile = path_join( $cacheDir, '.htaccess' );
67
+ $htContent = implode( "\n", [
68
+ "# BEGIN SHIELD",
69
+ "Options -Indexes",
70
+ "Order allow,deny",
71
+ "Deny from all",
72
+ '<FilesMatch "^.*\.(css|js)$">',
73
+ " Allow from all",
74
+ '</FilesMatch>',
75
+ "# END SHIELD"
76
+ ] );
77
+ if ( !$FS->exists( $htFile ) || ( md5_file( $htFile ) !== md5( $htContent ) ) ) {
78
+ $FS->putFileContent( $htFile, $htContent );
79
+ }
80
+ $index = path_join( $cacheDir, 'index.php' );
81
+ $indexContent = "<?php\nhttp_response_code(404);";
82
+ if ( !$FS->exists( $index ) || ( md5_file( $index ) !== md5( $indexContent ) ) ) {
83
+ $FS->putFileContent( $index, $indexContent );
84
+ }
85
+
86
+ return true;
87
+ }
88
+
89
+ /**
90
+ * @return string
91
+ * @throws \Exception
92
+ */
93
+ private function getDir() :string {
94
+ $con = $this->getCon();
95
+ if ( empty( $con->cfg->paths[ 'cache' ] ) ) {
96
+ throw new \Exception( 'No slug for cache dir' );
97
+ }
98
+ return $this->getCon()->paths->cacheDir();
99
+ }
100
+ }
src/lib/src/Utilities/HumanSpam/TestContent.php CHANGED
@@ -46,8 +46,8 @@ class TestContent {
46
  if ( empty( $this->list ) ) {
47
  $FS = Services::WpFs();
48
  $file = $this->getFile();
49
- if ( !$FS->exists( $file ) || Services::Request()
50
- ->ts() - $FS->getModifiedTime( $file ) > WEEK_IN_SECONDS ) {
51
  $this->importBlacklist();
52
  }
53
  $this->list = array_map( 'base64_decode', explode( "\n", $FS->getFileContent( $file, true ) ) );
@@ -59,7 +59,7 @@ class TestContent {
59
  $success = false;
60
  $mod = $this->getCon()->getModule_Comments();
61
  $rawList = Services::HttpRequest()->getContent( $mod->getOptions()->getDef( 'url_spam_blacklist_terms' ) );
62
- if ( !empty( $rawList ) ) {
63
  $success = Services::WpFs()->putFileContent(
64
  $this->getFile(),
65
  implode( "\n", array_map( 'base64_encode', array_filter( array_map( 'trim', explode( "\n", $rawList ) ) ) ) ),
@@ -70,6 +70,6 @@ class TestContent {
70
  }
71
 
72
  private function getFile() :string {
73
- return $this->getCon()->getModule_Comments()->getSpamBlacklistFile();
74
  }
75
  }
46
  if ( empty( $this->list ) ) {
47
  $FS = Services::WpFs();
48
  $file = $this->getFile();
49
+ if ( !$FS->exists( $file )
50
+ || Services::Request()->ts() - $FS->getModifiedTime( $file ) > MONTH_IN_SECONDS ) {
51
  $this->importBlacklist();
52
  }
53
  $this->list = array_map( 'base64_decode', explode( "\n", $FS->getFileContent( $file, true ) ) );
59
  $success = false;
60
  $mod = $this->getCon()->getModule_Comments();
61
  $rawList = Services::HttpRequest()->getContent( $mod->getOptions()->getDef( 'url_spam_blacklist_terms' ) );
62
+ if ( !empty( $rawList ) && !empty( $this->getFile() ) ) {
63
  $success = Services::WpFs()->putFileContent(
64
  $this->getFile(),
65
  implode( "\n", array_map( 'base64_encode', array_filter( array_map( 'trim', explode( "\n", $rawList ) ) ) ) ),
70
  }
71
 
72
  private function getFile() :string {
73
+ return $this->getCon()->paths->forCacheItem( 'spamblacklist.txt' );
74
  }
75
  }
src/lib/src/Utilities/Resources/Dynamic.php CHANGED
@@ -5,6 +5,11 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Utilities\Resources;
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
6
  use FernleafSystems\Wordpress\Services\Services;
7
 
 
 
 
 
 
8
  class Dynamic {
9
 
10
  const RESOURCES_DIR = 'resources';
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
6
  use FernleafSystems\Wordpress\Services\Services;
7
 
8
+ /**
9
+ * Class Dynamic
10
+ * @package FernleafSystems\Wordpress\Plugin\Shield\Utilities\Resources
11
+ * @deprecated 11.4
12
+ */
13
  class Dynamic {
14
 
15
  const RESOURCES_DIR = 'resources';
src/lib/src/Utilities/Tool/TmpFileStore.php CHANGED
@@ -2,19 +2,19 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Utilities\Tool;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
 
6
  use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class TmpFileStore {
9
 
 
10
  use PluginControllerConsumer;
11
 
12
  private static $slugs = [];
13
 
14
- /**
15
- * @throws \Exception
16
- */
17
- public function __construct() {
18
  if ( $this->getCon()->hasCacheDir() ) {
19
  add_action( $this->getCon()->prefix( 'plugin_shutdown' ), function () {
20
  $FS = Services::WpFs();
@@ -33,8 +33,15 @@ class TmpFileStore {
33
  * @return mixed|null
34
  */
35
  public function load( string $slug ) {
36
- $data = Services::WpFs()->getFileContent( path_join( $this->getTmpDir(), $slug ) );
37
- return empty( $data ) ? null : unserialize( $data );
 
 
 
 
 
 
 
38
  }
39
 
40
  public function store( string $slug, $data ) {
@@ -43,6 +50,8 @@ class TmpFileStore {
43
  }
44
 
45
  private function getTmpDir() :string {
46
- return path_join( $this->getCon()->getPluginCachePath(), 'tmp_files' );
 
 
47
  }
48
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Utilities\Tool;
4
 
5
+ use FernleafSystems\Utilities\Logic\ExecOnce;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Utilities\CacheDir;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
10
  class TmpFileStore {
11
 
12
+ use ExecOnce;
13
  use PluginControllerConsumer;
14
 
15
  private static $slugs = [];
16
 
17
+ protected function run() {
 
 
 
18
  if ( $this->getCon()->hasCacheDir() ) {
19
  add_action( $this->getCon()->prefix( 'plugin_shutdown' ), function () {
20
  $FS = Services::WpFs();
33
  * @return mixed|null
34
  */
35
  public function load( string $slug ) {
36
+ $data = null;
37
+ $tmpFile = path_join( $this->getTmpDir(), $slug );
38
+ if ( Services::WpFs()->isFile( $tmpFile ) ) {
39
+ $contents = Services::WpFs()->getFileContent( path_join( $this->getTmpDir(), $slug ) );
40
+ if ( !empty( $contents ) ) {
41
+ $data = unserialize( $contents );
42
+ }
43
+ }
44
+ return $data;
45
  }
46
 
47
  public function store( string $slug, $data ) {
50
  }
51
 
52
  private function getTmpDir() :string {
53
+ return ( new CacheDir() )
54
+ ->setCon( $this->getCon() )
55
+ ->buildSubDir( 'tmp_files' );
56
  }
57
  }
src/lib/vendor/composer/autoload_classmap.php CHANGED
@@ -251,6 +251,7 @@ return array(
251
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ModCon' => $baseDir . '/src/Modules/Base/ModCon.php',
252
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options' => $baseDir . '/src/Modules/Base/Options.php',
253
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\OptValueSanitize' => $baseDir . '/src/Modules/Base/Options/OptValueSanitize.php',
 
254
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Processor' => $baseDir . '/src/Modules/Base/Processor.php',
255
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Reporting' => $baseDir . '/src/Modules/Base/Reporting.php',
256
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\RestHandler' => $baseDir . '/src/Modules/Base/RestHandler.php',
@@ -291,7 +292,6 @@ return array(
291
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\EventsListener' => $baseDir . '/src/Modules/Events/Lib/EventsListener.php',
292
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\EventsService' => $baseDir . '/src/Modules/Events/Lib/EventsService.php',
293
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\Reports\\KeyStats' => $baseDir . '/src/Modules/Events/Lib/Reports/KeyStats.php',
294
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\Reports\\ScanRepairs' => $baseDir . '/src/Modules/Events/Lib/Reports/ScanRepairs.php',
295
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\StatsWriter' => $baseDir . '/src/Modules/Events/Lib/StatsWriter.php',
296
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\UI\\BuildDataForStats' => $baseDir . '/src/Modules/Events/Lib/UI/BuildDataForStats.php',
297
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\ModCon' => $baseDir . '/src/Modules/Events/ModCon.php',
@@ -333,6 +333,10 @@ return array(
333
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\FileLockerAlerts' => $baseDir . '/src/Modules/HackGuard/Lib/Reports/FileLockerAlerts.php',
334
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\Query\\ScanCounts' => $baseDir . '/src/Modules/HackGuard/Lib/Reports/Query/ScanCounts.php',
335
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\ScanAlerts' => $baseDir . '/src/Modules/HackGuard/Lib/Reports/ScanAlerts.php',
 
 
 
 
336
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\Build\\BuildHashesForAsset' => $baseDir . '/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesForAsset.php',
337
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\Build\\BuildHashesForCrowdSource' => $baseDir . '/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesForCrowdSource.php',
338
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\Build\\BuildHashesFromApi' => $baseDir . '/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesFromApi.php',
@@ -351,9 +355,17 @@ return array(
351
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\StoreAction\\ScheduleBuildAll' => $baseDir . '/src/Modules/HackGuard/Lib/Snapshots/StoreAction/ScheduleBuildAll.php',
352
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\StoreAction\\TouchAll' => $baseDir . '/src/Modules/HackGuard/Lib/Snapshots/StoreAction/TouchAll.php',
353
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Utility\\FileDownloadHandler' => $baseDir . '/src/Modules/HackGuard/Lib/Utility/FileDownloadHandler.php',
 
354
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\ModCon' => $baseDir . '/src/Modules/HackGuard/ModCon.php',
355
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Options' => $baseDir . '/src/Modules/HackGuard/Options.php',
356
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Processor' => $baseDir . '/src/Modules/HackGuard/Processor.php',
 
 
 
 
 
 
 
357
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Reporting' => $baseDir . '/src/Modules/HackGuard/Reporting.php',
358
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Scan\\Controller\\Apc' => $baseDir . '/src/Modules/HackGuard/Scan/Controller/Apc.php',
359
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Scan\\Controller\\Base' => $baseDir . '/src/Modules/HackGuard/Scan/Controller/Base.php',
@@ -472,6 +484,7 @@ return array(
472
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\KaliForms' => $baseDir . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/KaliForms.php',
473
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\NinjaForms' => $baseDir . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/NinjaForms.php',
474
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\SuperForms' => $baseDir . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/SuperForms.php',
 
475
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\WPForms' => $baseDir . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/WPForms.php',
476
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\WpForo' => $baseDir . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/WpForo.php',
477
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\SpamController' => $baseDir . '/src/Modules/Integrations/Lib/Bots/Spam/SpamController.php',
@@ -578,7 +591,6 @@ return array(
578
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\AntiBot\\ProtectionProviders\\GoogleRecaptcha' => $baseDir . '/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GoogleRecaptcha.php',
579
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\AntiBot\\ProtectionProviders\\HCaptcha' => $baseDir . '/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/HCaptcha.php',
580
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\CooldownFlagFile' => $baseDir . '/src/Modules/LoginGuard/Lib/CooldownFlagFile.php',
581
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\CooldownRedirect' => $baseDir . '/src/Modules/LoginGuard/Lib/CooldownRedirect.php',
582
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\Rename\\RenameLogin' => $baseDir . '/src/Modules/LoginGuard/Lib/Rename/RenameLogin.php',
583
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\TwoFactor\\LoginIntentPage' => $baseDir . '/src/Modules/LoginGuard/Lib/TwoFactor/LoginIntentPage.php',
584
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\TwoFactor\\MfaController' => $baseDir . '/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php',
@@ -734,7 +746,6 @@ return array(
734
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\UserManagement\\WpCli\\SessionTerminate' => $baseDir . '/src/Modules/UserManagement/WpCli/SessionTerminate.php',
735
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Render\\LocateTemplateDirs' => $baseDir . '/src/Render/LocateTemplateDirs.php',
736
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\BuildScanAction' => $baseDir . '/src/Scans/Apc/BuildScanAction.php',
737
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ConvertVosToResults' => $baseDir . '/src/Scans/Apc/ConvertVosToResults.php',
738
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\PluginScanner' => $baseDir . '/src/Scans/Apc/PluginScanner.php',
739
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ResultItem' => $baseDir . '/src/Scans/Apc/ResultItem.php',
740
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ResultsSet' => $baseDir . '/src/Scans/Apc/ResultsSet.php',
@@ -742,17 +753,19 @@ return array(
742
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ScanActionVO' => $baseDir . '/src/Scans/Apc/ScanActionVO.php',
743
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\Utilities\\ItemActionHandler' => $baseDir . '/src/Scans/Apc/Utilities/ItemActionHandler.php',
744
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\Utilities\\Repair' => $baseDir . '/src/Scans/Apc/Utilities/Repair.php',
 
745
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseBuildScanAction' => $baseDir . '/src/Scans/Base/BaseBuildScanAction.php',
746
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseConvertVosToResults' => $baseDir . '/src/Scans/Base/BaseConvertVosToResults.php',
747
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseMergeItems' => $baseDir . '/src/Scans/Base/BaseMergeItems.php',
748
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseResultItem' => $baseDir . '/src/Scans/Base/BaseResultItem.php',
749
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseResultsSet' => $baseDir . '/src/Scans/Base/BaseResultsSet.php',
750
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseScan' => $baseDir . '/src/Scans/Base/BaseScan.php',
751
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseScanActionVO' => $baseDir . '/src/Scans/Base/BaseScanActionVO.php',
752
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\DiffResultForStorage' => $baseDir . '/src/Scans/Base/DiffResultForStorage.php',
 
753
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Files\\BaseFileMapScan' => $baseDir . '/src/Scans/Base/Files/BaseFileMapScan.php',
754
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Files\\BaseFileScanner' => $baseDir . '/src/Scans/Base/Files/BaseFileScanner.php',
755
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Files\\BaseScanFromFileMap' => $baseDir . '/src/Scans/Base/Files/BaseScanFromFileMap.php',
 
 
756
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Table\\BaseEntryFormatter' => $baseDir . '/src/Scans/Base/Table/BaseEntryFormatter.php',
757
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Table\\BaseFileEntryFormatter' => $baseDir . '/src/Scans/Base/Table/BaseFileEntryFormatter.php',
758
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Utilities\\BaseRepair' => $baseDir . '/src/Scans/Base/Utilities/BaseRepair.php',
@@ -793,7 +806,6 @@ return array(
793
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ptg\\Utilities\\Repair' => $baseDir . '/src/Scans/Ptg/Utilities/Repair.php',
794
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\BuildFileMap' => $baseDir . '/src/Scans/Ufc/BuildFileMap.php',
795
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\BuildScanAction' => $baseDir . '/src/Scans/Ufc/BuildScanAction.php',
796
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\ConvertVosToResults' => $baseDir . '/src/Scans/Ufc/ConvertVosToResults.php',
797
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\FileScanner' => $baseDir . '/src/Scans/Ufc/FileScanner.php',
798
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\ResultItem' => $baseDir . '/src/Scans/Ufc/ResultItem.php',
799
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\ResultsSet' => $baseDir . '/src/Scans/Ufc/ResultsSet.php',
@@ -805,7 +817,6 @@ return array(
805
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\Utilities\\Repair' => $baseDir . '/src/Scans/Ufc/Utilities/Repair.php',
806
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\BuildFileMap' => $baseDir . '/src/Scans/Wcf/BuildFileMap.php',
807
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\BuildScanAction' => $baseDir . '/src/Scans/Wcf/BuildScanAction.php',
808
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\ConvertVosToResults' => $baseDir . '/src/Scans/Wcf/ConvertVosToResults.php',
809
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\FileScanner' => $baseDir . '/src/Scans/Wcf/FileScanner.php',
810
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\ResultItem' => $baseDir . '/src/Scans/Wcf/ResultItem.php',
811
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\ResultsSet' => $baseDir . '/src/Scans/Wcf/ResultsSet.php',
@@ -816,7 +827,6 @@ return array(
816
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\Utilities\\ItemActionHandler' => $baseDir . '/src/Scans/Wcf/Utilities/ItemActionHandler.php',
817
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\Utilities\\Repair' => $baseDir . '/src/Scans/Wcf/Utilities/Repair.php',
818
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\BuildScanAction' => $baseDir . '/src/Scans/Wpv/BuildScanAction.php',
819
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\ConvertVosToResults' => $baseDir . '/src/Scans/Wpv/ConvertVosToResults.php',
820
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\ResultItem' => $baseDir . '/src/Scans/Wpv/ResultItem.php',
821
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\ResultsSet' => $baseDir . '/src/Scans/Wpv/ResultsSet.php',
822
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\Scan' => $baseDir . '/src/Scans/Wpv/Scan.php',
@@ -854,10 +864,13 @@ return array(
854
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\ScanWpv' => $baseDir . '/src/Tables/Build/ScanWpv.php',
855
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\Sessions' => $baseDir . '/src/Tables/Build/Sessions.php',
856
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\Traffic' => $baseDir . '/src/Tables/Build/Traffic.php',
 
 
 
 
 
 
857
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\Common\\BaseTable' => $baseDir . '/src/Tables/Render/Common/BaseTable.php',
858
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\DataTable\\Base' => $baseDir . '/src/Tables/Render/DataTable/Base.php',
859
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\DataTable\\ScanBase' => $baseDir . '/src/Tables/Render/DataTable/ScanBase.php',
860
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\DataTable\\ScanWcf' => $baseDir . '/src/Tables/Render/DataTable/ScanWcf.php',
861
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\WpCliTable\\AuditTrail' => $baseDir . '/src/Tables/Render/WpCliTable/AuditTrail.php',
862
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\WpCliTable\\Base' => $baseDir . '/src/Tables/Render/WpCliTable/Base.php',
863
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\WpListTable\\AdminNotes' => $baseDir . '/src/Tables/Render/WpListTable/AdminNotes.php',
@@ -881,6 +894,7 @@ return array(
881
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Users\\ShieldUserMeta' => $baseDir . '/src/Users/ShieldUserMeta.php',
882
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\AdminNotices\\Controller' => $baseDir . '/src/Utilities/AdminNotices/Controller.php',
883
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\AdminNotices\\NoticeVO' => $baseDir . '/src/Utilities/AdminNotices/NoticeVO.php',
 
884
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Changelog\\Retrieve' => $baseDir . '/src/Utilities/Changelog/Retrieve.php',
885
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Consumer\\WpLoginCapture' => $baseDir . '/src/Utilities/Consumer/WpLoginCapture.php',
886
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Consumer\\WpUserConsumer' => $baseDir . '/src/Utilities/Consumer/WpUserConsumer.php',
@@ -933,9 +947,11 @@ return array(
933
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpPluginVo' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php',
934
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpThemeVo' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php',
935
  'FernleafSystems\\Wordpress\\Services\\Services' => $vendorDir . '/fernleafsystems/wordpress-services/src/Services.php',
 
936
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Autoloading\\FindClassFromNamespaceRoots' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/Autoloading/FindClassFromNamespaceRoots.php',
937
  'FernleafSystems\\Wordpress\\Services\\Utilities\\BackgroundProcessing\\BackgroundProcess' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/BackgroundProcessing/BackgroundProcess.php',
938
  'FernleafSystems\\Wordpress\\Services\\Utilities\\ClassicPress\\Checksums' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/ClassicPress/Checksums.php',
 
939
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Constants\\Regex' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/Constants/Regex.php',
940
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Consumers\\PluginConsumer' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/Consumers/PluginConsumer.php',
941
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Data' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/Data.php',
251
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ModCon' => $baseDir . '/src/Modules/Base/ModCon.php',
252
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options' => $baseDir . '/src/Modules/Base/Options.php',
253
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\OptValueSanitize' => $baseDir . '/src/Modules/Base/Options/OptValueSanitize.php',
254
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\WildCardOptions' => $baseDir . '/src/Modules/Base/Options/WildCardOptions.php',
255
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Processor' => $baseDir . '/src/Modules/Base/Processor.php',
256
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Reporting' => $baseDir . '/src/Modules/Base/Reporting.php',
257
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\RestHandler' => $baseDir . '/src/Modules/Base/RestHandler.php',
292
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\EventsListener' => $baseDir . '/src/Modules/Events/Lib/EventsListener.php',
293
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\EventsService' => $baseDir . '/src/Modules/Events/Lib/EventsService.php',
294
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\Reports\\KeyStats' => $baseDir . '/src/Modules/Events/Lib/Reports/KeyStats.php',
 
295
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\StatsWriter' => $baseDir . '/src/Modules/Events/Lib/StatsWriter.php',
296
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\UI\\BuildDataForStats' => $baseDir . '/src/Modules/Events/Lib/UI/BuildDataForStats.php',
297
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\ModCon' => $baseDir . '/src/Modules/Events/ModCon.php',
333
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\FileLockerAlerts' => $baseDir . '/src/Modules/HackGuard/Lib/Reports/FileLockerAlerts.php',
334
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\Query\\ScanCounts' => $baseDir . '/src/Modules/HackGuard/Lib/Reports/Query/ScanCounts.php',
335
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\ScanAlerts' => $baseDir . '/src/Modules/HackGuard/Lib/Reports/ScanAlerts.php',
336
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\ScanRepairs' => $baseDir . '/src/Modules/HackGuard/Lib/Reports/ScanRepairs.php',
337
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\ScanTables\\DelegateAjaxHandler' => $baseDir . '/src/Modules/HackGuard/Lib/ScanTables/DelegateAjaxHandler.php',
338
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\ScanTables\\LoadRawTableData' => $baseDir . '/src/Modules/HackGuard/Lib/ScanTables/LoadRawTableData.php',
339
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\ScanTables\\RetrieveFileContents' => $baseDir . '/src/Modules/HackGuard/Lib/ScanTables/RetrieveFileContents.php',
340
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\Build\\BuildHashesForAsset' => $baseDir . '/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesForAsset.php',
341
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\Build\\BuildHashesForCrowdSource' => $baseDir . '/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesForCrowdSource.php',
342
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\Build\\BuildHashesFromApi' => $baseDir . '/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesFromApi.php',
355
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\StoreAction\\ScheduleBuildAll' => $baseDir . '/src/Modules/HackGuard/Lib/Snapshots/StoreAction/ScheduleBuildAll.php',
356
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\StoreAction\\TouchAll' => $baseDir . '/src/Modules/HackGuard/Lib/Snapshots/StoreAction/TouchAll.php',
357
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Utility\\FileDownloadHandler' => $baseDir . '/src/Modules/HackGuard/Lib/Utility/FileDownloadHandler.php',
358
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Utility\\VerifyFileByHash' => $baseDir . '/src/Modules/HackGuard/Lib/Utility/VerifyFileByHash.php',
359
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\ModCon' => $baseDir . '/src/Modules/HackGuard/ModCon.php',
360
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Options' => $baseDir . '/src/Modules/HackGuard/Options.php',
361
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Processor' => $baseDir . '/src/Modules/HackGuard/Processor.php',
362
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionBase' => $baseDir . '/src/Modules/HackGuard/Render/ScanResults/SectionBase.php',
363
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionLogs' => $baseDir . '/src/Modules/HackGuard/Render/ScanResults/SectionLogs.php',
364
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionMalware' => $baseDir . '/src/Modules/HackGuard/Render/ScanResults/SectionMalware.php',
365
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionPluginThemesBase' => $baseDir . '/src/Modules/HackGuard/Render/ScanResults/SectionPluginThemesBase.php',
366
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionPlugins' => $baseDir . '/src/Modules/HackGuard/Render/ScanResults/SectionPlugins.php',
367
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionThemes' => $baseDir . '/src/Modules/HackGuard/Render/ScanResults/SectionThemes.php',
368
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionWordpress' => $baseDir . '/src/Modules/HackGuard/Render/ScanResults/SectionWordpress.php',
369
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Reporting' => $baseDir . '/src/Modules/HackGuard/Reporting.php',
370
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Scan\\Controller\\Apc' => $baseDir . '/src/Modules/HackGuard/Scan/Controller/Apc.php',
371
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Scan\\Controller\\Base' => $baseDir . '/src/Modules/HackGuard/Scan/Controller/Base.php',
484
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\KaliForms' => $baseDir . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/KaliForms.php',
485
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\NinjaForms' => $baseDir . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/NinjaForms.php',
486
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\SuperForms' => $baseDir . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/SuperForms.php',
487
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\SupportCandy' => $baseDir . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/SupportCandy.php',
488
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\WPForms' => $baseDir . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/WPForms.php',
489
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\WpForo' => $baseDir . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/WpForo.php',
490
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\SpamController' => $baseDir . '/src/Modules/Integrations/Lib/Bots/Spam/SpamController.php',
591
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\AntiBot\\ProtectionProviders\\GoogleRecaptcha' => $baseDir . '/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GoogleRecaptcha.php',
592
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\AntiBot\\ProtectionProviders\\HCaptcha' => $baseDir . '/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/HCaptcha.php',
593
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\CooldownFlagFile' => $baseDir . '/src/Modules/LoginGuard/Lib/CooldownFlagFile.php',
 
594
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\Rename\\RenameLogin' => $baseDir . '/src/Modules/LoginGuard/Lib/Rename/RenameLogin.php',
595
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\TwoFactor\\LoginIntentPage' => $baseDir . '/src/Modules/LoginGuard/Lib/TwoFactor/LoginIntentPage.php',
596
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\TwoFactor\\MfaController' => $baseDir . '/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php',
746
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\UserManagement\\WpCli\\SessionTerminate' => $baseDir . '/src/Modules/UserManagement/WpCli/SessionTerminate.php',
747
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Render\\LocateTemplateDirs' => $baseDir . '/src/Render/LocateTemplateDirs.php',
748
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\BuildScanAction' => $baseDir . '/src/Scans/Apc/BuildScanAction.php',
 
749
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\PluginScanner' => $baseDir . '/src/Scans/Apc/PluginScanner.php',
750
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ResultItem' => $baseDir . '/src/Scans/Apc/ResultItem.php',
751
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ResultsSet' => $baseDir . '/src/Scans/Apc/ResultsSet.php',
753
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ScanActionVO' => $baseDir . '/src/Scans/Apc/ScanActionVO.php',
754
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\Utilities\\ItemActionHandler' => $baseDir . '/src/Scans/Apc/Utilities/ItemActionHandler.php',
755
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\Utilities\\Repair' => $baseDir . '/src/Scans/Apc/Utilities/Repair.php',
756
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseBuildFileMap' => $baseDir . '/src/Scans/Base/BaseBuildFileMap.php',
757
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseBuildScanAction' => $baseDir . '/src/Scans/Base/BaseBuildScanAction.php',
758
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseFileScanActionVO' => $baseDir . '/src/Scans/Base/BaseFileScanActionVO.php',
759
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseMergeItems' => $baseDir . '/src/Scans/Base/BaseMergeItems.php',
 
 
760
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseScan' => $baseDir . '/src/Scans/Base/BaseScan.php',
761
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseScanActionVO' => $baseDir . '/src/Scans/Base/BaseScanActionVO.php',
762
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\DiffResultForStorage' => $baseDir . '/src/Scans/Base/DiffResultForStorage.php',
763
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\FileResultItem' => $baseDir . '/src/Scans/Base/FileResultItem.php',
764
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Files\\BaseFileMapScan' => $baseDir . '/src/Scans/Base/Files/BaseFileMapScan.php',
765
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Files\\BaseFileScanner' => $baseDir . '/src/Scans/Base/Files/BaseFileScanner.php',
766
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Files\\BaseScanFromFileMap' => $baseDir . '/src/Scans/Base/Files/BaseScanFromFileMap.php',
767
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\ResultItem' => $baseDir . '/src/Scans/Base/ResultItem.php',
768
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\ResultsSet' => $baseDir . '/src/Scans/Base/ResultsSet.php',
769
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Table\\BaseEntryFormatter' => $baseDir . '/src/Scans/Base/Table/BaseEntryFormatter.php',
770
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Table\\BaseFileEntryFormatter' => $baseDir . '/src/Scans/Base/Table/BaseFileEntryFormatter.php',
771
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Utilities\\BaseRepair' => $baseDir . '/src/Scans/Base/Utilities/BaseRepair.php',
806
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ptg\\Utilities\\Repair' => $baseDir . '/src/Scans/Ptg/Utilities/Repair.php',
807
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\BuildFileMap' => $baseDir . '/src/Scans/Ufc/BuildFileMap.php',
808
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\BuildScanAction' => $baseDir . '/src/Scans/Ufc/BuildScanAction.php',
 
809
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\FileScanner' => $baseDir . '/src/Scans/Ufc/FileScanner.php',
810
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\ResultItem' => $baseDir . '/src/Scans/Ufc/ResultItem.php',
811
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\ResultsSet' => $baseDir . '/src/Scans/Ufc/ResultsSet.php',
817
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\Utilities\\Repair' => $baseDir . '/src/Scans/Ufc/Utilities/Repair.php',
818
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\BuildFileMap' => $baseDir . '/src/Scans/Wcf/BuildFileMap.php',
819
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\BuildScanAction' => $baseDir . '/src/Scans/Wcf/BuildScanAction.php',
 
820
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\FileScanner' => $baseDir . '/src/Scans/Wcf/FileScanner.php',
821
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\ResultItem' => $baseDir . '/src/Scans/Wcf/ResultItem.php',
822
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\ResultsSet' => $baseDir . '/src/Scans/Wcf/ResultsSet.php',
827
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\Utilities\\ItemActionHandler' => $baseDir . '/src/Scans/Wcf/Utilities/ItemActionHandler.php',
828
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\Utilities\\Repair' => $baseDir . '/src/Scans/Wcf/Utilities/Repair.php',
829
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\BuildScanAction' => $baseDir . '/src/Scans/Wpv/BuildScanAction.php',
 
830
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\ResultItem' => $baseDir . '/src/Scans/Wpv/ResultItem.php',
831
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\ResultsSet' => $baseDir . '/src/Scans/Wpv/ResultsSet.php',
832
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\Scan' => $baseDir . '/src/Scans/Wpv/Scan.php',
864
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\ScanWpv' => $baseDir . '/src/Tables/Build/ScanWpv.php',
865
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\Sessions' => $baseDir . '/src/Tables/Build/Sessions.php',
866
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\Traffic' => $baseDir . '/src/Tables/Build/Traffic.php',
867
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\DataTables\\Build\\AuditTrail\\ForAuditTrail' => $baseDir . '/src/Tables/DataTables/Build/AuditTrail/ForAuditTrail.php',
868
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\DataTables\\Build\\Base' => $baseDir . '/src/Tables/DataTables/Build/Base.php',
869
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\DataTables\\Build\\Scans\\BaseForScan' => $baseDir . '/src/Tables/DataTables/Build/Scans/BaseForScan.php',
870
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\DataTables\\Build\\Scans\\ForMalware' => $baseDir . '/src/Tables/DataTables/Build/Scans/ForMalware.php',
871
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\DataTables\\Build\\Scans\\ForPluginTheme' => $baseDir . '/src/Tables/DataTables/Build/Scans/ForPluginTheme.php',
872
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\DataTables\\Build\\Scans\\ForWordpress' => $baseDir . '/src/Tables/DataTables/Build/Scans/ForWordpress.php',
873
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\Common\\BaseTable' => $baseDir . '/src/Tables/Render/Common/BaseTable.php',
 
 
 
874
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\WpCliTable\\AuditTrail' => $baseDir . '/src/Tables/Render/WpCliTable/AuditTrail.php',
875
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\WpCliTable\\Base' => $baseDir . '/src/Tables/Render/WpCliTable/Base.php',
876
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\WpListTable\\AdminNotes' => $baseDir . '/src/Tables/Render/WpListTable/AdminNotes.php',
894
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Users\\ShieldUserMeta' => $baseDir . '/src/Users/ShieldUserMeta.php',
895
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\AdminNotices\\Controller' => $baseDir . '/src/Utilities/AdminNotices/Controller.php',
896
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\AdminNotices\\NoticeVO' => $baseDir . '/src/Utilities/AdminNotices/NoticeVO.php',
897
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\CacheDir' => $baseDir . '/src/Utilities/CacheDir.php',
898
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Changelog\\Retrieve' => $baseDir . '/src/Utilities/Changelog/Retrieve.php',
899
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Consumer\\WpLoginCapture' => $baseDir . '/src/Utilities/Consumer/WpLoginCapture.php',
900
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Consumer\\WpUserConsumer' => $baseDir . '/src/Utilities/Consumer/WpUserConsumer.php',
947
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpPluginVo' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php',
948
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpThemeVo' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php',
949
  'FernleafSystems\\Wordpress\\Services\\Services' => $vendorDir . '/fernleafsystems/wordpress-services/src/Services.php',
950
+ 'FernleafSystems\\Wordpress\\Services\\Utilities\\Assets\\DetectInstallationDate' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/Assets/DetectInstallationDate.php',
951
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Autoloading\\FindClassFromNamespaceRoots' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/Autoloading/FindClassFromNamespaceRoots.php',
952
  'FernleafSystems\\Wordpress\\Services\\Utilities\\BackgroundProcessing\\BackgroundProcess' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/BackgroundProcessing/BackgroundProcess.php',
953
  'FernleafSystems\\Wordpress\\Services\\Utilities\\ClassicPress\\Checksums' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/ClassicPress/Checksums.php',
954
+ 'FernleafSystems\\Wordpress\\Services\\Utilities\\Code\\AssessPhpFile' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/Code/AssessPhpFile.php',
955
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Constants\\Regex' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/Constants/Regex.php',
956
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Consumers\\PluginConsumer' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/Consumers/PluginConsumer.php',
957
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Data' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/Data.php',
src/lib/vendor/composer/autoload_static.php CHANGED
@@ -424,6 +424,7 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
424
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ModCon' => __DIR__ . '/../..' . '/src/Modules/Base/ModCon.php',
425
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options' => __DIR__ . '/../..' . '/src/Modules/Base/Options.php',
426
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\OptValueSanitize' => __DIR__ . '/../..' . '/src/Modules/Base/Options/OptValueSanitize.php',
 
427
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Processor' => __DIR__ . '/../..' . '/src/Modules/Base/Processor.php',
428
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Reporting' => __DIR__ . '/../..' . '/src/Modules/Base/Reporting.php',
429
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\RestHandler' => __DIR__ . '/../..' . '/src/Modules/Base/RestHandler.php',
@@ -464,7 +465,6 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
464
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\EventsListener' => __DIR__ . '/../..' . '/src/Modules/Events/Lib/EventsListener.php',
465
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\EventsService' => __DIR__ . '/../..' . '/src/Modules/Events/Lib/EventsService.php',
466
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\Reports\\KeyStats' => __DIR__ . '/../..' . '/src/Modules/Events/Lib/Reports/KeyStats.php',
467
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\Reports\\ScanRepairs' => __DIR__ . '/../..' . '/src/Modules/Events/Lib/Reports/ScanRepairs.php',
468
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\StatsWriter' => __DIR__ . '/../..' . '/src/Modules/Events/Lib/StatsWriter.php',
469
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\UI\\BuildDataForStats' => __DIR__ . '/../..' . '/src/Modules/Events/Lib/UI/BuildDataForStats.php',
470
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\ModCon' => __DIR__ . '/../..' . '/src/Modules/Events/ModCon.php',
@@ -506,6 +506,10 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
506
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\FileLockerAlerts' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Reports/FileLockerAlerts.php',
507
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\Query\\ScanCounts' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Reports/Query/ScanCounts.php',
508
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\ScanAlerts' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Reports/ScanAlerts.php',
 
 
 
 
509
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\Build\\BuildHashesForAsset' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesForAsset.php',
510
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\Build\\BuildHashesForCrowdSource' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesForCrowdSource.php',
511
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\Build\\BuildHashesFromApi' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesFromApi.php',
@@ -524,9 +528,17 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
524
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\StoreAction\\ScheduleBuildAll' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Snapshots/StoreAction/ScheduleBuildAll.php',
525
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\StoreAction\\TouchAll' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Snapshots/StoreAction/TouchAll.php',
526
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Utility\\FileDownloadHandler' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Utility/FileDownloadHandler.php',
 
527
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\ModCon' => __DIR__ . '/../..' . '/src/Modules/HackGuard/ModCon.php',
528
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Options' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Options.php',
529
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Processor' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Processor.php',
 
 
 
 
 
 
 
530
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Reporting' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Reporting.php',
531
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Scan\\Controller\\Apc' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Scan/Controller/Apc.php',
532
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Scan\\Controller\\Base' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Scan/Controller/Base.php',
@@ -645,6 +657,7 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
645
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\KaliForms' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/KaliForms.php',
646
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\NinjaForms' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/NinjaForms.php',
647
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\SuperForms' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/SuperForms.php',
 
648
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\WPForms' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/WPForms.php',
649
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\WpForo' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/WpForo.php',
650
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\SpamController' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Bots/Spam/SpamController.php',
@@ -751,7 +764,6 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
751
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\AntiBot\\ProtectionProviders\\GoogleRecaptcha' => __DIR__ . '/../..' . '/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GoogleRecaptcha.php',
752
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\AntiBot\\ProtectionProviders\\HCaptcha' => __DIR__ . '/../..' . '/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/HCaptcha.php',
753
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\CooldownFlagFile' => __DIR__ . '/../..' . '/src/Modules/LoginGuard/Lib/CooldownFlagFile.php',
754
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\CooldownRedirect' => __DIR__ . '/../..' . '/src/Modules/LoginGuard/Lib/CooldownRedirect.php',
755
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\Rename\\RenameLogin' => __DIR__ . '/../..' . '/src/Modules/LoginGuard/Lib/Rename/RenameLogin.php',
756
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\TwoFactor\\LoginIntentPage' => __DIR__ . '/../..' . '/src/Modules/LoginGuard/Lib/TwoFactor/LoginIntentPage.php',
757
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\TwoFactor\\MfaController' => __DIR__ . '/../..' . '/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php',
@@ -907,7 +919,6 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
907
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\UserManagement\\WpCli\\SessionTerminate' => __DIR__ . '/../..' . '/src/Modules/UserManagement/WpCli/SessionTerminate.php',
908
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Render\\LocateTemplateDirs' => __DIR__ . '/../..' . '/src/Render/LocateTemplateDirs.php',
909
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\BuildScanAction' => __DIR__ . '/../..' . '/src/Scans/Apc/BuildScanAction.php',
910
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ConvertVosToResults' => __DIR__ . '/../..' . '/src/Scans/Apc/ConvertVosToResults.php',
911
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\PluginScanner' => __DIR__ . '/../..' . '/src/Scans/Apc/PluginScanner.php',
912
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ResultItem' => __DIR__ . '/../..' . '/src/Scans/Apc/ResultItem.php',
913
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ResultsSet' => __DIR__ . '/../..' . '/src/Scans/Apc/ResultsSet.php',
@@ -915,17 +926,19 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
915
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ScanActionVO' => __DIR__ . '/../..' . '/src/Scans/Apc/ScanActionVO.php',
916
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\Utilities\\ItemActionHandler' => __DIR__ . '/../..' . '/src/Scans/Apc/Utilities/ItemActionHandler.php',
917
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\Utilities\\Repair' => __DIR__ . '/../..' . '/src/Scans/Apc/Utilities/Repair.php',
 
918
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseBuildScanAction' => __DIR__ . '/../..' . '/src/Scans/Base/BaseBuildScanAction.php',
919
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseConvertVosToResults' => __DIR__ . '/../..' . '/src/Scans/Base/BaseConvertVosToResults.php',
920
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseMergeItems' => __DIR__ . '/../..' . '/src/Scans/Base/BaseMergeItems.php',
921
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseResultItem' => __DIR__ . '/../..' . '/src/Scans/Base/BaseResultItem.php',
922
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseResultsSet' => __DIR__ . '/../..' . '/src/Scans/Base/BaseResultsSet.php',
923
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseScan' => __DIR__ . '/../..' . '/src/Scans/Base/BaseScan.php',
924
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseScanActionVO' => __DIR__ . '/../..' . '/src/Scans/Base/BaseScanActionVO.php',
925
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\DiffResultForStorage' => __DIR__ . '/../..' . '/src/Scans/Base/DiffResultForStorage.php',
 
926
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Files\\BaseFileMapScan' => __DIR__ . '/../..' . '/src/Scans/Base/Files/BaseFileMapScan.php',
927
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Files\\BaseFileScanner' => __DIR__ . '/../..' . '/src/Scans/Base/Files/BaseFileScanner.php',
928
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Files\\BaseScanFromFileMap' => __DIR__ . '/../..' . '/src/Scans/Base/Files/BaseScanFromFileMap.php',
 
 
929
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Table\\BaseEntryFormatter' => __DIR__ . '/../..' . '/src/Scans/Base/Table/BaseEntryFormatter.php',
930
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Table\\BaseFileEntryFormatter' => __DIR__ . '/../..' . '/src/Scans/Base/Table/BaseFileEntryFormatter.php',
931
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Utilities\\BaseRepair' => __DIR__ . '/../..' . '/src/Scans/Base/Utilities/BaseRepair.php',
@@ -966,7 +979,6 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
966
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ptg\\Utilities\\Repair' => __DIR__ . '/../..' . '/src/Scans/Ptg/Utilities/Repair.php',
967
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\BuildFileMap' => __DIR__ . '/../..' . '/src/Scans/Ufc/BuildFileMap.php',
968
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\BuildScanAction' => __DIR__ . '/../..' . '/src/Scans/Ufc/BuildScanAction.php',
969
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\ConvertVosToResults' => __DIR__ . '/../..' . '/src/Scans/Ufc/ConvertVosToResults.php',
970
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\FileScanner' => __DIR__ . '/../..' . '/src/Scans/Ufc/FileScanner.php',
971
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\ResultItem' => __DIR__ . '/../..' . '/src/Scans/Ufc/ResultItem.php',
972
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\ResultsSet' => __DIR__ . '/../..' . '/src/Scans/Ufc/ResultsSet.php',
@@ -978,7 +990,6 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
978
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\Utilities\\Repair' => __DIR__ . '/../..' . '/src/Scans/Ufc/Utilities/Repair.php',
979
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\BuildFileMap' => __DIR__ . '/../..' . '/src/Scans/Wcf/BuildFileMap.php',
980
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\BuildScanAction' => __DIR__ . '/../..' . '/src/Scans/Wcf/BuildScanAction.php',
981
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\ConvertVosToResults' => __DIR__ . '/../..' . '/src/Scans/Wcf/ConvertVosToResults.php',
982
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\FileScanner' => __DIR__ . '/../..' . '/src/Scans/Wcf/FileScanner.php',
983
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\ResultItem' => __DIR__ . '/../..' . '/src/Scans/Wcf/ResultItem.php',
984
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\ResultsSet' => __DIR__ . '/../..' . '/src/Scans/Wcf/ResultsSet.php',
@@ -989,7 +1000,6 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
989
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\Utilities\\ItemActionHandler' => __DIR__ . '/../..' . '/src/Scans/Wcf/Utilities/ItemActionHandler.php',
990
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\Utilities\\Repair' => __DIR__ . '/../..' . '/src/Scans/Wcf/Utilities/Repair.php',
991
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\BuildScanAction' => __DIR__ . '/../..' . '/src/Scans/Wpv/BuildScanAction.php',
992
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\ConvertVosToResults' => __DIR__ . '/../..' . '/src/Scans/Wpv/ConvertVosToResults.php',
993
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\ResultItem' => __DIR__ . '/../..' . '/src/Scans/Wpv/ResultItem.php',
994
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\ResultsSet' => __DIR__ . '/../..' . '/src/Scans/Wpv/ResultsSet.php',
995
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\Scan' => __DIR__ . '/../..' . '/src/Scans/Wpv/Scan.php',
@@ -1027,10 +1037,13 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
1027
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\ScanWpv' => __DIR__ . '/../..' . '/src/Tables/Build/ScanWpv.php',
1028
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\Sessions' => __DIR__ . '/../..' . '/src/Tables/Build/Sessions.php',
1029
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\Traffic' => __DIR__ . '/../..' . '/src/Tables/Build/Traffic.php',
 
 
 
 
 
 
1030
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\Common\\BaseTable' => __DIR__ . '/../..' . '/src/Tables/Render/Common/BaseTable.php',
1031
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\DataTable\\Base' => __DIR__ . '/../..' . '/src/Tables/Render/DataTable/Base.php',
1032
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\DataTable\\ScanBase' => __DIR__ . '/../..' . '/src/Tables/Render/DataTable/ScanBase.php',
1033
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\DataTable\\ScanWcf' => __DIR__ . '/../..' . '/src/Tables/Render/DataTable/ScanWcf.php',
1034
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\WpCliTable\\AuditTrail' => __DIR__ . '/../..' . '/src/Tables/Render/WpCliTable/AuditTrail.php',
1035
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\WpCliTable\\Base' => __DIR__ . '/../..' . '/src/Tables/Render/WpCliTable/Base.php',
1036
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\WpListTable\\AdminNotes' => __DIR__ . '/../..' . '/src/Tables/Render/WpListTable/AdminNotes.php',
@@ -1054,6 +1067,7 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
1054
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Users\\ShieldUserMeta' => __DIR__ . '/../..' . '/src/Users/ShieldUserMeta.php',
1055
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\AdminNotices\\Controller' => __DIR__ . '/../..' . '/src/Utilities/AdminNotices/Controller.php',
1056
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\AdminNotices\\NoticeVO' => __DIR__ . '/../..' . '/src/Utilities/AdminNotices/NoticeVO.php',
 
1057
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Changelog\\Retrieve' => __DIR__ . '/../..' . '/src/Utilities/Changelog/Retrieve.php',
1058
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Consumer\\WpLoginCapture' => __DIR__ . '/../..' . '/src/Utilities/Consumer/WpLoginCapture.php',
1059
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Consumer\\WpUserConsumer' => __DIR__ . '/../..' . '/src/Utilities/Consumer/WpUserConsumer.php',
@@ -1106,9 +1120,11 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
1106
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpPluginVo' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php',
1107
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpThemeVo' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php',
1108
  'FernleafSystems\\Wordpress\\Services\\Services' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Services.php',
 
1109
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Autoloading\\FindClassFromNamespaceRoots' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/Autoloading/FindClassFromNamespaceRoots.php',
1110
  'FernleafSystems\\Wordpress\\Services\\Utilities\\BackgroundProcessing\\BackgroundProcess' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/BackgroundProcessing/BackgroundProcess.php',
1111
  'FernleafSystems\\Wordpress\\Services\\Utilities\\ClassicPress\\Checksums' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/ClassicPress/Checksums.php',
 
1112
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Constants\\Regex' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/Constants/Regex.php',
1113
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Consumers\\PluginConsumer' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/Consumers/PluginConsumer.php',
1114
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Data' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/Data.php',
424
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ModCon' => __DIR__ . '/../..' . '/src/Modules/Base/ModCon.php',
425
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options' => __DIR__ . '/../..' . '/src/Modules/Base/Options.php',
426
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\OptValueSanitize' => __DIR__ . '/../..' . '/src/Modules/Base/Options/OptValueSanitize.php',
427
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\WildCardOptions' => __DIR__ . '/../..' . '/src/Modules/Base/Options/WildCardOptions.php',
428
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Processor' => __DIR__ . '/../..' . '/src/Modules/Base/Processor.php',
429
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Reporting' => __DIR__ . '/../..' . '/src/Modules/Base/Reporting.php',
430
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\RestHandler' => __DIR__ . '/../..' . '/src/Modules/Base/RestHandler.php',
465
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\EventsListener' => __DIR__ . '/../..' . '/src/Modules/Events/Lib/EventsListener.php',
466
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\EventsService' => __DIR__ . '/../..' . '/src/Modules/Events/Lib/EventsService.php',
467
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\Reports\\KeyStats' => __DIR__ . '/../..' . '/src/Modules/Events/Lib/Reports/KeyStats.php',
 
468
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\StatsWriter' => __DIR__ . '/../..' . '/src/Modules/Events/Lib/StatsWriter.php',
469
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Lib\\UI\\BuildDataForStats' => __DIR__ . '/../..' . '/src/Modules/Events/Lib/UI/BuildDataForStats.php',
470
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\ModCon' => __DIR__ . '/../..' . '/src/Modules/Events/ModCon.php',
506
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\FileLockerAlerts' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Reports/FileLockerAlerts.php',
507
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\Query\\ScanCounts' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Reports/Query/ScanCounts.php',
508
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\ScanAlerts' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Reports/ScanAlerts.php',
509
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Reports\\ScanRepairs' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Reports/ScanRepairs.php',
510
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\ScanTables\\DelegateAjaxHandler' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/ScanTables/DelegateAjaxHandler.php',
511
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\ScanTables\\LoadRawTableData' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/ScanTables/LoadRawTableData.php',
512
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\ScanTables\\RetrieveFileContents' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/ScanTables/RetrieveFileContents.php',
513
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\Build\\BuildHashesForAsset' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesForAsset.php',
514
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\Build\\BuildHashesForCrowdSource' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesForCrowdSource.php',
515
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\Build\\BuildHashesFromApi' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesFromApi.php',
528
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\StoreAction\\ScheduleBuildAll' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Snapshots/StoreAction/ScheduleBuildAll.php',
529
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Snapshots\\StoreAction\\TouchAll' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Snapshots/StoreAction/TouchAll.php',
530
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Utility\\FileDownloadHandler' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Utility/FileDownloadHandler.php',
531
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\Utility\\VerifyFileByHash' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/Utility/VerifyFileByHash.php',
532
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\ModCon' => __DIR__ . '/../..' . '/src/Modules/HackGuard/ModCon.php',
533
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Options' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Options.php',
534
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Processor' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Processor.php',
535
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionBase' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Render/ScanResults/SectionBase.php',
536
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionLogs' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Render/ScanResults/SectionLogs.php',
537
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionMalware' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Render/ScanResults/SectionMalware.php',
538
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionPluginThemesBase' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Render/ScanResults/SectionPluginThemesBase.php',
539
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionPlugins' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Render/ScanResults/SectionPlugins.php',
540
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionThemes' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Render/ScanResults/SectionThemes.php',
541
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Render\\ScanResults\\SectionWordpress' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Render/ScanResults/SectionWordpress.php',
542
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Reporting' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Reporting.php',
543
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Scan\\Controller\\Apc' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Scan/Controller/Apc.php',
544
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Scan\\Controller\\Base' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Scan/Controller/Base.php',
657
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\KaliForms' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/KaliForms.php',
658
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\NinjaForms' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/NinjaForms.php',
659
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\SuperForms' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/SuperForms.php',
660
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\SupportCandy' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/SupportCandy.php',
661
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\WPForms' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/WPForms.php',
662
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\Handlers\\WpForo' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Bots/Spam/Handlers/WpForo.php',
663
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Bots\\Spam\\SpamController' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Bots/Spam/SpamController.php',
764
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\AntiBot\\ProtectionProviders\\GoogleRecaptcha' => __DIR__ . '/../..' . '/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GoogleRecaptcha.php',
765
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\AntiBot\\ProtectionProviders\\HCaptcha' => __DIR__ . '/../..' . '/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/HCaptcha.php',
766
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\CooldownFlagFile' => __DIR__ . '/../..' . '/src/Modules/LoginGuard/Lib/CooldownFlagFile.php',
 
767
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\Rename\\RenameLogin' => __DIR__ . '/../..' . '/src/Modules/LoginGuard/Lib/Rename/RenameLogin.php',
768
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\TwoFactor\\LoginIntentPage' => __DIR__ . '/../..' . '/src/Modules/LoginGuard/Lib/TwoFactor/LoginIntentPage.php',
769
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\LoginGuard\\Lib\\TwoFactor\\MfaController' => __DIR__ . '/../..' . '/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php',
919
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\UserManagement\\WpCli\\SessionTerminate' => __DIR__ . '/../..' . '/src/Modules/UserManagement/WpCli/SessionTerminate.php',
920
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Render\\LocateTemplateDirs' => __DIR__ . '/../..' . '/src/Render/LocateTemplateDirs.php',
921
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\BuildScanAction' => __DIR__ . '/../..' . '/src/Scans/Apc/BuildScanAction.php',
 
922
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\PluginScanner' => __DIR__ . '/../..' . '/src/Scans/Apc/PluginScanner.php',
923
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ResultItem' => __DIR__ . '/../..' . '/src/Scans/Apc/ResultItem.php',
924
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ResultsSet' => __DIR__ . '/../..' . '/src/Scans/Apc/ResultsSet.php',
926
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\ScanActionVO' => __DIR__ . '/../..' . '/src/Scans/Apc/ScanActionVO.php',
927
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\Utilities\\ItemActionHandler' => __DIR__ . '/../..' . '/src/Scans/Apc/Utilities/ItemActionHandler.php',
928
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Apc\\Utilities\\Repair' => __DIR__ . '/../..' . '/src/Scans/Apc/Utilities/Repair.php',
929
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseBuildFileMap' => __DIR__ . '/../..' . '/src/Scans/Base/BaseBuildFileMap.php',
930
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseBuildScanAction' => __DIR__ . '/../..' . '/src/Scans/Base/BaseBuildScanAction.php',
931
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseFileScanActionVO' => __DIR__ . '/../..' . '/src/Scans/Base/BaseFileScanActionVO.php',
932
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseMergeItems' => __DIR__ . '/../..' . '/src/Scans/Base/BaseMergeItems.php',
 
 
933
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseScan' => __DIR__ . '/../..' . '/src/Scans/Base/BaseScan.php',
934
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\BaseScanActionVO' => __DIR__ . '/../..' . '/src/Scans/Base/BaseScanActionVO.php',
935
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\DiffResultForStorage' => __DIR__ . '/../..' . '/src/Scans/Base/DiffResultForStorage.php',
936
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\FileResultItem' => __DIR__ . '/../..' . '/src/Scans/Base/FileResultItem.php',
937
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Files\\BaseFileMapScan' => __DIR__ . '/../..' . '/src/Scans/Base/Files/BaseFileMapScan.php',
938
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Files\\BaseFileScanner' => __DIR__ . '/../..' . '/src/Scans/Base/Files/BaseFileScanner.php',
939
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Files\\BaseScanFromFileMap' => __DIR__ . '/../..' . '/src/Scans/Base/Files/BaseScanFromFileMap.php',
940
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\ResultItem' => __DIR__ . '/../..' . '/src/Scans/Base/ResultItem.php',
941
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\ResultsSet' => __DIR__ . '/../..' . '/src/Scans/Base/ResultsSet.php',
942
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Table\\BaseEntryFormatter' => __DIR__ . '/../..' . '/src/Scans/Base/Table/BaseEntryFormatter.php',
943
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Table\\BaseFileEntryFormatter' => __DIR__ . '/../..' . '/src/Scans/Base/Table/BaseFileEntryFormatter.php',
944
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Base\\Utilities\\BaseRepair' => __DIR__ . '/../..' . '/src/Scans/Base/Utilities/BaseRepair.php',
979
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ptg\\Utilities\\Repair' => __DIR__ . '/../..' . '/src/Scans/Ptg/Utilities/Repair.php',
980
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\BuildFileMap' => __DIR__ . '/../..' . '/src/Scans/Ufc/BuildFileMap.php',
981
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\BuildScanAction' => __DIR__ . '/../..' . '/src/Scans/Ufc/BuildScanAction.php',
 
982
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\FileScanner' => __DIR__ . '/../..' . '/src/Scans/Ufc/FileScanner.php',
983
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\ResultItem' => __DIR__ . '/../..' . '/src/Scans/Ufc/ResultItem.php',
984
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\ResultsSet' => __DIR__ . '/../..' . '/src/Scans/Ufc/ResultsSet.php',
990
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Ufc\\Utilities\\Repair' => __DIR__ . '/../..' . '/src/Scans/Ufc/Utilities/Repair.php',
991
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\BuildFileMap' => __DIR__ . '/../..' . '/src/Scans/Wcf/BuildFileMap.php',
992
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\BuildScanAction' => __DIR__ . '/../..' . '/src/Scans/Wcf/BuildScanAction.php',
 
993
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\FileScanner' => __DIR__ . '/../..' . '/src/Scans/Wcf/FileScanner.php',
994
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\ResultItem' => __DIR__ . '/../..' . '/src/Scans/Wcf/ResultItem.php',
995
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\ResultsSet' => __DIR__ . '/../..' . '/src/Scans/Wcf/ResultsSet.php',
1000
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\Utilities\\ItemActionHandler' => __DIR__ . '/../..' . '/src/Scans/Wcf/Utilities/ItemActionHandler.php',
1001
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wcf\\Utilities\\Repair' => __DIR__ . '/../..' . '/src/Scans/Wcf/Utilities/Repair.php',
1002
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\BuildScanAction' => __DIR__ . '/../..' . '/src/Scans/Wpv/BuildScanAction.php',
 
1003
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\ResultItem' => __DIR__ . '/../..' . '/src/Scans/Wpv/ResultItem.php',
1004
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\ResultsSet' => __DIR__ . '/../..' . '/src/Scans/Wpv/ResultsSet.php',
1005
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Wpv\\Scan' => __DIR__ . '/../..' . '/src/Scans/Wpv/Scan.php',
1037
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\ScanWpv' => __DIR__ . '/../..' . '/src/Tables/Build/ScanWpv.php',
1038
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\Sessions' => __DIR__ . '/../..' . '/src/Tables/Build/Sessions.php',
1039
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\Traffic' => __DIR__ . '/../..' . '/src/Tables/Build/Traffic.php',
1040
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\DataTables\\Build\\AuditTrail\\ForAuditTrail' => __DIR__ . '/../..' . '/src/Tables/DataTables/Build/AuditTrail/ForAuditTrail.php',
1041
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\DataTables\\Build\\Base' => __DIR__ . '/../..' . '/src/Tables/DataTables/Build/Base.php',
1042
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\DataTables\\Build\\Scans\\BaseForScan' => __DIR__ . '/../..' . '/src/Tables/DataTables/Build/Scans/BaseForScan.php',
1043
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\DataTables\\Build\\Scans\\ForMalware' => __DIR__ . '/../..' . '/src/Tables/DataTables/Build/Scans/ForMalware.php',
1044
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\DataTables\\Build\\Scans\\ForPluginTheme' => __DIR__ . '/../..' . '/src/Tables/DataTables/Build/Scans/ForPluginTheme.php',
1045
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\DataTables\\Build\\Scans\\ForWordpress' => __DIR__ . '/../..' . '/src/Tables/DataTables/Build/Scans/ForWordpress.php',
1046
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\Common\\BaseTable' => __DIR__ . '/../..' . '/src/Tables/Render/Common/BaseTable.php',
 
 
 
1047
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\WpCliTable\\AuditTrail' => __DIR__ . '/../..' . '/src/Tables/Render/WpCliTable/AuditTrail.php',
1048
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\WpCliTable\\Base' => __DIR__ . '/../..' . '/src/Tables/Render/WpCliTable/Base.php',
1049
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Render\\WpListTable\\AdminNotes' => __DIR__ . '/../..' . '/src/Tables/Render/WpListTable/AdminNotes.php',
1067
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Users\\ShieldUserMeta' => __DIR__ . '/../..' . '/src/Users/ShieldUserMeta.php',
1068
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\AdminNotices\\Controller' => __DIR__ . '/../..' . '/src/Utilities/AdminNotices/Controller.php',
1069
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\AdminNotices\\NoticeVO' => __DIR__ . '/../..' . '/src/Utilities/AdminNotices/NoticeVO.php',
1070
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\CacheDir' => __DIR__ . '/../..' . '/src/Utilities/CacheDir.php',
1071
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Changelog\\Retrieve' => __DIR__ . '/../..' . '/src/Utilities/Changelog/Retrieve.php',
1072
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Consumer\\WpLoginCapture' => __DIR__ . '/../..' . '/src/Utilities/Consumer/WpLoginCapture.php',
1073
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Consumer\\WpUserConsumer' => __DIR__ . '/../..' . '/src/Utilities/Consumer/WpUserConsumer.php',
1120
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpPluginVo' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php',
1121
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpThemeVo' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php',
1122
  'FernleafSystems\\Wordpress\\Services\\Services' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Services.php',
1123
+ 'FernleafSystems\\Wordpress\\Services\\Utilities\\Assets\\DetectInstallationDate' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/Assets/DetectInstallationDate.php',
1124
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Autoloading\\FindClassFromNamespaceRoots' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/Autoloading/FindClassFromNamespaceRoots.php',
1125
  'FernleafSystems\\Wordpress\\Services\\Utilities\\BackgroundProcessing\\BackgroundProcess' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/BackgroundProcessing/BackgroundProcess.php',
1126
  'FernleafSystems\\Wordpress\\Services\\Utilities\\ClassicPress\\Checksums' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/ClassicPress/Checksums.php',
1127
+ 'FernleafSystems\\Wordpress\\Services\\Utilities\\Code\\AssessPhpFile' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/Code/AssessPhpFile.php',
1128
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Constants\\Regex' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/Constants/Regex.php',
1129
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Consumers\\PluginConsumer' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/Consumers/PluginConsumer.php',
1130
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Data' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/Data.php',
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Fs.php CHANGED
@@ -239,45 +239,37 @@ class Fs {
239
  );
240
  }
241
 
242
- /**
243
- * @param string $sFilePath
244
- * @return int|null
245
- */
246
- public function getModifiedTime( $sFilePath ) {
247
- return $this->getTime( $sFilePath, 'modified' );
248
  }
249
 
250
- /**
251
- * @param string $sFilePath
252
- * @return int|null
253
- */
254
- public function getAccessedTime( $sFilePath ) {
255
- return $this->getTime( $sFilePath, 'accessed' );
256
  }
257
 
258
  /**
259
- * @param string $sFilePath
260
- * @param string $sProperty
261
  * @return int|null
 
262
  */
263
- public function getTime( $sFilePath, $sProperty = 'modified' ) {
264
 
265
- if ( !$this->exists( $sFilePath ) ) {
266
  return null;
267
  }
268
 
269
- $oFs = $this->getWpfs();
270
- switch ( $sProperty ) {
271
 
272
  case 'modified' :
273
- return $oFs ? $oFs->mtime( $sFilePath ) : filemtime( $sFilePath );
274
- break;
275
  case 'accessed' :
276
- return $oFs ? $oFs->atime( $sFilePath ) : fileatime( $sFilePath );
277
- break;
278
  default:
279
  return null;
280
- break;
281
  }
282
  }
283
 
@@ -309,9 +301,9 @@ class Fs {
309
  */
310
  public function getFileContent( $sFilePath, $bIsCompressed = false ) {
311
  $sContents = null;
312
- $oFs = $this->getWpfs();
313
- if ( $oFs ) {
314
- $sContents = $oFs->get_contents( $sFilePath );
315
  }
316
 
317
  if ( empty( $sContents ) && function_exists( 'file_get_contents' ) ) {
@@ -476,7 +468,7 @@ class Fs {
476
 
477
  public function isFile( $path ) :bool {
478
  return ( $this->hasWpfs() && $this->getWpfs()->is_file( $path ) )
479
- || ( function_exists( 'is_file' ) ? is_file( $path ) : false );
480
  }
481
 
482
  public function isFilesystemAccessDirect() :bool {
@@ -493,15 +485,18 @@ class Fs {
493
 
494
  /**
495
  * @param string $path
496
- * @param int $nTime
497
  * @return bool|mixed
498
  */
499
- public function touch( $path, $nTime = null ) {
500
- $oFs = $this->getWpfs();
501
- if ( $oFs && $oFs->touch( $path, $nTime ) ) {
 
 
 
502
  return true;
503
  }
504
- return function_exists( 'touch' ) ? @touch( $path, $nTime ) : null;
505
  }
506
 
507
  /**
239
  );
240
  }
241
 
242
+ public function getModifiedTime( string $path ) :int {
243
+ $FS = $this->getWpfs();
244
+ return (int)( $FS ? $FS->mtime( $path ) : @filemtime( $path ) );
 
 
 
245
  }
246
 
247
+ public function getAccessedTime( string $path ) :int {
248
+ $FS = $this->getWpfs();
249
+ return (int)( $FS ? $FS->atime( $path ) : @fileatime( $path ) );
 
 
 
250
  }
251
 
252
  /**
253
+ * @param string $path
254
+ * @param string $property
255
  * @return int|null
256
+ * @deprecated
257
  */
258
+ public function getTime( $path, $property = 'modified' ) {
259
 
260
+ if ( !$this->exists( $path ) ) {
261
  return null;
262
  }
263
 
264
+ $FS = $this->getWpfs();
265
+ switch ( $property ) {
266
 
267
  case 'modified' :
268
+ return $FS ? $FS->mtime( $path ) : filemtime( $path );
 
269
  case 'accessed' :
270
+ return $FS ? $FS->atime( $path ) : fileatime( $path );
 
271
  default:
272
  return null;
 
273
  }
274
  }
275
 
301
  */
302
  public function getFileContent( $sFilePath, $bIsCompressed = false ) {
303
  $sContents = null;
304
+ $FS = $this->getWpfs();
305
+ if ( $FS ) {
306
+ $sContents = $FS->get_contents( $sFilePath );
307
  }
308
 
309
  if ( empty( $sContents ) && function_exists( 'file_get_contents' ) ) {
468
 
469
  public function isFile( $path ) :bool {
470
  return ( $this->hasWpfs() && $this->getWpfs()->is_file( $path ) )
471
+ || ( function_exists( 'is_file' ) && is_file( $path ) );
472
  }
473
 
474
  public function isFilesystemAccessDirect() :bool {
485
 
486
  /**
487
  * @param string $path
488
+ * @param int $time
489
  * @return bool|mixed
490
  */
491
+ public function touch( $path, $time = null ) {
492
+ $FS = $this->getWpfs();
493
+ if ( empty( $time ) ) {
494
+ $time = time();
495
+ }
496
+ if ( $FS && $FS->touch( $path, $time ) ) {
497
  return true;
498
  }
499
+ return function_exists( 'touch' ) && @touch( $path, $time );
500
  }
501
 
502
  /**
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/Assets/WpBaseVo.php CHANGED
@@ -21,13 +21,13 @@ use FernleafSystems\Utilities\Data\Adapter\DynPropertiesClass;
21
  */
22
  abstract class WpBaseVo extends DynPropertiesClass {
23
 
24
- protected $hasExtendedData = false;
25
 
26
  public function __get( string $key ) {
27
 
28
- if ( empty( $this->hasExtendedData ) && in_array( $key, $this->getExtendedDataSlugs() ) ) {
29
  $this->applyFromArray( array_merge( $this->getRawData(), $this->getExtendedData() ) );
30
- $this->hasExtendedData = true;
31
  }
32
 
33
  $value = parent::__get( $key );
21
  */
22
  abstract class WpBaseVo extends DynPropertiesClass {
23
 
24
+ protected $extendedDataLoaded = false;
25
 
26
  public function __get( string $key ) {
27
 
28
+ if ( empty( $this->extendedDataLoaded ) && in_array( $key, $this->getExtendedDataSlugs() ) ) {
29
  $this->applyFromArray( array_merge( $this->getRawData(), $this->getExtendedData() ) );
30
+ $this->extendedDataLoaded = true;
31
  }
32
 
33
  $value = parent::__get( $key );
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/Assets/WpPluginVo.php CHANGED
@@ -52,6 +52,12 @@ class WpPluginVo extends WpBaseVo {
52
  $value = 'plugin';
53
  break;
54
 
 
 
 
 
 
 
55
  case 'unique_id':
56
  $value = $this->file;
57
  break;
52
  $value = 'plugin';
53
  break;
54
 
55
+ case 'slug':
56
+ if ( empty( $value ) ) {
57
+ $value = dirname( $this->file );
58
+ }
59
+ break;
60
+
61
  case 'unique_id':
62
  $value = $this->file;
63
  break;
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/Assets/WpThemeVo.php CHANGED
@@ -21,7 +21,9 @@ use FernleafSystems\Wordpress\Services\Utilities\WpOrg\Theme;
21
  * @property bool $is_parent
22
  *
23
  * Dynamic Properties:
24
- * @property string $slug (alias for Stylesheet)
 
 
25
  */
26
  class WpThemeVo extends WpBaseVo {
27
 
@@ -44,6 +46,7 @@ class WpThemeVo extends WpBaseVo {
44
  }
45
 
46
  public function __get( string $key ) {
 
47
 
48
  $value = parent::__get( $key );
49
 
@@ -68,6 +71,14 @@ class WpThemeVo extends WpBaseVo {
68
  }
69
  break;
70
 
 
 
 
 
 
 
 
 
71
  default:
72
  break;
73
  }
21
  * @property bool $is_parent
22
  *
23
  * Dynamic Properties:
24
+ * @property string $slug (alias for Stylesheet)
25
+ * @property WpThemeVo|null $parent_theme
26
+ * @property WpThemeVo|null $child_theme
27
  */
28
  class WpThemeVo extends WpBaseVo {
29
 
46
  }
47
 
48
  public function __get( string $key ) {
49
+ $WPT = Services::WpThemes();
50
 
51
  $value = parent::__get( $key );
52
 
71
  }
72
  break;
73
 
74
+ case 'child_theme':
75
+ $value = $this->is_parent ? $WPT->getThemeAsVo( $this->wp_theme->get_stylesheet() ) : null;
76
+ break;
77
+
78
+ case 'parent_theme':
79
+ $value = $this->is_child ? $WPT->getThemeAsVo( $this->wp_theme->get_template() ) : null;
80
+ break;
81
+
82
  default:
83
  break;
84
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Assets/DetectInstallationDate.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Services\Utilities\Assets;
4
+
5
+ use FernleafSystems\Wordpress\Services\Core\VOs\Assets\{
6
+ WpPluginVo,
7
+ WpThemeVo
8
+ };
9
+ use FernleafSystems\Wordpress\Services\Services;
10
+
11
+ class DetectInstallationDate {
12
+
13
+ public function plugin( WpPluginVo $asset ) :int {
14
+ return $this->detectFromDir( $asset->getInstallDir() );
15
+ }
16
+
17
+ public function theme( WpThemeVo $asset ) :int {
18
+ return $this->detectFromDir( $asset->getInstallDir() );
19
+ }
20
+
21
+ private function detectFromDir( string $dir ) :int {
22
+ $FS = Services::WpFs();
23
+ $time = $FS->getModifiedTime( $dir );
24
+ foreach ( $FS->getFilesInDir( $dir ) as $fileInfo ) {
25
+ $time = min( $time, $fileInfo->getMTime() );
26
+ }
27
+ return $time;
28
+ }
29
+ }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Code/AssessPhpFile.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Services\Utilities\Code;
4
+
5
+ use FernleafSystems\Wordpress\Services\Services;
6
+
7
+ class AssessPhpFile {
8
+
9
+ /**
10
+ * @param string $file
11
+ * @return bool
12
+ * @throws \Exception
13
+ */
14
+ public function isEmptyOfCode( string $file ) :bool {
15
+ $this->canRun();
16
+
17
+ $ext = strtolower( Services::Data()->getExtension( $file ) );
18
+ if ( !in_array( $ext, [ 'php', 'php5', 'php7', 'phtml' ] ) ) {
19
+ throw new \Exception( 'Not a standard PHP file.' );
20
+ }
21
+ if ( !Services::WpFs()->isFile( $file ) ) {
22
+ throw new \Exception( 'File does not exist on disk.' );
23
+ }
24
+
25
+ $Ts = token_get_all( $this->getRelevantContent( $file ) );
26
+
27
+ if ( !is_array( $Ts ) ) {
28
+ throw new \Exception( 'Could not get tokens.' );
29
+ }
30
+
31
+ $Ts = array_values( array_filter( $Ts, function ( $token ) {
32
+ return is_array( $token ) &&
33
+ !in_array( $token[ 0 ], [ T_WHITESPACE, T_DOC_COMMENT, T_COMMENT, T_INLINE_HTML ] );
34
+ } ) );
35
+
36
+ // If there is at least 1 token we assess it
37
+ if ( !empty( $Ts ) ) {
38
+
39
+ // If the 1st token isn't <?php
40
+ if ( $Ts[ 0 ][ 0 ] !== T_OPEN_TAG ) {
41
+ throw new \Exception( 'Irregular start to PHP file.' );
42
+ }
43
+ unset( $Ts[ 0 ] );
44
+
45
+ $Ts = array_values( $Ts );
46
+
47
+ if ( count( $Ts ) >= 3 ) {
48
+ if ( $Ts[ 0 ][ 0 ] == T_DECLARE &&
49
+ $Ts[ 1 ][ 0 ] == T_STRING && $Ts[ 2 ][ 0 ] == T_LNUMBER ) {
50
+ unset( $Ts[ 0 ], $Ts[ 1 ], $Ts[ 2 ] );
51
+
52
+ $Ts = array_values( $Ts );
53
+ }
54
+ }
55
+ }
56
+
57
+ return empty( $Ts );
58
+ }
59
+
60
+ private function printTokens( $Ts ) {
61
+ foreach ( $Ts as $t ) {
62
+ if ( is_array( $t ) ) {
63
+ echo "Line {$t[2]}: ", token_name( $t[ 0 ] ), " ('{$t[1]}')", PHP_EOL;
64
+ }
65
+ }
66
+ }
67
+
68
+ private function getRelevantContent( $file ) :string {
69
+ return php_strip_whitespace( $file );
70
+ }
71
+
72
+ /**
73
+ * @throws \Exception
74
+ */
75
+ private function canRun() {
76
+ $constants = [
77
+ 'T_WHITESPACE',
78
+ 'T_DOC_COMMENT',
79
+ 'T_COMMENT',
80
+ 'T_INLINE_HTML',
81
+ 'T_OPEN_TAG',
82
+ 'T_DECLARE',
83
+ 'T_STRING',
84
+ 'T_LNUMBER',
85
+ ];
86
+ $notDefined = array_filter( $constants, function ( $constant ) {
87
+ return !defined( $constant );
88
+ } );
89
+ if ( !empty( $notDefined ) ) {
90
+ throw new \Exception( 'Not defined: '.implode( ', ', $notDefined ) );
91
+ }
92
+ }
93
+ }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Data.php CHANGED
@@ -35,12 +35,12 @@ class Data {
35
  }
36
 
37
  /**
38
- * @param string $sPath
39
  * @return string
40
  */
41
- public function getExtension( $sPath ) {
42
- $nLastPeriod = strrpos( $sPath, '.' );
43
- return ( $nLastPeriod === false ) ? $sPath : str_replace( '.', '', substr( $sPath, $nLastPeriod ) );
44
  }
45
 
46
  /**
35
  }
36
 
37
  /**
38
+ * @param string $path
39
  * @return string
40
  */
41
+ public function getExtension( $path ) {
42
+ $extPeriod = strrpos( $path, '.' );
43
+ return ( $extPeriod === false ) ? $path : str_replace( '.', '', substr( $path, $extPeriod ) );
44
  }
45
 
46
  /**
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Encrypt/OpenSslEncrypt.php CHANGED
@@ -9,124 +9,144 @@ namespace FernleafSystems\Wordpress\Services\Utilities\Encrypt;
9
  class OpenSslEncrypt {
10
 
11
  /**
12
- * @param array $aArgs
13
  * @return array - keys are private & public as pem strings
14
  * @throws \Exception
15
  */
16
- public function createNewPrivatePublicKeyPair( $aArgs = [] ) {
17
- $rKey = openssl_pkey_new( $aArgs );
18
  if ( empty( $rKey ) || !is_resource( $rKey ) ) {
19
  throw new \Exception( 'Could not generate new private key' );
20
  }
21
- if ( !openssl_pkey_export( $rKey, $sPriv ) || empty( $sPriv ) ) {
22
  throw new \Exception( 'Could not export new private key' );
23
  }
24
- $aPub = openssl_pkey_get_details( $rKey );
25
- if ( empty( $aPub ) || empty( $aPub[ 'key' ] ) ) {
26
  throw new \Exception( 'Could not generate public key from private' );
27
  }
28
  return [
29
- 'private' => $sPriv,
30
- 'public' => $aPub[ 'key' ],
31
  ];
32
  }
33
 
34
  /**
35
- * @param string $sKey
36
  * @return string
37
  * @throws \Exception
38
  */
39
- public function getPublicKeyFromPrivateKey( $sKey ) {
40
- $rKey = openssl_pkey_get_private( $sKey );
41
  if ( empty( $rKey ) || !is_resource( $rKey ) ) {
42
  throw new \Exception( 'Could not build private key' );
43
  }
44
- $aPub = openssl_pkey_get_details( $rKey );
45
- if ( empty( $aPub ) || empty( $aPub[ 'key' ] ) ) {
46
  throw new \Exception( 'Could not generate public key from private' );
47
  }
48
- return $aPub[ 'key' ];
49
  }
50
 
51
  /**
52
- * @param OpenSslEncryptVo $oVo
53
- * @param string $sPrivateKey
54
- * @return bool
55
  */
56
- public function openDataVo( $oVo, $sPrivateKey ) {
57
- return $this->openData( $oVo->sealed_data, $oVo->sealed_password, $sPrivateKey );
 
 
 
 
 
 
 
58
  }
59
 
60
  /**
61
- * @param string $sSealedData
62
- * @param string $sSealedPassword
63
- * @param string $sPrivateKey
64
  * @return string|false
 
65
  */
66
- public function openData( $sSealedData, $sSealedPassword, $sPrivateKey ) {
67
- $bResult = openssl_open( $sSealedData, $sOpenedData, $sSealedPassword, $sPrivateKey );
68
- return $bResult ? $sOpenedData : false;
69
  }
70
 
71
  /**
72
  * @param mixed $mDataToEncrypt
73
- * @param string $sPublicKey
74
  * @return OpenSslEncryptVo
75
  */
76
- public function sealData( $mDataToEncrypt, $sPublicKey ) {
77
 
78
- $oVo = $this->getStandardEncryptResponse();
79
 
80
  if ( empty( $mDataToEncrypt ) ) {
81
- $oVo->success = false;
82
- $oVo->message = 'Data to encrypt was empty';
83
- return $oVo;
84
  }
85
  elseif ( !$this->isSupportedOpenSslDataEncryption() ) {
86
- $oVo->success = false;
87
- $oVo->message = 'Does not support OpenSSL data encryption';
 
 
 
 
88
  }
89
  else {
90
- $oVo->success = true;
91
  }
92
 
93
  // If at this stage we're not 'success' we return it.
94
- if ( !$oVo->success ) {
95
- return $oVo;
96
  }
97
 
98
- if ( !is_string( $mDataToEncrypt ) ) {
99
- $mDataToEncrypt = json_encode( $mDataToEncrypt );
100
- $oVo->json_encoded = true;
 
 
101
  }
102
  else {
103
- $oVo->json_encoded = false;
 
104
  }
105
 
106
- $aPasswordKeys = [];
107
- $nResult = openssl_seal( $mDataToEncrypt, $sEncryptedData, $aPasswordKeys, [ $sPublicKey ] );
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
- $oVo->result = $nResult;
110
- $oVo->success = is_int( $nResult ) && $nResult > 0 && !is_null( $sEncryptedData );
111
- if ( $oVo->success ) {
112
- $oVo->sealed_data = $sEncryptedData;
113
- $oVo->sealed_password = $aPasswordKeys[ 0 ];
114
  }
115
 
116
- return $oVo;
117
  }
118
 
119
- /**
120
- * @return bool
121
- */
122
- public function isSupportedOpenSsl() {
123
  return extension_loaded( 'openssl' );
124
  }
125
 
126
- /**
127
- * @return bool
128
- */
129
- public function isSupportedOpenSslSign() {
130
  return function_exists( 'base64_decode' )
131
  && extension_loaded( 'openssl' )
132
  && function_exists( 'openssl_sign' )
@@ -134,43 +154,42 @@ class OpenSslEncrypt {
134
  && defined( 'OPENSSL_ALGO_SHA1' );
135
  }
136
 
137
- /**
138
- * @return bool
139
- */
140
- public function isSupportedOpenSslDataEncryption() {
141
- $bSupported = $this->isSupportedOpenSsl();
142
- $aFunc = [
143
  'openssl_seal',
144
  'openssl_open',
145
  'openssl_pkey_new',
146
  'openssl_pkey_export',
147
  'openssl_pkey_get_details',
148
- 'openssl_pkey_get_private'
 
149
  ];
150
- foreach ( $aFunc as $sFunc ) {
151
- $bSupported = $bSupported && function_exists( $sFunc );
152
  }
153
- return $bSupported;
154
  }
155
 
156
  /**
157
- * @param string $sVerificationCode
158
- * @param string $sSignature
159
- * @param string $sPublicKey
160
  * @return int 1: Success; 0: Failure; -1: Error; -2: Not supported
161
  */
162
- public function verifySslSignature( $sVerificationCode, $sSignature, $sPublicKey ) {
163
- $nResult = -2;
164
  if ( $this->isSupportedOpenSslSign() ) {
165
- $nResult = openssl_verify( $sVerificationCode, $sSignature, $sPublicKey );
166
  }
167
- return $nResult;
168
  }
169
 
170
- /**
171
- * @return OpenSslEncryptVo
172
- */
173
- protected function getStandardEncryptResponse() {
174
  return new OpenSslEncryptVo();
175
  }
 
 
 
 
176
  }
9
  class OpenSslEncrypt {
10
 
11
  /**
12
+ * @param array $args
13
  * @return array - keys are private & public as pem strings
14
  * @throws \Exception
15
  */
16
+ public function createNewPrivatePublicKeyPair( $args = [] ) {
17
+ $rKey = openssl_pkey_new( $args );
18
  if ( empty( $rKey ) || !is_resource( $rKey ) ) {
19
  throw new \Exception( 'Could not generate new private key' );
20
  }
21
+ if ( !openssl_pkey_export( $rKey, $private ) || empty( $private ) ) {
22
  throw new \Exception( 'Could not export new private key' );
23
  }
24
+ $pub = openssl_pkey_get_details( $rKey );
25
+ if ( empty( $pub ) || empty( $pub[ 'key' ] ) ) {
26
  throw new \Exception( 'Could not generate public key from private' );
27
  }
28
  return [
29
+ 'private' => $private,
30
+ 'public' => $pub[ 'key' ],
31
  ];
32
  }
33
 
34
  /**
35
+ * @param string $key
36
  * @return string
37
  * @throws \Exception
38
  */
39
+ public function getPublicKeyFromPrivateKey( $key ) {
40
+ $rKey = openssl_pkey_get_private( $key );
41
  if ( empty( $rKey ) || !is_resource( $rKey ) ) {
42
  throw new \Exception( 'Could not build private key' );
43
  }
44
+ $public = openssl_pkey_get_details( $rKey );
45
+ if ( empty( $public ) || empty( $public[ 'key' ] ) ) {
46
  throw new \Exception( 'Could not generate public key from private' );
47
  }
48
+ return $public[ 'key' ];
49
  }
50
 
51
  /**
52
+ * @param OpenSslEncryptVo $VO
53
+ * @param string $privateKey
54
+ * @return string|false
55
  */
56
+ public function openDataVo( OpenSslEncryptVo $VO, string $privateKey ) {
57
+ $success = \openssl_open(
58
+ $VO->sealed_data,
59
+ $openedData,
60
+ $VO->sealed_password,
61
+ $privateKey,
62
+ $VO->cipher
63
+ );
64
+ return $success ? $openedData : false;
65
  }
66
 
67
  /**
68
+ * @param string $sealedData
69
+ * @param string $sealedPassword
70
+ * @param string $privateKey
71
  * @return string|false
72
+ * @deprecated
73
  */
74
+ public function openData( $sealedData, $sealedPassword, $privateKey, string $cipher = 'rc4' ) {
75
+ $success = \openssl_open( $sealedData, $openedData, $sealedPassword, $privateKey, $cipher );
76
+ return $success ? $openedData : false;
77
  }
78
 
79
  /**
80
  * @param mixed $mDataToEncrypt
81
+ * @param string $publicKey
82
  * @return OpenSslEncryptVo
83
  */
84
+ public function sealData( $mDataToEncrypt, $publicKey, $cipher = 'rc4' ) {
85
 
86
+ $VO = $this->getStandardEncryptResponse();
87
 
88
  if ( empty( $mDataToEncrypt ) ) {
89
+ $VO->success = false;
90
+ $VO->message = 'Data to encrypt was empty';
91
+ return $VO;
92
  }
93
  elseif ( !$this->isSupportedOpenSslDataEncryption() ) {
94
+ $VO->success = false;
95
+ $VO->message = 'Does not support OpenSSL data encryption';
96
+ }
97
+ elseif ( !$this->hasCipherAlgo( $cipher ) ) {
98
+ $VO->message = sprintf( 'Defaulting to RC4 as cipher %s is not available', $cipher );
99
+ $cipher = 'rc4';
100
  }
101
  else {
102
+ $VO->success = true;
103
  }
104
 
105
  // If at this stage we're not 'success' we return it.
106
+ if ( !$VO->success ) {
107
+ return $VO;
108
  }
109
 
110
+ $VO->cipher = $cipher;
111
+
112
+ if ( is_string( $mDataToEncrypt ) ) {
113
+ $finalDataToEncrypt = $mDataToEncrypt;
114
+ $VO->json_encoded = false;
115
  }
116
  else {
117
+ $finalDataToEncrypt = json_encode( $mDataToEncrypt );
118
+ $VO->json_encoded = true;
119
  }
120
 
121
+ $passwordKeys = [];
122
+ $mResult = openssl_seal(
123
+ $finalDataToEncrypt,
124
+ $encryptedData,
125
+ $passwordKeys,
126
+ [ $publicKey ],
127
+ $cipher
128
+ );
129
+
130
+ $VO->result = $mResult;
131
+ $VO->success = is_int( $mResult ) && $mResult > 0 && !is_null( $encryptedData );
132
+ if ( $VO->success ) {
133
+ $VO->sealed_data = $encryptedData;
134
+ $VO->sealed_password = $passwordKeys[ 0 ];
135
+ }
136
 
137
+ if ( $cipher !== 'rc4' ) {
138
+ // we do a backup seal as rc4 while we determine availability of other cipers
139
+ $VO->rc4_fallback = $this->sealData( $mDataToEncrypt, $publicKey, 'rc4' );
 
 
140
  }
141
 
142
+ return $VO;
143
  }
144
 
145
+ public function isSupportedOpenSsl() :bool {
 
 
 
146
  return extension_loaded( 'openssl' );
147
  }
148
 
149
+ public function isSupportedOpenSslSign() :bool {
 
 
 
150
  return function_exists( 'base64_decode' )
151
  && extension_loaded( 'openssl' )
152
  && function_exists( 'openssl_sign' )
154
  && defined( 'OPENSSL_ALGO_SHA1' );
155
  }
156
 
157
+ public function isSupportedOpenSslDataEncryption() :bool {
158
+ $supported = $this->isSupportedOpenSsl();
159
+ $funcs = [
 
 
 
160
  'openssl_seal',
161
  'openssl_open',
162
  'openssl_pkey_new',
163
  'openssl_pkey_export',
164
  'openssl_pkey_get_details',
165
+ 'openssl_pkey_get_private',
166
+ 'openssl_get_cipher_methods',
167
  ];
168
+ foreach ( $funcs as $func ) {
169
+ $supported = $supported && function_exists( $func );
170
  }
171
+ return $supported;
172
  }
173
 
174
  /**
175
+ * @param string $verificationCode
176
+ * @param string $signature
177
+ * @param string $publicKey
178
  * @return int 1: Success; 0: Failure; -1: Error; -2: Not supported
179
  */
180
+ public function verifySslSignature( $verificationCode, $signature, $publicKey ) {
181
+ $result = -2;
182
  if ( $this->isSupportedOpenSslSign() ) {
183
+ $result = openssl_verify( $verificationCode, $signature, $publicKey );
184
  }
185
+ return $result;
186
  }
187
 
188
+ protected function getStandardEncryptResponse() :OpenSslEncryptVo {
 
 
 
189
  return new OpenSslEncryptVo();
190
  }
191
+
192
+ public function hasCipherAlgo( string $cipher ) :bool {
193
+ return in_array( strtolower( $cipher ), array_map( 'strtolower', openssl_get_cipher_methods( true ) ) );
194
+ }
195
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Encrypt/OpenSslEncryptVo.php CHANGED
@@ -7,12 +7,14 @@ use FernleafSystems\Utilities\Data\Adapter\DynPropertiesClass;
7
  /**
8
  * Class EncryptVo
9
  * @package FernleafSystems\Wordpress\Services\Utilities\Encrypt
10
- * @property bool $success
11
- * @property int $result
12
- * @property string $message
13
- * @property bool $json_encoded
14
- * @property string $sealed_data
15
- * @property string $sealed_password
 
 
16
  */
17
  class OpenSslEncryptVo extends DynPropertiesClass {
18
 
@@ -30,6 +32,12 @@ class OpenSslEncryptVo extends DynPropertiesClass {
30
  $value = base64_decode( $value );
31
  break;
32
 
 
 
 
 
 
 
33
  default:
34
  break;
35
  }
7
  /**
8
  * Class EncryptVo
9
  * @package FernleafSystems\Wordpress\Services\Utilities\Encrypt
10
+ * @property bool $success
11
+ * @property int $result
12
+ * @property string $cipher
13
+ * @property string $message
14
+ * @property bool $json_encoded
15
+ * @property string $sealed_data
16
+ * @property string $sealed_password
17
+ * @property OpenSslEncryptVo $rc4_fallback
18
  */
19
  class OpenSslEncryptVo extends DynPropertiesClass {
20
 
32
  $value = base64_decode( $value );
33
  break;
34
 
35
+ case 'cipher':
36
+ if ( empty( $value ) ) {
37
+ $value = 'rc4'; // The default
38
+ }
39
+ break;
40
+
41
  default:
42
  break;
43
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/LocateStrInFile.php CHANGED
@@ -35,6 +35,8 @@ class LocateStrInFile {
35
  */
36
  private $isRegExNeedle;
37
 
 
 
38
  /**
39
  * @return string[]
40
  */
@@ -48,10 +50,10 @@ class LocateStrInFile {
48
  protected function runAsRegEx() :array {
49
  $lines = [];
50
 
51
- if ( preg_match_all( '/('.$this->getNeedle().')/i', $this->getContent(), $matches, PREG_PATTERN_ORDER ) ) {
 
52
  foreach ( $matches[ 0 ] as $match ) {
53
- // use + for numerical index
54
- $lines = $lines + $this->findLinesFor( $match );
55
  }
56
  }
57
  return $lines;
@@ -93,20 +95,28 @@ class LocateStrInFile {
93
  */
94
  protected function getLines() :array {
95
  if ( is_null( $this->lines ) ) {
96
- $this->lines = array_filter(
97
- array_map( 'trim', preg_split( '/\r\n|\r|\n/', $this->getContent() ) )
98
- );
99
  }
100
  return $this->lines;
101
  }
102
 
103
  public function getContent() :string {
104
  if ( is_null( $this->content ) ) {
105
- $this->content = Services::WpFs()->getFileContent( $this->getPath() );
 
 
 
 
 
 
106
  }
107
  return $this->content;
108
  }
109
 
 
 
 
 
110
  public function getNeedle() :string {
111
  return $this->needle;
112
  }
@@ -116,7 +126,7 @@ class LocateStrInFile {
116
  }
117
 
118
  public function isRegEx() :bool {
119
- return (bool)$this->isRegExNeedle;
120
  }
121
 
122
  public function setIsRegEx( bool $isRegEx ) :self {
@@ -129,6 +139,11 @@ class LocateStrInFile {
129
  return $this;
130
  }
131
 
 
 
 
 
 
132
  /**
133
  * @param string $path
134
  * @return $this
@@ -143,7 +158,6 @@ class LocateStrInFile {
143
  throw new \Exception( "File isn't readable" );
144
  }
145
  $this->path = $path;
146
- $this->getContent();
147
  return $this->reset();
148
  }
149
 
35
  */
36
  private $isRegExNeedle;
37
 
38
+ private $stripPhpFile = true;
39
+
40
  /**
41
  * @return string[]
42
  */
50
  protected function runAsRegEx() :array {
51
  $lines = [];
52
 
53
+ $content = $this->getContent();
54
+ if ( !empty( $content ) && preg_match_all( '/('.$this->getNeedle().')/i', $content, $matches, PREG_PATTERN_ORDER ) ) {
55
  foreach ( $matches[ 0 ] as $match ) {
56
+ $lines = $lines + $this->findLinesFor( $match ); // use + for numerical index
 
57
  }
58
  }
59
  return $lines;
95
  */
96
  protected function getLines() :array {
97
  if ( is_null( $this->lines ) ) {
98
+ $this->lines = array_filter( array_map( 'trim', preg_split( '/\r\n|\r|\n/', $this->getRawContent() ) ) );
 
 
99
  }
100
  return $this->lines;
101
  }
102
 
103
  public function getContent() :string {
104
  if ( is_null( $this->content ) ) {
105
+ $p = $this->getPath();
106
+ if ( $this->stripPhpFile && in_array( Services::Data()->getExtension( $p ), [ 'php', 'php5', 'php7' ] ) ) {
107
+ $this->content = php_strip_whitespace( $p );
108
+ }
109
+ else {
110
+ $this->content = $this->getRawContent();
111
+ }
112
  }
113
  return $this->content;
114
  }
115
 
116
+ protected function getRawContent() :string {
117
+ return (string)Services::WpFs()->getFileContent( $this->getPath() );
118
+ }
119
+
120
  public function getNeedle() :string {
121
  return $this->needle;
122
  }
126
  }
127
 
128
  public function isRegEx() :bool {
129
+ return $this->isRegExNeedle ?? false;
130
  }
131
 
132
  public function setIsRegEx( bool $isRegEx ) :self {
139
  return $this;
140
  }
141
 
142
+ public function setIsStripPhp( bool $strip ) :self {
143
+ $this->stripPhpFile = $strip;
144
+ return $this;
145
+ }
146
+
147
  /**
148
  * @param string $path
149
  * @return $this
158
  throw new \Exception( "File isn't readable" );
159
  }
160
  $this->path = $path;
 
161
  return $this->reset();
162
  }
163
 
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/ReadDataFromFileEncrypted.php CHANGED
@@ -12,31 +12,31 @@ use FernleafSystems\Wordpress\Services\Utilities\Encrypt\OpenSslEncryptVo;
12
  class ReadDataFromFileEncrypted {
13
 
14
  /**
15
- * @param string $sPath
16
- * @param string $sPrivateKey
17
  * @return string
18
  * @throws \Exception
19
  */
20
- public function run( $sPath, $sPrivateKey ) {
21
- $oFs = Services::WpFs();
22
- if ( !$oFs->exists( $sPath ) || !$oFs->isFile( $sPath ) ) {
23
- throw new \Exception( 'File path does not exist: '.$sPath );
24
  }
25
- $sRawFile = $oFs->getFileContent( $sPath );
26
- if ( empty( $sRawFile ) ) {
27
- throw new \Exception( 'Could not read data from file: '.$sRawFile );
28
  }
29
- $aRawData = @json_decode( $sRawFile, true );
30
- if ( empty( $aRawData ) || !is_array( $aRawData ) ) {
31
  throw new \Exception( 'Parsing raw data from file failed' );
32
  }
33
 
34
- $oVo = ( new OpenSslEncryptVo() )->applyFromArray( $aRawData );
35
 
36
- $sData = Services::Encrypt()->openData( $oVo->sealed_data, $oVo->sealed_password, $sPrivateKey );
37
- if ( $sData === false ) {
38
  throw new \Exception( 'Decrypting sealed data failed.' );
39
  }
40
- return $sData;
41
  }
42
  }
12
  class ReadDataFromFileEncrypted {
13
 
14
  /**
15
+ * @param string $path
16
+ * @param string $privateKey
17
  * @return string
18
  * @throws \Exception
19
  */
20
+ public function run( $path, $privateKey ) {
21
+ $FS = Services::WpFs();
22
+ if ( !$FS->exists( $path ) || !$FS->isFile( $path ) ) {
23
+ throw new \Exception( 'File path does not exist: '.$path );
24
  }
25
+ $rawFile = $FS->getFileContent( $path );
26
+ if ( empty( $rawFile ) ) {
27
+ throw new \Exception( 'Could not read data from file: '.$rawFile );
28
  }
29
+ $rawData = @json_decode( $rawFile, true );
30
+ if ( empty( $rawData ) || !is_array( $rawData ) ) {
31
  throw new \Exception( 'Parsing raw data from file failed' );
32
  }
33
 
34
+ $VO = ( new OpenSslEncryptVo() )->applyFromArray( $rawData );
35
 
36
+ $data = Services::Encrypt()->openDataVo( $VO, $privateKey );
37
+ if ( $data === false ) {
38
  throw new \Exception( 'Decrypting sealed data failed.' );
39
  }
40
+ return $data;
41
  }
42
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/WriteDataToFileEncrypted.php CHANGED
@@ -11,24 +11,24 @@ use FernleafSystems\Wordpress\Services\Services;
11
  class WriteDataToFileEncrypted {
12
 
13
  /**
14
- * @param string $sPath
15
- * @param string $sData
16
- * @param string $sPublicKey
17
- * @param string $sPrivateKeyForVerify - verify writing successful if private key supplied
18
  * @return bool
19
  * @throws \Exception
20
  */
21
- public function run( $sPath, $sData, $sPublicKey, $sPrivateKeyForVerify = null ) {
22
- $oEncrypt = Services::Encrypt();
23
 
24
- $oEncrypted = $oEncrypt->sealData( $sData, $sPublicKey );
25
- if ( !$oEncrypted->success ) {
26
- throw new \Exception( 'Could not seal data with message: '.$oEncrypted->message );
27
  }
28
 
29
- $bSuccess = Services::WpFs()->putFileContent( $sPath, json_encode( $oEncrypted->getRawData() ) );
30
- if ( $bSuccess && !empty( $sPrivateKeyForVerify ) ) {
31
- $bSuccess = ( new ReadDataFromFileEncrypted() )->run( $sPath, $sPrivateKeyForVerify ) === $sData;
32
  }
33
  return $bSuccess;
34
  }
11
  class WriteDataToFileEncrypted {
12
 
13
  /**
14
+ * @param string $path
15
+ * @param string $data
16
+ * @param string $publicKey
17
+ * @param string $privateKeyForVerify - verify writing successful if private key supplied
18
  * @return bool
19
  * @throws \Exception
20
  */
21
+ public function run( $path, $data, $publicKey, $privateKeyForVerify = null ) {
22
+ $srvEncrypt = Services::Encrypt();
23
 
24
+ $encrypted = $srvEncrypt->sealData( $data, $publicKey );
25
+ if ( !$encrypted->success ) {
26
+ throw new \Exception( 'Could not seal data with message: '.$encrypted->message );
27
  }
28
 
29
+ $bSuccess = Services::WpFs()->putFileContent( $path, json_encode( $encrypted->getRawData() ) );
30
+ if ( $bSuccess && !empty( $privateKeyForVerify ) ) {
31
+ $bSuccess = ( new ReadDataFromFileEncrypted() )->run( $path, $privateKeyForVerify ) === $data;
32
  }
33
  return $bSuccess;
34
  }
templates/twig/components/reports/mod/events/alert_scanrepairs.twig DELETED
@@ -1,8 +0,0 @@
1
- <div style="padding-left: 10px;">
2
- <h3>{{ strings.title }}</h3>
3
- <ul>
4
- {% for event_data in vars.counts %}
5
- <li>{{ event_data.name }}: {{ event_data.count }}</li>
6
- {% endfor %}
7
- </ul>
8
- </div>
 
 
 
 
 
 
 
 
templates/twig/components/reports/mod/hack_protect/alert_scanrepairs.twig ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div style="padding-left: 10px;">
2
+ <h3>{{ strings.title }}</h3>
3
+ <ul>
4
+ {% for repair_data in vars.repairs %}
5
+ <li>
6
+ {{ repair_data.name }}: {{ repair_data.count }}
7
+ {% if repair_data.repairs is not empty %}
8
+ <ul>
9
+ {% for repair in repair_data.repairs %}
10
+ <li><code>{{ repair }}</code></li>
11
+ {% endfor %}
12
+ {{ repair_data.count > repair_data.repairs|length ? '<li>...</li>' : '' }}
13
+ </ul>
14
+ {% endif %}
15
+ </li>
16
+ {% endfor %}
17
+ </ul>
18
+ <p><a href="{{ hrefs.audit_trail }}" target="_blank">{{ strings.audit_trail }}</a></p>
19
+ </div>
templates/twig/components/reports/mod/hack_protect/alert_scanresults.twig CHANGED
@@ -1,5 +1,8 @@
1
  <div style="padding-left: 10px;">
2
- <h3>{{ strings.title }}</h3>
 
 
 
3
  <ul>
4
  {% for scan_slug,scan_data in vars.scan_counts %}
5
  <li>{{ scan_data.name }}: {{ scan_data.count }}</li>
1
  <div style="padding-left: 10px;">
2
+ <h3>
3
+ {{ strings.title }}
4
+ <br/><small>{{ strings.note_changes }}</small>
5
+ </h3>
6
  <ul>
7
  {% for scan_slug,scan_data in vars.scan_counts %}
8
  <li>{{ scan_data.name }}: {{ scan_data.count }}</li>
templates/twig/wpadmin_pages/components/page/nav_sidebar.twig CHANGED
@@ -13,8 +13,8 @@
13
  <li class="nav-item mb-0 pl-0 py-1">
14
  <span class="text-secondary font-italic text-monospace">{{ mitem.title }}</span>
15
  </li>
16
- {% for sub in mitem.sub_items %}
17
- <li class="mb-2 pl-0 py-1">
18
  <a class="p-0 nav-link {{ sub.classes|default([])|join( ' ' ) }}"
19
  href="{{ sub.href|default('#') }}"
20
  {% for data_key,data_val in sub.data|default([]) %}
@@ -23,13 +23,13 @@
23
  {% if sub.target|default('') is not empty %}target="{{ sub.target }}"{% endif %}
24
  >{{ sub.title }}</a>
25
  </li>
26
- {% endfor %}
27
  </ul>
28
  </div>
29
  {% endif %}
30
 
31
  <a class="nav-link p-0 {{ mitem.classes|default([])|join( ' ' ) }} mb-1 text-center"
32
- href="{{ mitem.href|default('#') }}"
33
  {% for data_key,data_val in mitem.data|default([]) %}
34
  data-{{ data_key }}="{{ data_val }}"
35
  {% endfor %}
13
  <li class="nav-item mb-0 pl-0 py-1">
14
  <span class="text-secondary font-italic text-monospace">{{ mitem.title }}</span>
15
  </li>
16
+ {% for sub in mitem.sub_items %}
17
+ <li class="mb-2 pl-0 py-1">
18
  <a class="p-0 nav-link {{ sub.classes|default([])|join( ' ' ) }}"
19
  href="{{ sub.href|default('#') }}"
20
  {% for data_key,data_val in sub.data|default([]) %}
23
  {% if sub.target|default('') is not empty %}target="{{ sub.target }}"{% endif %}
24
  >{{ sub.title }}</a>
25
  </li>
26
+ {% endfor %}
27
  </ul>
28
  </div>
29
  {% endif %}
30
 
31
  <a class="nav-link p-0 {{ mitem.classes|default([])|join( ' ' ) }} mb-1 text-center"
32
+ href="{{ mitem.sub_items|default([]) is empty ? mitem.href|default('javascript:{}') : 'javascript:{}' }}"
33
  {% for data_key,data_val in mitem.data|default([]) %}
34
  data-{{ data_key }}="{{ data_val }}"
35
  {% endfor %}
templates/twig/wpadmin_pages/insights/scans/modal/code_block.twig ADDED
@@ -0,0 +1 @@
 
1
+ <pre class="icwp-code-render">{% for line in lines %}<code>{{ line }}</code>{% endfor %}</pre>
templates/twig/wpadmin_pages/insights/scans/modal/code_render.twig ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="modal" id="CodeRenderModal" tabindex="-1" role="dialog" aria-labelledby=""
2
+ style="z-index: 10000000;"
3
+ aria-hidden="true">
4
+ <div class="modal-dialog modal-dialog-centered modal-xl modal-dialog-scrollable" role="document" style="z-index: 10000001;">
5
+ <div class="modal-content">
6
+ <div class="modal-header">
7
+ <h5 class="modal-title">
8
+ code
9
+ </h5>
10
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
11
+ <span aria-hidden="true">&times;</span>
12
+ </button>
13
+ </div>
14
+ <div class="modal-body">
15
+ </div>
16
+ <div class="modal-footer">
17
+ </div>
18
+ </div>
19
+ </div>
20
+ </div>
templates/twig/wpadmin_pages/insights/scans/results/index.twig CHANGED
@@ -6,5 +6,6 @@
6
  {% include '/wpadmin_pages/insights/scans/results/scan_results.twig' %}
7
  </div>
8
  </div>
 
9
  {% include '/wpadmin_pages/insights/scans/modal/progress.twig' %}
10
  {% endblock %}
6
  {% include '/wpadmin_pages/insights/scans/results/scan_results.twig' %}
7
  </div>
8
  </div>
9
+ {% include '/wpadmin_pages/insights/scans/modal/code_render.twig' %}
10
  {% include '/wpadmin_pages/insights/scans/modal/progress.twig' %}
11
  {% endblock %}
templates/twig/wpadmin_pages/insights/scans/results/scan_results.twig CHANGED
@@ -1,63 +1,117 @@
1
  <ul class="nav nav-tabs" id="ScanResultsTabsNav" role="tablist">
2
 
3
  <li class="nav-item">
4
- <a class="nav-link active" id="h-tabs-home-tab" data-toggle="tab" href="#h-tabs-aggregate"
5
- role="tab" aria-controls="h-tabs-aggregate" aria-selected="true">
6
- <span class="badge badge-{% if aggregate.count > 0 %}danger{% endif %}"
7
- >{% if aggregate.count > 0 %}&#33;{% endif %}</span>
8
- {{ aggregate.strings.title }}
 
 
 
9
  </a>
10
  </li>
11
 
12
- {% for scankey,scanvars in scans %}
13
- {% if scanvars.flags.show_table %}
14
- <li class="nav-item">
15
- <a class="nav-link"
16
- id="h-tabs-home-tab"
17
- data-toggle="tab"
18
- href="#h-tabs-{{ scankey }}"
19
- role="tab"
20
- aria-controls="h-tabs-{{ scankey }}"
21
- >
22
- <span class="badge badge-{% if scanvars.count > 0 %}danger{% endif %}"
23
- >{% if scanvars.count > 0 %}&#33;{% endif %}</span>
24
- {{ scanvars.strings.title }}
25
- </a>
26
- </li>
27
- {% endif %}
28
- {% endfor %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  <li class="nav-item">
31
  <a class="nav-link" id="h-tabs-home-tab" data-toggle="tab" href="#h-tabs-file_locker"
32
  role="tab" aria-controls="h-tabs-file_locker">
33
- <span class="badge badge-{% if file_locker.count > 0 %}danger{% endif %}"
34
- >{% if file_locker.count > 0 %}&#33;{% endif %}</span>
35
  {{ file_locker.strings.title }}
 
 
 
 
 
36
  </a>
37
  </li>
 
 
 
 
 
 
 
 
38
  </ul>
39
 
40
  <div class="tab-content mb-5" id="ScanResultsTabsContent">
41
 
42
- {% set scan = aggregate %}
43
- <div class="tab-pane show active" id="h-tabs-aggregate" role="tabpanel"
44
- aria-labelledby="h-tabs-aggregate-tab">
45
- {% include '/wpadmin_pages/insights/scans/results/results/aggregate.twig' %}
 
 
 
 
46
  </div>
47
 
48
- {% for scankey,scanvars in scans %}
49
- {% if scanvars.flags.show_table %}
50
- <div class="tab-pane" id="h-tabs-{{ scankey }}" role="tabpanel"
51
- aria-labelledby="h-tabs-{{ scankey }}-tab">
52
- {% set scan = attribute(scans, scankey) %}
53
- {% include '/wpadmin_pages/insights/scans/results/results/'~scankey~'.twig' %}
54
- </div>
55
- {% endif %}
56
- {% endfor %}
 
 
 
 
 
57
 
58
  <div class="tab-pane show" id="h-tabs-file_locker" role="tabpanel"
59
- aria-labelledby="h-tabs-aggregate-tab">
60
  {% set scan = file_locker %}
61
  {% include '/wpadmin_pages/insights/scans/results/realtime/file_locker/index.twig' %}
62
  </div>
63
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
1
  <ul class="nav nav-tabs" id="ScanResultsTabsNav" role="tablist">
2
 
3
  <li class="nav-item">
4
+ <a class="nav-link active" id="h-tabs-wordpress-tab" data-toggle="tab" href="#h-tabs-wordpress"
5
+ role="tab" aria-controls="h-tabs-wordpress">
6
+ WordPress
7
+ {% if vars.sections.wordpress.count > 0 %}
8
+ <span class="badge badge-danger">{{ vars.sections.wordpress.count }}</span>
9
+ {% else %}
10
+ <span class="badge badge-success">&check;</span>
11
+ {% endif %}
12
  </a>
13
  </li>
14
 
15
+ <li class="nav-item">
16
+ <a class="nav-link" id="h-tabs-plugins-tab" data-toggle="tab" href="#h-tabs-plugins"
17
+ role="tab" aria-controls="h-tabs-plugins">
18
+ Plugins
19
+ {% if vars.sections.plugins.count > 0 %}
20
+ <span class="badge badge-danger">{{ vars.sections.plugins.count }}</span>
21
+ {% else %}
22
+ <span class="badge badge-success">&check;</span>
23
+ {% endif %}
24
+ </a>
25
+ </li>
26
+
27
+ <li class="nav-item">
28
+ <a class="nav-link" id="h-tabs-themes-tab" data-toggle="tab" href="#h-tabs-themes"
29
+ role="tab" aria-controls="h-tabs-themes">
30
+ Themes
31
+ {% if vars.sections.themes.count > 0 %}
32
+ <span class="badge badge-danger">{{ vars.sections.themes.count }}</span>
33
+ {% else %}
34
+ <span class="badge badge-success">&check;</span>
35
+ {% endif %}
36
+ </a>
37
+ </li>
38
+
39
+ <li class="nav-item">
40
+ <a class="nav-link" id="h-tabs-malware-tab" data-toggle="tab" href="#h-tabs-malware"
41
+ role="tab" aria-controls="h-tabs-malware">
42
+ Malware
43
+ {% if vars.sections.malware.count > 0 %}
44
+ <span class="badge badge-danger">{{ vars.sections.malware.count }}</span>
45
+ {% else %}
46
+ <span class="badge badge-success">&check;</span>
47
+ {% endif %}
48
+ </a>
49
+ </li>
50
 
51
  <li class="nav-item">
52
  <a class="nav-link" id="h-tabs-home-tab" data-toggle="tab" href="#h-tabs-file_locker"
53
  role="tab" aria-controls="h-tabs-file_locker">
54
+
 
55
  {{ file_locker.strings.title }}
56
+ {% if file_locker.count > 0 %}
57
+ <span class="badge badge-danger">{{ file_locker.count }}</span>
58
+ {% else %}
59
+ <span class="badge badge-success">&check;</span>
60
+ {% endif %}
61
  </a>
62
  </li>
63
+
64
+ {# <li class="nav-item">#}
65
+ {# <a class="nav-link" id="h-tabs-log-tab" data-toggle="tab" href="#h-tabs-log"#}
66
+ {# role="tab" aria-controls="h-tabs-log">#}
67
+ {# Logs#}
68
+ {# </a>#}
69
+ {# </li>#}
70
+
71
  </ul>
72
 
73
  <div class="tab-content mb-5" id="ScanResultsTabsContent">
74
 
75
+ <div class="tab-pane show active" id="h-tabs-wordpress" role="tabpanel"
76
+ aria-labelledby="h-tabs-wordpress-tab">
77
+ {{ content.section.wordpress|raw }}
78
+ </div>
79
+
80
+ <div class="tab-pane" id="h-tabs-plugins" role="tabpanel"
81
+ aria-labelledby="h-tabs-plugins-tab">
82
+ {{ content.section.plugins|raw }}
83
  </div>
84
 
85
+ <div class="tab-pane" id="h-tabs-themes" role="tabpanel"
86
+ aria-labelledby="h-tabs-themes-tab">
87
+ {{ content.section.themes|raw }}
88
+ </div>
89
+
90
+ <div class="tab-pane" id="h-tabs-malware" role="tabpanel"
91
+ aria-labelledby="h-tabs-malware-tab">
92
+ {{ content.section.malware|raw }}
93
+ </div>
94
+
95
+ {# <div class="tab-pane" id="h-tabs-log" role="tabpanel"#}
96
+ {# aria-labelledby="h-tabs-log-tab">#}
97
+ {# {{ content.section.logs|raw }}#}
98
+ {# </div>#}
99
 
100
  <div class="tab-pane show" id="h-tabs-file_locker" role="tabpanel"
101
+ aria-labelledby="h-tabs-file_locker-tab">
102
  {% set scan = file_locker %}
103
  {% include '/wpadmin_pages/insights/scans/results/realtime/file_locker/index.twig' %}
104
  </div>
105
+ </div>
106
+
107
+ <script>
108
+ (function ( $ ) {
109
+ $( document ).ready( function () {
110
+ $( '.nav-vertical a[data-toggle="tab"]' ).on( 'shown.bs.tab', function ( evt ) {
111
+ console.log();
112
+ let target = document.querySelector( evt.currentTarget.getAttribute( 'href' ) );
113
+ window.scrollTo( { top: 0, behavior: 'smooth' } )
114
+ } );
115
+ } );
116
+ })( jQuery );
117
+ </script>
templates/twig/wpadmin_pages/insights/scans/results/section/malware/index.twig ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% set mal = vars.malware %}
2
+ <div class="row mt-3" id="ScanResultsMalware">
3
+ <div class="col-12">
4
+
5
+ <ul class="list-group">
6
+ <li class="list-group-item">
7
+ <span class="font-weight-bold">Understanding Malware</span>:
8
+ <span>
9
+ PHP Malware is a complex topic and scanning for malware is not simple.
10
+ Please take a moment to read some of
11
+ <a href="javascript:{}" class="font-weight-bold beacon-article"
12
+ data-beacon-article-format="sidebar"
13
+ data-beacon-article-id="{{ vars.beacon_help_id }}">
14
+ the help around this topic</a>.
15
+ </span>
16
+ </li>
17
+
18
+ {% if flags.mal_is_restricted %}
19
+ <li class="list-group-item list-group-item-warning">
20
+ <span class="font-weight-bold">{{ strings.file_integrity }}:</span>
21
+ <span>{{ strings.mal_restricted }}</span>
22
+ </li>
23
+ {% else %}
24
+ {% if mal.flags.has_malware %}
25
+ <li class="list-group-item list-group-item-danger">
26
+ <span class="font-weight-bold">{{ strings.file_integrity }}</span>
27
+ <span>{{ strings.files_found }}</span>
28
+ </li>
29
+ {% else %}
30
+ <li class="list-group-item list-group-item-success">
31
+ <span class="font-weight-bold">{{ strings.file_integrity }}</span>
32
+ <span>{{ strings.no_files }}</span>
33
+ </li>
34
+ {% endif %}
35
+ {% endif %}
36
+ </ul>
37
+ </div>
38
+
39
+ <div class="col-12">
40
+ <div class="row mt-3">
41
+ {% if mal.flags.has_malware %}
42
+ <div class="col">
43
+ <table id="table_id-malware"
44
+ class="table table-striped table-bordered" style="width: 100%;"></table>
45
+ </div>
46
+ <script>
47
+ jQuery( document ).ready( function () {
48
+ jQuery( '#table_id-malware' ).icwpWpsfScanTableActions(
49
+ {
50
+ 'type': 'malware',
51
+ 'file': 'malware',
52
+ 'ajax': {
53
+ 'scanresults_action':{{ ajax.scanresults_action|raw }},
54
+ },
55
+ 'datatables_init': {{ vars.datatables_init|raw }}
56
+ }
57
+ );
58
+ } );
59
+ </script>
60
+ {% endif %}
61
+ </div>
62
+ </div>
63
+
64
+ </div>
templates/twig/wpadmin_pages/insights/scans/results/section/plugins/index.twig ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="row mt-3 nav-vertical" id="ScanResultsPlugins">
2
+
3
+ <div class="col-2">
4
+ <ul class="nav nav-tabs flex-column">
5
+ {% for plugin_key,plugin in vars.plugins %}
6
+ <li class="nav-item mb-2">
7
+ <a class="nav-link {% if plugin_key < 1 %}active{% endif %}"
8
+ href="#plugin-tab-{{ plugin.info.slug }}"
9
+ data-toggle="tab"
10
+ >
11
+ {% if plugin.vars.count_items > 0 %}
12
+ <span class="badge badge-danger float-right">{{ plugin.vars.count_items }}</span>
13
+ {% elseif plugin.flags.has_warning %}
14
+ <span class="badge badge-warning float-right">&excl;</span>
15
+ {% else %}
16
+ <span class="badge badge-success float-right">&check;</span>
17
+ {% endif %}
18
+ {{ plugin.info.name }}
19
+ </a>
20
+ </li>
21
+ {% endfor %}
22
+ </ul>
23
+ </div>
24
+
25
+ <div class="col-10">
26
+ <div class="tab-content">
27
+ {% for plugin_key,plugin in vars.plugins %}
28
+ <div class="tab-pane {% if plugin_key < 1 %}show active{% endif %}"
29
+ id="plugin-tab-{{ plugin.info.slug }}"
30
+ role="tabpanel"
31
+ aria-labelledby="profile-tab"
32
+ >
33
+ {% include '/wpadmin_pages/insights/scans/results/section/plugins/plugin_panel.twig' %}
34
+ </div>
35
+ {% endfor %}
36
+ </div>
37
+ </div>
38
+
39
+ </div>
templates/twig/wpadmin_pages/insights/scans/results/section/plugins/plugin_panel.twig ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="row" id="PluginSection-{{ plugin.info.slug }}">
2
+ <div class="col">
3
+
4
+ <ul class="list-group">
5
+ {% if plugin.flags.is_vulnerable %}
6
+ <li class="list-group-item list-group-item-danger">
7
+ <span class="font-weight-bold">{{ strings.vulnerable }}</span>:
8
+ {{ strings.vulnerable_known }}
9
+ {{ strings.vulnerable_update }}
10
+ <a href="{{ plugin.hrefs.vul_info }}" target="_blank">{{ strings.more_info }}</a>
11
+ </li>
12
+ {% endif %}
13
+ {% if plugin.flags.is_abandoned %}
14
+ <li class="list-group-item list-group-item-danger">
15
+ <span class="font-weight-bold">{{ strings.abandoned }}</span>:
16
+ {{ plugin.info.abandoned_at }}
17
+ <button class="btn btn-light action standalone-action ignore"
18
+ title="Ignore"
19
+ data-rid="{{ plugin.vars.abandoned_rid }}">
20
+ {{ imgs.svgs.ignore|raw }}</button>
21
+ </li>
22
+ {% endif %}
23
+ {% if plugin.flags.has_update %}
24
+ <li class="list-group-item list-group-item-warning">
25
+ <span class="font-weight-bold">
26
+ <a href="{{ hrefs.upgrade }}" target="_blank">{{ strings.update_available }}</a>
27
+ </span>
28
+ </li>
29
+ {% endif %}
30
+ {% if not plugin.flags.is_active %}
31
+ <li class="list-group-item list-group-item-warning">
32
+ {{ strings.not_active }}
33
+ <a href="{{ hrefs.page_plugins }}" target="_blank">{{ strings.go_to_plugins }}</a>
34
+ </li>
35
+ {% endif %}
36
+ <li class="list-group-item">
37
+ <span class="font-weight-bold">{{ strings.name }}</span>: {{ plugin.info.name }};
38
+ <span class="font-weight-bold">{{ strings.version }}</span>: {{ plugin.info.version }};
39
+ <span class="font-weight-bold">{{ strings.installed_at }}</span>:
40
+ {{ plugin.info.installed_at }} <small>({{ strings.estimated }})</small>
41
+ </li>
42
+ <li class="list-group-item">
43
+ <span class="font-weight-bold">{{ strings.author }}</span>:
44
+ <a href="{{ plugin.info.author_url }}" target="_blank">{{ plugin.info.author }}</a>;
45
+ <span class="font-weight-bold">WordPress.org</span>: {{ plugin.flags.is_wporg ? 'Yes' : 'No' }}
46
+ </li>
47
+ <li class="list-group-item">
48
+ <span class="font-weight-bold">{{ strings.install_dir }}</span>:
49
+ <code>{{ plugin.info.dir }}</code>
50
+ <small>({{ strings.rel_to_abspath }})</small>
51
+ </li>
52
+
53
+ {% if flags.ptg_is_restricted %}
54
+ <li class="list-group-item list-group-item-warning">
55
+ <span class="font-weight-bold">{{ strings.file_integrity }}</span>:
56
+ <span>{{ strings.ptg_not_available }}</span>
57
+ </li>
58
+ {% else %}
59
+ {% if plugin.flags.has_guard_files %}
60
+ <li class="list-group-item list-group-item-danger">
61
+ <span class="font-weight-bold">{{ strings.file_integrity }}</span>:
62
+ <span>{{ strings.files_found }}</span>
63
+ </li>
64
+ {% else %}
65
+ <li class="list-group-item list-group-item-success">
66
+ <span class="font-weight-bold">{{ strings.file_integrity }}</span>:
67
+ <span>{{ strings.no_files }}</span>
68
+ </li>
69
+ {% endif %}
70
+ {% endif %}
71
+
72
+ </ul>
73
+ </div>
74
+ </div>
75
+
76
+ <div class="col-12">
77
+ <div class="row mt-3">
78
+ <div class="col">
79
+ <table id="table_id-{{ plugin.info.slug }}"
80
+ class="table table-striped table-bordered" style="width: 100%;"></table>
81
+ </div>
82
+ </div>
83
+ </div>
84
+
85
+ <script>
86
+ jQuery( document ).ready( function () {
87
+ {% if plugin.flags.is_abandoned %}
88
+ jQuery( '#PluginSection-{{ plugin.info.slug }}' ).icwpWpsfScanResultsActions(
89
+ {
90
+ 'ajax': {
91
+ 'scanresults_action':{{ ajax.scanresults_action|raw }},
92
+ }
93
+ }
94
+ );
95
+ {% endif %}
96
+ {% if plugin.flags.has_guard_files %}
97
+ jQuery( '#table_id-{{ plugin.info.slug }}' ).icwpWpsfScanTableActions(
98
+ {
99
+ 'type': '{{ plugin.info.type }}',
100
+ 'file': '{{ plugin.info.file }}',
101
+ 'ajax': {
102
+ 'scanresults_action':{{ ajax.scanresults_action|raw }},
103
+ },
104
+ 'datatables_init': {{ vars.datatables_init|raw }}
105
+ }
106
+ );
107
+ {% endif %}
108
+ } );
109
+ </script>
templates/twig/wpadmin_pages/insights/scans/results/section/themes/index.twig ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="row mt-3 nav-vertical" id="ScanResultsThemes">
2
+
3
+ <div class="col-2">
4
+ <ul class="nav nav-tabs flex-column">
5
+ {% for theme_key,theme in vars.themes %}
6
+ <li class="nav-item mb-2">
7
+ <a class="nav-link {% if theme_key < 1 %}active{% endif %}"
8
+ href="#theme-tab-{{ theme.info.slug }}"
9
+ data-toggle="tab"
10
+ >
11
+ {% if theme.vars.count_items > 0 %}
12
+ <span class="badge badge-danger float-right">{{ theme.vars.count_items }}</span>
13
+ {% elseif theme.flags.has_warning %}
14
+ <span class="badge badge-warning float-right">&excl;</span>
15
+ {% else %}
16
+ <span class="badge badge-success float-right">&check;</span>
17
+ {% endif %}
18
+ {{ theme.info.name }}
19
+ </a>
20
+ </li>
21
+ {% endfor %}
22
+ </ul>
23
+ </div>
24
+
25
+ <div class="col-10">
26
+ <div class="tab-content">
27
+ {% for theme_key,theme in vars.themes %}
28
+ <div class="tab-pane {% if theme_key < 1 %}show active{% endif %}"
29
+ id="theme-tab-{{ theme.info.slug }}"
30
+ role="tabpanel"
31
+ aria-labelledby="profile-tab"
32
+ >
33
+ {% include '/wpadmin_pages/insights/scans/results/section/themes/theme_panel.twig' %}
34
+ </div>
35
+ {% endfor %}
36
+ </div>
37
+ </div>
38
+ </div>
templates/twig/wpadmin_pages/insights/scans/results/section/themes/theme_panel.twig ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="row">
2
+ <div class="col">
3
+
4
+ <ul class="list-group">
5
+ {% if theme.flags.is_vulnerable %}
6
+ <li class="list-group-item list-group-item-danger">
7
+ <span class="font-weight-bold">{{ strings.vulnerable }}</span>:
8
+ {{ strings.vulnerable_known }}
9
+ {{ strings.vulnerable_update }}
10
+ <a href="{{ theme.hrefs.vul_info }}" target="_blank">{{ strings.more_info }}</a>
11
+ </li>
12
+ {% endif %}
13
+ {% if theme.flags.is_abandoned %}
14
+ <li class="list-group-item list-group-item-danger">
15
+ <span class="font-weight-bold">{{ strings.abandoned }}</span>:
16
+ {{ theme.info.abandoned_at }}
17
+ </li>
18
+ {% endif %}
19
+ {% if theme.flags.has_update %}
20
+ <li class="list-group-item list-group-item-warning">
21
+ <span class="font-weight-bold">
22
+ <a href="{{ hrefs.upgrade }}" target="_blank">{{ strings.update_available }}</a>
23
+ </span>
24
+ </li>
25
+ {% endif %}
26
+ {% if not theme.flags.is_active %}
27
+ <li class="list-group-item list-group-item-warning">
28
+ {{ strings.not_active }}
29
+ <a href="{{ hrefs.page_themes }}" target="_blank">{{ strings.go_to_themes }}</a>
30
+ </li>
31
+ {% endif %}
32
+ <li class="list-group-item">
33
+ <span class="font-weight-bold">{{ strings.name }}</span>: {{ theme.info.name }};
34
+ <span class="font-weight-bold">{{ strings.version }}</span>: {{ theme.info.version }};
35
+ <span class="font-weight-bold">{{ strings.installed_at }}</span>:
36
+ {{ theme.info.installed_at }} <small>({{ strings.estimated }})</small>
37
+ </li>
38
+ <li class="list-group-item">
39
+ <span class="font-weight-bold">{{ strings.author }}</span>:
40
+ <a href="{{ theme.info.author_url }}" target="_blank">{{ theme.info.author }}</a>;
41
+ <span class="font-weight-bold">WordPress.org</span>: {{ theme.flags.is_wporg ? 'Yes' : 'No' }}
42
+ </li>
43
+ {% if theme.flags.is_child %}
44
+ <li class="list-group-item">
45
+ <span class="font-weight-bold">{{ strings.parent_theme }}</span>: {{ theme.info.parent_theme }};
46
+ </li>
47
+ {% elseif theme.flags.is_parent %}
48
+ <li class="list-group-item">
49
+ <span class="font-weight-bold">{{ strings.child_theme }}</span>: {{ theme.info.child_theme }};
50
+ </li>
51
+ {% endif %}
52
+ <li class="list-group-item">
53
+ <span class="font-weight-bold">{{ strings.install_dir }}</span>:
54
+ <code>{{ theme.info.dir }}</code>
55
+ <small>({{ strings.rel_to_abspath }})</small>
56
+ </li>
57
+
58
+ {% if flags.ptg_is_restricted %}
59
+ <li class="list-group-item list-group-item-warning">
60
+ <span class="font-weight-bold">{{ strings.file_integrity }}</span>:
61
+ <span>{{ strings.ptg_not_available }}</span>
62
+ </li>
63
+ {% else %}
64
+ {% if theme.flags.has_guard_files %}
65
+ <li class="list-group-item list-group-item-danger">
66
+ <span class="font-weight-bold">{{ strings.file_integrity }}</span>:
67
+ <span>{{ strings.files_found }}</span>
68
+ </li>
69
+ {% else %}
70
+ <li class="list-group-item list-group-item-success">
71
+ <span class="font-weight-bold">{{ strings.file_integrity }}</span>:
72
+ <span>{{ strings.no_files }}</span>
73
+ </li>
74
+ {% endif %}
75
+ {% endif %}
76
+
77
+ </ul>
78
+ </div>
79
+ </div>
80
+
81
+ <div class="col-12">
82
+ <div class="row mt-3">
83
+ {% if theme.flags.has_guard_files %}
84
+ <div class="col">
85
+ <table id="table_id-{{ theme.info.slug }}"
86
+ class="table table-striped table-bordered w-100"></table>
87
+ </div>
88
+ <script>
89
+ jQuery( document ).ready( function () {
90
+ jQuery( '#table_id-{{ theme.info.slug }}' ).icwpWpsfScanTableActions(
91
+ {
92
+ 'type': '{{ theme.info.type }}',
93
+ 'file': '{{ theme.info.file }}',
94
+ 'ajax': {
95
+ 'scanresults_action':{{ ajax.scanresults_action|raw }},
96
+ },
97
+ 'datatables_init': {{ vars.datatables_init|raw }}
98
+ }
99
+ );
100
+ } );
101
+ </script>
102
+ {% endif %}
103
+ </div>
104
+ </div>
templates/twig/wpadmin_pages/insights/scans/results/section/wordpress/index.twig ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% set wp = vars.wordpress %}
2
+ <div class="row mt-3" id="ScanResultsWordpress">
3
+ <div class="col-12">
4
+
5
+ <ul class="list-group">
6
+ {% if wp.flags.is_vulnerable %}
7
+ <li class="list-group-item list-group-item-danger">
8
+ <span class="font-weight-bold">{{ strings.vulnerable }}</span>:
9
+ {{ strings.vulnerable_known }}
10
+ {{ strings.vulnerable_update }}
11
+ </li>
12
+ {% endif %}
13
+ {% if wp.flags.has_update %}
14
+ <li class="list-group-item list-group-item-warning">
15
+ <span class="font-weight-bold">
16
+ <a href="{{ hrefs.upgrade }}" target="_blank">{{ strings.update_available }}</a>
17
+ </span>
18
+ </li>
19
+ {% endif %}
20
+ <li class="list-group-item">
21
+ <span class="font-weight-bold">{{ strings.version }}</span>: {{ wp.info.version }}
22
+ </li>
23
+ <li class="list-group-item">
24
+ <span class="font-weight-bold">{{ strings.install_dir }}</span>:
25
+ <code>{{ wp.info.dir }}</code>
26
+ </li>
27
+ {% if wp.flags.has_core_files %}
28
+ <li class="list-group-item list-group-item-danger">
29
+ <span class="font-weight-bold">{{ strings.file_integrity }}</span>:
30
+ <span>{{ strings.files_found }}</span>
31
+ </li>
32
+ {% else %}
33
+ <li class="list-group-item list-group-item-success">
34
+ <span class="font-weight-bold">{{ strings.file_integrity }}</span>:
35
+ <span>{{ strings.no_files }}</span>
36
+ </li>
37
+ {% endif %}
38
+ </ul>
39
+ </div>
40
+
41
+ <div class="col-12">
42
+ <div class="row mt-3">
43
+ {% if wp.flags.has_core_files %}
44
+ <div class="col">
45
+ <table id="table_id-wordpress"
46
+ class="table table-striped table-bordered" style="width: 100%;"></table>
47
+ </div>
48
+ <script>
49
+ jQuery( document ).ready( function () {
50
+ jQuery( '#table_id-wordpress' ).icwpWpsfScanTableActions(
51
+ {
52
+ 'type': 'wordpress',
53
+ 'file': 'wordpress',
54
+ 'ajax': {
55
+ 'scanresults_action':{{ ajax.scanresults_action|raw }},
56
+ },
57
+ 'datatables_init': {{ vars.datatables_init|raw }}
58
+ }
59
+ );
60
+ } );
61
+ </script>
62
+ {% endif %}
63
+ </div>
64
+ </div>
65
+
66
+ </div>