Skip to content

Commit 2ac4f6d

Browse files
Avoid using NSRegularExpression in FIRApp (b/65122393) (#2529)
1 parent d5e197d commit 2ac4f6d

File tree

2 files changed

+82
-73
lines changed

2 files changed

+82
-73
lines changed

Example/Core/Tests/FIRAppTest.m

Lines changed: 15 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -389,11 +389,11 @@ - (void)testAppIDFormatInvalid {
389389
// Some direct tests of the validateAppIDFormat:withVersion: method.
390390
// Sanity checks first.
391391
NSString *const kGoodAppIDV1 = @"1:1337:ios:deadbeef";
392-
NSString *const kGoodVersionV1 = @"1:";
392+
NSString *const kGoodVersionV1 = @"1";
393393
XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:kGoodVersionV1]);
394394

395395
NSString *const kGoodAppIDV2 = @"2:1337:ios:5e18052ab54fbfec";
396-
NSString *const kGoodVersionV2 = @"2:";
396+
NSString *const kGoodVersionV2 = @"2";
397397
XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV2]);
398398

399399
// Version mismatch.
@@ -440,18 +440,13 @@ - (void)testAppIDFingerprintInvalid {
440440
// Some direct tests of the validateAppIDFingerprint:withVersion: method.
441441
// Sanity checks first.
442442
NSString *const kGoodAppIDV1 = @"1:1337:ios:deadbeef";
443-
NSString *const kGoodVersionV1 = @"1:";
443+
NSString *const kGoodVersionV1 = @"1";
444444
XCTAssertTrue([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodVersionV1]);
445445

446446
NSString *const kGoodAppIDV2 = @"2:1337:ios:5e18052ab54fbfec";
447-
NSString *const kGoodVersionV2 = @"2:";
447+
NSString *const kGoodVersionV2 = @"2";
448448
XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV2]);
449449

450-
// Version mismatch.
451-
XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV2 withVersion:kGoodVersionV1]);
452-
XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodVersionV2]);
453-
XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:@"999:"]);
454-
455450
// Nil or empty strings.
456451
XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:nil]);
457452
XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:@""]);
@@ -464,35 +459,19 @@ - (void)testAppIDFingerprintInvalid {
464459
XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodVersionV1 withVersion:kGoodVersionV1]);
465460
// The version is the entire app ID.
466461
XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodAppIDV1]);
467-
468-
// Versions digits that may make a partial match.
469-
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"01:1337:ios:deadbeef"
470-
withVersion:kGoodVersionV1]);
471-
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"10:1337:ios:deadbeef"
472-
withVersion:kGoodVersionV1]);
473-
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"11:1337:ios:deadbeef"
474-
withVersion:kGoodVersionV1]);
475-
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"21:1337:ios:5e18052ab54fbfec"
476-
withVersion:kGoodVersionV2]);
477-
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"22:1337:ios:5e18052ab54fbfec"
478-
withVersion:kGoodVersionV2]);
479-
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"02:1337:ios:5e18052ab54fbfec"
480-
withVersion:kGoodVersionV2]);
481-
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"20:1337:ios:5e18052ab54fbfec"
482-
withVersion:kGoodVersionV2]);
483-
// Extra fields.
484-
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"ab:1:1337:ios:deadbeef"
485-
withVersion:kGoodVersionV1]);
486-
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"1:ab:1337:ios:deadbeef"
487-
withVersion:kGoodVersionV1]);
488-
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"1:1337:ab:ios:deadbeef"
489-
withVersion:kGoodVersionV1]);
490-
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"1:1337:ios:ab:deadbeef"
491-
withVersion:kGoodVersionV1]);
492-
XCTAssertFalse([FIRApp validateAppIDFingerprint:@"1:1337:ios:deadbeef:ab"
493-
withVersion:kGoodVersionV1]);
494462
}
495463

464+
// Uncomment if you need to measure performance of [FIRApp validateAppID:].
465+
// It is commented because measures are heavily dependent on a build agent configuration,
466+
// so it cannot produce reliable resault on CI
467+
//- (void)testAppIDFingerprintPerfomance {
468+
// [self measureBlock:^{
469+
// for (NSInteger i = 0; i < 100; ++i) {
470+
// [self testAppIDPrefix];
471+
// }
472+
// }];
473+
//}
474+
496475
#pragma mark - Automatic Data Collection Tests
497476

