MalCare WordPress Security Plugin – Malware Scanner, Cleaner, Security Firewall - Version 3.1

Version Description

  • Adding params validation
  • Adding support for custom user tables
Download this release

Release Info

Developer ritesh.soni36
Plugin Icon 128x128 MalCare WordPress Security Plugin – Malware Scanner, Cleaner, Security Firewall
Version 3.1
Comparing to
See all releases

Code changes from version 2.1 to 3.1

account.php CHANGED
@@ -129,15 +129,15 @@ if (!class_exists('MCAccount')) :
129
  );
130
  }
131
 
132
- public function authenticate() {
133
- $method = $_REQUEST['bvMethod'];
134
- $time = intval($_REQUEST['bvTime']);
135
- $version = $_REQUEST['bvVersion'];
136
- $sig = $_REQUEST['sig'];
137
  if ($time < intval($this->settings->getOption('bvLastRecvTime')) - 300) {
138
  return false;
139
  }
140
- if (array_key_exists('sha1', $_REQUEST)) {
141
  $sig_match = sha1($method.$this->secret.$time.$version);
142
  } else {
143
  $sig_match = md5($method.$this->secret.$time.$version);
129
  );
130
  }
131
 
132
+ public function authenticate($request) {
133
+ $method = $request->method;
134
+ $time = $request->time;
135
+ $version = $request->version;
136
+ $sig = $request->sig;
137
  if ($time < intval($this->settings->getOption('bvLastRecvTime')) - 300) {
138
  return false;
139
  }
140
+ if ($request->is_sha1) {
141
  $sig_match = sha1($method.$this->secret.$time.$version);
142
  } else {
143
  $sig_match = md5($method.$this->secret.$time.$version);
callback/handler.php CHANGED
@@ -11,13 +11,13 @@ if (!class_exists('BVCallbackHandler')) :
11
  public $account;
12
  public $response;
13
 
14
- public function __construct($db, $settings, $siteinfo, $request, $account) {
15
  $this->db = $db;
16
  $this->settings = $settings;
17
  $this->siteinfo = $siteinfo;
18
  $this->request = $request;
19
  $this->account = $account;
20
- $this->response = new BVCallbackResponse();
21
  }
22
 
23
  public function bvAdmExecuteWithoutUser() {
@@ -37,7 +37,7 @@ if (!class_exists('BVCallbackHandler')) :
37
  "account_info" => $this->account->respInfo(),
38
  "bvinfo" => $bvinfo->respInfo()
39
  );
40
- $this->response->terminate($resp, $this->request->params);
41
  }
42
 
43
  public function routeRequest() {
11
  public $account;
12
  public $response;
13
 
14
+ public function __construct($db, $settings, $siteinfo, $request, $account, $response) {
15
  $this->db = $db;
16
  $this->settings = $settings;
17
  $this->siteinfo = $siteinfo;
18
  $this->request = $request;
19
  $this->account = $account;
20
+ $this->response = $response;
21
  }
22
 
23
  public function bvAdmExecuteWithoutUser() {
37
  "account_info" => $this->account->respInfo(),
38
  "bvinfo" => $bvinfo->respInfo()
39
  );
40
+ $this->response->terminate($resp);
41
  }
42
 
43
  public function routeRequest() {
callback/request.php CHANGED
@@ -9,16 +9,31 @@ if (!class_exists('BVCallbackRequest')) :
9
  public $is_afterload;
10
  public $is_admin_ajax;
11
  public $is_debug;
12
- public $is_recovery;
13
-
14
- public function __construct($params) {
15
- $this->params = $params;
16
- $this->wing = $this->params['wing'];
17
- $this->method = $this->params['bvMethod'];
18
- $this->is_afterload = array_key_exists('afterload', $this->params);
19
- $this->is_admin_ajax = array_key_exists('adajx', $this->params);
20
- $this->is_debug = array_key_exists('bvdbg', $this->params);
21
- $this->is_recovery = array_key_exists('bvrcvr', $this->params);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  }
23
 
24
  public function isAPICall() {
@@ -27,9 +42,9 @@ if (!class_exists('BVCallbackRequest')) :
27
 
28
  public function respInfo() {
29
  $info = array(
30
- "requestedsig" => $this->params['sig'],
31
- "requestedtime" => intval($this->params['bvTime']),
32
- "requestedversion" => $this->params['bvVersion']
33
  );
34
  if ($this->is_debug) {
35
  $info["inreq"] = $this->params;
@@ -40,74 +55,125 @@ if (!class_exists('BVCallbackRequest')) :
40
  if ($this->is_afterload) {
41
  $info["afterload"] = true;
42
  }
 
 
 
43
  return $info;
44
  }
45
 
46
- public function processParams() {
47
- $params = $this->params;
48
- if (array_key_exists('obend', $params) && function_exists('ob_end_clean'))
 
49
  @ob_end_clean();
50
- if (array_key_exists('op_reset', $params) && function_exists('output_reset_rewrite_vars'))
 
51
  @output_reset_rewrite_vars();
52
- if (array_key_exists('binhead', $params)) {
 
53
  header("Content-type: application/binary");
54
  header('Content-Transfer-Encoding: binary');
55
  }
56
- if (array_key_exists('concat', $params)) {
57
- foreach ($params['concat'] as $key) {
 
58
  $concated = '';
59
- $count = intval($params[$key]);
60
  for ($i = 1; $i <= $count; $i++) {
61
- $concated .= $params[$key."_bv_".$i];
62
  }
63
- $params[$key] = $concated;
64
  }
65
  }
66
- if (array_key_exists('b64', $params)) {
67
- foreach ($params['b64'] as $key) {
68
- if (is_array($params[$key])) {
69
- $params[$key] = array_map('base64_decode', $params[$key]);
70
- } else {
71
- $params[$key] = base64_decode($params[$key]);
72
- }
73
- }
74
- }
75
- if (array_key_exists('unser', $params)) {
76
- foreach ($params['unser'] as $key) {
77
- $params[$key] = json_decode($params[$key], TRUE);
78
  }
79
- }
80
- if (array_key_exists('b642', $params)) {
81
- foreach ($params['b642'] as $key) {
82
- if (is_array($params[$key])) {
83
- $params[$key] = array_map('base64_decode', $params[$key]);
84
- } else {
85
- $params[$key] = base64_decode($params[$key]);
 
 
 
 
 
 
 
86
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  }
88
  }
89
- if (array_key_exists('dic', $params)) {
90
- foreach ($params['dic'] as $key => $mkey) {
91
- $params[$mkey] = $params[$key];
92
- unset($params[$key]);
93
- }
 
 
94
  }
95
- if (array_key_exists('clacts', $params)) {
96
- foreach ($params['clacts'] as $action) {
97
- remove_all_actions($action);
98
- }
99
  }
100
- if (array_key_exists('clallacts', $params)) {
101
- global $wp_filter;
102
- foreach ( $wp_filter as $filter => $val ){
103
- remove_all_actions($filter);
104
- }
105
  }
106
- if (array_key_exists('memset', $params)) {
107
- $val = intval(urldecode($params['memset']));
108
- @ini_set('memory_limit', $val.'M');
 
 
 
 
109
  }
110
- return $params;
 
111
  }
112
  }
113
  endif;
9
  public $is_afterload;
10
  public $is_admin_ajax;
11
  public $is_debug;
12
+ public $account;
13
+ public $calculated_mac;
14
+ public $sig;
15
+ public $time;
16
+ public $version;
17
+ public $is_sha1;
18
+ public $bvb64stream;
19
+ public $bvb64cksize;
20
+ public $checksum;
21
+
22
+ public function __construct($account, $in_params) {
23
+ $this->params = array();
24
+ $this->account = $account;
25
+ $this->wing = $in_params['wing'];
26
+ $this->method = $in_params['bvMethod'];
27
+ $this->is_afterload = array_key_exists('afterload', $in_params);
28
+ $this->is_admin_ajax = array_key_exists('adajx', $in_params);
29
+ $this->is_debug = array_key_exists('bvdbg', $in_params);
30
+ $this->sig = $in_params['sig'];
31
+ $this->time = intval($in_params['bvTime']);
32
+ $this->version = $in_params['bvVersion'];
33
+ $this->is_sha1 = array_key_exists('sha1', $in_params);
34
+ $this->bvb64stream = isset($in_params['bvb64stream']);
35
+ $this->bvb64cksize = array_key_exists('bvb64cksize', $in_params) ? intval($in_params['bvb64cksize']) : false;
36
+ $this->checksum = array_key_exists('checksum', $in_params) ? $in_params['checksum'] : false;
37
  }
38
 
39
  public function isAPICall() {
42
 
43
  public function respInfo() {
44
  $info = array(
45
+ "requestedsig" => $this->sig,
46
+ "requestedtime" => $this->time,
47
+ "requestedversion" => $this->version
48
  );
49
  if ($this->is_debug) {
50
  $info["inreq"] = $this->params;
55
  if ($this->is_afterload) {
56
  $info["afterload"] = true;
57
  }
58
+ if ($this->calculated_mac) {
59
+ $info["calculated_mac"] = $this->calculated_mac;
60
+ }
61
  return $info;
62
  }
63
 
64
+ public function processParams($in_params) {
65
+ $params = array();
66
+
67
+ if (array_key_exists('obend', $in_params) && function_exists('ob_end_clean'))
68
  @ob_end_clean();
69
+
70
+ if (array_key_exists('op_reset', $in_params) && function_exists('output_reset_rewrite_vars'))
71
  @output_reset_rewrite_vars();
72
+
73
+ if (array_key_exists('binhead', $in_params)) {
74
  header("Content-type: application/binary");
75
  header('Content-Transfer-Encoding: binary');
76
  }
77
+
78
+ if (array_key_exists('concat', $in_params)) {
79
+ foreach ($in_params['concat'] as $key) {
80
  $concated = '';
81
+ $count = intval($in_params[$key]);
82
  for ($i = 1; $i <= $count; $i++) {
83
+ $concated .= $in_params[$key."_bv_".$i];
84
  }
85
+ $in_params[$key] = $concated;
86
  }
87
  }
88
+
89
+ if (array_key_exists('bvprms', $in_params) && isset($in_params['bvprms']) &&
90
+ array_key_exists('bvprmsmac', $in_params) && isset($in_params['bvprmsmac'])) {
91
+ $digest_algo = 'SHA1';
92
+ $sent_mac = $in_params['bvprmsmac'];
93
+
94
+ if (array_key_exists('bvprmshshalgo', $in_params) && isset($in_params['bvprmshshalgo'])) {
95
+ $digest_algo = $in_params['bvprmshshalgo'];
 
 
 
 
96
  }
97
+
98
+ $calculated_mac = hash_hmac($digest_algo, $in_params['bvprms'], $this->account->secret);
99
+ $this->calculated_mac = substr($calculated_mac, 0, 6);
100
+
101
+ if ($this->compare_mac($sent_mac, $calculated_mac) === true) {
102
+
103
+ if (array_key_exists('b64', $in_params)) {
104
+ foreach ($in_params['b64'] as $key) {
105
+ if (is_array($in_params[$key])) {
106
+ $in_params[$key] = array_map('base64_decode', $in_params[$key]);
107
+ } else {
108
+ $in_params[$key] = base64_decode($in_params[$key]);
109
+ }
110
+ }
111
  }
112
+
113
+ if (array_key_exists('unser', $in_params)) {
114
+ foreach ($in_params['unser'] as $key) {
115
+ $in_params[$key] = json_decode($in_params[$key], TRUE);
116
+ }
117
+ }
118
+
119
+ if (array_key_exists('sersafe', $in_params)) {
120
+ $key = $in_params['sersafe'];
121
+ $in_params[$key] = BVCallbackRequest::serialization_safe_decode($in_params[$key]);
122
+ }
123
+
124
+ if (array_key_exists('bvprms', $in_params) && isset($in_params['bvprms'])) {
125
+ $params = $in_params['bvprms'];
126
+ }
127
+
128
+ if (array_key_exists('clacts', $in_params)) {
129
+ foreach ($in_params['clacts'] as $action) {
130
+ remove_all_actions($action);
131
+ }
132
+ }
133
+
134
+ if (array_key_exists('clallacts', $in_params)) {
135
+ global $wp_filter;
136
+ foreach ( $wp_filter as $filter => $val ){
137
+ remove_all_actions($filter);
138
+ }
139
+ }
140
+
141
+ if (array_key_exists('memset', $in_params)) {
142
+ $val = intval(urldecode($in_params['memset']));
143
+ @ini_set('memory_limit', $val.'M');
144
+ }
145
+
146
+ return $params;
147
  }
148
  }
149
+
150
+ return false;
151
+ }
152
+
153
+ private function compare_mac($l_hash, $r_hash) {
154
+ if (!is_string($l_hash) || !is_string($r_hash)) {
155
+ return false;
156
  }
157
+
158
+ if (strlen($l_hash) !== strlen($r_hash)) {
159
+ return false;
 
160
  }
161
+
162
+ if (function_exists('hash_equals')) {
163
+ return hash_equals($l_hash, $r_hash);
164
+ } else {
165
+ return $l_hash === $r_hash;
166
  }
167
+ }
168
+
169
+ public static function serialization_safe_decode($data) {
170
+ if (is_array($data)) {
171
+ $data = array_map(array('BVCallbackRequest', 'serialization_safe_decode'), $data);
172
+ } elseif (is_string($data)) {
173
+ $data = base64_decode($data);
174
  }
175
+
176
+ return $data;
177
  }
178
  }
179
  endif;
callback/response.php CHANGED
@@ -5,9 +5,11 @@ if (!class_exists('BVCallbackResponse')) :
5
 
6
  class BVCallbackResponse extends BVCallbackBase {
7
  public $status;
 
8
 
9
- public function __construct() {
10
  $this->status = array("blogvault" => "response");
 
11
  }
12
 
13
  public function addStatus($key, $value) {
@@ -21,14 +23,11 @@ if (!class_exists('BVCallbackResponse')) :
21
  $this->status[$key][] = $value;
22
  }
23
 
24
- public function terminate($resp = array(), $req_params) {
25
  $resp = array_merge($this->status, $resp);
26
  $resp["signature"] = "Blogvault API";
27
  $response = "bvbvbvbvbv".serialize($resp)."bvbvbvbvbv";
28
- if (array_key_exists('bvb64resp', $req_params)) {
29
- $chunk_size = array_key_exists('bvb64cksize', $req_params) ? intval($req_params['bvb64cksize']) : false;
30
- $response = "bvb64bvb64".$this->base64Encode($response, $chunk_size)."bvb64bvb64";
31
- }
32
  die($response);
33
 
34
  exit;
5
 
6
  class BVCallbackResponse extends BVCallbackBase {
7
  public $status;
8
+ public $bvb64cksize;
9
 
10
+ public function __construct($bvb64cksize) {
11
  $this->status = array("blogvault" => "response");
12
+ $this->bvb64cksize = $bvb64cksize;
13
  }
14
 
15
  public function addStatus($key, $value) {
23
  $this->status[$key][] = $value;
24
  }
25
 
26
+ public function terminate($resp = array()) {
27
  $resp = array_merge($this->status, $resp);
28
  $resp["signature"] = "Blogvault API";
29
  $response = "bvbvbvbvbv".serialize($resp)."bvbvbvbvbv";
30
+ $response = "bvb64bvb64".$this->base64Encode($response, $this->bvb64cksize)."bvb64bvb64";
 
 
 
31
  die($response);
32
 
33
  exit;
callback/streams.php CHANGED
@@ -8,10 +8,10 @@ if (!class_exists('BVRespStream')) :
8
  public $bvb64cksize;
9
  public $checksum;
10
 
11
- function __construct($params) {
12
- $this->bvb64stream = isset($params['bvb64stream']);
13
- $this->bvb64cksize = array_key_exists('bvb64cksize', $params) ? intval($params['bvb64cksize']) : false;
14
- $this->checksum = array_key_exists('checksum', $params) ? $params['checksum'] : false;
15
  }
16
 
17
  public function writeChunk($chunk) {
@@ -20,9 +20,9 @@ if (!class_exists('BVRespStream')) :
20
  public static function startStream($account, $request) {
21
  $result = array();
22
  $params = $request->params;
23
- $stream = new BVRespStream($params);
24
  if ($request->isAPICall()) {
25
- $stream = new BVHttpStream($params);
26
  if (!$stream->connect()) {
27
  $apicallstatus = array(
28
  "httperror" => "Cannot Open Connection to Host",
@@ -65,8 +65,8 @@ if (!class_exists('BVRespStream')) :
65
  }
66
 
67
  class BVRespStream extends BVStream {
68
- function __construct($params) {
69
- parent::__construct($params);
70
  }
71
 
72
  public function writeChunk($_string) {
@@ -91,11 +91,11 @@ class BVHttpStream extends BVStream {
91
  var $boundary;
92
  var $apissl;
93
 
94
- function __construct($params) {
95
- parent::__construct($params);
96
- $this->host = $params['apihost'];
97
- $this->port = intval($params['apiport']);
98
- $this->apissl = array_key_exists('apissl', $params);
99
  }
100
 
101
  public function connect() {
8
  public $bvb64cksize;
9
  public $checksum;
10
 
11
+ function __construct($request) {
12
+ $this->bvb64stream = $request->bvb64stream;
13
+ $this->bvb64cksize = $request->bvb64cksize;
14
+ $this->checksum = $request->checksum;
15
  }
16
 
17
  public function writeChunk($chunk) {
20
  public static function startStream($account, $request) {
21
  $result = array();
22
  $params = $request->params;
23
+ $stream = new BVRespStream($request);
24
  if ($request->isAPICall()) {
25
+ $stream = new BVHttpStream($request);
26
  if (!$stream->connect()) {
27
  $apicallstatus = array(
28
  "httperror" => "Cannot Open Connection to Host",
65
  }
66
 
67
  class BVRespStream extends BVStream {
68
+ function __construct($request) {
69
+ parent::__construct($request);
70
  }
71
 
72
  public function writeChunk($_string) {
91
  var $boundary;
92
  var $apissl;
93
 
94
+ function __construct($request) {
95
+ parent::__construct($request);
96
+ $this->host = $request->params['apihost'];
97
+ $this->port = intval($request->params['apiport']);
98
+ $this->apissl = array_key_exists('apissl', $request->params);
99
  }
100
 
101
  public function connect() {
callback/wings/fw.php CHANGED
@@ -41,10 +41,22 @@ class BVFirewallCallback {
41
  $config->setReqProfilingMode($params['req_profiling_mode']);
42
  $resp = array("req_profiling_mode" => $config->getReqProfilingMode());
43
  break;
 
 
 
 
 
 
 
 
 
 
 
 
44
  default:
45
  $resp = false;
46
  }
47
  return $resp;
48
  }
49
  }
50
- endif;
41
  $config->setReqProfilingMode($params['req_profiling_mode']);
42
  $resp = array("req_profiling_mode" => $config->getReqProfilingMode());
43
  break;
44
+ case "stbypslevl":
45
+ $config->setBypassLevel($params['bypslevl']);
46
+ $resp = array("bypslevl" => $config->getBypassLevel());
47
+ break;
48
+ case "stcstmrls":
49
+ $config->setCustomRoles($params['cstmrls']);
50
+ $resp = array("cstmrls" => $config->getCustomRoles());
51
+ break;
52
+ case "stcookiemode":
53
+ $config->setCookieMode($params['mode']);
54
+ $resp = array("mode" => $config->getCookieMode());
55
+ break;
56
  default:
57
  $resp = false;
58
  }
59
  return $resp;
60
  }
61
  }
62
+ endif;
callback/wings/info.php CHANGED
@@ -142,6 +142,8 @@ class BVInfoCallback extends BVCallbackBase {
142
  'dbcharset' => defined('DB_CHARSET') ? DB_CHARSET : null,
143
  'disallow_file_edit' => defined('DISALLOW_FILE_EDIT'),
144
  'disallow_file_mods' => defined('DISALLOW_FILE_MODS'),
 
 
145
  'locale' => get_locale(),
146
  'wp_local_string' => $wp_local_package,
147
  'charset_collate' => $db->getCharsetCollate()
@@ -230,11 +232,19 @@ class BVInfoCallback extends BVCallbackBase {
230
  $arules = $settings->getOption('bvfwauditrules');
231
  $rmode = $settings->getOption('bvfwrulesmode');
232
  $reqprofilingmode = $settings->getOption('bvfwreqprofilingmode');
 
 
 
 
233
  $config['mode'] = intval($mode ? $mode : 1);
234
  $config['disabled_rules'] = $drules ? $drules : array();
235
  $config['audit_rules'] = $arules ? $arules : array();
236
  $config['rules_mode'] = intval($rmode ? $rmode : 1);
237
  $config['req_profiling_mode'] = intval($reqprofilingmode ? $reqprofilingmode : 1);
 
 
 
 
238
  return $config;
239
  }
240
 
142
  'dbcharset' => defined('DB_CHARSET') ? DB_CHARSET : null,
143
  'disallow_file_edit' => defined('DISALLOW_FILE_EDIT'),
144
  'disallow_file_mods' => defined('DISALLOW_FILE_MODS'),
145
+ 'custom_users' => defined('CUSTOM_USER_TABLE') ? CUSTOM_USER_TABLE : null,
146
+ 'custom_usermeta' => defined('CUSTOM_USERMETA_TABLE') ? CUSTOM_USERMETA_TABLE : null,
147
  'locale' => get_locale(),
148
  'wp_local_string' => $wp_local_package,
149
  'charset_collate' => $db->getCharsetCollate()
232
  $arules = $settings->getOption('bvfwauditrules');
233
  $rmode = $settings->getOption('bvfwrulesmode');
234
  $reqprofilingmode = $settings->getOption('bvfwreqprofilingmode');
235
+ $bypass_level = $settings->getOption('bvfwbypasslevel');
236
+ $custom_roles = $settings->getOption('bvfwcustomroles');
237
+ $cookiemode = $settings->getOption('bvfwcookiemode');
238
+ $cookiekey = (string) $settings->getOption('bvfwcookiekey');
239
  $config['mode'] = intval($mode ? $mode : 1);
240
  $config['disabled_rules'] = $drules ? $drules : array();
241
  $config['audit_rules'] = $arules ? $arules : array();
242
  $config['rules_mode'] = intval($rmode ? $rmode : 1);
243
  $config['req_profiling_mode'] = intval($reqprofilingmode ? $reqprofilingmode : 1);
244
+ $config['bypslevl'] = intval($bypass_level ? $bypass_level : 2);
245
+ $config['cstmrls'] = $custom_roles ? $custom_roles : array();
246
+ $config['cookiemode'] = intval($cookiemode ? $cookiemode : 2);
247
+ $config['cookiekey'] = $cookiekey;
248
  return $config;
249
  }
250
 
info.php CHANGED
@@ -9,7 +9,7 @@ if (!class_exists('MCInfo')) :
9
  public $badgeinfo = 'mcbadge';
10
  public $ip_header_option = 'mcipheader';
11
  public $brand_option = 'mcbrand';
12
- public $version = '2.1';
13
  public $webpage = 'https://www.malcare.com';
14
  public $appurl = 'https://app.malcare.com';
15
  public $slug = 'malcare-security/malcare.php';
@@ -78,7 +78,6 @@ if (!class_exists('MCInfo')) :
78
  public function respInfo() {
79
  return array(
80
  "bvversion" => $this->version,
81
- "asymauth" => "true",
82
  "sha1" => "true"
83
  );
84
  }
9
  public $badgeinfo = 'mcbadge';
10
  public $ip_header_option = 'mcipheader';
11
  public $brand_option = 'mcbrand';
12
+ public $version = '3.1';
13
  public $webpage = 'https://www.malcare.com';
14
  public $appurl = 'https://app.malcare.com';
15
  public $slug = 'malcare-security/malcare.php';
78
  public function respInfo() {
79
  return array(
80
  "bvversion" => $this->version,
 
81
  "sha1" => "true"
82
  );
83
  }
malcare.php CHANGED
@@ -5,7 +5,7 @@ Plugin URI: https://www.malcare.com
5
  Description: WordPress Security, Firewall and Malware Scanner
6
  Author: MalCare Security
7
  Author URI: https://www.malcare.com
8
- Version: 2.1
9
  Network: True
10
  */
11
 
@@ -75,19 +75,27 @@ if ((array_key_exists('bvreqmerge', $_POST)) || (array_key_exists('bvreqmerge',
75
 
76
  if ((array_key_exists('bvplugname', $_REQUEST)) && ($_REQUEST['bvplugname'] == "malcare")) {
77
  require_once dirname( __FILE__ ) . '/callback/base.php';
78
- require_once dirname( __FILE__ ) . '/callback/request.php';
79
  require_once dirname( __FILE__ ) . '/callback/response.php';
 
80
 
81
- $request = new BVCallbackRequest($_REQUEST);
82
  $account = MCAccount::find($bvsettings, $_REQUEST['pubkey']);
 
 
83
 
84
-
85
- ##RECOVERYMODULE##
86
-
87
- if ($account && (1 === $account->authenticate())) {
88
  require_once dirname( __FILE__ ) . '/callback/handler.php';
89
- $request->params = $request->processParams();
90
- $callback_handler = new BVCallbackHandler($bvdb, $bvsettings, $bvsiteinfo, $request, $account);
 
 
 
 
 
 
 
 
 
 
91
  if ($request->is_afterload) {
92
  add_action('wp_loaded', array($callback_handler, 'execute'));
93
  } else if ($request->is_admin_ajax) {
@@ -103,8 +111,7 @@ if ((array_key_exists('bvplugname', $_REQUEST)) && ($_REQUEST['bvplugname'] == "
103
  "bvinfo" => $bvinfo->respInfo(),
104
  "statusmsg" => "FAILED_AUTH"
105
  );
106
- $response = new BVCallbackResponse();
107
- $response->terminate($resp, $request->params);
108
  }
109
  } else {
110
  if ($bvinfo->isProtectModuleEnabled()) {
5
  Description: WordPress Security, Firewall and Malware Scanner
6
  Author: MalCare Security
7
  Author URI: https://www.malcare.com
8
+ Version: 3.1
9
  Network: True
10
  */
11
 
75
 
76
  if ((array_key_exists('bvplugname', $_REQUEST)) && ($_REQUEST['bvplugname'] == "malcare")) {
77
  require_once dirname( __FILE__ ) . '/callback/base.php';
 
78
  require_once dirname( __FILE__ ) . '/callback/response.php';
79
+ require_once dirname( __FILE__ ) . '/callback/request.php';
80
 
 
81
  $account = MCAccount::find($bvsettings, $_REQUEST['pubkey']);
82
+ $request = new BVCallbackRequest($account, $_REQUEST);
83
+ $response = new BVCallbackResponse($request->bvb64cksize);
84
 
85
+ if ($account && (1 === $account->authenticate($request))) {
 
 
 
86
  require_once dirname( __FILE__ ) . '/callback/handler.php';
87
+ $params = $request->processParams($_REQUEST);
88
+ if ($params === false) {
89
+ $resp = array(
90
+ "account_info" => $account->respInfo(),
91
+ "request_info" => $request->respInfo(),
92
+ "bvinfo" => $bvinfo->respInfo(),
93
+ "statusmsg" => "BVPRMS_CORRUPTED"
94
+ );
95
+ $response->terminate($resp);
96
+ }
97
+ $request->params = $params;
98
+ $callback_handler = new BVCallbackHandler($bvdb, $bvsettings, $bvsiteinfo, $request, $account, $response);
99
  if ($request->is_afterload) {
100
  add_action('wp_loaded', array($callback_handler, 'execute'));
101
  } else if ($request->is_admin_ajax) {
111
  "bvinfo" => $bvinfo->respInfo(),
112
  "statusmsg" => "FAILED_AUTH"
113
  );
114
+ $response->terminate($resp);
 
115
  }
116
  } else {
117
  if ($bvinfo->isProtectModuleEnabled()) {
protect/protect.php CHANGED
@@ -21,10 +21,16 @@ class BVProtect {
21
  $bvipstore->init();
22
  $ip = $this->getIP();
23
  $fw = new BVWPFW($this->db, $this->settings, $ip, $bvipstore);
24
- $fw->init();
25
- $fw->execute();
 
 
 
26
  $lp = new BVWPLP($this->db, $this->settings, $ip, $bvipstore);
27
- $lp->init();
 
 
 
28
  }
29
 
30
  public function getIP() {
21
  $bvipstore->init();
22
  $ip = $this->getIP();
23
  $fw = new BVWPFW($this->db, $this->settings, $ip, $bvipstore);
24
+ if ($fw->config->isActive()) {
25
+ $fw->init();
26
+ $fw->execute();
27
+ }
28
+ add_action('clear_fw_config', array($fw->config, 'clear'));
29
  $lp = new BVWPLP($this->db, $this->settings, $ip, $bvipstore);
30
+ if ($lp->isActive()) {
31
+ $lp->init();
32
+ }
33
+ add_action('clear_lp_config', array($lp->config, 'clear'));
34
  }
35
 
36
  public function getIP() {
protect/wp_fw/config.php CHANGED
@@ -7,6 +7,13 @@ class BVWPFWConfig {
7
  public $settings;
8
  public static $requests_table = 'fw_requests';
9
  public static $allRules = array(108, 112, 114, 115, 132, 133, 145, 146, 155, 156, 165, 167, 168, 169, 171, 172, 173, 174, 175, 176, 177, 178);
 
 
 
 
 
 
 
10
 
11
  function __construct($db, $settings) {
12
  $this->db = $db;
@@ -28,6 +35,18 @@ class BVWPFWConfig {
28
  const REQ_PROFILING_MODE_NORMAL = 2;
29
  const REQ_PROFILING_MODE_DEBUG = 3;
30
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  public static function isDisabledRule($mode) {
32
  return ($mode === BVWPFWConfig::DISABLEDRULE);
33
  }
@@ -60,6 +79,10 @@ class BVWPFWConfig {
60
  return ($this->getReqProfilingMode() !== BVWPFWConfig::REQ_PROFILING_MODE_DISABLED);
61
  }
62
 
 
 
 
 
63
  public function getRules() {
64
  $rules = array("audit" => array(), "protect" => array());
65
  $isAudit = false;
@@ -105,6 +128,22 @@ class BVWPFWConfig {
105
  }
106
  }
107
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  public function setReqProfilingMode($mode) {
109
  if (!$mode) {
110
  $this->settings->deleteOption('bvfwreqprofilingmode');
@@ -121,6 +160,22 @@ class BVWPFWConfig {
121
  }
122
  }
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  public function setAuditRules($rules) {
125
  if (!$rules) {
126
  $this->settings->deleteOption('bvfwauditrules');
@@ -139,6 +194,20 @@ class BVWPFWConfig {
139
  return intval($mode ? $mode : BVWPFWConfig::DISABLED);
140
  }
141
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  public function getReqProfilingMode() {
143
  $mode = $this->settings->getOption('bvfwreqprofilingmode');
144
  return intval($mode ? $mode : BVWPFWConfig::REQ_PROFILING_MODE_DISABLED);
@@ -154,9 +223,23 @@ class BVWPFWConfig {
154
  return ($rules ? $rules : array());
155
  }
156
 
 
 
 
 
 
 
 
 
 
 
157
  public function clear() {
158
  $this->setMode(false);
159
  $this->setRulesMode(false);
 
 
 
 
160
  $this->setDisabledRules(false);
161
  $this->setAuditRules(false);
162
  $this->setReqProfilingMode(false);
7
  public $settings;
8
  public static $requests_table = 'fw_requests';
9
  public static $allRules = array(108, 112, 114, 115, 132, 133, 145, 146, 155, 156, 165, 167, 168, 169, 171, 172, 173, 174, 175, 176, 177, 178);
10
+ public static $roleLevels = array(
11
+ 'administrator' => BVWPFWConfig::ROLE_LEVEL_ADMIN,
12
+ 'editor' => BVWPFWConfig::ROLE_LEVEL_EDITOR,
13
+ 'author' => BVWPFWConfig::ROLE_LEVEL_AUTHOR,
14
+ 'contributor' => BVWPFWConfig::ROLE_LEVEL_CONTRIBUTOR,
15
+ 'subscriber' => BVWPFWConfig::ROLE_LEVEL_SUBSCRIBER
16
+ );
17
 
18
  function __construct($db, $settings) {
19
  $this->db = $db;
35
  const REQ_PROFILING_MODE_NORMAL = 2;
36
  const REQ_PROFILING_MODE_DEBUG = 3;
37
 
38
+ #Cookie Mode
39
+ const COOKIE_MODE_ENABLED = 1;
40
+ const COOKIE_MODE_DISABLED = 2;
41
+
42
+ #Role Level
43
+ const ROLE_LEVEL_SUBSCRIBER = 1;
44
+ const ROLE_LEVEL_CONTRIBUTOR = 2;
45
+ const ROLE_LEVEL_AUTHOR = 3;
46
+ const ROLE_LEVEL_EDITOR = 4;
47
+ const ROLE_LEVEL_ADMIN = 5;
48
+ const ROLE_LEVEL_CUSTOM = 6;
49
+
50
  public static function isDisabledRule($mode) {
51
  return ($mode === BVWPFWConfig::DISABLEDRULE);
52
  }
79
  return ($this->getReqProfilingMode() !== BVWPFWConfig::REQ_PROFILING_MODE_DISABLED);
80
  }
81
 
82
+ public function canSetCookie() {
83
+ return ($this->getCookieMode() === BVWPFWConfig::COOKIE_MODE_ENABLED);
84
+ }
85
+
86
  public function getRules() {
87
  $rules = array("audit" => array(), "protect" => array());
88
  $isAudit = false;
128
  }
129
  }
130
 
131
+ public function setCookieMode($mode) {
132
+ if (!$mode) {
133
+ $this->settings->deleteOption('bvfwcookiemode');
134
+ } else {
135
+ $this->settings->updateOption('bvfwcookiemode', intval($mode));
136
+ }
137
+ }
138
+
139
+ public function setCookieKey($key) {
140
+ if (!$key) {
141
+ $this->settings->deleteOption('bvfwcookiekey');
142
+ } else {
143
+ $this->settings->updateOption('bvfwcookiekey', strval($key));
144
+ }
145
+ }
146
+
147
  public function setReqProfilingMode($mode) {
148
  if (!$mode) {
149
  $this->settings->deleteOption('bvfwreqprofilingmode');
160
  }
161
  }
162
 
163
+ public function setBypassLevel($level) {
164
+ if (!$level) {
165
+ $this->settings->deleteOption('bvfwbypasslevel');
166
+ } else {
167
+ $this->settings->updateOption('bvfwbypasslevel', $level);
168
+ }
169
+ }
170
+
171
+ public function setCustomRoles($roles) {
172
+ if (!$roles) {
173
+ $this->settings->deleteOption('bvfwcutomroles');
174
+ } else {
175
+ $this->settings->updateOption('bvfwcustomroles', $roles);
176
+ }
177
+ }
178
+
179
  public function setAuditRules($rules) {
180
  if (!$rules) {
181
  $this->settings->deleteOption('bvfwauditrules');
194
  return intval($mode ? $mode : BVWPFWConfig::DISABLED);
195
  }
196
 
197
+ public function getCookieMode() {
198
+ $mode = $this->settings->getOption('bvfwcookiemode');
199
+ return intval($mode ? $mode : BVWPFWConfig::COOKIE_MODE_DISABLED);
200
+ }
201
+
202
+ public function getCookieKey() {
203
+ $key = (string) $this->settings->getOption('bvfwcookiekey');
204
+ if ($key === '') {
205
+ $key = MCAccount::randString(32);
206
+ $this->setCookieKey($key);
207
+ }
208
+ return $key;
209
+ }
210
+
211
  public function getReqProfilingMode() {
212
  $mode = $this->settings->getOption('bvfwreqprofilingmode');
213
  return intval($mode ? $mode : BVWPFWConfig::REQ_PROFILING_MODE_DISABLED);
223
  return ($rules ? $rules : array());
224
  }
225
 
226
+ public function getBypassLevel() {
227
+ $level = $this->settings->getOption('bvfwbypasslevel');
228
+ return intval($level ? $level : BVWPFWConfig::ROLE_LEVEL_CONTRIBUTOR);
229
+ }
230
+
231
+ public function getCustomRoles() {
232
+ $roles = $this->settings->getOption('bvfwcustomroles');
233
+ return ($roles ? $roles : array());
234
+ }
235
+
236
  public function clear() {
237
  $this->setMode(false);
238
  $this->setRulesMode(false);
239
+ $this->setBypassLevel(false);
240
+ $this->setCustomRoles(false);
241
+ $this->setCookieMode(false);
242
+ $this->setCookieKey(false);
243
  $this->setDisabledRules(false);
244
  $this->setAuditRules(false);
245
  $this->setReqProfilingMode(false);
protect/wp_fw/fw.php CHANGED
@@ -49,6 +49,9 @@ class BVWPFW {
49
  (?:^|[^\\w])(?:on(?:abort|activate|afterprint|afterupdate|autocomplete|autocompleteerror|beforeactivate|beforecopy|beforecut|beforedeactivate|beforeeditfocus|beforepaste|beforeprint|beforeunload|beforeupdate|blur|bounce|cancel|canplay|canplaythrough|cellchange|change|click|close|contextmenu|controlselect|copy|cuechange|cut|dataavailable|datasetchanged|datasetcomplete|dblclick|deactivate|drag|dragend|dragenter|dragleave|dragover|dragstart|drop|durationchange|emptied|encrypted|ended|error|errorupdate|filterchange|finish|focus|focusin|focusout|formchange|forminput|hashchange|help|input|invalid|keydown|keypress|keyup|languagechange|layoutcomplete|load|loadeddata|loadedmetadata|loadstart|losecapture|message|mousedown|mouseenter|mouseleave|mousemove|mouseout|mouseover|mouseup|mousewheel|move|moveend|movestart|mozfullscreenchange|mozfullscreenerror|mozpointerlockchange|mozpointerlockerror|offline|online|page|pagehide|pageshow|paste|pause|play|playing|popstate|progress|propertychange|ratechange|readystatechange|reset|resize|resizeend|resizestart|rowenter|rowexit|rowsdelete|rowsinserted|scroll|search|seeked|seeking|select|selectstart|show|stalled|start|storage|submit|suspend|timer|timeupdate|toggle|unload|volumechange|waiting|webkitfullscreenchange|webkitfullscreenerror|wheel)|formaction|data\\-bind|ev:event)[^\\w]
50
  )/ix';
51
 
 
 
 
52
  public function __construct($db, $settings, $ip, $ipstore) {
53
  $this->db = $db;
54
  $this->settings = $settings;
@@ -59,15 +62,88 @@ class BVWPFW {
59
  }
60
 
61
  public function init() {
62
- if ($this->config->isActive()) {
63
- add_filter('status_header', array($this->request, 'captureRespCode'));
64
- register_shutdown_function(array($this, 'log'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  }
66
- add_action('clear_fw_config', array($this->config, 'clear'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  }
68
 
69
  public function log() {
70
- if (!function_exists('is_user_logged_in') || !is_user_logged_in()) {
 
 
 
 
 
71
  $this->logger->log($this->request->getDataToLog());
72
  }
73
  }
@@ -102,7 +178,7 @@ class BVWPFW {
102
  }
103
 
104
  public function canBypassFirewall() {
105
- if ($this->isWhitelistedIP()) {
106
  $this->request->setCategory(BVWPRequest::WHITELISTED);
107
  $this->request->setStatus(BVWPRequest::BYPASSED);
108
  return true;
@@ -119,6 +195,16 @@ class BVWPFW {
119
  true, 'GET_');
120
  $result += $this->profileRequestInfo($this->request->getFiles(),
121
  true, 'FILES_');
 
 
 
 
 
 
 
 
 
 
122
  $this->request->updateReqInfo($result);
123
  }
124
  if (!$this->canBypassFirewall()) {
@@ -508,4 +594,4 @@ class BVWPFW {
508
  return false;
509
  }
510
  }
511
- endif;
49
  (?:^|[^\\w])(?:on(?:abort|activate|afterprint|afterupdate|autocomplete|autocompleteerror|beforeactivate|beforecopy|beforecut|beforedeactivate|beforeeditfocus|beforepaste|beforeprint|beforeunload|beforeupdate|blur|bounce|cancel|canplay|canplaythrough|cellchange|change|click|close|contextmenu|controlselect|copy|cuechange|cut|dataavailable|datasetchanged|datasetcomplete|dblclick|deactivate|drag|dragend|dragenter|dragleave|dragover|dragstart|drop|durationchange|emptied|encrypted|ended|error|errorupdate|filterchange|finish|focus|focusin|focusout|formchange|forminput|hashchange|help|input|invalid|keydown|keypress|keyup|languagechange|layoutcomplete|load|loadeddata|loadedmetadata|loadstart|losecapture|message|mousedown|mouseenter|mouseleave|mousemove|mouseout|mouseover|mouseup|mousewheel|move|moveend|movestart|mozfullscreenchange|mozfullscreenerror|mozpointerlockchange|mozpointerlockerror|offline|online|page|pagehide|pageshow|paste|pause|play|playing|popstate|progress|propertychange|ratechange|readystatechange|reset|resize|resizeend|resizestart|rowenter|rowexit|rowsdelete|rowsinserted|scroll|search|seeked|seeking|select|selectstart|show|stalled|start|storage|submit|suspend|timer|timeupdate|toggle|unload|volumechange|waiting|webkitfullscreenchange|webkitfullscreenerror|wheel)|formaction|data\\-bind|ev:event)[^\\w]
50
  )/ix';
51
 
52
+ const BYPASS_COOKIE = "bvfw-bypass-cookie";
53
+ const IP_COOKIE = "bvfw-ip-cookie";
54
+
55
  public function __construct($db, $settings, $ip, $ipstore) {
56
  $this->db = $db;
57
  $this->settings = $settings;
62
  }
63
 
64
  public function init() {
65
+ if ($this->config->canSetCookie()) {
66
+ add_action('init', array($this, 'setBypassCookie'));
67
+ $this->setIPCookie();
68
+ }
69
+ add_filter('status_header', array($this->request, 'captureRespCode'));
70
+ register_shutdown_function(array($this, 'log'));
71
+ }
72
+
73
+ public function setcookie($name, $value, $expire, $path = COOKIEPATH, $domain = COOKIE_DOMAIN) {
74
+ if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
75
+ $secure = function_exists('is_ssl') ? is_ssl() : false;
76
+ @setcookie($name, $value, $expire, $path, $domain, $secure, true);
77
+ } else {
78
+ @setcookie($name, $value, $expire, $path);
79
+ }
80
+ }
81
+
82
+ public function setBypassCookie() {
83
+ if (function_exists('is_user_logged_in') && is_user_logged_in() && !$this->hasValidBypassCookie()) {
84
+ $roleLevel = $this->getCurrentRoleLevel();
85
+ $bypassLevel = $this->config->getBypassLevel();
86
+ if ($roleLevel >= $bypassLevel) {
87
+ $cookie = $this->generateBypassCookie();
88
+ $this->setcookie(BVWPFW::BYPASS_COOKIE, $cookie, time() + 43200);
89
+ }
90
+ }
91
+ }
92
+
93
+ public function generateBypassCookie() {
94
+ $time = floor(time() / 43200);
95
+ $bypassLevel = $this->config->getBypassLevel();
96
+ $cookiekey = $this->config->getCookieKey();
97
+ return sha1($bypassLevel.$time.$cookiekey);
98
+ }
99
+
100
+ public function hasValidBypassCookie() {
101
+ $cookie = (string) $this->request->getCookies(BVWPFW::BYPASS_COOKIE);
102
+ return ($this->config->canSetCookie() && ($cookie === $this->generateBypassCookie()));
103
+ }
104
+
105
+ public function setIPCookie() {
106
+ if (!$this->request->getCookies(BVWPFW::IP_COOKIE)) {
107
+ $ip = $this->request->getIP();
108
+ $cookiekey = $this->config->getCookieKey();
109
+ $time = floor(time() / 3600);
110
+ $cookie = sha1($ip.$time.$cookiekey);
111
+ $this->setcookie(BVWPFW::IP_COOKIE, $cookie, time() + 3600);
112
  }
113
+ }
114
+
115
+ public function getBVCookies() {
116
+ $cookies = array();
117
+ $cookies[BVWPFW::IP_COOKIE] = (string) $this->request->getCookies(BVWPFW::IP_COOKIE);
118
+ return $cookies;
119
+ }
120
+
121
+ public function getCurrentRoleLevel() {
122
+ if (function_exists('current_user_can')) {
123
+ if (function_exists('is_super_admin') && is_super_admin()) {
124
+ return BVWPFWConfig::ROLE_LEVEL_ADMIN;
125
+ }
126
+ foreach ($this->config->getCustomRoles() as $role) {
127
+ if (current_user_can($role)) {
128
+ return BVWPFWConfig::ROLE_LEVEL_CUSTOM;
129
+ }
130
+ }
131
+ foreach (BVWPFWConfig::$roleLevels as $role => $level) {
132
+ if (current_user_can($role)) {
133
+ return $level;
134
+ }
135
+ }
136
+ }
137
+ return 0;
138
  }
139
 
140
  public function log() {
141
+ if ($this->config->canSetCookie()) {
142
+ $canlog = !$this->hasValidBypassCookie();
143
+ } else {
144
+ $canlog = (!function_exists('is_user_logged_in') || !is_user_logged_in());
145
+ }
146
+ if ($canlog) {
147
  $this->logger->log($this->request->getDataToLog());
148
  }
149
  }
178
  }
179
 
180
  public function canBypassFirewall() {
181
+ if ($this->isWhitelistedIP() || $this->hasValidBypassCookie()) {
182
  $this->request->setCategory(BVWPRequest::WHITELISTED);
183
  $this->request->setStatus(BVWPRequest::BYPASSED);
184
  return true;
195
  true, 'GET_');
196
  $result += $this->profileRequestInfo($this->request->getFiles(),
197
  true, 'FILES_');
198
+ $result += $this->profileRequestInfo($this->getBVCookies(),
199
+ true, 'COOKIES_');
200
+ if (strpos($this->request->getPath(), 'admin-ajax.php') !== false) {
201
+ $result += array('BODY_ADMIN_AJAX_ACTION' => $this->request->getBody('action'));
202
+ $result += array('GET_ADMIN_AJAX_ACTION' => $this->request->getQueryString('action'));
203
+ }
204
+ if (strpos($this->request->getPath(), 'admin-post.php') !== false) {
205
+ $result += array('BODY_ADMIN_POST_ACTION' => $this->request->getBody('action'));
206
+ $result += array('GET_ADMIN_POST_ACTION' => $this->request->getQueryString('action'));
207
+ }
208
  $this->request->updateReqInfo($result);
209
  }
210
  if (!$this->canBypassFirewall()) {
594
  return false;
595
  }
596
  }
597
+ endif;
protect/wp_lp/lp.php CHANGED
@@ -44,13 +44,6 @@ class BVWPLP {
44
  }
45
 
46
  public function init() {
47
- if ($this->isActive()) {
48
- $this->lpInit();
49
- }
50
- add_action('clear_lp_config', array($this->config, 'clear'));
51
- }
52
-
53
- public function lpInit() {
54
  add_filter('authenticate', array($this, 'loginInit'), 30, 3);
55
  add_action('wp_login', array($this, 'loginSuccess'));
56
  add_action('wp_login_failed', array($this, 'loginFailed'));
@@ -246,4 +239,4 @@ class BVWPLP {
246
  return intval($rows[0]['count']);
247
  }
248
  }
249
- endif;
44
  }
45
 
46
  public function init() {
 
 
 
 
 
 
 
47
  add_filter('authenticate', array($this, 'loginInit'), 30, 3);
48
  add_action('wp_login', array($this, 'loginSuccess'));
49
  add_action('wp_login_failed', array($this, 'loginFailed'));
239
  return intval($rows[0]['count']);
240
  }
241
  }
242
+ endif;
readme.txt CHANGED
@@ -6,7 +6,7 @@ Donate link: https://www.malcare.com
6
  Requires at least: 4.0
7
  Tested up to: 5.2.1
8
  Requires PHP: 5.3.0
9
- Stable tag: 2.1
10
  License: GPLv2 or later
11
  License URI: [http://www.gnu.org/licenses/gpl-2.0.html](http://www.gnu.org/licenses/gpl-2.0.html)
12
 
@@ -218,6 +218,10 @@ FTP details input into MalCare is processed on our servers. We need your FTP cre
218
  8. With BlogVault's White-Label Solution you can showcase our service under your own brilliant brand.
219
 
220
  == CHANGELOG ==
 
 
 
 
221
  = 2.1 =
222
  * Restructuring classes
223
 
6
  Requires at least: 4.0
7
  Tested up to: 5.2.1
8
  Requires PHP: 5.3.0
9
+ Stable tag: 3.1
10
  License: GPLv2 or later
11
  License URI: [http://www.gnu.org/licenses/gpl-2.0.html](http://www.gnu.org/licenses/gpl-2.0.html)
12
 
218
  8. With BlogVault's White-Label Solution you can showcase our service under your own brilliant brand.
219
 
220
  == CHANGELOG ==
221
+ = 3.1 =
222
+ * Adding params validation
223
+ * Adding support for custom user tables
224
+
225
  = 2.1 =
226
  * Restructuring classes
227
 
wp_site_info.php CHANGED
@@ -38,7 +38,7 @@ class MCWPSiteInfo {
38
  return true;
39
  return is_main_site();
40
  }
41
-
42
  public function respInfo() {
43
  $info = array();
44
  $this->basic($info);
38
  return true;
39
  return is_main_site();
40
  }
41
+
42
  public function respInfo() {
43
  $info = array();
44
  $this->basic($info);