| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <algorithm> |
| #include <optional> |
| |
| #include "base/base64.h" |
| #include "base/base_paths.h" |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "base/files/file_path.h" |
| #include "base/functional/bind.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/metrics/field_trial_params.h" |
| #include "base/path_service.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/bind.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_command_line.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/interstitials/security_interstitial_page_test_utils.h" |
| #include "chrome/browser/net/system_network_context_manager.h" |
| #include "chrome/browser/policy/policy_test_utils.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_window.h" |
| #include "chrome/browser/safe_browsing/chrome_password_protection_service.h" |
| #include "chrome/browser/ssl/cert_verifier_browser_test.h" |
| #include "chrome/browser/ssl/chrome_security_state_tab_helper.h" |
| #include "chrome/browser/ssl/https_upgrades_util.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/password_manager/core/browser/password_manager_metrics_util.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/safe_browsing/content/browser/password_protection/password_protection_request_content.h" |
| #include "components/safe_browsing/content/browser/password_protection/password_protection_test_util.h" |
| #include "components/safe_browsing/core/browser/password_protection/metrics_util.h" |
| #include "components/safe_browsing/core/common/features.h" |
| #include "components/safe_browsing/core/common/proto/csd.pb.h" |
| #include "components/security_interstitials/content/security_interstitial_tab_helper.h" |
| #include "components/security_interstitials/content/ssl_blocking_page.h" |
| #include "components/security_interstitials/core/pref_names.h" |
| #include "components/security_state/core/security_state.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/file_select_listener.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/network_service_instance.h" |
| #include "content/public/browser/reload_type.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/ssl_status.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_contents_delegate.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/page_type.h" |
| #include "content/public/common/referrer.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/fenced_frame_test_util.h" |
| #include "content/public/test/navigation_handle_observer.h" |
| #include "content/public/test/prerender_test_util.h" |
| #include "content/public/test/signed_exchange_browser_test_helper.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "content/public/test/url_loader_interceptor.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "mojo/public/cpp/bindings/sync_call_restrictions.h" |
| #include "net/base/features.h" |
| #include "net/base/hash_value.h" |
| #include "net/base/net_errors.h" |
| #include "net/cert/cert_database.h" |
| #include "net/cert/cert_status_flags.h" |
| #include "net/cert/cert_verify_result.h" |
| #include "net/cert/ct_policy_status.h" |
| #include "net/cert/mock_cert_verifier.h" |
| #include "net/cert/x509_certificate.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/http/transport_security_state_test_util.h" |
| #include "net/ssl/ssl_cipher_suite_names.h" |
| #include "net/ssl/ssl_connection_status_flags.h" |
| #include "net/test/cert_test_util.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "net/test/embedded_test_server/request_handler_util.h" |
| #include "net/test/test_data_directory.h" |
| #include "services/network/public/cpp/features.h" |
| #include "services/network/public/mojom/network_service.mojom.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/boringssl/src/include/openssl/ssl.h" |
| |
| namespace { |
| |
| using password_manager::metrics_util::PasswordType; |
| using safe_browsing::LoginReputationClientResponse; |
| using safe_browsing::RequestOutcome; |
| |
| const char kCreateFilesystemUrlJavascript[] = |
| "new Promise(resolve => {" |
| " window.webkitRequestFileSystem(window.TEMPORARY, 4096, function(fs) {" |
| " fs.root.getFile('test.html', {create: true}, function(fileEntry) {" |
| " fileEntry.createWriter(function(writer) {" |
| " writer.onwriteend = function(e) {" |
| " resolve(fileEntry.toURL());" |
| " };" |
| " var blob = new Blob(['<html>hello</html>'], {type: 'text/html'});" |
| " writer.write(blob);" |
| " });" |
| " });" |
| " });" |
| "});"; |
| |
| const char kCreateBlobUrlJavascript[] = |
| "var blob = new Blob(['<html>hello</html>']," |
| " {type: 'text/html'});" |
| "URL.createObjectURL(blob);"; |
| |
| enum CertificateStatus { VALID_CERTIFICATE, INVALID_CERTIFICATE }; |
| |
| // Inject a script into every frame in the active page. Used by tests that check |
| // for visible password fields to wait for notifications about these fields. |
| // Notifications about visible password fields are queued at the end of the |
| // event loop, so waiting for a dummy script to run ensures that these |
| // notifications have been sent. |
| void InjectScript(content::WebContents* contents) { |
| // Any frame in the active page might have a password field, so inject scripts |
| // into all of them to ensure that notifications from all of them have been |
| // sent. |
| contents->GetPrimaryMainFrame()->ForEachRenderFrameHost( |
| [](content::RenderFrameHost* frame) { |
| EXPECT_EQ(true, content::EvalJs(frame, "true;")); |
| }); |
| } |
| |
| // A WebContentsObserver useful for testing the DidChangeVisibleSecurityState() |
| // method: it keeps track of the latest security level that was fired. |
| class SecurityStyleTestObserver : public content::WebContentsObserver { |
| public: |
| explicit SecurityStyleTestObserver(content::WebContents* web_contents) |
| : content::WebContentsObserver(web_contents) {} |
| |
| SecurityStyleTestObserver(const SecurityStyleTestObserver&) = delete; |
| SecurityStyleTestObserver& operator=(const SecurityStyleTestObserver&) = |
| delete; |
| |
| void DidChangeVisibleSecurityState() override { |
| auto* helper = SecurityStateTabHelper::FromWebContents(web_contents()); |
| latest_security_level_ = helper->GetSecurityLevel(); |
| run_loop_.Quit(); |
| } |
| |
| void WaitForDidChangeVisibleSecurityState() { run_loop_.Run(); } |
| |
| std::optional<security_state::SecurityLevel> latest_security_level() const { |
| return latest_security_level_; |
| } |
| |
| void ClearLatestSecurityLevel() { latest_security_level_ = std::nullopt; } |
| |
| private: |
| std::optional<security_state::SecurityLevel> latest_security_level_; |
| base::RunLoop run_loop_; |
| }; |
| |
| void CheckSecurityInfoForSecure( |
| content::WebContents* contents, |
| security_state::SecurityLevel expect_security_level, |
| bool expect_sha1_in_chain, |
| bool expect_displayed_mixed_content, |
| bool expect_ran_mixed_content, |
| bool expect_cert_error) { |
| ASSERT_TRUE(contents); |
| |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(contents); |
| ASSERT_TRUE(helper); |
| std::unique_ptr<security_state::VisibleSecurityState> visible_security_state = |
| helper->GetVisibleSecurityState(); |
| EXPECT_EQ(expect_security_level, helper->GetSecurityLevel()); |
| EXPECT_EQ(expect_sha1_in_chain, |
| security_state::IsSHA1InChain(*visible_security_state)); |
| EXPECT_EQ(expect_displayed_mixed_content, |
| visible_security_state->displayed_mixed_content); |
| EXPECT_EQ(expect_ran_mixed_content, |
| visible_security_state->ran_mixed_content); |
| EXPECT_TRUE( |
| security_state::IsSchemeCryptographic(visible_security_state->url)); |
| EXPECT_EQ(expect_cert_error, |
| net::IsCertStatusError(visible_security_state->cert_status)); |
| EXPECT_TRUE(visible_security_state->connection_info_initialized); |
| EXPECT_TRUE(visible_security_state->certificate); |
| } |
| |
| // Check that the current security state reflects a non-committed navigation. |
| void CheckSecurityInfoForNonCommitted(content::WebContents* contents) { |
| ASSERT_TRUE(contents); |
| |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(contents); |
| ASSERT_TRUE(helper); |
| std::unique_ptr<security_state::VisibleSecurityState> visible_security_state = |
| helper->GetVisibleSecurityState(); |
| EXPECT_EQ(security_state::NONE, helper->GetSecurityLevel()); |
| EXPECT_FALSE(security_state::IsSHA1InChain(*visible_security_state)); |
| EXPECT_FALSE(visible_security_state->displayed_mixed_content); |
| EXPECT_FALSE(visible_security_state->ran_mixed_content); |
| EXPECT_FALSE( |
| security_state::IsSchemeCryptographic(visible_security_state->url)); |
| EXPECT_FALSE(net::IsCertStatusError(visible_security_state->cert_status)); |
| EXPECT_FALSE(visible_security_state->connection_info_initialized); |
| EXPECT_FALSE(visible_security_state->certificate); |
| } |
| |
| void ProceedThroughInterstitial(content::WebContents* tab) { |
| content::TestNavigationObserver nav_observer(tab, 1); |
| security_interstitials::SecurityInterstitialTabHelper* helper = |
| security_interstitials::SecurityInterstitialTabHelper::FromWebContents( |
| tab); |
| helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting() |
| ->CommandReceived( |
| base::NumberToString(security_interstitials::CMD_PROCEED)); |
| nav_observer.Wait(); |
| } |
| |
| std::string GetFilePathWithHostAndPortReplacement( |
| const std::string& original_file_path, |
| const net::HostPortPair& host_port_pair) { |
| base::StringPairs replacement_text; |
| replacement_text.push_back( |
| make_pair("REPLACE_WITH_HOST_AND_PORT", host_port_pair.ToString())); |
| return net::test_server::GetFilePathWithReplacements(original_file_path, |
| replacement_text); |
| } |
| |
| GURL GetURLWithNonLocalHostname(net::EmbeddedTestServer* server, |
| const std::string& path) { |
| GURL::Replacements replace_host; |
| replace_host.SetHostStr("example1.test"); |
| return server->GetURL(path).ReplaceComponents(replace_host); |
| } |
| |
| class SecurityStateTabHelperTest : public CertVerifierBrowserTest { |
| public: |
| SecurityStateTabHelperTest() |
| : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) { |
| https_server_.ServeFilesFromSourceDirectory(GetChromeTestDataDir()); |
| SystemNetworkContextManager::SetEnableCertificateTransparencyForTesting( |
| true); |
| } |
| |
| SecurityStateTabHelperTest(const SecurityStateTabHelperTest&) = delete; |
| SecurityStateTabHelperTest& operator=(const SecurityStateTabHelperTest&) = |
| delete; |
| |
| ~SecurityStateTabHelperTest() override { |
| SystemNetworkContextManager::SetEnableCertificateTransparencyForTesting( |
| std::nullopt); |
| } |
| |
| void SetUpOnMainThread() override { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| ASSERT_TRUE(https_server_.Start()); |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| // Allow tests to trigger error pages. |
| host_resolver()->AddSimulatedFailure("nonexistent.test"); |
| } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| CertVerifierBrowserTest::SetUpCommandLine(command_line); |
| // Browser will both run and display insecure content. |
| command_line->AppendSwitch(switches::kAllowRunningInsecureContent); |
| } |
| |
| protected: |
| void SetUpMockCertVerifierForHttpsServer(net::CertStatus cert_status, |
| int net_result) { |
| scoped_refptr<net::X509Certificate> cert(https_server_.GetCertificate()); |
| net::CertVerifyResult verify_result; |
| verify_result.is_issued_by_known_root = false; |
| verify_result.verified_cert = cert; |
| verify_result.cert_status = cert_status; |
| |
| mock_cert_verifier()->AddResultForCert(cert, verify_result, net_result); |
| } |
| |
| // Navigates to an empty page and runs |javascript| to create a URL with with |
| // a scheme of |scheme|. Expects a security level of NONE if |
| // |use_secure_inner_origin| is true and DANGEROUS otherwise. |
| void TestBlobOrFilesystemURL(const std::string& scheme, |
| const std::string& javascript, |
| bool use_secure_inner_origin) { |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(contents); |
| |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(contents); |
| ASSERT_TRUE(helper); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), |
| GetURLWithNonLocalHostname( |
| use_secure_inner_origin ? &https_server_ : embedded_test_server(), |
| "/empty.html"))); |
| |
| // Create a URL and navigate to it. |
| std::string blob_or_filesystem_url = |
| content::EvalJs(contents, javascript).ExtractString(); |
| EXPECT_TRUE(GURL(blob_or_filesystem_url).SchemeIs(scheme)); |
| |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), GURL(blob_or_filesystem_url))); |
| InjectScript(contents); |
| |
| content::NavigationEntry* entry = |
| contents->GetController().GetVisibleEntry(); |
| ASSERT_TRUE(entry); |
| |
| EXPECT_EQ(use_secure_inner_origin ? security_state::NONE |
| : security_state::WARNING, |
| helper->GetSecurityLevel()); |
| } |
| |
| net::EmbeddedTestServer https_server_; |
| }; |
| |
| // Same as SecurityStateTabHelperTest, but with Incognito enabled. |
| class SecurityStateTabHelperIncognitoTest : public SecurityStateTabHelperTest { |
| public: |
| SecurityStateTabHelperIncognitoTest() : SecurityStateTabHelperTest() {} |
| |
| SecurityStateTabHelperIncognitoTest( |
| const SecurityStateTabHelperIncognitoTest&) = delete; |
| SecurityStateTabHelperIncognitoTest& operator=( |
| const SecurityStateTabHelperIncognitoTest&) = delete; |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| SecurityStateTabHelperTest::SetUpCommandLine(command_line); |
| // Test should run Incognito. |
| command_line->AppendSwitch(switches::kIncognito); |
| scoped_feature_list_.InitAndDisableFeature( |
| blink::features::kMixedContentAutoupgrade); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| class DidChangeVisibleSecurityStateTest |
| : public InProcessBrowserTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| DidChangeVisibleSecurityStateTest() |
| : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) { |
| https_server_.ServeFilesFromSourceDirectory(GetChromeTestDataDir()); |
| } |
| |
| DidChangeVisibleSecurityStateTest(const DidChangeVisibleSecurityStateTest&) = |
| delete; |
| DidChangeVisibleSecurityStateTest& operator=( |
| const DidChangeVisibleSecurityStateTest&) = delete; |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| // Browser will both run and display insecure content. |
| command_line->AppendSwitch(switches::kAllowRunningInsecureContent); |
| } |
| |
| void SetUpOnMainThread() override { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| } |
| |
| protected: |
| net::EmbeddedTestServer https_server_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, HttpPage) { |
| GURL http_url = |
| GetURLWithNonLocalHostname(embedded_test_server(), "/ssl/google.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), http_url)); |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(contents); |
| |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(contents); |
| ASSERT_TRUE(helper); |
| std::unique_ptr<security_state::VisibleSecurityState> visible_security_state = |
| helper->GetVisibleSecurityState(); |
| EXPECT_EQ(security_state::WARNING, helper->GetSecurityLevel()); |
| EXPECT_FALSE(security_state::IsSHA1InChain(*visible_security_state)); |
| EXPECT_FALSE(visible_security_state->displayed_mixed_content); |
| EXPECT_FALSE(visible_security_state->ran_mixed_content); |
| EXPECT_FALSE( |
| security_state::IsSchemeCryptographic(visible_security_state->url)); |
| EXPECT_FALSE(net::IsCertStatusError(visible_security_state->cert_status)); |
| // We expect that the security info fields for this committed navigation will |
| // be initialized to reflect the unencrypted connection. |
| EXPECT_TRUE(visible_security_state->connection_info_initialized); |
| EXPECT_FALSE(visible_security_state->certificate); |
| EXPECT_EQ(0, visible_security_state->connection_status); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, HttpsPage) { |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL("/ssl/google.html"))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::SECURE, false, false, false, |
| false /* expect cert status error */); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, DevToolsPage) { |
| GURL devtools_url("devtools://devtools/bundled/"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), devtools_url)); |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(contents); |
| |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(contents); |
| ASSERT_TRUE(helper); |
| std::unique_ptr<security_state::VisibleSecurityState> visible_security_state = |
| helper->GetVisibleSecurityState(); |
| EXPECT_EQ(security_state::NONE, helper->GetSecurityLevel()); |
| EXPECT_TRUE(visible_security_state->is_devtools); |
| } |
| |
| // Test security state after clickthrough for a SHA-1 certificate that is |
| // blocked by default. |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, SHA1CertificateBlocked) { |
| SetUpMockCertVerifierForHttpsServer( |
| net::CERT_STATUS_SHA1_SIGNATURE_PRESENT | |
| net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM, |
| net::ERR_CERT_WEAK_SIGNATURE_ALGORITHM); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| SecurityStyleTestObserver observer(web_contents); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL("/ssl/google.html"))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::DANGEROUS, true, false, false, |
| true /* expect cert status error */); |
| |
| ProceedThroughInterstitial( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::DANGEROUS, true, false, false, |
| true /* expect cert status error */); |
| } |
| |
| // Test security state for a SHA-1 certificate that is allowed by policy. |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, SHA1CertificateWarning) { |
| SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT, |
| net::OK); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| SecurityStyleTestObserver observer(web_contents); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL("/ssl/google.html"))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::NONE, true, false, false, |
| false /* expect cert status error */); |
| } |
| |
| class SecurityStateTabHelperTestWithAutoupgradesDisabled |
| : public SecurityStateTabHelperTest { |
| public: |
| SecurityStateTabHelperTestWithAutoupgradesDisabled() { |
| feature_list.InitAndDisableFeature( |
| blink::features::kMixedContentAutoupgrade); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithAutoupgradesDisabled, |
| MixedContent) { |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| |
| net::HostPortPair replacement_pair = embedded_test_server()->host_port_pair(); |
| replacement_pair.set_host("example.test"); |
| |
| // Navigate to an HTTPS page that displays mixed content. |
| std::string replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_displays_insecure_content.html", replacement_pair); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(replacement_path))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::WARNING, false, true, false, |
| false /* expect cert status error */); |
| |
| // Navigate to an HTTPS page that displays mixed content dynamically. |
| replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_with_dynamic_insecure_content.html", replacement_pair); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(replacement_path))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::SECURE, false, false, false, |
| false /* expect cert status error */); |
| // Load the insecure image. |
| EXPECT_EQ(true, content::EvalJs( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| "loadBadImage();")); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::WARNING, false, true, false, |
| false /* expect cert status error */); |
| |
| // Navigate to an HTTPS page that runs mixed content. |
| replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_runs_insecure_content.html", replacement_pair); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(replacement_path))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::DANGEROUS, false, false, true, |
| false /* expect cert status error */); |
| |
| // Navigate to an HTTPS page that runs and displays mixed content. |
| replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_runs_and_displays_insecure_content.html", replacement_pair); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(replacement_path))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::DANGEROUS, false, true, true, |
| false /* expect cert status error */); |
| |
| // Navigate to an HTTPS page that runs mixed content in an iframe. |
| net::HostPortPair host_port_pair = |
| net::HostPortPair::FromURL(https_server_.GetURL("/title1.html")); |
| host_port_pair.set_host("different-host.test"); |
| replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_runs_insecure_content_in_iframe.html", host_port_pair); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(replacement_path))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::DANGEROUS, false, false, true, |
| false /* expect cert status error */); |
| } |
| |
| class |
| SecurityStateTabHelperTestWithAutoupgradesDisabledAndMixedContentWarningEnabled |
| : public SecurityStateTabHelperTest { |
| public: |
| SecurityStateTabHelperTestWithAutoupgradesDisabledAndMixedContentWarningEnabled() { |
| feature_list.InitWithFeatures( |
| /* enabled_features */ |
| {}, |
| /* disabled_features */ |
| {blink::features::kMixedContentAutoupgrade}); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list; |
| }; |
| |
| // Checks that the proper security state is displayed when the passive mixed |
| // content warning feature is enabled. |
| // TODO(crbug.com/40108287): Once that launch is finished and other tests run |
| // with the feature enabled this test and the class will be redundant and can be |
| // removed. |
| IN_PROC_BROWSER_TEST_F( |
| SecurityStateTabHelperTestWithAutoupgradesDisabledAndMixedContentWarningEnabled, |
| PassiveMixedContentWarning) { |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| |
| net::HostPortPair replacement_pair = embedded_test_server()->host_port_pair(); |
| replacement_pair.set_host("example.test"); |
| |
| // Navigate to an HTTPS page that displays mixed content. |
| std::string replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_displays_insecure_content.html", replacement_pair); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(replacement_path))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::WARNING, false, true, false, |
| false /* expect cert status error */); |
| |
| // Navigate to an HTTPS page that displays mixed content dynamically. |
| replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_with_dynamic_insecure_content.html", replacement_pair); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(replacement_path))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::SECURE, false, false, false, |
| false /* expect cert status error */); |
| // Load the insecure image. |
| EXPECT_EQ(true, content::EvalJs( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| "loadBadImage();")); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::WARNING, false, true, false, |
| false /* expect cert status error */); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, |
| ActiveContentWithCertErrors) { |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| |
| // Navigate to an HTTPS page and simulate active content with |
| // certificate errors. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL("/title1.html"))); |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| content::NavigationEntry* entry = |
| web_contents->GetController().GetVisibleEntry(); |
| ASSERT_TRUE(entry); |
| entry->GetSSL().content_status |= |
| content::SSLStatus::RAN_CONTENT_WITH_CERT_ERRORS; |
| |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(web_contents); |
| ASSERT_TRUE(helper); |
| std::unique_ptr<security_state::VisibleSecurityState> visible_security_state = |
| helper->GetVisibleSecurityState(); |
| |
| EXPECT_FALSE(net::IsCertStatusError(visible_security_state->cert_status)); |
| EXPECT_EQ(security_state::DANGEROUS, helper->GetSecurityLevel()); |
| EXPECT_TRUE(visible_security_state->ran_content_with_cert_errors); |
| EXPECT_FALSE(visible_security_state->displayed_content_with_cert_errors); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, |
| PassiveContentWithCertErrors) { |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| |
| // Navigate to an HTTPS page and simulate passive content with |
| // certificate errors. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL("/title1.html"))); |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| content::NavigationEntry* entry = |
| web_contents->GetController().GetVisibleEntry(); |
| ASSERT_TRUE(entry); |
| entry->GetSSL().content_status |= |
| content::SSLStatus::DISPLAYED_CONTENT_WITH_CERT_ERRORS; |
| |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(web_contents); |
| ASSERT_TRUE(helper); |
| std::unique_ptr<security_state::VisibleSecurityState> visible_security_state = |
| helper->GetVisibleSecurityState(); |
| |
| EXPECT_FALSE(net::IsCertStatusError(visible_security_state->cert_status)); |
| EXPECT_EQ(security_state::NONE, helper->GetSecurityLevel()); |
| EXPECT_FALSE(visible_security_state->ran_content_with_cert_errors); |
| EXPECT_TRUE(visible_security_state->displayed_content_with_cert_errors); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, |
| ActiveAndPassiveContentWithCertErrors) { |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| |
| // Navigate to an HTTPS page and simulate active and passive content |
| // with certificate errors. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL("/title1.html"))); |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| content::NavigationEntry* entry = |
| web_contents->GetController().GetVisibleEntry(); |
| ASSERT_TRUE(entry); |
| entry->GetSSL().content_status |= |
| content::SSLStatus::RAN_CONTENT_WITH_CERT_ERRORS; |
| entry->GetSSL().content_status |= |
| content::SSLStatus::DISPLAYED_CONTENT_WITH_CERT_ERRORS; |
| |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(web_contents); |
| ASSERT_TRUE(helper); |
| std::unique_ptr<security_state::VisibleSecurityState> visible_security_state = |
| helper->GetVisibleSecurityState(); |
| |
| EXPECT_FALSE(net::IsCertStatusError(visible_security_state->cert_status)); |
| EXPECT_EQ(security_state::DANGEROUS, helper->GetSecurityLevel()); |
| EXPECT_TRUE(visible_security_state->ran_content_with_cert_errors); |
| EXPECT_TRUE(visible_security_state->displayed_content_with_cert_errors); |
| } |
| |
| // Same as SecurityStateTabHelperTest.ActiveAndPassiveContentWithCertErrors but |
| // with a SHA1 cert. |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithAutoupgradesDisabled, |
| MixedContentWithSHA1Cert) { |
| SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT, |
| net::OK); |
| |
| net::HostPortPair replacement_pair = embedded_test_server()->host_port_pair(); |
| replacement_pair.set_host("example.test"); |
| |
| // Navigate to an HTTPS page that displays mixed content. |
| std::string replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_displays_insecure_content.html", replacement_pair); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(replacement_path))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::NONE, true, true, false, |
| false /* expect cert status error */); |
| |
| // Navigate to an HTTPS page that displays mixed content dynamically. |
| replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_with_dynamic_insecure_content.html", replacement_pair); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(replacement_path))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::NONE, true, false, false, |
| false /* expect cert status error */); |
| // Load the insecure image. |
| EXPECT_EQ(true, content::EvalJs( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| "loadBadImage();")); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::NONE, true, true, false, |
| false /* expect cert status error */); |
| |
| // Navigate to an HTTPS page that runs mixed content. |
| replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_runs_insecure_content.html", replacement_pair); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(replacement_path))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::DANGEROUS, true, false, true, |
| false /* expect cert status error */); |
| |
| // Navigate to an HTTPS page that runs and displays mixed content. |
| replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_runs_and_displays_insecure_content.html", replacement_pair); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(replacement_path))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::DANGEROUS, true, true, true, |
| false /* expect cert status error */); |
| } |
| |
| // Tests that the Content Security Policy block-all-mixed-content |
| // directive stops mixed content from running. |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithAutoupgradesDisabled, |
| MixedContentStrictBlocking) { |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| |
| // Navigate to an HTTPS page that tries to run mixed content in an |
| // iframe, with strict mixed content blocking. |
| net::HostPortPair host_port_pair = |
| net::HostPortPair::FromURL(https_server_.GetURL("/title1.html")); |
| host_port_pair.set_host("different-host.test"); |
| std::string replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_runs_insecure_content_in_iframe_with_strict_blocking.html", |
| host_port_pair); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(replacement_path))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::SECURE, false, false, false, |
| false /* expect cert status error */); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithAutoupgradesDisabled, |
| BrokenHTTPS) { |
| SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_DATE_INVALID, |
| net::ERR_CERT_DATE_INVALID); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL("/ssl/google.html"))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::DANGEROUS, false, false, false, |
| true /* expect cert status error */); |
| |
| ProceedThroughInterstitial( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::DANGEROUS, false, false, false, |
| true /* expect cert status error */); |
| |
| // Navigate to a broken HTTPS page that displays mixed content. |
| std::string replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_displays_insecure_content.html", |
| embedded_test_server()->host_port_pair()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(replacement_path))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::DANGEROUS, false, true, false, |
| true /* expect cert status error */); |
| } |
| |
| // Tests that the security level of HTTP pages is set properly. |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, SecurityLevelForHttpPage) { |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(contents); |
| |
| SecurityStyleTestObserver observer(contents); |
| |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(contents); |
| ASSERT_TRUE(helper); |
| |
| GURL http_url = |
| GetURLWithNonLocalHostname(embedded_test_server(), "/title1.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), http_url)); |
| EXPECT_EQ(security_state::WARNING, helper->GetSecurityLevel()); |
| |
| content::NavigationEntry* entry = contents->GetController().GetVisibleEntry(); |
| ASSERT_TRUE(entry); |
| EXPECT_EQ(content::SSLStatus::NORMAL_CONTENT, entry->GetSSL().content_status); |
| } |
| |
| // Tests that the security level of data: URLs is always downgraded to |
| // WARNING. |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, |
| SecurityLevelDowngradedOnDataUrl) { |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(contents); |
| |
| SecurityStyleTestObserver observer(contents); |
| |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(contents); |
| ASSERT_TRUE(helper); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), GURL("data:text/html,<html></html>"))); |
| EXPECT_EQ(security_state::WARNING, helper->GetSecurityLevel()); |
| |
| content::NavigationEntry* entry = contents->GetController().GetVisibleEntry(); |
| ASSERT_TRUE(entry); |
| EXPECT_EQ(content::SSLStatus::NORMAL_CONTENT, entry->GetSSL().content_status); |
| } |
| |
| // Tests the security level and malicious content status for sign-in password |
| // reuse threat type. |
| IN_PROC_BROWSER_TEST_F( |
| SecurityStateTabHelperTest, |
| VerifySignInPasswordReuseMaliciousContentAndSecurityLevel) { |
| // Setup https server. This makes sure that the DANGEROUS security level is |
| // not caused by any certificate error rather than the password reuse SB |
| // threat type. |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(contents); |
| |
| SecurityStyleTestObserver observer(contents); |
| |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(contents); |
| ASSERT_TRUE(helper); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL("/ssl/google.html"))); |
| // Update security state of the current page to match |
| // SB_THREAT_TYPE_GAIA_PASSWORD_REUSE. |
| safe_browsing::ChromePasswordProtectionService* service = |
| safe_browsing::ChromePasswordProtectionService:: |
| GetPasswordProtectionService(browser()->profile()); |
| safe_browsing::ReusedPasswordAccountType account_type; |
| account_type.set_account_type( |
| safe_browsing::ReusedPasswordAccountType::GSUITE); |
| account_type.set_is_account_syncing(true); |
| scoped_refptr<safe_browsing::PasswordProtectionRequest> request = |
| safe_browsing::CreateDummyRequest(contents); |
| service->ShowModalWarning( |
| request.get(), LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED, |
| "unused_token", account_type); |
| observer.WaitForDidChangeVisibleSecurityState(); |
| |
| std::unique_ptr<security_state::VisibleSecurityState> visible_security_state = |
| helper->GetVisibleSecurityState(); |
| |
| EXPECT_EQ(security_state::DANGEROUS, helper->GetSecurityLevel()); |
| EXPECT_EQ( |
| security_state::MALICIOUS_CONTENT_STATUS_SIGNED_IN_SYNC_PASSWORD_REUSE, |
| visible_security_state->malicious_content_status); |
| |
| // Simulates a Gaia password change, then malicious content status will |
| // change to MALICIOUS_CONTENT_STATUS_SOCIAL_ENGINEERING. |
| service->OnGaiaPasswordChanged(/*username=*/"", false); |
| base::RunLoop().RunUntilIdle(); |
| visible_security_state = helper->GetVisibleSecurityState(); |
| EXPECT_EQ(security_state::DANGEROUS, helper->GetSecurityLevel()); |
| EXPECT_EQ(security_state::MALICIOUS_CONTENT_STATUS_SOCIAL_ENGINEERING, |
| visible_security_state->malicious_content_status); |
| } |
| |
| // Tests the security level and malicious content status for enterprise password |
| // reuse threat type. |
| IN_PROC_BROWSER_TEST_F( |
| SecurityStateTabHelperTest, |
| VerifyEnterprisePasswordReuseMaliciousContentAndSecurityLevel) { |
| // Setup https server. This makes sure that the DANGEROUS security level is |
| // not caused by any certificate error rather than the password reuse SB |
| // threat type. |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| SecurityStyleTestObserver observer(contents); |
| |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(contents); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL("/ssl/google.html"))); |
| // Update security state of the current page to match |
| // SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE. |
| safe_browsing::ChromePasswordProtectionService* service = |
| safe_browsing::ChromePasswordProtectionService:: |
| GetPasswordProtectionService(browser()->profile()); |
| scoped_refptr<safe_browsing::PasswordProtectionRequest> request = |
| safe_browsing::CreateDummyRequest(contents); |
| service->ShowModalWarning( |
| request.get(), LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED, |
| "unused_token", |
| service->GetPasswordProtectionReusedPasswordAccountType( |
| PasswordType::ENTERPRISE_PASSWORD, /*username=*/"")); |
| observer.WaitForDidChangeVisibleSecurityState(); |
| |
| std::unique_ptr<security_state::VisibleSecurityState> visible_security_state = |
| helper->GetVisibleSecurityState(); |
| EXPECT_EQ(security_state::DANGEROUS, helper->GetSecurityLevel()); |
| EXPECT_EQ(security_state::MALICIOUS_CONTENT_STATUS_ENTERPRISE_PASSWORD_REUSE, |
| visible_security_state->malicious_content_status); |
| |
| // Since these are non-Gaia enterprise passwords, Gaia password change won't |
| // have any impact here. |
| } |
| |
| class PKPModelClientTest : public SecurityStateTabHelperTest { |
| public: |
| static constexpr const char* kPKPHost = "example.test"; |
| |
| void SetUpOnMainThread() override { |
| // This test class intentionally does not call the parent SetUpOnMainThread. |
| |
| // Switch HTTPS server to use the "localhost" cert. The test mocks out cert |
| // verification results based on the used cert. |
| https_server_.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| ASSERT_TRUE(https_server_.Start()); |
| |
| host_resolver()->AddIPLiteralRule( |
| kPKPHost, https_server_.GetIPLiteralString(), std::string()); |
| |
| EnableStaticPins(); |
| } |
| |
| void TearDownOnMainThread() override { |
| mojo::ScopedAllowSyncCallForTesting allow_sync_call; |
| |
| mojo::Remote<network::mojom::NetworkServiceTest> network_service_test; |
| content::GetNetworkService()->BindTestInterfaceForTesting( |
| network_service_test.BindNewPipeAndPassReceiver()); |
| network_service_test->SetTransportSecurityStateSource(0); |
| |
| // This test class intentionally does not call the parent |
| // TearDownOnMainThread. |
| } |
| |
| private: |
| void EnableStaticPins() { |
| mojo::ScopedAllowSyncCallForTesting allow_sync_call; |
| |
| mojo::Remote<network::mojom::NetworkServiceTest> network_service_test; |
| content::GetNetworkService()->BindTestInterfaceForTesting( |
| network_service_test.BindNewPipeAndPassReceiver()); |
| // The tests don't depend on reporting, so the port doesn't matter. |
| network_service_test->SetTransportSecurityStateSource(80); |
| |
| content::StoragePartition* partition = |
| browser()->profile()->GetDefaultStoragePartition(); |
| partition->GetNetworkContext()->EnableStaticKeyPinningForTesting(); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(PKPModelClientTest, PKPBypass) { |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| SecurityStyleTestObserver observer(web_contents); |
| |
| scoped_refptr<net::X509Certificate> cert(https_server_.GetCertificate()); |
| net::CertVerifyResult verify_result; |
| // PKP is bypassed when |is_issued_by_known_root| is false. |
| verify_result.is_issued_by_known_root = false; |
| verify_result.verified_cert = cert; |
| // Public key hash which does not match the value in the static pin. |
| net::SHA256HashValue hash; |
| std::ranges::fill(hash, 1); |
| verify_result.public_key_hashes.push_back(hash); |
| |
| mock_cert_verifier()->AddResultForCert(cert, verify_result, net::OK); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(kPKPHost, "/ssl/google.html"))); |
| |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::SECURE, false, false, false, false); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PKPModelClientTest, PKPEnforced) { |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| SecurityStyleTestObserver observer(web_contents); |
| |
| scoped_refptr<net::X509Certificate> cert(https_server_.GetCertificate()); |
| net::CertVerifyResult verify_result; |
| // PKP requires |is_issued_by_known_root| to be true. |
| verify_result.is_issued_by_known_root = true; |
| verify_result.verified_cert = cert; |
| // Public key hash which does not match the value in the static pin. |
| net::SHA256HashValue hash; |
| std::ranges::fill(hash, 1); |
| verify_result.public_key_hashes.push_back(hash); |
| |
| mock_cert_verifier()->AddResultForCert(cert, verify_result, net::OK); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL(kPKPHost, "/ssl/google.html"))); |
| EXPECT_EQ(security_state::SecurityLevel::DANGEROUS, |
| observer.latest_security_level()); |
| } |
| |
| class SecurityStateLoadingTest : public SecurityStateTabHelperTest { |
| public: |
| SecurityStateLoadingTest() : SecurityStateTabHelperTest() {} |
| |
| SecurityStateLoadingTest(const SecurityStateLoadingTest&) = delete; |
| SecurityStateLoadingTest& operator=(const SecurityStateLoadingTest&) = delete; |
| |
| ~SecurityStateLoadingTest() override = default; |
| |
| protected: |
| void SetUpOnMainThread() override { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| } |
| }; |
| |
| // Tests that navigation state changes cause the security state to be |
| // updated. |
| IN_PROC_BROWSER_TEST_F(SecurityStateLoadingTest, NavigationStateChanges) { |
| ASSERT_TRUE(https_server_.Start()); |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| |
| // Navigate to an HTTPS page. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server_.GetURL("/ssl/google.html"))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::SECURE, false, false, false, |
| false /* expect cert status error */); |
| |
| // Navigate to a page that doesn't finish loading. Test that the |
| // security state is neutral while the page is loading. |
| browser()->OpenURL( |
| content::OpenURLParams( |
| embedded_test_server()->GetURL("/title1.html"), content::Referrer(), |
| WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false), |
| /*navigation_handle_callback=*/{}); |
| CheckSecurityInfoForNonCommitted( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| } |
| |
| // Tests the default security level on blob URLs. |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, |
| DefaultSecurityLevelOnBlobUrl) { |
| TestBlobOrFilesystemURL("blob", kCreateBlobUrlJavascript, |
| false /* use_secure_inner_origin */); |
| } |
| |
| // Same as DefaultSecurityLevelOnBlobUrl, but instead of a blob URL, |
| // this creates a filesystem URL. |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, |
| DefaultSecurityLevelOnFilesystemUrl) { |
| TestBlobOrFilesystemURL("filesystem", kCreateFilesystemUrlJavascript, |
| false /* use_secure_inner_origin */); |
| } |
| |
| // Tests the default security level on blob URLs with a secure inner origin. |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, |
| DefaultSecurityLevelOnSecureBlobUrl) { |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| TestBlobOrFilesystemURL("blob", kCreateBlobUrlJavascript, |
| true /* use_secure_inner_origin */); |
| } |
| |
| // Same as DefaultSecurityLevelOnBlobUrl, but instead of a blob URL, |
| // this creates a filesystem URL with a secure inner origin. |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, |
| DefaultSecurityLevelOnSecureFilesystemUrl) { |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| TestBlobOrFilesystemURL("filesystem", kCreateFilesystemUrlJavascript, |
| true /* use_secure_inner_origin */); |
| } |
| |
| // Tests that the security state for a WebContents is up to date when the |
| // WebContents is inserted into a Browser's TabStripModel. |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, AddedTab) { |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| |
| content::WebContents* tab = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(tab); |
| |
| std::unique_ptr<content::WebContents> new_contents = |
| content::WebContents::Create( |
| content::WebContents::CreateParams(tab->GetBrowserContext())); |
| content::NavigationController& controller = new_contents->GetController(); |
| ChromeSecurityStateTabHelper::CreateForWebContents(new_contents.get()); |
| CheckSecurityInfoForNonCommitted(new_contents.get()); |
| controller.LoadURL(https_server_.GetURL("/title1.html"), content::Referrer(), |
| ui::PAGE_TRANSITION_TYPED, std::string()); |
| EXPECT_TRUE(content::WaitForLoadStop(new_contents.get())); |
| CheckSecurityInfoForSecure(new_contents.get(), security_state::SECURE, false, |
| false, false, |
| false /* expect cert status error */); |
| |
| content::WebContents* raw_new_contents = new_contents.get(); |
| browser()->tab_strip_model()->InsertWebContentsAt(0, std::move(new_contents), |
| AddTabTypes::ADD_NONE); |
| CheckSecurityInfoForSecure(raw_new_contents, security_state::SECURE, false, |
| false, false, |
| false /* expect cert status error */); |
| } |
| |
| class DidChangeVisibleSecurityStateTestWithAutoupgradesDisabled |
| : public DidChangeVisibleSecurityStateTest { |
| public: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| DidChangeVisibleSecurityStateTest::SetUpCommandLine(command_line); |
| feature_list.InitAndDisableFeature( |
| blink::features::kMixedContentAutoupgrade); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list; |
| }; |
| |
| // Tests that the WebContentsObserver::DidChangeVisibleSecurityState event fires |
| // with the current level on HTTP, broken HTTPS, and valid HTTPS pages. |
| IN_PROC_BROWSER_TEST_F( |
| DidChangeVisibleSecurityStateTestWithAutoupgradesDisabled, |
| DidChangeVisibleSecurityStateObserver) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| ASSERT_TRUE(https_server_.Start()); |
| |
| net::EmbeddedTestServer https_test_server_expired( |
| net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server_expired.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED); |
| https_test_server_expired.ServeFilesFromSourceDirectory( |
| GetChromeTestDataDir()); |
| ASSERT_TRUE(https_test_server_expired.Start()); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| SecurityStyleTestObserver observer(web_contents); |
| |
| // Visit an HTTP url. |
| GURL http_url(embedded_test_server()->GetURL("/title1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), http_url)); |
| EXPECT_EQ(security_state::SecurityLevel::NONE, |
| observer.latest_security_level()); |
| |
| // Visit an (otherwise valid) HTTPS page that displays mixed content. |
| std::string replacement_path = GetFilePathWithHostAndPortReplacement( |
| "/ssl/page_displays_insecure_content.html", |
| embedded_test_server()->host_port_pair()); |
| |
| GURL mixed_content_url(https_server_.GetURL(replacement_path)); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), mixed_content_url)); |
| |
| EXPECT_EQ(security_state::SecurityLevel::WARNING, |
| observer.latest_security_level()); |
| |
| // Visit a broken HTTPS url. |
| GURL expired_url(https_test_server_expired.GetURL("/title1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), expired_url)); |
| |
| // An interstitial should show, and an event for the lock icon on the |
| // interstitial should fire. |
| ASSERT_TRUE( |
| chrome_browser_interstitials::IsShowingInterstitial(web_contents)); |
| EXPECT_EQ(security_state::SecurityLevel::DANGEROUS, |
| observer.latest_security_level()); |
| |
| // Before clicking through, navigate to a different page, and then go |
| // back to the interstitial. |
| GURL valid_https_url(https_server_.GetURL("/title1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), valid_https_url)); |
| EXPECT_EQ(security_state::SecurityLevel::SECURE, |
| observer.latest_security_level()); |
| |
| // After going back to the interstitial, an event for a broken lock |
| // icon should fire again. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), expired_url)); |
| ASSERT_TRUE( |
| chrome_browser_interstitials::IsShowingInterstitial(web_contents)); |
| EXPECT_EQ(security_state::SecurityLevel::DANGEROUS, |
| observer.latest_security_level()); |
| |
| // Since the next expected level is the same as the previous, clear |
| // the observer (to make sure that the event fires twice and we don't |
| // just see the previous event's level). |
| observer.ClearLatestSecurityLevel(); |
| |
| // Other conditions cannot be tested on this host after clicking |
| // through because once the interstitial is clicked through, all URLs |
| // for this host will remain in a broken state. |
| ProceedThroughInterstitial(web_contents); |
| EXPECT_EQ(security_state::SecurityLevel::DANGEROUS, |
| observer.latest_security_level()); |
| } |
| |
| // Visit a valid HTTPS page, then a broken HTTPS page, and then go back, |
| // and test that the observed security level matches. |
| IN_PROC_BROWSER_TEST_F(DidChangeVisibleSecurityStateTest, |
| DidChangeVisibleSecurityStateObserverGoBack) { |
| ASSERT_TRUE(https_server_.Start()); |
| |
| net::EmbeddedTestServer https_test_server_expired( |
| net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server_expired.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED); |
| https_test_server_expired.ServeFilesFromSourceDirectory( |
| GetChromeTestDataDir()); |
| ASSERT_TRUE(https_test_server_expired.Start()); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| SecurityStyleTestObserver observer(web_contents); |
| |
| // Visit a valid HTTPS url. |
| GURL valid_https_url(https_server_.GetURL("/title1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), valid_https_url)); |
| EXPECT_EQ(security_state::SecurityLevel::SECURE, |
| observer.latest_security_level()); |
| |
| // Navigate to a bad HTTPS page on a different host, and then click |
| // Back to verify that the previous good security level is seen again. |
| GURL expired_https_url(https_test_server_expired.GetURL("/title1.html")); |
| GURL::Replacements replace_host; |
| replace_host.SetHostStr("www.example_broken.test"); |
| GURL https_url_different_host = |
| expired_https_url.ReplaceComponents(replace_host); |
| |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), https_url_different_host)); |
| |
| ASSERT_TRUE( |
| chrome_browser_interstitials::IsShowingInterstitial(web_contents)); |
| EXPECT_EQ(security_state::SecurityLevel::DANGEROUS, |
| observer.latest_security_level()); |
| ProceedThroughInterstitial(web_contents); |
| EXPECT_EQ(security_state::SecurityLevel::DANGEROUS, |
| observer.latest_security_level()); |
| |
| content::LoadStopObserver back_nav_load_observer(web_contents); |
| chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB); |
| back_nav_load_observer.Wait(); |
| |
| EXPECT_EQ(security_state::SecurityLevel::SECURE, |
| observer.latest_security_level()); |
| } |
| |
| // Tests that the Not Secure chip does not show for error pages on http:// URLs. |
| // Regression test for https://crbug.com/760647. |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperIncognitoTest, HttpErrorPage) { |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(contents); |
| ASSERT_TRUE(helper); |
| |
| // Disable HTTPS upgrades on nonexistent.test for this test to work. |
| ScopedAllowHttpForHostnamesForTesting scoped_allow_http( |
| {"nonexistent.test"}, browser()->profile()->GetPrefs()); |
| |
| // Navigate to a URL that results in an error page. Even though the displayed |
| // URL is http://, there shouldn't be a Not Secure warning because the browser |
| // hasn't really navigated to an http:// page. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), |
| GURL("http://nonexistent.test:17"))); |
| // Sanity-check that it is indeed an error page. |
| content::NavigationEntry* entry = browser() |
| ->tab_strip_model() |
| ->GetActiveWebContents() |
| ->GetController() |
| .GetVisibleEntry(); |
| ASSERT_TRUE(entry); |
| ASSERT_EQ(content::PAGE_TYPE_ERROR, entry->GetPageType()); |
| |
| EXPECT_EQ(security_state::NONE, helper->GetSecurityLevel()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, |
| MixedFormsShowLockIfWarningsAreEnabled) { |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), |
| https_server_.GetURL("/ssl/page_displays_insecure_form.html"))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::SECURE, false, false, false, |
| false /* expect cert status error */); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, |
| MixedFormsDontShowLockIfWarningsAreDisabledByPolicy) { |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| |
| browser()->profile()->GetPrefs()->SetBoolean( |
| prefs::kMixedFormsWarningsEnabled, false); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), |
| https_server_.GetURL("/ssl/page_displays_insecure_form.html"))); |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::NONE, false, false, false, |
| false /* expect cert status error */); |
| } |
| |
| class SignedExchangeSecurityStateTest : public CertVerifierBrowserTest { |
| public: |
| SignedExchangeSecurityStateTest() = default; |
| ~SignedExchangeSecurityStateTest() override = default; |
| SignedExchangeSecurityStateTest(const SignedExchangeSecurityStateTest&) = |
| delete; |
| SignedExchangeSecurityStateTest& operator=( |
| const SignedExchangeSecurityStateTest&) = delete; |
| |
| protected: |
| void PreRunTestOnMainThread() override { |
| CertVerifierBrowserTest::PreRunTestOnMainThread(); |
| |
| sxg_test_helper_.InstallMockCert(mock_cert_verifier()); |
| sxg_test_helper_.InstallMockCertChainInterceptor(); |
| } |
| |
| private: |
| void SetUp() override { |
| sxg_test_helper_.SetUp(); |
| |
| CertVerifierBrowserTest::SetUp(); |
| } |
| |
| void TearDownOnMainThread() override { |
| sxg_test_helper_.TearDownOnMainThread(); |
| CertVerifierBrowserTest::TearDownOnMainThread(); |
| } |
| |
| content::SignedExchangeBrowserTestHelper sxg_test_helper_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(SignedExchangeSecurityStateTest, SecurityLevelIsSecure) { |
| embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data"); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| const GURL inner_url("https://test.example.org/test/"); |
| const GURL sxg_url = |
| embedded_test_server()->GetURL("/sxg/test.example.org_test.sxg"); |
| std::u16string expected_title = base::ASCIIToUTF16(inner_url.spec()); |
| content::TitleWatcher title_watcher(contents, expected_title); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), sxg_url)); |
| // The inner content of test.example.org_test.sxg has |
| // "<script> document.title = document.location.href; </script>". |
| EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); |
| ASSERT_EQ(inner_url, contents->GetLastCommittedURL()); |
| |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::SECURE, false /* expect_sha1_in_chain */, |
| false /* expect_displayed_mixed_content */, |
| false /* expect_ran_mixed_content */, false /* expect_cert_error */); |
| } |
| |
| // TODO(crbug.com/40921701): This test is failing on Mac12 Tests. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_SecurityLevelIsSecureAfterPrefetch \ |
| DISABLED_SecurityLevelIsSecureAfterPrefetch |
| #else |
| #define MAYBE_SecurityLevelIsSecureAfterPrefetch \ |
| SecurityLevelIsSecureAfterPrefetch |
| #endif // BUILDFLAG(IS_MAC) |
| IN_PROC_BROWSER_TEST_F(SignedExchangeSecurityStateTest, |
| MAYBE_SecurityLevelIsSecureAfterPrefetch) { |
| embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data"); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| const GURL inner_url("https://test.example.org/test/"); |
| const GURL sxg_url = |
| embedded_test_server()->GetURL("/sxg/test.example.org_test.sxg"); |
| |
| // prefetch.html prefetches the signed exchange. And the signed exchange will |
| // be served from PrefetchedSignedExchangeCache. |
| const GURL prefetch_html_url = embedded_test_server()->GetURL( |
| std::string("/sxg/prefetch.html#") + sxg_url.spec()); |
| { |
| std::u16string expected_title = u"OK"; |
| content::TitleWatcher title_watcher(contents, expected_title); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), prefetch_html_url)); |
| EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); |
| } |
| |
| { |
| std::u16string expected_title = base::ASCIIToUTF16(inner_url.spec()); |
| content::TitleWatcher title_watcher(contents, expected_title); |
| // Execute the JavaScript code to trigger the followup navigation from the |
| // current page. |
| EXPECT_TRUE(content::ExecJs( |
| contents, |
| base::StringPrintf("location.href = '%s';", sxg_url.spec().c_str()))); |
| // The inner content of test.example.org_test.sxg has |
| // "<script> document.title = document.location.href; </script>". |
| EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); |
| ASSERT_EQ(inner_url, contents->GetLastCommittedURL()); |
| } |
| |
| CheckSecurityInfoForSecure( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| security_state::SECURE, false /* expect_sha1_in_chain */, |
| false /* expect_displayed_mixed_content */, |
| false /* expect_ran_mixed_content */, false /* expect_cert_error */); |
| } |
| |
| class SecurityStateTabHelperPrerenderTest : public SecurityStateTabHelperTest { |
| public: |
| SecurityStateTabHelperPrerenderTest() |
| : prerender_helper_(base::BindRepeating( |
| &SecurityStateTabHelperPrerenderTest::web_contents, |
| base::Unretained(this))) { |
| // Disable mixed content autoupgrading to allow mixed content to load and |
| // test that mixed content is recorded correctly on the prerender navigation |
| // entry. |
| scoped_feature_list_.InitAndDisableFeature( |
| blink::features::kMixedContentAutoupgrade); |
| } |
| ~SecurityStateTabHelperPrerenderTest() override = default; |
| SecurityStateTabHelperPrerenderTest( |
| const SecurityStateTabHelperPrerenderTest&) = delete; |
| SecurityStateTabHelperPrerenderTest& operator=( |
| const SecurityStateTabHelperPrerenderTest&) = delete; |
| |
| void SetUp() override { |
| prerender_helper_.RegisterServerRequestMonitor(&https_server_); |
| SecurityStateTabHelperTest::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| web_contents_ = browser()->tab_strip_model()->GetActiveWebContents(); |
| SecurityStateTabHelperTest::SetUpOnMainThread(); |
| } |
| |
| void SetUpMockCertVerifierForHttpsServer(net::EmbeddedTestServer& test_server, |
| net::CertStatus cert_status, |
| int net_result) { |
| scoped_refptr<net::X509Certificate> cert(test_server.GetCertificate()); |
| net::CertVerifyResult verify_result; |
| verify_result.is_issued_by_known_root = false; |
| verify_result.verified_cert = cert; |
| verify_result.cert_status = cert_status; |
| |
| mock_cert_verifier()->AddResultForCert(cert, verify_result, net_result); |
| } |
| |
| content::WebContents* web_contents() { return web_contents_; } |
| |
| protected: |
| raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged> web_contents_ = |
| nullptr; |
| content::test::PrerenderTestHelper prerender_helper_; |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| // Tests that starting a prerender will not modify the existing security info |
| // and navigating to a previously prerendered URL may trigger a failure. |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperPrerenderTest, InvalidPrerender) { |
| // Start test server. |
| auto test_server = std::make_unique<net::EmbeddedTestServer>( |
| net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server->SetSSLConfig(net::EmbeddedTestServer::CERT_OK); |
| test_server->ServeFilesFromSourceDirectory(GetChromeTestDataDir()); |
| ASSERT_TRUE(test_server->Start()); |
| |
| // Setup a mock certificate verifier. |
| SetUpMockCertVerifierForHttpsServer(*test_server, 0, net::OK); |
| |
| // Load a valid HTTPS page. |
| auto primary_url = test_server->GetURL("/empty.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), primary_url)); |
| CheckSecurityInfoForSecure(web_contents(), security_state::SECURE, false, |
| false, false, |
| false /* expect cert status error */); |
| |
| // Shutdown server. |
| ASSERT_TRUE(test_server->ShutdownAndWaitUntilComplete()); |
| |
| // Start test server with invalid certificate. |
| auto port = test_server->port(); |
| test_server = std::make_unique<net::EmbeddedTestServer>( |
| net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server->SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED); |
| test_server->ServeFilesFromSourceDirectory(GetChromeTestDataDir()); |
| ASSERT_TRUE(test_server->Start(port)); |
| |
| // Setup a mock certificate verifier. |
| SetUpMockCertVerifierForHttpsServer( |
| *test_server, net::CERT_STATUS_DATE_INVALID, net::ERR_CERT_DATE_INVALID); |
| |
| // Try to prerender the page with an invalid certificate. |
| auto prerender_url = test_server->GetURL("/title1.html"); |
| content::test::PrerenderHostRegistryObserver registry_observer( |
| *web_contents()); |
| content::NavigationHandleObserver nav_observer(web_contents(), prerender_url); |
| prerender_helper_.AddPrerenderAsync(prerender_url); |
| |
| // Ensure that the prerender has started. |
| registry_observer.WaitForTrigger(prerender_url); |
| auto prerender_id = prerender_helper_.GetHostForUrl(prerender_url); |
| EXPECT_TRUE(prerender_id); |
| content::test::PrerenderHostObserver host_observer(*web_contents(), |
| prerender_id); |
| |
| // Since the prerender has not yet activated, the state should still be |
| // secure. |
| CheckSecurityInfoForSecure(web_contents(), security_state::SECURE, false, |
| false, false, |
| false /* expect cert status error */); |
| |
| // The prerender should be abandoned since the it commits an interstitial, |
| // wait for the PrerenderHost to be destroyed. |
| host_observer.WaitForDestroyed(); |
| EXPECT_TRUE(prerender_helper_.GetHostForUrl(prerender_url).is_null()); |
| |
| // Navigate to prerender_url and expect cert error. |
| prerender_helper_.NavigatePrimaryPage(prerender_url); |
| CheckSecurityInfoForSecure(web_contents(), security_state::DANGEROUS, false, |
| false, false, true /* expect cert status error */); |
| |
| // Make sure that the prerender was not activated. |
| EXPECT_FALSE(host_observer.was_activated()); |
| |
| // Shutdown server. |
| ASSERT_TRUE(test_server->ShutdownAndWaitUntilComplete()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperPrerenderTest, |
| PrerenderedMixedContent) { |
| // Start test server. |
| auto test_server = std::make_unique<net::EmbeddedTestServer>( |
| net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server->SetSSLConfig(net::EmbeddedTestServer::CERT_OK); |
| test_server->ServeFilesFromSourceDirectory(GetChromeTestDataDir()); |
| ASSERT_TRUE(test_server->Start()); |
| |
| // Setup a mock certificate verifier. |
| SetUpMockCertVerifierForHttpsServer(*test_server, 0, net::OK); |
| |
| // Load a valid HTTPS page. |
| auto primary_url = test_server->GetURL("/empty.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), primary_url)); |
| CheckSecurityInfoForSecure(web_contents(), security_state::SECURE, false, |
| false, false, |
| false /* expect cert status error */); |
| |
| // Load prerender page that displays mixed content. |
| auto prerender_url = |
| test_server->GetURL("/ssl/page_displays_insecure_content.html"); |
| prerender_helper_.AddPrerender(prerender_url); |
| auto prerender_id = prerender_helper_.GetHostForUrl(prerender_url); |
| EXPECT_TRUE(prerender_id); |
| content::test::PrerenderHostObserver host_observer(*web_contents(), |
| prerender_id); |
| |
| // Since the prerender has not yet activated, the state should still be |
| // secure. |
| CheckSecurityInfoForSecure(web_contents(), security_state::SECURE, false, |
| false, false, |
| false /* expect cert status error */); |
| |
| // Navigate to prerender_url and expect warning. |
| prerender_helper_.NavigatePrimaryPage(prerender_url); |
| CheckSecurityInfoForSecure(web_contents(), security_state::WARNING, false, |
| true, false, false /* expect cert status error */); |
| |
| // Make sure that the prerender was activated. |
| EXPECT_TRUE(host_observer.was_activated()); |
| |
| // Shutdown server. |
| ASSERT_TRUE(test_server->ShutdownAndWaitUntilComplete()); |
| } |
| |
| // For tests which use fenced frame. |
| class SecurityStateTabHelperFencedFrameTest |
| : public SecurityStateTabHelperTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| SecurityStateTabHelperFencedFrameTest() { |
| // Enable or disable MixedContentAutoupgrade feature based on the test |
| // parameter. |
| scoped_feature_list_.InitWithFeatureState( |
| blink::features::kMixedContentAutoupgrade, GetParam()); |
| } |
| ~SecurityStateTabHelperFencedFrameTest() override = default; |
| SecurityStateTabHelperFencedFrameTest( |
| const SecurityStateTabHelperFencedFrameTest&) = delete; |
| |
| SecurityStateTabHelperFencedFrameTest& operator=( |
| const SecurityStateTabHelperFencedFrameTest&) = delete; |
| |
| content::test::FencedFrameTestHelper& fenced_frame_test_helper() { |
| return fenced_frame_helper_; |
| } |
| |
| content::WebContents* web_contents() { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| bool IsAutoupgradeEnabled() { return GetParam(); } |
| |
| private: |
| content::test::FencedFrameTestHelper fenced_frame_helper_; |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| SecurityStateTabHelperFencedFrameTest, |
| testing::Bool()); |
| |
| IN_PROC_BROWSER_TEST_P(SecurityStateTabHelperFencedFrameTest, |
| ChangeSecurityStateWhenLoadingInsecureContent) { |
| // Setup a mock certificate verifier. |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| |
| // Load a valid HTTPS page. |
| auto primary_url = https_server_.GetURL("/empty.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), primary_url)); |
| CheckSecurityInfoForSecure(web_contents(), security_state::SECURE, false, |
| false, false, |
| false /* expect cert status error */); |
| |
| // Create a fenced frame with a page that displays insecure content. |
| GURL fenced_frame_url = |
| https_server_.GetURL("/ssl/page_displays_insecure_content.html"); |
| content::RenderFrameHost* fenced_frame_host = |
| fenced_frame_test_helper().CreateFencedFrame( |
| web_contents()->GetPrimaryMainFrame(), fenced_frame_url); |
| EXPECT_NE(nullptr, fenced_frame_host); |
| |
| if (IsAutoupgradeEnabled()) { |
| // The security state of the web contents should not be changed because |
| // the MixedContentAutoupgrade feature changes the internal insecure url |
| // from http to https. |
| CheckSecurityInfoForSecure(web_contents(), security_state::SECURE, false, |
| false, false, |
| false /* expect cert status error */); |
| } else { |
| // The security state of the web contents should be changed to WARNING. |
| CheckSecurityInfoForSecure(web_contents(), security_state::WARNING, false, |
| true, false, |
| false /* expect cert status error */); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SecurityStateTabHelperFencedFrameTest, |
| LoadFencedFrameViaInsecureURL) { |
| // Setup a mock certificate verifier. |
| SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| |
| // Load a valid HTTPS page. |
| auto primary_url = https_server_.GetURL("/empty.html"); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), primary_url)); |
| CheckSecurityInfoForSecure(web_contents(), security_state::SECURE, false, |
| false, false, |
| false /* expect cert status error */); |
| |
| // Create a fenced frame with an insecure url. |
| GURL fenced_frame_url = |
| embedded_test_server()->GetURL("b.com", "/fenced_frames/title1.html"); |
| |
| content::RenderFrameHost* fenced_frame_host = |
| fenced_frame_test_helper().CreateFencedFrame( |
| web_contents()->GetPrimaryMainFrame(), fenced_frame_url); |
| EXPECT_NE(nullptr, fenced_frame_host); |
| // Check that nothing has been loaded in the fenced frame. |
| EXPECT_EQ( |
| 0, content::EvalJs(fenced_frame_host, "document.body.childElementCount")); |
| |
| // Since we are blocking http content in a fenced frame, the security |
| // indicator should not change, and there should be no mixed content loaded. |
| CheckSecurityInfoForSecure(web_contents(), security_state::SECURE, false, |
| false, false /* expect no mixed content loaded */, |
| false /* expect cert status error */); |
| } |
| |
| } // namespace |