[go: nahoru, domu]

blob: f84c1801f727bc3157667124087ace447fae21c8 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "android_webview/browser/component_updater/loader_policies/aw_apps_package_names_allowlist_component_loader_policy.h"
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <utility>
#include "android_webview/browser/metrics/aw_metrics_service_client.h"
#include "android_webview/common/aw_features.h"
#include "android_webview/common/metrics/app_package_name_logging_rule.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/metrics/user_metrics.h"
#include "base/run_loop.h"
#include "base/sequence_checker.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "base/values.h"
#include "base/version.h"
#include "components/component_updater/android/component_loader_policy.h"
#include "components/optimization_guide/core/bloom_filter.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace android_webview {
using AllowlistPraseStatus =
AwAppsPackageNamesAllowlistComponentLoaderPolicy::AllowlistPraseStatus;
namespace {
constexpr int kNumHash = 11;
constexpr int kNumBitsPerEntry = 16;
constexpr char kTestAllowlistVersion[] = "123.456.789.10";
const std::string kTestAllowlist[] = {"com.example.test", "my.fake.app",
"yet.another.app"};
constexpr char kAllowlistPraseStatusHistogramName[] =
"Android.WebView.Metrics.PackagesAllowList.ParseStatus";
double MillisFromUnixEpoch(const base::Time& time) {
return (time - base::Time::UnixEpoch()).InMillisecondsF();
}
base::Value::Dict BuildTestManifest() {
base::Value::Dict manifest;
manifest.Set(kBloomFilterNumHashKey, base::Value(kNumHash));
manifest.Set(kBloomFilterNumBitsKey, base::Value(3 * kNumBitsPerEntry));
manifest.Set(
kExpiryDateKey,
base::Value(MillisFromUnixEpoch(base::Time::Now() + base::Days(1))));
return manifest;
}
} // namespace
class AwAppsPackageNamesAllowlistComponentLoaderPolicyTest
: public ::testing::Test {
public:
void SetUp() override {
ASSERT_TRUE(base::CreateTemporaryFile(&allowlist_path_));
}
void TearDown() override { base::DeleteFile(allowlist_path_); }
void WriteAllowListToFile(const std::vector<uint8_t>& data) {
ASSERT_TRUE(base::WriteFile(allowlist_path_, data));
}
void WritePackageNamesAllowListToFile() {
auto filter = std::make_unique<optimization_guide::BloomFilter>(
kNumHash, kNumBitsPerEntry * std::size(kTestAllowlist));
for (const auto& package : kTestAllowlist) {
filter->Add(package);
}
WriteAllowListToFile(filter->bytes());
}
base::ScopedFD OpenAndGetAllowlistFd() {
int allowlist_fd = open(allowlist_path_.value().c_str(), O_RDONLY);
CHECK(allowlist_fd) << "Failed to open FD for " << allowlist_path_;
return base::ScopedFD(allowlist_fd);
}
void LookupConfirmationCallback(
absl::optional<AppPackageNameLoggingRule> record) {
EXPECT_TRUE(checker_.CalledOnValidSequence());
allowlist_lookup_result_ = record;
lookup_run_loop_.Quit();
}
protected:
base::test::TaskEnvironment env_;
// Has to be init after TaskEnvironment.
base::SequenceCheckerImpl checker_;
base::RunLoop lookup_run_loop_;
base::HistogramTester histogram_tester_;
absl::optional<AppPackageNameLoggingRule> allowlist_lookup_result_;
private:
base::FilePath allowlist_path_;
};
TEST_F(AwAppsPackageNamesAllowlistComponentLoaderPolicyTest,
TestExistingPackageName) {
WritePackageNamesAllowListToFile();
base::flat_map<std::string, base::ScopedFD> fd_map;
fd_map[kAllowlistBloomFilterFileName] = OpenAndGetAllowlistFd();
base::Value::Dict manifest = BuildTestManifest();
base::Time one_day_from_now = base::Time::Now() + base::Days(1);
manifest.Set(kExpiryDateKey, MillisFromUnixEpoch(one_day_from_now));
base::Version new_version(kTestAllowlistVersion);
auto policy =
std::make_unique<AwAppsPackageNamesAllowlistComponentLoaderPolicy>(
kTestAllowlist[1],
AppPackageNameLoggingRule(base::Version("123.456.789.0"),
base::Time::Min()),
base::BindOnce(&AwAppsPackageNamesAllowlistComponentLoaderPolicyTest::
LookupConfirmationCallback,
base::Unretained(this)));
policy->ComponentLoaded(new_version, fd_map, std::move(manifest));
lookup_run_loop_.Run();
ASSERT_TRUE(allowlist_lookup_result_.has_value());
EXPECT_TRUE(allowlist_lookup_result_.value().IsAppPackageNameAllowed());
EXPECT_EQ(allowlist_lookup_result_.value().GetVersion(), new_version);
EXPECT_EQ(allowlist_lookup_result_.value().GetExpiryDate(), one_day_from_now);
histogram_tester_.ExpectBucketCount(kAllowlistPraseStatusHistogramName,
AllowlistPraseStatus::kSuccess, 1);
histogram_tester_.ExpectTotalCount(kAllowlistPraseStatusHistogramName, 1);
}
TEST_F(AwAppsPackageNamesAllowlistComponentLoaderPolicyTest,
TestSameVersionAsCache) {
base::flat_map<std::string, base::ScopedFD> fd_map;
base::Time one_day_from_now = base::Time::Now() + base::Days(1);
base::Version version(kTestAllowlistVersion);
AppPackageNameLoggingRule expected_record(version, one_day_from_now);
auto policy =
std::make_unique<AwAppsPackageNamesAllowlistComponentLoaderPolicy>(
"test.some.app", expected_record,
base::BindOnce(&AwAppsPackageNamesAllowlistComponentLoaderPolicyTest::
LookupConfirmationCallback,
base::Unretained(this)));
policy->ComponentLoaded(version, fd_map, BuildTestManifest());
lookup_run_loop_.Run();
ASSERT_TRUE(allowlist_lookup_result_.has_value());
EXPECT_TRUE(allowlist_lookup_result_.value().IsAppPackageNameAllowed());
EXPECT_TRUE(expected_record.IsSameAs(allowlist_lookup_result_.value()));
histogram_tester_.ExpectBucketCount(kAllowlistPraseStatusHistogramName,
AllowlistPraseStatus::kUsingCache, 1);
histogram_tester_.ExpectTotalCount(kAllowlistPraseStatusHistogramName, 1);
}
TEST_F(AwAppsPackageNamesAllowlistComponentLoaderPolicyTest,
TestNonExistingPackageName) {
WritePackageNamesAllowListToFile();
base::flat_map<std::string, base::ScopedFD> fd_map;
fd_map[kAllowlistBloomFilterFileName] = OpenAndGetAllowlistFd();
base::Version new_version(kTestAllowlistVersion);
auto policy =
std::make_unique<AwAppsPackageNamesAllowlistComponentLoaderPolicy>(
"non.existent.app", absl::optional<AppPackageNameLoggingRule>(),
base::BindOnce(&AwAppsPackageNamesAllowlistComponentLoaderPolicyTest::
LookupConfirmationCallback,
base::Unretained(this)));
policy->ComponentLoaded(new_version, fd_map, BuildTestManifest());
lookup_run_loop_.Run();
ASSERT_TRUE(allowlist_lookup_result_.has_value());
EXPECT_EQ(allowlist_lookup_result_.value().GetVersion(), new_version);
EXPECT_FALSE(allowlist_lookup_result_.value().IsAppPackageNameAllowed());
histogram_tester_.ExpectBucketCount(kAllowlistPraseStatusHistogramName,
AllowlistPraseStatus::kSuccess, 1);
histogram_tester_.ExpectTotalCount(kAllowlistPraseStatusHistogramName, 1);
}
TEST_F(AwAppsPackageNamesAllowlistComponentLoaderPolicyTest,
TestAllowlistFileNotInMap) {
base::flat_map<std::string, base::ScopedFD> fd_map;
fd_map["another_file"] = OpenAndGetAllowlistFd();
auto policy =
std::make_unique<AwAppsPackageNamesAllowlistComponentLoaderPolicy>(
kTestAllowlist[1], absl::optional<AppPackageNameLoggingRule>(),
base::BindOnce(&AwAppsPackageNamesAllowlistComponentLoaderPolicyTest::
LookupConfirmationCallback,
base::Unretained(this)));
policy->ComponentLoaded(base::Version(kTestAllowlistVersion), fd_map,
BuildTestManifest());
lookup_run_loop_.Run();
EXPECT_FALSE(allowlist_lookup_result_.has_value());
histogram_tester_.ExpectBucketCount(
kAllowlistPraseStatusHistogramName,
AllowlistPraseStatus::kMissingAllowlistFile, 1);
histogram_tester_.ExpectTotalCount(kAllowlistPraseStatusHistogramName, 1);
}
TEST_F(AwAppsPackageNamesAllowlistComponentLoaderPolicyTest,
TestMissingBloomFilterParams) {
WritePackageNamesAllowListToFile();
base::flat_map<std::string, base::ScopedFD> fd_map;
fd_map[kAllowlistBloomFilterFileName] = OpenAndGetAllowlistFd();
auto policy =
std::make_unique<AwAppsPackageNamesAllowlistComponentLoaderPolicy>(
kTestAllowlist[1], absl::optional<AppPackageNameLoggingRule>(),
base::BindOnce(&AwAppsPackageNamesAllowlistComponentLoaderPolicyTest::
LookupConfirmationCallback,
base::Unretained(this)));
policy->ComponentLoaded(base::Version(kTestAllowlistVersion), fd_map,
/*manifest=*/base::Value::Dict());
lookup_run_loop_.Run();
EXPECT_FALSE(allowlist_lookup_result_.has_value());
histogram_tester_.ExpectBucketCount(kAllowlistPraseStatusHistogramName,
AllowlistPraseStatus::kMissingFields, 1);
histogram_tester_.ExpectTotalCount(kAllowlistPraseStatusHistogramName, 1);
}
TEST_F(AwAppsPackageNamesAllowlistComponentLoaderPolicyTest,
TestTooShortBloomFilter) {
WriteAllowListToFile(std::vector<uint8_t>(2, 0xff));
base::flat_map<std::string, base::ScopedFD> fd_map;
fd_map[kAllowlistBloomFilterFileName] = OpenAndGetAllowlistFd();
auto policy =
std::make_unique<AwAppsPackageNamesAllowlistComponentLoaderPolicy>(
kTestAllowlist[1], absl::optional<AppPackageNameLoggingRule>(),
base::BindOnce(&AwAppsPackageNamesAllowlistComponentLoaderPolicyTest::
LookupConfirmationCallback,
base::Unretained(this)));
policy->ComponentLoaded(base::Version(kTestAllowlistVersion), fd_map,
BuildTestManifest());
lookup_run_loop_.Run();
EXPECT_FALSE(allowlist_lookup_result_.has_value());
histogram_tester_.ExpectBucketCount(
kAllowlistPraseStatusHistogramName,
AllowlistPraseStatus::kMalformedBloomFilter, 1);
histogram_tester_.ExpectTotalCount(kAllowlistPraseStatusHistogramName, 1);
}
TEST_F(AwAppsPackageNamesAllowlistComponentLoaderPolicyTest,
TestTooLongBloomFilter) {
WriteAllowListToFile(std::vector<uint8_t>(2000, 0xff));
base::flat_map<std::string, base::ScopedFD> fd_map;
fd_map[kAllowlistBloomFilterFileName] = OpenAndGetAllowlistFd();
auto policy =
std::make_unique<AwAppsPackageNamesAllowlistComponentLoaderPolicy>(
kTestAllowlist[1], absl::optional<AppPackageNameLoggingRule>(),
base::BindOnce(&AwAppsPackageNamesAllowlistComponentLoaderPolicyTest::
LookupConfirmationCallback,
base::Unretained(this)));
policy->ComponentLoaded(base::Version(kTestAllowlistVersion), fd_map,
BuildTestManifest());
lookup_run_loop_.Run();
EXPECT_FALSE(allowlist_lookup_result_.has_value());
histogram_tester_.ExpectBucketCount(
kAllowlistPraseStatusHistogramName,
AllowlistPraseStatus::kMalformedBloomFilter, 1);
histogram_tester_.ExpectTotalCount(kAllowlistPraseStatusHistogramName, 1);
}
TEST_F(AwAppsPackageNamesAllowlistComponentLoaderPolicyTest,
TestExpiredAllowlist) {
WritePackageNamesAllowListToFile();
base::flat_map<std::string, base::ScopedFD> fd_map;
fd_map[kAllowlistBloomFilterFileName] = OpenAndGetAllowlistFd();
base::Value::Dict manifest = BuildTestManifest();
manifest.Set(
kExpiryDateKey,
base::Value(MillisFromUnixEpoch(base::Time::Now() - base::Days(1))));
auto policy =
std::make_unique<AwAppsPackageNamesAllowlistComponentLoaderPolicy>(
kTestAllowlist[1], absl::optional<AppPackageNameLoggingRule>(),
base::BindOnce(&AwAppsPackageNamesAllowlistComponentLoaderPolicyTest::
LookupConfirmationCallback,
base::Unretained(this)));
policy->ComponentLoaded(base::Version(kTestAllowlistVersion), fd_map,
std::move(manifest));
lookup_run_loop_.Run();
EXPECT_FALSE(allowlist_lookup_result_.has_value());
histogram_tester_.ExpectBucketCount(kAllowlistPraseStatusHistogramName,
AllowlistPraseStatus::kExpiredAllowlist,
1);
histogram_tester_.ExpectTotalCount(kAllowlistPraseStatusHistogramName, 1);
}
// Helper functions for throttling tests, defining them near tests body for
// better readability.
namespace {
class AwMetricsServiceClientTestDelegate
: public AwMetricsServiceClient::Delegate {
void RegisterAdditionalMetricsProviders(
metrics::MetricsService* service) override {}
void AddWebViewAppStateObserver(WebViewAppStateObserver* observer) override {}
bool HasAwContentsEverCreated() const override { return false; }
};
void TestThrottling(base::Time time,
AwMetricsServiceClient* client,
bool expect_throttling) {
if (!time.is_null()) {
client->SetAppPackageNameLoggingRuleLastUpdateTime(time);
}
component_updater::ComponentLoaderPolicyVector policies;
LoadPackageNamesAllowlistComponent(policies, client);
EXPECT_EQ(policies.size(), expect_throttling ? 0 : 1u);
}
void TestThrottlingAllowlist(absl::optional<AppPackageNameLoggingRule> rule,
bool expect_throttling) {
TestingPrefServiceSimple prefs;
AwMetricsServiceClient::RegisterMetricsPrefs(prefs.registry());
AwMetricsServiceClient client(
std::make_unique<AwMetricsServiceClientTestDelegate>());
client.Initialize(&prefs);
client.SetAppPackageNameLoggingRule(rule);
// No previous last_update record: should never throttle.
TestThrottling(base::Time(), &client, /*expect_throttling=*/false);
// last_update record > max_throttle_time : should never throttle.
TestThrottling(base::Time::Now() - kWebViewAppsMaxAllowlistThrottleTimeDelta -
base::Days(1),
&client,
/*expect_throttling=*/false);
// min_throttle_time < last_update record < max_throttle_time : maybe
// throttle.
TestThrottling(base::Time::Now() - kWebViewAppsMaxAllowlistThrottleTimeDelta +
kWebViewAppsMinAllowlistThrottleTimeDelta,
&client,
/*expect_throttling=*/expect_throttling);
// last_update record < min_throttle_time : should always throttle.
TestThrottling(base::Time::Now() - kWebViewAppsMinAllowlistThrottleTimeDelta +
base::Minutes(30),
&client,
/*expect_throttling=*/true);
}
} // namespace
TEST_F(AwAppsPackageNamesAllowlistComponentLoaderPolicyTest,
TestThrottlingAllowlist_AbsentCache) {
// Since the server-side allowlist is now enabled by default,
// the simplest thing to do in order to preserve the same testing logic
// is to disable it.
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(
android_webview::features::kWebViewAppsPackageNamesServerSideAllowlist);
base::SetRecordActionTaskRunner(env_.GetMainThreadTaskRunner());
TestThrottlingAllowlist(absl::optional<AppPackageNameLoggingRule>(),
/*expect_throttling=*/false);
}
TEST_F(AwAppsPackageNamesAllowlistComponentLoaderPolicyTest,
TestThrottlingAllowlist_ValidCacheAllowedApp) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(
android_webview::features::kWebViewAppsPackageNamesServerSideAllowlist);
base::SetRecordActionTaskRunner(env_.GetMainThreadTaskRunner());
TestThrottlingAllowlist(
AppPackageNameLoggingRule(base::Version(kTestAllowlistVersion),
base::Time::Now() + base::Days(1)),
/*expect_throttling=*/true);
}
TEST_F(AwAppsPackageNamesAllowlistComponentLoaderPolicyTest,
TestThrottlingAllowlist_ValidCacheNotAllowedApp) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(
android_webview::features::kWebViewAppsPackageNamesServerSideAllowlist);
base::SetRecordActionTaskRunner(env_.GetMainThreadTaskRunner());
TestThrottlingAllowlist(
AppPackageNameLoggingRule(base::Version(kTestAllowlistVersion),
base::Time::Min()),
/*expect_throttling=*/true);
}
TEST_F(AwAppsPackageNamesAllowlistComponentLoaderPolicyTest,
TestThrottlingAllowlist_ExpiredAllowedCache) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(
android_webview::features::kWebViewAppsPackageNamesServerSideAllowlist);
base::SetRecordActionTaskRunner(env_.GetMainThreadTaskRunner());
TestThrottlingAllowlist(
AppPackageNameLoggingRule(base::Version(kTestAllowlistVersion),
base::Time::Now() - base::Days(1)),
/*expect_throttling=*/false);
}
} // namespace android_webview