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.

SslCertificate::isTrusted()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace LiquidWeb\SslCertificate;
4
5
use Carbon\Carbon;
6
use phpseclib\Math\BigInteger;
7
8
class SslCertificate
9
{
10
    /** @var bool */
11
    protected $trusted;
12
13
    /** @var bool */
14
    protected $revoked;
15
16
    /** @var string */
17
    protected $ip;
18
19
    /** @var BigInteger */
20
    protected $serial;
21
22
    /** @var string */
23
    protected $inputDomain;
24
25
    /** @var string */
26
    protected $testedDomain;
27
28
    /** @var array */
29
    protected $certificateFields = [];
30
31
    /** @var array */
32
    protected $certificateChains = [];
33
34
    /** @var array */
35
    protected $connectionMeta = [];
36
37
    /** @var array */
38
    protected $crlLinks = [];
39
40
    /** @var SslRevocationList */
41
    protected $crl = null;
42
43
    /** @var Carbon */
44
    protected $revokedTime = null;
45
46 3
    public static function createForHostName(string $url, int $timeout = 30): self
47
    {
48 3
        $downloadResults = Downloader::downloadCertificateFromUrl($url, $timeout);
49
50 3
        return new static($downloadResults);
51
    }
52
53 4
    private static function extractCrlLinks($rawCrlPoints): string
54
    {
55 4
        $tempCrlItem = explode('URI:', $rawCrlPoints);
56 4
        $cleanCrlItem = trim($tempCrlItem[1]);
57
58 4
        return $cleanCrlItem;
59
    }
60
61 4
    private static function parseCrlLinks($rawCrlInput): array
62
    {
63 4
        $crlLinks = [];
64 4
        $crlRawItems = explode('Full Name:', $rawCrlInput);
65
        // Remove the stuff before the first 'Full Name:' item
66 4
        array_splice($crlRawItems, 0, 1);
67 4
        foreach ($crlRawItems as $item) {
68 4
            $crlLink = self::extractCrlLinks($item);
69 4
            array_push($crlLinks, $crlLink);
70 4
            unset($crlLink);
71
        }
72
73 4
        return $crlLinks;
74
    }
75
76 25
    private static function parseCertChains(array $chains): array
77
    {
78 25
        $output = [];
79 25
        foreach ($chains as $cert) {
80 24
            array_push($output, new SslChain($cert));
81
        }
82
83 25
        return $output;
84
    }
85
86 2
    public function withSslCrlCheck(): self
87
    {
88 2
        $links = $this->getCrlLinks();
89 2
        if (is_null($links) === true || empty($links) === true) {
90
            return $this;
91
        }
92 2
        $this->crl = SslRevocationList::createFromUrl($links[0]);
93
94 2
        foreach ($this->crl->getRevokedList() as $revoked) {
95 2
            if ($this->serial->equals($revoked['userCertificate'])) {
96 1
                $this->trusted = false;
97 1
                $this->revoked = true;
98 1
                $this->revokedTime = new Carbon($revoked['revocationDate']['utcTime']);
99
100 2
                return $this;
101
            }
102
        }
103 1
        $this->revoked = false;
104
105 1
        return $this;
106
    }
107
108 25
    public function __construct(array $downloadResults)
109
    {
110 25
        $this->inputDomain = $downloadResults['inputDomain'];
111 25
        $this->testedDomain = $downloadResults['tested'];
112 25
        $this->trusted = $downloadResults['trusted'];
113 25
        $this->ip = $downloadResults['dns-resolves-to'];
114 25
        $this->certificateFields = $downloadResults['cert'];
115 25
        $this->certificateChains = self::parseCertChains($downloadResults['full_chain']);
116 25
        $this->connectionMeta = $downloadResults['connection'];
117 25
        $this->serial = new BigInteger($downloadResults['cert']['serialNumber']);
118
119 25
        if (isset($downloadResults['cert']['extensions']['crlDistributionPoints'])) {
120 4
            $this->crlLinks = self::parseCrlLinks($downloadResults['cert']['extensions']['crlDistributionPoints']);
121
        }
122 25
    }
123
124 2
    public function hasSslChain(): bool
125
    {
126 2
        if (isset($this->certificateChains) && count($this->certificateChains) >= 1) {
127 1
            return true;
128
        }
129
130 1
        return false;
131
    }
132
133 1
    public function getCertificateFields(): array
134
    {
135 1
        return $this->certificateFields;
136
    }
137
138 1
    public function getCertificateChains(): array
139
    {
140 1
        return $this->certificateChains;
141
    }
142
143 1
    public function getSerialNumber(): string
144
    {
145 1
        return strtoupper($this->serial->toHex());
146
    }
147
148 2
    public function hasCrlLink(): bool
149
    {
150 2
        return isset($this->certificateFields['extensions']['crlDistributionPoints']);
151
    }
152
153 2
    public function getCrlLinks()
154
    {
155 2
        if (! $this->hasCrlLink()) {
156
            return;
157
        }
158
159 2
        return $this->crlLinks;
160
    }
161
162
    public function getCrl()
163
    {
164
        if (! $this->hasCrlLink()) {
165
            return;
166
        }
167
168
        return $this->crl;
169
    }
170
171 7
    public function isRevoked()
172
    {
173 7
        return $this->revoked;
174
    }
175
176 1
    public function getCrlRevokedTime()
177
    {
178 1
        if ($this->isRevoked()) {
179 1
            return $this->revokedTime;
180
        }
181
    }
182
183 1
    public function getResolvedIp(): string
184
    {
185 1
        return $this->ip;
186
    }
187
188 2
    public function getIssuer(): string
189
    {
190 2
        return $this->certificateFields['issuer']['CN'];
191
    }
192
193 4
    public function getDomain(): string
194
    {
195 4
        $certDomain = $this->getCertificateDomain();
196 4
        if (str_contains($certDomain, $this->inputDomain) === false) {
197
            return $this->inputDomain;
198
        }
199
200 4
        return $certDomain ?? '';
201
    }
202
203 1
    public function getTestedDomain(): string
204
    {
205 1
        return $this->testedDomain;
206
    }
207
208
    public function getInputDomain(): string
209
    {
210
        return $this->inputDomain;
211
    }
212
213 8
    public function getCertificateDomain(): string
214
    {
215 8
        return $this->certificateFields['subject']['CN'];
216
    }
217
218 8
    public function getAdditionalDomains(): array
219
    {
220 8
        $additionalDomains = explode(', ', $this->certificateFields['extensions']['subjectAltName'] ?? '');
221
222 8
        return array_map(function (string $domain) {
223 8
            return str_replace('DNS:', '', $domain);
224 8
        }, $additionalDomains);
225
    }
226
227 1
    public function getSignatureAlgorithm(): string
228
    {
229 1
        return $this->certificateFields['signatureTypeSN'] ?? '';
230
    }
231
232
    public function getConnectionMeta(): array
233
    {
234
        return $this->connectionMeta;
235
    }
236
237 8
    public function validFromDate(): Carbon
238
    {
239 8
        return Carbon::createFromTimestampUTC($this->certificateFields['validFrom_time_t']);
240
    }
241
242 9
    public function expirationDate(): Carbon
243
    {
244 9
        return Carbon::createFromTimestampUTC($this->certificateFields['validTo_time_t']);
245
    }
246
247 1
    public function isExpired(): bool
248
    {
249 1
        return $this->expirationDate()->isPast();
250
    }
251
252 1
    public function isTrusted(): bool
253
    {
254 1
        return $this->trusted;
255
    }
256
257 6
    public function isValid(string $url = null): bool
258
    {
259
        // Verify SSL not expired
260 6
        if (! Carbon::now()->between($this->validFromDate(), $this->expirationDate())) {
261 2
            return false;
262
        }
263
        // Verify the SSL applies to the domain; use $url if provided, other wise use input
264 5
        if ($this->appliesToUrl($url ?? $this->inputDomain) === false) {
265 1
            return false;
266
        }
267
        // Check SerialNumber for CRL list
268 5
        if ($this->isRevoked()) {
269
            return false;
270
        }
271
272 5
        return true;
273
    }
274
275 1
    public function isValidUntil(Carbon $carbon, string $url = null): bool
276
    {
277 1
        if ($this->isValidDate($carbon) === false) {
278 1
            return false;
279
        }
280
281 1
        return $this->isValid($url);
282
    }
283
284 2 View Code Duplication
    public function isValidDate(Carbon $carbon): bool
285
    {
286 2
        if ($carbon->between($this->validFromDate(), $this->expirationDate()) === false) {
287 2
            return false;
288
        }
289
290 2
        return true;
291
    }
292
293 1
    public function isSelfSigned(): bool
294
    {
295
        // Get the issuer data
296 1
        $url = $this->getIssuer();
297
        // make sure we don't include wildcard if it's there...
298 1
        if (starts_with($url, '*.') === true) {
299
            $url = substr($url, 2);
300
        }
301
        //Try to parse the string
302
        try {
303 1
            $issuerUrl = new Url($url);
304
        } catch (\Exception $e) {
305
            // if we hit this exception then the string is not likely a URL
306
            // If it's not a URL and is valid we can assume it's not self signed
307
            return false;
308
        }
309
        // If it is a domain, run appliesToUrl
310 1
        if ($this->appliesToUrl((string) $issuerUrl) === true) {
311
            return true;
312
        }
313
314 1
        return false;
315
    }
316
317 7
    public function appliesToUrl(string $url): bool
318
    {
319 7
        if (starts_with($url, '*.') === true) {
320 1
            $url = substr($url, 2);
321
        }
322 7
        $host = (new Url($url))->getHostName() ?: $url;
323
324 7
        $certificateHosts = array_merge([$this->getCertificateDomain()], $this->getAdditionalDomains());
325
326 7
        foreach ($certificateHosts as $certificateHost) {
327 7
            if ($host === $certificateHost) {
328 5
                return true;
329
            }
330
331 4
            if ($this->wildcardHostCoversHost($certificateHost, $host)) {
332 4
                return true;
333
            }
334
        }
335
336 2
        return false;
337
    }
338
339 4
    protected function wildcardHostCoversHost(string $wildcardHost, string $host): bool
340
    {
341 4
        if ($host === $wildcardHost) {
342
            return true;
343
        }
344
345 4
        if (! starts_with($wildcardHost, '*')) {
346 3
            return false;
347
        }
348
349 2
        $wildcardHostWithoutWildcard = substr($wildcardHost, 2);
350
351 2
        return substr_count($wildcardHost, '.') >= substr_count($host, '.') && ends_with($host, $wildcardHostWithoutWildcard);
352
    }
353
}
354