// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #import "FirebasePerformance/Sources/FPRURLFilter.h" #import "FirebasePerformance/Sources/FPRURLFilter_Private.h" #import "FirebasePerformance/Sources/FPRConsoleLogger.h" #import /** The expected key of the domain allowlist array. */ static NSString *const kFPRAllowlistDomainsKey = @"FPRWhitelistedDomains"; /** Allowlist status enums. */ typedef NS_ENUM(NSInteger, FPRURLAllowlistStatus) { /** No allowlist is present, so the URL will be allowed. */ FPRURLAllowlistStatusDoesNotExist = 1, /** The URL is allowed. */ FPRURLAllowlistStatusAllowed = 2, /** The URL is NOT allowed. */ FPRURLAllowlistStatusNotAllowed = 3 }; /** Returns the set of denied URL strings. * * @return the set of denied URL strings. */ NSSet *GetSystemDenyListURLStrings(void) { // The denylist of URLs for uploading events to avoid cyclic generation of those network events. static NSSet *denylist = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ denylist = [[NSSet alloc] initWithArray:@[ [[GDTCOREndpoints uploadURLForTarget:kGDTCORTargetCCT] absoluteString], [[GDTCOREndpoints uploadURLForTarget:kGDTCORTargetFLL] absoluteString] ]]; }); return denylist; } @implementation FPRURLFilter + (instancetype)sharedInstance { static FPRURLFilter *sharedInstance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[FPRURLFilter alloc] initWithBundle:[NSBundle mainBundle]]; }); return sharedInstance; } - (instancetype)initWithBundle:(NSBundle *)bundle { self = [super init]; if (self) { _mainBundle = bundle; _allowlistDomains = [self retrieveAllowlistFromPlist]; } return self; } - (BOOL)shouldInstrumentURL:(NSString *)URL { if ([self isURLDeniedByTheSDK:URL]) { return NO; } FPRURLAllowlistStatus allowlistStatus = [self isURLAllowed:URL]; if (allowlistStatus == FPRURLAllowlistStatusDoesNotExist) { return YES; } return allowlistStatus == FPRURLAllowlistStatusAllowed; } #pragma mark - Private helper methods /** Determines if the URL is denied by the SDK. * * @param URL the URL string to check. * @return YES if the URL is allowed by the SDK, NO otherwise. */ - (BOOL)isURLDeniedByTheSDK:(NSString *)URL { BOOL shouldDenyURL = NO; for (NSString *denyListURL in GetSystemDenyListURLStrings()) { if ([URL hasPrefix:denyListURL]) { shouldDenyURL = YES; break; } } return shouldDenyURL; } /** Determines if the URL is allowed by the Developer. * * @param URL The URL string to check. * @return FPRURLAllowlistStatusAllowed if the URL is allowed, * FPRURLAllowlistStatusNotAllowed if the URL is not allowed, or * FPRURLAllowlistStatusDoesNotExist if the allowlist does not exist. */ - (FPRURLAllowlistStatus)isURLAllowed:(NSString *)URL { if (self.allowlistDomains && !self.disablePlist) { for (NSString *allowlistDomain in self.allowlistDomains) { NSURLComponents *components = [[NSURLComponents alloc] initWithString:URL]; if ([components.host containsString:allowlistDomain]) { return FPRURLAllowlistStatusAllowed; } } return FPRURLAllowlistStatusNotAllowed; } return FPRURLAllowlistStatusDoesNotExist; } /** Retrieves the allowlist from an Info.plist. * * @return An array of the allowlist values, or nil if the allowlist key is not found. */ - (nullable NSArray *)retrieveAllowlistFromPlist { NSArray *allowlist = nil; id plistObject = [self.mainBundle objectForInfoDictionaryKey:kFPRAllowlistDomainsKey]; if (!plistObject) { NSBundle *localBundle = [NSBundle bundleForClass:[self class]]; plistObject = [localBundle objectForInfoDictionaryKey:kFPRAllowlistDomainsKey]; } if ([plistObject isKindOfClass:[NSArray class]]) { FPRLogInfo(kFPRURLAllowlistingEnabled, @"A domain allowlist was detected. Domains not " "explicitly allowlisted will not be instrumented."); allowlist = plistObject; } return allowlist; } @end