Shield Security for WordPress - Version 11.3.0

Version Description

Download this release

Release Info

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

Code changes from version 11.2.4 to 11.3.0

Files changed (83) hide show
  1. cl.json +47 -0
  2. icwp-wpsf.php +1 -1
  3. plugin-spec.php +4 -4
  4. readme.txt +1 -1
  5. resources/js/shield/{antibot.js → notbot.js} +4 -4
  6. src/config/feature-admin_access_restriction.php +1 -2
  7. src/config/feature-ips.php +27 -2
  8. src/config/feature-plugin.php +0 -4
  9. src/lib/src/Databases/AuditTrail/Select.php +4 -4
  10. src/lib/src/Databases/Base/BaseQuery.php +7 -17
  11. src/lib/src/Databases/Base/EntryVoConsumer.php +4 -4
  12. src/lib/src/Databases/Base/Handler.php +0 -4
  13. src/lib/src/Databases/Events/Common.php +6 -6
  14. src/lib/src/Databases/Events/Select.php +6 -10
  15. src/lib/src/Modules/AuditTrail/Lib/AuditWriter.php +0 -3
  16. src/lib/src/Modules/AuditTrail/Lib/Ops/Commit.php +1 -1
  17. src/lib/src/Modules/AuditTrail/Options.php +1 -1
  18. src/lib/src/Modules/Base/ModCon.php +0 -55
  19. src/lib/src/Modules/BaseShield/ModCon.php +0 -17
  20. src/lib/src/Modules/Events/Consolidate/ConsolidateAllEvents.php +31 -31
  21. src/lib/src/Modules/Events/Lib/Reports/ScanRepairs.php +17 -17
  22. src/lib/src/Modules/Events/Lib/StatsWriter.php +0 -3
  23. src/lib/src/Modules/HackGuard/Scan/Controller/Base.php +0 -12
  24. src/lib/src/Modules/IPs/BotTrack/Base.php +19 -17
  25. src/lib/src/Modules/IPs/BotTrack/Track404.php +59 -1
  26. src/lib/src/Modules/IPs/BotTrack/TrackCommentSpam.php +1 -5
  27. src/lib/src/Modules/IPs/Components/IpAddressConsumer.php +1 -12
  28. src/lib/src/Modules/IPs/Lib/BlacklistHandler.php +4 -55
  29. src/lib/src/Modules/IPs/Lib/BlockRequest.php +11 -1
  30. src/lib/src/Modules/IPs/Lib/Bots/BotSignalsController.php +54 -2
  31. src/lib/src/Modules/IPs/Lib/Bots/BotSignalsRecord.php +14 -3
  32. src/lib/src/Modules/IPs/Lib/Bots/Calculator/BaseBuildScores.php +68 -0
  33. src/lib/src/Modules/IPs/Lib/Bots/Calculator/BuildScores.php +28 -285
  34. src/lib/src/Modules/IPs/Lib/Bots/Calculator/BuildScoresFallback.php +248 -0
  35. src/lib/src/Modules/IPs/Lib/Bots/Calculator/CalculateVisitorBotScores.php +1 -0
  36. src/lib/src/Modules/IPs/Lib/Bots/Calculator/ScoreLogic.php +159 -0
  37. src/lib/src/Modules/IPs/Lib/Bots/NotBot/InsertNotBotJs.php +3 -3
  38. src/lib/src/Modules/IPs/Lib/Bots/NotBot/TestNotBotLoading.php +21 -0
  39. src/lib/src/Modules/IPs/Lib/IpAnalyse/BuildDisplay.php +13 -13
  40. src/lib/src/Modules/IPs/Lib/OffenseTracker.php +0 -4
  41. src/lib/src/Modules/IPs/Options.php +5 -1
  42. src/lib/src/Modules/IPs/Strings.php +17 -2
  43. src/lib/src/Modules/IPs/UI.php +10 -0
  44. src/lib/src/Modules/IPs/WpCli.php +1 -1
  45. src/lib/src/Modules/Insights/Lib/SideMenuBuilder.php +1 -1
  46. src/lib/src/Modules/Insights/ModCon.php +0 -11
  47. src/lib/src/Modules/Insights/UI.php +0 -22
  48. src/lib/src/Modules/Integrations/Lib/Spam/SpamController.php +0 -51
  49. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BaseProvider.php +0 -16
  50. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/GoogleAuth.php +0 -15
  51. src/lib/src/Modules/Plugin/Lib/Debug/Collate.php +18 -14
  52. src/lib/src/Modules/Plugin/Lib/PluginTelemetry.php +32 -41
  53. src/lib/src/Modules/Reporting/Lib/ReportingController.php +0 -2
  54. src/lib/src/Modules/SecurityAdmin/ModCon.php +12 -43
  55. src/lib/src/Modules/SecurityAdmin/Options.php +0 -24
  56. src/lib/src/Modules/Sessions/Processor.php +0 -12
  57. src/lib/src/Modules/UserManagement/Processor.php +6 -3
  58. src/lib/src/ShieldNetApi/Common/BaseShieldNetApi.php +8 -2
  59. src/lib/src/ShieldNetApi/Reputation/BotScoringLogic.php +18 -0
  60. src/lib/src/ShieldNetApi/Telemetry/SendTelemetry.php +20 -0
  61. src/lib/src/Tables/Build/AuditTrail.php +3 -1
  62. src/lib/vendor/composer/autoload_classmap.php +23 -2
  63. src/lib/vendor/composer/autoload_files.php +1 -1
  64. src/lib/vendor/composer/autoload_psr4.php +1 -0
  65. src/lib/vendor/composer/autoload_static.php +29 -3
  66. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/BaseQuery.php +466 -0
  67. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Delete.php +118 -0
  68. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Handler.php +249 -0
  69. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Insert.php +78 -0
  70. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Record.php +88 -0
  71. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Select.php +304 -0
  72. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Traits/Select_IPTable.php +24 -0
  73. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Update.php +119 -0
  74. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/AlignTableWithSchema.php +99 -0
  75. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/BuildColumnFromDef.php +157 -0
  76. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/HandlerConsumer.php +29 -0
  77. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/Iterator.php +113 -0
  78. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/RecordConsumer.php +29 -0
  79. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/SubQueryLoader.php +86 -0
  80. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/TableSchema.php +155 -0
  81. src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Exceptions/NoSlugProvidedException.php +7 -0
  82. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Autoloading/FindClassFromNamespaceRoots.php +35 -0
  83. templates/twig/wpadmin_pages/insights/ips/ip_analyse/ip_general.twig +2 -2
