Medium severity5.4NVD Advisory· Published Apr 3, 2026· Updated Apr 7, 2026
CVE-2026-35540
CVE-2026-35540
Description
An issue was discovered in Roundcube Webmail 1.6.0 before 1.6.14. Insufficient Cascading Style Sheets (CSS) sanitization in HTML e-mail messages may lead to SSRF or Information Disclosure, e.g., if stylesheet links point to local network hosts.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
roundcube/roundcubemailPackagist | >= 1.7-beta, < 1.7-rc5 | 1.7-rc5 |
Affected products
1Patches
2579b68eff906Fix SSRF + Information Disclosure via stylesheet links to a local network hosts
7 files changed · +83 −3
CHANGELOG.md+1 −0 modified@@ -16,6 +16,7 @@ This file includes only changes we consider noteworthy for users, admins and plu - Security: Fix remote image blocking bypass via a crafted body background attribute - Security: Fix fixed position mitigation bypass via use of !important - Security: Fix XSS issue in a HTML attachment preview +- Security: Fix SSRF + Information Disclosure via stylesheet links to a local network hosts ## 1.7-rc4
composer.json+1 −0 modified@@ -9,6 +9,7 @@ "guzzlehttp/promises": "^2.0", "league/commonmark": "^2.7", "masterminds/html5": "~2.9.0", + "mlocati/ip-lib": "^1.22.0", "pear/auth_sasl": "~1.2.0", "pear/crypt_gpg": "~1.6.3", "pear/mail_mime": "~1.10.11",
program/actions/mail/index.php+1 −1 modified@@ -1280,7 +1280,7 @@ public static function washtml_link_callback($tag, $attribs, $content, $washtml) if (isset($attrib['href'])) { $attrib['href'] = preg_replace('/[\x00-\x1F]/', '', $attrib['href']); - if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href'])) { + if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href']) && !rcube_utils::is_local_url($attrib['href'])) { $tempurl = 'tmp-' . md5($attrib['href']) . '.css'; $_SESSION['modcssurls'][$tempurl] = $attrib['href']; $attrib['href'] = $rcmail->url([
program/actions/utils/modcss.php+1 −1 modified@@ -48,7 +48,7 @@ public function run($args = []) $ctype = null; try { - $client = rcube::get_instance()->get_http_client(); + $client = rcube::get_instance()->get_http_client(['allow_redirects' => false]); $response = $client->get($realurl); $source = $response->getBody();
program/lib/Roundcube/rcube_utils.php+44 −0 modified@@ -1,5 +1,7 @@ <?php +use IPLib\Factory; + /* +-----------------------------------------------------------------------+ | This file is part of the Roundcube Webmail client | @@ -414,6 +416,48 @@ public static function html_identifier($str, $encode = false) return asciiwords($str, true, '_'); } + /** + * Check if an URL point to a local network location. + * + * @param string $url + * + * @return bool + */ + public static function is_local_url($url) + { + $host = parse_url($url, \PHP_URL_HOST); + + if (is_string($host)) { + // TODO: This is pretty fast, but a single message can contain multiple links + // to the same target, maybe we should do some in-memory caching. + if ($address = Factory::parseAddressString($host = trim($host, '[]'))) { + $nets = [ + '127.0.0.0/8', // loopback + '10.0.0.0/8', // RFC1918 + '172.16.0.0/12', // RFC1918 + '192.168.0.0/16', // RFC1918 + '169.254.0.0/16', // link-local / cloud metadata + '::1/128', + 'fc00::/7', + ]; + + foreach ($nets as $net) { + $range = Factory::parseRangeString($net); + if ($range->contains($address)) { + return true; + } + } + + return false; + } + + // FIXME: Should we accept any non-fqdn hostnames? + return (bool) preg_match('/^localhost(\.localdomain)?$/i', $host); + } + + return false; + } + /** * Replace all css definitions with #container [def] * and remove css-inlined scripting, make position style safe
program/lib/Roundcube/rcube_washtml.php+1 −1 modified@@ -384,7 +384,7 @@ private function wash_uri($uri, $blocked_source = false, $is_image = true) } if (preg_match('/^(http|https|ftp):.+/i', $uri)) { - if (!empty($this->config['allow_remote'])) { + if (!empty($this->config['allow_remote']) || rcube_utils::is_local_url($uri)) { return $uri; }
tests/Framework/UtilsTest.php+34 −0 modified@@ -639,6 +639,40 @@ public function test_file2class() } } + /** + * Test is_local_url() + * + * @dataProvider provide_is_local_url_cases + */ + #[DataProvider('provide_is_local_url_cases')] + public function test_is_local_url($input, $output) + { + $this->assertSame($output, \rcube_utils::is_local_url($input)); + } + + /** + * Test-Cases for is_local_url() test + */ + public static function provide_is_local_url_cases(): iterable + { + return [ + // Local hosts + ['https://127.0.0.1', true], + ['https://10.1.1.1', true], + ['https://172.16.0.1', true], + ['https://192.168.0.100', true], + ['https://169.254.0.200', true], + ['http://[fc00::1]', true], + ['ftp://[::1]:8080', true], + ['//127.0.0.1', true], + ['http://localhost', true], + ['http://localhost.localdomain', true], + // Non-local hosts + ['http://[2001:470::76:0:0:0:2]', false], + ['http://domain.tld', false], + ]; + } + /** * rcube:utils::strtotime() */
27ec6cc9cb25Fix SSRF + Information Disclosure via stylesheet links to a local network hosts
7 files changed · +85 −5
CHANGELOG.md+1 −0 modified@@ -10,6 +10,7 @@ - Security: Fix remote image blocking bypass via a crafted body background attribute - Security: Fix fixed position mitigation bypass via use of !important - Security: Fix XSS issue in a HTML attachment preview +- Security: Fix SSRF + Information Disclosure via stylesheet links to a local network hosts ## Release 1.6.13
composer.json-dist+2 −1 modified@@ -20,7 +20,8 @@ "roundcube/rtf-html-php": "~2.1", "masterminds/html5": "~2.7.0", "bacon/bacon-qr-code": "^2.0.0", - "guzzlehttp/guzzle": "^7.3.0" + "guzzlehttp/guzzle": "^7.3.0", + "mlocati/ip-lib": "^1.22.0" }, "require-dev": { "phpunit/phpunit": "^9"
program/actions/mail/index.php+1 −1 modified@@ -1274,7 +1274,7 @@ public static function washtml_link_callback($tag, $attribs, $content, $washtml) if (isset($attrib['href'])) { $attrib['href'] = preg_replace('/[\x00-\x1F]/', '', $attrib['href']); - if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href'])) { + if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href']) && !rcube_utils::is_local_url($attrib['href'])) { $tempurl = 'tmp-' . md5($attrib['href']) . '.css'; $_SESSION['modcssurls'][$tempurl] = $attrib['href']; $attrib['href'] = $rcmail->url([
program/actions/utils/modcss.php+1 −1 modified@@ -47,7 +47,7 @@ public function run($args = []) $ctype = null; try { - $client = rcube::get_instance()->get_http_client(); + $client = rcube::get_instance()->get_http_client(['allow_redirects' => false]); $response = $client->get($realurl); if (!empty($response)) {
program/lib/Roundcube/rcube_utils.php+45 −1 modified@@ -1,6 +1,8 @@ <?php -/** +use IPLib\Factory; + +/* +-----------------------------------------------------------------------+ | This file is part of the Roundcube Webmail client | | | @@ -419,6 +421,48 @@ public static function html_identifier($str, $encode = false) return asciiwords($str, true, '_'); } + /** + * Check if an URL point to a local network location. + * + * @param string $url + * + * @return bool + */ + public static function is_local_url($url) + { + $host = parse_url($url, \PHP_URL_HOST); + + if (is_string($host)) { + // TODO: This is pretty fast, but a single message can contain multiple links + // to the same target, maybe we should do some in-memory caching. + if ($address = Factory::parseAddressString($host = trim($host, '[]'))) { + $nets = [ + '127.0.0.0/8', // loopback + '10.0.0.0/8', // RFC1918 + '172.16.0.0/12', // RFC1918 + '192.168.0.0/16', // RFC1918 + '169.254.0.0/16', // link-local / cloud metadata + '::1/128', + 'fc00::/7', + ]; + + foreach ($nets as $net) { + $range = Factory::parseRangeString($net); + if ($range->contains($address)) { + return true; + } + } + + return false; + } + + // FIXME: Should we accept any non-fqdn hostnames? + return (bool) preg_match('/^localhost(\.localdomain)?$/i', $host); + } + + return false; + } + /** * Replace all css definitions with #container [def] * and remove css-inlined scripting, make position style safe
program/lib/Roundcube/rcube_washtml.php+1 −1 modified@@ -393,7 +393,7 @@ private function wash_uri($uri, $blocked_source = false, $is_image = true) } if (preg_match('/^(http|https|ftp):.+/i', $uri)) { - if (!empty($this->config['allow_remote'])) { + if (!empty($this->config['allow_remote']) || rcube_utils::is_local_url($uri)) { return $uri; }
tests/Framework/Utils.php+34 −0 modified@@ -556,6 +556,40 @@ function test_file2class() } } + /** + * Test is_local_url() + * + * @dataProvider provide_is_local_url_cases + */ + #[DataProvider('provide_is_local_url_cases')] + public function test_is_local_url($input, $output) + { + $this->assertSame($output, \rcube_utils::is_local_url($input)); + } + + /** + * Test-Cases for is_local_url() test + */ + public static function provide_is_local_url_cases(): iterable + { + return [ + // Local hosts + ['https://127.0.0.1', true], + ['https://10.1.1.1', true], + ['https://172.16.0.1', true], + ['https://192.168.0.100', true], + ['https://169.254.0.200', true], + ['http://[fc00::1]', true], + ['ftp://[::1]:8080', true], + ['//127.0.0.1', true], + ['http://localhost', true], + ['http://localhost.localdomain', true], + // Non-local hosts + ['http://[2001:470::76:0:0:0:2]', false], + ['http://domain.tld', false], + ]; + } + /** * rcube:utils::strtotime() */
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
7- github.com/roundcube/roundcubemail/commit/27ec6cc9cb25e1ef8b4d4ef39ce76d619caa6870nvdPatchWEB
- github.com/roundcube/roundcubemail/commit/579b68eff90650a5c782e153debd66c765648942nvdPatchWEB
- github.com/advisories/GHSA-vxg2-hhgr-37fxghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-35540ghsaADVISORY
- roundcube.net/news/2026/03/18/security-updates-1.7-rc5-1.6.14-1.5.14nvdVendor AdvisoryWEB
- github.com/roundcube/roundcubemail/releases/tag/1.6.14nvdRelease NotesWEB
- github.com/roundcube/roundcubemail/releases/tag/1.7-rc5nvdRelease NotesWEB
News mentions
0No linked articles in our index yet.