[io/http] Validate method name passed to HttpClient.open/openUrl.

There should be no control characters or delimiters in method name
provided to open/openUrl methods.

Fixes https://github.com/dart-lang/sdk/issues/45744
TEST=http_open_method_validate_test

Change-Id: I0db98f2376c980a054420fb447d4f7ef9321f1a9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/256429
Reviewed-by: Siva Annamalai <asiva@google.com>
Reviewed-by: Brian Quinlan <bquinlan@google.com>
Commit-Queue: Alexander Aprelev <aam@google.com>
This commit is contained in:
Alexander Aprelev 2022-08-29 21:12:27 +00:00 committed by Commit Bot
parent 18e4b40dcc
commit 6abb6e5110
9 changed files with 115 additions and 11 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 -> MapConstant(const <Symbol*, dynamic>{})
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:564:8 -> SymbolConstant(#clear)
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:564:8 -> ListConstant(const <Type*>[])
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:564:8 -> ListConstant(const <dynamic>[])
Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:564:8 -> MapConstant(const <Symbol*, dynamic>{})
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:568:8 -> SymbolConstant(#clear)
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:568:8 -> ListConstant(const <Type*>[])
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:568:8 -> ListConstant(const <dynamic>[])
Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:568:8 -> MapConstant(const <Symbol*, dynamic>{})
Extra constant evaluation: evaluated: 268, effectively constant: 91

View file

@ -5,7 +5,11 @@
library dart._http;
import 'dart:_internal'
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
show
checkNotNullable,
Since,
valueOfNonNullableParamWithDefault,
HttpStatus;
import 'dart:async';
import 'dart:collection'
show

View file

@ -2678,6 +2678,30 @@ class _HttpClient implements HttpClient {
}
}
bool _isValidToken(String token) {
checkNotNullable(token, "token");
// from https://www.rfc-editor.org/rfc/rfc2616#page-15
//
// CTL = <any US-ASCII control character
// (octets 0 - 31) and DEL (127)>
// separators = "(" | ")" | "<" | ">" | "@"
// | "," | ";" | ":" | "\" | <">
// | "/" | "[" | "]" | "?" | "="
// | "{" | "}" | SP | HT
// token = 1*<any CHAR except CTLs or separators>
const _validChars = r" "
r" ! #$%&' *+ -. 0123456789 "
r" ABCDEFGHIJKLMNOPQRSTUVWXYZ ^_"
r"`abcdefghijklmnopqrstuvwxyz | ~ ";
for (int codeUnit in token.codeUnits) {
if (codeUnit >= _validChars.length ||
_validChars.codeUnitAt(codeUnit) == 0x20) {
return false;
}
}
return true;
}
Future<_HttpClientRequest> _openUrl(String method, Uri uri) {
if (_closing) {
throw StateError("Client is closed");
@ -2686,9 +2710,11 @@ class _HttpClient implements HttpClient {
// Ignore any fragments on the request URI.
uri = uri.removeFragment();
if (method == null) {
throw ArgumentError(method);
// from https://www.rfc-editor.org/rfc/rfc2616#page-35
if (!_isValidToken(method)) {
throw ArgumentError.value(method, "method");
}
if (method != "CONNECT") {
if (uri.host.isEmpty) {
throw ArgumentError("No host specified in URI $uri");

View file

@ -16,7 +16,11 @@ import "dart:typed_data";
import "package:expect/expect.dart";
import "../../../sdk/lib/internal/internal.dart"
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
show
checkNotNullable,
Since,
valueOfNonNullableParamWithDefault,
HttpStatus;
part "../../../sdk/lib/_http/crypto.dart";
part "../../../sdk/lib/_http/embedder_config.dart";

View file

@ -16,7 +16,11 @@ import "dart:typed_data";
import "package:expect/expect.dart";
import "../../../sdk/lib/internal/internal.dart"
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
show
checkNotNullable,
Since,
valueOfNonNullableParamWithDefault,
HttpStatus;
part "../../../sdk/lib/_http/crypto.dart";
part "../../../sdk/lib/_http/embedder_config.dart";

View file

@ -0,0 +1,28 @@
// Copyright (c) 2022, 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.
//
// Verify that HttpClient open, openUrl method argument is validated.
import "dart:io";
import "package:expect/expect.dart";
void testInvalidArgumentException(String method) {
Expect.throws(() => HttpClient()..open(method, "127.0.0.1", 8080, "/"),
(e) => e is ArgumentError);
Expect.throws(
() => HttpClient()..openUrl(method, Uri.parse("http://127.0.0.1/")),
(e) => e is ArgumentError);
}
main() {
const String separators = "\t\n\r()<>@,;:\\/[]?={}";
for (int i = 0; i < separators.length; i++) {
String separator = separators.substring(i, i + 1);
testInvalidArgumentException(separator);
testInvalidArgumentException(separator + "CONNECT");
testInvalidArgumentException("CONN" + separator + "ECT");
testInvalidArgumentException("CONN" + separator + separator + "ECT");
testInvalidArgumentException("CONNECT" + separator);
}
}

View file

@ -16,7 +16,11 @@ import "dart:typed_data";
import "package:expect/expect.dart";
import "../../../sdk/lib/internal/internal.dart"
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
show
checkNotNullable,
Since,
valueOfNonNullableParamWithDefault,
HttpStatus;
part "../../../sdk/lib/_http/crypto.dart";
part "../../../sdk/lib/_http/embedder_config.dart";

View file

@ -17,7 +17,11 @@ import "package:async_helper/async_helper.dart";
import "package:expect/expect.dart";
import "../../../sdk/lib/internal/internal.dart"
show Since, valueOfNonNullableParamWithDefault, HttpStatus;
show
checkNotNullable,
Since,
valueOfNonNullableParamWithDefault,
HttpStatus;
part "../../../sdk/lib/_http/crypto.dart";
part "../../../sdk/lib/_http/embedder_config.dart";

View file

@ -0,0 +1,30 @@
// Copyright (c) 2022, 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.
//
// Verify that HttpClient open, openUrl method argument is validated.
// @dart = 2.9
import "dart:io";
import "package:expect/expect.dart";
void testInvalidArgumentException(String method) {
Expect.throws(() => HttpClient()..open(method, "127.0.0.1", 8080, "/"),
(e) => e is ArgumentError);
Expect.throws(
() => HttpClient()..openUrl(method, Uri.parse("http://127.0.0.1/")),
(e) => e is ArgumentError);
}
main() {
const String separators = "\t\n\r()<>@,;:\\/[]?={}";
for (int i = 0; i < separators.length; i++) {
String separator = separators.substring(i, i + 1);
testInvalidArgumentException(separator);
testInvalidArgumentException(separator + "CONNECT");
testInvalidArgumentException("CONN" + separator + "ECT");
testInvalidArgumentException("CONN" + separator + separator + "ECT");
testInvalidArgumentException("CONNECT" + separator);
}
}