Skip to content

Commit

Permalink
santabs: use the ancestor bundle when searching for binaries (#175)
Browse files Browse the repository at this point in the history
* santabs: use the ancestor bundle when searching for binaries

* review updates

* bundle tests
  • Loading branch information
tburgin authored Jun 9, 2017
1 parent a824289 commit d3b3d72
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 25 deletions.
4 changes: 4 additions & 0 deletions Santa.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@
C7479F051E53704E0054C1CF /* SNTXPCBundleServiceInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7C721B01E23FF300051FAA6 /* SNTXPCBundleServiceInterface.m */; };
C7479F071E5374BF0054C1CF /* SNTXPCControlInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD605419115D17006B445C /* SNTXPCControlInterface.m */; };
C7479F091E5374E50054C1CF /* SNTRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE50F671912716A007B2B0C /* SNTRule.m */; };
C74D6CC61EEB3B9B00BB5A33 /* BundleExample.app in Resources */ = {isa = PBXBuildFile; fileRef = C74D6CC51EEB3B9B00BB5A33 /* BundleExample.app */; };
C76614EC1D142D3C00D150C1 /* SNTCommandCheckCache.m in Sources */ = {isa = PBXBuildFile; fileRef = C76614EB1D142D3C00D150C1 /* SNTCommandCheckCache.m */; };
C776A1071DEE160500A56616 /* SNTCommandSyncManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C776A1061DEE160500A56616 /* SNTCommandSyncManager.m */; };
C78227631E1C3C7D006EB2D6 /* santabs.xpc in CopyFiles */ = {isa = PBXBuildFile; fileRef = C78227541E1C3C58006EB2D6 /* santabs.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
Expand Down Expand Up @@ -431,6 +432,7 @@
A6A91785C40257CC156B4F05 /* Pods-Santa.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santa.release.xcconfig"; path = "Pods/Target Support Files/Pods-Santa/Pods-Santa.release.xcconfig"; sourceTree = "<group>"; };
C11A10A5D6E112788769CF70 /* libPods-santad.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-santad.a"; sourceTree = BUILT_PRODUCTS_DIR; };
C72E8D931D7F399900C86DD3 /* SNTCommandFileInfoTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandFileInfoTest.m; sourceTree = "<group>"; };
C74D6CC51EEB3B9B00BB5A33 /* BundleExample.app */ = {isa = PBXFileReference; lastKnownFileType = wrapper.application; path = BundleExample.app; sourceTree = "<group>"; };
C76614EB1D142D3C00D150C1 /* SNTCommandCheckCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandCheckCache.m; sourceTree = "<group>"; };
C776A1051DEE160500A56616 /* SNTCommandSyncManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCommandSyncManager.h; sourceTree = "<group>"; };
C776A1061DEE160500A56616 /* SNTCommandSyncManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandSyncManager.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -543,6 +545,7 @@
0D260DB018B68E12002A0B55 /* Resources */ = {
isa = PBXGroup;
children = (
C74D6CC51EEB3B9B00BB5A33 /* BundleExample.app */,
0D536ED51B8E7A2E0039A26D /* bad_pagezero */,
0D2CD4601A81C7B100C9C910 /* dn.plist */,
0D536ED61B8E7A2E0039A26D /* missing_pagezero */,
Expand Down Expand Up @@ -1082,6 +1085,7 @@
files = (
0DEA5F761CF6482E00704398 /* sync_eventupload_input_basic.plist in Resources */,
0D536ED71B8E7A2E0039A26D /* bad_pagezero in Resources */,
C74D6CC61EEB3B9B00BB5A33 /* BundleExample.app in Resources */,
0DEA5F7A1CF64C9200704398 /* sync_ruledownload_batch1.json in Resources */,
0DEA5F741CF63B9600704398 /* sync_eventupload_input_quarantine.plist in Resources */,
0D2CD4611A81C7B100C9C910 /* dn.plist in Resources */,
Expand Down
15 changes: 15 additions & 0 deletions Source/common/SNTFileInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,21 @@
///
- (BOOL)isMissingPageZero;

///
/// If set to YES, the bundle* and infoPlist methods will search for and use the highest NSBundle
/// found in the tree. Defaults to NO, which uses the first found bundle, if any.
///
/// @example:
/// An SNTFileInfo object that represents
/// /Applications/Photos.app/Contents/XPCServices/com.apple.Photos.librarychooserservice.xpc
/// useAncestorBundle is set to YES
/// /Applications/Photos.app will be used to get data backing all the bundle methods
///
/// @note: The NSBundle object backing the bundle* and infoPlist methods is cached once found.
/// Setting the useAncestorBundle propery will clear this cache and force a re-search.
///
@property(nonatomic) BOOL useAncestorBundle;

///
/// @return An NSBundle if this file is part of a bundle.
///
Expand Down
44 changes: 29 additions & 15 deletions Source/common/SNTFileInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -285,23 +285,29 @@ - (BOOL)isMissingPageZero {
///
/// This method walks up the path until a bundle is found, if any.
///
/// @param ancestor YES this will return the highest NSBundle found in the tree. No will return the
/// the lowest.
///
-(NSBundle *)findBundleWithAncestor:(BOOL)ancestor {
NSBundle *bundle;
NSMutableArray *pathComponents = [[self.path pathComponents] mutableCopy];

// Ignore the root path "/", for some reason this is considered a bundle.
while (pathComponents.count > 1) {
NSBundle *bndl = [NSBundle bundleWithPath:[NSString pathWithComponents:pathComponents]];
if (bndl && [bndl objectForInfoDictionaryKey:@"CFBundleIdentifier"]) {
bundle = bndl;
if (!ancestor) break;
}
[pathComponents removeLastObject];
}
return bundle;
}

- (NSBundle *)bundle {
if (!self.bundleRef) {
self.bundleRef = (NSBundle *)[NSNull null];

NSMutableArray *pathComponents = [[self.path pathComponents] mutableCopy];

// Ignore the checking the root path "/". For some reason this produces a valid bundle.
// Also only walk back a max 10 dirs.
for (short deep = 0;
pathComponents.count > 1 && deep < 10;
++deep, [pathComponents removeLastObject]) {
NSBundle *bndl = [NSBundle bundleWithPath:[NSString pathWithComponents:pathComponents]];
if (bndl && [bndl objectForInfoDictionaryKey:@"CFBundleIdentifier"]) {
self.bundleRef = bndl;
break;
}
}
self.bundleRef =
[self findBundleWithAncestor:self.useAncestorBundle] ?: (NSBundle *)[NSNull null];
}
return self.bundleRef == (NSBundle *)[NSNull null] ? nil : self.bundleRef;
}
Expand All @@ -310,6 +316,14 @@ - (NSString *)bundlePath {
return [self.bundle bundlePath];
}

- (void)setUseAncestorBundle:(BOOL)useAncestorBundle {
if (self.useAncestorBundle != useAncestorBundle) {
self.bundleRef = nil;
self.infoDict = nil;
}
_useAncestorBundle = useAncestorBundle;
}

- (NSDictionary *)infoPlist {
if (!self.infoDict) {
NSDictionary *d = [self embeddedPlist];
Expand Down
21 changes: 16 additions & 5 deletions Source/santabs/SNTBundleService.m
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ - (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event
dispatch_semaphore_t sema = dispatch_semaphore_create(0);

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// Use the highest bundle we can find. Save and reuse the bundle infomation when creating
// the related binary events.
SNTFileInfo *b = [[SNTFileInfo alloc] initWithPath:event.fileBundlePath];
b.useAncestorBundle = YES;
event.fileBundlePath = b.bundlePath;
event.fileBundleID = b.bundleIdentifier;
event.fileBundleName = b.bundleName;
event.fileBundleVersion = b.bundleVersion;
event.fileBundleVersionString = b.bundleShortVersionString;

NSArray *relatedBinaries = [self findRelatedBinaries:event progress:progress];
NSString *bundleHash = [self calculateBundleHashFromEvents:relatedBinaries];
NSNumber *ms = [NSNumber numberWithDouble:[startTime timeIntervalSinceNow] * -1000.0];
Expand Down Expand Up @@ -236,11 +246,12 @@ - (NSArray *)findRelatedBinaries:(SNTStoredEvent *)event progress:(NSProgress *)
se.fileSHA256 = fi.SHA256;
se.occurrenceDate = [NSDate distantFuture];
se.decision = SNTEventStateBundleBinary;
se.fileBundleID = fi.bundleIdentifier;
se.fileBundleName = fi.bundleName;
se.fileBundlePath = fi.bundlePath;
se.fileBundleVersion = fi.bundleVersion;
se.fileBundleVersionString = fi.bundleShortVersionString;

se.fileBundlePath = event.fileBundlePath;
se.fileBundleID = event.fileBundleID;
se.fileBundleName = event.fileBundleName;
se.fileBundleVersion = event.fileBundleVersion;
se.fileBundleVersionString = event.fileBundleVersionString;

MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithBinaryPath:se.filePath];
se.signingChain = cs.certificates;
Expand Down
52 changes: 52 additions & 0 deletions Tests/LogicTests/Resources/BundleExample.app/Contents/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>16F73</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>BundleExample</string>
<key>CFBundleIdentifier</key>
<string>com.google.santa.BundleExample</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>BundleExample</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>7D1014</string>
<key>DTPlatformVersion</key>
<string>GM</string>
<key>DTSDKBuild</key>
<string>15E60</string>
<key>DTSDKName</key>
<string>macosx10.11</string>
<key>DTXcode</key>
<string>0731</string>
<key>DTXcodeBuild</key>
<string>7D1014</string>
<key>LSMinimumSystemVersion</key>
<string>10.12</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2017 Google. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
Binary file not shown.
82 changes: 77 additions & 5 deletions Tests/LogicTests/SNTFileInfoTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,88 @@ - (void)testScript {
}

- (void)testBundle {
SNTFileInfo *sut =
[[SNTFileInfo alloc] initWithPath:@"/Applications/Safari.app/Contents/MacOS/Safari"];
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"BundleExample"
ofType:@"app"];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];

XCTAssertNotNil([sut bundle]);

XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.santa.BundleExample");
XCTAssertEqualObjects([sut bundleName], @"BundleExample");
XCTAssertEqualObjects([sut bundleVersion], @"1");
XCTAssertEqualObjects([sut bundleShortVersionString], @"1.0");
XCTAssertEqualObjects([sut bundlePath], path);
}

- (void)testAncestorBundle {
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"BundleExample"
ofType:@"app"];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
sut.useAncestorBundle = YES;

XCTAssertNotNil([sut bundle]);

XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.LogicTests");
XCTAssertNotNil([sut bundleVersion]);
XCTAssertNotNil([sut bundleShortVersionString]);

NSString *ancestorBundlePath = path;
for (int i = 0; i < 3; i++) {
ancestorBundlePath = [ancestorBundlePath stringByDeletingLastPathComponent];
}
XCTAssertEqualObjects([sut bundlePath], ancestorBundlePath);
}

- (void)testBundleIsAncestor {
NSString *path = [NSBundle bundleForClass:[self class]].bundlePath;
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
sut.useAncestorBundle = YES;

XCTAssertNotNil([sut bundle]);

XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.LogicTests");
XCTAssertNotNil([sut bundleVersion]);
XCTAssertNotNil([sut bundleShortVersionString]);
XCTAssertEqualObjects([sut bundlePath], path);
}

- (void)testBundleCacheReset {
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"BundleExample"
ofType:@"app"];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];

XCTAssertNotNil([sut bundle]);

XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.santa.BundleExample");
XCTAssertEqualObjects([sut bundleName], @"BundleExample");
XCTAssertEqualObjects([sut bundleVersion], @"1");
XCTAssertEqualObjects([sut bundleShortVersionString], @"1.0");
XCTAssertEqualObjects([sut bundlePath], path);

sut.useAncestorBundle = YES;

XCTAssertNotNil([sut bundle]);

XCTAssertEqualObjects([sut bundleIdentifier], @"com.apple.Safari");
XCTAssertEqualObjects([sut bundleName], @"Safari");
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.LogicTests");
XCTAssertNotNil([sut bundleVersion]);
XCTAssertNotNil([sut bundleShortVersionString]);
XCTAssertEqualObjects([sut bundlePath], @"/Applications/Safari.app");

NSString *ancestorBundlePath = path;
for (int i = 0; i < 3; i++) {
ancestorBundlePath = [ancestorBundlePath stringByDeletingLastPathComponent];
}
XCTAssertEqualObjects([sut bundlePath], ancestorBundlePath);
}

- (void)testNonBundle {
SNTFileInfo *sut =
[[SNTFileInfo alloc] initWithPath:@"/usr/bin/yes"];

XCTAssertNil([sut bundle]);

sut.useAncestorBundle = YES;

XCTAssertNil([sut bundle]);
}

- (void)testEmbeddedInfoPlist {
Expand Down

0 comments on commit d3b3d72

Please sign in to comment.