chromium/content/browser/loader/file_url_loader_factory_browsertest.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/memory/raw_ptr.h"

// This must be before Windows headers
#include "base/functional/callback_helpers.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"

#if BUILDFLAG(IS_WIN)
#include <objbase.h>

#include <windows.h>

#include <shlobj.h>
#include <wrl/client.h>
#endif

#include <string>

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/shell/browser/shell.h"
#include "net/base/filename_util.h"
#include "net/base/net_errors.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/gtest_util.h"
#include "url/gurl.h"

namespace content {
namespace {

const char16_t kSuccessTitle[] =;
const char16_t kErrorTitle[] =;

base::FilePath TestFilePath() {}

base::FilePath AbsoluteFilePath(const base::FilePath& file_path) {}

class TestFileAccessContentBrowserClient
    : public ContentBrowserTestContentBrowserClient {};

// This class contains integration tests for file URLs.
class FileURLLoaderFactoryBrowserTest : public ContentBrowserTest {};

IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryBrowserTest, Basic) {}

IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryBrowserTest, FileAccessNotAllowed) {}

#if BUILDFLAG(IS_POSIX)

// Test symbolic links on POSIX platforms. These act like the contents of
// the symbolic link are the same as the contents of the file it links to.
IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryBrowserTest, SymlinksToFiles) {}

#elif BUILDFLAG(IS_WIN)

// Test shortcuts on Windows. These are treated as redirects.
IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryBrowserTest, ResolveShortcutTest) {
  TestFileAccessContentBrowserClient test_browser_client;

  // Create an empty temp directory, to be sure there's no file in it.
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());

  base::FilePath lnk_path =
      temp_dir.GetPath().Append(FILE_PATH_LITERAL("foo.lnk"));

  base::FilePath test = TestFilePath();

  // Create a shortcut for the test.
  {
    Microsoft::WRL::ComPtr<IShellLink> shell;
    ASSERT_TRUE(SUCCEEDED(::CoCreateInstance(
        CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shell))));
    Microsoft::WRL::ComPtr<IPersistFile> persist;
    ASSERT_TRUE(SUCCEEDED(shell.As<IPersistFile>(&persist)));
    EXPECT_TRUE(SUCCEEDED(shell->SetPath(TestFilePath().value().c_str())));
    EXPECT_TRUE(SUCCEEDED(shell->SetDescription(L"ResolveShortcutTest")));
    std::wstring lnk_string = lnk_path.value();
    EXPECT_TRUE(SUCCEEDED(persist->Save(lnk_string.c_str(), TRUE)));
  }

  EXPECT_TRUE(NavigateToURL(
      shell(), net::FilePathToFileURL(lnk_path),
      net::FilePathToFileURL(TestFilePath()) /* expect_commit_url */));
  EXPECT_EQ(kSuccessTitle, shell()->web_contents()->GetTitle());

  ASSERT_EQ(2u, test_browser_client.access_allowed_args().size());
  EXPECT_EQ(lnk_path, test_browser_client.access_allowed_args()[0].path);
  EXPECT_EQ(AbsoluteFilePath(lnk_path),
            test_browser_client.access_allowed_args()[0].absolute_path);
  EXPECT_EQ(ProfilePath(),
            test_browser_client.access_allowed_args()[0].profile_path);

  EXPECT_EQ(TestFilePath(), test_browser_client.access_allowed_args()[1].path);
  EXPECT_EQ(AbsoluteFilePath(TestFilePath()),
            test_browser_client.access_allowed_args()[1].absolute_path);
  EXPECT_EQ(ProfilePath(),
            test_browser_client.access_allowed_args()[1].profile_path);

  // Test the case where access to the shortcut URL is blocked. Should display
  // an error page at the shortcut's file URL.

  test_browser_client.ClearAccessAllowedArgs();
  test_browser_client.set_blocked_path(lnk_path);

  TestNavigationObserver navigation_observer2(shell()->web_contents());
  EXPECT_FALSE(NavigateToURL(shell(), net::FilePathToFileURL(lnk_path)));
  EXPECT_FALSE(navigation_observer2.last_navigation_succeeded());
  EXPECT_THAT(navigation_observer2.last_net_error_code(),
              net::test::IsError(net::ERR_ACCESS_DENIED));
  EXPECT_EQ(net::FilePathToFileURL(lnk_path),
            shell()->web_contents()->GetURL());
  EXPECT_EQ(kErrorTitle, shell()->web_contents()->GetTitle());

  ASSERT_EQ(1u, test_browser_client.access_allowed_args().size());
  EXPECT_EQ(lnk_path, test_browser_client.access_allowed_args()[0].path);
  EXPECT_EQ(AbsoluteFilePath(lnk_path),
            test_browser_client.access_allowed_args()[0].absolute_path);
  EXPECT_EQ(ProfilePath(),
            test_browser_client.access_allowed_args()[0].profile_path);

  // Test the case where access to the destination URL is blocked. The redirect
  // is followed, so this should end up at the shortcut destination, but
  // displaying an error.

  test_browser_client.ClearAccessAllowedArgs();
  test_browser_client.set_blocked_path(TestFilePath());

  TestNavigationObserver navigation_observer3(shell()->web_contents());
  EXPECT_FALSE(NavigateToURL(shell(), net::FilePathToFileURL(lnk_path)));
  EXPECT_FALSE(navigation_observer3.last_navigation_succeeded());
  EXPECT_THAT(navigation_observer3.last_net_error_code(),
              net::test::IsError(net::ERR_ACCESS_DENIED));
  EXPECT_EQ(net::FilePathToFileURL(TestFilePath()),
            shell()->web_contents()->GetURL());
  EXPECT_EQ(kErrorTitle, shell()->web_contents()->GetTitle());

  ASSERT_EQ(2u, test_browser_client.access_allowed_args().size());
  EXPECT_EQ(lnk_path, test_browser_client.access_allowed_args()[0].path);
  EXPECT_EQ(AbsoluteFilePath(lnk_path),
            test_browser_client.access_allowed_args()[0].absolute_path);
  EXPECT_EQ(ProfilePath(),
            test_browser_client.access_allowed_args()[0].profile_path);

  EXPECT_EQ(TestFilePath(), test_browser_client.access_allowed_args()[1].path);
  EXPECT_EQ(AbsoluteFilePath(TestFilePath()),
            test_browser_client.access_allowed_args()[1].absolute_path);
  EXPECT_EQ(ProfilePath(),
            test_browser_client.access_allowed_args()[1].profile_path);
}

