Skip to content

Commit 927f79e

Browse files
authored
Validate method name passed to HttpClient.open/openUrl (#11)
1 parent ba716ca commit 927f79e

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

dio/lib/src/dio_mixin.dart

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,34 @@ abstract class DioMixin implements Dio {
704704
}
705705
}
706706

707+
bool _isValidToken(String token) {
708+
_checkNotNullable(token, "token");
709+
// from https://www.rfc-editor.org/rfc/rfc2616#page-15
710+
//
711+
// CTL = <any US-ASCII control character
712+
// (octets 0 - 31) and DEL (127)>
713+
// separators = "(" | ")" | "<" | ">" | "@"
714+
// | "," | ";" | ":" | "\" | <">
715+
// | "/" | "[" | "]" | "?" | "="
716+
// | "{" | "}" | SP | HT
717+
// token = 1*<any CHAR except CTLs or separators>
718+
const _validChars = r" "
719+
r" ! #$%&' *+ -. 0123456789 "
720+
r" ABCDEFGHIJKLMNOPQRSTUVWXYZ ^_"
721+
r"`abcdefghijklmnopqrstuvwxyz | ~ ";
722+
for (int codeUnit in token.codeUnits) {
723+
if (codeUnit >= _validChars.length ||
724+
_validChars.codeUnitAt(codeUnit) == 0x20) {
725+
return false;
726+
}
727+
}
728+
return true;
729+
}
730+
707731
Future<Stream<Uint8List>?> _transformData(RequestOptions options) async {
732+
if (!_isValidToken(options.method)) {
733+
throw ArgumentError.value(options.method, "method");
734+
}
708735
var data = options.data;
709736
List<int> bytes;
710737
Stream<List<int>> stream;
@@ -830,3 +857,30 @@ abstract class DioMixin implements Dio {
830857
return response;
831858
}
832859
}
860+
861+
/// A null-check function for function parameters in Null Safety enabled code.
862+
///
863+
/// Because Dart does not have full null safety
864+
/// until all legacy code has been removed from a program,
865+
/// a non-nullable parameter can still end up with a `null` value.
866+
/// This function can be used to guard those functions against null arguments.
867+
/// It throws a [TypeError] because we are really seeing the failure to
868+
/// assign `null` to a non-nullable type.
869+
///
870+
/// See http://dartbug.com/40614 for context.
871+
T _checkNotNullable<T extends Object>(T value, String name) {
872+
if ((value as dynamic) == null) {
873+
throw NotNullableError<T>(name);
874+
}
875+
return value;
876+
}
877+
878+
/// A [TypeError] thrown by [_checkNotNullable].
879+
class NotNullableError<T> extends Error implements TypeError {
880+
NotNullableError(this._name);
881+
882+
final String _name;
883+
884+
@override
885+
String toString() => "Null is not a valid value for '$_name' of type '$T'";
886+
}

dio/test/options_test.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,4 +301,24 @@ void main() {
301301
final textResponse2 = await dio.get<String>('/test-force-convert');
302302
expect(textResponse2.data, json.encode(expectedResponseData));
303303
});
304+
305+
test('Throws when using invalid methods', () async {
306+
final dio = Dio();
307+
void testInvalidArgumentException(String method) async {
308+
await expectLater(
309+
dio.fetch(RequestOptions(path: 'http://127.0.0.1', method: method)),
310+
throwsA((e) => e is DioError && e.error is ArgumentError),
311+
);
312+
}
313+
314+
const String separators = "\t\n\r()<>@,;:\\/[]?={}";
315+
for (int i = 0; i < separators.length; i++) {
316+
String separator = separators.substring(i, i + 1);
317+
testInvalidArgumentException(separator);
318+
testInvalidArgumentException(separator + "CONNECT");
319+
testInvalidArgumentException("CONN" + separator + "ECT");
320+
testInvalidArgumentException("CONN" + separator + separator + "ECT");
321+
testInvalidArgumentException("CONNECT" + separator);
322+
}
323+
});
304324
}

0 commit comments

Comments
 (0)