GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 9ed679...c45054 )
by Freek
01:20
created

Downloader::fetchCertificates()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 12
nop 1
dl 0
loc 50
rs 8.7797
c 0
b 0
f 0
1
<?php
2
3
namespace Spatie\SslCertificate;
4
5
use Spatie\SslCertificate\Exceptions\CouldNotDownloadCertificate;
6
use Spatie\SslCertificate\Exceptions\InvalidIpAddress;
7
use Throwable;
8
9
class Downloader
10
{
11
    /** @var int */
12
    protected $port = 443;
13
14
    /** @var string */
15
    protected $ipAddress = null;
16
17
    /** @var bool */
18
    protected $usingIpAddress = false;
19
20
    /** @var int */
21
    protected $timeout = 30;
22
23
    /** @var bool */
24
    protected $enableSni = true;
25
26
    /** @var bool */
27
    protected $capturePeerChain = false;
28
29
    /** @var bool */
30
    protected $verifyPeer = true;
31
32
    /** @var bool */
33
    protected $verifyPeerName = true;
34
35
    public function usingPort(int $port)
36
    {
37
        $this->port = $port;
38
39
        return $this;
40
    }
41
42
    public function usingSni(bool $sni)
43
    {
44
        $this->enableSni = $sni;
45
46
        return $this;
47
    }
48
49
    public function withFullChain(bool $fullChain)
50
    {
51
        $this->capturePeerChain = $fullChain;
52
53
        return $this;
54
    }
55
56
    public function withVerifyPeer(bool $verifyPeer)
57
    {
58
        $this->verifyPeer = $verifyPeer;
59
60
        return $this;
61
    }
62
63
    public function withVerifyPeerName(bool $verifyPeerName)
64
    {
65
        $this->verifyPeerName = $verifyPeerName;
66
67
        return $this;
68
    }
69
70
    public function setTimeout(int $timeOutInSeconds)
71
    {
72
        $this->timeout = $timeOutInSeconds;
73
74
        return $this;
75
    }
76
77
    public function fromIpAddress(string $ipAddress)
78
    {
79
        if (! filter_var($ipAddress, FILTER_VALIDATE_IP)) {
80
            throw InvalidIpAddress::couldNotValidate($ipAddress);
81
        }
82
83
        $this->ipAddress = $ipAddress;
84
        $this->usingIpAddress = true;
85
86
        return $this;
87
    }
88
89
    public function getCertificates(string $hostName): array
90
    {
91
        $response = $this->fetchCertificates($hostName);
92
        $remoteAddress = $response['remoteAddress'];
93
94
        $peerCertificate = $response['options']['ssl']['peer_certificate'];
95
96
        $peerCertificateChain = $response['options']['ssl']['peer_certificate_chain'] ?? [];
97
98
        $fullCertificateChain = array_merge([$peerCertificate], $peerCertificateChain);
99
100
        $certificates = array_map(function ($certificate) use ($remoteAddress) {
101
            $certificateFields = openssl_x509_parse($certificate);
102
103
            $fingerprint = openssl_x509_fingerprint($certificate);
104
            $fingerprintSha256 = openssl_x509_fingerprint($certificate, 'sha256');
105
106
            return new SslCertificate(
107
                $certificateFields,
108
                $fingerprint,
109
                $fingerprintSha256,
110
                $remoteAddress
111
            );
112
        }, $fullCertificateChain);
113
114
        return array_unique($certificates);
115
    }
116
117
    public function forHost(string $hostName): SslCertificate
118
    {
119
        $hostName = (new Url($hostName))->getHostName();
120
121
        $certificates = $this->getCertificates($hostName);
122
123
        return $certificates[0] ?? false;
124
    }
125
126
    public static function downloadCertificateFromUrl(string $url, int $timeout = 30): SslCertificate
127
    {
128
        return (new static())
129
            ->setTimeout($timeout)
130
            ->forHost($url);
131
    }
132
133
    protected function fetchCertificates(string $hostName): array
134
    {
135
        $hostName = (new Url($hostName))->getHostName();
136
137
        $sslOptions = [
138
            'capture_peer_cert' => true,
139
            'capture_peer_cert_chain' => $this->capturePeerChain,
140
            'SNI_enabled' => $this->enableSni,
141
            'peer_name' => $hostName,
142
            'verify_peer' => $this->verifyPeer,
143
            'verify_peer_name' => $this->verifyPeerName,
144
        ];
145
146
        $streamContext = stream_context_create([
147
            'ssl' => $sslOptions,
148
        ]);
149
150
        $connectTo = ($this->usingIpAddress)
151
            ? $this->ipAddress
152
            : $hostName;
153
154
        try {
155
            $client = stream_socket_client(
156
                "ssl://{$connectTo}:{$this->port}",
157
                $errorNumber,
158
                $errorDescription,
159
                $this->timeout,
160
                STREAM_CLIENT_CONNECT,
161
                $streamContext
162
            );
163
        } catch (Throwable $thrown) {
164
            $this->handleRequestFailure($connectTo, $thrown);
165
        }
166
167
        if (! $client) {
168
            $clientErrorMessage = ($this->usingIpAddress)
169
                ? "Could not connect to `{$connectTo}` or it does not have a certificate matching `${hostName}`."
170
                : "Could not connect to `{$connectTo}`.";
171
172
            throw CouldNotDownloadCertificate::unknownError($hostName, $clientErrorMessage);
173
        }
174
175
        $response = stream_context_get_params($client);
176
177
        $response['remoteAddress'] = stream_socket_get_name($client, true);
178
179
        fclose($client);
180
181
        return $response;
182
    }
183
184
    protected function handleRequestFailure(string $hostName, Throwable $thrown)
185
    {
186
        if (str_contains($thrown->getMessage(), 'getaddrinfo failed')) {
187
            throw CouldNotDownloadCertificate::hostDoesNotExist($hostName);
188
        }
189
190
        if (str_contains($thrown->getMessage(), 'error:14090086')) {
191
            throw CouldNotDownloadCertificate::noCertificateInstalled($hostName);
192
        }
193
194
        throw CouldNotDownloadCertificate::unknownError($hostName, $thrown->getMessage());
195
    }
196
}
197