Skip to content

Commit

Permalink
refactor: Introduce a PHAR SigningAlgorithm enum class (#1106)
Browse files Browse the repository at this point in the history
  • Loading branch information
theofidry authored Oct 22, 2023
1 parent 20027b9 commit 3077ab3
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 52 deletions.
42 changes: 15 additions & 27 deletions src/Configuration/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use KevinGH\Box\Json\Json;
use KevinGH\Box\MapFile;
use KevinGH\Box\Phar\CompressionAlgorithm;
use KevinGH\Box\Phar\SigningAlgorithm;
use KevinGH\Box\PhpScoper\SerializableScoper;
use Phar;
use phpDocumentor\Reflection\DocBlockFactory;
Expand All @@ -58,7 +59,6 @@
use function array_walk;
use function constant;
use function current;
use function defined;
use function dirname;
use function explode;
use function file_exists;
Expand All @@ -77,7 +77,6 @@
use function iter\toArray;
use function iter\values;
use function KevinGH\Box\get_box_version;
use function KevinGH\Box\get_phar_signing_algorithms;
use function KevinGH\Box\unique_id;
use function krsort;
use function preg_match;
Expand Down Expand Up @@ -110,7 +109,7 @@ final class Configuration
'finder',
];
private const PHP_SCOPER_CONFIG = 'scoper.inc.php';
private const DEFAULT_SIGNING_ALGORITHM = Phar::SHA1;
private const DEFAULT_SIGNING_ALGORITHM = SigningAlgorithm::SHA1;
private const DEFAULT_ALIAS_PREFIX = 'box-auto-generated-alias-';

