blob: 32f738c815ca3384becaee3d7916b4d197273d9a [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h"
#include <stdint.h>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/simple_test_clock.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ssl/stateful_ssl_host_state_delegate_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/browsing_data/core/browsing_data_utils.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/guest_view/browser/test_guest_view_manager.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/ssl_host_state_delegate.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/browsing_data_remover_test_util.h"
#include "extensions/browser/app_window/app_window.h"
#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/common/switches.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const char kOkCertFile[] = "ok_cert.pem";
const char kWWWGoogleHost[] = "www.google.com";
const char kGoogleHost[] = "google.com";
const char kExampleHost[] = "example.com";
const uint64_t kDeltaOneDayInSeconds = UINT64_C(86400);
const uint64_t kDeltaOneWeekInSeconds = UINT64_C(604800);
scoped_refptr<net::X509Certificate> GetOkCert() {
return net::ImportCertFromFile(net::GetTestCertsDirectory(), kOkCertFile);
}
bool CStrStringMatcher(const char* a, const std::string& b) {
return a == b;
}
} // namespace
class StatefulSSLHostStateDelegateTest : public InProcessBrowserTest {};
// StatefulSSLHostStateDelegateTest tests basic unit test functionality of the
// SSLHostStateDelegate class. For example, tests that if a certificate is
// accepted, then it is added to queryable, and if it is revoked, it is not
// queryable. Even though it is effectively a unit test, in needs to be an
// InProcessBrowserTest because the actual functionality is provided by
// StatefulSSLHostStateDelegate which is provided per-profile.
//
// QueryPolicy unit tests the expected behavior of calling QueryPolicy on the
// SSLHostStateDelegate class after various SSL cert decisions have been made.
IN_PROC_BROWSER_TEST_F(StatefulSSLHostStateDelegateTest, QueryPolicy) {
scoped_refptr<net::X509Certificate> cert = GetOkCert();
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
// Verifying that all three of the certs we will be looking at are denied
// before any action has been taken.
EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
EXPECT_EQ(
content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kGoogleHost, *cert, net::ERR_CERT_DATE_INVALID, tab));
EXPECT_EQ(
content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kExampleHost, *cert, net::ERR_CERT_DATE_INVALID, tab));
// Simulate a user decision to allow an invalid certificate exception for
// kWWWGoogleHost.
state->AllowCert(kWWWGoogleHost, *cert, net::ERR_CERT_DATE_INVALID, tab);
// Verify that only kWWWGoogleHost is allowed and that the other two certs
// being tested still are denied.
EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
EXPECT_EQ(
content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kGoogleHost, *cert, net::ERR_CERT_DATE_INVALID, tab));
EXPECT_EQ(
content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kExampleHost, *cert, net::ERR_CERT_DATE_INVALID, tab));
// Simulate a user decision to allow an invalid certificate exception for
// kExampleHost.
state->AllowCert(kExampleHost, *cert, net::ERR_CERT_DATE_INVALID, tab);
// Verify that both kWWWGoogleHost and kExampleHost have allow exceptions
// while kGoogleHost still is denied.
EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
EXPECT_EQ(
content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kGoogleHost, *cert, net::ERR_CERT_DATE_INVALID, tab));
EXPECT_EQ(
content::SSLHostStateDelegate::ALLOWED,
state->QueryPolicy(kExampleHost, *cert, net::ERR_CERT_DATE_INVALID, tab));
}
// HasPolicyAndRevoke unit tests the expected behavior of calling
// HasAllowException before and after calling RevokeUserAllowExceptions on the
// SSLHostStateDelegate class.
IN_PROC_BROWSER_TEST_F(StatefulSSLHostStateDelegateTest, HasPolicyAndRevoke) {
scoped_refptr<net::X509Certificate> cert = GetOkCert();
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
StatefulSSLHostStateDelegate* state =
StatefulSSLHostStateDelegateFactory::GetForProfile(profile);
// Simulate a user decision to allow an invalid certificate exception for
// kWWWGoogleHost and for kExampleHost.
state->AllowCert(kWWWGoogleHost, *cert, net::ERR_CERT_DATE_INVALID, tab);
state->AllowCert(kExampleHost, *cert, net::ERR_CERT_DATE_INVALID, tab);
// Verify that HasAllowException correctly acknowledges that a user decision
// has been made about kWWWGoogleHost. Then verify that HasAllowException
// correctly identifies that the decision has been revoked.
EXPECT_TRUE(state->HasAllowException(kWWWGoogleHost, tab));
state->RevokeUserAllowExceptions(kWWWGoogleHost);
EXPECT_FALSE(state->HasAllowException(kWWWGoogleHost, tab));
EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
// Verify that the revocation of the kWWWGoogleHost decision does not affect
// the Allow for kExampleHost.
EXPECT_TRUE(state->HasAllowException(kExampleHost, tab));
// Verify the revocation of the kWWWGoogleHost decision does not affect the
// non-decision for kGoogleHost. Then verify that a revocation of a URL with
// no decision has no effect.
EXPECT_FALSE(state->HasAllowException(kGoogleHost, tab));
state->RevokeUserAllowExceptions(kGoogleHost);
EXPECT_FALSE(state->HasAllowException(kGoogleHost, tab));
}
// Clear unit tests the expected behavior of calling Clear to forget all cert
// decision state on the SSLHostStateDelegate class.
IN_PROC_BROWSER_TEST_F(StatefulSSLHostStateDelegateTest, Clear) {
scoped_refptr<net::X509Certificate> cert = GetOkCert();
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
StatefulSSLHostStateDelegate* state =
StatefulSSLHostStateDelegateFactory::GetForProfile(profile);
// Simulate a user decision to allow an invalid certificate exception for
// kWWWGoogleHost and for kExampleHost.
state->AllowCert(kWWWGoogleHost, *cert, net::ERR_CERT_DATE_INVALID, tab);
state->AllowCert(kExampleHost, *cert, net::ERR_CERT_DATE_INVALID, tab);
EXPECT_TRUE(state->HasAllowException(kWWWGoogleHost, tab));
EXPECT_TRUE(state->HasAllowException(kExampleHost, tab));
// Clear data for kWWWGoogleHost. kExampleHost will not be modified.
state->Clear(base::BindRepeating(&CStrStringMatcher,
base::Unretained(kWWWGoogleHost)));
EXPECT_FALSE(state->HasAllowException(kWWWGoogleHost, tab));
EXPECT_TRUE(state->HasAllowException(kExampleHost, tab));
// Do a full clear, then make sure that both kWWWGoogleHost and kExampleHost,
// which had a decision made, and kGoogleHost, which was untouched, are now
// in a denied state.
state->Clear(base::RepeatingCallback<bool(const std::string&)>());
EXPECT_FALSE(state->HasAllowException(kWWWGoogleHost, tab));
EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
EXPECT_FALSE(state->HasAllowException(kExampleHost, tab));
EXPECT_EQ(
content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kExampleHost, *cert, net::ERR_CERT_DATE_INVALID, tab));
EXPECT_FALSE(state->HasAllowException(kGoogleHost, tab));
EXPECT_EQ(
content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kGoogleHost, *cert, net::ERR_CERT_DATE_INVALID, tab));
}
// DidHostRunInsecureContent unit tests the expected behavior of calling
// DidHostRunInsecureContent as well as HostRanInsecureContent to check if
// insecure content has been run and to mark it as such.
IN_PROC_BROWSER_TEST_F(StatefulSSLHostStateDelegateTest,
DidHostRunInsecureContent) {
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
EXPECT_FALSE(state->DidHostRunInsecureContent(
"www.google.com", 42, content::SSLHostStateDelegate::MIXED_CONTENT));
EXPECT_FALSE(state->DidHostRunInsecureContent(
"www.google.com", 191, content::SSLHostStateDelegate::MIXED_CONTENT));
EXPECT_FALSE(state->DidHostRunInsecureContent(
"example.com", 42, content::SSLHostStateDelegate::MIXED_CONTENT));
EXPECT_FALSE(state->DidHostRunInsecureContent(
"www.google.com", 42,
content::SSLHostStateDelegate::CERT_ERRORS_CONTENT));
EXPECT_FALSE(state->DidHostRunInsecureContent(
"www.google.com", 191,
content::SSLHostStateDelegate::CERT_ERRORS_CONTENT));
EXPECT_FALSE(state->DidHostRunInsecureContent(
"example.com", 42, content::SSLHostStateDelegate::CERT_ERRORS_CONTENT));
// Mark a site as MIXED_CONTENT and check that only that host/child id
// is affected, and only for MIXED_CONTENT (not for
// CERT_ERRORS_CONTENT);
state->HostRanInsecureContent("www.google.com", 42,
content::SSLHostStateDelegate::MIXED_CONTENT);
EXPECT_TRUE(state->DidHostRunInsecureContent(
"www.google.com", 42, content::SSLHostStateDelegate::MIXED_CONTENT));
EXPECT_FALSE(state->DidHostRunInsecureContent(
"www.google.com", 42,
content::SSLHostStateDelegate::CERT_ERRORS_CONTENT));
EXPECT_FALSE(state->DidHostRunInsecureContent(
"www.google.com", 191, content::SSLHostStateDelegate::MIXED_CONTENT));
EXPECT_FALSE(state->DidHostRunInsecureContent(
"example.com", 42, content::SSLHostStateDelegate::MIXED_CONTENT));
// Mark another site as MIXED_CONTENT, and check that that host/child
// id is affected (for MIXED_CONTENT only), and that the previously
// host/child id is still marked as MIXED_CONTENT.
state->HostRanInsecureContent("example.com", 42,
content::SSLHostStateDelegate::MIXED_CONTENT);
EXPECT_TRUE(state->DidHostRunInsecureContent(
"www.google.com", 42, content::SSLHostStateDelegate::MIXED_CONTENT));
EXPECT_FALSE(state->DidHostRunInsecureContent(
"www.google.com", 191, content::SSLHostStateDelegate::MIXED_CONTENT));
EXPECT_TRUE(state->DidHostRunInsecureContent(
"example.com", 42, content::SSLHostStateDelegate::MIXED_CONTENT));
EXPECT_FALSE(state->DidHostRunInsecureContent(
"example.com", 42, content::SSLHostStateDelegate::CERT_ERRORS_CONTENT));
// Mark a MIXED_CONTENT host/child id as CERT_ERRORS_CONTENT also.
state->HostRanInsecureContent(
"example.com", 42, content::SSLHostStateDelegate::CERT_ERRORS_CONTENT);
EXPECT_FALSE(state->DidHostRunInsecureContent(
"www.google.com", 191, content::SSLHostStateDelegate::MIXED_CONTENT));
EXPECT_TRUE(state->DidHostRunInsecureContent(
"example.com", 42, content::SSLHostStateDelegate::MIXED_CONTENT));
EXPECT_TRUE(state->DidHostRunInsecureContent(
"example.com", 42, content::SSLHostStateDelegate::CERT_ERRORS_CONTENT));
// Mark a non-MIXED_CONTENT host as CERT_ERRORS_CONTENT.
state->HostRanInsecureContent(
"www.google.com", 191,
content::SSLHostStateDelegate::CERT_ERRORS_CONTENT);
EXPECT_TRUE(state->DidHostRunInsecureContent(
"www.google.com", 191,
content::SSLHostStateDelegate::CERT_ERRORS_CONTENT));
EXPECT_FALSE(state->DidHostRunInsecureContent(
"www.google.com", 191, content::SSLHostStateDelegate::MIXED_CONTENT));
}
// Tests that StatefulSSLHostStateDelegate::HasSeenRecurrentErrors returns true
// after seeing an error of interest multiple times, in the default mode in
// which error occurrences are stored in-memory.
IN_PROC_BROWSER_TEST_F(StatefulSSLHostStateDelegateTest,
HasSeenRecurrentErrors) {
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
StatefulSSLHostStateDelegate* chrome_state =
static_cast<StatefulSSLHostStateDelegate*>(state);
chrome_state->SetRecurrentInterstitialThresholdForTesting(2);
chrome_state->SetRecurrentInterstitialModeForTesting(
StatefulSSLHostStateDelegate::RecurrentInterstitialMode::PREF);
chrome_state->DidDisplayErrorPage(net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED);
EXPECT_FALSE(chrome_state->HasSeenRecurrentErrors(
net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
chrome_state->DidDisplayErrorPage(net::ERR_CERT_SYMANTEC_LEGACY);
EXPECT_FALSE(chrome_state->HasSeenRecurrentErrors(
net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
chrome_state->DidDisplayErrorPage(net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED);
EXPECT_TRUE(chrome_state->HasSeenRecurrentErrors(
net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
}
// Tests that StatefulSSLHostStateDelegate::HasSeenRecurrentErrors returns true
// after seeing an error of interest multiple times in pref mode (where the
// count of each error is persisted across browsing sessions).
IN_PROC_BROWSER_TEST_F(StatefulSSLHostStateDelegateTest,
HasSeenRecurrentErrorsPref) {
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
StatefulSSLHostStateDelegate* chrome_state =
static_cast<StatefulSSLHostStateDelegate*>(state);
chrome_state->SetRecurrentInterstitialThresholdForTesting(2);
chrome_state->SetRecurrentInterstitialModeForTesting(
StatefulSSLHostStateDelegate::RecurrentInterstitialMode::PREF);
chrome_state->DidDisplayErrorPage(net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED);
EXPECT_FALSE(chrome_state->HasSeenRecurrentErrors(
net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
chrome_state->DidDisplayErrorPage(net::ERR_CERT_SYMANTEC_LEGACY);
EXPECT_FALSE(
chrome_state->HasSeenRecurrentErrors(net::ERR_CERT_SYMANTEC_LEGACY));
chrome_state->DidDisplayErrorPage(net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED);
EXPECT_TRUE(chrome_state->HasSeenRecurrentErrors(
net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
chrome_state->DidDisplayErrorPage(net::ERR_CERT_SYMANTEC_LEGACY);
EXPECT_TRUE(
chrome_state->HasSeenRecurrentErrors(net::ERR_CERT_SYMANTEC_LEGACY));
// Create a new StatefulSSLHostStateDelegate to check that the state has been
// saved to the pref and that the new StatefulSSLHostStateDelegate reads it.
StatefulSSLHostStateDelegate new_state(
profile, profile->GetPrefs(),
HostContentSettingsMapFactory::GetForProfile(profile));
new_state.SetRecurrentInterstitialThresholdForTesting(2);
new_state.SetRecurrentInterstitialModeForTesting(
StatefulSSLHostStateDelegate::RecurrentInterstitialMode::PREF);
EXPECT_TRUE(new_state.HasSeenRecurrentErrors(
net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
EXPECT_TRUE(new_state.HasSeenRecurrentErrors(net::ERR_CERT_SYMANTEC_LEGACY));
// Also test the logic for when the number of displayed errors exceeds the
// threshold.
new_state.DidDisplayErrorPage(net::ERR_CERT_SYMANTEC_LEGACY);
EXPECT_TRUE(new_state.HasSeenRecurrentErrors(net::ERR_CERT_SYMANTEC_LEGACY));
}
// Tests that StatefulSSLHostStateDelegate::HasSeenRecurrentErrors handles
// clocks going backwards in pref mode.
IN_PROC_BROWSER_TEST_F(StatefulSSLHostStateDelegateTest,
HasSeenRecurrentErrorsPrefClockGoesBackwards) {
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
StatefulSSLHostStateDelegate* chrome_state =
static_cast<StatefulSSLHostStateDelegate*>(state);
chrome_state->SetRecurrentInterstitialThresholdForTesting(2);
chrome_state->SetRecurrentInterstitialModeForTesting(
StatefulSSLHostStateDelegate::RecurrentInterstitialMode::PREF);
base::SimpleTestClock* clock = new base::SimpleTestClock();
clock->SetNow(base::Time::Now());
chrome_state->SetClockForTesting(
std::unique_ptr<base::SimpleTestClock>(clock));
chrome_state->DidDisplayErrorPage(net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED);
EXPECT_FALSE(chrome_state->HasSeenRecurrentErrors(
net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
// Move the clock backwards and test that the recurrent error state is reset.
clock->Advance(-base::TimeDelta::FromSeconds(10));
chrome_state->DidDisplayErrorPage(net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED);
EXPECT_FALSE(chrome_state->HasSeenRecurrentErrors(
net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
// If the clock continues to move forwards, a subsequent error page should
// trigger the recurrent error message.
clock->Advance(base::TimeDelta::FromSeconds(10));
chrome_state->DidDisplayErrorPage(net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED);
EXPECT_TRUE(chrome_state->HasSeenRecurrentErrors(
net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
}
// Tests that StatefulSSLHostStateDelegate::HasSeenRecurrentErrors in pref mode
// ignores errors that occurred too far in the past. Note that this test uses a
// threshold of 3 errors, unlike previous tests which use a threshold of 2.
IN_PROC_BROWSER_TEST_F(StatefulSSLHostStateDelegateTest,
HasSeenRecurrentErrorsPrefErrorsInPast) {
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
StatefulSSLHostStateDelegate* chrome_state =
static_cast<StatefulSSLHostStateDelegate*>(state);
chrome_state->SetRecurrentInterstitialResetTimeForTesting(10);
chrome_state->SetRecurrentInterstitialModeForTesting(
StatefulSSLHostStateDelegate::RecurrentInterstitialMode::PREF);
base::SimpleTestClock* clock = new base::SimpleTestClock();
clock->SetNow(base::Time::Now());
chrome_state->SetClockForTesting(
std::unique_ptr<base::SimpleTestClock>(clock));
chrome_state->DidDisplayErrorPage(net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED);
EXPECT_FALSE(chrome_state->HasSeenRecurrentErrors(
net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
// Subsequent errors more than 10 seconds later shouldn't trigger the
// recurrent error message.
clock->Advance(base::TimeDelta::FromSeconds(12));
chrome_state->DidDisplayErrorPage(net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED);
EXPECT_FALSE(chrome_state->HasSeenRecurrentErrors(
net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
clock->Advance(base::TimeDelta::FromSeconds(3));
chrome_state->DidDisplayErrorPage(net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED);
EXPECT_FALSE(chrome_state->HasSeenRecurrentErrors(
net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
// But a third subsequent error within 10 seconds should.
clock->Advance(base::TimeDelta::FromSeconds(3));
chrome_state->DidDisplayErrorPage(net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED);
EXPECT_TRUE(chrome_state->HasSeenRecurrentErrors(
net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
}
// Tests the basic behavior of cert memory in incognito.
class IncognitoSSLHostStateDelegateTest
: public StatefulSSLHostStateDelegateTest {};
IN_PROC_BROWSER_TEST_F(IncognitoSSLHostStateDelegateTest, PRE_AfterRestart) {
scoped_refptr<net::X509Certificate> cert = GetOkCert();
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
// Add a cert exception to the profile and then verify that it still exists
// in the incognito profile.
state->AllowCert(kWWWGoogleHost, *cert, net::ERR_CERT_DATE_INVALID, tab);
auto* incognito_browser = CreateIncognitoBrowser(profile);
auto* incognito_tab =
incognito_browser->tab_strip_model()->GetActiveWebContents();
Profile* incognito = profile->GetPrimaryOTRProfile(/*create_if_needed=*/true);
content::SSLHostStateDelegate* incognito_state =
incognito->GetSSLHostStateDelegate();
EXPECT_EQ(
content::SSLHostStateDelegate::ALLOWED,
incognito_state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, incognito_tab));
// Add a cert exception to the incognito profile. It will be checked after
// restart that this exception does not exist. Note the different cert URL and
// error than above thus mapping to a second exception. Also validate that it
// was not added as an exception to the regular profile.
incognito_state->AllowCert(kGoogleHost, *cert,
net::ERR_CERT_COMMON_NAME_INVALID, incognito_tab);
EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kGoogleHost, *cert,
net::ERR_CERT_COMMON_NAME_INVALID, tab));
}
// AfterRestart ensures that any cert decisions made in an incognito profile are
// forgetten after a session restart even if the field trial group parameter
// specifies to remember cert decisions after restart.
IN_PROC_BROWSER_TEST_F(IncognitoSSLHostStateDelegateTest, AfterRestart) {
scoped_refptr<net::X509Certificate> cert = GetOkCert();
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
// Verify that the exception added before restart to the regular
// (non-incognito) profile still exists and was not cleared after the
// incognito session ended.
EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
auto* incognito_browser = CreateIncognitoBrowser(profile);
auto* incognito_tab =
incognito_browser->tab_strip_model()->GetActiveWebContents();
Profile* incognito = profile->GetPrimaryOTRProfile(/*create_if_needed=*/true);
content::SSLHostStateDelegate* incognito_state =
incognito->GetSSLHostStateDelegate();
// Verify that the exception added before restart to the incognito profile was
// cleared when the incognito session ended.
EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
incognito_state->QueryPolicy(kGoogleHost, *cert,
net::ERR_CERT_COMMON_NAME_INVALID,
incognito_tab));
}
// Tests the default certificate memory, which is one week.
class DefaultMemorySSLHostStateDelegateTest
: public StatefulSSLHostStateDelegateTest {};
IN_PROC_BROWSER_TEST_F(DefaultMemorySSLHostStateDelegateTest,
PRE_AfterRestart) {
scoped_refptr<net::X509Certificate> cert = GetOkCert();
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
state->AllowCert(kWWWGoogleHost, *cert, net::ERR_CERT_DATE_INVALID, tab);
EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
}
IN_PROC_BROWSER_TEST_F(DefaultMemorySSLHostStateDelegateTest, AfterRestart) {
scoped_refptr<net::X509Certificate> cert = GetOkCert();
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
// chrome_state takes ownership of this clock
base::SimpleTestClock* clock = new base::SimpleTestClock();
StatefulSSLHostStateDelegate* chrome_state =
static_cast<StatefulSSLHostStateDelegate*>(state);
chrome_state->SetClockForTesting(std::unique_ptr<base::Clock>(clock));
// Start the clock at standard system time.
clock->SetNow(base::Time::NowFromSystemTime());
// This should only pass if the cert was allowed before the test was restart
// and thus has now been rememebered across browser restarts.
EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
// Simulate the clock advancing by one day, which is less than the expiration
// length.
clock->Advance(base::TimeDelta::FromSeconds(kDeltaOneDayInSeconds + 1));
// The cert should still be |ALLOWED| because the default expiration length
// has not passed yet.
EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
// Now simulate the clock advancing by one week, which is past the expiration
// point.
clock->Advance(base::TimeDelta::FromSeconds(kDeltaOneWeekInSeconds -
kDeltaOneDayInSeconds + 1));
// The cert should now be |DENIED| because the specified delta has passed.
EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
}
// The same test as StatefulSSLHostStateDelegateTest.QueryPolicyExpired but now
// applied to a browser context that expires based on time, not restart. This
// unit tests to make sure that if a certificate decision has expired, the
// return value from QueryPolicy returns the correct vaule.
IN_PROC_BROWSER_TEST_F(DefaultMemorySSLHostStateDelegateTest,
QueryPolicyExpired) {
scoped_refptr<net::X509Certificate> cert = GetOkCert();
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
// chrome_state takes ownership of this clock
base::SimpleTestClock* clock = new base::SimpleTestClock();
StatefulSSLHostStateDelegate* chrome_state =
static_cast<StatefulSSLHostStateDelegate*>(state);
chrome_state->SetClockForTesting(std::unique_ptr<base::Clock>(clock));
// Start the clock at standard system time but do not advance at all to
// emphasize that instant forget works.
clock->SetNow(base::Time::NowFromSystemTime());
// The certificate has never been seen before, so it should be UNKONWN.
EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
// After allowing the certificate, a query should say that it is allowed.
state->AllowCert(kWWWGoogleHost, *cert, net::ERR_CERT_DATE_INVALID, tab);
EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
// Simulate the clock advancing by one week, the default expiration time.
clock->Advance(base::TimeDelta::FromSeconds(kDeltaOneWeekInSeconds + 1));
// The decision expiration time has come, so it should indicate that the
// certificate and error are DENIED.
EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
}
// Tests to make sure that if the user deletes their browser history, SSL
// exceptions will be deleted as well.
class RemoveBrowsingHistorySSLHostStateDelegateTest
: public StatefulSSLHostStateDelegateTest {
public:
void RemoveAndWait(Profile* profile) {
content::BrowsingDataRemover* remover = profile->GetBrowsingDataRemover();
content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
remover->RemoveAndReply(
browsing_data::CalculateBeginDeleteTime(
browsing_data::TimePeriod::LAST_HOUR),
browsing_data::CalculateEndDeleteTime(
browsing_data::TimePeriod::LAST_HOUR),
chrome_browsing_data_remover::DATA_TYPE_HISTORY,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
&completion_observer);
completion_observer.BlockUntilCompletion();
}
};
IN_PROC_BROWSER_TEST_F(RemoveBrowsingHistorySSLHostStateDelegateTest,
DeleteHistory) {
scoped_refptr<net::X509Certificate> cert = GetOkCert();
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
// Add an exception for an invalid certificate. Then remove the last hour's
// worth of browsing history and verify that the exception has been deleted.
state->AllowCert(kGoogleHost, *cert, net::ERR_CERT_DATE_INVALID, tab);
RemoveAndWait(profile);
EXPECT_EQ(
content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kGoogleHost, *cert, net::ERR_CERT_DATE_INVALID, tab));
}
// Tests to make sure that localhost certificate errors are treated as
// normal errors or ignored, depending on whether the
// kAllowInsecureLocalhost flag is set.
//
// When the flag isn't set, requests to localhost with invalid
// certificates should be denied.
IN_PROC_BROWSER_TEST_F(StatefulSSLHostStateDelegateTest,
LocalhostErrorWithoutFlag) {
// Serve the Google cert for localhost to generate an error.
scoped_refptr<net::X509Certificate> cert = GetOkCert();
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
state->QueryPolicy("localhost", *cert,
net::ERR_CERT_COMMON_NAME_INVALID, tab));
EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
state->QueryPolicy("127.0.0.1", *cert,
net::ERR_CERT_COMMON_NAME_INVALID, tab));
}
class StatefulSSLHostStateDelegateExtensionTest
: public extensions::ExtensionBrowserTest {
public:
StatefulSSLHostStateDelegateExtensionTest() {
guest_view::GuestViewManager::set_factory_for_testing(&factory_);
}
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
ExtensionBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(
extensions::switches::kEnableExperimentalExtensionApis);
}
private:
guest_view::TestGuestViewManagerFactory factory_;
};
// Tests that certificate decisions are isolated by storage partition. In
// particular, clicking through a certificate error in a <webview> in a Chrome
// App shouldn't affect normal browsing. See https://crbug.com/639173.
IN_PROC_BROWSER_TEST_F(StatefulSSLHostStateDelegateExtensionTest,
StoragePartitionIsolation) {
ASSERT_TRUE(embedded_test_server()->Start());
// Launch a Chrome app and store a certificate exception.
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
extensions::ChromeTestExtensionLoader loader(profile);
const extensions::Extension* app =
LoadAndLaunchApp(test_data_dir_.AppendASCII("platform_apps")
.AppendASCII("web_view")
.AppendASCII("simple"));
ASSERT_TRUE(app);
auto app_windows =
extensions::AppWindowRegistry::Get(profile)->GetAppWindowsForApp(
app->id());
ASSERT_EQ(1u, app_windows.size());
// Wait for the app's guest WebContents to load.
guest_view::TestGuestViewManager* guest_manager =
static_cast<guest_view::TestGuestViewManager*>(
guest_view::TestGuestViewManager::FromBrowserContext(profile));
content::WebContents* guest = guest_manager->WaitForSingleGuestCreated();
guest_manager->WaitUntilAttached(guest);
// Store a certificate exception for the guest.
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
scoped_refptr<net::X509Certificate> cert = GetOkCert();
state->AllowCert(kWWWGoogleHost, *cert, net::ERR_CERT_DATE_INVALID, guest);
EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, guest));
EXPECT_TRUE(state->HasAllowException(kWWWGoogleHost, guest));
// Navigate to a non-app page and test that the exception is not carried over.
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/extensions/isolated_apps/non_app/main.html"));
EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
state->QueryPolicy(kWWWGoogleHost, *cert,
net::ERR_CERT_DATE_INVALID, tab));
EXPECT_FALSE(state->HasAllowException(kWWWGoogleHost, tab));
}
// When the flag is set, requests to localhost with invalid certificates
// should be allowed.
class AllowLocalhostErrorsSSLHostStateDelegateTest
: public StatefulSSLHostStateDelegateTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
StatefulSSLHostStateDelegateTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kAllowInsecureLocalhost);
}
};
IN_PROC_BROWSER_TEST_F(AllowLocalhostErrorsSSLHostStateDelegateTest,
LocalhostErrorWithFlag) {
// Serve the Google cert for localhost to generate an error.
scoped_refptr<net::X509Certificate> cert = GetOkCert();
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
state->QueryPolicy("localhost", *cert,
net::ERR_CERT_COMMON_NAME_INVALID, tab));
EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
state->QueryPolicy("127.0.0.1", *cert,
net::ERR_CERT_COMMON_NAME_INVALID, tab));
}