Commit c99af485 authored by Karandeep Bhatia's avatar Karandeep Bhatia Committed by Commit Bot

DNR: Implement indexing of multiple static rulesets.

This CL:
  1. changes the installation code to index all static rulesets provided
  by an extension.
  2. Adds unittests to ensure 1. above works as intended.

Remaining work:
  1. Currently rule limits for static rulesets are only evaluated per
  ruleset. Implement them for the set of enabled rulesets once we
  start supporting the 'enabled' property as part of rulesets specified
  in manifest.
  2. We have limits on the no. of install warnings per ruleset. Also,
  add a limit on the total number of warnings across all rulesets.
  3. Load multiple static rulesets on extension load.
  4. Provide ability to dynamically toggle the set of enabled static
  rulesets.

BUG=754526

Change-Id: I92202ae69724f41ce1645af72dbc88e04fdc9f3c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2107239
Commit-Queue: Karan Bhatia <karandeepb@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#754716}
parent 936d24e0
...@@ -390,7 +390,7 @@ class DeclarativeNetRequestBrowserTest ...@@ -390,7 +390,7 @@ class DeclarativeNetRequestBrowserTest
"Extensions.DeclarativeNetRequest.LoadRulesetResult", "Extensions.DeclarativeNetRequest.LoadRulesetResult",
RulesetMatcher::kLoadSuccess /*sample*/, 1 /*count*/); RulesetMatcher::kLoadSuccess /*sample*/, 1 /*count*/);
EXPECT_TRUE(HasValidIndexedRuleset(*extension, profile())); EXPECT_TRUE(AreAllIndexedStaticRulesetsValid(*extension, profile()));
// Wait for the background page to load if needed. // Wait for the background page to load if needed.
if (flags_ & kConfig_HasBackgroundScript) if (flags_ & kConfig_HasBackgroundScript)
...@@ -1877,7 +1877,9 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed, ...@@ -1877,7 +1877,9 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,
const Extension* extension = extension_registry()->GetExtensionById( const Extension* extension = extension_registry()->GetExtensionById(
extension_id, ExtensionRegistry::ENABLED); extension_id, ExtensionRegistry::ENABLED);
RulesetSource static_source = RulesetSource::CreateStatic(*extension); std::vector<RulesetSource> static_sources =
RulesetSource::CreateStatic(*extension);
ASSERT_EQ(1u, static_sources.size());
RulesetSource dynamic_source = RulesetSource dynamic_source =
RulesetSource::CreateDynamic(profile(), *extension); RulesetSource::CreateDynamic(profile(), *extension);
...@@ -1921,7 +1923,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed, ...@@ -1921,7 +1923,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,
// Test static ruleset re-indexing. // Test static ruleset re-indexing.
{ {
SCOPED_TRACE("Static ruleset corruption"); SCOPED_TRACE("Static ruleset corruption");
corrupt_file_for_checksum_mismatch(static_source.indexed_path()); corrupt_file_for_checksum_mismatch(static_sources[0].indexed_path());
base::HistogramTester tester; base::HistogramTester tester;
test_extension_works_after_reload(); test_extension_works_after_reload();
...@@ -1968,7 +1970,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed, ...@@ -1968,7 +1970,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,
{ {
SCOPED_TRACE("Static and dynamic ruleset corruption"); SCOPED_TRACE("Static and dynamic ruleset corruption");
corrupt_file_for_checksum_mismatch(dynamic_source.indexed_path()); corrupt_file_for_checksum_mismatch(dynamic_source.indexed_path());
corrupt_file_for_checksum_mismatch(static_source.indexed_path()); corrupt_file_for_checksum_mismatch(static_sources[0].indexed_path());
base::HistogramTester tester; base::HistogramTester tester;
test_extension_works_after_reload(); test_extension_works_after_reload();
...@@ -2005,12 +2007,14 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, ...@@ -2005,12 +2007,14 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
last_loaded_extension_id(), ExtensionRegistry::ENABLED); last_loaded_extension_id(), ExtensionRegistry::ENABLED);
ASSERT_TRUE(extension); ASSERT_TRUE(extension);
RulesetSource source = RulesetSource::CreateStatic(*extension); std::vector<RulesetSource> sources = RulesetSource::CreateStatic(*extension);
ASSERT_EQ(1u, sources.size());
// Mimic extension prefs corruption by overwriting the indexed ruleset // Mimic extension prefs corruption by overwriting the indexed ruleset
// checksum. // checksum.
const int kInvalidRulesetChecksum = -1; const int kInvalidRulesetChecksum = -1;
ExtensionPrefs::Get(profile())->SetDNRStaticRulesetChecksum( ExtensionPrefs::Get(profile())->SetDNRStaticRulesetChecksum(
extension_id, source.id(), kInvalidRulesetChecksum); extension_id, sources[0].id(), kInvalidRulesetChecksum);
TestExtensionRegistryObserver registry_observer( TestExtensionRegistryObserver registry_observer(
ExtensionRegistry::Get(profile()), extension_id); ExtensionRegistry::Get(profile()), extension_id);
......
...@@ -82,17 +82,20 @@ class RulesetManagerTest : public DNRTestBase { ...@@ -82,17 +82,20 @@ class RulesetManagerTest : public DNRTestBase {
ExtensionRegistry::Get(browser_context()) ExtensionRegistry::Get(browser_context())
->AddEnabled(last_loaded_extension_); ->AddEnabled(last_loaded_extension_);
RulesetSource source = RulesetSource::CreateStatic(*last_loaded_extension_); std::vector<RulesetSource> sources =
RulesetSource::CreateStatic(*last_loaded_extension_);
ASSERT_EQ(1u, sources.size());
int expected_checksum; int expected_checksum;
EXPECT_TRUE(ExtensionPrefs::Get(browser_context()) EXPECT_TRUE(ExtensionPrefs::Get(browser_context())
->GetDNRStaticRulesetChecksum(last_loaded_extension_->id(), ->GetDNRStaticRulesetChecksum(last_loaded_extension_->id(),
source.id(), sources[0].id(),
&expected_checksum)); &expected_checksum));
std::vector<std::unique_ptr<RulesetMatcher>> matchers(1); std::vector<std::unique_ptr<RulesetMatcher>> matchers(1);
EXPECT_EQ(RulesetMatcher::kLoadSuccess, EXPECT_EQ(RulesetMatcher::kLoadSuccess,
RulesetMatcher::CreateVerifiedMatcher( RulesetMatcher::CreateVerifiedMatcher(
std::move(source), expected_checksum, &matchers[0])); std::move(sources[0]), expected_checksum, &matchers[0]));
*matcher = std::make_unique<CompositeMatcher>(std::move(matchers)); *matcher = std::make_unique<CompositeMatcher>(std::move(matchers));
} }
......
...@@ -273,20 +273,28 @@ bool UnpackedInstaller::IndexAndPersistRulesIfNeeded(std::string* error) { ...@@ -273,20 +273,28 @@ bool UnpackedInstaller::IndexAndPersistRulesIfNeeded(std::string* error) {
return true; return true;
} }
using RulesetSource = declarative_net_request::RulesetSource;
// TODO(crbug.com/761107): Change this so that we don't need to parse JSON // TODO(crbug.com/761107): Change this so that we don't need to parse JSON
// in the browser process. // in the browser process.
auto ruleset_source = // TODO(crbug.com/754526): Impose a limit on the total number of rules across
declarative_net_request::RulesetSource::CreateStatic(*extension()); // all the rulesets for an extension. Also, limit the number of install
declarative_net_request::IndexAndPersistJSONRulesetResult result = // warnings across all rulesets.
ruleset_source.IndexAndPersistJSONRulesetUnsafe(); std::vector<RulesetSource> sources =
if (!result.success) { RulesetSource::CreateStatic(*extension());
*error = std::move(result.error);
return false; for (const RulesetSource& source : sources) {
} declarative_net_request::IndexAndPersistJSONRulesetResult result =
source.IndexAndPersistJSONRulesetUnsafe();
if (!result.success) {
*error = std::move(result.error);
return false;
}
ruleset_checksums_.emplace_back(result.ruleset_id, result.ruleset_checksum); ruleset_checksums_.emplace_back(result.ruleset_id, result.ruleset_checksum);
if (!result.warnings.empty()) if (!result.warnings.empty())
extension_->AddInstallWarnings(std::move(result.warnings)); extension_->AddInstallWarnings(std::move(result.warnings));
}
return true; return true;
} }
......
...@@ -125,10 +125,10 @@ class UnpackedInstaller ...@@ -125,10 +125,10 @@ class UnpackedInstaller
int flags, int flags,
std::string* error); std::string* error);
// Reads the Declarative Net Request JSON ruleset for the extension, if it // Reads the Declarative Net Request JSON rulesets for the extension, if it
// provided one, and persists the indexed ruleset. Returns false and populates // provided any, and persists the indexed rulesets. Returns false and
// |error| in case of an error. Should be called on a sequence where file IO // populates |error| in case of an error. Should be called on a sequence where
// is allowed. // file IO is allowed.
bool IndexAndPersistRulesIfNeeded(std::string* error); bool IndexAndPersistRulesIfNeeded(std::string* error);
const Extension* extension() { return extension_.get(); } const Extension* extension() { return extension_.get(); }
......
...@@ -67,6 +67,8 @@ source_set("core") { ...@@ -67,6 +67,8 @@ source_set("core") {
"constants.h", "constants.h",
"flat_ruleset_indexer.cc", "flat_ruleset_indexer.cc",
"flat_ruleset_indexer.h", "flat_ruleset_indexer.h",
"index_helper.cc",
"index_helper.h",
"indexed_rule.cc", "indexed_rule.cc",
"indexed_rule.h", "indexed_rule.h",
"parse_info.cc", "parse_info.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/api/declarative_net_request/index_helper.h"
#include <utility>
#include "base/barrier_closure.h"
namespace extensions {
namespace declarative_net_request {
// static
void IndexHelper::Start(std::vector<RulesetSource> sources,
IndexCallback callback) {
// Note we use ref-counting instead of manual memory management since there
// are some subtle cases:
// - Zero rulesets to index.
// - All individual callbacks return synchronously.
// In these cases there's a potential for a use-after-free with manual memory
// management.
auto index_helper = base::WrapRefCounted(
new IndexHelper(std::move(sources), std::move(callback)));
index_helper->Start();
}
IndexHelper::IndexHelper(std::vector<RulesetSource> sources,
IndexCallback callback)
: sources_(std::move(sources)), callback_(std::move(callback)) {}
IndexHelper::~IndexHelper() = default;
void IndexHelper::Start() {
// |all_done_closure| will be invoked once |barrier_closure| is run
// |sources_.size()| times.
base::OnceClosure all_done_closure =
base::BindOnce(&IndexHelper::OnAllRulesetsIndexed, this);
base::RepeatingClosure barrier_closure =
base::BarrierClosure(sources_.size(), std::move(all_done_closure));
for (const RulesetSource& source : sources_) {
auto callback =
base::BindOnce(&IndexHelper::OnRulesetIndexed, this, barrier_closure);
source.IndexAndPersistJSONRuleset(&decoder_, std::move(callback));
}
}
void IndexHelper::OnAllRulesetsIndexed() {
DCHECK_EQ(sources_.size(), results_.size());
// Our job is done.
std::move(callback_).Run(std::move(results_));
}
// Callback invoked when indexing of a single ruleset is completed.
void IndexHelper::OnRulesetIndexed(base::OnceClosure ruleset_done_closure,
IndexAndPersistJSONRulesetResult result) {
results_.push_back(std::move(result));
std::move(ruleset_done_closure).Run();
}
} // namespace declarative_net_request
} // namespace extensions
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_INDEX_HELPER_H_
#define EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_INDEX_HELPER_H_
#include <vector>
#include "base/callback_forward.h"
#include "base/memory/ref_counted.h"
#include "extensions/browser/api/declarative_net_request/ruleset_source.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
namespace extensions {
namespace declarative_net_request {
// A class to help in indexing multiple rulesets.
class IndexHelper : public base::RefCountedThreadSafe<IndexHelper> {
public:
// Starts indexing rulesets. Must be called on a sequence which supports file
// IO. The |callback| will be dispatched to the same sequence on which Start()
// is called.
using Results = std::vector<IndexAndPersistJSONRulesetResult>;
using IndexCallback = base::OnceCallback<void(Results)>;
static void Start(std::vector<RulesetSource> sources, IndexCallback callback);
private:
friend class base::RefCountedThreadSafe<IndexHelper>;
IndexHelper(std::vector<RulesetSource> sources, IndexCallback callback);
~IndexHelper();
// Starts indexing the rulesets.
void Start();
// Callback invoked when indexing of all rulesets is completed.
void OnAllRulesetsIndexed();
// Callback invoked when indexing of a single ruleset is completed.
void OnRulesetIndexed(base::OnceClosure ruleset_done_closure,
IndexAndPersistJSONRulesetResult result);
std::vector<RulesetSource> sources_;
IndexCallback callback_;
Results results_;
// We use a single shared Data Decoder service instance to process all of the
// rulesets for this IndexHelper.
data_decoder::DataDecoder decoder_;
};
} // namespace declarative_net_request
} // namespace extensions
#endif // EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_INDEX_HELPER_H_
...@@ -190,7 +190,11 @@ void RulesMonitorService::OnExtensionLoaded( ...@@ -190,7 +190,11 @@ void RulesMonitorService::OnExtensionLoaded(
// Static ruleset. // Static ruleset.
{ {
RulesetInfo static_ruleset(RulesetSource::CreateStatic(*extension)); std::vector<RulesetSource> static_rulesets =
RulesetSource::CreateStatic(*extension);
// TODO(crbug.com/754526): Load all static rulesets for the extension.
RulesetInfo static_ruleset(std::move(static_rulesets[0]));
bool has_checksum = prefs_->GetDNRStaticRulesetChecksum( bool has_checksum = prefs_->GetDNRStaticRulesetChecksum(
extension->id(), static_ruleset.source().id(), extension->id(), static_ruleset.source().id(),
&expected_ruleset_checksum); &expected_ruleset_checksum);
......
...@@ -301,16 +301,21 @@ ReadJSONRulesResult& ReadJSONRulesResult::operator=(ReadJSONRulesResult&&) = ...@@ -301,16 +301,21 @@ ReadJSONRulesResult& ReadJSONRulesResult::operator=(ReadJSONRulesResult&&) =
default; default;
// static // static
RulesetSource RulesetSource::CreateStatic(const Extension& extension) { std::vector<RulesetSource> RulesetSource::CreateStatic(
const DNRManifestData::RulesetInfo& info = const Extension& extension) {
declarative_net_request::DNRManifestData::GetRuleset(extension); const std::vector<DNRManifestData::RulesetInfo>& rulesets =
declarative_net_request::DNRManifestData::GetRulesets(extension);
DCHECK_GE(info.id, kMinValidStaticRulesetID);
return RulesetSource(extension.path().Append(info.relative_path), std::vector<RulesetSource> sources;
extension.path().Append( for (const auto& info : rulesets) {
file_util::GetIndexedRulesetRelativePath(info.id)), sources.push_back(
info.id, dnr_api::SOURCE_TYPE_MANIFEST, RulesetSource(extension.path().Append(info.relative_path),
dnr_api::MAX_NUMBER_OF_RULES, extension.id()); extension.path().Append(
file_util::GetIndexedRulesetRelativePath(info.id)),
info.id, dnr_api::SOURCE_TYPE_MANIFEST,
dnr_api::MAX_NUMBER_OF_RULES, extension.id()));
}
return sources;
} }
// static // static
......
...@@ -122,10 +122,10 @@ struct ReadJSONRulesResult { ...@@ -122,10 +122,10 @@ struct ReadJSONRulesResult {
// Holds paths for an extension ruleset. // Holds paths for an extension ruleset.
class RulesetSource { class RulesetSource {
public: public:
// Creates RulesetSource corresponding to the static ruleset in the extension // Creates RulesetSources corresponding to the static rulesets in the
// package. This must only be called for extensions which specified a // extension package. This must only be called for extensions which specified
// declarative ruleset. // a declarative ruleset.
static RulesetSource CreateStatic(const Extension& extension); static std::vector<RulesetSource> CreateStatic(const Extension& extension);
// Creates RulesetSource corresponding to the dynamic rules added by the // Creates RulesetSource corresponding to the dynamic rules added by the
// extension. This must only be called for extensions which specified a // extension. This must only be called for extensions which specified a
......
...@@ -255,20 +255,28 @@ std::ostream& operator<<(std::ostream& output, const ParseResult& result) { ...@@ -255,20 +255,28 @@ std::ostream& operator<<(std::ostream& output, const ParseResult& result) {
return output; return output;
} }
bool HasValidIndexedRuleset(const Extension& extension, bool AreAllIndexedStaticRulesetsValid(
content::BrowserContext* browser_context) { const Extension& extension,
RulesetSource source = RulesetSource::CreateStatic(extension); content::BrowserContext* browser_context) {
int expected_checksum; std::vector<RulesetSource> sources = RulesetSource::CreateStatic(extension);
if (!ExtensionPrefs::Get(browser_context)
->GetDNRStaticRulesetChecksum(extension.id(), source.id(), for (RulesetSource& source : sources) {
&expected_checksum)) { int expected_checksum = -1;
return false; if (!ExtensionPrefs::Get(browser_context)
->GetDNRStaticRulesetChecksum(extension.id(), source.id(),
&expected_checksum)) {
return false;
}
std::unique_ptr<RulesetMatcher> matcher;
if (RulesetMatcher::CreateVerifiedMatcher(std::move(source),
expected_checksum, &matcher) !=
RulesetMatcher::kLoadSuccess) {
return false;
}
} }
std::unique_ptr<RulesetMatcher> matcher; return true;
return RulesetMatcher::CreateVerifiedMatcher(std::move(source),
expected_checksum, &matcher) ==
RulesetMatcher::kLoadSuccess;
} }
bool CreateVerifiedMatcher(const std::vector<TestRule>& rules, bool CreateVerifiedMatcher(const std::vector<TestRule>& rules,
......
...@@ -54,10 +54,10 @@ std::ostream& operator<<(std::ostream& output, const ParseResult& result); ...@@ -54,10 +54,10 @@ std::ostream& operator<<(std::ostream& output, const ParseResult& result);
std::ostream& operator<<(std::ostream& output, std::ostream& operator<<(std::ostream& output,
const base::Optional<RequestAction>& action); const base::Optional<RequestAction>& action);
// Returns true if the given extension has a valid indexed ruleset. Should be // Returns true if the given extension's indexed static rulesets are all valid.
// called on a sequence where file IO is allowed. // Should be called on a sequence where file IO is allowed.
bool HasValidIndexedRuleset(const Extension& extension, bool AreAllIndexedStaticRulesetsValid(const Extension& extension,
content::BrowserContext* browser_context); content::BrowserContext* browser_context);
// Helper to create a verified ruleset matcher. Populates |matcher| and // Helper to create a verified ruleset matcher. Populates |matcher| and
// |expected_checksum|. Returns true on success. // |expected_checksum|. Returns true on success.
......
...@@ -130,10 +130,11 @@ std::unique_ptr<ContentVerifierIOData::ExtensionData> CreateIOData( ...@@ -130,10 +130,11 @@ std::unique_ptr<ContentVerifierIOData::ExtensionData> CreateIOData(
std::make_unique<std::set<CanonicalRelativePath>>(); std::make_unique<std::set<CanonicalRelativePath>>();
using DNRManifestData = declarative_net_request::DNRManifestData; using DNRManifestData = declarative_net_request::DNRManifestData;
if (DNRManifestData::HasRuleset(*extension)) { if (DNRManifestData::HasRuleset(*extension)) {
const DNRManifestData::RulesetInfo& info = for (const DNRManifestData::RulesetInfo& info :
DNRManifestData::GetRuleset(*extension); DNRManifestData::GetRulesets(*extension)) {
indexed_ruleset_paths->insert( indexed_ruleset_paths->insert(
canonicalize_path(file_util::GetIndexedRulesetRelativePath(info.id))); canonicalize_path(file_util::GetIndexedRulesetRelativePath(info.id)));
}
} }
return std::make_unique<ContentVerifierIOData::ExtensionData>( return std::make_unique<ContentVerifierIOData::ExtensionData>(
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "extensions/browser/api/declarative_net_request/constants.h" #include "extensions/browser/api/declarative_net_request/constants.h"
#include "extensions/browser/api/declarative_net_request/index_helper.h"
#include "extensions/browser/api/declarative_net_request/ruleset_source.h" #include "extensions/browser/api/declarative_net_request/ruleset_source.h"
#include "extensions/browser/computed_hashes.h" #include "extensions/browser/computed_hashes.h"
#include "extensions/browser/extension_file_task_runner.h" #include "extensions/browser/extension_file_task_runner.h"
...@@ -652,7 +653,7 @@ void SandboxedUnpacker::MessageCatalogsSanitized( ...@@ -652,7 +653,7 @@ void SandboxedUnpacker::MessageCatalogsSanitized(
const std::string& error_msg) { const std::string& error_msg) {
DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence()); DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
if (status == JsonFileSanitizer::Status::kSuccess) { if (status == JsonFileSanitizer::Status::kSuccess) {
IndexAndPersistJSONRulesetIfNeeded(); IndexAndPersistJSONRulesetsIfNeeded();
return; return;
} }
...@@ -687,7 +688,7 @@ void SandboxedUnpacker::MessageCatalogsSanitized( ...@@ -687,7 +688,7 @@ void SandboxedUnpacker::MessageCatalogsSanitized(
ReportFailure(failure_reason, error); ReportFailure(failure_reason, error);
} }
void SandboxedUnpacker::IndexAndPersistJSONRulesetIfNeeded() { void SandboxedUnpacker::IndexAndPersistJSONRulesetsIfNeeded() {
DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence()); DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
DCHECK(extension_); DCHECK(extension_);
...@@ -697,32 +698,43 @@ void SandboxedUnpacker::IndexAndPersistJSONRulesetIfNeeded() { ...@@ -697,32 +698,43 @@ void SandboxedUnpacker::IndexAndPersistJSONRulesetIfNeeded() {
return; return;
} }
auto ruleset_source = declarative_net_request::IndexHelper::Start(
declarative_net_request::RulesetSource::CreateStatic(*extension_); declarative_net_request::RulesetSource::CreateStatic(*extension_),
ruleset_source.IndexAndPersistJSONRuleset( base::BindOnce(&SandboxedUnpacker::OnJSONRulesetsIndexed, this));
&data_decoder_,
base::BindOnce(&SandboxedUnpacker::OnJSONRulesetIndexed, this));
} }
void SandboxedUnpacker::OnJSONRulesetIndexed( void SandboxedUnpacker::OnJSONRulesetsIndexed(
declarative_net_request::IndexAndPersistJSONRulesetResult result) { std::vector<declarative_net_request::IndexAndPersistJSONRulesetResult>
if (result.success) { results) {
base::TimeDelta total_index_and_persist_time;
size_t total_rules_count = 0;
// TODO(crbug.com/754526): Impose a limit on the total number of rules across
// all the rulesets for an extension. Also, limit the number of install
// warnings across all rulesets.
for (auto& result : results) {
if (!result.success) {
ReportFailure(
SandboxedUnpackerFailureReason::ERROR_INDEXING_DNR_RULESET,
l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE,
base::UTF8ToUTF16(result.error)));
return;
}
if (!result.warnings.empty()) if (!result.warnings.empty())
extension_->AddInstallWarnings(std::move(result.warnings)); extension_->AddInstallWarnings(std::move(result.warnings));
UMA_HISTOGRAM_COUNTS_100000(
declarative_net_request::kManifestRulesCountHistogram, total_index_and_persist_time += result.index_and_persist_time;
result.rules_count); total_rules_count += result.rules_count;
UMA_HISTOGRAM_TIMES(
declarative_net_request::kIndexAndPersistRulesTimeHistogram,
result.index_and_persist_time);
ruleset_checksums_.emplace_back(result.ruleset_id, result.ruleset_checksum); ruleset_checksums_.emplace_back(result.ruleset_id, result.ruleset_checksum);
CheckComputeHashes();
return;
} }
ReportFailure(SandboxedUnpackerFailureReason::ERROR_INDEXING_DNR_RULESET, UMA_HISTOGRAM_TIMES(
l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, declarative_net_request::kIndexAndPersistRulesTimeHistogram,
base::UTF8ToUTF16(result.error))); total_index_and_persist_time);
UMA_HISTOGRAM_COUNTS_100000(
declarative_net_request::kManifestRulesCountHistogram, total_rules_count);
CheckComputeHashes();
} }
void SandboxedUnpacker::CheckComputeHashes() { void SandboxedUnpacker::CheckComputeHashes() {
......
...@@ -218,12 +218,13 @@ class SandboxedUnpacker : public base::RefCountedThreadSafe<SandboxedUnpacker> { ...@@ -218,12 +218,13 @@ class SandboxedUnpacker : public base::RefCountedThreadSafe<SandboxedUnpacker> {
void Cleanup(); void Cleanup();
// If a Declarative Net Request JSON ruleset is present, parses the JSON // If a Declarative Net Request JSON ruleset is present, parses the JSON
// ruleset for the Declarative Net Request API and persists the indexed // rulesets for the Declarative Net Request API and persists the indexed
// ruleset. // rulesets.
void IndexAndPersistJSONRulesetIfNeeded(); void IndexAndPersistJSONRulesetsIfNeeded();
void OnJSONRulesetIndexed( void OnJSONRulesetsIndexed(
declarative_net_request::IndexAndPersistJSONRulesetResult result); std::vector<declarative_net_request::IndexAndPersistJSONRulesetResult>
results);
// Computed hashes: if requested (via ShouldComputeHashes callback in // Computed hashes: if requested (via ShouldComputeHashes callback in
// SandbloxedUnpackerClient), calculate hashes of all extensions' resources // SandbloxedUnpackerClient), calculate hashes of all extensions' resources
......
...@@ -26,15 +26,13 @@ bool DNRManifestData::HasRuleset(const Extension& extension) { ...@@ -26,15 +26,13 @@ bool DNRManifestData::HasRuleset(const Extension& extension) {
} }
// static // static
const DNRManifestData::RulesetInfo& DNRManifestData::GetRuleset( const std::vector<DNRManifestData::RulesetInfo>& DNRManifestData::GetRulesets(
const Extension& extension) { const Extension& extension) {
Extension::ManifestData* data = Extension::ManifestData* data =
extension.GetManifestData(manifest_keys::kDeclarativeNetRequestKey); extension.GetManifestData(manifest_keys::kDeclarativeNetRequestKey);
DCHECK(data); DCHECK(data);
// TODO(crbug.com/754526): Change this to return |rulesets|. Currently we only return static_cast<DNRManifestData*>(data)->rulesets;
// index the first ruleset specified by the extension in its manifest.
return static_cast<DNRManifestData*>(data)->rulesets[0];
} }
} // namespace declarative_net_request } // namespace declarative_net_request
......
...@@ -37,7 +37,8 @@ struct DNRManifestData : Extension::ManifestData { ...@@ -37,7 +37,8 @@ struct DNRManifestData : Extension::ManifestData {
// Returns the RulesetInfo for the |extension|. This must be called only if // Returns the RulesetInfo for the |extension|. This must be called only if
// HasRuleset returns true for the |extension|. // HasRuleset returns true for the |extension|.
static const RulesetInfo& GetRuleset(const Extension& extension); static const std::vector<RulesetInfo>& GetRulesets(
const Extension& extension);
std::vector<RulesetInfo> rulesets; std::vector<RulesetInfo> rulesets;
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "extensions/common/file_util.h" #include "extensions/common/file_util.h"
#include "extensions/common/manifest_constants.h" #include "extensions/common/manifest_constants.h"
#include "extensions/common/value_builder.h" #include "extensions/common/value_builder.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace extensions { namespace extensions {
...@@ -56,15 +57,24 @@ class DNRManifestTest : public testing::Test { ...@@ -56,15 +57,24 @@ class DNRManifestTest : public testing::Test {
EXPECT_EQ(expected_error, error); EXPECT_EQ(expected_error, error);
} }
// Loads the extension and verifies that the JSON ruleset location is // Loads the extension and verifies that the manifest info is correctly set
// correctly set up. Returns the loaded extension. // up.. Returns the loaded extension.
scoped_refptr<Extension> LoadAndExpectSuccess() { void LoadAndExpectSuccess(const std::vector<base::FilePath>& expected_paths) {
std::string error; std::string error;
scoped_refptr<Extension> extension = file_util::LoadExtension( scoped_refptr<Extension> extension = file_util::LoadExtension(
temp_dir_.GetPath(), Manifest::UNPACKED, Extension::NO_FLAGS, &error); temp_dir_.GetPath(), Manifest::UNPACKED, Extension::NO_FLAGS, &error);
EXPECT_TRUE(extension) << error; ASSERT_TRUE(extension) << error;
EXPECT_TRUE(error.empty()); EXPECT_TRUE(error.empty());
return extension;
ASSERT_TRUE(DNRManifestData::HasRuleset(*extension));
const std::vector<DNRManifestData::RulesetInfo>& rulesets =
DNRManifestData::GetRulesets(*extension);
ASSERT_EQ(expected_paths.size(), rulesets.size());
for (size_t i = 0; i < rulesets.size(); ++i) {
EXPECT_GE(rulesets[i].id, kMinValidStaticRulesetID);
EXPECT_EQ(rulesets[i].relative_path, expected_paths[i]);
}
} }
void WriteManifestAndRuleset( void WriteManifestAndRuleset(
...@@ -98,10 +108,7 @@ TEST_F(DNRManifestTest, EmptyRuleset) { ...@@ -98,10 +108,7 @@ TEST_F(DNRManifestTest, EmptyRuleset) {
base::FilePath ruleset_path = base::FilePath(kJSONRulesetFilepath); base::FilePath ruleset_path = base::FilePath(kJSONRulesetFilepath);
WriteManifestAndRuleset(*CreateManifest(kJSONRulesFilename), {ruleset_path}); WriteManifestAndRuleset(*CreateManifest(kJSONRulesFilename), {ruleset_path});
scoped_refptr<Extension> extension = LoadAndExpectSuccess(); LoadAndExpectSuccess({ruleset_path});
ASSERT_TRUE(DNRManifestData::HasRuleset(*extension));
EXPECT_EQ(ruleset_path,
DNRManifestData::GetRuleset(*extension).relative_path);
} }
TEST_F(DNRManifestTest, InvalidManifestKey) { TEST_F(DNRManifestTest, InvalidManifestKey) {
...@@ -139,14 +146,16 @@ TEST_F(DNRManifestTest, MultipleRulesFileSuccess) { ...@@ -139,14 +146,16 @@ TEST_F(DNRManifestTest, MultipleRulesFileSuccess) {
ruleset.path = path2.AsUTF8Unsafe(); ruleset.path = path2.AsUTF8Unsafe();
rule_resources.Append(ruleset.ToValue()); rule_resources.Append(ruleset.ToValue());
manifest->SetList(GetRuleResourcesKey(), rule_resources.Build()); base::FilePath path3(FILE_PATH_LITERAL("file3.json"));
ruleset.path = path3.AsUTF8Unsafe();
rule_resources.Append(ruleset.ToValue());
WriteManifestAndRuleset(*manifest, {path1, path2}); manifest->SetList(GetRuleResourcesKey(), rule_resources.Build());
scoped_refptr<Extension> extension = LoadAndExpectSuccess(); std::vector<base::FilePath> paths = {path1, path2, path3};
WriteManifestAndRuleset(*manifest, paths);
ASSERT_TRUE(DNRManifestData::HasRuleset(*extension)); LoadAndExpectSuccess(paths);
EXPECT_EQ(path1, DNRManifestData::GetRuleset(*extension).relative_path);
} }
TEST_F(DNRManifestTest, MultipleRulesFileInvalidPath) { TEST_F(DNRManifestTest, MultipleRulesFileInvalidPath) {
...@@ -244,10 +253,7 @@ TEST_F(DNRManifestTest, RulesFileInNestedDirectory) { ...@@ -244,10 +253,7 @@ TEST_F(DNRManifestTest, RulesFileInNestedDirectory) {
WriteManifestAndRuleset(*manifest, {nested_path}); WriteManifestAndRuleset(*manifest, {nested_path});
scoped_refptr<Extension> extension = LoadAndExpectSuccess(); LoadAndExpectSuccess({nested_path});
ASSERT_TRUE(DNRManifestData::HasRuleset(*extension));
EXPECT_EQ(nested_path, DNRManifestData::GetRuleset(*extension).relative_path);
} }
} // namespace } // namespace
......
...@@ -57,6 +57,56 @@ void SetValue(base::DictionaryValue* dict, ...@@ -57,6 +57,56 @@ void SetValue(base::DictionaryValue* dict,
dict->Set(key, ToValue(*value)); dict->Set(key, ToValue(*value));
} }
// Helper to build an extension manifest which uses the
// kDeclarativeNetRequestKey manifest key. |hosts| specifies the host
// permissions to grant. |flags| is a bitmask of ConfigFlag to configure the
// extension. |ruleset_info| specifies the static rulesets for the extension.
std::unique_ptr<base::DictionaryValue> CreateManifest(
const std::vector<TestRulesetInfo>& ruleset_info,
const std::vector<std::string>& hosts,
unsigned flags) {
std::vector<std::string> permissions = hosts;
permissions.push_back(kAPIPermission);
// These permissions are needed for some tests. TODO(karandeepb): Add a
// ConfigFlag for these.
permissions.push_back("webRequest");
permissions.push_back("webRequestBlocking");
if (flags & kConfig_HasFeedbackPermission)
permissions.push_back(kFeedbackAPIPermission);
if (flags & kConfig_HasActiveTab)
permissions.push_back("activeTab");
std::vector<std::string> background_scripts;
if (flags & kConfig_HasBackgroundScript)
background_scripts.push_back("background.js");
ListBuilder rule_resources_builder;
for (const TestRulesetInfo& info : ruleset_info) {
dnr_api::Ruleset ruleset;
ruleset.path = info.relative_file_path;
rule_resources_builder.Append(ruleset.ToValue());
}
return DictionaryBuilder()
.Set(keys::kName, "Test extension")
.Set(keys::kDeclarativeNetRequestKey,
DictionaryBuilder()
.Set(keys::kDeclarativeRuleResourcesKey,
rule_resources_builder.Build())
.Build())
.Set(keys::kPermissions, ToListValue(permissions))
.Set(keys::kVersion, "1.0")
.Set(keys::kManifestVersion, 2)
.Set("background", DictionaryBuilder()
.Set("scripts", ToListValue(background_scripts))
.Build())
.Set(keys::kBrowserAction, DictionaryBuilder().Build())
.Build();
}
} // namespace } // namespace
TestRuleCondition::TestRuleCondition() = default; TestRuleCondition::TestRuleCondition() = default;
...@@ -187,42 +237,9 @@ std::unique_ptr<base::DictionaryValue> CreateManifest( ...@@ -187,42 +237,9 @@ std::unique_ptr<base::DictionaryValue> CreateManifest(
const std::string& json_rules_filename, const std::string& json_rules_filename,
const std::vector<std::string>& hosts, const std::vector<std::string>& hosts,
unsigned flags) { unsigned flags) {
std::vector<std::string> permissions = hosts; std::vector<TestRulesetInfo> rulesets;
permissions.push_back(kAPIPermission); rulesets.push_back({json_rules_filename, base::ListValue()});
permissions.push_back("webRequest"); return CreateManifest(rulesets, hosts, flags);
permissions.push_back("webRequestBlocking");
if (flags & kConfig_HasFeedbackPermission)
permissions.push_back(kFeedbackAPIPermission);
if (flags & kConfig_HasActiveTab)
permissions.push_back("activeTab");
std::vector<std::string> background_scripts;
if (flags & kConfig_HasBackgroundScript)
background_scripts.push_back("background.js");
dnr_api::Ruleset ruleset;
ruleset.path = json_rules_filename;
std::unique_ptr<base::ListValue> rule_resources =
ListBuilder().Append(ruleset.ToValue()).Build();
return DictionaryBuilder()
.Set(keys::kName, "Test extension")
.Set(keys::kDeclarativeNetRequestKey,
DictionaryBuilder()
.Set(keys::kDeclarativeRuleResourcesKey,
std::move(rule_resources))
.Build())
.Set(keys::kPermissions, ToListValue(permissions))
.Set(keys::kVersion, "1.0")
.Set(keys::kManifestVersion, 2)
.Set("background", DictionaryBuilder()
.Set("scripts", ToListValue(background_scripts))
.Build())
.Set(keys::kBrowserAction, DictionaryBuilder().Build())
.Build();
} }
std::unique_ptr<base::ListValue> ToListValue( std::unique_ptr<base::ListValue> ToListValue(
...@@ -240,13 +257,15 @@ std::unique_ptr<base::ListValue> ToListValue(const std::vector<TestRule>& vec) { ...@@ -240,13 +257,15 @@ std::unique_ptr<base::ListValue> ToListValue(const std::vector<TestRule>& vec) {
return builder.Build(); return builder.Build();
} }
void WriteManifestAndRuleset(const base::FilePath& extension_dir, void WriteManifestAndRulesets(const base::FilePath& extension_dir,
const TestRulesetInfo& info, const std::vector<TestRulesetInfo>& ruleset_info,
const std::vector<std::string>& hosts, const std::vector<std::string>& hosts,
unsigned flags) { unsigned flags) {
// Persist JSON rules file. // Persist JSON rules files.
JSONFileValueSerializer(extension_dir.AppendASCII(info.relative_file_path)) for (const TestRulesetInfo& info : ruleset_info) {
.Serialize(info.rules_value); JSONFileValueSerializer(extension_dir.AppendASCII(info.relative_file_path))
.Serialize(info.rules_value);
}
// Persists a background script if needed. // Persists a background script if needed.
if (flags & ConfigFlag::kConfig_HasBackgroundScript) { if (flags & ConfigFlag::kConfig_HasBackgroundScript) {
...@@ -258,7 +277,16 @@ void WriteManifestAndRuleset(const base::FilePath& extension_dir, ...@@ -258,7 +277,16 @@ void WriteManifestAndRuleset(const base::FilePath& extension_dir,
// Persist manifest file. // Persist manifest file.
JSONFileValueSerializer(extension_dir.Append(kManifestFilename)) JSONFileValueSerializer(extension_dir.Append(kManifestFilename))
.Serialize(*CreateManifest(info.relative_file_path, hosts, flags)); .Serialize(*CreateManifest(ruleset_info, hosts, flags));
}
void WriteManifestAndRuleset(const base::FilePath& extension_dir,
const TestRulesetInfo& info,
const std::vector<std::string>& hosts,
unsigned flags) {
std::vector<TestRulesetInfo> rulesets;
rulesets.push_back({info.relative_file_path, info.rules_value.Clone()});
WriteManifestAndRulesets(extension_dir, rulesets, hosts, flags);
} }
} // namespace declarative_net_request } // namespace declarative_net_request
......
...@@ -152,10 +152,19 @@ enum ConfigFlag { ...@@ -152,10 +152,19 @@ enum ConfigFlag {
kConfig_HasActiveTab = 1 << 2, kConfig_HasActiveTab = 1 << 2,
}; };
// Describes a single extension ruleset.
struct TestRulesetInfo {
// File path relative to the extension directory.
std::string relative_file_path;
// The base::Value corresponding to the rules in the ruleset.
base::Value rules_value;
};
// Helper to build an extension manifest which uses the // Helper to build an extension manifest which uses the
// kDeclarativeNetRequestKey manifest key. |hosts| specifies the host // kDeclarativeNetRequestKey manifest key. |hosts| specifies the host
// permissions to grant. |flags| is a bitmask of ConfigFlag to configure the // permissions to grant. |flags| is a bitmask of ConfigFlag to configure the
// extension. // extension. Should be used when the extension has a single static ruleset.
std::unique_ptr<base::DictionaryValue> CreateManifest( std::unique_ptr<base::DictionaryValue> CreateManifest(
const std::string& json_rules_filename, const std::string& json_rules_filename,
const std::vector<std::string>& hosts = {}, const std::vector<std::string>& hosts = {},
...@@ -169,16 +178,17 @@ std::unique_ptr<base::ListValue> ToListValue( ...@@ -169,16 +178,17 @@ std::unique_ptr<base::ListValue> ToListValue(
std::unique_ptr<base::ListValue> ToListValue( std::unique_ptr<base::ListValue> ToListValue(
const std::vector<TestRule>& rules); const std::vector<TestRule>& rules);
// Describes a single extension ruleset. // Writes the rulesets specified in |ruleset_info| in the given |extension_dir|
struct TestRulesetInfo { // together with the manifest file. |hosts| specifies the host permissions, the
std::string relative_file_path; // extensions should have. |flags| is a bitmask of ConfigFlag to configure the
base::Value rules_value; // extension.
}; void WriteManifestAndRulesets(const base::FilePath& extension_dir,
const std::vector<TestRulesetInfo>& ruleset_info,
const std::vector<std::string>& hosts,
unsigned flags = ConfigFlag::kConfig_None);
// Writes the declarative rules specified in |ruleset_info| in the given // Specialization of WriteManifestAndRulesets above for an extension with a
// |extension_dir| together with the manifest file. |hosts| specifies the host // single static ruleset.
// permissions, the extensions should have. |flags| is a bitmask of ConfigFlag
// to configure the extension.
void WriteManifestAndRuleset(const base::FilePath& extension_dir, void WriteManifestAndRuleset(const base::FilePath& extension_dir,
const TestRulesetInfo& ruleset_info, const TestRulesetInfo& ruleset_info,
const std::vector<std::string>& hosts, const std::vector<std::string>& hosts,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment