From b002c0c9e178ffaee9a1253ef8ee24f2c0919659 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 23 Jan 2019 11:49:27 -0500 Subject: [PATCH] Refine link parsing and validation logic. --- .../Interactions/OWSLinkPreview.swift | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift b/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift index 00ffb6167..a09cf3933 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift +++ b/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift @@ -120,7 +120,7 @@ public class OWSLinkPreview: MTLModel { Logger.error("Discarding link preview; message has attachments.") throw LinkPreviewError.invalidInput } - let urlString = previewProto.url + let urlString = stripPossibleLinkUrl(previewProto.url) guard URL(string: urlString) != nil else { Logger.error("Could not parse preview URL.") @@ -265,7 +265,7 @@ public class OWSLinkPreview: MTLModel { let maxCharacterCount = 2048 if result.count > maxCharacterCount { let endIndex = result.index(result.startIndex, offsetBy: maxCharacterCount) - result = String(result[...endIndex]) + result = String(result[.. 0 else { + owsFailDebug("Invalid url (empty path).") + return nil + } guard let result = whitelistedDomain(forUrl: url, domainWhitelist: OWSLinkPreview.linkDomainWhitelist) else { owsFailDebug("Missing domain.") @@ -315,6 +319,16 @@ public class OWSLinkPreview: MTLModel { return result } + private class func stripPossibleLinkUrl(_ urlString: String) -> String { + var result = urlString.ows_stripped() + let suffixToStrip = "," + while result.hasSuffix(suffixToStrip) { + let endIndex = result.index(result.endIndex, offsetBy: -suffixToStrip.count) + result = String(result[.. Bool { guard let url = URL(string: urlString) else { @@ -396,11 +410,14 @@ public class OWSLinkPreview: MTLModel { } let components = body.components(separatedBy: .whitespacesAndNewlines) for component in components { - if isValidLinkUrl(component) { - previewUrlCache.setObject(component as AnyObject, forKey: body as AnyObject) - return component + let urlString = stripPossibleLinkUrl(component) + if isValidLinkUrl(urlString) { + previewUrlCache.setObject(urlString as AnyObject, forKey: body as AnyObject) + return urlString } } + // Use empty string to indicate "no preview URL" in the cache. + previewUrlCache.setObject("" as AnyObject, forKey: body as AnyObject) return nil } @@ -546,7 +563,7 @@ public class OWSLinkPreview: MTLModel { } var title: String? - if let rawTitle = NSRegularExpression.parseFirstMatch(pattern: "", text: linkText) { + if let rawTitle = NSRegularExpression.parseFirstMatch(pattern: "", text: linkText) { if let decodedTitle = decodeHTMLEntities(inString: rawTitle) { let normalizedTitle = OWSLinkPreview.normalizeTitle(title: decodedTitle) if normalizedTitle.count > 0 { @@ -557,12 +574,16 @@ public class OWSLinkPreview: MTLModel { Logger.verbose("title: \(String(describing: title))") - guard let rawImageUrlString = NSRegularExpression.parseFirstMatch(pattern: "", text: linkText) else { + guard let rawImageUrlString = NSRegularExpression.parseFirstMatch(pattern: "", text: linkText) else { return completion(OWSLinkPreviewDraft(urlString: linkUrlString, title: title)) } guard let imageUrlString = decodeHTMLEntities(inString: rawImageUrlString)?.ows_stripped() else { return completion(OWSLinkPreviewDraft(urlString: linkUrlString, title: title)) } + guard isValidMediaUrl(imageUrlString) else { + Logger.error("Invalid image URL.") + return completion(OWSLinkPreviewDraft(urlString: linkUrlString, title: title)) + } Logger.verbose("imageUrlString: \(imageUrlString)") guard let imageUrl = URL(string: imageUrlString) else { Logger.error("Could not parse image URL.")