From 942e4220ed6240b8ceaa0b088a97b372c45484b6 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Mon, 19 Oct 2026 18:26:35 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20RemoteFileFetcher::fetch()=20pr=C3=BCft?= =?UTF-8?q?=20die=20URL=20selbst;=20Doppelpr=C3=BCfung=20im=20Schriften-Up?= =?UTF-8?q?load=20entf=C3=A4llt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5.5 --- src/Integrations/Divi/Design/DiviFontsUploadAbility.php | 6 ------ src/Integrations/Media/RemoteFileFetcher.php | 6 +++++- .../Divi/Design/DiviFontsUploadAbilityTest.php | 5 ++++- tests/Unit/Integrations/Media/RemoteFileFetcherTest.php | 9 +++++++++ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Integrations/Divi/Design/DiviFontsUploadAbility.php b/src/Integrations/Divi/Design/DiviFontsUploadAbility.php index 0afd34c..baef4fa 100644 --- a/src/Integrations/Divi/Design/DiviFontsUploadAbility.php +++ b/src/Integrations/Divi/Design/DiviFontsUploadAbility.php @@ -325,12 +325,6 @@ final class DiviFontsUploadAbility implements Ability, RequiresDiviFeatures $problems = []; $unreachable = false; foreach ($urls as $format => $url) { - // fetch() does not check the URL itself: a refused URL is never requested (same answer as a failed one). - if (! $this->fetcher->allows($url)) { - $problems['files.' . $format] = 'nicht abrufbar'; - $unreachable = true; - continue; - } $tmp = (string) wp_tempnam('lit-mcp-font'); if ($tmp === '') { return Errors::writeFailed('Die temporäre Datei konnte nicht angelegt werden.'); diff --git a/src/Integrations/Media/RemoteFileFetcher.php b/src/Integrations/Media/RemoteFileFetcher.php index 00e76d2..3bb67b3 100644 --- a/src/Integrations/Media/RemoteFileFetcher.php +++ b/src/Integrations/Media/RemoteFileFetcher.php @@ -42,7 +42,7 @@ class RemoteFileFetcher } /** - * Streams $url into $tmp; the caller checks allows() first and the size of $tmp afterwards (a body above + * Streams $url into $tmp; fetch() checks the URL itself (allows()), the caller checks the size of $tmp afterwards (a body above * $maxBytes is cut off at $maxBytes + 1 bytes). * * @param string $context Names the download in the failure log, e.g. "media import". @@ -50,6 +50,10 @@ class RemoteFileFetcher */ public function fetch(string $url, string $tmp, int $maxBytes, string $context): ?array { + if (! $this->allows($url)) { + return $this->failed($context, $url, 'URL refused'); + } + // IPv4 only, for this request only: wp_http_validate_url() checks the IPv4 address // (gethostbyname), so an AAAA record must not lead cURL to an unchecked IPv6 target. $ipv4 = static function ($handle): void { diff --git a/tests/Unit/Integrations/Divi/Design/DiviFontsUploadAbilityTest.php b/tests/Unit/Integrations/Divi/Design/DiviFontsUploadAbilityTest.php index c571dad..d058d02 100644 --- a/tests/Unit/Integrations/Divi/Design/DiviFontsUploadAbilityTest.php +++ b/tests/Unit/Integrations/Divi/Design/DiviFontsUploadAbilityTest.php @@ -118,6 +118,9 @@ final class DiviFontsUploadAbilityTest extends TestCase /** The fake fetcher's download: writes the body of $url into $tmp. */ public function fakeFetch(string $url, string $tmp, int $maxBytes, string $context): ?array { + if (! $this->allowed($url)) { // like the real fetch(): a refused URL is never requested + return null; + } $this->fetches[] = ['url' => $url, 'tmp' => $tmp, 'max' => $maxBytes, 'context' => $context]; $body = $this->bodies[$url] ?? null; if ($body === null) { @@ -258,7 +261,7 @@ final class DiviFontsUploadAbilityTest extends TestCase self::assertToolError($result, 'font_fetch_failed'); self::assertSame(['files.ttf' => 'nicht abrufbar'], self::errorFields($result)); - self::assertSame([self::WOFF2_URL], array_column($this->fetches, 'url'), 'fetch() skipped the refused URL'); + self::assertSame([self::WOFF2_URL], array_column($this->fetches, 'url'), 'the refused URL was never requested'); $this->assertTempFilesRemoved(); } diff --git a/tests/Unit/Integrations/Media/RemoteFileFetcherTest.php b/tests/Unit/Integrations/Media/RemoteFileFetcherTest.php index 5b0d595..74becf6 100644 --- a/tests/Unit/Integrations/Media/RemoteFileFetcherTest.php +++ b/tests/Unit/Integrations/Media/RemoteFileFetcherTest.php @@ -84,6 +84,15 @@ final class RemoteFileFetcherTest extends TestCase self::assertSame('wOF2DATA', file_get_contents($this->tmp)); } + public function testARefusedUrlIsNullAndNeverRequested(): void + { + Functions\expect('wp_safe_remote_get')->never(); + + self::assertNull($this->fetcher(['10.0.0.5'])->fetch('https://intern.example.com/x.woff2', $this->tmp, 2048, 'x')); + self::assertNull($this->fetcher()->fetch('file:///etc/passwd', $this->tmp, 2048, 'x')); + self::assertStringContainsString('URL refused', (string) file_get_contents($this->errorLog)); + } + public function testAFailedRequestIsNullAndOnlyTheLogNamesTheReasonAndTheContext(): void { Functions\when('wp_safe_remote_get')->justReturn(new WP_Error('http_request_failed', 'Connection refused'));