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.
This commit is contained in:
Simon Kornblith 2010-08-02 02:26:46 +00:00
parent 4037193f92
commit 021cbc8725
2 changed files with 151 additions and 11 deletions

View file

@ -977,6 +977,11 @@ Zotero.Translate.prototype._generateSandbox = function() {
// for loading other translators and accessing their methods // for loading other translators and accessing their methods
this._sandbox.Zotero.loadTranslator = function(type) { 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); var translation = new Zotero.Translate(type);
translation._parentTranslator = me; translation._parentTranslator = me;
@ -1089,6 +1094,14 @@ Zotero.Translate.prototype._setSandboxMode = function(mode) {
* children * children
*/ */
Zotero.Translate.prototype._configure = function(option, value) { 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; this.configOptions[option] = value;
Zotero.debug("Translate: Setting configure option "+option+" to "+value, 4); 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 * current options are exportNotes and exportFileData
*/ */
Zotero.Translate.prototype._addOption = function(option, value) { 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; this.displayOptions[option] = value;
Zotero.debug("Translate: Setting display option "+option+" to "+value, 4); 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) { Zotero.Translate.prototype._getOption = function(option) {
if(typeof option !== "string") {
Zotero.debug ("getOption: option must be a string");
return;
}
return this.displayOptions[option]; return this.displayOptions[option];
} }
@ -1124,13 +1149,26 @@ Zotero.Translate.prototype._getOption = function(option) {
Zotero.Translate.prototype._enableAsynchronousDetect = function() { Zotero.Translate.prototype._enableAsynchronousDetect = function() {
var me = this; var me = this;
this.waitForCompletion = true; 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() { Zotero.Translate.prototype._enableAsynchronousTranslate = function() {
var me = this; var me = this;
this.waitForCompletion = true; this.waitForCompletion = true;
this._sandbox.Zotero.done = function(val, error) { 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 : "")) me._translationComplete(val == undefined ? true : val, (error ? error : ""))
}; };
} }
@ -1141,6 +1179,10 @@ Zotero.Translate.prototype._enableAsynchronousTranslate = function() {
* called as selectItems() in translator code * called as selectItems() in translator code
*/ */
Zotero.Translate.prototype._selectItems = function(options) { Zotero.Translate.prototype._selectItems = function(options) {
if(!options instanceof XPCSafeJSObjectWrapper) {
options = new XPCSafeJSObjectWrapper(options);
}
// hack to see if there are options // hack to see if there are options
var haveOptions = false; var haveOptions = false;
for(var i in options) { 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); 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") { if(this.type == "web") {
// store library catalog if this item was captured from a website, and // store library catalog if this item was captured from a website, and
// libraryCatalog is truly undefined (not false or "") // libraryCatalog is truly undefined (not false or "")
@ -1874,6 +1921,12 @@ Zotero.Translate.prototype._processCollection = function(collection, parentID) {
* logs a debugging message * logs a debugging message
*/ */
Zotero.Translate.prototype._debug = function(string, level) { 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 // if handler does not return anything explicitly false, show debug
// message in console // message in console
if(this.runHandler("debug", string) !== false) { if(this.runHandler("debug", string) !== false) {
@ -1889,6 +1942,10 @@ Zotero.Translate.prototype._web = function() {
try { try {
this._sandbox.doWeb(this.document, this.location); this._sandbox.doWeb(this.document, this.location);
} catch(e) { } catch(e) {
if(typeof e === "object" && e !== null) {
e = new XPCSafeJSObjectWrapper(e);
}
if(this._parentTranslator) { if(this._parentTranslator) {
throw(e); throw(e);
} else { } else {
@ -1907,6 +1964,10 @@ Zotero.Translate.prototype._search = function() {
try { try {
this._sandbox.doSearch(this.search); this._sandbox.doSearch(this.search);
} catch(e) { } catch(e) {
if(typeof e === "object" && e !== null) {
e = new XPCSafeJSObjectWrapper(e);
}
this._translationComplete(false, e); this._translationComplete(false, e);
return false; return false;
} }
@ -1960,6 +2021,10 @@ Zotero.Translate.prototype._importDoneSniffing = function(charset) {
try { try {
this._sandbox.doImport(); this._sandbox.doImport();
} catch(e) { } catch(e) {
if(typeof e === "object" && e !== null) {
e = new XPCSafeJSObjectWrapper(e);
}
if(this._parentTranslator) { if(this._parentTranslator) {
throw(e); throw(e);
} else { } else {
@ -2099,6 +2164,10 @@ Zotero.Translate.prototype._importConfigureIO = function(charset) {
// allow translator to set charset // allow translator to set charset
this._sandbox.Zotero.setCharacterSet = function(charset) { this._sandbox.Zotero.setCharacterSet = function(charset) {
if(typeof charset !== "string") {
throw "setCharacterSet: charset must be a string";
}
// seek back to the beginning // seek back to the beginning
me._inputStream.QueryInterface(Components.interfaces.nsISeekableStream) me._inputStream.QueryInterface(Components.interfaces.nsISeekableStream)
.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, bomLength); .seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, bomLength);
@ -2109,7 +2178,7 @@ Zotero.Translate.prototype._importConfigureIO = function(charset) {
intlStream.init(me._inputStream, charset, 65535, intlStream.init(me._inputStream, charset, 65535,
Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
} catch(e) { } catch(e) {
throw "Text encoding not supported"; throw "setCharacterSet: text encoding not supported";
} }
me._streams.push(intlStream); me._streams.push(intlStream);
} }
@ -2132,6 +2201,10 @@ Zotero.Translate.prototype._importConfigureIO = function(charset) {
} }
} else { // block reading } else { // block reading
this._sandbox.Zotero.read = function(amount) { this._sandbox.Zotero.read = function(amount) {
if(typeof amount === "object") {
throw "read: amount must be an integer";
}
if(intlStream) { if(intlStream) {
// read from international stream, if one is available // read from international stream, if one is available
var amountRead = intlStream.readString(amount, str); var amountRead = intlStream.readString(amount, str);
@ -2324,6 +2397,10 @@ Zotero.Translate.prototype._export = function() {
try { try {
this._sandbox.doExport(); this._sandbox.doExport();
} catch(e) { } catch(e) {
if(typeof e === "object" && e !== null) {
e = new XPCSafeJSObjectWrapper(e);
}
this._translationComplete(false, e); this._translationComplete(false, e);
return false; return false;
} }
@ -2360,6 +2437,10 @@ Zotero.Translate.prototype._exportConfigureIO = function() {
// allow setting of character sets // allow setting of character sets
this._sandbox.Zotero.setCharacterSet = function(charset) { this._sandbox.Zotero.setCharacterSet = function(charset) {
if(typeof charset !== "string") {
throw "setCharacterSet: charset must be a string";
}
streamCharset = charset.toUpperCase(); streamCharset = charset.toUpperCase();
intlStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"] intlStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
.createInstance(Components.interfaces.nsIConverterOutputStream); .createInstance(Components.interfaces.nsIConverterOutputStream);
@ -2373,6 +2454,8 @@ Zotero.Translate.prototype._exportConfigureIO = function() {
} }
this._sandbox.Zotero.write = function(data) { this._sandbox.Zotero.write = function(data) {
if(typeof data === "object") data = new XPCSafeJSObjectWrapper(data);
if(streamCharset) { if(streamCharset) {
if(!writtenToStream && BOMs[streamCharset]) { if(!writtenToStream && BOMs[streamCharset]) {
// If stream has not yet been written to, and a UTF type // 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() {}; this._sandbox.Zotero.setCharacterSet = function() {};
// write to string // write to string
this._sandbox.Zotero.write = function(data) { this._sandbox.Zotero.write = function(data) {
if(typeof data === "object") data = new XPCSafeJSObjectWrapper(data);
me.output += data; me.output += data;
}; };
} }
@ -2757,6 +2842,10 @@ Zotero.Translate.TranslatorSearch.prototype.runDetectCode = function(translator)
returnValue = this.translate._sandbox.detectImport(); returnValue = this.translate._sandbox.detectImport();
} }
} catch(e) { } catch(e) {
if(typeof e === "object" && e !== null) {
e = new XPCSafeJSObjectWrapper(e);
}
this.complete(returnValue, e); this.complete(returnValue, e);
return; return;
} }
@ -3035,4 +3124,28 @@ Zotero.Translate.RDF.prototype.getStatementsMatching = function(subj, pred, obj,
undefined, justOne); undefined, justOne);
if(!statements.length) return false; if(!statements.length) return false;
return [[s.subject, s.predicate, (s.object.termType == "literal" ? s.object.toString() : s.object)] for each(s in statements)]; 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);
}
} }

View file

@ -716,20 +716,12 @@ Zotero.Utilities.Translate = function(translate) {
this.translate = 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 * Gets the current Zotero version
* *
* @type String * @type String
*/ */
Zotero.Utilities.prototype.getVersion = function() { Zotero.Utilities.Translate.prototype.getVersion = function() {
return Zotero.version; return Zotero.version;
} }
@ -1024,6 +1016,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 * Functions for performing HTTP requests, both via XMLHTTPRequest and using a hidden browser
* @namespace * @namespace