Skip to content

Commit 3aff256

Browse files
authored
Enable iOS plumbing for network security and add tests (#20492)
1 parent 3ebcde6 commit 3aff256

File tree

6 files changed

+149
-41
lines changed

6 files changed

+149
-41
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryM
920920
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm
921921
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h
922922
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm
923+
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProjectTest.mm
923924
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h
924925
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
925926
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm

shell/platform/darwin/ios/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ shared_library("ios_test_flutter") {
208208
]
209209
sources = [
210210
"framework/Source/FlutterBinaryMessengerRelayTest.mm",
211+
"framework/Source/FlutterDartProjectTest.mm",
211212
"framework/Source/FlutterEngineTest.mm",
212213
"framework/Source/FlutterPluginAppLifeCycleDelegateTest.m",
213214
"framework/Source/FlutterTextInputPluginTest.m",

shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm

Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -26,42 +26,6 @@
2626

2727
static const char* kApplicationKernelSnapshotFileName = "kernel_blob.bin";
2828

29-
// TODO(mehmetf): Announce this since it is breaking change then enable it.
30-
// static NSString* DomainNetworkPolicy(NSDictionary* appTransportSecurity) {
31-
// if (appTransportSecurity == nil) {
32-
// return @"";
33-
// }
34-
// //
35-
// https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsexceptiondomains
36-
// NSDictionary* exceptionDomains = [appTransportSecurity objectForKey:@"NSExceptionDomains"];
37-
// if (exceptionDomains == nil) {
38-
// return @"";
39-
// }
40-
// NSMutableArray* networkConfigArray = [[NSMutableArray alloc] init];
41-
// for (NSString* domain in exceptionDomains) {
42-
// NSDictionary* domainConfiguration = [exceptionDomains objectForKey:domain];
43-
// BOOL includesSubDomains =
44-
// [[domainConfiguration objectForKey:@"NSIncludesSubdomains"] boolValue];
45-
// BOOL allowsCleartextCommunication =
46-
// [[domainConfiguration objectForKey:@"NSExceptionAllowsInsecureHTTPLoads"] boolValue];
47-
// [networkConfigArray addObject:[NSArray arrayWithObjects:domain, includesSubDomains,
48-
// allowsCleartextCommunication, nil]];
49-
// }
50-
// NSData* jsonData = [NSJSONSerialization dataWithJSONObject:networkConfigArray
51-
// options:0
52-
// error:NULL];
53-
// return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
54-
// }
55-
56-
// TODO(mehmetf): Announce this since it is breaking change then enable it.
57-
// static bool AllowsArbitraryLoads(NSDictionary* appTransportSecurity) {
58-
// if (appTransportSecurity != nil) {
59-
// return [[appTransportSecurity objectForKey:@"NSAllowsArbitraryLoads"] boolValue];
60-
// } else {
61-
// return false;
62-
// }
63-
// }
64-
6529
static flutter::Settings DefaultSettingsForProcess(NSBundle* bundle = nil) {
6630
auto command_line = flutter::CommandLineFromNSProcessInfo();
6731

@@ -168,12 +132,19 @@
168132
}
169133
}
170134

171-
// TODO(mehmetf): Announce this since it is breaking change then enable it.
172135
// Domain network configuration
173-
// NSDictionary* appTransportSecurity =
174-
// [mainBundle objectForInfoDictionaryKey:@"NSAppTransportSecurity"];
175-
// settings.may_insecurely_connect_to_all_domains = AllowsArbitraryLoads(appTransportSecurity);
176-
// settings.domain_network_policy = DomainNetworkPolicy(appTransportSecurity).UTF8String;
136+
NSDictionary* appTransportSecurity =
137+
[mainBundle objectForInfoDictionaryKey:@"NSAppTransportSecurity"];
138+
settings.may_insecurely_connect_to_all_domains =
139+
[FlutterDartProject allowsArbitraryLoads:appTransportSecurity];
140+
settings.domain_network_policy =
141+
[FlutterDartProject domainNetworkPolicy:appTransportSecurity].UTF8String;
142+
143+
// TODO(mehmetf): We need to announce this change since it is breaking.
144+
// Remove these two lines after we announce and we know which release this is
145+
// going to be part of.
146+
settings.may_insecurely_connect_to_all_domains = true;
147+
settings.domain_network_policy = "";
177148

178149
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
179150
// There are no ownership concerns here as all mappings are owned by the
@@ -262,6 +233,34 @@ + (NSString*)flutterAssetsName:(NSBundle*)bundle {
262233
return flutterAssetsName;
263234
}
264235

236+
+ (NSString*)domainNetworkPolicy:(NSDictionary*)appTransportSecurity {
237+
// https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsexceptiondomains
238+
NSDictionary* exceptionDomains = [appTransportSecurity objectForKey:@"NSExceptionDomains"];
239+
if (exceptionDomains == nil) {
240+
return @"";
241+
}
242+
NSMutableArray* networkConfigArray = [[NSMutableArray alloc] init];
243+
for (NSString* domain in exceptionDomains) {
244+
NSDictionary* domainConfiguration = [exceptionDomains objectForKey:domain];
245+
// Default value is false.
246+
bool includesSubDomains =
247+
[[domainConfiguration objectForKey:@"NSIncludesSubdomains"] boolValue];
248+
bool allowsCleartextCommunication =
249+
[[domainConfiguration objectForKey:@"NSExceptionAllowsInsecureHTTPLoads"] boolValue];
250+
[networkConfigArray addObject:@[
251+
domain, includesSubDomains ? @YES : @NO, allowsCleartextCommunication ? @YES : @NO
252+
]];
253+
}
254+
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:networkConfigArray
255+
options:0
256+
error:NULL];
257+
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
258+
}
259+
260+
+ (bool)allowsArbitraryLoads:(NSDictionary*)appTransportSecurity {
261+
return [[appTransportSecurity objectForKey:@"NSAllowsArbitraryLoads"] boolValue];
262+
}
263+
265264
+ (NSString*)lookupKeyForAsset:(NSString*)asset {
266265
return [self lookupKeyForAsset:asset fromBundle:nil];
267266
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#import <XCTest/XCTest.h>
6+
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
7+
8+
FLUTTER_ASSERT_ARC
9+
10+
@interface FlutterDartProjectTest : XCTestCase
11+
@end
12+
13+
@implementation FlutterDartProjectTest
14+
15+
- (void)setUp {
16+
}
17+
18+
- (void)tearDown {
19+
}
20+
21+
- (void)testMainBundleSettingsAreCorrectlyParsed {
22+
NSBundle* mainBundle = [NSBundle mainBundle];
23+
NSDictionary* appTransportSecurity =
24+
[mainBundle objectForInfoDictionaryKey:@"NSAppTransportSecurity"];
25+
XCTAssertTrue([FlutterDartProject allowsArbitraryLoads:appTransportSecurity]);
26+
XCTAssertEqualObjects(
27+
@"[[\"invalid-site.com\",true,false],[\"sub.invalid-site.com\",false,false]]",
28+
[FlutterDartProject domainNetworkPolicy:appTransportSecurity]);
29+
}
30+
31+
- (void)testEmptySettingsAreCorrect {
32+
XCTAssertFalse([FlutterDartProject allowsArbitraryLoads:[[NSDictionary alloc] init]]);
33+
XCTAssertEqualObjects(@"", [FlutterDartProject domainNetworkPolicy:[[NSDictionary alloc] init]]);
34+
}
35+
36+
- (void)testAllowsArbitraryLoads {
37+
XCTAssertFalse([FlutterDartProject allowsArbitraryLoads:@{@"NSAllowsArbitraryLoads" : @false}]);
38+
XCTAssertTrue([FlutterDartProject allowsArbitraryLoads:@{@"NSAllowsArbitraryLoads" : @true}]);
39+
}
40+
41+
- (void)testProperlyFormedExceptionDomains {
42+
NSDictionary* domainInfoOne = @{
43+
@"NSIncludesSubdomains" : @false,
44+
@"NSExceptionAllowsInsecureHTTPLoads" : @true,
45+
@"NSExceptionMinimumTLSVersion" : @"4.0"
46+
};
47+
NSDictionary* domainInfoTwo = @{
48+
@"NSIncludesSubdomains" : @true,
49+
@"NSExceptionAllowsInsecureHTTPLoads" : @false,
50+
@"NSExceptionMinimumTLSVersion" : @"4.0"
51+
};
52+
NSDictionary* domainInfoThree = @{
53+
@"NSIncludesSubdomains" : @false,
54+
@"NSExceptionAllowsInsecureHTTPLoads" : @true,
55+
@"NSExceptionMinimumTLSVersion" : @"4.0"
56+
};
57+
NSDictionary* exceptionDomains = @{
58+
@"domain.name" : domainInfoOne,
59+
@"sub.domain.name" : domainInfoTwo,
60+
@"sub.two.domain.name" : domainInfoThree
61+
};
62+
NSDictionary* appTransportSecurity = @{@"NSExceptionDomains" : exceptionDomains};
63+
XCTAssertEqualObjects(@"[[\"domain.name\",false,true],[\"sub.domain.name\",true,false],"
64+
@"[\"sub.two.domain.name\",false,true]]",
65+
[FlutterDartProject domainNetworkPolicy:appTransportSecurity]);
66+
}
67+
68+
- (void)testExceptionDomainsWithMissingInfo {
69+
NSDictionary* domainInfoOne = @{@"NSExceptionMinimumTLSVersion" : @"4.0"};
70+
NSDictionary* domainInfoTwo = @{
71+
@"NSIncludesSubdomains" : @true,
72+
};
73+
NSDictionary* domainInfoThree = @{};
74+
NSDictionary* exceptionDomains = @{
75+
@"domain.name" : domainInfoOne,
76+
@"sub.domain.name" : domainInfoTwo,
77+
@"sub.two.domain.name" : domainInfoThree
78+
};
79+
NSDictionary* appTransportSecurity = @{@"NSExceptionDomains" : exceptionDomains};
80+
XCTAssertEqualObjects(@"[[\"domain.name\",false,false],[\"sub.domain.name\",true,false],"
81+
@"[\"sub.two.domain.name\",false,false]]",
82+
[FlutterDartProject domainNetworkPolicy:appTransportSecurity]);
83+
}
84+
85+
@end

shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ NS_ASSUME_NONNULL_BEGIN
2323
libraryOrNil:(nullable NSString*)dartLibraryOrNil;
2424

2525
+ (NSString*)flutterAssetsName:(NSBundle*)bundle;
26+
+ (NSString*)domainNetworkPolicy:(NSDictionary*)appTransportSecurity;
27+
+ (bool)allowsArbitraryLoads:(NSDictionary*)appTransportSecurity;
2628

2729
/**
2830
* The embedder can specify data that the isolate can request synchronously on launch. Engines

testing/ios/IosUnitTests/App/Info.plist

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,26 @@
2424
<string>LaunchScreen</string>
2525
<key>UIMainStoryboardFile</key>
2626
<string>Main</string>
27+
<key>NSAppTransportSecurity</key>
28+
<dict>
29+
<key>NSAllowsArbitraryLoads</key>
30+
<true/>
31+
<key>NSExceptionDomains</key>
32+
<dict>
33+
<key>invalid-site.com</key>
34+
<dict>
35+
<key>NSIncludesSubdomains</key>
36+
<true/>
37+
<key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
38+
<true/>
39+
</dict>
40+
<key>sub.invalid-site.com</key>
41+
<dict>
42+
<key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
43+
<false/>
44+
</dict>
45+
</dict>
46+
</dict>
2747
<key>UIRequiredDeviceCapabilities</key>
2848
<array>
2949
<string>armv7</string>

0 commit comments

Comments
 (0)