Shield Security for WordPress - Version 15.0.8

Version Description

Download this release

Release Info

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

Code changes from version 15.0.6 to 15.0.8

cl.json CHANGED
@@ -184,6 +184,28 @@
184
  "type": "fixed"
185
  }
186
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  }
188
  ]
189
  },
184
  "type": "fixed"
185
  }
186
  ]
187
+ },
188
+ {
189
+ "version": "8",
190
+ "released_at": 1652357000,
191
+ "items": [
192
+ {
193
+ "title": "Adjusted how the security progress meters are displayed and switch to grades instead of percentages.",
194
+ "type": "improved"
195
+ },
196
+ {
197
+ "title": "Work around a horrendous Godaddy server 'protection' that was blocking access to the site entirely.",
198
+ "type": "fixed"
199
+ },
200
+ {
201
+ "title": "Prevent an error when handling user meta data.",
202
+ "type": "fixed"
203
+ },
204
+ {
205
+ "title": "Ensure Whitelabel logo is correctly displayed on dashboard widget.",
206
+ "type": "fixed"
207
+ }
208
+ ]
209
  }
210
  ]
211
  },
config/deprecated/hack_protect.php CHANGED
@@ -565,7 +565,7 @@
565
  "wp-content/uploads/bb-plugin/cache/*",
566
  "wp-content/uploads/cache/wpml/twig/*",
567
  "wp-content/cache/*",
568
- "src/lib/src/Rules/rules.json",
569
  "*/error_log",
570
  "*/php_error_log",
571
  "*/mail.log",
565
  "wp-content/uploads/bb-plugin/cache/*",
566
  "wp-content/uploads/cache/wpml/twig/*",
567
  "wp-content/cache/*",
568
+ "*/src/lib/src/Rules/rules.json",
569
  "*/error_log",
570
  "*/php_error_log",
571
  "*/mail.log",
config/deprecated/login_protect.php CHANGED
@@ -564,9 +564,16 @@
564
  "level": "warning"
565
  },
566
  "login_block": {
567
- "level": "warning",
568
- "recent": true,
569
- "offense": true
 
 
 
 
 
 
 
570
  },
571
  "hide_login_url": {
572
  "level": "notice"
564
  "level": "warning"
565
  },
566
  "login_block": {
567
+ "level": "warning"
568
+ },
569
+ "block_register": {
570
+ "level": "warning"
571
+ },
572
+ "block_lostpassword": {
573
+ "level": "warning"
574
+ },
575
+ "block_checkout": {
576
+ "level": "warning"
577
  },
