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 ( cc194b...accbf4 )
by Daniel
12:20 queued 01:33
created

SslCertificate::getCertificateDomain()   A

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
    public static function createForHostName(string $url, int $timeout = 30): self
47
    {
48
        $downloadResults = Downloader::downloadCertificateFromUrl($url, $timeout);
49
50
        return new static($downloadResults);
51
    }
52
53 1
    private static function extractCrlLinks($rawCrlPoints): string
54
    {
55 1
        $tempCrlItem = explode('URI:', $rawCrlPoints);
56 1
        $cleanCrlItem = trim($tempCrlItem[1]);
57
58 1
        return $cleanCrlItem;
59
    }
60
61 1
    private static function parseCrlLinks($rawCrlInput): array
62
    {
63 1
        $crlLinks = [];
64 1
        $crlRawItems = explode('Full Name:', $rawCrlInput);
65
        // Remove the stuff before the first 'Full Name:' item
66 1
        array_splice($crlRawItems, 0, 1);
67 1
        foreach ($crlRawItems as $item) {
68 1
            $crlLink = self::extractCrlLinks($item);
69 1
            array_push($crlLinks, $crlLink);
70 1
            unset($crlLink);
71
        }
72
73 1
        return $crlLinks;
74
    }
75
76 18
    private static function parseCertChains(array $chains): array
77
    {
78 18
        $output = [];
79 18
        foreach ($chains as $cert) {
80 18
            array_push($output, new SslChain($cert));
81
        }
82
83 18
        return $output;
84
    }
85
86
    public function withSslCrlCheck(): self
87
    {
88
        $links = $this->getCrlLinks();
89
        if (is_null($links) === true || empty($links) === true) {
90
            return $this;
91
        }
92
        $this->crl = SslRevocationList::createFromUrl($links[0]);
93
94
        foreach ($this->crl->getRevokedList() as $revoked) {
95
            if ($this->serial->equals($revoked['userCertificate'])) {
96
                $this->trusted = false;
97
                $this->revoked = true;
98
                $this->revokedTime = new Carbon($revoked['revocationDate']['utcTime']);
99
100
                return $this;
101
            }
102
        }
103
        $this->revoked = false;
104
105
        return $this;
106
    }
107
108 18
    public function __construct(array $downloadResults)
109
    {
110 18
        $this->inputDomain = $downloadResults['inputDomain'];
111 18
        $this->testedDomain = $downloadResults['tested'];
112 18
        $this->trusted = $downloadResults['trusted'];
113 18
        $this->ip = $downloadResults['dns-resolves-to'];
114 18
        $this->certificateFields = $downloadResults['cert'];
115 18
        $this->certificateChains = self::parseCertChains($downloadResults['full_chain']);
116 18
        $this->connectionMeta = $downloadResults['connection'];
117 18
        $this->serial = new BigInteger($downloadResults['cert']['serialNumber']);
118
119 18
        if (isset($downloadResults['cert']['extensions']['crlDistributionPoints'])) {
120 1
            $this->crlLinks = self::parseCrlLinks($downloadResults['cert']['extensions']['crlDistributionPoints']);
121
        }
122 18
    }
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
    public function hasCrlLink(): bool
149
    {
150
        return isset($this->certificateFields['extensions']['crlDistributionPoints']);
151
    }
152
153
    public function getCrlLinks()
154
    {
155
        if (! $this->hasCrlLink()) {
156
            return;
157
        }
158
159
        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 1
    public function isRevoked()
172
    {
173 1
        return $this->revoked;
174
    }
175
176
    public function getCrlRevokedTime()
177
    {
178
        if ($this->isRevoked()) {
179
            return $this->revokedTime;
180
        }
181
    }
182
183 1
    public function getResolvedIp(): string
184
    {
185 1
        return $this->ip;
186
    }
187
188 1
    public function getIssuer(): string
189
    {
190 1
        return $this->certificateFields['issuer']['CN'];
191
    }
192
193 1
    public function getDomain(): string
194
    {
195 1
        $certDomain = $this->getCertificateDomain();
196 1
        if (str_contains($certDomain, $this->inputDomain) === false) {
197
            return $this->inputDomain;
198
        }
199
200 1
        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 1
    public function getCertificateDomain(): string
214
    {
215 1
        return $this->certificateFields['subject']['CN'];
216
    }
217
218 1
    public function getAdditionalDomains(): array
219
    {
220 1
        $additionalDomains = explode(', ', $this->certificateFields['extensions']['subjectAltName'] ?? '');
221
222 1
        return array_map(function (string $domain) {
223 1
            return str_replace('DNS:', '', $domain);
224 1
        }, $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 3
    public function validFromDate(): Carbon
238
    {
239 3
        return Carbon::createFromTimestampUTC($this->certificateFields['validFrom_time_t']);
240
    }
241
242 4
    public function expirationDate(): Carbon
243
    {
244 4
        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 1
    public function isValid(string $url = null): bool
258
    {
259
        // Verify SSL not expired
260 1
        if (! Carbon::now()->between($this->validFromDate(), $this->expirationDate())) {
261 1
            return false;
262
        }
263
        // Verify the SSL applies to the domain; use $url if provided, other wise use input
264 1
        if ($this->appliesToUrl($url ?? $this->inputDomain) === false) {
265
            return false;
266
        }
267
        // Check SerialNumber for CRL list
268
        if ($this->isRevoked()) {
269
            return false;
270
        }
271
272
        return true;
273
    }
274
275
    public function isValidUntil(Carbon $carbon, string $url = null): bool
276
    {
277
        if ($this->isValidDate($carbon) === false) {
278
            return false;
279
        }
280
281
        return $this->isValid($url);
282
    }
283
284 1 View Code Duplication
    public function isValidDate(Carbon $carbon): bool
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
285
    {
286 1
        if ($carbon->between($this->validFromDate(), $this->expirationDate()) === false) {
287 1
            return false;
288
        }
289
290 1
        return true;
291
    }
292
293
    public function isSelfSigned(): bool
294
    {
295
        // Get the issuer data
296
        $url = $this->getIssuer();
297
        // make sure we don't include wildcard if it's there...
298
        if (starts_with($url, '*.') === true) {
299
            $url = substr($url, 2);
300
        }
301
        //Try to parse the string
302
        try {
303
            $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
        if ($this->appliesToUrl((string) $issuerUrl) === true) {
311
            return true;
312
        }
313
314
        return false;
315
    }
316
317 1
    public function appliesToUrl(string $url): bool
318
    {
319 1
        if (starts_with($url, '*.') === true) {
320
            $url = substr($url, 2);
321
        }
322 1
        $host = (new Url($url))->getHostName() ?: $url;
323
324
        $certificateHosts = array_merge([$this->getCertificateDomain()], $this->getAdditionalDomains());
325
326
        foreach ($certificateHosts as $certificateHost) {
327
            if ($host === $certificateHost) {
328
                return true;
329
            }
330
331
            if ($this->wildcardHostCoversHost($certificateHost, $host)) {
332
                return true;
333
            }
334
        }
335
336
        return false;
337
    }
338
339
    protected function wildcardHostCoversHost(string $wildcardHost, string $host): bool
340
    {
341
        if ($host === $wildcardHost) {
342
            return true;
343
        }
344
345
        if (! starts_with($wildcardHost, '*')) {
346
            return false;
347
        }
348
349
        $wildcardHostWithoutWildcard = substr($wildcardHost, 2);
350
351
        return substr_count($wildcardHost, '.') >= substr_count($host, '.') && ends_with($host, $wildcardHostWithoutWildcard);
352
    }
353
}
354