From 3a0295e096adada7c33f924a7d952e3c079498a4 Mon Sep 17 00:00:00 2001 From: Jonathan Gregson Date: Mon, 19 Sep 2022 01:00:30 -0700 Subject: [PATCH] Major update. - Some tests now replace 1337 characters for better matching - Added an analyzer tab to test if a message would be blocked by Message Guard - Replaced regex lookups with 'contains'. - Misc changes and fixes. --- src/Message Guard.xcodeproj/project.pbxproj | 2 + .../UserInterfaceState.xcuserstate | Bin 18405 -> 22253 bytes src/Message Guard/ContentView.swift | 159 ++++- .../MessageFilterExtension.swift | 600 ++++++++++-------- 4 files changed, 484 insertions(+), 277 deletions(-) diff --git a/src/Message Guard.xcodeproj/project.pbxproj b/src/Message Guard.xcodeproj/project.pbxproj index 883054d..94d6fe6 100644 --- a/src/Message Guard.xcodeproj/project.pbxproj +++ b/src/Message Guard.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ AB6F664727B67941001DBA61 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF78D3A27B1FB270002B523 /* ContentView.swift */; }; + ABA0B77128D2FE620072E902 /* MessageFilterExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF78D6827B1FBD70002B523 /* MessageFilterExtension.swift */; }; ABF78D3927B1FB270002B523 /* Message_GuardApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF78D3827B1FB270002B523 /* Message_GuardApp.swift */; }; ABF78D3D27B1FB2D0002B523 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ABF78D3C27B1FB2D0002B523 /* Assets.xcassets */; }; ABF78D4027B1FB2D0002B523 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ABF78D3F27B1FB2D0002B523 /* Preview Assets.xcassets */; }; @@ -333,6 +334,7 @@ files = ( AB6F664727B67941001DBA61 /* ContentView.swift in Sources */, ABF78D3927B1FB270002B523 /* Message_GuardApp.swift in Sources */, + ABA0B77128D2FE620072E902 /* MessageFilterExtension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/Message Guard.xcodeproj/project.xcworkspace/xcuserdata/jonathan.xcuserdatad/UserInterfaceState.xcuserstate b/src/Message Guard.xcodeproj/project.xcworkspace/xcuserdata/jonathan.xcuserdatad/UserInterfaceState.xcuserstate index 7da0840b7afa76515bef00691a72c313278100ef..9743ca32f6e4401f9b22bf3ea2ccb8929d7f46b4 100644 GIT binary patch delta 12538 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 Sr8Wja|B-50H$;hwML@u>ON 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: View { + let test = getLeetVariations(initialString: "tttt") let content: Content init(@ViewBuilder content: () -> Content) { @@ -63,22 +69,22 @@ struct AboutView: View { } VStack(alignment: .leading) { Label("Messages sent from email addresses (Email to Text)", systemImage: "envelope") - .padding(.bottom, 2) + .padding(.bottom, 2.0) Label("Messages containing URLs with risky TLDs, such as .ru", systemImage: "link") - .padding(.bottom, 2) + .padding(.bottom, 2.0) Label("Messages containing IP addresses", systemImage: "number") - .padding(.bottom, 2) - Label("Messages from full phone numbers (as opposed to SMS shortcodes) which mention popular banks", systemImage: "building.columns") - .padding(.bottom, 2) - Label("Messages containing common phishing ohrases", systemImage: "exclamationmark.bubble") - .padding(.bottom, 2) + .padding(.bottom, 2.0) + Label("Messages containing popular bank names which were not sent using an SMS shortcode", systemImage: "building.columns") + .padding(.bottom, 2.0) + Label("Messages containing common phishing phrases", systemImage: "exclamationmark.bubble") + .padding(.bottom, 2.0) } .padding(.all) } VStack { GroupBox(label: Label("IMPORTANT", systemImage: "exclamationmark.triangle")) { Text("Message Guard cannot filter messages from senders that are in your contacts or senders that you have replied to, as iOS does not send messages from known senders to message filters.") - + } } VStack { @@ -110,34 +116,34 @@ struct SetupView: View { Text("To enable Message Guard:") Spacer() } - .padding(.bottom, 1) + .padding(.bottom, 1.0) VStack(alignment: .leading) { Label("Open Settings", systemImage: "1.circle.fill") - .padding(.bottom, 1) + .padding(.bottom, 1.0) Label("Tap \"Messages\"", systemImage: "2.circle.fill") - .padding(.bottom, 1) + .padding(.bottom, 1.0) Label("Tap \"Unknown & Spam\"", systemImage: "3.circle.fill") - .padding(.bottom, 1) + .padding(.bottom, 1.0) Label("Enable \"Filter Unknown Senders\"", systemImage: "4.circle.fill") - .padding(.bottom, 1) + .padding(.bottom, 1.0) Label("Under \"SMS FILTERING,\" select \"Message Guard\"", systemImage: "5.circle.fill") } } - .padding(.top, 2) + .padding(.top, 2.0) } GroupBox(label: Label("Filter a Sender", systemImage: "person.fill.xmark")) { VStack(alignment: .leading) { - Text("To filter messages from a sender, make sure the sender is not in your contacts, delete any messages from and to the sender, and do not reply to messages from the sender. Replying to a sender tells Apple that you are interested in hearing from the sender and iOS will stop filtering messages from the sender through Message Guard.") - .padding(.bottom, 1) + Text("To filter messages from a sender, make sure the sender is not in your contacts, delete any messages from and to the sender, and do not reply to messages from the sender. Replying to a sender tells Apple that you are interested in receiving messages from the sender and iOS will stop filtering messages from the sender through Message Guard.") + .padding(.bottom, 1.0) Text("If you want to stop receiving all messages from a given sender, blocking the sender would be a safer approach.") } - .padding(.top, 2) + .padding(.top, 2.0) } GroupBox(label: Label("Stop Filtering a Sender", systemImage: "person.fill.checkmark")) { VStack(alignment: .leading) { Text("To prevent Message Guard from filtering any messages from a sender, you can add the sender to your contacts, or reply to a message from them two or more times. This will cause iOS to stop filtering messages from the sender through Message Guard.") } - .padding(.top, 2) + .padding(.top, 2.0) } } .padding(.all) @@ -146,6 +152,123 @@ struct SetupView: View { } } +struct AnalyzeView: View { + @State private var messageSender = "" + @State private var messageBody = "" + @State var resultColor = Color(wordName: "red") + @State var resultImage = "" + @State var resultText = "" + @State var resultDetail = "" + + var body: some View { + let messageSenderBinding = Binding(get: { + self.messageSender + }, set: { + self.messageSender = $0 + resultImage = "" + resultText = "" + resultDetail = "" + }) + + let messageBodyBinding = Binding(get: { + self.messageBody + }, set: { + self.messageBody = $0 + resultImage = "" + resultText = "" + resultDetail = "" + }) + + TitleView { + ScrollView { + VStack(alignment: .leading) { + VStack { + VStack { + Text("You can use the fields below to check if a message would be blocked by Message Guard and why it would be blocked.") + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.bottom, 1.0) + Text("Some Message Guard filters consider both the sender and the message body, so be sure to include both for a more accurate analysis.") + .frame(maxWidth: .infinity, alignment: .leading) + } + .padding(.bottom, 15.0) + VStack { + TextField("Sender", text: messageSenderBinding) + .padding(.leading, 4.0) + .frame(width: .infinity, height: 30.0) + .foregroundColor(.gray) + .overlay( + RoundedRectangle(cornerRadius: 7.0) + .stroke(.primary, lineWidth: 1.0) + ) + TextEditor(text: messageBodyBinding) + .frame(width: .infinity, height: 200.0) + .foregroundColor(.gray) + .overlay( + RoundedRectangle(cornerRadius: 7.0) + .stroke(.primary, lineWidth: 1.0) + ) + .padding(.bottom) + Button("Analyze") + { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil) + let results = analyzeMessage(messageBody: messageBody, messageSender: messageSender) + if (results.count > 0) { + resultColor = Color(wordName: "red") + resultImage = "x.circle.fill" + resultText = "This message is blocked by Message Guard." + resultDetail = "The message is blocked due to the following:\n • " + + results.joined(separator: "\n • ") + } else { + resultColor = Color(wordName: "green") + resultImage = "checkmark.circle.fill" + resultText = "This message is not blocked by Message Guard." + resultDetail = "" + } + } + .padding(.all) + .background(Color(hex: 0x14b7f1)) + .foregroundColor(.white) + .cornerRadius(7.0) + HStack { + Image(systemName: resultImage).foregroundColor(resultColor) + Text(resultText) + .frame(maxWidth: .infinity, alignment: .leading) + } + .padding(.top, 15.0) + Text(resultDetail) + .multilineTextAlignment(.leading) + .padding(.top, 5.0) + } + } + } + .padding(.all) + } + .onTapGesture { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil) + } + } + } +} + +extension Color { + init(hex: Int, opacity: Double = 1.0) { + let red = Double((hex & 0xff0000) >> 16) / 255.0 + let green = Double((hex & 0xff00) >> 8) / 255.0 + let blue = Double((hex & 0xff) >> 0) / 255.0 + self.init(.sRGB, red: red, green: green, blue: blue, opacity: opacity) + } +} + +extension Color { + init?(wordName: String) { + switch wordName { + case "red": self = .red + case "green": self = .green + default: return nil + } + } +} + struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() diff --git a/src/MessageFilterExtension/MessageFilterExtension.swift b/src/MessageFilterExtension/MessageFilterExtension.swift index 722256a..d90e271 100644 --- a/src/MessageFilterExtension/MessageFilterExtension.swift +++ b/src/MessageFilterExtension/MessageFilterExtension.swift @@ -41,122 +41,160 @@ extension MessageFilterExtension: ILMessageFilterQueryHandling { return .none } - let sortedSender = String(messageSender.lowercased().sorted()) - let ipRegex = #"\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b"# - - // Filter all messages from email addresses - if (messageSender.lowercased().contains("@")) { + let result = analyzeMessage(messageBody: messageBody, messageSender: messageSender, returnFirstMatch: true) + if (result.count > 0) { return .junk + } else { + return .none } - - // Filte all messages containing URLs using risky TLDs - if (hasAbuseiveTld(messageBody: messageBody)) { - return .junk + } +} + +func analyzeMessage(messageBody: String, messageSender: String, returnFirstMatch: Bool = false) -> [String] { + let sortedSender = String(messageSender.lowercased().sorted()) + let senderNumbers = sortedSender.filter("0123456789".contains) + let ipRegex = #"\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b"# + var allMatches: [String] = [] + + // Filter messages sent from email addresses + if (messageSender.lowercased().contains("@")) { + let reason = "Sender appears to be an email address." + if returnFirstMatch { + return [reason] } - - // Filter all messages containing IPv4 addresses - if (messageBody.range(of: ipRegex, options: .regularExpression) != nil) { - return .junk + if !allMatches.contains(reason) { + allMatches.append(reason) } - - // Filter messages from 7+ digit phone numbers which mention banks - if (sortedSender.range(of: #"\d{7,}"#, options: .regularExpression) != nil) { - if (hasBankName(messageBody: messageBody)) { - return .junk - } + } + + // Filter messages containing risky/abusive TLDs + if (hasAbuseiveTld(messageBody: messageBody)) { + let reason = "Message contains an abusive TLD." + if returnFirstMatch { + return [reason] } - - // Filter messages from 7+ digit phone numbers which contain common phishing phrases - if (sortedSender.range(of: #"\d{7,}"#, options: .regularExpression) != nil) { - if (hasPhishingPhrase(messageBody: messageBody)) { - return .junk + if !allMatches.contains(reason) { + allMatches.append(reason) + } + } + + // Filter messages containing IPv4 addresses + if (messageBody.range(of: ipRegex, options: .regularExpression) != nil) { + let reason = "Message contains an IPv4 address." + if returnFirstMatch { + return [reason] + } + if !allMatches.contains(reason) { + allMatches.append(reason) + } + } + + // Filter messages which mention banks and are not sent from SMS shortcodes + if (senderNumbers.range(of: #"^\d{4,6}$"#, options: .regularExpression) == nil) { + if (hasBankName(messageBody: messageBody)) { + let reason = "Message appears to contain a bank name and is not from an SMS shortcode." + if returnFirstMatch { + return [reason] + } + if !allMatches.contains(reason) { + allMatches.append(reason) } } - - // Default action - return .none; } + // Filter messages which contain common phishing phrases + if (hasPhishingPhrase(messageBody: messageBody)) { + let reason = "Message contains a common phishing phrase." + if returnFirstMatch { + return [reason] + } + if !allMatches.contains(reason) { + allMatches.append(reason) + } + } + + return allMatches } func hasAbuseiveTld(messageBody: String) -> Bool { let abusiveTlds = [ - #"\b\.amazonaws\.com\b"#, - #"\b\.amplifyap\.com\b"#, - #"\b\.au\b"#, - #"\b\.bar\b"#, - #"\b\.bd\b"#, - #"\b\.best\b"#, - #"\b\.br\b"#, - #"\b\.business\b"#, - #"\b\.buzz\b"#, - #"\b\.cam\b"#, - #"\b\.casa\b"#, - #"\b\.cc\b"#, - #"\b\.center\b"#, - #"\b\.cloud\b"#, - #"\b\.cn\b"#, - #"\b\.cyou\b"#, - #"\b\.digital\b"#, - #"\b\.digital\b"#, - #"\b\.email\b"#, - #"\b\.fun\b"#, - #"\b\.funance\b"#, - #"\b\.godaddysites\.com\b"#, - #"\b\.host\b"#, - #"\b\.icu\b"#, - #"\b\.ik\b"#, - #"\b\.in\b"#, - #"\b\.info\b"#, - #"\b\.ir\b"#, - #"\b\.ke\b"#, - #"\b\.ke\b"#, - #"\b\.link\b"#, - #"\b\.live\b"#, - #"\b\.monster\b"#, - #"\b\.net\b"#, - #"\b\.netfly\.app\b"#, - #"\b\.ng\b"#, - #"\b\.np\b"#, - #"\b\.one\b"#, - #"\b\.online\b"#, - #"\b\.online\b"#, - #"\b\.pe\b"#, - #"\b\.ph\b"#, - #"\b\.pk\b"#, - #"\b\.pl\b"#, - #"\b\.quest\b"#, - #"\b\.rest\b"#, - #"\b\.ru\b"#, - #"\b\.sa\b"#, - #"\b\.sbs\b"#, - #"\b\.services\b"#, - #"\b\.shop\b"#, - #"\b\.site\b"#, - #"\b\.store\b"#, - #"\b\.su\b"#, - #"\b\.support\b"#, - #"\b\.surf\b"#, - #"\b\.td\b"#, - #"\b\.th\b"#, - #"\b\.tk\b"#, - #"\b\.top\b"#, - #"\b\.tr\b"#, - #"\b\.trycloudflare\.com\b"#, - #"\b\.uz\b"#, - #"\b\.ve\b"#, - #"\b\.vn\b"#, - #"\b\.wang\b"#, - #"\b\.web\.app\b"#, - #"\b\.website\b"#, - #"\b\.weebly\.com\b"#, - #"\b\.work\b"#, - #"\b\.xyz\b"#, + ".amazonaws.com", + ".amplifyap.com", + ".au", + ".bar", + ".bd", + ".best", + ".br", + ".business", + ".buzz", + ".cam", + ".casa", + ".cc", + ".center", + ".cloud", + ".cn", + ".cyou", + ".digital", + ".digital", + ".email", + ".fun", + ".funance", + ".godaddysites.com", + ".host", + ".icu", + ".ik", + ".in", + ".info", + ".ir", + ".ke", + ".ke", + ".link", + ".live", + ".monster", + ".net", + ".netfly.app", + ".ng", + ".np", + ".one", + ".online", + ".online", + ".pe", + ".ph", + ".pk", + ".pl", + ".quest", + ".rest", + ".ru", + ".sa", + ".sbs", + ".services", + ".shop", + ".site", + ".store", + ".su", + ".support", + ".surf", + ".td", + ".th", + ".tk", + ".top", + ".tr", + ".trycloudflare.com", + ".workers.dev", + ".uz", + ".ve", + ".vn", + ".wang", + ".web.app", + ".website", + ".weebly.com", + ".work", + ".xyz", ] let filteredMessageBody = messageBody.lowercased() for abusiveTld in abusiveTlds { - if (filteredMessageBody.range(of: abusiveTld, options: .regularExpression) != nil) { + if (filteredMessageBody.contains(abusiveTld)) { return true } } @@ -165,119 +203,125 @@ func hasAbuseiveTld(messageBody: String) -> Bool { func hasBankName(messageBody: String) -> Bool { let bankNames = [ - #"\bafcu\b"#, - #"\balliant\b"#, - #"\ballyfinancial\b"#, - #"\bamericafirst\b"#, - #"\bamericanexpress\b"#, - #"\bamericu\b"#, - #"\bameriprise\b"#, - #"\bameris\b"#, - #"\bamex\b"#, - #"\barvest\b"#, - #"\batlanticunionbank\b"#, - #"\bbanccorp\b"#, - #"\bbancorp\b"#, - #"\bbancshares\b"#, - #"\bbankof\b"#, - #"\bbankozk\b"#, - #"\bbankunited\b"#, - #"\bbarclays\b"#, - #"\bbcifinancial\b"#, - #"\bbecu\b"#, - #"\bbmoharris\b"#, - #"\bbnp\b"#, - #"\bbofa\b"#, - #"\bbokfinancial\b"#, - #"\bcadencebank\b"#, - #"\bcapitalone\b"#, - #"\bcathaybank\b"#, - #"\bcentralbancompany\b"#, - #"\bchase\b"#, - #"\bcibcbank\b"#, - #"\bcitgroup\b"#, - #"\bciti\b"#, - #"\bcitizensfinancial\b"#, - #"\bcitynationalbank\b"#, - #"\bcomerica\b"#, - #"\bcommercebancshares\b"#, - #"\bcreditsuisse\b"#, - #"\bcreditunion\b"#, - #"\bdeutschebank\b"#, - #"\bdiscover\b"#, - #"\beastwestbank\b"#, - #"\bfargo\b"#, - #"\bfifththird\b"#, - #"\bfirstcitizens\b"#, - #"\bfirsthawaiian bank\b"#, - #"\bfirsthorizon\b"#, - #"\bfirstinterstate\b"#, - #"\bfirstmidwest bank\b"#, - #"\bfirstnational\b"#, - #"\bfirstrepublic bank\b"#, - #"\bfirstbank\b"#, - #"\bflagstar\b"#, - #"\bfnb\b"#, - #"\bgolden1\b"#, - #"\bhsbc\b"#, - #"\binvestors bank\b"#, - #"\bjpmorgan\b"#, - #"\bkeybank\b"#, - #"\bkeycorp\b"#, - #"\bm&tbank\b"#, - #"\bmastercard\b"#, - #"\bmidfirstbank\b"#, - #"\bmufgunion\b"#, - #"\bnavyfederal\b"#, - #"\bncfu\b"#, - #"\bnewyorkcommunitybank\b"#, - #"\bnortherntrust\b"#, - #"\bnycb\b"#, - #"\boldnationalbank\b"#, - #"\bpacificpremier\b"#, - #"\bpacwest\b"#, - #"\bpaypal\b"#, - #"\bpeoplesunited\b"#, - #"\bpinnaclefinancial\b"#, - #"\bpnc\b"#, - #"\brbcbank\b"#, - #"\bregionsbank\b"#, - #"\bregionsfinancial\b"#, - #"\bsantanderbank\b"#, - #"\bschwab\b"#, - #"\bsignaturebank\b"#, - #"\bsimmonsbank\b"#, - #"\bsmbc\b"#, - #"\bsterling\b"#, - #"\bstifel\b"#, - #"\bsuncoast\b"#, - #"\bsvb\b"#, - #"\bsynchrony\b"#, - #"\bsynovus\b"#, - #"\btdbank\b"#, - #"\btexascapitalbank\b"#, - #"\btiaa\b"#, - #"\btruist\b"#, - #"\bubs\b"#, - #"\bumbfinancial\b"#, - #"\bumpqua\b"#, - #"\bunitedbank\b"#, - #"\bunitedcommunitybank\b"#, - #"\busbank\b"#, - #"\busaa\b"#, - #"\bvisa\b"#, - #"\bwashingtonfederal\b"#, - #"\bwebsterbank\b"#, - #"\bwells\b"#, - #"\bwesternalliancebank\b"#, - #"\bwintrust\b"#, - #"\bzionsbancorporation\b"#, + "afcu", + "alaskausa", + "alliant", + "allyfinancial", + "americafirst", + "americanexpress", + "americu", + "ameriprise", + "ameris", + "amex", + "arvest", + "atlanticunionbank", + "aufcu", + "banccorp", + "bancorp", + "bancshares", + "bankof", + "bankozk", + "bankunited", + "barclays", + "bcifinancial", + "becu", + "bmoharris", + "bnp", + "bofa", + "boa", + "bokfinancial", + "cadencebank", + "capitalone", + "cathaybank", + "centralbancompany", + "chase", + "cibcbank", + "citgroup", + "citi", + "citizensfinancial", + "citynationalbank", + "comerica", + "commercebancshares", + "creditsuisse", + "creditunion", + "deutschebank", + "discover", + "eastwestbank", + "fargo", + "fifththird", + "firstcitizens", + "firsthawaiian bank", + "firsthorizon", + "firstinterstate", + "firstmidwest bank", + "firstnational", + "firstrepublic bank", + "firstbank", + "flagstar", + "fnb", + "golden1", + "hsbc", + "investors bank", + "jpmorgan", + "keybank", + "keycorp", + "m&tbank", + "mastercard", + "midfirstbank", + "mufgunion", + "navyfederal", + "ncfu", + "newyorkcommunitybank", + "northerntrust", + "nycb", + "oldnationalbank", + "pacificpremier", + "pacwest", + "paypal", + "peoplesunited", + "pinnaclefinancial", + "pnc", + "rbcbank", + "regionsbank", + "regionsfinancial", + "santanderbank", + "schwab", + "signaturebank", + "simmonsbank", + "smbc", + "sterling", + "stifel", + "suncoast", + "svb", + "synchrony", + "synovus", + "tdbank", + "texascapitalbank", + "tiaa", + "truist", + "ubs", + "umbfinancial", + "umpqua", + "unitedbank", + "unitedcommunitybank", + "usbank", + "usaa", + "visa", + "washingtonfederal", + "websterbank", + "wells", + "westernalliancebank", + "wintrust", + "zionsbancorporation", ] let filteredMessageBody = messageBody.lowercased().filter("abcdefghijklmnopqrstuvwxyz0123456789".contains) for bankName in bankNames { - if (filteredMessageBody.range(of: bankName, options: .regularExpression) != nil) { - return true + let variations = getLeetVariations(initialString: bankName) + for variation in variations { + if (filteredMessageBody.contains(variation)) { + return true + } } } return false @@ -285,61 +329,99 @@ func hasBankName(messageBody: String) -> Bool { func hasPhishingPhrase(messageBody: String) -> Bool { let phishingPhrases = [ - #"\bfreepiano\b"#, - #"\byourorderhasbeen\b"#, - #"\bcontactcustomercare\b"#, - #"\bcontactcustomersupport\b"#, - #"\bifyouwishtocancel\b"#, - #"\bpleasecall\b"#, - #"\bdearcustomer\b"#, - #"\byouraccount\b"#, - #"\baprize\b"#, - #"\byouwon\b"#, - #"\byouvewon\b"#, - #"\bgiftcard\b"#, - #"\bgiftcertificate\b"#, - #"\byourcard\b"#, - #"\bcardislocked\b"#, - #"\btemporarilylocked\b"#, - #"\baccfrozen\b"#, - #"\baccountfrozen\b"#, - #"\bcardalert\b"#, - #"\btaxrefund\b"#, - #"\btaxreturn\b"#, - #"\brefunding\b"#, - #"\byourrefund\b"#, - #"\bbankaccount\b"#, - #"\bppackage\b"#, - #"\bfedex\b"#, - #"\bups\b"#, - #"\bdhl\b"#, - #"\bdelivery\b"#, - #"\bicloudid\b"#, - #"\bbitcoin\b"#, - #"\bconfirmtransaction\b"#, - #"\bbailmoney\b"#, - #"\bwesternunion\b"#, - #"\barrestwarrant\b"#, - #"\blawsuitagainst\b"#, - #"\bwarrantagainst\b"#, - #"\bgooglesecurity\b"#, - #"\bapplesecurity\b"#, - #"\bamazonsecurity\b"#, - #"\bunusualactivity\b"#, - #"\bunusualsignin\b"#, - #"\bunusuallogin\b"#, - #"\brandomlypicked\b"#, - #"\brandomlyselected\b"#, - #"\bwinner\b"#, - #"\bsweepstake\b"#, - #"\bpayrollproviders\b"#, + "freepiano", + "yourorderhasbeen", + "contactcustomercare", + "contactcustomersupport", + "ifyouwishtocancel", + "pleasecall", + "dearcustomer", + "youraccount", + "aprize", + "youwon", + "youvewon", + "giftcard", + "giftcertificate", + "yourcard", + "caccountlocked", + "beenlocked", + "cardislocked", + "temporarilylocked", + "accfrozen", + "accountfrozen", + "cardalert", + "taxrefund", + "taxreturn", + "refunding", + "yourrefund", + "bankaccount", + "ppackage", + "fedex", + "ups", + "dhl", + "usps", + "delivery", + "icloudid", + "bitcoin", + "confirmtransaction", + "bailmoney", + "westernunion", + "arrestwarrant", + "lawsuitagainst", + "warrantagainst", + "googlesecurity", + "applesecurity", + "amazonsecurity", + "unusualactivity", + "unusualsignin", + "unusuallogin", + "randomlypicked", + "randomlyselected", + "winner", + "sweepstake", + "payrollproviders", + "chargeissued", + "tocancel", + "hasbeendisabled", + "failedlogin", + "loginattempts", ] - let filteredMessageBody = messageBody.lowercased().filter("abcdefghijklmnopqrstuvwxyz".contains) + let filteredMessageBody = messageBody.lowercased().filter("abcdefghijklmnopqrstuvwxyz0123456789".contains) for phishingPhrase in phishingPhrases { - if (filteredMessageBody.range(of: phishingPhrase, options: .regularExpression) != nil) { - return true + let variations = getLeetVariations(initialString: phishingPhrase) + for variation in variations { + if (filteredMessageBody.contains(variation)) { + return true + } } } return false } + +func getLeetVariations(initialString: String) -> [String] { + var variations = [initialString] + let leetSubstitutions = [ + ["a", "4"], + ["b", "8"], + ["e", "3"], + ["g", "9"], + ["i", "1"], + ["i", "l"], + ["l", "1"], + ["l", "i"], + ["o", "0"], + ["t", "7"], + ] + for substitution in leetSubstitutions { + var variation = initialString.replacingOccurrences(of: substitution[0], with: substitution[1]) + if !variations.contains(variation) { + variations.append(variation) + } + variation = initialString.replacingOccurrences(of: substitution[1], with: substitution[0]) + if !variations.contains(variation) { + variations.append(variation) + } + } + return variations +}