Passed
Push — developer ( 6b5868...bed0f9 )
by Radosław
22:42 queued 03:39
created

ApiClient   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 22
eloc 57
c 1
b 0
f 0
dl 0
loc 148
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getRequestOptions() 0 17 3
A isWritable() 0 4 4
A getError() 0 3 1
A basicValidations() 0 9 4
B send() 0 34 8
A getStatusCode() 0 3 1
A getResponseBody() 0 3 1
1
<?php
2
/**
3
 * YetiForce register file.
4
 * Modifying this file or functions that affect the footer appearance will violate the license terms!!!
5
 *
6
 * @package App
7
 *
8
 * @copyright YetiForce S.A.
9
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
10
 * @author    Radosław Skrzypczak <[email protected]>
11
 */
12
13
namespace App\YetiForce;
14
15
/**
16
 * YetiForce register class.
17
 */
18
final class ApiClient
19
{
20
	/** @var string URL */
21
	public const URL = 'https://api.yetiforce.eu/registrations';
22
23
	/** @var string Last error. */
24
	public ?string $error = null;
25
	/** @var bool Response result */
26
	private bool $success;
27
28
	/** @var int|null Resopnse code */
29
	private ?int $responseCode = 0;
30
	/** @var string Resopnse body */
31
	private $responseBody;
32
33
	/** @var int Total timeout of the request in seconds. */
34
	private int $timeout = 20;
35
	/** @var int The number of seconds to wait while trying to connect to a server. */
36
	private int $connectTimeout = 2;
37
38
	/**
39
	 * Send registration data.
40
	 *
41
	 * @param string $url
42
	 * @param string $method
43
	 * @param array  $option
44
	 *
45
	 * @return bool
46
	 */
47
	public function send(string $url, string $method, array $option = []): bool
48
	{
49
		$this->error = null;
50
		$this->success = false;
51
		$this->basicValidations($url);
52
		if ($this->error) {
53
			return $this->success;
54
		}
55
56
		try {
57
			\App\Log::beginProfile($method . '|' . __METHOD__ . "|{$url}", __NAMESPACE__);
58
			$response = (new \GuzzleHttp\Client($this->getRequestOptions()))->request($method, $url, $option);
0 ignored issues
show
Security Code Execution introduced by
$option can contain request data and is used in code execution context(s) leading to a potential security vulnerability.

2 paths for user data to reach this point

  1. Path: Read from $_REQUEST, and Request::__construct() is called in api/webservice/Core/Request.php on line 56
  1. Read from $_REQUEST, and Request::__construct() is called
    in api/webservice/Core/Request.php on line 56
  2. Enters via parameter $rawValues
    in app/Request.php on line 110
  3. $rawValues is assigned to property Request::$rawValues
    in app/Request.php on line 112
  4. Read from property Request::$rawValues, and Data is passed through purifyByType(), and $this->purifiedValuesByType[$key][$type] = App\Purifier::purifyByType($this->rawValues[$key], $type, $convert) is returned
    in app/Request.php on line 170
  5. Order::setPackageId() is called
    in modules/Settings/YetiForce/actions/Buy.php on line 31
  6. Enters via parameter $packageId
    in app/YetiForce/Order.php on line 159
  7. $packageId is assigned to property Order::$packageId
    in app/YetiForce/Order.php on line 161
  8. Read from property Order::$packageId
    in app/YetiForce/Order.php on line 187
  9. array('packageId' => $this->packageId, 'company' => $this->data['name'], 'city' => $this->data['city'], 'vatId' => $this->data['vat_id'], 'country' => $this->data['country'], 'postCode' => $this->data['post_code'], 'address' => $this->data['address']) is returned
    in app/YetiForce/Order.php on line 186
  10. ApiClient::send() is called
    in app/YetiForce/Order.php on line 119
  11. Enters via parameter $option
    in app/YetiForce/ApiClient.php on line 47
  2. Path: Read from $_REQUEST, and Request::__construct() is called in app/Request.php on line 728
  1. Read from $_REQUEST, and Request::__construct() is called
    in app/Request.php on line 728
  2. Enters via parameter $rawValues
    in app/Request.php on line 110
  3. $rawValues is assigned to property Request::$rawValues
    in app/Request.php on line 112
  4. Read from property Request::$rawValues, and Data is passed through purifyByType(), and $this->purifiedValuesByType[$key][$type] = App\Purifier::purifyByType($this->rawValues[$key], $type, $convert) is returned
    in app/Request.php on line 170
  5. Order::setPackageId() is called
    in modules/Settings/YetiForce/actions/Buy.php on line 31
  6. Enters via parameter $packageId
    in app/YetiForce/Order.php on line 159
  7. $packageId is assigned to property Order::$packageId
    in app/YetiForce/Order.php on line 161
  8. Read from property Order::$packageId
    in app/YetiForce/Order.php on line 187
  9. array('packageId' => $this->packageId, 'company' => $this->data['name'], 'city' => $this->data['city'], 'vatId' => $this->data['vat_id'], 'country' => $this->data['country'], 'postCode' => $this->data['post_code'], 'address' => $this->data['address']) is returned
    in app/YetiForce/Order.php on line 186
  10. ApiClient::send() is called
    in app/YetiForce/Order.php on line 119
  11. Enters via parameter $option
    in app/YetiForce/ApiClient.php on line 47

Used in code-execution context

  1. Client::request() is called
    in app/YetiForce/ApiClient.php on line 58
  2. Enters via parameter $options
    in vendor/guzzlehttp/guzzle/src/Client.php on line 185
  3. Client::requestAsync() is called
    in vendor/guzzlehttp/guzzle/src/Client.php on line 189
  4. Enters via parameter $options
    in vendor/guzzlehttp/guzzle/src/Client.php on line 153
  5. Data is passed through prepareDefaults()
    in vendor/guzzlehttp/guzzle/src/Client.php on line 155
  6. $this->prepareDefaults($options) is assigned to $options
    in vendor/guzzlehttp/guzzle/src/Client.php on line 155
  7. Client::transfer() is called
    in vendor/guzzlehttp/guzzle/src/Client.php on line 169
  8. Enters via parameter $options
    in vendor/guzzlehttp/guzzle/src/Client.php on line 326
  9. $options['handler'] is assigned to $handler
    in vendor/guzzlehttp/guzzle/src/Client.php on line 330
  10. $handler() is called dynamically
    in vendor/guzzlehttp/guzzle/src/Client.php on line 333

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
59
			\App\Log::endProfile($method . '|' . __METHOD__ . "|{$url}", __NAMESPACE__);
60
61
			$this->responseCode = $response->getStatusCode();
62
			$this->responseBody = $response->getBody()->getContents();
63
			$this->success = true;
64
		} catch (\GuzzleHttp\Exception\ClientException $e) {
65
			$this->responseCode = $e->getResponse()->getStatusCode();
66
			$this->error = $e->getResponse()->getBody()->getContents();
67
			if (\App\Json::isJson($this->error) && ($error = \App\Json::decode($this->error)['errors'] ?? null)) {
68
				$this->error = \is_array($error) ? implode(' | ', $error) : $error;
69
			}
70
			\App\Log::error($e->getMessage(), __METHOD__);
71
		} catch (\GuzzleHttp\Exception\ServerException $e) {
72
			$this->responseCode = $e->getResponse()->getStatusCode();
73
			$this->error = $this->responseCode . ' Internal Server Error';
74
			\App\Log::error($e->getMessage(), __METHOD__);
75
		} catch (\Throwable $e) {
76
			$this->error = \App\Language::translate("LBL_ERROR");
77
			\App\Log::error($e->getMessage(), __METHOD__);
78
		}
79
80
		return $this->success;
81
	}
82
83
	/**
84
	 * Get response status code.
85
	 *
86
	 * @return int
87
	 */
88
	public function getStatusCode()
89
	{
90
		return $this->responseCode;
91
	}
92
93
	/**
94
	 * Get response content.
95
	 *
96
	 * @return mixed
97
	 */
98
	public function getResponseBody()
99
	{
100
		return $this->responseBody;
101
	}
102
103
	/**
104
	 * Get last error.
105
	 *
106
	 * @return string
107
	 */
108
	public function getError(): string
109
	{
110
		return $this->error ?? '';
111
	}
112
113
	/**
114
	 * Get request options.
115
	 *
116
	 * @return array
117
	 */
118
	public function getRequestOptions(): array
119
	{
120
		$headers = [
121
			'x-crm-id' => \App\Config::main('application_unique_key'),
122
			'x-app-id' => Register::getInstanceKey(),
123
			'accept-language' => \App\Language::getLanguage() ?: 'en'
124
		];
125
		if ($key = (new Config())->getToken()) {
126
			$headers['x-api-key'] = $key;
127
		}
128
129
		$options = \App\RequestHttp::getOptions();
130
131
		return array_merge($options, [
132
			'headers' => array_merge($options['headers'] ?? [], $headers),
133
			'timeout' => $this->timeout,
134
			'connect_timeout' => $this->connectTimeout
135
		]);
136
	}
137
138
	/**
139
	 * Basic validations.
140
	 *
141
	 * @param string $url
142
	 *
143
	 * @return void
144
	 */
145
	private function basicValidations($url)
146
	{
147
		$hostName = parse_url($url, PHP_URL_HOST);
148
		if (!\App\RequestUtil::isNetConnection() || $hostName === gethostbyname($hostName)) {
149
			\App\Log::warning('ERR_NO_INTERNET_CONNECTION', __METHOD__);
150
			$this->error = 'ERR_NO_INTERNET_CONNECTION';
151
		} elseif (!$this->isWritable()) {
152
			\App\Log::warning('ERR_REGISTER_FILES_PERMISSIONS||app_data', __METHOD__);
153
			$this->error = 'ERR_REGISTER_FILES_PERMISSIONS||app_data';
154
		}
155
	}
156
157
	/**
158
	 * Check write permissions for the registry file.
159
	 *
160
	 * @return bool
161
	 */
162
	private function isWritable(): bool
163
	{
164
		$path = Register::REGISTRATION_FILE;
165
		return (file_exists($path) && is_writable($path)) || (!file_exists($path) && is_writable(\dirname($path)));
166
	}
167
}
168