From 51fdc3bbb80c2c9e34af66a63768b0e458129d05 Mon Sep 17 00:00:00 2001 From: Jonas Elfering Date: Thu, 23 Jul 2026 11:29:01 +0200 Subject: [PATCH 1/3] fix(composer): allow resolving older dompdf versions --- Services/ProjectComposerJsonUpdater.php | 47 +++++++++++ .../ProjectComposerJsonUpdaterTest.php | 83 ++++++++++++++++--- 2 files changed, 119 insertions(+), 11 deletions(-) diff --git a/Services/ProjectComposerJsonUpdater.php b/Services/ProjectComposerJsonUpdater.php index e533787..96c518d 100644 --- a/Services/ProjectComposerJsonUpdater.php +++ b/Services/ProjectComposerJsonUpdater.php @@ -14,6 +14,15 @@ */ class ProjectComposerJsonUpdater { + private const DOMPDF_ADVISORIES = [ + 'PKSA-cv56-2228-pzr6', + 'PKSA-6r8f-nxsb-67bq', + 'PKSA-gh7h-hhy4-byg7', + 'PKSA-mwt3-h9tv-kx78', + 'PKSA-hp6n-n4kz-21wk', + 'PKSA-mckv-s5hg-868k', + ]; + public function __construct(private readonly HttpClientInterface $httpClient) {} public function update(string $file, string $latestVersion): void @@ -58,10 +67,48 @@ public function update(string $file, string $latestVersion): void $composerJson = $this->ensureConflictsRepository($composerJson); $composerJson = $this->configureRepositories($composerJson); + $composerJson = $this->ignoreDompdfAdvisories($composerJson); file_put_contents($file, json_encode($composerJson, \JSON_THROW_ON_ERROR | \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)); } + /** + * @param array $config + * + * @return array + */ + private function ignoreDompdfAdvisories(array $config): array + { + /** @var list|array $ignoredAdvisories */ + $ignoredAdvisories = $config['config']['audit']['ignore'] ?? []; + + if (array_is_list($ignoredAdvisories)) { + $ignoredAdvisoryIds = $ignoredAdvisories; + $ignoredAdvisories = []; + + foreach ($ignoredAdvisoryIds as $ignoredAdvisory) { + if (\is_string($ignoredAdvisory)) { + $ignoredAdvisories[$ignoredAdvisory] = null; + } + } + } + + foreach (self::DOMPDF_ADVISORIES as $advisory) { + if (array_key_exists($advisory, $ignoredAdvisories)) { + continue; + } + + $ignoredAdvisories[$advisory] = [ + 'apply' => 'block', + 'reason' => 'Required to install or update Shopware versions that require dompdf below 3.1.6.', + ]; + } + + $config['config']['audit']['ignore'] = $ignoredAdvisories; + + return $config; + } + private function getVersion(string $latestVersion): string { $nextVersion = Platform::getEnv('SW_RECOVERY_NEXT_VERSION'); diff --git a/Tests/Services/ProjectComposerJsonUpdaterTest.php b/Tests/Services/ProjectComposerJsonUpdaterTest.php index 1da75af..a03fa06 100644 --- a/Tests/Services/ProjectComposerJsonUpdaterTest.php +++ b/Tests/Services/ProjectComposerJsonUpdaterTest.php @@ -43,7 +43,7 @@ public function testUpdate(): void '6.4.18.0' ); - $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); + $composerJson = $this->readComposerJsonWithoutDompdfAdvisories(); static::assertSame( [ @@ -61,6 +61,47 @@ public function testUpdate(): void ); } + public function testUpdateIgnoresDompdfAdvisoriesForDependencyBlocking(): void + { + file_put_contents($this->json, json_encode([ + 'require' => [ + 'shopware/core' => '1.2.3', + ], + 'config' => [ + 'audit' => [ + 'ignore' => ['GHSA-existing-advisory'], + ], + ], + ], \JSON_THROW_ON_ERROR)); + + (new ProjectComposerJsonUpdater(new MockHttpClient([$this->getEmptyVersionsResponse()])))->update( + $this->json, + '6.4.18.0' + ); + + $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); + $ignoredAdvisories = $composerJson['config']['audit']['ignore']; + + static::assertArrayHasKey('GHSA-existing-advisory', $ignoredAdvisories); + static::assertNull($ignoredAdvisories['GHSA-existing-advisory']); + unset($ignoredAdvisories['GHSA-existing-advisory']); + + static::assertSame(array_fill_keys( + [ + 'PKSA-cv56-2228-pzr6', + 'PKSA-6r8f-nxsb-67bq', + 'PKSA-gh7h-hhy4-byg7', + 'PKSA-mwt3-h9tv-kx78', + 'PKSA-hp6n-n4kz-21wk', + 'PKSA-mckv-s5hg-868k', + ], + [ + 'apply' => 'block', + 'reason' => 'Required to install or update Shopware versions that require dompdf below 3.1.6.', + ] + ), $ignoredAdvisories); + } + public function testUpdateWithRC(): void { (new ProjectComposerJsonUpdater(new MockHttpClient([$this->getEmptyVersionsResponse()])))->update( @@ -68,7 +109,7 @@ public function testUpdateWithRC(): void '6.4.18.0-rc1' ); - $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); + $composerJson = $this->readComposerJsonWithoutDompdfAdvisories(); static::assertSame( [ @@ -98,7 +139,7 @@ public function testUpdateWithFixVersion(): void unset($_SERVER['SW_RECOVERY_NEXT_VERSION']); - $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); + $composerJson = $this->readComposerJsonWithoutDompdfAdvisories(); static::assertSame( [ @@ -129,7 +170,7 @@ public function testUpdateWithFixVersionAndBranch(): void unset($_SERVER['SW_RECOVERY_NEXT_VERSION']); - $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); + $composerJson = $this->readComposerJsonWithoutDompdfAdvisories(); static::assertSame( [ @@ -160,7 +201,7 @@ public function testUpdateWithFixVersionAndBranchSame(): void unset($_SERVER['SW_RECOVERY_NEXT_VERSION']); - $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); + $composerJson = $this->readComposerJsonWithoutDompdfAdvisories(); static::assertSame( [ @@ -193,7 +234,7 @@ public function testUpdateWithSymfonyRuntimeRequirement(): void '6.6.0.0' ); - $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); + $composerJson = $this->readComposerJsonWithoutDompdfAdvisories(); static::assertSame( [ @@ -227,7 +268,7 @@ public function testUpdate67RemovesConflict(): void '6.7.0.0' ); - $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); + $composerJson = $this->readComposerJsonWithoutDompdfAdvisories(); static::assertSame( [ @@ -260,7 +301,7 @@ public function testUpdateConflictPackageGetsAdded(): void '6.6.0.0' ); - $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); + $composerJson = $this->readComposerJsonWithoutDompdfAdvisories(); static::assertSame( [ @@ -294,7 +335,7 @@ public function testUpdateConflictPackageGetsAddedMinified(): void '6.2.0.0' ); - $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); + $composerJson = $this->readComposerJsonWithoutDompdfAdvisories(); static::assertSame( [ @@ -328,7 +369,7 @@ public function testUpdateConflictPackageGetsAddedUsesOlderVersionWhenConstraint '6.4.0.0' ); - $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); + $composerJson = $this->readComposerJsonWithoutDompdfAdvisories(); static::assertSame( [ @@ -369,7 +410,7 @@ public function testWithRecoveryRepository(): void unset($_SERVER['SW_RECOVERY_NEXT_VERSION']); - $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); + $composerJson = $this->readComposerJsonWithoutDompdfAdvisories(); static::assertSame( [ @@ -498,6 +539,26 @@ public function testConflictsRepositoryPreservesKeyedMapForm(): void ); } + /** + * @return array + */ + private function readComposerJsonWithoutDompdfAdvisories(): array + { + $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); + + unset($composerJson['config']['audit']['ignore']); + + if ($composerJson['config']['audit'] === []) { + unset($composerJson['config']['audit']); + } + + if ($composerJson['config'] === []) { + unset($composerJson['config']); + } + + return $composerJson; + } + private function getEmptyVersionsResponse(): MockResponse { $json = << Date: Thu, 23 Jul 2026 11:39:03 +0200 Subject: [PATCH 2/3] fix(composer): document ignored dompdf CVEs --- Services/ProjectComposerJsonUpdater.php | 21 ++++++++----- .../ProjectComposerJsonUpdaterTest.php | 30 +++++++++++-------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/Services/ProjectComposerJsonUpdater.php b/Services/ProjectComposerJsonUpdater.php index 96c518d..0c1a734 100644 --- a/Services/ProjectComposerJsonUpdater.php +++ b/Services/ProjectComposerJsonUpdater.php @@ -14,13 +14,18 @@ */ class ProjectComposerJsonUpdater { + /** + * Shopware is not affected because only authenticated administration users can manipulate input rendered by dompdf. + * + * @var array + */ private const DOMPDF_ADVISORIES = [ - 'PKSA-cv56-2228-pzr6', - 'PKSA-6r8f-nxsb-67bq', - 'PKSA-gh7h-hhy4-byg7', - 'PKSA-mwt3-h9tv-kx78', - 'PKSA-hp6n-n4kz-21wk', - 'PKSA-mckv-s5hg-868k', + 'CVE-2026-59943' => 'https://github.com/advisories/GHSA-j8qw-6jw8-r297', + 'CVE-2026-59942' => 'https://github.com/advisories/GHSA-f5gf-2cj8-52g2', + 'CVE-2026-59941' => 'https://github.com/advisories/GHSA-8hg6-c449-896m', + 'CVE-2026-56722' => 'https://github.com/advisories/GHSA-cx96-42px-69fm', + 'CVE-2026-55555' => 'https://github.com/advisories/GHSA-7x2p-4jvh-6384', + 'CVE-2026-55554' => 'https://github.com/advisories/GHSA-wvh6-f5jh-8gw4', ]; public function __construct(private readonly HttpClientInterface $httpClient) {} @@ -93,14 +98,14 @@ private function ignoreDompdfAdvisories(array $config): array } } - foreach (self::DOMPDF_ADVISORIES as $advisory) { + foreach (self::DOMPDF_ADVISORIES as $advisory => $link) { if (array_key_exists($advisory, $ignoredAdvisories)) { continue; } $ignoredAdvisories[$advisory] = [ 'apply' => 'block', - 'reason' => 'Required to install or update Shopware versions that require dompdf below 3.1.6.', + 'reason' => 'Shopware is not affected because only authenticated administration users can manipulate input rendered by dompdf. See ' . $link, ]; } diff --git a/Tests/Services/ProjectComposerJsonUpdaterTest.php b/Tests/Services/ProjectComposerJsonUpdaterTest.php index a03fa06..f0d68fc 100644 --- a/Tests/Services/ProjectComposerJsonUpdaterTest.php +++ b/Tests/Services/ProjectComposerJsonUpdaterTest.php @@ -82,24 +82,28 @@ public function testUpdateIgnoresDompdfAdvisoriesForDependencyBlocking(): void $composerJson = json_decode((string) file_get_contents($this->json), true, 512, \JSON_THROW_ON_ERROR); $ignoredAdvisories = $composerJson['config']['audit']['ignore']; + static::assertIsArray($ignoredAdvisories); static::assertArrayHasKey('GHSA-existing-advisory', $ignoredAdvisories); static::assertNull($ignoredAdvisories['GHSA-existing-advisory']); unset($ignoredAdvisories['GHSA-existing-advisory']); - static::assertSame(array_fill_keys( - [ - 'PKSA-cv56-2228-pzr6', - 'PKSA-6r8f-nxsb-67bq', - 'PKSA-gh7h-hhy4-byg7', - 'PKSA-mwt3-h9tv-kx78', - 'PKSA-hp6n-n4kz-21wk', - 'PKSA-mckv-s5hg-868k', - ], - [ + $expectedAdvisories = [ + 'CVE-2026-59943' => 'https://github.com/advisories/GHSA-j8qw-6jw8-r297', + 'CVE-2026-59942' => 'https://github.com/advisories/GHSA-f5gf-2cj8-52g2', + 'CVE-2026-59941' => 'https://github.com/advisories/GHSA-8hg6-c449-896m', + 'CVE-2026-56722' => 'https://github.com/advisories/GHSA-cx96-42px-69fm', + 'CVE-2026-55555' => 'https://github.com/advisories/GHSA-7x2p-4jvh-6384', + 'CVE-2026-55554' => 'https://github.com/advisories/GHSA-wvh6-f5jh-8gw4', + ]; + + static::assertSame(array_keys($expectedAdvisories), array_keys($ignoredAdvisories)); + + foreach ($expectedAdvisories as $cve => $link) { + static::assertSame([ 'apply' => 'block', - 'reason' => 'Required to install or update Shopware versions that require dompdf below 3.1.6.', - ] - ), $ignoredAdvisories); + 'reason' => 'Shopware is not affected because only authenticated administration users can manipulate input rendered by dompdf. See ' . $link, + ], $ignoredAdvisories[$cve]); + } } public function testUpdateWithRC(): void From 3021b1dfb65749e96759ece1d27ef1dcc1a706e3 Mon Sep 17 00:00:00 2001 From: Jonas Elfering Date: Thu, 23 Jul 2026 13:27:28 +0200 Subject: [PATCH 3/3] refactor(composer): clarify advisory normalization --- Services/ProjectComposerJsonUpdater.php | 26 +++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Services/ProjectComposerJsonUpdater.php b/Services/ProjectComposerJsonUpdater.php index 0c1a734..f780cb6 100644 --- a/Services/ProjectComposerJsonUpdater.php +++ b/Services/ProjectComposerJsonUpdater.php @@ -84,32 +84,34 @@ public function update(string $file, string $latestVersion): void */ private function ignoreDompdfAdvisories(array $config): array { - /** @var list|array $ignoredAdvisories */ - $ignoredAdvisories = $config['config']['audit']['ignore'] ?? []; - - if (array_is_list($ignoredAdvisories)) { - $ignoredAdvisoryIds = $ignoredAdvisories; - $ignoredAdvisories = []; - - foreach ($ignoredAdvisoryIds as $ignoredAdvisory) { + /** @var list|array $rawIgnoredAdvisories */ + $rawIgnoredAdvisories = $config['config']['audit']['ignore'] ?? []; + /** @var array $normalizedIgnoredAdvisories */ + $normalizedIgnoredAdvisories = []; + + // Normalize input so advisories can always be looked up by array key. + if (array_is_list($rawIgnoredAdvisories)) { + foreach ($rawIgnoredAdvisories as $ignoredAdvisory) { if (\is_string($ignoredAdvisory)) { - $ignoredAdvisories[$ignoredAdvisory] = null; + $normalizedIgnoredAdvisories[$ignoredAdvisory] = null; } } + } else { + $normalizedIgnoredAdvisories = $rawIgnoredAdvisories; } foreach (self::DOMPDF_ADVISORIES as $advisory => $link) { - if (array_key_exists($advisory, $ignoredAdvisories)) { + if (array_key_exists($advisory, $normalizedIgnoredAdvisories)) { continue; } - $ignoredAdvisories[$advisory] = [ + $normalizedIgnoredAdvisories[$advisory] = [ 'apply' => 'block', 'reason' => 'Shopware is not affected because only authenticated administration users can manipulate input rendered by dompdf. See ' . $link, ]; } - $config['config']['audit']['ignore'] = $ignoredAdvisories; + $config['config']['audit']['ignore'] = $normalizedIgnoredAdvisories; return $config; }