VYPR
Medium severity6.8NVD Advisory· Published Mar 26, 2026· Updated Mar 31, 2026

CVE-2026-33486

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.

PackageAffected versionsPatched versions
roadiz/documentsPackagist
>= 2.7.0, < 2.7.92.7.9
roadiz/documentsPackagist
>= 2.6.0, < 2.6.282.6.28
roadiz/documentsPackagist
>= 2.4.0, < 2.5.442.5.44
roadiz/documentsPackagist
< 2.3.422.3.42

Affected products

1

Patches

1
7904f690a51b

fix(security): reject unsafe remote URLs in DownloadedFile::fromUrl method - GHSA-rc55-58f4-687g

https://github.com/roadiz/core-bundle-dev-appAmbroise MaupateMar 19, 2026via ghsa
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

News mentions

0

No linked articles in our index yet.