Shield Security for WordPress - Version 16.1.4

Version Description

Download this release

Release Info

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

Code changes from version 16.1.3 to 16.1.4

cl.json CHANGED
@@ -128,8 +128,8 @@
128
  ],
129
  "patches": [
130
  {
131
- "version": "3",
132
- "released_at": 1663061215,
133
  "items": [
134
  {
135
  "title": "Security fix for reported 2FA vulnerability. More info will be released after allowing time for client upgrades.",
@@ -146,6 +146,10 @@
146
  {
147
  "title": "Bug: an error was generated when assessing some IP addresses.",
148
  "type": "fixed"
 
 
 
 
149
  }
150
  ]
151
  },
128
  ],
129
  "patches": [
130
  {
131
+ "version": "4",
132
+ "released_at": 1663064000,
133
  "items": [
134
  {
135
  "title": "Security fix for reported 2FA vulnerability. More info will be released after allowing time for client upgrades.",
146
  {
147
  "title": "Bug: an error was generated when assessing some IP addresses.",
148
  "type": "fixed"
149
+ },
150
+ {
151
+ "title": "Bug: API requests for certain types of options were appearing to fail (they weren't) and generating an error.",
152
+ "type": "fixed"
153
  }
154
  ]
155
  },
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: 16.1.3
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: 16.1.4
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": "16.1.3",
4
- "release_timestamp": 1663061215,
5
- "build": "202209.1301",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "text_domain": "wp-simple-firewall",
1
  {
2
  "properties": {
3
+ "version": "16.1.4",
4
+ "release_timestamp": 1663064000,
5
+ "build": "202209.1302",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "text_domain": "wp-simple-firewall",
plugin.json CHANGED
@@ -1,8 +1,8 @@
1
  {
2
  "properties": {
3
- "version": "16.1.3",
4
- "release_timestamp": 1663061215,
5
- "build": "202209.1301",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "text_domain": "wp-simple-firewall",
1
  {
2
  "properties": {
3
+ "version": "16.1.4",
4
+ "release_timestamp": 1663064000,
5
+ "build": "202209.1302",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "text_domain": "wp-simple-firewall",
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: 16.1.3
12
 
13
  Bad Bots Are Your #1 Security Risk. Stop playing whack-a-mole with malware and vulnerabilities. Discover the advantage of putting real security before marketing.
14
 
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 6.0
11
+ Stable tag: 16.1.4
12
 
13
  Bad Bots Are Your #1 Security Risk. Stop playing whack-a-mole with malware and vulnerabilities. Discover the advantage of putting real security before marketing.
14
 
resources/js/shield/login2fa.js CHANGED
@@ -139,5 +139,4 @@ jQuery( document ).ready( function () {
139
  } )
140
  .catch();
141
  }
142
-
143
  } );
139
  } )
140
  .catch();
141
  }
 
142
  } );
src/lib/src/Modules/Base/Options.php CHANGED
@@ -502,13 +502,13 @@ class Options {
502
 
503
  if ( $verified ) {
504
  // Here we try to ensure that values that are repeatedly changed properly reflect their changed
505
- // states, as they may be reverted back to their original state and we "think" it's been changed.
506
- $bValueIsDifferent = serialize( $mCurrent ) !== serialize( $newValue );
507
  // basically if we're actually resetting back to the original value
508
- $bIsResetting = $bValueIsDifferent && $this->isOptChanged( $key )
509
- && ( serialize( $this->getOldValue( $key ) ) === serialize( $newValue ) );
510
 
511
- if ( $bValueIsDifferent && $this->verifyCanSet( $key, $newValue ) ) {
512
  $this->setNeedSave( true );
513
 
514
  //Load the config and do some pre-set verification where possible. This will slowly grow.
@@ -519,7 +519,7 @@ class Options {
519
  ->setOptValue( $key, $newValue );
520
  }
521
 
522
- if ( $bIsResetting ) {
523
  unset( $this->aOld[ $key ] );
524
  }
525
  }
502
 
503
  if ( $verified ) {
504
  // Here we try to ensure that values that are repeatedly changed properly reflect their changed
505
+ // states, as they may be reverted to their original state and we "think" it's been changed.
506
+ $valueIsDifferent = serialize( $mCurrent ) !== serialize( $newValue );
507
  // basically if we're actually resetting back to the original value
508
+ $isResetting = $valueIsDifferent && $this->isOptChanged( $key )
509
+ && ( serialize( $this->getOldValue( $key ) ) === serialize( $newValue ) );
510
 
511
+ if ( $valueIsDifferent && $this->verifyCanSet( $key, $newValue ) ) {
512
  $this->setNeedSave( true );
513
 
514
  //Load the config and do some pre-set verification where possible. This will slowly grow.
519
  ->setOptValue( $key, $newValue );
520
  }
521
 
522
+ if ( $isResetting ) {
523
  unset( $this->aOld[ $key ] );
524
  }
525
  }
src/lib/src/Modules/Base/Options/OptValueSanitize.php CHANGED
@@ -20,43 +20,43 @@ class OptValueSanitize {
20
  throw new \Exception( sprintf( 'Not a valid option key for module: %s', $key ) );
21
  }
22
 
23
- $validValue = false;
24
  switch ( $opts->getOptionType( $key ) ) {
25
 
26
  case 'boolean':
27
- $validValue = is_bool( $value );
28
  break;
29
 
30
  case 'integer':
31
- $validValue = is_numeric( $value );
32
- if ( $validValue ) {
33
  $value = (int)$value;
34
  }
35
  break;
36
 
37
  case 'email':
38
  $value = trim( (string)$value );
39
- $validValue = empty( $value ) || Services::Data()->validEmail( $value );
40
  break;
41
 
42
  case 'array':
43
- $validValue = is_array( $value );
44
  break;
45
 
46
  case 'text':
47
  if ( is_null( $value ) || is_scalar( $value ) ) {
48
- $validValue = true;
49
  $value = (string)$value;
50
  }
51
  break;
52
 
53
  case 'select':
54
- $validValue = is_string( $value ) && strlen( $value ) > 0;
55
  break;
56
 
57
  case 'multiple_select':
58
  if ( is_array( $value ) ) {
59
- $validValue = count( array_diff(
60
  $value,
61
  array_map(
62
  function ( $aValueOption ) {
@@ -71,16 +71,16 @@ class OptValueSanitize {
71
  case 'checkbox':
72
  if ( is_string( $value ) ) {
73
  $value = strtoupper( $value );
74
- $validValue = in_array( $value, [ 'Y', 'N' ] );
75
  }
76
  break;
77
 
78
  default:
79
- $validValue = true;
80
  break;
81
  }
82
 
83
- if ( !$validValue ) {
84
  throw new \Exception( 'Not a valid value type for option.' );
85
  }
86
 
20
  throw new \Exception( sprintf( 'Not a valid option key for module: %s', $key ) );
21
  }
22
 
23
+ $isValid = false;
24
  switch ( $opts->getOptionType( $key ) ) {
25
 
26
  case 'boolean':
27
+ $isValid = is_bool( $value );
28
  break;
29
 
30
  case 'integer':
31
+ $isValid = is_numeric( $value );
32
+ if ( $isValid ) {
33
  $value = (int)$value;
34
  }
35
  break;
36
 
37
  case 'email':
38
  $value = trim( (string)$value );
39
+ $isValid = empty( $value ) || Services::Data()->validEmail( $value );
40
  break;
41
 
42
  case 'array':
43
+ $isValid = is_array( $value );
44
  break;
45
 
46
  case 'text':
47
  if ( is_null( $value ) || is_scalar( $value ) ) {
48
+ $isValid = true;
49
  $value = (string)$value;
50
  }
51
  break;
52
 
53
  case 'select':
54
+ $isValid = is_string( $value ) && strlen( $value ) > 0;
55
  break;
56
 
57
  case 'multiple_select':
58
  if ( is_array( $value ) ) {
59
+ $isValid = count( array_diff(
60
  $value,
61
  array_map(
62
  function ( $aValueOption ) {
71
  case 'checkbox':
72
  if ( is_string( $value ) ) {
73
  $value = strtoupper( $value );
74
+ $isValid = in_array( $value, [ 'Y', 'N' ] );
75
  }
76
  break;
77
 
78
  default:
79
+ $isValid = true;
80
  break;
81
  }
82
 
83
+ if ( !$isValid ) {
84
  throw new \Exception( 'Not a valid value type for option.' );
85
  }
86
 
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/LoginIntentRequestCapture.php CHANGED
@@ -79,7 +79,7 @@ class LoginIntentRequestCapture extends Shield\Modules\Base\Common\ExecOnceModCo
79
  $pageRender = $mfaCon->useLoginIntentPage() ? new Render\RenderLoginIntentPage() : new Render\RenderWpLoginReplica();
80
  $pageRender->setMod( $mod )
81
  ->setWpUser( $this->user );
82
- $pageRender->login_nonce = $req->request( 'login_nonce', false, '' );
83
  $pageRender->redirect_to = $req->request( 'redirect_to', false, '' );
84
  $pageRender->rememberme = $req->request( 'rememberme' );
85
  $pageRender->msg_error = __( 'Could not verify your 2FA codes', 'wp-simple-firewall' );
@@ -105,11 +105,7 @@ class LoginIntentRequestCapture extends Shield\Modules\Base\Common\ExecOnceModCo
105
  $valid = ( new LoginIntentRequestValidate() )
106
  ->setMod( $mod )
107
  ->setWpUser( $this->user )
108
- ->run( (string)$req->post( 'login_nonce' ) );
109
-
110
- if ( $req->post( 'cancel' ) ) {
111
- throw new LoginCancelException();
112
- }
113
 
114
  if ( $valid ) {
115
  wp_set_auth_cookie( $this->user->ID, (bool)$req->post( 'rememberme' ) );
79
  $pageRender = $mfaCon->useLoginIntentPage() ? new Render\RenderLoginIntentPage() : new Render\RenderWpLoginReplica();
80
  $pageRender->setMod( $mod )
81
  ->setWpUser( $this->user );
82
+ $pageRender->plain_login_nonce = $req->request( 'login_nonce', false, '' );
83
  $pageRender->redirect_to = $req->request( 'redirect_to', false, '' );
84
  $pageRender->rememberme = $req->request( 'rememberme' );
85
  $pageRender->msg_error = __( 'Could not verify your 2FA codes', 'wp-simple-firewall' );
105
  $valid = ( new LoginIntentRequestValidate() )
106
  ->setMod( $mod )
107
  ->setWpUser( $this->user )
108
+ ->run( (string)$req->post( 'login_nonce' ), $req->post( 'cancel' ) == '1' );
 
 
 
 
109
 
110
  if ( $valid ) {
111
  wp_set_auth_cookie( $this->user->ID, (bool)$req->post( 'rememberme' ) );
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/LoginIntentRequestValidate.php CHANGED
@@ -5,8 +5,9 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFact
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor\Exceptions\{
7
  CouldNotValidate2FA,
8
- NoActiveProvidersForUserException,
9
  InvalidLoginIntentException,
 
 
10
  TooManyAttemptsException
11
  };
12
 
@@ -17,11 +18,12 @@ class LoginIntentRequestValidate {
17
 
18
  /**
19
  * @throws CouldNotValidate2FA
20
- * @throws NoActiveProvidersForUserException
21
  * @throws InvalidLoginIntentException
 
22
  * @throws TooManyAttemptsException
23
  */
24
- public function run( string $plainNonce ) :bool {
25
  /** @var Shield\Modules\LoginGuard\ModCon $mod */
26
  $mod = $this->getMod();
27
  $mfaCon = $mod->getMfaController();
@@ -31,6 +33,11 @@ class LoginIntentRequestValidate {
31
  throw new InvalidLoginIntentException();
32
  }
33
 
 
 
 
 
 
34
  $providers = $mfaCon->getProvidersForUser( $user, true );
35
  if ( empty( $providers ) ) {
36
  throw new NoActiveProvidersForUserException();
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor\Exceptions\{
7
  CouldNotValidate2FA,
 
8
  InvalidLoginIntentException,
9
+ LoginCancelException,
10
+ NoActiveProvidersForUserException,
11
  TooManyAttemptsException
12
  };
13
 
18
 
19
  /**
20
  * @throws CouldNotValidate2FA
21
+ * @throws LoginCancelException
22
  * @throws InvalidLoginIntentException
23
+ * @throws NoActiveProvidersForUserException
24
  * @throws TooManyAttemptsException
25
  */
26
+ public function run( string $plainNonce, bool $isCancel = false ) :bool {
27
  /** @var Shield\Modules\LoginGuard\ModCon $mod */
28
  $mod = $this->getMod();
29
  $mfaCon = $mod->getMfaController();
33
  throw new InvalidLoginIntentException();
34
  }
35
 
36
+ if ( $isCancel ) {
37
+ // only allowed to cancel if the intent is verified.
38
+ throw new LoginCancelException();
39
+ }
40
+
41
  $providers = $mfaCon->getProvidersForUser( $user, true );
42
  if ( empty( $providers ) ) {
43
  throw new NoActiveProvidersForUserException();
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/LoginRequestCapture.php CHANGED
@@ -53,7 +53,7 @@ class LoginRequestCapture extends Shield\Modules\Base\Common\ExecOnceModConsumer
53
  $pageRender = $mfaCon->useLoginIntentPage() ? new Render\RenderLoginIntentPage() : new Render\RenderWpLoginReplica();
54
  $pageRender->setMod( $mod )
55
  ->setWpUser( $user );
56
- $pageRender->login_nonce = $loginNonce;
57
  $pageRender->interim_login = $req->request( 'interim-login' );
58
  $pageRender->redirect_to = $req->request( 'redirect_to', false, '' );
59
  $pageRender->rememberme = $req->request( 'rememberme' );
53
  $pageRender = $mfaCon->useLoginIntentPage() ? new Render\RenderLoginIntentPage() : new Render\RenderWpLoginReplica();
54
  $pageRender->setMod( $mod )
55
  ->setWpUser( $user );
56
+ $pageRender->plain_login_nonce = $loginNonce;
57
  $pageRender->interim_login = $req->request( 'interim-login' );
58
  $pageRender->redirect_to = $req->request( 'redirect_to', false, '' );
59
  $pageRender->rememberme = $req->request( 'rememberme' );
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php CHANGED
@@ -188,6 +188,9 @@ class MfaController extends Shield\Modules\Base\Common\ExecOnceModConsumer {
188
  return $result;
189
  }
190
 
 
 
 
191
  public function getActiveLoginIntents( \WP_User $user ) :array {
192
  $meta = $this->getCon()->getUserMeta( $user );
193
  return array_filter(
188
  return $result;
189
  }
190
 
191
+ /**
192
+ * @return array[]
193
+ */
194
  public function getActiveLoginIntents( \WP_User $user ) :array {
195
  $meta = $this->getCon()->getUserMeta( $user );
196
  return array_filter(
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Render/RenderBase.php CHANGED
@@ -2,24 +2,37 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor\Render;
4
 
5
- use FernleafSystems\Utilities\Data\Adapter\DynProperties;
6
  use FernleafSystems\Wordpress\Plugin\Shield;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
10
  /**
11
- * @property string $login_nonce
12
  * @property string $interim_login
13
  * @property string $rememberme
14
  * @property string $redirect_to
15
  * @property string $msg_error
16
  * @property string $interim_message
17
  */
18
- abstract class RenderBase {
19
 
20
  use Shield\Modules\ModConsumer;
21
  use Shield\Utilities\Consumer\WpUserConsumer;
22
- use DynProperties;
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  public function render() {
25
  echo $this->buildPage();
@@ -114,7 +127,7 @@ abstract class RenderBase {
114
 
115
  $fields = array_filter( [
116
  'interim-login' => ( $interim_login || $this->interim_login ) ? '1' : false,
117
- 'login_nonce' => $this->login_nonce,
118
  'rememberme' => esc_attr( $this->rememberme ),
119
  'redirect_to' => esc_attr( esc_url( $redirectTo ) ),
120
  'cancel_href' => esc_attr( esc_url( $cancelHref ) ),
@@ -134,7 +147,9 @@ abstract class RenderBase {
134
  $mfaCon = $mod->getMfaController();
135
  /** @var LoginGuard\Options $opts */
136
  $opts = $this->getOptions();
137
- $intentAt = $mfaCon->getActiveLoginIntents( $this->getWpUser() )[ $this->login_nonce ][ 'start' ] ?? 0;
 
 
138
  return Services::Request()
139
  ->carbon()
140
  ->setTimestamp( $intentAt )
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor\Render;
4
 
5
+ use FernleafSystems\Utilities\Data\Adapter\DynPropertiesClass;
6
  use FernleafSystems\Wordpress\Plugin\Shield;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
10
  /**
11
+ * @property string $plain_login_nonce
12
  * @property string $interim_login
13
  * @property string $rememberme
14
  * @property string $redirect_to
15
  * @property string $msg_error
16
  * @property string $interim_message
17
  */
18
+ abstract class RenderBase extends DynPropertiesClass {
19
 
20
  use Shield\Modules\ModConsumer;
21
  use Shield\Utilities\Consumer\WpUserConsumer;
22
+
23
+ public function __get( string $key ) {
24
+ $value = parent::__get( $key );
25
+ switch ( $key ) {
26
+ case 'plain_login_nonce':
27
+ case 'msg_error':
28
+ case 'interim_message':
29
+ $value = (string)$value;
30
+ break;
31
+ default:
32
+ break;
33
+ }
34
+ return $value;
35
+ }
36
 
37
  public function render() {
38
  echo $this->buildPage();
127
 
128
  $fields = array_filter( [
129
  'interim-login' => ( $interim_login || $this->interim_login ) ? '1' : false,
130
+ 'login_nonce' => $this->plain_login_nonce,
131
  'rememberme' => esc_attr( $this->rememberme ),
132
  'redirect_to' => esc_attr( esc_url( $redirectTo ) ),
133
  'cancel_href' => esc_attr( esc_url( $cancelHref ) ),
147
  $mfaCon = $mod->getMfaController();
148
  /** @var LoginGuard\Options $opts */
149
  $opts = $this->getOptions();
150
+
151
+ $intentAt = $mfaCon->getActiveLoginIntents( $this->getWpUser() )
152
+ [ $mfaCon->findHashedNonce( $this->getWpUser(), $this->plain_login_nonce ) ][ 'start' ] ?? 0;
153
  return Services::Request()
154
  ->carbon()
155
  ->setTimestamp( $intentAt )
src/lib/src/Modules/Plugin/Rest/Request/Options/SetBulk.php CHANGED
@@ -2,6 +2,8 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Rest\Request\Options;
4
 
 
 
5
  class SetBulk extends Base {
6
 
7
  /**
@@ -22,9 +24,18 @@ class SetBulk extends Base {
22
  $opts->resetOptToDefault( $opt[ 'key' ] );
23
  }
24
  else {
 
 
 
 
 
 
 
 
25
  $opts->setOpt( $opt[ 'key' ], $opt[ 'value' ] );
 
26
  if ( serialize( $opt[ 'value' ] ) !== serialize( $opts->getOpt( $opt[ 'key' ] ) ) ) {
27
- throw new \Exception( sprintf( 'Failed to update option (%s). Value may be of an incorrect type.', $opt[ 'key' ] ) );
28
  }
29
  }
30
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Rest\Request\Options;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Core\Rest\Exceptions\ApiException;
6
+
7
  class SetBulk extends Base {
8
 
9
  /**
24
  $opts->resetOptToDefault( $opt[ 'key' ] );
25
  }
26
  else {
27
+ /**
28
+ * It turns out JSON-encoded integers come out as type:double, so we have to convert it,
29
+ * so we can validate it after the fact using serialize, or we'll get i:0 vs d:0.
30
+ */
31
+ if ( $def[ 'type' ] === 'integer' ) {
32
+ $opt[ 'value' ] = (int)$opt[ 'value' ];
33
+ }
34
+
35
  $opts->setOpt( $opt[ 'key' ], $opt[ 'value' ] );
36
+
37
  if ( serialize( $opt[ 'value' ] ) !== serialize( $opts->getOpt( $opt[ 'key' ] ) ) ) {
38
+ throw new ApiException( sprintf( 'Failed to update option (%s). Value may be of an incorrect type.', $opt[ 'key' ] ) );
39
  }
40
  }
41
  }