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
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);
}
}

View file

@ -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;
}
@ -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
* @namespace