Reland "Revert commits part of #40548 which still has some design debates and breaks valid local network http traffic"

This is a reland of d84f359786

Original change's description:
> Revert commits part of #40548 which still has some design debates and breaks valid local network http traffic
>
> Bug: https://github.com/flutter/flutter/issues/72723
> Change-Id: Ib5f809fccf1fad69add441fd40c463da8dc8be36
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/192953
> Auto-Submit: Xiao Yu <xster@google.com>
> Commit-Queue: Vyacheslav Egorov <vegorov@google.com>
> Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
> Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>

Bug: https://github.com/flutter/flutter/issues/72723
Change-Id: I466d888b3f324a996f0bef0463e7ab3df3179c56
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/195485
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
Commit-Queue: Xiao Yu <xster@google.com>
This commit is contained in:
Xiao Yu 2021-04-16 20:25:36 +00:00 committed by commit-bot@chromium.org
parent 7eda3cfe76
commit d968304570
28 changed files with 363 additions and 531 deletions

View file

@ -170,8 +170,8 @@ Evaluated: MapLiteral @ org-dartlang-testcase:///mock_http_headers.dart:13:7 ->
Evaluated: SymbolLiteral @ org-dartlang-testcase:///mock_http_headers.dart:13:7 -> SymbolConstant(#noFolding)
Evaluated: ListLiteral @ org-dartlang-testcase:///mock_http_headers.dart:13:7 -> ListConstant(const <Type*>[])
Evaluated: MapLiteral @ org-dartlang-testcase:///mock_http_headers.dart:13:7 -> InstanceConstant(const _ImmutableMap<Symbol*, dynamic>{_ImmutableMap._kvPairs: const <dynamic>[]})
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:761:8 -> SymbolConstant(#clear)
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:761:8 -> ListConstant(const <Type*>[])
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:761:8 -> ListConstant(const <dynamic>[])
Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:761:8 -> InstanceConstant(const _ImmutableMap<Symbol*, dynamic>{_ImmutableMap._kvPairs: const <dynamic>[]})
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:762:8 -> SymbolConstant(#clear)
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:762:8 -> ListConstant(const <Type*>[])
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:762:8 -> ListConstant(const <dynamic>[])
Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:762:8 -> InstanceConstant(const _ImmutableMap<Symbol*, dynamic>{_ImmutableMap._kvPairs: const <dynamic>[]})
Extra constant evaluation: evaluated: 268, effectively constant: 91

View file

@ -0,0 +1,11 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of dart._http;
/// Embedder-specific `dart:_http` configuration.
/// [HttpClient] will disallow HTTP URLs if this value is set to `false`.
@pragma("vm:entry-point")
bool _embedderAllowsHttp = true;

View file

@ -24,6 +24,7 @@ import 'dart:io';
import 'dart:typed_data';
part 'crypto.dart';
part 'embedder_config.dart';
part 'http_date.dart';
part 'http_headers.dart';
part 'http_impl.dart';

View file

@ -2620,6 +2620,40 @@ class _HttpClient implements HttpClient {
set findProxy(String f(Uri uri)?) => _findProxy = f;
static void _startRequestTimelineEvent(
TimelineTask? timeline, String method, Uri uri) {
timeline?.start('HTTP CLIENT ${method.toUpperCase()}', arguments: {
'method': method.toUpperCase(),
'uri': uri.toString(),
});
}
/// Whether HTTP requests are currently allowed.
///
/// If the [Zone] variable `#dart.library.io.allow_http` is set to a boolean,
/// it determines whether the HTTP protocol is allowed. If the zone variable
/// is set to any other non-null value, HTTP is not allowed.
/// Otherwise, if the `dart.library.io.allow_http` environment flag
/// is set to `false`, HTTP is not allowed.
/// Otherwise, [_embedderAllowsHttp] determines the result.
bool get _isHttpAllowed {
final zoneOverride = Zone.current[#dart.library.io.allow_http];
if (zoneOverride != null) return true == zoneOverride;
bool envOverride =
bool.fromEnvironment("dart.library.io.allow_http", defaultValue: true);
return envOverride && _embedderAllowsHttp;
}
bool _isLoopback(String host) {
if (host.isEmpty) return false;
if ("localhost" == host) return true;
try {
return InternetAddress(host).isLoopback;
} on ArgumentError {
return false;
}
}
Future<_HttpClientRequest> _openUrl(String method, Uri uri) {
if (_closing) {
throw new StateError("Client is closed");
@ -2641,9 +2675,11 @@ class _HttpClient implements HttpClient {
}
bool isSecure = uri.isScheme("https");
if (!isSecure && !isInsecureConnectionAllowed(uri.host)) {
throw new StateError("Insecure HTTP is not allowed by platform: $uri");
if (!_isHttpAllowed && !isSecure && !_isLoopback(uri.host)) {
throw new StateError(
"Insecure HTTP is not allowed by the current platform: $uri");
}
int port = uri.port;
if (port == 0) {
port =

View file

@ -36,13 +36,18 @@ abstract class _EmbedderConfig {
/// to all domains.
///
/// This setting can be overridden by per-domain policies.
@Deprecated(
"To be re-implemented in https://github.com/flutter/flutter/issues/54448")
@pragma('vm:entry-point')
static bool _mayInsecurelyConnectToAllDomains = true;
/// Domain network policies set by embedder.
@Deprecated(
"To be re-implemented in https://github.com/flutter/flutter/issues/54448")
@pragma('vm:entry-point')
static void _setDomainPolicies(String domainNetworkPolicyJson) {
_domainPolicies = _constructDomainPolicies(domainNetworkPolicyJson);
// Doesn't do anything because the implementation has been reverted in
// https://github.com/flutter/flutter/issues/72723.
}
// TODO(zra): Consider adding:

View file

@ -6,6 +6,9 @@ part of dart.io;
/// Whether insecure connections to [host] are allowed.
///
/// This API is deprecated and always returns true. See
/// https://github.com/flutter/flutter/issues/72723 for more details.
///
/// [host] must be a [String] or [InternetAddress].
///
/// If any of the domain policies match [host], the matching policy will make
@ -14,179 +17,7 @@ part of dart.io;
/// used.
///
/// Loopback addresses are always allowed.
@Deprecated("See https://github.com/flutter/flutter/issues/54448 for followup")
bool isInsecureConnectionAllowed(dynamic host) {
String hostString;
if (host is String) {
try {
if ("localhost" == host || InternetAddress(host).isLoopback) return true;
} on ArgumentError {
// Assume not loopback.
}
hostString = host;
} else if (host is InternetAddress) {
if (host.isLoopback) return true;
hostString = host.host;
} else {
throw ArgumentError.value(
host, "host", "Must be a String or InternetAddress");
}
final topMatchedPolicy = _findBestDomainNetworkPolicy(hostString);
final envOverride = bool.fromEnvironment(
"dart.library.io.may_insecurely_connect_to_all_domains",
defaultValue: true);
return topMatchedPolicy?.allowInsecureConnections ??
(envOverride && _EmbedderConfig._mayInsecurelyConnectToAllDomains);
}
/// Policy for a specific domain.
///
/// [_DomainNetworkPolicy] can be used to create exceptions to the global
/// network policy.
class _DomainNetworkPolicy {
/// https://tools.ietf.org/html/rfc1034#:~:text=Name%20space%20specifications
///
/// We specifically do not allow IP addresses.
static final _domainMatcher = RegExp(
r"^(?:[a-z\d-]{1,63}\.)+[a-z][a-z\d-]{0,62}$",
caseSensitive: false);
/// The domain on which the policy is being set.
///
/// This cannot be a numeric IP address.
///
/// For example: `example.com`.
final String domain;
/// Whether to allow insecure socket connections for this domain.
final bool allowInsecureConnections;
/// Whether this domain policy covers sub-domains as well.
///
/// If this is true, all subdomains inherit the same policy. For instance,
/// a policy set on `example.com` would apply to `*.example.com` such as
/// `subdomain.example.com` or `www.example.com`.
final bool includesSubDomains;
/// Creates a new domain exception in the network policy.
///
/// [domain] is the domain on which the policy is being set.
///
/// [includesSubDomains] determines whether the policy applies to
/// all sub domains. If this is set to true, all subdomains inherit the
/// same policy. For instance, a policy set on `example.com` would apply to
/// `*.example.com` such as `subdomain.example.com` or `www.example.com`.
///
/// [allowInsecureConnections] determines whether to allow insecure socket
/// connections for this [domain].
_DomainNetworkPolicy(this.domain,
{this.includesSubDomains = false,
this.allowInsecureConnections = false}) {
if (domain.length > 255 || !_domainMatcher.hasMatch(domain)) {
throw ArgumentError.value(domain, "domain", "Invalid domain name");
}
}
/// Calculates how well the policy matches to a given host string.
///
/// A host matches a [policy] if it ends with its [domain].
///
/// A score is given to such a match depending on the specificity of the
/// [domain]:
///
/// * A longer domain receives a higher score.
/// * A domain that does not allow sub domains receives a higher score.
///
/// Returns -1 if the policy does not match.
int matchScore(String host) {
final domainLength = domain.length;
final hostLength = host.length;
final lengthDelta = hostLength - domainLength;
if (host.endsWith(domain) &&
(lengthDelta == 0 ||
includesSubDomains && host.codeUnitAt(lengthDelta - 1) == 0x2e)) {
return domainLength * 2 + (includesSubDomains ? 0 : 1);
}
return -1;
}
/// Checks whether the [policy] to be added conflicts with existing policies.
///
/// Returns [true] if policy is safe to add to existing policy set and [false]
/// if policy can safely be ignored.
///
/// Throws [ArgumentError] if a conflict is detected.
bool checkConflict(List<_DomainNetworkPolicy> existingPolicies) {
for (final existingPolicy in existingPolicies) {
if (includesSubDomains == existingPolicy.includesSubDomains &&
domain == existingPolicy.domain) {
if (allowInsecureConnections ==
existingPolicy.allowInsecureConnections) {
// This is a duplicate policy
return false;
}
throw StateError("Contradiction in the domain security policies: "
"'$this' contradicts '$existingPolicy'");
}
}
return true;
}
/// This is used for encoding information about the policy in user visible
/// errors.
@override
String toString() {
final subDomainPrefix = includesSubDomains ? '*.' : '';
final insecureConnectionPermission =
allowInsecureConnections ? 'Allows' : 'Disallows';
return "$subDomainPrefix$domain: "
"$insecureConnectionPermission insecure connections";
}
}
/// Finds the top [DomainNetworkPolicy] instance that match given a single
/// [domain].
///
/// We order the policies according to how specific they are. The final policy
/// for a given [domain] is determined by the top matching
/// [DomainNetworkPolicy].
///
/// Returns null if there's no matching policy.
_DomainNetworkPolicy? _findBestDomainNetworkPolicy(String domain) {
var topScore = 0;
_DomainNetworkPolicy? topPolicy;
for (final _DomainNetworkPolicy policy in _domainPolicies) {
final score = policy.matchScore(domain);
if (score > topScore) {
topScore = score;
topPolicy = policy;
}
}
return topPolicy;
}
/// Domain level policies that dart:io is enforcing.
late List<_DomainNetworkPolicy> _domainPolicies =
_constructDomainPolicies(null);
List<_DomainNetworkPolicy> _constructDomainPolicies(
String? domainPoliciesString) {
final domainPolicies = <_DomainNetworkPolicy>[];
domainPoliciesString ??= String.fromEnvironment(
"dart.library.io.domain_network_policies",
defaultValue: "");
if (domainPoliciesString.isNotEmpty) {
final List<dynamic> policiesJson = json.decode(domainPoliciesString);
for (final List<dynamic> policyJson in policiesJson) {
assert(policyJson.length == 3);
final policy = _DomainNetworkPolicy(
policyJson[0],
includesSubDomains: policyJson[1],
allowInsecureConnections: policyJson[2],
);
if (policy.checkConflict(domainPolicies)) {
domainPolicies.add(policy);
}
}
}
return domainPolicies;
return true;
}

View file

@ -1,73 +0,0 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This test file disallows VM from accepting insecure connections to all
// domains and tests that HTTP connections to non-localhost targets fail.
// HTTPS connections and localhost connections should still succeed.
// SharedOptions=-Ddart.library.io.may_insecurely_connect_to_all_domains=false
import "dart:async";
import "dart:io";
import "package:async_helper/async_helper.dart";
import "http_bind_test.dart";
Future<String> getLocalHostIP() async {
final interfaces = await NetworkInterface.list(
includeLoopback: false, type: InternetAddressType.IPv4);
return interfaces.first.addresses.first.address;
}
Future<void> testBanHttp(String serverHost,
Future<void> testCode(HttpClient client, Uri uri)) async {
final httpClient = new HttpClient();
final server = await HttpServer.bind(serverHost, 0);
final uri = Uri(scheme: 'http', host: serverHost, port: server.port);
try {
await testCode(httpClient, uri);
} finally {
httpClient.close(force: true);
await server.close();
}
}
Future<void> testWithLoopback() async {
await testBanHttp("127.0.0.1", (httpClient, uri) async {
await asyncTest(() async =>
await httpClient.getUrl(Uri.parse('http://localhost:${uri.port}')));
await asyncTest(() async =>
await httpClient.getUrl(Uri.parse('http://127.0.0.1:${uri.port}')));
});
}
Future<void> testWithIPv6() async {
if (await supportsIPV6()) {
await testBanHttp("::1", (httpClient, uri) async {
await asyncTest(() => httpClient.getUrl(uri));
});
}
}
Future<void> testWithHttps() async {
await testBanHttp(await getLocalHostIP(), (httpClient, uri) async {
asyncExpectThrows(
() => httpClient.getUrl(Uri(
scheme: 'https',
host: uri.host,
port: uri.port,
)),
(e) => e is SocketException || e is HandshakeException);
});
}
main() {
asyncStart();
Future.wait(<Future>[
testWithLoopback(),
testWithIPv6(),
testWithHttps(),
]).then((_) => asyncEnd());
}

View file

@ -0,0 +1,69 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// SharedOptions=-Ddart.library.io.allow_http=false
import 'dart:async';
import 'dart:io';
import "package:async_helper/async_helper.dart";
import 'http_ban_http_normal_test.dart';
import 'http_bind_test.dart';
Future<void> testWithHostname() async {
await testBanHttp(await getLocalHostIP(), (httpClient, httpUri) async {
asyncExpectThrows(
() async => await httpClient.getUrl(httpUri), (e) => e is StateError);
asyncExpectThrows(
() async => await runZoned(() => httpClient.getUrl(httpUri),
zoneValues: {#dart.library.io.allow_http: 'foo'}),
(e) => e is StateError);
asyncExpectThrows(
() async => await runZoned(() => httpClient.getUrl(httpUri),
zoneValues: {#dart.library.io.allow_http: false}),
(e) => e is StateError);
await asyncTest(() => runZoned(() => httpClient.getUrl(httpUri),
zoneValues: {#dart.library.io.allow_http: true}));
});
}
Future<void> testWithLoopback() async {
await testBanHttp("127.0.0.1", (httpClient, uri) async {
await asyncTest(
() => httpClient.getUrl(Uri.parse('http://localhost:${uri.port}')));
await asyncTest(
() => httpClient.getUrl(Uri.parse('http://127.0.0.1:${uri.port}')));
});
}
Future<void> testWithIPv6() async {
if (await supportsIPV6()) {
await testBanHttp("::1", (httpClient, uri) async {
await asyncTest(() => httpClient.getUrl(uri));
});
}
}
Future<void> testWithHTTPS() async {
await testBanHttp(await getLocalHostIP(), (httpClient, uri) async {
asyncExpectThrows(
() => httpClient.getUrl(Uri(
scheme: 'https',
host: uri.host,
port: uri.port,
)),
(e) => e is SocketException || e is HandshakeException);
});
}
main() {
asyncStart();
Future.wait(<Future>[
testWithHostname(),
testWithLoopback(),
testWithIPv6(),
testWithHTTPS(),
]).then((_) => asyncEnd());
}

View file

@ -0,0 +1,44 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import "package:async_helper/async_helper.dart";
Future<String> getLocalHostIP() async {
final interfaces = await NetworkInterface.list(
includeLoopback: false, type: InternetAddressType.IPv4);
return interfaces.first.addresses.first.address;
}
Future<void> testBanHttp(String serverHost,
Future<void> testCode(HttpClient client, Uri uri)) async {
final httpClient = new HttpClient();
final server = await HttpServer.bind(serverHost, 0);
final uri = Uri(scheme: 'http', host: serverHost, port: server.port);
try {
await testCode(httpClient, uri);
} finally {
httpClient.close(force: true);
await server.close();
}
}
main() async {
await asyncTest(() async {
final host = await getLocalHostIP();
// Normal HTTP request succeeds.
await testBanHttp(host, (httpClient, uri) async {
await asyncTest(() => httpClient.getUrl(uri));
});
// We can ban HTTP explicitly.
await testBanHttp(host, (httpClient, uri) async {
asyncExpectThrows(
() async => await runZoned(() => httpClient.getUrl(uri),
zoneValues: {#dart.library.io.allow_http: false}),
(e) => e is StateError);
});
});
}

View file

@ -1,29 +0,0 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This test file disallows VM from accepting insecure connections to all
// domains and tests that HTTP connections to non-localhost targets fail.
// HTTPS connections and localhost connections should still succeed.
// SharedOptions=-Ddart.library.io.may_insecurely_connect_to_all_domains=false
import "dart:async";
import "dart:io";
import "package:async_helper/async_helper.dart";
Future<void> testWithHostname() async {
final httpClient = new HttpClient();
final uri = Uri(scheme: 'http', host: 'domain.invalid', port: 12345);
asyncExpectThrows(
() async => await httpClient.getUrl(uri),
(e) =>
e is StateError &&
e.message.contains("Insecure HTTP is not allowed by platform"));
}
main() {
asyncStart();
testWithHostname().then((_) => asyncEnd());
}

View file

@ -19,6 +19,7 @@ import "../../../sdk/lib/internal/internal.dart"
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
part "../../../sdk/lib/_http/crypto.dart";
part "../../../sdk/lib/_http/embedder_config.dart";
part "../../../sdk/lib/_http/http_impl.dart";
part "../../../sdk/lib/_http/http_date.dart";
part "../../../sdk/lib/_http/http_parser.dart";

View file

@ -19,6 +19,7 @@ import "../../../sdk/lib/internal/internal.dart"
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
part "../../../sdk/lib/_http/crypto.dart";
part "../../../sdk/lib/_http/embedder_config.dart";
part "../../../sdk/lib/_http/http_impl.dart";
part "../../../sdk/lib/_http/http_date.dart";
part "../../../sdk/lib/_http/http_parser.dart";

View file

@ -19,6 +19,7 @@ import "../../../sdk/lib/internal/internal.dart"
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
part "../../../sdk/lib/_http/crypto.dart";
part "../../../sdk/lib/_http/embedder_config.dart";
part "../../../sdk/lib/_http/http_impl.dart";
part "../../../sdk/lib/_http/http_date.dart";
part "../../../sdk/lib/_http/http_parser.dart";

View file

@ -1,40 +0,0 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// SharedOptions=-Ddart.library.io.domain_network_policies=[["foobar.com",true,true],["foobar.com",true,true],["baz.foobar.com",true,true],["baz.foobar.com",false,false]] -Ddart.library.io.may_insecurely_connect_to_all_domains=false
import 'dart:io';
import "package:expect/expect.dart";
void _checkAllows(List<String> domains) {
for (final domain in domains) {
Expect.isTrue(
isInsecureConnectionAllowed(domain), "$domain should be allowed.");
}
}
void _checkDenies(List<String> domains) {
for (final domain in domains) {
Expect.isFalse(
isInsecureConnectionAllowed(domain), "$domain should not be allowed.");
}
}
void main() {
// These have no policy but the default is false.
_checkDenies([
"mailfoobar.com",
"abc.com",
"oobar.com",
"foobar.co",
"128.221.55.31",
"fe80::4607:0bff:fea0:7747%invalid",
]);
// These are explicitly denied.
_checkDenies(["baz.foobar.com"]);
_checkAllows(
["foobar.com", "test.baz.foobar.com", "test2.test.baz.foobar.com"]);
_checkAllows(["::1", "localhost"]);
}

View file

@ -1,15 +0,0 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// SharedOptions=-Ddart.library.io.domain_network_policies=[["com",true,true]]
import 'dart:io';
import "package:expect/expect.dart";
// This test passes in an invalid domain as a network policy and checks that
// loading the policies throws.
void main() {
Expect.throwsArgumentError(() => isInsecureConnectionAllowed("test.com"));
}

View file

@ -0,0 +1,32 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';
import "package:expect/expect.dart";
void _checkAllows(List<String> domains) {
for (final domain in domains) {
Expect.isTrue(
isInsecureConnectionAllowed(domain), "$domain should be allowed.");
}
}
void main() {
// All domains and addresses are allowed.
_checkAllows([
"mailfoobar.com",
"abc.com",
"oobar.com",
"foobar.co",
"128.221.55.31",
"fe80::4607:0bff:fea0:7747%invalid",
"baz.foobar.com",
"foobar.com",
"test.baz.foobar.com",
"test2.test.baz.foobar.com",
"::1",
"localhost",
]);
}

View file

@ -1,14 +0,0 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// SharedOptions=-Ddart.library.io.domain_network_policies=[["baz.foobar.com",true,true],["baz.foobar.com",false,false]]
import 'dart:io';
import "package:expect/expect.dart";
void main() {
Expect.isFalse(isInsecureConnectionAllowed("baz.foobar.com"));
Expect.isTrue(isInsecureConnectionAllowed("test.baz.foobar.com"));
}

View file

@ -20,6 +20,7 @@ import "../../../sdk/lib/internal/internal.dart"
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
part "../../../sdk/lib/_http/crypto.dart";
part "../../../sdk/lib/_http/embedder_config.dart";
part "../../../sdk/lib/_http/http_impl.dart";
part "../../../sdk/lib/_http/http_date.dart";
part "../../../sdk/lib/_http/http_parser.dart";

View file

@ -12,10 +12,8 @@ fragmentation_typed_data_test: Skip # This test crashes on the bot, but not loca
[ $system == android ]
entrypoints_verification_test: Skip # Requires shared objects which the test script doesn't "adb push".
io/http_ban_http_allowed_cases_test: Skip # Depends on grabbing local hostname which isn't supported.
io/network_policy_configuration_test: Skip # Can't pass -D params containing quotes to adb.
io/network_policy_invalid_domain_test: Skip # Can't pass -D params containing quotes to adb.
io/network_policy_tie_breaker_test: Skip # Can't pass -D params containing quotes to adb.
io/http_ban_http_embedder_test: Skip # Requires http server bound to non-loopback; not provided by system.
io/http_ban_http_normal_test: Skip # Requires http server bound to non-loopback; not provided by system.
[ $arch == ia32 && $builder_tag == optimization_counter_threshold ]
io/file_lock_test: SkipSlow # Timeout

View file

@ -1,73 +0,0 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This test file disallows VM from accepting insecure connections to all
// domains and tests that HTTP connections to non-localhost targets fail.
// HTTPS connections and localhost connections should still succeed.
// SharedOptions=-Ddart.library.io.may_insecurely_connect_to_all_domains=false
import "dart:async";
import "dart:io";
import "package:async_helper/async_helper.dart";
import "http_bind_test.dart";
Future<String> getLocalHostIP() async {
final interfaces = await NetworkInterface.list(
includeLoopback: false, type: InternetAddressType.IPv4);
return interfaces.first.addresses.first.address;
}
Future<void> testBanHttp(String serverHost,
Future<void> testCode(HttpClient client, Uri uri)) async {
final httpClient = new HttpClient();
final server = await HttpServer.bind(serverHost, 0);
final uri = Uri(scheme: 'http', host: serverHost, port: server.port);
try {
await testCode(httpClient, uri);
} finally {
httpClient.close(force: true);
await server.close();
}
}
Future<void> testWithLoopback() async {
await testBanHttp("127.0.0.1", (httpClient, uri) async {
await asyncTest(() async =>
await httpClient.getUrl(Uri.parse('http://localhost:${uri.port}')));
await asyncTest(() async =>
await httpClient.getUrl(Uri.parse('http://127.0.0.1:${uri.port}')));
});
}
Future<void> testWithIPv6() async {
if (await supportsIPV6()) {
await testBanHttp("::1", (httpClient, uri) async {
await asyncTest(() => httpClient.getUrl(uri));
});
}
}
Future<void> testWithHttps() async {
await testBanHttp(await getLocalHostIP(), (httpClient, uri) async {
asyncExpectThrows(
() => httpClient.getUrl(Uri(
scheme: 'https',
host: uri.host,
port: uri.port,
)),
(e) => e is SocketException || e is HandshakeException);
});
}
main() {
asyncStart();
Future.wait(<Future>[
testWithLoopback(),
testWithIPv6(),
testWithHttps(),
]).then((_) => asyncEnd());
}

View file

@ -0,0 +1,69 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// SharedOptions=-Ddart.library.io.allow_http=false
import 'dart:async';
import 'dart:io';
import "package:async_helper/async_helper.dart";
import 'http_ban_http_normal_test.dart';
import 'http_bind_test.dart';
Future<void> testWithHostname() async {
await testBanHttp(await getLocalHostIP(), (httpClient, httpUri) async {
asyncExpectThrows(
() async => await httpClient.getUrl(httpUri), (e) => e is StateError);
asyncExpectThrows(
() async => await runZoned(() => httpClient.getUrl(httpUri),
zoneValues: {#dart.library.io.allow_http: 'foo'}),
(e) => e is StateError);
asyncExpectThrows(
() async => await runZoned(() => httpClient.getUrl(httpUri),
zoneValues: {#dart.library.io.allow_http: false}),
(e) => e is StateError);
await asyncTest(() => runZoned(() => httpClient.getUrl(httpUri),
zoneValues: {#dart.library.io.allow_http: true}));
});
}
Future<void> testWithLoopback() async {
await testBanHttp("127.0.0.1", (httpClient, uri) async {
await asyncTest(
() => httpClient.getUrl(Uri.parse('http://localhost:${uri.port}')));
await asyncTest(
() => httpClient.getUrl(Uri.parse('http://127.0.0.1:${uri.port}')));
});
}
Future<void> testWithIPv6() async {
if (await supportsIPV6()) {
await testBanHttp("::1", (httpClient, uri) async {
await asyncTest(() => httpClient.getUrl(uri));
});
}
}
Future<void> testWithHTTPS() async {
await testBanHttp(await getLocalHostIP(), (httpClient, uri) async {
asyncExpectThrows(
() => httpClient.getUrl(Uri(
scheme: 'https',
host: uri.host,
port: uri.port,
)),
(e) => e is SocketException || e is HandshakeException);
});
}
main() {
asyncStart();
Future.wait(<Future>[
testWithHostname(),
testWithLoopback(),
testWithIPv6(),
testWithHTTPS(),
]).then((_) => asyncEnd());
}

View file

@ -0,0 +1,44 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import "package:async_helper/async_helper.dart";
Future<String> getLocalHostIP() async {
final interfaces = await NetworkInterface.list(
includeLoopback: false, type: InternetAddressType.IPv4);
return interfaces.first.addresses.first.address;
}
Future<void> testBanHttp(String serverHost,
Future<void> testCode(HttpClient client, Uri uri)) async {
final httpClient = new HttpClient();
final server = await HttpServer.bind(serverHost, 0);
final uri = Uri(scheme: 'http', host: serverHost, port: server.port);
try {
await testCode(httpClient, uri);
} finally {
httpClient.close(force: true);
await server.close();
}
}
main() async {
await asyncTest(() async {
final host = await getLocalHostIP();
// Normal HTTP request succeeds.
await testBanHttp(host, (httpClient, uri) async {
await asyncTest(() => httpClient.getUrl(uri));
});
// We can ban HTTP explicitly.
await testBanHttp(host, (httpClient, uri) async {
asyncExpectThrows(
() async => await runZoned(() => httpClient.getUrl(uri),
zoneValues: {#dart.library.io.allow_http: false}),
(e) => e is StateError);
});
});
}

View file

@ -1,29 +0,0 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This test file disallows VM from accepting insecure connections to all
// domains and tests that HTTP connections to non-localhost targets fail.
// HTTPS connections and localhost connections should still succeed.
// SharedOptions=-Ddart.library.io.may_insecurely_connect_to_all_domains=false
import "dart:async";
import "dart:io";
import "package:async_helper/async_helper.dart";
Future<void> testWithHostname() async {
final httpClient = new HttpClient();
final uri = Uri(scheme: 'http', host: 'domain.invalid', port: 12345);
asyncExpectThrows(
() async => await httpClient.getUrl(uri),
(e) =>
e is StateError &&
e.message.contains("Insecure HTTP is not allowed by platform"));
}
main() {
asyncStart();
testWithHostname().then((_) => asyncEnd());
}

View file

@ -1,40 +0,0 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// SharedOptions=-Ddart.library.io.domain_network_policies=[["foobar.com",true,true],["foobar.com",true,true],["baz.foobar.com",true,true],["baz.foobar.com",false,false]] -Ddart.library.io.may_insecurely_connect_to_all_domains=false
import 'dart:io';
import "package:expect/expect.dart";
void _checkAllows(List<String> domains) {
for (final domain in domains) {
Expect.isTrue(
isInsecureConnectionAllowed(domain), "$domain should be allowed.");
}
}
void _checkDenies(List<String> domains) {
for (final domain in domains) {
Expect.isFalse(
isInsecureConnectionAllowed(domain), "$domain should not be allowed.");
}
}
void main() {
// These have no policy but the default is false.
_checkDenies([
"mailfoobar.com",
"abc.com",
"oobar.com",
"foobar.co",
"128.221.55.31",
"fe80::4607:0bff:fea0:7747%invalid",
]);
// These are explicitly denied.
_checkDenies(["baz.foobar.com"]);
_checkAllows(
["foobar.com", "test.baz.foobar.com", "test2.test.baz.foobar.com"]);
_checkAllows(["::1", "localhost"]);
}

View file

@ -1,15 +0,0 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// SharedOptions=-Ddart.library.io.domain_network_policies=[["com",true,true]]
import 'dart:io';
import "package:expect/expect.dart";
// This test passes in an invalid domain as a network policy and checks that
// loading the policies throws.
void main() {
Expect.throwsArgumentError(() => isInsecureConnectionAllowed("test.com"));
}

View file

@ -0,0 +1,32 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';
import "package:expect/expect.dart";
void _checkAllows(List<String> domains) {
for (final domain in domains) {
Expect.isTrue(
isInsecureConnectionAllowed(domain), "$domain should be allowed.");
}
}
void main() {
// All domains and addresses are allowed.
_checkAllows([
"mailfoobar.com",
"abc.com",
"oobar.com",
"foobar.co",
"128.221.55.31",
"fe80::4607:0bff:fea0:7747%invalid",
"baz.foobar.com",
"foobar.com",
"test.baz.foobar.com",
"test2.test.baz.foobar.com",
"::1",
"localhost",
]);
}

View file

@ -1,14 +0,0 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// SharedOptions=-Ddart.library.io.domain_network_policies=[["baz.foobar.com",true,true],["baz.foobar.com",false,false]]
import 'dart:io';
import "package:expect/expect.dart";
void main() {
Expect.isFalse(isInsecureConnectionAllowed("baz.foobar.com"));
Expect.isTrue(isInsecureConnectionAllowed("test.baz.foobar.com"));
}

View file

@ -14,10 +14,8 @@ fragmentation_typed_data_test: Skip # This test crashes on the bot, but not loca
[ $system == android ]
entrypoints_verification_test: Skip # Requires shared objects which the test script doesn't "adb push".
io/http_ban_http_allowed_cases_test: Skip # Depends on grabbing local hostname which isn't supported.
io/network_policy_configuration_test: Skip # Can't pass -D params containing quotes to adb.
io/network_policy_invalid_domain_test: Skip # Can't pass -D params containing quotes to adb.
io/network_policy_tie_breaker_test: Skip # Can't pass -D params containing quotes to adb.
io/http_ban_http_embedder_test: Skip # Requires http server bound to non-loopback; not provided by system.
io/http_ban_http_normal_test: Skip # Requires http server bound to non-loopback; not provided by system.
[ $arch == ia32 && $builder_tag == optimization_counter_threshold ]
io/file_lock_test: SkipSlow # Timeout