Commit 7cfe6fc5 authored by Emily Stark's avatar Emily Stark Committed by Commit Bot

Display precursor origins when applicable in external protocol dialog

External protocol dialogs display the origin that initiated the
external protocol request. The initiating origin helps the user
attribute the request to a particular site, so that they can decide if
they can trust that site to launch an external application. When the
initiating origin was opaque (such as from a sandboxed iframe), the
dialog would display no origin or a generic message, so the user
didn't have any information for making a trust decision. This CL
converts the initiating origin to its precursor origin (the origin
that created the initiating origin) when creating the external
protocol dialog. Displaying the precursor origin gives the user more
useful information for making a trust decision.

Bug: 1041749
Change-Id: I0b21d20e13d7d71db361746dbb18df8d980339bd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2063420
Commit-Queue: Emily Stark <estark@chromium.org>
Reviewed-by: default avatarMustafa Emre Acer <meacer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#742492}
parent a60287b8
......@@ -316,13 +316,27 @@ void ExternalProtocolHandler::LaunchUrl(
g_accept_requests = false;
base::Optional<url::Origin> initiating_origin_or_precursor;
if (initiating_origin) {
// Transform the initiating origin to its precursor origin if it is
// opaque. |initiating_origin| is shown in the UI to attribute the external
// protocol request to a particular site, and showing an opaque origin isn't
// useful.
if (initiating_origin->opaque()) {
initiating_origin_or_precursor = url::Origin::Create(
initiating_origin->GetTupleOrPrecursorTupleIfOpaque().GetURL());
} else {
initiating_origin_or_precursor = initiating_origin;
}
}
// The worker creates tasks with references to itself and puts them into
// message loops.
shell_integration::DefaultWebClientWorkerCallback callback =
base::Bind(&OnDefaultProtocolClientWorkerFinished, escaped_url,
render_process_host_id, render_view_routing_id,
block_state == UNKNOWN, page_transition, has_user_gesture,
initiating_origin, g_external_protocol_handler_delegate);
shell_integration::DefaultWebClientWorkerCallback callback = base::Bind(
&OnDefaultProtocolClientWorkerFinished, escaped_url,
render_process_host_id, render_view_routing_id, block_state == UNKNOWN,
page_transition, has_user_gesture, initiating_origin_or_precursor,
g_external_protocol_handler_delegate);
// Start the check process running. This will send tasks to a worker task
// runner and when the answer is known will send the result back to
......
......@@ -131,6 +131,12 @@ class ExternalProtocolHandler {
// This is implemented separately on each platform.
// TODO(davidsac): Consider refactoring this to take a WebContents directly.
// crbug.com/668289
//
// The dialog displays |initiating_origin| to the user so that they can
// attribute the external protocol request to a site that initiated it. If an
// opaque origin (for example, an origin inside a sandboxed iframe) initiated
// the request, then |initiating_origin| should be set to the precursor origin
// (that is, the origin that created the opaque origin).
static void RunExternalProtocolDialog(
const GURL& url,
content::WebContents* web_contents,
......
......@@ -153,16 +153,26 @@ class ExternalProtocolHandlerTest : public testing::Test {
void DoTest(ExternalProtocolHandler::BlockState block_state,
shell_integration::DefaultWebClientState os_state,
Action expected_action) {
DoTest(block_state, os_state, expected_action,
GURL("mailto:test@test.com"));
DoTest(block_state, os_state, expected_action, GURL("mailto:test@test.com"),
url::Origin::Create(GURL("https://example.test")),
url::Origin::Create(GURL("https://precursor.test")));
}
// Launches |url| in the current WebContents and checks that the
// ExternalProtocolHandler's delegate is called with the correct action (as
// given in |expected_action|). |initiating_origin| is passed to the
// ExternalProtocolHandler to attribute the request to launch the URL to a
// particular site. If |initiating_origin| is opaque (in production, an
// example would be a sandboxed iframe), then the delegate should be passed
// the origin's precursor origin. The precursor origin is the origin that
// created |initiating_origin|, and the expected precursor origin, if any, is
// provided in |expected_initiating_precursor_origin|.
void DoTest(ExternalProtocolHandler::BlockState block_state,
shell_integration::DefaultWebClientState os_state,
Action expected_action,
const GURL& url) {
url::Origin initiating_origin =
url::Origin::Create(GURL("https://example.test"));
const GURL& url,
const url::Origin& initiating_origin,
const url::Origin& expected_initiating_precursor_origin) {
EXPECT_FALSE(delegate_.has_prompted());
EXPECT_FALSE(delegate_.has_launched());
EXPECT_FALSE(delegate_.has_blocked());
......@@ -182,7 +192,12 @@ class ExternalProtocolHandlerTest : public testing::Test {
EXPECT_EQ(expected_action == Action::BLOCK, delegate_.has_blocked());
if (expected_action == Action::PROMPT) {
ASSERT_TRUE(delegate_.initiating_origin().has_value());
EXPECT_EQ(initiating_origin, delegate_.initiating_origin().value());
if (initiating_origin.opaque()) {
EXPECT_EQ(expected_initiating_precursor_origin,
delegate_.initiating_origin().value());
} else {
EXPECT_EQ(initiating_origin, delegate_.initiating_origin().value());
}
} else {
EXPECT_FALSE(delegate_.initiating_origin().has_value());
}
......@@ -264,7 +279,8 @@ TEST_F(ExternalProtocolHandlerTest,
TEST_F(ExternalProtocolHandlerTest, TestUrlEscape) {
GURL url("alert:test message\" --bad%2B\r\n 文本 \"file");
DoTest(ExternalProtocolHandler::UNKNOWN, shell_integration::NOT_DEFAULT,
Action::PROMPT, url);
Action::PROMPT, url, url::Origin::Create(GURL("https://example.test")),
url::Origin::Create(GURL("https://precursor.test")));
// Expect that the "\r\n" has been removed, and all other illegal URL
// characters have been escaped.
EXPECT_EQ("alert:test%20message%22%20--bad%2B%20%E6%96%87%E6%9C%AC%20%22file",
......@@ -329,3 +345,15 @@ TEST_F(ExternalProtocolHandlerTest, TestSetBlockState) {
EXPECT_TRUE(
profile_->GetPrefs()->GetDictionary(prefs::kExcludedSchemes)->empty());
}
// Test that an opaque initiating origin gets transformed to its precursor
// origin when the dialog is shown.
TEST_F(ExternalProtocolHandlerTest, TestOpaqueInitiatingOrigin) {
url::Origin precursor_origin =
url::Origin::Create(GURL("https://precursor.test"));
url::Origin opaque_origin =
url::Origin::Resolve(GURL("data:text/html,hi"), precursor_origin);
DoTest(ExternalProtocolHandler::UNKNOWN, shell_integration::NOT_DEFAULT,
Action::PROMPT, GURL("mailto:test@test.test"), opaque_origin,
precursor_origin);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment