filter undisplayable text

// FREEBIE
This commit is contained in:
Michael Kirk 2017-03-09 01:36:09 -05:00
parent a9b722ae16
commit 3e651fb8df
6 changed files with 110 additions and 4 deletions

View File

@ -45,6 +45,7 @@
452ECA4D1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */; };
452ECA4E1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */; };
4531C9C41DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 4531C9C31DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.m */; };
453201251E71100C00F20761 /* DisplayableTextFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 453201241E71100C00F20761 /* DisplayableTextFilter.swift */; };
45387B041E36D650005D00B3 /* OWS102MoveLoggingPreferenceToUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 45387B031E36D650005D00B3 /* OWS102MoveLoggingPreferenceToUserDefaults.m */; };
453D28B71D32BA5F00D523F0 /* OWSDisplayedMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B61D32BA5F00D523F0 /* OWSDisplayedMessage.m */; };
453D28BA1D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */; };
@ -120,6 +121,8 @@
45E1F3A31DEF1DF000852CF1 /* NoSignalContactsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 45E1F3A21DEF1DF000852CF1 /* NoSignalContactsView.xib */; };
45E1F3A51DEF20A100852CF1 /* NoSignalContactsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E1F3A41DEF20A100852CF1 /* NoSignalContactsView.swift */; };
45E2E9201E153B3D00457AA0 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E2E91F1E153B3D00457AA0 /* Strings.swift */; };
45E7A6A81E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E7A6A61E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift */; };
45E7A6A91E71CC2E00D44FB5 /* DisplayableTextFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 453201241E71100C00F20761 /* DisplayableTextFilter.swift */; };
45EB32CF1D7465C900735B2E /* OWSLinkedDevicesTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 45EB32CE1D7465C900735B2E /* OWSLinkedDevicesTableViewController.m */; };
45F170AC1E2F0351003FC1F2 /* CallAudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */; };
45F170AD1E2F0351003FC1F2 /* CallAudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */; };
@ -653,6 +656,7 @@
452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageFetcherJob.swift; path = Jobs/MessageFetcherJob.swift; sourceTree = "<group>"; };
4531C9C21DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JSQMessagesCollectionViewCell+OWS.h"; sourceTree = "<group>"; };
4531C9C31DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "JSQMessagesCollectionViewCell+OWS.m"; sourceTree = "<group>"; };
453201241E71100C00F20761 /* DisplayableTextFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayableTextFilter.swift; sourceTree = "<group>"; };
45387B021E36D650005D00B3 /* OWS102MoveLoggingPreferenceToUserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWS102MoveLoggingPreferenceToUserDefaults.h; path = Migrations/OWS102MoveLoggingPreferenceToUserDefaults.h; sourceTree = "<group>"; };
45387B031E36D650005D00B3 /* OWS102MoveLoggingPreferenceToUserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWS102MoveLoggingPreferenceToUserDefaults.m; path = Migrations/OWS102MoveLoggingPreferenceToUserDefaults.m; sourceTree = "<group>"; };
453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -734,6 +738,7 @@
45E282DF1D08E6CC00ADD4C8 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = translations/id.lproj/Localizable.strings; sourceTree = "<group>"; };
45E2E91E1E13EE3500457AA0 /* OWSCallNotificationsAdaptee.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSCallNotificationsAdaptee.h; path = UserInterface/OWSCallNotificationsAdaptee.h; sourceTree = "<group>"; };
45E2E91F1E153B3D00457AA0 /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Strings.swift; path = UserInterface/Strings.swift; sourceTree = "<group>"; };
45E7A6A61E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayableTextFilterTest.swift; sourceTree = "<group>"; };
45EB32CD1D7465C900735B2E /* OWSLinkedDevicesTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSLinkedDevicesTableViewController.h; sourceTree = "<group>"; };
45EB32CE1D7465C900735B2E /* OWSLinkedDevicesTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSLinkedDevicesTableViewController.m; sourceTree = "<group>"; };
45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallAudioSession.swift; sourceTree = "<group>"; };
@ -2009,6 +2014,7 @@
450DF2041E0D74AC003D14BE /* Platform.swift */,
45F170D51E315310003FC1F2 /* Weak.swift */,
45F170CB1E310E22003FC1F2 /* WeakTimer.swift */,
453201241E71100C00F20761 /* DisplayableTextFilter.swift */,
);
path = util;
sourceTree = "<group>";
@ -2402,6 +2408,7 @@
B660F6A21C29868000687D6E /* util */ = {
isa = PBXGroup;
children = (
45E7A6A61E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift */,
B660F6A31C29868000687D6E /* ConversionsTest.h */,
B660F6A41C29868000687D6E /* ConversionsTest.m */,
B660F6A51C29868000687D6E /* Crc32Test.h */,
@ -3132,6 +3139,7 @@
B67ADDC41989FF8700E1A773 /* RPServerRequestsManager.m in Sources */,
348F3A4F1E4A533900750D44 /* CallInterstitialViewController.swift in Sources */,
EF764C351DB67CC5000D9A87 /* UIViewController+CameraPermissions.m in Sources */,
453201251E71100C00F20761 /* DisplayableTextFilter.swift in Sources */,
76EB059418170B33006006FC /* HttpManager.m in Sources */,
45CD81EF1DC030E7004C9430 /* AccountManager.swift in Sources */,
76EB05EC18170B33006006FC /* CallState.m in Sources */,
@ -3422,6 +3430,7 @@
B660F7391C29988E00687D6E /* DH3KKeyAgreementProtocol.m in Sources */,
B660F73A1C29988E00687D6E /* EC25KeyAgreementParticipant.m in Sources */,
B660F73B1C29988E00687D6E /* EC25KeyAgreementProtocol.m in Sources */,
45E7A6A91E71CC2E00D44FB5 /* DisplayableTextFilter.swift in Sources */,
B660F73C1C29988E00687D6E /* EvpKeyAgreement.m in Sources */,
451DE9FE1DC1A28200810E42 /* SyncPushTokensJob.swift in Sources */,
B660F73D1C29988E00687D6E /* HashChain.m in Sources */,
@ -3546,6 +3555,7 @@
B660F6D71C29868000687D6E /* Crc32Test.m in Sources */,
B660F6C51C29868000687D6E /* HashChainTest.m in Sources */,
B660F6D01C29868000687D6E /* DecayingSampleEstimatorTest.m in Sources */,
45E7A6A81E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift in Sources */,
B660F6C91C29868000687D6E /* ZrtpTest.m in Sources */,
B660F6D11C29868000687D6E /* EventWindowTest.m in Sources */,
B660F6BF1C29868000687D6E /* IpEndPointTest.m in Sources */,

View File

@ -14,6 +14,7 @@
#import "TSIncomingMessage.h"
#import "TSInfoMessage.h"
#import "TSOutgoingMessage.h"
#import "Signal-Swift.h"
#import <MobileCoreServices/MobileCoreServices.h>
@ -162,6 +163,11 @@
NSStringFromClass([attachment class]));
}
}
} else { // no attachment, plain text message
if ([[DisplayableTextFilter new] shouldPreventDisplayOfText:adapter.messageBody]) {
adapter.messageType = TSInfoMessageAdapter;
adapter.messageBody = NSLocalizedString(@"INFO_MESSAGE_UNABLE_TO_DISPLAY_MESSAGE", @"Generic error text when message contents are undisplayable");
}
}
} else if ([interaction isKindOfClass:[TSCall class]]) {
TSCall *callRecord = (TSCall *)interaction;
@ -352,4 +358,16 @@
return NO;
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end

View File

@ -0,0 +1,41 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
import Foundation
@objc class DisplayableTextFilter: NSObject {
// don't bother filtering on small text, lest we inadvertently catch legitimate usage of rare code point stacking
let allowAnyTextLessThanByteSize: Int
convenience override init() {
self.init(allowAnyTextLessThanByteSize: 10000)
}
required init(allowAnyTextLessThanByteSize: Int) {
self.allowAnyTextLessThanByteSize = allowAnyTextLessThanByteSize
}
@objc(shouldPreventDisplayOfText:)
func shouldPreventDisplay(text: String?) -> Bool {
guard let text = text else {
return false
}
let byteCount = text.lengthOfBytes(using: .utf8)
guard byteCount >= allowAnyTextLessThanByteSize else {
return false
}
let characterCount = text.characters.count
// discard any zalgo style text, which we detect by enforcing avg bytes per character ratio.
if byteCount / characterCount > 10 {
return true
} else {
Logger.warn("filtering undisplayable text bytes: \(byteCount), characterCount: \(characterCount)")
return false
}
}
}

View File

@ -1,5 +1,6 @@
// Created by Dylan Bourgeois on 27/10/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "InboxTableViewCell.h"
#import "Environment.h"
@ -9,6 +10,7 @@
#import "TSGroupThread.h"
#import "TSMessagesManager.h"
#import "Util.h"
#import "Signal-Swift.h"
#import <JSQMessagesViewController/JSQMessagesAvatarImageFactory.h>
#import <JSQMessagesViewController/UIImage+JSQMessages.h>
@ -61,7 +63,12 @@ NS_ASSUME_NONNULL_BEGIN
}
UIImage *avatar = [OWSAvatarBuilder buildImageForThread:thread contactsManager:contactsManager];
self.threadId = thread.uniqueId;
NSString *snippetLabel = thread.lastMessageLabel;
NSString *snippetLabel;
if ([[DisplayableTextFilter new] shouldPreventDisplayOfText:thread.lastMessageLabel]) {
snippetLabel = NSLocalizedString(@"INFO_MESSAGE_UNABLE_TO_DISPLAY_MESSAGE", @"Generic error text when message contents are undisplayable");
} else {
snippetLabel = thread.lastMessageLabel;
}
NSAttributedString *attributedDate = [self dateAttributedString:thread.lastMessageDate];
NSUInteger unreadCount = [[TSMessagesManager sharedManager] unreadMessagesInThread:thread];

