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 ( e9301e...862f74 )
by Daniel
01:53
created

SslCertificate::extractCrlLinks()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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