From edb1ffc626049c7933398e6c51ec92a306bdd5e0 Mon Sep 17 00:00:00 2001
From: Brian Wolff <bawolff+wn@gmail.com>
Date: Mon, 25 Apr 2016 14:08:46 -0400
Subject: [PATCH] Add rel="noreferrer noopener" when target attribute would
 open window

noreferrer is used as support for noopener is very limited.
This is to prevent the attack detailed at
https://mathiasbynens.github.io/rel-noopener/ where you can
navigate the parent window, even if the new window is a cross-origin.

Bug: T133507
Change-Id: I6e4ab938861e246ff44048077b94847e303f1859
---
 includes/DefaultSettings.php |  8 +++++++-
 includes/Linker.php          | 11 ++++++++++-
 includes/parser/Parser.php   | 19 +++++++++++++++----
 tests/parser/parserTests.txt |  4 ++--
 4 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 2a30352..e17ac87 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -4203,7 +4203,13 @@ $wgDebugTidy = false;
 $wgRawHtml = false;
 
 /**
- * Set a default target for external links, e.g. _blank to pop up a new window
+ * Set a default target for external links, e.g. _blank to pop up a new window.
+ *
+ * This will also set the "noreferrer" and "noopener" link rel to prevent the
+ * attack described at https://mathiasbynens.github.io/rel-noopener/ .
+ * Some older browsers may not support these link attributes, hence
+ * setting $wgExternalLinkTarget to _blank may represent a security risk
+ * to some of your users.
  */
 $wgExternalLinkTarget = false;
 
diff --git a/includes/Linker.php b/includes/Linker.php
index ccf948a..5569b80 100644
--- a/includes/Linker.php
+++ b/includes/Linker.php
@@ -1069,7 +1069,16 @@ class Linker {
 		if ( !$title ) {
 			$title = $wgTitle;
 		}
-		$attribs['rel'] = Parser::getExternalLinkRel( $url, $title );
+		$newRel = Parser::getExternalLinkRel( $url, $title );
+		if ( !isset( $attribs['rel'] ) || $attribs['rel'] === '' ) {
+			$attribs['rel'] = $newRel;
+		} elseif( $newRel !== '' ) {
+			// Merge the rel attributes.
+			$newRels = explode( ' ', $newRel );
+			$oldRels = explode( ' ', $attribs['rel'] );
+			$combined = array_unique( array_merge( $newRels, $oldRels ) );
+			$attribs['rel'] = implode( ' ', $combined );
+		}
 		$link = '';
 		$success = Hooks::run( 'LinkerMakeExternalLink',
 			[ &$url, &$text, &$link, &$attribs, $linktype ] );
diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php
index a1d62e5..891b8d2 100644
--- a/includes/parser/Parser.php
+++ b/includes/parser/Parser.php
@@ -1872,11 +1872,22 @@ class Parser {
 	 */
 	public function getExternalLinkAttribs( $url = false ) {
 		$attribs = [];
-		$attribs['rel'] = self::getExternalLinkRel( $url, $this->mTitle );
-
-		if ( $this->mOptions->getExternalLinkTarget() ) {
-			$attribs['target'] = $this->mOptions->getExternalLinkTarget();
+		$rel = self::getExternalLinkRel( $url, $this->mTitle );
+
+		$target = $this->mOptions->getExternalLinkTarget();
+		if ( $target ) {
+			$attribs['target'] = $target;
+			if ( !in_array( $target, [ '_self', '_parent', '_top' ] ) ) {
+				// T133507. New windows can navigate parent cross-origin.
+				// Including noreferrer due to lacking browser
+				// support of noopener. Eventually noreferrer should be removed.
+				if ( $rel !== '' ) {
+					$rel .= ' ';
+				}
+				$rel .= 'noreferrer noopener';
+			}
 		}
+		$attribs['rel'] = $rel;
 		return $attribs;
 	}
 
diff --git a/tests/parser/parserTests.txt b/tests/parser/parserTests.txt
index 23bdbde..b3aca74 100644
--- a/tests/parser/parserTests.txt
+++ b/tests/parser/parserTests.txt
@@ -13132,7 +13132,7 @@ Image with link parameter, wgExternalLinkTarget
 !! config
 wgExternalLinkTarget='foobar'
 !! html/php
-<p><a href="http://example.com/" target="foobar" rel="nofollow"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+<p><a href="http://example.com/" target="foobar" rel="nofollow noreferrer noopener"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
 </p>
 !! end
 
@@ -13166,7 +13166,7 @@ Image with link parameter, wgExternalLinkTarget, unnamed parameter
 !! config
 wgExternalLinkTarget='foobar'
 !! html/php
-<p><a href="http://example.com/" title="Title" target="foobar" rel="nofollow"><img alt="Title" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+<p><a href="http://example.com/" title="Title" target="foobar" rel="nofollow noreferrer noopener"><img alt="Title" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
 </p>
 !! end
 
-- 
2.0.1

