CVE-2026-33486
Description
Roadiz is a polymorphic content management system based on a node system that can handle many types of services. A vulnerability in roadiz/documents prior to versions 2.7.9, 2.6.28, 2.5.44, and 2.3.42 allows an authenticated attacker to read any file on the server's local file system that the web server process has access to, including highly sensitive environment variables, database credentials, and internal configuration files. Versions 2.7.9, 2.6.28, 2.5.44, and 2.3.42 contain a patch.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
roadiz/documentsPackagist | >= 2.7.0, < 2.7.9 | 2.7.9 |
roadiz/documentsPackagist | >= 2.6.0, < 2.6.28 | 2.6.28 |
roadiz/documentsPackagist | >= 2.4.0, < 2.5.44 | 2.5.44 |
roadiz/documentsPackagist | < 2.3.42 | 2.3.42 |
Affected products
1Patches
17904f690a51bfix(security): reject unsafe remote URLs in DownloadedFile::fromUrl method - GHSA-rc55-58f4-687g
2 files changed · +98 −1
lib/Documents/src/DownloadedFile.php+74 −1 modified@@ -66,8 +66,23 @@ public static function sanitizeFilename(?string $string): string public static function fromUrl(string $url, ?string $originalName = null): ?DownloadedFile { try { + if (!self::isSafeRemoteUrl($url)) { + return null; + } + $baseName = static::sanitizeFilename(pathinfo($url, PATHINFO_BASENAME)); - $distantResource = fopen($url, 'r'); + $streamContext = stream_context_create([ + 'http' => [ + 'follow_location' => 0, + 'timeout' => 10, + ], + 'https' => [ + 'follow_location' => 0, + 'timeout' => 10, + ], + ]); + + $distantResource = fopen($url, 'r', false, $streamContext); if (false === $distantResource) { return null; } @@ -81,6 +96,8 @@ public static function fromUrl(string $url, ?string $originalName = null): ?Down throw new \RuntimeException('Unable to open local resource.'); } $result = \stream_copy_to_stream($distantResource, $localResource); + fclose($distantResource); + fclose($localResource); if (false === $result) { throw new \RuntimeException('Unable to copy distant stream to local resource.'); } @@ -107,4 +124,60 @@ public static function fromUrl(string $url, ?string $originalName = null): ?Down return null; } + + private static function isSafeRemoteUrl(string $url): bool + { + $parts = parse_url($url); + if (false === $parts) { + return false; + } + + $scheme = strtolower((string) ($parts['scheme'] ?? '')); + if (!\in_array($scheme, ['http', 'https'], true)) { + return false; + } + + $host = strtolower((string) ($parts['host'] ?? '')); + if ('' === $host) { + return false; + } + if ('localhost' === $host || str_ends_with($host, '.localhost')) { + return false; + } + + $asciiHost = $host; + if (function_exists('idn_to_ascii')) { + $idnHost = idn_to_ascii($host, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46); + if (false !== $idnHost) { + $asciiHost = strtolower($idnHost); + } + } + + if (false !== filter_var($asciiHost, FILTER_VALIDATE_IP)) { + return self::isGlobalIp($asciiHost); + } + + $records = dns_get_record($asciiHost, DNS_A + DNS_AAAA); + if (false === $records || 0 === count($records)) { + return false; + } + + foreach ($records as $record) { + $ip = $record['ip'] ?? $record['ipv6'] ?? null; + if (null === $ip || !self::isGlobalIp($ip)) { + return false; + } + } + + return true; + } + + private static function isGlobalIp(string $ip): bool + { + return false !== filter_var( + $ip, + FILTER_VALIDATE_IP, + FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE + ); + } }
lib/Documents/tests/DownloadedFileTest.php+24 −0 modified@@ -86,4 +86,28 @@ public function sanitizeFilenameProvider(): array ], ]; } + + /** + * @dataProvider blockedUrlProvider + */ + public function testFromUrlRejectsUnsafeUrls(string $url): void + { + $this->assertNull(DownloadedFile::fromUrl($url)); + } + + public function blockedUrlProvider(): array + { + return [ + ['file:///etc/passwd'], + ['file:///app/.env'], + ['file:///app/.env.prod.local'], + ['php://filter/read=convert.base64-encode/resource=/etc/passwd'], + ['http://127.0.0.1/test.jpg'], + ['http://[::1]/test.jpg'], + ['http://192.168.1.10/test.jpg'], + ['https://localhost/test.jpg'], + ['https://sub.localhost/test.jpg'], + ['ftp://example.com/test.jpg'], + ]; + } }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/roadiz/core-bundle-dev-app/commit/7904f690a51b88b1c72c02149ebdf85fa81f19f2nvdPatchWEB
- github.com/roadiz/core-bundle-dev-app/security/advisories/GHSA-rc55-58f4-687gnvdExploitVendor AdvisoryWEB
- github.com/advisories/GHSA-rc55-58f4-687gghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-33486ghsaADVISORY
News mentions
0No linked articles in our index yet.