diff --git a/.gitignore b/.gitignore index ee47f7b..bf8bd3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.xcscheme *.xcuserstate -KnockKnock.xcodeproj/xcuserdata/* \ No newline at end of file +KnockKnock.xcodeproj/xcuserdata/* +Carthage/Build +Carthage/Checkouts diff --git a/AppDelegate.h b/AppDelegate.h index fc5d30b..e68f2c2 100755 --- a/AppDelegate.h +++ b/AppDelegate.h @@ -39,7 +39,10 @@ extern Filter* itemFilter; /* PROPERTIES */ //friends -@property (weak) IBOutlet NSWindow *friends; +@property (weak) IBOutlet NSWindow* friends; + +//close button +@property (weak) IBOutlet NSButton* closeButton; //flag for secondary scan // ->need to restart shared enumerator diff --git a/AppDelegate.m b/AppDelegate.m index f93b684..264dc40 100755 --- a/AppDelegate.m +++ b/AppDelegate.m @@ -108,6 +108,9 @@ -(void)applicationDidFinishLaunching:(NSNotification *)notification //show friends window [self.friends makeKeyAndOrderFront:self]; + //then make action button first responder + [self.friends makeFirstResponder:self.closeButton]; + //close after a few seconds dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ @@ -246,7 +249,7 @@ -(void)requestFullDiskAcces infoAlert.messageText = @"Open 'System Preferences' to give KnockKnock Full Disk Access?"; //detailed test - infoAlert.informativeText = @"This allows the app to perform a comprehensive scan.\n\nIn System Preferences:\r β–ͺ click the πŸ”’ to authenticate\r β–ͺ click the βž• to add KnockKnock.app\n"; + infoAlert.informativeText = @"This allows the app to perform a comprehensive scan.\n\nIn System Preferences:\r β–ͺ Click the πŸ”’ to authenticate\r β–ͺ Click the βž• to add KnockKnock.app\n"; //ok button [infoAlert addButtonWithTitle:@"OK"]; @@ -481,7 +484,7 @@ -(void)scan self.statusText.hidden = NO; //update - [self.statusText setStringValue:[NSString stringWithFormat:@"scanning %@", plugin.name]]; + [self.statusText setStringValue:[NSString stringWithFormat:@"Scanning: %@", plugin.name]]; }); @@ -694,28 +697,18 @@ -(void)itemsProcessed:(PluginBase*)plugin // ->reload category table (to trigger title turning red) if(0 != plugin.flaggedItems.count) { - //execute on main (UI) thread - dispatch_sync(dispatch_get_main_queue(), ^{ - - //reload category table - [self.categoryTableController customReload]; - - }); + //reload category table + [self.categoryTableController customReload]; } //check if active plugin matches if(plugin == self.selectedPlugin) { - //execute on main (UI) thread - dispatch_sync(dispatch_get_main_queue(), ^{ - - //scroll to top of item table - [self.itemTableController scrollToTop]; + //scroll to top of item table + [self.itemTableController scrollToTop]; - //reload item table - [self.itemTableController.itemTableView reloadData]; - - }); + //reload item table + [self.itemTableController.itemTableView reloadData]; } return; @@ -931,7 +924,7 @@ -(void)stopScanUI:(NSString*)statusMsg [self.progressIndicator stopAnimation:nil]; //hide progress indicator - self.progressIndicator.hidden = YES; + //self.progressIndicator.hidden = YES; //shift over status msg self.statusTextConstraint.constant = 10; @@ -992,7 +985,7 @@ -(void)displayScanStats flaggedItemCount += plugin.flaggedItems.count; //init detailed msg - details = [NSMutableString stringWithFormat:@"β–  found %lu items", (unsigned long)itemCount]; + details = [NSMutableString stringWithFormat:@"β–  Found %lu items", (unsigned long)itemCount]; } //otherwise just unknown items else @@ -1013,7 +1006,7 @@ -(void)displayScanStats } //init detailed msg - details = [NSMutableString stringWithFormat:@"β–  found %lu non-OS items", (unsigned long)itemCount]; + details = [NSMutableString stringWithFormat:@"β–  Found %lu non-OS items", (unsigned long)itemCount]; } } @@ -1026,7 +1019,7 @@ -(void)displayScanStats if(YES != self.isConnected) { //add disconnected msg - [details appendFormat:@" \r\nβ–  unable to query VirusTotal (network)"]; + [details appendFormat:@" \r\nβ–  Unable to query VirusTotal (network)"]; } //otherwise // ->add details about # of flagged items diff --git a/Assets.xcassets/FriendsMalwarebytes.imageset/Contents.json b/Assets.xcassets/Friends1Password.imageset/Contents.json similarity index 79% rename from Assets.xcassets/FriendsMalwarebytes.imageset/Contents.json rename to Assets.xcassets/Friends1Password.imageset/Contents.json index f3c6d2f..28e5e0e 100644 --- a/Assets.xcassets/FriendsMalwarebytes.imageset/Contents.json +++ b/Assets.xcassets/Friends1Password.imageset/Contents.json @@ -2,11 +2,11 @@ "images" : [ { "idiom" : "mac", - "filename" : "malwarebytes.pdf" + "filename" : "darkMode.png" }, { "idiom" : "mac", - "filename" : "malwarebytesLight.pdf", + "filename" : "lightMode.png", "appearances" : [ { "appearance" : "luminosity", @@ -16,7 +16,7 @@ }, { "idiom" : "mac", - "filename" : "malwarebytesDark.pdf", + "filename" : "darkMode.png", "appearances" : [ { "appearance" : "luminosity", @@ -32,4 +32,4 @@ "properties" : { "preserves-vector-representation" : true } -} \ No newline at end of file +} diff --git a/Assets.xcassets/Friends1Password.imageset/darkMode.png b/Assets.xcassets/Friends1Password.imageset/darkMode.png new file mode 100644 index 0000000..771e67b Binary files /dev/null and b/Assets.xcassets/Friends1Password.imageset/darkMode.png differ diff --git a/Assets.xcassets/Friends1Password.imageset/lightMode.png b/Assets.xcassets/Friends1Password.imageset/lightMode.png new file mode 100644 index 0000000..5789fbc Binary files /dev/null and b/Assets.xcassets/Friends1Password.imageset/lightMode.png differ diff --git a/Assets.xcassets/FriendsAiro.imageset/Contents.json b/Assets.xcassets/FriendsAiro.imageset/Contents.json deleted file mode 100644 index bb0cceb..0000000 --- a/Assets.xcassets/FriendsAiro.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "idiom" : "mac", - "filename" : "airo.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "preserves-vector-representation" : true - } -} \ No newline at end of file diff --git a/Assets.xcassets/FriendsAiro.imageset/airo.png b/Assets.xcassets/FriendsAiro.imageset/airo.png deleted file mode 100644 index 25e7ef1..0000000 Binary files a/Assets.xcassets/FriendsAiro.imageset/airo.png and /dev/null differ diff --git a/Assets.xcassets/FriendsJamf.imageset/Contents.json b/Assets.xcassets/FriendsJamf.imageset/Contents.json new file mode 100644 index 0000000..28e5e0e --- /dev/null +++ b/Assets.xcassets/FriendsJamf.imageset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "mac", + "filename" : "darkMode.png" + }, + { + "idiom" : "mac", + "filename" : "lightMode.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ] + }, + { + "idiom" : "mac", + "filename" : "darkMode.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ] + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Assets.xcassets/FriendsJamf.imageset/darkMode.png b/Assets.xcassets/FriendsJamf.imageset/darkMode.png new file mode 100644 index 0000000..c4f7cc3 Binary files /dev/null and b/Assets.xcassets/FriendsJamf.imageset/darkMode.png differ diff --git a/Assets.xcassets/FriendsJamf.imageset/lightMode.png b/Assets.xcassets/FriendsJamf.imageset/lightMode.png new file mode 100644 index 0000000..97d543a Binary files /dev/null and b/Assets.xcassets/FriendsJamf.imageset/lightMode.png differ diff --git a/Assets.xcassets/FriendsMacPaw.imageset/Contents.json b/Assets.xcassets/FriendsMacPaw.imageset/Contents.json deleted file mode 100644 index d11c3c0..0000000 --- a/Assets.xcassets/FriendsMacPaw.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "idiom" : "mac", - "filename" : "macpaw.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "preserves-vector-representation" : true - } -} \ No newline at end of file diff --git a/Assets.xcassets/FriendsMacPaw.imageset/macpaw.pdf b/Assets.xcassets/FriendsMacPaw.imageset/macpaw.pdf deleted file mode 100644 index 3150eac..0000000 Binary files a/Assets.xcassets/FriendsMacPaw.imageset/macpaw.pdf and /dev/null differ diff --git a/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytes.pdf b/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytes.pdf deleted file mode 100644 index 943c2de..0000000 Binary files a/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytes.pdf and /dev/null differ diff --git a/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytesDark.pdf b/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytesDark.pdf deleted file mode 100644 index 943c2de..0000000 Binary files a/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytesDark.pdf and /dev/null differ diff --git a/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytesLight.pdf b/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytesLight.pdf deleted file mode 100644 index 943c2de..0000000 Binary files a/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytesLight.pdf and /dev/null differ diff --git a/Cartfile b/Cartfile index 36d649c..cf79f9f 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "getsentry/sentry-cocoa" "4.1.0" +github "getsentry/sentry-cocoa" "6.2.0" diff --git a/Cartfile.resolved b/Cartfile.resolved index 36d649c..cf79f9f 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1 @@ -github "getsentry/sentry-cocoa" "4.1.0" +github "getsentry/sentry-cocoa" "6.2.0" diff --git a/Consts.h b/Consts.h index 7b210ce..b813047 100755 --- a/Consts.h +++ b/Consts.h @@ -18,6 +18,9 @@ //supported plugins static NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtensions", @"CronJobs", @"DirectoryServicesPlugins", @"EventRules", @"Extensions", @"Kexts", @"LaunchItems", @"DylibInserts", @"DylibProxies", @"LoginItems", @"LogInOutHooks", @"PeriodicScripts", @"QuicklookPlugins", @"SpotlightImporters", @"StartupScripts"}; +//sentry crash reporting URL +#define SENTRY_DSN @"https://ba5d094e87014a529b25d90bae010b1c@sentry.io/1321683" + //button text, start scan #define START_SCAN @"Start Scan" @@ -25,13 +28,13 @@ static NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"Browse #define STOP_SCAN @"Stop Scan" //status msg -#define SCAN_MSG_STARTED @"scanning started" +#define SCAN_MSG_STARTED @"Scanning Started" //status msg -#define SCAN_MSG_STOPPED @"scan stopped" +#define SCAN_MSG_STOPPED @"Scan Stopped" //status msg -#define SCAN_MSG_COMPLETE @"scan complete" +#define SCAN_MSG_COMPLETE @"Scan Complete" //prefs // ->filter out OS/known @@ -183,10 +186,6 @@ enum Signer{None, Apple, AppStore, DevID, AdHoc}; //id (tag) for 'show' button #define TABLE_ROW_SHOW_BUTTON 107 -//ellipis -// ->for long paths... -#define ELLIPIS @"..." - //known kexts #define WHITE_LISTED_KEXTS @"whitelistedKexts" diff --git a/Filter.m b/Filter.m index f13b558..afec868 100755 --- a/Filter.m +++ b/Filter.m @@ -154,31 +154,25 @@ -(BOOL)isTrustedKext:(File*)file // either list of hashes, or dev id id whitelistInfo = nil; + //ignore any signing issues + if(noErr != [file.signingInfo[KEY_SIGNATURE_STATUS] intValue]) goto bail; + //lookup based on name whitelistInfo = self.trustedKexts[file.path]; - //hashes? - if( (YES == [whitelistInfo isKindOfClass:[NSArray class]]) && - (YES == [whitelistInfo containsObject:[file.hashes[KEY_HASH_MD5] lowercaseString]]) ) + //dev id? + if( (YES == [((NSArray*)whitelistInfo).firstObject hasPrefix:@"Developer ID Application"]) && + (YES == [[file.signingInfo[KEY_SIGNATURE_AUTHORITIES] lastObject] isEqualToString:@"Apple Root CA"]) ) { - //got match - isTrusted = YES; - - //bail - goto bail; + //check + isTrusted = [whitelistInfo containsObject:[file.signingInfo[KEY_SIGNATURE_AUTHORITIES] firstObject]]; + if(YES == isTrusted) goto bail; } - - //dev id? - // note: these are only for kexts that ship with macOS! - if( (YES == [whitelistInfo isKindOfClass:[NSString class]]) && - (YES == [[file.signingInfo[KEY_SIGNATURE_AUTHORITIES] lastObject] isEqualToString:@"Apple Root CA"]) && - (YES == [file.signingInfo[KEY_SIGNATURE_AUTHORITIES] containsObject:whitelistInfo]) ) + //hash + else { - //got match - isTrusted = YES; - - //bail - goto bail; + isTrusted = [whitelistInfo containsObject:[file.hashes[KEY_HASH_MD5] lowercaseString]]; + if(YES == isTrusted) goto bail; } //check for apple signature diff --git a/ItemTableController.m b/ItemTableController.m index b8ba877..334121c 100755 --- a/ItemTableController.m +++ b/ItemTableController.m @@ -117,7 +117,7 @@ -(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView self.noItemsLabel.hidden = NO; //set string - self.noItemsLabel.stringValue = [NSString stringWithFormat:@"no %@ found", [selectedPluginObj.name lowercaseString]]; + self.noItemsLabel.stringValue = [NSString stringWithFormat:@"No %@ found", [selectedPluginObj.name lowercaseString]]; } //there *are* items diff --git a/KKRow.m b/KKRow.m index d757d5d..96d30a4 100755 --- a/KKRow.m +++ b/KKRow.m @@ -24,7 +24,7 @@ -(void)drawSelectionInRect:(NSRect)dirtyRect if(self.selectionHighlightStyle != NSTableViewSelectionHighlightStyleNone) { //make selection rect - selectionRect = NSInsetRect(self.bounds, 2.5, 2.5); + selectionRect = NSInsetRect(self.bounds, 10.0, 1.0); //dark mode highlight if(YES == isDarkMode()) diff --git a/KnockKnock.xcodeproj/project.pbxproj b/KnockKnock.xcodeproj/project.pbxproj index 578903d..80f77a3 100755 --- a/KnockKnock.xcodeproj/project.pbxproj +++ b/KnockKnock.xcodeproj/project.pbxproj @@ -526,40 +526,40 @@ CDA81D861A96F429009790E2 /* Plugins */ = { isa = PBXGroup; children = ( - CD2B611320675EEB00BF72E9 /* EventRules.h */, - CD2B611220675EEB00BF72E9 /* EventRules.m */, + CDAB98971AEAA6CB00C75B4B /* AuthorizationPlugins.h */, + CDAB98981AEAA6CB00C75B4B /* AuthorizationPlugins.m */, + CDA81E081A9B0AD8009790E2 /* BrowserExtensions.h */, + CDA81E091A9B0AD8009790E2 /* BrowserExtensions.m */, + CDD83FD21B50C48C0037124E /* Cronjobs.h */, + CDD83FD31B50C48C0037124E /* Cronjobs.m */, CD890F4023778F7B00029D99 /* DirectoryServicesPlugins.h */, CD890F4123778F7B00029D99 /* DirectoryServicesPlugins.m */, + CDAB989D1AEADE5A00C75B4B /* DylibInserts.h */, + CDAB989E1AEADE5A00C75B4B /* DylibInserts.m */, 7D44F4C31DAAEDE40085859C /* DylibProxies.h */, 7D44F4C41DAAEDE40085859C /* DylibProxies.m */, + CD2B611320675EEB00BF72E9 /* EventRules.h */, + CD2B611220675EEB00BF72E9 /* EventRules.m */, 7DE2FE2C1D3F30BE006C1438 /* Extensions.h */, 7DE2FE2D1D3F30BE006C1438 /* Extensions.m */, - 7DE29CA31CA7BC5600DFA6A6 /* StartupScripts.h */, - 7DE29CA21CA7BC5600DFA6A6 /* StartupScripts.m */, - 7D0DAB841CA752AE0049BAFF /* PeriodicScrips.h */, - 7D0DAB851CA752AE0049BAFF /* PeriodicScrips.m */, + CDA81D8A1A96F557009790E2 /* Kexts.h */, + CDA81D8B1A96F557009790E2 /* Kexts.m */, + CDA81E021A9AB89C009790E2 /* LaunchItems.h */, + CDA81E031A9AB89C009790E2 /* LaunchItems.m */, + CDA81E051A9AFFA7009790E2 /* LoginItems.h */, + CDA81E061A9AFFA7009790E2 /* LoginItems.m */, CDBE491B1B5B44BB0031FC22 /* LogInOutHooks.h */, CDBE491C1B5B44BB0031FC22 /* LogInOutHooks.m */, - CDD83FD21B50C48C0037124E /* Cronjobs.h */, - CDD83FD31B50C48C0037124E /* Cronjobs.m */, - CDAB989D1AEADE5A00C75B4B /* DylibInserts.h */, - CDAB989E1AEADE5A00C75B4B /* DylibInserts.m */, + 7D0DAB841CA752AE0049BAFF /* PeriodicScrips.h */, + 7D0DAB851CA752AE0049BAFF /* PeriodicScrips.m */, + CDA81D8C1A96F557009790E2 /* PluginBase.h */, + CDA81D8D1A96F557009790E2 /* PluginBase.m */, CDD1837D23774DF000BA6B86 /* QuicklookPlugins.h */, CDD1837E23774DF100BA6B86 /* QuicklookPlugins.m */, - CDAB98971AEAA6CB00C75B4B /* AuthorizationPlugins.h */, - CDAB98981AEAA6CB00C75B4B /* AuthorizationPlugins.m */, CD7B9F511ACBAE2900DF3C71 /* SpotlightImporters.h */, CD7B9F521ACBAE2900DF3C71 /* SpotlightImporters.m */, - CDA81E081A9B0AD8009790E2 /* BrowserExtensions.h */, - CDA81E091A9B0AD8009790E2 /* BrowserExtensions.m */, - CDA81E051A9AFFA7009790E2 /* LoginItems.h */, - CDA81E061A9AFFA7009790E2 /* LoginItems.m */, - CDA81E021A9AB89C009790E2 /* LaunchItems.h */, - CDA81E031A9AB89C009790E2 /* LaunchItems.m */, - CDA81D8A1A96F557009790E2 /* Kexts.h */, - CDA81D8B1A96F557009790E2 /* Kexts.m */, - CDA81D8C1A96F557009790E2 /* PluginBase.h */, - CDA81D8D1A96F557009790E2 /* PluginBase.m */, + 7DE29CA31CA7BC5600DFA6A6 /* StartupScripts.h */, + 7DE29CA21CA7BC5600DFA6A6 /* StartupScripts.m */, ); name = Plugins; sourceTree = ""; @@ -923,7 +923,7 @@ CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2.1.0; + CURRENT_PROJECT_VERSION = 2.2.0; DEVELOPMENT_TEAM = VBG97UB4TA; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -934,7 +934,9 @@ GCC_PREFIX_HEADER = "KnockKnock-Prefix.pch"; INFOPLIST_FILE = "KnockKnock-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks"; - MARKETING_VERSION = 2.1.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + MARKETING_VERSION = 2.2.0; + ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = KnockKnock; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -950,7 +952,7 @@ CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2.1.0; + CURRENT_PROJECT_VERSION = 2.2.0; DEVELOPMENT_TEAM = VBG97UB4TA; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -961,7 +963,9 @@ GCC_PREFIX_HEADER = "KnockKnock-Prefix.pch"; INFOPLIST_FILE = "KnockKnock-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks"; - MARKETING_VERSION = 2.1.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + MARKETING_VERSION = 2.2.0; + ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = KnockKnock; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Plugins/BrowserExtensions.h b/Plugins/BrowserExtensions.h index 08438e7..943cb49 100755 --- a/Plugins/BrowserExtensions.h +++ b/Plugins/BrowserExtensions.h @@ -1,5 +1,5 @@ // -// Kexts.h +// BrowserExtensions.h // KnockKnock // // Created by Patrick Wardle on 2/19/15. @@ -31,6 +31,4 @@ //scan for Firefox extensions -(void)scanExtensionsFirefox:(NSString*)browserPath; - - @end diff --git a/Plugins/BrowserExtensions.m b/Plugins/BrowserExtensions.m index 9eb1c19..07130d7 100755 --- a/Plugins/BrowserExtensions.m +++ b/Plugins/BrowserExtensions.m @@ -1,5 +1,5 @@ // -// LaunchItems.m +// BrowserExtensions.m // KnockKnock // @@ -29,6 +29,9 @@ //name account for safari extensions #define SAFARI_KEYCHAIN_ACCOUNT "Safari" +//safari's default location +#define SAFARI_DEFAULT_LOCATION @"/Applications/Safari.app" + //google chrome's base directory #define CHROME_BASE_PROFILE_DIRECTORY @"~/Library/Application Support/Google/Chrome/" @@ -160,163 +163,129 @@ -(NSArray*)getInstalledBrowsers { //release CFRelease(browserIDs); + browserIDs = nil; } return browsers; } //scan for Safari extensions -// note: on Mojave+ this will just silently fail :( +// invokes pluginkit to enumerate extensions... -(void)scanExtensionsSafari:(NSString*)browserPath { - //status - OSStatus status = !noErr; - - //keychain data - // ->binary plist of extensions - void *keychainData = NULL; - - //item ref - SecKeychainItemRef keychainItemRef = NULL; - - //length of keychain data - UInt32 keychainDataLength = 0; - - //dictionary of extensions info - NSDictionary* extensions = nil; - - //extension path - NSString* path = nil; - - //extension id - NSString* extensionID = nil; + //output from pluginkit + NSData* taskOutput = nil; + //exec pluginkit for each type + // then invoke helper to parse/create extension objects + for(NSString* match in @[@"com.apple.Safari.extension", @"com.apple.Safari.content-blocker"]) + { + //enumerate via pluginkit + taskOutput = execTask(PLUGIN_KIT, @[@"-mAvv", @"-p", match]); + if(0 != taskOutput.length) + { + //parse output + [self parseSafariExtensions:taskOutput browserPath:browserPath]; + } + } + + return; +} + +//parse the output from pluginkit +// create extension objects for any/all +-(void)parseSafariExtensions:(NSData*)extensions browserPath:(NSString*)browserPath +{ //extension info NSMutableDictionary* extensionInfo = nil; //Extension object Extension* extensionObj = nil; - //query keychain to get safari extensions - status = SecKeychainFindGenericPassword(NULL, (UInt32)strlen(SAFARI_KEYCHAIN_SERVICE), SAFARI_KEYCHAIN_SERVICE, (UInt32)strlen(SAFARI_KEYCHAIN_ACCOUNT), SAFARI_KEYCHAIN_ACCOUNT, &keychainDataLength, &keychainData, &keychainItemRef); - - //on success - // ->convert binary plist keychain data (extensions) into dictionary - if(errSecSuccess == status) - { - //convert - extensions = [NSPropertyListSerialization propertyListWithData: [NSData dataWithBytes:keychainData length:keychainDataLength] options:0 format:NULL error:NULL]; - } - - //some versions/instances of Safari don't store their extensions in the keychain - // ->so manually load from extensions plist file - if( (errSecItemNotFound == status) || - (0 == [extensions[@"Installed Extensions"] count]) ) + //split each time + // and parse, extracing name, path, etc... + for(NSString* line in [[[NSString alloc] initWithData:extensions encoding:NSUTF8StringEncoding] componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]) { - //try load from extensions plist file - extensions = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", [SAFARI_EXTENSION_DIRECTORY stringByExpandingTildeInPath], @"Extensions.plist"]]; - } - - //make sure extensions were found - // bail if nothing was found/parsed - if(nil == extensions) - { - //bail - goto bail; - } - - //iterate over all installed extensions - // save/report enabled ones - for(NSDictionary* extension in extensions[@"Installed Extensions"]) - { - //alloc extension info - extensionInfo = [NSMutableDictionary dictionary]; - - //skip disable ones - if(YES != [extension[@"Enabled"] boolValue]) - { - //skip - continue; - } + //key + NSString* key = nil; - //skip extensions without paths or names - if( (nil == extension[@"Archive File Name"]) || - (nil == extension[@"Bundle Directory Name"]) ) - { - //skip - continue; - } + //value + NSString* value = nil; - //save name - extensionInfo[KEY_RESULT_NAME] = extension[@"Bundle Directory Name"]; + //components + NSArray* components = nil; - //extract path component - // ...and build full path - path = [NSString stringWithFormat:@"%@/%@", [SAFARI_EXTENSION_DIRECTORY stringByExpandingTildeInPath], extension[@"Archive File Name"]]; + //details + NSString* details = nil; - //skip extensions w/ invalid paths - if(YES != [[NSFileManager defaultManager] fileExistsAtPath:path]) + //split + components = [[line stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] componentsSeparatedByString:@"="]; + + //init + if(nil == extensionInfo) { - //skip - continue; + //init + extensionInfo = [NSMutableDictionary dictionary]; + + //save plugin + extensionInfo[KEY_RESULT_PLUGIN] = self; + + //save browser path (i.e. Safari) + extensionInfo[KEY_EXTENSION_BROWSER] = browserPath; } - //save plugin - extensionInfo[KEY_RESULT_PLUGIN] = self; - - //save path - extensionInfo[KEY_RESULT_PATH] = path; + //grab key + key = [components.firstObject stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - //extract id - extensionID = extension[@"Bundle Identifier"]; + //grab value + value = [components.lastObject stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - //provide default value for nil ids - if(nil == extensionID) + //name + if(YES == [key isEqualToString:@"Display Name"]) { - //default - extensionID = @"unknown"; + //save + extensionInfo[KEY_RESULT_NAME] = value; } - //save identifier - extensionInfo[KEY_EXTENSION_ID] = extensionID; - - //save browser path (i.e. Safari) - extensionInfo[KEY_EXTENSION_BROWSER] = browserPath; - - //create Extension object for launch item - // ->skip those that err out for any reason - if(nil == (extensionObj = [[Extension alloc] initWithParams:extensionInfo])) + //path + else if(YES == [key isEqualToString:@"Path"]) { - //skip - continue; + //save + extensionInfo[KEY_RESULT_PATH] = value; + } + //uuid + else if(YES == [key isEqualToString:@"UUID"]) + { + //save + extensionInfo[KEY_EXTENSION_ID] = value; } - //process item - // ->save and report to UI - [super processItem:extensionObj]; - } - -//bail -bail: - - //release password data - if(NULL != keychainData) - { - //release - SecKeychainItemFreeContent(NULL, keychainData); - - //reset - keychainData = NULL; - } - - //release keychain item reference - if(NULL != keychainItemRef) - { - //release - CFRelease(keychainItemRef); - - //reset - keychainItemRef = NULL; + //have all three? + if( (nil != extensionInfo[KEY_RESULT_NAME]) && + (nil != extensionInfo[KEY_RESULT_PATH]) && + (nil != extensionInfo[KEY_EXTENSION_ID]) ) + { + //grab details + // found in Info.plist -> 'NSHumanReadableDescription' + details = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/Contents/Info.plist", extensionInfo[KEY_RESULT_PATH]]][@"NSHumanReadableDescription"]; + if(nil != details) + { + //add + extensionInfo[KEY_EXTENSION_DETAILS] = details; + } + + //create extension object + if(nil != (extensionObj = [[Extension alloc] initWithParams:extensionInfo])) + { + //process item + // save and report to UI + [super processItem:extensionObj]; + } + + //unset + // ...for next + extensionInfo = nil; + } } return; @@ -539,7 +508,6 @@ -(void)scanExtensionsChrome:(NSString*)browserPath }//for all profile files - bail: return; @@ -946,7 +914,6 @@ -(void)scanExtensionsOpera:(NSString*)browserPath }//for all extensions -//bail bail: return; diff --git a/Results/ItemBase.m b/Results/ItemBase.m index 7e8d250..1642eaa 100755 --- a/Results/ItemBase.m +++ b/Results/ItemBase.m @@ -54,13 +54,10 @@ -(NSString*)pathForFinder return self.path; } -//return - /* OPTIONAL METHODS */ - /* REQUIRED METHODS */ //stubs for inherited methods @@ -84,4 +81,4 @@ -(NSString*)toJSON return nil; } -@end \ No newline at end of file +@end diff --git a/ResultsWindowController.h b/ResultsWindowController.h index c7047ab..5f3ae46 100755 --- a/ResultsWindowController.h +++ b/ResultsWindowController.h @@ -1,5 +1,5 @@ // -// PrefsWindowController.h +// ResultsWindowController.h // KnockKnock // // Created by Patrick Wardle on 2/6/15. diff --git a/ResultsWindowController.m b/ResultsWindowController.m index 0b9db29..686f0ff 100755 --- a/ResultsWindowController.m +++ b/ResultsWindowController.m @@ -1,5 +1,5 @@ // -// PrefsWindowController.m +// ResultsWindowController.m // KnockKnock // // Created by Patrick Wardle on 2/6/15. diff --git a/UI/ExtensionInfoWindow.xib b/UI/ExtensionInfoWindow.xib index e455517..eedf037 100644 --- a/UI/ExtensionInfoWindow.xib +++ b/UI/ExtensionInfoWindow.xib @@ -1,8 +1,9 @@ - - + + - + + @@ -20,17 +21,18 @@ - + - + + @@ -48,6 +51,7 @@ + @@ -56,6 +60,7 @@ + @@ -64,7 +69,8 @@ - + + @@ -72,25 +78,26 @@ - + + - + - - - + + - + + @@ -98,6 +105,7 @@ + @@ -106,6 +114,7 @@ + diff --git a/UI/FileInfoWindow.xib b/UI/FileInfoWindow.xib index 8446ea6..b67902f 100644 --- a/UI/FileInfoWindow.xib +++ b/UI/FileInfoWindow.xib @@ -1,8 +1,8 @@ - + - + @@ -24,7 +24,7 @@ - + @@ -50,7 +50,7 @@ - + @@ -104,7 +104,7 @@ - + @@ -113,7 +113,7 @@ - + @@ -122,7 +122,7 @@ - + @@ -131,7 +131,7 @@ - + @@ -149,7 +149,7 @@ - + diff --git a/UI/PrefsWindow.xib b/UI/PrefsWindow.xib index fbf0f36..3b1a9f4 100644 --- a/UI/PrefsWindow.xib +++ b/UI/PrefsWindow.xib @@ -1,8 +1,8 @@ - + - + @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -54,7 +54,7 @@ - + @@ -63,18 +63,18 @@ - + - + - + - + @@ -92,7 +92,7 @@ - + diff --git a/Utilities.h b/Utilities.h index 0dc0605..2575765 100755 --- a/Utilities.h +++ b/Utilities.h @@ -27,10 +27,6 @@ NSMutableDictionary* allUsers(void); // convert any `~` to all or current user NSMutableArray* expandPaths(const __strong NSString* const paths[], int count); -//if string is too long to fit into a the text field -// ->truncate and insert ellipises before /file -NSString* stringByTruncatingString(NSTextField* textField, NSString* string, float width); - //given a path to binary // parse it back up to find app's bundle NSBundle* findAppBundle(NSString* binaryPath); diff --git a/Utilities.m b/Utilities.m index a3dc1ca..b87aeec 100755 --- a/Utilities.m +++ b/Utilities.m @@ -318,75 +318,6 @@ void disableSTDERR() } -//if string is too long to fit into a the text field -// ->truncate and insert ellipises before /file -NSString* stringByTruncatingString(NSTextField* textField, NSString* string, float width) -{ - //trucated string (with ellipis) - NSMutableString *truncatedString = nil; - - //offset of last '/' - NSRange lastSlash = {}; - - //make copy of string - truncatedString = [string mutableCopy]; - - //sanity check - // ->make sure string needs truncating - if([string sizeWithAttributes: @{NSFontAttributeName: textField.font}].width < width) - { - //bail - goto bail; - } - - //find instance of last '/ - lastSlash = [string rangeOfString:@"/" options:NSBackwardsSearch]; - - //sanity check - // ->make sure found a '/' - if(NSNotFound == lastSlash.location) - { - //bail - goto bail; - } - - //account for added ellipsis - width -= [ELLIPIS sizeWithAttributes: @{NSFontAttributeName: textField.font}].width; - - //delete characters until string will fit into specified size - while([truncatedString sizeWithAttributes: @{NSFontAttributeName: textField.font}].width > width) - { - //sanity check - // ->make sure we don't run off the front - if(0 == lastSlash.location) - { - //bail - goto bail; - } - - //skip back - lastSlash.location--; - - //delete char - [truncatedString deleteCharactersInRange:lastSlash]; - } - - //set length of range - lastSlash.length = ELLIPIS.length; - - //back up location - lastSlash.location -= ELLIPIS.length; - - //add in ellipis - [truncatedString replaceCharactersInRange:lastSlash withString:ELLIPIS]; - - -//bail -bail: - - return truncatedString; -} - //given a directory and a filter predicate // ->return all matches NSArray* directoryContents(NSString* directory, NSString* predicate) @@ -620,11 +551,8 @@ void makeTextViewHyperlink(NSTextField* textField, NSURL* url) //grab any left over data [output appendData:[readHandle readDataToEndOfFile]]; - -//bail bail: - //return output as string return output; } @@ -632,28 +560,33 @@ void makeTextViewHyperlink(NSTextField* textField, NSURL* url) // ->then make it modal void makeModal(NSWindowController* windowController) { + //flag + __block BOOL madeModal = NO; + //wait up to 1 second window to be non-nil // ->then make modal for(int i=0; i<20; i++) { - //can make it modal once we have a window - if(nil != windowController.window) - { - //make modal on main thread - dispatch_sync(dispatch_get_main_queue(), ^{ - + //nap + [NSThread sleepForTimeInterval:0.05f]; + + //make modal on main thread + dispatch_sync(dispatch_get_main_queue(), ^{ + + //can make it modal once we have a window + if(nil != windowController.window) + { //modal [[NSApplication sharedApplication] runModalForWindow:windowController.window]; - }); - - //all done - break; - } + //set flag + madeModal = YES; + } + }); + + //done? + if(YES == madeModal) break; - //nap - [NSThread sleepForTimeInterval:0.05f]; - }//until 1 second return; diff --git a/VTInfoWindowController.m b/VTInfoWindowController.m index 369eecc..79a5f83 100755 --- a/VTInfoWindowController.m +++ b/VTInfoWindowController.m @@ -113,25 +113,25 @@ -(void)configure vtDetectionRatio = [NSString stringWithFormat:@"%lu/%lu", (unsigned long)[self.fileObj.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue], (unsigned long)[self.fileObj.vtInfo[VT_RESULTS_TOTAL] unsignedIntegerValue]]; //set name - [self.fileName setStringValue:self.fileObj.name]; + self.fileName.stringValue = self.fileObj.name; //set color self.fileName.textColor = textColor; //detection ratio - [self.detectionRatio setStringValue:vtDetectionRatio]; + self.detectionRatio.stringValue = vtDetectionRatio; //set color self.detectionRatio.textColor = textColor; //analysis url - [self.analysisURL setStringValue:@"VirusTotal report"]; + self.analysisURL.stringValue = @"VirusTotal Report"; //make analysis url a hyperlink makeTextViewHyperlink(self.analysisURL, [NSURL URLWithString:self.fileObj.vtInfo[VT_RESULTS_URL]]); - //set 'submit' button text to 'rescan' - self.submitButton.title = @"Rescan?"; + //disable scan button + self.submitButton.enabled = NO; } //unknown file else @@ -185,8 +185,8 @@ -(void)windowWillClose:(NSNotification *)notification return; } -//automatically invoked when user clicks 'rescan'/'submit' -// ->rescan or upload to VT! +//invoked when user clicks 'submit' +// upload file to VT, open VT scan, etc... -(IBAction)vtButtonHandler:(id)sender { //VT object @@ -197,10 +197,7 @@ -(IBAction)vtButtonHandler:(id)sender //analyis URL NSMutableAttributedString* hyperlinkString = nil; - - //VT scan ID - __block NSString* scanID = nil; - + //new report __block NSURL* newReport = nil; @@ -226,7 +223,7 @@ -(IBAction)vtButtonHandler:(id)sender [hyperlinkString endEditing]; //set text - // ->will look the same, but the URL will be disabled! + // will look the same, but the URL will be disabled! [self.analysisURL setAttributedStringValue:hyperlinkString]; //pre-req @@ -259,208 +256,93 @@ -(IBAction)vtButtonHandler:(id)sender //animate it [self.progressIndicator startAnimation:nil]; - //rescan file? - if(YES == [((NSButton*)sender).title isEqualToString:@"Rescan?"]) - { - //set status msg - [self.statusMsg setStringValue:[NSString stringWithFormat:@"submitting re-scan request for %@", self.fileObj.name]]; - - //show status msg - self.statusMsg.hidden = NO; - - //submit rescan request in background - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - - //make request to VT - result = [vtObj reScan:self.fileObj]; + //set status msg + self.statusMsg.stringValue = [NSString stringWithFormat:@"submitting %@", self.fileObj.name]; + + //show status msg + self.statusMsg.hidden = NO; + + //submit rescan request in background + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + //submit file to VT + result = [vtObj submit:self.fileObj]; + + //got response + // ->requery VT to get scan results + if( (nil != result) && + (nil != result[VT_RESULTS_SCANID]) ) + { + //reset file's VT info + self.fileObj.vtInfo = nil; + + //kick off task to re-query VT + // ->pass in scan result ID + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 60 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [vtObj getInfoForItem:self.fileObj scanID:result[VT_RESULTS_SCANID]]; + }); + + //ask app delegate to update item in table + // ->will change the item's VT status to ... (pending) + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) itemProcessed:self.fileObj]; - //got result - // ->update UI and launch browswer to show report - if(nil != result) - { - //grab scan ID - // ->need this for (re)queries - scanID = result[VT_RESULTS_SCANID]; + //update status msg + dispatch_sync(dispatch_get_main_queue(), ^{ - //if file was flagged - // ->remove it from list of plugin's flagged - if(0 != [self.fileObj.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) - { - //sync - // ->since array will be reset if user clicks 'stop' scan - @synchronized(self.fileObj.plugin.flaggedItems) - { - //remove - [self.fileObj.plugin.flaggedItems removeObject:self.fileObj]; - } - } + //update + [self.statusMsg setStringValue:@"file submitted"]; - //remove file's VT info (since it'd now out of date) - self.fileObj.vtInfo = nil; + }); + + //nap + // allows msg to show up, and give VT some time + [NSThread sleepForTimeInterval:2.0]; + + //launch browser to show new report + dispatch_sync(dispatch_get_main_queue(), ^{ - //with a scan id can re-query VT - // ->will update VT button in UI once results are retrieved - if(nil != scanID) + //sanity check + // then launch browser + if( (nil != result[@"permalink"]) && + (nil != (newReport = [NSURL URLWithString:result[@"permalink"]])) ) { - //kick off task to re-query VT - // ->wait 60 seconds though to give VT servers some time to process - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 60 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [vtObj getInfoForItem:self.fileObj scanID:scanID]; - }); + //launch browser + [[NSWorkspace sharedWorkspace] openURL:newReport]; } - //ask app delegate to update item in table - // ->will change the item's VT status to ... (pending) - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) itemProcessed:self.fileObj]; - - //nap so user can see msg 'submitting' msg - [NSThread sleepForTimeInterval:0.5]; - - //update status msg - dispatch_sync(dispatch_get_main_queue(), ^{ + }); - //update - [self.statusMsg setStringValue:@"request submitted"]; - - }); - - //nap so user can see msg - [NSThread sleepForTimeInterval:0.5]; - - //launch browser to show new report - dispatch_sync(dispatch_get_main_queue(), ^{ - - //sanity check - // then launch browser - if( (nil != result[@"permalink"]) && - (nil != (newReport = [NSURL URLWithString:result[@"permalink"]])) ) - { - //launch browser - [[NSWorkspace sharedWorkspace] openURL:newReport]; - } - - }); + //wait to browser is up and happy + [NSThread sleepForTimeInterval:0.5]; + + //close window + dispatch_sync(dispatch_get_main_queue(), ^{ - //wait to browser is up and happy - [NSThread sleepForTimeInterval:0.5]; + //close + [self.window close]; - //close window - dispatch_sync(dispatch_get_main_queue(), ^{ - - //close - [self.window close]; - - }); - } - - //error - else - { - //show error msg - dispatch_async(dispatch_get_main_queue(), ^{ - - //update status msg - [self.statusMsg setStringValue:@"failed to submit request :("]; - - //stop activity indicator - [self.progressIndicator stopAnimation:nil]; - - }); - } - - }); - } - - //submit file - else - { - //set status msg - [self.statusMsg setStringValue:[NSString stringWithFormat:@"submitting %@", self.fileObj.name]]; - - //show status msg - self.statusMsg.hidden = NO; + }); - //submit rescan request in background - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + }//got result - //submit file to VT - result = [vtObj submit:self.fileObj]; - - //got response - // ->requery VT to get scan results - if( (nil != result) && - (nil != result[VT_RESULTS_SCANID]) ) - { - //reset file's VT info - self.fileObj.vtInfo = nil; - - //kick off task to re-query VT - // ->pass in scan result ID - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 60 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [vtObj getInfoForItem:self.fileObj scanID:result[VT_RESULTS_SCANID]]; - }); - - //ask app delegate to update item in table - // ->will change the item's VT status to ... (pending) - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) itemProcessed:self.fileObj]; + //error + else + { + //show error msg + dispatch_sync(dispatch_get_main_queue(), ^{ //update status msg - dispatch_sync(dispatch_get_main_queue(), ^{ - - //update - [self.statusMsg setStringValue:@"file submitted"]; - - }); - - //nap so user can see msg - [NSThread sleepForTimeInterval:0.5]; + [self.statusMsg setStringValue:@"failed to submit request :("]; - //launch browser to show new report - dispatch_sync(dispatch_get_main_queue(), ^{ - - //sanity check - // then launch browser - if( (nil != result[@"permalink"]) && - (nil != (newReport = [NSURL URLWithString:result[@"permalink"]])) ) - { - //launch browser - [[NSWorkspace sharedWorkspace] openURL:newReport]; - } - - }); - - //wait to browser is up and happy - [NSThread sleepForTimeInterval:0.5]; + //stop activity indicator + [self.progressIndicator stopAnimation:nil]; - //close window - dispatch_sync(dispatch_get_main_queue(), ^{ - - //close - [self.window close]; - - }); - - }//got result - - //error - else - { - //show error msg - dispatch_sync(dispatch_get_main_queue(), ^{ - - //update status msg - [self.statusMsg setStringValue:@"failed to submit request :("]; - - //stop activity indicator - [self.progressIndicator stopAnimation:nil]; - - }); - } - - }); - } + }); + } + + }); -//bail + bail: return; diff --git a/VirusTotal.m b/VirusTotal.m index 1b89e62..e0e3918 100755 --- a/VirusTotal.m +++ b/VirusTotal.m @@ -161,8 +161,14 @@ -(void)getInfo:(PluginBase*)plugin //tell UI all plugin's items have all be processed if(YES != cmdlineMode) { - //call up into UI - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) itemsProcessed:plugin]; + //on main thread + dispatch_sync(dispatch_get_main_queue(), ^{ + + //call up into UI + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) itemsProcessed:plugin]; + + }); + } return; diff --git a/WhiteList/whitelistedKexts.json b/WhiteList/whitelistedKexts.json index aea0632..44e9985 100644 --- a/WhiteList/whitelistedKexts.json +++ b/WhiteList/whitelistedKexts.json @@ -1,14 +1,16 @@ { - "/Library/Extensions/HighPointIOP.kext/Contents/MacOS/HighPointIOP":"Developer ID Application: HighPoint Technologies, Inc (DX6G69M9N2)", - "/Library/Extensions/PromiseSTEX.kext/Contents/MacOS/PromiseSTEX":"Developer ID Application: Promise Technology Mobile Apps (268CCUR4WN)", - "/Library/Extensions/ATTOCelerityFC8.kext/Contents/MacOS/ATTOCelerityFC8":"Developer ID Application: ATTO Technology, Inc. (FC94733TZD)", - "/Library/Extensions/HighPointRR.kext/Contents/MacOS/HighPointRR":"Developer ID Application: HighPoint Technologies, Inc (DX6G69M9N2)", - "/Library/Extensions/CalDigitHDProDrv.kext/Contents/MacOS/CalDigitHDProDrv":"Developer ID Application: CalDigit, Inc (8R7PS6VYW7)", - "/Library/Extensions/SoftRAID.kext/Contents/MacOS/SoftRAID":"Developer ID Application: SoftRAID LLC (NDGSU3WA4Y)", - "/Library/Extensions/ATTOExpressSASHBA2.kext/Contents/MacOS/ATTOExpressSASHBA2":"Developer ID Application: ATTO Technology, Inc. (FC94733TZD)", - "/Library/Extensions/ArcMSR.kext/Contents/MacOS/ArcMSR":"Developer ID Application: Areca Technology Corporation (34JN824YNC)", - "/Library/Extensions/ATTOExpressSASRAID2.kext/Contents/MacOS/ATTOExpressSASRAID2":"Developer ID Application: ATTO Technology, Inc. (FC94733TZD)", - "/Library/Extensions/ACS6x.kext/Contents/MacOS/ACS6x":"Developer ID Application: Accusys,Inc (K3TDMD9Y6B)", + "/Library/Extensions/HighPointIOP.kext/Contents/MacOS/HighPointIOP":["Developer ID Application: HighPoint Technologies, Inc (DX6G69M9N2)"], + "/Library/Extensions/PromiseSTEX.kext/Contents/MacOS/PromiseSTEX":["Developer ID Application: Promise Technology Mobile Apps (268CCUR4WN)"], + "/System/Library/Extensions/PromiseSTEX.kext/Contents/MacOS/PromiseSTEX":["Developer ID Application: Promise Technology Mobile Apps (268CCUR4WN)"], + "/Library/Extensions/ATTOCelerityFC8.kext/Contents/MacOS/ATTOCelerityFC8":["Developer ID Application: ATTO Technology, Inc. (FC94733TZD)"], + "/Library/Extensions/HighPointRR.kext/Contents/MacOS/HighPointRR":["Developer ID Application: HighPoint Technologies, Inc (DX6G69M9N2)"], + "/Library/Extensions/CalDigitHDProDrv.kext/Contents/MacOS/CalDigitHDProDrv":["Developer ID Application: CalDigit, Inc (8R7PS6VYW7)"], + "/Library/Extensions/SoftRAID.kext/Contents/MacOS/SoftRAID":["Developer ID Application: SoftRAID LLC (NDGSU3WA4Y)", "Developer ID Application: Other World Computing (Q9P8K45M5C)"], + "/Library/Extensions/ATTOExpressSASHBA2.kext/Contents/MacOS/ATTOExpressSASHBA2":["Developer ID Application: ATTO Technology, Inc. (FC94733TZD)"], + "/Library/Extensions/ArcMSR.kext/Contents/MacOS/ArcMSR":["Developer ID Application: Areca Technology Corporation (34JN824YNC)"], + "/System/Library/Extensions/ArcMSR.kext/Contents/MacOS/ArcMSR":["Developer ID Application: Areca Technology Corporation (34JN824YNC)"], + "/Library/Extensions/ATTOExpressSASRAID2.kext/Contents/MacOS/ATTOExpressSASRAID2":["Developer ID Application: ATTO Technology, Inc. (FC94733TZD)"], + "/Library/Extensions/ACS6x.kext/Contents/MacOS/ACS6x":["Developer ID Application: Accusys,Inc (K3TDMD9Y6B)"], "/System/Library/Extensions/Accusys6xxxx.kext/Contents/MacOS/Accusys6xxxx":["b02c769f3b6635e307b551462a48107d"], "/System/Library/Extensions/AppleBacklightExpert.kext/AppleBacklightExpert":["6501354452b48465dea904e27fe8c5f9", "8854000a050f4bbadd36db2b8118ffa1", "678c0efdde7c4a90e5e5b76855c9ee85"], "/System/Library/Extensions/ArcMSR.kext/Contents/MacOS/ArcMSR":["c57237576ff817b13246296ec786cce2"], diff --git a/en.lproj/MainMenu.xib b/en.lproj/MainMenu.xib index cf42742..ad8e077 100755 --- a/en.lproj/MainMenu.xib +++ b/en.lproj/MainMenu.xib @@ -1,8 +1,8 @@ - + - + @@ -52,30 +52,32 @@ - - + + - - - + + + + + + - - + + - - + + - - + + - + - @@ -86,29 +88,41 @@ - - + + - + - + + - + + + + + + + + + + + + - + - + @@ -116,29 +130,17 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + @@ -157,7 +159,7 @@ - + - - - - - + + + + + - - + + - + - @@ -195,11 +196,11 @@ - + - + @@ -215,7 +216,7 @@ - + @@ -223,7 +224,7 @@ - + @@ -246,7 +247,7 @@ - + @@ -272,7 +273,7 @@ - + @@ -298,7 +299,7 @@ - + @@ -306,7 +307,7 @@ - + + @@ -592,7 +641,6 @@ - @@ -610,27 +658,17 @@ - - + + - + - - + - - - - - - - - - - - - - - - - - - - + - + - + - + - - - - - - - - - - - - - - - - + + - - + + - + - - - - + + + diff --git a/main.m b/main.m index b6321bd..c5fbaa6 100755 --- a/main.m +++ b/main.m @@ -20,17 +20,11 @@ int main(int argc, char *argv[]) // sentry dumps to this, and we want only JSON to output... disableSTDERR(); - //sentry client - SentryClient *client = nil; - - //init sentry client - client = [[SentryClient alloc] initWithDsn:@"https://ba5d094e87014a529b25d90bae010b1c@sentry.io/1321683" didFailWithError:nil]; - - //set shared client - SentryClient.sharedClient = client; - - //start crash handler - [SentryClient.sharedClient startCrashHandlerWithError:nil]; + //init crash reporting + [SentrySDK startWithConfigureOptions:^(SentryOptions *options) { + options.dsn = SENTRY_DSN; + options.debug = YES; + }]; //handle '-h' or '-help' if( (YES == [[[NSProcessInfo processInfo] arguments] containsObject:@"-h"]) || diff --git a/patrons.txt b/patrons.txt index e28a351..4cb0e51 100644 --- a/patrons.txt +++ b/patrons.txt @@ -1,5 +1,5 @@ Patrons (2^6+): -Halo Privacy, Ash Morgan, Nando Mendonca, Geoffrey Weber, Randy Wong, Aaron Kiemele, Beau Galbraith, Stuart Ashenbrenner, whiskerz, Gamer_Bot +Jan Koum, Christian BlΓΌmlein, MikeyH Friends of Objective-See: -Digita Security, Sophos, Malwarebytes, SmugMug, Guardian Mobile Firewall, SecureMac +1Password, Jamf, SmugMug, Guardian Mobile Firewall, SecureMac, iVerify, Halo Privacy