Index: content/browser/cross_site_transfer_browsertest.cc |
diff --git a/content/browser/cross_site_transfer_browsertest.cc b/content/browser/cross_site_transfer_browsertest.cc |
index 601909e0dc2ac1c70cd7a71f8744f5a47eee6428..f0c8e1fb22f96da918b8e325e366a122f17bbd28 100644 |
--- a/content/browser/cross_site_transfer_browsertest.cc |
+++ b/content/browser/cross_site_transfer_browsertest.cc |
@@ -15,6 +15,7 @@ |
#include "content/browser/loader/resource_dispatcher_host_impl.h" |
#include "content/public/browser/navigation_entry.h" |
#include "content/public/browser/navigation_handle.h" |
+#include "content/public/browser/render_frame_host.h" |
#include "content/public/browser/render_process_host.h" |
#include "content/public/browser/resource_dispatcher_host_delegate.h" |
#include "content/public/browser/resource_throttle.h" |
@@ -515,6 +516,97 @@ IN_PROC_BROWSER_TEST_P(CrossSiteTransferTest, PostWithFileData) { |
::testing::HasSubstr("form-data; name=\"file\"")); |
} |
+// Test that verifies that if navigation originator doesn't have access to a |
+// file, then no access is granted after a cross-process transfer of POST data. |
+// This is a regression test for https://crbug.com/726067. |
+// |
+// This test is somewhat similar to |
+// http/tests/navigation/form-targets-cross-site-frame-post.html layout test |
+// except that it 1) tests with files, 2) simulates a malicious scenario and 3) |
+// verifies file access (all of these 3 things are not possible with layout |
+// tests). |
+// |
+// This test is very similar to CrossSiteTransferTest.PostWithFileData above, |
+// except that it simulates a malicious form / POST originator. |
+IN_PROC_BROWSER_TEST_P(CrossSiteTransferTest, MaliciousPostWithFileData) { |
+ // The initial test window is a named form target. |
+ GURL initial_target_url( |
+ embedded_test_server()->GetURL("initial-target.com", "/title1.html")); |
+ EXPECT_TRUE(NavigateToURL(shell(), initial_target_url)); |
+ WebContents* target_contents = shell()->web_contents(); |
+ EXPECT_TRUE(ExecuteScript(target_contents, "window.name = 'form-target';")); |
+ |
+ // Create a new window containing a form targeting |target_contents|. |
+ GURL form_url(embedded_test_server()->GetURL( |
+ "main.com", "/form_that_posts_cross_site.html")); |
+ Shell* other_window = OpenPopup(target_contents, form_url, "form-window"); |
+ WebContents* form_contents = other_window->web_contents(); |
+ EXPECT_TRUE(ExecuteScript( |
+ form_contents, |
+ "document.getElementById('file-form').target = 'form-target';")); |
+ |
+ // Verify the current locations and process placement of |target_contents| |
+ // and |form_contents|. |
+ EXPECT_EQ(initial_target_url, target_contents->GetLastCommittedURL()); |
+ EXPECT_EQ(form_url, form_contents->GetLastCommittedURL()); |
+ EXPECT_NE(target_contents->GetMainFrame()->GetProcess()->GetID(), |
+ form_contents->GetMainFrame()->GetProcess()->GetID()); |
+ |
+ // Prepare a file to upload. |
+ base::ThreadRestrictions::ScopedAllowIO allow_io_for_temp_dir; |
+ base::ScopedTempDir temp_dir; |
+ base::FilePath file_path; |
+ std::string file_content("test-file-content"); |
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
+ ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.GetPath(), &file_path)); |
+ ASSERT_LT( |
+ 0, base::WriteFile(file_path, file_content.data(), file_content.size())); |
+ |
+ // Fill out the form to refer to the test file. |
+ std::unique_ptr<FileChooserDelegate> delegate( |
+ new FileChooserDelegate(file_path)); |
+ form_contents->Focus(); |
+ form_contents->SetDelegate(delegate.get()); |
+ EXPECT_TRUE( |
+ ExecuteScript(form_contents, "document.getElementById('file').click();")); |
+ EXPECT_TRUE(delegate->file_chosen()); |
+ ChildProcessSecurityPolicyImpl* security_policy = |
+ ChildProcessSecurityPolicyImpl::GetInstance(); |
+ EXPECT_TRUE(security_policy->CanReadFile( |
+ form_contents->GetMainFrame()->GetProcess()->GetID(), file_path)); |
+ |
+ // Simulate a malicious situation, where the renderer doesn't really have |
+ // access to the file. |
+ security_policy->RevokeAllPermissionsForFile( |
+ form_contents->GetMainFrame()->GetProcess()->GetID(), file_path); |
+ EXPECT_FALSE(security_policy->CanReadFile( |
+ form_contents->GetMainFrame()->GetProcess()->GetID(), file_path)); |
+ EXPECT_FALSE(security_policy->CanReadFile( |
+ target_contents->GetMainFrame()->GetProcess()->GetID(), file_path)); |
+ |
+ // Submit the form and wait until the malicious renderer gets killed. |
+ RenderProcessHostWatcher process_exit_observer( |
+ form_contents->GetMainFrame()->GetProcess(), |
+ RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
+ EXPECT_TRUE(ExecuteScript( |
+ form_contents, |
+ "setTimeout(\n" |
+ " function() { document.getElementById('file-form').submit(); },\n" |
+ " 0);")); |
+ process_exit_observer.Wait(); |
+ EXPECT_FALSE(process_exit_observer.did_exit_normally()); |
+ |
+ // The target frame should still be at the original location - the malicious |
+ // navigation should have been stopped. |
+ EXPECT_EQ(initial_target_url, target_contents->GetLastCommittedURL()); |
+ |
+ // Both processes still shouldn't have access. |
+ EXPECT_FALSE(security_policy->CanReadFile( |
+ form_contents->GetMainFrame()->GetProcess()->GetID(), file_path)); |
+ EXPECT_FALSE(security_policy->CanReadFile( |
+ target_contents->GetMainFrame()->GetProcess()->GetID(), file_path)); |
+} |
+ |
INSTANTIATE_TEST_CASE_P(CrossSiteTransferTest, |
CrossSiteTransferTest, |
::testing::Values(TestParameter::LOADING_WITHOUT_MOJO, |