578
  "hide_login_url": {
579
  "level": "notice"
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: 15.0.6
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: 15.0.8
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": "15.0.6",
4
- "release_timestamp": 1652183772,
5
- "build": "202205.1002",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
1
  {
2
  "properties": {
3
+ "version": "15.0.8",
4
+ "release_timestamp": 1652357000,
5
+ "build": "202205.1202",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
plugin.json CHANGED
@@ -1,8 +1,8 @@
1
  {
2
  "properties": {
3
- "version": "15.0.6",
4
- "release_timestamp": 1652183772,
5
- "build": "202205.1002",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
1
  {
2
  "properties": {
3
+ "version": "15.0.8",
4
+ "release_timestamp": 1652357000,
5
+ "build": "202205.1202",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
readme.txt CHANGED
@@ -8,7 +8,7 @@ Requires at least: 3.7
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 6.0
11
- Stable tag: 15.0.6
12
 
13
  No-Nonsense Security Hardening that protects WordPress against hackers, malicious bots, and spammers (no captchas!). Now with exclusive ShieldNET Technology.
14
 
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 6.0
11
+ Stable tag: 15.0.8
12
 
13
  No-Nonsense Security Hardening that protects WordPress against hackers, malicious bots, and spammers (no captchas!). Now with exclusive ShieldNET Technology.
14
 
resources/css/plugin.css CHANGED
@@ -102,7 +102,7 @@ label input[type=checkbox] {
102
  pointer-events: auto;
103
  }
104
  .content-wizards,
105
- /** NEW FORMS **/
106
  #wpwrap {
107
  background-color: #f1f1f1;
108
  }
@@ -1071,9 +1071,15 @@ body.folded #FooterBannerGoPro {
1071
  .card.progress-meter .card-footer {
1072
  text-align: right;
1073
  border: 0 none;
1074
- padding: 0.5rem 1rem 1rem;
1075
  background-color: transparent;
1076
  }
 
 
 
 
 
 
1077
 
1078
  #ScanResultsPlugins li.nav-item a.nav-link {
1079
  /*background-color: white;*/
102
  pointer-events: auto;
103
  }
104
  .content-wizards,
105
+ /** NEW FORMS **/
106
  #wpwrap {
107
  background-color: #f1f1f1;
108
  }
1071
  .card.progress-meter .card-footer {
1072
  text-align: right;
1073
  border: 0 none;
1074
+ padding: 0.5rem 1rem 1rem;
1075
  background-color: transparent;
1076
  }
1077
+ .card.progress-meter .letter-score {
1078
+ position: absolute;
1079
+ font-size: 5rem;
1080
+ margin: -10px auto 0;
1081
+ text-shadow: 2px 2px 2px rgb(0 0 0 / 40%);
1082
+ }
1083
 
1084
  #ScanResultsPlugins li.nav-item a.nav-link {
1085
  /*background-color: white;*/
resources/js/shield/notbot.js CHANGED
@@ -8,6 +8,7 @@ if ( typeof Shield_Antibot === typeof undefined && typeof shield_vars_notbotjs !
8
  let can_send_request = true;
9
  let nonce_cook = '';
10
  let ajaxurl = shield_vars_notbotjs.ajax.not_bot.ajaxurl;
 
11
 
12
  this.initialise = function () {
13
  /**
@@ -48,9 +49,14 @@ if ( typeof Shield_Antibot === typeof undefined && typeof shield_vars_notbotjs !
48
  let sendReq = function () {
49
  request_count++;
50
 
 
 
 
 
51
  let xhr = new XMLHttpRequest();
52
  xhr.open( "POST", ajaxurl, true );
53
- xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded;' );
 
54
 
55
  /**
56
  * Ensures that if there's an error with the AJAX, we don't keep retrying the requests.
@@ -74,6 +80,34 @@ if ( typeof Shield_Antibot === typeof undefined && typeof shield_vars_notbotjs !
74
  xhr.send( (new URLSearchParams( shield_vars_notbotjs.ajax.not_bot )).toString() );
75
  };
76
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  let getCookie = function ( name ) {
78
  let value = "; " + document.cookie;
79
  let parts = value.split( "; " + name + "=" );
8
  let can_send_request = true;
9
  let nonce_cook = '';
10
  let ajaxurl = shield_vars_notbotjs.ajax.not_bot.ajaxurl;
11
+ let use_fetch = false;
12
 
13
  this.initialise = function () {
14
  /**
49
  let sendReq = function () {
50
  request_count++;
51
 
52
+ if ( use_fetch ) {
53
+ return sendReqWithFetch();
54
+ }
55
+
56
  let xhr = new XMLHttpRequest();
57
  xhr.open( "POST", ajaxurl, true );
58
+ xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8' );
59
+ xhr.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' );
60
 
61
  /**
62
  * Ensures that if there's an error with the AJAX, we don't keep retrying the requests.
80
  xhr.send( (new URLSearchParams( shield_vars_notbotjs.ajax.not_bot )).toString() );
81
  };
82
 
83
+ async function sendReqWithFetch() {
84
+ try {
85
+ let resp = await fetch( ajaxurl, {
86
+ method: 'POST',
87
+ body: (new URLSearchParams( shield_vars_notbotjs.ajax.not_bot )).toString(),
88
+ headers: {
89
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
90
+ 'X-Requested-With': 'XMLHttpRequest',
91
+ }
92
+ } )
93
+ .then( response => response.json() )
94
+ .catch( error => {
95
+ console.log( error );
96
+ use_fetch = false;
97
+ } );
98
+ if ( resp ) {
99
+ can_send_request = resp && resp.success;
100
+ }
101
+ else {
102
+ use_fetch = false;
103
+ }
104
+ }
105
+ catch ( error ) {
106
+ use_fetch = false;
107
+ console.log( error );
108
+ }
109
+ }
110
+
111
  let getCookie = function ( name ) {
112
  let value = "; " + document.cookie;
113
  let parts = value.split( "; " + name + "=" );
src/lib/src/Modules/Data/DB/UserMeta/MetaRecords.php CHANGED
@@ -2,6 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\UserMeta;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\ModCon;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
 
@@ -40,6 +41,10 @@ class MetaRecords {
40
  /** @var Ops\Record $record */
41
  $record = $dbh->getRecord();
42
  $record->user_id = $userID;
 
 
 
 
43
  return $insert->insert( $record );
44
  }
45
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\UserMeta;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\DB\IPs\IPRecords;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Data\ModCon;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
 
41
  /** @var Ops\Record $record */
42
  $record = $dbh->getRecord();
43
  $record->user_id = $userID;
44
+ $record->ip_ref = ( new IPRecords() )
45
+ ->setMod( $this->getCon()->getModule_Data() )
46
+ ->loadIP( $this->getCon()->this_req->ip, true )
47
+ ->id;
48
  return $insert->insert( $record );
49
  }
50
  }
src/lib/src/Modules/HackGuard/DB/Utility/Clean.php CHANGED
@@ -39,16 +39,20 @@ class Clean extends ExecOnceModConsumer {
39
  foreach ( $opts->getScanSlugs() as $scanSlug ) {
40
  /** @var ScansDB\Select $select */
41
  $select = $dbhScan->getQuerySelector();
42
- $scanRecord = $select->filterByFinished()
43
- ->filterByScan( $scanSlug )
44
- ->setOrderBy( 'finished_at', 'DESC' )
45
- ->setLimit( 1 )
46
- ->queryWithResult();
47
- $scanIDsToKeep[] = $scanRecord[ 0 ]->id;
 
 
48
  }
49
 
50
- Services::WpDb()->doSql( sprintf( 'DELETE FROM %s WHERE `id` NOT IN (%s) AND `finished_at`>0',
51
- $mod->getDbH_Scans()->getTableSchema()->table,
52
- implode( ', ', $scanIDsToKeep ) ) );
 
 
53
  }
54
  }
39
  foreach ( $opts->getScanSlugs() as $scanSlug ) {
40
  /** @var ScansDB\Select $select */
41
  $select = $dbhScan->getQuerySelector();
42
+ $scanRecords = $select->filterByFinished()
43
+ ->filterByScan( $scanSlug )
44
+ ->setOrderBy( 'finished_at', 'DESC' )
45
+ ->setLimit( 1 )
46
+ ->queryWithResult();
47
+ if ( is_array( $scanRecords ) && count( $scanRecords ) === 1 ) {
48
+ $scanIDsToKeep[] = $scanRecords[ 0 ]->id;
49
+ }
50
  }
51
 
52
+ if ( !empty( $scanIDsToKeep ) ) {
53
+ Services::WpDb()->doSql( sprintf( 'DELETE FROM %s WHERE `id` NOT IN (%s) AND `finished_at`>0',
54
+ $mod->getDbH_Scans()->getTableSchema()->table,
55
+ implode( ', ', $scanIDsToKeep ) ) );
56
+ }
57
  }
58
  }