#endif  // BUILDFLAG(IS_WIN)

IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryBrowserTest,
                       RedirectToFileUrlMainFrame) {}

IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryBrowserTest,
                       RedirectToFileUrlFetch) {}

IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryBrowserTest,
                       RedirectToFileUrlSubFrame) {}

// The test below opens a |popup| in a way that doesn't trigger a
// RenderFrameImpl::CommitNavigation.  This means that the popup will depend on
// inheriting URLLoaderFactoryBundle from the opener.
//
// Before triggering any subresource fetches in the popup, the test closes the
// opener window.  If the URLLoaderFactoryBundle hasn't been inherited at this
// point yet, then we might run into trouble later.  Another way how we might
// get into trouble is if FileURLLoaderFactory's clone doesn't survive closing
// of the opener.
//
// Finally, the test triggers a subresource fetch in the popup.  We expect that
// this fetch should use the right URLLoaderFactoryBundle - one that contains
// a functional FileURLLoaderFactory.
//
// This is a regression test for https://crbug.com/1106995
IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryBrowserTest,
                       LifetimeOfClonedFactory) {}

class FileURLLoaderFactoryDisabledSecurityBrowserTest
    : public FileURLLoaderFactoryBrowserTest {};

// Test whether sandboxed file: frames still can access file: subresources.
IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryDisabledSecurityBrowserTest,
                       SubresourcesInSandboxedFileFrame) {}

IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryBrowserTest, LastModified) {}

}  // namespace
}  // namespace content