Failed Conditions
Push — v7 ( 6a6683...1d7ef8 )
by Florent
03:11
created

KeysetAnalyzerCommand   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 111
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 0
Metric Value
wmc 15
lcom 2
cbo 6
dl 0
loc 111
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A configure() 0 10 1
C execute() 0 51 11
A getKeyset() 0 10 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace Jose\Component\Console\Command;
15
16
use Jose\Component\Core\Converter\JsonConverterInterface;
17
use Jose\Component\Core\JWKSet;
18
use Jose\Component\KeyManagement\KeyAnalyzer\JWKAnalyzerManager;
19
use Symfony\Component\Console\Command\Command;
20
use Symfony\Component\Console\Input\InputArgument;
21
use Symfony\Component\Console\Input\InputInterface;
22
use Symfony\Component\Console\Output\OutputInterface;
23
24
final class KeysetAnalyzerCommand extends Command
25
{
26
    /**
27
     * @var JWKAnalyzerManager
28
     */
29
    private $analyzerManager;
30
31
    /**
32
     * @var JsonConverterInterface
33
     */
34
    private $jsonConverter;
35
36
    /**
37
     * KeyAnalyzerCommand constructor.
38
     *
39
     * @param JWKAnalyzerManager     $analyzerManager
40
     * @param JsonConverterInterface $jsonConverter
41
     * @param string|null            $name
42
     */
43
    public function __construct(JWKAnalyzerManager $analyzerManager, JsonConverterInterface $jsonConverter, string $name = null)
44
    {
45
        parent::__construct($name);
46
        $this->analyzerManager = $analyzerManager;
47
        $this->jsonConverter = $jsonConverter;
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53
    protected function configure()
54
    {
55
        parent::configure();
56
        $this
57
            ->setName('keyset:analyze')
58
            ->setDescription('JWKSet quality analyzer.')
59
            ->setHelp('This command will analyze a JWKSet object and find security issues.')
60
            ->addArgument('jwkset', InputArgument::REQUIRED, 'The JWKSet object')
61
        ;
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67
    protected function execute(InputInterface $input, OutputInterface $output)
68
    {
69
        $jwkset = $this->getKeyset($input);
70
71
        $privateKeys = 0;
72
        $publicKeys = 0;
73
        $sharedKeys = 0;
74
        $mixedKeys = false;
75
76
        foreach ($jwkset as $kid => $jwk) {
77
            $output->writeln(sprintf('Analysing key with index/kid "%s"', $kid));
78
            $messages = $this->analyzerManager->analyze($jwk);
0 ignored issues
show
Bug introduced by
It seems like $jwk defined by $jwk on line 76 can be null; however, Jose\Component\KeyManage...lyzerManager::analyze() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
79
            if (!empty($messages)) {
80
                foreach ($messages as $message) {
81
                    $output->writeln('    '.$message);
82
                }
83
            } else {
84
                $output->writeln('    No issue with this key');
85
            }
86
87
            switch (true) {
88
                case 'oct' === $jwk->get('kty'):
89
                    $sharedKeys++;
90
                    if (0 !== $privateKeys + $publicKeys) {
91
                        $mixedKeys = true;
92
                    }
93
94
                    break;
95
                case in_array($jwk->get('kty'), ['RSA', 'EC', 'OKP']):
96
                    if ($jwk->has('d')) {
97
                        ++$privateKeys;
98
                        if (0 !== $sharedKeys + $publicKeys) {
99
                            $mixedKeys = true;
100
                        }
101
                    } else {
102
                        ++$publicKeys;
103
                        if (0 !== $privateKeys + $sharedKeys) {
104
                            $mixedKeys = true;
105
                        }
106
                    }
107
108
                    break;
109
                default:
110
                    break;
111
            }
112
        }
113
114
        if ($mixedKeys) {
115
            $output->writeln('/!\\ This key set mixes share, public and private keys. You should create one key set per key type. /!\\');
116
        }
117
    }
118
119
    /**
120
     * @param InputInterface $input
121
     *
122
     * @return JWKSet
123
     */
124
    private function getKeyset(InputInterface $input): JWKSet
125
    {
126
        $jwkset = $input->getArgument('jwkset');
127
        $json = $this->jsonConverter->decode($jwkset);
128
        if (is_array($json)) {
129
            return JWKSet::createFromKeyData($json);
130
        }
131
132
        throw new \InvalidArgumentException('The argument must be a valid JWKSet.');
133
    }
134
}
135