Skip to content

Commit

Permalink
[io/http] Validate method name passed to HttpClient.open/openUrl.
Browse files Browse the repository at this point in the history
There should be no control characters or delimiters in method name
provided to open/openUrl methods.

Fixes #45744
TEST=http_open_method_validate_test

Change-Id: I0db98f2376c980a054420fb447d4f7ef9321f1a9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/256429
Reviewed-by: Siva Annamalai <[email protected]>
Reviewed-by: Brian Quinlan <[email protected]>
Commit-Queue: Alexander Aprelev <[email protected]>
  • Loading branch information
aam authored and Commit Bot committed Aug 29, 2022
1 parent 18e4b40 commit 6abb6e5
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 5 additions & 1 deletion sdk/lib/_http/http.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 28 additions & 2 deletions sdk/lib/_http/http_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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");
Expand Down
6 changes: 5 additions & 1 deletion tests/standalone/io/http_cookie_date_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
6 changes: 5 additions & 1 deletion tests/standalone/io/http_headers_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
28 changes: 28 additions & 0 deletions tests/standalone/io/http_open_method_validate_test.dart
Original file line number Diff line number Diff line change
@@ -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);
}
}
6 changes: 5 additions & 1 deletion tests/standalone/io/http_parser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
6 changes: 5 additions & 1 deletion tests/standalone/io/web_socket_protocol_processor_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
30 changes: 30 additions & 0 deletions tests/standalone_2/io/http_open_method_validate_test.dart
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit 6abb6e5

Please sign in to comment.