www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

commit 021cbc87259edcd993646569614118278c9bf6ab
parent 4037193f92c77ca1c6ef53c4e937a9f30f0d9414
Author: Simon Kornblith <simon@simonster.com>
Date:   Mon,  2 Aug 2010 02:26:46 +0000

closes #1285, Add typeof checks to data read from scraper sandbox

use XPCSafeJSObjectWrappers to enforce security of everything coming out of translators. this seems to work, but needs testing.


Diffstat:
Mchrome/content/zotero/xpcom/translate.js | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mchrome/content/zotero/xpcom/utilities.js | 45++++++++++++++++++++++++++++++++++++---------
2 files changed, 151 insertions(+), 11 deletions(-)

diff --git a/chrome/content/zotero/xpcom/translate.js b/chrome/content/zotero/xpcom/translate.js @@ -977,6 +977,11 @@ Zotero.Translate.prototype._generateSandbox = function() { // for loading other translators and accessing their methods this._sandbox.Zotero.loadTranslator = function(type) { + if(typeof type !== "string") { + Zotero.debug("loadTranslator: type must be a string"); + return; + } + var translation = new Zotero.Translate(type); translation._parentTranslator = me; @@ -1089,6 +1094,14 @@ Zotero.Translate.prototype._setSandboxMode = function(mode) { * children */ Zotero.Translate.prototype._configure = function(option, value) { + if(typeof option !== "string") { + Zotero.debug ("configure: option must be a string"); + return; + } + if(typeof value === "object" && value !== null) { + value = new XPCSafeJSObjectWrapper(value); + } + this.configOptions[option] = value; Zotero.debug("Translate: Setting configure option "+option+" to "+value, 4); } @@ -1101,6 +1114,14 @@ Zotero.Translate.prototype._configure = function(option, value) { * current options are exportNotes and exportFileData */ Zotero.Translate.prototype._addOption = function(option, value) { + if(typeof option !== "string") { + Zotero.debug ("addOption: option must be a string"); + return; + } + if(typeof value === "object" && value !== null) { + value = new XPCSafeJSObjectWrapper(value); + } + this.displayOptions[option] = value; Zotero.debug("Translate: Setting display option "+option+" to "+value, 4); } @@ -1112,6 +1133,10 @@ Zotero.Translate.prototype._addOption = function(option, value) { * */ Zotero.Translate.prototype._getOption = function(option) { + if(typeof option !== "string") { + Zotero.debug ("getOption: option must be a string"); + return; + } return this.displayOptions[option]; } @@ -1124,13 +1149,26 @@ Zotero.Translate.prototype._getOption = function(option) { Zotero.Translate.prototype._enableAsynchronousDetect = function() { var me = this; this.waitForCompletion = true; - this._sandbox.Zotero.done = function(arg) { me._translatorSearch.complete(arg) }; + this._sandbox.Zotero.done = function(arg) { + if(arg !== null && typeof arg === "object") { + Zotero.debug("done: argument must not be an object"); + me._translatorSearch.complete(false); + } else { + me._translatorSearch.complete(arg); + } + }; } Zotero.Translate.prototype._enableAsynchronousTranslate = function() { var me = this; this.waitForCompletion = true; this._sandbox.Zotero.done = function(val, error) { + if(val !== null && typeof val === "object") { + val = new XPCSafeJSObjectWrapper(val); + } + if(error !== null && typeof error === "object") { + error = new XPCSafeJSObjectWrapper(error); + } me._translationComplete(val == undefined ? true : val, (error ? error : "")) }; } @@ -1141,6 +1179,10 @@ Zotero.Translate.prototype._enableAsynchronousTranslate = function() { * called as selectItems() in translator code */ Zotero.Translate.prototype._selectItems = function(options) { + if(!options instanceof XPCSafeJSObjectWrapper) { + options = new XPCSafeJSObjectWrapper(options); + } + // hack to see if there are options var haveOptions = false; for(var i in options) { @@ -1375,6 +1417,11 @@ Zotero.Translate.prototype._itemDone = function(item, attachedTo) { Zotero.debug("Translate: WARNING: Zotero.Item.complete() called after Zotero.done(); please fix your code", 2); } + // make item safe to access + if(!item instanceof XPCSafeJSObjectWrapper) { + item = new XPCSafeJSObjectWrapper(item); + } + if(this.type == "web") { // store library catalog if this item was captured from a website, and // libraryCatalog is truly undefined (not false or "") @@ -1874,6 +1921,12 @@ Zotero.Translate.prototype._processCollection = function(collection, parentID) { * logs a debugging message */ Zotero.Translate.prototype._debug = function(string, level) { + if(typeof string === "object") string = new XPCSafeJSObjectWrapper(string); + if(level !== undefined && typeof level !== "number") { + Zotero.debug("debug: level must be an integer"); + return; + } + // if handler does not return anything explicitly false, show debug // message in console if(this.runHandler("debug", string) !== false) { @@ -1889,6 +1942,10 @@ Zotero.Translate.prototype._web = function() { try { this._sandbox.doWeb(this.document, this.location); } catch(e) { + if(typeof e === "object" && e !== null) { + e = new XPCSafeJSObjectWrapper(e); + } + if(this._parentTranslator) { throw(e); } else { @@ -1907,6 +1964,10 @@ Zotero.Translate.prototype._search = function() { try { this._sandbox.doSearch(this.search); } catch(e) { + if(typeof e === "object" && e !== null) { + e = new XPCSafeJSObjectWrapper(e); + } + this._translationComplete(false, e); return false; } @@ -1960,6 +2021,10 @@ Zotero.Translate.prototype._importDoneSniffing = function(charset) { try { this._sandbox.doImport(); } catch(e) { + if(typeof e === "object" && e !== null) { + e = new XPCSafeJSObjectWrapper(e); + } + if(this._parentTranslator) { throw(e); } else { @@ -2099,6 +2164,10 @@ Zotero.Translate.prototype._importConfigureIO = function(charset) { // allow translator to set charset this._sandbox.Zotero.setCharacterSet = function(charset) { + if(typeof charset !== "string") { + throw "setCharacterSet: charset must be a string"; + } + // seek back to the beginning me._inputStream.QueryInterface(Components.interfaces.nsISeekableStream) .seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, bomLength); @@ -2109,7 +2178,7 @@ Zotero.Translate.prototype._importConfigureIO = function(charset) { intlStream.init(me._inputStream, charset, 65535, Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); } catch(e) { - throw "Text encoding not supported"; + throw "setCharacterSet: text encoding not supported"; } me._streams.push(intlStream); } @@ -2132,6 +2201,10 @@ Zotero.Translate.prototype._importConfigureIO = function(charset) { } } else { // block reading this._sandbox.Zotero.read = function(amount) { + if(typeof amount === "object") { + throw "read: amount must be an integer"; + } + if(intlStream) { // read from international stream, if one is available var amountRead = intlStream.readString(amount, str); @@ -2324,6 +2397,10 @@ Zotero.Translate.prototype._export = function() { try { this._sandbox.doExport(); } catch(e) { + if(typeof e === "object" && e !== null) { + e = new XPCSafeJSObjectWrapper(e); + } + this._translationComplete(false, e); return false; } @@ -2360,6 +2437,10 @@ Zotero.Translate.prototype._exportConfigureIO = function() { // allow setting of character sets this._sandbox.Zotero.setCharacterSet = function(charset) { + if(typeof charset !== "string") { + throw "setCharacterSet: charset must be a string"; + } + streamCharset = charset.toUpperCase(); intlStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"] .createInstance(Components.interfaces.nsIConverterOutputStream); @@ -2373,6 +2454,8 @@ Zotero.Translate.prototype._exportConfigureIO = function() { } this._sandbox.Zotero.write = function(data) { + if(typeof data === "object") data = new XPCSafeJSObjectWrapper(data); + if(streamCharset) { if(!writtenToStream && BOMs[streamCharset]) { // If stream has not yet been written to, and a UTF type @@ -2408,6 +2491,8 @@ Zotero.Translate.prototype._exportConfigureIO = function() { this._sandbox.Zotero.setCharacterSet = function() {}; // write to string this._sandbox.Zotero.write = function(data) { + if(typeof data === "object") data = new XPCSafeJSObjectWrapper(data); + me.output += data; }; } @@ -2757,6 +2842,10 @@ Zotero.Translate.TranslatorSearch.prototype.runDetectCode = function(translator) returnValue = this.translate._sandbox.detectImport(); } } catch(e) { + if(typeof e === "object" && e !== null) { + e = new XPCSafeJSObjectWrapper(e); + } + this.complete(returnValue, e); return; } @@ -3035,4 +3124,28 @@ Zotero.Translate.RDF.prototype.getStatementsMatching = function(subj, pred, obj, undefined, justOne); if(!statements.length) return false; return [[s.subject, s.predicate, (s.object.termType == "literal" ? s.object.toString() : s.object)] for each(s in statements)]; +} + +/* + * Wrap arguments to RDF functions in XPCSafeJSObjectWrappers + */ +for(var wrapFunctionBad in Zotero.Translate.RDF) { + let wrapFunction = wrapFunctionBad; + if(!(Zotero.Translate.RDF[wrapFunction] instanceof Function)) continue; + + let unwrappedFunction = Zotero.Translate.RDF[wrapFunction]; + + Zotero.Translate.RDF.prototype[wrapFunction] = function() { + let args = []; + for(let i=0; i<arguments.length; i++) { + if(typeof arguments[i] != "object" + && arguments[i] instanceof XPCSafeJSObjectWrapper + && arguments[i] !== null) { + args.push(new XPCSafeJSObjectWrapper(arguments[i])); + } else { + args.push(arguments[i]); + } + } + return unwrappedFunction.apply(this, args); + } } \ No newline at end of file diff --git a/chrome/content/zotero/xpcom/utilities.js b/chrome/content/zotero/xpcom/utilities.js @@ -716,20 +716,12 @@ Zotero.Utilities.Translate = function(translate) { this.translate = translate; } -Zotero.Utilities.Translate.prototype = new Zotero.Utilities(); -Zotero.Utilities.Translate.prototype.inArray = Zotero.inArray; -Zotero.Utilities.Translate.prototype.formatDate = Zotero.Date.formatDate; -Zotero.Utilities.Translate.prototype.strToDate = Zotero.Date.strToDate; -Zotero.Utilities.Translate.prototype.strToISO = Zotero.Date.strToISO; -Zotero.Utilities.Translate.prototype.createContextObject = Zotero.OpenURL.createContextObject; -Zotero.Utilities.Translate.prototype.parseContextObject = Zotero.OpenURL.parseContextObject; - /** * Gets the current Zotero version * * @type String */ -Zotero.Utilities.prototype.getVersion = function() { +Zotero.Utilities.Translate.prototype.getVersion = function() { return Zotero.version; } @@ -1025,6 +1017,41 @@ Zotero.Utilities.Translate.prototype._convertURL = function(url) { } /** + * Wrap all functions so that arguments are guaranteed safe + */ +borrowedFunctions = { + "inArray":Zotero.inArray, + "formatDate":Zotero.Date.formatDate, + "strToDate":Zotero.Date.strToDate, + "strToISO":Zotero.Date.strToISO, + "createContextObject":Zotero.OpenURL.createContextObject, + "parseContextObject":Zotero.OpenURL.parseContextObject +}; +for each(var wrapArrayBad in [borrowedFunctions, Zotero.Utilities.prototype, Zotero.Utilities.Translate.prototype]) { + let wrapArray = wrapArrayBad; + for(var wrapFunctionBad in wrapArray) { + let wrapFunction = wrapFunctionBad; + if(!(wrapArray[wrapFunction] instanceof Function)) continue; + + let unwrappedFunction = wrapArray[wrapFunction]; + + Zotero.Utilities.Translate.prototype[wrapFunction] = function() { + let args = []; + for(let i=0; i<arguments.length; i++) { + if(typeof arguments[i] != "object" + && arguments[i] instanceof XPCSafeJSObjectWrapper + && arguments[i] !== null) { + args.push(new XPCSafeJSObjectWrapper(arguments[i])); + } else { + args.push(arguments[i]); + } + } + return unwrappedFunction.apply(this, args); + } + } +} + +/** * Functions for performing HTTP requests, both via XMLHTTPRequest and using a hidden browser * @namespace */