Skip to content

Commit 101086e

Browse files
Merge branch '6.4' into 7.3
* 6.4: Fix inline var annotations [Console][Table] Don't split grapheme clusters [FrameworkBundle] Fix block type from `OK` to `ERROR` when local vault is disabled in `SecretsGenerateKeysCommand` [FrameworkBundle] Add tests for `secrets:decrypt-to-local`, `encrypt-from-local`, and `generate-keys` commands Reflection*::setAccessible() has no effect as of PHP 8.1
2 parents 1df3a3d + b5187b8 commit 101086e

File tree

18 files changed

+318
-24
lines changed

18 files changed

+318
-24
lines changed

src/Symfony/Bridge/PhpUnit/CoverageListener.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ public function startTest(Test $test): void
8686
private function addCoversForClassToAnnotationCache(Test $test, array $covers): void
8787
{
8888
$r = new \ReflectionProperty(TestUtil::class, 'annotationCache');
89-
$r->setAccessible(true);
89+
if (\PHP_VERSION_ID < 80100) {
90+
$r->setAccessible(true);
91+
}
9092

9193
$cache = $r->getValue();
9294
$cache = array_replace_recursive($cache, [
@@ -103,7 +105,9 @@ private function addCoversForDocBlockInsideRegistry(Test $test, array $covers):
103105
$docBlock = Registry::getInstance()->forClassName(\get_class($test));
104106

105107
$symbolAnnotations = new \ReflectionProperty($docBlock, 'symbolAnnotations');
106-
$symbolAnnotations->setAccessible(true);
108+
if (\PHP_VERSION_ID < 80100) {
109+
$symbolAnnotations->setAccessible(true);
110+
}
107111

108112
// Exclude internal classes; PHPUnit 9.1+ is picky about tests covering, say, a \RuntimeException
109113
$covers = array_filter($covers, function (string $class) {

src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,9 @@ public function toString(): string
389389
{
390390
$exception = new \Exception($this->message);
391391
$reflection = new \ReflectionProperty($exception, 'trace');
392-
$reflection->setAccessible(true);
392+
if (\PHP_VERSION_ID < 80100) {
393+
$reflection->setAccessible(true);
394+
}
393395
$reflection->setValue($exception, $this->trace);
394396

395397
return ($this->originatesFromAnObject() ? 'deprecation triggered by '.$this->originatingClass().'::'.$this->originatingMethod().":\n" : '')

src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,9 @@ private function willBeIsolated(TestCase $test): bool
357357
}
358358

359359
$r = new \ReflectionProperty($test, 'runTestInSeparateProcess');
360-
$r->setAccessible(true);
360+
if (\PHP_VERSION_ID < 80100) {
361+
$r->setAccessible(true);
362+
}
361363

362364
return $r->getValue($test) ?? false;
363365
}

src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,9 @@ public static function setUpBeforeClass(): void
275275
$loader = require $v.'/autoload.php';
276276
$reflection = new \ReflectionClass($loader);
277277
$prop = $reflection->getProperty('prefixDirsPsr4');
278-
$prop->setAccessible(true);
278+
if (\PHP_VERSION_ID < 80100) {
279+
$prop->setAccessible(true);
280+
}
279281
$currentValue = $prop->getValue($loader);
280282
self::$prefixDirsPsr4[] = [$prop, $loader, $currentValue];
281283
$currentValue['Symfony\\Bridge\\PhpUnit\\'] = [realpath(__DIR__.'/../..')];

src/Symfony/Bundle/FrameworkBundle/Command/SecretsGenerateKeysCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
6363
$vault = $input->getOption('local') ? $this->localVault : $this->vault;
6464

6565
if (null === $vault) {
66-
$io->success('The local vault is disabled.');
66+
$io->error('The local vault is disabled.');
6767

6868
return 1;
6969
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Command;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bundle\FrameworkBundle\Command\SecretsDecryptToLocalCommand;
16+
use Symfony\Bundle\FrameworkBundle\Secrets\SodiumVault;
17+
use Symfony\Component\Console\Tester\CommandTester;
18+
use Symfony\Component\Filesystem\Filesystem;
19+
20+
/**
21+
* @requires extension sodium
22+
*/
23+
class SecretsDecryptToLocalCommandTest extends TestCase
24+
{
25+
private string $mainDir;
26+
private string $localDir;
27+
28+
protected function setUp(): void
29+
{
30+
$this->mainDir = sys_get_temp_dir().'/sf_secrets/main/';
31+
$this->localDir = sys_get_temp_dir().'/sf_secrets/local/';
32+
33+
$fs = new Filesystem();
34+
$fs->remove([$this->mainDir, $this->localDir]);
35+
36+
$mainVault = new SodiumVault($this->mainDir);
37+
$mainVault->generateKeys();
38+
$mainVault->seal('FOO_SECRET', 'super_secret_value');
39+
40+
$localVault = new SodiumVault($this->localDir);
41+
$localVault->generateKeys();
42+
}
43+
44+
protected function tearDown(): void
45+
{
46+
(new Filesystem())->remove([$this->mainDir, $this->localDir]);
47+
}
48+
49+
public function testSecretsAreDecryptedAndStoredInLocalVault()
50+
{
51+
$mainVault = new SodiumVault($this->mainDir);
52+
$localVault = new SodiumVault($this->localDir);
53+
$tester = new CommandTester(new SecretsDecryptToLocalCommand($mainVault, $localVault));
54+
55+
$this->assertSame(0, $tester->execute([]));
56+
$this->assertStringContainsString('1 secret found in the vault.', $tester->getDisplay());
57+
$this->assertStringContainsString('Secret "FOO_SECRET" encrypted', $tester->getDisplay());
58+
59+
$this->assertArrayHasKey('FOO_SECRET', $localVault->list(true));
60+
$this->assertSame('super_secret_value', $localVault->reveal('FOO_SECRET'));
61+
}
62+
63+
public function testExistingLocalSecretsAreSkippedWithoutForce()
64+
{
65+
$mainVault = new SodiumVault($this->mainDir);
66+
$localVault = new SodiumVault($this->localDir);
67+
$localVault->seal('FOO_SECRET', 'old_value');
68+
$tester = new CommandTester(new SecretsDecryptToLocalCommand($mainVault, $localVault));
69+
70+
$this->assertSame(0, $tester->execute([]));
71+
$this->assertStringContainsString('1 secret is already overridden in the local vault and will be skipped.', $tester->getDisplay());
72+
$this->assertSame('old_value', $localVault->reveal('FOO_SECRET'));
73+
}
74+
75+
public function testForceOptionOverridesLocalSecrets()
76+
{
77+
$mainVault = new SodiumVault($this->mainDir);
78+
$localVault = new SodiumVault($this->localDir);
79+
$localVault->seal('FOO_SECRET', 'old_value');
80+
$tester = new CommandTester(new SecretsDecryptToLocalCommand($mainVault, $localVault));
81+
82+
$this->assertSame(0, $tester->execute(['--force' => true]));
83+
$this->assertStringContainsString('Secret "FOO_SECRET" encrypted', $tester->getDisplay());
84+
$this->assertSame('super_secret_value', $localVault->reveal('FOO_SECRET'));
85+
}
86+
87+
public function testFailsGracefullyWhenLocalVaultIsDisabled()
88+
{
89+
$mainVault = new SodiumVault($this->mainDir);
90+
$tester = new CommandTester(new SecretsDecryptToLocalCommand($mainVault, null));
91+
92+
$this->assertSame(1, $tester->execute([]));
93+
$this->assertStringContainsString('The local vault is disabled.', $tester->getDisplay());
94+
}
95+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Command;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bundle\FrameworkBundle\Command\SecretsEncryptFromLocalCommand;
16+
use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
17+
use Symfony\Bundle\FrameworkBundle\Secrets\SodiumVault;
18+
use Symfony\Component\Console\Tester\CommandTester;
19+
use Symfony\Component\Filesystem\Filesystem;
20+
21+
/**
22+
* @requires extension sodium
23+
*/
24+
class SecretsEncryptFromLocalCommandTest extends TestCase
25+
{
26+
private string $vaultDir;
27+
private string $localVaultDir;
28+
private Filesystem $fs;
29+
30+
protected function setUp(): void
31+
{
32+
$this->vaultDir = sys_get_temp_dir().'/sf_secrets/vault_'.uniqid();
33+
$this->localVaultDir = sys_get_temp_dir().'/sf_secrets/local_'.uniqid();
34+
$this->fs = new Filesystem();
35+
$this->fs->remove([$this->vaultDir, $this->localVaultDir]);
36+
}
37+
38+
protected function tearDown(): void
39+
{
40+
$this->fs->remove([$this->vaultDir, $this->localVaultDir]);
41+
}
42+
43+
public function testFailsWhenLocalVaultIsDisabled()
44+
{
45+
$vault = $this->createMock(AbstractVault::class);
46+
$command = new SecretsEncryptFromLocalCommand($vault, null);
47+
$tester = new CommandTester($command);
48+
49+
$this->assertSame(1, $tester->execute([]));
50+
$this->assertStringContainsString('The local vault is disabled.', $tester->getDisplay());
51+
}
52+
53+
public function testEncryptsLocalOverrides()
54+
{
55+
$vault = new SodiumVault($this->vaultDir);
56+
$vault->generateKeys();
57+
58+
$localVault = new SodiumVault($this->localVaultDir);
59+
$localVault->generateKeys();
60+
61+
$vault->seal('MY_SECRET', 'prod-value');
62+
$localVault->seal('MY_SECRET', 'local-value');
63+
64+
$command = new SecretsEncryptFromLocalCommand($vault, $localVault);
65+
$tester = new CommandTester($command);
66+
67+
$exitCode = $tester->execute([]);
68+
$this->assertSame(0, $exitCode);
69+
70+
$revealed = $vault->reveal('MY_SECRET');
71+
$this->assertSame('local-value', $revealed);
72+
}
73+
74+
public function testDoesNotSealIfSameValue()
75+
{
76+
$vault = new SodiumVault($this->vaultDir);
77+
$vault->generateKeys();
78+
79+
$localVault = new SodiumVault($this->localVaultDir);
80+
$localVault->generateKeys();
81+
82+
$vault->seal('SHARED_SECRET', 'same-value');
83+
$localVault->seal('SHARED_SECRET', 'same-value');
84+
85+
$command = new SecretsEncryptFromLocalCommand($vault, $localVault);
86+
$tester = new CommandTester($command);
87+
88+
$exitCode = $tester->execute([]);
89+
$this->assertSame(0, $exitCode);
90+
91+
$revealed = $vault->reveal('SHARED_SECRET');
92+
$this->assertSame('same-value', $revealed);
93+
}
94+
95+
public function testFailsIfLocalSecretIsMissing()
96+
{
97+
$vault = new SodiumVault($this->vaultDir);
98+
$vault->generateKeys();
99+
100+
$localVault = new SodiumVault($this->localVaultDir);
101+
$localVault->generateKeys();
102+
103+
$vault->seal('MISSING_IN_LOCAL', 'prod-only');
104+
105+
$command = new SecretsEncryptFromLocalCommand($vault, $localVault);
106+
$tester = new CommandTester($command);
107+
108+
$this->assertSame(1, $tester->execute([]));
109+
$this->assertStringContainsString('Secret "MISSING_IN_LOCAL" not found', $tester->getDisplay());
110+
}
111+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Command;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bundle\FrameworkBundle\Command\SecretsGenerateKeysCommand;
16+
use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
17+
use Symfony\Bundle\FrameworkBundle\Secrets\SodiumVault;
18+
use Symfony\Component\Console\Tester\CommandTester;
19+
use Symfony\Component\Filesystem\Filesystem;
20+
21+
/**
22+
* @requires extension sodium
23+
*/
24+
class SecretsGenerateKeysCommandTest extends TestCase
25+
{
26+
private string $secretsDir;
27+
private const ENC_KEY_FILE = 'test.encrypt.public.php';
28+
private const DEC_KEY_FILE = 'test.decrypt.private.php';
29+
30+
protected function setUp(): void
31+
{
32+
$this->secretsDir = sys_get_temp_dir().'/sf_secrets/test/';
33+
(new Filesystem())->remove($this->secretsDir);
34+
}
35+
36+
protected function tearDown(): void
37+
{
38+
(new Filesystem())->remove($this->secretsDir);
39+
}
40+
41+
public function testItGeneratesSodiumKeys()
42+
{
43+
$vault = new SodiumVault($this->secretsDir);
44+
$tester = new CommandTester(new SecretsGenerateKeysCommand($vault));
45+
46+
$this->assertSame(0, $tester->execute([]));
47+
$this->assertKeysExistAndReadable();
48+
}
49+
50+
public function testItRotatesSodiumKeysWhenRequested()
51+
{
52+
$vault = new SodiumVault($this->secretsDir);
53+
$tester = new CommandTester(new SecretsGenerateKeysCommand($vault));
54+
55+
$this->assertSame(0, $tester->execute(['--rotate' => true]));
56+
$this->assertKeysExistAndReadable();
57+
}
58+
59+
public function testItFailsGracefullyWhenLocalVaultIsDisabled()
60+
{
61+
$vault = $this->createMock(AbstractVault::class);
62+
$tester = new CommandTester(new SecretsGenerateKeysCommand($vault));
63+
64+
$this->assertSame(1, $tester->execute(['--local' => true]));
65+
$this->assertStringContainsString('The local vault is disabled.', $tester->getDisplay());
66+
}
67+
68+
public function testFailsWhenKeysAlreadyExistAndRotateNotPassed()
69+
{
70+
$vault = new SodiumVault($this->secretsDir);
71+
$vault->generateKeys();
72+
73+
$command = new SecretsGenerateKeysCommand($vault);
74+
$tester = new CommandTester($command);
75+
76+
$this->assertSame(1, $tester->execute([]));
77+
$this->assertStringContainsString('Sodium keys already exist at', $tester->getDisplay());
78+
}
79+
80+
private function assertKeysExistAndReadable(): void
81+
{
82+
$encPath = $this->secretsDir.'/'.self::ENC_KEY_FILE;
83+
$decPath = $this->secretsDir.'/'.self::DEC_KEY_FILE;
84+
85+
$this->assertFileExists($encPath, 'Encryption key file does not exist.');
86+
$this->assertFileExists($decPath, 'Decryption key file does not exist.');
87+
$this->assertNotFalse(@file_get_contents($encPath), 'Encryption key file is not readable.');
88+
$this->assertNotFalse(@file_get_contents($decPath), 'Decryption key file is not readable.');
89+
}
90+
}

src/Symfony/Component/AssetMapper/ImportMap/Resolver/JsDelivrEsmResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ public function downloadPackages(array $importMapEntries, ?callable $progressCal
184184
$errors = [];
185185
$contents = [];
186186
$extraFileResponses = [];
187+
/** @var ImportMapEntry $entry */
187188
foreach ($responses as $package => [$response, $entry]) {
188189
if (200 !== $response->getStatusCode()) {
189190
$errors[] = [$package, $response];
@@ -196,7 +197,6 @@ public function downloadPackages(array $importMapEntries, ?callable $progressCal
196197

197198
$dependencies = [];
198199
$extraFiles = [];
199-
/** @var ImportMapEntry $entry */
200200
$contents[$package] = [
201201
'content' => $this->makeImportsBare($response->getContent(), $dependencies, $extraFiles, $entry->type, $entry->getPackagePathString()),
202202
'dependencies' => $dependencies,

src/Symfony/Component/Console/Formatter/OutputFormatter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,6 @@ private function addLineBreaks(string $text, int $width): string
275275
{
276276
$encoding = mb_detect_encoding($text, null, true) ?: 'UTF-8';
277277

278-
return b($text)->toCodePointString($encoding)->wordwrap($width, "\n", true)->toByteString($encoding);
278+
return b($text)->toUnicodeString($encoding)->wordwrap($width, "\n", true)->toByteString($encoding);
279279
}
280280
}

0 commit comments

Comments
 (0)