mpyw /
coutte
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | namespace mpyw\Coutte; |
||
| 4 | |||
| 5 | use mpyw\Co\CoInterface; |
||
| 6 | use mpyw\Co\CURLException; |
||
| 7 | use mpyw\Coutte\Internal\AsyncClient as AsyncBaseClient; |
||
| 8 | |||
| 9 | use Symfony\Component\BrowserKit\CookieJar; |
||
| 10 | use Symfony\Component\BrowserKit\History; |
||
| 11 | use Symfony\Component\BrowserKit\Request; |
||
| 12 | use Symfony\Component\BrowserKit\Response; |
||
| 13 | |||
| 14 | class Client extends AsyncBaseClient implements BasicInterface, RequesterInterface, AsyncRequesterInterface |
||
| 15 | { |
||
| 16 | protected $options = []; |
||
| 17 | |||
| 18 | /** |
||
| 19 | * Constructor. |
||
| 20 | * |
||
| 21 | * @param array $curlOptions Options for curl_setopt_array(). |
||
| 22 | * @param array $server The server parameters (equivalent of $_SERVER) |
||
| 23 | * @param History $history A History instance to store the browser history |
||
| 24 | * @param CookieJar $cookieJar A CookieJar instance to store the cookies |
||
| 25 | */ |
||
| 26 | 14 | public function __construct( |
|
| 27 | array $curlOptions = [], |
||
| 28 | array $server = [], |
||
| 29 | History $history = null, |
||
| 30 | CookieJar $cookieJar = null |
||
| 31 | 14 | ) { |
|
| 32 | 14 | $this->setCurlOptions($curlOptions); |
|
| 33 | 14 | parent::__construct($server, $history, $cookieJar); |
|
| 34 | 14 | } |
|
| 35 | |||
| 36 | 14 | public function setCurlOptions(array $options) |
|
| 37 | 14 | { |
|
| 38 | 14 | $this->options = $options + $this->options; |
|
| 39 | 14 | } |
|
| 40 | |||
| 41 | 1 | public function getCurlOptions() |
|
| 42 | 1 | { |
|
| 43 | 1 | return $this->options; |
|
| 44 | } |
||
| 45 | |||
| 46 | /** |
||
| 47 | * Makes a request. |
||
| 48 | * |
||
| 49 | * @param object $request An origin request instance |
||
| 50 | * |
||
| 51 | * @return Response An origin response instance |
||
| 52 | */ |
||
| 53 | 11 | protected function doRequest($request) |
|
| 54 | 11 | { |
|
| 55 | 11 | $ch = $this->createCurl($request); |
|
| 56 | 11 | $content = curl_exec($ch); |
|
| 57 | 11 | if ($content === false) { |
|
| 58 | 1 | throw new CURLException(curl_error($ch), curl_errno($ch), $ch); |
|
| 59 | } |
||
| 60 | 10 | return $this->processResult($content, $ch); |
|
| 61 | } |
||
| 62 | |||
| 63 | /** |
||
| 64 | * Makes an asynchronous request. |
||
| 65 | * |
||
| 66 | * @param object $request An origin request instance |
||
| 67 | * |
||
| 68 | * @return \Generator object An origin response instance |
||
| 69 | */ |
||
| 70 | 2 | protected function doRequestAsync($request) |
|
| 71 | 2 | { |
|
| 72 | 2 | $ch = $this->createCurl($request); |
|
| 73 | 2 | $content = (yield $ch); |
|
| 74 | 2 | yield CoInterface::RETURN_WITH => $this->processResult($content, $ch); |
|
| 75 | // @codeCoverageIgnoreStart |
||
| 76 | } |
||
| 77 | // @codeCoverageIgnoreEnd |
||
| 78 | |||
| 79 | 13 | protected function createCurl($request) |
|
| 80 | 13 | { |
|
| 81 | 13 | $method = strtoupper($request->getMethod()); |
|
| 82 | 13 | $ch = curl_init(); |
|
| 83 | 13 | curl_setopt_array($ch, [ |
|
| 84 | 13 | CURLOPT_URL => $request->getUri(), |
|
| 85 | 13 | CURLOPT_RETURNTRANSFER => true, |
|
| 86 | 13 | CURLOPT_HEADER => true, |
|
| 87 | 13 | CURLOPT_HTTPHEADER => $this->createHeaders($request), |
|
| 88 | 13 | CURLOPT_COOKIE => $this->createCookieHeader($request), |
|
| 89 | 13 | CURLOPT_CUSTOMREQUEST => $method, |
|
| 90 | 13 | CURLOPT_NOBODY => $method === 'HEAD', |
|
| 91 | 13 | ] + $this->options + [ |
|
| 92 | 13 | CURLOPT_ENCODING => 'gzip', |
|
| 93 | ]); |
||
| 94 | 13 | if ($method === 'HEAD' || $method === 'GET') { |
|
| 95 | 8 | return $ch; |
|
| 96 | } |
||
| 97 | 7 | if (null !== $content = $request->getContent()) { |
|
| 98 | 1 | curl_setopt($ch, CURLOPT_POSTFIELDS, $content); |
|
| 99 | 1 | return $ch; |
|
| 100 | } |
||
| 101 | 6 | $params = $request->getParameters(); |
|
| 102 | 6 | $files = $request->getFiles(); |
|
| 103 | 6 | if (!$files && !self::containsCURLFile($params)) { |
|
| 104 | 1 | $content = http_build_query($params, '', '&', PHP_QUERY_RFC3986); |
|
| 105 | 1 | curl_setopt($ch, CURLOPT_POSTFIELDS, $content); |
|
| 106 | 1 | return $ch; |
|
| 107 | } |
||
| 108 | 5 | $multipart = []; |
|
| 109 | 5 | $this->addMultipartFields($params, $multipart); |
|
| 110 | 5 | $this->addMultipartFiles($files, $multipart); |
|
| 111 | 5 | curl_setopt($ch, CURLOPT_POSTFIELDS, $multipart); |
|
| 112 | 5 | return $ch; |
|
| 113 | } |
||
| 114 | |||
| 115 | /** |
||
| 116 | * @param resource $ch |
||
| 117 | */ |
||
| 118 | 12 | protected function processResult($content, $ch) |
|
| 119 | 12 | { |
|
| 120 | 12 | list($head, $data) = explode("\r\n\r\n", $content, 2) + [1 => '']; |
|
| 121 | 12 | preg_match_all('/^([^:]+?):(.+)$/m', $head, $matches, PREG_SET_ORDER); |
|
| 122 | 12 | $headers = []; |
|
| 123 | 12 | foreach ($matches as $match) { |
|
|
0 ignored issues
–
show
|
|||
| 124 | 12 | $headers[trim($match[1])][] = trim($match[2]); |
|
| 125 | } |
||
| 126 | 12 | return new Response($data, curl_getinfo($ch, CURLINFO_HTTP_CODE), $headers); |
|
| 127 | } |
||
| 128 | |||
| 129 | 13 | protected function createHeaders($request) |
|
| 130 | 13 | { |
|
| 131 | 13 | static $contentHeaders = [ |
|
| 132 | 'content-length' => true, |
||
| 133 | 'content-md5' => true, |
||
| 134 | 'content-type' => true, |
||
| 135 | ]; |
||
| 136 | 13 | $headers = []; |
|
| 137 | 13 | foreach ($request->getServer() as $key => $val) { |
|
| 138 | 13 | $key = strtolower(strtr($key, '_', '-')); |
|
| 139 | 13 | if (!strncmp($key, 'http-', 5)) { |
|
| 140 | 13 | $headers[] = substr($key, 5) . ': ' . $val; |
|
| 141 | 13 | continue; |
|
| 142 | } |
||
| 143 | 13 | if (isset($contentHeaders[$key])) { |
|
| 144 | $headers[] = "$key: $val"; |
||
| 145 | 13 | continue; |
|
| 146 | } |
||
| 147 | } |
||
| 148 | 13 | if ($request->getContent() !== null && !preg_grep('/^content-type:/', $headers)) { |
|
| 149 | 1 | $headers[] = 'content-type: application/octet-stream'; |
|
| 150 | } |
||
| 151 | 13 | return $headers; |
|
| 152 | } |
||
| 153 | |||
| 154 | 13 | protected function createCookieHeader($request) |
|
| 155 | 13 | { |
|
| 156 | 13 | $cookies = $this->getCookieJar()->allValues($request->getUri()); |
|
| 157 | 13 | $pairs = []; |
|
| 158 | 13 | foreach ($cookies as $name => $value) { |
|
| 159 | $pairs[] = "$name=$value"; |
||
| 160 | } |
||
| 161 | 13 | return implode('; ', $pairs); |
|
| 162 | } |
||
| 163 | |||
| 164 | 5 | protected function addMultipartFiles(array $files, array &$multipart, $arrayName = '') |
|
| 165 | 5 | { |
|
| 166 | 5 | foreach ($files as $name => $info) { |
|
| 167 | 4 | if ($arrayName !== '') { |
|
| 168 | 4 | $name = "{$arrayName}[{$name}]"; |
|
| 169 | } |
||
| 170 | 4 | if ($info instanceof \CURLFile) { |
|
| 171 | 1 | $multipart[$name] = $info; |
|
| 172 | 1 | continue; |
|
| 173 | } |
||
| 174 | 4 | if (!is_array($info)) { |
|
| 175 | 1 | $multipart[$name] = new \CURLFile($info); |
|
| 176 | 1 | continue; |
|
| 177 | } |
||
| 178 | 4 | if (!isset($info['tmp_name']) || is_array($info['tmp_name'])) { |
|
| 179 | 4 | $this->addMultipartFiles($info, $multipart, $name); |
|
| 180 | 4 | continue; |
|
| 181 | } |
||
| 182 | 2 | if ('' === $info['tmp_name']) { |
|
| 183 | // @codeCoverageIgnoreStart |
||
| 184 | continue; |
||
| 185 | // @codeCoverageIgnoreEnd |
||
| 186 | } |
||
| 187 | 2 | $multipart[$name] = new \CURLFile( |
|
| 188 | 2 | $info['tmp_name'], |
|
| 189 | 2 | isset($info['type']) ? $info['type'] : '', |
|
| 190 | 2 | isset($info['name']) ? $info['name'] : '' |
|
| 191 | ); |
||
| 192 | } |
||
| 193 | 5 | } |
|
| 194 | |||
| 195 | 5 | protected function addMultipartFields(array $params, array &$multipart, $arrayName = '') |
|
| 196 | 5 | { |
|
| 197 | 5 | foreach ($params as $name => $value) { |
|
| 198 | 1 | if ($arrayName !== '') { |
|
| 199 | 1 | $name = "{$arrayName}[{$name}]"; |
|
| 200 | } |
||
| 201 | 1 | if (!is_array($value)) { |
|
| 202 | 1 | $multipart[$name] = $value; |
|
| 203 | 1 | continue; |
|
| 204 | } |
||
| 205 | 1 | $this->addMultipartFields($value, $multipart, $name); |
|
| 206 | } |
||
| 207 | 5 | } |
|
| 208 | |||
| 209 | 2 | protected static function containsCURLFile(array $array) |
|
| 210 | 2 | { |
|
| 211 | 2 | foreach ($array as $item) { |
|
| 212 | 2 | if (is_array($item)) { |
|
| 213 | 1 | if (self::containsCURLFile($item)) { |
|
| 214 | 1 | return true; |
|
| 215 | } |
||
| 216 | 1 | continue; |
|
| 217 | } |
||
| 218 | 2 | if ($item instanceof \CURLFile) { |
|
| 219 | 2 | return true; |
|
| 220 | } |
||
| 221 | } |
||
| 222 | 2 | return false; |
|
| 223 | } |
||
| 224 | } |
||
| 225 |
There are different options of fixing this problem.
If you want to be on the safe side, you can add an additional type-check:
If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:
Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.