Enable iOS plumbing for network security and add tests (#20492)

This commit is contained in:
Mehmet Fidanboylu 2020-08-16 21:20:26 -07:00 committed by GitHub
parent 3ebcde6a17
commit 3aff256928
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 149 additions and 41 deletions

View File

@ -920,6 +920,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryM
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProjectTest.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm

View File

@ -208,6 +208,7 @@ shared_library("ios_test_flutter") {
]
sources = [
"framework/Source/FlutterBinaryMessengerRelayTest.mm",
"framework/Source/FlutterDartProjectTest.mm",
"framework/Source/FlutterEngineTest.mm",
"framework/Source/FlutterPluginAppLifeCycleDelegateTest.m",
"framework/Source/FlutterTextInputPluginTest.m",

View File

@ -26,42 +26,6 @@ extern const intptr_t kPlatformStrongDillSize;
static const char* kApplicationKernelSnapshotFileName = "kernel_blob.bin";
// TODO(mehmetf): Announce this since it is breaking change then enable it.
// static NSString* DomainNetworkPolicy(NSDictionary* appTransportSecurity) {
// if (appTransportSecurity == nil) {
// return @"";
// }
// //
// https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsexceptiondomains
// NSDictionary* exceptionDomains = [appTransportSecurity objectForKey:@"NSExceptionDomains"];
// if (exceptionDomains == nil) {
// return @"";
// }
// NSMutableArray* networkConfigArray = [[NSMutableArray alloc] init];
// for (NSString* domain in exceptionDomains) {
// NSDictionary* domainConfiguration = [exceptionDomains objectForKey:domain];
// BOOL includesSubDomains =
// [[domainConfiguration objectForKey:@"NSIncludesSubdomains"] boolValue];
// BOOL allowsCleartextCommunication =
// [[domainConfiguration objectForKey:@"NSExceptionAllowsInsecureHTTPLoads"] boolValue];
// [networkConfigArray addObject:[NSArray arrayWithObjects:domain, includesSubDomains,
// allowsCleartextCommunication, nil]];
// }
// NSData* jsonData = [NSJSONSerialization dataWithJSONObject:networkConfigArray
// options:0
// error:NULL];
// return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// }
// TODO(mehmetf): Announce this since it is breaking change then enable it.
// static bool AllowsArbitraryLoads(NSDictionary* appTransportSecurity) {
// if (appTransportSecurity != nil) {
// return [[appTransportSecurity objectForKey:@"NSAllowsArbitraryLoads"] boolValue];
// } else {
// return false;
// }
// }
static flutter::Settings DefaultSettingsForProcess(NSBundle* bundle = nil) {
auto command_line = flutter::CommandLineFromNSProcessInfo();
@ -168,12 +132,19 @@ static flutter::Settings DefaultSettingsForProcess(NSBundle* bundle = nil) {
}
}
// TODO(mehmetf): Announce this since it is breaking change then enable it.
// Domain network configuration
// NSDictionary* appTransportSecurity =
// [mainBundle objectForInfoDictionaryKey:@"NSAppTransportSecurity"];
// settings.may_insecurely_connect_to_all_domains = AllowsArbitraryLoads(appTransportSecurity);
// settings.domain_network_policy = DomainNetworkPolicy(appTransportSecurity).UTF8String;
NSDictionary* appTransportSecurity =
[mainBundle objectForInfoDictionaryKey:@"NSAppTransportSecurity"];
settings.may_insecurely_connect_to_all_domains =
[FlutterDartProject allowsArbitraryLoads:appTransportSecurity];
settings.domain_network_policy =
[FlutterDartProject domainNetworkPolicy:appTransportSecurity].UTF8String;
// TODO(mehmetf): We need to announce this change since it is breaking.
// Remove these two lines after we announce and we know which release this is
// going to be part of.
settings.may_insecurely_connect_to_all_domains = true;
settings.domain_network_policy = "";
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// There are no ownership concerns here as all mappings are owned by the
@ -262,6 +233,34 @@ static flutter::Settings DefaultSettingsForProcess(NSBundle* bundle = nil) {
return flutterAssetsName;
}
+ (NSString*)domainNetworkPolicy:(NSDictionary*)appTransportSecurity {
// https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsexceptiondomains
NSDictionary* exceptionDomains = [appTransportSecurity objectForKey:@"NSExceptionDomains"];
if (exceptionDomains == nil) {
return @"";
}
NSMutableArray* networkConfigArray = [[NSMutableArray alloc] init];
for (NSString* domain in exceptionDomains) {
NSDictionary* domainConfiguration = [exceptionDomains objectForKey:domain];
// Default value is false.
bool includesSubDomains =
[[domainConfiguration objectForKey:@"NSIncludesSubdomains"] boolValue];
bool allowsCleartextCommunication =
[[domainConfiguration objectForKey:@"NSExceptionAllowsInsecureHTTPLoads"] boolValue];
[networkConfigArray addObject:@[
domain, includesSubDomains ? @YES : @NO, allowsCleartextCommunication ? @YES : @NO
]];
}
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:networkConfigArray
options:0
error:NULL];
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
+ (bool)allowsArbitraryLoads:(NSDictionary*)appTransportSecurity {
return [[appTransportSecurity objectForKey:@"NSAllowsArbitraryLoads"] boolValue];
}
+ (NSString*)lookupKeyForAsset:(NSString*)asset {
return [self lookupKeyForAsset:asset fromBundle:nil];
}

View File

@ -0,0 +1,85 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <XCTest/XCTest.h>
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
FLUTTER_ASSERT_ARC
@interface FlutterDartProjectTest : XCTestCase
@end
@implementation FlutterDartProjectTest
- (void)setUp {
}
- (void)tearDown {
}
- (void)testMainBundleSettingsAreCorrectlyParsed {
NSBundle* mainBundle = [NSBundle mainBundle];
NSDictionary* appTransportSecurity =
[mainBundle objectForInfoDictionaryKey:@"NSAppTransportSecurity"];
XCTAssertTrue([FlutterDartProject allowsArbitraryLoads:appTransportSecurity]);
XCTAssertEqualObjects(
@"[[\"invalid-site.com\",true,false],[\"sub.invalid-site.com\",false,false]]",
[FlutterDartProject domainNetworkPolicy:appTransportSecurity]);
}
- (void)testEmptySettingsAreCorrect {
XCTAssertFalse([FlutterDartProject allowsArbitraryLoads:[[NSDictionary alloc] init]]);
XCTAssertEqualObjects(@"", [FlutterDartProject domainNetworkPolicy:[[NSDictionary alloc] init]]);
}
- (void)testAllowsArbitraryLoads {
XCTAssertFalse([FlutterDartProject allowsArbitraryLoads:@{@"NSAllowsArbitraryLoads" : @false}]);
XCTAssertTrue([FlutterDartProject allowsArbitraryLoads:@{@"NSAllowsArbitraryLoads" : @true}]);
}
- (void)testProperlyFormedExceptionDomains {
NSDictionary* domainInfoOne = @{
@"NSIncludesSubdomains" : @false,
@"NSExceptionAllowsInsecureHTTPLoads" : @true,
@"NSExceptionMinimumTLSVersion" : @"4.0"
};
NSDictionary* domainInfoTwo = @{
@"NSIncludesSubdomains" : @true,
@"NSExceptionAllowsInsecureHTTPLoads" : @false,
@"NSExceptionMinimumTLSVersion" : @"4.0"
};
NSDictionary* domainInfoThree = @{
@"NSIncludesSubdomains" : @false,
@"NSExceptionAllowsInsecureHTTPLoads" : @true,
@"NSExceptionMinimumTLSVersion" : @"4.0"
};
NSDictionary* exceptionDomains = @{
@"domain.name" : domainInfoOne,
@"sub.domain.name" : domainInfoTwo,
@"sub.two.domain.name" : domainInfoThree
};
NSDictionary* appTransportSecurity = @{@"NSExceptionDomains" : exceptionDomains};
XCTAssertEqualObjects(@"[[\"domain.name\",false,true],[\"sub.domain.name\",true,false],"
@"[\"sub.two.domain.name\",false,true]]",
[FlutterDartProject domainNetworkPolicy:appTransportSecurity]);
}
- (void)testExceptionDomainsWithMissingInfo {
NSDictionary* domainInfoOne = @{@"NSExceptionMinimumTLSVersion" : @"4.0"};
NSDictionary* domainInfoTwo = @{
@"NSIncludesSubdomains" : @true,
};
NSDictionary* domainInfoThree = @{};
NSDictionary* exceptionDomains = @{
@"domain.name" : domainInfoOne,
@"sub.domain.name" : domainInfoTwo,
@"sub.two.domain.name" : domainInfoThree
};
NSDictionary* appTransportSecurity = @{@"NSExceptionDomains" : exceptionDomains};
XCTAssertEqualObjects(@"[[\"domain.name\",false,false],[\"sub.domain.name\",true,false],"
@"[\"sub.two.domain.name\",false,false]]",
[FlutterDartProject domainNetworkPolicy:appTransportSecurity]);
}
@end

View File

@ -23,6 +23,8 @@ NS_ASSUME_NONNULL_BEGIN
libraryOrNil:(nullable NSString*)dartLibraryOrNil;
+ (NSString*)flutterAssetsName:(NSBundle*)bundle;
+ (NSString*)domainNetworkPolicy:(NSDictionary*)appTransportSecurity;
+ (bool)allowsArbitraryLoads:(NSDictionary*)appTransportSecurity;
/**
* The embedder can specify data that the isolate can request synchronously on launch. Engines

View File

@ -24,6 +24,26 @@
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>invalid-site.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
<key>sub.invalid-site.com</key>
<dict>
<key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
<false/>
</dict>
</dict>
</dict>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>