cl.json CHANGED
@@ -1,4 +1,51 @@
1
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  "11.2": {
3
  "version": "11.2",
4
  "released_at": 1621844125,
1
  {
2
+ "11.3": {
3
+ "version": "11.3",
4
+ "released_at": 1623057021,
5
+ "hrefs": {
6
+ "release": "https://shsec.io/shieldrelease113",
7
+ "upgrade": "https://shsec.io/shieldupgradeguide113"
8
+ },
9
+ "title": "",
10
+ "description": [
11
+ ],
12
+ "items": [
13
+ {
14
+ "type": "new",
15
+ "pro_only": false,
16
+ "title": "High IP Reputation Bypass",
17
+ "description": [
18
+ "Added an option to ensure that IP addresses with a high-enough reputation are never blocked by Shield."
19
+ ]
20
+ },
21
+ {
22
+ "type": "new",
23
+ "pro_only": false,
24
+ "title": "Bot Scoring Logic Is Provisioned From ShieldNET API",
25
+ "description": [
26
+ "To allow for easier and faster updates and improvements to the bot scoring logic, they are served from our ShieldNET API.",
27
+ "If, for whatever reason, the API is unavailable the plugin will use its built-in scoring logic."
28
+ ]
29
+ },
30
+ {
31
+ "type": "new",
32
+ "pro_only": false,
33
+ "title": "NotBot Javascript Loading Check",
34
+ "description": [
35
+ "The NotBot Javascript that loads for visitor is critical to Shield's ability to detect bots - we now show a warning when we can't detect it."
36
+ ]
37
+ },
38
+ {
39
+ "type": "improved",
40
+ "pro_only": false,
41
+ "title": "404 Bot Signal doesn't trigger Shield offense on certain requests for assets",
42
+ "description": [
43
+ "404s encountered for requests for assets such as images, javascript and CSS no longer trigger offenses.",
44
+ "The 1 exception is if the asset URL is within a plugin/theme directory that doesn't exist on the site."
45
+ ]
46
+ }
47
+ ]
48
+ },
49
  "11.2": {
50
  "version": "11.2",
51
  "released_at": 1621844125,
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.2.4
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.3.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.2.4",
4
- "release_timestamp": 1622362224,
5
- "build": "202105.3001",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
@@ -255,7 +255,7 @@
255
  "introjs"
256
  ]
257
  },
258
- "shield/antibot": {
259
  },
260
  "shield/scans": {
261
  "deps": [
1
  {
2
  "properties": {
3
+ "version": "11.3.0",
4
+ "release_timestamp": 1623057021,
5
+ "build": "202106.0701",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
255
  "introjs"
256
  ]
257
  },
258
+ "shield/notbot": {
259
  },
260
  "shield/scans": {
261
  "deps": [
readme.txt CHANGED
@@ -8,7 +8,7 @@ Requires at least: 3.5.2
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 5.7
11
- Stable tag: 11.2.4
12
  Security against hackers and brute force bots with firewall, login security hiding and hardening, Antispam, Audit Trail, Live Traffic, and much more...
13
 
14
  == Description ==
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 5.7
11
+ Stable tag: 11.3.0
12
  Security against hackers and brute force bots with firewall, login security hiding and hardening, Antispam, Audit Trail, Live Traffic, and much more...
13
 
14
  == Description ==
resources/js/shield/{antibot.js → notbot.js} RENAMED
@@ -1,4 +1,4 @@
1
- if ( typeof Shield_Antibot === typeof undefined && typeof shield_vars_antibotjs !== typeof undefined ) {
2
 
3
  var Shield_Antibot = new function () {
4
 
@@ -26,7 +26,7 @@ if ( typeof Shield_Antibot === typeof undefined && typeof shield_vars_antibotjs
26
  * Early execution also helps mitigate the case where login requests are
27
  * sent quickly, before browser has fired NotBot request.
28
  */
29
- if ( shield_vars_antibotjs.flags.run ) {
30
  sendReq();
31
  }
32
  /**
@@ -59,9 +59,9 @@ if ( typeof Shield_Antibot === typeof undefined && typeof shield_vars_antibotjs
59
 
60
  var sendReq = function ( name ) {
61
  var xhttp = new XMLHttpRequest();
62
- xhttp.open( "POST", shield_vars_antibotjs.hrefs.ajax, true );
63
  xhttp.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded;' );
64
- xhttp.send( shield_vars_antibotjs.ajax.not_bot );
65
  request_count++;
66
  };
67
 
1
+ if ( typeof Shield_Antibot === typeof undefined && typeof shield_vars_notbotjs !== typeof undefined ) {
2
 
3
  var Shield_Antibot = new function () {
4
 
26
  * Early execution also helps mitigate the case where login requests are
27
  * sent quickly, before browser has fired NotBot request.
28
  */
29
+ if ( shield_vars_notbotjs.flags.run ) {
30
  sendReq();
31
  }
32
  /**
59
 
60
  var sendReq = function ( name ) {
61
  var xhttp = new XMLHttpRequest();
62
+ xhttp.open( "POST", shield_vars_notbotjs.hrefs.ajax, true );
63
  xhttp.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded;' );
64
+ xhttp.send( shield_vars_notbotjs.ajax.not_bot );
65
  request_count++;
66
  };
67
 
src/config/feature-admin_access_restriction.php CHANGED
@@ -55,8 +55,7 @@
55
  "summary": [
56
  "Purpose - Restricts access to key WordPress areas for all users not authenticated with the Security Admin Access system.",
57
  "Recommendation - Use of this feature is highly recommend."
58
- ],
59
- "help_video_id": "339824074"
60
  },
61
  {
62
  "slug": "section_whitelabel",
55
  "summary": [
56
  "Purpose - Restricts access to key WordPress areas for all users not authenticated with the Security Admin Access system.",
57
  "Recommendation - Use of this feature is highly recommend."
58
+ ]
 
59
  },
60
  {
61
  "slug": "section_whitelabel",
src/config/feature-ips.php CHANGED
@@ -151,6 +151,19 @@
151
  "summary": "AntiBot Testing Threshold (Percentage)",
152
  "description": "When using Shield's AntiBot system, this is the threshold used for testing (between 1 and 99)."
153
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  {
155
  "key": "transgression_limit",
156
  "section": "section_auto_black_list",
@@ -559,6 +572,18 @@
559
  }
560
  ],
561
  "definitions": {
 
 
 
 
 
 
 
 
 
 
 
 
562
  "db_classes": {
563
  "botsignals": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\BotSignals\\Handler",
564
  "ip_lists": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\IPs\\Handler"
@@ -648,7 +673,7 @@
648
  "stat": false
649
  },
650
  "bottrack_404": {
651
- "cat": 2,
652
  "offense": true
653
  },
654
  "bottrack_fakewebcrawler": {
@@ -668,7 +693,7 @@
668
  "offense": true
669
  },
670
  "bottrack_useragent": {
671
- "cat": 2,
672
  "offense": true
673
  },
674
  "bottrack_xmlrpc": {
151
  "summary": "AntiBot Testing Threshold (Percentage)",
152
  "description": "When using Shield's AntiBot system, this is the threshold used for testing (between 1 and 99)."
153
  },
154
+ {
155
+ "key": "antibot_high_reputation_minimum",
156
+ "section": "section_antibot",
157
+ "default": 200,
158
+ "type": "integer",
159
+ "min": 0,
160
+ "link_info": "https://shsec.io/jy",
161
+ "link_blog": "https://shsec.io/jz",
162
+ "beacon_id": 431,
163
+ "name": "High Reputation Bypass",
164
+ "summary": "Prevent IPs/Visitors With High Reputation Scores From Being Blocked",
165
+ "description": "Ensures that visitors with a high reputation are never blocked by Shield."
166
+ },
167
  {
168
  "key": "transgression_limit",
169
  "section": "section_auto_black_list",
572
  }
573
  ],
574
  "definitions": {
575
+ "allowable_ext_404s": [
576
+ "js",
577
+ "css",
578
+ "gif",
579
+ "jpg",
580
+ "jpeg",
581
+ "png",
582
+ "map",
583
+ "ttf",
584
+ "woff",
585
+ "woff2"
586
+ ],
587
  "db_classes": {
588
  "botsignals": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\BotSignals\\Handler",
589
  "ip_lists": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\IPs\\Handler"
673
  "stat": false
674
  },
675
  "bottrack_404": {
676
+ "cat": 1,
677
  "offense": true
678
  },
679
  "bottrack_fakewebcrawler": {
693
  "offense": true
694
  },
695
  "bottrack_useragent": {
696
+ "cat": 1,
697
  "offense": true
698
  },
699
  "bottrack_xmlrpc": {
src/config/feature-plugin.php CHANGED
@@ -538,10 +538,6 @@
538
  }
539
  ],
540
  "definitions": {
541
- "help_video_id": "",
542
- "tracking_cron_handle": "plugin_tracking_cron",
543
- "tracking_post_url": "https://tracking.icontrolwp.com/track/plugin/shield",
544
- "importexport_cron_name": "autoimport",
545
  "href_privacy_policy": "https://shsec.io/wpshieldprivacypolicy",
546
  "db_classes": {
547
  "geoip": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\GeoIp\\Handler",
538
  }
539
  ],
540
  "definitions": {
 
 
 
 
541
  "href_privacy_policy": "https://shsec.io/wpshieldprivacypolicy",
542
  "db_classes": {
543
  "geoip": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\GeoIp\\Handler",
src/lib/src/Databases/AuditTrail/Select.php CHANGED
@@ -24,12 +24,12 @@ class Select extends Base\Select {
24
  }
25
 
26
  /**
27
- * @param string $sEvent
28
  * @return $this
29
  */
30
- public function filterByEvent( $sEvent ) {
31
- if ( !empty( $sEvent ) && strtolower( $sEvent ) != 'all' ) {
32
- $this->addWhereEquals( 'event', $sEvent );
33
  }
34
  return $this;
35
  }
24
  }
25
 
26
  /**
27
+ * @param string $event
28
  * @return $this
29
  */
30
+ public function filterByEvent( $event ) {
31
+ if ( !empty( $event ) && strtolower( $event ) != 'all' ) {
32
+ $this->addWhereEquals( 'event', $event );
33
  }
34
  return $this;
35
  }
src/lib/src/Databases/Base/BaseQuery.php CHANGED
@@ -194,23 +194,13 @@ abstract class BaseQuery {
194
  * @return string
195
  */
196
  public function buildWhere() {
197
-
198
- if ( method_exists( $this, 'getRawWheres' ) ) {
199
- $wheres = $this->getRawWheres();
200
- if ( !$this->isIncludeSoftDeletedRows() ) {
201
- $wheres[] = [ 'deleted_at', '=', 0 ];
202
- }
203
- $wheres = array_map( function ( array $where ) {
204
- return $this->rawWhereToString( $where );
205
- }, $wheres );
206
  }
207
- else { // TODO: @deprecated 11.2
208
- $wheres = $this->getWheres();
209
- if ( !$this->isIncludeSoftDeletedRows() ) {
210
- $wheres[] = '`deleted_at`=0';
211
- }
212
- }
213
-
214
  return implode( ' AND ', $wheres );
215
  }
216
 
@@ -394,7 +384,7 @@ abstract class BaseQuery {
394
  return $this->setLimit( 0 )
395
  ->setRawWheres( [] )
396
  ->setPage( 1 )
397
- ->setOrderBy( null );
398
  }
399
 
400
  /**
194
  * @return string
195
  */
196
  public function buildWhere() {
197
+ $wheres = $this->getRawWheres();
198
+ if ( !$this->isIncludeSoftDeletedRows() ) {
199
+ $wheres[] = [ 'deleted_at', '=', 0 ];
 
 
 
 
 
 
200
  }
201
+ $wheres = array_map( function ( array $where ) {
202
+ return $this->rawWhereToString( $where );
203
+ }, $wheres );
 
 
 
 
204
  return implode( ' AND ', $wheres );
205
  }
206
 
384
  return $this->setLimit( 0 )
385
  ->setRawWheres( [] )
386
  ->setPage( 1 )
387
+ ->setOrderBy( '' );
388
  }
389
 
390
  /**
src/lib/src/Databases/Base/EntryVoConsumer.php CHANGED
@@ -1,4 +1,4 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
4
 
@@ -11,13 +11,13 @@ trait EntryVoConsumer {
11
  /**
12
  * @var EntryVO
13
  */
14
- private $oEntryVO;
15
 
16
  /**
17
  * @return EntryVO|mixed
18
  */
19
  public function getEntryVO() {
20
- return $this->oEntryVO;
21
  }
22
 
23
  /**
@@ -25,7 +25,7 @@ trait EntryVoConsumer {
25
  * @return $this
26
  */
27
  public function setEntryVO( $entry ) {
28
- $this->oEntryVO = $entry;
29
  return $this;
30
  }
31
  }
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
4
 
11
  /**
12
  * @var EntryVO
13
  */
14
+ private $entryVO;
15
 
16
  /**
17
  * @return EntryVO|mixed
18
  */
19
  public function getEntryVO() {
20
+ return $this->entryVO;
21
  }
22
 
23
  /**
25
  * @return $this
26
  */
27
  public function setEntryVO( $entry ) {
28
+ $this->entryVO = $entry;
29
  return $this;
30
  }
31
  }
src/lib/src/Databases/Base/Handler.php CHANGED
@@ -91,10 +91,6 @@ abstract class Handler extends ExecOnceModConsumer {
91
  }
92
  }
93
 
94
- protected function getColumnForOlderThanComparison() :string {
95
- return 'created_at';
96
- }
97
-
98
  /**
99
  * @param int $timestamp
100
  * @return bool
91
  }
92
  }
93
 
 
 
 
 
94
  /**
95
  * @param int $timestamp
96
  * @return bool
src/lib/src/Databases/Events/Common.php CHANGED
@@ -9,18 +9,18 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Events;
9
  trait Common {
10
 
11
  /**
12
- * @param string $sEvent
13
  * @return $this
14
  */
15
- public function filterByEvent( $sEvent ) {
16
- return $this->filterByEvents( [ $sEvent ] );
17
  }
18
 
19
  /**
20
- * @param string[] $aEvents
21
  * @return $this
22
  */
23
- public function filterByEvents( $aEvents ) {
24
- return $this->addWhereIn( 'event', $aEvents );
25
  }
26
  }
9
  trait Common {
10
 
11
  /**
12
+ * @param string $event
13
  * @return $this
14
  */
15
+ public function filterByEvent( $event ) {
16
+ return $this->filterByEvents( [ $event ] );
17
  }
18
 
19
  /**
20
+ * @param string[] $events
21
  * @return $this
22
  */
23
+ public function filterByEvents( array $events ) {
24
+ return $this->addWhereIn( 'event', $events );
25
  }
26
  }
src/lib/src/Databases/Events/Select.php CHANGED
@@ -26,12 +26,8 @@ class Select extends Base\Select {
26
  ->sum();
27
  }
28
 
29
- /**
30
- * @param string $sEvent
31
- * @return int
32
- */
33
- public function sumEventsLike( $sEvent ) {
34
- return (int)$this->addWhereLike( 'event', $sEvent )
35
  ->setColumnsToSelect( [ 'count' ] )
36
  ->sum();
37
  }
@@ -46,17 +42,17 @@ class Select extends Base\Select {
46
 
47
  natsort( $allEvents );
48
  foreach ( $allEvents as $event ) {
49
- $sums[ $event ] = $this->clearWheres()->sumEvent( $event );
50
  }
51
  return $sums;
52
  }
53
 
54
  /**
55
- * @param string $sEvent
56
  * @return EntryVO|null
57
  */
58
- public function getLatestForEvent( $sEvent ) {
59
- return $this->filterByEvent( $sEvent )
60
  ->setOrderBy( 'created_at', 'DESC' )
61
  ->setResultsAsVo( true )
62
  ->first();
26
  ->sum();
27
  }
28
 
29
+ public function sumEventsLike( string $event ) :int {
30
+ return (int)$this->addWhereLike( 'event', $event )
 
 
 
 
31
  ->setColumnsToSelect( [ 'count' ] )
32
  ->sum();
33
  }
42
 
43
  natsort( $allEvents );
44
  foreach ( $allEvents as $event ) {
45
+ $sums[ $event ] = (int)$this->clearWheres()->sumEvent( $event );
46
  }
47
  return $sums;
48
  }
49
 
50
  /**
51
+ * @param string $event
52
  * @return EntryVO|null
53
  */
54
+ public function getLatestForEvent( string $event ) {
55
+ return $this->filterByEvent( $event )
56
  ->setOrderBy( 'created_at', 'DESC' )
57
  ->setResultsAsVo( true )
58
  ->first();
src/lib/src/Modules/AuditTrail/Lib/AuditWriter.php CHANGED
@@ -23,9 +23,6 @@ class AuditWriter extends EventsListener {
23
  */
24
  protected function captureEvent( string $evt, $meta = [], $def = [] ) {
25
  $con = $this->getCon();
26
- if ( empty( $def ) ) { // TODO: @deprecated 11.2 - remove this if
27
- $def = $con->loadEventsService()->getEventDef( $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();
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();
src/lib/src/Modules/AuditTrail/Lib/Ops/Commit.php CHANGED
@@ -56,7 +56,7 @@ class Commit {
56
  ->filterByCreatedAt( Services::Request()->carbon()->subDay()->timestamp, '>' )
57
  ->first();
58
  $canCount = ( $latest instanceof AuditTrail\EntryVO )
59
- && ( $latest->event === $entry->event && $latest->ip === $entry->ip );
60
  }
61
 
62
  if ( $canCount ) {
56
  ->filterByCreatedAt( Services::Request()->carbon()->subDay()->timestamp, '>' )
57
  ->first();
58
  $canCount = ( $latest instanceof AuditTrail\EntryVO )
59
+ && ( $latest->ip === $entry->ip );
60
  }
61
 
62
  if ( $canCount ) {
src/lib/src/Modules/AuditTrail/Options.php CHANGED
@@ -1,4 +1,4 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
4
 
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
4
 
src/lib/src/Modules/Base/ModCon.php CHANGED
@@ -565,52 +565,6 @@ abstract class ModCon {
565
  return $this->sModSlug;
566
  }
567
 
568
- /**
569
- * @param array $items
570
- * @return array
571
- * @deprecated 11.2
572
- */
573
- public function supplySubMenuItem( $items ) {
574
-
575
- $title = $this->getOptions()->getFeatureProperty( 'menu_title' );
576
- $title = empty( $title ) ? $this->getMainFeatureName() : __( $title, 'wp-simple-firewall' );
577
-
578
- if ( !empty( $title ) ) {
579
- $highlightedTemplate = '<span class="shield_highlighted_menu">%s</span>';
580
- $humanName = $this->getCon()->getHumanName();
581
-
582
- if ( $this->getOptions()->getFeatureProperty( 'highlight_menu_item' ) ) {
583
- $title = sprintf( $highlightedTemplate, $title );
584
- }
585
-
586
- $menuPageTitle = $title.' - '.$humanName;
587
- $items[ $menuPageTitle ] = [
588
- $title,
589
- $this->getModSlug(),
590
- [ $this, 'displayModuleAdminPage' ],
591
- $this->getIfShowModuleMenuItem()
592
- ];
593
-
594
- foreach ( $this->getOptions()->getAdditionalMenuItems() as $menuItem ) {
595
-
596
- // special case: don't show go pro if you're pro.
597
- if ( $menuItem[ 'slug' ] !== 'pro-redirect' || !$this->isPremium() ) {
598
-
599
- $title = __( $menuItem[ 'title' ], 'wp-simple-firewall' );
600
- $menuPageTitle = $humanName.' - '.$title;
601
- $isHighlighted = $menuItem[ 'highlight' ] ?? false;
602
- $items[ $menuPageTitle ] = [
603
- $isHighlighted ? sprintf( $highlightedTemplate, $title ) : $title,
604
- $this->prefix( $menuItem[ 'slug' ] ),
605
- [ $this, $menuItem[ 'callback' ] ?? '' ],
606
- true
607
- ];
608
- }
609
- }
610
- }
611
- return $items;
612
- }
613
-
614
  /**
615
  * Handles the case where we want to redirect certain menu requests to other pages
616
  * of the plugin automatically. It lets us create custom menu items.
@@ -970,14 +924,6 @@ abstract class ModCon {
970
  return $this;
971
  }
972
 
973
- /**
974
- * @deprecated 11.2
975
- */
976
- protected function isThisModAdminPage() :bool {
977
- return is_admin() && !Services::WpGeneral()->isAjax()
978
- && Services::Request()->isGet() && $this->isThisModulePage();
979
- }
980
-
981
  public function isPremium() :bool {
982
  return $this->getCon()->isPremiumActive();
983
  }
@@ -1375,7 +1321,6 @@ abstract class ModCon {
1375
  ->setOptionsStorageKey( $this->getOptionsStorageKey() )
1376
  ->setIfLoadOptionsFromStorage( !$con->getIsResetPlugin() );
1377
  $opts = $this->opts;
1378
- /** @deprecated 11.2 */
1379
  }
1380
  return $opts;
1381
  }
565
  return $this->sModSlug;
566
  }
567
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
568
  /**
569
  * Handles the case where we want to redirect certain menu requests to other pages
570
  * of the plugin automatically. It lets us create custom menu items.
924
  return $this;
925
  }
926
 
 
 
 
 
 
 
 
 
927
  public function isPremium() :bool {
928
  return $this->getCon()->isPremiumActive();
929
  }
1321
  ->setOptionsStorageKey( $this->getOptionsStorageKey() )
1322
  ->setIfLoadOptionsFromStorage( !$con->getIsResetPlugin() );
1323
  $opts = $this->opts;
 
1324
  }
1325
  return $opts;
1326
  }
src/lib/src/Modules/BaseShield/ModCon.php CHANGED
@@ -43,13 +43,6 @@ class ModCon extends Base\ModCon {
43
  ->getCurrent();
44
  }
45
 
46
- /**
47
- * @deprecated 11.2
48
- */
49
- public function hasValidRequestIP() :bool {
50
- return !empty( Services::IP()->isValidIp( Services::IP()->getRequestIp() ) );
51
- }
52
-
53
  public function onWpInit() {
54
  parent::onWpInit();
55
  if ( $this->isThisModulePage() && !$this->isWizardPage() && ( $this->getSlug() != 'insights' ) ) {
@@ -234,14 +227,4 @@ class ModCon extends Base\ModCon {
234
  $this->getBaseNamespace(),
235
  ];
236
  }
237
-
238
- /**
239
- * @return bool
240
- * @deprecated 11.2
241
- */
242
- public function isEnabledWhitelabel() :bool {
243
- /** @var SecurityAdmin\Options $opts */
244
- $opts = $this->getCon()->getModule_SecAdmin()->getOptions();
245
- return $opts->isEnabledWhitelabel();
246
- }
247
  }
43
  ->getCurrent();
44
  }
45
 
 
 
 
 
 
 
 
46
  public function onWpInit() {
47
  parent::onWpInit();
48
  if ( $this->isThisModulePage() && !$this->isWizardPage() && ( $this->getSlug() != 'insights' ) ) {
227
  $this->getBaseNamespace(),
228
  ];
229
  }
 
 
 
 
 
 
 
 
 
 
230
  }
src/lib/src/Modules/Events/Consolidate/ConsolidateAllEvents.php CHANGED
@@ -22,9 +22,9 @@ class ConsolidateAllEvents {
22
  }
23
 
24
  /**
25
- * @param $sEvent
26
  */
27
- protected function consolidateEventIntoHourly( $sEvent ) {
28
  /** @var ModCon $mod */
29
  $mod = $this->getMod();
30
  $oDbH = $mod->getDbHandler_Events();
@@ -39,7 +39,7 @@ class ConsolidateAllEvents {
39
  /** @var Events\Select $oSel */
40
  $oSel = $oDbH->getQuerySelector();
41
  $nRecords = $oSel->filterByBoundary_Hour( $oTime->timestamp )
42
- ->filterByEvent( $sEvent )
43
  ->count();
44
 
45
  if ( $nRecords > 1 ) {
@@ -47,17 +47,17 @@ class ConsolidateAllEvents {
47
  $oSel = $oDbH->getQuerySelector();
48
  /** @var Events\EntryVO[] $aRecords */
49
  $nSum = $oSel->filterByBoundary_Hour( $oTime->timestamp )
50
- ->sumEvent( $sEvent );
51
  if ( $nSum > 0 ) {
52
 
53
  /** @var Events\Delete $oDel */
54
  $oDel = $oDbH->getQueryDeleter();
55
  $oDel->filterByBoundary_Hour( $oTime->timestamp )
56
- ->filterByEvent( $sEvent )
57
  ->query();
58
 
59
  $oEntry = new Events\EntryVO();
60
- $oEntry->event = $sEvent;
61
  $oEntry->count = $nSum;
62
  $oEntry->created_at = $oTime->timestamp + 1;
63
  /** @var Events\Insert $oQI */
@@ -74,52 +74,52 @@ class ConsolidateAllEvents {
74
  /**
75
  * Consolidates each event in Daily sums. Doesn't process events from the previous 48hrs.
76
  * Processes event for the 7 days previous to the last 48 hours.
77
- * @param $sEvent
78
  */
79
- protected function consolidateEventIntoDaily( $sEvent ) {
80
  /** @var ModCon $mod */
81
  $mod = $this->getMod();
82
- $oDbH = $mod->getDbHandler_Events();
83
 
84
- $oTime = Services::Request()
85
- ->carbon()
86
- ->subDays( 2 )
87
- ->startOfDay();
88
 
89
  $nDayCount = 0;
90
  do {
91
- /** @var Events\Select $oSel */
92
- $oSel = $oDbH->getQuerySelector();
93
- $nRecords = $oSel->filterByBoundary_Day( $oTime->timestamp )
94
- ->filterByEvent( $sEvent )
95
- ->count();
96
 
97
- if ( $nRecords > 1 ) {
98
  /** @var Events\Select $oSel */
99
- $oSel = $oDbH->getQuerySelector();
100
  /** @var Events\EntryVO[] $aRecords */
101
- $nSum = $oSel->filterByBoundary_Day( $oTime->timestamp )
102
- ->sumEvent( $sEvent );
103
  if ( $nSum > 0 ) {
104
 
105
- /** @var Events\Delete $oDel */
106
- $oDel = $oDbH->getQueryDeleter();
107
- $oDel->filterByBoundary_Day( $oTime->timestamp )
108
- ->filterByEvent( $sEvent )
109
- ->query();
110
 
111
  $oEntry = new Events\EntryVO();
112
- $oEntry->event = $sEvent;
113
  $oEntry->count = $nSum;
114
- $oEntry->created_at = $oTime->timestamp + 1;
115
  /** @var Events\Insert $oQI */
116
- $oQI = $oDbH->getQueryInserter();
117
  $oQI->insert( $oEntry );
118
  }
119
  }
120
 
121
  $nDayCount++;
122
- $oTime->subDay();
123
  } while ( $nDayCount < 13 );
124
  }
125
 
22
  }
23
 
24
  /**
25
+ * @param $event
26
  */
27
+ protected function consolidateEventIntoHourly( $event ) {
28
  /** @var ModCon $mod */
29
  $mod = $this->getMod();
30
  $oDbH = $mod->getDbHandler_Events();
39
  /** @var Events\Select $oSel */
40
  $oSel = $oDbH->getQuerySelector();
41
  $nRecords = $oSel->filterByBoundary_Hour( $oTime->timestamp )
42
+ ->filterByEvent( $event )
43
  ->count();
44
 
45
  if ( $nRecords > 1 ) {
47
  $oSel = $oDbH->getQuerySelector();
48
  /** @var Events\EntryVO[] $aRecords */
49
  $nSum = $oSel->filterByBoundary_Hour( $oTime->timestamp )
50
+ ->sumEvent( $event );
51
  if ( $nSum > 0 ) {
52
 
53
  /** @var Events\Delete $oDel */
54
  $oDel = $oDbH->getQueryDeleter();
55
  $oDel->filterByBoundary_Hour( $oTime->timestamp )
56
+ ->filterByEvent( $event )
57
  ->query();
58
 
59
  $oEntry = new Events\EntryVO();
60
+ $oEntry->event = $event;
61
  $oEntry->count = $nSum;
62
  $oEntry->created_at = $oTime->timestamp + 1;
63
  /** @var Events\Insert $oQI */
74
  /**
75
  * Consolidates each event in Daily sums. Doesn't process events from the previous 48hrs.
76
  * Processes event for the 7 days previous to the last 48 hours.
77
+ * @param $event
78
  */
79
+ protected function consolidateEventIntoDaily( $event ) {
80
  /** @var ModCon $mod */
81
  $mod = $this->getMod();
82
+ $dbh = $mod->getDbHandler_Events();
83
 
84
+ $time = Services::Request()
85
+ ->carbon()
86
+ ->subDays( 2 )
87
+ ->startOfDay();
88
 
89
  $nDayCount = 0;
90
  do {
91
+ /** @var Events\Select $select */
92
+ $select = $dbh->getQuerySelector();
93
+ $recordsCount = $select->filterByBoundary_Day( $time->timestamp )
94
+ ->filterByEvent( $event )
95
+ ->count();
96
 
97
+ if ( $recordsCount > 1 ) {
98
  /** @var Events\Select $oSel */
99
+ $select = $dbh->getQuerySelector();
100
  /** @var Events\EntryVO[] $aRecords */
101
+ $nSum = $select->filterByBoundary_Day( $time->timestamp )
102
+ ->sumEvent( $event );
103
  if ( $nSum > 0 ) {
104
 
105
+ /** @var Events\Delete $deleter */
106
+ $deleter = $dbh->getQueryDeleter();
107
+ $deleter->filterByBoundary_Day( $time->timestamp )
108
+ ->filterByEvent( $event )
109
+ ->query();
110
 
111
  $oEntry = new Events\EntryVO();
112
+ $oEntry->event = $event;
113
  $oEntry->count = $nSum;
114
+ $oEntry->created_at = $time->timestamp + 1;
115
  /** @var Events\Insert $oQI */
116
+ $oQI = $dbh->getQueryInserter();
117
  $oQI->insert( $oEntry );
118
  }
119
  }
120
 
121
  $nDayCount++;
122
+ $time->subDay();
123
  } while ( $nDayCount < 13 );
124
  }
125
 
src/lib/src/Modules/Events/Lib/Reports/ScanRepairs.php CHANGED
@@ -17,28 +17,28 @@ class ScanRepairs extends BaseReporter {
17
 
18
  /** @var Events\ModCon $mod */
19
  $mod = $this->getMod();
20
- /** @var DBEvents\Select $oSelEvts */
21
- $oSelEvts = $mod->getDbHandler_Events()->getQuerySelector();
22
- /** @var Events\Strings $oStrings */
23
- $oStrings = $mod->getStrings();
24
 
25
- $oRep = $this->getReport();
26
 
27
- $aCounts = [];
28
 
29
  /** @var Options $oHGOptions */
30
  $oHGOptions = $this->getCon()->getModule_HackGuard()->getOptions();
31
- foreach ( $oHGOptions->getScanSlugs() as $sScan ) {
32
  try {
33
- $sEvt = $sScan.'_item_repair_success';
34
- $nCount = $oSelEvts
35
- ->filterByEvent( $sEvt )
36
- ->filterByBoundary( $oRep->interval_start_at, $oRep->interval_end_at )
37
  ->count();
38
- if ( $nCount > 0 ) {
39
- $aCounts[ $sScan ] = [
40
- 'count' => $nCount,
41
- 'name' => $oStrings->getEventName( $sEvt ),
42
  ];
43
  }
44
  }
@@ -46,12 +46,12 @@ class ScanRepairs extends BaseReporter {
46
  }
47
  }
48
 
49
- if ( count( $aCounts ) > 0 ) {
50
  $aAlerts[] = $this->getMod()->renderTemplate(
51
  '/components/reports/mod/events/info_keystats.twig',
52
  [
53
  'vars' => [
54
- 'counts' => $aCounts
55
  ],
56
  'strings' => [
57
  'title' => __( 'Scanner Repairs', 'wp-simple-firewall' ),
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
  }
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' ),
src/lib/src/Modules/Events/Lib/StatsWriter.php CHANGED
@@ -21,9 +21,6 @@ class StatsWriter extends EventsListener {
21
  * @param array $def
22
  */
23
  protected function captureEvent( string $evt, $meta = [], $def = [] ) {
24
- if ( empty( $def ) ) { // TODO: @deprecated 11.2 - remove this if
25
- $def = $this->getCon()->loadEventsService()->getEventDef( $evt );
26
- }
27
  if ( !empty( $def[ 'stat' ] ) ) {
28
  $stats = $this->getEventStats();
29
  $stats[ $evt ] = $meta[ 'ts' ] ?? Services::Request()->ts();
21
  * @param array $def
22
  */
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();
src/lib/src/Modules/HackGuard/Scan/Controller/Base.php CHANGED
@@ -202,18 +202,6 @@ abstract class Base extends ExecOnceModConsumer {
202
  return $this->isPremiumOnly() && !$this->getCon()->isPremiumActive();
203
  }
204
 
205
- /**
206
- * @return bool
207
- * @deprecated 11.2
208
- */
209
- public function isScanningAvailable() :bool {
210
- /** @var ModCon $mod */
211
- $mod = $this->getMod();
212
- /** @var HackGuard\Options $opts */
213
- $opts = $this->getOptions();
214
- return $mod->isModuleEnabled() && ( !$this->isPremiumOnly() || $opts->isPremium() );
215
- }
216
-
217
  /**
218
  * @return $this
219
  */
202
  return $this->isPremiumOnly() && !$this->getCon()->isPremiumActive();
203
  }
204
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  /**
206
  * @return $this
207
  */
src/lib/src/Modules/IPs/BotTrack/Base.php CHANGED
@@ -2,15 +2,11 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\BotTrack;
4
 
5
- use FernleafSystems\Utilities\Logic\ExecOnce;
6
  use FernleafSystems\Wordpress\Plugin\Shield;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
10
- abstract class Base {
11
-
12
- use Shield\Modules\ModConsumer;
13
- use ExecOnce;
14
 
15
  const OPT_KEY = '';
16
 
@@ -18,28 +14,34 @@ abstract class Base {
18
  $this->process();
19
  }
20
 
21
- protected function doTransgression() {
22
  /** @var IPs\Options $opts */
23
  $opts = $this->getOptions();
24
 
25
- $block = $opts->isTrackOptImmediateBlock( static::OPT_KEY );
26
- if ( $block ) {
27
- $count = 1;
28
- }
29
- elseif ( $opts->isTrackOptTransgression( static::OPT_KEY ) ) {
30
- $count = $opts->isTrackOptDoubleTransgression( static::OPT_KEY ) ? 2 : 1;
31
- }
32
- else {
33
- $count = 0;
 
 
34
  }
35
 
 
 
 
 
36
  $this->getCon()
37
  ->fireEvent(
38
  'bot'.static::OPT_KEY,
39
  [
40
  'audit' => $this->getAuditData(),
41
- 'offense_count' => $count,
42
- 'block' => $block,
43
  ]
44
  );
45
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\BotTrack;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
+ abstract class Base extends Shield\Modules\Base\Common\ExecOnceModConsumer {
 
 
 
10
 
11
  const OPT_KEY = '';
12
 
14
  $this->process();
15
  }
16
 
17
+ protected function doTransgression( bool $fireEventOnly = false ) {
18
  /** @var IPs\Options $opts */
19
  $opts = $this->getOptions();
20
 
21
+ $count = 0;
22
+ $block = false;
23
+
24
+ if ( !$fireEventOnly ) {
25
+ $block = $opts->isTrackOptImmediateBlock( static::OPT_KEY );
26
+ if ( $block ) {
27
+ $count = 1;
28
+ }
29
+ elseif ( $opts->isTrackOptTransgression( static::OPT_KEY ) ) {
30
+ $count = $opts->isTrackOptDoubleTransgression( static::OPT_KEY ) ? 2 : 1;
31
+ }
32
  }
33
 
34
+ $this->fireEvent( $count, $block );
35
+ }
36
+
37
+ protected function fireEvent( $offenseCount = 0, $isBlock = false ) {
38
  $this->getCon()
39
  ->fireEvent(
40
  'bot'.static::OPT_KEY,
41
  [
42
  'audit' => $this->getAuditData(),
43
+ 'offense_count' => $offenseCount,
44
+ 'block' => $isBlock,
45
  ]
46
  );
47
  }
src/lib/src/Modules/IPs/BotTrack/Track404.php CHANGED
@@ -2,6 +2,8 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\BotTrack;
4
 
 
 
5
  class Track404 extends Base {
6
 
7
  const OPT_KEY = 'track_404';
@@ -9,8 +11,64 @@ class Track404 extends Base {
9
  protected function process() {
10
  add_action( 'template_redirect', function () {
11
  if ( is_404() ) {
12
- $this->doTransgression();
 
 
 
 
 
 
 
 
 
13
  }
14
  } );
15
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\BotTrack;
4
 
5
+ use FernleafSystems\Wordpress\Services\Services;
6
+
7
  class Track404 extends Base {
8
 
9
  const OPT_KEY = 'track_404';
11
  protected function process() {
12
  add_action( 'template_redirect', function () {
13
  if ( is_404() ) {
14
+ $reqPath = $this->getRequestPath();
15
+ $extensions = implode( '|', $this->getAllowableExtensions() );
16
+
17
+ // if the request's file extension is allowed to trigger 404s, we fire only the event, without transgression.
18
+ // However, if the requested asset is within for a plugin or theme that doesn't exists, it's not allowed.
19
+ $allowed = preg_match( sprintf( '#\.(%s)$#i', $extensions ), $reqPath ) === 1
20
+ && !$this->isRequestToInvalidPlugin()
21
+ && !$this->isRequestToInvalidTheme();
22
+
23
+ $this->doTransgression( $allowed );
24
  }
25
  } );
26
  }
27
+
28
+ private function isRequestToInvalidPlugin() :bool {
29
+ $isInvalid = false;
30
+
31
+ $reqPath = $this->getRequestPath();
32
+ $pathToPlugins = ltrim( wp_parse_url( plugins_url(), PHP_URL_PATH ), '/' );
33
+
34
+ if ( strpos( $reqPath, $pathToPlugins ) === 0 ) {
35
+ $assetStub = trim( str_replace( $pathToPlugins, '', $reqPath ), '/' );
36
+ if ( substr_count( $assetStub, '/' ) > 0 ) {
37
+ $dir = explode( '/', $assetStub, 2 )[ 0 ];
38
+ $file = Services::WpPlugins()->findPluginFileFromDirName( $dir );
39
+ if ( empty( $file ) ) {
40
+ $isInvalid = true;
41
+ }
42
+ }
43
+ }
44
+
45
+ return $isInvalid;
46
+ }
47
+
48
+ private function isRequestToInvalidTheme() :bool {
49
+ $isInvalid = false;
50
+
51
+ $reqPath = $this->getRequestPath();
52
+ $pathsToThemes = ltrim( dirname( wp_parse_url( get_stylesheet_directory_uri(), PHP_URL_PATH ) ), '/' );
53
+
54
+ if ( strpos( $reqPath, $pathsToThemes ) === 0 ) {
55
+ $assetStub = trim( str_replace( $pathsToThemes, '', $reqPath ), '/' );
56
+ if ( substr_count( $assetStub, '/' ) > 0 ) {
57
+ $dir = explode( '/', $assetStub, 2 )[ 0 ];
58
+ $isInvalid = !Services::WpThemes()->getExists( $dir );
59
+ }
60
+ }
61
+
62
+ return $isInvalid;
63
+ }
64
+
65
+ private function getAllowableExtensions() :array {
66
+ $defExts = $this->getOptions()->getDef( 'allowable_ext_404s' );
67
+ $extensions = apply_filters( 'shield/allowable_extensions_404s', $defExts );
68
+ return is_array( $extensions ) ? $extensions : $defExts;
69
+ }
70
+
71
+ private function getRequestPath() :string {
72
+ return ltrim( Services::Request()->getPath(), '/' );
73
+ }
74
  }
src/lib/src/Modules/IPs/BotTrack/TrackCommentSpam.php CHANGED
@@ -2,13 +2,9 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\BotTrack;
4
 
5
- use FernleafSystems\Utilities\Logic\ExecOnce;
6
  use FernleafSystems\Wordpress\Plugin\Shield;
7
 
8
- class TrackCommentSpam {
9
-
10
- use Shield\Modules\ModConsumer;
11
- use ExecOnce;
12
 
13
  protected function canRun() :bool {
14
  return is_admin() || is_network_admin();
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\BotTrack;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
 
7
+ class TrackCommentSpam extends Shield\Modules\Base\Common\ExecOnceModConsumer {
 
 
 
8
 
9
  protected function canRun() :bool {
10
  return is_admin() || is_network_admin();
src/lib/src/Modules/IPs/Components/IpAddressConsumer.php CHANGED
@@ -4,18 +4,8 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
 
7
- /**
8
- * Trait BaseIp
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components
10
- */
11
  trait IpAddressConsumer {
12
 
13
- /**
14
- * @var string
15
- * @deprecated 11.2
16
- */
17
- private $sIpAddress;
18
-
19
  /**
20
  * @var string
21
  */
@@ -25,7 +15,7 @@ trait IpAddressConsumer {
25
  * @return string
26
  */
27
  public function getIP() {
28
- return $this->ipAddress ?? $this->sIpAddress;
29
  }
30
 
31
  /**
@@ -34,7 +24,6 @@ trait IpAddressConsumer {
34
  */
35
  public function setIP( $IP ) {
36
  $this->ipAddress = $IP;
37
- $this->sIpAddress = $IP;
38
  return $this;
39
  }
40
  }
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
 
 
 
 
 
7
  trait IpAddressConsumer {
8
 
 
 
 
 
 
 
9
  /**
10
  * @var string
11
  */
15
  * @return string
16
  */
17
  public function getIP() {
18
+ return $this->ipAddress;
19
  }
20
 
21
  /**
24
  */
25
  public function setIP( $IP ) {
26
  $this->ipAddress = $IP;
 
27
  return $this;
28
  }
29
  }
src/lib/src/Modules/IPs/Lib/BlacklistHandler.php CHANGED
@@ -29,11 +29,9 @@ class BlacklistHandler extends Modules\Base\Common\ExecOnceModConsumer {
29
  ->setMod( $mod )
30
  ->run();
31
 
32
- add_action( 'init', [ $this, 'loadBotDetectors' ] ); // hook in the bot detection
33
-
34
  if ( !$mod->isVisitorWhitelisted() && !$this->isRequestWhitelisted() ) {
35
 
36
- // We setup offenses processing immediately but run the blocks on 'init
37
  ( new ProcessOffenses() )
38
  ->setMod( $this->getMod() )
39
  ->execute();
@@ -46,59 +44,10 @@ class BlacklistHandler extends Modules\Base\Common\ExecOnceModConsumer {
46
  }
47
  }
48
 
 
 
 
49
  public function loadBotDetectors() {
50
- /** @var IPs\ModCon $mod */
51
- $mod = $this->getMod();
52
- /** @var IPs\Options $opts */
53
- $opts = $this->getOptions();
54
-
55
- if ( !Services::WpUsers()->isUserLoggedIn() ) {
56
-
57
- if ( !$mod->isTrustedVerifiedBot() ) {
58
- if ( $opts->isEnabledTrackXmlRpc() ) {
59
- ( new IPs\BotTrack\TrackXmlRpc() )
60
- ->setMod( $mod )
61
- ->execute();
62
- }
63
- if ( $opts->isEnabledTrack404() ) {
64
- ( new IPs\BotTrack\Track404() )
65
- ->setMod( $mod )
66
- ->execute();
67
- }
68
- if ( $opts->isEnabledTrackLoginFailed() ) {
69
- ( new IPs\BotTrack\TrackLoginFailed() )
70
- ->setMod( $mod )
71
- ->execute();
72
- }
73
- if ( $opts->isEnabledTrackLoginInvalid() ) {
74
- ( new IPs\BotTrack\TrackLoginInvalid() )
75
- ->setMod( $mod )
76
- ->execute();
77
- }
78
- if ( $opts->isEnabledTrackFakeWebCrawler() ) {
79
- ( new IPs\BotTrack\TrackFakeWebCrawler() )
80
- ->setMod( $mod )
81
- ->execute();
82
- }
83
- if ( $opts->isEnabledTrackInvalidScript() ) {
84
- ( new IPs\BotTrack\TrackInvalidScriptLoad() )
85
- ->setMod( $mod )
86
- ->execute();
87
- }
88
- }
89
-
90
- /** Always run link cheese regardless of the verified bot or not */
91
- if ( $opts->isEnabledTrackLinkCheese() && $mod->canLinkCheese() ) {
92
- ( new IPs\BotTrack\TrackLinkCheese() )
93
- ->setMod( $mod )
94
- ->execute();
95
- }
96
- }
97
-
98
- // Capture when admins un/mark comments as spam
99
- ( new IPs\BotTrack\TrackCommentSpam() )
100
- ->setMod( $mod )
101
- ->execute();
102
  }
103
 
104
  private function isRequestWhitelisted() :bool {
29
  ->setMod( $mod )
30
  ->run();
31
 
 
 
32
  if ( !$mod->isVisitorWhitelisted() && !$this->isRequestWhitelisted() ) {
33
 
34
+ // We setup offenses processing immediately but run the blocks on 'init'
35
  ( new ProcessOffenses() )
36
  ->setMod( $this->getMod() )
37
  ->execute();
44
  }
45
  }
46
 
47
+ /**
48
+ * @deprecated 11.3
49
+ */
50
  public function loadBotDetectors() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  }
52
 
53
  private function isRequestWhitelisted() :bool {
src/lib/src/Modules/IPs/Lib/BlockRequest.php CHANGED
@@ -10,7 +10,7 @@ use FernleafSystems\Wordpress\Services\Utilities\Obfuscate;
10
  class BlockRequest extends ExecOnceModConsumer {
11
 
12
  protected function run() {
13
- if ( $this->isBlocked() ) {
14
 
15
  if ( $this->isAutoUnBlocked() ) {
16
  Services::Response()->redirectToHome();
@@ -30,6 +30,16 @@ class BlockRequest extends ExecOnceModConsumer {
30
  ->run();
31
  }
32
 
 
 
 
 
 
 
 
 
 
 
33
  private function isAutoUnBlocked() :bool {
34
  return ( new AutoUnblock() )
35
  ->setMod( $this->getMod() )
10
  class BlockRequest extends ExecOnceModConsumer {
11
 
12
  protected function run() {
13
+ if ( $this->isBlocked() && !$this->isHighReputationIP() ) {
14
 
15
  if ( $this->isAutoUnBlocked() ) {
16
  Services::Response()->redirectToHome();
30
  ->run();
31
  }
32
 
33
+ private function isHighReputationIP() :bool {
34
+ /** @var IPs\Options $opts */
35
+ $opts = $this->getOptions();
36
+ return ( new IPs\Lib\Bots\Calculator\CalculateVisitorBotScores() )
37
+ ->setMod( $this->getMod() )
38
+ ->setIP( Services::IP()->getRequestIp() )
39
+ ->total() >
40
+ (int)apply_filters( 'shield/high_reputation_ip_minimum', $opts->getAntiBotHighReputationMinimum() );
41
+ }
42
+
43
  private function isAutoUnBlocked() :bool {
44
  return ( new AutoUnblock() )
45
  ->setMod( $this->getMod() )
src/lib/src/Modules/IPs/Lib/Bots/BotSignalsController.php CHANGED
@@ -3,8 +3,12 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator\CalculateVisitorBotScores;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Options;
 
 
 
 
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
10
  class BotSignalsController extends ExecOnceModConsumer {
@@ -67,7 +71,55 @@ class BotSignalsController extends ExecOnceModConsumer {
67
  protected function run() {
68
  $this->getEventListener()->execute();
69
  add_action( 'init', function () {
 
 
 
70
  $this->getHandlerNotBot()->execute();
71
  } );
72
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\{
7
+ BotTrack,
8
+ Lib\Bots\Calculator\CalculateVisitorBotScores,
9
+ ModCon,
10
+ Options
11
+ };
12
  use FernleafSystems\Wordpress\Services\Services;
13
 
14
  class BotSignalsController extends ExecOnceModConsumer {
71
  protected function run() {
72
  $this->getEventListener()->execute();
73
  add_action( 'init', function () {
74
+ foreach ( $this->enumerateBotTrackers() as $botTracker ) {
75
+ $botTracker->setMod( $this->getMod() )->execute();
76
+ }
77
  $this->getHandlerNotBot()->execute();
78
  } );
79
  }
80
+
81
+ /**
82
+ * @return BotTrack\Base[]
83
+ */
84
+ private function enumerateBotTrackers() :array {
85
+ /** @var ModCon $mod */
86
+ $mod = $this->getMod();
87
+ /** @var Options $opts */
88
+ $opts = $this->getOptions();
89
+
90
+ $trackers = [
91
+ new BotTrack\TrackCommentSpam()
92
+ ];
93
+
94
+ if ( !Services::WpUsers()->isUserLoggedIn() ) {
95
+
96
+ if ( !$mod->isTrustedVerifiedBot() ) {
97
+
98
+ if ( $opts->isEnabledTrack404() ) {
99
+ $trackers[] = new BotTrack\Track404();
100
+ }
101
+ if ( $opts->isEnabledTrackXmlRpc() ) {
102
+ $trackers[] = new BotTrack\TrackXmlRpc();
103
+ }
104
+ if ( $opts->isEnabledTrackLoginFailed() ) {
105
+ $trackers[] = new BotTrack\TrackLoginFailed();
106
+ }
107
+ if ( $opts->isEnabledTrackLoginInvalid() ) {
108
+ $trackers[] = new BotTrack\TrackLoginInvalid();
109
+ }
110
+ if ( $opts->isEnabledTrackFakeWebCrawler() ) {
111
+ $trackers[] = new BotTrack\TrackFakeWebCrawler();
112
+ }
113
+ if ( $opts->isEnabledTrackInvalidScript() ) {
114
+ $trackers[] = new BotTrack\TrackInvalidScriptLoad();
115
+ }
116
+ }
117
+
118
+ if ( $opts->isEnabledTrackLinkCheese() && $mod->canLinkCheese() ) {
119
+ $trackers[] = new BotTrack\TrackLinkCheese();
120
+ }
121
+ }
122
+
123
+ return $trackers;
124
+ }
125
  }
src/lib/src/Modules/IPs/Lib/Bots/BotSignalsRecord.php CHANGED
@@ -27,9 +27,8 @@ class BotSignalsRecord {
27
  public function retrieve( bool $storeOnLoad = true ) :EntryVO {
28
  /** @var ModCon $mod */
29
  $mod = $this->getMod();
30
- /** @var Select $select */
31
- $select = $mod->getDbHandler_BotSignals()->getQuerySelector();
32
- $e = $select->filterByIPHuman( $this->getIP() )->first();
33
  if ( !$e instanceof EntryVO ) {
34
  $e = new EntryVO();
35
  $e->ip = $this->getIP();
@@ -75,6 +74,18 @@ class BotSignalsRecord {
75
  return $e;
76
  }
77
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  public function store( EntryVO $entry ) :bool {
79
  /** @var ModCon $mod */
80
  $mod = $this->getMod();
27
  public function retrieve( bool $storeOnLoad = true ) :EntryVO {
28
  /** @var ModCon $mod */
29
  $mod = $this->getMod();
30
+
31
+ $e = $this->dbLoad();
 
32
  if ( !$e instanceof EntryVO ) {
33
  $e = new EntryVO();
34
  $e->ip = $this->getIP();
74
  return $e;
75
  }
76
 
77
+ /**
78
+ * @return EntryVO|null
79
+ */
80
+ private function dbLoad() {
81
+ /** @var ModCon $mod */
82
+ $mod = $this->getMod();
83
+ /** @var Select $select */
84
+ $select = $mod->getDbHandler_BotSignals()->getQuerySelector();
85
+ /** @var EntryVO $record */
86
+ return $select->filterByIPHuman( $this->getIP() )->first();
87
+ }
88
+
89
  public function store( EntryVO $entry ) :bool {
90
  /** @var ModCon $mod */
91
  $mod = $this->getMod();
src/lib/src/Modules/IPs/Lib/Bots/Calculator/BaseBuildScores.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base\EntryVoConsumer;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\BotSignals\EntryVO;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
+ use FernleafSystems\Wordpress\Services\Services;
9
+ use FernleafSystems\Wordpress\Services\Utilities\Net\IpID;
10
+
11
+ abstract class BaseBuildScores {
12
+
13
+ use EntryVoConsumer;
14
+ use ModConsumer;
15
+
16
+ abstract public function build() :array;
17
+
18
+ protected function score_known() :int {
19
+ try {
20
+ list( $ipID, $ipName ) = ( new IpID( $this->getRecord()->ip ) )->run();
21
+ }
22
+ catch ( \Exception $e ) {
23
+ $ipID = null;
24
+ }
25
+ return ( empty( $ipID ) || in_array( $ipID, [ IpID::UNKNOWN, IpID::VISITOR ] ) )
26
+ ? 0 : 100;
27
+ }
28
+
29
+ protected function lastAtTs( $fieldFunction ) :int {
30
+ $field = str_replace( 'score_', '', $fieldFunction ).'_at';
31
+ return $this->getRecord()->{$field} ?? 0;
32
+ }
33
+
34
+ protected function diffTs( $fieldFunction ) :int {
35
+ $field = str_replace( 'score_', '', $fieldFunction ).'_at';
36
+ return Services::Request()->ts() - ( $this->getRecord()->{$field} ?? 0 );
37
+ }
38
+
39
+ protected function getAllFields( $filterForMethods = false ) :array {
40
+ $botSignalDBH = shield_security_get_plugin()->getController()
41
+ ->getModule_IPs()
42
+ ->getDbHandler_BotSignals();
43
+ $fields = array_map(
44
+ function ( $col ) {
45
+ return str_replace( '_at', '', $col );
46
+ },
47
+ array_filter(
48
+ $botSignalDBH->getTableSchema()->getColumnNames(),
49
+ function ( $col ) {
50
+ return preg_match( '#_at$#', $col ) &&
51
+ !in_array( $col, [ 'updated_at', 'deleted_at' ] );
52
+ }
53
+ )
54
+ );
55
+
56
+ if ( $filterForMethods ) {
57
+ $fields = array_filter( $fields, function ( $field ) {
58
+ return method_exists( $this, 'score_'.$field );
59
+ } );
60
+ }
61
+
62
+ return $fields;
63
+ }
64
+
65
+ protected function getRecord() :EntryVO {
66
+ return $this->getEntryVO();
67
+ }
68
+ }
src/lib/src/Modules/IPs/Lib/Bots/Calculator/BuildScores.php CHANGED
@@ -2,310 +2,53 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base\EntryVoConsumer;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\BotSignals\EntryVO;
7
- use FernleafSystems\Wordpress\Services\Services;
8
- use FernleafSystems\Wordpress\Services\Utilities\Net\IpID;
9
 
10
- class BuildScores {
11
-
12
- use EntryVoConsumer;
 
13
 
14
  public function build() :array {
15
  $scores = [];
16
- foreach ( $this->getAllFields( true ) as $field ) {
17
- $scores[ $field ] = $this->{'score_'.$field}();
18
  }
 
19
  $scores[ 'known' ] = $this->score_known();
20
 
21
  return $scores;
22
  }
23
 
24
- private function score_known() :int {
25
- try {
26
- list( $ipID, $ipName ) = ( new IpID( $this->getRecord()->ip ) )->run();
27
- }
28
- catch ( \Exception $e ) {
29
- $ipID = null;
30
- }
31
- return ( empty( $ipID ) || in_array( $ipID, [ IpID::UNKNOWN, IpID::VISITOR ] ) )
32
- ? 0 : 100;
33
- }
34
-
35
- private function score_auth() :int {
36
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
37
- $score = 0;
38
- }
39
- else {
40
- $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? 175 : 150;
41
- }
42
- return $score;
43
- }
44
-
45
- private function score_created() :int {
46
- $score = 0;
47
- $tsDiff = $this->diffTs( __FUNCTION__ );
48
- if ( $tsDiff > 3 && $tsDiff < 15 ) {
49
- $score = 15;
50
- }
51
- return $score;
52
- }
53
-
54
- private function score_bt404() :int {
55
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
56
- $score = 0;
57
- }
58
- else {
59
- $score = $this->diffTs( __FUNCTION__ ) < HOUR_IN_SECONDS ? -15 : -5;
60
- }
61
- return $score;
62
- }
63
-
64
- private function score_btcheese() :int {
65
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
66
- $score = 0;
67
- }
68
- else {
69
- $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -65 : -45;
70
- }
71
- return $score;
72
- }
73
-
74
- private function score_btfake() :int {
75
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
76
- $score = 0;
77
- }
78
- else {
79
- $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -75 : -45;
80
- }
81
- return $score;
82
- }
83
-
84
- private function score_btinvalidscript() :int {
85
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
86
- $score = 0;
87
- }
88
- else {
89
- $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -25 : -15;
90
- }
91
- return $score;
92
- }
93
-
94
- private function score_btloginfail() :int {
95
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
96
- $score = 0;
97
- }
98
- else {
99
- $score = $this->diffTs( __FUNCTION__ ) < MINUTE_IN_SECONDS ? -35 : -15;
100
- }
101
- return $score;
102
- }
103
-
104
- private function score_btlogininvalid() :int {
105
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
106
- $score = 0;
107
- }
108
- else {
109
- $score = $this->diffTs( __FUNCTION__ ) < HOUR_IN_SECONDS ? -85 : -55;
110
- }
111
- return $score;
112
- }
113
-
114
- private function score_btua() :int {
115
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
116
- $score = 0;
117
- }
118
- else {
119
- $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -35 : -25;
120
- }
121
- return $score;
122
- }
123
-
124
- private function score_btxml() :int {
125
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
126
- $score = 0;
127
- }
128
- else {
129
- $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -55 : -35;
130
- }
131
- return $score;
132
- }
133
-
134
- private function score_cooldown() :int {
135
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
136
- $score = 0;
137
- }
138
- else {
139
- $score = $this->diffTs( __FUNCTION__ ) < MINUTE_IN_SECONDS ? -25 : -15;
140
- }
141
- return $score;
142
- }
143
-
144
- private function score_firewall() :int {
145
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
146
- $score = 0;
147
- }
148
- else {
149
- $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -35 : -15;
150
- }
151
- return $score;
152
- }
153
-
154
- private function score_offense() :int {
155
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
156
- $score = 0;
157
- }
158
- else {
159
- $score = $this->diffTs( __FUNCTION__ ) < MINUTE_IN_SECONDS ? -35 : -25;
160
- }
161
- return $score;
162
- }
163
-
164
- private function score_blocked() :int {
165
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
166
- $score = 0;
167
- }
168
- else {
169
- $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -55 : -45;
170
- }
171
- return $score;
172
- }
173
-
174
- private function score_unblocked() :int {
175
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
176
- $score = 0;
177
- }
178
- else {
179
- $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? 100 : 75;
180
- }
181
- return $score;
182
- }
183
-
184
- private function score_bypass() :int {
185
- return $this->lastAtTs( __FUNCTION__ ) > 0 ? 150 : 0;
186
- }
187
-
188
- private function score_captchapass() :int {
189
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
190
- $score = 0;
191
- }
192
- else {
193
- $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? 55 : 25;
194
- }
195
- return $score;
196
- }
197
-
198
- private function score_ratelimit() :int {
199
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
200
- $score = 0;
201
- }
202
- else {
203
- $score = $this->diffTs( __FUNCTION__ ) < MINUTE_IN_SECONDS ? -55 : -25;
204
- }
205
- return $score;
206
- }
207
-
208
- private function score_captchafail() :int {
209
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
210
- $score = 0;
211
- }
212
- else {
213
- $score = $this->diffTs( __FUNCTION__ ) < HOUR_IN_SECONDS ? -55 : -25;
214
- }
215
- return $score;
216
- }
217
-
218
- private function score_humanspam() :int {
219
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
220
- $score = 0;
221
- }
222
- else {
223
- $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -30 : -15;
224
- }
225
- return $score;
226
- }
227
 
228
- private function score_markspam() :int {
229
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
230
- $score = 0;
231
- }
232
- else {
233
- $score = $this->diffTs( __FUNCTION__ ) < WEEK_IN_SECONDS ? -50 : -25;
234
- }
235
- return $score;
236
- }
237
 
238
- private function score_unmarkspam() :int {
239
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
240
- $score = 0;
241
  }
242
  else {
243
- $score = $this->diffTs( __FUNCTION__ ) < WEEK_IN_SECONDS ? 75 : 35;
244
- }
245
- return $score;
246
- }
247
 
248
- private function score_frontpage() :int {
249
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
250
- $score = -15;
251
- }
252
- else {
253
- $score = $this->diffTs( __FUNCTION__ ) < HOUR_IN_SECONDS ? 25 : 15;
254
- }
255
- return $score;
256
- }
257
-
258
- private function score_loginpage() :int {
259
- return $this->lastAtTs( __FUNCTION__ ) > 0 ? 15 : 0;
260
- }
261
-
262
- private function score_notbot() :int {
263
- if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
264
- $score = -10;
265
- }
266
- else {
267
- $score = $this->diffTs( __FUNCTION__ ) < HOUR_IN_SECONDS ? 150 : 75;
268
- }
269
- return $score;
270
- }
271
-
272
- private function lastAtTs( $fieldFunction ) :int {
273
- $field = str_replace( 'score_', '', $fieldFunction ).'_at';
274
- return $this->getRecord()->{$field} ?? 0;
275
- }
276
-
277
- private function diffTs( $fieldFunction ) :int {
278
- $field = str_replace( 'score_', '', $fieldFunction ).'_at';
279
- return Services::Request()->ts() - ( $this->getRecord()->{$field} ?? 0 );
280
- }
281
-
282
- private function getAllFields( $filterForMethods = false ) :array {
283
- $botSignalDBH = shield_security_get_plugin()->getController()
284
- ->getModule_IPs()
285
- ->getDbHandler_BotSignals();
286
- $fields = array_map(
287
- function ( $col ) {
288
- return str_replace( '_at', '', $col );
289
- },
290
- array_filter(
291
- $botSignalDBH->getTableSchema()->getColumnNames(),
292
- function ( $col ) {
293
- return preg_match( '#_at$#', $col ) &&
294
- !in_array( $col, [ 'updated_at', 'deleted_at' ] );
295
  }
296
- )
297
- );
298
-
299
- if ( $filterForMethods ) {
300
- $fields = array_filter( $fields, function ( $field ) {
301
- return method_exists( $this, 'score_'.$field );
302
- } );
303
  }
304
 
305
- return $fields;
306
  }
307
 
308
- private function getRecord() :EntryVO {
309
- return $this->getEntryVO();
 
 
 
310
  }
311
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator;
4
 
5
+ class BuildScores extends BaseBuildScores {
 
 
 
6
 
7
+ /**
8
+ * @var ScoreLogic
9
+ */
10
+ private $logic;
11
 
12
  public function build() :array {
13
  $scores = [];
14
+ foreach ( $this->getAllFields() as $field ) {
15
+ $scores[ $field ] = $this->calcFieldScore( $field );
16
  }
17
+
18
  $scores[ 'known' ] = $this->score_known();
19
 
20
  return $scores;
21
  }
22
 
23
+ private function calcFieldScore( string $field ) :int {
24
+ $logic = $this->getScoringLogic()->getFieldScoreLogic( $field );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
+ // -1 represents the default if none of the following boundaries are satisfied
27
+ $score = $logic[ -1 ];
 
 
 
 
 
 
 
28
 
29
+ if ( $this->lastAtTs( $field ) === 0 ) {
30
+ $score = $logic[ 0 ];
 
31
  }
32
  else {
33
+ unset( $logic[ 0 ] );
34
+ ksort( $logic );
 
 
35
 
36
+ $diff = $this->diffTs( $field );
37
+ foreach ( $logic as $boundary => $boundaryScore ) {
38
+ if ( $diff < $boundary ) {
39
+ $score = $boundaryScore;
40
+ break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
+ }
 
 
 
 
 
 
43
  }
44
 
45
+ return (int)$score;
46
  }
47
 
48
+ private function getScoringLogic() :ScoreLogic {
49
+ if ( !$this->logic instanceof ScoreLogic ) {
50
+ $this->logic = ( new ScoreLogic() )->setCon( $this->getCon() );
51
+ }
52
+ return $this->logic;
53
  }
54
  }
src/lib/src/Modules/IPs/Lib/Bots/Calculator/BuildScoresFallback.php ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator;
4
+
5
+ use FernleafSystems\Wordpress\Services\Services;
6
+
7
+ class BuildScoresFallback extends BaseBuildScores {
8
+
9
+ public function build() :array {
10
+ $scores = [];
11
+ foreach ( $this->getAllFields( true ) as $field ) {
12
+ $scores[ $field ] = $this->{'score_'.$field}();
13
+ }
14
+ $scores[ 'known' ] = $this->score_known();
15
+ if ( Services::Request()->ts() - $this->getRecord()->created_at < 20 ) {
16
+ $scores[ 'baseline' ] = 35;
17
+ }
18
+ return $scores;
19
+ }
20
+
21
+ private function score_auth() :int {
22
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
23
+ $score = 0;
24
+ }
25
+ else {
26
+ $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? 175 : 150;
27
+ }
28
+ return $score;
29
+ }
30
+
31
+ private function score_bt404() :int {
32
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
33
+ $score = 0;
34
+ }
35
+ else {
36
+ $score = $this->diffTs( __FUNCTION__ ) < HOUR_IN_SECONDS ? -15 : -5;
37
+ }
38
+ return $score;
39
+ }
40
+
41
+ private function score_btcheese() :int {
42
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
43
+ $score = 0;
44
+ }
45
+ else {
46
+ $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -65 : -45;
47
+ }
48
+ return $score;
49
+ }
50
+
51
+ private function score_btfake() :int {
52
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
53
+ $score = 0;
54
+ }
55
+ else {
56
+ $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -75 : -45;
57
+ }
58
+ return $score;
59
+ }
60
+
61
+ private function score_btinvalidscript() :int {
62
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
63
+ $score = 0;
64
+ }
65
+ else {
66
+ $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -25 : -15;
67
+ }
68
+ return $score;
69
+ }
70
+
71
+ private function score_btloginfail() :int {
72
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
73
+ $score = 0;
74
+ }
75
+ else {
76
+ $score = $this->diffTs( __FUNCTION__ ) < MINUTE_IN_SECONDS ? -35 : -15;
77
+ }
78
+ return $score;
79
+ }
80
+
81
+ private function score_btlogininvalid() :int {
82
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
83
+ $score = 0;
84
+ }
85
+ else {
86
+ $score = $this->diffTs( __FUNCTION__ ) < HOUR_IN_SECONDS ? -85 : -55;
87
+ }
88
+ return $score;
89
+ }
90
+
91
+ private function score_btua() :int {
92
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
93
+ $score = 0;
94
+ }
95
+ else {
96
+ $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -35 : -25;
97
+ }
98
+ return $score;
99
+ }
100
+
101
+ private function score_btxml() :int {
102
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
103
+ $score = 0;
104
+ }
105
+ else {
106
+ $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -55 : -35;
107
+ }
108
+ return $score;
109
+ }
110
+
111
+ private function score_cooldown() :int {
112
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
113
+ $score = 0;
114
+ }
115
+ else {
116
+ $score = $this->diffTs( __FUNCTION__ ) < MINUTE_IN_SECONDS ? -25 : -15;
117
+ }
118
+ return $score;
119
+ }
120
+
121
+ private function score_firewall() :int {
122
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
123
+ $score = 0;
124
+ }
125
+ else {
126
+ $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -35 : -15;
127
+ }
128
+ return $score;
129
+ }
130
+
131
+ private function score_offense() :int {
132
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
133
+ $score = 0;
134
+ }
135
+ else {
136
+ $score = $this->diffTs( __FUNCTION__ ) < MINUTE_IN_SECONDS ? -35 : -25;
137
+ }
138
+ return $score;
139
+ }
140
+
141
+ private function score_blocked() :int {
142
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
143
+ $score = 0;
144
+ }
145
+ else {
146
+ $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -55 : -45;
147
+ }
148
+ return $score;
149
+ }
150
+
151
+ private function score_unblocked() :int {
152
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
153
+ $score = 0;
154
+ }
155
+ else {
156
+ $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? 100 : 75;
157
+ }
158
+ return $score;
159
+ }
160
+
161
+ private function score_bypass() :int {
162
+ return $this->lastAtTs( __FUNCTION__ ) > 0 ? 150 : 0;
163
+ }
164
+
165
+ private function score_captchapass() :int {
166
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
167
+ $score = 0;
168
+ }
169
+ else {
170
+ $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? 55 : 25;
171
+ }
172
+ return $score;
173
+ }
174
+
175
+ private function score_ratelimit() :int {
176
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
177
+ $score = 0;
178
+ }
179
+ else {
180
+ $score = $this->diffTs( __FUNCTION__ ) < MINUTE_IN_SECONDS ? -55 : -25;
181
+ }
182
+ return $score;
183
+ }
184
+
185
+ private function score_captchafail() :int {
186
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
187
+ $score = 0;
188
+ }
189
+ else {
190
+ $score = $this->diffTs( __FUNCTION__ ) < HOUR_IN_SECONDS ? -55 : -25;
191
+ }
192
+ return $score;
193
+ }
194
+
195
+ private function score_humanspam() :int {
196
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
197
+ $score = 0;
198
+ }
199
+ else {
200
+ $score = $this->diffTs( __FUNCTION__ ) < DAY_IN_SECONDS ? -30 : -15;
201
+ }
202
+ return $score;
203
+ }
204
+
205
+ private function score_markspam() :int {
206
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
207
+ $score = 0;
208
+ }
209
+ else {
210
+ $score = $this->diffTs( __FUNCTION__ ) < WEEK_IN_SECONDS ? -50 : -25;
211
+ }
212
+ return $score;
213
+ }
214
+
215
+ private function score_unmarkspam() :int {
216
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
217
+ $score = 0;
218
+ }
219
+ else {
220
+ $score = $this->diffTs( __FUNCTION__ ) < WEEK_IN_SECONDS ? 75 : 35;
221
+ }
222
+ return $score;
223
+ }
224
+
225
+ private function score_frontpage() :int {
226
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
227
+ $score = -15;
228
+ }
229
+ else {
230
+ $score = $this->diffTs( __FUNCTION__ ) < HOUR_IN_SECONDS ? 25 : 15;
231
+ }
232
+ return $score;
233
+ }
234
+
235
+ private function score_loginpage() :int {
236
+ return $this->lastAtTs( __FUNCTION__ ) > 0 ? 15 : 0;
237
+ }
238
+
239
+ private function score_notbot() :int {
240
+ if ( $this->lastAtTs( __FUNCTION__ ) === 0 ) {
241
+ $score = -10;
242
+ }
243
+ else {
244
+ $score = $this->diffTs( __FUNCTION__ ) < HOUR_IN_SECONDS ? 150 : 75;
245
+ }
246
+ return $score;
247
+ }
248
+ }
src/lib/src/Modules/IPs/Lib/Bots/Calculator/CalculateVisitorBotScores.php CHANGED
@@ -18,6 +18,7 @@ class CalculateVisitorBotScores {
18
  public function scores() :array {
19
  $this->scores = ( new BuildScores() )
20
  ->setEntryVO( $this->loadEntry() )
 
21
  ->build();
22
  return $this->getActiveScores();
23
  }
18
  public function scores() :array {
19
  $this->scores = ( new BuildScores() )
20
  ->setEntryVO( $this->loadEntry() )
21
+ ->setMod( $this->getMod() )
22
  ->build();
23
  return $this->getActiveScores();
24
  }
src/lib/src/Modules/IPs/Lib/Bots/Calculator/ScoreLogic.php ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\Reputation\BotScoringLogic;
7
+ use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
8
+
9
+ class ScoreLogic {
10
+
11
+ use PluginControllerConsumer;
12
+
13
+ private $rawLogic = [];
14
+
15
+ public function getFieldScoreLogic( $field ) :array {
16
+ return $this->getScoringLogic()[ $field ] ?? [];
17
+ }
18
+
19
+ private function getScoringLogic() :array {
20
+ if ( empty( $this->rawLogic ) ) {
21
+ $logic = Transient::Get( 'shield-bot-scoring-logic' );
22
+ if ( empty( $logic ) ) {
23
+ $logicLoader = ( new BotScoringLogic() )
24
+ ->setMod( $this->getCon()->getModule_Plugin() );
25
+ $logicLoader->shield_net_params_required = false;
26
+ $logic = $logicLoader->retrieve();
27
+ if ( !empty( $logic ) ) {
28
+ Transient::Set( 'shield-bot-scoring-logic', $logic, DAY_IN_SECONDS );
29
+ }
30
+ }
31
+
32
+ $this->rawLogic = empty( $logic ) ? $this->buildFallback() : $logic;
33
+ }
34
+ return $this->rawLogic;
35
+ }
36
+
37
+ /**
38
+ * @return array[]
39
+ * @note Copied from ShieldNET API
40
+ */
41
+ protected function buildFallback() :array {
42
+ return array_map( function ( $score ) {
43
+ return $score + [
44
+ 0 => 0,
45
+ -1 => 0,
46
+ ];
47
+ }, array_merge( $this->getPositiveSignals(), $this->getNegativeSignals() ) );
48
+ }
49
+
50
+ protected function getPositiveSignals() :array {
51
+ return [
52
+ 'created' => [
53
+ 3 => 0,
54
+ 15 => 15,
55
+ ],
56
+ 'notbot' => [
57
+ 0 => -10,
58
+ HOUR_IN_SECONDS => 150,
59
+ -1 => 75,
60
+ ],
61
+ 'frontpage' => [
62
+ 0 => -15,
63
+ HOUR_IN_SECONDS => 25,
64
+ -1 => 15,
65
+ ],
66
+ 'loginpage' => [
67
+ -1 => 15,
68
+ ],
69
+ 'unmarkspam' => [
70
+ WEEK_IN_SECONDS => 75,
71
+ -1 => 35,
72
+ ],
73
+ 'captchapass' => [
74
+ DAY_IN_SECONDS => 55,
75
+ -1 => 25,
76
+ ],
77
+ 'auth' => [
78
+ DAY_IN_SECONDS => 175,
79
+ -1 => 150,
80
+ ],
81
+ 'unblocked' => [
82
+ DAY_IN_SECONDS => 100,
83
+ -1 => 75,
84
+ ],
85
+ 'bypass' => [
86
+ -1 => 150,
87
+ ],
88
+ ];
89
+ }
90
+
91
+ protected function getNegativeSignals() :array {
92
+ return [
93
+ 'bt404' => [
94
+ HOUR_IN_SECONDS => -15,
95
+ -1 => -5,
96
+ ],
97
+ 'btfake' => [
98
+ DAY_IN_SECONDS => -75,
99
+ -1 => -45,
100
+ ],
101
+ 'btcheese' => [
102
+ DAY_IN_SECONDS => -65,
103
+ -1 => -45,
104
+ ],
105
+ 'btloginfail' => [
106
+ MINUTE_IN_SECONDS => -75,
107
+ -1 => -45,
108
+ ],
109
+ 'btua' => [
110
+ DAY_IN_SECONDS => -35,
111
+ -1 => -25,
112
+ ],
113
+ 'btxml' => [
114
+ DAY_IN_SECONDS => -55,
115
+ -1 => -35,
116
+ ],
117
+ 'btlogininvalid' => [
118
+ HOUR_IN_SECONDS => -85,
119
+ -1 => -55,
120
+ ],
121
+ 'btinvalidscript' => [
122
+ HOUR_IN_SECONDS => -25,
123
+ -1 => -15,
124
+ ],
125
+ 'cooldown' => [
126
+ MINUTE_IN_SECONDS => -25,
127
+ -1 => -15,
128
+ ],
129
+ 'humanspam' => [
130
+ DAY_IN_SECONDS => -30,
131
+ -1 => -15,
132
+ ],
133
+ 'markspam' => [
134
+ WEEK_IN_SECONDS => -50,
135
+ -1 => -25,
136
+ ],
137
+ 'captchafail' => [
138
+ MINUTE_IN_SECONDS => -55,
139
+ -1 => -25,
140
+ ],
141
+ 'firewall' => [
142
+ DAY_IN_SECONDS => -35,
143
+ -1 => -15,
144
+ ],
145
+ 'ratelimit' => [
146
+ MINUTE_IN_SECONDS => -55,
147
+ -1 => -25,
148
+ ],
149
+ 'offense' => [
150
+ MINUTE_IN_SECONDS => -35,
151
+ -1 => -25,
152
+ ],
153
+ 'blocked' => [
154
+ DAY_IN_SECONDS => -55,
155
+ -1 => -45,
156
+ ],
157
+ ];
158
+ }
159
+ }
src/lib/src/Modules/IPs/Lib/Bots/NotBot/InsertNotBotJs.php CHANGED
@@ -23,7 +23,7 @@ class InsertNotBotJs extends ExecOnceModConsumer {
23
 
24
  protected function enqueueJS() {
25
  add_filter( 'shield/custom_enqueues', function ( array $enqueues ) {
26
- $enqueues[ Enqueue::JS ][] = 'shield/antibot';
27
  return $enqueues;
28
  } );
29
  }
@@ -39,8 +39,8 @@ class InsertNotBotJs extends ExecOnceModConsumer {
39
  unset( $ajaxData[ 'ajaxurl' ] );
40
 
41
  $localz[] = [
42
- 'shield/antibot',
43
- 'shield_vars_antibotjs',
44
  apply_filters( 'shield/notbot_data_js', [
45
  'ajax' => [
46
  'not_bot' => http_build_query( $ajaxData )
23
 
24
  protected function enqueueJS() {
25
  add_filter( 'shield/custom_enqueues', function ( array $enqueues ) {
26
+ $enqueues[ Enqueue::JS ][] = 'shield/notbot';
27
  return $enqueues;
28
  } );
29
  }
39
  unset( $ajaxData[ 'ajaxurl' ] );
40
 
41
  $localz[] = [
42
+ 'shield/notbot',
43
+ 'shield_vars_notbotjs',
44
  apply_filters( 'shield/notbot_data_js', [
45
  'ajax' => [
46
  'not_bot' => http_build_query( $ajaxData )
src/lib/src/Modules/IPs/Lib/Bots/NotBot/TestNotBotLoading.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\NotBot;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+
8
+ class TestNotBotLoading {
9
+
10
+ use ModConsumer;
11
+
12
+ public function test() :bool {
13
+ $urlToFind = explode( '?', $this->getCon()->urls->forJs( 'shield/notbot' ) )[ 0 ];
14
+ return preg_match(
15
+ sprintf( '#%s#i', preg_quote( $urlToFind, '#' ) ),
16
+ Services::HttpRequest()->getContent( network_home_url( '/' ), [
17
+ 'timeout' => 5
18
+ ] )
19
+ ) === 1;
20
+ }
21
+ }
src/lib/src/Modules/IPs/Lib/IpAnalyse/BuildDisplay.php CHANGED
@@ -136,11 +136,11 @@ class BuildDisplay {
136
  'delete_notbot' => __( 'Reset For This IP', 'wp-simple-firewall' ),
137
 
138
  'status' => [
139
- 'is_you' => __( 'Is It You?', 'wp-simple-firewall' ),
140
- 'offenses' => __( 'Number of offenses', 'wp-simple-firewall' ),
141
- 'is_blocked' => __( 'Is Blocked', 'wp-simple-firewall' ),
142
- 'is_bypass' => __( 'Is Bypass IP', 'wp-simple-firewall' ),
143
- 'notbot_score' => __( 'NotBot Score', 'wp-simple-firewall' ),
144
  ],
145
 
146
  'yes' => __( 'Yes', 'wp-simple-firewall' ),
@@ -163,12 +163,12 @@ class BuildDisplay {
163
  'vars' => [
164
  'ip' => $ip,
165
  'status' => [
166
- 'is_you' => Services::IP()->checkIp( $ip, Services::IP()->getRequestIp() ),
167
- 'offenses' => $blockIP instanceof Databases\IPs\EntryVO ? $blockIP->transgressions : 0,
168
- 'is_blocked' => $blockIP instanceof Databases\IPs\EntryVO ? $blockIP->blocked_at > 0 : false,
169
- 'is_bypass' => $bypassIP instanceof Databases\IPs\EntryVO,
170
- 'notbot_score' => $botScore,
171
- 'is_bot' => $isBot,
172
  ],
173
  'identity' => [
174
  'who_is_it' => $ipName,
@@ -321,9 +321,9 @@ class BuildDisplay {
321
  'title' => __( 'Bot Signals', 'wp-simple-firewall' ),
322
  'signal' => __( 'Signal', 'wp-simple-firewall' ),
323
  'score' => __( 'Score', 'wp-simple-firewall' ),
324
- 'total_score' => __( 'Total NotBot Score', 'wp-simple-firewall' ),
325
  'when' => __( 'When', 'wp-simple-firewall' ),
326
- 'bot_probability' => __( 'Bot Probability', 'wp-simple-firewall' ),
327
  'botsignal_delete' => __( 'Delete All Bot Signals', 'wp-simple-firewall' ),
328
  'signal_names' => $names,
329
  'no_signals' => __( 'There are no bot signals for this IP address.', 'wp-simple-firewall' ),
136
  'delete_notbot' => __( 'Reset For This IP', 'wp-simple-firewall' ),
137
 
138
  'status' => [
139
+ 'is_you' => __( 'Is It You?', 'wp-simple-firewall' ),
140
+ 'offenses' => __( 'Number of offenses', 'wp-simple-firewall' ),
141
+ 'is_blocked' => __( 'Is Blocked', 'wp-simple-firewall' ),
142
+ 'is_bypass' => __( 'Is Bypass IP', 'wp-simple-firewall' ),
143
+ 'ip_reputation' => __( 'IP Reputation Score', 'wp-simple-firewall' ),
144
  ],
145
 
146
  'yes' => __( 'Yes', 'wp-simple-firewall' ),
163
  'vars' => [
164
  'ip' => $ip,
165
  'status' => [
166
+ 'is_you' => Services::IP()->checkIp( $ip, Services::IP()->getRequestIp() ),
167
+ 'offenses' => $blockIP instanceof Databases\IPs\EntryVO ? $blockIP->transgressions : 0,
168
+ 'is_blocked' => $blockIP instanceof Databases\IPs\EntryVO ? $blockIP->blocked_at > 0 : false,
169
+ 'is_bypass' => $bypassIP instanceof Databases\IPs\EntryVO,
170
+ 'ip_reputation_score' => $botScore,
171
+ 'is_bot' => $isBot,
172
  ],
173
  'identity' => [
174
  'who_is_it' => $ipName,
321
  'title' => __( 'Bot Signals', 'wp-simple-firewall' ),
322
  'signal' => __( 'Signal', 'wp-simple-firewall' ),
323
  'score' => __( 'Score', 'wp-simple-firewall' ),
324
+ 'total_score' => __( 'Total Reputation Score', 'wp-simple-firewall' ),
325
  'when' => __( 'When', 'wp-simple-firewall' ),
326
+ 'bot_probability' => __( 'Bad Bot Probability', 'wp-simple-firewall' ),
327
  'botsignal_delete' => __( 'Delete All Bot Signals', 'wp-simple-firewall' ),
328
  'signal_names' => $names,
329
  'no_signals' => __( 'There are no bot signals for this IP address.', 'wp-simple-firewall' ),
src/lib/src/Modules/IPs/Lib/OffenseTracker.php CHANGED
@@ -22,10 +22,6 @@ class OffenseTracker extends EventsListener {
22
  * @param array $def
23
  */
24
  protected function captureEvent( string $evt, $meta = [], $def = [] ) {
25
- if ( empty( $def ) ) { // TODO: @deprecated 11.2 - remove this if
26
- $def = $this->getCon()->loadEventsService()->getEventDef( $evt );
27
- }
28
-
29
  if ( !empty( $def[ 'offense' ] ) && empty( $meta[ 'suppress_offense' ] ) ) {
30
  $this->incrementCount( (int)( $meta[ 'offense_count' ] ?? 1) );
31
  if ( !empty( $meta[ 'block' ] ) ) {
22
  * @param array $def
23
  */
24
  protected function captureEvent( string $evt, $meta = [], $def = [] ) {
 
 
 
 
25
  if ( !empty( $def[ 'offense' ] ) && empty( $meta[ 'suppress_offense' ] ) ) {
26
  $this->incrementCount( (int)( $meta[ 'offense_count' ] ?? 1) );
27
  if ( !empty( $meta[ 'block' ] ) ) {
src/lib/src/Modules/IPs/Options.php CHANGED
@@ -56,6 +56,10 @@ class Options extends BaseShield\Options {
56
  return (int)$this->getOpt( 'antibot_minimum', 50 );
57
  }
58
 
 
 
 
 
59
  public function isEnabledAntiBotEngine() :bool {
60
  return $this->getAntiBotMinimum() > 0;
61
  }
@@ -123,7 +127,7 @@ class Options extends BaseShield\Options {
123
  * @param string $key
124
  * @return bool
125
  */
126
- public function isTrackOptImmediateBlock( $key ) {
127
  return $this->isOpt( $key, 'block' );
128
  }
129
 
56
  return (int)$this->getOpt( 'antibot_minimum', 50 );
57
  }
58
 
59
+ public function getAntiBotHighReputationMinimum() :int {
60
+ return (int)$this->getOpt( 'antibot_high_reputation_minimum', 200 );
61
+ }
62
+
63
  public function isEnabledAntiBotEngine() :bool {
64
  return $this->getAntiBotMinimum() > 0;
65
  }
127
  * @param string $key
128
  * @return bool
129
  */
130
+ public function isTrackOptImmediateBlock( $key ) :bool {
131
  return $this->isOpt( $key, 'block' );
132
  }
133
 
src/lib/src/Modules/IPs/Strings.php CHANGED
@@ -161,6 +161,15 @@ class Strings extends Base\Strings {
161
  ];
162
  break;
163
 
 
 
 
 
 
 
 
 
 
164
  case 'text_loginfailed' :
165
  $name = __( 'Login Failed', 'wp-simple-firewall' );
166
  $summary = __( 'Visitor Triggers The IP Offense System Through A Failed Login', 'wp-simple-firewall' );
@@ -176,8 +185,14 @@ class Strings extends Base\Strings {
176
  case 'track_404' :
177
  $name = __( '404 Detect', 'wp-simple-firewall' );
178
  $summary = __( 'Identify A Bot When It Hits A 404', 'wp-simple-firewall' );
179
- $desc = __( "Detect when a visitor tries to load a non-existent page.", 'wp-simple-firewall' )
180
- .'<br/>'.__( "Care should be taken to ensure you don't have legitimate links on your site that are 404s.", 'wp-simple-firewall' );
 
 
 
 
 
 
181
  break;
182
 
183
  case 'track_xmlrpc' :
161
  ];
162
  break;
163
 
164
+ case 'antibot_high_reputation_minimum' :
165
+ $name = __( 'High Reputation Bypass', 'wp-simple-firewall' );
166
+ $summary = __( 'Prevent Visitors With A High Reputation Scores From Being Blocked', 'wp-simple-firewall' );
167
+ $desc = [
168
+ __( "Visitors that have accumulated a high IP reputation and AntiBot score should ideally never be blocked.", 'wp-simple-firewall' ),
169
+ __( "This option ensures that visitors with a high reputation never have their IP blocked by Shield.", 'wp-simple-firewall' ),
170
+ ];
171
+ break;
172
+
173
  case 'text_loginfailed' :
174
  $name = __( 'Login Failed', 'wp-simple-firewall' );
175
  $summary = __( 'Visitor Triggers The IP Offense System Through A Failed Login', 'wp-simple-firewall' );
185
  case 'track_404' :
186
  $name = __( '404 Detect', 'wp-simple-firewall' );
187
  $summary = __( 'Identify A Bot When It Hits A 404', 'wp-simple-firewall' );
188
+ $desc = [
189
+ __( 'Detect when a visitor tries to load a non-existent page.', 'wp-simple-firewall' ),
190
+ __( "Care should be taken to ensure that your website doesn't generate 404 errors for normal visitors.", 'wp-simple-firewall' ),
191
+ sprintf( '%s: <br/><strong>%s</strong>',
192
+ __( "404 errors generated for the following file types won't trigger an offense", 'wp-simple-firewall' ),
193
+ implode( ', ', $this->getOptions()->getDef( 'allowable_ext_404s' ) )
194
+ ),
195
+ ];
196
  break;
197
 
198
  case 'track_xmlrpc' :
src/lib/src/Modules/IPs/UI.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\Plugin\Shield\Modules\IPs\Lib\IpAnalyse\FindAllPluginIps;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\RetrieveIpsForLists;
8
  use FernleafSystems\Wordpress\Services\Services;
@@ -90,6 +91,15 @@ class UI extends BaseShield\UI {
90
  $warnings[] = sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ),
91
  sprintf( __( "The AntiBot Detection Engine is disabled when set to a minimum score of %s.", 'wp-simple-firewall' ), '0' ) );
92
  }
 
 
 
 
 
 
 
 
 
93
  break;
94
 
95
  case 'section_behaviours':
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\NotBot\TestNotBotLoading;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\IpAnalyse\FindAllPluginIps;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\RetrieveIpsForLists;
9
  use FernleafSystems\Wordpress\Services\Services;
91
  $warnings[] = sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ),
92
  sprintf( __( "The AntiBot Detection Engine is disabled when set to a minimum score of %s.", 'wp-simple-firewall' ), '0' ) );
93
  }
94
+ else {
95
+ $notbotFound = ( new TestNotBotLoading() )
96
+ ->setMod( $this->getCon()->getModule_IPs() )
97
+ ->test();
98
+ if ( !$notbotFound ) {
99
+ $warnings[] = sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ),
100
+ sprintf( __( "Shield couldn't determine whether the NotBot JS was loading correctly on your site.", 'wp-simple-firewall' ), '0' ) );
101
+ }
102
+ }
103
  break;
104
 
105
  case 'section_behaviours':
src/lib/src/Modules/IPs/WpCli.php CHANGED
@@ -1,4 +1,4 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
4
 
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
4
 
src/lib/src/Modules/Insights/Lib/SideMenuBuilder.php CHANGED
@@ -14,7 +14,6 @@ class SideMenuBuilder {
14
  $menu = [
15
  $this->search(),
16
  $this->overview(),
17
- $this->stats(),
18
  $this->configuration(),
19
  $this->scans(),
20
  $this->ips(),
@@ -22,6 +21,7 @@ class SideMenuBuilder {
22
  $this->traffic(),
23
  $this->users(),
24
  $this->integrations(),
 
25
  $this->gopro(),
26
  $this->tools(),
27
  $this->docs(),
14
  $menu = [
15
  $this->search(),
16
  $this->overview(),
 
17
  $this->configuration(),
18
  $this->scans(),
19
  $this->ips(),
21
  $this->traffic(),
22
  $this->users(),
23
  $this->integrations(),
24
+ $this->stats(),
25
  $this->gopro(),
26
  $this->tools(),
27
  $this->docs(),
src/lib/src/Modules/Insights/ModCon.php CHANGED
@@ -32,17 +32,6 @@ class ModCon extends BaseShield\ModCon {
32
  }
33
  }
34
 
35
- /**
36
- * @deprecated 11.2
37
- */
38
- private function maybeRedirectToAdmin() {
39
- $con = $this->getCon();
40
- $activeFor = $con->getModule_Plugin()->getActivateLength();
41
- if ( !Services::WpGeneral()->isAjax() && $activeFor < 4 ) {
42
- Services::Response()->redirect( $this->getCon()->getPluginUrl_DashboardHome() );
43
- }
44
- }
45
-
46
  public function getUrl_IpAnalysis( string $ip ) :string {
47
  return add_query_arg( [ 'analyse_ip' => $ip ], $this->getUrl_SubInsightsPage( 'ips' ) );
48
  }
32
  }
33
  }
34
 
 
 
 
 
 
 
 
 
 
 
 
35
  public function getUrl_IpAnalysis( string $ip ) :string {
36
  return add_query_arg( [ 'analyse_ip' => $ip ], $this->getUrl_SubInsightsPage( 'ips' ) );
37
  }
src/lib/src/Modules/Insights/UI.php CHANGED
@@ -265,28 +265,6 @@ class UI extends BaseShield\UI {
265
  );
266
  }
267
 
268
- /**
269
- * @return string
270
- * @deprecated 11.2
271
- */
272
- private function renderFreeTrial() :string {
273
- $user = Services::WpUsers()->getCurrentWpUser();
274
- return $this->getMod()
275
- ->renderTemplate(
276
- '/forms/drip_trial_signup.twig',
277
- [
278
- 'vars' => [
279
- 'activation_url' => Services::WpGeneral()->getHomeUrl(),
280
- 'email' => $user->user_email,
281
- 'name' => $user->user_firstname,
282
- ],
283
- 'strings' => [
284
- ],
285
- ],
286
- true
287
- );
288
- }
289
-
290
  private function renderTabUpdates() :string {
291
  try {
292
  $changelog = ( new Retrieve() )
265
  );
266
  }
267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  private function renderTabUpdates() :string {
269
  try {
270
  $changelog = ( new Retrieve() )
src/lib/src/Modules/Integrations/Lib/Spam/SpamController.php DELETED
@@ -1,51 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam;
4
-
5
- use FernleafSystems\Utilities\Logic\ExecOnce;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
-
8
- /**
9
- * Class SpamController
10
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\Spam
11
- * @deprecated 11.2
12
- */
13
- class SpamController {
14
-
15
- use ModConsumer;
16
- use ExecOnce;
17
-
18
- protected function canRun() :bool {
19
- return $this->isEnabledSpamDetect();
20
- }
21
-
22
- protected function run() {
23
- foreach ( $this->enumProviders() as $provider ) {
24
- $provider->setMod( $this->getMod() )->execute();
25
- }
26
- }
27
-
28
- private function isEnabledSpamDetect() :bool {
29
- $opts = $this->getOptions();
30
- return ( $opts->isOpt( 'enable_spam_antibot', 'Y' ) || $opts->isOpt( 'enable_spam_human', 'Y' ) )
31
- && !empty( $opts->getOpt( 'form_spam_providers' ) );
32
- }
33
-
34
- /**
35
- * @return Handlers\Base[]
36
- */
37
- private function enumProviders() :array {
38
- return [
39
- new Handlers\ContactForm7(),
40
- new Handlers\ElementorPro(),
41
- new Handlers\FormidableForms(),
42
- new Handlers\FluentForms(),
43
- new Handlers\Forminator(),
44
- new Handlers\GravityForms(),
45
- new Handlers\KaliForms(),
46
- new Handlers\NinjaForms(),
47
- new Handlers\WPForms(),
48
- new Handlers\WpForo(),
49
- ];
50
- }
51
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BaseProvider.php CHANGED
@@ -199,22 +199,6 @@ abstract class BaseProvider {
199
  return [];
200
  }
201
 
202
- /**
203
- * ONLY TO BE HOOKED TO USER PROFILE EDIT
204
- * @param \WP_User $user
205
- * @return string
206
- * @deprecated 11.2
207
- */
208
- public function renderUserEditProfileOptions( \WP_User $user ) {
209
- return '';
210
- }
211
-
212
- /**
213
- * @param \WP_User $user
214
- */
215
- public function handleEditOtherUserProfileSubmit( \WP_User $user ) {
216
- }
217
-
218
  /**
219
  * @param \WP_User $user
220
  */
199
  return [];
200
  }
201
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  /**
203
  * @param \WP_User $user
204
  */
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/GoogleAuth.php CHANGED
@@ -78,21 +78,6 @@ class GoogleAuth extends BaseProvider {
78
  return $url;
79
  }
80
 
81
- /**
82
- * The only thing we can do is REMOVE Google Authenticator from an account that is not our own
83
- * But, only admins can do this. If Security Admin feature is enabled, then only they can do it.
84
- * @inheritDoc
85
- */
86
- public function handleEditOtherUserProfileSubmit( \WP_User $user ) {
87
-
88
- // Can only edit other users if you're admin/security-admin
89
- if ( $this->getCon()->isPluginAdmin() && Services::Request()->post( 'shield_turn_off_ga' ) === 'Y' ) {
90
- $this->processRemovalFromAccount( $user );
91
- $msg = __( 'Google Authenticator was successfully removed from the account.', 'wp-simple-firewall' );
92
- $this->getMod()->setFlashAdminNotice( $msg );
93
- }
94
- }
95
-
96
  public function removeGaOnAccount( \WP_User $user ) :StdResponse {
97
  $this->processRemovalFromAccount( $user );
98
  $response = new StdResponse();
78
  return $url;
79
  }
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  public function removeGaOnAccount( \WP_User $user ) :StdResponse {
82
  $this->processRemovalFromAccount( $user );
83
  $response = new StdResponse();
src/lib/src/Modules/Plugin/Lib/Debug/Collate.php CHANGED
@@ -2,6 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Debug;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Options;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Time\WorldTimeApi;
@@ -208,10 +209,13 @@ class Collate {
208
  }
209
 
210
  $data = [
211
- 'Can Loopback Request' => $loopback,
212
- 'Handshake ShieldNET' => $modPlug->getShieldNetApiController()
213
- ->canHandshake() ? 'Yes' : 'No',
214
- 'WP Hashes Ping' => ( new ApiPing() )->ping() ? 'Yes' : 'No',
 
 
 
215
  ];
216
 
217
  $licPing = new Licenses\Keyless\Ping();
@@ -225,11 +229,11 @@ class Collate {
225
 
226
  private function getShieldSummary() :array {
227
  $con = $this->getCon();
228
- $oModLicense = $con->getModule_License();
229
- $oModPlugin = $con->getModule_Plugin();
230
- $oWpHashes = $oModLicense->getWpHashesTokenManager();
231
 
232
- $nPrevAttempt = $oWpHashes->getPreviousAttemptAt();
233
  if ( empty( $nPrevAttempt ) ) {
234
  $sPrev = 'Never';
235
  }
@@ -240,21 +244,21 @@ class Collate {
240
  ->diffForHumans();
241
  }
242
 
243
- $aD = [
244
  'Version' => $con->getVersion(),
245
  'PRO' => $con->isPremiumActive() ? 'Yes' : 'No',
246
- 'WP Hashes Token' => ( $oWpHashes->hasToken() ? $oWpHashes->getToken() : '' ).' ('.$sPrev.')',
247
  'Security Admin Enabled' => $con->getModule_SecAdmin()
248
  ->getSecurityAdminController()
249
  ->isEnabledSecAdmin() ? 'Yes' : 'No',
250
  ];
251
 
252
  /** @var Options $oOptsIP */
253
- $oOptsPlugin = $oModPlugin->getOptions();
254
- $sSource = $oOptsPlugin->getSelectOptionValueText( 'visitor_address_source' );
255
- $aD[ 'Visitor IP Source' ] = $sSource.' - '.Services::Request()->server( $sSource );
256
 
257
- return $aD;
258
  }
259
 
260
  private function getWordPressSummary() :array {
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Debug;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\NotBot\TestNotBotLoading;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Options;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Time\WorldTimeApi;
209
  }
210
 
211
  $data = [
212
+ 'Can Loopback Request' => $loopback,
213
+ 'NotBot Frontend JS Loading' => ( new TestNotBotLoading() )
214
+ ->setMod( $this->getCon()->getModule_IPs() )
215
+ ->test() ? 'Yes' : 'No',
216
+ 'Handshake ShieldNET' => $modPlug->getShieldNetApiController()
217
+ ->canHandshake() ? 'Yes' : 'No',
218
+ 'WP Hashes Ping' => ( new ApiPing() )->ping() ? 'Yes' : 'No',
219
  ];
220
 
221
  $licPing = new Licenses\Keyless\Ping();
229
 
230
  private function getShieldSummary() :array {
231
  $con = $this->getCon();
232
+ $modLicense = $con->getModule_License();
233
+ $modPlugin = $con->getModule_Plugin();
234
+ $wpHashes = $modLicense->getWpHashesTokenManager();
235
 
236
+ $nPrevAttempt = $wpHashes->getPreviousAttemptAt();
237
  if ( empty( $nPrevAttempt ) ) {
238
  $sPrev = 'Never';
239
  }
244
  ->diffForHumans();
245
  }
246
 
247
+ $data = [
248
  'Version' => $con->getVersion(),
249
  'PRO' => $con->isPremiumActive() ? 'Yes' : 'No',
250
+ 'WP Hashes Token' => ( $wpHashes->hasToken() ? $wpHashes->getToken() : '' ).' ('.$sPrev.')',
251
  'Security Admin Enabled' => $con->getModule_SecAdmin()
252
  ->getSecurityAdminController()
253
  ->isEnabledSecAdmin() ? 'Yes' : 'No',
254
  ];
255
 
256
  /** @var Options $oOptsIP */
257
+ $optsPlugin = $modPlugin->getOptions();
258
+ $source = $optsPlugin->getSelectOptionValueText( 'visitor_address_source' );
259
+ $data[ 'Visitor IP Source' ] = $source.': '.var_export( Services::IP()->getRequestIp(), true );
260
 
261
+ return $data;
262
  }
263
 
264
  private function getWordPressSummary() :array {
src/lib/src/Modules/Plugin/Lib/PluginTelemetry.php CHANGED
@@ -7,6 +7,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events\Select;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\ModCon;
9
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
 
10
  use FernleafSystems\Wordpress\Services\Services;
11
 
12
  class PluginTelemetry extends ExecOnceModConsumer {
@@ -39,39 +40,27 @@ class PluginTelemetry extends ExecOnceModConsumer {
39
  }
40
 
41
  public function runDailyCron() {
42
- $this->sendTrackingData();
43
- }
44
-
45
- private function sendTrackingData() {
46
  /** @var Plugin\Options $opts */
47
  $opts = $this->getOptions();
48
 
49
- $success = false;
50
-
51
- $bCanSend = Services::Request()
52
- ->carbon()
53
- ->subWeek()->timestamp
54
- > (int)$opts->getOpt( 'tracking_last_sent_at', 0 );
55
- if ( $bCanSend && $opts->isTrackingEnabled() ) {
56
-
57
- $data = $this->collectTrackingData();
58
- if ( !empty( $data ) ) {
59
- $opts->setOpt( 'tracking_last_sent_at', Services::Request()->ts() );
60
- $success = Services::HttpRequest()->post(
61
- $opts->getDef( 'tracking_post_url' ),
62
- [
63
- 'timeout' => 20,
64
- 'redirection' => 5,
65
- 'httpversion' => '1.1',
66
- 'blocking' => true,
67
- 'body' => [ 'tracking_data' => $data ],
68
- 'user-agent' => 'SHIELD/'.$this->getCon()->getVersion().';'
69
- ]
70
- );
71
- }
72
  }
 
73
 
74
- return $success;
 
 
 
 
 
 
 
 
75
  }
76
 
77
  /**
@@ -92,10 +81,12 @@ class PluginTelemetry extends ExecOnceModConsumer {
92
  ->getQuerySelector();
93
  $data[ 'events' ][ 'stats' ] = $select->sumAllEvents();
94
  }
 
95
  if ( !empty( $data[ 'login_protect' ] ) ) {
96
  $data[ 'login_protect' ][ 'options' ][ 'email_can_send_verified_at' ] =
97
  $data[ 'login_protect' ][ 'options' ][ 'email_can_send_verified_at' ] > 0 ? 1 : 0;
98
  }
 
99
  if ( !empty( $data[ 'admin_access_restriction' ] ) ) {
100
  $keys = [
101
  'admin_access_restrict_plugins',
@@ -107,6 +98,7 @@ class PluginTelemetry extends ExecOnceModConsumer {
107
  = empty( $data[ 'admin_access_restriction' ][ 'options' ][ $key ] ) ? 0 : 1;
108
  }
109
  }
 
110
  if ( !empty( $data[ 'plugin' ] ) ) {
111
  /** @var Plugin\ModCon $mod */
112
  $mod = $this->getMod();
@@ -148,19 +140,18 @@ class PluginTelemetry extends ExecOnceModConsumer {
148
  $WPP = Services::WpPlugins();
149
  return [
150
  'env' => [
151
- 'options' => [
152
- 'php' => Services::Data()->getPhpVersionCleaned(),
153
- 'wordpress' => $WP->getVersion(),
154
- 'slug' => $this->getCon()->getPluginSlug(),
155
- 'version' => $this->getCon()->getVersion(),
156
- 'is_wpms' => $WP->isMultisite() ? 1 : 0,
157
- 'is_cp' => $WP->isClassicPress() ? 1 : 0,
158
- 'ssl' => is_ssl() ? 1 : 0,
159
- 'locale' => get_locale(),
160
- 'plugins_total' => count( $WPP->getPlugins() ),
161
- 'plugins_active' => count( $WPP->getActivePlugins() ),
162
- 'plugins_updates' => count( $WPP->getUpdates() )
163
- ]
164
  ]
165
  ];
166
  }
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Common\ExecOnceModConsumer;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\ModCon;
9
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
10
+ use FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\Telemetry\SendTelemetry;
11
  use FernleafSystems\Wordpress\Services\Services;
12
 
13
  class PluginTelemetry extends ExecOnceModConsumer {
40
  }
41
 
42
  public function runDailyCron() {
 
 
 
 
43
  /** @var Plugin\Options $opts */
44
  $opts = $this->getOptions();
45
 
46
+ $canSend = $opts->isTrackingEnabled()
47
+ && Services::Request()
48
+ ->carbon()
49
+ ->subWeek()->timestamp > (int)$opts->getOpt( 'tracking_last_sent_at', 0 );
50
+ if ( $canSend ) {
51
+ $this->collectAndSend();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  }
53
+ }
54
 
55
+ public function collectAndSend() {
56
+ $data = $this->collectTrackingData();
57
+ if ( !empty( $data ) ) {
58
+ $this->getOptions()->setOpt( 'tracking_last_sent_at', Services::Request()->ts() );
59
+ $this->getMod()->saveModOptions();
60
+ ( new SendTelemetry() )
61
+ ->setMod( $this->getMod() )
62
+ ->sendData( $data );
63
+ }
64
  }
65
 
66
  /**
81
  ->getQuerySelector();
82
  $data[ 'events' ][ 'stats' ] = $select->sumAllEvents();
83
  }
84
+
85
  if ( !empty( $data[ 'login_protect' ] ) ) {
86
  $data[ 'login_protect' ][ 'options' ][ 'email_can_send_verified_at' ] =
87
  $data[ 'login_protect' ][ 'options' ][ 'email_can_send_verified_at' ] > 0 ? 1 : 0;
88
  }
89
+
90
  if ( !empty( $data[ 'admin_access_restriction' ] ) ) {
91
  $keys = [
92
  'admin_access_restrict_plugins',
98
  = empty( $data[ 'admin_access_restriction' ][ 'options' ][ $key ] ) ? 0 : 1;
99
  }
100
  }
101
+
102
  if ( !empty( $data[ 'plugin' ] ) ) {
103
  /** @var Plugin\ModCon $mod */
104
  $mod = $this->getMod();
140
  $WPP = Services::WpPlugins();
141
  return [
142
  'env' => [
143
+ 'slug' => $this->getCon()->getPluginSlug(),
144
+ 'unique_site_hash' => sha1( network_home_url( '/' ) ),
145
+ 'php' => Services::Data()->getPhpVersionCleaned(),
146
+ 'wordpress' => $WP->getVersion(),
147
+ 'version' => $this->getCon()->getVersion(),
148
+ 'is_wpms' => $WP->isMultisite() ? 1 : 0,
149
+ 'is_cp' => $WP->isClassicPress() ? 1 : 0,
150
+ 'ssl' => is_ssl() ? 1 : 0,
151
+ 'locale' => get_locale(),
152
+ 'plugins_total' => count( $WPP->getPlugins() ),
153
+ 'plugins_active' => count( $WPP->getActivePlugins() ),
154
+ 'plugins_updates' => count( $WPP->getUpdates() )
 
155
  ]
156
  ];
157
  }
src/lib/src/Modules/Reporting/Lib/ReportingController.php CHANGED
@@ -44,7 +44,6 @@ class ReportingController {
44
  }
45
  }
46
  catch ( \Exception $e ) {
47
- var_dump( $e->getMessage() );
48
  }
49
  }
50
 
@@ -57,7 +56,6 @@ class ReportingController {
57
  }
58
  }
59
  catch ( \Exception $e ) {
60
- var_dump( $e->getMessage() );
61
  }
62
  }
63
 
44
  }
45
  }
46
  catch ( \Exception $e ) {
 
47
  }
48
  }
49
 
56
  }
57
  }
58
  catch ( \Exception $e ) {
 
59
  }
60
  }
61
 
src/lib/src/Modules/SecurityAdmin/ModCon.php CHANGED
@@ -80,34 +80,6 @@ class ModCon extends BaseShield\ModCon {
80
  }
81
  }
82
 
83
- /**
84
- * We cater for 3 options:
85
- * Full URL
86
- * Relative path URL: i.e. starts with /
87
- * Or Plugin image URL i.e. doesn't start with HTTP or /
88
- * @param string $key
89
- * @return string
90
- * @deprecated 11.2
91
- */
92
- private function buildWlImageUrl( $key ) {
93
- $opts = $this->getOptions();
94
-
95
- $url = $opts->getOpt( $key );
96
- if ( empty( $url ) ) {
97
- $opts->resetOptToDefault( $key );
98
- $url = $opts->getOpt( $key );
99
- }
100
- if ( !empty( $url ) && !Services::Data()->isValidWebUrl( $url ) && strpos( $url, '/' ) !== 0 ) {
101
- $url = $this->getCon()->urls->forImage( $url );
102
- if ( empty( $url ) ) {
103
- $opts->resetOptToDefault( $key );
104
- $url = $this->getCon()->urls->forImage( $opts->getOpt( $key ) );
105
- }
106
- }
107
-
108
- return $url;
109
- }
110
-
111
  /**
112
  * Used by Wizard. TODO: sort out the wizard requests!
113
  * @param string $pin
@@ -139,11 +111,10 @@ class ModCon extends BaseShield\ModCon {
139
  $opts = $this->getOptions();
140
 
141
  // Restricting Activate Plugins also means restricting the rest.
142
- $pluginsRestrictions = $opts->getAdminAccessArea_Plugins();
143
- if ( in_array( 'activate_plugins', $pluginsRestrictions ) ) {
144
- $opts->setOpt(
145
- 'admin_access_restrict_plugins',
146
- array_unique( array_merge( $pluginsRestrictions, [
147
  'install_plugins',
148
  'update_plugins',
149
  'delete_plugins'
@@ -152,11 +123,10 @@ class ModCon extends BaseShield\ModCon {
152
  }
153
 
154
  // Restricting Switch (Activate) Themes also means restricting the rest.
155
- $themesRestrictions = $opts->getAdminAccessArea_Themes();
156
- if ( in_array( 'switch_themes', $themesRestrictions ) && in_array( 'edit_theme_options', $themesRestrictions ) ) {
157
- $opts->setOpt(
158
- 'admin_access_restrict_themes',
159
- array_unique( array_merge( $themesRestrictions, [
160
  'install_themes',
161
  'update_themes',
162
  'delete_themes'
@@ -164,11 +134,10 @@ class ModCon extends BaseShield\ModCon {
164
  );
165
  }
166
 
167
- $postRestrictions = $opts->getAdminAccessArea_Posts();
168
- if ( in_array( 'edit', $postRestrictions ) ) {
169
- $opts->setOpt(
170
- 'admin_access_restrict_posts',
171
- array_unique( array_merge( $postRestrictions, [ 'create', 'publish', 'delete' ] ) )
172
  );
173
  }
174
  }
80
  }
81
  }
82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  /**
84
  * Used by Wizard. TODO: sort out the wizard requests!
85
  * @param string $pin
111
  $opts = $this->getOptions();
112
 
113
  // Restricting Activate Plugins also means restricting the rest.
114
+ $plugins = $opts->getOpt( 'admin_access_restrict_plugins', [] );
115
+ if ( in_array( 'activate_plugins', is_array( $plugins ) ? $plugins : [] ) ) {
116
+ $opts->setOpt( 'admin_access_restrict_plugins',
117
+ array_unique( array_merge( $plugins, [
 
118
  'install_plugins',
119
  'update_plugins',
120
  'delete_plugins'
123
  }
124
 
125
  // Restricting Switch (Activate) Themes also means restricting the rest.
126
+ $themes = $opts->getOpt( 'admin_access_restrict_themes', [] );
127
+ if ( is_array( $themes ) && in_array( 'switch_themes', $themes ) && in_array( 'edit_theme_options', $themes ) ) {
128
+ $opts->setOpt( 'admin_access_restrict_themes',
129
+ array_unique( array_merge( $themes, [
 
130
  'install_themes',
131
  'update_themes',
132
  'delete_themes'
134
  );
135
  }
136
 
137
+ $posts = $opts->getOpt( 'admin_access_restrict_posts', [] );
138
+ if ( in_array( 'edit', is_array( $posts ) ? $posts : [] ) ) {
139
+ $opts->setOpt( 'admin_access_restrict_posts',
140
+ array_unique( array_merge( $posts, [ 'create', 'publish', 'delete' ] ) )
 
141
  );
142
  }
143
  }
src/lib/src/Modules/SecurityAdmin/Options.php CHANGED
@@ -86,14 +86,6 @@ class Options extends BaseShield\Options {
86
  return strlen( $this->getSecurityPIN() ) == 32;
87
  }
88
 
89
- /**
90
- * @return bool
91
- * @deprecated 11.2
92
- */
93
- public function isEnabledWhitelabel() :bool {
94
- return $this->isOpt( 'whitelabel_enable', 'Y' ) && $this->isPremium();
95
- }
96
-
97
  public function isEmailOverridePermitted() :bool {
98
  return $this->isOpt( 'allow_email_override', 'Y' );
99
  }
@@ -101,20 +93,4 @@ class Options extends BaseShield\Options {
101
  public function isSecAdminRestrictUsersEnabled() :bool {
102
  return $this->isOpt( 'admin_access_restrict_admin_users', 'Y' );
103
  }
104
-
105
- /**
106
- * @return bool
107
- * @deprecated 11.2
108
- */
109
- public function isReplacePluginBadge() :bool {
110
- return $this->isOpt( 'wl_replace_badge_url', 'Y' );
111
- }
112
-
113
- /**
114
- * @return bool
115
- * @deprecated 11.2
116
- */
117
- public function isWlHideUpdates() :bool {
118
- return false;
119
- }
120
  }
86
  return strlen( $this->getSecurityPIN() ) == 32;
87
  }
88
 
 
 
 
 
 
 
 
 
89
  public function isEmailOverridePermitted() :bool {
90
  return $this->isOpt( 'allow_email_override', 'Y' );
91
  }
93
  public function isSecAdminRestrictUsersEnabled() :bool {
94
  return $this->isOpt( 'admin_access_restrict_admin_users', 'Y' );
95
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  }
src/lib/src/Modules/Sessions/Processor.php CHANGED
@@ -97,18 +97,6 @@ class Processor extends BaseShield\Processor {
97
  return $msg;
98
  }
99
 
100
- /**
101
- * @param \WP_User $user
102
- * @deprecated 11.2
103
- */
104
- private function activateUserSession( \WP_User $user ) {
105
- /** @var ModCon $mod */
106
- $mod = $this->getMod();
107
- // If they have a currently active session, terminate it (i.e. we replace it)
108
- $mod->getSessionCon()->terminateCurrentSession();
109
- $mod->getSessionCon()->createSession( $user );
110
- }
111
-
112
  protected function getHookPriority() :int {
113
  return 100;
114
  }
97
  return $msg;
98
  }
99
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  protected function getHookPriority() :int {
101
  return 100;
102
  }
src/lib/src/Modules/UserManagement/Processor.php CHANGED
@@ -71,9 +71,12 @@ class Processor extends BaseShield\Processor {
71
  if ( !$user instanceof \WP_User && !empty( $username ) ) {
72
  $user = Services::WpUsers()->getUserByUsername( $username );
73
  }
74
- $this->setPasswordStartedAt( $user )// used by Password Policies
75
- ->setUserLastLoginTime( $user )
76
- ->sendLoginNotifications( $user );
 
 
 
77
  }
78
 
79
  /**
71
  if ( !$user instanceof \WP_User && !empty( $username ) ) {
72
  $user = Services::WpUsers()->getUserByUsername( $username );
73
  }
74
+ // One might think it should be. It's not always the case it seems...
75
+ if ( $user instanceof \WP_User ) {
76
+ $this->setPasswordStartedAt( $user )// used by Password Policies
77
+ ->setUserLastLoginTime( $user )
78
+ ->sendLoginNotifications( $user );
79
+ }
80
  }
81
 
82
  /**
src/lib/src/ShieldNetApi/Common/BaseShieldNetApi.php CHANGED
@@ -10,10 +10,12 @@ use FernleafSystems\Wordpress\Services\Services;
10
  * Class BaseShieldNetApi
11
  * @package FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\Common
12
  * @property array $shield_net_params
 
13
  */
14
  class BaseShieldNetApi extends BaseApi {
15
 
16
  use ModConsumer;
 
17
  const DEFAULT_URL_STUB = 'https://net.getshieldsecurity.com/wp-json/apto-snapi/v1';
18
 
19
  /**
@@ -40,11 +42,15 @@ class BaseShieldNetApi extends BaseApi {
40
 
41
  case 'shield_net_params':
42
  if ( !is_array( $value ) ) {
43
- $value = $this->getShieldNetApiParams();
44
  $this->shield_net_params = $value;
45
  }
46
  break;
47
 
 
 
 
 
48
  default:
49
  break;
50
  }
@@ -55,7 +61,7 @@ class BaseShieldNetApi extends BaseApi {
55
  /**
56
  * @return string[]
57
  */
58
- protected function getShieldNetApiParams() {
59
  return [
60
  'url' => Services::WpGeneral()->getHomeUrl( '', true ),
61
  'install_id' => $this->getCon()->getSiteInstallationId(),
10
  * Class BaseShieldNetApi
11
  * @package FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\Common
12
  * @property array $shield_net_params
13
+ * @property bool $shield_net_params_required
14
  */
15
  class BaseShieldNetApi extends BaseApi {
16
 
17
  use ModConsumer;
18
+
19
  const DEFAULT_URL_STUB = 'https://net.getshieldsecurity.com/wp-json/apto-snapi/v1';
20
 
21
  /**
42
 
43
  case 'shield_net_params':
44
  if ( !is_array( $value ) ) {
45
+ $value = $this->shield_net_params_required ? $this->getShieldNetApiParams() : [];
46
  $this->shield_net_params = $value;
47
  }
48
  break;
49
 
50
+ case 'shield_net_params_required':
51
+ $value = is_null( $value ) || $value;
52
+ break;
53
+
54
  default:
55
  break;
56
  }
61
  /**
62
  * @return string[]
63
  */
64
+ protected function getShieldNetApiParams() :array {
65
  return [
66
  'url' => Services::WpGeneral()->getHomeUrl( '', true ),
67
  'install_id' => $this->getCon()->getSiteInstallationId(),
src/lib/src/ShieldNetApi/Reputation/BotScoringLogic.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\Reputation;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\Common;
6
+
7
+ class BotScoringLogic extends Common\BaseShieldNetApi {
8
+
9
+ const API_ACTION = 'bot/antibot_scoring_logic';
10
+
11
+ /**
12
+ * @return array|null
13
+ */
14
+ public function retrieve() {
15
+ $raw = $this->sendReq();
16
+ return ( is_array( $raw ) && !empty( $raw[ 'data' ] ) ) ? $raw[ 'data' ] : null;
17
+ }
18
+ }
src/lib/src/ShieldNetApi/Telemetry/SendTelemetry.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\Telemetry;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\Common;
6
+
7
+ class SendTelemetry extends Common\BaseShieldNetApi {
8
+
9
+ const API_ACTION = 'telemetry/receive';
10
+
11
+ public function sendData( array $data ) :bool {
12
+ $this->shield_net_params_required = false;
13
+ $this->request_method = 'post';
14
+ $this->params_body = [
15
+ 'telemetry' => $data,
16
+ ];
17
+ $raw = $this->sendReq();
18
+ return !( !is_array( $raw ) || $raw[ 'error' ] ?? false );
19
+ }
20
+ }
src/lib/src/Tables/Build/AuditTrail.php CHANGED
@@ -20,7 +20,9 @@ class AuditTrail extends BaseBuild {
20
  /** @var Shield\Databases\AuditTrail\Select $selector */
21
  $selector = $this->getWorkingSelector();
22
 
23
- $selector->filterByEvent( $params[ 'fEvent' ] );
 
 
24
 
25
  // If an IP is specified, it takes priority
26
  if ( Services::IP()->isValidIp( $params[ 'fIp' ] ) ) {
20
  /** @var Shield\Databases\AuditTrail\Select $selector */
21
  $selector = $this->getWorkingSelector();
22
 
23
+ if ( !empty( $params[ 'fEvent' ] ) ) {
24
+ $selector->filterByEvent( $params[ 'fEvent' ] );
25
+ }
26
 
27
  // If an IP is specified, it takes priority
28
  if ( Services::IP()->isValidIp( $params[ 'fIp' ] ) ) {
src/lib/vendor/composer/autoload_classmap.php CHANGED
@@ -99,6 +99,22 @@ return array(
99
  'FernleafSystems\\Utilities\\Logic\\ExecOnce' => $vendorDir . '/fernleafsystems/utilities/src/Logic/ExecOnce.php',
100
  'FernleafSystems\\Utilities\\Logic\\OneTimeExecute' => $vendorDir . '/fernleafsystems/utilities/src/Logic/OneTimeExecute.php',
101
  'FernleafSystems\\Utilities\\Response' => $vendorDir . '/fernleafsystems/utilities/src/Response.php',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => $baseDir . '/src/ChangeTrack/Diff/Base.php',
103
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffComments' => $baseDir . '/src/ChangeTrack/Diff/DiffComments.php',
104
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffMedia' => $baseDir . '/src/ChangeTrack/Diff/DiffMedia.php',
@@ -466,10 +482,14 @@ return array(
466
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\BotEventListener' => $baseDir . '/src/Modules/IPs/Lib/Bots/BotEventListener.php',
467
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\BotSignalsController' => $baseDir . '/src/Modules/IPs/Lib/Bots/BotSignalsController.php',
468
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\BotSignalsRecord' => $baseDir . '/src/Modules/IPs/Lib/Bots/BotSignalsRecord.php',
 
469
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\BuildScores' => $baseDir . '/src/Modules/IPs/Lib/Bots/Calculator/BuildScores.php',
 
470
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\CalculateVisitorBotScores' => $baseDir . '/src/Modules/IPs/Lib/Bots/Calculator/CalculateVisitorBotScores.php',
 
471
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\NotBot\\InsertNotBotJs' => $baseDir . '/src/Modules/IPs/Lib/Bots/NotBot/InsertNotBotJs.php',
472
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\NotBot\\NotBotHandler' => $baseDir . '/src/Modules/IPs/Lib/Bots/NotBot/NotBotHandler.php',
 
473
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\IpAnalyse\\BuildDisplay' => $baseDir . '/src/Modules/IPs/Lib/IpAnalyse/BuildDisplay.php',
474
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\IpAnalyse\\FindAllPluginIps' => $baseDir . '/src/Modules/IPs/Lib/IpAnalyse/FindAllPluginIps.php',
475
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\OffenseTracker' => $baseDir . '/src/Modules/IPs/Lib/OffenseTracker.php',
@@ -568,7 +588,6 @@ return array(
568
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\Handlers\\NinjaForms' => $baseDir . '/src/Modules/Integrations/Lib/Spam/Handlers/NinjaForms.php',
569
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\Handlers\\WPForms' => $baseDir . '/src/Modules/Integrations/Lib/Spam/Handlers/WPForms.php',
570
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\Handlers\\WpForo' => $baseDir . '/src/Modules/Integrations/Lib/Spam/Handlers/WpForo.php',
571
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\SpamController' => $baseDir . '/src/Modules/Integrations/Lib/Spam/SpamController.php',
572
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\ModCon' => $baseDir . '/src/Modules/Integrations/ModCon.php',
573
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Options' => $baseDir . '/src/Modules/Integrations/Options.php',
574
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Processor' => $baseDir . '/src/Modules/Integrations/Processor.php',
@@ -730,7 +749,6 @@ return array(
730
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\WpCli\\AdminAdd' => $baseDir . '/src/Modules/SecurityAdmin/WpCli/AdminAdd.php',
731
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\WpCli\\AdminRemove' => $baseDir . '/src/Modules/SecurityAdmin/WpCli/AdminRemove.php',
732
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\WpCli\\Pin' => $baseDir . '/src/Modules/SecurityAdmin/WpCli/Pin.php',
733
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\Lib\\Ops\\Retrieve' => $baseDir . '/src/Modules/Sessions/Lib/Ops/Retrieve.php',
734
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\Lib\\Ops\\Terminate' => $baseDir . '/src/Modules/Sessions/Lib/Ops/Terminate.php',
735
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\Lib\\SessionController' => $baseDir . '/src/Modules/Sessions/Lib/SessionController.php',
736
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\ModCon' => $baseDir . '/src/Modules/Sessions/ModCon.php',
@@ -873,9 +891,11 @@ return array(
873
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\FileLocker\\GetPublicKey' => $baseDir . '/src/ShieldNetApi/FileLocker/GetPublicKey.php',
874
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\Handshake\\Verify' => $baseDir . '/src/ShieldNetApi/Handshake/Verify.php',
875
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\HandshakingNonce' => $baseDir . '/src/ShieldNetApi/HandshakingNonce.php',
 
876
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\ShieldNetApiController' => $baseDir . '/src/ShieldNetApi/ShieldNetApiController.php',
877
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\ShieldNetApiDataVO' => $baseDir . '/src/ShieldNetApi/ShieldNetApiDataVO.php',
878
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\SureSend\\SendEmail' => $baseDir . '/src/ShieldNetApi/SureSend/SendEmail.php',
 
879
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\AdminNotes' => $baseDir . '/src/Tables/Build/AdminNotes.php',
880
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\AuditTrail' => $baseDir . '/src/Tables/Build/AuditTrail.php',
881
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\BaseBuild' => $baseDir . '/src/Tables/Build/BaseBuild.php',
@@ -965,6 +985,7 @@ return array(
965
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpPluginVo' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php',
966
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpThemeVo' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php',
967
  'FernleafSystems\\Wordpress\\Services\\Services' => $vendorDir . '/fernleafsystems/wordpress-services/src/Services.php',
 
968
  'FernleafSystems\\Wordpress\\Services\\Utilities\\BackgroundProcessing\\BackgroundProcess' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/BackgroundProcessing/BackgroundProcess.php',
969
  'FernleafSystems\\Wordpress\\Services\\Utilities\\ClassicPress\\Checksums' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/ClassicPress/Checksums.php',
970
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Consumers\\PluginConsumer' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/Consumers/PluginConsumer.php',
99
  'FernleafSystems\\Utilities\\Logic\\ExecOnce' => $vendorDir . '/fernleafsystems/utilities/src/Logic/ExecOnce.php',
100
  'FernleafSystems\\Utilities\\Logic\\OneTimeExecute' => $vendorDir . '/fernleafsystems/utilities/src/Logic/OneTimeExecute.php',
101
  'FernleafSystems\\Utilities\\Response' => $vendorDir . '/fernleafsystems/utilities/src/Response.php',
102
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\BaseQuery' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/BaseQuery.php',
103
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Delete' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Delete.php',
104
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Handler' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Handler.php',
105
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Insert' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Insert.php',
106
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Record' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Record.php',
107
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Select' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Select.php',
108
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Traits\\Select_IPTable' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Traits/Select_IPTable.php',
109
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Update' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Update.php',
110
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\AlignTableWithSchema' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/AlignTableWithSchema.php',
111
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\BuildColumnFromDef' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/BuildColumnFromDef.php',
112
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\HandlerConsumer' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/HandlerConsumer.php',
113
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\Iterator' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/Iterator.php',
114
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\RecordConsumer' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/RecordConsumer.php',
115
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\SubQueryLoader' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/SubQueryLoader.php',
116
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\TableSchema' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/TableSchema.php',
117
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Exceptions\\NoSlugProvidedException' => $vendorDir . '/fernleafsystems/wordpress-plugin-core/src/Databases/Exceptions/NoSlugProvidedException.php',
118
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => $baseDir . '/src/ChangeTrack/Diff/Base.php',
119
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffComments' => $baseDir . '/src/ChangeTrack/Diff/DiffComments.php',
120
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffMedia' => $baseDir . '/src/ChangeTrack/Diff/DiffMedia.php',
482
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\BotEventListener' => $baseDir . '/src/Modules/IPs/Lib/Bots/BotEventListener.php',
483
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\BotSignalsController' => $baseDir . '/src/Modules/IPs/Lib/Bots/BotSignalsController.php',
484
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\BotSignalsRecord' => $baseDir . '/src/Modules/IPs/Lib/Bots/BotSignalsRecord.php',
485
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\BaseBuildScores' => $baseDir . '/src/Modules/IPs/Lib/Bots/Calculator/BaseBuildScores.php',
486
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\BuildScores' => $baseDir . '/src/Modules/IPs/Lib/Bots/Calculator/BuildScores.php',
487
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\BuildScoresFallback' => $baseDir . '/src/Modules/IPs/Lib/Bots/Calculator/BuildScoresFallback.php',
488
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\CalculateVisitorBotScores' => $baseDir . '/src/Modules/IPs/Lib/Bots/Calculator/CalculateVisitorBotScores.php',
489
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\ScoreLogic' => $baseDir . '/src/Modules/IPs/Lib/Bots/Calculator/ScoreLogic.php',
490
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\NotBot\\InsertNotBotJs' => $baseDir . '/src/Modules/IPs/Lib/Bots/NotBot/InsertNotBotJs.php',
491
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\NotBot\\NotBotHandler' => $baseDir . '/src/Modules/IPs/Lib/Bots/NotBot/NotBotHandler.php',
492
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\NotBot\\TestNotBotLoading' => $baseDir . '/src/Modules/IPs/Lib/Bots/NotBot/TestNotBotLoading.php',
493
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\IpAnalyse\\BuildDisplay' => $baseDir . '/src/Modules/IPs/Lib/IpAnalyse/BuildDisplay.php',
494
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\IpAnalyse\\FindAllPluginIps' => $baseDir . '/src/Modules/IPs/Lib/IpAnalyse/FindAllPluginIps.php',
495
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\OffenseTracker' => $baseDir . '/src/Modules/IPs/Lib/OffenseTracker.php',
588
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\Handlers\\NinjaForms' => $baseDir . '/src/Modules/Integrations/Lib/Spam/Handlers/NinjaForms.php',
589
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\Handlers\\WPForms' => $baseDir . '/src/Modules/Integrations/Lib/Spam/Handlers/WPForms.php',
590
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\Handlers\\WpForo' => $baseDir . '/src/Modules/Integrations/Lib/Spam/Handlers/WpForo.php',
 
591
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\ModCon' => $baseDir . '/src/Modules/Integrations/ModCon.php',
592
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Options' => $baseDir . '/src/Modules/Integrations/Options.php',
593
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Processor' => $baseDir . '/src/Modules/Integrations/Processor.php',
749
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\WpCli\\AdminAdd' => $baseDir . '/src/Modules/SecurityAdmin/WpCli/AdminAdd.php',
750
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\WpCli\\AdminRemove' => $baseDir . '/src/Modules/SecurityAdmin/WpCli/AdminRemove.php',
751
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\WpCli\\Pin' => $baseDir . '/src/Modules/SecurityAdmin/WpCli/Pin.php',
 
752
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\Lib\\Ops\\Terminate' => $baseDir . '/src/Modules/Sessions/Lib/Ops/Terminate.php',
753
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\Lib\\SessionController' => $baseDir . '/src/Modules/Sessions/Lib/SessionController.php',
754
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\ModCon' => $baseDir . '/src/Modules/Sessions/ModCon.php',
891
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\FileLocker\\GetPublicKey' => $baseDir . '/src/ShieldNetApi/FileLocker/GetPublicKey.php',
892
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\Handshake\\Verify' => $baseDir . '/src/ShieldNetApi/Handshake/Verify.php',
893
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\HandshakingNonce' => $baseDir . '/src/ShieldNetApi/HandshakingNonce.php',
894
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\Reputation\\BotScoringLogic' => $baseDir . '/src/ShieldNetApi/Reputation/BotScoringLogic.php',
895
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\ShieldNetApiController' => $baseDir . '/src/ShieldNetApi/ShieldNetApiController.php',
896
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\ShieldNetApiDataVO' => $baseDir . '/src/ShieldNetApi/ShieldNetApiDataVO.php',
897
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\SureSend\\SendEmail' => $baseDir . '/src/ShieldNetApi/SureSend/SendEmail.php',
898
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\Telemetry\\SendTelemetry' => $baseDir . '/src/ShieldNetApi/Telemetry/SendTelemetry.php',
899
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\AdminNotes' => $baseDir . '/src/Tables/Build/AdminNotes.php',
900
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\AuditTrail' => $baseDir . '/src/Tables/Build/AuditTrail.php',
901
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\BaseBuild' => $baseDir . '/src/Tables/Build/BaseBuild.php',
985
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpPluginVo' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php',
986
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpThemeVo' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php',
987
  'FernleafSystems\\Wordpress\\Services\\Services' => $vendorDir . '/fernleafsystems/wordpress-services/src/Services.php',
988
+ 'FernleafSystems\\Wordpress\\Services\\Utilities\\Autoloading\\FindClassFromNamespaceRoots' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/Autoloading/FindClassFromNamespaceRoots.php',
989
  'FernleafSystems\\Wordpress\\Services\\Utilities\\BackgroundProcessing\\BackgroundProcess' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/BackgroundProcessing/BackgroundProcess.php',
990
  'FernleafSystems\\Wordpress\\Services\\Utilities\\ClassicPress\\Checksums' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/ClassicPress/Checksums.php',
991
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Consumers\\PluginConsumer' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/Consumers/PluginConsumer.php',
src/lib/vendor/composer/autoload_files.php CHANGED
@@ -6,8 +6,8 @@ $vendorDir = dirname(dirname(__FILE__));
6
  $baseDir = dirname($vendorDir);
7
 
8
  return array(
9
- '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
10
  '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
 
11
  '626dcc41390ebdaa619faa02d99943b0' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/customFunctions.php',
12
  'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',
13
  'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
6
  $baseDir = dirname($vendorDir);
7
 
8
  return array(
 
9
  '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
10
+ '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
11
  '626dcc41390ebdaa619faa02d99943b0' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/customFunctions.php',
12
  'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',
13
  'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
src/lib/vendor/composer/autoload_psr4.php CHANGED
@@ -24,6 +24,7 @@ return array(
24
  'Html2Text\\' => array($vendorDir . '/soundasleep/html2text/src'),
25
  'FernleafSystems\\Wordpress\\Services\\' => array($vendorDir . '/fernleafsystems/wordpress-services/src'),
26
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\' => array($baseDir . '/src'),
 
27
  'FernleafSystems\\Utilities\\' => array($vendorDir . '/fernleafsystems/utilities/src'),
28
  'Endroid\\QrCode\\' => array($vendorDir . '/endroid/qr-code/src'),
29
  'Dolondro\\GoogleAuthenticator\\' => array($vendorDir . '/dolondro/google-authenticator/src'),
24
  'Html2Text\\' => array($vendorDir . '/soundasleep/html2text/src'),
25
  'FernleafSystems\\Wordpress\\Services\\' => array($vendorDir . '/fernleafsystems/wordpress-services/src'),
26
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\' => array($baseDir . '/src'),
27
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\' => array($vendorDir . '/fernleafsystems/wordpress-plugin-core/src'),
28
  'FernleafSystems\\Utilities\\' => array($vendorDir . '/fernleafsystems/utilities/src'),
29
  'Endroid\\QrCode\\' => array($vendorDir . '/endroid/qr-code/src'),
30
  'Dolondro\\GoogleAuthenticator\\' => array($vendorDir . '/dolondro/google-authenticator/src'),
src/lib/vendor/composer/autoload_static.php CHANGED
@@ -7,8 +7,8 @@ namespace Composer\Autoload;
7
  class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
8
  {
9
  public static $files = array (
10
- '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
11
  '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
 
12
  '626dcc41390ebdaa619faa02d99943b0' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/customFunctions.php',
13
  'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php',
14
  'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
@@ -60,6 +60,7 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
60
  array (
61
  'FernleafSystems\\Wordpress\\Services\\' => 35,
62
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\' => 40,
 
63
  'FernleafSystems\\Utilities\\' => 26,
64
  ),
65
  'E' =>
@@ -149,6 +150,10 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
149
  array (
150
  0 => __DIR__ . '/../..' . '/src',
151
  ),
 
 
 
 
152
  'FernleafSystems\\Utilities\\' =>
153
  array (
154
  0 => __DIR__ . '/..' . '/fernleafsystems/utilities/src',
@@ -301,6 +306,22 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
301
  'FernleafSystems\\Utilities\\Logic\\ExecOnce' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Logic/ExecOnce.php',
302
  'FernleafSystems\\Utilities\\Logic\\OneTimeExecute' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Logic/OneTimeExecute.php',
303
  'FernleafSystems\\Utilities\\Response' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Response.php',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/Base.php',
305
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffComments' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/DiffComments.php',
306
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffMedia' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/DiffMedia.php',
@@ -668,10 +689,14 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
668
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\BotEventListener' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/BotEventListener.php',
669
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\BotSignalsController' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/BotSignalsController.php',
670
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\BotSignalsRecord' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/BotSignalsRecord.php',
 
671
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\BuildScores' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/Calculator/BuildScores.php',
 
672
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\CalculateVisitorBotScores' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/Calculator/CalculateVisitorBotScores.php',
 
673
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\NotBot\\InsertNotBotJs' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/NotBot/InsertNotBotJs.php',
674
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\NotBot\\NotBotHandler' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/NotBot/NotBotHandler.php',
 
675
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\IpAnalyse\\BuildDisplay' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/IpAnalyse/BuildDisplay.php',
676
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\IpAnalyse\\FindAllPluginIps' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/IpAnalyse/FindAllPluginIps.php',
677
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\OffenseTracker' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/OffenseTracker.php',
@@ -770,7 +795,6 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
770
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\Handlers\\NinjaForms' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Spam/Handlers/NinjaForms.php',
771
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\Handlers\\WPForms' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Spam/Handlers/WPForms.php',
772
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\Handlers\\WpForo' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Spam/Handlers/WpForo.php',
773
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\SpamController' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Spam/SpamController.php',
774
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\ModCon' => __DIR__ . '/../..' . '/src/Modules/Integrations/ModCon.php',
775
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Options' => __DIR__ . '/../..' . '/src/Modules/Integrations/Options.php',
776
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Processor' => __DIR__ . '/../..' . '/src/Modules/Integrations/Processor.php',
@@ -932,7 +956,6 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
932
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\WpCli\\AdminAdd' => __DIR__ . '/../..' . '/src/Modules/SecurityAdmin/WpCli/AdminAdd.php',
933
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\WpCli\\AdminRemove' => __DIR__ . '/../..' . '/src/Modules/SecurityAdmin/WpCli/AdminRemove.php',
934
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\WpCli\\Pin' => __DIR__ . '/../..' . '/src/Modules/SecurityAdmin/WpCli/Pin.php',
935
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\Lib\\Ops\\Retrieve' => __DIR__ . '/../..' . '/src/Modules/Sessions/Lib/Ops/Retrieve.php',
936
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\Lib\\Ops\\Terminate' => __DIR__ . '/../..' . '/src/Modules/Sessions/Lib/Ops/Terminate.php',
937
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\Lib\\SessionController' => __DIR__ . '/../..' . '/src/Modules/Sessions/Lib/SessionController.php',
938
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\ModCon' => __DIR__ . '/../..' . '/src/Modules/Sessions/ModCon.php',
@@ -1075,9 +1098,11 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
1075
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\FileLocker\\GetPublicKey' => __DIR__ . '/../..' . '/src/ShieldNetApi/FileLocker/GetPublicKey.php',
1076
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\Handshake\\Verify' => __DIR__ . '/../..' . '/src/ShieldNetApi/Handshake/Verify.php',
1077
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\HandshakingNonce' => __DIR__ . '/../..' . '/src/ShieldNetApi/HandshakingNonce.php',
 
1078
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\ShieldNetApiController' => __DIR__ . '/../..' . '/src/ShieldNetApi/ShieldNetApiController.php',
1079
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\ShieldNetApiDataVO' => __DIR__ . '/../..' . '/src/ShieldNetApi/ShieldNetApiDataVO.php',
1080
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\SureSend\\SendEmail' => __DIR__ . '/../..' . '/src/ShieldNetApi/SureSend/SendEmail.php',
 
1081
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\AdminNotes' => __DIR__ . '/../..' . '/src/Tables/Build/AdminNotes.php',
1082
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\AuditTrail' => __DIR__ . '/../..' . '/src/Tables/Build/AuditTrail.php',
1083
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\BaseBuild' => __DIR__ . '/../..' . '/src/Tables/Build/BaseBuild.php',
@@ -1167,6 +1192,7 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
1167
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpPluginVo' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php',
1168
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpThemeVo' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php',
1169
  'FernleafSystems\\Wordpress\\Services\\Services' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Services.php',
 
1170
  'FernleafSystems\\Wordpress\\Services\\Utilities\\BackgroundProcessing\\BackgroundProcess' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/BackgroundProcessing/BackgroundProcess.php',
1171
  'FernleafSystems\\Wordpress\\Services\\Utilities\\ClassicPress\\Checksums' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/ClassicPress/Checksums.php',
1172
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Consumers\\PluginConsumer' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/Consumers/PluginConsumer.php',
7
  class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
8
  {
9
  public static $files = array (
 
10
  '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
11
+ '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
12
  '626dcc41390ebdaa619faa02d99943b0' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/customFunctions.php',
13
  'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php',
14
  'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
60
  array (
61
  'FernleafSystems\\Wordpress\\Services\\' => 35,
62
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\' => 40,
63
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\' => 38,
64
  'FernleafSystems\\Utilities\\' => 26,
65
  ),
66
  'E' =>
150
  array (
151
  0 => __DIR__ . '/../..' . '/src',
152
  ),
153
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\' =>
154
+ array (
155
+ 0 => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src',
156
+ ),
157
  'FernleafSystems\\Utilities\\' =>
158
  array (
159
  0 => __DIR__ . '/..' . '/fernleafsystems/utilities/src',
306
  'FernleafSystems\\Utilities\\Logic\\ExecOnce' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Logic/ExecOnce.php',
307
  'FernleafSystems\\Utilities\\Logic\\OneTimeExecute' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Logic/OneTimeExecute.php',
308
  'FernleafSystems\\Utilities\\Response' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Response.php',
309
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\BaseQuery' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/BaseQuery.php',
310
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Delete' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Delete.php',
311
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Handler' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Handler.php',
312
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Insert' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Insert.php',
313
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Record' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Record.php',
314
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Select' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Select.php',
315
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Traits\\Select_IPTable' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Traits/Select_IPTable.php',
316
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Base\\Update' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Update.php',
317
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\AlignTableWithSchema' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/AlignTableWithSchema.php',
318
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\BuildColumnFromDef' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/BuildColumnFromDef.php',
319
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\HandlerConsumer' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/HandlerConsumer.php',
320
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\Iterator' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/Iterator.php',
321
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\RecordConsumer' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/RecordConsumer.php',
322
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\SubQueryLoader' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/SubQueryLoader.php',
323
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Common\\TableSchema' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Common/TableSchema.php',
324
+ 'FernleafSystems\\Wordpress\\Plugin\\Core\\Databases\\Exceptions\\NoSlugProvidedException' => __DIR__ . '/..' . '/fernleafsystems/wordpress-plugin-core/src/Databases/Exceptions/NoSlugProvidedException.php',
325
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/Base.php',
326
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffComments' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/DiffComments.php',
327
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffMedia' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/DiffMedia.php',
689
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\BotEventListener' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/BotEventListener.php',
690
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\BotSignalsController' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/BotSignalsController.php',
691
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\BotSignalsRecord' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/BotSignalsRecord.php',
692
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\BaseBuildScores' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/Calculator/BaseBuildScores.php',
693
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\BuildScores' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/Calculator/BuildScores.php',
694
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\BuildScoresFallback' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/Calculator/BuildScoresFallback.php',
695
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\CalculateVisitorBotScores' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/Calculator/CalculateVisitorBotScores.php',
696
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\Calculator\\ScoreLogic' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/Calculator/ScoreLogic.php',
697
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\NotBot\\InsertNotBotJs' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/NotBot/InsertNotBotJs.php',
698
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\NotBot\\NotBotHandler' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/NotBot/NotBotHandler.php',
699
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\Bots\\NotBot\\TestNotBotLoading' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/Bots/NotBot/TestNotBotLoading.php',
700
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\IpAnalyse\\BuildDisplay' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/IpAnalyse/BuildDisplay.php',
701
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\IpAnalyse\\FindAllPluginIps' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/IpAnalyse/FindAllPluginIps.php',
702
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Lib\\OffenseTracker' => __DIR__ . '/../..' . '/src/Modules/IPs/Lib/OffenseTracker.php',
795
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\Handlers\\NinjaForms' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Spam/Handlers/NinjaForms.php',
796
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\Handlers\\WPForms' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Spam/Handlers/WPForms.php',
797
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Lib\\Spam\\Handlers\\WpForo' => __DIR__ . '/../..' . '/src/Modules/Integrations/Lib/Spam/Handlers/WpForo.php',
 
798
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\ModCon' => __DIR__ . '/../..' . '/src/Modules/Integrations/ModCon.php',
799
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Options' => __DIR__ . '/../..' . '/src/Modules/Integrations/Options.php',
800
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Integrations\\Processor' => __DIR__ . '/../..' . '/src/Modules/Integrations/Processor.php',
956
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\WpCli\\AdminAdd' => __DIR__ . '/../..' . '/src/Modules/SecurityAdmin/WpCli/AdminAdd.php',
957
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\WpCli\\AdminRemove' => __DIR__ . '/../..' . '/src/Modules/SecurityAdmin/WpCli/AdminRemove.php',
958
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\WpCli\\Pin' => __DIR__ . '/../..' . '/src/Modules/SecurityAdmin/WpCli/Pin.php',
 
959
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\Lib\\Ops\\Terminate' => __DIR__ . '/../..' . '/src/Modules/Sessions/Lib/Ops/Terminate.php',
960
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\Lib\\SessionController' => __DIR__ . '/../..' . '/src/Modules/Sessions/Lib/SessionController.php',
961
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Sessions\\ModCon' => __DIR__ . '/../..' . '/src/Modules/Sessions/ModCon.php',
1098
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\FileLocker\\GetPublicKey' => __DIR__ . '/../..' . '/src/ShieldNetApi/FileLocker/GetPublicKey.php',
1099
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\Handshake\\Verify' => __DIR__ . '/../..' . '/src/ShieldNetApi/Handshake/Verify.php',
1100
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\HandshakingNonce' => __DIR__ . '/../..' . '/src/ShieldNetApi/HandshakingNonce.php',
1101
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\Reputation\\BotScoringLogic' => __DIR__ . '/../..' . '/src/ShieldNetApi/Reputation/BotScoringLogic.php',
1102
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\ShieldNetApiController' => __DIR__ . '/../..' . '/src/ShieldNetApi/ShieldNetApiController.php',
1103
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\ShieldNetApiDataVO' => __DIR__ . '/../..' . '/src/ShieldNetApi/ShieldNetApiDataVO.php',
1104
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\SureSend\\SendEmail' => __DIR__ . '/../..' . '/src/ShieldNetApi/SureSend/SendEmail.php',
1105
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\ShieldNetApi\\Telemetry\\SendTelemetry' => __DIR__ . '/../..' . '/src/ShieldNetApi/Telemetry/SendTelemetry.php',
1106
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\AdminNotes' => __DIR__ . '/../..' . '/src/Tables/Build/AdminNotes.php',
1107
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\AuditTrail' => __DIR__ . '/../..' . '/src/Tables/Build/AuditTrail.php',
1108
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Tables\\Build\\BaseBuild' => __DIR__ . '/../..' . '/src/Tables/Build/BaseBuild.php',
1192
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpPluginVo' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php',
1193
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpThemeVo' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php',
1194
  'FernleafSystems\\Wordpress\\Services\\Services' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Services.php',
1195
+ 'FernleafSystems\\Wordpress\\Services\\Utilities\\Autoloading\\FindClassFromNamespaceRoots' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/Autoloading/FindClassFromNamespaceRoots.php',
1196
  'FernleafSystems\\Wordpress\\Services\\Utilities\\BackgroundProcessing\\BackgroundProcess' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/BackgroundProcessing/BackgroundProcess.php',
1197
  'FernleafSystems\\Wordpress\\Services\\Utilities\\ClassicPress\\Checksums' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/ClassicPress/Checksums.php',
1198
  'FernleafSystems\\Wordpress\\Services\\Utilities\\Consumers\\PluginConsumer' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/Consumers/PluginConsumer.php',
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/BaseQuery.php ADDED
@@ -0,0 +1,466 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
4
+
5
+ use Carbon\Carbon;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+
8
+ abstract class BaseQuery {
9
+
10
+ /**
11
+ * @var Handler
12
+ */
13
+ protected $dbh;
14
+
15
+ /**
16
+ * @var array
17
+ */
18
+ protected $rawWheres;
19
+
20
+ protected $includeSoftDeleted;
21
+
22
+ /**
23
+ * @var int
24
+ */
25
+ protected $limit = 0;
26
+
27
+ /**
28
+ * @var int
29
+ */
30
+ protected $page = 1;
31
+
32
+ /**
33
+ * @var array
34
+ */
35
+ protected $orderBys;
36
+
37
+ /**
38
+ * @var string
39
+ */
40
+ protected $groupBy;
41
+
42
+ /**
43
+ * @var mixed
44
+ */
45
+ protected $lastQueryResult;
46
+
47
+ public function __construct() {
48
+ $this->customInit();
49
+ }
50
+
51
+ /**
52
+ * override to add custom init actions
53
+ */
54
+ protected function customInit() {
55
+ }
56
+
57
+ /**
58
+ * @param string $column
59
+ * @param string|array $value
60
+ * @param string $operator
61
+ * @return $this
62
+ */
63
+ public function addWhere( $column, $value, $operator = '=' ) {
64
+ if ( !$this->isValidComparisonOperator( $operator ) ) {
65
+ return $this; // Exception?
66
+ }
67
+
68
+ if ( is_array( $value ) ) {
69
+ $value = array_map( 'esc_sql', $value );
70
+ $value = "('".implode( "','", $value )."')";
71
+ }
72
+ else {
73
+ if ( strtoupper( $operator ) === 'LIKE' ) {
74
+ $value = sprintf( '%%%s%%', $value );
75
+ }
76
+ if ( !is_int( $value ) ) {
77
+ $value = sprintf( "'%s'", esc_sql( $value ) );
78
+ }
79
+ }
80
+
81
+ $rawWheres = $this->getRawWheres();
82
+ $rawWheres[] = [
83
+ esc_sql( $column ),
84
+ $operator,
85
+ $value
86
+ ];
87
+
88
+ return $this->setRawWheres( $rawWheres );
89
+ }
90
+
91
+ /**
92
+ * @param string $column
93
+ * @param mixed $mValue
94
+ * @return $this
95
+ */
96
+ public function addWhereEquals( string $column, $mValue ) {
97
+ return $this->addWhere( $column, $mValue, '=' );
98
+ }
99
+
100
+ /**
101
+ * @param string $column
102
+ * @param array $values
103
+ * @return $this
104
+ */
105
+ public function addWhereIn( string $column, $values ) {
106
+ if ( !empty( $values ) && is_array( $values ) ) {
107
+ $this->addWhere( $column, $values, 'IN' );
108
+ }
109
+ return $this;
110
+ }
111
+
112
+ /**
113
+ * @param string $column
114
+ * @param string $like
115
+ * @param string $left
116
+ * @param string $right
117
+ * @return $this
118
+ */
119
+ public function addWhereLike( string $column, $like, $left = '%', $right = '%' ) {
120
+ return $this->addWhere( $column, $left.$like.$right, 'LIKE' );
121
+ }
122
+
123
+ /**
124
+ * @param int $nNewerThanTimeStamp
125
+ * @param string $column
126
+ * @return $this
127
+ */
128
+ public function addWhereNewerThan( $nNewerThanTimeStamp, $column = 'created_at' ) {
129
+ return $this->addWhere( $column, $nNewerThanTimeStamp, '>' );
130
+ }
131
+
132
+ /**
133
+ * @param int $nOlderThanTimeStamp
134
+ * @param string $column
135
+ * @return $this
136
+ */
137
+ public function addWhereOlderThan( $nOlderThanTimeStamp, $column = 'created_at' ) {
138
+ return $this->addWhere( $column, $nOlderThanTimeStamp, '<' );
139
+ }
140
+
141
+ /**
142
+ * @param string $sColumn
143
+ * @param mixed $mValue
144
+ * @return $this
145
+ */
146
+ public function addWhereSearch( $sColumn, $mValue ) {
147
+ return $this->addWhere( $sColumn, $mValue, 'LIKE' );
148
+ }
149
+
150
+ /**
151
+ * @return string
152
+ */
153
+ public function buildExtras() {
154
+ $aExtras = array_filter(
155
+ [
156
+ $this->getGroupBy(),
157
+ $this->buildOrderBy(),
158
+ $this->buildLimitPhrase(),
159
+ $this->buildOffsetPhrase(),
160
+ ]
161
+ );
162
+ return implode( "\n", $aExtras );
163
+ }
164
+
165
+ /**
166
+ * @return string
167
+ */
168
+ public function buildLimitPhrase() {
169
+ return $this->hasLimit() ? sprintf( 'LIMIT %s', $this->getLimit() ) : '';
170
+ }
171
+
172
+ /**
173
+ * @return string
174
+ */
175
+ protected function buildOffsetPhrase() {
176
+ return $this->hasLimit() ? sprintf( 'OFFSET %s', $this->getOffset() ) : '';
177
+ }
178
+
179
+ /**
180
+ * @return $this
181
+ */
182
+ public function clearWheres() {
183
+ return $this->setRawWheres( [] );
184
+ }
185
+
186
+ /**
187
+ * @return int
188
+ */
189
+ protected function getOffset() {
190
+ return (int)$this->getLimit()*( $this->getPage() - 1 );
191
+ }
192
+
193
+ /**
194
+ * @return string
195
+ */
196
+ public function buildWhere() {
197
+ $wheres = $this->getRawWheres();
198
+ if ( !$this->isIncludeSoftDeletedRows() ) {
199
+ $wheres[] = [ 'deleted_at', '=', 0 ];
200
+ }
201
+ $wheres = array_map( function ( array $where ) {
202
+ return $this->rawWhereToString( $where );
203
+ }, $wheres );
204
+ return implode( ' AND ', $wheres );
205
+ }
206
+
207
+ public function buildQuery() :string {
208
+ return sprintf( $this->getBaseQuery(),
209
+ $this->getDbH()->getTable(),
210
+ $this->buildWhere(),
211
+ $this->buildExtras()
212
+ );
213
+ }
214
+
215
+ /**
216
+ * @param int $ts
217
+ * @param string $comparisonOp
218
+ * @return $this
219
+ */
220
+ public function filterByCreatedAt( $ts, $comparisonOp ) {
221
+ if ( !preg_match( '#[^=<>]#', $comparisonOp ) && is_numeric( $ts ) ) {
222
+ $this->addWhere( 'created_at', (int)$ts, $comparisonOp );
223
+ }
224
+ return $this;
225
+ }
226
+
227
+ /**
228
+ * @param int $startTS
229
+ * @param int $endTS
230
+ * @return $this
231
+ */
232
+ public function filterByBoundary( $startTS, $endTS ) {
233
+ return $this->filterByCreatedAt( $endTS, '<=' )
234
+ ->filterByCreatedAt( $startTS, '>=' );
235
+ }
236
+
237
+ /**
238
+ * @param int $ts
239
+ * @return $this
240
+ */
241
+ public function filterByBoundary_Day( $ts ) {
242
+ $carbon = ( new Carbon() )->setTimestamp( $ts );
243
+ return $this->filterByBoundary( $carbon->startOfDay()->timestamp, $carbon->endOfDay()->timestamp );
244
+ }
245
+
246
+ /**
247
+ * @param int $ts
248
+ * @return $this
249
+ */
250
+ public function filterByBoundary_Hour( $ts ) {
251
+ $carbon = ( new Carbon() )->setTimestamp( $ts );
252
+ return $this->filterByBoundary( $carbon->startOfHour()->timestamp, $carbon->endOfHour()->timestamp );
253
+ }
254
+
255
+ /**
256
+ * @param int $ts
257
+ * @return $this
258
+ */
259
+ public function filterByBoundary_Month( $ts ) {
260
+ $carbon = ( new Carbon() )->setTimestamp( $ts );
261
+ return $this->filterByBoundary( $carbon->startOfMonth()->timestamp, $carbon->endOfMonth()->timestamp );
262
+ }
263
+
264
+ /**
265
+ * @param int $ts
266
+ * @return $this
267
+ */
268
+ public function filterByBoundary_Week( $ts ) {
269
+ $carbon = ( new Carbon() )->setTimestamp( $ts );
270
+ return $this->filterByBoundary( $carbon->startOfWeek()->timestamp, $carbon->endOfWeek()->timestamp );
271
+ }
272
+
273
+ /**
274
+ * @param int $ts
275
+ * @return $this
276
+ */
277
+ public function filterByBoundary_Year( $ts ) {
278
+ $carbon = ( new Carbon() )->setTimestamp( $ts );
279
+ return $this->filterByBoundary( $carbon->startOfYear()->timestamp, $carbon->endOfYear()->timestamp );
280
+ }
281
+
282
+ protected function getBaseQuery() :string {
283
+ return "SELECT * FROM `%s` WHERE %s %s";
284
+ }
285
+
286
+ /**
287
+ * @return Handler
288
+ */
289
+ public function getDbH() {
290
+ return $this->dbh;
291
+ }
292
+
293
+ /**
294
+ * @param Handler $dbh
295
+ * @return $this
296
+ */
297
+ public function setDbH( $dbh ) {
298
+ $this->dbh = $dbh;
299
+ return $this;
300
+ }
301
+
302
+ public function query() :bool {
303
+ $this->lastQueryResult = Services::WpDb()->doSql( $this->buildQuery() );
304
+ return ( $this->lastQueryResult === false ) ? false : $this->lastQueryResult > 0;
305
+ }
306
+
307
+ /**
308
+ * @return array[]|int|string[]|Record[]|mixed|null
309
+ */
310
+ public function queryWithResult() {
311
+ return $this->query() ? $this->getLastQueryResult() : null;
312
+ }
313
+
314
+ /**
315
+ * @return array[]|int|string[]|Record[]|mixed
316
+ */
317
+ public function getLastQueryResult() {
318
+ return $this->lastQueryResult;
319
+ }
320
+
321
+ public function getLimit() :int {
322
+ return (int)max( $this->limit, 0 );
323
+ }
324
+
325
+ public function getRawWheres() :array {
326
+ return is_array( $this->rawWheres ) ? $this->rawWheres : [];
327
+ }
328
+
329
+ public function getGroupBy() :string {
330
+ return empty( $this->groupBy ) ? '' : sprintf( 'GROUP BY `%s`', $this->groupBy );
331
+ }
332
+
333
+ protected function buildOrderBy() :string {
334
+ $order = '';
335
+ if ( !is_array( $this->orderBys ) ) {
336
+ // Defaults to created_at if aOrderBys is untouched. Set to empty array for no order
337
+ $this->orderBys = [ 'created_at' => 'DESC' ];
338
+ }
339
+ if ( !empty( $this->orderBys ) ) {
340
+ $orders = [];
341
+ foreach ( $this->orderBys as $col => $order ) {
342
+ $orders[] = sprintf( '`%s` %s', esc_sql( $col ), esc_sql( $order ) );
343
+ }
344
+ $order = sprintf( 'ORDER BY %s', implode( ', ', $orders ) );
345
+ }
346
+ return $order;
347
+ }
348
+
349
+ public function getPage() :int {
350
+ return (int)max( $this->page, 1 );
351
+ }
352
+
353
+ public function hasLimit() :bool {
354
+ return $this->getLimit() > 0;
355
+ }
356
+
357
+ public function isIncludeSoftDeletedRows() :bool {
358
+ return $this->includeSoftDeleted ?? false;
359
+ }
360
+
361
+ protected function rawWhereToString( array $rawWhere ) :string {
362
+ return vsprintf( '`%s` %s %s', $rawWhere );
363
+ }
364
+
365
+ /**
366
+ * @return $this
367
+ */
368
+ public function reset() {
369
+ return $this->setLimit( 0 )
370
+ ->setRawWheres( [] )
371
+ ->setPage( 1 )
372
+ ->setOrderBy( '' );
373
+ }
374
+
375
+ /**
376
+ * @param bool $includeSoftDeleted
377
+ * @return $this
378
+ */
379
+ public function setIncludeSoftDeleted( bool $includeSoftDeleted ) {
380
+ $this->includeSoftDeleted = $includeSoftDeleted;
381
+ return $this;
382
+ }
383
+
384
+ /**
385
+ * @param int $limit
386
+ * @return $this
387
+ */
388
+ public function setLimit( int $limit ) {
389
+ $this->limit = $limit;
390
+ return $this;
391
+ }
392
+
393
+ /**
394
+ * @param string $groupByColumn
395
+ * @return $this
396
+ */
397
+ public function setGroupBy( $groupByColumn ) {
398
+ if ( empty( $groupByColumn ) ) {
399
+ $this->groupBy = '';
400
+ }
401
+ elseif ( $this->getDbH()->getTableSchema()->hasColumn( $groupByColumn ) ) {
402
+ $this->groupBy = $groupByColumn;
403
+ }
404
+ return $this;
405
+ }
406
+
407
+ /**
408
+ * @param string $orderByColumn
409
+ * @param string $order
410
+ * @param bool $replace
411
+ * @return $this
412
+ */
413
+ public function setOrderBy( string $orderByColumn, $order = 'DESC', $replace = false ) {
414
+ if ( empty( $orderByColumn ) ) {
415
+ $this->orderBys = $orderByColumn;
416
+ }
417
+ else {
418
+ if ( !is_array( $this->orderBys ) || $replace ) {
419
+ $this->orderBys = [];
420
+ }
421
+ $this->orderBys[ $orderByColumn ] = $order;
422
+ }
423
+ return $this;
424
+ }
425
+
426
+ /**
427
+ * @param int $nPage
428
+ * @return $this
429
+ */
430
+ public function setPage( $nPage ) {
431
+ $this->page = $nPage;
432
+ return $this;
433
+ }
434
+
435
+ /**
436
+ * @param array[] $wheres
437
+ * @return $this
438
+ */
439
+ public function setRawWheres( array $wheres ) {
440
+ $this->rawWheres = $wheres;
441
+ return $this;
442
+ }
443
+
444
+ /**
445
+ * @param Record $VO
446
+ * @return $this
447
+ */
448
+ public function setWheresFromVo( $VO ) {
449
+ foreach ( $VO->getRawData() as $col => $mVal ) {
450
+ $this->addWhereEquals( $col, $mVal );
451
+ }
452
+ return $this;
453
+ }
454
+
455
+ /**
456
+ * Very basic
457
+ * @param string $op
458
+ * @return bool
459
+ */
460
+ protected function isValidComparisonOperator( $op ) {
461
+ return in_array(
462
+ strtoupper( $op ),
463
+ [ '=', '<', '>', '!=', '<>', '<=', '>=', '<=>', 'IN', 'LIKE', 'NOT LIKE' ]
464
+ );
465
+ }
466
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Delete.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
4
+
5
+ use FernleafSystems\Wordpress\Services\Services;
6
+
7
+ class Delete extends BaseQuery {
8
+
9
+ private $isSoftDelete = false;
10
+
11
+ public function query() :bool {
12
+ if ( $this->isSoftDelete && $this->getDbH()->getTableSchema()->hasColumn( 'deleted_at' ) ) {
13
+
14
+ $updateWheres = [];
15
+ foreach ( $this->getRawWheres() as $where ) {
16
+ $updateWheres[ $where[ 0 ] ] = $where[ 2 ];
17
+ }
18
+
19
+ $updater = $this->getDbH()
20
+ ->getQueryUpdater()
21
+ ->setUpdateWheres( $updateWheres )
22
+ ->setUpdateData( [ 'deleted_at' => Services::Request()->ts(), ] );
23
+ $success = $updater->query();
24
+ $this->lastQueryResult = $updater->getLastQueryResult();
25
+ }
26
+ else {
27
+ $success = parent::query();
28
+ }
29
+ return $success;
30
+ }
31
+
32
+ /**
33
+ * @return bool
34
+ */
35
+ public function all() {
36
+ return $this->query();
37
+ }
38
+
39
+ /**
40
+ * @param int $id
41
+ * @return bool
42
+ */
43
+ public function deleteById( $id ) {
44
+ return $this->reset()
45
+ ->addWhereEquals( 'id', (int)$id )
46
+ ->query();
47
+ }
48
+
49
+ /**
50
+ * @param Record $record
51
+ * @return bool
52
+ * @deprecated
53
+ */
54
+ public function deleteEntry( $record ) {
55
+ return $this->deleteRecord( $record );
56
+ }
57
+
58
+ /**
59
+ * @param Record $record
60
+ * @return bool
61
+ */
62
+ public function deleteRecord( $record ) {
63
+ return $this->deleteById( $record->id );
64
+ }
65
+
66
+ /**
67
+ * NOTE: Does not reset() before query, so may be customized with where.
68
+ * @param int $maxEntries
69
+ * @param string $orderByColumn
70
+ * @param bool $bOldestFirst
71
+ * @return int
72
+ * @throws \Exception
73
+ */
74
+ public function deleteExcess( $maxEntries, $orderByColumn = 'created_at', $bOldestFirst = true ) {
75
+ if ( is_null( $maxEntries ) ) {
76
+ throw new \Exception( 'Max Entries not specified for table excess delete.' );
77
+ }
78
+
79
+ $nEntriesDeleted = 0;
80
+
81
+ // The same WHEREs should apply
82
+ $nTotal = $this->getDbH()
83
+ ->getQuerySelector()
84
+ ->setRawWheres( $this->getRawWheres() )
85
+ ->count();
86
+ $toDelete = $nTotal - $maxEntries;
87
+
88
+ if ( $toDelete > 0 ) {
89
+ $nEntriesDeleted = $this->setOrderBy( $orderByColumn, $bOldestFirst ? 'ASC' : 'DESC' )
90
+ ->setLimit( $toDelete )
91
+ ->query();
92
+ }
93
+
94
+ return $nEntriesDeleted;
95
+ }
96
+
97
+ protected function getBaseQuery() :string {
98
+ return "DELETE FROM `%s` WHERE %s %s";
99
+ }
100
+
101
+ /**
102
+ * Offset never applies to DELETE
103
+ * @return string
104
+ */
105
+ protected function buildOffsetPhrase() {
106
+ return '';
107
+ }
108
+
109
+ public function setIsHardDelete() {
110
+ $this->isSoftDelete = false;
111
+ return $this;
112
+ }
113
+
114
+ public function setIsSoftDelete() {
115
+ $this->isSoftDelete = true;
116
+ return $this;
117
+ }
118
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Handler.php ADDED
@@ -0,0 +1,249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
4
+
5
+ use FernleafSystems\Utilities\Logic\ExecOnce;
6
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Common\{
7
+ AlignTableWithSchema,
8
+ Iterator,
9
+ SubQueryLoader,
10
+ TableSchema
11
+ };
12
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Exceptions\{
13
+ NoSlugProvidedException
14
+ };
15
+ use FernleafSystems\Wordpress\Services\Services;
16
+
17
+ abstract class Handler {
18
+
19
+ use ExecOnce;
20
+
21
+ /**
22
+ * @var bool
23
+ */
24
+ private $isReady;
25
+
26
+ /**
27
+ * @var array
28
+ */
29
+ protected $tableDefinition;
30
+
31
+ /**
32
+ * @var TableSchema
33
+ */
34
+ protected $schema;
35
+
36
+ public function __construct( array $tableDefinition ) {
37
+ if ( empty( $tableDefinition[ 'slug' ] ) ) {
38
+ throw new NoSlugProvidedException( 'Slug not provided in Table Definition' );
39
+ }
40
+ $this->tableDefinition = $tableDefinition;
41
+ }
42
+
43
+ /**
44
+ * @throws \Exception
45
+ */
46
+ protected function run() {
47
+ $this->tableInit();
48
+ }
49
+
50
+ /**
51
+ * @return $this
52
+ * @throws \Exception
53
+ */
54
+ public function tableInit() {
55
+
56
+ $this->setupTableSchema();
57
+
58
+ if ( !$this->isReady() ) {
59
+
60
+ $this->tableCreate();
61
+
62
+ if ( !$this->isReady( true ) ) {
63
+ $this->tableDelete();
64
+ $this->tableCreate();
65
+ }
66
+ }
67
+
68
+ return $this;
69
+ }
70
+
71
+ private function setupTableSchema() :TableSchema {
72
+ $this->schema = new TableSchema();
73
+
74
+ $this->schema->applyFromArray( array_merge(
75
+ [
76
+ 'slug' => '',
77
+ 'table_prefix' => '',
78
+ 'primary_key' => 'id',
79
+ 'cols_custom' => [],
80
+ 'cols_timestamps' => [],
81
+ 'has_updated_at' => false,
82
+ 'col_older_than' => 'created_at',
83
+ 'autoexpire' => 0,
84
+ 'has_ip_col' => false,
85
+ ],
86
+ $this->tableDefinition
87
+ ) );
88
+
89
+ $this->schema->table = $this->getTable();
90
+
91
+ return $this->schema;
92
+ }
93
+
94
+ public function autoCleanDb() {
95
+ }
96
+
97
+ public function tableCleanExpired( int $autoExpireDays ) {
98
+ if ( $autoExpireDays > 0 ) {
99
+ $this->deleteRowsOlderThan( Services::Request()->ts() - $autoExpireDays*DAY_IN_SECONDS );
100
+ }
101
+ }
102
+
103
+ /**
104
+ * @param int $timestamp
105
+ * @return bool
106
+ */
107
+ public function deleteRowsOlderThan( int $timestamp ) :bool {
108
+ return $this->isReady() &&
109
+ $this->getQueryDeleter()
110
+ ->addWhereOlderThan( $timestamp, $this->getTableSchema()->col_older_than )
111
+ ->query();
112
+ }
113
+
114
+ public function getTable() :string {
115
+ return $this->getTableSchema()->table;
116
+ }
117
+
118
+ /**
119
+ * @return Iterator
120
+ */
121
+ public function getIterator() {
122
+ $o = new Iterator();
123
+ return $o->setDbHandler( $this );
124
+ }
125
+
126
+ /**
127
+ * @return Delete|mixed
128
+ */
129
+ public function getQueryDeleter() {
130
+ return ( new SubQueryLoader() )
131
+ ->setDbHandler( $this )
132
+ ->delete();
133
+ }
134
+
135
+ /**
136
+ * @return Insert|mixed
137
+ */
138
+ public function getQueryInserter() {
139
+ return ( new SubQueryLoader() )
140
+ ->setDbHandler( $this )
141
+ ->insert();
142
+ }
143
+
144
+ /**
145
+ * @return Select|mixed
146
+ */
147
+ public function getQuerySelector() {
148
+ return ( new SubQueryLoader() )
149
+ ->setDbHandler( $this )
150
+ ->select()
151
+ ->setResultsAsVo( true );
152
+ }
153
+
154
+ /**
155
+ * @return Update|mixed
156
+ */
157
+ public function getQueryUpdater() {
158
+ return ( new SubQueryLoader() )
159
+ ->setDbHandler( $this )
160
+ ->update();
161
+ }
162
+
163
+ /**
164
+ * @return Record|mixed
165
+ */
166
+ public function getRecord() {
167
+ return ( new SubQueryLoader() )
168
+ ->setDbHandler( $this )
169
+ ->record();
170
+ }
171
+
172
+ /**
173
+ * @return Record|mixed
174
+ * @deprecated
175
+ */
176
+ public function getVo() {
177
+ return $this->getRecord();
178
+ }
179
+
180
+ /**
181
+ * @return $this
182
+ * @throws \Exception
183
+ */
184
+ protected function tableCreate() {
185
+ $DB = Services::WpDb();
186
+ $sch = $this->getTableSchema();
187
+ if ( !$DB->getIfTableExists( $sch->table ) ) {
188
+ $DB->doSql( $sch->buildCreate() );
189
+ }
190
+ return $this;
191
+ }
192
+
193
+ public function tableDelete( bool $truncate = false ) :bool {
194
+ $table = $this->getTable();
195
+ $DB = Services::WpDb();
196
+ $mResult = !$this->tableExists() ||
197
+ ( $truncate ? $DB->doTruncateTable( $table ) : $DB->doDropTable( $table ) );
198
+ $this->reset();
199
+ return $mResult !== false;
200
+ }
201
+
202
+ public function tableExists() :bool {
203
+ return Services::WpDb()->getIfTableExists( $this->getTable() );
204
+ }
205
+
206
+ public function tableTrimExcess( int $rowsLimit ) :self {
207
+ try {
208
+ $this->getQueryDeleter()->deleteExcess( $rowsLimit );
209
+ }
210
+ catch ( \Exception $e ) {
211
+ }
212
+ return $this;
213
+ }
214
+
215
+ public function isReady( bool $reTest = false ) :bool {
216
+ if ( $reTest ) {
217
+ $this->reset();
218
+ }
219
+
220
+ if ( !isset( $this->isReady ) ) {
221
+ try {
222
+ $align = new AlignTableWithSchema( $this->getTableSchema() );
223
+ $align->align();
224
+ $this->isReady = $this->tableExists() && $align->isAligned();
225
+ }
226
+ catch ( \Exception $e ) {
227
+ $this->isReady = false;
228
+ }
229
+ }
230
+
231
+ return $this->isReady;
232
+ }
233
+
234
+ public function getTableSchema() :TableSchema {
235
+ return $this->schema;
236
+ }
237
+
238
+ /**
239
+ * @return $this
240
+ */
241
+ private function reset() {
242
+ unset( $this->isReady );
243
+ return $this;
244
+ }
245
+
246
+ public function getBaseNamespaces() :array {
247
+ return [ __NAMESPACE__ ];
248
+ }
249
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Insert.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
4
+
5
+ use FernleafSystems\Wordpress\Services\Services;
6
+
7
+ class Insert extends BaseQuery {
8
+
9
+ /**
10
+ * @var array
11
+ */
12
+ protected $insertData;
13
+
14
+ public function getInsertData() :array {
15
+ return array_intersect_key(
16
+ is_array( $this->insertData ) ? $this->insertData : [],
17
+ array_flip( $this->getDbH()->getTableSchema()->getColumnNames() )
18
+ );
19
+ }
20
+
21
+ /**
22
+ * @param Record $record
23
+ * @return bool
24
+ */
25
+ public function insert( $record ) :bool {
26
+ return $this->setInsertData( $record->getRawData() )->query() === 1;
27
+ }
28
+
29
+ /**
30
+ * Verifies insert data keys against actual table columns
31
+ * @param array $data
32
+ * @return $this
33
+ */
34
+ protected function setInsertData( $data ) {
35
+ $this->insertData = array_intersect_key(
36
+ is_array( $data ) ? $data : [],
37
+ array_flip( $this->getDbH()->getTableSchema()->getColumnNames() )
38
+ );
39
+ return $this;
40
+ }
41
+
42
+ /**
43
+ * @return $this
44
+ * @throws \Exception
45
+ */
46
+ protected function verifyInsertData() {
47
+ $baseData = [ 'created_at' => Services::Request()->ts() ];
48
+ if ( $this->getDbH()->getTableSchema()->hasColumn( 'updated_at' ) ) {
49
+ $baseData[ 'updated_at' ] = Services::Request()->ts();
50
+ }
51
+ return $this->setInsertData( array_merge( $baseData, $this->getInsertData() ) );
52
+ }
53
+
54
+ public function query() :bool {
55
+ try {
56
+ $this->verifyInsertData();
57
+ $this->lastQueryResult = Services::WpDb()
58
+ ->insertDataIntoTable(
59
+ $this->getDbH()->getTable(),
60
+ $this->getInsertData()
61
+ );
62
+ $success = (bool)$this->lastQueryResult;
63
+ }
64
+ catch ( \Exception $e ) {
65
+ $success = false;
66
+ }
67
+ return $success;
68
+ }
69
+
70
+ /**
71
+ * Offset never applies
72
+ *
73
+ * @return string
74
+ */
75
+ protected function buildOffsetPhrase() {
76
+ return '';
77
+ }
78
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Record.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
4
+
5
+ use FernleafSystems\Utilities\Data\Adapter\DynPropertiesClass;
6
+
7
+ /**
8
+ * @property int $id
9
+ * @property array $meta
10
+ * @property int $created_at
11
+ * @property int $deleted_at
12
+ */
13
+ class Record extends DynPropertiesClass {
14
+
15
+ public function __construct( array $row = [] ) {
16
+ $this->applyFromArray( $row );
17
+ }
18
+
19
+ /**
20
+ * @param string $key
21
+ * @return mixed
22
+ */
23
+ public function __get( string $key ) {
24
+
25
+ $value = parent::__get( $key );
26
+
27
+ switch ( $key ) {
28
+
29
+ case 'meta':
30
+ if ( is_string( $value ) && !empty( $value ) ) {
31
+ $value = base64_decode( $value );
32
+ if ( !empty( $value ) ) {
33
+ $value = @json_decode( $value, true );
34
+ }
35
+ }
36
+
37
+ if ( !is_array( $value ) ) {
38
+ $value = [];
39
+ }
40
+ break;
41
+
42
+ default:
43
+ break;
44
+ }
45
+
46
+ if ( $key === 'id' || preg_match( '#^.*_at$#i', $key ) ) {
47
+ $value = (int)$value;
48
+ }
49
+
50
+ return $value;
51
+ }
52
+
53
+ /**
54
+ * @param string $key
55
+ * @param mixed $value
56
+ */
57
+ public function __set( string $key, $value ) {
58
+
59
+ switch ( $key ) {
60
+
61
+ case 'meta':
62
+ if ( !is_array( $value ) ) {
63
+ $value = [];
64
+ }
65
+ $value = base64_encode( json_encode( $value ) );
66
+ break;
67
+
68
+ default:
69
+ break;
70
+ }
71
+
72
+ parent::__set( $key, $value );
73
+ }
74
+
75
+ public function getCreatedAt() :int {
76
+ return (int)$this->created_at;
77
+ }
78
+
79
+ public function getHash() :string {
80
+ $data = $this->getRawData();
81
+ asort( $data );
82
+ return md5( serialize( $data ) );
83
+ }
84
+
85
+ public function isDeleted() :bool {
86
+ return $this->deleted_at > 0;
87
+ }
88
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Select.php ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
4
+
5
+ use FernleafSystems\Wordpress\Services\Services;
6
+
7
+ class Select extends BaseQuery {
8
+
9
+ /**
10
+ * @var array
11
+ */
12
+ protected $columnsToSelect;
13
+
14
+ protected $isCount = false;
15
+
16
+ protected $isSum = false;
17
+
18
+ protected $isDistinct = false;
19
+
20
+ protected $resultsAsVO;
21
+
22
+ /**
23
+ * @var string
24
+ */
25
+ protected $customSelect;
26
+
27
+ /**
28
+ * @var string
29
+ */
30
+ protected $resultFormat;
31
+
32
+ /**
33
+ * @param string $col
34
+ * @return $this
35
+ */
36
+ public function addColumnToSelect( $col ) {
37
+ $aCols = $this->getColumnsToSelect();
38
+ $aCols[] = $col;
39
+ return $this->setColumnsToSelect( $aCols );
40
+ }
41
+
42
+ /**
43
+ * @return array[]|int|string[]|Record[]|mixed
44
+ */
45
+ public function all() {
46
+ return $this->reset()->queryWithResult();
47
+ }
48
+
49
+ /**
50
+ * @param int $ID
51
+ * @return \stdClass
52
+ */
53
+ public function byId( $ID ) {
54
+ $items = $this->reset()
55
+ ->addWhereEquals( 'id', $ID )
56
+ ->queryWithResult();
57
+ return array_shift( $items );
58
+ }
59
+
60
+ public function buildQuery() :string {
61
+ return sprintf( $this->getBaseQuery(),
62
+ $this->buildSelect(),
63
+ $this->getDbH()->getTable(),
64
+ $this->buildWhere(),
65
+ $this->buildExtras()
66
+ );
67
+ }
68
+
69
+ protected function buildSelect() :string {
70
+ $cols = $this->getColumnsToSelect();
71
+
72
+ if ( $this->isCount() ) {
73
+ $substitute = 'COUNT(*)';
74
+ }
75
+ elseif ( $this->isSum() ) {
76
+ $substitute = sprintf( 'SUM(%s)', array_shift( $cols ) );
77
+ }
78
+ elseif ( $this->isDistinct() && $this->hasColumnsToSelect() ) {
79
+ $substitute = sprintf( 'DISTINCT %s', implode( ',', $cols ) );
80
+ }
81
+ elseif ( $this->hasColumnsToSelect() ) {
82
+ $substitute = implode( ',', $cols );
83
+ }
84
+ elseif ( $this->isCustomSelect() ) {
85
+ $substitute = $this->customSelect;
86
+ }
87
+ else {
88
+ $substitute = '*';
89
+ }
90
+ return $substitute;
91
+ }
92
+
93
+ public function sumColumn() :int {
94
+ return (int)$this->setIsCount( true )->queryWithResult();
95
+ }
96
+
97
+ public function count() :int {
98
+ return (int)$this->setIsCount( true )->queryWithResult();
99
+ }
100
+
101
+ /**
102
+ * @return int
103
+ */
104
+ public function sum() {
105
+ return $this->setIsSum( true )->queryWithResult();
106
+ }
107
+
108
+ /**
109
+ * @return Record|\stdClass|mixed|null
110
+ */
111
+ public function first() {
112
+ $r = $this->setLimit( 1 )->queryWithResult();
113
+ return empty( $r ) ? null : array_shift( $r );
114
+ }
115
+
116
+ protected function getBaseQuery() :string {
117
+ return "SELECT %s FROM `%s` WHERE %s %s";
118
+ }
119
+
120
+ public function getColumnsToSelect() :array {
121
+ return is_array( $this->columnsToSelect ) ? $this->columnsToSelect : [];
122
+ }
123
+
124
+ public function getDistinctForColumn( string $col ) :array {
125
+ return $this->reset()
126
+ ->addColumnToSelect( $col )
127
+ ->setIsDistinct( true )
128
+ ->queryWithResult();
129
+ }
130
+
131
+ protected function getDistinct_FilterAndSort( string $col ) :array {
132
+ $a = array_filter( $this->getDistinctForColumn( $col ) );
133
+ natcasesort( $a );
134
+ return $a;
135
+ }
136
+
137
+ protected function getSelectDataFormat() :string {
138
+ if ( $this->isResultsAsVO() ) {
139
+ $format = ARRAY_A;
140
+ }
141
+ else {
142
+ $format = in_array( $this->resultFormat, [ OBJECT_K, ARRAY_A ] ) ? $this->resultFormat : OBJECT_K;
143
+ }
144
+ return $format;
145
+ }
146
+
147
+ protected function hasColumnsToSelect() :bool {
148
+ return count( $this->getColumnsToSelect() ) > 0;
149
+ }
150
+
151
+ public function isCount() :bool {
152
+ return (bool)$this->isCount;
153
+ }
154
+
155
+ public function isSum() :bool {
156
+ return (bool)$this->isSum;
157
+ }
158
+
159
+ public function isCustomSelect() :bool {
160
+ return !empty( $this->customSelect );
161
+ }
162
+
163
+ public function isDistinct() :bool {
164
+ return $this->isDistinct;
165
+ }
166
+
167
+ public function isResultsAsVO() :bool {
168
+ return ( $this->resultsAsVO ?? true ) && !$this->isSum();
169
+ }
170
+
171
+ /**
172
+ * COUNT, DISTINCT, & normal SELECT
173
+ */
174
+ public function query() :bool {
175
+ $mData = [];
176
+
177
+ if ( $this->isCount() || $this->isSum() ) {
178
+ $this->lastQueryResult = $this->queryVar();
179
+ }
180
+ elseif ( $this->isDistinct() ) {
181
+
182
+ $this->lastQueryResult = $this->queryDistinct();
183
+ if ( is_array( $this->lastQueryResult ) ) {
184
+ $this->lastQueryResult = array_map( function ( $record ) {
185
+ return array_shift( $record );
186
+ }, $this->lastQueryResult );
187
+ }
188
+ else {
189
+ $this->lastQueryResult = [];
190
+ }
191
+ }
192
+ else {
193
+
194
+ $this->lastQueryResult = $this->querySelect();
195
+ if ( $this->isResultsAsVO() && !empty( $this->lastQueryResult ) ) {
196
+ $this->lastQueryResult = array_map( function ( $record ) {
197
+ return $this->getDbH()->getRecord()->applyFromArray( $record );
198
+ }, $this->lastQueryResult );
199
+ }
200
+ else {
201
+ $this->lastQueryResult = $mData;
202
+ }
203
+ }
204
+
205
+ $this->reset();
206
+ return !is_null( $this->lastQueryResult );
207
+ }
208
+
209
+ /**
210
+ * @return array[]
211
+ */
212
+ protected function querySelect() {
213
+ return Services::WpDb()->selectCustom( $this->buildQuery(), $this->getSelectDataFormat() );
214
+ }
215
+
216
+ /**
217
+ * @return int
218
+ */
219
+ protected function queryVar() {
220
+ return Services::WpDb()->getVar( $this->buildQuery() );
221
+ }
222
+
223
+ /**
224
+ * @return array[]
225
+ */
226
+ protected function queryDistinct() {
227
+ return Services::WpDb()->selectCustom( $this->buildQuery() );
228
+ }
229
+
230
+ /**
231
+ * @return $this
232
+ */
233
+ public function reset() {
234
+ parent::reset();
235
+ return $this->setIsCount( false )
236
+ ->setIsDistinct( false )
237
+ ->setGroupBy( '' )
238
+ ->setSelectResultsFormat( '' )
239
+ ->setCustomSelect( '' )
240
+ ->setColumnsToSelect( [] )
241
+ ->clearWheres();
242
+ }
243
+
244
+ /**
245
+ * @return Record|mixed|\stdClass|null
246
+ */
247
+ public function selectLatestById() {
248
+ return $this->setOrderBy( 'id' )
249
+ ->setLimit( 1 )
250
+ ->first();
251
+ }
252
+
253
+ /**
254
+ * @return Record|mixed|\stdClass|null
255
+ */
256
+ public function selectFirstById() {
257
+ return $this->setOrderBy( 'id', 'ASC' )
258
+ ->setLimit( 1 )
259
+ ->first();
260
+ }
261
+
262
+ /**
263
+ * Verifies the given columns are valid and unique
264
+ * @param string[] $columns
265
+ * @return $this
266
+ */
267
+ public function setColumnsToSelect( array $columns ) {
268
+ $this->columnsToSelect = array_intersect(
269
+ $this->getDbH()->getTableSchema()->getColumnNames(),
270
+ array_map( 'strtolower', $columns )
271
+ );
272
+ return $this;
273
+ }
274
+
275
+ public function setCustomSelect( string $select ) :self {
276
+ $this->customSelect = $select;
277
+ return $this;
278
+ }
279
+
280
+ public function setIsCount( bool $isCount ) :self {
281
+ $this->isCount = $isCount;
282
+ return $this;
283
+ }
284
+
285
+ public function setIsSum( bool $sum ) :self {
286
+ $this->isSum = $sum;
287
+ return $this;
288
+ }
289
+
290
+ public function setIsDistinct( bool $distinct ) :self {
291
+ $this->isDistinct = $distinct;
292
+ return $this;
293
+ }
294
+
295
+ public function setResultsAsVo( bool $asVO ) :self {
296
+ $this->resultsAsVO = $asVO;
297
+ return $this;
298
+ }
299
+
300
+ public function setSelectResultsFormat( string $format ) :self {
301
+ $this->resultFormat = $format;
302
+ return $this;
303
+ }
304
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Traits/Select_IPTable.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Base\Traits;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Utilities\Tool\IpListSort;
6
+
7
+ trait Select_IPTable {
8
+
9
+ /**
10
+ * @return string[]
11
+ */
12
+ public function getDistinctIps() :array {
13
+ $ips = $this->getDistinctForColumn( 'ip' );
14
+ if ( $this->getDbH()->getTableSchema()->is_ip_binary ) {
15
+ $ips = array_map(
16
+ function ( $binaryIP ) {
17
+ return inet_ntop( $binaryIP );
18
+ },
19
+ $ips
20
+ );
21
+ }
22
+ return IpListSort::Sort( $ips );
23
+ }
24
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Base/Update.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Base;
4
+
5
+ use FernleafSystems\Wordpress\Services\Services;
6
+
7
+ class Update extends Insert {
8
+
9
+ /**
10
+ * @var array
11
+ */
12
+ protected $updateWheres = [];
13
+
14
+ /**
15
+ * @return array
16
+ */
17
+ public function getUpdateData() {
18
+ return $this->getInsertData();
19
+ }
20
+
21
+ public function getUpdateWheres() :array {
22
+ return is_array( $this->updateWheres ) ? $this->updateWheres : [];
23
+ }
24
+
25
+ /**
26
+ * @param array $data
27
+ * @return $this
28
+ */
29
+ public function setUpdateData( $data ) {
30
+ return $this->setInsertData( $data );
31
+ }
32
+
33
+ /**
34
+ * @param array $updateWheres
35
+ * @return $this
36
+ */
37
+ public function setUpdateWheres( $updateWheres ) {
38
+ $this->updateWheres = $updateWheres;
39
+ return $this;
40
+ }
41
+
42
+ /**
43
+ * @param int $ID
44
+ * @return $this
45
+ */
46
+ public function setUpdateId( $ID ) {
47
+ $this->updateWheres = [ 'id' => $ID ];
48
+ return $this;
49
+ }
50
+
51
+ /**
52
+ * @param Record $record
53
+ * @param array $updateData
54
+ * @return bool
55
+ */
56
+ public function updateEntry( $record, $updateData = [] ) :bool {
57
+ return $this->updateRecord( $record, $updateData );
58
+ }
59
+
60
+ /**
61
+ * @param Record $record
62
+ * @param array $updateData
63
+ * @return bool
64
+ */
65
+ public function updateRecord( $record, $updateData = [] ) :bool {
66
+ $success = false;
67
+
68
+ if ( $record instanceof Record ) {
69
+
70
+ foreach ( $record->getRawData() as $key => $value ) {
71
+ if ( isset( $updateData[ $key ] ) && $updateData[ $key ] === $value ) {
72
+ unset( $updateData[ $key ] );
73
+ }
74
+ }
75
+
76
+ if ( empty( $updateData ) ) {
77
+ $success = true;
78
+ }
79
+ else {
80
+ if ( $this->getDbH()->getTableSchema()->hasColumn( 'updated_at' )
81
+ && !isset( $updateData[ 'updated_at' ] ) ) {
82
+ $updateData[ 'updated_at' ] = Services::Request()->ts();
83
+ }
84
+ if ( $this->updateById( $record->id, $updateData ) ) {
85
+ $record->applyFromArray( array_merge( $record->getRawData(), $updateData ) );
86
+ $success = true;
87
+ }
88
+ }
89
+ }
90
+
91
+ return $success;
92
+ }
93
+
94
+ /**
95
+ * @param int $id
96
+ * @param array $updateData
97
+ * @return bool true is success or no update necessary
98
+ */
99
+ public function updateById( $id, $updateData = [] ) {
100
+ $success = true;
101
+
102
+ if ( !empty( $updateData ) ) {
103
+ $success = $this->setUpdateId( $id )
104
+ ->setUpdateData( $updateData )
105
+ ->query();
106
+ }
107
+ return $success;
108
+ }
109
+
110
+ public function query() :bool {
111
+ $this->lastQueryResult = Services::WpDb()
112
+ ->updateRowsFromTableWhere(
113
+ $this->getDbH()->getTable(),
114
+ $this->getUpdateData(),
115
+ $this->getUpdateWheres()
116
+ );
117
+ return (bool)$this->lastQueryResult;
118
+ }
119
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/AlignTableWithSchema.php ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Common;
4
+
5
+ use FernleafSystems\Wordpress\Services\Services;
6
+
7
+ class AlignTableWithSchema {
8
+
9
+ /**
10
+ * @var TableSchema
11
+ */
12
+ private $schema;
13
+
14
+ /**
15
+ * @var string[]
16
+ */
17
+ private $cols = null;
18
+
19
+ public function __construct( TableSchema $schema ) {
20
+ $this->schema = $schema;
21
+ }
22
+
23
+ public function isAligned() :bool {
24
+ $colsActual = $this->getColumnsActual();
25
+ $colsSchema = $this->schema->getColumnNames();
26
+ asort( $colsActual );
27
+ asort( $colsSchema );
28
+ return $colsActual === $colsSchema;
29
+ }
30
+
31
+ public function align() {
32
+ $DB = Services::WpDb();
33
+ if ( !$DB->getIfTableExists( $this->schema->table ) ) {
34
+ $DB->doSql( $this->schema->buildCreate() );
35
+ }
36
+ else {
37
+ $this->alignColumns();
38
+ }
39
+ }
40
+
41
+ private function alignColumns() {
42
+ $DB = Services::WpDb();
43
+ $colsUpdated = false;
44
+
45
+ $colsActual = $this->getColumnsActual();
46
+ $colsSchema = $this->schema->getColumnNames();
47
+
48
+ // Are columns missing?
49
+ foreach ( array_diff( $colsSchema, $colsActual ) as $col ) {
50
+ $this->addColumn( $col );
51
+ $colsUpdated = true;
52
+ }
53
+
54
+ // Extra columns?
55
+ foreach ( array_diff( $colsActual, $colsSchema ) as $col ) {
56
+ $DB->doSql( sprintf( 'ALTER TABLE `%s` DROP `%s`;', $this->schema->table, $col ) );
57
+ $colsUpdated = true;
58
+ }
59
+
60
+ if ( $colsUpdated ) {
61
+ $this->cols = null;
62
+ }
63
+ }
64
+
65
+ private function addColumn( string $col ) {
66
+ $colsSchema = $this->schema->enumerateColumns();
67
+ if ( array_key_exists( $col, $colsSchema ) ) {
68
+
69
+ if ( key( $colsSchema ) === $col ) {
70
+ $position = 'FIRST';
71
+ }
72
+ else {
73
+ // find the correct position to insert the col
74
+ while ( key( $colsSchema ) !== $col ) {
75
+ next( $colsSchema );
76
+ }
77
+ prev( $colsSchema );
78
+ $position = 'AFTER '.key( $colsSchema );
79
+ }
80
+
81
+ Services::WpDb()->doSql( sprintf( 'ALTER TABLE `%s` ADD COLUMN %s %s %s;',
82
+ $this->schema->table,
83
+ $col,
84
+ $colsSchema[ $col ],
85
+ $position
86
+ ) );
87
+ }
88
+ }
89
+
90
+ /**
91
+ * @return string[]
92
+ */
93
+ private function getColumnsActual() :array {
94
+ if ( is_null( $this->cols ) ) {
95
+ $this->cols = Services::WpDb()->getColumnsForTable( $this->schema->table );
96
+ }
97
+ return is_array( $this->cols ) ? array_map( 'strtolower', $this->cols ) : [];
98
+ }
99
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/BuildColumnFromDef.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Common;
4
+
5
+ use FernleafSystems\Wordpress\Services\Services;
6
+
7
+ class BuildColumnFromDef {
8
+
9
+ const MACROTYPE_PRIMARYID = 'primary_id';
10
+ const MACROTYPE_TIMESTAMP = 'timestamp';
11
+ const MACROTYPE_UNSIGNEDINT = 'unsigned_int';
12
+ const MACROTYPE_HASH = 'hash';
13
+ const MACROTYPE_IP = 'ip';
14
+ const MACROTYPE_META = 'meta';
15
+ const MACROTYPE_TEXT = 'text';
16
+ const MACROTYPE_URL = 'url';
17
+ const MACROTYPE_BOOL = 'bool';
18
+ const MACROTYPE_VARCHAR = 'varchar';
19
+
20
+ private $def;
21
+
22
+ public function __construct( array $def ) {
23
+ $this->setDef( $def );
24
+ }
25
+
26
+ public function setDef( array $def ) {
27
+ $this->def = $def;
28
+ return $this;
29
+ }
30
+
31
+ public function build() :string {
32
+ $def = $this->buildStructure();
33
+ return sprintf( '%s%s %s %s %s',
34
+ $def[ 'type' ],
35
+ isset( $def[ 'length' ] ) ? sprintf( '(%s)', $def[ 'length' ] ) : '',
36
+ implode( ' ', $def[ 'attr' ] ?? [] ),
37
+ isset( $def[ 'default' ] ) ? sprintf( "DEFAULT %s", $def[ 'default' ] ) : '',
38
+ isset( $def[ 'comment' ] ) ? sprintf( "COMMENT '%s'", str_replace( "'", '', $def[ 'comment' ] ) ) : ''
39
+ );
40
+ }
41
+
42
+ protected function buildStructure() :array {
43
+ return Services::DataManipulation()->mergeArraysRecursive(
44
+ $this->getMacroTypeDef( $this->def[ 'macro_type' ] ?? '' ),
45
+ $this->def
46
+ );
47
+ }
48
+
49
+ protected function getMacroTypeDef( string $macroType ) :array {
50
+ switch ( $macroType ) {
51
+
52
+ case self::MACROTYPE_HASH:
53
+ $def = array_merge(
54
+ $this->getMacroTypeDef( self::MACROTYPE_VARCHAR ),
55
+ [
56
+ 'length' => 40,
57
+ 'comment' => 'SHA1 Hash',
58
+ ]
59
+ );
60
+ break;
61
+
62
+ case self::MACROTYPE_IP:
63
+ $def = [
64
+ 'type' => 'varbinary',
65
+ 'length' => 16,
66
+ 'attr' => [],
67
+ 'default' => 'NULL',
68
+ 'comment' => 'IP Address',
69
+ ];
70
+ break;
71
+
72
+ case self::MACROTYPE_META:
73
+ $def = array_merge(
74
+ $this->getMacroTypeDef( self::MACROTYPE_TEXT ),
75
+ [
76
+ 'comment' => 'Meta Data',
77
+ ]
78
+ );
79
+ break;
80
+
81
+ case self::MACROTYPE_TEXT:
82
+ $def = [
83
+ 'type' => 'text',
84
+ ];
85
+ break;
86
+
87
+ case self::MACROTYPE_URL:
88
+ $def = array_merge(
89
+ $this->getMacroTypeDef( self::MACROTYPE_VARCHAR ),
90
+ [
91
+ 'comment' => 'Site URL',
92
+ ]
93
+ );
94
+ break;
95
+
96
+ case self::MACROTYPE_VARCHAR:
97
+ $def = [
98
+ 'type' => 'varchar',
99
+ 'length' => 60,
100
+ 'attr' => [
101
+ 'NOT NULL',
102
+ ],
103
+ 'default' => "''",
104
+ ];
105
+ break;
106
+
107
+ case self::MACROTYPE_BOOL:
108
+ $def = array_merge(
109
+ $this->getMacroTypeDef( self::MACROTYPE_UNSIGNEDINT ),
110
+ [
111
+ 'type' => 'tinyint',
112
+ 'length' => 1,
113
+ 'comment' => 'Boolean',
114
+ ]
115
+ );
116
+ break;
117
+
118
+ case self::MACROTYPE_TIMESTAMP:
119
+ $def = array_merge(
120
+ $this->getMacroTypeDef( self::MACROTYPE_UNSIGNEDINT ),
121
+ [
122
+ 'length' => 15,
123
+ 'comment' => 'Epoch Timestamp',
124
+ ]
125
+ );
126
+ break;
127
+
128
+ case self::MACROTYPE_UNSIGNEDINT:
129
+ $def = [
130
+ 'type' => 'int',
131
+ 'length' => 11,
132
+ 'attr' => [
133
+ 'UNSIGNED',
134
+ 'NOT NULL',
135
+ ],
136
+ 'default' => 0,
137
+ ];
138
+ break;
139
+
140
+ case self::MACROTYPE_PRIMARYID:
141
+ $def = array_merge(
142
+ $this->getMacroTypeDef( self::MACROTYPE_UNSIGNEDINT ),
143
+ [
144
+ 'comment' => 'Primary ID',
145
+ ]
146
+ );
147
+ $def[ 'attr' ][] = 'AUTO_INCREMENT';
148
+ break;
149
+
150
+ default:
151
+ $def = [];
152
+ break;
153
+ }
154
+
155
+ return $def;
156
+ }
157
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/HandlerConsumer.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Common;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base\Handler;
6
+
7
+ trait HandlerConsumer {
8
+
9
+ /**
10
+ * @var Handler
11
+ */
12
+ private $dbh;
13
+
14
+ /**
15
+ * @return Handler|mixed
16
+ */
17
+ public function getDbHandler() {
18
+ return $this->dbh;
19
+ }
20
+
21
+ /**
22
+ * @param Handler $dbh
23
+ * @return $this
24
+ */
25
+ public function setDbHandler( $dbh ) {
26
+ $this->dbh = $dbh;
27
+ return $this;
28
+ }
29
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/Iterator.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Common;
4
+
5
+ use Elliotchance\Iterator\AbstractPagedIterator;
6
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base\Record;
7
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base\Select;
8
+
9
+ class Iterator extends AbstractPagedIterator {
10
+
11
+ const PAGE_LIMIT = 50;
12
+ use HandlerConsumer;
13
+
14
+ /**
15
+ * @var Select|mixed
16
+ */
17
+ private $selector;
18
+
19
+ /**
20
+ * @var int
21
+ */
22
+ private $totalSize;
23
+
24
+ /**
25
+ * @var array
26
+ */
27
+ private $customFilters = [];
28
+
29
+ /**
30
+ * @return Record|mixed|null
31
+ */
32
+ public function current() {
33
+ return parent::current();
34
+ }
35
+
36
+ public function getCustomQueryFilters() :array {
37
+ return is_array( $this->customFilters ) ? $this->customFilters : [];
38
+ }
39
+
40
+ protected function getDefaultQueryFilters() :array {
41
+ return [
42
+ 'orderby' => 'id',
43
+ 'order' => 'ASC',
44
+ ];
45
+ }
46
+
47
+ protected function getFinalQueryFilters() :array {
48
+ return array_merge( $this->getDefaultQueryFilters(), $this->getCustomQueryFilters() );
49
+ }
50
+
51
+ /**
52
+ * @param int $nPage - always starts at 0
53
+ * @return array
54
+ */
55
+ public function getPage( $nPage ) {
56
+ $aParams = $this->getFinalQueryFilters();
57
+
58
+ $this->getSelector()
59
+ ->setResultsAsVo( true )
60
+ ->setPage( $nPage + 1 ) // Pages start at 1, not zero.
61
+ ->setLimit( $this->getPageSize() )
62
+ ->setOrderBy( $aParams[ 'orderby' ], $aParams[ 'order' ] );
63
+
64
+ return $this->runQuery();
65
+ }
66
+
67
+ /**
68
+ * @return int
69
+ */
70
+ public function getPageSize() {
71
+ return static::PAGE_LIMIT;
72
+ }
73
+
74
+ /**
75
+ * @return Select|mixed
76
+ */
77
+ public function getSelector() {
78
+ if ( empty( $this->selector ) ) {
79
+ $this->selector = $this->getDbHandler()->getQuerySelector();
80
+ }
81
+ return $this->selector;
82
+ }
83
+
84
+ /**
85
+ * @return int
86
+ */
87
+ public function getTotalSize() {
88
+ if ( !isset( $this->totalSize ) ) {
89
+ $this->totalSize = $this->runQueryCount();
90
+ }
91
+ return $this->totalSize;
92
+ }
93
+
94
+ /**
95
+ * @return Record[]|mixed[]
96
+ */
97
+ protected function runQuery() {
98
+ return ( clone $this->getSelector() )->query();
99
+ }
100
+
101
+ protected function runQueryCount() :int {
102
+ return (int)( clone $this->getSelector() )->count();
103
+ }
104
+
105
+ /**
106
+ * @param Select|mixed $selector
107
+ * @return $this
108
+ */
109
+ public function setSelector( $selector ) {
110
+ $this->selector = $selector;
111
+ return $this;
112
+ }
113
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/RecordConsumer.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Common;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base\Record;
6
+
7
+ trait RecordConsumer {
8
+
9
+ /**
10
+ * @var Record
11
+ */
12
+ private $dbRecord;
13
+
14
+ /**
15
+ * @return Record|mixed
16
+ */
17
+ public function getRecord() {
18
+ return $this->dbRecord;
19
+ }
20
+
21
+ /**
22
+ * @param Record $record
23
+ * @return $this
24
+ */
25
+ public function setRecord( $record ) {
26
+ $this->dbRecord = $record;
27
+ return $this;
28
+ }
29
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/SubQueryLoader.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Common;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Core\Databases\Base\{
6
+ BaseQuery,
7
+ Delete,
8
+ Insert,
9
+ Record,
10
+ Select,
11
+ Update
12
+ };
13
+ use FernleafSystems\Wordpress\Services\Utilities\Autoloading\FindClassFromNamespaceRoots;
14
+
15
+ class SubQueryLoader {
16
+
17
+ use HandlerConsumer;
18
+
19
+ public function loadClass( string $class ) {
20
+ /** @var BaseQuery $o */
21
+ $o = new $class;
22
+ return method_exists( $o, 'setDbH' ) ? $o->setDbH( $this->getDbHandler() ) : $o;
23
+ }
24
+
25
+ /**
26
+ * @return Delete|mixed
27
+ */
28
+ public function delete() {
29
+ return $this->loadClass(
30
+ FindClassFromNamespaceRoots::Find( 'Delete', $this->getNamespaceRoots() )
31
+ );
32
+ }
33
+
34
+ /**
35
+ * @return Insert|mixed
36
+ */
37
+ public function insert() {
38
+ return $this->loadClass(
39
+ FindClassFromNamespaceRoots::Find( 'Insert', $this->getNamespaceRoots() )
40
+ );
41
+ }
42
+
43
+ /**
44
+ * @return Record|mixed
45
+ */
46
+ public function record() {
47
+ return $this->loadClass(
48
+ FindClassFromNamespaceRoots::Find( 'Record', $this->getNamespaceRoots() )
49
+ );
50
+ }
51
+
52
+ /**
53
+ * @return Select|mixed
54
+ */
55
+ public function select() {
56
+ return $this->loadClass(
57
+ FindClassFromNamespaceRoots::Find( 'Select', $this->getNamespaceRoots() )
58
+ );
59
+ }
60
+
61
+ /**
62
+ * @return Update|mixed
63
+ */
64
+ public function update() {
65
+ return $this->loadClass(
66
+ FindClassFromNamespaceRoots::Find( 'Update', $this->getNamespaceRoots() )
67
+ );
68
+ }
69
+
70
+ protected function getNamespaceRoots() :array {
71
+ $dbh = $this->getDbHandler();
72
+ $roots = $dbh->getBaseNamespaces();
73
+ array_unshift( $roots, $this->getPrimaryNamespace() );
74
+ return $roots;
75
+ }
76
+
77
+ private function getPrimaryNamespace() :string {
78
+ try {
79
+ $namespace = ( new \ReflectionClass( $this->getDbHandler() ) )->getNamespaceName();
80
+ }
81
+ catch ( \Exception $e ) {
82
+ $namespace = __NAMESPACE__;
83
+ }
84
+ return rtrim( $namespace, '\\' );
85
+ }
86
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Common/TableSchema.php ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Common;
4
+
5
+ use FernleafSystems\Utilities\Data\Adapter\DynPropertiesClass;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+
8
+ /**
9
+ * Class TableSchema
10
+ * @package FernleafSystems\Wordpress\Plugin\Shield\Databases\Common
11
+ * @property string $slug
12
+ * @property string $table - full and complete table name including any prefixes
13
+ * @property string $table_prefix
14
+ * @property string $primary_key
15
+ * @property string[] $cols_ids
16
+ * @property string[] $cols_custom
17
+ * @property string[] $cols_timestamps
18
+ * @property string $col_older_than
19
+ * @property bool $has_updated_at
20
+ * @property int $autoexpire
21
+ * @property bool $has_ip_col
22
+ * @property bool $is_ip_binary
23
+ */
24
+ class TableSchema extends DynPropertiesClass {
25
+
26
+ const PRIMARY_KEY = 'id';
27
+
28
+ public function __get( string $key ) {
29
+ $val = parent::__get( $key );
30
+ switch ( $key ) {
31
+ case 'has_ip_col':
32
+ $val = array_key_exists( 'ip', $this->enumerateColumns() );
33
+ break;
34
+ case 'is_ip_binary':
35
+ $val = $this->has_ip_col && ( stripos( $this->cols_custom[ 'ip' ], 'varbinary' ) !== false );
36
+ break;
37
+ case 'table':
38
+ $val = $this->buildTableName();
39
+ break;
40
+ case 'col_older_than':
41
+ if ( empty( $val ) || !$this->hasColumn( $val ) ) {
42
+ $val = 'created_at';
43
+ }
44
+ break;
45
+ case 'has_updated_at':
46
+ $val = is_null( $val ) ? true : $val;
47
+ break;
48
+ default:
49
+ break;
50
+ }
51
+ return $val;
52
+ }
53
+
54
+ protected function buildTableName() :string {
55
+ return sprintf( '%s%s%s',
56
+ Services::WpDb()->getPrefix(),
57
+ empty( $this->table_prefix ) ? '' : $this->table_prefix.'_',
58
+ $this->slug
59
+ );
60
+ }
61
+
62
+ public function buildCreate() :string {
63
+ $cols = [];
64
+ foreach ( $this->enumerateColumns() as $col => $def ) {
65
+ $cols[] = sprintf( '%s %s', $col, $def );
66
+ }
67
+ $cols[] = $this->getPrimaryKeyDef();
68
+
69
+ return sprintf(
70
+ 'CREATE TABLE %s (
71
+ %s
72
+ ) %s;',
73
+ $this->table,
74
+ implode( ", ", $cols ),
75
+ Services::WpDb()->getCharCollate()
76
+ );
77
+ }
78
+
79
+ /**
80
+ * @return string[]
81
+ */
82
+ public function getColumnNames() :array {
83
+ return array_keys( $this->enumerateColumns() );
84
+ }
85
+
86
+ /**
87
+ * @return string[]
88
+ */
89
+ public function enumerateColumns() :array {
90
+ return array_map(
91
+ function ( array $colDef ) {
92
+ // convert from array column def to string.
93
+ return ( new BuildColumnFromDef( $colDef ) )->build();
94
+ },
95
+ array_merge(
96
+ $this->getColumn_ID(),
97
+ $this->cols_custom ?? [],
98
+ $this->getColumns_Timestamps()
99
+ )
100
+ );
101
+ }
102
+
103
+ /**
104
+ * @return string[]
105
+ */
106
+ protected function getColumn_ID() :array {
107
+ return [
108
+ $this->getPrimaryKeyColumnName() => [ 'macro_type' => BuildColumnFromDef::MACROTYPE_PRIMARYID ],
109
+ ];
110
+ }
111
+
112
+ /**
113
+ * @return string[]
114
+ */
115
+ protected function getColumns_Timestamps() :array {
116
+
117
+ $standardTsCols = [
118
+ 'created_at' => [
119
+ 'comment' => 'Created'
120
+ ],
121
+ 'deleted_at' => [
122
+ 'comment' => 'Soft Deleted'
123
+ ],
124
+ ];
125
+
126
+ if ( $this->has_updated_at && !array_key_exists( 'updated_at', $this->cols_timestamps ) ) {
127
+ $standardTsCols[ 'updated_at' ] = [
128
+ 'comment' => 'Last Updated'
129
+ ];
130
+ }
131
+
132
+ return array_map(
133
+ function ( array $colDef ) {
134
+ $colDef[ 'macro_type' ] = BuildColumnFromDef::MACROTYPE_TIMESTAMP;
135
+ return $colDef;
136
+ },
137
+ array_merge(
138
+ $this->cols_timestamps ?? [],
139
+ $standardTsCols
140
+ )
141
+ );
142
+ }
143
+
144
+ protected function getPrimaryKeyDef() :string {
145
+ return sprintf( 'PRIMARY KEY (%s)', $this->getPrimaryKeyColumnName() );
146
+ }
147
+
148
+ protected function getPrimaryKeyColumnName() :string {
149
+ return $this->primary_key ?? static::PRIMARY_KEY;
150
+ }
151
+
152
+ public function hasColumn( string $col ) :bool {
153
+ return in_array( strtolower( $col ), $this->getColumnNames() );
154
+ }
155
+ }
src/lib/vendor/fernleafsystems/wordpress-plugin-core/src/Databases/Exceptions/NoSlugProvidedException.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Core\Databases\Exceptions;
4
+
5
+ class NoSlugProvidedException extends \InvalidArgumentException {
6
+
7
+ }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Autoloading/FindClassFromNamespaceRoots.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Services\Utilities\Autoloading;
4
+
5
+ class FindClassFromNamespaceRoots {
6
+
7
+ public static function Find( string $class, array $roots, bool $beInstantiable = true, bool $throwEx = true ) :string {
8
+ $theClass = '';
9
+
10
+ $roots = array_map( function ( $root ) {
11
+ return rtrim( $root, '\\' ).'\\';
12
+ }, $roots );
13
+
14
+ foreach ( $roots as $NS ) {
15
+ $maybe = $NS.$class;
16
+ if ( @class_exists( $maybe ) ) {
17
+ try {
18
+ if ( !$beInstantiable || ( new \ReflectionClass( $maybe ) )->isInstantiable() ) {
19
+ $theClass = $maybe;
20
+ break;
21
+ }
22
+ }
23
+ catch ( \ReflectionException $e ) {
24
+ continue;
25
+ }
26
+ }
27
+ }
28
+
29
+ if ( $throwEx && empty( $theClass ) ) {
30
+ throw new \Exception( sprintf( 'Could not find class for element "%s".', $class ) );
31
+ }
32
+
33
+ return $theClass;
34
+ }
35
+ }
templates/twig/wpadmin_pages/insights/ips/ip_analyse/ip_general.twig CHANGED
@@ -45,11 +45,11 @@
45
  </p>
46
  </dd>
47
 
48
- <dt class="mb-1">{{ strings.status.notbot_score }}</dt>
49
  <dd>
50
  <p>
51
  <span class="bigger-badge badge badge-{{ vars.status.is_bot ? 'warning' : 'success' }}">
52
- {{ vars.status.notbot_score }}
53
  </span>
54
  <a href="#" class="ml-2 text-primary ip_analyse_action"
55
  data-ip_action="delete_notbot" data-ip="{{ vars.ip }}">{{ strings.delete_notbot }}</a>
45
  </p>
46
  </dd>
47
 
48
+ <dt class="mb-1">{{ strings.status.ip_reputation }}</dt>
49
  <dd>
50
  <p>
51
  <span class="bigger-badge badge badge-{{ vars.status.is_bot ? 'warning' : 'success' }}">
52
+ {{ vars.status.ip_reputation_score }}
53
  </span>
54
  <a href="#" class="ml-2 text-primary ip_analyse_action"
55
  data-ip_action="delete_notbot" data-ip="{{ vars.ip }}">{{ strings.delete_notbot }}</a>