Skip to content

Commit

Permalink
Revert "WebView: Remove client-side package name allowlist"
Browse files Browse the repository at this point in the history
This reverts commit 7f92f6e.
Note: Due to the deprecation of NO_SERVER_SIDE_FILTER_REQUIRED_DUE_TO_CLIENT_FILTERING enum value, records will now default to SERVER_SIDE_FILTER_UNSPECIFIED. Tests were modified to reflect that expectation.

Reason for revert: Regression detected in crbug/1495877

Original change's description:
> WebView: Remove client-side package name allowlist
>
> WebView currently supports server-side app package name allowlist
> filtering of app package names. The current on-device functionality
> allows for client-side allowlist filtering via the server-side
> allowlist flag.
> This will remove the allowlist flag and all of the client-side
> package name allowlist filtering code that uses the allowlist to determine whether to record app package names and the non-embedded WebView service that stores, caches, and updates the app package name allowlist filter via the component updater.
>
> Bug: 1484262
> Change-Id: I2e47e2b15a5c7aebdbb65c22532306cef1d1eeb2
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4875196
> Reviewed-by: Richard (Torne) Coles <torne@chromium.org>
> Commit-Queue: Adam Walls <avvall@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1198412}

(cherry picked from commit e381e58)

(cherry picked from commit 8337f70)

