@@ -42,6 +42,7 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
42
42
43
43
let mutableAttributedString = NSMutableAttributedString ( string: string)
44
44
removeDefaultForegroundColors ( mutableAttributedString)
45
+ detectPhishingAttempts ( mutableAttributedString)
45
46
addLinksAndMentions ( mutableAttributedString)
46
47
detectPermalinks ( mutableAttributedString)
47
48
@@ -98,6 +99,7 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
98
99
99
100
let mutableAttributedString = NSMutableAttributedString ( attributedString: attributedString)
100
101
removeDefaultForegroundColors ( mutableAttributedString)
102
+ detectPhishingAttempts ( mutableAttributedString)
101
103
addLinksAndMentions ( mutableAttributedString)
102
104
replaceMarkedBlockquotes ( mutableAttributedString)
103
105
replaceMarkedCodeBlocks ( mutableAttributedString)
@@ -135,6 +137,23 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
135
137
attributedString. removeAttribute ( . foregroundColor, range: . init( location: 0 , length: attributedString. length) )
136
138
}
137
139
140
+ private func detectPhishingAttempts( _ attributedString: NSMutableAttributedString ) {
141
+ attributedString. enumerateAttribute ( . link, in: . init( location: 0 , length: attributedString. length) , options: [ ] ) { value, range, _ in
142
+ guard value != nil , let internalURL = value as? URL else {
143
+ return
144
+ }
145
+
146
+ let linkString = attributedString. attributedSubstring ( from: range) . string
147
+ guard MatrixEntityRegex . linkRegex. firstMatch ( in: linkString) != nil ,
148
+ // To avoid false positives like [Matrix.org](https://matrix.org)
149
+ sanitizeLink ( linkString) . lowercased ( ) != sanitizeLink ( internalURL. absoluteString) . lowercased ( ) else {
150
+ return
151
+ }
152
+
153
+ handlePhishingAttempt ( for: attributedString, in: range, internalURL: internalURL, externalURL: linkString)
154
+ }
155
+ }
156
+
138
157
// swiftlint:disable:next cyclomatic_complexity
139
158
private func addLinksAndMentions( _ attributedString: NSMutableAttributedString ) {
140
159
let string = attributedString. string
@@ -177,19 +196,7 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
177
196
return nil
178
197
}
179
198
180
- var link = String ( string [ matchRange] )
181
-
182
- if !link. contains ( " :// " ) {
183
- link. insert ( contentsOf: " https:// " , at: link. startIndex)
184
- }
185
-
186
- // Don't include punctuation characters at the end of links
187
- // e.g `https://element.io/blog:` <- which is a valid link but the wrong place
188
- while !link. isEmpty,
189
- link. rangeOfCharacter ( from: . punctuationCharacters, options: . backwards) ? . upperBound == link. endIndex {
190
- link = String ( link. dropLast ( ) )
191
- }
192
-
199
+ let link = sanitizeLink ( String ( string [ matchRange] ) )
193
200
return TextParsingMatch ( type: . link( urlString: link) , range: match. range)
194
201
} )
195
202
@@ -278,7 +285,8 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
278
285
func detectPermalinks( _ attributedString: NSMutableAttributedString ) {
279
286
attributedString. enumerateAttribute ( . link, in: . init( location: 0 , length: attributedString. length) , options: [ ] ) { value, range, _ in
280
287
if value != nil {
281
- if let url = value as? URL , let matrixEntity = parseMatrixEntityFrom ( uri: url. absoluteString) {
288
+ if let url = value as? URL ,
289
+ let matrixEntity = parseMatrixEntityFrom ( uri: url. absoluteString) {
282
290
switch matrixEntity. id {
283
291
case . user( let userID) :
284
292
mentionBuilder. handleUserMention ( for: attributedString, in: range, url: url, userID: userID, userDisplayName: nil )
@@ -303,6 +311,42 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
303
311
}
304
312
}
305
313
314
+ private func sanitizeLink( _ string: String ) -> String {
315
+ var link = string
316
+ if !link. contains ( " :// " ) {
317
+ link. insert ( contentsOf: " https:// " , at: link. startIndex)
318
+ }
319
+
320
+ // Don't include punctuation characters at the end of links
321
+ // e.g `https://element.io/blog:` <- which is a valid link but the wrong place
322
+ while !link. isEmpty,
323
+ link. rangeOfCharacter ( from: . punctuationCharacters, options: . backwards) ? . upperBound == link. endIndex {
324
+ link = String ( link. dropLast ( ) )
325
+ }
326
+
327
+ return link
328
+ }
329
+
330
+ private func handlePhishingAttempt( for attributedString: NSMutableAttributedString ,
331
+ in range: NSRange ,
332
+ internalURL: URL ,
333
+ externalURL: String ) {
334
+ // Let's remove the existing link attribute
335
+ attributedString. removeAttribute ( . link, range: range)
336
+
337
+ var urlComponents = URLComponents ( )
338
+ urlComponents. scheme = URL . confirmationScheme
339
+ urlComponents. host = " "
340
+ let parameters = ConfirmURLParameters ( internalURL: internalURL, externalURL: externalURL)
341
+ urlComponents. queryItems = parameters. urlQueryItems
342
+
343
+ guard let finalURL = urlComponents. url else {
344
+ return
345
+ }
346
+
347
+ attributedString. addAttribute ( . link, value: finalURL, range: range)
348
+ }
349
+
306
350
private func removeDTCoreTextArtifacts( _ attributedString: NSMutableAttributedString ) {
307
351
guard attributedString. length > 0 else {
308
352
return
0 commit comments