diff options
-rw-r--r-- | CHANGELOG | 1 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_string_replacer.php | 13 | ||||
-rw-r--r-- | program/steps/mail/func.inc | 14 | ||||
-rw-r--r-- | tests/Framework/StringReplacer.php | 22 | ||||
-rw-r--r-- | tests/MailFunc.php | 8 |
5 files changed, 34 insertions, 24 deletions
@@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Add rel="noreferrer" for links in displayed messages (#1484686) - Fix so forward as attachment works if additional attachment is added by message_compose hook (#1489000) - Add ability to toggle between HTML and text while viewing a message (#1486939) - Better handling of session errors in ajax requests (#1488960) diff --git a/program/lib/Roundcube/rcube_string_replacer.php b/program/lib/Roundcube/rcube_string_replacer.php index 49a378166..b8768bc98 100644 --- a/program/lib/Roundcube/rcube_string_replacer.php +++ b/program/lib/Roundcube/rcube_string_replacer.php @@ -28,9 +28,10 @@ class rcube_string_replacer public $mailto_pattern; public $link_pattern; private $values = array(); + private $options = array(); - function __construct() + function __construct($options = array()) { // Simplified domain expression for UTF8 characters handling // Support unicode/punycode in top-level domain part @@ -44,6 +45,8 @@ class rcube_string_replacer ."@$utf_domain" // domain-part ."(\?[$url1$url2]+)?" // e.g. ?subject=test... .")/"; + + $this->options = $options; } /** @@ -89,10 +92,10 @@ class rcube_string_replacer if ($url) { $suffix = $this->parse_url_brackets($url); - $i = $this->add($prefix . html::a(array( - 'href' => $url_prefix . $url, - 'target' => '_blank' - ), rcube::Q($url)) . $suffix); + $attrib = (array)$this->options['link_attribs']; + $attrib['href'] = $url_prefix . $url; + + $i = $this->add($prefix . html::a($attrib, rcube::Q($url)) . $suffix); } // Return valid link for recognized schemes, otherwise diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 8c9743949..274c40b5c 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -760,7 +760,8 @@ function rcmail_plain_body($body, $flowed=false) global $RCMAIL; // make links and email-addresses clickable - $replacer = new rcmail_string_replacer; + $attribs = array('link_attribs' => array('rel' => 'noreferrer', 'target' => '_blank')); + $replacer = new rcmail_string_replacer($attribs); // search for patterns like links and e-mail addresses and replace with tokens $body = $replacer->replace($body); @@ -1373,7 +1374,7 @@ function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null /** - * parse link attributes and set correct target + * parse link (a, link, area) attributes and set correct target */ function rcmail_alter_html_link($matches) { @@ -1382,9 +1383,9 @@ function rcmail_alter_html_link($matches) // Support unicode/punycode in top-level domain part $EMAIL_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[^&@"\'.][^@&"\']*\\.([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))'; - $tag = $matches[1]; + $tag = strtolower($matches[1]); $attrib = parse_attrib_string($matches[2]); - $end = '>'; + $end = '>'; // Remove non-printable characters in URL (#1487805) if ($attrib['href']) @@ -1411,6 +1412,11 @@ function rcmail_alter_html_link($matches) $attrib['target'] = '_blank'; } + // Better security by adding rel="noreferrer" (#1484686) + if (($tag == 'a' || $tag == 'area') && $attrib['href'] && $attrib['href'][0] != '#') { + $attrib['rel'] = 'noreferrer'; + } + // allowed attributes for a|link|area tags $allow = array('href','name','target','onclick','id','class','style','title', 'rel','type','media','alt','coords','nohref','hreflang','shape'); diff --git a/tests/Framework/StringReplacer.php b/tests/Framework/StringReplacer.php index e630ebac0..95c59221b 100644 --- a/tests/Framework/StringReplacer.php +++ b/tests/Framework/StringReplacer.php @@ -24,17 +24,17 @@ class Framework_StringReplacer extends PHPUnit_Framework_TestCase function data_replace() { return array( - array('http://domain.tld/path*path2', '<a href="http://domain.tld/path*path2" target="_blank">http://domain.tld/path*path2</a>'), - array("Click this link:\nhttps://mail.xn--brderli-o2a.ch/rc/ EOF", "Click this link:\n<a href=\"https://mail.xn--brderli-o2a.ch/rc/\" target=\"_blank\">https://mail.xn--brderli-o2a.ch/rc/</a> EOF"), - array('Start http://localhost/?foo End', 'Start <a href="http://localhost/?foo" target="_blank">http://localhost/?foo</a> End'), - array('www.domain.tld', '<a href="http://www.domain.tld" target="_blank">www.domain.tld</a>'), - array('WWW.DOMAIN.TLD', '<a href="http://WWW.DOMAIN.TLD" target="_blank">WWW.DOMAIN.TLD</a>'), - array('[http://link.com]', '[<a href="http://link.com" target="_blank">http://link.com</a>]'), - array('http://link.com?a[]=1', '<a href="http://link.com?a[]=1" target="_blank">http://link.com?a[]=1</a>'), - array('http://link.com?a[]', '<a href="http://link.com?a[]" target="_blank">http://link.com?a[]</a>'), - array('(http://link.com)', '(<a href="http://link.com" target="_blank">http://link.com</a>)'), - array('http://link.com?a(b)c', '<a href="http://link.com?a(b)c" target="_blank">http://link.com?a(b)c</a>'), - array('http://link.com?(link)', '<a href="http://link.com?(link)" target="_blank">http://link.com?(link)</a>'), + array('http://domain.tld/path*path2', '<a href="http://domain.tld/path*path2">http://domain.tld/path*path2</a>'), + array("Click this link:\nhttps://mail.xn--brderli-o2a.ch/rc/ EOF", "Click this link:\n<a href=\"https://mail.xn--brderli-o2a.ch/rc/\">https://mail.xn--brderli-o2a.ch/rc/</a> EOF"), + array('Start http://localhost/?foo End', 'Start <a href="http://localhost/?foo">http://localhost/?foo</a> End'), + array('www.domain.tld', '<a href="http://www.domain.tld">www.domain.tld</a>'), + array('WWW.DOMAIN.TLD', '<a href="http://WWW.DOMAIN.TLD">WWW.DOMAIN.TLD</a>'), + array('[http://link.com]', '[<a href="http://link.com">http://link.com</a>]'), + array('http://link.com?a[]=1', '<a href="http://link.com?a[]=1">http://link.com?a[]=1</a>'), + array('http://link.com?a[]', '<a href="http://link.com?a[]">http://link.com?a[]</a>'), + array('(http://link.com)', '(<a href="http://link.com">http://link.com</a>)'), + array('http://link.com?a(b)c', '<a href="http://link.com?a(b)c">http://link.com?a(b)c</a>'), + array('http://link.com?(link)', '<a href="http://link.com?(link)">http://link.com?(link)</a>'), array('http://<test>', 'http://<test>'), array('http://', 'http://'), ); diff --git a/tests/MailFunc.php b/tests/MailFunc.php index 38c0bac30..319075abd 100644 --- a/tests/MailFunc.php +++ b/tests/MailFunc.php @@ -54,7 +54,7 @@ class MailFunc extends PHPUnit_Framework_TestCase $this->assertNotRegExp('/<form [^>]+>/', $html, "No form tags allowed"); $this->assertRegExp('/Subscription form/', $html, "Include <form> contents"); $this->assertRegExp('/<!-- link ignored -->/', $html, "No external links allowed"); - $this->assertRegExp('/<a[^>]+ target="_blank">/', $html, "Set target to _blank"); + $this->assertRegExp('/<a[^>]+ target="_blank"/', $html, "Set target to _blank"); $this->assertTrue($GLOBALS['REMOTE_OBJECTS'], "Remote object detected"); // render HTML in safe mode @@ -133,8 +133,8 @@ class MailFunc extends PHPUnit_Framework_TestCase $html = rcmail_print_body($part, array('safe' => true)); $this->assertRegExp('/<a href="mailto:nobody@roundcube.net" onclick="return rcmail.command\(\'compose\',\'nobody@roundcube.net\',this\)">nobody@roundcube.net<\/a>/', $html, "Mailto links with onclick"); - $this->assertRegExp('#<a href="http://www.apple.com/legal/privacy" target="_blank">http://www.apple.com/legal/privacy</a>#', $html, "Links with target=_blank"); - $this->assertRegExp('#\\[<a href="http://example.com/\\?tx\\[a\\]=5" target="_blank">http://example.com/\\?tx\\[a\\]=5</a>\\]#', $html, "Links with square brackets"); + $this->assertRegExp('#<a rel="noreferrer" target="_blank" href="http://www.apple.com/legal/privacy">http://www.apple.com/legal/privacy</a>#', $html, "Links with target=_blank"); + $this->assertRegExp('#\\[<a rel="noreferrer" target="_blank" href="http://example.com/\\?tx\\[a\\]=5">http://example.com/\\?tx\\[a\\]=5</a>\\]#', $html, "Links with square brackets"); } /** @@ -148,7 +148,7 @@ class MailFunc extends PHPUnit_Framework_TestCase $html = rcmail_html4inline(rcmail_print_body($part, array('safe' => false)), 'foo'); $mailto = '<a href="mailto:me@me.com?subject=this is the subject&body=this is the body"' - .' onclick="return rcmail.command(\'compose\',\'me@me.com?subject=this is the subject&body=this is the body\',this)">e-mail</a>'; + .' onclick="return rcmail.command(\'compose\',\'me@me.com?subject=this is the subject&body=this is the body\',this)" rel="noreferrer">e-mail</a>'; $this->assertRegExp('|'.preg_quote($mailto, '|').'|', $html, "Extended mailto links"); } |