Commit 89c1673e authored by rdevlin.cronin's avatar rdevlin.cronin Committed by Commit bot

[Extensions Bindings] Apply per-context restrictions to events

Similar to functions, API events can be restricted on a per-context
basis. Apply the same deletion logic to them if they are unavailable
in the given context.

Modify APIBinding unittests to reflect this, and add more comprehensive
tests in NativeExtensionBindingsSystem to test APIs that can be
partially-available to websites.

BUG=653596

Review-Url: https://codereview.chromium.org/2821793003
Cr-Commit-Position: refs/heads/master@{#465736}
parent 438c2eb7
......@@ -311,6 +311,14 @@ v8::Local<v8::Object> APIBinding::CreateInstance(
CHECK(success.FromJust());
}
}
for (const auto& event : events_) {
if (!is_available.Run(event->full_name)) {
v8::Maybe<bool> success = object->Delete(
context, gin::StringToSymbol(isolate, event->exposed_name));
CHECK(success.IsJust());
CHECK(success.FromJust());
}
}
return object;
}
......
......@@ -148,12 +148,18 @@ class APIBindingUnittest : public APIBindingTest {
}
void TearDown() override {
DisposeAllContexts();
request_handler_.reset();
event_handler_.reset();
binding_.reset();
APIBindingTest::TearDown();
}
void OnWillDisposeContext(v8::Local<v8::Context> context) override {
event_handler_->InvalidateContext(context);
request_handler_->InvalidateContext(context);
}
void SetFunctions(const char* functions) {
binding_functions_ = ListValueFromString(functions);
ASSERT_TRUE(binding_functions_);
......@@ -506,7 +512,7 @@ TEST_F(APIBindingUnittest, TypeRefsTest) {
}
TEST_F(APIBindingUnittest, RestrictedAPIs) {
const char kRestrictedFunctions[] =
const char kFunctions[] =
"[{"
" 'name': 'allowedOne',"
" 'parameters': []"
......@@ -520,18 +526,22 @@ TEST_F(APIBindingUnittest, RestrictedAPIs) {
" 'name': 'restrictedTwo',"
" 'parameters': []"
"}]";
SetFunctions(kRestrictedFunctions);
SetFunctions(kFunctions);
const char kEvents[] =
"[{'name': 'allowedEvent'}, {'name': 'restrictedEvent'}]";
SetEvents(kEvents);
InitializeBinding();
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = MainContext();
auto is_available = [](const std::string& name) {
std::set<std::string> functions = {"test.allowedOne", "test.allowedTwo",
"test.restrictedOne",
"test.restrictedTwo"};
EXPECT_TRUE(functions.count(name));
return name == "test.allowedOne" || name == "test.allowedTwo";
std::set<std::string> allowed = {"test.allowedOne", "test.allowedTwo",
"test.allowedEvent"};
std::set<std::string> restricted = {
"test.restrictedOne", "test.restrictedTwo", "test.restrictedEvent"};
EXPECT_TRUE(allowed.count(name) || restricted.count(name)) << name;
return allowed.count(name) != 0;
};
v8::Local<v8::Object> binding_object =
......@@ -546,8 +556,10 @@ TEST_F(APIBindingUnittest, RestrictedAPIs) {
EXPECT_TRUE(is_defined("allowedOne"));
EXPECT_TRUE(is_defined("allowedTwo"));
EXPECT_TRUE(is_defined("allowedEvent"));
EXPECT_FALSE(is_defined("restrictedOne"));
EXPECT_FALSE(is_defined("restrictedTwo"));
EXPECT_FALSE(is_defined("restrictedEvent"));
}
// Tests that events specified in the API are created as properties of the API
......
......@@ -793,4 +793,49 @@ TEST_F(NativeExtensionBindingsSystemUnittest,
check_properties_inequal(context_a, context_b, "chrome.idle.onStateChanged");
}
// Tests that API methods and events that are conditionally available based on
// context are properly present or absent from the API object.
TEST_F(NativeExtensionBindingsSystemUnittest,
CheckRestrictedFeaturesBasedOnContext) {
scoped_refptr<Extension> extension =
CreateExtension("extension", ItemType::EXTENSION, {"idle"});
RegisterExtension(extension->id());
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> blessed_context = MainContext();
v8::Local<v8::Context> webpage_context = AddContext();
// Create two contexts - a blessed extension context and a normal web page
// context.
ScriptContext* blessed_script_context = CreateScriptContext(
blessed_context, extension.get(), Feature::BLESSED_EXTENSION_CONTEXT);
blessed_script_context->set_url(extension->url());
bindings_system()->UpdateBindingsForContext(blessed_script_context);
ScriptContext* webpage_script_context =
CreateScriptContext(webpage_context, nullptr, Feature::WEB_PAGE_CONTEXT);
webpage_script_context->set_url(GURL("http://example.com"));
bindings_system()->UpdateBindingsForContext(webpage_script_context);
auto property_exists = [](v8::Local<v8::Context> context,
base::StringPiece property) {
v8::Local<v8::Value> value = V8ValueFromScriptSource(context, property);
EXPECT_FALSE(value.IsEmpty());
return !value->IsUndefined();
};
// Check that properties are correctly restricted. The blessed context should
// have access to the whole runtime API, but the webpage should only have
// access to sendMessage.
const char kSendMessage[] = "chrome.runtime.sendMessage";
const char kGetUrl[] = "chrome.runtime.getURL";
const char kOnMessage[] = "chrome.runtime.onMessage";
EXPECT_TRUE(property_exists(blessed_context, kSendMessage));
EXPECT_TRUE(property_exists(blessed_context, kGetUrl));
EXPECT_TRUE(property_exists(blessed_context, kOnMessage));
EXPECT_TRUE(property_exists(webpage_context, kSendMessage));
EXPECT_FALSE(property_exists(webpage_context, kGetUrl));
EXPECT_FALSE(property_exists(webpage_context, kOnMessage));
}
} // namespace extensions
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