Commit 18d7dcfd authored by Erik Luo's avatar Erik Luo Committed by Commit Bot

Whitelist side-effect-free CommandLineAPI methods

Marks methods on the Command Line API that produce no JS-observable
side-effect.

Bug: 829571
Change-Id: I7920b2ded3eada33fc2d000aa46a0a7eef61daca
Reviewed-on: https://chromium-review.googlesource.com/1025171Reviewed-by: default avatarDmitry Gozman <dgozman@chromium.org>
Reviewed-by: default avatarAleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Commit-Queue: Erik Luo <luoe@chromium.org>
Cr-Commit-Position: refs/heads/master@{#553270}
parent da37423f
Tests that evaluating V8-embedder callbacks allows side-effect-free attribute getters. Should not crash. Tests that evaluating V8-embedder callbacks allows side-effect-free attribute getters. Should not crash.
Expression `document.title = "foo"`
has side effect: true, expected: true
Expression `document.domain`
has side effect: false, expected: false
Expression `document.referrer`
has side effect: false, expected: false
Expression `document.cookie`
has side effect: false, expected: false
Expression `document.title`
has side effect: false, expected: false
Expression `document.documentElement`
has side effect: false, expected: false
Expression `document.scrollingElement`
has side effect: false, expected: false
Expression `document.body`
has side effect: false, expected: false
Expression `document.head`
has side effect: false, expected: false
Expression `document.activeElement`
has side effect: false, expected: false
Expression `div.tagName`
has side effect: false, expected: false
Expression `div.id`
has side effect: false, expected: false
Expression `div.className`
has side effect: false, expected: false
Expression `div.nodeType`
has side effect: false, expected: false
Expression `div.nodeName`
has side effect: false, expected: false
Expression `div.nodeValue`
has side effect: false, expected: false
Expression `div.textContent`
has side effect: false, expected: false
Expression `div.isConnected`
has side effect: false, expected: false
Expression `div.parentNode`
has side effect: false, expected: false
Expression `div.parentElement`
has side effect: false, expected: false
Expression `div.childNodes`
has side effect: false, expected: false
Expression `div.firstChild`
has side effect: false, expected: false
Expression `div.lastChild`
has side effect: false, expected: false
Expression `div.previousSibling`
has side effect: false, expected: false
Expression `div.nextSibling`
has side effect: false, expected: false
Expression `document.nodeType`
has side effect: false, expected: false
Expression `document.nodeName`
has side effect: false, expected: false
Expression `document.nodeValue`
has side effect: false, expected: false
Expression `document.textContent`
has side effect: false, expected: false
Expression `document.isConnected`
has side effect: false, expected: false
Expression `document.parentNode`
has side effect: false, expected: false
Expression `document.parentElement`
has side effect: false, expected: false
Expression `document.childNodes`
has side effect: false, expected: false
Expression `document.firstChild`
has side effect: false, expected: false
Expression `document.lastChild`
has side effect: false, expected: false
Expression `document.previousSibling`
has side effect: false, expected: false
Expression `document.nextSibling`
has side effect: false, expected: false
Expression `textNode.nodeType`
has side effect: false, expected: false
Expression `textNode.nodeName`
has side effect: false, expected: false
Expression `textNode.nodeValue`
has side effect: false, expected: false
Expression `textNode.textContent`
has side effect: false, expected: false
Expression `textNode.isConnected`
has side effect: false, expected: false
Expression `textNode.parentNode`
has side effect: false, expected: false
Expression `textNode.parentElement`
has side effect: false, expected: false
Expression `textNode.childNodes`
has side effect: false, expected: false
Expression `textNode.firstChild`
has side effect: false, expected: false
Expression `textNode.lastChild`
has side effect: false, expected: false
Expression `textNode.previousSibling`
has side effect: false, expected: false
Expression `textNode.nextSibling`
has side effect: false, expected: false
Expression `div.childElementCount`
has side effect: false, expected: false
Expression `div.children`
has side effect: false, expected: false
Expression `div.firstElementChild`
has side effect: false, expected: false
Expression `div.lastElementChild`
has side effect: false, expected: false
Expression `document.childElementCount`
has side effect: false, expected: false
Expression `document.children`
has side effect: false, expected: false
Expression `document.firstElementChild`
has side effect: false, expected: false
Expression `document.lastElementChild`
has side effect: false, expected: false
Expression `textNode.childElementCount`
has side effect: false, expected: false
Expression `textNode.children`
has side effect: false, expected: false
Expression `textNode.firstElementChild`
has side effect: false, expected: false
Expression `textNode.lastElementChild`
has side effect: false, expected: false
Expression `devicePixelRatio`
has side effect: false, expected: false
Expression `screenX`
has side effect: false, expected: false
Expression `screenY`
has side effect: false, expected: false
...@@ -81,10 +81,7 @@ ...@@ -81,10 +81,7 @@
if (exceptionDetails && if (exceptionDetails &&
exceptionDetails.exception.description.startsWith('EvalError: Possible side-effect in debug-evaluate')) exceptionDetails.exception.description.startsWith('EvalError: Possible side-effect in debug-evaluate'))
hasSideEffect = true; hasSideEffect = true;
if (hasSideEffect !== expectSideEffect) { const failed = (hasSideEffect !== expectSideEffect);
testRunner.log(`FAIL: "${expression}" hasSideEffect = ${hasSideEffect}, expectSideEffect = ${expectSideEffect}`); testRunner.log(`${failed ? 'FAIL: ' : ''}Expression \`${expression}\`\nhas side effect: ${hasSideEffect}, expected: ${expectSideEffect}`);
testRunner.completeTest();
return;
}
} }
}) })
Tests that evaluating V8-embedder callbacks allows side-effect-free methods. Should not crash. Tests that evaluating V8-embedder callbacks allows side-effect-free methods. Should not crash.
Expression `document.querySelector('div').x = "foo"`
has side effect: true, expected: true
Expression `$('div')`
has side effect: false, expected: false
Expression `$$('div')`
has side effect: false, expected: false
Expression `$x('//div')`
has side effect: false, expected: false
Expression `getEventListeners(document)`
has side effect: false, expected: false
Expression `$.toString()`
has side effect: false, expected: false
Expression `$$.toString()`
has side effect: false, expected: false
Expression `$x.toString()`
has side effect: false, expected: false
Expression `getEventListeners.toString()`
has side effect: false, expected: false
Expression `monitorEvents()`
has side effect: true, expected: true
Expression `unmonitorEvents()`
has side effect: true, expected: true
Expression `monitorEvents.toString()`
has side effect: false, expected: false
Expression `unmonitorEvents.toString()`
has side effect: false, expected: false
Expression `document.getElementsByTagName('div')`
has side effect: false, expected: false
Expression `document.getElementsByTagNameNS('http://www.w3.org/1999/xhtml', 'div')`
has side effect: false, expected: false
Expression `document.getElementsByClassName('foo')`
has side effect: false, expected: false
Expression `document.getElementsByName('div-name')`
has side effect: false, expected: false
Expression `div.getAttributeNames()`
has side effect: false, expected: false
Expression `divNoAttrs.getAttributeNames()`
has side effect: false, expected: false
Expression `div.getAttribute()`
has side effect: false, expected: false
Expression `div.getAttribute('attr1')`
has side effect: false, expected: false
Expression `div.getAttribute({})`
has side effect: false, expected: false
Expression `divNoAttrs.getAttribute('attr1')`
has side effect: false, expected: false
Expression `div.hasAttribute()`
has side effect: false, expected: false
Expression `div.hasAttribute('attr1')`
has side effect: false, expected: false
Expression `div.hasAttribute({})`
has side effect: false, expected: false
Expression `divNoAttrs.hasAttribute('attr1')`
has side effect: false, expected: false
Expression `div.contains(textNode)`
has side effect: false, expected: false
Expression `div.contains()`
has side effect: false, expected: false
Expression `div.contains({})`
has side effect: false, expected: false
Expression `div.querySelector('div')`
has side effect: false, expected: false
Expression `div.querySelectorAll('div')`
has side effect: false, expected: false
Expression `document.contains(textNode)`
has side effect: false, expected: false
Expression `document.contains()`
has side effect: false, expected: false
Expression `document.contains({})`
has side effect: false, expected: false
Expression `document.querySelector('div')`
has side effect: false, expected: false
Expression `document.querySelectorAll('div')`
has side effect: false, expected: false
Expression `textNode.contains(textNode)`
has side effect: false, expected: false
Expression `textNode.contains()`
has side effect: false, expected: false
Expression `textNode.contains({})`
has side effect: false, expected: false
Expression `textNode.querySelector('div')`
has side effect: false, expected: false
Expression `textNode.querySelectorAll('div')`
has side effect: false, expected: false
Expression `global_performance.now()`
has side effect: false, expected: false
...@@ -27,6 +27,22 @@ ...@@ -27,6 +27,22 @@
// Sanity check: test that setters are not allowed on whitelisted methods. // Sanity check: test that setters are not allowed on whitelisted methods.
await checkHasSideEffect(`document.querySelector('div').x = "foo"`); await checkHasSideEffect(`document.querySelector('div').x = "foo"`);
// Command Line API
await checkHasNoSideEffect(`$('div')`);
await checkHasNoSideEffect(`$$('div')`);
await checkHasNoSideEffect(`$x('//div')`);
await checkHasNoSideEffect(`getEventListeners(document)`);
await checkHasNoSideEffect(`$.toString()`);
await checkHasNoSideEffect(`$$.toString()`);
await checkHasNoSideEffect(`$x.toString()`);
await checkHasNoSideEffect(`getEventListeners.toString()`);
// Unsafe Command Line API
await checkHasSideEffect(`monitorEvents()`);
await checkHasSideEffect(`unmonitorEvents()`);
await checkHasNoSideEffect(`monitorEvents.toString()`);
await checkHasNoSideEffect(`unmonitorEvents.toString()`);
// Document // Document
await checkHasNoSideEffect(`document.getElementsByTagName('div')`); await checkHasNoSideEffect(`document.getElementsByTagName('div')`);
await checkHasNoSideEffect(`document.getElementsByTagNameNS('http://www.w3.org/1999/xhtml', 'div')`); await checkHasNoSideEffect(`document.getElementsByTagNameNS('http://www.w3.org/1999/xhtml', 'div')`);
...@@ -70,16 +86,13 @@ ...@@ -70,16 +86,13 @@
} }
async function checkExpression(expression, expectSideEffect) { async function checkExpression(expression, expectSideEffect) {
var response = await dp.Runtime.evaluate({expression, throwOnSideEffect: true}); var response = await dp.Runtime.evaluate({expression, throwOnSideEffect: true, includeCommandLineAPI: true});
var hasSideEffect = false; var hasSideEffect = false;
var exceptionDetails = response.result.exceptionDetails; var exceptionDetails = response.result.exceptionDetails;
if (exceptionDetails && if (exceptionDetails &&
exceptionDetails.exception.description.startsWith('EvalError: Possible side-effect in debug-evaluate')) exceptionDetails.exception.description.startsWith('EvalError: Possible side-effect in debug-evaluate'))
hasSideEffect = true; hasSideEffect = true;
if (hasSideEffect !== expectSideEffect) { const failed = (hasSideEffect !== expectSideEffect);
testRunner.log(`FAIL: ${expression} hasSideEffect = ${hasSideEffect}, expectSideEffect = ${expectSideEffect}`); testRunner.log(`${failed ? 'FAIL: ' : ''}Expression \`${expression}\`\nhas side effect: ${hasSideEffect}, expected: ${expectSideEffect}`);
testRunner.completeTest();
return;
}
} }
}) })
...@@ -340,13 +340,16 @@ void MainThreadDebugger::installAdditionalCommandLineAPI( ...@@ -340,13 +340,16 @@ void MainThreadDebugger::installAdditionalCommandLineAPI(
ThreadDebugger::installAdditionalCommandLineAPI(context, object); ThreadDebugger::installAdditionalCommandLineAPI(context, object);
CreateFunctionProperty( CreateFunctionProperty(
context, object, "$", MainThreadDebugger::QuerySelectorCallback, context, object, "$", MainThreadDebugger::QuerySelectorCallback,
"function $(selector, [startNode]) { [Command Line API] }"); "function $(selector, [startNode]) { [Command Line API] }",
v8::SideEffectType::kHasNoSideEffect);
CreateFunctionProperty( CreateFunctionProperty(
context, object, "$$", MainThreadDebugger::QuerySelectorAllCallback, context, object, "$$", MainThreadDebugger::QuerySelectorAllCallback,
"function $$(selector, [startNode]) { [Command Line API] }"); "function $$(selector, [startNode]) { [Command Line API] }",
v8::SideEffectType::kHasNoSideEffect);
CreateFunctionProperty( CreateFunctionProperty(
context, object, "$x", MainThreadDebugger::XpathSelectorCallback, context, object, "$x", MainThreadDebugger::XpathSelectorCallback,
"function $x(xpath, [startNode]) { [Command Line API] }"); "function $x(xpath, [startNode]) { [Command Line API] }",
v8::SideEffectType::kHasNoSideEffect);
} }
static Node* SecondArgumentAsNode( static Node* SecondArgumentAsNode(
......
...@@ -219,16 +219,18 @@ static v8::Maybe<bool> CreateDataProperty(v8::Local<v8::Context> context, ...@@ -219,16 +219,18 @@ static v8::Maybe<bool> CreateDataProperty(v8::Local<v8::Context> context,
return object->CreateDataProperty(context, key, value); return object->CreateDataProperty(context, key, value);
} }
static void CreateFunctionPropertyWithData(v8::Local<v8::Context> context, static void CreateFunctionPropertyWithData(
v8::Local<v8::Object> object, v8::Local<v8::Context> context,
const char* name, v8::Local<v8::Object> object,
v8::FunctionCallback callback, const char* name,
v8::Local<v8::Value> data, v8::FunctionCallback callback,
const char* description) { v8::Local<v8::Value> data,
const char* description,
v8::SideEffectType side_effect_type) {
v8::Local<v8::String> func_name = V8String(context->GetIsolate(), name); v8::Local<v8::String> func_name = V8String(context->GetIsolate(), name);
v8::Local<v8::Function> func; v8::Local<v8::Function> func;
if (!v8::Function::New(context, callback, data, 0, if (!v8::Function::New(context, callback, data, 0,
v8::ConstructorBehavior::kThrow) v8::ConstructorBehavior::kThrow, side_effect_type)
.ToLocal(&func)) .ToLocal(&func))
return; return;
func->SetName(func_name); func->SetName(func_name);
...@@ -236,7 +238,8 @@ static void CreateFunctionPropertyWithData(v8::Local<v8::Context> context, ...@@ -236,7 +238,8 @@ static void CreateFunctionPropertyWithData(v8::Local<v8::Context> context,
V8String(context->GetIsolate(), description); V8String(context->GetIsolate(), description);
v8::Local<v8::Function> to_string_function; v8::Local<v8::Function> to_string_function;
if (v8::Function::New(context, ReturnDataCallback, return_value, 0, if (v8::Function::New(context, ReturnDataCallback, return_value, 0,
v8::ConstructorBehavior::kThrow) v8::ConstructorBehavior::kThrow,
v8::SideEffectType::kHasNoSideEffect)
.ToLocal(&to_string_function)) .ToLocal(&to_string_function))
CreateDataProperty(context, func, CreateDataProperty(context, func,
V8AtomicString(context->GetIsolate(), "toString"), V8AtomicString(context->GetIsolate(), "toString"),
...@@ -256,14 +259,16 @@ v8::Maybe<bool> ThreadDebugger::CreateDataPropertyInArray( ...@@ -256,14 +259,16 @@ v8::Maybe<bool> ThreadDebugger::CreateDataPropertyInArray(
return array->CreateDataProperty(context, index, value); return array->CreateDataProperty(context, index, value);
} }
void ThreadDebugger::CreateFunctionProperty(v8::Local<v8::Context> context, void ThreadDebugger::CreateFunctionProperty(
v8::Local<v8::Object> object, v8::Local<v8::Context> context,
const char* name, v8::Local<v8::Object> object,
v8::FunctionCallback callback, const char* name,
const char* description) { v8::FunctionCallback callback,
const char* description,
v8::SideEffectType side_effect_type) {
CreateFunctionPropertyWithData(context, object, name, callback, CreateFunctionPropertyWithData(context, object, name, callback,
v8::External::New(context->GetIsolate(), this), v8::External::New(context->GetIsolate(), this),
description); description, side_effect_type);
} }
void ThreadDebugger::installAdditionalCommandLineAPI( void ThreadDebugger::installAdditionalCommandLineAPI(
...@@ -272,7 +277,8 @@ void ThreadDebugger::installAdditionalCommandLineAPI( ...@@ -272,7 +277,8 @@ void ThreadDebugger::installAdditionalCommandLineAPI(
CreateFunctionProperty( CreateFunctionProperty(
context, object, "getEventListeners", context, object, "getEventListeners",
ThreadDebugger::GetEventListenersCallback, ThreadDebugger::GetEventListenersCallback,
"function getEventListeners(node) { [Command Line API] }"); "function getEventListeners(node) { [Command Line API] }",
v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::Value> function_value; v8::Local<v8::Value> function_value;
bool success = bool success =
...@@ -287,11 +293,13 @@ void ThreadDebugger::installAdditionalCommandLineAPI( ...@@ -287,11 +293,13 @@ void ThreadDebugger::installAdditionalCommandLineAPI(
CreateFunctionPropertyWithData( CreateFunctionPropertyWithData(
context, object, "monitorEvents", ThreadDebugger::MonitorEventsCallback, context, object, "monitorEvents", ThreadDebugger::MonitorEventsCallback,
function_value, function_value,
"function monitorEvents(object, [types]) { [Command Line API] }"); "function monitorEvents(object, [types]) { [Command Line API] }",
v8::SideEffectType::kHasSideEffect);
CreateFunctionPropertyWithData( CreateFunctionPropertyWithData(
context, object, "unmonitorEvents", context, object, "unmonitorEvents",
ThreadDebugger::UnmonitorEventsCallback, function_value, ThreadDebugger::UnmonitorEventsCallback, function_value,
"function unmonitorEvents(object, [types]) { [Command Line API] }"); "function unmonitorEvents(object, [types]) { [Command Line API] }",
v8::SideEffectType::kHasSideEffect);
} }
static Vector<String> NormalizeEventTypes( static Vector<String> NormalizeEventTypes(
......
...@@ -69,7 +69,8 @@ class CORE_EXPORT ThreadDebugger : public v8_inspector::V8InspectorClient, ...@@ -69,7 +69,8 @@ class CORE_EXPORT ThreadDebugger : public v8_inspector::V8InspectorClient,
v8::Local<v8::Object>, v8::Local<v8::Object>,
const char* name, const char* name,
v8::FunctionCallback, v8::FunctionCallback,
const char* description); const char* description,
v8::SideEffectType side_effect_type);
static v8::Maybe<bool> CreateDataPropertyInArray(v8::Local<v8::Context>, static v8::Maybe<bool> CreateDataPropertyInArray(v8::Local<v8::Context>,
v8::Local<v8::Array>, v8::Local<v8::Array>,
int index, int index,
......
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