Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions src/Library/KeyManagement/Analyzer/UsageAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

use Jose\Component\Core\JWK;
use Override;
use function array_diff;
use function in_array;
use function is_array;
use function sprintf;

final readonly class UsageAnalyzer implements KeyAnalyzer
Expand All @@ -20,22 +22,31 @@
$bag->add(
Message::high(sprintf(
'The parameter "use" has an unsupported value "%s". Please use "sig" (signature) or "enc" (encryption).',
$jwk->get('use')

Check failure on line 25 in src/Library/KeyManagement/Analyzer/UsageAnalyzer.php

View workflow job for this annotation

GitHub Actions / 3️⃣ Static Analysis

Ignored error pattern #^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$# (argument.type) in path /home/runner/work/jwt-framework/jwt-framework/src/Library/KeyManagement/Analyzer/UsageAnalyzer.php is expected to occur 2 times, but occurred only 1 time.

Check failure on line 25 in src/Library/KeyManagement/Analyzer/UsageAnalyzer.php

View workflow job for this annotation

GitHub Actions / 1️⃣ Static Analysis (PHPStan)

Ignored error pattern "Parameter #2 ...$values of function sprintf expects bool|float|int|string|null, mixed given." (argument.type) in path /__w/jwt-framework/jwt-framework/src/Library/KeyManagement/Analyzer/UsageAnalyzer.php is expected to occur 2 times, but occurred only 1 time.
))
);
}
if ($jwk->has('key_ops') && ! in_array(
$jwk->get('key_ops'),
['sign', 'verify', 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey'],
true
)) {
$bag->add(
Message::high(sprintf(
'The parameter "key_ops" has an unsupported value "%s". Please use one of the following values: %s.',
$jwk->get('key_ops'),
implode(', ', ['verify', 'sign', 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey'])
))
);
if ($jwk->has('key_ops')) {
$key_ops = $jwk->get('key_ops');
if (! is_array($key_ops)) {
$bag->add(
Message::high(
'The parameter "key_ops" must be an array of key operation values.'
)
);
} else {
$allowedOps = ['sign', 'verify', 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey', 'deriveKey', 'deriveBits'];
$unsupportedOps = array_diff($key_ops, $allowedOps);
if ($unsupportedOps !== []) {
$bag->add(
Message::high(sprintf(
'The parameter "key_ops" contains unsupported values: "%s". Please use only the following values: %s.',
implode('", "', $unsupportedOps),
implode(', ', $allowedOps)
))
);
}
}
}
}
}
166 changes: 166 additions & 0 deletions tests/Component/KeyManagement/UsageAnalyzerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

declare(strict_types=1);

namespace Jose\Tests\Component\KeyManagement;

use Jose\Component\Core\JWK;
use Jose\Component\KeyManagement\Analyzer\Message;
use Jose\Component\KeyManagement\Analyzer\MessageBag;
use Jose\Component\KeyManagement\Analyzer\UsageAnalyzer;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

/**
* @internal
*/
final class UsageAnalyzerTest extends TestCase
{
private UsageAnalyzer $analyzer;

protected function setUp(): void
{
$this->analyzer = new UsageAnalyzer();
}

#[Test]
public function keyWithoutUseShouldGetMediumMessage(): void
{
$jwk = new JWK([
'kty' => 'oct',
'k' => 'GawgguFyGrWKav7AX4VKUg',
]);

$bag = new MessageBag();
$this->analyzer->analyze($jwk, $bag);

$messages = $bag->all();
static::assertCount(1, $messages);
static::assertSame(Message::SEVERITY_MEDIUM, $messages[0]->getSeverity());
static::assertSame('The parameter "use" should be added.', $messages[0]->getMessage());
}

#[Test]
public function keyWithValidUseShouldHaveNoMessages(): void
{
$jwk = new JWK([
'kty' => 'oct',
'k' => 'GawgguFyGrWKav7AX4VKUg',
'use' => 'sig',
]);

$bag = new MessageBag();
$this->analyzer->analyze($jwk, $bag);

static::assertEmpty($bag->all());
}

#[Test]
public function keyWithInvalidUseShouldGetHighMessage(): void
{
$jwk = new JWK([
'kty' => 'oct',
'k' => 'GawgguFyGrWKav7AX4VKUg',
'use' => 'invalid',
]);

$bag = new MessageBag();
$this->analyzer->analyze($jwk, $bag);

$messages = $bag->all();
static::assertCount(1, $messages);
static::assertSame(Message::SEVERITY_HIGH, $messages[0]->getSeverity());
static::assertStringContainsString('unsupported value "invalid"', $messages[0]->getMessage());
}

#[Test]
public function keyWithValidKeyOpsArrayShouldHaveNoMessages(): void
{
$jwk = new JWK([
'kty' => 'oct',
'k' => 'GawgguFyGrWKav7AX4VKUg',
'use' => 'sig',
'key_ops' => ['sign', 'verify'],
]);

$bag = new MessageBag();
$this->analyzer->analyze($jwk, $bag);

static::assertEmpty($bag->all());
}

#[Test]
public function keyWithKeyOpsAsStringShouldGetHighMessage(): void
{
$jwk = new JWK([
'kty' => 'oct',
'k' => 'GawgguFyGrWKav7AX4VKUg',
'use' => 'sig',
'key_ops' => 'sign',
]);

$bag = new MessageBag();
$this->analyzer->analyze($jwk, $bag);

$messages = $bag->all();
static::assertCount(1, $messages);
static::assertSame(Message::SEVERITY_HIGH, $messages[0]->getSeverity());
static::assertSame(
'The parameter "key_ops" must be an array of key operation values.',
$messages[0]->getMessage()
);
}

#[Test]
public function keyWithInvalidKeyOpsValuesShouldGetHighMessage(): void
{
$jwk = new JWK([
'kty' => 'oct',
'k' => 'GawgguFyGrWKav7AX4VKUg',
'use' => 'sig',
'key_ops' => ['sign', 'invalid', 'unknownOp'],
]);

$bag = new MessageBag();
$this->analyzer->analyze($jwk, $bag);

$messages = $bag->all();
static::assertCount(1, $messages);
static::assertSame(Message::SEVERITY_HIGH, $messages[0]->getSeverity());
static::assertStringContainsString('unsupported values', $messages[0]->getMessage());
static::assertStringContainsString('invalid', $messages[0]->getMessage());
static::assertStringContainsString('unknownOp', $messages[0]->getMessage());
}

#[Test]
public function keyWithAllValidKeyOpsValuesShouldHaveNoMessages(): void
{
$jwk = new JWK([
'kty' => 'oct',
'k' => 'GawgguFyGrWKav7AX4VKUg',
'use' => 'enc',
'key_ops' => ['sign', 'verify', 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey', 'deriveKey', 'deriveBits'],
]);

$bag = new MessageBag();
$this->analyzer->analyze($jwk, $bag);

static::assertEmpty($bag->all());
}

#[Test]
public function keyWithEmptyKeyOpsArrayShouldHaveNoMessages(): void
{
$jwk = new JWK([
'kty' => 'oct',
'k' => 'GawgguFyGrWKav7AX4VKUg',
'use' => 'sig',
'key_ops' => [],
]);

$bag = new MessageBag();
$this->analyzer->analyze($jwk, $bag);

static::assertEmpty($bag->all());
}
}
Loading