Translation updates
[torbutton.git] / components / dragDropFilter.js
blobf763595be5e6f1450e5a0d7d23dbaea62725bec1
1 /*************************************************************************
2 * Drag and Drop Handler.
4 * Implements an observer that filters drag events to prevent OS
5 * access to URLs (a potential proxy bypass vector).
6 *************************************************************************/
8 const { XPCOMUtils } = ChromeUtils.import(
9 "resource://gre/modules/XPCOMUtils.jsm"
11 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
13 XPCOMUtils.defineLazyModuleGetters(this, {
14 ComponentUtils: "resource://gre/modules/ComponentUtils.jsm",
15 });
17 // Module specific constants
18 const kMODULE_NAME = "Torbutton Drag and Drop Handler";
19 const kCONTRACT_ID = "@torproject.org/torbutton-dragDropFilter;1";
20 const kMODULE_CID = Components.ID("f605ec27-d867-44b5-ad97-2a29276642c3");
22 const kInterfaces = [Ci.nsIObserver, Ci.nsIClassInfo];
24 const URLISH_TYPES = Object.freeze([
25 "text/x-moz-url",
26 "text/x-moz-url-data",
27 "text/uri-list",
28 "application/x-moz-file-promise-url",
29 ]);
32 Returns true if the text resembles a URL or even just a hostname
33 in a way that may prompt the O.S. or other applications to send out a
34 validation DNS query, if not a full request (e.g. " torproject.org",
35 even with the leading whitespace).
37 function isURLish(text) {
38 // Ignore leading whitespace.
39 text = text.trim();
41 // Without any protocol or dot in the first chunk, this is unlikely
42 // to be considered URLish (exception: localhost, but we don't care).
43 if (!/^[a-z][a-z0-9+-]*:\/\//i.test(text)) {
44 // no protocol
45 if (!/^[^.\s\/]+\.[^.\s\/]/.test(text)) {
46 // no dot
47 return false;
49 // Prepare for hostname validation via relative URL building.
50 text = `//${text}`;
52 // Validate URL or hostname.
53 try {
54 new URL(text, "https://localhost");
55 return true;
56 } catch (e) {
57 // invalid URL, bail out
59 return false;
62 // Returns true if any chunk of text is URLish
63 const hasURLish = text => text.split(/[^\p{L}_.-:\/%~@$-]+/u).some(isURLish);
65 function DragDropFilter() {
66 this.logger = Cc["@torproject.org/torbutton-logger;1"].getService(
67 Ci.nsISupports
68 ).wrappedJSObject;
69 this.logger.log(3, "Component Load 0: New DragDropFilter.");
71 try {
72 Services.obs.addObserver(this, "on-datatransfer-available");
73 } catch (e) {
74 this.logger.log(5, "Failed to register drag observer");
78 DragDropFilter.prototype = {
79 QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
81 // make this an nsIClassInfo object
82 flags: Ci.nsIClassInfo.DOM_OBJECT,
83 classDescription: kMODULE_NAME,
84 contractID: kCONTRACT_ID,
85 classID: kMODULE_CID,
87 // method of nsIClassInfo
88 getInterfaces(count) {
89 count.value = kInterfaces.length;
90 return kInterfaces;
93 // method of nsIClassInfo
94 getHelperForLanguage(count) {
95 return null;
98 // method of nsIObserver
99 observe(subject, topic, data) {
100 if (topic === "on-datatransfer-available") {
101 this.logger.log(3, "The DataTransfer is available");
102 this.filterDataTransferURLs(subject);
106 filterDataTransferURLs(aDataTransfer) {
107 for (let i = 0, count = aDataTransfer.mozItemCount; i < count; ++i) {
108 this.logger.log(3, `Inspecting the data transfer: ${i}.`);
109 const types = aDataTransfer.mozTypesAt(i);
110 for (const type of types) {
111 this.logger.log(3, `Type is: ${type}.`);
112 if (
113 URLISH_TYPES.includes(type) ||
114 ((type === "text/plain" || type === "text/html") &&
115 hasURLish(aDataTransfer.getData(type)))
117 this.logger.log(
119 `Removing transfer data ${aDataTransfer.getData(type)}`
121 for (const type of types) {
122 aDataTransfer.clearData(type);
124 break;
131 // Assign factory to global object.
132 const NSGetFactory = XPCOMUtils.generateNSGetFactory
133 ? XPCOMUtils.generateNSGetFactory([DragDropFilter])
134 : ComponentUtils.generateNSGetFactory([DragDropFilter]);