Shield Security for WordPress - Version 16.1.11

Version Description

Download this release

Release Info

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

Code changes from version 16.1.10 to 16.1.11

cl.json CHANGED
@@ -128,9 +128,17 @@
128
  ],
129
  "patches": [
130
  {
131
- "version": "10",
132
- "released_at": 1666622000,
133
  "items": [
 
 
 
 
 
 
 
 
134
  {
135
  "title": "Accessibility of user 2FA setup form has been improved for screen readers.",
136
  "type": "improved"
128
  ],
129
  "patches": [
130
  {
131
+ "version": "11",
132
+ "released_at": 1666868000,
133
  "items": [
134
+ {
135
+ "title": "Attempt to eliminate CrowdSec API issues with repeated logins.",
136
+ "type": "improved"
137
+ },
138
+ {
139
+ "title": "Attempt to mitigate import/export errors for certain configurations.",
140
+ "type": "improved"
141
+ },
142
  {
143
  "title": "Accessibility of user 2FA setup form has been improved for screen readers.",
144
  "type": "improved"
config/plugin.json CHANGED
@@ -555,6 +555,20 @@
555
  "section": "section_non_ui",
556
  "type": "integer",
557
  "default": 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  }
559
  ],
560
  "definitions": {
555
  "section": "section_non_ui",
556
  "type": "integer",
557
  "default": 0
558
+ },
559
+ {
560
+ "key": "import_id",
561
+ "transferable": false,
562
+ "section": "section_non_ui",
563
+ "type": "text",
564
+ "default": ""
565
+ },
566
+ {
567
+ "key": "import_url_ids",
568
+ "transferable": false,
569
+ "section": "section_non_ui",
570
+ "type": "array",
571
+ "default": []
572
  }
573
  ],
574
  "definitions": {
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.10
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.11
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.10",
4
- "release_timestamp": 1666622000,
5
- "build": "202210.2401",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "text_domain": "wp-simple-firewall",
1
  {
2
  "properties": {
3
+ "version": "16.1.11",
4
+ "release_timestamp": 1666868000,
5
+ "build": "202210.2701",
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.10",
4
- "release_timestamp": 1666622000,
5
- "build": "202210.2401",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "text_domain": "wp-simple-firewall",
1
  {
2
  "properties": {
3
+ "version": "16.1.11",
4
+ "release_timestamp": 1666868000,
5
+ "build": "202210.2701",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "text_domain": "wp-simple-firewall",
readme.txt CHANGED
@@ -7,10 +7,10 @@ Tags: limit login, malware scan, firewall, two factor authentication, login prot
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.10
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
 
15
  == Description ==
16
 
7
  Requires at least: 3.7
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
+ Tested up to: 6.1
11
+ Stable tag: 16.1.11
12
 
13
+ Bad Bots Are Your #1 Security Risk. Are you playing whack-a-mole with malware & vulnerabilities? Discover the real security before marketing hype.
14
 
15
  == Description ==
16
 
src/lib/src/Modules/IPs/Lib/CrowdSec/CrowdSecApi.php CHANGED
@@ -4,16 +4,14 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\CrowdSec;
4
 
5
  use Carbon\Carbon;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\{
8
- ModCon,
9
- Options
10
- };
11
  use FernleafSystems\Wordpress\Services\Services;
12
 
13
  class CrowdSecApi {
14
 
15
  use ModConsumer;
16
 
 
17
  const STATE_NO_URL = 'no_url';
18
  const STATE_INVALID_URL = 'invalid_url';
19
  const STATE_NO_MACHINE_ID = 'no_mach_id';
@@ -22,16 +20,16 @@ class CrowdSecApi {
22
  const STATE_NO_AUTH_TOKEN = 'no_auth_token';
23
  const STATE_NO_AUTH_EXPIRE = 'no_auth_expire';
24
  const STATE_AUTH_EXPIRED = 'auth_expired';
25
- const STATE_NO_ENROLL_ID = 'no_enroll_id';
26
- const STATE_MACH_NOT_ENROLLED = 'mach_not_enrolled';
27
- const STATE_READY = 'ready';
28
 
29
  public function isReady() :bool {
30
  $this->login();
31
  return in_array( $this->getAuthStatus(), [
32
- self::STATE_NO_ENROLL_ID,
33
- self::STATE_MACH_NOT_ENROLLED,
34
- self::STATE_READY
35
  ] );
36
  }
37
 
@@ -71,13 +69,13 @@ class CrowdSecApi {
71
  $state = self::STATE_AUTH_EXPIRED;
72
  }
73
  elseif ( empty( $this->getOptions()->getOpt( 'cs_enroll_id' ) ) ) {
74
- $state = self::STATE_NO_ENROLL_ID;
75
  }
76
  elseif ( empty( $csAuth[ 'machine_enrolled' ] ) ) {
77
- $state = self::STATE_MACH_NOT_ENROLLED;
78
  }
79
  else {
80
- $state = self::STATE_READY;
81
  }
82
 
83
  return $state;
@@ -97,7 +95,7 @@ class CrowdSecApi {
97
  catch ( Exceptions\AuthenticationInProgressException $e ) {
98
  $clearAuthStartAt = false;
99
  }
100
- catch ( Exceptions\MachineRegisterFailedException $e ) {
101
  $fieldsToClear = [
102
  'machine_id',
103
  'machine_registered',
@@ -109,6 +107,7 @@ class CrowdSecApi {
109
  }
110
  catch ( Exceptions\MachineLoginFailedException $e ) {
111
  $fieldsToClear = [
 
112
  'machine_id',
113
  'machine_registered',
114
  'password',
@@ -126,7 +125,7 @@ class CrowdSecApi {
126
  }
127
  finally {
128
  if ( !empty( $e ) ) {
129
- error_log( $e->getMessage() );
130
  }
131
  if ( empty( $auth ) ) {
132
  $auth = $this->getCsAuth();
@@ -216,18 +215,33 @@ class CrowdSecApi {
216
  ( empty( $auth[ 'auth_token' ] ) || empty( $auth[ 'auth_expire' ] )
217
  || ( $auth[ 'auth_expire' ] < Services::Request()->ts() ) )
218
  ) {
219
- $login = ( new Api\MachineLogin( $this->getApiUserAgent() ) )
220
- ->run( $auth[ 'machine_id' ], $auth[ 'password' ], $this->getScenarios() );
221
 
222
- $auth[ 'auth_token' ] = $login[ 'token' ];
223
- $auth[ 'auth_expire' ] = ( new Carbon( $login[ 'expire' ] ) )->subMinute()->timestamp;
224
- $this->storeCsAuth( $auth );
225
 
226
- $this->getCon()->fireEvent( 'crowdsec_auth_acquire', [
227
- 'audit_params' => [
228
- 'expiration' => $login[ 'expire' ], // format: 2022-06-09T14:15:50Z
229
- ]
230
- ] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  }
232
  }
233
 
@@ -235,7 +249,7 @@ class CrowdSecApi {
235
  * @throws Exceptions\MachineEnrollFailedException
236
  */
237
  public function machineEnroll() {
238
- /** @var Options $opts */
239
  $opts = $this->getOptions();
240
 
241
  $auth = $this->getCsAuth();
@@ -310,7 +324,7 @@ class CrowdSecApi {
310
  } );
311
  Services::WpGeneral()->updateOption( $this->getCon()->prefix( 'cs_auths' ), $auths );
312
 
313
- /** @var ModCon $mod */
314
  $mod = $this->getMod();
315
  $cfg = $mod->getCrowdSecCon()->cfg();
316
  $cfg->cs_auths = $auths;
@@ -337,7 +351,7 @@ class CrowdSecApi {
337
  $pass .= substr( $chars, wp_rand( 26, 51 ), 1 );
338
  }
339
  if ( !preg_match( '#\d#', $pass ) ) {
340
- $pass .= substr( $chars, wp_rand( 52, 63 ), 1 );
341
  }
342
  return substr( $pass.wp_generate_password( 22, false ), 0, 32 );
343
  }
4
 
5
  use Carbon\Carbon;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
 
 
 
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
10
  class CrowdSecApi {
11
 
12
  use ModConsumer;
13
 
14
+ const MAX_FAILED_LOGINS = 5;
15
  const STATE_NO_URL = 'no_url';
16
  const STATE_INVALID_URL = 'invalid_url';
17
  const STATE_NO_MACHINE_ID = 'no_mach_id';
20
  const STATE_NO_AUTH_TOKEN = 'no_auth_token';
21
  const STATE_NO_AUTH_EXPIRE = 'no_auth_expire';
22
  const STATE_AUTH_EXPIRED = 'auth_expired';
23
+ const STATE_READY_NO_ENROLL_ID = 'ready_no_enroll_id';
24
+ const STATE_READY_MACH_NOT_ENROLLED = 'ready_mach_not_enrolled';
25
+ const STATE_READY_COMPLETE = 'ready_complete';
26
 
27
  public function isReady() :bool {
28
  $this->login();
29
  return in_array( $this->getAuthStatus(), [
30
+ self::STATE_READY_NO_ENROLL_ID,
31
+ self::STATE_READY_MACH_NOT_ENROLLED,
32
+ self::STATE_READY_COMPLETE
33
  ] );
34
  }
35
 
69
  $state = self::STATE_AUTH_EXPIRED;
70
  }
71
  elseif ( empty( $this->getOptions()->getOpt( 'cs_enroll_id' ) ) ) {
72
+ $state = self::STATE_READY_NO_ENROLL_ID;
73
  }
74
  elseif ( empty( $csAuth[ 'machine_enrolled' ] ) ) {
75
+ $state = self::STATE_READY_MACH_NOT_ENROLLED;
76
  }
77
  else {
78
+ $state = self::STATE_READY_COMPLETE;
79
  }
80
 
81
  return $state;
95
  catch ( Exceptions\AuthenticationInProgressException $e ) {
96
  $clearAuthStartAt = false;
97
  }
98
+ catch ( Exceptions\MachineRegisterFailedException $e ) {
99
  $fieldsToClear = [
100
  'machine_id',
101
  'machine_registered',
107
  }
108
  catch ( Exceptions\MachineLoginFailedException $e ) {
109
  $fieldsToClear = [
110
+ 'failed_login_count',
111
  'machine_id',
112
  'machine_registered',
113
  'password',
125
  }
126
  finally {
127
  if ( !empty( $e ) ) {
128
+ error_log( '[CROWDSEC EXCEPTION] '.$e->getMessage() );
129
  }
130
  if ( empty( $auth ) ) {
131
  $auth = $this->getCsAuth();
215
  ( empty( $auth[ 'auth_token' ] ) || empty( $auth[ 'auth_expire' ] )
216
  || ( $auth[ 'auth_expire' ] < Services::Request()->ts() ) )
217
  ) {
 
 
218
 
219
+ if ( !isset( $auth[ 'failed_login_count' ] ) ) {
220
+ $auth[ 'failed_login_count' ] = 0;
221
+ }
222
 
223
+ try {
224
+ $login = ( new Api\MachineLogin( $this->getApiUserAgent() ) )
225
+ ->run( $auth[ 'machine_id' ], $auth[ 'password' ], $this->getScenarios() );
226
+
227
+ $auth[ 'auth_token' ] = $login[ 'token' ];
228
+ $auth[ 'auth_expire' ] = ( new Carbon( $login[ 'expire' ] ) )->subMinute()->timestamp;
229
+ $auth[ 'failed_login_count' ] = 0;
230
+ $this->storeCsAuth( $auth );
231
+
232
+ $this->getCon()->fireEvent( 'crowdsec_auth_acquire', [
233
+ 'audit_params' => [
234
+ 'expiration' => $login[ 'expire' ], // format: 2022-06-09T14:15:50Z
235
+ ]
236
+ ] );
237
+ }
238
+ catch ( Exceptions\MachineLoginFailedException $e ) {
239
+ $auth[ 'failed_login_count' ]++;
240
+ if ( $auth[ 'failed_login_count' ] >= self::MAX_FAILED_LOGINS ) {
241
+ throw $e;
242
+ }
243
+ $this->storeCsAuth( $auth );
244
+ }
245
  }
246
  }
247
 
249
  * @throws Exceptions\MachineEnrollFailedException
250
  */
251
  public function machineEnroll() {
252
+ /** @var IPs\Options $opts */
253
  $opts = $this->getOptions();
254
 
255
  $auth = $this->getCsAuth();
324
  } );
325
  Services::WpGeneral()->updateOption( $this->getCon()->prefix( 'cs_auths' ), $auths );
326
 
327
+ /** @var IPs\ModCon $mod */
328
  $mod = $this->getMod();
329
  $cfg = $mod->getCrowdSecCon()->cfg();
330
  $cfg->cs_auths = $auths;
351
  $pass .= substr( $chars, wp_rand( 26, 51 ), 1 );
352
  }
353
  if ( !preg_match( '#\d#', $pass ) ) {
354
+ $pass .= substr( $chars, wp_rand( 52, 61 ), 1 );
355
  }
356
  return substr( $pass.wp_generate_password( 22, false ), 0, 32 );
357
  }
src/lib/src/Modules/Plugin/Lib/AllowBetaUpgrades.php CHANGED
@@ -15,6 +15,11 @@ class AllowBetaUpgrades extends ExecOnceModConsumer {
15
 
16
  use PluginCronsConsumer;
17
 
 
 
 
 
 
18
  protected function canRun() :bool {
19
  return $this->getCon()->isPremiumActive()
20
  && apply_filters( 'shield/enable_beta', $this->getOptions()->isOpt( 'enable_beta', 'Y' ) );
@@ -24,36 +29,56 @@ class AllowBetaUpgrades extends ExecOnceModConsumer {
24
  add_filter( 'pre_set_site_transient_update_plugins', function ( $updates ) {
25
  $con = $this->getCon();
26
  // only offer "betas" when there is no "normal" upgrade already available
27
- if ( is_object( $updates ) && isset( $updates->response )
28
- && is_array( $updates->response ) && empty( $updates->response[ $con->base_file ] ) ) {
29
-
30
- $thisPlugin = Services::WpPlugins()->getPluginAsVo( $con->base_file );
31
- $versionsLookup = ( new Versions() )->setWorkingSlug( $thisPlugin->slug );
32
- $betas = array_filter(
33
- $versionsLookup->all(),
34
- function ( $betaVersion ) {
35
- return is_string( $betaVersion )
36
- && preg_match( '#^\d(\.\d)+$#', $betaVersion )
37
- && version_compare( $betaVersion, $this->getCon()->getVersion(), '>' );
38
- }
39
- );
40
- if ( !empty( $betas ) ) {
41
- natsort( $betas );
42
- $beta = array_pop( $betas );
43
- $versionsLookup->setWorkingVersion( $beta );
44
- $url = $versionsLookup->allVersionsUrls()[ $beta ] ?? '';
45
- if ( !empty( $url ) ) {
46
- $update = new \stdClass();
47
- $update->id = $thisPlugin->id;
48
- $update->slug = $thisPlugin->slug;
49
- $update->plugin = $con->base_file;
50
- $update->new_version = $beta;
51
- $update->package = $url;
52
- $updates->response[ $con->base_file ] = $update;
53
- }
54
  }
55
  }
56
  return $updates;
57
  } );
58
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  }
15
 
16
  use PluginCronsConsumer;
17
 
18
+ /**
19
+ * @var \stdClass
20
+ */
21
+ private $beta;
22
+
23
  protected function canRun() :bool {
24
  return $this->getCon()->isPremiumActive()
25
  && apply_filters( 'shield/enable_beta', $this->getOptions()->isOpt( 'enable_beta', 'Y' ) );
29
  add_filter( 'pre_set_site_transient_update_plugins', function ( $updates ) {
30
  $con = $this->getCon();
31
  // only offer "betas" when there is no "normal" upgrade already available
32
+ if ( is_object( $updates )
33
+ && isset( $updates->response )
34
+ && is_array( $updates->response )
35
+ && empty( $updates->response[ $con->base_file ] ) ) {
36
+
37
+ $beta = $this->getBeta();
38
+ if ( !empty( $beta ) ) {
39
+ $updates->response[ $con->base_file ] = $beta;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  }
41
  }
42
  return $updates;
43
  } );
44
  }
45
+
46
+ private function getBeta() {
47
+ if ( !isset( $this->beta ) ) {
48
+ $con = $this->getCon();
49
+
50
+ $this->beta = false;
51
+
52
+ $thisPlugin = Services::WpPlugins()->getPluginAsVo( $con->base_file );
53
+ $versionsLookup = ( new Versions() )->setWorkingSlug( $thisPlugin->slug );
54
+ $betas = array_filter(
55
+ $versionsLookup->all(),
56
+ function ( $betaVersion ) {
57
+ return is_string( $betaVersion )
58
+ && preg_match( '#^\d+(\.\d+)+$#', $betaVersion )
59
+ && version_compare( $betaVersion, $this->getCon()->getVersion(), '>' );
60
+ }
61
+ );
62
+ if ( !empty( $betas ) ) {
63
+ natsort( $betas );
64
+ $beta = array_pop( $betas );
65
+ $versionsLookup->setWorkingVersion( $beta );
66
+ $url = $versionsLookup->allVersionsUrls()[ $beta ] ?? '';
67
+ if ( !empty( $url ) ) {
68
+ $this->beta = new \stdClass();
69
+ $this->beta->id = $thisPlugin->id;
70
+ $this->beta->slug = $thisPlugin->slug;
71
+ $this->beta->plugin = $con->base_file;
72
+ $this->beta->new_version = $beta;
73
+ $this->beta->package = $url;
74
+ $this->beta->icons = [
75
+ '2x' => sprintf( 'https://ps.w.org/%s/assets/icon-256x256.png', $thisPlugin->slug ),
76
+ '1x' => sprintf( 'https://ps.w.org/%s/assets/icon-128x128.png', $thisPlugin->slug ),
77
+ ];
78
+ }
79
+ }
80
+ }
81
+
82
+ return $this->beta;
83
+ }
84
  }
src/lib/src/Modules/Plugin/Lib/ImportExport/Export.php CHANGED
@@ -34,17 +34,13 @@ class Export {
34
  $ieCon = $mod->getImpExpController();
35
  $req = Services::Request();
36
 
37
- $url = Services::Data()->validateSimpleHttpUrl( (string)$req->query( 'url', '' ) );
38
- if ( !$ieCon->verifySecretKey( (string)$req->query( 'secret', '' ) ) && !$this->isUrlOnWhitelist( $url ) ) {
39
- return; // we show no signs of responding to invalid secret keys or unwhitelisted URLs
40
- }
41
-
42
  $success = false;
43
  $data = [];
44
 
45
- if ( !$this->verifyUrlWithHandshake( $url ) ) {
 
46
  $code = 3;
47
- $msg = __( 'Handshake verification failed.', 'wp-simple-firewall' );
48
  }
49
  else {
50
  $code = 0;
@@ -134,27 +130,51 @@ class Export {
134
  }
135
 
136
  /**
137
- * @param string $url
 
 
 
 
 
 
 
 
138
  */
139
- private function isUrlOnWhitelist( $url ) :bool {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  /** @var Plugin\Options $opts */
141
  $opts = $this->getOptions();
142
  return !empty( $url ) && in_array( $url, $opts->getImportExportWhitelist() );
143
  }
144
 
145
- /**
146
- * @param string $url
147
- * @return bool
148
- */
149
- private function verifyUrlWithHandshake( $url ) :bool {
150
- $bVerified = false;
151
-
152
- if ( !empty( $url ) ) {
153
- $sReqUrl = add_query_arg( [ 'shield_action' => 'importexport_handshake' ], $url );
154
- $aResp = @json_decode( Services::HttpRequest()->getContent( $sReqUrl ), true );
155
- $bVerified = is_array( $aResp ) && isset( $aResp[ 'success' ] ) && ( $aResp[ 'success' ] === true );
156
- }
157
-
158
- return $bVerified;
159
  }
160
  }
34
  $ieCon = $mod->getImpExpController();
35
  $req = Services::Request();
36
 
 
 
 
 
 
37
  $success = false;
38
  $data = [];
39
 
40
+ $url = (string)Services::Data()->validateSimpleHttpUrl( (string)$req->query( 'url', '' ) );
41
+ if ( !$this->verifyUrl( $url, (string)$req->query( 'id', '' ), (string)$req->query( 'secret', '' ) ) ) {
42
  $code = 3;
43
+ $msg = __( 'Verification failed.', 'wp-simple-firewall' );
44
  }
45
  else {
46
  $code = 0;
130
  }
131
 
132
  /**
133
+ * 2022-10-27:
134
+ * There is real issue with some sites being able to perform automated import and export. So we want to simplify
135
+ * this so that if the URL handshake doesn't work, we can fallback to an ID lookup. The one issue here is that we
136
+ * accept the ID if it's the first time see this URL. However, at this stage, the requesting URL has either already
137
+ * been added to the "whitelist" or they're sending the correct secret key.
138
+ *
139
+ * So you're verified if:
140
+ * - You're on the whitelist and your ID is valid, OR you can handshake
141
+ * - You're not on the whitelist AND your secret is valid AND ( ID is valid OR you can handshake ).
142
  */
143
+ private function verifyUrl( string $url, string $id, string $secret ) :bool {
144
+ /** @var Plugin\ModCon $mod */
145
+ $mod = $this->getMod();
146
+
147
+ $urlIDs = $this->getOptions()->getOpt( 'import_url_ids' );
148
+ if ( !is_array( $urlIDs ) ) {
149
+ $urlIDs = [];
150
+ }
151
+
152
+ $verified = !empty( $url ) &&
153
+ (
154
+ $mod->getImpExpController()->verifySecretKey( $secret )
155
+ || ( !empty( $id ) && ( $urlIDs[ md5( $url ) ] ?? '' ) === $id )
156
+ || ( $this->isUrlOnWhitelist( $url ) && $this->handshake( $url ) )
157
+ );
158
+
159
+ // Update the stored ID, so it can be used at a later date.
160
+ if ( $verified && !empty( $id ) ) {
161
+ $urlIDs[ md5( $url ) ] = $id;
162
+ $this->getOptions()->setOpt( 'import_url_ids', $urlIDs );
163
+ $this->getMod()->saveModOptions();
164
+ }
165
+
166
+ return $verified;
167
+ }
168
+
169
+ private function isUrlOnWhitelist( string $url ) :bool {
170
  /** @var Plugin\Options $opts */
171
  $opts = $this->getOptions();
172
  return !empty( $url ) && in_array( $url, $opts->getImportExportWhitelist() );
173
  }
174
 
175
+ private function handshake( string $url ) :bool {
176
+ $reqURL = add_query_arg( [ 'shield_action' => 'importexport_handshake' ], $url );
177
+ $dec = @json_decode( Services::HttpRequest()->getContent( $reqURL ), true );
178
+ return is_array( $dec ) && isset( $dec[ 'success' ] ) && ( $dec[ 'success' ] === true );
 
 
 
 
 
 
 
 
 
 
179
  }
180
  }
src/lib/src/Modules/Plugin/Lib/ImportExport/Import.php CHANGED
@@ -153,7 +153,8 @@ class Import {
153
  $data = [
154
  'shield_action' => 'importexport_export',
155
  'secret' => $secretKey,
156
- 'url' => Services::WpGeneral()->getHomeUrl()
 
157
  ];
158
  // Don't send the network setup request if it's the cron.
159
  if ( !is_null( $isEnableNetwork ) && !Services::WpGeneral()->isCron() ) {
@@ -231,4 +232,15 @@ class Import {
231
  );
232
  }
233
  }
 
 
 
 
 
 
 
 
 
 
 
234
  }
153
  $data = [
154
  'shield_action' => 'importexport_export',
155
  'secret' => $secretKey,
156
+ 'url' => Services::WpGeneral()->getHomeUrl(),
157
+ 'id' => $this->getImportID(),
158
  ];
159
  // Don't send the network setup request if it's the cron.
160
  if ( !is_null( $isEnableNetwork ) && !Services::WpGeneral()->isCron() ) {
232
  );
233
  }
234
  }
235
+
236
+ private function getImportID() :string {
237
+ $opts = $this->getOptions();
238
+ $id = $opts->getOpt( 'import_id' );
239
+ if ( empty( $id ) ) {
240
+ $id = bin2hex( random_bytes( 8 ) );
241
+ $opts->setOpt( 'import_id', $id );
242
+ $this->getMod()->saveModOptions();
243
+ }
244
+ return $id;
245
+ }
246
  }
src/lib/vendor/composer/installed.php CHANGED
@@ -1,11 +1,11 @@
1
  <?php return array(
2
  'root' => array(
3
- 'pretty_version' => 'dev-develop',
4
- 'version' => 'dev-develop',
5
  'type' => 'library',
6
  'install_path' => __DIR__ . '/../../',
7
  'aliases' => array(),
8
- 'reference' => '6cdb22524231e2cd7dd7f796762d20a93e0ef514',
9
  'name' => 'apto-shield/requirements',
10
  'dev' => true,
11
  ),
@@ -20,12 +20,12 @@
20
  'dev_requirement' => false,
21
  ),
22
  'apto-shield/requirements' => array(
23
- 'pretty_version' => 'dev-develop',
24
- 'version' => 'dev-develop',
25
  'type' => 'library',
26
  'install_path' => __DIR__ . '/../../',
27
  'aliases' => array(),
28
- 'reference' => '6cdb22524231e2cd7dd7f796762d20a93e0ef514',
29
  'dev_requirement' => false,
30
  ),
31
  'christian-riesen/base32' => array(
1
  <?php return array(
2
  'root' => array(
3
+ 'pretty_version' => 'dev-master',
4
+ 'version' => 'dev-master',
5
  'type' => 'library',
6
  'install_path' => __DIR__ . '/../../',
7
  'aliases' => array(),
8
+ 'reference' => 'e7e5d1652fb897530552e6b174a1b403f0df014e',
9
  'name' => 'apto-shield/requirements',
10
  'dev' => true,
11
  ),
20
  'dev_requirement' => false,
21
  ),
22
  'apto-shield/requirements' => array(
23
+ 'pretty_version' => 'dev-master',
24
+ 'version' => 'dev-master',
25
  'type' => 'library',
26
  'install_path' => __DIR__ . '/../../',
27
  'aliases' => array(),
28
+ 'reference' => 'e7e5d1652fb897530552e6b174a1b403f0df014e',
29
  'dev_requirement' => false,
30
  ),
31
  'christian-riesen/base32' => array(
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Render.php CHANGED
@@ -85,7 +85,7 @@ class Render {
85
  ->render( $this->getTemplate(), $this->getRenderVars() );
86
  }
87
  catch ( \Exception $e ) {
88
- return 'Could not render Twig with following Exception: '.$e->getMessage();
89
  }
90
  }
91
 
85
  ->render( $this->getTemplate(), $this->getRenderVars() );
86
  }
87
  catch ( \Exception $e ) {
88
+ return sprintf('Could not render %s Twig with following Exception: '.$e->getMessage(), $this->getTemplate());
89
  }
90
  }
91