diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-10-14 11:58:26 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-10-14 11:52:44 +0000 |
commit | d627df149baae2da731ed8f0eeca6b91778d485c (patch) | |
tree | 86998c53502c6212516021779e85e6e63852fc26 | |
parent | 00d9e1e3be09a80f9c03302f86b77a9d849a822f (diff) | |
download | qtwebengine-chromium-d627df149baae2da731ed8f0eeca6b91778d485c.tar.gz |
[Backport] CVE-2019-5875
Pass renderer initiated flag for navigations triggered by cross-origin download
If a navigation is triggered by a cross-origin download initiated by
renderer, the navigation should carry the renderer initiated flag.
BUG=979443
(cherry picked from commit 6d7c9ec2923a71d3f9acb7fdd019a5e56f44c09b)
Change-Id: I0716193768a5473f70e909b7efbb5fc74933d89e
Reviewed-by: Xing Liu <xingliu@chromium.org>
Commit-Queue: Min Qin <qinmin@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#690869}
Reviewed-by: Min Qin <qinmin@chromium.org>
Cr-Commit-Position: refs/branch-heads/3865@{#714}
Cr-Branched-From: 0cdcc6158160790658d1f033d3db873603250124-refs/heads/master@{#681094}
Reviewed-by: Jüri Valdmann <juri.valdmann@qt.io>
6 files changed, 90 insertions, 1 deletions
diff --git a/chromium/components/download/internal/common/download_create_info.cc b/chromium/components/download/internal/common/download_create_info.cc index ebc8df19a11..1cd8bd169b0 100644 --- a/chromium/components/download/internal/common/download_create_info.cc +++ b/chromium/components/download/internal/common/download_create_info.cc @@ -31,7 +31,8 @@ DownloadCreateInfo::DownloadCreateInfo( accept_range(false), connection_info(net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN), method("GET"), - ukm_source_id(ukm::kInvalidSourceId) {} + ukm_source_id(ukm::kInvalidSourceId), + is_content_initiated(false) {} DownloadCreateInfo::DownloadCreateInfo() : DownloadCreateInfo(base::Time(), base::WrapUnique(new DownloadSaveInfo)) { diff --git a/chromium/components/download/internal/common/resource_downloader.cc b/chromium/components/download/internal/common/resource_downloader.cc index 751474fd56f..25158cae55d 100644 --- a/chromium/components/download/internal/common/resource_downloader.cc +++ b/chromium/components/download/internal/common/resource_downloader.cc @@ -129,6 +129,7 @@ ResourceDownloader::ResourceDownloader( delegate_task_runner_(task_runner), url_loader_factory_getter_(std::move(url_loader_factory_getter)), url_security_policy_(url_security_policy), + is_content_initiated_(false), weak_ptr_factory_(this) {} ResourceDownloader::~ResourceDownloader() = default; @@ -139,6 +140,7 @@ void ResourceDownloader::Start( callback_ = download_url_parameters->callback(); upload_callback_ = download_url_parameters->upload_callback(); guid_ = download_url_parameters->guid(); + is_content_initiated_ = download_url_parameters->content_initiated(); // Set up the URLLoaderClient. url_loader_client_ = std::make_unique<DownloadResponseHandler>( @@ -214,6 +216,7 @@ void ResourceDownloader::OnResponseStarted( download_create_info->render_process_id = render_process_id_; download_create_info->render_frame_id = render_frame_id_; download_create_info->has_user_gesture = resource_request_->has_user_gesture; + download_create_info->is_content_initiated = is_content_initiated_; delegate_task_runner_->PostTask( FROM_HERE, diff --git a/chromium/components/download/internal/common/resource_downloader.h b/chromium/components/download/internal/common/resource_downloader.h index 63664cfbe8f..0e2477411ba 100644 --- a/chromium/components/download/internal/common/resource_downloader.h +++ b/chromium/components/download/internal/common/resource_downloader.h @@ -154,6 +154,9 @@ class COMPONENTS_DOWNLOAD_EXPORT ResourceDownloader // Used to check if the URL is safe to request. URLSecurityPolicy url_security_policy_; + // Whether download is initated by the content on the page. + bool is_content_initiated_; + base::WeakPtrFactory<ResourceDownloader> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(ResourceDownloader); diff --git a/chromium/components/download/public/common/download_create_info.h b/chromium/components/download/public/common/download_create_info.h index 7c82695416b..ec793ca4218 100644 --- a/chromium/components/download/public/common/download_create_info.h +++ b/chromium/components/download/public/common/download_create_info.h @@ -166,6 +166,9 @@ struct COMPONENTS_DOWNLOAD_EXPORT DownloadCreateInfo { // Source of the download, used in metrics. DownloadSource download_source = DownloadSource::UNKNOWN; + // Whether download is initated by the content on the page. + bool is_content_initiated; + private: DISALLOW_COPY_AND_ASSIGN(DownloadCreateInfo); }; diff --git a/chromium/content/browser/download/download_browsertest.cc b/chromium/content/browser/download/download_browsertest.cc index 6cdb9cd8d52..ed9e9c1de83 100644 --- a/chromium/content/browser/download/download_browsertest.cc +++ b/chromium/content/browser/download/download_browsertest.cc @@ -620,6 +620,38 @@ class ErrorStreamCountingObserver : download::DownloadItem::Observer { base::Closure completion_closure_; }; +// Class to wait for a WebContents to kick off a specified number of +// navigations. +class NavigationStartObserver : public WebContentsObserver { + public: + explicit NavigationStartObserver(WebContents* web_contents) + : WebContentsObserver(web_contents) {} + ~NavigationStartObserver() override {} + + void WaitForFinished(int navigation_count) { + if (start_count_ >= navigation_count) + return; + navigation_count_ = navigation_count; + base::RunLoop run_loop; + completion_closure_ = run_loop.QuitClosure(); + run_loop.Run(); + } + + private: + // WebContentsObserver implementations. + void DidStartNavigation(NavigationHandle* navigation_handle) override { + start_count_++; + if (start_count_ >= navigation_count_ && !completion_closure_.is_null()) { + std::move(completion_closure_).Run(); + } + } + + int navigation_count_ = 0; + int start_count_ = 0; + base::Closure completion_closure_; + DISALLOW_COPY_AND_ASSIGN(NavigationStartObserver); +}; + bool IsDownloadInState(download::DownloadItem::DownloadState state, download::DownloadItem* item) { return item->GetState() == state; @@ -3146,8 +3178,54 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, std::vector<download::DownloadItem*> downloads; DownloadManagerForShell(shell())->GetAllDownloads(&downloads); ASSERT_EQ(0u, downloads.size()); + ASSERT_TRUE(origin_one.ShutdownAndWaitUntilComplete()); + ASSERT_TRUE(origin_two.ShutdownAndWaitUntilComplete()); +} + +// Tests that if a renderer initiated download triggers cross origin in the +// redirect chain, the visible URL of the current tab shouldn't change. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, + DownloadAttributeSameOriginRedirectNavigationTimeOut) { + net::EmbeddedTestServer origin_one; + net::EmbeddedTestServer origin_two; + ASSERT_TRUE(origin_one.InitializeAndListen()); + ASSERT_TRUE(origin_two.InitializeAndListen()); + // The download-attribute.html page contains an anchor element whose href is + // set to the value of the query parameter (specified as |target| in the URL + // below). The suggested filename for the anchor is 'suggested-filename'. When + // the page is loaded, a script simulates a click on the anchor, triggering a + // download of the target URL. + // + // We construct two test servers; origin_one and origin_two. Once started, the + // server URLs will differ by the port number. Therefore they will be in + // different origins. + GURL download_url = origin_one.GetURL("/ping"); + GURL referrer_url = origin_one.GetURL( + std::string("/download-attribute.html?target=") + download_url.spec()); + origin_one.ServeFilesFromDirectory(GetTestFilePath("download", "")); + + // <origin_one>/download-attribute.html initiates a download of + // <origin_one>/ping, which redirects to <origin_two>/download. The latter + // will time out. + origin_one.RegisterRequestHandler( + CreateRedirectHandler("/ping", origin_two.GetURL("/download"))); + + origin_one.StartAcceptingConnections(); + + NavigationStartObserver obs(shell()->web_contents()); + NavigationController::LoadURLParams params(referrer_url); + params.transition_type = ui::PageTransitionFromInt( + ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR); + shell()->web_contents()->GetController().LoadURLWithParams(params); + shell()->web_contents()->Focus(); + + // Waiting for 2 navigation to happen, one for the original request, one for + // the redirect. + obs.WaitForFinished(2); + EXPECT_EQ(referrer_url, shell()->web_contents()->GetVisibleURL()); ASSERT_TRUE(origin_one.ShutdownAndWaitUntilComplete()); + origin_two.StartAcceptingConnections(); ASSERT_TRUE(origin_two.ShutdownAndWaitUntilComplete()); } diff --git a/chromium/content/browser/download/download_manager_impl.cc b/chromium/content/browser/download/download_manager_impl.cc index a68e0c52394..771bcf5ac57 100644 --- a/chromium/content/browser/download/download_manager_impl.cc +++ b/chromium/content/browser/download/download_manager_impl.cc @@ -532,6 +532,7 @@ bool DownloadManagerImpl::InterceptDownload( params.frame_tree_node_id = RenderFrameHost::GetFrameTreeNodeIdForRoutingId( info.render_process_id, info.render_frame_id); + params.is_renderer_initiated = info.is_content_initiated; web_contents->GetController().LoadURLWithParams(params); } if (info.request_handle) |