blob: 42d9fdf5e4717591a1b8728150a45524d05e7bdb [file] [log] [blame]
// Copyright 2023 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/tpcd/metadata/browser/parser.h"
#include <string>
#include <utility>
#include "base/base64.h"
#include "base/check.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/field_trial_params.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/sequence_checker.h"
#include "base/strings/strcat.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/features.h"
#include "components/tpcd/metadata/common/proto/metadata.pb.h"
#include "net/base/features.h"
#include "third_party/zlib/google/compression_utils.h"
namespace tpcd::metadata {
// static
Parser* Parser::GetInstance() {
static base::NoDestructor<Parser> instance;
return instance.get();
}
// static
TpcdMetadataRuleSource Parser::ToRuleSource(const std::string& source) {
if (source == kSourceTest) {
return TpcdMetadataRuleSource::SOURCE_TEST;
} else if (source == kSource1pDt) {
return TpcdMetadataRuleSource::SOURCE_1P_DT;
} else if (source == kSource3pDt) {
return TpcdMetadataRuleSource::SOURCE_3P_DT;
} else if (source == kSourceDogFood) {
return TpcdMetadataRuleSource::SOURCE_DOGFOOD;
} else if (source == kSourceCriticalSector) {
return TpcdMetadataRuleSource::SOURCE_CRITICAL_SECTOR;
} else if (source == kSourceCuj) {
return TpcdMetadataRuleSource::SOURCE_CUJ;
} else if (source == kSourceGovEduTld) {
return TpcdMetadataRuleSource::SOURCE_GOV_EDU_TLD;
}
// `SOURCE_UNSPECIFIED` is never send by the server. It is considered
// invalid by the sanitizer. Thus, used here as a translation for any new,
// uncategorized server source type.
return TpcdMetadataRuleSource::SOURCE_UNSPECIFIED;
}
// static
bool Parser::IsValidMetadata(const Metadata& metadata) {
for (const tpcd::metadata::MetadataEntry& me : metadata.metadata_entries()) {
if (!me.has_primary_pattern_spec() ||
!ContentSettingsPattern::FromString(me.primary_pattern_spec())
.IsValid()) {
return false;
}
if (!me.has_secondary_pattern_spec() ||
!ContentSettingsPattern::FromString(me.secondary_pattern_spec())
.IsValid()) {
return false;
}
if (!me.has_source()) {
return false;
}
if (base::FeatureList::IsEnabled(
net::features::kTpcdMetadataStageControl)) {
if (me.has_dtrp() && !IsValidDtrp(me.dtrp())) {
return false;
} else if (me.has_dtrp_override() &&
(!me.has_dtrp() || !IsValidDtrp(me.dtrp_override()))) {
return false;
}
}
}
return true;
}
Parser::Parser() = default;
Parser::~Parser() = default;
MetadataEntries ToMetadataEntries(const Metadata& metadata) {
MetadataEntries metadata_entries;
for (const auto& me : metadata.metadata_entries()) {
metadata_entries.emplace_back(me);
}
return metadata_entries;
}
void Parser::ParseMetadata(const std::string& raw_metadata) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Metadata metadata;
// Not expected to ever fail when called by the component updater as
// the `TpcdMetadataComponentInstaller::VerifyInstallation()` already makes
// sure of it.
CHECK(metadata.ParseFromString(raw_metadata));
metadata_ = ToMetadataEntries(metadata);
CallOnMetadataReady();
}
MetadataEntries ParseMetadataFromFeatureParam(
const base::FieldTrialParams& params) {
Metadata metadata;
std::string raw_metadata;
CHECK(base::Base64Decode(
params.find(Parser::kMetadataFeatureParamName)->second, &raw_metadata));
std::string uncompressed;
CHECK(compression::GzipUncompress(raw_metadata, &uncompressed));
CHECK(metadata.ParseFromString(uncompressed));
CHECK(Parser::IsValidMetadata(metadata));
return ToMetadataEntries(metadata);
}
void Parser::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void Parser::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void Parser::CallOnMetadataReady() {
for (auto& observer : observers_) {
observer.OnMetadataReady();
}
}
MetadataEntries Parser::GetMetadata() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::FieldTrialParams params;
bool has_feature_params = base::GetFieldTrialParamsByFeature(
net::features::kTpcdMetadataGrants, &params);
if (has_feature_params &&
params.find(kMetadataFeatureParamName) != params.end()) {
metadata_source_ = MetadataSource::kFeatureParams;
return ParseMetadataFromFeatureParam(params);
}
metadata_source_ = MetadataSource::kServer;
// If no metadata are present within the Feature params, use the metadata
// provided by the Component Updater if present.
return metadata_.value_or(MetadataEntries());
}
// Start Parser testing methods impl:
MetadataEntries Parser::GetInstalledMetadataForTesting() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return metadata_.value_or(MetadataEntries());
}
void Parser::ResetStatesForTesting() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
metadata_.reset();
}
MetadataEntries Parser::ParseMetadataFromFeatureParamForTesting(
const base::FieldTrialParams& params) {
return ParseMetadataFromFeatureParam(params);
}
// End Parser testing methods impl.
namespace helpers {
MetadataEntry* AddEntryToMetadata(
Metadata& metadata,
const std::string& primary_pattern_spec,
const std::string& secondary_pattern_spec,
const std::string& source,
const std::optional<uint32_t>& dtrp,
const std::optional<uint32_t>& dtrp_override) {
MetadataEntry* me = metadata.add_metadata_entries();
me->set_primary_pattern_spec(primary_pattern_spec);
me->set_secondary_pattern_spec(secondary_pattern_spec);
me->set_source(source);
if (dtrp.has_value()) {
me->set_dtrp(dtrp.value());
}
if (dtrp_override.has_value()) {
me->set_dtrp_override(dtrp_override.value());
}
DCHECK(Parser::IsValidMetadata(metadata));
return me;
}
} // namespace helpers
} // namespace tpcd::metadata