From 1e32540839683c1309db012c4d5b9aff35ec6ae3 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 19 Mar 2013 12:47:07 +0100 Subject: Add rel="noreferrer" for links in displayed messages (#1484686) --- CHANGELOG | 1 + program/lib/Roundcube/rcube_string_replacer.php | 13 ++++++++----- program/steps/mail/func.inc | 14 ++++++++++---- tests/Framework/StringReplacer.php | 22 +++++++++++----------- tests/MailFunc.php | 8 ++++---- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 1e3eb77b5..ed0ea6ef3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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', 'http://domain.tld/path*path2'), - array("Click this link:\nhttps://mail.xn--brderli-o2a.ch/rc/ EOF", "Click this link:\nhttps://mail.xn--brderli-o2a.ch/rc/ EOF"), - array('Start http://localhost/?foo End', 'Start http://localhost/?foo End'), - array('www.domain.tld', 'www.domain.tld'), - array('WWW.DOMAIN.TLD', 'WWW.DOMAIN.TLD'), - array('[http://link.com]', '[http://link.com]'), - array('http://link.com?a[]=1', 'http://link.com?a[]=1'), - array('http://link.com?a[]', 'http://link.com?a[]'), - array('(http://link.com)', '(http://link.com)'), - array('http://link.com?a(b)c', 'http://link.com?a(b)c'), - array('http://link.com?(link)', 'http://link.com?(link)'), + array('http://domain.tld/path*path2', 'http://domain.tld/path*path2'), + array("Click this link:\nhttps://mail.xn--brderli-o2a.ch/rc/ EOF", "Click this link:\nhttps://mail.xn--brderli-o2a.ch/rc/ EOF"), + array('Start http://localhost/?foo End', 'Start http://localhost/?foo End'), + array('www.domain.tld', 'www.domain.tld'), + array('WWW.DOMAIN.TLD', 'WWW.DOMAIN.TLD'), + array('[http://link.com]', '[http://link.com]'), + array('http://link.com?a[]=1', 'http://link.com?a[]=1'), + array('http://link.com?a[]', 'http://link.com?a[]'), + array('(http://link.com)', '(http://link.com)'), + array('http://link.com?a(b)c', 'http://link.com?a(b)c'), + array('http://link.com?(link)', 'http://link.com?(link)'), array('http://', 'http://'), 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('/
]+>/', $html, "No form tags allowed"); $this->assertRegExp('/Subscription form/', $html, "Include contents"); $this->assertRegExp('//', $html, "No external links allowed"); - $this->assertRegExp('/]+ target="_blank">/', $html, "Set target to _blank"); + $this->assertRegExp('/]+ 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('/nobody@roundcube.net<\/a>/', $html, "Mailto links with onclick"); - $this->assertRegExp('#http://www.apple.com/legal/privacy#', $html, "Links with target=_blank"); - $this->assertRegExp('#\\[http://example.com/\\?tx\\[a\\]=5\\]#', $html, "Links with square brackets"); + $this->assertRegExp('#http://www.apple.com/legal/privacy#', $html, "Links with target=_blank"); + $this->assertRegExp('#\\[http://example.com/\\?tx\\[a\\]=5\\]#', $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 = 'e-mail'; + .' onclick="return rcmail.command(\'compose\',\'me@me.com?subject=this is the subject&body=this is the body\',this)" rel="noreferrer">e-mail'; $this->assertRegExp('|'.preg_quote($mailto, '|').'|', $html, "Extended mailto links"); } -- cgit v1.2.3