Medium severity5.3NVD Advisory· Published Apr 3, 2026· Updated Apr 7, 2026
CVE-2026-35543
CVE-2026-35543
Description
An issue was discovered in Roundcube Webmail before 1.5.14 and 1.6.14. The remote image blocking feature can be bypassed via SVG content (with animate attributes) in an e-mail message. This may lead to information disclosure or access-control bypass.
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
31a63e01542bfFix remote image blocking bypass via various SVG animate attributes
3 files changed · +44 −9
CHANGELOG.md+1 −0 modified@@ -5,6 +5,7 @@ - Security: Fix pre-auth arbitrary file write via unsafe deserialization in redis/memcache session handler - Security: Fix bug where a password could get changed without providing the old password - Security: Fix IMAP Injection + CSRF bypass in mail search +- Security: Fix remote image blocking bypass via various SVG animate attributes ## Release 1.5.13
program/lib/Roundcube/rcube_washtml.php+29 −9 modified@@ -501,22 +501,22 @@ private function is_funciri_attribute($tag, $attr) * Do it in case-insensitive manner. * * @param DOMElement $node The element - * @param string $attr_name The attribute name - * @param string $attr_value The attribute value to find + * @param string $attr_value The attribute value to find (regexp) * * @return bool True if the specified attribute exists and has the expected value */ private static function attribute_value($node, $attr_name, $attr_value) { $attr_name = strtolower($attr_name); - $attr_value = strtolower($attr_value); foreach ($node->attributes as $name => $attr) { if (strtolower($name) === $attr_name) { + $val = trim($attr->nodeValue); // Read the attribute name, remove the namespace (e.g. xlink:href => href) - $val = strtolower(trim($attr->nodeValue)); - $val = trim(preg_replace('/^.*:/', '', $val)); - if ($attr_value === $val) { + if ($attr_name === 'attributename') { + $val = trim(preg_replace('/^.*:/', '', $val)); + } + if (preg_match($attr_value, $val)) { return true; } } @@ -525,6 +525,27 @@ private static function attribute_value($node, $attr_name, $attr_value) return false; } + /** + * Check if the node is an insecure element + * + * @param \DOMElement $node + */ + private static function is_insecure_tag($node) + { + $tagName = strtolower($node->nodeName); + + if (!in_array($tagName, ['animate', 'animatecolor', 'set', 'animatetransform'])) { + return false; + } + + if (self::attribute_value($node, 'attributeName', '/^href$/i')) { + return true; + } + + return self::attribute_value($node, 'attributeName', '/^(mask|cursor)$/i') + && self::attribute_value($node, 'values', '/url\(/i'); + } + /** * The main loop that recurse on a node tree. * It output only allowed tags with allowed attributes and allowed inline styles @@ -576,10 +597,9 @@ private function dumpHtml($node, $level = 20) $node->setAttribute('href', (string) $uri); } - else if (in_array($tagName, ['animate', 'animatecolor', 'set', 'animatetransform']) - && self::attribute_value($node, 'attributename', 'href') - ) { + else if (self::is_insecure_tag($node)) { // Insecure svg tags + // TODO: We really should use wash_attribs()/wash_uri() for these cases if ($this->config['add_comments']) { $dump .= "<!-- {$tagName} blocked -->"; }
tests/Framework/Washtml.php+14 −0 modified@@ -480,6 +480,20 @@ function data_wash_svg_tests() '<html><svg><defs><filter><feImage xlink:href="http://external.site"/></filter></defs></html>', '<svg xmlns="http://www.w3.org/1999/xhtml"><defs><filter><feImage x-washed="xlink:href"></feImage></filter></defs></svg>', ], + [ + '<svg><animate attributeName="mask" values="url(https://external.site)" fill="freeze" dur="0.1s" /></svg>', + '<svg><!-- animate blocked --></svg>', + ], + [ + '<svg><animate attributeName="mask" values="none;url(https://external.site);url(https://external.site)"' + . ' repeatCount="indefinite" dur="1s" /></svg>', + '<svg><!-- animate blocked --></svg>', + ], + [ + '<svg><animate attributeName="cursor" attributeType="CSS" values="url(https://external.site),auto"' + . ' feel="freeze" dur="1s" /></svg>', + '<svg><!-- animate blocked --></svg>', + ], ]; }
39471343ee08Fix remote image blocking bypass via various SVG animate attributes
3 files changed · +44 −9
CHANGELOG.md+1 −0 modified@@ -6,6 +6,7 @@ - Security: Fix pre-auth arbitrary file write via unsafe deserialization in redis/memcache session handler - Security: Fix bug where a password could get changed without providing the old password - Security: Fix IMAP Injection + CSRF bypass in mail search +- Security: Fix remote image blocking bypass via various SVG animate attributes ## Release 1.6.13
program/lib/Roundcube/rcube_washtml.php+29 −9 modified@@ -504,22 +504,22 @@ private function is_funciri_attribute($tag, $attr) * Do it in case-insensitive manner. * * @param DOMElement $node The element - * @param string $attr_name The attribute name - * @param string $attr_value The attribute value to find + * @param string $attr_value The attribute value to find (regexp) * * @return bool True if the specified attribute exists and has the expected value */ private static function attribute_value($node, $attr_name, $attr_value) { $attr_name = strtolower($attr_name); - $attr_value = strtolower($attr_value); foreach ($node->attributes as $name => $attr) { if (strtolower($name) === $attr_name) { + $val = trim($attr->nodeValue); // Read the attribute name, remove the namespace (e.g. xlink:href => href) - $val = strtolower(trim($attr->nodeValue)); - $val = trim(preg_replace('/^.*:/', '', $val)); - if ($attr_value === $val) { + if ($attr_name === 'attributename') { + $val = trim(preg_replace('/^.*:/', '', $val)); + } + if (preg_match($attr_value, $val)) { return true; } } @@ -528,6 +528,27 @@ private static function attribute_value($node, $attr_name, $attr_value) return false; } + /** + * Check if the node is an insecure element + * + * @param \DOMElement $node + */ + private static function is_insecure_tag($node) + { + $tagName = strtolower($node->nodeName); + + if (!in_array($tagName, ['animate', 'animatecolor', 'set', 'animatetransform'])) { + return false; + } + + if (self::attribute_value($node, 'attributeName', '/^href$/i')) { + return true; + } + + return self::attribute_value($node, 'attributeName', '/^(mask|cursor)$/i') + && self::attribute_value($node, 'values', '/url\(/i'); + } + /** * The main loop that recurse on a node tree. * It output only allowed tags with allowed attributes and allowed inline styles @@ -579,10 +600,9 @@ private function dumpHtml($node, $level = 20) $node->setAttribute('href', (string) $uri); } - else if (in_array($tagName, ['animate', 'animatecolor', 'set', 'animatetransform']) - && self::attribute_value($node, 'attributename', 'href') - ) { + else if (self::is_insecure_tag($node)) { // Insecure svg tags + // TODO: We really should use wash_attribs()/wash_uri() for these cases if ($this->config['add_comments']) { $dump .= "<!-- {$tagName} blocked -->"; }
tests/Framework/Washtml.php+14 −0 modified@@ -500,6 +500,20 @@ function data_wash_svg_tests() '<html><svg><defs><filter><feImage xlink:href="http://external.site"/></filter></defs></html>', '<svg><defs><filter><feImage x-washed="xlink:href"></feImage></filter></defs></svg>', ], + [ + '<svg><animate attributeName="mask" values="url(https://external.site)" fill="freeze" dur="0.1s" /></svg>', + '<svg><!-- animate blocked --></svg>', + ], + [ + '<svg><animate attributeName="mask" values="none;url(https://external.site);url(https://external.site)"' + . ' repeatCount="indefinite" dur="1s" /></svg>', + '<svg><!-- animate blocked --></svg>', + ], + [ + '<svg><animate attributeName="cursor" attributeType="CSS" values="url(https://external.site),auto"' + . ' feel="freeze" dur="1s" /></svg>', + '<svg><!-- animate blocked --></svg>', + ], ]; }
82ab5eca7b33Fix remote image blocking bypass via various SVG animate attributes
3 files changed · +44 −8
CHANGELOG.md+1 −0 modified@@ -12,6 +12,7 @@ This file includes only changes we consider noteworthy for users, admins and plu - Security: Fix pre-auth arbitrary file write via unsafe deserialization in redis/memcache session handler - Security: Fix bug where a password could get changed without providing the old password - Security: Fix IMAP Injection + CSRF bypass in mail search +- Security: Fix remote image blocking bypass via various SVG animate attributes ## 1.7-rc4
program/lib/Roundcube/rcube_washtml.php+29 −8 modified@@ -498,21 +498,22 @@ private function is_funciri_attribute($tag, $attr) * * @param \DOMElement $node The element * @param string $attr_name The attribute name - * @param string $attr_value The attribute value to find + * @param string $attr_value The attribute value to find (regexp) * * @return bool True if the specified attribute exists and has the expected value */ private static function attribute_value($node, $attr_name, $attr_value) { $attr_name = strtolower($attr_name); - $attr_value = strtolower($attr_value); foreach ($node->attributes as $name => $attr) { if (strtolower($name) === $attr_name) { + $val = trim($attr->nodeValue); // Read the attribute name, remove the namespace (e.g. xlink:href => href) - $val = strtolower(trim($attr->nodeValue)); - $val = trim(preg_replace('/^.*:/', '', $val)); - if ($attr_value === $val) { + if ($attr_name === 'attributename') { + $val = trim(preg_replace('/^.*:/', '', $val)); + } + if (preg_match($attr_value, $val)) { return true; } } @@ -521,6 +522,27 @@ private static function attribute_value($node, $attr_name, $attr_value) return false; } + /** + * Check if the node is an insecure element + * + * @param \DOMElement $node + */ + private static function is_insecure_tag($node) + { + $tagName = strtolower($node->nodeName); + + if (!in_array($tagName, ['animate', 'animatecolor', 'set', 'animatetransform'])) { + return false; + } + + if (self::attribute_value($node, 'attributeName', '/^href$/i')) { + return true; + } + + return self::attribute_value($node, 'attributeName', '/^(mask|cursor)$/i') + && self::attribute_value($node, 'values', '/url\(/i'); + } + /** * The main loop that recurse on a node tree. * It output only allowed tags with allowed attributes and allowed inline styles @@ -570,10 +592,9 @@ private function dumpHtml($node, $level = 20) } $node->setAttribute('href', (string) $uri); - } elseif (in_array($tagName, ['animate', 'animatecolor', 'set', 'animatetransform']) - && self::attribute_value($node, 'attributename', 'href') - ) { + } elseif (self::is_insecure_tag($node)) { // Insecure svg tags + // TODO: We really should use wash_attribs()/wash_uri() for these cases if ($this->config['add_comments']) { $dump .= "<!-- {$tagName} blocked -->"; }
tests/Framework/WashtmlTest.php+14 −0 modified@@ -517,6 +517,20 @@ public static function provide_wash_svg_tests_cases(): iterable '<html><svg><defs><filter><feImage xlink:href="http://external.site"/></filter></defs></html>', '<svg><defs><filter><feimage x-washed="xlink:href"></feimage></filter></defs></svg>', ], + [ + '<svg><animate attributeName="mask" values="url(https://external.site)" fill="freeze" dur="0.1s" /></svg>', + '<svg><!-- animate blocked --></svg>', + ], + [ + '<svg><animate attributeName="mask" values="none;url(https://external.site);url(https://external.site)"' + . ' repeatCount="indefinite" dur="1s" /></svg>', + '<svg><!-- animate blocked --></svg>', + ], + [ + '<svg><animate attributeName="cursor" attributeType="CSS" values="url(https://external.site),auto"' + . ' feel="freeze" dur="1s" /></svg>', + '<svg><!-- animate blocked --></svg>', + ], ]; }
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
9- github.com/roundcube/roundcubemail/commit/1a63e01542bff42aaa71c00c4c279a09ef31f20cnvdPatchWEB
- github.com/roundcube/roundcubemail/commit/39471343ee081ce1d31696c456a2c163462daae3nvdPatchWEB
- github.com/roundcube/roundcubemail/commit/82ab5eca7b332fce7a174b2b987f0957a66377cdnvdPatchWEB
- github.com/advisories/GHSA-j2g6-8rvg-7mf6ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-35543ghsaADVISORY
- 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.5.14nvdRelease NotesWEB
- github.com/roundcube/roundcubemail/releases/tag/1.6.14nvdRelease NotesWEB
- github.com/roundcube/roundcubemail/releases/tag/1.7-rc5nvdRelease NotesWEB
News mentions
1- Why Malwarebytes blocks some Yahoo Mail redirectsMalwarebytes Labs · May 14, 2026