summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrdevlin.cronin <rdevlin.cronin@chromium.org>2016-02-22 12:09:24 -0800
committerCommit bot <commit-bot@chromium.org>2016-02-22 20:10:55 +0000
commitdc42ae208c2744f7fb144b2e396358a1fc34db87 (patch)
tree27610c21fbd408a1c50e6bbfbe2d56a06ff2e074
parent12372dcba97c093259993fd9fbc4096c3e385cd3 (diff)
downloadchromium_src-dc42ae208c2744f7fb144b2e396358a1fc34db87.zip
chromium_src-dc42ae208c2744f7fb144b2e396358a1fc34db87.tar.gz
chromium_src-dc42ae208c2744f7fb144b2e396358a1fc34db87.tar.bz2
[Extensions] Update web API cloberring for platform apps
Platform App clobbering for certain Web APIs was incomplete. Make it more complete. See bug for more details. BUG=585282 Review URL: https://codereview.chromium.org/1716513002 Cr-Commit-Position: refs/heads/master@{#376784}
-rw-r--r--chrome/browser/extensions/app_window_overrides_browsertest.cc29
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/test/data/extensions/app_forbidden_apis/document_apis/background.js17
-rw-r--r--chrome/test/data/extensions/app_forbidden_apis/document_apis/manifest.json9
-rw-r--r--chrome/test/data/extensions/app_forbidden_apis/onbeforeunload/background.js37
-rw-r--r--chrome/test/data/extensions/app_forbidden_apis/onbeforeunload/manifest.json9
-rw-r--r--chrome/test/data/extensions/platform_apps/restrictions/main.js8
-rw-r--r--extensions/renderer/resources/platform_app.js49
8 files changed, 142 insertions, 17 deletions
diff --git a/chrome/browser/extensions/app_window_overrides_browsertest.cc b/chrome/browser/extensions/app_window_overrides_browsertest.cc
new file mode 100644
index 0000000..3f7a8d8
--- /dev/null
+++ b/chrome/browser/extensions/app_window_overrides_browsertest.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 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 "chrome/browser/extensions/extension_browsertest.h"
+#include "extensions/test/result_catcher.h"
+
+namespace extensions {
+
+using AppWindowRestrictedApisBrowserTest = ExtensionBrowserTest;
+
+// Test that the window events like onbeforeunload event are correctly
+// clobbered.
+IN_PROC_BROWSER_TEST_F(AppWindowRestrictedApisBrowserTest, UnloadEvents) {
+ ResultCatcher catcher;
+ ASSERT_TRUE(LoadExtension(
+ test_data_dir_.AppendASCII("app_forbidden_apis/onbeforeunload")));
+ ASSERT_TRUE(catcher.GetNextResult());
+}
+
+// Test that Document apis like document.write are correctly clobbered.
+IN_PROC_BROWSER_TEST_F(AppWindowRestrictedApisBrowserTest, DocumentApis) {
+ ResultCatcher catcher;
+ ASSERT_TRUE(LoadExtension(
+ test_data_dir_.AppendASCII("app_forbidden_apis/document_apis")));
+ ASSERT_TRUE(catcher.GetNextResult());
+}
+
+} // namespace extensions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index ea7dddf..8b20c08 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -213,6 +213,7 @@
'browser/extensions/api/webstore_private/webstore_private_apitest.cc',
'browser/extensions/app_background_page_apitest.cc',
'browser/extensions/app_process_apitest.cc',
+ 'browser/extensions/app_window_overrides_browsertest.cc',
'browser/extensions/background_app_browsertest.cc',
'browser/extensions/background_page_apitest.cc',
'browser/extensions/background_scripts_apitest.cc',
diff --git a/chrome/test/data/extensions/app_forbidden_apis/document_apis/background.js b/chrome/test/data/extensions/app_forbidden_apis/document_apis/background.js
new file mode 100644
index 0000000..06c4e90
--- /dev/null
+++ b/chrome/test/data/extensions/app_forbidden_apis/document_apis/background.js
@@ -0,0 +1,17 @@
+// Copyright 2016 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.
+
+'use strict';
+
+var writeSuccess = true;
+try {
+ Document.prototype.write.call(document, 'Hello, world');
+} catch (e) {
+ writeSuccess = false;
+}
+
+if (writeSuccess)
+ chrome.test.fail();
+else
+ chrome.test.succeed();
diff --git a/chrome/test/data/extensions/app_forbidden_apis/document_apis/manifest.json b/chrome/test/data/extensions/app_forbidden_apis/document_apis/manifest.json
new file mode 100644
index 0000000..ae7374d
--- /dev/null
+++ b/chrome/test/data/extensions/app_forbidden_apis/document_apis/manifest.json
@@ -0,0 +1,9 @@
+{
+ "name": "Using restricted APIs",
+ "description": "An app using forbidden window apis",
+ "manifest_version": 2,
+ "version": "1",
+ "app": {
+ "background": { "scripts": ["background.js"] }
+ }
+}
diff --git a/chrome/test/data/extensions/app_forbidden_apis/onbeforeunload/background.js b/chrome/test/data/extensions/app_forbidden_apis/onbeforeunload/background.js
new file mode 100644
index 0000000..0f0d75c
--- /dev/null
+++ b/chrome/test/data/extensions/app_forbidden_apis/onbeforeunload/background.js
@@ -0,0 +1,37 @@
+// Copyright 2016 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.
+
+'use strict';
+
+var didRun = sessionStorage.didRun;
+
+function beforeUnload() {
+ chrome.test.fail();
+}
+
+try {
+ delete window.onbeforeunload;
+ window.onbeforeunload = beforeUnload;
+} catch (e) {}
+
+try {
+ window.addEventListener('beforeunload', beforeUnload);
+} catch (e) {}
+
+try {
+ var beforeUnloadTricky = {
+ toString: function() {
+ beforeUnloadTricky.toString = function() { return 'beforeunload'; };
+ return 'something not beforeunload';
+ }
+ };
+ window.addEventListener(beforeUnloadTricky, beforeUnload);
+} catch (e) {}
+
+if (!didRun) {
+ sessionStorage.didRun = true;
+ location.reload();
+} else {
+ chrome.test.succeed();
+}
diff --git a/chrome/test/data/extensions/app_forbidden_apis/onbeforeunload/manifest.json b/chrome/test/data/extensions/app_forbidden_apis/onbeforeunload/manifest.json
new file mode 100644
index 0000000..ae7374d
--- /dev/null
+++ b/chrome/test/data/extensions/app_forbidden_apis/onbeforeunload/manifest.json
@@ -0,0 +1,9 @@
+{
+ "name": "Using restricted APIs",
+ "description": "An app using forbidden window apis",
+ "manifest_version": 2,
+ "version": "1",
+ "app": {
+ "background": { "scripts": ["background.js"] }
+ }
+}
diff --git a/chrome/test/data/extensions/platform_apps/restrictions/main.js b/chrome/test/data/extensions/platform_apps/restrictions/main.js
index cd7ddd5..856b0c8 100644
--- a/chrome/test/data/extensions/platform_apps/restrictions/main.js
+++ b/chrome/test/data/extensions/platform_apps/restrictions/main.js
@@ -74,28 +74,28 @@ chrome.test.runTests([
},
function testWindowFind() {
- assertEq('undefined', typeof(Window.prototype.find('needle')));
+ assertEq('undefined', typeof(Window.prototype.find));
assertEq('undefined', typeof(window.find('needle')));
assertEq('undefined', typeof(find('needle')));
succeed();
},
function testWindowAlert() {
- assertEq('undefined', typeof(Window.prototype.alert()));
+ assertEq('undefined', typeof(Window.prototype.alert));
assertEq('undefined', typeof(window.alert()));
assertEq('undefined', typeof(alert()));
succeed();
},
function testWindowConfirm() {
- assertEq('undefined', typeof(Window.prototype.confirm('Failed')));
+ assertEq('undefined', typeof(Window.prototype.confirm));
assertEq('undefined', typeof(window.confirm('Failed')));
assertEq('undefined', typeof(confirm('Failed')));
succeed();
},
function testWindowPrompt() {
- assertEq('undefined', typeof(Window.prototype.prompt('Failed')));
+ assertEq('undefined', typeof(Window.prototype.prompt));
assertEq('undefined', typeof(window.prompt('Failed')));
assertEq('undefined', typeof(prompt('Failed')));
succeed();
diff --git a/extensions/renderer/resources/platform_app.js b/extensions/renderer/resources/platform_app.js
index 21263d3..9f386d7 100644
--- a/extensions/renderer/resources/platform_app.js
+++ b/extensions/renderer/resources/platform_app.js
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+var logging = requireNative('logging');
+
/**
* Returns a function that logs a 'not available' error to the console and
* returns undefined.
@@ -65,10 +67,16 @@ function generateThrowingMethodStub(messagePrefix, opt_messageSuffix) {
*/
function disableMethods(object, objectName, methodNames, useThrowingStubs) {
$Array.forEach(methodNames, function(methodName) {
+ logging.DCHECK($Object.getOwnPropertyDescriptor(object, methodName),
+ objectName + ': ' + methodName);
var messagePrefix = objectName + '.' + methodName + '()';
- object[methodName] = useThrowingStubs ?
- generateThrowingMethodStub(messagePrefix) :
- generateDisabledMethodStub(messagePrefix);
+ $Object.defineProperty(object, methodName, {
+ configurable: false,
+ enumerable: false,
+ value: useThrowingStubs ?
+ generateThrowingMethodStub(messagePrefix) :
+ generateDisabledMethodStub(messagePrefix)
+ });
});
}
@@ -85,9 +93,16 @@ function disableMethods(object, objectName, methodNames, useThrowingStubs) {
* referred to by web developers, e.g. "document" instead of
* "HTMLDocument").
* @param {Array<string>} propertyNames names of properties to disable.
+ * @param {?string=} opt_messageSuffix An optional suffix for the message.
+ * @param {boolean=} opt_ignoreMissingProperty True if we allow disabling
+ * getters for non-existent properties.
*/
-function disableGetters(object, objectName, propertyNames, opt_messageSuffix) {
+function disableGetters(object, objectName, propertyNames, opt_messageSuffix,
+ opt_ignoreMissingProperty) {
$Array.forEach(propertyNames, function(propertyName) {
+ logging.DCHECK(opt_ignoreMissingProperty ||
+ $Object.getOwnPropertyDescriptor(object, propertyName),
+ objectName + ': ' + propertyName);
var stub = generateDisabledMethodStub(objectName + '.' + propertyName,
opt_messageSuffix);
stub._is_platform_app_disabled_getter = true;
@@ -130,10 +145,12 @@ function disableGetters(object, objectName, propertyNames, opt_messageSuffix) {
*/
function disableSetters(object, objectName, propertyNames, opt_messageSuffix) {
$Array.forEach(propertyNames, function(propertyName) {
+ logging.DCHECK($Object.getOwnPropertyDescriptor(object, propertyName),
+ objectName + ': ' + propertyName);
var stub = generateDisabledMethodStub(objectName + '.' + propertyName,
opt_messageSuffix);
$Object.defineProperty(object, propertyName, {
- configurable: true,
+ configurable: false,
enumerable: false,
get: function() {
return;
@@ -144,24 +161,26 @@ function disableSetters(object, objectName, propertyNames, opt_messageSuffix) {
}
// Disable benign Document methods.
-disableMethods(HTMLDocument.prototype, 'document', ['open', 'clear', 'close']);
+disableMethods(Document.prototype, 'document', ['open', 'close']);
+disableMethods(HTMLDocument.prototype, 'document', ['clear']);
// Replace evil Document methods with exception-throwing stubs.
-disableMethods(HTMLDocument.prototype, 'document', ['write', 'writeln'], true);
+disableMethods(Document.prototype, 'document', ['write', 'writeln'], true);
// Disable history.
Object.defineProperty(window, "history", { value: {} });
+// Note: we just blew away the history object, so we need to ignore the fact
+// that these properties aren't defined on the object.
disableGetters(window.history, 'history',
- ['back', 'forward', 'go', 'length', 'pushState', 'replaceState', 'state']);
+ ['back', 'forward', 'go', 'length', 'pushState', 'replaceState', 'state'],
+ null, true);
// Disable find.
disableMethods(window, 'window', ['find']);
-disableMethods(Window.prototype, 'window', ['find']);
// Disable modal dialogs. Shell windows disable these anyway, but it's nice to
// warn.
disableMethods(window, 'window', ['alert', 'confirm', 'prompt']);
-disableMethods(Window.prototype, 'window', ['alert', 'confirm', 'prompt']);
// Disable window.*bar.
disableGetters(window, 'window',
@@ -194,16 +213,20 @@ window.addEventListener('readystatechange', function(event) {
// it first to 'undefined' to avoid this.
document.all = undefined;
disableGetters(document, 'document',
- ['alinkColor', 'all', 'bgColor', 'fgColor', 'linkColor', 'vlinkColor']);
+ ['alinkColor', 'all', 'bgColor', 'fgColor', 'linkColor', 'vlinkColor'],
+ null, true);
}, true);
// Disable onunload, onbeforeunload.
disableSetters(window, 'window', ['onbeforeunload', 'onunload']);
-disableSetters(Window.prototype, 'window', ['onbeforeunload', 'onunload']);
var eventTargetAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type) {
+ var args = $Array.slice(arguments);
+ // Note: Force conversion to a string in order to catch any funny attempts
+ // to pass in something that evals to 'unload' but wouldn't === 'unload'.
+ var type = (args[0] += '');
if (type === 'unload' || type === 'beforeunload')
generateDisabledMethodStub(type)();
else
- return $Function.apply(eventTargetAddEventListener, this, arguments);
+ return $Function.apply(eventTargetAddEventListener, this, args);
};