Skip to content

Commit

Permalink
[Core] Fix API misnomer within FIRApp.m (#11648)
Browse files Browse the repository at this point in the history
  • Loading branch information
ncooke3 authored Aug 4, 2023
1 parent 45c3004 commit d4beb34
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 49 deletions.
61 changes: 31 additions & 30 deletions FirebaseCore/Sources/FIRApp.m
Original file line number Diff line number Diff line change
Expand Up @@ -573,10 +573,11 @@ - (void)checkExpectedBundleID {
#pragma mark - private - App ID Validation

/**
* Validates the format and fingerprint of the app ID contained in GOOGLE_APP_ID in the plist file.
* This is the main method for validating app ID.
* Validates the format of app ID and its included bundle ID hash contained in GOOGLE_APP_ID in the
* plist file. This is the main method for validating app ID.
*
* @return YES if the app ID fulfills the expected format and fingerprint, NO otherwise.
* @return YES if the app ID fulfills the expected format and contains a hashed bundle ID, NO
* otherwise.
*/
- (BOOL)isAppIDValid {
NSString *appID = _options.googleAppID;
Expand All @@ -597,7 +598,7 @@ - (BOOL)isAppIDValid {

+ (BOOL)validateAppID:(NSString *)appID {
// Failing validation only occurs when we are sure we are looking at a V2 app ID and it does not
// have a valid fingerprint, otherwise we just warn about the potential issue.
// have a valid hashed bundle ID, otherwise we just warn about the potential issue.
if (!appID.length) {
return NO;
}
Expand Down Expand Up @@ -627,7 +628,7 @@ + (BOOL)validateAppID:(NSString *)appID {
return NO;
}

if (![self validateAppIDFingerprint:appID withVersion:appIDVersion]) {
if (![self validateBundleIDHashWithinAppID:appID forVersion:appIDVersion]) {
return NO;
}

Expand All @@ -643,7 +644,7 @@ + (NSString *)actualBundleID {
* The version must end in ":".
*
* For v1 app ids the format is expected to be
* '<version #>:<project number>:ios:<fingerprint of bundle id>'.
* '<version #>:<project number>:ios:<hashed bundle id>'.
*
* This method does not verify that the contents of the app id are correct, just that they fulfill
* the expected format.
Expand All @@ -661,36 +662,36 @@ + (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version {
stringScanner.charactersToBeSkipped = nil;

// Skip version part
// '*<version #>*:<project number>:ios:<fingerprint of bundle id>'
// '*<version #>*:<project number>:ios:<hashed bundle id>'
if (![stringScanner scanString:version intoString:NULL]) {
// The version part is missing or mismatched
return NO;
}

// Validate version part (see part between '*' symbols below)
// '<version #>*:*<project number>:ios:<fingerprint of bundle id>'
// '<version #>*:*<project number>:ios:<hashed bundle id>'
if (![stringScanner scanString:@":" intoString:NULL]) {
// appIDVersion must be separated by ":"
return NO;
}

// Validate version part (see part between '*' symbols below)
// '<version #>:*<project number>*:ios:<fingerprint of bundle id>'.
// '<version #>:*<project number>*:ios:<hashed bundle id>'.
NSInteger projectNumber = NSNotFound;
if (![stringScanner scanInteger:&projectNumber]) {
// NO project number found.
return NO;
}

// Validate version part (see part between '*' symbols below)
// '<version #>:<project number>*:*ios:<fingerprint of bundle id>'.
// '<version #>:<project number>*:*ios:<hashed bundle id>'.
if (![stringScanner scanString:@":" intoString:NULL]) {
// The project number must be separated by ":"
return NO;
}

// Validate version part (see part between '*' symbols below)
// '<version #>:<project number>:*ios*:<fingerprint of bundle id>'.
// '<version #>:<project number>:*ios*:<hashed bundle id>'.
NSString *platform;
if (![stringScanner scanUpToString:@":" intoString:&platform]) {
return NO;
Expand All @@ -702,57 +703,57 @@ + (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version {
}

// Validate version part (see part between '*' symbols below)
// '<version #>:<project number>:ios*:*<fingerprint of bundle id>'.
// '<version #>:<project number>:ios*:*<hashed bundle id>'.
if (![stringScanner scanString:@":" intoString:NULL]) {
// The platform must be separated by ":"
return NO;
}

// Validate version part (see part between '*' symbols below)
// '<version #>:<project number>:ios:*<fingerprint of bundle id>*'.
unsigned long long fingerprint = NSNotFound;
if (![stringScanner scanHexLongLong:&fingerprint]) {
// Fingerprint part is missing
// '<version #>:<project number>:ios:*<hashed bundle id>*'.
unsigned long long bundleIDHash = NSNotFound;
if (![stringScanner scanHexLongLong:&bundleIDHash]) {
// Hashed bundleID part is missing
return NO;
}

if (!stringScanner.isAtEnd) {
// There are not allowed characters in the fingerprint part
// There are not allowed characters in the hashed bundle ID part
return NO;
}

return YES;
}

/**
* Validates that the fingerprint of the app ID string is what is expected based on the supplied
* version.
* Validates that the hashed bundle ID included in the app ID string is what is expected based on
* the supplied version.
*
* Note that the v1 hash algorithm is not permitted on the client and cannot be fully validated.
*
* @param appID Contents of GOOGLE_APP_ID from the plist file.
* @param version Indicates what version of the app id format this string should be.
* @return YES if provided string fufills the expected fingerprint and the version is known, NO
* @return YES if provided string fufills the expected hashed bundle ID and the version is known, NO
* otherwise.
*/
+ (BOOL)validateAppIDFingerprint:(NSString *)appID withVersion:(NSString *)version {
// Extract the supplied fingerprint from the supplied app ID.
// This assumes the app ID format is the same for all known versions below. If the app ID format
// changes in future versions, the tokenizing of the app ID format will need to take into account
// the version of the app ID.
+ (BOOL)validateBundleIDHashWithinAppID:(NSString *)appID forVersion:(NSString *)version {
// Extract the hashed bundle ID from the given app ID.
// This assumes the app ID format is the same for all known versions below.
// If the app ID format changes in future versions, the tokenizing of the app
// ID format will need to take into account the version of the app ID.
NSArray *components = [appID componentsSeparatedByString:@":"];
if (components.count != 4) {
return NO;
}

NSString *suppliedFingerprintString = components[3];
if (!suppliedFingerprintString.length) {
NSString *suppliedBundleIDHashString = components[3];
if (!suppliedBundleIDHashString.length) {
return NO;
}

uint64_t suppliedFingerprint;
NSScanner *scanner = [NSScanner scannerWithString:suppliedFingerprintString];
if (![scanner scanHexLongLong:&suppliedFingerprint]) {
uint64_t suppliedBundleIDHash;
NSScanner *scanner = [NSScanner scannerWithString:suppliedBundleIDHashString];
if (![scanner scanHexLongLong:&suppliedBundleIDHash]) {
return NO;
}

Expand Down
39 changes: 20 additions & 19 deletions FirebaseCore/Tests/Unit/FIRAppTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ + (BOOL)writeString:(NSString *)string toURL:(NSURL *)filePathURL;
+ (void)logAppInfo:(NSNotification *)notification;
+ (BOOL)validateAppID:(NSString *)appID;
+ (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version;
+ (BOOL)validateAppIDFingerprint:(NSString *)appID withVersion:(NSString *)version;
+ (BOOL)validateBundleIDHashWithinAppID:(NSString *)appID forVersion:(NSString *)version;

+ (nullable NSNumber *)readDataCollectionSwitchFromPlist;
+ (nullable NSNumber *)readDataCollectionSwitchFromUserDefaultsForApp:(FIRApp *)app;
Expand Down Expand Up @@ -423,33 +423,34 @@ - (void)testOptionsLocking {
#pragma mark - App ID v1

- (void)testAppIDV1 {
// Missing separator between platform:fingerprint.
// Missing separator between platform:hashed bundle ID.
XCTAssertFalse([FIRApp validateAppID:@"1:1337:iosdeadbeef"]);

// Wrong platform "android".
XCTAssertFalse([FIRApp validateAppID:@"1:1337:android:deadbeef"]);

// The fingerprint, aka 4th field, should only contain hex characters.
// The hashed bundle ID, aka 4th field, should only contain hex characters.
XCTAssertFalse([FIRApp validateAppID:@"1:1337:ios:123abcxyz"]);

// The fingerprint, aka 4th field, is not tested in V1, so a bad value shouldn't cause a failure.
// The hashed bundle ID, aka 4th field, is not tested in V1, so a bad value shouldn't cause a
// failure.
XCTAssertTrue([FIRApp validateAppID:@"1:1337:ios:deadbeef"]);
}

#pragma mark - App ID v2

- (void)testAppIDV2 {
// Missing separator between platform:fingerprint.
// Missing separator between platform:hashed bundle ID.
XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios5e18052ab54fbfec"]);

// Unknown versions may contain anything.
XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios:123abcxyz"]);
XCTAssertTrue([FIRApp validateAppID:@"2:thisdoesn'teven_m:a:t:t:e:r_"]);

// Known good fingerprint.
// Known good hashed bundle ID.
XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios:5e18052ab54fbfec"]);

// Unknown fingerprint, not tested so shouldn't cause a failure.
// Unknown hashed bundle ID, not tested so shouldn't cause a failure.
XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios:deadbeef"]);
}

Expand Down Expand Up @@ -571,36 +572,36 @@ - (void)testAppIDFormatInvalid {
XCTAssertFalse([FIRApp validateAppIDFormat:@"1:1337:ios:deadbeef:ab" withVersion:kGoodVersionV1]);
}

- (void)testAppIDFingerprintInvalid {
- (void)testAppIDContainsInvalidBundleIDHash {
OCMStub([self.appClassMock actualBundleID]).andReturn(@"com.google.bundleID");
// Some direct tests of the validateAppIDFingerprint:withVersion: method.
// Some direct tests of the validateBundleIDHashWithinAppID:forVersion: method.
// Sanity checks first.
NSString *const kGoodAppIDV1 = @"1:1337:ios:deadbeef";
NSString *const kGoodVersionV1 = @"1";
XCTAssertTrue([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodVersionV1]);
XCTAssertTrue([FIRApp validateBundleIDHashWithinAppID:kGoodAppIDV1 forVersion:kGoodVersionV1]);

NSString *const kGoodAppIDV2 = @"2:1337:ios:5e18052ab54fbfec";
NSString *const kGoodVersionV2 = @"2";
XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV2]);

// Nil or empty strings.
XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:nil]);
XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:@""]);
XCTAssertFalse([FIRApp validateAppIDFingerprint:nil withVersion:kGoodVersionV1]);
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"" withVersion:kGoodVersionV1]);
XCTAssertFalse([FIRApp validateAppIDFingerprint:nil withVersion:nil]);
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"" withVersion:@""]);
XCTAssertFalse([FIRApp validateBundleIDHashWithinAppID:kGoodAppIDV1 forVersion:nil]);
XCTAssertFalse([FIRApp validateBundleIDHashWithinAppID:kGoodAppIDV1 forVersion:@""]);
XCTAssertFalse([FIRApp validateBundleIDHashWithinAppID:nil forVersion:kGoodVersionV1]);
XCTAssertFalse([FIRApp validateBundleIDHashWithinAppID:@"" forVersion:kGoodVersionV1]);
XCTAssertFalse([FIRApp validateBundleIDHashWithinAppID:nil forVersion:nil]);
XCTAssertFalse([FIRApp validateBundleIDHashWithinAppID:@"" forVersion:@""]);

// App ID contains only the version prefix.
XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodVersionV1 withVersion:kGoodVersionV1]);
XCTAssertFalse([FIRApp validateBundleIDHashWithinAppID:kGoodVersionV1 forVersion:kGoodVersionV1]);
// The version is the entire app ID.
XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodAppIDV1]);
XCTAssertFalse([FIRApp validateBundleIDHashWithinAppID:kGoodAppIDV1 forVersion:kGoodAppIDV1]);
}

// Uncomment if you need to measure performance of [FIRApp validateAppID:].
// It is commented because measures are heavily dependent on a build agent configuration,
// so it cannot produce reliable resault on CI
//- (void)testAppIDFingerprintPerfomance {
//- (void)testAppIDValidationPerfomance {
// [self measureBlock:^{
// for (NSInteger i = 0; i < 100; ++i) {
// [self testAppIDPrefix];
Expand Down

0 comments on commit d4beb34

Please sign in to comment.