diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/suite/mailnews/content/phishingDetector.js | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/suite/mailnews/content/phishingDetector.js')
-rw-r--r-- | comm/suite/mailnews/content/phishingDetector.js | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/comm/suite/mailnews/content/phishingDetector.js b/comm/suite/mailnews/content/phishingDetector.js new file mode 100644 index 0000000000..04d2910753 --- /dev/null +++ b/comm/suite/mailnews/content/phishingDetector.js @@ -0,0 +1,173 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Dependencies: +// gBrandBundle, gMessengerBundle should already be defined +// gatherTextUnder from utilityOverlay.js + +ChromeUtils.import("resource:///modules/hostnameUtils.jsm"); + +const kPhishingNotSuspicious = 0; +const kPhishingWithIPAddress = 1; +const kPhishingWithMismatchedHosts = 2; + +////////////////////////////////////////////////////////////////////////////// +// isEmailScam --> examines the message currently loaded in the message pane +// and returns true if we think that message is an e-mail scam. +// Assumes the message has been completely loaded in the message pane (i.e. OnMsgParsed has fired) +// aUrl: nsIURI object for the msg we want to examine... +////////////////////////////////////////////////////////////////////////////// +function isMsgEmailScam(aUrl) +{ + var isEmailScam = false; + if (!aUrl || !Services.prefs.getBoolPref("mail.phishing.detection.enabled")) + return isEmailScam; + + try { + // nsIMsgMailNewsUrl.folder can throw an NS_ERROR_FAILURE, especially if + // we are opening an .eml file. + var folder = aUrl.folder; + + // Ignore NNTP and RSS messages. + if (folder.server.type == 'nntp' || folder.server.type == 'rss') + return isEmailScam; + + // Also ignore messages in Sent/Drafts/Templates/Outbox. + let outgoingFlags = Ci.nsMsgFolderFlags.SentMail | + Ci.nsMsgFolderFlags.Drafts | + Ci.nsMsgFolderFlags.Templates | + Ci.nsMsgFolderFlags.Queue; + if (folder.isSpecialFolder(outgoingFlags, true)) + return isEmailScam; + + } catch (ex) { + if (ex.result != Cr.NS_ERROR_FAILURE) + throw ex; + } + + // loop through all of the link nodes in the message's DOM, looking for phishing URLs... + var msgDocument = document.getElementById('messagepane').contentDocument; + var index; + + // examine all links... + var linkNodes = msgDocument.links; + for (index = 0; index < linkNodes.length && !isEmailScam; index++) + isEmailScam = isPhishingURL(linkNodes[index], true); + + // if an e-mail contains a non-addressbook form element, then assume the message is + // a phishing attack. Legitimate sites should not be using forms inside of e-mail + if (!isEmailScam) + { + var forms = msgDocument.getElementsByTagName("form"); + for (index = 0; index < forms.length && !isEmailScam; index++) + isEmailScam = forms[index].action != "" && !/^addbook:/.test(forms[index].action); + } + + // we'll add more checks here as our detector matures.... + return isEmailScam; +} + +////////////////////////////////////////////////////////////////////////////// +// isPhishingURL --> examines the passed in linkNode and returns true if we think +// the URL is an email scam. +// aLinkNode: the link node to examine +// aSilentMode: don't prompt the user to confirm +// aHref: optional href for XLinks +////////////////////////////////////////////////////////////////////////////// + +function isPhishingURL(aLinkNode, aSilentMode, aHref) +{ + if (!Services.prefs.getBoolPref("mail.phishing.detection.enabled")) + return false; + + var phishingType = kPhishingNotSuspicious; + var aLinkText = gatherTextUnder(aLinkNode); + var href = aHref || aLinkNode.href; + if (!href) + return false; + + var linkTextURL = {}; + var isPhishingURL = false; + + var hrefURL; + // Make sure relative link urls don't make us bail out. + try { + hrefURL = Services.io.newURI(href); + } catch(ex) { return false; } + + // only check for phishing urls if the url is an http or https link. + // this prevents us from flagging imap and other internally handled urls + if (hrefURL.schemeIs('http') || hrefURL.schemeIs('https')) + { + + if (aLinkText) + aLinkText = aLinkText.replace(/^<(.+)>$|^"(.+)"$/, "$1$2"); + if (aLinkText != aLinkNode.href && + aLinkText.replace(/\/+$/, "") != aLinkNode.href.replace(/\/+$/, "")) + { + let ipAddress = isLegalIPAddress(hrefURL.host, true); + if (ipAddress && !isLegalLocalIPAddress(ipAddress)) + phishingType = kPhishingWithIPAddress; + else if (misMatchedHostWithLinkText(aLinkNode, hrefURL)) + phishingType = kPhishingWithMismatchedHosts; + + isPhishingURL = phishingType != kPhishingNotSuspicious; + + if (!aSilentMode && isPhishingURL) // allow the user to override the decision + isPhishingURL = confirmSuspiciousURL(phishingType, hrefURL.host); + } + } + + return isPhishingURL; +} + +////////////////////////////////////////////////////////////////////////////// +// helper methods in support of isPhishingURL +////////////////////////////////////////////////////////////////////////////// + +function misMatchedHostWithLinkText(aLinkNode, aHrefURL) +{ + var linkNodeText = gatherTextUnder(aLinkNode); + + // gatherTextUnder puts a space between each piece of text it gathers, + // so strip the spaces out (see bug 326082 for details). + linkNodeText = linkNodeText.replace(/ /g, ""); + + // only worry about http and https urls + if (linkNodeText) + { + // does the link text look like a http url? + if (linkNodeText.search(/(^http:|^https:)/) != -1) + { + var linkURI = Services.io.newURI(linkNodeText); + // compare hosts, but ignore possible www. prefix + return !(aHrefURL.host.replace(/^www\./, "") == linkURI.host.replace(/^www\./, "")); + } + } + + return false; +} + +// returns true if the user confirms the URL is a scam +function confirmSuspiciousURL(aPhishingType, aSuspiciousHostName) +{ + var brandShortName = gBrandBundle.getString("brandShortName"); + var titleMsg = gMessengerBundle.getString("confirmPhishingTitle"); + var dialogMsg; + + switch (aPhishingType) + { + case kPhishingWithIPAddress: + case kPhishingWithMismatchedHosts: + dialogMsg = gMessengerBundle.getFormattedString("confirmPhishingUrl" + aPhishingType, [brandShortName, aSuspiciousHostName], 2); + break; + default: + return false; + } + + var buttons = Services.prompt.STD_YES_NO_BUTTONS + + Services.prompt.BUTTON_POS_1_DEFAULT; + return Services.prompt.confirmEx(window, titleMsg, dialogMsg, buttons, "", "", "", "", {}); /* the yes button is in position 0 */ +}
\ No newline at end of file |