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 ( ecd45b...16c344 )
by Daniel
02:29
created

SslCertificate   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 309
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Test Coverage

Coverage 3.03%

Importance

Changes 0
Metric Value
dl 0
loc 309
ccs 4
cts 132
cp 0.0303
rs 6.8
c 0
b 0
f 0
wmc 55
lcom 2
cbo 5

32 Methods

Rating   Name   Duplication   Size   Complexity  
A createForHostName() 0 6 1
A extractCrlLinks() 0 7 1
A parseCrlLinks() 0 14 2
A getRevokedDate() 0 8 3
A isClrRevoked() 0 15 4
A parseCertChains() 0 9 2
A __construct() 0 18 2
A hasSslChain() 0 8 3
A getTestedDomain() 0 4 1
A getCertificateFields() 0 4 1
A getCertificateChains() 0 4 1
A getSerialNumber() 0 4 1
A hasCrlLink() 0 4 1
A getCrlLinks() 0 8 2
A getCrl() 0 8 2
A isRevoked() 0 4 1
A getCrlRevokedTime() 0 6 2
A getResolvedIp() 0 4 1
A getIssuer() 0 4 1
A getDomain() 0 8 2
A getCertificateDomain() 0 4 1
A getSignatureAlgorithm() 0 4 1
A getAdditionalDomains() 0 8 1
A getConnectionMeta() 0 4 1
A validFromDate() 0 4 1
A expirationDate() 0 4 1
A isExpired() 0 4 1
A isTrusted() 0 4 1
A isValid() 0 17 4
A isValidUntil() 0 8 2
B appliesToUrl() 0 21 5
A wildcardHostCoversHost() 0 10 2

How to fix   Complexity   

Complex Class

Complex classes like SslCertificate often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SslCertificate, and based on these observations, apply Extract Interface, too.

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 2
    public static function createForHostName(string $url, int $timeout = 30): SslCertificate
47
    {
48 2
        $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 21
    public function __construct(array $downloadResults)
112
    {
113 21
        $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 getTestedDomain(): string
140
    {
141
        return $this->testedDomain;
142
    }
143
144
    public function getCertificateFields(): array
145
    {
146
        return $this->certificateFields;
147
    }
148
149
    public function getCertificateChains(): array
150
    {
151
        return $this->certificateChains;
152
    }
153
154
    public function getSerialNumber(): string
155
    {
156
        return strtoupper($this->serial->toHex());
157
    }
158
159
    public function hasCrlLink(): bool
160
    {
161
        return isset($this->certificateFields['extensions']['crlDistributionPoints']);
162
    }
163
164
    public function getCrlLinks()
165
    {
166
        if (! $this->hasCrlLink()) {
167
            return;
168
        }
169
170
        return $this->crlLinks;
171
    }
172
173
    public function getCrl()
174
    {
175
        if (! $this->hasCrlLink()) {
176
            return;
177
        }
178
179
        return $this->crl;
180
    }
181
182
    public function isRevoked()
183
    {
184
        return $this->revoked;
185
    }
186
187
    public function getCrlRevokedTime()
188
    {
189
        if ($this->isRevoked()) {
190
            return $this->revokedTime;
191
        }
192
    }
193
194
    public function getResolvedIp(): string
195
    {
196
        return $this->ip;
197
    }
198
199
    public function getIssuer(): string
200
    {
201
        return $this->certificateFields['issuer']['CN'];
202
    }
203
204
    public function getDomain(): string
205
    {
206
        $certDomain = $this->getCertificateDomain();
207
        if (str_contains($certDomain, $this->inputDomain) === false) {
208
          return $this->inputDomain;
209
        }
210
        return $certDomain ?? '';
211
    }
212
213
    public function getCertificateDomain(): string
214
    {
215
        return $this->certificateFields['subject']['CN'];
216
    }
217
218
    public function getSignatureAlgorithm(): string
219
    {
220
        return $this->certificateFields['signatureTypeSN'] ?? '';
221
    }
222
223
    public function getAdditionalDomains(): array
224
    {
225
        $additionalDomains = explode(', ', $this->certificateFields['extensions']['subjectAltName'] ?? '');
226
227
        return array_map(function (string $domain) {
228
            return str_replace('DNS:', '', $domain);
229
        }, $additionalDomains);
230
    }
231
232
    public function getConnectionMeta(): array
233
    {
234
        return $this->connectionMeta;
235
    }
236
237
    public function validFromDate(): Carbon
238
    {
239
        return Carbon::createFromTimestampUTC($this->certificateFields['validFrom_time_t']);
240
    }
241
242
    public function expirationDate(): Carbon
243
    {
244
        return Carbon::createFromTimestampUTC($this->certificateFields['validTo_time_t']);
245
    }
246
247
    public function isExpired(): bool
248
    {
249
        return $this->expirationDate()->isPast();
250
    }
251
252
    public function isTrusted(): bool
253
    {
254
        return $this->trusted;
255
    }
256
257
    public function isValid(string $url = null): bool
258
    {
259
        // Verify SSL not expired
260
        if (! Carbon::now()->between($this->validFromDate(), $this->expirationDate())) {
261
            return false;
262
        }
263
        // If a URL is provided verify the SSL applies to the domain
264
        if ($this->appliesToUrl($url ?? $this->getDomain()) === 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->expirationDate()->gt($carbon)) {
278
            return false;
279
        }
280
281
        return $this->isValid($url);
282
    }
283
284
    public function appliesToUrl(string $url): bool
285
    {
286
        if (starts_with($url, '*.') === true) {
287
            $url = substr($url, 2);
288
        }
289
        $host = (new Url($url))->getHostName();
290
291
        $certificateHosts = array_merge([$this->getDomain()], $this->getAdditionalDomains());
292
293
        foreach ($certificateHosts as $certificateHost) {
294
            if ($host === $certificateHost) {
295
                return true;
296
            }
297
298
            if ($this->wildcardHostCoversHost($certificateHost, $host)) {
299
                return true;
300
            }
301
        }
302
303
        return false;
304
    }
305
306
    protected function wildcardHostCoversHost(string $wildcardHost, string $host): bool
307
    {
308
        if (! starts_with($wildcardHost, '*')) {
309
            return false;
310
        }
311
312
        $wildcardHostWithoutWildcard = substr($wildcardHost, 2);
313
314
        return ends_with($host, $wildcardHostWithoutWildcard);
315
    }
316
}
317