Skip to content

Commit 74a20b7

Browse files
mehmetfcommit-bot@chromium.org
authored andcommitted
Reland "[dart:_http] Allow the embedder to prohibit HTTP traffic."
This is a reland of 6b44c63 Original change's description: > [dart:_http] Allow the embedder to prohibit HTTP traffic. > > This can be configured by embedders by setting the `_embedderAllowsHttp` > variable. It can also be overridden by client apps by introducing a > `Zone` with the variable `#dart.library.io.allow_http` set to `true` or > `false`. > > The default behavior in SDK is unchanged. > > Bug: #40548 > Change-Id: Ifec0ad2d759de4bbb836644840d8c312e560f285 > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/138911 > Commit-Queue: Martin Kustermann <[email protected]> > Reviewed-by: Jonas Termansen <[email protected]> Bug: #40548 Change-Id: I6ced6c1248b3b6687f6c7d998e5206b2b385f00b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/142446 Reviewed-by: Martin Kustermann <[email protected]> Commit-Queue: Martin Kustermann <[email protected]>
1 parent f44b532 commit 74a20b7

20 files changed

+328
-2
lines changed

sdk/lib/_http/embedder_config.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// @dart = 2.6
6+
7+
part of dart._http;
8+
9+
/// Embedder-specific `dart:_http` configuration.
10+
11+
/// [HttpClient] will disallow HTTP URLs if this value is set to `false`.
12+
@pragma("vm:entry-point")
13+
bool _embedderAllowsHttp = true;

sdk/lib/_http/http.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import 'dart:io';
2424
import 'dart:typed_data';
2525

2626
part 'crypto.dart';
27+
part 'embedder_config.dart';
2728
part 'http_date.dart';
2829
part 'http_headers.dart';
2930
part 'http_impl.dart';

sdk/lib/_http/http_impl.dart

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2266,6 +2266,32 @@ class _HttpClient implements HttpClient {
22662266
});
22672267
}
22682268