View File

@ -0,0 +1,27 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
import XCTest
class DisplayableTextFilterTest: XCTestCase {
override func setUp() {
super.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.
super.tearDown()
}
func testFiltering() {
// Ignore default byte size limitations to test other filtering behaviors
let filter = DisplayableTextFilter(allowAnyTextLessThanByteSize: 0)
XCTAssertFalse( filter.shouldPreventDisplay(text: "normal text") )
XCTAssertFalse( filter.shouldPreventDisplay(text: "🇹🇹🌼🇹🇹🌼🇹🇹") )
XCTAssertTrue( filter.shouldPreventDisplay(text: "L̷̳͔̲͝Ģ̵̮̯̤̩̙͍̬̟͉̹̘̹͍͈̮̦̰̣͟͝O̶̴̮̻̮̗͘͡!̴̷̟͓͓") )
}
}

View File

@ -403,6 +403,9 @@
/* No comment provided by engineer. */
"INCOMING_INCOMPLETE_CALL" = "Incomplete incoming call from";
/* Generic error text when message contents are undisplayable */
"INFO_MESSAGE_UNABLE_TO_DISPLAY_MESSAGE" = "Unable to display message.";
/* Text for button at the top of the contact picker */
"INVITE_FRIENDS_CONTACT_TABLE_BUTTON" = "Invite Friends to Signal";
@ -946,7 +949,7 @@
/* No comment provided by engineer. */
"UPDATE_BUTTON_TITLE" = "Update";
/* Description of CallKit to upgrading (existing) users. Space is tight, try to keep this as short as the English */
/* Description of CallKit to upgrading (existing) users */
"UPGRADE_EXPERIENCE_CALLKIT_DESCRIPTION" = "Answering calls from your lock screen is easy with iOS call integration. We anonymize your caller by default, so it's private too.";
/* button label shown once when when user upgrades app, in context of call kit */