private const DEFAULT_IGNORED_ANNOTATIONS = [
Expand Down Expand Up @@ -406,7 +405,7 @@ public static function create(?string $file, stdClass $raw): self
* @param bool $promptForPrivateKey If the user should be prompted for the private key passphrase
* @param array $processedReplacements The processed list of replacement placeholders and their values
* @param null|non-empty-string $shebang The shebang line
* @param int $signingAlgorithm The PHAR siging algorithm. See \Phar constants
* @param SigningAlgorithm $signingAlgorithm The PHAR siging algorithm. See \Phar constants
* @param null|string $stubBannerContents The stub banner comment
* @param null|string $stubBannerPath The path to the stub banner comment file
* @param null|string $stubPath The PHAR stub file path
Expand Down Expand Up @@ -443,7 +442,7 @@ private function __construct(
private bool $promptForPrivateKey,
private array $processedReplacements,
private ?string $shebang,
private int|string $signingAlgorithm,
private SigningAlgorithm $signingAlgorithm,
private ?string $stubBannerContents,
private ?string $stubBannerPath,
private ?string $stubPath,
Expand Down Expand Up @@ -650,7 +649,7 @@ public function getShebang(): ?string
return $this->shebang;
}

public function getSigningAlgorithm(): int
public function getSigningAlgorithm(): SigningAlgorithm
{
return $this->signingAlgorithm;
}
Expand Down Expand Up @@ -1929,10 +1928,10 @@ private static function retrieveOutputPath(
private static function retrievePrivateKeyPath(
stdClass $raw,
string $basePath,
int $signingAlgorithm,
SigningAlgorithm $signingAlgorithm,
ConfigurationLogger $logger,
): ?string {
if (property_exists($raw, self::KEY_KEY) && Phar::OPENSSL !== $signingAlgorithm) {
if (property_exists($raw, self::KEY_KEY) && SigningAlgorithm::OPENSSL !== $signingAlgorithm) {
if (null === $raw->{self::KEY_KEY}) {
$logger->addRecommendation(
'The setting "key" has been set but is unnecessary since the signing algorithm is not "OPENSSL".',
Expand All @@ -1948,7 +1947,7 @@ private static function retrievePrivateKeyPath(

if (!isset($raw->{self::KEY_KEY})) {
Assert::true(
Phar::OPENSSL !== $signingAlgorithm,
SigningAlgorithm::OPENSSL !== $signingAlgorithm,
'Expected to have a private key for OpenSSL signing but none have been provided.',
);

Expand All @@ -1964,7 +1963,7 @@ private static function retrievePrivateKeyPath(

private static function retrievePrivateKeyPassphrase(
stdClass $raw,
int $algorithm,
SigningAlgorithm $algorithm,
ConfigurationLogger $logger,
): ?string {
self::checkIfDefaultValue($logger, $raw, self::KEY_PASS_KEY);
Expand All @@ -1976,7 +1975,7 @@ private static function retrievePrivateKeyPassphrase(
/** @var null|false|string $keyPass */
$keyPass = $raw->{self::KEY_PASS_KEY};

if (Phar::OPENSSL !== $algorithm) {
if (SigningAlgorithm::OPENSSL !== $algorithm) {
if (false === $keyPass || null === $keyPass) {
$logger->addRecommendation(
sprintf(
Expand Down Expand Up @@ -2264,7 +2263,7 @@ private static function retrieveShebang(stdClass $raw, bool $stubIsGenerated, Co
return $shebang;
}

private static function retrieveSigningAlgorithm(stdClass $raw, ConfigurationLogger $logger): int
private static function retrieveSigningAlgorithm(stdClass $raw, ConfigurationLogger $logger): SigningAlgorithm
{
if (property_exists($raw, self::ALGORITHM_KEY) && null === $raw->{self::ALGORITHM_KEY}) {
self::addRecommendationForDefaultValue($logger, self::ALGORITHM_KEY);
Expand All @@ -2274,19 +2273,8 @@ private static function retrieveSigningAlgorithm(stdClass $raw, ConfigurationLog
return self::DEFAULT_SIGNING_ALGORITHM;
}

$algorithm = mb_strtoupper($raw->{self::ALGORITHM_KEY});

Assert::inArray($algorithm, array_keys(get_phar_signing_algorithms()));

Assert::true(
defined('Phar::'.$algorithm),
sprintf(
'The signing algorithm "%s" is not supported by your current PHAR version.',
$algorithm,
),
);

$algorithm = constant('Phar::'.$algorithm);
$algorithmLabel = mb_strtoupper($raw->{self::ALGORITHM_KEY});
$algorithm = SigningAlgorithm::fromLabel($algorithmLabel);

if (self::DEFAULT_SIGNING_ALGORITHM === $algorithm) {
self::addRecommendationForDefaultValue($logger, self::ALGORITHM_KEY);
Expand Down Expand Up @@ -2424,11 +2412,11 @@ private static function retrieveInterceptsFileFunctions(

private static function retrievePromptForPrivateKey(
stdClass $raw,
int $signingAlgorithm,
SigningAlgorithm $signingAlgorithm,
ConfigurationLogger $logger,
): bool {
if (isset($raw->{self::KEY_PASS_KEY}) && true === $raw->{self::KEY_PASS_KEY}) {
if (Phar::OPENSSL !== $signingAlgorithm) {
if (SigningAlgorithm::OPENSSL !== $signingAlgorithm) {
$logger->addWarning(
'A prompt for password for the private key has been requested but ignored since the signing '
.'algorithm used is not "OPENSSL.',
Expand Down
4 changes: 1 addition & 3 deletions src/Configuration/ExportableConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@
use Symfony\Component\Finder\SplFileInfo as SymfonySplFileInfo;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use function array_flip;
use function array_map;
use function iter\values;
use function KevinGH\Box\get_phar_signing_algorithms;
use function sort;
use const SORT_STRING;

Expand Down Expand Up @@ -84,7 +82,7 @@ public static function create(Configuration $configuration): self
$configuration->promptForPrivateKey(),
$configuration->getReplacements(),
$configuration->getShebang(),
array_flip(get_phar_signing_algorithms())[$configuration->getSigningAlgorithm()],
$configuration->getSigningAlgorithm()->name,
$configuration->getStubBannerContents(),
$normalizePath($configuration->getStubBannerPath()),
$normalizePath($configuration->getStubPath()),
Expand Down
2 changes: 1 addition & 1 deletion src/Console/Command/Compile.php
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ private static function signPhar(

if (null === $key) {
$box->getPhar()->setSignatureAlgorithm(
$config->getSigningAlgorithm(),
$config->getSigningAlgorithm()->value,
);

return;
Expand Down
75 changes: 75 additions & 0 deletions src/Phar/SigningAlgorithm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);

/*
* This file is part of the box project.
*
* (c) Kevin Herrera <kevin@herrera.io>
* Théo Fidry <theo.fidry@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace KevinGH\Box\Phar;

use Phar;
use UnexpectedValueException;
use function array_keys;
use function array_search;

/**
* The required extension to execute the PHAR now that it is compressed.
*
* This is a tiny wrapper around the PHAR compression algorithm
* to make it a bit more type-safe and convenient to work with.
*
* @private
*/
enum SigningAlgorithm: int
{
case MD5 = Phar::MD5;
case SHA1 = Phar::SHA1;
case SHA256 = Phar::SHA256;
case SHA512 = Phar::SHA512;
case OPENSSL = Phar::OPENSSL;

private const LABELS = [
'MD5' => Phar::MD5,
'SHA1' => Phar::SHA1,
'SHA256' => Phar::SHA256,
'SHA512' => Phar::SHA512,
'OPENSSL' => Phar::OPENSSL,
];

/**
* @return list<string>
*/
public static function getLabels(): array
{
return array_keys(self::LABELS);
}

public static function fromLabel(string $label): self
{
return match ($label) {
'MD5' => self::MD5,
'SHA1' => self::SHA1,
'SHA256' => self::SHA256,
'SHA512' => self::SHA512,
'OPENSSL' => self::OPENSSL,
default => throw new UnexpectedValueException(
sprintf(
'The signing algorithm "%s" is not supported by your current PHAR version.',
$label,
),
),
};
}

public function getLabel(): string
{
return array_search($this, self::LABELS, true);
}
}
5 changes: 3 additions & 2 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ function get_box_version(): string
}

/**
* TODO: to remove once up update PHP-Scoper.
*
* @deprecated since 4.3.0. Use \KevinGH\Box\Phar\CompressionAlgorithm instead.
* @private
*
* @return array<string,int>
Expand All @@ -89,6 +88,8 @@ function get_phar_compression_algorithms(): array
}

/**
* @deprecated Since 4.5.0. Use \KevinGH\Box\Phar\SigningAlgorithm instead.
*
* @private
*
* @return array<string,int>
Expand Down
34 changes: 16 additions & 18 deletions tests/Configuration/ConfigurationSigningTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

use Fidry\FileSystem\FS;
use InvalidArgumentException;
use Phar;
use KevinGH\Box\Phar\SigningAlgorithm;
use UnexpectedValueException;
use function array_unshift;
use function in_array;
use const DIRECTORY_SEPARATOR;
Expand All @@ -33,7 +34,7 @@ class ConfigurationSigningTest extends ConfigurationTestCase
{
public function test_the_default_signing_is_sha1(): void
{
self::assertSame(Phar::SHA1, $this->config->getSigningAlgorithm());
self::assertSame(SigningAlgorithm::SHA1, $this->config->getSigningAlgorithm());

self::assertNull($this->config->getPrivateKeyPath());
self::assertNull($this->config->getPrivateKeyPassphrase());
Expand Down Expand Up @@ -69,7 +70,7 @@ public function test_a_recommendation_is_given_if_the_configured_algorithm_is_th
/**
* @dataProvider passFileFreeSigningAlgorithmProvider
*/
public function test_the_signing_algorithm_can_be_configured(string $algorithm, int $expected): void
public function test_the_signing_algorithm_can_be_configured(string $algorithm, SigningAlgorithm $expected): void
{
$this->setConfig([
'algorithm' => $algorithm,
Expand All @@ -85,18 +86,15 @@ public function test_the_signing_algorithm_can_be_configured(string $algorithm,

public function test_the_signing_algorithm_provided_must_be_valid(): void
{
try {
$this->setConfig([
'algorithm' => 'INVALID',
]);
$this->expectExceptionObject(
new UnexpectedValueException(
'The signing algorithm "INVALID" is not supported by your current PHAR version.',
),
);

self::fail('Expected exception to be thrown.');
} catch (InvalidArgumentException $exception) {
self::assertSame(
'Expected one of: "MD5", "SHA1", "SHA256", "SHA512", "OPENSSL". Got: "INVALID"',
$exception->getMessage(),
);
}
$this->setConfig([
'algorithm' => 'INVALID',
]);
}

public function test_the_openssl_algorithm_requires_a_private_key(): void
Expand Down Expand Up @@ -302,9 +300,9 @@ public function test_the_key_pass_can_be_configured(): void

public static function passFileFreeSigningAlgorithmProvider(): iterable
{
yield ['MD5', Phar::MD5];
yield ['SHA1', Phar::SHA1];
yield ['SHA256', Phar::SHA256];
yield ['SHA512', Phar::SHA512];
yield ['MD5', SigningAlgorithm::MD5];
yield ['SHA1', SigningAlgorithm::SHA1];
yield ['SHA256', SigningAlgorithm::SHA256];
yield ['SHA512', SigningAlgorithm::SHA512];
}
}
3 changes: 2 additions & 1 deletion tests/Configuration/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use KevinGH\Box\Json\JsonValidationException;
use KevinGH\Box\MapFile;
use KevinGH\Box\Phar\CompressionAlgorithm;
use KevinGH\Box\Phar\SigningAlgorithm;
use KevinGH\Box\VarDumperNormalizer;
use Phar;
use RuntimeException;
Expand Down Expand Up @@ -2932,7 +2933,7 @@ public function test_it_can_be_created_with_only_default_values(): void
self::assertNull($this->config->getPrivateKeyPath());
self::assertSame([], $this->config->getReplacements());
self::assertSame('#!/usr/bin/env php', $this->config->getShebang());
self::assertSame(Phar::SHA1, $this->config->getSigningAlgorithm());
self::assertSame(SigningAlgorithm::SHA1, $this->config->getSigningAlgorithm());

$version = self::$version;

Expand Down
Loading

0 comments on commit 3077ab3

Please sign in to comment.