Skip to content

Commit

Permalink
Return 400 instead of a crash with certain illegal query strings. (#29)
Browse files Browse the repository at this point in the history
Fixes #28
  • Loading branch information
mworrell authored Jan 4, 2022
1 parent 994d6da commit 290d5f2
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 16 deletions.
3 changes: 3 additions & 0 deletions src/cowmachine.erl
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ request_1(Controller, Req, Env, Options, Context) ->
throw:invalid_percent_encoding ->
log(#{ at => ?AT, level => error, code => 400, text => "Illegal percent encoding" }, Req),
{stop, cowboy_req:reply(400, Req)};
throw:invalid_qs_name ->
log(#{ at => ?AT, level => error, code => 400, text => "Illegal query argument name" }, Req),
{stop, cowboy_req:reply(400, Req)};
throw:Reason:Stacktrace ->
log(#{ at => ?AT, level => error, code => 500, text => "Unexpected throw",
class => throw, reason => Reason,
Expand Down
36 changes: 20 additions & 16 deletions src/cowmachine_util.erl
Original file line number Diff line number Diff line change
Expand Up @@ -281,35 +281,39 @@ parse_qs(<<>>) ->
parse_qs(Qs) ->
parse_qs_name(Qs, [], <<>>).

parse_qs_name(<< $%, H, L, Rest/bits >>, Acc, Name) ->
parse_qs_name(<< $%, H, L, Rest/binary >>, Acc, Name) ->
C = (unhex(H) bsl 4 bor unhex(L)),
parse_qs_name(Rest, Acc, << Name/bits, C >>);
parse_qs_name(<< $+, Rest/bits >>, Acc, Name) ->
parse_qs_name(Rest, Acc, << Name/bits, " " >>);
parse_qs_name(<< $=, Rest/bits >>, Acc, Name) when Name =/= <<>> ->
parse_qs_name(Rest, Acc, << Name/binary, C >>);
parse_qs_name(<< $+, Rest/binary >>, Acc, Name) ->
parse_qs_name(Rest, Acc, << Name/binary, " " >>);
parse_qs_name(<< $=, _/binary >>, _Acc, <<>>) ->
throw(invalid_qs_name);
parse_qs_name(<< $=, Rest/binary >>, Acc, Name) ->
parse_qs_value(Rest, Acc, Name, <<>>);
parse_qs_name(<< $&, Rest/bits >>, Acc, Name) ->
parse_qs_name(<< $&, Rest/binary >>, Acc, Name) ->
case Name of
<<>> -> parse_qs_name(Rest, Acc, <<>>);
_ -> parse_qs_name(Rest, [{Name, <<>>}|Acc], <<>>)
end;
parse_qs_name(<< C, Rest/bits >>, Acc, Name) when C =/= $%, C =/= $= ->
parse_qs_name(Rest, Acc, << Name/bits, C >>);
parse_qs_name(<< C, Rest/binary >>, Acc, Name) when C =/= $% ->
parse_qs_name(Rest, Acc, << Name/binary, C >>);
parse_qs_name(<<>>, Acc, Name) ->
case Name of
<<>> -> lists:reverse(Acc);
_ -> lists:reverse([{Name, <<>>}|Acc])
end.
end;
parse_qs_name(_Rest, _Acc, _Name) ->
throw(invalid_percent_encoding).

parse_qs_value(<< $%, H, L, Rest/bits >>, Acc, Name, Value) ->
parse_qs_value(<< $%, H, L, Rest/binary >>, Acc, Name, Value) ->
C = (unhex(H) bsl 4 bor unhex(L)),
parse_qs_value(Rest, Acc, Name, << Value/bits, C >>);
parse_qs_value(<< $+, Rest/bits >>, Acc, Name, Value) ->
parse_qs_value(Rest, Acc, Name, << Value/bits, " " >>);
parse_qs_value(<< $&, Rest/bits >>, Acc, Name, Value) ->
parse_qs_value(Rest, Acc, Name, << Value/binary, C >>);
parse_qs_value(<< $+, Rest/binary >>, Acc, Name, Value) ->
parse_qs_value(Rest, Acc, Name, << Value/binary, " " >>);
parse_qs_value(<< $&, Rest/binary >>, Acc, Name, Value) ->
parse_qs_name(Rest, [{Name, Value}|Acc], <<>>);
parse_qs_value(<< C, Rest/bits >>, Acc, Name, Value) when C =/= $% ->
parse_qs_value(Rest, Acc, Name, << Value/bits, C >>);
parse_qs_value(<< C, Rest/binary >>, Acc, Name, Value) when C =/= $% ->
parse_qs_value(Rest, Acc, Name, << Value/binary, C >>);
parse_qs_value(<<>>, Acc, Name, Value) ->
lists:reverse([{Name, Value}|Acc]);
parse_qs_value(_Rest, _Acc, _Name, _Value) ->
Expand Down
33 changes: 33 additions & 0 deletions test/cowmachine_util_tests.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
%% @author Marc Worrell <[email protected]>
%% @hidden

-module(cowmachine_util_tests).

-include_lib("eunit/include/eunit.hrl").


cowmachine_parse_test() ->
[ {<<"a">>, <<"b">>} ] = cowmachine_util:parse_qs(<<"a=b">>),
[ {<<"a">>, <<>>}, {<<"b">>, <<>>} ] = cowmachine_util:parse_qs(<<"a&b">>),
[ {<<"a">>, <<"b">>}, {<<"ab">>, <<"cd">>} ] = cowmachine_util:parse_qs(<<"a=b&ab=cd">>),
[ {<<"a b">>, <<"b c">>} ] = cowmachine_util:parse_qs(<<"a+b=b+c">>),
[ {<<"a b">>, <<"b c">>} ] = cowmachine_util:parse_qs(<<"a%20b=b%20c">>),
ok.

cowmachine_parse_error_test() ->
error = try
cowmachine_util:parse_qs(<<"=b">>)
catch
throw:invalid_qs_name -> error
end,
error = try
cowmachine_util:parse_qs(<<"b%2">>)
catch
throw:invalid_percent_encoding -> error
end,
error = try
cowmachine_util:parse_qs(<<"a%2=b">>)
catch
throw:invalid_percent_encoding -> error
end,
ok.

0 comments on commit 290d5f2

Please sign in to comment.