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 ( c20117...e9301e )
by Daniel
02:01
created

SslCertificate::parseCrlLinks()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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