blob: ec72900a5e20476294715e3f8d75a8f93c126cff [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 "extensions/common/manifest_handlers/trial_tokens_handler.h"
#include <memory>
#include <utility>
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"
namespace extensions {
namespace {
// The maximum number of tokens which will be processed
// This value should be sufficiently large to avoid any issues in practice,
// but small enough to bound resource consumption to something reasonable
const size_t kMaxTokenCount = 100;
// The maximum length of a single token
// Keep this value in sync with the value of same name in
// third_party/blink/public/common/origin_trials/trial_token.cc
const size_t kMaxTokenSize = 6144;
const TrialTokens* GetTokens(const Extension& extension) {
return static_cast<const TrialTokens*>(
extension.GetManifestData(manifest_keys::kTrialTokens));
}
} // namespace
TrialTokens::TrialTokens(std::set<std::string> tokens)
: tokens_(std::move(tokens)) {}
TrialTokens::TrialTokens(TrialTokens&& other) = default;
TrialTokens::~TrialTokens() = default;
// static
const std::set<std::string>* TrialTokens::GetTrialTokens(
const Extension& extension) {
const auto* tokens = GetTokens(extension);
if (!tokens) {
return nullptr;
}
DCHECK(!tokens->tokens_.empty());
return &tokens->tokens_;
}
// static
bool TrialTokens::HasTrialTokens(const Extension& extension) {
return GetTokens(extension);
}
TrialTokensHandler::TrialTokensHandler() = default;
TrialTokensHandler::~TrialTokensHandler() = default;
bool TrialTokensHandler::Parse(Extension* extension, std::u16string* error) {
const base::Value* trial_tokens = nullptr;
// 'trial_tokens' must be a non-empty list. Otherwise, log a benign warning
if (!extension->manifest()->GetList(manifest_keys::kTrialTokens,
&trial_tokens) ||
trial_tokens->GetList().empty()) {
extension->AddInstallWarning(
InstallWarning(manifest_errors::kInvalidTrialTokensNonEmptyList,
manifest_keys::kTrialTokens));
return true;
}
std::set<std::string> tokens;
for (const auto& token : trial_tokens->GetList()) {
// Avoid processing an arbitrarily large number of trial tokens
if (tokens.size() >= kMaxTokenCount) {
extension->AddInstallWarning(extensions::InstallWarning(
base::StringPrintf(manifest_errors::kInvalidTrialTokensTooManyTokens,
kMaxTokenCount),
manifest_keys::kTrialTokens));
break;
}
// Skip non-string token or empty string and add a benign warning
if (!token.is_string() || token.GetString().empty()) {
extension->AddInstallWarning(
InstallWarning(manifest_errors::kInvalidTrialTokensValue,
manifest_keys::kTrialTokens));
continue;
}
// Skip excessively long long token and add a benign warning
if (token.GetString().length() > kMaxTokenSize) {
extension->AddInstallWarning(extensions::InstallWarning(
base::StringPrintf(manifest_errors::kInvalidTrialTokensValueTooLong,
kMaxTokenSize),
manifest_keys::kTrialTokens));
continue;
}
// Warn about duplicate tokens
if (tokens.contains(token.GetString())) {
extension->AddInstallWarning(extensions::InstallWarning(
base::StringPrintf(manifest_errors::kInvalidTrialTokensValueDuplicate,
token.GetString().c_str()),
manifest_keys::kTrialTokens));
continue;
}
// TODO(crbug.com/40282364): Add validation of trial token contents, log an
// InstallWarning and skip the token here
tokens.insert(token.GetString());
}
if (tokens.empty()) {
// If we did not find a single valid token, do not save anything
return true;
}
extension->SetManifestData(manifest_keys::kTrialTokens,
std::make_unique<TrialTokens>(std::move(tokens)));
return true;
}
base::span<const char* const> TrialTokensHandler::Keys() const {
static constexpr const char* kKeys[] = {manifest_keys::kTrialTokens};
return kKeys;
}
} // namespace extensions