diff options
author | rafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-01 22:33:23 +0000 |
---|---|---|
committer | rafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-01 22:33:23 +0000 |
commit | 200dcd387d6902556e41e9439ce64f7f219f5809 (patch) | |
tree | 4389542472391337f43240c963ff282f696e8676 | |
parent | bc2ff51999fe9ef5c041f2df2ffbbdb4c30baec9 (diff) | |
download | chromium_src-200dcd387d6902556e41e9439ce64f7f219f5809.zip chromium_src-200dcd387d6902556e41e9439ce64f7f219f5809.tar.gz chromium_src-200dcd387d6902556e41e9439ce64f7f219f5809.tar.bz2 |
Prevent extensions from clobbering JSON implementation that extension calls use
BUG=38857
TEST=none
Review URL: http://codereview.chromium.org/2387002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@48667 0039d316-1c4b-4281-b951-d872f2087c98
5 files changed, 75 insertions, 21 deletions
diff --git a/chrome/renderer/resources/event_bindings.js b/chrome/renderer/resources/event_bindings.js index aaea01c..4fa1309 100644 --- a/chrome/renderer/resources/event_bindings.js +++ b/chrome/renderer/resources/event_bindings.js @@ -9,6 +9,40 @@ var chrome = chrome || {}; native function DetachEvent(eventName); var chromeHidden = GetChromeHidden(); + + // Local implementation of JSON.parse & JSON.stringify that protect us + // from being clobbered by an extension. + chromeHidden.JSON = new (function() { + const $Object = Object; + const $Array = Array; + const $jsonStringify = JSON.stringify; + const $jsonParse = JSON.parse; + + this.stringify = function(thing) { + var customizedObjectToJSON = $Object.prototype.toJSON; + var customizedArrayToJSON = $Array.prototype.toJSON; + if (customizedObjectToJSON !== undefined) { + $Object.prototype.toJSON = null; + } + if (customizedArrayToJSON !== undefined) { + $Array.prototype.toJSON = null; + } + try { + return $jsonStringify(thing); + } finally { + if (customizedObjectToJSON !== undefined) { + $Object.prototype.toJSON = customizedObjectToJSON; + } + if (customizedArrayToJSON !== undefined) { + $Array.prototype.toJSON = customizedArrayToJSON; + } + } + }; + + this.parse = function(thing) { + return $jsonParse(thing); + }; + })(); // Event object. If opt_eventName is provided, this object represents // the unique instance of that named event, and dispatching an event @@ -53,7 +87,7 @@ var chrome = chrome || {}; chromeHidden.Event.dispatchJSON = function(name, args) { if (attachedNamedEvents[name]) { if (args) { - args = JSON.parse(args); + args = chromeHidden.JSON.parse(args); } return attachedNamedEvents[name].dispatch.apply( attachedNamedEvents[name], args); diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js index fe393ba..8955daf 100644 --- a/chrome/renderer/resources/extension_process_bindings.js +++ b/chrome/renderer/resources/extension_process_bindings.js @@ -92,7 +92,7 @@ var chrome = chrome || {}; if (request.callback) { // Callbacks currently only support one callback argument. - var callbackArgs = response ? [JSON.parse(response)] : []; + var callbackArgs = response ? [chromeHidden.JSON.parse(response)] : []; // Validate callback in debug only -- and only when the // caller has provided a callback. Implementations of api @@ -177,20 +177,8 @@ var chrome = chrome || {}; if (request.args === undefined) request.args = null; - // Some javascript libraries (e.g. prototype.js version <= 1.6) add a toJSON - // serializer function on Array.prototype that is incompatible with our - // native JSON library, causing incorrect deserialization in the C++ side of - // StartRequest. We work around that here by temporarily removing the toJSON - // function. - var arrayToJsonTmp; - if (Array.prototype.toJSON) { - arrayToJsonTmp = Array.prototype.toJSON; - Array.prototype.toJSON = null; - } - var sargs = JSON.stringify(request.args); - if (arrayToJsonTmp) { - Array.prototype.toJSON = arrayToJsonTmp; - } + var sargs = chromeHidden.JSON.stringify(request.args); + var requestId = GetNextRequestId(); requests[requestId] = request; var hasCallback = (request.callback || customCallback) ? true : false; @@ -334,7 +322,7 @@ var chrome = chrome || {}; // TODO(rafaelw): Handle synchronous functions. // TOOD(rafaelw): Consider providing some convenient override points // for api functions that wish to insert themselves into the call. - var apiDefinitions = JSON.parse(GetExtensionAPIDefinition()); + var apiDefinitions = chromeHidden.JSON.parse(GetExtensionAPIDefinition()); apiDefinitions.forEach(function(apiDef) { var module = chrome; @@ -609,7 +597,7 @@ var chrome = chrome || {}; // Set up the onclick handler if we were passed one in the request. if (request.args.onclick) { - var menuItemId = JSON.parse(response); + var menuItemId = chromeHidden.JSON.parse(response); chromeHidden.contextMenuHandlers[menuItemId] = request.args.onclick; } }; diff --git a/chrome/renderer/resources/renderer_extension_bindings.js b/chrome/renderer/resources/renderer_extension_bindings.js index dc53cd7..1f3220c 100644 --- a/chrome/renderer/resources/renderer_extension_bindings.js +++ b/chrome/renderer/resources/renderer_extension_bindings.js @@ -75,7 +75,7 @@ var chrome = chrome || {}; var isExternal = sourceExtensionId != chromeHidden.extensionId; if (tab) - tab = JSON.parse(tab); + tab = chromeHidden.JSON.parse(tab); var sender = {tab: tab, id: sourceExtensionId}; // Special case for sendRequest/onRequest. @@ -119,7 +119,7 @@ var chrome = chrome || {}; var port = ports[portId]; if (port) { if (msg) { - msg = JSON.parse(msg); + msg = chromeHidden.JSON.parse(msg); } port.onMessage.dispatch(msg, port); } @@ -131,7 +131,7 @@ var chrome = chrome || {}; // JSON.stringify doesn't support a root object which is undefined. if (msg === undefined) msg = null; - PostMessage(this.portId_, JSON.stringify(msg)); + PostMessage(this.portId_, chromeHidden.JSON.stringify(msg)); }; // Disconnects the port from the other end. diff --git a/chrome/test/data/extensions/api_test/messaging/connect/page.js b/chrome/test/data/extensions/api_test/messaging/connect/page.js index b876f7e..9d45f64 100644 --- a/chrome/test/data/extensions/api_test/messaging/connect/page.js +++ b/chrome/test/data/extensions/api_test/messaging/connect/page.js @@ -1,3 +1,19 @@ +JSON.parse = function() { + return "JSON.parse clobbered by content script."; +} + +JSON.stringify = function() { + return "JSON.stringify clobbered by content script."; +} + +Array.prototype.toJSON = function() { + return "Array.prototype.toJSON clobbered by content script."; +} + +Object.prototype.toJSON = function() { + return "Object.prototype.toJSON clobbered by content script."; +} + // For complex connect tests. chrome.extension.onConnect.addListener(function(port) { console.log('connected'); diff --git a/chrome/test/data/extensions/api_test/messaging/connect/test.html b/chrome/test/data/extensions/api_test/messaging/connect/test.html index 3e24916..4983a75 100644 --- a/chrome/test/data/extensions/api_test/messaging/connect/test.html +++ b/chrome/test/data/extensions/api_test/messaging/connect/test.html @@ -1,4 +1,20 @@ <script> +JSON.parse = function() { + return "JSON.parse clobbered by extension."; +} + +JSON.stringify = function() { + return "JSON.stringify clobbered by extension."; +} + +Array.prototype.toJSON = function() { + return "Array.prototype.toJSON clobbered by extension."; +} + +Object.prototype.toJSON = function() { + return "Object.prototype.toJSON clobbered by extension."; +} + // Keep track of the tab that we're running tests in, for simplicity. var testTab = null; |