CryptoSafetyParent.sys.mjs (3755B)
1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ 2 /* Copyright (c) 2020, The Tor Project, Inc. 3 * 4 * This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 7 8 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; 9 10 const lazy = {}; 11 12 ChromeUtils.defineESModuleGetters(lazy, { 13 TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs", 14 Bech32Decode: "resource://gre/modules/Bech32Decode.sys.mjs", 15 }); 16 17 ChromeUtils.defineLazyGetter(lazy, "CryptoStrings", function () { 18 return new Localization(["toolkit/global/tor-browser.ftl"]); 19 }); 20 21 XPCOMUtils.defineLazyPreferenceGetter( 22 lazy, 23 "isCryptoSafetyEnabled", 24 "security.cryptoSafety", 25 true // Defaults to true. 26 ); 27 28 function looksLikeCryptoAddress(s) { 29 // P2PKH and P2SH addresses 30 // https://stackoverflow.com/a/24205650 31 const bitcoinAddr = /^[13][a-km-zA-HJ-NP-Z1-9]{25,39}$/; 32 if (bitcoinAddr.test(s)) { 33 return true; 34 } 35 36 // Bech32 addresses 37 if (lazy.Bech32Decode(s) !== null) { 38 return true; 39 } 40 41 // regular addresses 42 const etherAddr = /^0x[a-fA-F0-9]{40}$/; 43 if (etherAddr.test(s)) { 44 return true; 45 } 46 47 // t-addresses 48 // https://www.reddit.com/r/zec/comments/8mxj6x/simple_regex_to_validate_a_zcash_tz_address/dzr62p5/ 49 const zcashAddr = /^t1[a-zA-Z0-9]{33}$/; 50 if (zcashAddr.test(s)) { 51 return true; 52 } 53 54 // Standard, Integrated, and 256-bit Integrated addresses 55 // https://monero.stackexchange.com/a/10627 56 const moneroAddr = 57 /^4(?:[0-9AB]|[1-9A-HJ-NP-Za-km-z]{12}(?:[1-9A-HJ-NP-Za-km-z]{30})?)[1-9A-HJ-NP-Za-km-z]{93}$/; 58 if (moneroAddr.test(s)) { 59 return true; 60 } 61 62 return false; 63 } 64 65 export class CryptoSafetyParent extends JSWindowActorParent { 66 async receiveMessage(aMessage) { 67 if ( 68 !lazy.isCryptoSafetyEnabled || 69 aMessage.name !== "CryptoSafety:CopiedText" 70 ) { 71 return; 72 } 73 74 // Read the global clipboard. We assume the contents come from the HTTP 75 // page specified in `aMessage.data.host`. 76 const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance( 77 Ci.nsITransferable 78 ); 79 trans.init(null); 80 trans.addDataFlavor("text/plain"); 81 Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard); 82 let data = {}; 83 trans.getTransferData("text/plain", data); 84 data = data?.value.QueryInterface(Ci.nsISupportsString).data; 85 86 let address = data?.replace(/\s+/g, ""); 87 88 if (!address || !looksLikeCryptoAddress(address)) { 89 return; 90 } 91 92 if (address.length > 32) { 93 address = `${address.substring(0, 32)}…`; 94 } 95 96 const [titleText, bodyText, reloadText, dismissText] = 97 await lazy.CryptoStrings.formatValues([ 98 { id: "crypto-safety-prompt-title" }, 99 { 100 id: "crypto-safety-prompt-body", 101 args: { address, host: aMessage.data.host }, 102 }, 103 { id: "crypto-safety-prompt-reload-button" }, 104 { id: "crypto-safety-prompt-dismiss-button" }, 105 ]); 106 107 const buttonPressed = Services.prompt.confirmEx( 108 this.browsingContext.topChromeWindow, 109 titleText, 110 bodyText, 111 Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 + 112 Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_1, 113 reloadText, 114 dismissText, 115 null, 116 null, 117 {} 118 ); 119 120 if (buttonPressed === 0) { 121 const { browsingContext } = this.manager; 122 const browser = browsingContext.embedderElement; 123 if (browser) { 124 lazy.TorDomainIsolator.newCircuitForBrowser( 125 browser.ownerGlobal.gBrowser.selectedBrowser 126 ); 127 } 128 } 129 } 130 }