diff --git a/enzevalos_iphone.xcodeproj/project.pbxproj b/enzevalos_iphone.xcodeproj/project.pbxproj index 691e4ed123c0307f1ee48248a9ec9c4bddddd4ae..66bb7ef7ad9f087eee0a5c663fcc96ec6646f328 100644 --- a/enzevalos_iphone.xcodeproj/project.pbxproj +++ b/enzevalos_iphone.xcodeproj/project.pbxproj @@ -171,6 +171,10 @@ 47F867E22052B48E00AA832F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 47F867E12052B48E00AA832F /* libz.tbd */; }; 47F867E42052B49800AA832F /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 47F867E32052B49800AA832F /* libbz2.tbd */; }; 50F2E7D66366C779705987A7 /* Pods_enzevalos_iphoneUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF67EF30BB065CC9C0D17940 /* Pods_enzevalos_iphoneUITests.framework */; }; + 676C2D3024321F8100B631B3 /* TyposquattingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676C2D2F24321F8100B631B3 /* TyposquattingTests.swift */; }; + 6789425F2430C3B300C746D1 /* MailComparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6789425E2430C3B300C746D1 /* MailComparison.swift */; }; + 678942612430C3D600C746D1 /* Typosquatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678942602430C3D600C746D1 /* Typosquatting.swift */; }; + 678942632430C40600C746D1 /* MailComparisonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678942622430C40600C746D1 /* MailComparisonTests.swift */; }; 71DF08982421520D00162B74 /* EmailStringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DF08972421520D00162B74 /* EmailStringExtensionTests.swift */; }; 71DFE5BA240679E80042019C /* HeaderExtractionValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DFE5B9240679E80042019C /* HeaderExtractionValues.swift */; }; 7500EE9D4F3130671F5C1AE2 /* Pods_enzevalos_iphoneTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7977EA7012D8E98D186D5C60 /* Pods_enzevalos_iphoneTests.framework */; }; @@ -591,6 +595,10 @@ 48C250BB32BF11B683003BA1 /* Pods-enzevalos_iphone-enzevalos_iphoneUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphone-enzevalos_iphoneUITests.debug.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphone-enzevalos_iphoneUITests/Pods-enzevalos_iphone-enzevalos_iphoneUITests.debug.xcconfig"; sourceTree = "<group>"; }; 66E758F271CD65AB3E5FE7A7 /* Pods-enzevalos_iphoneUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphoneUITests.debug.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphoneUITests/Pods-enzevalos_iphoneUITests.debug.xcconfig"; sourceTree = "<group>"; }; 670159DF240FB4E800797FA5 /* enzevalos_iphone 9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "enzevalos_iphone 9.xcdatamodel"; sourceTree = "<group>"; }; + 676C2D2F24321F8100B631B3 /* TyposquattingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TyposquattingTests.swift; sourceTree = "<group>"; }; + 6789425E2430C3B300C746D1 /* MailComparison.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailComparison.swift; sourceTree = "<group>"; }; + 678942602430C3D600C746D1 /* Typosquatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Typosquatting.swift; sourceTree = "<group>"; }; + 678942622430C40600C746D1 /* MailComparisonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailComparisonTests.swift; sourceTree = "<group>"; }; 6EBCCD02AD3B95D8317810E2 /* Pods-enzevalos_iphoneTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphoneTests.debug.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphoneTests/Pods-enzevalos_iphoneTests.debug.xcconfig"; sourceTree = "<group>"; }; 71DF08972421520D00162B74 /* EmailStringExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailStringExtensionTests.swift; sourceTree = "<group>"; }; 71DFE5B9240679E80042019C /* HeaderExtractionValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderExtractionValues.swift; sourceTree = "<group>"; }; @@ -1220,11 +1228,22 @@ path = private; sourceTree = "<group>"; }; + 6789425D2430C38800C746D1 /* Phishing */ = { + isa = PBXGroup; + children = ( + 6789425E2430C3B300C746D1 /* MailComparison.swift */, + 678942602430C3D600C746D1 /* Typosquatting.swift */, + ); + name = Phishing; + sourceTree = "<group>"; + }; 71DF0896242151E200162B74 /* phishing */ = { isa = PBXGroup; children = ( 988C9C5C240D507A006213F0 /* UrlStringExtensionTests.swift */, 71DF08972421520D00162B74 /* EmailStringExtensionTests.swift */, + 678942622430C40600C746D1 /* MailComparisonTests.swift */, + 676C2D2F24321F8100B631B3 /* TyposquattingTests.swift */, ); path = phishing; sourceTree = "<group>"; @@ -1375,6 +1394,7 @@ isa = PBXGroup; children = ( 0EFEF0932417C08B00BB2FF7 /* C Helpers */, + 6789425D2430C38800C746D1 /* Phishing */, 47EABF05241A9C7000774A93 /* Authentication */, 476406872416B54D00C7D426 /* SwiftUI */, 476403FA2413F95300C7D426 /* OpenSSL */, @@ -1941,7 +1961,9 @@ "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", "${BUILT_PRODUCTS_DIR}/KeychainAccess/KeychainAccess.framework", "${BUILT_PRODUCTS_DIR}/Onboard/Onboard.framework", + "${BUILT_PRODUCTS_DIR}/Punycode/Punycode.framework", "${BUILT_PRODUCTS_DIR}/QAKit/QAKit.framework", + "${BUILT_PRODUCTS_DIR}/TLDExtract/TLDExtract.framework", "${BUILT_PRODUCTS_DIR}/Travellib/Travellib.framework", "${BUILT_PRODUCTS_DIR}/VENTokenField/VENTokenField.framework", ); @@ -1955,7 +1977,9 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KeychainAccess.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Onboard.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Punycode.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/QAKit.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TLDExtract.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Travellib.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/VENTokenField.framework", ); @@ -2104,6 +2128,7 @@ F1984D721E1D327200804E1E /* IconsStyleKit.swift in Sources */, F1737ACB2031D7D70000312B /* StudySettings.swift in Sources */, 0ECA5798240D496800B0F231 /* SMIME.swift in Sources */, + 6789425F2430C3B300C746D1 /* MailComparison.swift in Sources */, 8428A8691F436A11007649A5 /* UserNameGamificationTableViewCell.swift in Sources */, A114E4321FACB23000E40243 /* StringExtension.swift in Sources */, 472F398C1E2519C8009260FB /* CNContactExtension.swift in Sources */, @@ -2172,6 +2197,7 @@ A182182C21E5072200918A29 /* IntroDescriptionViewController.swift in Sources */, F1C7AC821FED6473007629DB /* AboutViewController.swift in Sources */, A182183421E51DD200918A29 /* IntroContactCell.swift in Sources */, + 678942612430C3D600C746D1 /* Typosquatting.swift in Sources */, 472F397C1E1D0B0B009260FB /* PersistentMail +CoreDataProperties.swift in Sources */, 8428A85C1F436A05007649A5 /* ArrowView.swift in Sources */, 47E737762284632F00972401 /* KeyCell.swift in Sources */, @@ -2236,10 +2262,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 676C2D3024321F8100B631B3 /* TyposquattingTests.swift in Sources */, 8428A8831F436AC9007649A5 /* GamificationDataUnitTest.swift in Sources */, 71DF08982421520D00162B74 /* EmailStringExtensionTests.swift in Sources */, 3EC35F302003838E008BDF95 /* InvitationTests.swift in Sources */, 474054982244D7A9007CF83B /* MailServerConfigurationTest.swift in Sources */, + 678942632430C40600C746D1 /* MailComparisonTests.swift in Sources */, 479B5977206914BE00B3944D /* CryptoTests.swift in Sources */, A15D215F223BE6E4003E0CE0 /* MailTest.swift in Sources */, 0ED9073024338E3C008CF9D0 /* SMIMETests.swift in Sources */, diff --git a/enzevalos_iphone.xcodeproj/xcshareddata/xcschemes/enzevalos_iphone.xcscheme b/enzevalos_iphone.xcodeproj/xcshareddata/xcschemes/enzevalos_iphone.xcscheme index 5750422cbbc26f8b1dec60b3add457775f258a4b..3a73a19b36e716b830ae25b5f3732b80a63ac844 100644 --- a/enzevalos_iphone.xcodeproj/xcshareddata/xcschemes/enzevalos_iphone.xcscheme +++ b/enzevalos_iphone.xcodeproj/xcshareddata/xcschemes/enzevalos_iphone.xcscheme @@ -48,9 +48,39 @@ ReferencedContainer = "container:enzevalos_iphone.xcodeproj"> </BuildableReference> <SkippedTests> + <Test + Identifier = "CryptoTests/testfindNotSignedMailForPublicKey()"> + </Test> <Test Identifier = "GamificationDataUnitTest"> </Test> + <Test + Identifier = "MailServerConfigurationTest"> + </Test> + <Test + Identifier = "MailTest/testK9SecureInlineMail()"> + </Test> + <Test + Identifier = "MailTest/testK9SecureMail()"> + </Test> + <Test + Identifier = "MailTest/testK9SigedInlineMail()"> + </Test> + <Test + Identifier = "MailTest/testK9SigedMail()"> + </Test> + <Test + Identifier = "MailTest/testMacSecureMail()"> + </Test> + <Test + Identifier = "MailTest/testMacSigedMail()"> + </Test> + <Test + Identifier = "MailTest/testThunderbirdSigedInlineMail()"> + </Test> + <Test + Identifier = "MailTest/testThunderbirdSigedMail()"> + </Test> </SkippedTests> </TestableReference> <TestableReference diff --git a/enzevalos_iphone/DataHandler.swift b/enzevalos_iphone/DataHandler.swift index 402f99c00c383dced15b6323e628c6a4c7ec60c5..c3f22f4c655dfa15235ae34bb2d69983c97c6403 100644 --- a/enzevalos_iphone/DataHandler.swift +++ b/enzevalos_iphone/DataHandler.swift @@ -1119,19 +1119,17 @@ class DataHandler { return [] } + /** + Filters all Presistent Mails with encState == EncryptionState.UnableToDecrypt + */ func getAllNotDecryptedPersistentMail() -> [PersistentMail] { let result = getAllPersistentMails().filter({ $0.encState == EncryptionState.UnableToDecrypt }) return result } - // filter all presistent mails without public key - func getAllNoPublicKeyPersistentMail() -> [PersistentMail] { - let result = getAllPersistentMails().filter({ $0.sigState == SignatureState.NoPublicKey }) - return result - } - - - + /** + Filters all Presistent Mails with sigState == SignatureState.NoPublicKey + */ func getAllNotSignedPersistentMail() -> [PersistentMail] { let result = getAllPersistentMails().filter({ $0.sigState == SignatureState.NoPublicKey }) return result diff --git a/enzevalos_iphone/MailComparison.swift b/enzevalos_iphone/MailComparison.swift new file mode 100644 index 0000000000000000000000000000000000000000..b221bb319c57f101c8b932d50a8590b1c3b39816 --- /dev/null +++ b/enzevalos_iphone/MailComparison.swift @@ -0,0 +1,92 @@ +// +// MailComparison.swift +// enzevalos_iphone +// +// Created by Katharina Müller and Viktoria Sorgalla on 04.03.20. +// Copyright © 2020 fu-berlin. All rights reserved. +// + +import Foundation +import CoreData + +private let datahandler = DataHandler.handler + +public enum ResultCompareSenderToContacts { + case isContact, isSender, OnlyIdentity, Unknown + } + +extension String { + + /** + compares the given mail address to the persistant mails and returns the number of matches + ***/ + private func countMatches(_ mailAddr: String, _ inboxMails: [PersistentMail]) -> Int { + var senders: [String] = [] + for sender in inboxMails{ + let sender: String = sender.from.mailAddress + senders.append(sender) + } + var numberOfFoundMatches = 0 + for addr in senders{ + if addr == mailAddr{ + numberOfFoundMatches += 1 + } + } + return numberOfFoundMatches + } + + /** + returns a mail address, wich has the same identity as the given one but a different domain + ***/ + private func getIdentityWithDifferentDomain(_ mailAdd: String, inboxMails: [PersistentMail]) -> String{ + let senderIdentity = mailAdd.getLocalMailIdentity() + var senderDomain = mailAdd.splitAddress() + senderDomain.removeFirst() + + var senders: [String] = [] + for sender in inboxMails{ + let sender: String = sender.from.mailAddress + senders.append(sender) + } + var foundMatch = "" + for addr in senders{ + let foundIdentity = addr.getLocalMailIdentity() + var foundDomain = addr.splitAddress() + foundDomain.removeFirst() + if senderIdentity == foundIdentity && senderDomain != foundDomain{ + foundMatch = addr + return foundMatch + } + } + return foundMatch + } + + /** + looks if the mail address of the sender is already known + ***/ + public func compareSenderToContacts() -> ResultCompareSenderToContacts { + // Case 1: Is the address of the sender known through previous mails? + let inboxMails = datahandler.getAllPersistentMails() + if countMatches(self, inboxMails) >= 2{ + return ResultCompareSenderToContacts.isSender + } + // Case 2: Is the address of the sender in the address book? + let enzContacts = datahandler.getContacts() + for contact in enzContacts { + let contMailAddr = contact.cnContact?.getMailAddresses() + if contMailAddr != nil{ + for mAddr in contMailAddr!{ + if mAddr.mailAddress == self { + return ResultCompareSenderToContacts.isContact + } + } + } + } + // Case 3: The identity of an mail address is known, but not the (sub-)domain + let foundIdentity = getIdentityWithDifferentDomain(self, inboxMails: inboxMails) + if foundIdentity != ""{ + return ResultCompareSenderToContacts.OnlyIdentity + } + return ResultCompareSenderToContacts.Unknown + } +} diff --git a/enzevalos_iphone/Settings.bundle/Acknowledgements.plist b/enzevalos_iphone/Settings.bundle/Acknowledgements.plist index 4965e6f4c68ffa3c00dbc86a5c9b77a9261d4de6..41fc4f8b65bcafeade28b7a17e46dc5d359d9925 100644 --- a/enzevalos_iphone/Settings.bundle/Acknowledgements.plist +++ b/enzevalos_iphone/Settings.bundle/Acknowledgements.plist @@ -972,6 +972,37 @@ THE SOFTWARE. <key>Type</key> <string>PSGroupSpecifier</string> </dict> + <dict> + <key>FooterText</key> + <string>MIT License + +Copyright (c) 2018 Gumob + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +</string> + <key>License</key> + <string>MIT</string> + <key>Title</key> + <string>Punycode</string> + <key>Type</key> + <string>PSGroupSpecifier</string> + </dict> <dict> <key>FooterText</key> <string>The MIT License (MIT) @@ -1002,6 +1033,37 @@ SOFTWARE.</string> <key>Type</key> <string>PSGroupSpecifier</string> </dict> + <dict> + <key>FooterText</key> + <string>MIT License + +Copyright (c) 2018 Gumob + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +</string> + <key>License</key> + <string>MIT</string> + <key>Title</key> + <string>TLDExtract</string> + <key>Type</key> + <string>PSGroupSpecifier</string> + </dict> <dict> <key>FooterText</key> <string> GNU LESSER GENERAL PUBLIC LICENSE diff --git a/enzevalos_iphone/StringExtension.swift b/enzevalos_iphone/StringExtension.swift index a9656f4fd454fdb3c13cc00530b489bda328b586..c3806413ba900a87b3f4920570da7a17de9f65ac 100644 --- a/enzevalos_iphone/StringExtension.swift +++ b/enzevalos_iphone/StringExtension.swift @@ -19,8 +19,11 @@ // import Foundation +import TLDExtract + +let extractor = try! TLDExtract(useFrozenData: true) + extension String { - static func random(length: Int = 20) -> String { var randomBytes = Data(count: length) @@ -62,7 +65,45 @@ extension String { } extension String { - func splitAddress() -> [String]{ + /** + Returns true if a String contains upper case letters. + */ + func containsUpperCaseLetter() -> Bool { + let url = self + let regEx = "[A-Z]" + let range = NSRange(location: 0, length: url.utf16.count) + let regex = try! NSRegularExpression(pattern: regEx) + if regex.firstMatch(in: url, options:[], range: range) != nil { + return true + } + return false + } + + /** + Returns true if the string contains two or more consecutive dots + */ + func twoDotsOrMore() -> Bool { + let url = self + // Finds 2 or more consecutive dots + let regEx = "\\.{2,}" + let range = NSRange(location: 0, length: url.utf16.count) + let regex = try! NSRegularExpression(pattern: regEx) + if regex.firstMatch(in: url, options:[], range: range) != nil { + return true + } + return false + } +} + +/** +functions to split an emailadress into their domains +*/ +extension String { + /** + Returns the E-Mail address splitted by "." and "@": + Example: test@imp.fu-berlin.de -> ["test", "imp", "fu-berlin", "de"] + */ + func splitAddress() -> [String] { if self == ""{ return [] } @@ -73,13 +114,21 @@ extension String { return splittetMailArr } - func getLocalMailIdentity() -> String{ + /** + Returns the extracted local mail identity from an E-Mail address in + Example: test@imp.fu-berlin.de -> test + */ + func getLocalMailIdentity() -> String { guard let localIdentity = self.splitAddress().first else { return ""} return localIdentity } - func getSubdomains() -> [String]{ + /** + Returns the extracted sub domain from an E-Mail address in + Example: test@imp.fu-berlin.de -> ["imp", "fu-berlin"] + */ + func getSubdomains() -> [String] { if self == ""{ return [] } @@ -88,18 +137,41 @@ extension String { subDomains.removeLast() return subDomains } - - - func getDomain() -> String{ + + /** + Returns the extracted top level domain from an E-Mail address + Example: test@imp.fu-berlin.de -> de + */ + func getDomain() -> String { guard let domain = self.splitAddress().last else {return ""} return domain + } + + /** + Returns the extracted sub - and second evel domain from an E-Mail address in + Example: test@imp.fu-berlin.de -> imp.fu-berlin OR test@example.de -> example + */ + func getMailDomains() -> String { + var domain = "" + if var domains = self.components(separatedBy: "@").last?.components(separatedBy: ".") { + domains.removeLast() + for dom in domains { + domain += dom + "." + } + domain.removeLast() + return domain + } + return "" } } +/** +Functions to find E-mail adresses and URLs +*/ extension String { + func isValidEmail() -> Bool { - let emailRegEx = "(?!\\u10000-\\uEFFFF.*\\.\\.)([^@\\s]{2,64}+@[^@\\s]+\\.[^@\\s\\.]+$)" let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx) return emailTest.evaluate(with: self) @@ -136,16 +208,35 @@ extension String { } return nil } + + /** + Returns a list of E-Mail Addresses extracted from a given string + */ + func findMailAddress() -> [String] { + let splitString = self.split(separator: " ").map(String.init) + var results:[String] = [] + for val in splitString { + if val.contains("@") && val.isValidEmail(){ + results.append(val) + } + } + return(results) + } - //for text only, not for hyperlink detection - func findURL() -> [String]{ - var urls : [String] = [] + /** + For text only, not for hyperlink detection + Returns URLs from a given String + */ + func findURL() -> [String?] { + var urls : [String?] = [] do { let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) detector.enumerateMatches(in: self, options: [], range: NSMakeRange(0, self.count), using: { (result, _, _) in if let match = result{ if let match2 = match.url?.absoluteString, !match2.contains("@") { - urls.append(match2) + if !match2.twoDotsOrMore() { + urls.append(match2) + } } } }) @@ -155,7 +246,9 @@ extension String { return urls } - // to find HTML tags and hyperlinks + /** + To find HTML tags and hyperlinks + */ func findHtmlTags() -> [String]{ let htmlTagPattern = "<(.*?)a.*?href=.*?>(.*?)<\\/.*?a.*?>" let range = NSRange(location: 0, length: self.utf16.count) @@ -195,4 +288,196 @@ extension String { } return (foundLinks) } + +} + +/** +Functions for splitting a URL into their domains +*/ +extension String { + + /** + Returns a list of all second level domains taken out of all URLs from a String. + */ + func getSLD()-> [String] { + let urls = self.findURL() + var SLD: [String] = [] + for url in urls { + let urlString: String = url! + guard let result: TLDResult = extractor.parse(urlString) else { continue } + if let sld = result.secondLevelDomain { + SLD.append(sld) + } + } + return SLD + } + + /** + Returns a list of all top level domains taken out of all URLs from a String. + */ + func getTLD()-> [String] { + let urls = self.findURL() + var TLD: [String] = [] + for url in urls { + let urlString: String = url! + guard let result: TLDResult = extractor.parse(urlString) else { continue } + if let tld = result.topLevelDomain { + TLD.append(tld) + } + } + return TLD + } + + /** + Returns a list of all root domains taken out of all URLs from a String. + */ + func getRD() -> [String] { + let urls = self.findURL() + var RD: [String] = [] + for url in urls { + let urlString: String = url! + guard let result: TLDResult = extractor.parse(urlString) else { continue } + if let rd = result.rootDomain { + RD.append(rd) + } + } + return RD + } + + /** + Returns the root domain of a URL. + */ + func getRootDomain() -> String { + if let result: TLDResult = extractor.parse(self) { + if let rootDomain = result.rootDomain { + return rootDomain + } + } + return "" + } + + /** + Returns the second-level domain of a URL. + */ + func getSecondLevelDomain() -> String { + if let result: TLDResult = extractor.parse(self) { + if let secondLevelDomain = result.secondLevelDomain { + return secondLevelDomain + } + } + return "" + } +} + +/** +Levenshtein Algorithm to calculate the editdistance +Returns the editdistance betwenn two Strings +Weights for transpositiona and editdistance are 2, weights for the rest is 1 +*/ +extension String { + + /** + Returns the editdistance between two strings + */ + func getEditDistance(str: String) -> Int{ + return self.levenshtein(str) + } + + /** + Returns an dictionary of every editdistance between every string of a given list and a given string + Key: string, Value: editdistance + */ + func getEditDistances(list: [String]) -> [String : String]{ + var editDistance = 0 + var editDic : [String:String] = [:] + for elm in list { + editDistance = self.getEditDistance(str: elm) + editDic.updateValue(String(editDistance), forKey: elm) + } + return editDic + } + + /** + Compares two strings with each other. Return true if the edit distance is in the allowed range. + Not allowed distance: 0 < distance < allowedEditDistance. + */ + func isAllowedDistance(str: String, allowedEditDistance: Int) -> Bool { + let distance = self.levenshtein(str) + if distance < allowedEditDistance && distance != 0 { + return false + } + return true + } + + /** + Compares a String with a list of strings and checks whether the edit distance between them is in the allowed range. + Returns a boolean value and a list of strings that had an un-allowed edit distance with the original string + The Bool values is returned False: if the string has an un-allowed edit distance with any other element from the list. + */ + func compareWith(strings: [String], allowedEditDistance: Int) -> (Bool, [String]) { + var isAllowedEditDistance = true + var strList: [String] = [] + for str in strings { + if !self.isAllowedDistance(str: str, allowedEditDistance: allowedEditDistance) { + isAllowedEditDistance = false + strList.append(str) + } + } + return (isAllowedEditDistance, strList) + } + + /** + Levenshtein Algorithm + Resource: https://gist.github.com/RuiCarneiro/82bf91214e3e09222233b1fc04139c86 + edited the source code: added transposition, edited weights + */ + public func levenshtein(_ other: String) -> Int { + let sCount = self.count + let oCount = other.count + + let a = Array(self) + let b = Array(other) + + guard sCount != 0 else { + return oCount + } + + guard oCount != 0 else { + return sCount + } + + let line : [Int] = Array(repeating: 0, count: oCount + 1) + var mat : [[Int]] = Array(repeating: line, count: sCount + 1) + + for i in 0...sCount { + mat[i][0] = i + } + + for j in 0...oCount { + mat[0][j] = j + } + + for j in 1...oCount { + for i in 1...sCount { + + if a[i - 1] == b[j - 1] { + mat[i][j] = mat[i - 1][j - 1] // no operation + } + + else { + // weight for transposition and substitution are 2, the others have weight 1 + let del = mat[i - 1][j] + 1 // deletion + let ins = mat[i][j - 1] + 1 // insertion + let sub = mat[i - 1][j - 1] + 2 // substitution + mat[i][j] = min(min(del, ins), sub) + + if (i > 1) && (j > 1) && (i < sCount) && (j < oCount) && (a[i - 1] == b[j]) && (a[i] == b[j - 1]) { + mat[i][j] = min(mat[i][j], mat[i-2][j-2]+1) // transposition + } + } + } + } + return mat[sCount][oCount] + } + } diff --git a/enzevalos_iphone/SwiftPGP.swift b/enzevalos_iphone/SwiftPGP.swift index 79d04e45079728f2e887ea0f55c352a10f008b37..fdb750554d55bd7dd5b1ca5860fa71a67b601a23 100644 --- a/enzevalos_iphone/SwiftPGP.swift +++ b/enzevalos_iphone/SwiftPGP.swift @@ -595,7 +595,10 @@ class SwiftPGP: Encryption { return CryptoObject(chiphertext: data, plaintext: plaintext, decryptedData: plaindata, sigState: sigState, encState: encState, signKey: sigKeyID, encType: CryptoScheme.PGP, signedAdrs: signedAdr) } - // A help function for findMailForSecrectKey and findNotSignedMailForPublicKey + /** + A help function for findMailForSecrectKey and findNotSignedMailForPublicKey + Receives a keyID as String and returns the corresponding key as a [Key] List. + */ func keyAsKeyList(keyID: String) -> [Key] { var keyList = [Key]() if let key: Key = loadKey(id: keyID) { @@ -604,7 +607,9 @@ class SwiftPGP: Encryption { return keyList } - // Finds undecrypted mails in persistent mails and and tries to decrypt with the incoming secret key + /** + Receives a keyID as String and searches for undecrypted mails in persistent mails and and tries to decrypt them with the incoming secret key. + */ func findMailForSecretKey(keyID: String) { var encState = EncryptionState.UnableToDecrypt var plaindata: Data? = nil @@ -654,7 +659,9 @@ class SwiftPGP: Encryption { return (nil, EncryptionState.NoEncryption) } - // Finds unsigned mails in persistent mails and and tries to verify them with the incoming public key + /** + Receives a keyID as String and searches for unsigned mails in persistent mails and and tries to verify them with the incoming public key. + */ func findNotSignedMailForPublicKey(keyID: String) { var sigState = SignatureState.NoPublicKey let key: [Key] = keyAsKeyList(keyID: keyID) diff --git a/enzevalos_iphone/Typosquatting.swift b/enzevalos_iphone/Typosquatting.swift new file mode 100644 index 0000000000000000000000000000000000000000..580b709363e7afb198fcffb73df7ee60eed774e6 --- /dev/null +++ b/enzevalos_iphone/Typosquatting.swift @@ -0,0 +1,206 @@ +// +// Typosquatting.swift +// enzevalos_iphone +// +// Created by Lauren Elden on 29.03.20. +// Copyright © 2020 fu-berlin. All rights reserved. +// + +import Foundation +import TLDExtract + +class Typosquatting { + + /** + Compares a url's root domain with all root domain of all elements in a given url list. + false: if the URL's root domain has an unallowed edit distance with any other root domain. + */ + func compareURLs(myUrl: String, urls: [String]) -> (Bool, [String]) { + let rootDomain = myUrl.getRootDomain() + var isAllowedEditDistance = true + var closeStrings = [String]() + for url in urls { + let rd = url.getRootDomain() + if !rootDomain.isAllowedDistance(str: rd, allowedEditDistance: 4) { + // 2 domains contain less than 4 differences --> warning here needed + isAllowedEditDistance = false + closeStrings.append(url) + } + } + return (isAllowedEditDistance , closeStrings) + } + + /** + Compares a url's root domain with all root domain of all elements in a given url list. + Returns an dictionary with the urls as the keys and and a bollean if it fits the allowed editdistance as value + isAllowedDistance is false: if the URL's root domain has an unallowed edit distance with any other root domain. + */ + func compareURLsAsDic(url: String, urls: [String], allowedEditDistance: Int) -> [String:Bool] { + let rootDomain = url.getRootDomain() + var isAllowedEditDistance = true + var typoDic: [String:Bool] = [:] + for url in urls { + let rd = url.getRootDomain() + if !rootDomain.isAllowedDistance(str: rd, allowedEditDistance: allowedEditDistance) { + // 2 domains contain less than 4 differences --> warning here needed + isAllowedEditDistance = false + typoDic.updateValue(isAllowedEditDistance, forKey : url) + isAllowedEditDistance = true + } + else{ + typoDic.updateValue(isAllowedEditDistance, forKey : url) + } + } + return typoDic + } + + /** + Compares a url's root domain with all root domain of all elements in a givin url list. + Returns an dictionary with the URLs as keys and the Editdistance as value + */ + func getURLEditdistanceAsDic(url: String, urls: [String]) -> [String:Int] { + let rootDomain = url.getRootDomain() + var urlEditDic: [String:Int] = [:] + var editDistance = 0 + for url in urls { + let rd = url.getRootDomain() + editDistance = rootDomain.getEditDistance(str: rd) + urlEditDic.updateValue(editDistance, forKey : url) + } + return urlEditDic + } + + /** + Compares a URLs second-level domain with all elements of a givin domain list. + false: if the URL's second-level domain has an unallowed edit distance with any other domain. + */ + func compareURLWithSLDList(url: String, domains: [String]) -> Bool { + let secondLevelDomain = url.getSecondLevelDomain() + var isAllowedEditDistance = true + for domain in domains { + if !secondLevelDomain.isAllowedDistance(str: domain, allowedEditDistance: 4) { + // 2 domains contain less than 4 differences --> warning here needed + isAllowedEditDistance = false + } + } + return isAllowedEditDistance + } + + /** + Compares a url's second level domain with all domains of all elements in a givin second level domain list. + Returns an dictionary with the domains as the keys and and a boolean if it fits the allowed edit-distance as value + isAllowedDistance is false: if the URL's second level domain has an unallowed edit distance with any other domain. + */ + func compareURLWithSLDListAsDic(url: String, domains: [String], allowedEditDistance: Int) -> [String:Bool] { + let secondLevelDomain = url.getSecondLevelDomain() + var isAllowedEditDistance = true + var typoDic: [String:Bool] = [:] + for domain in domains { + if !secondLevelDomain.isAllowedDistance(str: domain, allowedEditDistance: allowedEditDistance) { + // 2 domains contain less than 4 differences --> warning here needed + isAllowedEditDistance = false + typoDic.updateValue(isAllowedEditDistance, forKey : domain) + isAllowedEditDistance = true + } + else{ + typoDic.updateValue(isAllowedEditDistance, forKey : domain) + } + } + return typoDic + } + + /** + Compares a url's second-level domain with all elements of a givin domain list. + Returns an dictionary with the second level domain as keys and the editdistance as value + */ + func getSLDEditdistanceAsDic(url: String, domains: [String]) -> [String:Int] { + let secondLevelDomain = url.getSecondLevelDomain() + var sldEditDic: [String:Int] = [:] + var editDistance = 0 + for domain in domains { + editDistance = secondLevelDomain.getEditDistance(str: domain) + sldEditDic.updateValue(editDistance, forKey : domain) + } + return sldEditDic + } + + /** + Compares a second-level domain with all elements of a givin second-level domain list. + false: if the second-level domain has an unallowed edit distance with any other domain. + */ + func compareDomainWithDomians(secondLvlDomain: String, domains: [String], allowedEditDistance: Int) -> Bool { + var isAllowedEditDistance = true + for domain in domains { + if !secondLvlDomain.isAllowedDistance(str: domain, allowedEditDistance: allowedEditDistance) { + // 2 domains contain less than 4 differences --> warning here needed + isAllowedEditDistance = false + } + } + return isAllowedEditDistance + } + + /** + Compares a second level domain with all elements of a givin second-level domain list. + Returns an dictionary with the domains as the keys and and a boolean if it fits the allowed editdistance as value + isAllowedDistance is false: if the second level domain has an unallowed edit distance with any other domain. + */ + func compareDomainWithDomiansAsDic(secondLvlDomain: String, domains: [String], allowedEditDistance: Int) -> [String:Bool] { + var isAllowedEditDistance = true + var typoDic: [String:Bool] = [:] + for domain in domains { + if !secondLvlDomain.isAllowedDistance(str: domain, allowedEditDistance: allowedEditDistance) { + // 2 domains contain less than 4 differences --> warning here needed + isAllowedEditDistance = false + typoDic.updateValue(isAllowedEditDistance, forKey : domain) + isAllowedEditDistance = true + } + else{ + typoDic.updateValue(isAllowedEditDistance, forKey : domain) + } + } + return typoDic + } + + /** + Compares a domain with all elements of a givin domain list. + Returns an dictionary with domains as keys and the editdistance as value + */ + func getDomainEditDistanceAsDic(domain: String, domains: [String]) -> [String:Int] { + var domainEditDic: [String:Int] = [:] + var editDistance = 0 + for dom in domains { + editDistance = domain.getEditDistance(str: dom) + domainEditDic.updateValue(editDistance, forKey : dom) + } + return domainEditDic + } + + /** + Receives a mail body and takes out the URLs and filters valid URLs by verifying their Root Domain + Return a list of valid URLs + */ + func isValidRD(mailBody: String) -> [String] { + let urls = mailBody.findURL() + var foundRDs: [String] = [] + for url in urls { + let urlString: String = url! + if !urlString.containsUpperCaseLetter() { + guard let result: TLDResult = extractor.parse(urlString) else { continue } + if let rd = result.rootDomain { + let rdPatternRegEx = "^([a-z0-9])*([a-z0-9-]+\\.)*[a-z0-9]*([a-z0-9-]+)*[a-z0-9]+\\.[a-z]{2,11}?$" // for hostname + let range = NSRange(location: 0, length: rd.utf16.count) + let regex = try! NSRegularExpression(pattern: rdPatternRegEx) + if regex.firstMatch(in: rd, options:[], range: range) != nil { + foundRDs.append(rd) + } else { + // domain did not pass the regex --> warning here needed + } + } + } else { + // domain contains capital letters --> warning here needed + } + } + // Returns non-Critical Root Domain list + return (foundRDs) + } +} diff --git a/enzevalos_iphoneTests/CoreDataTests.swift b/enzevalos_iphoneTests/CoreDataTests.swift index 1386de55de99d70db3269cc90691135afb542dc4..9d041ae6345f5180efbaa42cd7883e68aaf7cdf2 100644 --- a/enzevalos_iphoneTests/CoreDataTests.swift +++ b/enzevalos_iphoneTests/CoreDataTests.swift @@ -104,52 +104,6 @@ class CoraDataTests: XCTestCase { } } - // Generate a test secret key - func testkey() -> (String){ - let testsender = createUser() - let testkeyID = pgp.generateKey(adr: testsender.mailbox, new: true) - return testkeyID - - } - - // Test decryption of all undecrypted mails with new secret key - func testfindMailForSecretKey() { - let testkeyID = testkey() - let swiftpgp = SwiftPGP() - - // E-Mail generieren - guard let from = MCOAddress(mailbox: "sender@example.com") else { - return - } - - // Create test Mail - guard let m1 = testMail(from: from, to: [user], cc: [], bcc: []) else { - XCTFail("No test mail") - return - } - - // Create Ciphertext - let body = "encrypted text" - let senderPGP = SwiftPGP() - let encryptedObject = senderPGP.encrypt(plaintext: body, ids: [testkeyID], myId: "") - XCTAssert(encryptedObject.encryptionState == .ValidedEncryptedWithCurrentKey && encryptedObject.signatureState == .NoSignature) - - guard let cipher = encryptedObject.chiperString else { - XCTFail("No chipher data") - return - } - - // Change Mail - m1.unableToDecrypt = true - m1.body = cipher - - datahandler.save(during: "") - - swiftpgp.findMailForSecretKey(keyID: testkeyID) - XCTAssertEqual(m1.body, "encrypted text") - XCTAssertFalse(m1.unableToDecrypt) - } - func createUser(adr: String = String.random().lowercased(), name: String = String.random()) -> MCOAddress { return MCOAddress.init(displayName: name, mailbox: adr.lowercased()) } diff --git a/enzevalos_iphoneTests/CryptoTests.swift b/enzevalos_iphoneTests/CryptoTests.swift index 3b2d859734c3195805192c54b9966dae2b03b9ad..36805bf17cc7220add86308c03f96fe706b61ca8 100644 --- a/enzevalos_iphoneTests/CryptoTests.swift +++ b/enzevalos_iphoneTests/CryptoTests.swift @@ -610,7 +610,54 @@ class CryptoTests: XCTestCase { return mail } - // Not finished yet + /** + Generate a test secret key + */ + func testkey() -> (String){ + let testsender = createUser() + let testkeyID = pgp.generateKey(adr: testsender.mailbox, new: true) + return testkeyID + } + + func testfindMailForSecretKey() { + let testkeyID = testkey() + + // E-Mail generieren + guard let from = MCOAddress(mailbox: "sender@example.com") else { + return + } + + // Create test Mail + guard let m1 = testMail(from: from, to: [user], cc: [], bcc: []) else { + XCTFail("No test mail") + return + } + + // Create Ciphertext + let body = "encrypted text" + let senderPGP = SwiftPGP() + let encryptedObject = senderPGP.encrypt(plaintext: body, ids: [testkeyID], myId: "") + XCTAssert(encryptedObject.encryptionState == .ValidedEncryptedWithCurrentKey && encryptedObject.signatureState == .NoSignature) + + guard let cipher = encryptedObject.chiperString else { + XCTFail("No chipher data") + return + } + + // Change Mail + m1.unableToDecrypt = true + m1.body = cipher + + datahandler.save(during: "") + + pgp.findMailForSecretKey(keyID: testkeyID) + XCTAssertEqual(m1.body, "encrypted text") + XCTAssertFalse(m1.unableToDecrypt) + } + + /* + !!!!! Not finished yet !!!!! + */ func testfindNotSignedMailForPublicKey() { // E-Mail generieren guard let from = MCOAddress(mailbox: "alice@example.com") else { diff --git a/enzevalos_iphoneTests/phishing/EmailStringExtensionTests.swift b/enzevalos_iphoneTests/phishing/EmailStringExtensionTests.swift index bfe4779c689e28e83678bc8d4198f6ce9cc62158..e12f53778674ec1e14bdff10267b27cca5ce5661 100644 --- a/enzevalos_iphoneTests/phishing/EmailStringExtensionTests.swift +++ b/enzevalos_iphoneTests/phishing/EmailStringExtensionTests.swift @@ -1,141 +1,155 @@ - // - // EmailAdressTests.swift - // enzevalos_iphoneTests - // - // Created by Katharina Müller on 17.03.20. - // Copyright © 2020 fu-berlin. All rights reserved. - // - import XCTest - @testable import enzevalos_iphone +// +// EmailAdressTests.swift +// enzevalos_iphoneTests +// +// Created by Katharina Müller on 17.03.20. +// Copyright © 2020 fu-berlin. All rights reserved. +// +import XCTest +@testable import enzevalos_iphone - class EmailStringExtensionTests: XCTestCase { +class EmailStringExtensionTests: XCTestCase { - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - } + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - // General TestText for all (needs to simulate a body of text from which to extract specific Strings - var TestText = "blabla moep jkjlkj lkjkljknmbjks llhil k. jhkhkih. huhuhj! fsdf bob.alice@zedat.fu-berlin.de dfsf. jhjknjknjkh https://git.imp.fu-berlin.de/enzevalos/enzevalos_iphone/issues/240 hjkhjkhkhn www.google.de kljhl@hjkghgkhj.com nljbjkjk.de url tag html mail <a href='https://www.w3schools.com'>Visit W3Schools</a> hjhkhiuhziu kjhkl <a href=\"https://www.w2schools.com\">Visit W2Schools</a>. lknljnlk. kmm /n lmölmpöl < a href=\"https://www.w8schools.com\">Visit W8Schools</a> gfg fghfghnhg <a href=\"https://www.w33schools.com\">Visit W33Schools</a> nkjhjkhkjn,mn jnmnklmj j <a href=\"https://www.w22schools.com\">Visit W22Schools</ a> hghjcfgh hfgchnvhj vgjcgj cjghcj <a href=\"https://www.w99schools.com\">Visit W99Schools</a > 你好@yahoo.com eewfve test@莎士比亚.org 你好@莎士比亚.org Rδοκιμή@παράδειγμα.δοκιμή or 管理员@中国互联网络信息中心.中国 你好45@yahoo.com" + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + // General TestText for all (needs to simulate a body of text from which to extract specific Strings + var TestText = "blabla moep jkjlkj lkjkljknmbjks llhil k. jhkhkih. huhuhj! fsdf bob.alice@zedat.fu-berlin.de dfsf. jhjknjknjkh https://git.imp.fu-berlin.de/enzevalos/enzevalos_iphone/issues/240 hjkhjkhkhn www.google.de kljhl@hjkghgkhj.com nljbjkjk.de url tag html mail <a href='https://www.w3schools.com'>Visit W3Schools</a> hjhkhiuhziu kjhkl <a href=\"https://www.w2schools.com\">Visit W2Schools</a>. lknljnlk. kmm /n lmölmpöl < a href=\"https://www.w8schools.com\">Visit W8Schools</a> gfg fghfghnhg <a href=\"https://www.w33schools.com\">Visit W33Schools</a> nkjhjkhkjn,mn jnmnklmj j <a href=\"https://www.w22schools.com\">Visit W22Schools</ a> hghjcfgh hfgchnvhj vgjcgj cjghcj <a href=\"https://www.w99schools.com\">Visit W99Schools</a > 你好@yahoo.com eewfve test@莎士比亚.org 你好@莎士比亚.org Rδοκιμή@παράδειγμα.δοκιμή or 管理员@中国互联网络信息中心.中国 你好45@yahoo.com" - // setup for find mail address in text - var finalMailAddresses = ["bob.alice@zedat.fu-berlin.de", - "kljhl@hjkghgkhj.com", - "你好@yahoo.com", - "test@莎士比亚.org", - "你好@莎士比亚.org", - "Rδοκιμή@παράδειγμα.δοκιμή", - "管理员@中国互联网络信息中心.中国", - "你好45@yahoo.com"] + // setup for find mail address in text + var finalMailAddresses = ["bob.alice@zedat.fu-berlin.de", + "kljhl@hjkghgkhj.com", + "你好@yahoo.com", + "test@莎士比亚.org", + "你好@莎士比亚.org", + "Rδοκιμή@παράδειγμα.δοκιμή", + "管理员@中国互联网络信息中心.中国", + "你好45@yahoo.com"] - func testFindEmails(){ - let arr = TestText.findEmails() - XCTAssertNotNil(arr) - XCTAssertEqual(arr[0], finalMailAddresses[0]) - XCTAssertEqual(arr[1], finalMailAddresses[1]) - XCTAssertEqual(arr[2], finalMailAddresses[2]) - XCTAssertEqual(arr[3], finalMailAddresses[3]) - XCTAssertEqual(arr[4], finalMailAddresses[4]) - XCTAssertEqual(arr[5], finalMailAddresses[5]) - XCTAssertEqual(arr[6], finalMailAddresses[6]) - XCTAssertEqual(arr[7], finalMailAddresses[7]) - } - - // setup email validation - var validEmails = ["alice.bob@moep.blubb.de", - "test@google.com", - "test@google.co.uk", - "你好@yahoo.com", - "test@莎士比亚.org", - "你好@莎士比亚.org", - "Rδοκιμή@παράδειγμα.δοκιμή", - "管理员@中国互联网络信息中心.中国", - "你好45@yahoo.com"] + func testFindEmails(){ + let arr = TestText.findEmails() + XCTAssertNotNil(arr) + XCTAssertEqual(arr[0], finalMailAddresses[0]) + XCTAssertEqual(arr[1], finalMailAddresses[1]) + XCTAssertEqual(arr[2], finalMailAddresses[2]) + XCTAssertEqual(arr[3], finalMailAddresses[3]) + XCTAssertEqual(arr[4], finalMailAddresses[4]) + XCTAssertEqual(arr[5], finalMailAddresses[5]) + XCTAssertEqual(arr[6], finalMailAddresses[6]) + XCTAssertEqual(arr[7], finalMailAddresses[7]) + } + + // setup email validation + var validEmails = ["alice.bob@moep.blubb.de", + "test@google.com", + "test@google.co.uk", + "你好@yahoo.com", + "test@莎士比亚.org", + "你好@莎士比亚.org", + "Rδοκιμή@παράδειγμα.δοκιμή", + "管理员@中国互联网络信息中心.中国", + "你好45@yahoo.com"] - var notValidEmails = ["test@@google.com", - "test@google", - "Abc.example.com", - "A@b@c@example.com", - "this is\"not\\allowed@example.com", - "this\\ still\\\"not\\\\allowed@example.com", - "1234567890123456789012345678901234567890123456789012345678901234+x@example.com"] - ///Finding Email adresses etc. - func testIsValidEmail(){ - for v in validEmails { - XCTAssertTrue(v.isValidEmail()) - } - for n in notValidEmails{ - XCTAssertFalse(n.isValidEmail()) - } + var notValidEmails = ["test@@google.com", + "test@google", + "Abc.example.com", + "A@b@c@example.com", + "this is\"not\\allowed@example.com", + "this\\ still\\\"not\\\\allowed@example.com", + "1234567890123456789012345678901234567890123456789012345678901234+x@example.com"] + + //Finding Email adresses etc. + func testIsValidEmail(){ + for v in validEmails { + XCTAssertTrue(v.isValidEmail()) } - - // setup for mail address splitting - var correctSplit_0 = ["alice.bob", "moep", "blubb", "de"] - var correctSplit_1 = ["test", "google", "com"] - var correctSplit_2 = ["test", "google", "co", "uk"] - var correctSplit_3 = ["你好", "yahoo", "com"] - var correctSplit_4 = ["test", "莎士比亚", "org"] - var correctSplit_5 = ["你好", "莎士比亚", "org"] - var correctSplit_6 = ["Rδοκιμή", "παράδειγμα", "δοκιμή"] - var correctSplit_7 = ["管理员", "中国互联网络信息中心", "中国"] - var correctSplit_8 = ["你好45", "yahoo", "com"] - var spliEmails:[Any] { - return [correctSplit_0, correctSplit_1, correctSplit_2, correctSplit_3, correctSplit_4, correctSplit_5, correctSplit_6, correctSplit_7, correctSplit_8] + for n in notValidEmails{ + XCTAssertFalse(n.isValidEmail()) } + } + + // setup for mail address splitting + var correctSplit_0 = ["alice.bob", "moep", "blubb", "de"] + var correctSplit_1 = ["test", "google", "com"] + var correctSplit_2 = ["test", "google", "co", "uk"] + var correctSplit_3 = ["你好", "yahoo", "com"] + var correctSplit_4 = ["test", "莎士比亚", "org"] + var correctSplit_5 = ["你好", "莎士比亚", "org"] + var correctSplit_6 = ["Rδοκιμή", "παράδειγμα", "δοκιμή"] + var correctSplit_7 = ["管理员", "中国互联网络信息中心", "中国"] + var correctSplit_8 = ["你好45", "yahoo", "com"] + var spliEmails:[Any] { + return [correctSplit_0, correctSplit_1, correctSplit_2, correctSplit_3, correctSplit_4, correctSplit_5, correctSplit_6, correctSplit_7, correctSplit_8] + } - func testSplitMailAddress(){ - for (i, email) in validEmails.enumerated(){ - let arr = (email.splitAddress()) - XCTAssertNotNil(arr) - for (j, _) in arr.enumerated() - { - let array = spliEmails[i] as? [String] - XCTAssertEqual(arr[j], array?[j]) - } + func testSplitMailAddress(){ + for (i, email) in validEmails.enumerated(){ + let arr = (email.splitAddress()) + XCTAssertNotNil(arr) + for (j, _) in arr.enumerated() + { + let array = spliEmails[i] as? [String] + XCTAssertEqual(arr[j], array?[j]) } } + } - var finalTestEMailIdentity = ["alice.bob", "test", "test", "你好", "test", "你好", "Rδοκιμή", "管理员", "你好45"] - func testGetMailIdentity(){ - for (i, email) in validEmails.enumerated(){ - let arr = (email.getLocalMailIdentity()) - XCTAssertNotNil(arr) - XCTAssertEqual(arr, finalTestEMailIdentity[i]) - } + var finalTestEMailIdentity = ["alice.bob", "test", "test", "你好", "test", "你好", "Rδοκιμή", "管理员", "你好45"] + func testGetMailIdentity(){ + for (i, email) in validEmails.enumerated(){ + let arr = (email.getLocalMailIdentity()) + XCTAssertNotNil(arr) + XCTAssertEqual(arr, finalTestEMailIdentity[i]) } + } - var eMailSubDomain_0 = ["moep", "blubb"] - var eMailSubDomain_1 = ["παράδειγμα"] - var eMailSubDomain_2 = ["中国互联网络信息中心"] - var eMailSubDomain_3 = ["google", "co"] - func testGetSubdomains(){ - let arr_0 = validEmails[0].getSubdomains() - XCTAssertNotNil(arr_0) - XCTAssertEqual(arr_0[0], eMailSubDomain_0[0]) - XCTAssertEqual(arr_0[1], eMailSubDomain_0[1]) - - let arr_1 = validEmails[6].getSubdomains() - XCTAssertNotNil(arr_1) - XCTAssertEqual(arr_1[0], eMailSubDomain_1[0]) - - let arr_2 = validEmails[7].getSubdomains() - XCTAssertNotNil(arr_2) - XCTAssertEqual(arr_2[0], eMailSubDomain_2[0]) + var eMailSubDomain_0 = ["moep", "blubb"] + var eMailSubDomain_1 = ["παράδειγμα"] + var eMailSubDomain_2 = ["中国互联网络信息中心"] + var eMailSubDomain_3 = ["google", "co"] + func testGetSubdomains(){ + let arr_0 = validEmails[0].getSubdomains() + XCTAssertNotNil(arr_0) + XCTAssertEqual(arr_0[0], eMailSubDomain_0[0]) + XCTAssertEqual(arr_0[1], eMailSubDomain_0[1]) + + let arr_1 = validEmails[6].getSubdomains() + XCTAssertNotNil(arr_1) + XCTAssertEqual(arr_1[0], eMailSubDomain_1[0]) - let arr_3 = validEmails[2].getSubdomains() - XCTAssertNotNil(arr_3) - XCTAssertEqual(arr_3[0], eMailSubDomain_3[0]) - XCTAssertEqual(arr_3[1], eMailSubDomain_3[1]) - } + let arr_2 = validEmails[7].getSubdomains() + XCTAssertNotNil(arr_2) + XCTAssertEqual(arr_2[0], eMailSubDomain_2[0]) - var finalTestEMailDomain = ["de", "com", "uk", "com", "org", "org", "δοκιμή", "中国", "com"] - func testGetDomain(){ - for (i, email) in validEmails.enumerated(){ - let arr = (email.getDomain()) - XCTAssertNotNil(arr) - XCTAssertEqual(arr, finalTestEMailDomain[i]) - } + let arr_3 = validEmails[2].getSubdomains() + XCTAssertNotNil(arr_3) + XCTAssertEqual(arr_3[0], eMailSubDomain_3[0]) + XCTAssertEqual(arr_3[1], eMailSubDomain_3[1]) + } + + var finalTestEMailDomain = ["de", "com", "uk", "com", "org", "org", "δοκιμή", "中国", "com"] + func testGetDomain(){ + for (i, email) in validEmails.enumerated(){ + let arr = (email.getDomain()) + XCTAssertNotNil(arr) + XCTAssertEqual(arr, finalTestEMailDomain[i]) } } + + func testTextFindMailAddress() { + let arr = TestText.findMailAddress() + XCTAssertNotNil(arr) + XCTAssertEqual(arr[0], finalMailAddresses[0]) + XCTAssertEqual(arr[1], finalMailAddresses[1]) + } + + func testGetMailDomain() { + XCTAssertEqual("test@google.com".getMailDomains(), "google") + XCTAssertEqual("test@google.co.uk".getMailDomains(), "google.co") + } +} + diff --git a/enzevalos_iphoneTests/phishing/MailComparisonTests.swift b/enzevalos_iphoneTests/phishing/MailComparisonTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..910b3334651ca0f53c233047fa07846a48c47451 --- /dev/null +++ b/enzevalos_iphoneTests/phishing/MailComparisonTests.swift @@ -0,0 +1,114 @@ +// +// MailComparisonTests.swift +// enzevalos_iphoneTests +// +// Created by Viktoria Sorgalla on 29.03.20. +// Copyright © 2020 fu-berlin. All rights reserved. +// +import XCTest +import Contacts + +@testable import enzevalos_iphone +class MailComparisonTests: XCTestCase { + let datahandler = DataHandler.handler + let userAdr = "alice@example.com" + let userName = "alice" + var user: MCOAddress = MCOAddress.init(displayName: "alice", mailbox: "alice@example.com") + let userAdr_1 = "bob@enzevalos.de" + let userName_1 = "bob" + var user_1: MCOAddress = MCOAddress.init(displayName: "bob", mailbox: "bob@enzevalos.de") + let userAdr_2 = "carl@test.com" + let userName_2 = "carl" + var user_2: MCOAddress = MCOAddress.init(displayName: "Carl", mailbox: "carl@test.com") + let userAddr_3_1 = "david@123.com" + let userAddr_3_2 = "david@1234.com" + let userName_3 = "david" + var user_3_1: MCOAddress = MCOAddress.init(displayName: "david", mailbox: "david@123.com") + var user_3_2: MCOAddress = MCOAddress.init(displayName: "david", mailbox: "david@1234.com") + + override func setUp() { + super.setUp() + datahandler.reset() + XCTAssertEqual(datahandler.getContacts().count, 0) + XCTAssertEqual(datahandler.getAddresses().count, 0) + } + + override func tearDown() { + datahandler.reset() + super.tearDown() + } + + func testMail(from: MCOAddress, to: [MCOAddress], cc: [MCOAddress], bcc: [MCOAddress], flags: MCOMessageFlag = MCOMessageFlag.init(rawValue: 0), folder: String = "INBOX", date: Date = Date(timeIntervalSince1970: TimeInterval(arc4random())), cryptoObject: CryptoObject? = nil, body: String = String.random(length: 20)) -> PersistentMail? { + + let subject = String.random(length: 20) + let id = UInt64(arc4random()) + var body = body + + if let decryptedBody = cryptoObject?.decryptedText { + body = decryptedBody + } + var mail: PersistentMail? + mail = datahandler.createMail(id, sender: from, receivers: to, cc: cc, time: date, received: true, subject: subject, body: body, flags: flags, record: nil, autocrypt: nil, decryptedData: cryptoObject, folderPath: folder, secretKey: nil, encryptedBody: cryptoObject?.chiperString) + + return mail + } + + private func createAddressBook(_ senders: [MCOAddress]) { + for sender in senders { + let con = CNMutableContact() + let value = sender.mailbox! as AnyObject + + let name = sender.displayName + let nameArray = name!.split(separator: " ").map(String.init) + con.givenName = nameArray.first! + con.emailAddresses.append(CNLabeledValue(label: CNLabelOther, value: value as! NSString)) + // Saving the newly created contact + let store = CNContactStore() + let saveRequest = CNSaveRequest() + saveRequest.add(con, toContainerWithIdentifier:nil) + try! store.execute(saveRequest) + } + + } + private func resetAddressBook(_ senders: [MCOAddress]) { + for sender in senders { + do { + let name = sender.displayName! + let predicate = CNContact.predicateForContacts(matchingName: name) + let store = CNContactStore() + let contacts = try store.unifiedContacts(matching: predicate,keysToFetch: [CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactImageDataKey as CNKeyDescriptor]) + guard contacts.count > 0, let selectedContact = contacts.first else { throw Error.self as! Error } + + let request = CNSaveRequest() + let mutableContact = selectedContact.mutableCopy() as! CNMutableContact + request.delete(mutableContact) + try store.execute(request) + } catch { } + } + } + + func testCompareSenderToContacts () { + let mail_1 = testMail(from: user, to: [], cc: [], bcc: []) + let mail_2 = testMail(from: user_1, to: [], cc: [], bcc: []) + let mail_3 = testMail(from: user_2, to: [], cc: [], bcc: []) + let mail_4 = testMail(from: user_2, to: [], cc: [], bcc: []) + let mail_5 = testMail(from: user_3_1, to: [], cc: [], bcc: []) + let mail_6 = testMail(from: user_3_2, to: [], cc: [], bcc: []) + + let mailsInInbox: [PersistentMail] = [mail_1!, mail_2!, mail_3!, mail_4!, mail_5!, mail_6!] + + + createAddressBook([user]) + + XCTAssertEqual(ResultCompareSenderToContacts.isContact, mailsInInbox[0].from.mailAddress.compareSenderToContacts()) + XCTAssertEqual(ResultCompareSenderToContacts.Unknown, mailsInInbox[1].from.mailAddress.compareSenderToContacts()) + XCTAssertEqual(ResultCompareSenderToContacts.isSender, mailsInInbox[2].from.mailAddress.compareSenderToContacts()) + + XCTAssertEqual(ResultCompareSenderToContacts.OnlyIdentity, mailsInInbox[4].from.mailAddress.compareSenderToContacts()) // userAddr_3_2 + + // The created user has to be deleted after the test + resetAddressBook([user]) + + } +} + diff --git a/enzevalos_iphoneTests/phishing/TyposquattingTests.swift b/enzevalos_iphoneTests/phishing/TyposquattingTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..7f280ebce9a211b1f8cb01dacde05825cda2be98 --- /dev/null +++ b/enzevalos_iphoneTests/phishing/TyposquattingTests.swift @@ -0,0 +1,88 @@ +// +// TyposquattingTests.swift +// enzevalos_iphoneTests +// +// Created by Lauren Elden on 30.03.20. +// Copyright © 2020 fu-berlin. All rights reserved. +// + +import XCTest + +@testable import enzevalos_iphone + +class TyposquattingTests: XCTestCase { + + let typosquatting = Typosquatting() + + // Setup for testcases + let validRD = ["fu-berlin.de", "google.de", "nljbjkjk.de", "w3schools.com", "w2schools.com", "w8schools.com", "w33schools.com", "w22schools.com", "w99schools.eu.com", "3schools.com"] + + let validSLD = ["fu-berlin", "google", "nljbjkjk", "w3schools", "w2schools", "w8schools", "w33schools", "w22schools", "w99schools.eu", "3schools"] + + let validURLs = ["www.fu-berlin.de", "www.google.de", "nljbjkjk.com", "www.w3schools.com", "www.w2schools.com", "www.w8schools.de", "www.w33schools.com", "www.w22schools.com", "www.w99schools.eu.com", "www.3schools.com"] + + let testMailText = "https://git.imp.fu-berlin.de/enzevalos/enzevalos_iphone/issues/240http:// www.google.de http://nljbjkjk.de https://www.w3schools.com https://www.w2schools.com https://www.w8schools.com https://www.w33schools.com https://www.w22schools.com https://www.w99schools.eu.com https://git.imp.fu-ber_lin.de/enzevalos/enzevalos_iphone/issues/240 http://www.google-.de http://nljbj#kjk.de https://www.3schools.com https://www.w2sc..hools.com https://www.w8sch?ools.com https://www.w33sc_hools.com https://www.w22schooIs.com https://www.W99schools$.com" + + + func testIsValidRD() { + let pattern = typosquatting.isValidRD(mailBody: testMailText) + XCTAssertNotNil(pattern) + XCTAssertEqual(pattern[0], validRD[0]) + XCTAssertEqual(pattern[1], validRD[1]) + XCTAssertEqual(pattern[2], validRD[2]) + XCTAssertEqual(pattern[3], validRD[3]) + XCTAssertEqual(pattern[4], validRD[4]) + XCTAssertEqual(pattern[5], validRD[5]) + XCTAssertEqual(pattern[6], validRD[6]) + XCTAssertEqual(pattern[7], validRD[7]) + XCTAssertEqual(pattern[8], validRD[8]) + XCTAssertEqual(pattern[9], validRD[9]) + } + + func testCompareDomainWithDomians() { + let domain = "w2schools" + XCTAssertFalse(typosquatting.compareDomainWithDomians(secondLvlDomain: domain, domains: validSLD, allowedEditDistance: 4)) + } + + func testGetDomainEditDistance() { + let domain = "w2schools" + print(typosquatting.getDomainEditDistanceAsDic(domain: domain, domains: validSLD)) + } + + func testCompareDomainWithDomiansDic() { + let domain = "w2schools" + print(typosquatting.compareDomainWithDomiansAsDic(secondLvlDomain: domain, domains: validSLD, allowedEditDistance: 4)) + } + + func testGetSLDEditdistance() { + let url = "www.w2schools.com" + print(typosquatting.getSLDEditdistanceAsDic(url: url, domains: validSLD)) + } + + func testGetURLEditdistanceAsDic() { + let url = "www.w2schools.com" + print(typosquatting.getURLEditdistanceAsDic(url: url, urls: validURLs)) + } + + func testCompareURLWithSLDListAsDic() { + let url = "www.w2schools.com" + print(typosquatting.compareURLWithSLDListAsDic(url: url, domains: validSLD, allowedEditDistance: 4)) + } + + func testCompareURLWithSLDList() { + let url = "www.google.com" + let domains = ["googIe", "google", "gogle", "gooGle", "gle", "glllll"] + XCTAssertFalse(typosquatting.compareURLWithSLDList(url: url, domains: domains)) + } + + func testCompareURLsAsDic() { + let url = "www.google.com" + print(typosquatting.compareURLsAsDic(url: url, urls: validURLs, allowedEditDistance: 4)) + } + + func testCompareURLs() { + let url = "www.w2schools.com" + let (bool, _) = typosquatting.compareURLs(myUrl: url, urls: validURLs) + XCTAssertFalse(bool) + } +} diff --git a/enzevalos_iphoneTests/phishing/UrlStringExtensionTests.swift b/enzevalos_iphoneTests/phishing/UrlStringExtensionTests.swift index 8bcabf7dc73b30376b1c1a47ae5734baebccb684..d1f26673af1290824c667325f1e86ebd959a53ea 100644 --- a/enzevalos_iphoneTests/phishing/UrlStringExtensionTests.swift +++ b/enzevalos_iphoneTests/phishing/UrlStringExtensionTests.swift @@ -10,13 +10,26 @@ import XCTest class UrlStringExtensionTests: XCTestCase { + // Setup for Critical Pattern of a Root Domain + var testMailText = "https://git.imp.fu-berlin.de/enzevalos/enzevalos_iphone/issues/240http:// www.google.de http://nljbjkjk.de https://www.w3schools.com https://www.w2schools.com https://www.w8schools.com https://www.w33schools.com https://www.w22schools.com https://www.w99schools.eu.com https://git.imp.fu-ber_lin.de/enzevalos/enzevalos_iphone/issues/240 http://www.google-.de http://nljbj#kjk.de https://www.3schools.com https://www.w2sc..hools.com https://www.w8sch?ools.com https://www.w33sc_hools.com https://www.w22schooIs.com https://www.W99schools$.com" + var vaildRD = ["fu-berlin.de", "google.de", "nljbjkjk.de", "w3schools.com", "w2schools.com", "w8schools.com", "w33schools.com", "w22schools.com", "w99schools.eu.com", "3schools.com"] + // Setup for get second level doamin of URL + var finalSLD = ["fu-berlin", "google", "nljbjkjk", "w3schools", "w2schools", "w8schools", "w33schools", "w22schools", "w99schools"] + // Setup for get root domein of URL + var testmailURL = "https://git.imp.fu-ber_lin.de/enzevalos/enzevalos_iphone/issues/240 http://www.google-.de http://nljbj#kjk.de https://www.3schools.com https://www.w2sc..hools.com https://www.w8sch?ools.com https://www.w33sc_hools.com https://www.w22schooIs.com https://www.W99schools$.com https://git.imp.fu-berlin.de/enzevalos/enzevalos_iphone/issues/240 http://www.google.de http://nljbjkjk.de https://www.w3schools.com https://www.w2schools.com https://www.w8schools.com https://www.w33schools.com https://www.w22schools.com https://www.w99schools.eu.com bob.alice@zedat.fu-berlin.de https://git.imp.fu-berlin.de/enzevalos/enzevalos_iphone/issues/240 www.google.de kljhl@hjkghgkhj.com nljbjkjk.de https://www.w3#schools.com/ https://www.w2schools.com/ https://www.w8schools.com/ https://www.w33schools.com/ https://www.w22schools.com/ https://www.w99schools.com/ 你好@yahoo.com test@莎士比亚.org 你好@莎士比亚.org Rδοκιμή@παράδειγμα.δοκιμή 管理员@中国互联网络信息中心.中国 你好@yahoo.com test@莎士比亚.org 你好@莎士比亚.org" + var finalRD = ["fu-berlin.de", "google.de", "nljbjkjk.de", "w3schools.com", "w2schools.com", "w8schools.com", "w33schools.com", "w22schools.com", "w99schools.com"] + + // Setup for critical pattern of a root domain + var testmail = "fu-ber_lin.de google-.de nljbj#kjk.de 3schools.com w2sc..hools.com w8sch?ools.com w33sc_hools.com w22schooIs.com W99schools$.com fu-berlin.de google.de nljbjkjk.de w3schools.com w2schools.com w8schools.com w33schools.com w22schools.com w99schools.eu.com bob.alice@zedat.fu-berlin.de git.imp.fu-berlin.de google.de kljhl@hjkghgkhj.com nljbjkjk.de w3#schools.com w2schools.com w8schools.com w33schools.com w22schools.com w99schools.com 你好@yahoo.com test@莎士比亚.org 你好@莎士比亚.org Rδοκιμή@παράδειγμα.δοκιμή 管理员@中国互联网络信息中心.中国 你好@yahoo.com test@莎士比亚.org 你好@莎士比亚.org" + var finalunciticalrd = ["3schools.com", "fu-berlin.de", "google.de", "nljbjkjk.de", "w3schools.com", "w2schools.com", "w8schools.com", "w33schools.com", "w22schools.com", "w99schools.eu.com", "git.imp.fu-berlin.de", "google.de", "w2schools.com", "w8schools.com", "w33schools.com", "w22schools.com", "w99schools.com"] + // General TestText for all (needs to simulate a body of text from which to extract specific Strings var TestText = "blabla moep jkjlkj lkjkljknmbjks llhil k. jhkhkih. huhuhj! fsdf bob.alice@zedat.fu-berlin.de dfsf. jhjknjknjkh https://git.imp.fu-berlin.de/enzevalos/enzevalos_iphone/issues/240 hjkhjkhkhn www.google.de kljhl@hjkghgkhj.com nljbjkjk.de url tag html mail <a href='https://www.w3schools.com'>Visit W3Schools</a> hjhkhiuhziu kjhkl <a href=\"https://www.w2schools.com\">Visit W2Schools</a>. lknljnlk. kmm /n lmölmpöl < a href=\"https://www.w8schools.com\">Visit W8Schools</a> gfg fghfghnhg <a href=\"https://www.w33schools.com\">Visit W33Schools</a> nkjhjkhkjn,mn jnmnklmj j <a href=\"https://www.w22schools.com\">Visit W22Schools</ a> hghjcfgh hfgchnvhj vgjcgj cjghcj <a href=\"https://www.w99schools.com\">Visit W99Schools</a > 你好@yahoo.com eewfve test@莎士比亚.org 你好@莎士比亚.org Rδοκιμή@παράδειγμα.δοκιμή or 管理员@中国互联网络信息中心.中国 你好45@yahoo.com" - // setup for find URLs in text + // Setup for find URLs in text var finalURLs = ["https://git.imp.fu-berlin.de/enzevalos/enzevalos_iphone/issues/240", "http://www.google.de", "http://nljbjkjk.de", "https://www.w3schools.com", "https://www.w2schools.com", "https://www.w8schools.com", "https://www.w33schools.com", "https://www.w22schools.com", "https://www.w99schools.com"] - // setup for tag tests + // Setup for tag tests var finalTestTextFindHtmlTags = ["<a href=\'https://www.w3schools.com\'>Visit W3Schools</a>", "<a href=\"https://www.w2schools.com\">Visit W2Schools</a>", "< a href=\"https://www.w8schools.com\">Visit W8Schools</a>", "<a href=\"https://www.w33schools.com\">Visit W33Schools</a>", "<a href=\"https://www.w22schools.com\">Visit W22Schools</ a>", "<a href=\"https://www.w99schools.com\">Visit W99Schools</a >"] var finalTestTextHtmlTagURL = ["https://www.w3schools.com", "https://www.w2schools.com", "https://www.w8schools.com", "https://www.w33schools.com", "https://www.w22schools.com", "https://www.w99schools.com"] @@ -24,6 +37,7 @@ class UrlStringExtensionTests: XCTestCase { var finalTestTexthttpTagLinkName = ["Visit W3Schools", "Visit W2Schools", "Visit W8Schools", "Visit W33Schools", "Visit W22Schools", "Visit W99Schools"] var testUrl = "www.google.de" + var testURL2 = "https://www.mycampus.imp.fu-berlin.de/portal" override func setUp() { super.setUp() @@ -79,4 +93,53 @@ class UrlStringExtensionTests: XCTestCase { XCTAssertNotNil(testUrl.url2ip()) //It changes often therefore a direct comparisson is not useful } + + // Test get Root Domain + func testGetRD() { + let rd = testMailText.getRD() + print(rd) + } + + // Test get Second Level Domain + func testGetSLD(){ + let sld = TestText.getSLD() + XCTAssertNotNil(sld) + XCTAssertEqual(sld[0], finalSLD[0]) + XCTAssertEqual(sld[1], finalSLD[1]) + XCTAssertEqual(sld[2], finalSLD[2]) + XCTAssertEqual(sld[3], finalSLD[3]) + } + + func testIsAllowedDistance() { + let str = "google" + // #distance: 0 1 2 3 4 < + let strArray = ["google", "googIe", "gogIe", "gooogIIe", "go", "amazon"] + XCTAssertTrue(str.isAllowedDistance(str: strArray[0], allowedEditDistance: 4)) + XCTAssertFalse(str.isAllowedDistance(str: strArray[1], allowedEditDistance: 4)) + XCTAssertFalse(str.isAllowedDistance(str: strArray[2], allowedEditDistance: 4)) + XCTAssertFalse(str.isAllowedDistance(str: strArray[3], allowedEditDistance: 6)) + XCTAssertTrue(str.isAllowedDistance(str: strArray[4], allowedEditDistance: 4)) + XCTAssertTrue(str.isAllowedDistance(str: strArray[5], allowedEditDistance: 4)) + } + + func testLevenshtein(){ + let str1 = "hallo" + let str2 = "hlalo" + let str3 = "haloo" + let str4 = "halo" + let str5 = "halllo" + let str6 = "halol" + let edd1 = str1.levenshtein(str2) + let edd2 = str1.levenshtein(str3) + let edd3 = str1.levenshtein(str4) + let edd4 = str1.levenshtein(str5) + let edd5 = str1.levenshtein(str6) + XCTAssertEqual(edd1, 2, "Wrong distance \(str1) vs \(str2)") + XCTAssertEqual(edd2, 2, "Wrong distance \(str1) vs \(str3)") + XCTAssertEqual(edd3, 1, "Wrong distance \(str1) vs \(str4)") + XCTAssertEqual(edd4, 1, "Wrong distance \(str1) vs \(str5)") + XCTAssertEqual(edd5, 2, "Wrong distance \(str1) vs \(str6)") + } + } +