diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 6c65d4e83..93aca560e 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -114,4 +114,4 @@ after_pipeline: jobs: - name: Publish Results commands: - - test-results gen-pipeline-report + - test-results gen-pipeline-report \ No newline at end of file diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index 06f1b2b1a..2f867087b 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -420,6 +420,7 @@ D2FD0F692453245E00259FF0 /* Either.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FD0F682453245E00259FF0 /* Either.swift */; }; D2FF6966243115EC007182F0 /* SetupImapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FF6965243115EC007182F0 /* SetupImapViewController.swift */; }; D2FF6968243115F9007182F0 /* SetupImapViewDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FF6967243115F9007182F0 /* SetupImapViewDecorator.swift */; }; + D741F9B22CA5661C00E1CAFF /* SecurityWarningNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D741F9B12CA5661400E1CAFF /* SecurityWarningNode.swift */; }; F191F621272511790053833E /* BlurViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F191F620272511790053833E /* BlurViewController.swift */; }; F8678DCC2722143300BB1710 /* GmailService+draft.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8678DCB2722143300BB1710 /* GmailService+draft.swift */; }; F8A72FA12729F82800E4BCAB /* DraftGatewayMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A72FA02729F82800E4BCAB /* DraftGatewayMock.swift */; }; @@ -886,6 +887,7 @@ D2FD0F682453245E00259FF0 /* Either.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Either.swift; sourceTree = ""; }; D2FF6965243115EC007182F0 /* SetupImapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupImapViewController.swift; sourceTree = ""; }; D2FF6967243115F9007182F0 /* SetupImapViewDecorator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupImapViewDecorator.swift; sourceTree = ""; }; + D741F9B12CA5661400E1CAFF /* SecurityWarningNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurityWarningNode.swift; sourceTree = ""; }; E26D5E20275AA417007B8802 /* BundleExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleExtensions.swift; sourceTree = ""; }; F191F620272511790053833E /* BlurViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurViewController.swift; sourceTree = ""; }; F8678DCB2722143300BB1710 /* GmailService+draft.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GmailService+draft.swift"; sourceTree = ""; }; @@ -2212,6 +2214,7 @@ D2A1D3D123FD9A9E00D626D6 /* Cell Nodes */ = { isa = PBXGroup; children = ( + D741F9B12CA5661400E1CAFF /* SecurityWarningNode.swift */, 9F8D5E61236B04E300186E43 /* CellNode.swift */, 9F1797652368EE50002BF770 /* SetupTitleNode.swift */, 5A39F433239EC61C001F4607 /* TitleCellNode.swift */, @@ -2904,6 +2907,7 @@ D2CDC3D72404704D002B045F /* RecipientEmailsCellNode.swift in Sources */, 5165ABCC27B526D100CCC379 /* RecipientEmailTextFieldNode.swift in Sources */, 51C56BE82901867D00610D12 /* ENSideMenu.swift in Sources */, + D741F9B22CA5661C00E1CAFF /* SecurityWarningNode.swift in Sources */, 955475FB2B0650AC00F52076 /* WebNode.swift in Sources */, D2717752242567EB00BDA9A9 /* KeyTextCellNode.swift in Sources */, 511D07E12769FBBA0050417B /* MessageActionCellNode.swift in Sources */, diff --git a/FlowCrypt/Controllers/Threads/ThreadDetailsViewController+TableView.swift b/FlowCrypt/Controllers/Threads/ThreadDetailsViewController+TableView.swift index 2e2b2479d..a8ea7e780 100644 --- a/FlowCrypt/Controllers/Threads/ThreadDetailsViewController+TableView.swift +++ b/FlowCrypt/Controllers/Threads/ThreadDetailsViewController+TableView.swift @@ -27,7 +27,8 @@ extension ThreadDetailsViewController: ASTableDelegate, ASTableDataSource { let processedMessage = input[section - 1].processedMessage let attachmentsCount = processedMessage?.attachments.count ?? 0 let pubkeysCount = processedMessage?.keyDetails.count ?? 0 - return Parts.allCases.count + attachmentsCount + pubkeysCount + let securityWarningBlockCount = processedMessage?.message.isSuspicious == true ? 1 : 0 + return Parts.allCases.count + attachmentsCount + pubkeysCount + securityWarningBlockCount } // swiftlint:disable cyclomatic_complexity function_body_length @@ -65,10 +66,16 @@ extension ThreadDetailsViewController: ASTableDelegate, ASTableDataSource { } } + if message.rawMessage.isSuspicious, row == 1 { + return SecurityWarningNode() + } + guard message.isExpanded, let processedMessage = message.processedMessage else { return self.dividerNode(indexPath: indexPath) } - guard row > 1 else { + let securityWarningBlockCount = message.rawMessage.isSuspicious == true ? 1 : 0 + + guard row > 1 + securityWarningBlockCount else { if processedMessage.text.isHTMLString { return ThreadDetailWebNode( input: .init(message: processedMessage.text, quote: processedMessage.quote, index: messageIndex) @@ -85,7 +92,7 @@ extension ThreadDetailsViewController: ASTableDelegate, ASTableDataSource { } let keyCount = processedMessage.keyDetails.count - let keyIndex = row - 2 + let keyIndex = row - 2 - securityWarningBlockCount if keyIndex < keyCount { let keyDetails = processedMessage.keyDetails[keyIndex] let node = PublicKeyDetailNode( @@ -97,7 +104,7 @@ extension ThreadDetailsViewController: ASTableDelegate, ASTableDataSource { return node } - let attachmentIndex = row - 2 - keyCount + let attachmentIndex = row - 2 - keyCount - securityWarningBlockCount if let attachment = processedMessage.attachments[safe: attachmentIndex] { return AttachmentNode( input: .init( diff --git a/FlowCrypt/Functionality/Mail Provider/Gmail/GmailService.swift b/FlowCrypt/Functionality/Mail Provider/Gmail/GmailService.swift index 23ec145e5..ed40e5c2c 100644 --- a/FlowCrypt/Functionality/Mail Provider/Gmail/GmailService.swift +++ b/FlowCrypt/Functionality/Mail Provider/Gmail/GmailService.swift @@ -60,5 +60,6 @@ extension String { static let bcc = "bcc" static let replyTo = "reply-to" static let inReplyTo = "in-reply-to" + static let receivedSPF = "received-spf" static let identifier = "message-id" } diff --git a/FlowCrypt/Functionality/Mail Provider/Message Provider/Gmail+MessageExtension.swift b/FlowCrypt/Functionality/Mail Provider/Message Provider/Gmail+MessageExtension.swift index 78c25099d..0b7d6a6de 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Provider/Gmail+MessageExtension.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Provider/Gmail+MessageExtension.swift @@ -44,6 +44,7 @@ extension Message { var replyTo: String? var inReplyTo: String? var rfc822MsgId: String? + var isSuspicious = false for messageHeader in messageHeaders.compactMap({ $0 }) { guard let name = messageHeader.name?.lowercased(), @@ -58,6 +59,7 @@ extension Message { case .bcc: bcc = value case .replyTo: replyTo = value case .inReplyTo: inReplyTo = value + case .receivedSPF: isSuspicious = value.contains("softfail") case .identifier: rfc822MsgId = value default: break } @@ -81,7 +83,8 @@ extension Message { cc: cc, bcc: bcc, replyTo: replyTo, - inReplyTo: inReplyTo + inReplyTo: inReplyTo, + isSuspicious: isSuspicious ) } } diff --git a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift index d16a2586e..a0c795e8b 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift @@ -28,6 +28,7 @@ struct Message: Hashable { let body: MessageBody let inReplyTo: String? let replyToMsgId: String? + let isSuspicious: Bool private(set) var labels: [MessageLabel] var isRead: Bool { @@ -83,7 +84,8 @@ struct Message: Hashable { bcc: String? = nil, replyTo: String? = nil, inReplyTo: String? = nil, - replyToMsgId: String? = nil + replyToMsgId: String? = nil, + isSuspicious: Bool = false ) { self.identifier = identifier self.date = date @@ -104,6 +106,7 @@ struct Message: Hashable { self.replyTo = Self.parseRecipients(replyTo) self.inReplyTo = inReplyTo self.replyToMsgId = replyToMsgId + self.isSuspicious = isSuspicious } } diff --git a/FlowCrypt/Resources/en.lproj/Localizable.strings b/FlowCrypt/Resources/en.lproj/Localizable.strings index 44b652fa0..4a1f4f3e0 100644 --- a/FlowCrypt/Resources/en.lproj/Localizable.strings +++ b/FlowCrypt/Resources/en.lproj/Localizable.strings @@ -72,6 +72,9 @@ "message_mark_read_error" = "Could not mark message as read: %@"; "message_reply_all" = "Reply all"; "message_not_found_in_folder" = "Message not found in folder: "; +"message_security_warning_subject" = "Potentially suspicious message"; +"message_security_warning_message" = "It wasn't properly verified by the sender, so its authenticity can't be confirmed. +Be careful - avoid clicking links and downloading attachments, or sharing personal info."; // Passphrase Anti BruteForce Protection "passphrase_anti_brute_force_protection_hint" = "To protect you and your data, the next attempt will only be possible after the timer below finishes. Please wait until then before trying again."; diff --git a/FlowCryptUI/Cell Nodes/SecurityWarningNode.swift b/FlowCryptUI/Cell Nodes/SecurityWarningNode.swift new file mode 100644 index 000000000..7a9b88812 --- /dev/null +++ b/FlowCryptUI/Cell Nodes/SecurityWarningNode.swift @@ -0,0 +1,43 @@ +// +// SecurityWarningNode.swift +// FlowCrypt +// +// Created by Ioan Moldovan on 9/26/24 +// Copyright © 2017-present FlowCrypt a. s. All rights reserved. +// + +import AsyncDisplayKit + +public final class SecurityWarningNode: CellNode { + + private lazy var subjectNode: ASTextNode2 = { + let textNode = ASTextNode2() + textNode.attributedText = "message_security_warning_subject".localized.attributed(.bold(18), color: .black, alignment: .left) + textNode.accessibilityIdentifier = "aid-security-warning-subject-node" + return textNode + }() + + private lazy var messageNode: ASTextNode2 = { + let textNode = ASTextNode2() + textNode.attributedText = "message_security_warning_message".localized.attributed(.regular(15), color: .black, alignment: .left) + textNode.accessibilityIdentifier = "aid-security-warning-message-node" + return textNode + }() + + override public init() { + super.init() + + backgroundColor = UIColor(hex: "FABD03") + } + + override public func layoutSpecThatFits(_: ASSizeRange) -> ASLayoutSpec { + let stack = ASStackLayoutSpec.vertical() + stack.children = [subjectNode, messageNode] + stack.spacing = 10 + + return ASInsetLayoutSpec( + insets: .deviceSpecificTextInsets(top: 16, bottom: 16), + child: stack + ) + } +} diff --git a/appium/api-mocks/apis/google/exported-messages/message-export-1922cd70bf323ea6.json b/appium/api-mocks/apis/google/exported-messages/message-export-1922cd70bf323ea6.json new file mode 100644 index 000000000..b00b39cf7 --- /dev/null +++ b/appium/api-mocks/apis/google/exported-messages/message-export-1922cd70bf323ea6.json @@ -0,0 +1,74 @@ +{ + "acctEmail": "e2e.enterprise.test@flowcrypt.com", + "full": { + "id": "1922cd70bf323ea6", + "threadId": "1922cd70bf323ea6", + "labelIds": ["CATEGORY_PERSONAL", "INBOX"], + "snippet": "test -Mart at FlowCrypt", + "payload": { + "partId": "", + "mimeType": "text/plain", + "filename": "", + "headers": [ + { + "name": "To", + "value": "flowcrypt.compatibility@gmail.com" + }, + { + "name": "Subject", + "value": "Test Spoofed email by Mart" + }, + { + "name": "From", + "value": "sender@domain.com" + }, + { + "name": "X-Priority", + "value": "3 (Normal)" + }, + { + "name": "Importance", + "value": "Normal" + }, + { + "name": "Received-SPF", + "value": "softfail (google.com: domain of transitioning hacker@gmail.com does not designate 0.0.0.0 as permitted sender) client-ip=0.0.0.0;" + }, + { + "name": "Errors-To", + "value": "hacker@gmail.com" + }, + { + "name": "Reply-To", + "value": "hacker@gmail.com" + }, + { + "name": "Content-Type", + "value": "text/plain; charset=utf-8" + }, + { + "name": "Date", + "value": "Thu, 26 Sep 2024 07:39:02 +0200 (CEST)" + } + ], + "body": { + "size": 28, + "data": "dGVzdA0KDQotTWFydCBhdCBGbG93Q3J5cHQNCg==" + } + }, + "sizeEstimate": 3183, + "historyId": "298166", + "internalDate": "1727329142000" + }, + "attachments": {}, + "raw": { + "id": "1922cd70bf323ea6", + "threadId": "1922cd70bf323ea6", + "labelIds": ["CATEGORY_PERSONAL", "SPAM"], + "snippet": "test -Mart at FlowCrypt", + "sizeEstimate": 3183, + "raw": "RGVsaXZlcmVkLVRvOiBpb2FuQGZsb3djcnlwdC5jb20NClJlY2VpdmVkOiBieSAyMDAyOmEwNTo2MzU5OjQ1OWE6YjA6MWJlOmI4N2E6NDZhNCB3aXRoIFNNVFAgaWQgbm8yNmNzcDE1MDk5MXJ3YjsNCiAgICAgICAgV2VkLCAyNSBTZXAgMjAyNCAyMjozOTowMyAtMDcwMCAoUERUKQ0KWC1Hb29nbGUtU210cC1Tb3VyY2U6IEFHSFQrSUhocFVnem42dE4ycnAyUDc0NkNKQ1JDZ1NXZXEyYndWdVFDNWFpcXFFSThOdGhhSTlER0JJb2V5V3VRTWFQeXVCOUV2WmoNClgtUmVjZWl2ZWQ6IGJ5IDIwMDI6YTE3OjkwYjozMTQ0OmIwOjJkODo3N2NjOjg1ZSB3aXRoIFNNVFAgaWQgOThlNjdlZDU5ZTFkMS0yZTA2YWZlYjc4ZW1yNjMzMTEzMGE5MS4zNy4xNzI3MzI5MTQzNTQ1Ow0KICAgICAgICBXZWQsIDI1IFNlcCAyMDI0IDIyOjM5OjAzIC0wNzAwIChQRFQpDQpBUkMtU2VhbDogaT0xOyBhPXJzYS1zaGEyNTY7IHQ9MTcyNzMyOTE0MzsgY3Y9bm9uZTsNCiAgICAgICAgZD1nb29nbGUuY29tOyBzPWFyYy0yMDI0MDYwNTsNCiAgICAgICAgYj1TOVRyZzluaUsyQURsQWQrWnYwZE9CbnhBVStvYlN5RkUyUkgvVFFucS9ZL2M2VmNCd3pkcmM4d3A4Y3BJL1ZUUWsNCiAgICAgICAgIDJjc3F6TEIzczRCQjBTMFB0U0pFd0VJTUUvd2J1SWtTUWl5RkpjOURsY3R2VFBST1RhSmNHc0xKTmQvWlBvRzhMQTJCDQogICAgICAgICBpZVJ6YlpsbWs2TmpKMFFPVURNZlVzYU8reXp0dzcwSmU1OWgwbjZQVUoyWTloNW5UdXFpYzFzbmpqZVc5b0dPcDF3Mg0KICAgICAgICAgWWJGQzFrK09GbjkyZElxeFZkNHZsNnp5blBrZytxSFoyQi9HSTE1ZHRjaFIyZFpXOEJlcWh3cU00TXIvdFFpVE9TQmgNCiAgICAgICAgIDRSdjBzTm1yam5LSVJCR2JPRWVBMHNMTndWam5kTWJjaDMwVGdIb1Y1NEoxakcyQVZnY1F0aDFTTjVuc0ovOVJoL0d3DQogICAgICAgICB6VWp3PT0NCkFSQy1NZXNzYWdlLVNpZ25hdHVyZTogaT0xOyBhPXJzYS1zaGEyNTY7IGM9cmVsYXhlZC9yZWxheGVkOyBkPWdvb2dsZS5jb207IHM9YXJjLTIwMjQwNjA1Ow0KICAgICAgICBoPWRhdGU6bWVzc2FnZS1pZDpyZXBseS10bzplcnJvcnMtdG86aW1wb3J0YW5jZTpmcm9tOnN1YmplY3Q6dG87DQogICAgICAgIGJoPWNXY3VTNmxuVTE4VTU4YTUwT3dqelFzMGEwK3FsNGxZbnBDSEZoZ3Yxalk9Ow0KICAgICAgICBmaD1yVjZaTXJhY21nOVg1TUdtam9YdVJ6YmZ4dGpEUldSZkEzWkZLQTNRZC9jPTsNCiAgICAgICAgYj1MaTRxMjRuNXUxajFPWEFqUG1JN0RDNlFXOVJTd2tSOUFjaHd6bEJkb0ZLbXYwdmpXclo3dVFQS2UyMUVGZnlJUVQNCiAgICAgICAgIFk5M3pQbWg5U1RacVFaV3FGTS9SaXJGTFlRRjV2UGpZTzdVRWpiSkoyS05lYlFtS3lNT1ZDN2UydlNxdzRsWVQ1bUh6DQogICAgICAgICAwUEpmbzNUK2cvWUE2UU0zbldmbjIzU1pONHRTS3hIU1c2VXh6N3F0S3FCNXFtQjJSalFrUFZod0FGMEw3SFEva0RWNA0KICAgICAgICAgWk1QaDVuMUR2SHdqWDlxRURWUmk1d3NQdGg4Y1NRbEY2dnFSdXZjZzhyVjdGajZnRzA1QVFyeFp2bTQ2TVVMU0t0ckINCiAgICAgICAgIG5NYjhQYmpreEdJUHdBQ1gyODNzbDRKWEh3Q1ZiTVdYRHEzeDZPck01a2tKdmxJcGdLd2l6UEhOOGs2cW9Ud1pmbHV2DQogICAgICAgICBvMjNnPT07DQogICAgICAgIGRhcmE9Z29vZ2xlLmNvbQ0KQVJDLUF1dGhlbnRpY2F0aW9uLVJlc3VsdHM6IGk9MTsgbXguZ29vZ2xlLmNvbTsNCiAgICAgICBzcGY9c29mdGZhaWwgKGdvb2dsZS5jb206IGRvbWFpbiBvZiB0cmFuc2l0aW9uaW5nIGhhY2tlckBnbWFpbC5jb20gZG9lcyBub3QgZGVzaWduYXRlIDExNC4yOS4yMzYuMjQ3IGFzIHBlcm1pdHRlZCBzZW5kZXIpIHNtdHAubWFpbGZyb209aGFja2VyQGdtYWlsLmNvbTsNCiAgICAgICBkbWFyYz1mYWlsIChwPU5PTkUgc3A9UVVBUkFOVElORSBkaXM9Tk9ORSkgaGVhZGVyLmZyb209Z21haWwuY29tDQpSZXR1cm4tUGF0aDogPGhhY2tlckBnbWFpbC5jb20-DQpSZWNlaXZlZDogZnJvbSBlbWtlaS5jeiAoZW1rZWkuY3ouIFsxMTQuMjkuMjM2LjI0N10pDQogICAgICAgIGJ5IG14Lmdvb2dsZS5jb20gd2l0aCBFU01UUFMgaWQgOThlNjdlZDU5ZTFkMS0yZTA2ZTJmMjQ2Y3NpMzAzNTc1NWE5MS4xNTkuMjAyNC4wOS4yNS4yMi4zOS4wMw0KICAgICAgICBmb3IgPGlvYW5AZmxvd2NyeXB0LmNvbT4NCiAgICAgICAgKHZlcnNpb249VExTMV8zIGNpcGhlcj1UTFNfQUVTXzI1Nl9HQ01fU0hBMzg0IGJpdHM9MjU2LzI1Nik7DQogICAgICAgIFdlZCwgMjUgU2VwIDIwMjQgMjI6Mzk6MDMgLTA3MDAgKFBEVCkNClJlY2VpdmVkLVNQRjogc29mdGZhaWwgKGdvb2dsZS5jb206IGRvbWFpbiBvZiB0cmFuc2l0aW9uaW5nIGhhY2tlckBnbWFpbC5jb20gZG9lcyBub3QgZGVzaWduYXRlIDExNC4yOS4yMzYuMjQ3IGFzIHBlcm1pdHRlZCBzZW5kZXIpIGNsaWVudC1pcD0xMTQuMjkuMjM2LjI0NzsNCkF1dGhlbnRpY2F0aW9uLVJlc3VsdHM6IG14Lmdvb2dsZS5jb207DQogICAgICAgc3BmPXNvZnRmYWlsIChnb29nbGUuY29tOiBkb21haW4gb2YgdHJhbnNpdGlvbmluZyBoYWNrZXJAZ21haWwuY29tIGRvZXMgbm90IGRlc2lnbmF0ZSAxMTQuMjkuMjM2LjI0NyBhcyBwZXJtaXR0ZWQgc2VuZGVyKSBzbXRwLm1haWxmcm9tPWhhY2tlckBnbWFpbC5jb207DQogICAgICAgZG1hcmM9ZmFpbCAocD1OT05FIHNwPVFVQVJBTlRJTkUgZGlzPU5PTkUpIGhlYWRlci5mcm9tPWdtYWlsLmNvbQ0KUmVjZWl2ZWQ6IGJ5IGVta2VpLmN6IChQb3N0Zml4LCBmcm9tIHVzZXJpZCAzMykNCglpZCA0NzU5QzFGMDk7IFRodSwgMjYgU2VwIDIwMjQgMDc6Mzk6MDIgKzAyMDAgKENFU1QpDQpUbzogaW9hbkBmbG93Y3J5cHQuY29tDQpTdWJqZWN0OiBUZXN0IFNwb29mZWQgZW1haWwgYnkgTWFydA0KRnJvbTogIkFkbWluaXN0cmF0b3IiIDxoYWNrZXJAZ21haWwuY29tPg0KWC1Qcmlvcml0eTogMyAoTm9ybWFsKQ0KSW1wb3J0YW5jZTogTm9ybWFsDQpFcnJvcnMtVG86IGhhY2tlckBnbWFpbC5jb20NClJlcGx5LVRvOiBoYWNrZXJAZ21haWwuY29tDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCk1lc3NhZ2UtSWQ6IDwyMDI0MDkyNjA1MzkwMi40NzU5QzFGMDlAZW1rZWkuY3o-DQpEYXRlOiBUaHUsIDI2IFNlcCAyMDI0IDA3OjM5OjAyICswMjAwIChDRVNUKQ0KDQp0ZXN0DQoNCi1NYXJ0IGF0IEZsb3dDcnlwdA0K", + "historyId": "298166", + "internalDate": "1727329142000" + } +} diff --git a/appium/api-mocks/apis/google/google-messages.ts b/appium/api-mocks/apis/google/google-messages.ts index 011b62476..db113ce5a 100644 --- a/appium/api-mocks/apis/google/google-messages.ts +++ b/appium/api-mocks/apis/google/google-messages.ts @@ -31,4 +31,5 @@ export type GoogleMockMessage = | 'Encrypted email with public key attached' | 'Email with another user public key attached' | 'Test remote images #2414' + | 'Test Spoofed email by Mart' | 'Test encrypted message sent via encrypted contact page with attachment'; diff --git a/appium/tests/screenobjects/email.screen.ts b/appium/tests/screenobjects/email.screen.ts index de1cdf295..86bd949c6 100644 --- a/appium/tests/screenobjects/email.screen.ts +++ b/appium/tests/screenobjects/email.screen.ts @@ -38,6 +38,8 @@ const SELECTORS = { TOGGLE_PUBLIC_KEY_NODE: '~aid-toggle-public-key-node', PUBLIC_KEY_VALUE: '~aid-public-key-value', IMPORT_PUBLIC_KEY_BUTTON: '~aid-import-key-button', + SECURITY_WARNING_SUBJECT: '~aid-security-warning-subject-node', + SECURITY_WARNING_MESSSAGE: '~aid-security-warning-message-node', }; class EmailScreen extends BaseScreen { @@ -173,6 +175,14 @@ class EmailScreen extends BaseScreen { return $(SELECTORS.ATTACHMENT_TEXT_VIEW); } + get securityWarningSubjectLabel() { + return $(SELECTORS.SECURITY_WARNING_SUBJECT); + } + + get securityWarningMessageLabel() { + return $(SELECTORS.SECURITY_WARNING_MESSSAGE); + } + checkEmailSender = async (sender: string, index = 0) => { const element = await this.senderEmail(index); await ElementHelper.waitElementVisible(element); @@ -204,6 +214,16 @@ class EmailScreen extends BaseScreen { } }; + checkSecurityWarningBlock = async () => { + await ElementHelper.waitForText(await this.securityWarningSubjectLabel, 'Potentially suspicious message'); + await ElementHelper.waitForText( + await this.securityWarningMessageLabel, + "It wasn't properly verified by the sender, so its authenticity can't be confirmed.", + 15000, + true, + ); + }; + checkOpenedEmail = async (email: string, subject: string, text: string, isHtml = false) => { await this.checkEmailSender(email); await this.checkEmailSubject(subject); diff --git a/appium/tests/specs/mock/inbox/CheckMessageProcessingErrors.spec.ts b/appium/tests/specs/mock/inbox/CheckMessageProcessingErrors.spec.ts index 9a4e6455d..23569697b 100644 --- a/appium/tests/specs/mock/inbox/CheckMessageProcessingErrors.spec.ts +++ b/appium/tests/specs/mock/inbox/CheckMessageProcessingErrors.spec.ts @@ -29,6 +29,7 @@ describe('INBOX: ', () => { const notIntegrityProtectedSender = CommonData.notIntegrityProtected.senderName; const notIntegrityProtectedText = CommonData.notIntegrityProtected.message; + const spoofedEmailSubject = 'Test Spoofed email by Mart'; const keyMismatchSubject = CommonData.keyMismatch.subject; const keyMismatchName = CommonData.keyMismatch.senderName; const keyMismatchText = CommonData.keyMismatch.message; @@ -49,6 +50,7 @@ describe('INBOX: ', () => { 'wrong checksum', 'not integrity protected - should show a warning and not decrypt automatically', 'key mismatch unexpectedly produces a modal', + spoofedEmailSubject, ], }); mockApi.attesterConfig = { @@ -83,6 +85,11 @@ describe('INBOX: ', () => { await EmailScreen.clickBackButton(); await MailFolderScreen.checkInboxScreen(); + // Check spoofed email to see if security warning block exists + await MailFolderScreen.clickOnEmailBySubject(spoofedEmailSubject); + await EmailScreen.checkSecurityWarningBlock(); + await EmailScreen.clickBackButton(); + // Checking error for wrong checksum message await MailFolderScreen.clickOnEmailBySubject(wrongChecksumSubject); await EmailScreen.checkOpenedEmail(wrongChecksumName, wrongChecksumSubject, wrongChecksumText);