Commit f9f9d7af authored by Ali Juma's avatar Ali Juma Committed by Commit Bot

[iOS] Disallow non-app-initiated navigations to file URLs

WKWebView and iOS do not permit navigation to arbitrary files on a
device's file system. Attempting to load such inaccessible files in
WKWebView can later trigger broken behavior (see crbug.com/1010526
for more details). To prevent this brokenness, disallow these
navigations right away, rather than waiting for WKWebView to disallow
them.

Bug: 1010526
Change-Id: Ieefb5befad659669a38efad127dfdb6e0179a165
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1881667
Commit-Queue: Ali Juma <ajuma@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#710125}
parent 337dc7a1
...@@ -670,6 +670,16 @@ enum class BackForwardNavigationType { ...@@ -670,6 +670,16 @@ enum class BackForwardNavigationType {
web::NavigationItem* item = self.currentNavItem; web::NavigationItem* item = self.currentNavItem;
GURL navigationURL = item ? item->GetURL() : GURL::EmptyGURL(); GURL navigationURL = item ? item->GetURL() : GURL::EmptyGURL();
GURL virtualURL = item ? item->GetVirtualURL() : GURL::EmptyGURL(); GURL virtualURL = item ? item->GetVirtualURL() : GURL::EmptyGURL();
// Do not attempt to navigate to file URLs that are typed into the
// omnibox.
if (navigationURL.SchemeIsFile() &&
!web::GetWebClient()->IsAppSpecificURL(virtualURL) &&
!IsRestoreSessionUrl(navigationURL)) {
[_delegate webRequestControllerStopLoading:self];
return;
}
// Set |item| to nullptr here to avoid any use-after-free issues, as it can // Set |item| to nullptr here to avoid any use-after-free issues, as it can
// be cleared by the call to -registerLoadRequestForURL below. // be cleared by the call to -registerLoadRequestForURL below.
item = nullptr; item = nullptr;
......
...@@ -67,6 +67,22 @@ const char kExpectedMimeType[] = "text/html"; ...@@ -67,6 +67,22 @@ const char kExpectedMimeType[] = "text/html";
const char kFailedTitle[] = "failed_title"; const char kFailedTitle[] = "failed_title";
// Location of a test page.
const char kTestPageURL[] = "/pony.html";
// A text string from the test HTML page at |kTestPageURL|.
const char kTestSessionStoragePageText[] = "pony";
// Returns a session storage with a single committed entry of a test HTML page.
CRWSessionStorage* GetTestSessionStorage(const GURL& testUrl) {
CRWSessionStorage* result = [[CRWSessionStorage alloc] init];
result.lastCommittedItemIndex = 0;
CRWNavigationItemStorage* item = [[CRWNavigationItemStorage alloc] init];
[item setVirtualURL:testUrl];
[result setItemStorages:@[ item ]];
return result;
}
// WebStateObserverTest is parameterized on this enum to test both // WebStateObserverTest is parameterized on this enum to test both
// LegacyNavigationManagerImpl and WKBasedNavigationManagerImpl. // LegacyNavigationManagerImpl and WKBasedNavigationManagerImpl.
enum NavigationManagerChoice { enum NavigationManagerChoice {
...@@ -2890,6 +2906,41 @@ TEST_P(WebStateObserverTest, RestoreSessionOnline) { ...@@ -2890,6 +2906,41 @@ TEST_P(WebStateObserverTest, RestoreSessionOnline) {
ASSERT_FALSE(navigation_manager()->CanGoBack()); ASSERT_FALSE(navigation_manager()->CanGoBack());
} }
// Tests that if a saved session is provided when creating a new WebState, it is
// restored after the first NavigationManager::LoadIfNecessary() call.
TEST_P(WebStateObserverTest, RestoredFromHistory) {
auto web_state = WebState::CreateWithStorageSession(
WebState::CreateParams(GetBrowserState()),
GetTestSessionStorage(test_server_->GetURL(kTestPageURL)));
ASSERT_FALSE(test::IsWebViewContainingText(web_state.get(),
kTestSessionStoragePageText));
web_state->GetNavigationManager()->LoadIfNecessary();
EXPECT_TRUE(test::WaitForWebViewContainingText(web_state.get(),
kTestSessionStoragePageText));
}
// Tests that NavigationManager::LoadIfNecessary() restores the page after
// disabling and re-enabling web usage.
TEST_P(WebStateObserverTest, DisableAndReenableWebUsage) {
auto web_state = WebState::CreateWithStorageSession(
WebState::CreateParams(GetBrowserState()),
GetTestSessionStorage(test_server_->GetURL(kTestPageURL)));
web_state->GetNavigationManager()->LoadIfNecessary();
ASSERT_TRUE(test::WaitForWebViewContainingText(web_state.get(),
kTestSessionStoragePageText));
web_state->SetWebUsageEnabled(false);
web_state->SetWebUsageEnabled(true);
// NavigationManager::LoadIfNecessary() should restore the page.
ASSERT_FALSE(test::IsWebViewContainingText(web_state.get(),
kTestSessionStoragePageText));
web_state->GetNavigationManager()->LoadIfNecessary();
EXPECT_TRUE(test::WaitForWebViewContainingText(web_state.get(),
kTestSessionStoragePageText));
}
// Tests successful navigation to a PDF file:// URL. // Tests successful navigation to a PDF file:// URL.
TEST_P(WebStateObserverTest, PdfFileUrlNavigation) { TEST_P(WebStateObserverTest, PdfFileUrlNavigation) {
// Construct a valid file:// URL. // Construct a valid file:// URL.
......
...@@ -48,31 +48,11 @@ using base::test::ios::kWaitForPageLoadTimeout; ...@@ -48,31 +48,11 @@ using base::test::ios::kWaitForPageLoadTimeout;
namespace web { namespace web {
namespace { namespace {
// A text string from the test HTML page in the session storage returned by
// GetTestSessionStorage().
const char kTestSessionStoragePageText[] = "pony";
// A text string that is included in |kTestPageHTML|. // A text string that is included in |kTestPageHTML|.
const char kTextInTestPageHTML[] = "this_is_a_test_string"; const char kTextInTestPageHTML[] = "this_is_a_test_string";
// A test page HTML containing |kTextInTestPageHTML|. // A test page HTML containing |kTextInTestPageHTML|.
const char kTestPageHTML[] = "<html><body>this_is_a_test_string</body><html>"; const char kTestPageHTML[] = "<html><body>this_is_a_test_string</body><html>";
// Returns a session storage with a single committed entry of a test HTML page.
CRWSessionStorage* GetTestSessionStorage() {
base::FilePath path;
base::PathService::Get(base::DIR_MODULE, &path);
path = path.Append(
FILE_PATH_LITERAL("ios/testing/data/http_server_files/pony.html"));
GURL testFileUrl(base::StringPrintf("file://%s", path.value().c_str()));
CRWSessionStorage* result = [[CRWSessionStorage alloc] init];
result.lastCommittedItemIndex = 0;
CRWNavigationItemStorage* item = [[CRWNavigationItemStorage alloc] init];
[item setVirtualURL:testFileUrl];
[result setItemStorages:@[ item ]];
return result;
}
} // namespace } // namespace
using wk_navigation_util::IsWKInternalUrl; using wk_navigation_util::IsWKInternalUrl;
...@@ -576,19 +556,6 @@ TEST_P(WebStateTest, CallReloadDuringSessionRestore) { ...@@ -576,19 +556,6 @@ TEST_P(WebStateTest, CallReloadDuringSessionRestore) {
})); }));
} }
// Tests that if a saved session is provided when creating a new WebState, it is
// restored after the first NavigationManager::LoadIfNecessary() call.
TEST_P(WebStateTest, RestoredFromHistory) {
auto web_state = WebState::CreateWithStorageSession(
WebState::CreateParams(GetBrowserState()), GetTestSessionStorage());
ASSERT_FALSE(test::IsWebViewContainingText(web_state.get(),
kTestSessionStoragePageText));
web_state->GetNavigationManager()->LoadIfNecessary();
EXPECT_TRUE(test::WaitForWebViewContainingText(web_state.get(),
kTestSessionStoragePageText));
}
// Verifies that each page title is restored. // Verifies that each page title is restored.
TEST_P(WebStateTest, RestorePageTitles) { TEST_P(WebStateTest, RestorePageTitles) {
// Create session storage. // Create session storage.
...@@ -626,26 +593,6 @@ TEST_P(WebStateTest, RestorePageTitles) { ...@@ -626,26 +593,6 @@ TEST_P(WebStateTest, RestorePageTitles) {
} }
} }
// Tests that NavigationManager::LoadIfNecessary() restores the page after
// disabling and re-enabling web usage.
TEST_P(WebStateTest, DisableAndReenableWebUsage) {
auto web_state = WebState::CreateWithStorageSession(
WebState::CreateParams(GetBrowserState()), GetTestSessionStorage());
web_state->GetNavigationManager()->LoadIfNecessary();
ASSERT_TRUE(test::WaitForWebViewContainingText(web_state.get(),
kTestSessionStoragePageText));
web_state->SetWebUsageEnabled(false);
web_state->SetWebUsageEnabled(true);
// NavigationManager::LoadIfNecessary() should restore the page.
ASSERT_FALSE(test::IsWebViewContainingText(web_state.get(),
kTestSessionStoragePageText));
web_state->GetNavigationManager()->LoadIfNecessary();
EXPECT_TRUE(test::WaitForWebViewContainingText(web_state.get(),
kTestSessionStoragePageText));
}
// Tests that loading an HTML page after a failed navigation works. // Tests that loading an HTML page after a failed navigation works.
TEST_P(WebStateTest, LoadChromeThenHTML) { TEST_P(WebStateTest, LoadChromeThenHTML) {
GURL app_specific_url( GURL app_specific_url(
...@@ -669,6 +616,14 @@ TEST_P(WebStateTest, LoadChromeThenHTML) { ...@@ -669,6 +616,14 @@ TEST_P(WebStateTest, LoadChromeThenHTML) {
test::WaitForWebViewContainingText(web_state(), kTextInTestPageHTML)); test::WaitForWebViewContainingText(web_state(), kTextInTestPageHTML));
} }
// Tests that loading an arbitrary file URL is a no-op.
TEST_P(WebStateTest, LoadFileURL) {
GURL file_url("file:///path/to/file.html");
web::NavigationManager::WebLoadParams load_params(file_url);
web_state()->GetNavigationManager()->LoadURLWithParams(load_params);
EXPECT_FALSE(web_state()->IsLoading());
}
// Tests that reloading after loading HTML page will load the online page. // Tests that reloading after loading HTML page will load the online page.
TEST_P(WebStateTest, LoadChromeThenWaitThenHTMLThenReload) { TEST_P(WebStateTest, LoadChromeThenWaitThenHTMLThenReload) {
net::EmbeddedTestServer server; net::EmbeddedTestServer server;
......
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