From 190757854d9ce3b3ce3100dc76de54277f3bdd14 Mon Sep 17 00:00:00 2001 From: Cameron Ball Date: Thu, 11 Feb 2016 11:25:53 +0800 Subject: [PATCH] MDL-52651 htmlpurifier: Append rel=noreferrer to links. Thank you to Zachary Durber for originally working on this issue. --- lib/htmlpurifier/locallib.php | 66 ++++++++++++++++++++++++++++++++++++++ lib/tests/htmlpurifier_test.php | 6 +++- lib/weblib.php | 5 ++- mod/data/field/url/field.class.php | 1 + 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/lib/htmlpurifier/locallib.php b/lib/htmlpurifier/locallib.php index b949b7404fc..362ddd58f95 100644 --- a/lib/htmlpurifier/locallib.php +++ b/lib/htmlpurifier/locallib.php @@ -119,3 +119,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 51d22c83b34..ae22daee0de 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 a2bd45ab9bf..00349b97e52 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -1733,7 +1733,7 @@ function purify_html($text, $options = array()) { $config = HTMLPurifier_Config::createDefault(); $config->set('HTML.DefinitionID', 'moodlehtml'); - $config->set('HTML.DefinitionRev', 3); + $config->set('HTML.DefinitionRev', 4); $config->set('Cache.SerializerPath', $cachedir); $config->set('Cache.SerializerPermissions', $CFG->directorypermissions); $config->set('Core.NormalizeNewlines', false); @@ -1775,6 +1775,9 @@ function purify_html($text, $options = array()) { // Use the built-in Ruby module to add annotation support. $def->manager->addModule(new HTMLPurifier_HTMLModule_Ruby()); + + // 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)) { -- 2.11.4.GIT