KeysetAnalyzerCommand::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 3
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;
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
/**
25
 * Class KeysetAnalyzerCommand.
26
 */
27
final class KeysetAnalyzerCommand extends Command
28
{
29
    /**
30
     * @var JWKAnalyzerManager
31
     */
32
    private $analyzerManager;
33
34
    /**
35
     * @var JsonConverterInterface
36
     */
37
    private $jsonConverter;
38
39
    /**
40
     * KeyAnalyzerCommand constructor.
41
     *
42
     * @param JWKAnalyzerManager     $analyzerManager
43
     * @param JsonConverterInterface $jsonConverter
44
     * @param string|null            $name
45
     */
46
    public function __construct(JWKAnalyzerManager $analyzerManager, JsonConverterInterface $jsonConverter, string $name = null)
47
    {
48
        parent::__construct($name);
49
        $this->analyzerManager = $analyzerManager;
50
        $this->jsonConverter = $jsonConverter;
51
    }
52
53
    /**
54
     * {@inheritdoc}
55
     */
56
    protected function configure()
57
    {
58
        parent::configure();
59
        $this
60
            ->setName('keyset:analyze')
61
            ->setDescription('JWKSet quality analyzer.')
62
            ->setHelp('This command will analyze a JWKSet object and find security issues.')
63
            ->addArgument('jwkset', InputArgument::REQUIRED, 'The JWKSet object')
64
        ;
65
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70
    protected function execute(InputInterface $input, OutputInterface $output)
71
    {
72
        $jwkset = $this->getKeyset($input);
73
74
        $privateKeys = 0;
75
        $publicKeys = 0;
76
        $sharedKeys = 0;
77
        $mixedKeys = false;
78
79
        foreach ($jwkset as $kid => $jwk) {
80
            $output->writeln(sprintf('Analysing key with index/kid "%s"', $kid));
81
            $messages = $this->analyzerManager->analyze($jwk);
0 ignored issues
show
Bug introduced by
It seems like $jwk defined by $jwk on line 79 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...
82
            if (!empty($messages)) {
83
                foreach ($messages as $message) {
84
                    $output->writeln('    '.$message);
85
                }
86
            } else {
87
                $output->writeln('    No issue with this key');
88
            }
89
90
            switch (true) {
91
                case 'oct' === $jwk->get('kty'):
92
                    $sharedKeys++;
93
                    if (0 !== $privateKeys + $publicKeys) {
94
                        $mixedKeys = true;
95
                    }
96
97
                    break;
98
                case in_array($jwk->get('kty'), ['RSA', 'EC', 'OKP']):
99
                    if ($jwk->has('d')) {
100
                        ++$privateKeys;
101
                        if (0 !== $sharedKeys + $publicKeys) {
102
                            $mixedKeys = true;
103
                        }
104
                    } else {
105
                        ++$publicKeys;
106
                        if (0 !== $privateKeys + $sharedKeys) {
107
                            $mixedKeys = true;
108
                        }
109
                    }
110
111
                    break;
112
                default:
113
                    break;
114
            }
115
        }
116
117
        if ($mixedKeys) {
118
            $output->writeln('/!\\ This key set mixes share, public and private keys. You should create one key set per key type. /!\\');
119
        }
120
    }
121
122
    /**
123
     * @param InputInterface $input
124
     *
125
     * @return JWKSet
126
     */
127
    private function getKeyset(InputInterface $input): JWKSet
128
    {
129
        $jwkset = $input->getArgument('jwkset');
130
        $json = $this->jsonConverter->decode($jwkset);
131
        if (is_array($json)) {
132
            return JWKSet::createFromKeyData($json);
133
        }
134
135
        throw new \InvalidArgumentException('The argument must be a valid JWKSet.');
136
    }
137
}
138