Bug: 1495877
Change-Id: I63e1460358e036d0d9ee50e2ab25cdfedf36061c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4977288
Commit-Queue: Adam Walls <avvall@chromium.org>
Reviewed-by: Nate Fischer <ntfschr@chromium.org>
Cr-Original-Original-Commit-Position: refs/heads/main@{#1215697}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4981262
Commit-Queue: Krishna Govind <govind@chromium.org>
Cr-Original-Commit-Position: refs/branch-heads/6045@{#1025}
Cr-Original-Branched-From: 905e8bd-refs/heads/main@{#1204232}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4984458
Reviewed-by: Krishna Govind <govind@chromium.org>
Owners-Override: Krishna Govind <govind@chromium.org>
Cr-Commit-Position: refs/branch-heads/6045_53@{#4}
Cr-Branched-From: 7ff9f59-refs/branch-heads/6045@{#929}
Cr-Branched-From: 905e8bd-refs/heads/main@{#1204232}
  • Loading branch information
Adam Walls authored and Chromium LUCI CQ committed Oct 27, 2023
1 parent d11f31e commit 8351b0d
Show file tree
Hide file tree
Showing 33 changed files with 1,980 additions and 6 deletions.
2 changes: 2 additions & 0 deletions android_webview/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ source_set("browser") {
"aw_web_contents_view_delegate.h",
"aw_web_ui_controller_factory.cc",
"aw_web_ui_controller_factory.h",
"component_updater/loader_policies/aw_apps_package_names_allowlist_component_loader_policy.cc",
"component_updater/loader_policies/aw_apps_package_names_allowlist_component_loader_policy.h",
"component_updater/loader_policies/empty_component_loader_policy.cc",
"component_updater/loader_policies/empty_component_loader_policy.h",
"component_updater/loader_policies/origin_trials_component_loader_policy.cc",
Expand Down
8 changes: 8 additions & 0 deletions android_webview/browser/aw_feature_list_creator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ const char* const kPersistentPrefsAllowlist[] = {
variations::prefs::kVariationsLastFetchTime,
variations::prefs::kVariationsSeedDate,

// A dictionary that caches 'AppPackageNameLoggingRule' object which decides
// whether the app package name should be recorded in UMA or not.
prefs::kMetricsAppPackageNameLoggingRule,

// The last time the apps package name allowlist was queried from the
// component update service, regardless if it was successful or not.
prefs::kAppPackageNameLoggingRuleLastUpdateTime,

// The state of the previous background tracing session.
tracing::kBackgroundTracingSessionState,

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
// 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 <stdint.h>
#include <stdio.h>

#include <cstring>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "android_webview/browser/metrics/aw_metrics_service_client.h"
#include "android_webview/common/aw_features.h"
#include "android_webview/common/aw_switches.h"
#include "android_webview/common/components/aw_apps_package_names_allowlist_component_utils.h"
#include "android_webview/common/metrics/app_package_name_logging_rule.h"
#include "base/command_line.h"
#include "base/containers/flat_map.h"
#include "base/feature_list.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/task/thread_pool.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 "third_party/abseil-cpp/absl/types/optional.h"

namespace android_webview {

const base::TimeDelta kWebViewAppsMinAllowlistThrottleTimeDelta =
base::Hours(1);
const base::TimeDelta kWebViewAppsMaxAllowlistThrottleTimeDelta = base::Days(2);

namespace {

// Persisted to logs, should never change.
constexpr char kAppsPackageNamesAllowlistMetricsSuffix[] =
"WebViewAppsPackageNamesAllowlist";
constexpr int kBitsPerByte = 8;

using AllowlistPraseStatus =
AwAppsPackageNamesAllowlistComponentLoaderPolicy::AllowlistPraseStatus;

struct AllowListLookupResult {
AllowlistPraseStatus parse_status;
absl::optional<AppPackageNameLoggingRule> record_rule;
};

void RecordAndReportResult(AllowListLookupCallback lookup_callback,
AllowListLookupResult result) {
DCHECK(lookup_callback);

UMA_HISTOGRAM_ENUMERATION(
"Android.WebView.Metrics.PackagesAllowList.ParseStatus",
result.parse_status);
std::move(lookup_callback).Run(result.record_rule);
}

// Lookup the `package_name` in `allowlist_fd`, returns a null
// an `AllowListLookupResult` containing an `AppPackageNameLoggingRule` if it
// fails.
//
// `allowlist_fd` the fd of a file the contain a bloomfilter that represents an
// allowlist of apps package names.
// `num_hash` the number of hash functions to use in the
// `optimization_guide::BloomFilter`.
// `num_bits` the number of bits in the `optimization_guide::BloomFilter`.
// `package_name` the app package name to look up in the allowlist.
// `version` the allowlist version.
// `expiry_date` the expiry date of the allowlist, i.e the date after which
// this allowlist shouldn't be used.
AllowListLookupResult GetAppPackageNameLoggingRule(
base::ScopedFD allowlist_fd,
int num_hash,
int num_bits,
const std::string& package_name,
const base::Version& version,
const base::Time& expiry_date) {
// Transfer the ownership of the file from `allowlist_fd` to `file_stream`.
base::ScopedFILE file_stream(fdopen(allowlist_fd.release(), "r"));
if (!file_stream.get()) {
return {AllowlistPraseStatus::kIOError};
}

// TODO(https://crbug.com/1219496): use mmap instead of reading the whole
// file.
std::string bloom_filter_data;
if (!base::ReadStreamToString(file_stream.get(), &bloom_filter_data) ||
bloom_filter_data.empty()) {
return {AllowlistPraseStatus::kIOError};
}

// Make sure the bloomfilter binary data is of the correct length.
if (bloom_filter_data.size() !=
size_t((num_bits + kBitsPerByte - 1) / kBitsPerByte)) {
return {AllowlistPraseStatus::kMalformedBloomFilter};
}

if (optimization_guide::BloomFilter(num_hash, num_bits, bloom_filter_data)
.Contains(package_name)) {
return {AllowlistPraseStatus::kSuccess,
AppPackageNameLoggingRule(version, expiry_date)};
} else {
return {AllowlistPraseStatus::kSuccess,
AppPackageNameLoggingRule(version, base::Time::Min())};
}
}

void SetAppPackageNameLoggingRule(
absl::optional<AppPackageNameLoggingRule> record) {
auto* metrics_service_client = AwMetricsServiceClient::GetInstance();
DCHECK(metrics_service_client);
metrics_service_client->SetAppPackageNameLoggingRule(record);
metrics_service_client->SetAppPackageNameLoggingRuleLastUpdateTime(
base::Time::Now());

if (!record.has_value()) {
VLOG(2) << "Failed to load WebView apps package allowlist";
return;
}

VLOG(2) << "WebView apps package allowlist version "
<< record.value().GetVersion() << " is loaded";
if (record.value().IsAppPackageNameAllowed()) {
VLOG(2) << "App package name should be recorded until "
<< record.value().GetExpiryDate();
} else {
VLOG(2) << "App package name shouldn't be recorded";
}
}

bool ShouldThrottleAppPackageNamesAllowlistComponent(
base::Time last_update,
absl::optional<AppPackageNameLoggingRule> cached_record) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kWebViewDisablePackageAllowlistThrottling) ||
last_update.is_null()) {
return false;
}
base::TimeDelta throttle_time_delta(
kWebViewAppsMinAllowlistThrottleTimeDelta);
if (cached_record.has_value()) {
base::Time expiry_date = cached_record.value().GetExpiryDate();
bool in_the_allowlist = !expiry_date.is_min();
bool is_allowlist_expired = expiry_date <= base::Time::Now();
if (!in_the_allowlist || !is_allowlist_expired) {
throttle_time_delta = kWebViewAppsMaxAllowlistThrottleTimeDelta;
}
}
return base::Time::Now() - last_update <= throttle_time_delta;
}

} // namespace

AwAppsPackageNamesAllowlistComponentLoaderPolicy::
AwAppsPackageNamesAllowlistComponentLoaderPolicy(
std::string app_package_name,
absl::optional<AppPackageNameLoggingRule> cached_record,
AllowListLookupCallback lookup_callback)
: app_package_name_(std::move(app_package_name)),
cached_record_(cached_record),
lookup_callback_(std::move(lookup_callback)) {
DCHECK(!app_package_name_.empty());
DCHECK(lookup_callback_);
}

AwAppsPackageNamesAllowlistComponentLoaderPolicy::
~AwAppsPackageNamesAllowlistComponentLoaderPolicy() = default;

// `manifest` represents a JSON object that looks like this:
// {
// "name": "WebViewAppsPackageNamesAllowlist",
//
// /* The component version string, matches the param `version`. */
// "version": "xxxx.xx.xx.xx",
//
// /* Bloomfilter parameters, set by the server side */
// /* int32: number of hash functions used by the bloomfilter */
// "bloomfilter_num_hash": xx
// /* int32: number of bits in the bloomfilter binary array */
// "bloomfilter_num_bits": xx
//
// /* The allowlist expiry date after which the allowlist shouldn't be used.
// Its format is an int64 number representing the number of milliseconds
// after UNIX Epoch. */
// "expiry_date": 12345678910
// }
void AwAppsPackageNamesAllowlistComponentLoaderPolicy::ComponentLoaded(
const base::Version& version,
base::flat_map<std::string, base::ScopedFD>& fd_map,
base::Value::Dict manifest) {
DCHECK(version.IsValid());

// Have to use double because base::DictionaryValue doesn't support int64
// values.
absl::optional<double> expiry_date_ms =
manifest.FindDoubleByDottedPath(kExpiryDateKey);
absl::optional<int> num_hash =
manifest.FindIntByDottedPath(kBloomFilterNumHashKey);
absl::optional<int> num_bits =
manifest.FindIntByDottedPath(kBloomFilterNumBitsKey);
// Being conservative and consider the allowlist expired when a valid expiry
// date is absent.
if (!expiry_date_ms.has_value() || !num_hash.has_value() ||
!num_bits.has_value() || num_hash.value() <= 0 || num_bits.value() <= 0) {
RecordAndReportResult(std::move(lookup_callback_),
{AllowlistPraseStatus::kMissingFields});
return;
}

base::Time expiry_date = base::Time::UnixEpoch() +
base::Milliseconds(expiry_date_ms.value_or(0.0));
if (expiry_date <= base::Time::Now()) {
RecordAndReportResult(std::move(lookup_callback_),
{AllowlistPraseStatus::kExpiredAllowlist});
return;
}

if (cached_record_.has_value() &&
cached_record_.value().GetVersion() == version) {
RecordAndReportResult(std::move(lookup_callback_),
{AllowlistPraseStatus::kUsingCache, cached_record_});
return;
}

auto allowlist_iterator = fd_map.find(kAllowlistBloomFilterFileName);
if (allowlist_iterator == fd_map.end()) {
RecordAndReportResult(std::move(lookup_callback_),
{AllowlistPraseStatus::kMissingAllowlistFile});
return;
}

base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&GetAppPackageNameLoggingRule,
std::move(allowlist_iterator->second), num_hash.value(),
num_bits.value(), std::move(app_package_name_), version,
expiry_date),
base::BindOnce(&RecordAndReportResult, std::move(lookup_callback_)));
}

void AwAppsPackageNamesAllowlistComponentLoaderPolicy::ComponentLoadFailed(
component_updater::ComponentLoadResult /*error*/) {
DCHECK(lookup_callback_);
// TODO(crbug.com/1216200): Record the error in a histogram in the
// ComponentLoader for each component.
std::move(lookup_callback_).Run(absl::optional<AppPackageNameLoggingRule>());
}

void AwAppsPackageNamesAllowlistComponentLoaderPolicy::GetHash(
std::vector<uint8_t>* hash) const {
GetWebViewAppsPackageNamesAllowlistPublicKeyHash(hash);
}

std::string AwAppsPackageNamesAllowlistComponentLoaderPolicy::GetMetricsSuffix()
const {
return kAppsPackageNamesAllowlistMetricsSuffix;
}

void LoadPackageNamesAllowlistComponent(
component_updater::ComponentLoaderPolicyVector& policies,
AwMetricsServiceClient* metrics_service_client) {
DCHECK(metrics_service_client);

// Prevent loading of client-side allowlist if using server-side allowlist
if (base::FeatureList::IsEnabled(
android_webview::features::
kWebViewAppsPackageNamesServerSideAllowlist)) {
return;
}
absl::optional<AppPackageNameLoggingRule> cached_record =
metrics_service_client->GetCachedAppPackageNameLoggingRule();
base::Time last_update =
metrics_service_client->GetAppPackageNameLoggingRuleLastUpdateTime();

bool should_throttle = ShouldThrottleAppPackageNamesAllowlistComponent(
last_update, cached_record);
UMA_HISTOGRAM_BOOLEAN(
"Android.WebView.Metrics.PackagesAllowList.ThrottleStatus",
should_throttle);
if (should_throttle) {
return;
}

policies.push_back(
std::make_unique<AwAppsPackageNamesAllowlistComponentLoaderPolicy>(
metrics_service_client->GetAppPackageName(), std::move(cached_record),
base::BindOnce(&SetAppPackageNameLoggingRule)));
}

} // namespace android_webview

0 comments on commit 8351b0d

Please sign in to comment.