diff --git a/chrome/content/zotero/xpcom/server.js b/chrome/content/zotero/xpcom/server.js index 74c9d68a32..ea09ab5777 100755 --- a/chrome/content/zotero/xpcom/server.js +++ b/chrome/content/zotero/xpcom/server.js @@ -216,7 +216,19 @@ Zotero.Server.DataListener.prototype._headerFinished = function() { Zotero.debug(this.header, 5); const methodRe = /^([A-Z]+) ([^ \r\n?]+)(\?[^ \r\n]+)?/; - const contentTypeRe = /[\r\n]Content-Type: +([^ \r\n]+)/i; + const contentTypeRe = /[\r\n]Content-Type: *([^ \r\n]+)/i; + + if(!Zotero.isServer) { + const originRe = /[\r\n]Origin: *([^ \r\n]+)/i; + var m = originRe.exec(this.header); + if(m) { + this.origin = m[1]; + } else { + const bookmarkletRe = /[\r\n]X-Zotero-Bookmarklet: *([^ \r\n]+)/i; + var m = bookmarkletRe.exec(this.header); + if(m) this.origin = "https://www.zotero.org"; + } + } // get first line of request var method = methodRe.exec(this.header); @@ -294,11 +306,8 @@ Zotero.Server.DataListener.prototype._generateResponse = function(status, conten if(!Zotero.isServer) { response += "X-Zotero-Version: "+Zotero.version+"\r\n"; response += "X-Zotero-Connector-API-Version: "+CONNECTOR_API_VERSION+"\r\n"; - - const originRe = /[\r\n]Origin: +([^ \r\n]+)/i; - var m = originRe.exec(this.header); - if(m && (m[1] === "https://www.zotero.org" || m[1] === "http://www.zotero.org")) { - response += "Access-Control-Allow-Origin: "+m[1]+"\r\n"; + if(this.origin === "https://www.zotero.org" || this.origin === "http://www.zotero.org") { + response += "Access-Control-Allow-Origin: "+this.origin+"\r\n"; response += "Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n"; response += "Access-Control-Allow-Headers: Content-Type,X-Zotero-Connector-API-Version,X-Zotero-Version\r\n"; } @@ -324,12 +333,24 @@ Zotero.Server.DataListener.prototype._processEndpoint = function(method, postDat try { var endpoint = new this.endpoint; - // check that endpoint supports method + // Check that endpoint supports method if(endpoint.supportedMethods && endpoint.supportedMethods.indexOf(method) === -1) { this._requestFinished(this._generateResponse(400, "text/plain", "Endpoint does not support method\n")); return; } + // Check that endpoint supports bookmarklet + if(this.origin) { + var isBookmarklet = this.origin === "https://www.zotero.org" || this.origin === "http://www.zotero.org"; + // Disallow bookmarklet origins to access endpoints without permitBookmarklet + // set. We allow other origins to access these endpoints because they have to + // be privileged to avoid being blocked by our headers. + if(isBookmarklet && !endpoint.permitBookmarklet) { + this._requestFinished(this._generateResponse(403, "text/plain", "Access forbidden to bookmarklet\n")); + return; + } + } + var decodedData = null; if(postData && this.contentType) { // check that endpoint supports contentType diff --git a/chrome/content/zotero/xpcom/server_connector.js b/chrome/content/zotero/xpcom/server_connector.js index 139f6ebbf7..8af9801ebe 100755 --- a/chrome/content/zotero/xpcom/server_connector.js +++ b/chrome/content/zotero/xpcom/server_connector.js @@ -70,6 +70,7 @@ Zotero.Server.Endpoints["/connector/getTranslators"] = Zotero.Server.Connector.G Zotero.Server.Connector.GetTranslators.prototype = { "supportedMethods":["POST"], "supportedDataTypes":["application/json"], + "permitBookmarklet":true, /** * Gets available translator list and other important data @@ -121,6 +122,7 @@ Zotero.Server.Connector.Data = {}; Zotero.Server.Connector.Detect.prototype = { "supportedMethods":["POST"], "supportedDataTypes":["application/json"], + "permitBookmarklet":true, /** * Loads HTML into a hidden browser and initiates translator detection @@ -209,6 +211,7 @@ Zotero.Server.Endpoints["/connector/savePage"] = Zotero.Server.Connector.SavePag Zotero.Server.Connector.SavePage.prototype = { "supportedMethods":["POST"], "supportedDataTypes":["application/json"], + "permitBookmarklet":true, /** * Either loads HTML into a hidden browser and initiates translation, or saves items directly @@ -311,6 +314,7 @@ Zotero.Server.Endpoints["/connector/saveItems"] = Zotero.Server.Connector.SaveIt Zotero.Server.Connector.SaveItem.prototype = { "supportedMethods":["POST"], "supportedDataTypes":["application/json"], + "permitBookmarklet":true, /** * Either loads HTML into a hidden browser and initiates translation, or saves items directly @@ -372,6 +376,7 @@ Zotero.Server.Endpoints["/connector/saveSnapshot"] = Zotero.Server.Connector.Sav Zotero.Server.Connector.SaveSnapshot.prototype = { "supportedMethods":["POST"], "supportedDataTypes":["application/json"], + "permitBookmarklet":true, /** * Save snapshot @@ -454,6 +459,7 @@ Zotero.Server.Endpoints["/connector/selectItems"] = Zotero.Server.Connector.Sele Zotero.Server.Connector.SelectItems.prototype = { "supportedMethods":["POST"], "supportedDataTypes":["application/json"], + "permitBookmarklet":true, /** * Finishes up translation when item selection is complete @@ -487,6 +493,7 @@ Zotero.Server.Endpoints["/connector/attachmentProgress"] = Zotero.Server.Connect Zotero.Server.Connector.Progress.prototype = { "supportedMethods":["POST"], "supportedDataTypes":["application/json"], + "permitBookmarklet":true, /** * @param {String} data POST data or GET query string @@ -511,6 +518,7 @@ Zotero.Server.Endpoints["/connector/getTranslatorCode"] = Zotero.Server.Connecto Zotero.Server.Connector.GetTranslatorCode.prototype = { "supportedMethods":["POST"], "supportedDataTypes":["application/json"], + "permitBookmarklet":true, /** * Returns a 200 response to say the server is alive @@ -539,6 +547,7 @@ Zotero.Server.Endpoints["/connector/getSelectedCollection"] = Zotero.Server.Conn Zotero.Server.Connector.GetSelectedCollection.prototype = { "supportedMethods":["POST"], "supportedDataTypes":["application/json"], + "permitBookmarklet":true, /** * Returns a 200 response to say the server is alive @@ -594,6 +603,7 @@ Zotero.Server.Endpoints["/connector/ping"] = Zotero.Server.Connector.Ping; Zotero.Server.Connector.Ping.prototype = { "supportedMethods":["POST"], "supportedDataTypes":["application/json", "text/plain"], + "permitBookmarklet":true, /** * Sends nothing @@ -617,6 +627,7 @@ Zotero.Server.Connector.IEHack = function() {}; Zotero.Server.Endpoints["/connector/ieHack"] = Zotero.Server.Connector.IEHack; Zotero.Server.Connector.IEHack.prototype = { "supportedMethods":["GET"], + "permitBookmarklet":true, /** * Sends a fixed webpage @@ -642,6 +653,7 @@ Zotero.Server.Endpoints["/translate/select"] = Zotero.Server.Connector.Incompati Zotero.Server.Connector.IncompatibleVersion.prototype = { "supportedMethods":["POST"], "supportedDataTypes":["application/json"], + "permitBookmarklet":true, "init":function(postData, sendResponseCallback) { sendResponseCallback(404);