2269+
/// Whether HTTP requests are currently allowed.
2270+
///
2271+
/// If the [Zone] variable `#dart.library.io.allow_http` is set to a boolean,
2272+
/// it determines whether the HTTP protocol is allowed. If the zone variable
2273+
/// is set to any other non-null value, HTTP is not allowed.
2274+
/// Otherwise, if the `dart.library.io.allow_http` environment flag
2275+
/// is set to `false`, HTTP is not allowed.
2276+
/// Otherwise, [_embedderAllowsHTTP] determines the result.
2277+
bool get _isHttpAllowed {
2278+
final zoneOverride = Zone.current[#dart.library.io.allow_http];
2279+
if (zoneOverride != null) return true == zoneOverride;
2280+
bool envOverride =
2281+
bool.fromEnvironment("dart.library.io.allow_http", defaultValue: true);
2282+
return envOverride && _embedderAllowsHttp;
2283+
}
2284+
2285+
bool _isLoopback(String host) {
2286+
if (host.isEmpty) return false;
2287+
if ("localhost" == host) return true;
2288+
try {
2289+
return InternetAddress(host).isLoopback;
2290+
} on ArgumentError {
2291+
return false;
2292+
}
2293+
}
2294+
22692295
Future<_HttpClientRequest> _openUrl(String method, Uri uri) {
22702296
if (_closing) {
22712297
throw new StateError("Client is closed");
@@ -2286,7 +2312,12 @@ class _HttpClient implements HttpClient {
22862312
}
22872313
}
22882314

2289-
bool isSecure = (uri.scheme == "https");
2315+
bool isSecure = uri.isScheme("https");
2316+
if (!_isHttpAllowed && !isSecure && !_isLoopback(uri.host)) {
2317+
throw new StateError(
2318+
"Insecure HTTP is not allowed by the current platform: $uri");
2319+
}
2320+
22902321
int port = uri.port;
22912322
if (port == 0) {
22922323
port =
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
part of dart._http;
6+
7+
/// Embedder-specific `dart:_http` configuration.
8+
9+
/// [HttpClient] will disallow HTTP URLs if this value is set to `false`.
10+
@pragma("vm:entry-point")
11+
bool _embedderAllowsHttp = true;

sdk_nnbd/lib/_http/http.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import 'dart:io';
2323
import 'dart:typed_data';
2424

2525
part 'crypto.dart';
26+
part 'embedder_config.dart';
2627
part 'http_date.dart';
2728
part 'http_headers.dart';
2829
part 'http_impl.dart';

sdk_nnbd/lib/_http/http_impl.dart

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2267,6 +2267,32 @@ class _HttpClient implements HttpClient {
22672267
});
22682268
}
22692269

2270+
/// Whether HTTP requests are currently allowed.
2271+
///
2272+
/// If the [Zone] variable `#dart.library.io.allow_http` is set to a boolean,
2273+
/// it determines whether the HTTP protocol is allowed. If the zone variable
2274+
/// is set to any other non-null value, HTTP is not allowed.
2275+
/// Otherwise, if the `dart.library.io.allow_http` environment flag
2276+
/// is set to `false`, HTTP is not allowed.
2277+
/// Otherwise, [_embedderAllowsHttp] determines the result.
2278+
bool get _isHttpAllowed {
2279+
final zoneOverride = Zone.current[#dart.library.io.allow_http];
2280+
if (zoneOverride != null) return true == zoneOverride;
2281+
bool envOverride =
2282+
bool.fromEnvironment("dart.library.io.allow_http", defaultValue: true);
2283+
return envOverride && _embedderAllowsHttp;
2284+
}
2285+
2286+
bool _isLoopback(String host) {
2287+
if (host.isEmpty) return false;
2288+
if ("localhost" == host) return true;
2289+
try {
2290+
return InternetAddress(host).isLoopback;
2291+
} on ArgumentError {
2292+
return false;
2293+
}
2294+
}
2295+
22702296
Future<_HttpClientRequest> _openUrl(String method, Uri uri) {
22712297
if (_closing) {
22722298
throw new StateError("Client is closed");
@@ -2287,7 +2313,12 @@ class _HttpClient implements HttpClient {
22872313
}
22882314
}
22892315

2290-
bool isSecure = (uri.scheme == "https");
2316+
bool isSecure = uri.isScheme("https");
2317+
if (!_isHttpAllowed && !isSecure && !_isLoopback(uri.host)) {
2318+
throw new StateError(
2319+
"Insecure HTTP is not allowed by the current platform: $uri");
2320+
}
2321+
22912322
int port = uri.port;
22922323
if (port == 0) {
22932324
port =
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// SharedOptions=-Ddart.library.io.allow_http=false
6+
7+
import 'dart:async';
8+
import 'dart:io';
9+
10+
import "package:async_helper/async_helper.dart";
11+
12+
import 'http_ban_http_normal_test.dart';
13+
import 'http_bind_test.dart';
14+
15+
Future<void> testWithHostname() async {
16+
await testBanHttp(await getLocalHostIP(), (httpClient, httpUri) async {
17+
asyncExpectThrows(
18+
() async => await httpClient.getUrl(httpUri), (e) => e is StateError);
19+
asyncExpectThrows(
20+
() async => await runZoned(() => httpClient.getUrl(httpUri),
21+
zoneValues: {#dart.library.io.allow_http: 'foo'}),
22+
(e) => e is StateError);
23+
asyncExpectThrows(
24+
() async => await runZoned(() => httpClient.getUrl(httpUri),
25+
zoneValues: {#dart.library.io.allow_http: false}),
26+
(e) => e is StateError);
27+
await asyncTest(() => runZoned(() => httpClient.getUrl(httpUri),
28+
zoneValues: {#dart.library.io.allow_http: true}));
29+
});
30+
}
31+
32+
Future<void> testWithLoopback() async {
33+
await testBanHttp("127.0.0.1", (httpClient, uri) async {
34+
await asyncTest(
35+
() => httpClient.getUrl(Uri.parse('http://localhost:${uri.port}')));
36+
await asyncTest(
37+
() => httpClient.getUrl(Uri.parse('http://127.0.0.1:${uri.port}')));
38+
});
39+
}
40+
41+
Future<void> testWithIPv6() async {
42+
if (await supportsIPV6()) {
43+
await testBanHttp("::1", (httpClient, uri) async {
44+
await asyncTest(() => httpClient.getUrl(uri));
45+
});
46+
}
47+
}
48+
49+
Future<void> testWithHTTPS() async {
50+
await testBanHttp(await getLocalHostIP(), (httpClient, uri) async {
51+
asyncExpectThrows(
52+
() => httpClient.getUrl(Uri(
53+
scheme: 'https',
54+
host: uri.host,
55+
port: uri.port,
56+
)),
57+
(e) => e is SocketException || e is HandshakeException);
58+
});
59+
}
60+
61+
main() {
62+
asyncStart();
63+
Future.wait(<Future>[
64+
testWithHostname(),
65+
testWithLoopback(),
66+
testWithIPv6(),
67+
testWithHTTPS(),
68+
]).then((_) => asyncEnd());
69+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'dart:io';
7+
8+
import "package:async_helper/async_helper.dart";
9+
10+
Future<String> getLocalHostIP() async {
11+
final interfaces = await NetworkInterface.list(
12+
includeLoopback: false, type: InternetAddressType.IPv4);
13+
return interfaces.first.addresses.first.address;
14+
}
15+
16+
Future<void> testBanHttp(String serverHost,
17+
Future<void> testCode(HttpClient client, Uri uri)) async {
18+
final httpClient = new HttpClient();
19+
final server = await HttpServer.bind(serverHost, 0);
20+
final uri = Uri(scheme: 'http', host: serverHost, port: server.port);
21+
try {
22+
await testCode(httpClient, uri);
23+
} finally {
24+
httpClient.close(force: true);
25+
await server.close();
26+
}
27+
}
28+
29+
main() async {
30+
await asyncTest(() async {
31+
final host = await getLocalHostIP();
32+
// Normal HTTP request succeeds.
33+
await testBanHttp(host, (httpClient, uri) async {
34+
await asyncTest(() => httpClient.getUrl(uri));
35+
});
36+
// We can ban HTTP explicitly.
37+
await testBanHttp(host, (httpClient, uri) async {
38+
asyncExpectThrows(
39+
() async => await runZoned(() => httpClient.getUrl(uri),
40+
zoneValues: {#dart.library.io.allow_http: false}),
41+
(e) => e is StateError);
42+
});
43+
});
44+
}

tests/standalone/io/http_cookie_date_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import "dart:typed_data";
1616
import "package:expect/expect.dart";
1717

1818
part "../../../sdk/lib/_http/crypto.dart";
19+
part "../../../sdk/lib/_http/embedder_config.dart";
1920
part "../../../sdk/lib/_http/http_impl.dart";
2021
part "../../../sdk/lib/_http/http_date.dart";
2122
part "../../../sdk/lib/_http/http_parser.dart";

tests/standalone/io/http_headers_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import "dart:typed_data";
1717
import "package:expect/expect.dart";
1818

1919
part "../../../sdk/lib/_http/crypto.dart";
20+
part "../../../sdk/lib/_http/embedder_config.dart";
2021
part "../../../sdk/lib/_http/http_impl.dart";
2122
part "../../../sdk/lib/_http/http_date.dart";
2223
part "../../../sdk/lib/_http/http_parser.dart";

0 commit comments

Comments
 (0)