498477
- (void)testGlobalDataCollectionNoFlags {

Firebase/Core/FIRApp.m

Lines changed: 67 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -575,33 +575,32 @@ + (BOOL)validateAppID:(NSString *)appID {
575575
return NO;
576576
}
577577

578-
// All app IDs must start with at least "<version number>:".
579-
NSString *const versionPattern = @"^\\d+:";
580-
NSRegularExpression *versionRegex =
581-
[NSRegularExpression regularExpressionWithPattern:versionPattern options:0 error:NULL];
582-
if (!versionRegex) {
578+
NSScanner *stringScanner = [NSScanner scannerWithString:appID];
579+
stringScanner.charactersToBeSkipped = nil;
580+
581+
NSString *appIDVersion;
582+
if (![stringScanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet]
583+
intoString:&appIDVersion]) {
583584
return NO;
584585
}
585586

586-
NSRange appIDRange = NSMakeRange(0, appID.length);
587-
NSArray *versionMatches = [versionRegex matchesInString:appID options:0 range:appIDRange];
588-
if (versionMatches.count != 1) {
587+
if (![stringScanner scanString:@":" intoString:NULL]) {
588+
// appIDVersion must be separated by ":"
589589
return NO;
590590
}
591591

592-
NSRange versionRange = [(NSTextCheckingResult *)versionMatches.firstObject range];
593-
NSString *appIDVersion = [appID substringWithRange:versionRange];
594-
NSArray *knownVersions = @[ @"1:" ];
592+
NSArray *knownVersions = @[ @"1" ];
595593
if (![knownVersions containsObject:appIDVersion]) {
596594
// Permit unknown yet properly formatted app ID versions.
595+
FIRLogInfo(kFIRLoggerCore, @"I-COR000010", @"Unknown GOOGLE_APP_ID version: %@", appIDVersion);
597596
return YES;
598597
}
599598

600-
if (![FIRApp validateAppIDFormat:appID withVersion:appIDVersion]) {
599+
if (![self validateAppIDFormat:appID withVersion:appIDVersion]) {
601600
return NO;
602601
}
603602

604-
if (![FIRApp validateAppIDFingerprint:appID withVersion:appIDVersion]) {
603+
if (![self validateAppIDFingerprint:appID withVersion:appIDVersion]) {
605604
return NO;
606605
}
607606

@@ -631,33 +630,76 @@ + (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version {
631630
return NO;
632631
}
633632

634-
if (![version hasSuffix:@":"]) {
633+
NSScanner *stringScanner = [NSScanner scannerWithString:appID];
634+
stringScanner.charactersToBeSkipped = nil;
635+
636+
// Skip version part
637+
// '*<version #>*:<project number>:ios:<fingerprint of bundle id>'
638+
if (![stringScanner scanString:version intoString:NULL]) {
639+
// The version part is missing or mismatched
640+
return NO;
641+
}
642+
643+
// Validate version part (see part between '*' symbols below)
644+
// '<version #>*:*<project number>:ios:<fingerprint of bundle id>'
645+
if (![stringScanner scanString:@":" intoString:NULL]) {
646+
// appIDVersion must be separated by ":"
647+
return NO;
648+
}
649+
650+
// Validate version part (see part between '*' symbols below)
651+
// '<version #>:*<project number>*:ios:<fingerprint of bundle id>'.
652+
NSInteger projectNumber = NSNotFound;
653+
if (![stringScanner scanInteger:&projectNumber]) {
654+
// NO project number found.
655+
return NO;
656+
}
657+
658+
// Validate version part (see part between '*' symbols below)
659+
// '<version #>:<project number>*:*ios:<fingerprint of bundle id>'.
660+
if (![stringScanner scanString:@":" intoString:NULL]) {
661+
// The project number must be separated by ":"
662+
return NO;
663+
}
664+
665+
// Validate version part (see part between '*' symbols below)
666+
// '<version #>:<project number>:*ios*:<fingerprint of bundle id>'.
667+
NSString *platform;
668+
if (![stringScanner scanUpToString:@":" intoString:&platform]) {
635669
return NO;
636670
}
637671

638-
if (![appID hasPrefix:version]) {
672+
if (![platform isEqualToString:@"ios"]) {
673+
// The platform must be @"ios"
639674
return NO;
640675
}
641676

642-
NSString *const pattern = @"^\\d+:ios:[a-f0-9]+$";
643-
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern
644-
options:0
645-
error:NULL];
646-
if (!regex) {
677+
// Validate version part (see part between '*' symbols below)
678+
// '<version #>:<project number>:ios*:*<fingerprint of bundle id>'.
679+
if (![stringScanner scanString:@":" intoString:NULL]) {
680+
// The platform must be separated by ":"
647681
return NO;
648682
}
649683

650-
NSRange localRange = NSMakeRange(version.length, appID.length - version.length);
651-
NSUInteger numberOfMatches = [regex numberOfMatchesInString:appID options:0 range:localRange];
652-
if (numberOfMatches != 1) {
684+
// Validate version part (see part between '*' symbols below)
685+
// '<version #>:<project number>:ios:*<fingerprint of bundle id>*'.
686+
unsigned long long fingerprint = NSNotFound;
687+
if (![stringScanner scanHexLongLong:&fingerprint]) {
688+
// Fingerprint part is missing
653689
return NO;
654690
}
691+
692+
if (!stringScanner.isAtEnd) {
693+
// There are not allowed characters in the fingerprint part
694+
return NO;
695+
}
696+
655697
return YES;
656698
}
657699

658700
/**
659701
* Validates that the fingerprint of the app ID string is what is expected based on the supplied
660-
* version. The version must end in ":".
702+
* version.
661703
*
662704
* Note that the v1 hash algorithm is not permitted on the client and cannot be fully validated.
663705
*
@@ -667,18 +709,6 @@ + (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version {
667709
* otherwise.
668710
*/
669711
+ (BOOL)validateAppIDFingerprint:(NSString *)appID withVersion:(NSString *)version {
670-
if (!appID.length || !version.length) {
671-
return NO;
672-
}
673-
674-
if (![version hasSuffix:@":"]) {
675-
return NO;
676-
}
677-
678-
if (![appID hasPrefix:version]) {
679-
return NO;
680-
}
681-
682712
// Extract the supplied fingerprint from the supplied app ID.
683713
// This assumes the app ID format is the same for all known versions below. If the app ID format
684714
// changes in future versions, the tokenizing of the app ID format will need to take into account
@@ -699,7 +729,7 @@ + (BOOL)validateAppIDFingerprint:(NSString *)appID withVersion:(NSString *)versi
699729
return NO;
700730
}
701731

702-
if ([version isEqual:@"1:"]) {
732+
if ([version isEqual:@"1"]) {
703733
// The v1 hash algorithm is not permitted on the client so the actual hash cannot be validated.
704734
return YES;
705735
}

0 commit comments

Comments
 (0)