Loading browser/components/places/PlacesUIUtils.jsm +5 −1 Original line number Diff line number Diff line Loading @@ -1903,7 +1903,11 @@ XPCOMUtils.defineLazyGetter(PlacesUIUtils, "URI_FLAVORS", () => { return [PlacesUtils.TYPE_X_MOZ_URL, TAB_DROP_TYPE, PlacesUtils.TYPE_UNICODE]; }); XPCOMUtils.defineLazyGetter(PlacesUIUtils, "SUPPORTED_FLAVORS", () => { return [...PlacesUIUtils.PLACES_FLAVORS, ...PlacesUIUtils.URI_FLAVORS]; return [ ...PlacesUIUtils.PLACES_FLAVORS, ...PlacesUIUtils.URI_FLAVORS, "application/x-torbrowser-opaque", ]; }); XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() { Loading browser/components/places/content/controller.js +1 −0 Original line number Diff line number Diff line Loading @@ -1251,6 +1251,7 @@ PlacesController.prototype = { [ PlacesUtils.TYPE_X_MOZ_PLACE, PlacesUtils.TYPE_X_MOZ_URL, "application/x-torbrowser-opaque", PlacesUtils.TYPE_UNICODE, ].forEach(type => xferable.addDataFlavor(type)); Loading dom/base/ContentAreaDropListener.jsm +18 −2 Original line number Diff line number Diff line Loading @@ -5,6 +5,16 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); const { XPCOMUtils } = ChromeUtils.import( "resource://gre/modules/XPCOMUtils.jsm" ); XPCOMUtils.defineLazyGetter(this, "gOpaqueDrag", () => { return Cc["@torproject.org/torbutton-dragDropFilter;1"].getService( Ci.nsISupports ).wrappedJSObject.opaqueDrag; }); // This component is used for handling dragover and drop of urls. // // It checks to see whether a drop of a url is allowed. For instance, a url Loading Loading @@ -43,10 +53,15 @@ ContentAreaDropListener.prototype = { } } type = "text/x-moz-url"; if (types.contains(type)) { for (let type of ["text/x-moz-url", "application/x-torbrowser-opaque"]) { if (!types.contains(type)) { continue; } data = dt.mozGetDataAt(type, i); if (data) { if (type === "application/x-torbrowser-opaque") { ({ type, value: data = "" } = gOpaqueDrag.get(data)); } let lines = data.split("\n"); for (let i = 0, length = lines.length; i < length; i += 2) { this._addLink(links, lines[i], lines[i + 1], type); Loading Loading @@ -250,6 +265,7 @@ ContentAreaDropListener.prototype = { if ( !types.includes("application/x-moz-file") && !types.includes("text/x-moz-url") && !types.includes("application/x-torbrowser-opaque") && !types.includes("text/uri-list") && !types.includes("text/x-moz-text-internal") && !types.includes("text/plain") Loading toolkit/components/places/PlacesUtils.jsm +9 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,12 @@ XPCOMUtils.defineLazyGetter(this, "gCryptoHash", () => { return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash); }); XPCOMUtils.defineLazyGetter(this, "gOpaqueDrag", () => { return Cc["@torproject.org/torbutton-dragDropFilter;1"].getService( Ci.nsISupports ).wrappedJSObject.opaqueDrag; }); // On Mac OSX, the transferable system converts "\r\n" to "\n\n", where // we really just want "\n". On other platforms, the transferable system // converts "\r\n" to "\n". Loading Loading @@ -1132,6 +1138,9 @@ var PlacesUtils = { unwrapNodes: function PU_unwrapNodes(blob, type) { // We split on "\n" because the transferable system converts "\r\n" to "\n" var nodes = []; if (type === "application/x-torbrowser-opaque") { ({ value: blob, type } = gOpaqueDrag.get(blob)); } switch (type) { case this.TYPE_X_MOZ_PLACE: case this.TYPE_X_MOZ_PLACE_SEPARATOR: Loading toolkit/torbutton/components/dragDropFilter.js 0 → 100644 +155 −0 Original line number Diff line number Diff line /************************************************************************* * Drag and Drop Handler. * * Implements an observer that filters drag events to prevent OS * access to URLs (a potential proxy bypass vector). *************************************************************************/ const { XPCOMUtils } = ChromeUtils.import( "resource://gre/modules/XPCOMUtils.jsm" ); const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetters(this, { ComponentUtils: "resource://gre/modules/ComponentUtils.jsm", }); XPCOMUtils.defineLazyGlobalGetters(this, ["crypto"]); // Module specific constants const kMODULE_NAME = "Torbutton Drag and Drop Handler"; const kCONTRACT_ID = "@torproject.org/torbutton-dragDropFilter;1"; const kMODULE_CID = Components.ID("f605ec27-d867-44b5-ad97-2a29276642c3"); const kInterfaces = [Ci.nsIObserver, Ci.nsIClassInfo]; const URLISH_TYPES = Object.freeze([ "text/x-moz-url", "text/x-moz-url-data", "text/uri-list", "application/x-moz-file-promise-url", ]); const MAIN_PROCESS = Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT; const EMPTY_PAYLOAD = {}; const OpaqueDrag = { listening: false, payload: EMPTY_PAYLOAD, store(value, type) { let opaqueKey = crypto.randomUUID(); this.payload = { opaqueKey, value, type }; if (!this.listening && MAIN_PROCESS) { Services.ppmm.addMessageListener( "DragDropFilter:GetOpaqueDrag", () => this.payload ); this.listening = true; } return opaqueKey; }, retrieve(key) { let { opaqueKey, value, type } = this.payload; if (opaqueKey === key) { return { value, type }; } if (!MAIN_PROCESS) { this.payload = Services.cpmm.sendSyncMessage( "DragDropFilter:GetOpaqueDrag" )[0]; if (key === this.payload.opaqueKey) { return this.retrieve(key); } } return EMPTY_PAYLOAD; }, }; function DragDropFilter() { this.logger = Cc["@torproject.org/torbutton-logger;1"].getService( Ci.nsISupports ).wrappedJSObject; this.logger.log(3, "Component Load 0: New DragDropFilter."); if (MAIN_PROCESS) { // We want to update our status in the main process only, in order to // serve the same opaque drag payload in every process. try { Services.obs.addObserver(this, "on-datatransfer-available"); } catch (e) { this.logger.log(5, "Failed to register drag observer"); } } } DragDropFilter.prototype = { QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]), // make this an nsIClassInfo object flags: Ci.nsIClassInfo.DOM_OBJECT, classDescription: kMODULE_NAME, contractID: kCONTRACT_ID, classID: kMODULE_CID, // method of nsIClassInfo getInterfaces(count) { count.value = kInterfaces.length; return kInterfaces; }, // method of nsIClassInfo getHelperForLanguage(count) { return null; }, // method of nsIObserver observe(subject, topic, data) { if (topic === "on-datatransfer-available") { this.logger.log(3, "The DataTransfer is available"); this.filterDataTransferURLs(subject); } }, filterDataTransferURLs(aDataTransfer) { for (let i = 0, count = aDataTransfer.mozItemCount; i < count; ++i) { this.logger.log(3, `Inspecting the data transfer: ${i}.`); const types = aDataTransfer.mozTypesAt(i); for (const type of types) { this.logger.log(3, `Type is: ${type}.`); if (URLISH_TYPES.includes(type)) { this.logger.log( 3, `Removing transfer data ${aDataTransfer.mozGetDataAt(type, i)}` ); const urlType = "text/x-moz-url"; // Fallback url type, to be parsed by this browser but not externally const INTERNAL_FALLBACK = "application/x-torbrowser-opaque"; if (types.contains(urlType)) { const link = aDataTransfer.mozGetDataAt(urlType, i); const opaqueKey = OpaqueDrag.store(link, urlType); aDataTransfer.mozSetDataAt(INTERNAL_FALLBACK, opaqueKey, i); } for (const type of types) { if ( type !== INTERNAL_FALLBACK && type !== "text/x-moz-place" // don't touch bookmarks ) { aDataTransfer.mozClearDataAt(type, i); } } break; } } } }, opaqueDrag: { get(opaqueKey) { return OpaqueDrag.retrieve(opaqueKey); }, }, }; // Assign factory to global object. const NSGetFactory = XPCOMUtils.generateNSGetFactory ? XPCOMUtils.generateNSGetFactory([DragDropFilter]) : ComponentUtils.generateNSGetFactory([DragDropFilter]); Loading
browser/components/places/PlacesUIUtils.jsm +5 −1 Original line number Diff line number Diff line Loading @@ -1903,7 +1903,11 @@ XPCOMUtils.defineLazyGetter(PlacesUIUtils, "URI_FLAVORS", () => { return [PlacesUtils.TYPE_X_MOZ_URL, TAB_DROP_TYPE, PlacesUtils.TYPE_UNICODE]; }); XPCOMUtils.defineLazyGetter(PlacesUIUtils, "SUPPORTED_FLAVORS", () => { return [...PlacesUIUtils.PLACES_FLAVORS, ...PlacesUIUtils.URI_FLAVORS]; return [ ...PlacesUIUtils.PLACES_FLAVORS, ...PlacesUIUtils.URI_FLAVORS, "application/x-torbrowser-opaque", ]; }); XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() { Loading
browser/components/places/content/controller.js +1 −0 Original line number Diff line number Diff line Loading @@ -1251,6 +1251,7 @@ PlacesController.prototype = { [ PlacesUtils.TYPE_X_MOZ_PLACE, PlacesUtils.TYPE_X_MOZ_URL, "application/x-torbrowser-opaque", PlacesUtils.TYPE_UNICODE, ].forEach(type => xferable.addDataFlavor(type)); Loading
dom/base/ContentAreaDropListener.jsm +18 −2 Original line number Diff line number Diff line Loading @@ -5,6 +5,16 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); const { XPCOMUtils } = ChromeUtils.import( "resource://gre/modules/XPCOMUtils.jsm" ); XPCOMUtils.defineLazyGetter(this, "gOpaqueDrag", () => { return Cc["@torproject.org/torbutton-dragDropFilter;1"].getService( Ci.nsISupports ).wrappedJSObject.opaqueDrag; }); // This component is used for handling dragover and drop of urls. // // It checks to see whether a drop of a url is allowed. For instance, a url Loading Loading @@ -43,10 +53,15 @@ ContentAreaDropListener.prototype = { } } type = "text/x-moz-url"; if (types.contains(type)) { for (let type of ["text/x-moz-url", "application/x-torbrowser-opaque"]) { if (!types.contains(type)) { continue; } data = dt.mozGetDataAt(type, i); if (data) { if (type === "application/x-torbrowser-opaque") { ({ type, value: data = "" } = gOpaqueDrag.get(data)); } let lines = data.split("\n"); for (let i = 0, length = lines.length; i < length; i += 2) { this._addLink(links, lines[i], lines[i + 1], type); Loading Loading @@ -250,6 +265,7 @@ ContentAreaDropListener.prototype = { if ( !types.includes("application/x-moz-file") && !types.includes("text/x-moz-url") && !types.includes("application/x-torbrowser-opaque") && !types.includes("text/uri-list") && !types.includes("text/x-moz-text-internal") && !types.includes("text/plain") Loading
toolkit/components/places/PlacesUtils.jsm +9 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,12 @@ XPCOMUtils.defineLazyGetter(this, "gCryptoHash", () => { return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash); }); XPCOMUtils.defineLazyGetter(this, "gOpaqueDrag", () => { return Cc["@torproject.org/torbutton-dragDropFilter;1"].getService( Ci.nsISupports ).wrappedJSObject.opaqueDrag; }); // On Mac OSX, the transferable system converts "\r\n" to "\n\n", where // we really just want "\n". On other platforms, the transferable system // converts "\r\n" to "\n". Loading Loading @@ -1132,6 +1138,9 @@ var PlacesUtils = { unwrapNodes: function PU_unwrapNodes(blob, type) { // We split on "\n" because the transferable system converts "\r\n" to "\n" var nodes = []; if (type === "application/x-torbrowser-opaque") { ({ value: blob, type } = gOpaqueDrag.get(blob)); } switch (type) { case this.TYPE_X_MOZ_PLACE: case this.TYPE_X_MOZ_PLACE_SEPARATOR: Loading
toolkit/torbutton/components/dragDropFilter.js 0 → 100644 +155 −0 Original line number Diff line number Diff line /************************************************************************* * Drag and Drop Handler. * * Implements an observer that filters drag events to prevent OS * access to URLs (a potential proxy bypass vector). *************************************************************************/ const { XPCOMUtils } = ChromeUtils.import( "resource://gre/modules/XPCOMUtils.jsm" ); const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetters(this, { ComponentUtils: "resource://gre/modules/ComponentUtils.jsm", }); XPCOMUtils.defineLazyGlobalGetters(this, ["crypto"]); // Module specific constants const kMODULE_NAME = "Torbutton Drag and Drop Handler"; const kCONTRACT_ID = "@torproject.org/torbutton-dragDropFilter;1"; const kMODULE_CID = Components.ID("f605ec27-d867-44b5-ad97-2a29276642c3"); const kInterfaces = [Ci.nsIObserver, Ci.nsIClassInfo]; const URLISH_TYPES = Object.freeze([ "text/x-moz-url", "text/x-moz-url-data", "text/uri-list", "application/x-moz-file-promise-url", ]); const MAIN_PROCESS = Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT; const EMPTY_PAYLOAD = {}; const OpaqueDrag = { listening: false, payload: EMPTY_PAYLOAD, store(value, type) { let opaqueKey = crypto.randomUUID(); this.payload = { opaqueKey, value, type }; if (!this.listening && MAIN_PROCESS) { Services.ppmm.addMessageListener( "DragDropFilter:GetOpaqueDrag", () => this.payload ); this.listening = true; } return opaqueKey; }, retrieve(key) { let { opaqueKey, value, type } = this.payload; if (opaqueKey === key) { return { value, type }; } if (!MAIN_PROCESS) { this.payload = Services.cpmm.sendSyncMessage( "DragDropFilter:GetOpaqueDrag" )[0]; if (key === this.payload.opaqueKey) { return this.retrieve(key); } } return EMPTY_PAYLOAD; }, }; function DragDropFilter() { this.logger = Cc["@torproject.org/torbutton-logger;1"].getService( Ci.nsISupports ).wrappedJSObject; this.logger.log(3, "Component Load 0: New DragDropFilter."); if (MAIN_PROCESS) { // We want to update our status in the main process only, in order to // serve the same opaque drag payload in every process. try { Services.obs.addObserver(this, "on-datatransfer-available"); } catch (e) { this.logger.log(5, "Failed to register drag observer"); } } } DragDropFilter.prototype = { QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]), // make this an nsIClassInfo object flags: Ci.nsIClassInfo.DOM_OBJECT, classDescription: kMODULE_NAME, contractID: kCONTRACT_ID, classID: kMODULE_CID, // method of nsIClassInfo getInterfaces(count) { count.value = kInterfaces.length; return kInterfaces; }, // method of nsIClassInfo getHelperForLanguage(count) { return null; }, // method of nsIObserver observe(subject, topic, data) { if (topic === "on-datatransfer-available") { this.logger.log(3, "The DataTransfer is available"); this.filterDataTransferURLs(subject); } }, filterDataTransferURLs(aDataTransfer) { for (let i = 0, count = aDataTransfer.mozItemCount; i < count; ++i) { this.logger.log(3, `Inspecting the data transfer: ${i}.`); const types = aDataTransfer.mozTypesAt(i); for (const type of types) { this.logger.log(3, `Type is: ${type}.`); if (URLISH_TYPES.includes(type)) { this.logger.log( 3, `Removing transfer data ${aDataTransfer.mozGetDataAt(type, i)}` ); const urlType = "text/x-moz-url"; // Fallback url type, to be parsed by this browser but not externally const INTERNAL_FALLBACK = "application/x-torbrowser-opaque"; if (types.contains(urlType)) { const link = aDataTransfer.mozGetDataAt(urlType, i); const opaqueKey = OpaqueDrag.store(link, urlType); aDataTransfer.mozSetDataAt(INTERNAL_FALLBACK, opaqueKey, i); } for (const type of types) { if ( type !== INTERNAL_FALLBACK && type !== "text/x-moz-place" // don't touch bookmarks ) { aDataTransfer.mozClearDataAt(type, i); } } break; } } } }, opaqueDrag: { get(opaqueKey) { return OpaqueDrag.retrieve(opaqueKey); }, }, }; // Assign factory to global object. const NSGetFactory = XPCOMUtils.generateNSGetFactory ? XPCOMUtils.generateNSGetFactory([DragDropFilter]) : ComponentUtils.generateNSGetFactory([DragDropFilter]);