From 8e4cacf0a76458d6203f53113c47b49c6edc2dc5 Mon Sep 17 00:00:00 2001 From: Bastian Hofmann Date: Fri, 13 Jan 2017 17:21:24 +0100 Subject: [PATCH] Refactor HTML.Noopener to HTML.TargetNoopener so that it behaves like HTML.TargetNoreferrer and is active by default if a target is set --- NEWS | 6 ++- configdoc/usage.xml | 8 ++-- library/HTMLPurifier.includes.php | 4 +- library/HTMLPurifier.safe-includes.php | 4 +- library/HTMLPurifier/AttrTransform/Noopener.php | 52 --------------------- .../HTMLPurifier/AttrTransform/TargetNoopener.php | 37 +++++++++++++++ library/HTMLPurifier/ConfigSchema/schema.ser | Bin 15674 -> 15692 bytes .../ConfigSchema/schema/HTML.Noopener.txt | 7 --- .../ConfigSchema/schema/HTML.TargetNoopener.txt | 10 ++++ library/HTMLPurifier/HTMLModule/Noopener.php | 25 ---------- library/HTMLPurifier/HTMLModule/TargetNoopener.php | 21 +++++++++ library/HTMLPurifier/HTMLModuleManager.php | 8 ++-- tests/HTMLPurifier/HTMLModule/NoopenerTest.php | 30 ------------ tests/HTMLPurifier/HTMLModule/TargetBlankTest.php | 9 +++- ...etNoreferrerTest.php => TargetNoopenerTest.php} | 23 ++++++--- .../HTMLModule/TargetNoreferrerTest.php | 9 ++++ .../Strategy/ValidateAttributesTest.php | 2 +- 17 files changed, 119 insertions(+), 136 deletions(-) delete mode 100644 library/HTMLPurifier/AttrTransform/Noopener.php create mode 100644 library/HTMLPurifier/AttrTransform/TargetNoopener.php rewrite library/HTMLPurifier/ConfigSchema/schema.ser (76%) delete mode 100644 library/HTMLPurifier/ConfigSchema/schema/HTML.Noopener.txt create mode 100644 library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt delete mode 100644 library/HTMLPurifier/HTMLModule/Noopener.php create mode 100644 library/HTMLPurifier/HTMLModule/TargetNoopener.php delete mode 100644 tests/HTMLPurifier/HTMLModule/NoopenerTest.php copy tests/HTMLPurifier/HTMLModule/{TargetNoreferrerTest.php => TargetNoopenerTest.php} (63%) diff --git a/NEWS b/NEWS index b12c63ad..afa01d70 100644 --- a/NEWS +++ b/NEWS @@ -19,7 +19,11 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier - Deleted some asserts to avoid linters from choking (#97) - Rework Serializer cache behavior to avoid chmod'ing if possible (#32) - Embedded semicolons in strings in CSS are now handled correctly! -! Added %HTML.Noopener to add rel="noopener" to external links. +# By default, when a link has a target attribute associated + with it, we now also add rel="noopener" in order to + prevent the new window from being able to overwrite + the original frame. To disable this protection, + set %HTML.TargetNoopener to FALSE. 4.8.0, released 2016-07-16 # By default, when a link has a target attribute associated diff --git a/configdoc/usage.xml b/configdoc/usage.xml index 67020803..ac0726fe 100644 --- a/configdoc/usage.xml +++ b/configdoc/usage.xml @@ -222,17 +222,17 @@ 268 - + 271 - + - 274 + 276 - + 279 diff --git a/library/HTMLPurifier.includes.php b/library/HTMLPurifier.includes.php index 0a4c6096..21021b59 100644 --- a/library/HTMLPurifier.includes.php +++ b/library/HTMLPurifier.includes.php @@ -132,13 +132,13 @@ require 'HTMLPurifier/AttrTransform/Length.php'; require 'HTMLPurifier/AttrTransform/Name.php'; require 'HTMLPurifier/AttrTransform/NameSync.php'; require 'HTMLPurifier/AttrTransform/Nofollow.php'; -require 'HTMLPurifier/AttrTransform/Noopener.php'; require 'HTMLPurifier/AttrTransform/SafeEmbed.php'; require 'HTMLPurifier/AttrTransform/SafeObject.php'; require 'HTMLPurifier/AttrTransform/SafeParam.php'; require 'HTMLPurifier/AttrTransform/ScriptRequired.php'; require 'HTMLPurifier/AttrTransform/TargetBlank.php'; require 'HTMLPurifier/AttrTransform/TargetNoreferrer.php'; +require 'HTMLPurifier/AttrTransform/TargetNoopener.php'; require 'HTMLPurifier/AttrTransform/Textarea.php'; require 'HTMLPurifier/ChildDef/Chameleon.php'; require 'HTMLPurifier/ChildDef/Custom.php'; @@ -164,7 +164,6 @@ require 'HTMLPurifier/HTMLModule/Legacy.php'; require 'HTMLPurifier/HTMLModule/List.php'; require 'HTMLPurifier/HTMLModule/Name.php'; require 'HTMLPurifier/HTMLModule/Nofollow.php'; -require 'HTMLPurifier/HTMLModule/Noopener.php'; require 'HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php'; require 'HTMLPurifier/HTMLModule/Object.php'; require 'HTMLPurifier/HTMLModule/Presentation.php'; @@ -179,6 +178,7 @@ require 'HTMLPurifier/HTMLModule/Tables.php'; require 'HTMLPurifier/HTMLModule/Target.php'; require 'HTMLPurifier/HTMLModule/TargetBlank.php'; require 'HTMLPurifier/HTMLModule/TargetNoreferrer.php'; +require 'HTMLPurifier/HTMLModule/TargetNoopener.php'; require 'HTMLPurifier/HTMLModule/Text.php'; require 'HTMLPurifier/HTMLModule/Tidy.php'; require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php'; diff --git a/library/HTMLPurifier.safe-includes.php b/library/HTMLPurifier.safe-includes.php index a81ea5c6..b37dad74 100644 --- a/library/HTMLPurifier.safe-includes.php +++ b/library/HTMLPurifier.safe-includes.php @@ -126,13 +126,13 @@ require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/NameSync.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Nofollow.php'; -require_once $__dir . '/HTMLPurifier/AttrTransform/Noopener.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/TargetBlank.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoreferrer.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoopener.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php'; require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php'; require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php'; @@ -158,7 +158,6 @@ require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/List.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Name.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Nofollow.php'; -require_once $__dir . '/HTMLPurifier/HTMLModule/Noopener.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Object.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Presentation.php'; @@ -173,6 +172,7 @@ require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/TargetBlank.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoreferrer.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoopener.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php'; diff --git a/library/HTMLPurifier/AttrTransform/Noopener.php b/library/HTMLPurifier/AttrTransform/Noopener.php deleted file mode 100644 index fb72d307..00000000 --- a/library/HTMLPurifier/AttrTransform/Noopener.php +++ /dev/null @@ -1,52 +0,0 @@ -parser = new HTMLPurifier_URIParser(); - } - - /** - * @param array $attr - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return array - */ - public function transform($attr, $config, $context) - { - if (!isset($attr['href'])) { - return $attr; - } - - // XXX Kind of inefficient - $url = $this->parser->parse($attr['href']); - $scheme = $url->getSchemeObj($config, $context); - - if ($scheme->browsable && !$url->isLocal($config, $context)) { - if (isset($attr['rel'])) { - $rels = explode(' ', $attr['rel']); - if (!in_array('noopener', $rels)) { - $rels[] = 'noopener'; - } - $attr['rel'] = implode(' ', $rels); - } else { - $attr['rel'] = 'noopener'; - } - } - return $attr; - } -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrTransform/TargetNoopener.php b/library/HTMLPurifier/AttrTransform/TargetNoopener.php new file mode 100644 index 00000000..1db3c6c0 --- /dev/null +++ b/library/HTMLPurifier/AttrTransform/TargetNoopener.php @@ -0,0 +1,37 @@ +GZ&ne`$0=PSf*~`>{LbWF LCF#v#7G|sfOtm3< diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.Noopener.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.Noopener.txt deleted file mode 100644 index 59245688..00000000 --- a/library/HTMLPurifier/ConfigSchema/schema/HTML.Noopener.txt +++ /dev/null @@ -1,7 +0,0 @@ -HTML.Noopener -TYPE: bool -VERSION: 4.9.0 -DEFAULT: FALSE ---DESCRIPTION-- -If enabled, noopener rel attributes are added to all outgoing links. ---# vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt new file mode 100644 index 00000000..dd514c0d --- /dev/null +++ b/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt @@ -0,0 +1,10 @@ +--# vim: et sw=4 sts=4 +HTML.TargetNoopener +TYPE: bool +VERSION: 4.8.0 +DEFAULT: TRUE +--DESCRIPTION-- +If enabled, noopener rel attributes are added to links which have +a target attribute associated with them. This prevents malicious +destinations from overwriting the original window. +--# vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/HTMLModule/Noopener.php b/library/HTMLPurifier/HTMLModule/Noopener.php deleted file mode 100644 index 1fa629ba..00000000 --- a/library/HTMLPurifier/HTMLModule/Noopener.php +++ /dev/null @@ -1,25 +0,0 @@ -addBlankElement('a'); - $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_Noopener(); - } -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/HTMLModule/TargetNoopener.php b/library/HTMLPurifier/HTMLModule/TargetNoopener.php new file mode 100644 index 00000000..b967ff56 --- /dev/null +++ b/library/HTMLPurifier/HTMLModule/TargetNoopener.php @@ -0,0 +1,21 @@ +addBlankElement('a'); + $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoopener(); + } +} diff --git a/library/HTMLPurifier/HTMLModuleManager.php b/library/HTMLPurifier/HTMLModuleManager.php index 4d0fc6ea..38c058fe 100644 --- a/library/HTMLPurifier/HTMLModuleManager.php +++ b/library/HTMLPurifier/HTMLModuleManager.php @@ -268,17 +268,17 @@ class HTMLPurifier_HTMLModuleManager if ($config->get('HTML.Nofollow')) { $modules[] = 'Nofollow'; } - if ($config->get('HTML.Noopener')) { - $modules[] = 'Noopener'; - } if ($config->get('HTML.TargetBlank')) { $modules[] = 'TargetBlank'; } - // NB: HTML.TargetNoreferrer must be AFTER HTML.TargetBlank + // NB: HTML.TargetNoreferrer and HTML.TargetNoopener must be AFTER HTML.TargetBlank // so that its post-attr-transform gets run afterwards. if ($config->get('HTML.TargetNoreferrer')) { $modules[] = 'TargetNoreferrer'; } + if ($config->get('HTML.TargetNoopener')) { + $modules[] = 'TargetNoopener'; + } // merge in custom modules $modules = array_merge($modules, $this->userModules); diff --git a/tests/HTMLPurifier/HTMLModule/NoopenerTest.php b/tests/HTMLPurifier/HTMLModule/NoopenerTest.php deleted file mode 100644 index a53fb64e..00000000 --- a/tests/HTMLPurifier/HTMLModule/NoopenerTest.php +++ /dev/null @@ -1,30 +0,0 @@ -config->set('HTML.Noopener', true); - $this->config->set('Attr.AllowedRel', array("noopener", "blah")); - } - - public function testNoopener() - { - $this->assertResult( - 'xabc', - 'xabc' - ); - } - - public function testNoopenerDupe() - { - $this->assertResult( - 'xabc' - ); - } - -} - -// vim: et sw=4 sts=4 diff --git a/tests/HTMLPurifier/HTMLModule/TargetBlankTest.php b/tests/HTMLPurifier/HTMLModule/TargetBlankTest.php index dbb755f6..f837309f 100644 --- a/tests/HTMLPurifier/HTMLModule/TargetBlankTest.php +++ b/tests/HTMLPurifier/HTMLModule/TargetBlankTest.php @@ -13,7 +13,14 @@ class HTMLPurifier_HTMLModule_TargetBlankTest extends HTMLPurifier_HTMLModuleHar { $this->assertResult( 'abc', - 'abc' + 'abc' + ); + } + + public function testTargetBlankNoDupe() { + $this->assertResult( + 'a', + 'a' ); } diff --git a/tests/HTMLPurifier/HTMLModule/TargetNoreferrerTest.php b/tests/HTMLPurifier/HTMLModule/TargetNoopenerTest.php similarity index 63% copy from tests/HTMLPurifier/HTMLModule/TargetNoreferrerTest.php copy to tests/HTMLPurifier/HTMLModule/TargetNoopenerTest.php index 52dbb4a1..dcbb9a1d 100644 --- a/tests/HTMLPurifier/HTMLModule/TargetNoreferrerTest.php +++ b/tests/HTMLPurifier/HTMLModule/TargetNoopenerTest.php @@ -1,12 +1,13 @@ config->set('HTML.TargetNoreferrer', true); + $this->config->set('HTML.TargetNoreferrer', false); + $this->config->set('HTML.TargetNoopener', true); $this->config->set('Attr.AllowedFrameTargets', '_blank'); } @@ -14,16 +15,16 @@ class HTMLPurifier_HTMLModule_TargetNoreferrerTest extends HTMLPurifier_HTMLModu { $this->assertResult( 'x', - 'x' + 'x' ); } public function testNoreferrerNoDupe() { - $this->config->set('Attr.AllowedRel', 'noreferrer'); + $this->config->set('Attr.AllowedRel', 'noopener'); $this->assertResult( - 'x', - 'x' + 'x', + 'x' ); } @@ -32,7 +33,15 @@ class HTMLPurifier_HTMLModule_TargetNoreferrerTest extends HTMLPurifier_HTMLModu $this->config->set('HTML.TargetBlank', true); $this->assertResult( 'x', - 'x' + 'x' + ); + } + + public function testNoTarget() + { + $this->assertResult( + 'x', + 'x' ); } diff --git a/tests/HTMLPurifier/HTMLModule/TargetNoreferrerTest.php b/tests/HTMLPurifier/HTMLModule/TargetNoreferrerTest.php index 52dbb4a1..c7abc542 100644 --- a/tests/HTMLPurifier/HTMLModule/TargetNoreferrerTest.php +++ b/tests/HTMLPurifier/HTMLModule/TargetNoreferrerTest.php @@ -7,6 +7,7 @@ class HTMLPurifier_HTMLModule_TargetNoreferrerTest extends HTMLPurifier_HTMLModu { parent::setUp(); $this->config->set('HTML.TargetNoreferrer', true); + $this->config->set('HTML.TargetNoopener', false); $this->config->set('Attr.AllowedFrameTargets', '_blank'); } @@ -36,6 +37,14 @@ class HTMLPurifier_HTMLModule_TargetNoreferrerTest extends HTMLPurifier_HTMLModu ); } + public function testNoTarget() + { + $this->assertResult( + 'x', + 'x' + ); + } + } diff --git a/tests/HTMLPurifier/Strategy/ValidateAttributesTest.php b/tests/HTMLPurifier/Strategy/ValidateAttributesTest.php index 19fca6ac..a2125522 100644 --- a/tests/HTMLPurifier/Strategy/ValidateAttributesTest.php +++ b/tests/HTMLPurifier/Strategy/ValidateAttributesTest.php @@ -189,7 +189,7 @@ class HTMLPurifier_Strategy_ValidateAttributesTest extends { $this->config->set('Attr.AllowedFrameTargets', '_top'); $this->config->set('HTML.Doctype', 'XHTML 1.0 Transitional'); - $this->assertResult(''); + $this->assertResult(''); } public function testRemoveTargetWhenNotSupported() -- 2.11.4.GIT