Anchor target=_blank implies rel=noopener

To mitigate "tab-napping" attacks, in which a new tab/window opened by
a victim context may navigate that opener context, the HTML standard
changed to specify that anchors that target _blank should behave as if
|rel="noopener"| is set. A page wishing to opt out of this behavior may
set |rel="opener"|.

Bug: 898942
Change-Id: Id34bbc480e96cb2cc8e922388a9a5bc4161b03b1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1630010
Reviewed-by: Julian Pastarmov <[email protected]>
Reviewed-by: John Abd-El-Malek <[email protected]>
Reviewed-by: Marijn Kruisselbrink <[email protected]>
Reviewed-by: Mike West <[email protected]>
Reviewed-by: Arthur Sonzogni <[email protected]>
Commit-Queue: Eric Lawrence [MSFT] <[email protected]>
Cr-Commit-Position: refs/heads/master@{#825022}
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index f140422..c7e9d29 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2326,6 +2326,12 @@
             blink::switches::kUserAgentClientHintDisable);
       }
 
+      if (!local_state->GetBoolean(
+              policy::policy_prefs::kTargetBlankImpliesNoOpener)) {
+        command_line->AppendSwitch(
+            switches::kDisableTargetBlankImpliesNoOpener);
+      }
+
 #if defined(OS_ANDROID)
       // Communicating to content/ for BackForwardCache.
       if (prefs->HasPrefPath(policy::policy_prefs::kBackForwardCacheEnabled) &&
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index b4bd7c6..90debc9 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1300,6 +1300,9 @@
   { key::kLookalikeWarningAllowlistDomains,
     prefs::kLookalikeWarningAllowlistDomains,
     base::Value::Type::LIST },
+  { key::kTargetBlankImpliesNoOpener,
+    policy::policy_prefs::kTargetBlankImpliesNoOpener,
+    base::Value::Type::BOOLEAN },
 
 #if defined(OS_ANDROID)
   { key::kTosDialogBehavior,
diff --git a/chrome/browser/policy/window_opener_policy_browsertest.cc b/chrome/browser/policy/window_opener_policy_browsertest.cc
new file mode 100644
index 0000000..3cbf1ac
--- /dev/null
+++ b/chrome/browser/policy/window_opener_policy_browsertest.cc
@@ -0,0 +1,71 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// TODO(crbug.com/898942): Remove this in Chrome 95.
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace policy {
+
+class PolicyTestWindowOpener : public PolicyTest {
+  void SetUpInProcessBrowserTestFixture() override {
+    PolicyTest::SetUpInProcessBrowserTestFixture();
+    PolicyMap policies;
+    // Configure the policy to disable the new behavior.
+    SetPolicy(&policies, policy::key::kTargetBlankImpliesNoOpener,
+              base::Value(false));
+    provider_.UpdateChromePolicy(policies);
+  }
+};
+
+// Check that when the TargetBlankImpliesNoOpener policy is configured and set
+// to false, windows targeting _blank do not have their opener cleared.
+IN_PROC_BROWSER_TEST_F(PolicyTestWindowOpener, CheckWindowOpenerNonNull) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  PrefService* local_state = g_browser_process->local_state();
+  EXPECT_FALSE(local_state->GetBoolean(
+      policy::policy_prefs::kTargetBlankImpliesNoOpener));
+
+  GURL url(
+      "data:text/html,<a href='about:blank' target='_blank' "
+      "id='link'>popup</a>");
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  ASSERT_EQ(browser()->tab_strip_model()->count(), 1);
+  content::WebContents* tab_1 =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  ui_test_utils::TabAddedWaiter tab_Added_waiter(browser());
+  SimulateMouseClickOrTapElementWithId(tab_1, "link");
+  tab_Added_waiter.Wait();
+  content::WebContents* tab_2 =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_NE(tab_1, tab_2);
+
+  constexpr char kScript[] =
+      R"({ window.domAutomationController.send(window.opener === null); })";
+  content::ExecuteScriptAsync(tab_2, kScript);
+
+  content::DOMMessageQueue message_queue;
+  std::string message;
+  EXPECT_TRUE(message_queue.WaitForMessage(&message));
+  EXPECT_EQ("false", message);
+}
+
+}  // namespace policy
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 43fe63e..b1817554 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -604,6 +604,8 @@
       policy::policy_prefs::kIntensiveWakeUpThrottlingEnabled, false);
   registry->RegisterBooleanPref(
       policy::policy_prefs::kUserAgentClientHintsEnabled, true);
+  registry->RegisterBooleanPref(
+      policy::policy_prefs::kTargetBlankImpliesNoOpener, true);
 #if defined(OS_ANDROID)
   registry->RegisterBooleanPref(policy::policy_prefs::kBackForwardCacheEnabled,
                                 true);