Skip to content

Commit 1f39614

Browse files
authored
Enhance key operations validation in UsageAnalyzer and add unit tests (#641)
1 parent 2362f23 commit 1f39614

File tree

2 files changed

+189
-12
lines changed

2 files changed

+189
-12
lines changed

src/Library/KeyManagement/Analyzer/UsageAnalyzer.php

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
use Jose\Component\Core\JWK;
88
use Override;
9+
use function array_diff;
910
use function in_array;
11+
use function is_array;
1012
use function sprintf;
1113

1214
final readonly class UsageAnalyzer implements KeyAnalyzer
@@ -24,18 +26,27 @@ public function analyze(JWK $jwk, MessageBag $bag): void
2426
))
2527
);
2628
}
27-
if ($jwk->has('key_ops') && ! in_array(
28-
$jwk->get('key_ops'),
29-
['sign', 'verify', 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey'],
30-
true
31-
)) {
32-
$bag->add(
33-
Message::high(sprintf(
34-
'The parameter "key_ops" has an unsupported value "%s". Please use one of the following values: %s.',
35-
$jwk->get('key_ops'),
36-
implode(', ', ['verify', 'sign', 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey'])
37-
))
38-
);
29+
if ($jwk->has('key_ops')) {
30+
$key_ops = $jwk->get('key_ops');
31+
if (! is_array($key_ops)) {
32+
$bag->add(
33+
Message::high(
34+
'The parameter "key_ops" must be an array of key operation values.'
35+
)
36+
);
37+
} else {
38+
$allowedOps = ['sign', 'verify', 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey', 'deriveKey', 'deriveBits'];
39+
$unsupportedOps = array_diff($key_ops, $allowedOps);
40+
if ($unsupportedOps !== []) {
41+
$bag->add(
42+
Message::high(sprintf(
43+
'The parameter "key_ops" contains unsupported values: "%s". Please use only the following values: %s.',
44+
implode('", "', $unsupportedOps),
45+
implode(', ', $allowedOps)
46+
))
47+
);
48+
}
49+
}
3950
}
4051
}
4152
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Jose\Tests\Component\KeyManagement;
6+
7+
use Jose\Component\Core\JWK;
8+
use Jose\Component\KeyManagement\Analyzer\Message;
9+
use Jose\Component\KeyManagement\Analyzer\MessageBag;
10+
use Jose\Component\KeyManagement\Analyzer\UsageAnalyzer;
11+
use PHPUnit\Framework\Attributes\Test;
12+
use PHPUnit\Framework\TestCase;
13+
14+
/**
15+
* @internal
16+
*/
17+
final class UsageAnalyzerTest extends TestCase
18+
{
19+
private UsageAnalyzer $analyzer;
20+
21+
protected function setUp(): void
22+
{
23+
$this->analyzer = new UsageAnalyzer();
24+
}
25+
26+
#[Test]
27+
public function keyWithoutUseShouldGetMediumMessage(): void
28+
{
29+
$jwk = new JWK([
30+
'kty' => 'oct',
31+
'k' => 'GawgguFyGrWKav7AX4VKUg',
32+
]);
33+
34+
$bag = new MessageBag();
35+
$this->analyzer->analyze($jwk, $bag);
36+
37+
$messages = $bag->all();
38+
static::assertCount(1, $messages);
39+
static::assertSame(Message::SEVERITY_MEDIUM, $messages[0]->getSeverity());
40+
static::assertSame('The parameter "use" should be added.', $messages[0]->getMessage());
41+
}
42+
43+
#[Test]
44+
public function keyWithValidUseShouldHaveNoMessages(): void
45+
{
46+
$jwk = new JWK([
47+
'kty' => 'oct',
48+
'k' => 'GawgguFyGrWKav7AX4VKUg',
49+
'use' => 'sig',
50+
]);
51+
52+
$bag = new MessageBag();
53+
$this->analyzer->analyze($jwk, $bag);
54+
55+
static::assertEmpty($bag->all());
56+
}
57+
58+
#[Test]
59+
public function keyWithInvalidUseShouldGetHighMessage(): void
60+
{
61+
$jwk = new JWK([
62+
'kty' => 'oct',
63+
'k' => 'GawgguFyGrWKav7AX4VKUg',
64+
'use' => 'invalid',
65+
]);
66+
67+
$bag = new MessageBag();
68+
$this->analyzer->analyze($jwk, $bag);
69+
70+
$messages = $bag->all();
71+
static::assertCount(1, $messages);
72+
static::assertSame(Message::SEVERITY_HIGH, $messages[0]->getSeverity());
73+
static::assertStringContainsString('unsupported value "invalid"', $messages[0]->getMessage());
74+
}
75+
76+
#[Test]
77+
public function keyWithValidKeyOpsArrayShouldHaveNoMessages(): void
78+
{
79+
$jwk = new JWK([
80+
'kty' => 'oct',
81+
'k' => 'GawgguFyGrWKav7AX4VKUg',
82+
'use' => 'sig',
83+
'key_ops' => ['sign', 'verify'],
84+
]);
85+
86+
$bag = new MessageBag();
87+
$this->analyzer->analyze($jwk, $bag);
88+
89+
static::assertEmpty($bag->all());
90+
}
91+
92+
#[Test]
93+
public function keyWithKeyOpsAsStringShouldGetHighMessage(): void
94+
{
95+
$jwk = new JWK([
96+
'kty' => 'oct',
97+
'k' => 'GawgguFyGrWKav7AX4VKUg',
98+
'use' => 'sig',
99+
'key_ops' => 'sign',
100+
]);
101+
102+
$bag = new MessageBag();
103+
$this->analyzer->analyze($jwk, $bag);
104+
105+
$messages = $bag->all();
106+
static::assertCount(1, $messages);
107+
static::assertSame(Message::SEVERITY_HIGH, $messages[0]->getSeverity());
108+
static::assertSame(
109+
'The parameter "key_ops" must be an array of key operation values.',
110+
$messages[0]->getMessage()
111+
);
112+
}
113+
114+
#[Test]
115+
public function keyWithInvalidKeyOpsValuesShouldGetHighMessage(): void
116+
{
117+
$jwk = new JWK([
118+
'kty' => 'oct',
119+
'k' => 'GawgguFyGrWKav7AX4VKUg',
120+
'use' => 'sig',
121+
'key_ops' => ['sign', 'invalid', 'unknownOp'],
122+
]);
123+
124+
$bag = new MessageBag();
125+
$this->analyzer->analyze($jwk, $bag);
126+
127+
$messages = $bag->all();
128+
static::assertCount(1, $messages);
129+
static::assertSame(Message::SEVERITY_HIGH, $messages[0]->getSeverity());
130+
static::assertStringContainsString('unsupported values', $messages[0]->getMessage());
131+
static::assertStringContainsString('invalid', $messages[0]->getMessage());
132+
static::assertStringContainsString('unknownOp', $messages[0]->getMessage());
133+
}
134+
135+
#[Test]
136+
public function keyWithAllValidKeyOpsValuesShouldHaveNoMessages(): void
137+
{
138+
$jwk = new JWK([
139+
'kty' => 'oct',
140+
'k' => 'GawgguFyGrWKav7AX4VKUg',
141+
'use' => 'enc',
142+
'key_ops' => ['sign', 'verify', 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey', 'deriveKey', 'deriveBits'],
143+
]);
144+
145+
$bag = new MessageBag();
146+
$this->analyzer->analyze($jwk, $bag);
147+
148+
static::assertEmpty($bag->all());
149+
}
150+
151+
#[Test]
152+
public function keyWithEmptyKeyOpsArrayShouldHaveNoMessages(): void
153+
{
154+
$jwk = new JWK([
155+
'kty' => 'oct',
156+
'k' => 'GawgguFyGrWKav7AX4VKUg',
157+
'use' => 'sig',
158+
'key_ops' => [],
159+
]);
160+
161+
$bag = new MessageBag();
162+
$this->analyzer->analyze($jwk, $bag);
163+
164+
static::assertEmpty($bag->all());
165+
}
166+
}

0 commit comments

Comments
 (0)