src/lib/src/Modules/Insights/Lib/MeterAnalysis/Components.php CHANGED
@@ -109,9 +109,8 @@ class Components {
109
  [
110
  'all' => function () {
111
  $con = $this->getCon();
112
- $meter = ( new MeterAll() )
113
- ->setCon( $this->getCon() )
114
- ->buildMeterComponents();
115
 
116
  $weight = 200;
117
  return [
@@ -123,6 +122,7 @@ class Components {
123
  'score' => $meter[ 'totals' ][ 'percentage' ]*$weight/100,
124
  'weight' => $weight,
125
  'original_score' => $meter[ 'totals' ][ 'percentage' ],
 
126
  ];
127
  },
128
  'shieldpro' => function () {
@@ -436,7 +436,7 @@ class Components {
436
  'weight' => 25,
437
  ];
438
  },
439
- 'activity_log_enabled' => function () {
440
  $modAudit = $this->getCon()->getModule_AuditTrail();
441
  /** @var AuditTrail\Options $optsAudit */
442
  $optsAudit = $modAudit->getOptions();
109
  [
110
  'all' => function () {
111
  $con = $this->getCon();
112
+ $allMeterGuage = ( new MeterAll() )->setCon( $this->getCon() );
113
+ $meter = $allMeterGuage->buildMeterComponents();
 
114
 
115
  $weight = 200;
116
  return [
122
  'score' => $meter[ 'totals' ][ 'percentage' ]*$weight/100,
123
  'weight' => $weight,
124
  'original_score' => $meter[ 'totals' ][ 'percentage' ],
125
+ 'letter_score' => $allMeterGuage->letterScoreFromPercentage( $meter[ 'totals' ][ 'percentage' ] ),
126
  ];
127
  },
128
  'shieldpro' => function () {
436
  'weight' => 25,
437
  ];
438
  },
439
+ 'activity_log_enabled' => function () {
440
  $modAudit = $this->getCon()->getModule_AuditTrail();
441
  /** @var AuditTrail\Options $optsAudit */
442
  $optsAudit = $modAudit->getOptions();
src/lib/src/Modules/Insights/Lib/MeterAnalysis/MeterBase.php CHANGED
@@ -92,9 +92,10 @@ abstract class MeterBase extends BaseTemplateRenderer {
92
 
93
  $percentage = (int)round( 100*$totalScore/$totalWeight );
94
  $meter[ 'totals' ] = [
95
- 'score' => $totalScore,
96
- 'max_weight' => $totalWeight,
97
- 'percentage' => $percentage,
 
98
  ];
99
  $meter[ 'rgbs' ] = [
100
  ( 100 - $percentage )*128/100,
@@ -107,6 +108,14 @@ abstract class MeterBase extends BaseTemplateRenderer {
107
  return $meter;
108
  }
109
 
 
 
 
 
 
 
 
 
110
  protected function buildComponents() :array {
111
  return ( new Components() )
112
  ->setCon( $this->getCon() )
@@ -118,16 +127,19 @@ abstract class MeterBase extends BaseTemplateRenderer {
118
  }
119
 
120
  protected function getRenderData() :array {
 
121
  return Services::DataManipulation()->mergeArraysRecursive(
122
  $this->getCon()->getModule_Plugin()->getUIHandler()->getBaseDisplayData(),
123
  [
124
  'strings' => [
125
  'title' => sprintf( '%s: %s', __( 'Analysis', 'wp-simple-firewall' ), $this->title() ),
 
126
  'scores_footnote1' => __( 'Scores are an approximate weighting for each component.', 'wp-simple-firewall' ),
127
  'scores_footnote2' => __( 'As each issue is resolved the overall score will improve, up to 100%.', 'wp-simple-firewall' ),
128
  ],
129
  'vars' => [
130
- 'components' => $this->buildMeterComponents()[ 'components' ]
 
131
  ]
132
  ],
133
  $this->getMeterRenderData()
92
 
93
  $percentage = (int)round( 100*$totalScore/$totalWeight );
94
  $meter[ 'totals' ] = [
95
+ 'score' => $totalScore,
96
+ 'max_weight' => $totalWeight,
97
+ 'percentage' => $percentage,
98
+ 'letter_score' => $this->letterScoreFromPercentage( $percentage ),
99
  ];
100
  $meter[ 'rgbs' ] = [
101
  ( 100 - $percentage )*128/100,
108
  return $meter;
109
  }
110
 
111
+ public function letterScoreFromPercentage( int $percentage ) :string {
112
+ return ( $percentage > 95 ? 'A+' :
113
+ ( $percentage > 80 ? 'A' :
114
+ ( $percentage > 60 ? 'B' :
115
+ ( $percentage > 40 ? 'C' :
116
+ ( $percentage > 20 ? 'D' : 'F' ) ) ) ) );
117
+ }
118
+
119
  protected function buildComponents() :array {
120
  return ( new Components() )
121
  ->setCon( $this->getCon() )
127
  }
128
 
129
  protected function getRenderData() :array {
130
+ $components = $this->buildMeterComponents();
131
  return Services::DataManipulation()->mergeArraysRecursive(
132
  $this->getCon()->getModule_Plugin()->getUIHandler()->getBaseDisplayData(),
133
  [
134
  'strings' => [
135
  'title' => sprintf( '%s: %s', __( 'Analysis', 'wp-simple-firewall' ), $this->title() ),
136
+ 'total_score' => __( 'Total Score', 'wp-simple-firewall' ),
137
  'scores_footnote1' => __( 'Scores are an approximate weighting for each component.', 'wp-simple-firewall' ),
138
  'scores_footnote2' => __( 'As each issue is resolved the overall score will improve, up to 100%.', 'wp-simple-firewall' ),
139
  ],
140
  'vars' => [
141
+ 'total_percentage_score' => $components[ 'totals' ][ 'percentage' ],
142
+ 'components' => $components[ 'components' ],
143
  ]
144
  ],
145
  $this->getMeterRenderData()
src/lib/src/Modules/Plugin/Components/DashboardWidget.php CHANGED
@@ -26,7 +26,7 @@ class DashboardWidget {
26
 
27
  $logoSrc = $con->urls->forImage( 'pluginlogo_banner-772x250.png' );
28
  if ( $con->getModule_SecAdmin()->getWhiteLabelController()->isEnabled() ) {
29
- $logoSrc = $con->getLabels()[ 'wl_login2fa_logourl' ] ?? ( $con->getLabels()[ 'wl_dashboardlogourl' ] ?? '' );
30
  }
31
 
32
  return $this->getMod()
@@ -47,6 +47,7 @@ class DashboardWidget {
47
  'logo' => $logoSrc,
48
  ],
49
  'strings' => [
 
50
  'security_progress' => __( 'Overall Security Progress', 'wp-simple-firewall' ),
51
  'progress_overview' => __( 'Go To Overview', 'wp-simple-firewall' ),
52
  'recent_blocked' => __( 'Recently Blocked', 'wp-simple-firewall' ),
@@ -82,8 +83,8 @@ class DashboardWidget {
82
  $vars = [
83
  'generated_at' => Services::Request()->ts(),
84
  'security_progress' => ( new Components() )
85
- ->setCon( $con )
86
- ->getComponent( 'all' )[ 'original_score' ],
87
  'jump_links' => [
88
  [
89
  'href' => $modInsights->getUrl_SubInsightsPage( 'overview' ),
26
 
27
  $logoSrc = $con->urls->forImage( 'pluginlogo_banner-772x250.png' );
28
  if ( $con->getModule_SecAdmin()->getWhiteLabelController()->isEnabled() ) {
29
+ $logoSrc = $con->getLabels()[ 'url_login2fa_logourl' ] ?? ( $con->getLabels()[ 'url_dashboardlogourl' ] ?? '' );
30
  }
31
 
32
  return $this->getMod()
47
  'logo' => $logoSrc,
48
  ],
49
  'strings' => [
50
+ 'security_level' => __( 'Level', 'wp-simple-firewall' ),
51
  'security_progress' => __( 'Overall Security Progress', 'wp-simple-firewall' ),
52
  'progress_overview' => __( 'Go To Overview', 'wp-simple-firewall' ),
53
  'recent_blocked' => __( 'Recently Blocked', 'wp-simple-firewall' ),
83
  $vars = [
84
  'generated_at' => Services::Request()->ts(),
85
  'security_progress' => ( new Components() )
86
+ ->setCon( $con )
87
+ ->getComponent( 'all' ),
88
  'jump_links' => [
89
  [
90
  'href' => $modInsights->getUrl_SubInsightsPage( 'overview' ),
src/lib/src/Users/UserMetas.php CHANGED
@@ -59,40 +59,44 @@ class UserMetas {
59
  $userID = (int)$meta->user_id;
60
 
61
  $metaRecord = $metaLoader->loadMeta( $userID );
62
-
63
- $dataToUpdate = [];
64
-
65
- // Copy old meta to new:
66
- $directMap = [
67
- 'first_seen_at',
68
- 'last_login_at',
69
- 'hard_suspended_at',
70
- 'pass_started_at',
71
- ];
72
- foreach ( $directMap as $directMapKey ) {
73
- if ( !empty( $meta->{$directMapKey} ) && $meta->{$directMapKey} !== $metaRecord->{$directMapKey} ) {
74
- $dataToUpdate[ $directMapKey ] = $meta->{$directMapKey};
75
- unset( $meta->{$directMapKey} );
76
- }
77
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
- $mfaProfiles = [
80
- 'backup',
81
- 'email',
82
- 'ga',
83
- 'u2f',
84
- 'yubi',
85
- ];
86
- foreach ( $mfaProfiles as $profile ) {
87
- $metaKey = $profile.'_validated';
88
- if ( !empty( $meta->{$metaKey} ) && empty( $metaRecord->{$profile.'_ready_at'} ) ) {
89
- $dataToUpdate[ $profile.'_ready_at' ] = Services::Request()->ts();
 
90
  }
91
- }
92
 
93
- if ( !empty( $dataToUpdate ) ) {
94
- $dbh->getQueryUpdater()->updateRecord( $metaRecord, $dataToUpdate );
95
- $metaRecord = $metaLoader->loadMeta( $userID );
 
96
  }
97
 
98
  $meta->record = $metaRecord;
59
  $userID = (int)$meta->user_id;
60
 
61
  $metaRecord = $metaLoader->loadMeta( $userID );
62
+ if ( empty( $metaRecord ) ) {
63
+ $metaRecord = $dbh->getRecord();
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  }
65
+ else {
66
+ $dataToUpdate = [];
67
+
68
+ // Copy old meta to new:
69
+ $directMap = [
70
+ 'first_seen_at',
71
+ 'last_login_at',
72
+ 'hard_suspended_at',
73
+ 'pass_started_at',
74
+ ];
75
+ foreach ( $directMap as $directMapKey ) {
76
+ if ( !empty( $meta->{$directMapKey} ) && $meta->{$directMapKey} !== $metaRecord->{$directMapKey} ) {
77
+ $dataToUpdate[ $directMapKey ] = $meta->{$directMapKey};
78
+ unset( $meta->{$directMapKey} );
79
+ }
80
+ }
81
 
82
+ $mfaProfiles = [
83
+ 'backup',
84
+ 'email',
85
+ 'ga',
86
+ 'u2f',
87
+ 'yubi',
88
+ ];
89
+ foreach ( $mfaProfiles as $profile ) {
90
+ $metaKey = $profile.'_validated';
91
+ if ( !empty( $meta->{$metaKey} ) && empty( $metaRecord->{$profile.'_ready_at'} ) ) {
92
+ $dataToUpdate[ $profile.'_ready_at' ] = Services::Request()->ts();
93
+ }
94
  }
 
95
 
96
+ if ( !empty( $dataToUpdate ) ) {
97
+ $dbh->getQueryUpdater()->updateRecord( $metaRecord, $dataToUpdate );
98
+ $metaRecord = $metaLoader->loadMeta( $userID );
99
+ }
100
  }
101
 
102
  $meta->record = $metaRecord;
src/lib/vendor/composer/autoload_classmap.php CHANGED
@@ -107,6 +107,7 @@ return array(
107
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Read' => $baseDir . '/src/Controller/Config/Ops/Read.php',
108
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Save' => $baseDir . '/src/Controller/Config/Ops/Save.php',
109
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Controller' => $baseDir . '/src/Controller/Controller.php',
 
110
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Exceptions\\VersionMismatchException' => $baseDir . '/src/Controller/Exceptions/VersionMismatchException.php',
111
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\I18n\\GetAllAvailableLocales' => $baseDir . '/src/Controller/I18n/GetAllAvailableLocales.php',
112
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\I18n\\LoadTextDomain' => $baseDir . '/src/Controller/I18n/LoadTextDomain.php',
107
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Read' => $baseDir . '/src/Controller/Config/Ops/Read.php',
108
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Save' => $baseDir . '/src/Controller/Config/Ops/Save.php',
109
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Controller' => $baseDir . '/src/Controller/Controller.php',
110
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Exceptions\\PluginConfigInvalidException' => $baseDir . '/src/Controller/Exceptions/PluginConfigInvalidException.php',
111
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Exceptions\\VersionMismatchException' => $baseDir . '/src/Controller/Exceptions/VersionMismatchException.php',
112
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\I18n\\GetAllAvailableLocales' => $baseDir . '/src/Controller/I18n/GetAllAvailableLocales.php',
113
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\I18n\\LoadTextDomain' => $baseDir . '/src/Controller/I18n/LoadTextDomain.php',
src/lib/vendor/composer/autoload_static.php CHANGED
@@ -294,6 +294,7 @@ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
294
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Read' => __DIR__ . '/../..' . '/src/Controller/Config/Ops/Read.php',
295
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Save' => __DIR__ . '/../..' . '/src/Controller/Config/Ops/Save.php',
296
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Controller' => __DIR__ . '/../..' . '/src/Controller/Controller.php',
 
297
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Exceptions\\VersionMismatchException' => __DIR__ . '/../..' . '/src/Controller/Exceptions/VersionMismatchException.php',
298
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\I18n\\GetAllAvailableLocales' => __DIR__ . '/../..' . '/src/Controller/I18n/GetAllAvailableLocales.php',
299
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\I18n\\LoadTextDomain' => __DIR__ . '/../..' . '/src/Controller/I18n/LoadTextDomain.php',
294
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Read' => __DIR__ . '/../..' . '/src/Controller/Config/Ops/Read.php',
295
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Save' => __DIR__ . '/../..' . '/src/Controller/Config/Ops/Save.php',
296
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Controller' => __DIR__ . '/../..' . '/src/Controller/Controller.php',
297
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Exceptions\\PluginConfigInvalidException' => __DIR__ . '/../..' . '/src/Controller/Exceptions/PluginConfigInvalidException.php',
298
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Exceptions\\VersionMismatchException' => __DIR__ . '/../..' . '/src/Controller/Exceptions/VersionMismatchException.php',
299
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\I18n\\GetAllAvailableLocales' => __DIR__ . '/../..' . '/src/Controller/I18n/GetAllAvailableLocales.php',
300
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\I18n\\LoadTextDomain' => __DIR__ . '/../..' . '/src/Controller/I18n/LoadTextDomain.php',
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Consumers/RequestCacheConsumer.php CHANGED
@@ -12,11 +12,13 @@ trait RequestCacheConsumer {
12
  * @param mixed|null $result
13
  * @return $this
14
  */
15
- protected function addCache( string $key, $result ) {
16
  $store = $this->loadCacheStore();
17
  $store[ $key ] = $result;
18
  self::$cache = $store;
19
- Transient::Set( 'apto-request-cache-consumer', self::$cache, 60 );
 
 
20
  return $this;
21
  }
22
 
@@ -27,9 +29,11 @@ trait RequestCacheConsumer {
27
  return $this->loadCacheStore()[ $key ] ?? null;
28
  }
29
 
30
- private function loadCacheStore() :array {
31
  if ( !isset( self::$cache ) ) {
32
- self::$cache = Transient::Get( 'apto-request-cache-consumer' );
 
 
33
  if ( !is_array( self::$cache ) ) {
34
  self::$cache = [];
35
  }
12
  * @param mixed|null $result
13
  * @return $this
14
  */
15
+ protected function addCache( string $key, $result, bool $setTransient = false ) {
16
  $store = $this->loadCacheStore();
17
  $store[ $key ] = $result;
18
  self::$cache = $store;
19
+ if ( $setTransient ) {
20
+ Transient::Set( 'apto-request-cache-consumer', self::$cache, 60 );
21
+ }
22
  return $this;
23
  }
24
 
29
  return $this->loadCacheStore()[ $key ] ?? null;
30
  }
31
 
32
+ private function loadCacheStore( bool $getTransient = false ) :array {
33
  if ( !isset( self::$cache ) ) {
34
+ if ( $getTransient ) {
35
+ self::$cache = Transient::Get( 'apto-request-cache-consumer' );
36
+ }
37
  if ( !is_array( self::$cache ) ) {
38
  self::$cache = [];
39
  }
templates/twig/admin/admin_dashboard_widget.twig CHANGED
@@ -3,15 +3,15 @@
3
  </a>
4
 
5
  <div class="shield-progress-bar
6
- {% if vars.security_progress < 45 %}
7
  red
8
- {% elseif vars.security_progress < 70 %}
9
  orange
10
  {% endif %}
11
  ">
12
- <a class="thebar" style="width: {{ vars.security_progress }}%"
13
  href="{{ hrefs.overview }}" title="{{ strings.progress_overview }}">
14
- <div class="thepercent">{{ vars.security_progress }}&percnt;</div>
15
  </a>
16
  </div>
17
 
3
  </a>
4
 
5
  <div class="shield-progress-bar
6
+ {% if vars.security_progress.original_score < 45 %}
7
  red
8
+ {% elseif vars.security_progress.original_score < 70 %}
9
  orange
10
  {% endif %}
11
  ">
12
+ <a class="thebar" style="width: {{ vars.security_progress.original_score }}%"
13
  href="{{ hrefs.overview }}" title="{{ strings.progress_overview }}">
14
+ <div class="thepercent">{{ strings.security_level }}: {{ vars.security_progress.letter_score }}</div>
15
  </a>
16
  </div>
17
 
templates/twig/wpadmin_pages/insights/overview/progress_meter/analysis/standard.twig CHANGED
@@ -8,6 +8,14 @@
8
  <div class="offcanvas-body">
9
  {% block body %}
10
  {% block body_components %}
 
 
 
 
 
 
 
 
11
  <ul class="list-group">
12
 
13
  {% for component in vars.components %}
8
  <div class="offcanvas-body">
9
  {% block body %}
10
  {% block body_components %}
11
+
12
+ <div class="alert alert-secondary mb-4">
13
+ <h3 class="m-0">{{ strings.total_score }}:
14
+ <span class="badge bg-dark text-white float-end">
15
+ {{ vars.total_percentage_score }}/100
16
+ </span>
17
+ </h3>
18
+ </div>
19
  <ul class="list-group">
20
 
21
  {% for component in vars.components %}
templates/twig/wpadmin_pages/insights/overview/progress_meter/progress_meters.twig CHANGED
@@ -16,11 +16,14 @@
16
  <div class="row g-0">
17
  <div class="col-md-4 col-lg-5 col-xl-5 d-flex justify-content-center align-items-center py-lg-4">
18
 
19
- <div class="pie" data-pie='{
 
 
20
  "colorSlice": "rgb( {{ meter.rgbs|join(',') }} )",
21
- "percent": {{ meter.totals.percentage }},
22
- "colorCircle": "#e6e6e6"
23
- }'></div>
 
24
 
25
  </div>
26
 
16
  <div class="row g-0">
17
  <div class="col-md-4 col-lg-5 col-xl-5 d-flex justify-content-center align-items-center py-lg-4">
18
 
19
+ <div class="pie d-flex align-items-center justify-content-center" data-pie='{
20
+ "number": false,
21
+ "colorCircle": "rgba( {{ meter.rgbs|join(',') }},0.08 )",
22
  "colorSlice": "rgb( {{ meter.rgbs|join(',') }} )",
23
+ "percent": {{ meter.totals.percentage }}
24
+ }'>
25
+ <div class="letter-score" style="color: rgb( {{ meter.rgbs|join(',') }} )">{{ meter.totals.letter_score }}</div>
26
+ </div>
27
 
28
  </div>
29