blob: 1b18a059c289bb71517dac805471593ee106f8b7 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/password_manager/content/browser/bad_message.h"
#include "base/test/metrics/histogram_tester.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/test_renderer_host.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace password_manager::bad_message {
class BadMessageTest : public content::RenderViewHostTestHarness {
public:
BadMessageTest() = default;
~BadMessageTest() override = default;
void SetUp() override {
content::RenderViewHostTestHarness::SetUp();
NavigateAndCommit(GURL("https://example.com"));
}
protected:
content::RenderFrameHost* GetMainFrame() {
return web_contents()->GetPrimaryMainFrame();
}
void ExpectNoHistogramEntries(const base::HistogramTester& histogram_tester) {
histogram_tester.ExpectTotalCount(
"Stability.BadMessageTerminated.PasswordManager", 0);
}
void ExpectHistogramEntry(const base::HistogramTester& histogram_tester,
BadMessageReason reason) {
histogram_tester.ExpectUniqueSample(
"Stability.BadMessageTerminated.PasswordManager",
static_cast<int>(reason), 1);
}
};
// Tests that CheckForIllegalURL() allows legitimate HTTPS URLs to proceed.
TEST_F(BadMessageTest, CheckForIllegalURL_ValidURL) {
base::HistogramTester histogram_tester;
GURL valid_url("https://example.com/login");
bool result =
CheckForIllegalURL(GetMainFrame(), valid_url,
BadMessageReason::CPMD_BAD_ORIGIN_FORM_SUBMITTED);
EXPECT_TRUE(result);
ExpectNoHistogramEntries(histogram_tester);
}
// Tests that CheckForIllegalURL() blocks about: URLs and terminates the
// renderer. About URLs (like about:blank) are dangerous because they bypass
// normal web security constraints and could be exploited for privilege
// escalation attacks.
TEST_F(BadMessageTest, CheckForIllegalURL_AboutURL) {
base::HistogramTester histogram_tester;
GURL about_url("about:blank");
bool result =
CheckForIllegalURL(GetMainFrame(), about_url,
BadMessageReason::CPMD_BAD_ORIGIN_FORM_SUBMITTED);
EXPECT_FALSE(result);
ExpectHistogramEntry(histogram_tester,
BadMessageReason::CPMD_BAD_ORIGIN_FORM_SUBMITTED);
}
// Tests that CheckForIllegalURL() blocks data: URLs and terminates the
// renderer. Data URLs contain embedded content that bypasses origin
// restrictions and could be used to inject malicious content for password theft
// attacks.
TEST_F(BadMessageTest, CheckForIllegalURL_DataURL) {
base::HistogramTester histogram_tester;
GURL data_url("data:text/html,<html></html>");
bool result = CheckForIllegalURL(
GetMainFrame(), data_url,
BadMessageReason::CPMD_BAD_ORIGIN_PASSWORD_NO_LONGER_GENERATED);
EXPECT_FALSE(result);
ExpectHistogramEntry(
histogram_tester,
BadMessageReason::CPMD_BAD_ORIGIN_PASSWORD_NO_LONGER_GENERATED);
}
// Tests that CheckChildProcessSecurityPolicyForURL() allows URLs when the
// renderer process has proper permissions.
TEST_F(BadMessageTest, CheckChildProcessSecurityPolicyForURL_ValidURL) {
base::HistogramTester histogram_tester;
GURL valid_url("https://example.com/login");
content::ChildProcessSecurityPolicy::GetInstance()->GrantRequestOrigin(
GetMainFrame()->GetProcess()->GetDeprecatedID(),
url::Origin::Create(valid_url));
bool result = CheckChildProcessSecurityPolicyForURL(
GetMainFrame(), valid_url,
BadMessageReason::CPMD_BAD_ORIGIN_FORM_SUBMITTED);
EXPECT_TRUE(result);
ExpectNoHistogramEntries(histogram_tester);
}
// Tests that CheckChildProcessSecurityPolicyForURL() delegates illegal URL
// detection to CheckForIllegalURL().
TEST_F(BadMessageTest, CheckChildProcessSecurityPolicyForURL_AboutURL) {
base::HistogramTester histogram_tester;
GURL about_url("about:blank");
bool result = CheckChildProcessSecurityPolicyForURL(
GetMainFrame(), about_url,
BadMessageReason::CPMD_BAD_ORIGIN_FORM_SUBMITTED);
EXPECT_FALSE(result);
ExpectHistogramEntry(histogram_tester,
BadMessageReason::CPMD_BAD_ORIGIN_FORM_SUBMITTED);
}
// Tests that CheckFrameNotPrerendering() allows active frames to proceed with
// password manager operations.
TEST_F(BadMessageTest, CheckFrameNotPrerendering_ActiveFrame) {
base::HistogramTester histogram_tester;
bool result = CheckFrameNotPrerendering(GetMainFrame());
EXPECT_TRUE(result);
ExpectNoHistogramEntries(histogram_tester);
}
// Tests that CheckGeneratedPassword() allows non-empty passwords to proceed.
TEST_F(BadMessageTest, CheckGeneratedPassword_ValidPassword) {
base::HistogramTester histogram_tester;
std::u16string valid_password = u"test_password123";
bool result = CheckGeneratedPassword(GetMainFrame(), valid_password);
EXPECT_TRUE(result);
ExpectNoHistogramEntries(histogram_tester);
}
// Tests that CheckGeneratedPassword() blocks empty passwords and terminates
// the renderer.
TEST_F(BadMessageTest, CheckGeneratedPassword_EmptyPassword) {
base::HistogramTester histogram_tester;
std::u16string empty_password;
bool result = CheckGeneratedPassword(GetMainFrame(), empty_password);
EXPECT_FALSE(result);
ExpectHistogramEntry(
histogram_tester,
BadMessageReason::CPMD_BAD_ORIGIN_NO_GENERATED_PASSWORD_TO_EDIT);
}
// Tests that CheckGeneratedPassword() allows whitespace-only passwords.
TEST_F(BadMessageTest, CheckGeneratedPassword_WhitespaceOnlyPassword) {
base::HistogramTester histogram_tester;
std::u16string whitespace_password = u" ";
bool result = CheckGeneratedPassword(GetMainFrame(), whitespace_password);
EXPECT_TRUE(result);
ExpectNoHistogramEntries(histogram_tester);
}
} // namespace password_manager::bad_message