Tweak sandboxing for Firefox 35
Now we have to wrap cross-origin objects with a wrapper on the sandbox side. Also, Function.prototype.apply.apply...
This commit is contained in:
parent
fa75beeefd
commit
da7ee2ba26
|
@ -242,6 +242,9 @@ Zotero.Translate.Sandbox = {
|
|||
};
|
||||
safeTranslator.setDocument = function(arg) {
|
||||
if (Zotero.isFx && !Zotero.isBookmarklet) {
|
||||
if (arg.wrappedJSObject && arg.wrappedJSObject.__wrappedObject) {
|
||||
arg = arg.wrappedJSObject.__wrappedObject;
|
||||
}
|
||||
return translation.setDocument(new XPCNativeWrapper(arg));
|
||||
} else {
|
||||
return translation.setDocument(arg);
|
||||
|
@ -388,7 +391,7 @@ Zotero.Translate.Sandbox = {
|
|||
if (typeof(safeTranslator[i]) === "function") {
|
||||
safeTranslator[i] = translate._sandboxManager._makeContentForwarder(function(func) {
|
||||
return function() {
|
||||
func.apply(safeTranslator, this.args.wrappedJSObject);
|
||||
func.apply(safeTranslator, this.args.wrappedJSObject || this.args);
|
||||
}
|
||||
}(safeTranslator[i]));
|
||||
}
|
||||
|
@ -1169,7 +1172,7 @@ Zotero.Translate.Base.prototype = {
|
|||
|
||||
// translate
|
||||
try {
|
||||
this._sandboxManager.sandbox["do"+this._entryFunctionSuffix].apply(null, this._getParameters());
|
||||
Function.prototype.apply.apply(this._sandboxManager.sandbox["do"+this._entryFunctionSuffix], [null, this._getParameters()]);
|
||||
} catch(e) {
|
||||
this.complete(false, e);
|
||||
return false;
|
||||
|
@ -1442,7 +1445,7 @@ Zotero.Translate.Base.prototype = {
|
|||
this.incrementAsyncProcesses("Zotero.Translate#getTranslators");
|
||||
|
||||
try {
|
||||
var returnValue = this._sandboxManager.sandbox["detect"+this._entryFunctionSuffix].apply(null, this._getParameters());
|
||||
var returnValue = Function.prototype.apply.apply(this._sandboxManager.sandbox["detect"+this._entryFunctionSuffix], [null, this._getParameters()]);
|
||||
} catch(e) {
|
||||
this.complete(false, e);
|
||||
return;
|
||||
|
@ -1987,13 +1990,13 @@ Zotero.Translate.Import.prototype._loadTranslatorPrepareIO = function(translator
|
|||
if(!this._io) {
|
||||
if(Zotero.Translate.IO.Read && this.location && this.location instanceof Components.interfaces.nsIFile) {
|
||||
try {
|
||||
this._io = new Zotero.Translate.IO.Read(this.location);
|
||||
this._io = new Zotero.Translate.IO.Read(this.location, this._sandboxManager);
|
||||
} catch(e) {
|
||||
err = e;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
this._io = new Zotero.Translate.IO.String(this._string, this.path ? this.path : "");
|
||||
this._io = new Zotero.Translate.IO.String(this._string, this.path ? this.path : "", this._sandboxManager);
|
||||
} catch(e) {
|
||||
err = e;
|
||||
}
|
||||
|
@ -2156,7 +2159,7 @@ Zotero.Translate.Export.prototype._prepareTranslation = function() {
|
|||
// this is currently hackish since we pass null callbacks to the init function (they have
|
||||
// callbacks to be consistent with import, but they are synchronous, so we ignore them)
|
||||
if(!this.location) {
|
||||
this._io = new Zotero.Translate.IO.String(null, this.path ? this.path : "");
|
||||
this._io = new Zotero.Translate.IO.String(null, this.path ? this.path : "", this._sandboxManager);
|
||||
this._io.init(configOptions["dataMode"], function() {});
|
||||
} else if(!Zotero.Translate.IO.Write) {
|
||||
throw new Error("Writing to files is not supported in this build of Zotero.");
|
||||
|
@ -2344,7 +2347,7 @@ Zotero.Translate.IO = {
|
|||
/**
|
||||
* @class Translate backend for translating from a string
|
||||
*/
|
||||
Zotero.Translate.IO.String = function(string, uri) {
|
||||
Zotero.Translate.IO.String = function(string, uri, sandboxManager) {
|
||||
if(string && typeof string === "string") {
|
||||
this.string = string;
|
||||
} else {
|
||||
|
@ -2353,6 +2356,7 @@ Zotero.Translate.IO.String = function(string, uri) {
|
|||
this.contentLength = this.string.length;
|
||||
this.bytesRead = 0;
|
||||
this._uri = uri;
|
||||
this._sandboxManager = sandboxManager;
|
||||
}
|
||||
|
||||
Zotero.Translate.IO.String.prototype = {
|
||||
|
@ -2442,7 +2446,7 @@ Zotero.Translate.IO.String.prototype = {
|
|||
this._xmlInvalid = true;
|
||||
throw e;
|
||||
}
|
||||
return (Zotero.isFx ? Zotero.Translate.DOMWrapper.wrap(xml) : xml);
|
||||
return (Zotero.isFx && !Zotero.isBookmarklet ? this._sandboxManager.wrap(xml) : xml);
|
||||
},
|
||||
|
||||
"init":function(newMode, callback) {
|
||||
|
|
|
@ -49,7 +49,7 @@ Zotero.Translate.DOMWrapper = new function() {
|
|||
};
|
||||
|
||||
function isWrapper(x) {
|
||||
return isWrappable(x) && (typeof x.SpecialPowers_wrappedObject !== "undefined");
|
||||
return isWrappable(x) && (typeof x.__wrappedObject !== "undefined");
|
||||
};
|
||||
|
||||
function unwrapIfWrapped(x) {
|
||||
|
@ -130,7 +130,7 @@ Zotero.Translate.DOMWrapper = new function() {
|
|||
throw "Trying to unwrap a non-wrapped object!";
|
||||
|
||||
// Unwrap.
|
||||
return x.SpecialPowers_wrappedObject;
|
||||
return x.__wrappedObject;
|
||||
};
|
||||
|
||||
function crawlProtoChain(obj, fn) {
|
||||
|
@ -172,7 +172,7 @@ Zotero.Translate.DOMWrapper = new function() {
|
|||
SpecialPowersHandler.prototype.doGetPropertyDescriptor = function(name, own) {
|
||||
|
||||
// Handle our special API.
|
||||
if (name == "SpecialPowers_wrappedObject")
|
||||
if (name == "__wrappedObject")
|
||||
return { value: this.wrappedObject, writeable: false, configurable: false, enumerable: false };
|
||||
// Handle __exposedProps__.
|
||||
if (name == "__exposedProps__")
|
||||
|
@ -281,7 +281,7 @@ Zotero.Translate.DOMWrapper = new function() {
|
|||
|
||||
// Insert our special API. It's not enumerable, but getPropertyNames()
|
||||
// includes non-enumerable properties.
|
||||
var specialAPI = 'SpecialPowers_wrappedObject';
|
||||
var specialAPI = '__wrappedObject';
|
||||
if (props.indexOf(specialAPI) == -1)
|
||||
props.push(specialAPI);
|
||||
|
||||
|
@ -366,6 +366,14 @@ Zotero.Translate.DOMWrapper = new function() {
|
|||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an object in the same sandbox as another object
|
||||
*/
|
||||
this.wrapIn = function(obj, insamebox) {
|
||||
if(insamebox.__wrappingManager) return insamebox.__wrappingManager.wrap(obj);
|
||||
return this.wrap(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an object is wrapped by a DOM wrapper
|
||||
|
@ -381,7 +389,8 @@ Zotero.Translate.DOMWrapper = new function() {
|
|||
* @param {String|window} sandboxLocation
|
||||
*/
|
||||
Zotero.Translate.SandboxManager = function(sandboxLocation) {
|
||||
this.sandbox = new Components.utils.Sandbox(sandboxLocation);
|
||||
// sandboxLocation = Components.classes["@mozilla.org/systemprincipal;1"].createInstance(Components.interfaces.nsIPrincipal);
|
||||
var sandbox = this.sandbox = new Components.utils.Sandbox(sandboxLocation, {wantComponents:false, wantGlobalProperties:["XMLHttpRequest"]});
|
||||
this.sandbox.Zotero = {};
|
||||
|
||||
// import functions missing from global scope into Fx sandbox
|
||||
|
@ -389,28 +398,14 @@ Zotero.Translate.SandboxManager = function(sandboxLocation) {
|
|||
if(typeof sandboxLocation === "object" && "DOMParser" in sandboxLocation) {
|
||||
this.sandbox.DOMParser = sandboxLocation.DOMParser;
|
||||
} else {
|
||||
var sandbox = this.sandbox;
|
||||
this.sandbox.DOMParser = function() {
|
||||
var uri, principal;
|
||||
// get URI
|
||||
if(typeof sandboxLocation === "string") { // if sandbox specified by URI
|
||||
var secMan = Services.scriptSecurityManager;
|
||||
uri = Services.io.newURI(sandboxLocation, "UTF-8", null);
|
||||
principal = (secMan.getCodebasePrincipal || secMan.getSimpleCodebasePrincipal)(uri);
|
||||
} else { // if sandbox specified by DOM document
|
||||
principal = sandboxLocation.document.nodePrincipal;
|
||||
uri = sandboxLocation.document.documentURIObject;
|
||||
}
|
||||
|
||||
// initialize DOM parser
|
||||
var _DOMParser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
|
||||
.createInstance(Components.interfaces.nsIDOMParser);
|
||||
_DOMParser.init(principal, uri, uri);
|
||||
|
||||
// expose parseFromString
|
||||
this.__exposedProps__ = {"parseFromString":"r"};
|
||||
this.parseFromString = function(str, contentType) {
|
||||
return Zotero.Translate.DOMWrapper.wrap(_DOMParser.parseFromString(str, contentType));
|
||||
var xhr = sandbox.XMLHttpRequest();
|
||||
xhr.open("GET", "data:"+contentType+";base64,"+btoa(str), false);
|
||||
xhr.send();
|
||||
if (!xhr.responseXML) throw new Error("error parsing XML");
|
||||
return xhr.responseXML;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -427,7 +422,55 @@ Zotero.Translate.SandboxManager = function(sandboxLocation) {
|
|||
this.sandbox.XMLSerializer.prototype = {"__exposedProps__":{"serializeToString":"r"}};
|
||||
|
||||
var expr = "(function(x) { return function() { this.args = arguments; return x.apply(this); }.bind({}); })";
|
||||
this._makeContentForwarder = Components.utils.evalInSandbox(expr, this.sandbox);
|
||||
this._makeContentForwarder = Components.utils.evalInSandbox(expr, sandbox);
|
||||
|
||||
if (Zotero.platformMajorVersion >= 35) {
|
||||
var _proxy = Components.utils.evalInSandbox('(function (target, x) {'+
|
||||
' return new Proxy(x, ProxyHandler(target));'+
|
||||
'})', sandbox);
|
||||
var wrap = this.wrap = function(target, x) {
|
||||
if (target === null || (typeof target !== "object" && typeof target !== "function")) return target;
|
||||
if (!x) x = new sandbox.Object();
|
||||
return _proxy(target, x);
|
||||
};
|
||||
var me = this;
|
||||
sandbox.ProxyHandler = this._makeContentForwarder(function() {
|
||||
var target = (this.args.wrappedJSObject || this.args)[0];
|
||||
if(target instanceof Components.interfaces.nsISupports) {
|
||||
target = new XPCNativeWrapper(target);
|
||||
}
|
||||
var ret = new sandbox.Object();
|
||||
ret.wrappedJSObject.has = function(x, prop) {
|
||||
return prop in target;
|
||||
};
|
||||
ret.wrappedJSObject.get = function(x, prop, receiver) {
|
||||
if (prop === "__wrappedObject") return target;
|
||||
if (prop === "__wrappingManager") return me;
|
||||
var y = target[prop];
|
||||
if (y === null || (typeof y !== "object" && typeof y !== "function")) return y;
|
||||
return wrap(y, typeof y === "function" ? function() {
|
||||
var args = Array.prototype.slice.apply(arguments);
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
if (typeof args[i] === "object" && args[i] !== null &&
|
||||
args[i].wrappedJSObject && args[i].wrappedJSObject.__wrappedObject)
|
||||
args[i] = new XPCNativeWrapper(args[i].wrappedJSObject.__wrappedObject);
|
||||
}
|
||||
return wrap(y.apply(target, args));
|
||||
} : new sandbox.Object());
|
||||
};
|
||||
ret.wrappedJSObject.ownKeys = function(x) {
|
||||
return Components.utils.cloneInto(target.getOwnPropertyNames(), sandbox);
|
||||
};
|
||||
ret.wrappedJSObject.enumerate = function(x) {
|
||||
var y = new sandbox.Array();
|
||||
for (var i in target) y.wrappedJSObject.push(i);
|
||||
return y;
|
||||
};
|
||||
return ret;
|
||||
});
|
||||
} else {
|
||||
this.wrap = Zotero.Translate.DOMWrapper.wrap;
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.Translate.SandboxManager.prototype = {
|
||||
|
@ -463,7 +506,7 @@ Zotero.Translate.SandboxManager.prototype = {
|
|||
if(isFunction) {
|
||||
if (Zotero.platformMajorVersion >= 33) {
|
||||
attachTo[localKey] = this._makeContentForwarder(function() {
|
||||
var args = Array.prototype.slice.apply(this.args.wrappedJSObject);
|
||||
var args = Array.prototype.slice.apply(this.args.wrappedJSObject || this.args);
|
||||
for(var i = 0; i<args.length; i++) {
|
||||
// Make sure we keep XPCNativeWrappers
|
||||
if(args[i] instanceof Components.interfaces.nsISupports) {
|
||||
|
@ -503,7 +546,7 @@ Zotero.Translate.SandboxManager.prototype = {
|
|||
"_canCopy":function(obj) {
|
||||
if(typeof obj !== "object" || obj === null) return false;
|
||||
if((obj.constructor.name !== "Object" && obj.constructor.name !== "Array") ||
|
||||
"__exposedProps__" in obj) {
|
||||
"__exposedProps__" in obj || (obj.wrappedJSObject && obj.wrappedJSObject.__wrappingManager)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -582,6 +625,9 @@ Zotero.Translate.ChildSandboxManager.prototype = {
|
|||
},
|
||||
"_makeContentForwarder":function(f) {
|
||||
return this._parent._makeContentForwarder(f);
|
||||
},
|
||||
"wrap":function(x) {
|
||||
return this._parent.wrap(x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -594,10 +640,11 @@ Zotero.Translate.IO.maintainedInstances = [];
|
|||
|
||||
/******* (Native) Read support *******/
|
||||
|
||||
Zotero.Translate.IO.Read = function(file, mode) {
|
||||
Zotero.Translate.IO.Read = function(file, sandboxManager) {
|
||||
Zotero.Translate.IO.maintainedInstances.push(this);
|
||||
|
||||
this.file = file;
|
||||
this._sandboxManager = sandboxManager;
|
||||
|
||||
// open file
|
||||
this._openRawStream();
|
||||
|
@ -862,7 +909,7 @@ Zotero.Translate.IO.Read.prototype = {
|
|||
this._xmlInvalid = true;
|
||||
throw e;
|
||||
}
|
||||
return (Zotero.isFx ? Zotero.Translate.DOMWrapper.wrap(xml) : xml);
|
||||
return (Zotero.isFx ? this._sandboxManager.wrap(xml) : xml);
|
||||
},
|
||||
|
||||
"init":function(newMode, callback) {
|
||||
|
|
|
@ -229,7 +229,7 @@ Zotero.Utilities = {
|
|||
*/
|
||||
"trimInternal":function(/**String*/ s) {
|
||||
if (typeof(s) != "string") {
|
||||
throw "trimInternal: argument must be a string";
|
||||
throw new Error("trimInternal: argument must be a string");
|
||||
}
|
||||
|
||||
s = s.replace(/[\xA0\r\n\s]+/g, " ");
|
||||
|
@ -1046,11 +1046,11 @@ Zotero.Utilities = {
|
|||
// For some reason, if elements is wrapped by an object
|
||||
// Xray, we won't be able to unwrap the DOMWrapper around
|
||||
// the element. So waive the object Xray.
|
||||
var element = elements.wrappedJSObject ? elements.wrappedJSObject[i] : elements[i];
|
||||
var maybeWrappedEl = elements.wrappedJSObject ? elements.wrappedJSObject[i] : elements[i];
|
||||
|
||||
// Firefox 5 hack, so we will preserve Fx5DOMWrappers
|
||||
var isWrapped = Zotero.Translate.DOMWrapper && Zotero.Translate.DOMWrapper.isWrapped(element);
|
||||
if(isWrapped) element = Zotero.Translate.DOMWrapper.unwrap(element);
|
||||
var isWrapped = Zotero.Translate.DOMWrapper && Zotero.Translate.DOMWrapper.isWrapped(maybeWrappedEl);
|
||||
var element = isWrapped ? Zotero.Translate.DOMWrapper.unwrap(maybeWrappedEl) : maybeWrappedEl;
|
||||
|
||||
// We waived the object Xray above, which will waive the
|
||||
// DOM Xray, so make sure we have a DOM Xray wrapper.
|
||||
|
@ -1083,7 +1083,7 @@ Zotero.Utilities = {
|
|||
var newEl;
|
||||
while(newEl = xpathObject.iterateNext()) {
|
||||
// Firefox 5 hack
|
||||
results.push(isWrapped ? Zotero.Translate.DOMWrapper.wrap(newEl) : newEl);
|
||||
results.push(isWrapped ? Zotero.Translate.DOMWrapper.wrapIn(newEl, maybeWrappedEl) : newEl);
|
||||
}
|
||||
} else if("selectNodes" in element) {
|
||||
// We use JavaScript-XPath in IE for HTML documents, but with an XML
|
||||
|
@ -1125,6 +1125,8 @@ Zotero.Utilities = {
|
|||
var strings = new Array(elements.length);
|
||||
for(var i=0, n=elements.length; i<n; i++) {
|
||||
var el = elements[i];
|
||||
if(el.wrappedJSObject) el = el.wrappedJSObject;
|
||||
if(Zotero.Translate.DOMWrapper) el = Zotero.Translate.DOMWrapper.unwrap(el);
|
||||
strings[i] =
|
||||
(el.nodeType === 2 /*ATTRIBUTE_NODE*/ && "value" in el) ? el.value
|
||||
: "textContent" in el ? el.textContent
|
||||
|
|
|
@ -268,9 +268,9 @@ Zotero.Utilities.Translate.prototype.processDocuments = function(urls, processor
|
|||
if(!processor) return;
|
||||
|
||||
var newLoc = doc.location;
|
||||
if(Zotero.isFx && (protocol != newLoc.protocol || host != newLoc.host)) {
|
||||
if(Zotero.isFx && !Zotero.isBookmarklet && (protocol != newLoc.protocol || host != newLoc.host)) {
|
||||
// Cross-site; need to wrap
|
||||
processor(Zotero.Translate.DOMWrapper.wrap(doc), newLoc.toString());
|
||||
processor(translate._sandboxManager.wrap(doc), newLoc.toString());
|
||||
} else {
|
||||
// Not cross-site; no need to wrap
|
||||
processor(doc, newLoc.toString());
|
||||
|
|
Loading…
Reference in New Issue
Block a user