PluginProbe
WebTotem Security / 2.3.2
WebTotem Security v2.3.2
3.0.2 3.0.1 3.0.0 trunk 1.0 1.1 1.2 1.3 1.3.1 1.3.2 1.3.3 2.0 2.1 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.1 2.2.2 2.2.3 All 110 releases
wt-security / library / WT.php

WT.php in WebTotem Security 2.3.2, at library/WT.php

442 lines 25.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php defined('ABSPATH') or die("Protected By WT!");
2
3
4 class WTSEC_LIBRARY_WT
5 {
6
7 const URL = "https://api.wtotem.com/graphql";
8
9 public static function auth($key)
10 {
11 $payload = '{"query":"mutation{ guest{ apiKeys{ auth(apiKey:\"' . $key . '\"),{ token{ value,refreshToken,expiresIn } } } } }"}';
12 return self::requestApi($payload);
13 }
14
15 protected static function requestApi($payload, $token = false, $repeat = false)
16 {
17 if ($token) {
18 $token = WTSEC_LIBRARY_App::getToken();
19 }
20 $args = [
21 'body' => $payload,
22 'timeout' => '60',
23 'sslverify' => false,
24 'headers' => ['Content-Type:application/json', 'Content-Type' => 'application/json', 'source:WORDPRESS', 'Accept: application/json'],
25 ];
26 if (!is_null($token) && $token) {
27 $authorization = "Bearer " . $token;
28 $args['headers'] = array_merge($args['headers'], ["Authorization" => $authorization]);
29 }
30 $response = wp_remote_post(self::URL, $args);
31 $httpcode = wp_remote_retrieve_response_code($response);
32
33 if ($httpcode < 200) {
34 WTSEC_LIBRARY_Session::setNotification("error", WTSEC_LIBRARY_Localization::lmsg('could_not_connect_to_the_server'));
35 }
36 $response = wp_remote_retrieve_body($response);
37 $result = json_decode($response, true);
38 if (isset($result['errors'][0]['message'])) {
39 $message = self::diffMesageForHuman($result['errors'][0]['message']);
40 if (stripos($result['errors'][0]['message'], "token") !== false && !$repeat) {
41 $result = WTSEC_LIBRARY_WT::auth(wtsec_app()->get("api_key"));
42 if (isset($result['data']['guest']['apiKeys']['auth']['token']['value'])) {
43 $token_ = $result['data']['guest']['apiKeys']['auth']['token']['value'];
44 WTSEC_LIBRARY_App::login($token_);
45 return self::requestApi($payload, $token, true);
46 } else {
47 WTSEC_LIBRARY_App::logout();
48 }
49 } else {
50 if ($message !== false) {
51 WTSEC_LIBRARY_Session::setNotification("warning", $message);
52 }
53 }
54 }
55 return $result;
56 }
57
58 public static function diffMesageForHuman($message)
59 {
60 $definition = $message;
61 $excepts = [
62 "RESOURCE_NOT_FOUND", "DUPLICATE_HOST", "INVALID_CREDENTIALS", "INVALID_API_KEY", "Invalid token",
63 ];
64 if (in_array($message, $excepts)) {
65 return false;
66 }
67 switch ($message) {
68 case 'HOSTS_LIMIT_EXCEEDED':
69 $definition = WTSEC_LIBRARY_Localization::lmsg('hosts_limit_exceed');
70 break;
71 case 'Invalid token':
72 $definition = WTSEC_LIBRARY_Localization::lmsg('invalid_token');
73 break;
74 case 'USER_ALREADY_REGISTERED':
75 $definition = WTSEC_LIBRARY_Localization::lmsg('user_already_exist');
76 break;
77 case 'DUPLICATE_HOST':
78 $definition = WTSEC_LIBRARY_Localization::lmsg('duplicate_host');
79 break;
80 case 'INVALID_DOMAIN_NAME':
81 $definition = WTSEC_LIBRARY_Localization::lmsg('invalid_domain_name');
82 break;
83 }
84 return $definition;
85 }
86
87 //del
88 public static function requestURL($url)
89 {
90 $args = [
91 'timeout' => '30',
92 'sslverify' => false,
93 ];
94 $response = wp_remote_get($url, $args);
95 $httpcode = wp_remote_retrieve_response_code($response);
96 if ($httpcode < 200) {
97 WTSEC_LIBRARY_Session::setNotification("error", WTSEC_LIBRARY_Localization::lmsg('could_not_connect_to_the_server')." requestURL");
98 }
99 $response = wp_remote_retrieve_body($response);
100 return $response;
101 }
102
103
104 public static function getFileByUrl($url, $fileName)
105 {
106 $url = $url.'/'.$fileName;
107 $args = [
108 'timeout' => '30',
109 'sslverify' => false,
110 ];
111 $response = wp_remote_get($url, $args);
112 $httpcode = wp_remote_retrieve_response_code($response);
113 if ($httpcode < 200) {
114 WTSEC_LIBRARY_Session::setNotification("error", WTSEC_LIBRARY_Localization::lmsg('could_not_connect_to_the_server')." getFileByUrl");
115 }
116
117 $response = wp_remote_retrieve_body($response);
118 return ["body" => $response, "filename" => $fileName];
119 }
120
121
122 public static function getEmail(){
123 $payload = '{"query":"query { auth { viewer { email __typename } __typename } }"}';
124 $result = self::requestApi($payload, true);
125
126 return $result['data']['auth']['viewer']['email'];
127 }
128
129 public static function getOwnSite($attempt = false)
130 {
131 $payload = '{"query":"query getSites { auth { viewer { sites { ...sites __typename } __typename } __typename }}fragment sites on SiteQueries { list(filter: {}) { edges { node { id hostname title ssl { status __typename } availability { status __typename } reputation { status __typename } ports { status __typename } deface { status __typename } domain { status __typename } antivirus { status __typename } firewall { status __typename } maliciousScript { stack { name __typename } __typename } __typename } __typename } __typename } __typename}"}';
132
133 $result = self::requestApi($payload, true);
134 if (isset($result['data']['auth']['viewer']['sites']['list']['edges'])) {
135 //mutator
136 foreach ($result['data']['auth']['viewer']['sites']['list']['edges'] as &$m) {
137 if (isset($m['node']['hostname'])) {
138 $m['node']['hostname'] = WTSEC_LIBRARY_Idn::idn_to_utf8($m['node']['hostname']);
139 // if ($m['node']['id'] == "c2l0ZV8yNTg=") {
140 // return $m['node'];
141 // }
142 if (self::isSiteUrl($m['node']['hostname'])) {
143 return $m['node'];
144 }
145 }
146 }
147 $add_site = self::addSite(WTSEC_SITE_URL);
148 if (isset($add_site['errors'])) {
149 return $result['data']['node'] = [];
150 } else {
151 if (!$attempt) {
152 return self::getOwnSite(true);
153 }
154 }
155 }
156 return [];
157 }
158
159 public static function isSiteUrl($url)
160 {
161 return WTSEC_LIBRARY_Idn::idn_to_utf8(WTSEC_SITE_URL) === $url;
162 }
163
164 public static function addSite($url)
165 {
166 $payload = '{"variables":{"input":{"title":"' . $url . '","hostname":"' . $url . '","configs":{"scheme":"http","port":80,"wa":{},"dec":{},"ps":{}}}},"query":"mutation ($input: CreateSiteInput) {\n auth {\n sites {\n create(input: $input) {\n id\n hostname\n title\n __typename\n }\n __typename\n }\n __typename\n }\n}\n"}';
167 return self::requestApi($payload, true);
168 }
169
170 public static function getAllChecks($host_id)
171 {
172 $from = time() - (60 * 60 * 24);
173 $to = time();
174 $from_waf = time() - (60 * 60 * 24 * 30);
175 $payload = '{"query":"query($id: ID!, $dateRange: DateRangeInput!) { auth { viewer { sites { one(id: $id) { ports { status ip tcp lastTest { time } } availability { status lastTest { time } responseTime downTime(dateRange: $dateRange) percent(dateRange: $dateRange) } deface { status lastTest { time } words count } domain { status registrar owner email createdDate expiredDate } ports { status lastTest { time } ip tcp country } ssl { status daysLeft expiryDate issueDate } reputation { status lastTest { time } virusList { virus{ type path } antiVirus } } firewall { lastTest { time } status chart(dateRange: $dateRange) { time attacks blocked } report(dateRange: $dateRange) { time attacks ip } } maliciousScript { lastTest { time } status } antivirus { status stats { changed deleted scaned infected error } lastTest { time } isFirstCheck } } } } } }","variables":{"id":"' . $host_id . '","dateRange":{"to":' . $to . ',"from":' . $from_waf . '}}}';
176 // $payload = '{"query":"query($id: ID!, $dateRange:DateRangeInput!) {\n auth {\n viewer {\n sites {\n one(id: $id) {\n ports{\n status\n ip\n tcp\n lastTest{\n time\n }\n }\n availability {\n status\n lastTest {\n time\n }\n responseTime\n downTime(dateRange: $dateRange)\n percent(dateRange: $dateRange)\n }\n deface {\n status\n lastTest {\n time\n }\n words\n count\n }\n domain {\n status\n registrar\n owner\n email\n createdDate\n expiredDate\n }\n ports {\n status\n lastTest {\n time\n }\n ip\n tcp\n country\n }\n ssl {\n status\n daysLeft\n expiryDate\n issueDate\n }\n reputation {\n status\n lastTest {\n time\n }\n virusList {\n viruses\n antiVirus\n }\n }\n firewall {\n lastTest { \n time\n } \n status\n logs{\n edges\n{\n node{\n status\n country\n type\n}\n}\n}\n chart(dateRange: $dateRange) {\n time\n attacks\n blocked\n }\n report(dateRange: $dateRange) {\n time\n attacks\n ip\n }\n }\n maliciousScript{\n lastTest{\n time\n }\n status\n }\n antivirus{\n status \n stats { \n changed\n deleted\n scaned\n infected\n error\n } \n lastTest { \n time\n } \n isFirstCheck\n }\n }\n }\n }\n }\n}","variables":{"id":"' . $host_id . '","dateRange":{"to":' . $to . ',"from":' . $from_waf . '}}}';
177 $response = self::requestApi($payload, true);
178 if (isset($response['data']['auth']['viewer']['sites']['one'])) {
179 return $response['data']['auth']['viewer']['sites']['one'];
180 }
181 return [];
182 }
183
184 public static function changeStatus($config_id, $host_id)
185 {
186 $payload = '{"query":"\n mutation {\n toggleServiceConfig(\n id: ' . $config_id . '\n userhostId: ' . $host_id . '\n ) {\n isActive\n }\n }\n "}';
187 return self::requestApi($payload, true);
188 }
189
190 public static function serviceConnect($id, $service)
191 {
192 $payload = '{"query":"mutation {\n auth {\n agents{\n check(siteId:\"' . $id . '\",service:' . strtolower($service) . ',plugin:WORDPRESS)\n }\n }\n}\n"}';
193 return self::requestApi($payload, true);
194 }
195
196 public static function generateFile($id, $service)
197 {
198 $payload = '{"query":"mutation {\n auth {\n agents{\n generate(siteId:\"' . $id . '\",service:' . strtolower($service) . '){\n agentName\n }\n }\n }\n}\n"}';
199 return self::requestApi($payload, true);
200 }
201
202 public static function generateAmFile($id)
203 {
204 $payload = '{ "operationName":null, "variables":{}, "query":"mutation { auth { am { install(siteId: \"'.$id.'\"){ downloadLink, amFilename, wafFilename, avFilename } } } }"}';
205 return self::requestApi($payload, true);
206 }
207
208 public static function checkStatus($id, $service)
209 {
210 $payload = '{"operationName":null,"variables":{"id":"' . $id . '"},"query":"query ($id: ID!) {\n auth {\n viewer {\n sites {\n one(id: $id) {\n ... on Site {\n configs {\n ... on ' . $service . 'Config {\n isActive\n id\n }\n }\n }\n }\n }\n }\n }\n}\n"}';
211 return self::requestApi($payload, true);
212 }
213
214 public static function getAntivirus($host_id, $limit = 10, $cursor = null, $days = 365, $event = 'infected')
215 {
216 $from = (is_array($days)) ? $days['from'] : time() - (60 * 60 * 24 * $days);
217 $to = (is_array($days)) ? $days['to'] : time();
218 $cursor = ($cursor == null) ? 'null' : '"' . $cursor . '"';
219 $payload = '{"operationName":null,"variables":{"id":"' . $host_id . '","avLogFilter":{"event":"'.$event.'","dateRange":{"to":'.$to.',"from":'.$from.'},"order":{"direction":"DESC","field":"time"},"pagination":{"first":' . $limit . ',"cursor":' . $cursor . '}}},"query":"query ($id: ID!, $avLogFilter: AvLogFilter!) { auth { viewer { sites { one(id: $id) { id ... on Site { configs { ... on AvConfig { isActive id } } } antivirus { status log(avLogFilter: $avLogFilter) { edges { node { filePath matches event signatures time permissions permissionsChanged } } pageInfo { endCursor hasNextPage __typename } } lastTest { time } stats { changed deleted scaned infected } } } } } } }"}';
220 return self::requestApi($payload, true);
221 }
222
223 public static function getQuarantineList($host_id)
224 {
225 $payload = '{"query":"query{ auth{ viewer{ sites{ one(id:\"'.$host_id.'\"){ antivirus{ quarantine{ id path date } } } } } } } "}';
226 return self::requestApi($payload, true);
227 }
228
229 public static function moveToQuarantine($host_id, $path)
230 {
231 $payload = '{"query":"mutation{ auth{ sites{ av{ moveToQuarantine(input:{ siteId:\"'.$host_id.'\", path:\"'.$path.'\" }) } } } } "}';
232 return self::requestApi($payload, true);
233 }
234
235 public static function moveFromQuarantine($id)
236 {
237 $payload = '{"query":"mutation{ auth{ sites{ av{ moveFromQuarantine(id: \"'.$id.'\") } } } } "}';
238 return self::requestApi($payload, true);
239 }
240
241 public static function reportsList($host_id, $limit = 10, $cursor = null)
242 {
243 $cursor = ($cursor == null) ? 'null' : '"' . $cursor . '"';
244 $payload = '{"variables":{ "filter": { "order": { "direction": "DESC", "field": "created_at"}, "siteId":"'.$host_id.'", "pagination":{"first":' . $limit . ', "cursor":' . $cursor . '} } },"query":"query ReportsQuery($filter: ReportListFilter!) { auth { viewer { reports { list(filter: $filter) { edges { node { id site { hostname } createdAt wa dc ps rc sc av waf } cursor } pageInfo { endCursor hasNextPage } } } } } }"}';
245 return self::requestApi($payload, true);
246 }
247
248 public static function reportGenerate($host_id, $period, $services, $language = "en")
249 {
250 $from = (is_array($period)) ? $period['from'] : time() - (60 * 60 * 24 * $period);
251 $to = (is_array($period)) ? $period['to'] : time();
252 $payload = '{"query":"query ($input: GenerateReportInput) { auth { viewer { reports { generate(input: $input) } } } }", "variables":{ "input": { "siteId": "' . $host_id . '", "from": ' . $from . ', "to": ' . $to . ', "wa": '.$services['wa'].', "dc": '.$services['dc'].', "ps": '.$services['ps'].', "rc": '.$services['rc'].', "sc": '.$services['sc'].', "av": '.$services['av'].', "waf": '.$services['waf'].', "language": "'.$language.'" } } }';
253
254 return self::requestApi($payload, true);
255 }
256
257 public static function reportDownload($id)
258 {
259 $payload = '{"query": "query { auth { viewer { reports { download(id: \"' . $id . '\") } } } }"}';
260 return self::requestApi($payload, true);
261 }
262
263 public static function getConfigs($host_id)
264 {
265 $payload = '{"query":"query{ auth{ viewer{ sites{ one(id:\"'.$host_id.'\"){ configs{ ... on WaConfig { id service isActive } ... on AvConfig { id service isActive } ... on DcConfig { id service isActive } ... on DecConfig { id service isActive } ... on RcConfig { id service isActive } ... on CmsConfig { id service isActive } ... on PsConfig { id service isActive } ... on WafConfig { id service isActive } } } } } } } "}';
266 return self::requestApi($payload, true);
267 }
268
269 public static function toggleConfigs($service_id)
270 {
271 $payload = '{"query":"mutation{ auth{ configs{ toggle(id: \"'.$service_id.'\"){ ... on WaConfig { service isActive } ... on AvConfig { service isActive } ... on DcConfig { service isActive } ... on DecConfig { service isActive } ... on RcConfig { service isActive } ... on CmsConfig { service isActive } ... on PsConfig { service isActive } ... on WafConfig { service isActive } } } } } "}';
272 return self::requestApi($payload, true);
273 }
274
275 public static function getFirewall($host_id, $limit = 20, $cursor = null, $days = 365)
276 {
277 $from = (is_array($days)) ? $days['from'] : time() - (60 * 60 * 24 * $days);
278 $to = (is_array($days)) ? $days['to'] : time();
279 $cursor = ($cursor == null) ? 'null' : '"'.$cursor.'"';
280
281 $payload = '{"operationName":"FirewallAttackLog","variables":{"dateRange":{"to":'.$to.',"from":'.$from.'},"id":"'.$host_id.'","wafLogFilter":{"dateRange":{"to":'.$to.',"from":'.$from.'},"order":{"direction":"DESC","field":"time"},"pagination":{"first":'.$limit.',"cursor":' . $cursor . '}}},"query":"query FirewallAttackLog($id: ID!, $wafLogFilter: WafLogFilter!, $dateRange: DateRangeInput!) { auth { viewer { sites { one(id: $id) { firewall { lastTest { time } status... FirewallLogFragment map(dateRange: $dateRange) { attacks, country } __typename } __typename } __typename } __typename } __typename } } fragment FirewallLogFragment on Waf { logs(wafLogFilter: $wafLogFilter) { edges { cursor node { type blocked payload ip location { country { nameEn __typename } __typename } time request status country category __typename } __typename } pageInfo { endCursor hasNextPage __typename } __typename } __typename }"}';
282 $res = self::requestApi($payload, true);
283
284 return $res;
285 }
286
287 public static function getFirewallChart($host_id, $days = 7)
288 {
289 $to = time();
290 $from_waf = ($days <= 1) ? strtotime(date('Y-m-d 00:00:01')) : time() - (60 * 60 * 24 * $days);
291
292 $payload = '{ "query":"query($id: ID!, $dateRange: DateRangeInput!, $wafLogFilter: WafLogFilter!) { auth { viewer { sites { one(id: $id) { firewall { lastTest { time } status logs(wafLogFilter: $wafLogFilter) { edges { node { status country type userAgent } } } chart(dateRange: $dateRange) { time attacks blocked } report(dateRange: $dateRange) { time attacks ip } } } } } } }", "operationName":null,"variables":{"id":"' . $host_id . '","dateRange":{"to":' . $to . ',"from":' . $from_waf . '}, "wafLogFilter": { "dateRange": { "from": ' .$from_waf. ', "to": ' .$to. ' }, "pagination": { "cursor": null }, "order": { "direction": "DESC", "field": "time" } } } }';
293
294 return self::requestApi($payload, true);
295 }
296
297 public static function getStatusIcon($status)
298 {
299 $statuses = [
300 "clean" => [
301 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.ok'),
302 "color" => self::getColor("success"),
303 "image" => "check-mark.svg"
304 ],
305 "pending" => [
306 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.pending'),
307 "color" => self::getColor("grey"),
308 "image" => "loading.svg"
309 ],
310 "expired" => [
311 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.expired'),
312 "color" => self::getColor("orange"),
313 "image" => "warning.svg"
314
315 ],
316 "invalid" => [
317 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.invalid'),
318 "color" => self::getColor("error"),
319 "image" => "warning.svg"
320 ],
321 "no_cert" => [
322 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.no_cert'),
323 "color" => self::getColor("orange"),
324 "image" => "warning.svg"
325 ],
326 "error" => [
327 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.error'),
328 "color" => self::getColor("error"),
329 "image" => "warning.svg"
330 ],
331 "expires" => [
332 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.expires'),
333 "color" => self::getColor("orange"),
334 "image" => "warning.svg"
335 ],
336 "expires_today" => [
337 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.expires_today'),
338 "color" => self::getColor("error"),
339 "image" => "warning.svg"
340 ],
341 "down" => [
342 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.down'),
343 "color" => self::getColor("error"),
344 "image" => "warning.svg"
345 ],
346 "up" => [
347 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.up'),
348 "color" => self::getColor("success"),
349 "image" => "check-mark.svg"
350 ],
351 "infected" => [
352 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.infected'),
353 "color" => self::getColor("error"),
354 "image" => "warning.svg"
355 ],
356 "open_ports" => [
357 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.open'),
358 "color" => self::getColor("orange"),
359 "image" => "warning.svg"
360 ],
361 "deface" => [
362 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.deface'),
363 "color" => self::getColor("error"),
364 "image" => "warning.svg"
365 ],
366 "modified" => [
367 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.modified'),
368 "color" => self::getColor("grey"),
369 "image" => "warning.svg"
370 ],
371 "not_supported" => [
372 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.unsupported'),
373 "color" => self::getColor("orange"),
374 "image" => "warning.svg"
375 ],
376 "not_registered" => [
377 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.not_registered'),
378 "color" => self::getColor("orange"),
379 "image" => "warning.svg"
380 ],
381 "not_installed" => [
382 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.not_installed'),
383 "color" => self::getColor("error"),
384 "image" => "warning.svg"
385 ],
386 "working" => [
387 "text" => WTSEC_LIBRARY_Localization::lmsg('statuses.working'),
388 "color" => self::getColor("success"),
389 "image" => "check-mark.svg"
390 ],
391 ];
392 if (isset($statuses[$status])) {
393 return ["image" => $statuses[$status]["image"], "icon" => $statuses[$status]["color"]["icon"], "color" => $statuses[$status]["color"]["color"], "text" => "<span class='" . $statuses[$status]["color"]["color"] . "'>" . $statuses[$status]["text"] . "</span>"];
394 } else {
395 return ["image" => "warning.svg", "icon" => "ww-icon--status_unknow", "color" => "ww--status_unknow", "text" => "<span class='ww--status_unknow'>Unknown status</span>"];
396 }
397 }
398
399 public static function getColor($type)
400 {
401 $types = [
402 "green" => "is--status--ok",
403 "red" => "is--status--error",
404 "success" => "is--status--ok",
405 "error" => "is--status--error",
406 "orange" => "is--status--warning",
407 "warning" => "is--status--warning",
408 "grey" => "ww--status_unknow"
409 ];
410 $icon_types = [
411 "green" => "ww-icon--status_ok",
412 "red" => "ww-icon--status_error",
413 "success" => "ww-icon--status_ok",
414 "error" => "ww-icon--status_error",
415 "orange" => "ww-icon--status_warning",
416 "warning" => "ww-icon--status_warning",
417 "grey" => "ww-icon--status_unknow"
418 ];
419 return ["icon" => $icon_types[$type], "color" => $types[$type]];
420 }
421
422 public static function getConfigId($configs)
423 {
424 $id = 0;
425 $is_active = false;
426 foreach ($configs as &$config) {
427 if (!empty($config)) {
428 $id = $config['id'];
429 $is_active = $config['isActive'];
430 break;
431 }
432 }
433 return compact('id', 'is_active');
434 }
435
436 public static function getScore(){
437 return self::requestURL("https://api.wtotem.com/site/score?url=".WTSEC_SITE_URL);
438 }
439
440 }
441
442