mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 03:07:49 +00:00
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:
parent
7eda3cfe76
commit
d968304570
|
@ -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: 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: 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: 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: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:762: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:762:8 -> ListConstant(const <Type*>[])
|
||||||
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:761:8 -> ListConstant(const <dynamic>[])
|
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:761:8 -> InstanceConstant(const _ImmutableMap<Symbol*, dynamic>{_ImmutableMap._kvPairs: 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
|
Extra constant evaluation: evaluated: 268, effectively constant: 91
|
||||||
|
|
11
sdk/lib/_http/embedder_config.dart
Normal file
11
sdk/lib/_http/embedder_config.dart
Normal 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;
|
|
@ -24,6 +24,7 @@ import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
part 'crypto.dart';
|
part 'crypto.dart';
|
||||||
|
part 'embedder_config.dart';
|
||||||
part 'http_date.dart';
|
part 'http_date.dart';
|
||||||
part 'http_headers.dart';
|
part 'http_headers.dart';
|
||||||
part 'http_impl.dart';
|
part 'http_impl.dart';
|
||||||
|
|
|
@ -2620,6 +2620,40 @@ class _HttpClient implements HttpClient {
|
||||||
|
|
||||||
set findProxy(String f(Uri uri)?) => _findProxy = f;
|
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) {
|
Future<_HttpClientRequest> _openUrl(String method, Uri uri) {
|
||||||
if (_closing) {
|
if (_closing) {
|
||||||
throw new StateError("Client is closed");
|
throw new StateError("Client is closed");
|
||||||
|
@ -2641,9 +2675,11 @@ class _HttpClient implements HttpClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSecure = uri.isScheme("https");
|
bool isSecure = uri.isScheme("https");
|
||||||
if (!isSecure && !isInsecureConnectionAllowed(uri.host)) {
|
if (!_isHttpAllowed && !isSecure && !_isLoopback(uri.host)) {
|
||||||
throw new StateError("Insecure HTTP is not allowed by platform: $uri");
|
throw new StateError(
|
||||||
|
"Insecure HTTP is not allowed by the current platform: $uri");
|
||||||
}
|
}
|
||||||
|
|
||||||
int port = uri.port;
|
int port = uri.port;
|
||||||
if (port == 0) {
|
if (port == 0) {
|
||||||
port =
|
port =
|
||||||
|
|
|
@ -36,13 +36,18 @@ abstract class _EmbedderConfig {
|
||||||
/// to all domains.
|
/// to all domains.
|
||||||
///
|
///
|
||||||
/// This setting can be overridden by per-domain policies.
|
/// 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')
|
@pragma('vm:entry-point')
|
||||||
static bool _mayInsecurelyConnectToAllDomains = true;
|
static bool _mayInsecurelyConnectToAllDomains = true;
|
||||||
|
|
||||||
/// Domain network policies set by embedder.
|
/// Domain network policies set by embedder.
|
||||||
|
@Deprecated(
|
||||||
|
"To be re-implemented in https://github.com/flutter/flutter/issues/54448")
|
||||||
@pragma('vm:entry-point')
|
@pragma('vm:entry-point')
|
||||||
static void _setDomainPolicies(String domainNetworkPolicyJson) {
|
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:
|
// TODO(zra): Consider adding:
|
||||||
|
|
|
@ -6,6 +6,9 @@ part of dart.io;
|
||||||
|
|
||||||
/// Whether insecure connections to [host] are allowed.
|
/// 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].
|
/// [host] must be a [String] or [InternetAddress].
|
||||||
///
|
///
|
||||||
/// If any of the domain policies match [host], the matching policy will make
|
/// If any of the domain policies match [host], the matching policy will make
|
||||||
|
@ -14,179 +17,7 @@ part of dart.io;
|
||||||
/// used.
|
/// used.
|
||||||
///
|
///
|
||||||
/// Loopback addresses are always allowed.
|
/// Loopback addresses are always allowed.
|
||||||
|
@Deprecated("See https://github.com/flutter/flutter/issues/54448 for followup")
|
||||||
bool isInsecureConnectionAllowed(dynamic host) {
|
bool isInsecureConnectionAllowed(dynamic host) {
|
||||||
String hostString;
|
return true;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
|
69
tests/standalone/io/http_ban_http_embedder_test.dart
Normal file
69
tests/standalone/io/http_ban_http_embedder_test.dart
Normal 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());
|
||||||
|
}
|
44
tests/standalone/io/http_ban_http_normal_test.dart
Normal file
44
tests/standalone/io/http_ban_http_normal_test.dart
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -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());
|
|
||||||
}
|
|
|
@ -19,6 +19,7 @@ import "../../../sdk/lib/internal/internal.dart"
|
||||||
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
|
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
|
||||||
|
|
||||||
part "../../../sdk/lib/_http/crypto.dart";
|
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_impl.dart";
|
||||||
part "../../../sdk/lib/_http/http_date.dart";
|
part "../../../sdk/lib/_http/http_date.dart";
|
||||||
part "../../../sdk/lib/_http/http_parser.dart";
|
part "../../../sdk/lib/_http/http_parser.dart";
|
||||||
|
|
|
@ -19,6 +19,7 @@ import "../../../sdk/lib/internal/internal.dart"
|
||||||
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
|
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
|
||||||
|
|
||||||
part "../../../sdk/lib/_http/crypto.dart";
|
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_impl.dart";
|
||||||
part "../../../sdk/lib/_http/http_date.dart";
|
part "../../../sdk/lib/_http/http_date.dart";
|
||||||
part "../../../sdk/lib/_http/http_parser.dart";
|
part "../../../sdk/lib/_http/http_parser.dart";
|
||||||
|
|
|
@ -19,6 +19,7 @@ import "../../../sdk/lib/internal/internal.dart"
|
||||||
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
|
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
|
||||||
|
|
||||||
part "../../../sdk/lib/_http/crypto.dart";
|
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_impl.dart";
|
||||||
part "../../../sdk/lib/_http/http_date.dart";
|
part "../../../sdk/lib/_http/http_date.dart";
|
||||||
part "../../../sdk/lib/_http/http_parser.dart";
|
part "../../../sdk/lib/_http/http_parser.dart";
|
||||||
|
|
|
@ -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"]);
|
|
||||||
}
|
|
|
@ -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"));
|
|
||||||
}
|
|
32
tests/standalone/io/network_policy_test.dart
Normal file
32
tests/standalone/io/network_policy_test.dart
Normal 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",
|
||||||
|
]);
|
||||||
|
}
|
|
@ -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"));
|
|
||||||
}
|
|
|
@ -20,6 +20,7 @@ import "../../../sdk/lib/internal/internal.dart"
|
||||||
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
|
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
|
||||||
|
|
||||||
part "../../../sdk/lib/_http/crypto.dart";
|
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_impl.dart";
|
||||||
part "../../../sdk/lib/_http/http_date.dart";
|
part "../../../sdk/lib/_http/http_date.dart";
|
||||||
part "../../../sdk/lib/_http/http_parser.dart";
|
part "../../../sdk/lib/_http/http_parser.dart";
|
||||||
|
|
|
@ -12,10 +12,8 @@ fragmentation_typed_data_test: Skip # This test crashes on the bot, but not loca
|
||||||
|
|
||||||
[ $system == android ]
|
[ $system == android ]
|
||||||
entrypoints_verification_test: Skip # Requires shared objects which the test script doesn't "adb push".
|
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/http_ban_http_embedder_test: Skip # Requires http server bound to non-loopback; not provided by system.
|
||||||
io/network_policy_configuration_test: Skip # Can't pass -D params containing quotes to adb.
|
io/http_ban_http_normal_test: Skip # Requires http server bound to non-loopback; not provided by system.
|
||||||
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.
|
|
||||||
|
|
||||||
[ $arch == ia32 && $builder_tag == optimization_counter_threshold ]
|
[ $arch == ia32 && $builder_tag == optimization_counter_threshold ]
|
||||||
io/file_lock_test: SkipSlow # Timeout
|
io/file_lock_test: SkipSlow # Timeout
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
|
69
tests/standalone_2/io/http_ban_http_embedder_test.dart
Normal file
69
tests/standalone_2/io/http_ban_http_embedder_test.dart
Normal 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());
|
||||||
|
}
|
44
tests/standalone_2/io/http_ban_http_normal_test.dart
Normal file
44
tests/standalone_2/io/http_ban_http_normal_test.dart
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -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());
|
|
||||||
}
|
|
|
@ -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"]);
|
|
||||||
}
|
|
|
@ -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"));
|
|
||||||
}
|
|
32
tests/standalone_2/io/network_policy_test.dart
Normal file
32
tests/standalone_2/io/network_policy_test.dart
Normal 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",
|
||||||
|
]);
|
||||||
|
}
|
|
@ -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"));
|
|
||||||
}
|
|
|
@ -14,10 +14,8 @@ fragmentation_typed_data_test: Skip # This test crashes on the bot, but not loca
|
||||||
|
|
||||||
[ $system == android ]
|
[ $system == android ]
|
||||||
entrypoints_verification_test: Skip # Requires shared objects which the test script doesn't "adb push".
|
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/http_ban_http_embedder_test: Skip # Requires http server bound to non-loopback; not provided by system.
|
||||||
io/network_policy_configuration_test: Skip # Can't pass -D params containing quotes to adb.
|
io/http_ban_http_normal_test: Skip # Requires http server bound to non-loopback; not provided by system.
|
||||||
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.
|
|
||||||
|
|
||||||
[ $arch == ia32 && $builder_tag == optimization_counter_threshold ]
|
[ $arch == ia32 && $builder_tag == optimization_counter_threshold ]
|
||||||
io/file_lock_test: SkipSlow # Timeout
|
io/file_lock_test: SkipSlow # Timeout
|
||||||
|
|
Loading…
Reference in a new issue