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 ( 83077e...058dc7 )
by Freek
01:30
created

Downloader   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 178
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
dl 0
loc 178
rs 10
c 0
b 0
f 0
wmc 15
lcom 1
cbo 3

11 Methods

Rating   Name   Duplication   Size   Complexity  
A usingPort() 0 6 1
A usingSni() 0 6 1
A withFullChain() 0 6 1
A withVerifyPeer() 0 6 1
A withVerifyPeerName() 0 6 1
A setTimeout() 0 6 1
A getCertificates() 0 18 1
A forHost() 0 8 1
A downloadCertificateFromUrl() 0 6 1
B fetchCertificates() 0 37 3
A handleRequestFailure() 0 12 3
1
<?php
2
3
namespace Spatie\SslCertificate;
4
5
use Throwable;
6
use Spatie\SslCertificate\Exceptions\CouldNotDownloadCertificate;
7
8
class Downloader
9
{
10
    /** @var int */
11
    protected $port = 443;
12
13
    /** @var int */
14
    protected $timeout = 30;
15
16
    /** @var bool */
17
    protected $enableSni = true;
18
19
    /** @var bool */
20
    protected $capturePeerChain = false;
21
22
    /** @var bool */
23
    protected $verifyPeer = true;
24
25
    /** @var bool */
26
    protected $verifyPeerName = true;
27
28
    /**
29
     * @param int $port
30
     *
31
     * @return $this
32
     */
33
    public function usingPort(int $port)
34
    {
35
        $this->port = $port;
36
37
        return $this;
38
    }
39
40
    /**
41
     * @param int $sni
42
     *
43
     * @return $this
44
     */
45
    public function usingSni(bool $sni)
46
    {
47
        $this->enableSni = $sni;
0 ignored issues
show
Documentation Bug introduced by
It seems like $sni can also be of type integer. However, the property $enableSni is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
48
49
        return $this;
50
    }
51
52
    /**
53
     * @param int $ca_chain
54
     *
55
     * @return $this
56
     */
57
    public function withFullChain(bool $ca_chain)
58
    {
59
        $this->capturePeerChain = $ca_chain;
0 ignored issues
show
Documentation Bug introduced by
It seems like $ca_chain can also be of type integer. However, the property $capturePeerChain is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
60
61
        return $this;
62
    }
63
64
    /**
65
     * @param int $verify_peer
66
     *
67
     * @return $this
68
     */
69
    public function withVerifyPeer(bool $verify_peer)
70
    {
71
        $this->verifyPeer = $verify_peer;
0 ignored issues
show
Documentation Bug introduced by
It seems like $verify_peer can also be of type integer. However, the property $verifyPeer is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
72
73
        return $this;
74
    }
75
76
    /**
77
     * @param int $verify_peer_name
78
     *
79
     * @return $this
80
     */
81
    public function withVerifyPeerName(bool $verify_peer_name)
82
    {
83
        $this->verifyPeerName = $verify_peer_name;
0 ignored issues
show
Documentation Bug introduced by
It seems like $verify_peer_name can also be of type integer. However, the property $verifyPeerName is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
84
85
        return $this;
86
    }
87
88
    /**
89
     * @param int $timeOutInSeconds
90
     *
91
     * @return $this
92
     */
93
    public function setTimeout(int $timeOutInSeconds)
94
    {
95
        $this->timeout = $timeOutInSeconds;
96
97
        return $this;
98
    }
99
100
    public function getCertificates(string $hostName): array
101
    {
102
        $response = $this->fetchCertificates($hostName);
103
104
        $peerCertificate = $response['options']['ssl']['peer_certificate'];
105
106
        $peerCertificateChain = $response['options']['ssl']['peer_certificate_chain'] ?? [];
107
108
        $fullCertificateChain = array_merge([$peerCertificate], $peerCertificateChain);
109
110
        // Filter duplicates: wildcard SSL certs are reported in both
111
        // 'peer_certificate' as well as 'peer_certificate_chain'
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
112
        return array_unique(array_map(function ($certificate) {
113
            $certificateFields = openssl_x509_parse($certificate);
114
115
            return new SslCertificate($certificateFields);
116
        }, $fullCertificateChain));
117
    }
118
119
    public function forHost(string $hostName): SslCertificate
120
    {
121
        $hostName = (new Url($hostName))->getHostName();
122
123
        $certificates = $this->getCertificates($hostName);
124
125
        return $certificates[0] ?? false;
126
    }
127
128
    public static function downloadCertificateFromUrl(string $url, int $timeout = 30): SslCertificate
129
    {
130
        return (new static())
131
            ->setTimeout($timeout)
132
            ->forHost($url);
133
    }
134
135
    protected function fetchCertificates(string $hostName): array
136
    {
137
        $hostName = (new Url($hostName))->getHostName();
138
139
        $sslOptions = [
140
            'capture_peer_cert' => true,
141
            'capture_peer_cert_chain' => $this->capturePeerChain,
142
            'SNI_enabled' => $this->enableSni,
143
            'verify_peer' => $this->verifyPeer,
144
            'verify_peer_name' => $this->verifyPeerName,
145
        ];
146
147
        $streamContext = stream_context_create([
148
            'ssl' => $sslOptions,
149
        ]);
150
151
        try {
152
            $client = stream_socket_client(
153
                "ssl://{$hostName}:{$this->port}",
154
                $errorNumber,
155
                $errorDescription,
156
                $this->timeout,
157
                STREAM_CLIENT_CONNECT,
158
                $streamContext
159
            );
160
        } catch (Throwable $thrown) {
161
            $this->handleRequestFailure($hostName, $thrown);
162
        }
163
164
        if (! $client) {
165
            throw CouldNotDownloadCertificate::unknownError($hostName, "Could not connect to `{$hostName}`.");
166
        }
167
168
        $response = stream_context_get_params($client);
169
170
        return $response;
171
    }
172
173
    protected function handleRequestFailure(string $hostName, Throwable $thrown)
174
    {
175
        if (str_contains($thrown->getMessage(), 'getaddrinfo failed')) {
176
            throw CouldNotDownloadCertificate::hostDoesNotExist($hostName);
177
        }
178
179
        if (str_contains($thrown->getMessage(), 'error:14090086')) {
180
            throw CouldNotDownloadCertificate::noCertificateInstalled($hostName);
181
        }
182
183
        throw CouldNotDownloadCertificate::unknownError($hostName, $thrown->getMessage());
184
    }
185
}
186