PluginProbe
WebTotem Security / 2.4.31
WebTotem Security v2.4.31
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 2.2.4 All 109 releases
wt-security / lib / API.php

API.php in WebTotem Security 2.4.31, at lib/API.php

1,260 lines 47.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('WEBTOTEM_INIT') || WEBTOTEM_INIT !== true) {
4 if (!headers_sent()) {
5 header('HTTP/1.1 403 Forbidden');
6 }
7 die("Protected By WebTotem!");
8 }
9
10 /**
11 * WebTotem API class.
12 *
13 * Mostly contains wrappers for API methods. Check and send methods.
14 *
15 * @version 1.0
16 * @copyright (C) 2022 WebTotem team (http://wtotem.com)
17 * @license GNU/GPL: http://www.gnu.org/copyleft/gpl.html
18 */
19 class WebTotemAPI extends WebTotem
20 {
21
22 /**
23 * Method for getting an auth token.
24 *
25 * @param string $api_key
26 * Application programming interface key.
27 *
28 * @return bool|string
29 * Returns auth status
30 */
31 public static function auth($api_key)
32 {
33 $domain = WEBTOTEM_SITE_DOMAIN;
34
35 if (substr($api_key, 1, 1) == "-") {
36 $prefix = substr($api_key, 0, 1);
37 if ($api_url = self::getApiUrl($prefix)) {
38 WebTotemOption::setOptions(['api_url' => $api_url]);
39 } else {
40 WebTotemOption::setNotification('error', __('Invalid API key', 'wtotem'));
41 return FALSE;
42 }
43 $api_key = substr($api_key, 2);
44 }
45
46 if (empty($api_key)) {
47 return FALSE;
48 }
49 $payload = '{"query":"mutation{ guest{ apiKeys{ auth(apiKey:\"' . $api_key . '\", source:\"' . $domain . '\"),{ token{ value, refreshToken, expiresIn } } } } }"}';
50 $result = self::sendRequest($payload, FALSE, TRUE);
51
52 if (isset($result['data']['guest']['apiKeys']['auth']['token']['value'])) {
53 $auth_token = $result['data']['guest']['apiKeys']['auth']['token'];
54 if(!WebTotemOption::isActivated()){
55 WebTotemOption::login(['token' => $auth_token, 'api_key' => $api_key]);
56 WebTotemAgentManager::postdelete();
57 } else {
58 WebTotemOption::setOptions(['auth_token' => $auth_token['value'], 'auth_token_expired' => time() + $auth_token['expiresIn'] - 60]);
59 }
60
61 return 'success';
62 } elseif (isset($result['errors'][0]['message']) and $result['errors'][0]['message'] == 'INVALID_API_KEY') {
63 WebTotemOption::logout();
64 }
65
66 return FALSE;
67 }
68
69 /**
70 * Method for getting API url.
71 *
72 * @param string $prefix
73 *
74 * @return string|bool
75 * API url
76 */
77 public static function getApiUrl($prefix)
78 {
79 $urls = [
80 'P' => '.wtotem.com',
81 'C' => '.webtotem.kz',
82 ];
83
84 if (array_key_exists($prefix, $urls)) {
85 return 'https://api' . $urls[$prefix] . '/graphql';
86 }
87 return false;
88 }
89
90 /**
91 * Get site info from API server.
92 *
93 * @param string $attempt
94 * Is the request an attempt to get host data.
95 *
96 * @return array
97 * Returns host data.
98 */
99 public static function siteInfo($attempt = FALSE)
100 {
101 if (self::isMultiSite()) {
102 $host['id'] = WebTotemOption::getSessionOption('host_id');
103
104 if ($host['id']) {
105 return $host;
106 }
107 }
108
109 $host = WebTotemOption::getHost();
110
111 if ($host['id']) {
112 return $host;
113 }
114
115 if (self::isMultiSite()) {
116 $sites = get_sites();
117 foreach ($sites as $site) {
118 $domain = untrailingslashit($site->domain . $site->path);
119 self::addSite($domain);
120 }
121
122 if (!$attempt) {
123 return self::siteInfo(TRUE);
124 }
125 } else {
126 $domain = WEBTOTEM_SITE_DOMAIN;
127 return self::addSite($domain);
128 }
129
130
131 return [];
132 }
133
134 /**
135 * Method for adding a site to the WebTotem platform.
136 *
137 * @param string $domain
138 * Domain to add.
139 *
140 * @return array
141 * Returns host data.
142 */
143 public static function addSite($domain)
144 {
145 if (function_exists('idn_to_utf8')) {
146 $domain = idn_to_utf8($domain);
147 }
148
149 $matches = self::checkForMatches($domain);
150
151 // Checking if the site has been added to the WebTotem.
152 if (array_key_exists('edges', $matches)) {
153
154 foreach ($matches['edges'] as $site) {
155 $site = $site['node'];
156 $hostname = untrailingslashit($site['hostname']);
157 // If it added, save site data to DB.
158 if ($hostname == $domain or $hostname == 'www.' . $domain) {
159 WebTotemOption::setHost($site['hostname'], $site['id']);
160 return [
161 'id' => $site['id'],
162 'name' => $site['hostname'],
163 ];
164 }
165 }
166
167 }
168
169 $scheme = is_ssl() ? 'https' : 'http';
170
171 // If the site is not added then try to add.
172 $payload = '{"variables":{"input":{"title":"' . $domain . '","hostname":"' . $domain . '","configs":{"scheme":"' . $scheme . '","port":' . $_SERVER['SERVER_PORT'] . ',"wa":{},"dec":{},"ps":{}}}},"query":"mutation ($input: CreateSiteInput) { auth { sites { create(input: $input) { id hostname title } } } }"}';
173 $add_site = self::sendRequest($payload, TRUE);
174 if (isset($add_site['errors'])) {
175 WebTotemOption::setNotification('error', __('Failed to add the site to the WebTotem platform.', 'wtotem'));
176 } else {
177 if ($host = $add_site['data']['auth']['sites']['create']) {
178 // If it added, save site ID.
179 WebTotemOption::setHost($host['title'], $host['id']);
180 return [
181 'id' => $host['id'],
182 'name' => $host['title'],
183 ];
184 }
185 }
186 return [];
187 }
188
189 /**
190 * Get all sites from API.
191 *
192 * @param string $cursor
193 * Mark for loading data.
194 * @param string $limit
195 * Limit of sites to loading.
196 *
197 * @return array
198 * Returns host data.
199 */
200 public static function getSites($cursor = null, $limit = 15, $filter = false)
201 {
202 $cursor = ($cursor == null) ? 'null' : '\"' . $cursor . '\"';
203 if (!$filter) {
204 $filter = ($limit === 0) ? '' : 'pagination:{ first: ' . $limit . ', cursor: ' . $cursor . ' }';
205 }
206
207 $payload = '{"query":"query getSites { auth { viewer { sites { ...sites } } } } fragment sites on SiteQueries { list(filter: { ' . $filter . ' }) { pageInfo{ hasNextPage endCursor } edges { node { id hostname title createdAt ssl { status } availability { status } reputation { status } ports { status } deface { status } antivirus { status } firewall { status } maliciousScript { stack { name } } } } } }"}';
208 $result = self::sendRequest($payload, true);
209
210 if (isset($result['data']['auth']['viewer']['sites']['list']['edges'])) {
211 return $result['data']['auth']['viewer']['sites']['list'];
212 }
213
214 return [];
215 }
216
217 /**
218 * Check the site's presence in the list on the API side.
219 *
220 * @param string $site
221 * The domain we want to check.
222 *
223 * @return array
224 * Returns host data.
225 */
226 public static function checkForMatches($site)
227 {
228 $payload = '{"query": "query getSites { auth { viewer { sites { list(filter: { search: \"'. $site .'\" }) { edges{ node{ hostname id } } } } } } }" }';
229 $result = self::sendRequest($payload, true);
230
231 if (isset($result['data']['auth']['viewer']['sites']['list']['edges'])) {
232 return $result['data']['auth']['viewer']['sites']['list'];
233 }
234
235 return [];
236 }
237
238
239 /**
240 * Method to get the agents file names and AM file link.
241 *
242 * @param string $host_id
243 * Host id on WebTotem.
244 *
245 * @return array
246 * Returns agents files data.
247 */
248 public static function getAgentsFiles($host_id)
249 {
250
251 if (WebTotem::isMultiSite()) {
252 $all_hosts = WebTotemOption::getOption('all_hosts');
253 $all_hosts = $all_hosts ? json_decode($all_hosts, true) : [];
254
255 $siteIdsArray = $all_hosts ? array_values($all_hosts) : [];
256 $siteIds = $siteIdsArray ? addslashes(WebTotem::convertArrayToString($siteIdsArray)) : '';
257
258 $payload = '{"query":"mutation { auth { am { installMultisite(mainSiteId: \"' . $host_id . '\", siteIds: [' . $siteIds . ']){ downloadLink, amFilename, wafFilename, avFilename } } } }"}';
259 $response = self::sendRequest($payload, TRUE);
260
261 if (isset($response['data']['auth']['am']['installMultisite'])) {
262 return $response['data']['auth']['am']['installMultisite'];
263 }
264 } else {
265 $payload = '{"query":"mutation { auth { am { install(siteId: \"' . $host_id . '\"){ downloadLink, amFilename, wafFilename, avFilename } } } }"}';
266 $response = self::sendRequest($payload, TRUE);
267 if (isset($response['data']['auth']['am']['install'])) {
268 return $response['data']['auth']['am']['install'];
269 }
270 }
271 return [];
272 }
273
274 /**
275 * Add secondary MultiSite host.
276 *
277 * @param $new_sites
278 * An array with sites to add.
279 *
280 * @return void.
281 */
282 public static function addMultiSiteNewSites($new_sites)
283 {
284 // Host id of the main site in MultiSite network.
285 $main_host = WebTotemOption::getMainHost();
286
287 foreach ($new_sites as $site) {
288 $all_sites = self::getSites(null, 1000000);
289 $host = self::addSite($site, $all_sites);
290 if (key_exists('id', $host)) {
291 $payload = '{"query":"mutation { auth { am { addMultisiteHost(mainSiteId: \"' . $main_host['id'] . '\", siteId: \"' . $host['id'] . '\") } } }"}';
292
293 $result = self::sendRequest($payload, TRUE);
294 if (!$result['errors'][0]['message']) {
295 WebTotemOption::setNotification('info', __('A new website has been added: ', 'wtotem') . $site);
296 }
297 }
298 }
299 }
300
301 /**
302 * Remove secondary MultiSite host.
303 *
304 * @param $host_id
305 * Host id on WebTotem.
306 *
307 * @return bool
308 * Returns result removing host.
309 */
310 public static function removeMultiSiteHost($host_id)
311 {
312 $payload = '{"query":"mutation { auth { am { removeMultisiteHost(siteId: \"' . $host_id . '\") } } }"}';
313 $response = self::sendRequest($payload, TRUE);
314 if (isset($response['data']['auth']['am']['removeSecondaryMultisiteHost'])) {
315 return $response['data']['auth']['am']['removeSecondaryMultisiteHost'];
316 }
317
318 return false;
319 }
320
321 /**
322 * Method to get agents (AM, WAF, AV) statuses.
323 *
324 * @param string $host_id
325 * Host id on WebTotem.
326 *
327 * @return array
328 * Returns agents statuses data.
329 */
330 public static function getAgentsStatusesFromAPI($host_id)
331 {
332 $payload = '{"query":"query ($id: ID!) { auth { viewer { sites { one(id: $id) { agentManager { statuses { am { status } av { status } waf { status } } } } } } } }", "variables":{"id":"' . $host_id . '"}}';
333 $response = self::sendRequest($payload, TRUE);
334
335 if (isset($response['data']['auth']['viewer']['sites']['one']['agentManager']['statuses'])) {
336 return $response['data']['auth']['viewer']['sites']['one']['agentManager']['statuses'];
337 }
338
339 return [];
340 }
341
342 /**
343 * Method to get user time zone.
344 *
345 * @return string|bool
346 * Returns time zone data.
347 */
348 public static function getTimeZone()
349 {
350 $payload = '{"query":"query { auth { viewer{ timezone } } } "}';
351 $response = self::sendRequest($payload, TRUE);
352
353 if (isset($response['data']['auth']['viewer']['timezone'])) {
354 return $response['data']['auth']['viewer']['timezone'];
355 }
356 return FALSE;
357 }
358
359 /**
360 * Method for get all the site security data.
361 *
362 * @param string $host_id
363 * Host id on WebTotem.
364 * @param int|array $days
365 * For what period data is needed.
366 *
367 * @return array
368 * Returns all data.
369 */
370 public static function getAllData($host_id, $days = 7)
371 {
372 $language = WebTotem::getLanguage();
373 $period = WebTotem::getPeriod($days);
374
375 $payload = '{"query":"query($id: ID!, $dateRange: DateRangeInput!, $language: Language!, $dateRangeWeek: DateRangeInput!, $wafLogFilter: WafLogFilter!) { auth { viewer { sites { one(id: $id) { openPathSearch { time paths { httpCode severity path } } ports { status lastTest { time } ignorePorts TCPResults{ port technology version cveList{id summary } } UDPResults { port technology version cveList{id summary } } } domain { lastScanResult { isTaken hasSite redirectLink isLocal protection ips { ip location } status time } } sslResults{ results{ certStatus certIssuerName certExpiryDate certIssueDate } } ssl { status daysLeft expiryDate issueDate } reputation { status lastTest { time } virusList { virus{ type path } antiVirus } } firewall { lastTest { time } logs(wafLogFilter: $wafLogFilter){ edges{ node{ type blocked payload ip proxyIp userAgent description source region signatureId location{ country{ nameEn } } time request status country category } } } map(dateRange: $dateRange) { attacks, country } status chart(dateRange: $dateRange) { time attacks blocked } report(dateRange: $dateRange) { time attacks ip } } serverStatus { info { phpVersion phpServerUser phpServerSoftware phpGatewayInterface phpServerProtocol osInfo cpuCount cpuModel CpuFreq cpuFamily lsCpu maxExecTime mathLibraries } ramChart(dateRange: $dateRangeWeek){ total value time } cpuChart(dateRange: $dateRangeWeek){ value time } discUsage{ total free } status } maliciousScript { lastTest { time } status } scoring( language: $language ){ score lastTest{ time } result{ ip country isHigherThan }} agentManager{ createdAt } antivirus { status stats { changed deleted scanned infected error } lastTest { time } isFirstCheck } } } } } }","variables":{"id":"' . $host_id . '","dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '}, "dateRangeWeek":{"to":' . $period['to'] . ',"from":' . $period['from'] . '}, "wafLogFilter": {"dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '},"order":{"direction":"DESC","field":"time"},"pagination":{"first": 10,"cursor":null}}, "language":"' . $language . '"}}';
376 $response = self::sendRequest($payload, TRUE);
377
378 if (isset($response['data']['auth']['viewer']['sites']['one'])) {
379 return $response['data']['auth']['viewer']['sites']['one'];
380 }
381
382 return [];
383 }
384
385
386 /**
387 * Method for get all the site security data.
388 *
389 * @param string $host_id
390 * Host id on WebTotem.
391 *
392 * @return array
393 * Returns all data.
394 */
395 public static function getMonitoring($host_id)
396 {
397
398 $payload = '{"query":"query($id: ID!) { auth { viewer { sites { one(id: $id) { domain { lastScanResult { isTaken hasSite redirectLink isLocal protection ips { ip location } status time } } sslResults{ results{ certStatus certIssuerName certExpiryDate certIssueDate } } ssl { status daysLeft expiryDate issueDate } reputation { status lastTest { time } virusList { virus{ type path } antiVirus } } } } } } }","variables":{"id":"' . $host_id . '"}}';
399 $response = self::sendRequest($payload, TRUE);
400
401 if (isset($response['data']['auth']['viewer']['sites']['one'])) {
402 return $response['data']['auth']['viewer']['sites']['one'];
403 }
404
405 return [];
406 }
407
408 /**
409 * Method to get firewall data.
410 *
411 * @param string $host_id
412 * Host id on WebTotem.
413 * @param int $limit
414 * Limit on the number of records.
415 * @param string $cursor
416 * Mark for loading data.
417 * @param int|array $days
418 * For what period data is needed.
419 *
420 * @return array
421 * Returns firewall data.
422 */
423 public static function getFirewall($host_id, $limit = 20, $cursor = NULL, $days = 365)
424 {
425 $period = WebTotem::getPeriod($days);
426 $cursor = ($cursor == NULL) ? 'null' : '"' . $cursor . '"';
427
428 $payload = '{"query":"query($id: ID!, $wafLogFilter: WafLogFilter!, $dateRange: DateRangeInput!) { auth { viewer { sites { one(id: $id) { firewall { lastTest { time } status map(dateRange: $dateRange) { attacks, country, location { country { nameEn } } } chart(dateRange: $dateRange) { time attacks blocked } ...FirewallLogFragment } agentManager { createdAt } } } } } } fragment FirewallLogFragment on Waf { logs(wafLogFilter: $wafLogFilter) { edges { cursor node { type blocked payload ip proxyIp userAgent description source region signatureId location { country { nameEn } } time request status country category } } pageInfo { endCursor hasNextPage } } }", "variables":{"dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '},"id":"' . $host_id . '","wafLogFilter":{"dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '},"order":{"direction":"DESC","field":"time"},"pagination":{"first":' . $limit . ',"cursor":' . $cursor . '}}} }';
429 $response = self::sendRequest($payload, TRUE);
430
431 if (isset($response['data']['auth']['viewer']['sites']['one'])) {
432 return $response['data']['auth']['viewer']['sites']['one'];
433 }
434
435 return [];
436 }
437
438 /**
439 * Method to get firewall chart data.
440 *
441 * @param string $host_id
442 * Host id on WebTotem.
443 * @param int $days
444 * For what period data is needed.
445 *
446 * @return array
447 * Returns firewall chart data.
448 */
449 public static function getFirewallChart($host_id, $days = 7)
450 {
451 $period = WebTotem::getPeriod($days);
452
453 $payload = '{ "query":"query($id: ID!, $dateRange: DateRangeInput!) { auth { viewer { sites { one(id: $id) { firewall { lastTest { time } status map(dateRange: $dateRange) { attacks, country, location { country { nameEn } } } chart(dateRange: $dateRange) { time attacks blocked } } } } } } }", "operationName":null,"variables":{"id":"' . $host_id . '","dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '} } }';
454 $response = self::sendRequest($payload, TRUE);
455
456 if (isset($response['data']['auth']['viewer']['sites']['one']['firewall'])) {
457 return $response['data']['auth']['viewer']['sites']['one']['firewall'];
458 }
459
460 return [];
461 }
462
463 /**
464 * Method to set firewall settings.
465 *
466 * @param string $host_id
467 * Host id on WebTotem.
468 * @param array $settings
469 * User-specified settings.
470 *
471 * @return array
472 * Returns information whether the request was successful.
473 */
474 public static function setFirewallSettings($host_id, array $settings)
475 {
476 $payload = '{"variables":{"input": {"siteId": "' . $host_id . '", "gdn": ' . $settings['gdn'] . ', "dosProtection": ' . $settings['dosProtection'] . ', "dosLimit": ' . $settings['dosLimit'] . ', "loginAttemptsProtection": ' . $settings['loginAttemptsProtection'] . ', "loginAttemptsLimit": ' . $settings['loginAttemptsLimit'] . '}},"query":"mutation WafSettings($input: WafSettingsInput!) { auth { sites { waf{ settings(input: $input) { gdn dosProtection loginAttemptsProtection dosLimit loginAttemptsLimit } } } } }"}';
477 return self::sendRequest($payload, TRUE);
478 }
479
480 /**
481 * Method to get antivirus data.
482 *
483 * @param array $params
484 * Parameters for filtering data.
485 *
486 * @return array
487 * Returns antivirus data.
488 */
489 public static function getAntivirus(array $params)
490 {
491
492 $cursor = ($params['cursor']) ? '"' . $params['cursor'] . '"' : 'null';
493 $event = ($params['event']) ? '"' . $params['event'] . '"' : '"new"';
494 $permissions = ($params['permissions']) ? ' "permissionsChanged":true, ' : '';
495 $period = WebTotem::getPeriod($params['days']);
496
497 $payload = '{"operationName":null,"variables":{"id":"' . $params['host_id'] . '","avLogFilter":{' . $permissions . '"event":' . $event . ', "dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '},"order":{"direction":"DESC","field":"time"},"pagination":{"first":' . $params['limit'] . ',"cursor":' . $cursor . '}}},"query":"query ($id: ID!, $avLogFilter: AvLogFilter!) { auth { viewer { sites { one(id: $id) { id ... on Site { configs { ... on AvConfig { isActive id } } } antivirus { quarantine{ id path date } status log(avLogFilter: $avLogFilter) { edges { node { filePath event signatures time permissions permissionsChanged } } pageInfo { endCursor hasNextPage } } lastTest { time } stats { changed deleted scanned infected } } } } } } }"}';
498 $response = self::sendRequest($payload, TRUE);
499
500 if (isset($response['data']['auth']['viewer']['sites']['one']['antivirus'])) {
501 return $response['data']['auth']['viewer']['sites']['one']['antivirus'];
502 }
503 return [];
504 }
505
506 /**
507 * Method to get antivirus last test.
508 *
509 * @param string $host_id
510 * Host id on WebTotem.
511 *
512 * @return array
513 * Returns antivirus last test data.
514 */
515 public static function getAntivirusLastTest($host_id)
516 {
517
518 $payload = '{"variables":{"id":"' . $host_id . '"},"query":"query ($id: ID!) { auth { viewer { sites { one(id: $id) { antivirus { status lastTest { time } } } } } } }"}';
519 $response = self::sendRequest($payload, TRUE);
520
521 if (isset($response['data']['auth']['viewer']['sites']['one']['antivirus'])) {
522 return $response['data']['auth']['viewer']['sites']['one']['antivirus'];
523 }
524 return [];
525 }
526
527 /**
528 * Method to force check services.
529 *
530 * @param string $host_id
531 * Host id on WebTotem.
532 * @param string $service
533 * Service that needs to be checked.
534 *
535 * @return array
536 * Returns information whether the request was successful.
537 */
538 public static function forceCheck($host_id, $service)
539 {
540 $payload = '{"variables":{"id":"' . $host_id . '","service":"' . $service . '"},"query":"mutation ($id: ID!, $service: ForceCheckService!) { auth { sites { forceCheck(siteId: $id, service: $service) } } }"} ';
541 return self::sendRequest($payload, TRUE);
542 }
543
544 /**
545 * Method to export antivirus report.
546 *
547 * @param string $host_id
548 * Host id on WebTotem.
549 * @param int|array $days
550 * For what period data is needed.
551 *
552 * @return array
553 * Returns information whether the request was successful.
554 */
555 public static function avExport($host_id, $days = 30)
556 {
557 $period = WebTotem::getPeriod($days);
558 $payload = '{"variables":{ "input":{"siteId":"' . $host_id . '", "dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '} }},"query":"mutation ($input: AvLogExportInput!) { auth { sites { av { export(input: $input) } } } }"} ';
559 return self::sendRequest($payload, TRUE);
560 }
561
562 /**
563 * Method to get quarantine data.
564 *
565 * @param string $host_id
566 * Host id on WebTotem.
567 *
568 * @return array
569 * Returns quarantine data.
570 */
571 public static function getQuarantineList($host_id)
572 {
573 $payload = '{"query":"query{ auth{ viewer{ sites{ one(id:\"' . $host_id . '\"){ antivirus{ quarantine{ id path date } } } } } } } "}';
574 $response = self::sendRequest($payload, TRUE);
575
576 if (isset($response['data']['auth']['viewer']['sites']['one']['antivirus']['quarantine'])) {
577 return $response['data']['auth']['viewer']['sites']['one']['antivirus']['quarantine'];
578 }
579 return [];
580 }
581
582 /**
583 * Method to move file to quarantine.
584 *
585 * @param string $host_id
586 * Host id on WebTotem.
587 * @param string $path
588 * Path to the file.
589 *
590 * @return array
591 * Returns information whether the request was successful.
592 */
593 public static function moveToQuarantine($host_id, $path)
594 {
595 $payload = '{"query":"mutation{ auth{ sites{ av{ moveToQuarantine(input:{ siteId:\"' . $host_id . '\", path:\"' . $path . '\" }) } } } } "}';
596 return self::sendRequest($payload, TRUE);
597 }
598
599 /**
600 * Method to move file from quarantine.
601 *
602 * @param string $id
603 * Id assigned to the file.
604 *
605 * @return array
606 * Returns information whether the request was successful.
607 */
608 public static function moveFromQuarantine($id)
609 {
610 $payload = '{"query":"mutation{ auth{ sites{ av{ moveFromQuarantine(id: \"' . $id . '\") } } } } "}';
611 return self::sendRequest($payload, TRUE);
612 }
613
614 /**
615 * Method to get server status data.
616 *
617 * @param string $host_id
618 * Host id on WebTotem.
619 * @param int|array $days
620 * For what period data is needed.
621 *
622 * @return array
623 * Returns server status data.
624 */
625 public static function getServerStatusData($host_id, $days = 7)
626 {
627 $period = WebTotem::getPeriod($days);
628 $payload = '{ "query":"query($id: ID!, $dateRange: DateRangeInput!) { auth { viewer { sites { one(id: $id) { serverStatus { info { phpVersion phpServerUser phpServerSoftware phpGatewayInterface phpServerProtocol osInfo cpuCount cpuModel CpuFreq cpuFamily lsCpu maxExecTime mathLibraries } ramChart(dateRange: $dateRange){ total value time } cpuChart(dateRange: $dateRange){ value time } } } } } } }", "variables":{"id":"' . $host_id . '","dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '} } }';
629
630 $response = self::sendRequest($payload, TRUE);
631
632 if (isset($response['data']['auth']['viewer']['sites']['one']['serverStatus'])) {
633 return $response['data']['auth']['viewer']['sites']['one']['serverStatus'];
634 }
635
636 return [];
637 }
638
639 /**
640 * Method to remove port from ignore list.
641 *
642 * @param string $host_id
643 * Host id on WebTotem.
644 * @param string $port
645 * User specified port.
646 *
647 * @return array
648 * Returns information whether the request was successful.
649 */
650 public static function removeIgnorePort($host_id, $port)
651 {
652 $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "port":' . $port . '} },"query":"mutation($input: IgnorePortInput!) { auth { sites { ps { removeIgnorePort(input: $input) } } } }"} ';
653 return self::sendRequest($payload, TRUE);
654 }
655
656 /**
657 * Method to add port to ignore list.
658 *
659 * @param string $host_id
660 * Host id on WebTotem.
661 * @param string $port
662 * User specified port.
663 *
664 * @return array
665 * Returns information whether the request was successful.
666 */
667 public static function addIgnorePort($host_id, $port)
668 {
669 $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "port":' . (int)$port . '} },"query":"mutation($input: IgnorePortInput!) { auth { sites { ps { addIgnorePort(input: $input) } } } }"} ';
670 return self::sendRequest($payload, TRUE);
671 }
672
673 /**
674 * Method to get all ports list.
675 *
676 * @param string $host_id
677 * Host id on WebTotem.
678 *
679 * @return array
680 * Returns ports data.
681 */
682 public static function getAllPortsList($host_id)
683 {
684 $payload = '{"query":"query($id: ID!) { auth { viewer { sites { one(id: $id) { ports { status lastTest { time } ignorePorts TCPResults{ port technology version cveList{id summary } } UDPResults { port technology version cveList{id summary } } } } } } } } ","variables":{"id":"' . $host_id . '"}}';
685
686 $response = self::sendRequest($payload, TRUE);
687
688 if (isset($response['data']['auth']['viewer']['sites']['one']['ports'])) {
689 return $response['data']['auth']['viewer']['sites']['one']['ports'];
690 }
691
692 return [];
693 }
694
695 /**
696 * Method to get all ports list.
697 *
698 * @param string $host_id
699 * Host id on WebTotem.
700 *
701 * @return array
702 * Returns ports data.
703 */
704 public static function getOpenPaths($host_id)
705 {
706 $payload = '{"query":"query($id: ID!) { auth { viewer { sites { one(id: $id) { openPathSearch { time paths { httpCode severity path } } } } } } } ","variables":{"id":"' . $host_id . '"}}';
707
708 $response = self::sendRequest($payload, TRUE);
709
710 if (isset($response['data']['auth']['viewer']['sites']['one']['openPathSearch'])) {
711 return $response['data']['auth']['viewer']['sites']['one']['openPathSearch'];
712 }
713
714 return [];
715 }
716
717 /**
718 * Method to get all reports.
719 *
720 * @param string $host_id
721 * Host id on WebTotem.
722 * @param int $limit
723 * Limit on the number of records.
724 * @param string $cursor
725 * Mark for loading data.
726 *
727 * @return array
728 * Returns reports data.
729 */
730 public static function getAllReports($host_id, $limit = 10, $cursor = NULL)
731 {
732 $cursor = ($cursor == NULL) ? 'null' : '"' . $cursor . '"';
733 $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 } } } } } }"}';
734 $response = self::sendRequest($payload, TRUE);
735
736 if (isset($response['data']['auth']['viewer']['reports']['list']['edges'])) {
737 return $response['data']['auth']['viewer']['reports']['list'];
738 }
739
740 return [];
741 }
742
743 /**
744 * Method to generate report.
745 *
746 * @param string $host_id
747 * Host id on WebTotem.
748 * @param int|array $days
749 * For what period data is needed.
750 * @param array $services
751 * User-specified module settings.
752 *
753 * @return string|bool
754 * Returns report download link.
755 */
756 public static function generateReport(string $host_id, $days, array $services)
757 {
758 $period = WebTotem::getPeriod($days);
759 $language = WebTotem::getLanguage();
760
761 $payload = '{"query":"query ($input: GenerateReportInput) { auth { viewer { reports { generate(input: $input) } } } }", "variables":{ "input": { "siteId": "' . $host_id . '", "from": ' . $period['from'] . ', "to": ' . $period['to'] . ', "wa": ' . $services['wa'] . ', "dc": ' . $services['dc'] . ', "ps": ' . $services['ps'] . ', "rc": ' . $services['rc'] . ', "sc": ' . $services['sc'] . ', "av": ' . $services['av'] . ', "waf": ' . $services['waf'] . ', "language": "' . $language . '" } } }';
762 $response = self::sendRequest($payload, TRUE);
763
764 if (isset($response['data']['auth']['viewer']['reports']['generate'])) {
765 return $response['data']['auth']['viewer']['reports']['generate'];
766 }
767
768 return FALSE;
769 }
770
771 /**
772 * Method to download report.
773 *
774 * @param string $id
775 * Assigned to the report.
776 *
777 * @return string|bool
778 * Returns report download link.
779 */
780 public static function downloadReport($id)
781 {
782 $payload = '{"query": "query { auth { viewer { reports { download(id: \"' . $id . '\") } } } }"}';
783 $response = self::sendRequest($payload, TRUE);
784
785 if (isset($response['data']['auth']['viewer']['reports']['download'])) {
786 return $response['data']['auth']['viewer']['reports']['download'];
787 }
788
789 return FALSE;
790 }
791
792 /**
793 * Method to get configs data.
794 *
795 * @param string $host_id
796 * Host id on WebTotem.
797 *
798 * @return array|bool
799 * Returns configs data.
800 */
801 public static function getConfigs($host_id)
802 {
803 $payload = '{"query":"query{ auth{ viewer{ sites{ one(id:\"' . $host_id . '\"){ configs{ ... on WaConfig { id service isActive notifications } ... on WafConfig { id service isActive notifications } ... on AvConfig { id service isActive notifications } ... on DcConfig { id service isActive notifications } ... on DecConfig { id service isActive } ... on RcConfig { id service isActive notifications} ... on CmsConfig { id service isActive } ... on PsConfig { id service isActive notifications } ... on SsConfig { id service isActive } ... on ScConfig { id service isActive } } } } } } } "}';
804 $response = self::sendRequest($payload, TRUE);
805
806 if (isset($response['data']['auth']['viewer']['sites']['one']['configs'])) {
807 return $response['data']['auth']['viewer']['sites']['one']['configs'];
808 }
809
810 return FALSE;
811 }
812
813 /**
814 * Method to toggle modules config.
815 *
816 * @param string $service_id
817 * Service id that we enable or disable.
818 *
819 * @return string|bool
820 * Returns information whether the request was successful.
821 */
822 public static function toggleConfigs($service_id)
823 {
824 $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 } } } } } "}';
825 $response = self::sendRequest($payload, TRUE);
826
827 if (isset($response['data']['auth']['configs']['toggle'])) {
828 return $response['data']['auth']['configs']['toggle'];
829 }
830
831 return FALSE;
832 }
833
834 /**
835 * Method to toggle modules notification.
836 *
837 * @param string $host_id
838 * Host id on WebTotem.
839 * @param string $service
840 * Service id in which we enable or disable notifications.
841 *
842 * @return string|bool
843 * Returns information whether the request was successful.
844 */
845 public static function toggleNotifications($host_id, $service)
846 {
847 $payload = '{"query":"mutation{ auth{ sites{ toggleNotifications(siteId: \"' . $host_id . '\", service: ' . $service . ') } } }"}';
848 $response = self::sendRequest($payload, TRUE);
849
850 if (isset($response['data']['auth']['sites']['toggleNotifications'])) {
851 return $response;//['data']['auth']['sites']['toggleNotifications'];
852 }
853
854 return FALSE;
855 }
856
857 /**
858 * Method to get allow/deny ip list.
859 *
860 * @param string $host_id
861 * Host id on WebTotem.
862 *
863 * @return array|bool
864 * Returns ip allow/deny lists.
865 */
866 public static function getIpLists($host_id)
867 {
868 $payload = '{"variables":{ "id": "' . $host_id . '" },"query":"query($id: ID!) { auth { viewer { sites{ one(id: $id){ firewall{ blackList{ id ip createdAt } whiteList{ id ip createdAt } settings{ gdn dosProtection dosLimit loginAttemptsProtection loginAttemptsLimit } } } } } } }"} ';
869 $response = self::sendRequest($payload, TRUE);
870
871 if (isset($response['data']['auth']['viewer']['sites']['one']['firewall'])) {
872 return $response['data']['auth']['viewer']['sites']['one']['firewall'];
873 }
874
875 return [];
876 }
877
878 /**
879 * Method to add ip to allow/deny list.
880 *
881 * @param string $host_id
882 * Host id on WebTotem.
883 * @param string $ips
884 * Ip address list.
885 * @param string $list
886 * Allow or deny list.
887 *
888 * @return bool
889 * Returns information whether the request was successful.
890 */
891 public static function addIpToList($host_id, $ips, $list)
892 {
893
894 if ($ips) {
895 $ips = WebTotem::convertIpListForApi($ips);
896 $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "ips": ' . $ips . ', "color": "' . $list . '" } }, "query":"mutation($input: WafListInput!) { auth { sites { waf { addToList(input: $input){ status invalidIPs} } } } }"} ';
897 $response = self::sendRequest($payload, TRUE);
898
899 if (isset($response['data']['auth']['sites']['waf']['addToList'])) {
900 return $response['data']['auth']['sites']['waf']['addToList'];
901 }
902 }
903
904 return FALSE;
905 }
906
907 /**
908 * Method to remove ip from allow/deny list by id.
909 *
910 * @param string $id
911 * Id assignment to ip address.
912 *
913 * @return bool
914 * Returns information whether the request was successful.
915 */
916 public static function removeIpFromList($id)
917 {
918 $payload = '{"variables":{ "id": "' . $id . '" },"query":"mutation($id: ID!) { auth { sites { waf { removeFromList(id: $id) } } } }"} ';
919 $response = self::sendRequest($payload, TRUE);
920
921 if (isset($response['data']['auth']['sites']['waf']['removeFromList'])) {
922 return $response['data']['auth']['sites']['waf']['removeFromList'];
923 }
924
925 return FALSE;
926 }
927
928 /**
929 * Method to get allow url list.
930 *
931 * @param string $host_id
932 * Host id on WebTotem.
933 *
934 * @return array
935 * Returns url allow lists.
936 */
937 public static function getAllowUrlList($host_id)
938 {
939 $payload = '{"query":"query { auth { viewer { sites { one(id: \"' . $host_id . '\"){ firewall{ urlWhiteList{ id url createdAt } } } } } } }"} ';
940 $response = self::sendRequest($payload, TRUE);
941
942 if (isset($response['data']['auth']['viewer']['sites']['one']['firewall']['urlWhiteList'])) {
943 return $response['data']['auth']['viewer']['sites']['one']['firewall']['urlWhiteList'];
944 }
945
946 return [];
947 }
948
949 /**
950 * Method to add url to allow list.
951 *
952 * @param string $host_id
953 * Host id on WebTotem.
954 * @param string $url
955 * User-specified url.
956 *
957 * @return bool|string
958 * Returns information whether the request was successful.
959 */
960 public static function addUrlToAllowList($host_id, $url)
961 {
962 $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "url": "' . $url . '" } }, "query":"mutation($input: WafUrlWhiteListInput!) { auth { sites { waf { addToUrlWhiteList(input: $input) } } } }"} ';
963 $response = self::sendRequest($payload, TRUE);
964
965 if (isset($response['data']['auth']['sites']['waf']['addToUrlWhiteList'])) {
966 return $response['data']['auth']['sites']['waf']['addToUrlWhiteList'];
967 }
968
969 return FALSE;
970 }
971
972 /**
973 * Method to remove url from allow list.
974 *
975 * @param string $id
976 * Id assignment to url address.
977 *
978 * @return bool|string
979 * Returns information whether the request was successful.
980 */
981 public static function removeUrlFromAllowList($id)
982 {
983 $payload = '{"variables":{ "id": "' . $id . '" }, "query":"mutation($id: ID!) { auth { sites { waf { removeFromUrlWhiteList(id: $id) } } } }"} ';
984 $response = self::sendRequest($payload, TRUE);
985
986 if (isset($response['data']['auth']['sites']['waf']['removeFromUrlWhiteList'])) {
987 return $response['data']['auth']['sites']['waf']['removeFromUrlWhiteList'];
988 }
989
990 return FALSE;
991 }
992
993 /**
994 * Method to get blocked countries list.
995 *
996 * @param string $host_id
997 * Host id on WebTotem.
998 *
999 * @return array
1000 * Returns blocked countries list.
1001 */
1002 public static function getBlockedCountries($host_id)
1003 {
1004 $period = WebTotem::getPeriod(7);
1005 $payload = '{"variables":{"dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '}} , "query":"query($dateRange: DateRangeInput!){ auth { viewer { sites { one(id: \"' . $host_id . '\"){ firewall{ blockedCountries map(dateRange: $dateRange) { attacks, country, location { country { nameEn } } } } } } } } }"}';
1006 $response = self::sendRequest($payload, TRUE);
1007
1008 if (isset($response['data']['auth']['viewer']['sites']['one']['firewall'])) {
1009 return $response['data']['auth']['viewer']['sites']['one']['firewall'];
1010 }
1011
1012 return [];
1013 }
1014
1015 /**
1016 * Method for synchronizing data on the list of blocked countries.
1017 *
1018 * @param string $host_id
1019 * Host id on WebTotem.
1020 * @param array $countries
1021 * Array of countries to block.
1022 *
1023 * @return bool|string
1024 * Returns information whether the request was successful.
1025 */
1026 public static function syncBlockedCountries($host_id, $countries)
1027 {
1028
1029 $countries = $countries ? WebTotem::convertArrayToString($countries) : '';
1030 $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "countries": [' . $countries . '] } }, "query":"mutation($input: WafBlockedCountriesInput!) { auth { sites { waf { syncBlockedCountries(input: $input) } } } }"} ';
1031 $response = self::sendRequest($payload, TRUE);
1032
1033 if (isset($response['data']['auth']['sites']['waf']['syncBlockedCountries'])) {
1034 return $response['data']['auth']['sites']['waf']['syncBlockedCountries'];
1035 }
1036
1037 return FALSE;
1038 }
1039
1040 /**
1041 * Method to get user's email.
1042 *
1043 * @return string
1044 * Returns user's email.
1045 */
1046 public static function getEmail()
1047 {
1048 $payload = '{"query":"query { auth { viewer { email } } }"}';
1049 $response = self::sendRequest($payload, true);
1050
1051 return $response['data']['auth']['viewer']['email'];
1052 }
1053
1054 /**
1055 * Method to get user's email.
1056 *
1057 * @param string $plugin_list
1058 * List of plugins and their versions.
1059 *
1060 * @return array
1061 * Returns cve list.
1062 */
1063 public static function getCVE($plugin_list)
1064 {
1065 $payload = '{"variables":{ "params": [' . $plugin_list . '] }, "query":"query searchByTechnologyAndVersion($params: [SearchByTechnologyAndVersionInput!]) { auth { viewer { cve { searchByTechnologyAndVersion(params: $params) { cves { cve_id id summary published reference } technology version } } } } }"}';
1066 $response = self::sendRequest($payload, true);
1067
1068 if (isset($response['data']['auth']['viewer']['cve']['searchByTechnologyAndVersion'])) {
1069 return $response['data']['auth']['viewer']['cve']['searchByTechnologyAndVersion'];
1070 }
1071
1072 return [];
1073 }
1074
1075 /**
1076 * Method to get user's feedback.
1077 *
1078 * @return array
1079 */
1080 public static function getFeedback()
1081 {
1082 return self::sendFeedbackRequest("GET");
1083 }
1084
1085 /**
1086 * Method to set user's feedback.
1087 *
1088 * @return array
1089 */
1090 public static function setFeedback($data)
1091 {
1092 return self::sendFeedbackRequest("POST", $data);
1093 }
1094
1095 /**
1096 * Function sends data request to endpoint.
1097 *
1098 * @param array $data
1099 * Data array to be sent to endpoint.
1100 *
1101 * @return array
1102 * Returns response from WebTotem endpoint.
1103 */
1104 protected static function sendFeedbackRequest($method, $data = [])
1105 {
1106 $url = 'https://nps.wtotem.com/user-score';
1107 $email = WebTotem::getUserEmail();
1108
1109 if (!$email) {
1110 return [];
1111 }
1112
1113 if ($method == "GET") {
1114
1115 $args = [
1116 'timeout' => '30',
1117 'sslverify' => FALSE,
1118 ];
1119
1120 $response = wp_remote_get($url . '?email=' . urlencode($email), $args);
1121
1122 } else {
1123 $data['email'] = $email;
1124 $data['platform'] = 'WORDPRESS';
1125 $data = json_encode($data);
1126
1127 $args = [
1128 'body' => $data,
1129 'timeout' => '30',
1130 'sslverify' => FALSE,
1131 'headers' => [
1132 'Content-Type' => 'application/json',
1133 ],
1134 ];
1135
1136 $response = wp_remote_post($url, $args);
1137 }
1138
1139
1140 $http_code = wp_remote_retrieve_response_code($response);
1141
1142 if ($http_code < 200) {
1143 WebTotemOption::setNotification('error', __('Could not connect to feedback endpoint.', 'wtotem'));
1144 return [];
1145 }
1146
1147 $response_body = wp_remote_retrieve_body($response);
1148 return json_decode($response_body, true);
1149 }
1150
1151 /**
1152 * Function sends GraphQL request to API server.
1153 *
1154 * @param string $payload
1155 * Payload to be sent to API server.
1156 * @param bool $token
1157 * Whether a token is needed when sending a request.
1158 * @param bool $repeat
1159 * Required to avoid recursion.
1160 *
1161 * @return array
1162 * Returns response from WebTotem API.
1163 */
1164 protected static function sendRequest($payload, $token = FALSE, $repeat = FALSE)
1165 {
1166 $api_key = WebTotemOption::getOption('api_key');
1167
1168 // Remote URL where the public WebTotem API service is running.
1169 $api_url = WebTotemOption::getOption('api_url');
1170 if (!$api_url) {
1171 $api_url = self::getApiUrl('P');
1172 WebTotemOption::setOptions(['api_url' => $api_url]);
1173 }
1174
1175 // Checking whether a token is needed.
1176 if ($token) {
1177 $auth_token = WebTotemOption::getOption('auth_token');
1178 $auth_token_expired = WebTotemOption::getOption('auth_token_expired');
1179
1180 // Checking whether the token has expired.
1181 if ($auth_token_expired <= time() && !$repeat) {
1182 $result = self::auth($api_key);
1183 if ($result === 'success') {
1184 return self::sendRequest($payload, $token, TRUE);
1185 } else {
1186 if (isset($result['errors'])) {
1187 $message = WebTotem::messageForHuman($result['errors'][0]['message']);
1188 WebTotemOption::setNotification('error', $message);
1189 }
1190 }
1191 }
1192 }
1193
1194 if (function_exists('wp_remote_post')) {
1195
1196 $args = [
1197 'body' => $payload,
1198 'timeout' => '60',
1199 'sslverify' => false,
1200 'headers' => [
1201 'Content-Type:application/json',
1202 'Content-Type' => 'application/json',
1203 'Accept: application/json',
1204 'source: WORDPRESS',
1205 ],
1206 ];
1207
1208 if (isset($auth_token)) {
1209 $auth = "Bearer " . $auth_token;
1210 $args['headers'] = array_merge($args['headers'], ["Authorization" => $auth]);
1211 }
1212
1213 $response = wp_remote_post($api_url, $args);
1214 $response = wp_remote_retrieve_body($response);
1215 $response = json_decode($response, true);
1216
1217 } else {
1218 $error = 'WP_REMOTE_POST_NOT_EXIST';
1219 }
1220
1221 // Checking if there are errors in the response.
1222 if (isset($response['errors'][0]['message'])) {
1223 // Show error page if WebTotem cabinet's password is expired.
1224 if (stripos($response['errors'][0]['message'], "Password expired") !== FALSE) {
1225 wtotem_error_page(['errors' => 'PASSWORD_EXPIRED']);
1226 exit();
1227 }
1228
1229 if (stripos($response['errors'][0]['message'], "API_KEY_DEACTIVATED") !== FALSE) {
1230 wtotem_error_page(['errors' => 'TARIFF_EXPIRED']);
1231 exit();
1232 }
1233
1234 $message = WebTotem::messageForHuman($response['errors'][0]['message']);
1235 if (stripos($response['errors'][0]['message'], "INVALID_TOKEN") !== FALSE && !$repeat) {
1236 $response = self::auth($api_key);
1237 if ($response === 'success') {
1238 return self::sendRequest($payload, $token, TRUE);
1239 }
1240 } elseif (stripos($response['errors'][0]['message'], "USERHOST_NOT_BELONG_TO_USER") !== FALSE) {
1241 if (WebTotem::isMultiSite()) {
1242 WebTotemOption::clearAllHosts();
1243 WebTotemOption::clearOptions(['host_id', 'host_name']);
1244 } else {
1245 WebTotemOption::clearOptions(['host_id', 'host_name']);
1246 }
1247 } else {
1248 WebTotemOption::setNotification('error', $message);
1249 }
1250 }
1251
1252 if (!empty($error)) {
1253 WebTotemOption::setNotification('error', $error);
1254 }
1255
1256 return $response;
1257 }
1258
1259 }
1260