diff --git a/lib/htmlpurifier/locallib.php b/lib/htmlpurifier/locallib.php index 66e4e97ef0e..fa7e3596aa6 100644 --- a/lib/htmlpurifier/locallib.php +++ b/lib/htmlpurifier/locallib.php @@ -103,3 +103,69 @@ class HTMLPurifier_URIScheme_teamspeak extends HTMLPurifier_URIScheme { } } + +/** + * A custom HTMLPurifier transformation. Adds rel="noreferrer" to all links with target="_blank". + * + * @package core + * @copyright Cameron Ball + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class HTMLPurifier_AttrTransform_Noreferrer extends HTMLPurifier_AttrTransform { + /** @var HTMLPurifier_URIParser $parser */ + private $parser; + + /** + * Constructor. + */ + public function __construct() { + $this->parser = new HTMLPurifier_URIParser(); + } + + /** + * Transforms a tags such that when a target attribute is present, rel="noreferrer" is added. + * + * Note that this will not respect Attr.AllowedRel + * + * @param array $attr Assoc array of attributes, usually from + * HTMLPurifier_Token_Tag::$attr + * @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object. + * @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object + * @return array Processed attribute array. + */ + public function transform($attr, $config, $context) { + // Nothing to do If we already have noreferrer in the rel attribute + if (!empty($attr['rel']) && substr($attr['rel'], 'noreferrer') !== false) { + return $attr; + } + + // If _blank target attribute exists, add rel=noreferrer + if (!empty($attr['target']) && $attr['target'] == '_blank') { + $attr['rel'] = !empty($attr['rel']) ? $attr['rel'] . ' noreferrer' : 'noreferrer'; + } + + return $attr; + } +} + +/** + * A custom HTMLPurifier module to add rel="noreferrer" attributes a tags. + * + * @package core + * @copyright Cameron Ball + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class HTMLPurifier_HTMLModule_Noreferrer extends HTMLPurifier_HTMLModule { + /** @var string $name */ + public $name = 'Noreferrer'; + + /** + * Module setup + * + * @param HTMLPurifier_Config $config + */ + public function setup($config) { + $a = $this->addBlankElement('a'); + $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_Noreferrer(); + } +} diff --git a/lib/tests/htmlpurifier_test.php b/lib/tests/htmlpurifier_test.php index 88a16440ae1..28d8c71163b 100644 --- a/lib/tests/htmlpurifier_test.php +++ b/lib/tests/htmlpurifier_test.php @@ -40,9 +40,13 @@ class core_htmlpurifier_testcase extends basic_testcase { * Verify _blank target is allowed. */ public function test_allow_blank_target() { + // See MDL-52651 for an explanation as to why the rel="noreferrer" attribute is expected here. + // Also note we do not need to test links with an existing rel attribute as the HTML Purifier is configured to remove + // the rel attribute. $text = 'Some link'; + $expected = 'Some link'; $result = format_text($text, FORMAT_HTML); - $this->assertSame($text, $result); + $this->assertSame($expected, $result); $result = format_text('Some link', FORMAT_HTML); $this->assertSame('Some link', $result); diff --git a/lib/weblib.php b/lib/weblib.php index ef3e6a5829a..1910cc7d9b7 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -1671,7 +1671,7 @@ function purify_html($text, $options = array()) { $config = HTMLPurifier_Config::createDefault(); $config->set('HTML.DefinitionID', 'moodlehtml'); - $config->set('HTML.DefinitionRev', 2); + $config->set('HTML.DefinitionRev', 3); $config->set('Cache.SerializerPath', $cachedir); $config->set('Cache.SerializerPermissions', $CFG->directorypermissions); $config->set('Core.NormalizeNewlines', false); @@ -1709,6 +1709,9 @@ function purify_html($text, $options = array()) { $def->addElement('algebra', 'Inline', 'Inline', array()); // Algebra syntax, equivalent to @@xx@@. $def->addElement('lang', 'Block', 'Flow', array(), array('lang'=>'CDATA')); // Original multilang style - only our hacked lang attribute. $def->addAttribute('span', 'xxxlang', 'CDATA'); // Current very problematic multilang. + + // Use the custom Noreferrer module. + $def->manager->addModule(new HTMLPurifier_HTMLModule_Noreferrer()); } $purifier = new HTMLPurifier($config); diff --git a/mod/data/field/url/field.class.php b/mod/data/field/url/field.class.php index b2e549f87f5..5f938724f66 100644 --- a/mod/data/field/url/field.class.php +++ b/mod/data/field/url/field.class.php @@ -114,6 +114,7 @@ class data_field_url extends data_field_base { if ($this->field->param3) { // param3 defines whether this URL should open in a new window. $attributes['target'] = '_blank'; + $attributes['rel'] = 'noreferrer'; } if (empty($text)) {