0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 12:20:24 +00:00

Fix cowboy_http:cookie_to_iodata/3

No more trying to quote, this is still completely broken everywhere.
This commit is contained in:
Loïc Hoguin 2012-12-17 12:32:17 +01:00
parent 1851032482
commit f077c711a8
2 changed files with 47 additions and 22 deletions

View file

@ -815,9 +815,20 @@ ce_identity(Data) ->
-spec cookie_to_iodata(iodata(), iodata(), cowboy_req:cookie_opts()) -spec cookie_to_iodata(iodata(), iodata(), cowboy_req:cookie_opts())
-> iodata(). -> iodata().
cookie_to_iodata(Name, Value, Opts) -> cookie_to_iodata(Name, Value, Opts) ->
case binary:match(Name, [<<$=>>, <<$,>>, <<$;>>,
<<$\s>>, <<$\t>>, <<$\r>>, <<$\n>>, <<$\013>>, <<$\014>>]) of
nomatch -> ok
end,
case binary:match(Value, [<<$,>>, <<$;>>,
<<$\s>>, <<$\t>>, <<$\r>>, <<$\n>>, <<$\013>>, <<$\014>>]) of
nomatch -> ok
end,
MaxAgeBin = case lists:keyfind(max_age, 1, Opts) of MaxAgeBin = case lists:keyfind(max_age, 1, Opts) of
false -> <<>>; false -> <<>>;
{_, MaxAge} when is_integer(MaxAge), MaxAge >= 0 -> {_, 0} ->
%% MSIE requires an Expires date in the past to delete a cookie.
<<"; Expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0">>;
{_, MaxAge} when is_integer(MaxAge), MaxAge > 0 ->
UTC = calendar:universal_time(), UTC = calendar:universal_time(),
Secs = calendar:datetime_to_gregorian_seconds(UTC), Secs = calendar:datetime_to_gregorian_seconds(UTC),
Expires = calendar:gregorian_seconds_to_datetime(Secs + MaxAge), Expires = calendar:gregorian_seconds_to_datetime(Secs + MaxAge),
@ -826,11 +837,11 @@ cookie_to_iodata(Name, Value, Opts) ->
end, end,
DomainBin = case lists:keyfind(domain, 1, Opts) of DomainBin = case lists:keyfind(domain, 1, Opts) of
false -> <<>>; false -> <<>>;
{_, Domain} -> [<<"; Domain=">>, quote(Domain)] {_, Domain} -> [<<"; Domain=">>, Domain]
end, end,
PathBin = case lists:keyfind(path, 1, Opts) of PathBin = case lists:keyfind(path, 1, Opts) of
false -> <<>>; false -> <<>>;
{_, Path} -> [<<"; Path=">>, quote(Path)] {_, Path} -> [<<"; Path=">>, Path]
end, end,
SecureBin = case lists:keyfind(secure, 1, Opts) of SecureBin = case lists:keyfind(secure, 1, Opts) of
false -> <<>>; false -> <<>>;
@ -840,21 +851,9 @@ cookie_to_iodata(Name, Value, Opts) ->
false -> <<>>; false -> <<>>;
{_, true} -> <<"; HttpOnly">> {_, true} -> <<"; HttpOnly">>
end, end,
[Name, <<"=">>, quote(Value), <<"; Version=1">>, [Name, <<"=">>, Value, <<"; Version=1">>,
MaxAgeBin, DomainBin, PathBin, SecureBin, HttpOnlyBin]. MaxAgeBin, DomainBin, PathBin, SecureBin, HttpOnlyBin].
-spec quote(binary()) -> binary().
quote(Bin) ->
quote(Bin, << $" >>).
-spec quote(binary(), binary()) -> binary().
quote(<<>>, Acc) ->
<< Acc/binary, $" >>;
quote(<< $", Rest/bits >>, Acc) ->
quote(Rest, << Acc/binary, $\\, $" >>);
quote(<< C, Rest/bits >>, Acc) ->
quote(Rest, << Acc/binary, C >>).
%% @doc Convert an HTTP version tuple to its binary form. %% @doc Convert an HTTP version tuple to its binary form.
-spec version_to_binary(version()) -> binary(). -spec version_to_binary(version()) -> binary().
version_to_binary({1, 1}) -> <<"HTTP/1.1">>; version_to_binary({1, 1}) -> <<"HTTP/1.1">>;
@ -1160,14 +1159,14 @@ cookie_to_iodata_test_() ->
Tests = [ Tests = [
{<<"Customer">>, <<"WILE_E_COYOTE">>, {<<"Customer">>, <<"WILE_E_COYOTE">>,
[{http_only, true}, {domain, <<"acme.com">>}], [{http_only, true}, {domain, <<"acme.com">>}],
<<"Customer=\"WILE_E_COYOTE\"; Version=1; " <<"Customer=WILE_E_COYOTE; Version=1; "
"Domain=\"acme.com\"; HttpOnly">>}, "Domain=acme.com; HttpOnly">>},
{<<"Customer">>, <<"WILE_E_COYOTE">>, {<<"Customer">>, <<"WILE_E_COYOTE">>,
[{path, <<"/acme">>}], [{path, <<"/acme">>}],
<<"Customer=\"WILE_E_COYOTE\"; Version=1; Path=\"/acme\"">>}, <<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>},
{<<"Customer">>, <<"WILE_E_COYOTE">>, {<<"Customer">>, <<"WILE_E_COYOTE">>,
[{path, <<"/acme">>}, {badoption, <<"negatory">>}], [{path, <<"/acme">>}, {badoption, <<"negatory">>}],
<<"Customer=\"WILE_E_COYOTE\"; Version=1; Path=\"/acme\"">>} <<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>}
], ],
[{R, fun() -> R = iolist_to_binary(cookie_to_iodata(N, V, O)) end} [{R, fun() -> R = iolist_to_binary(cookie_to_iodata(N, V, O)) end}
|| {N, V, O, R} <- Tests]. || {N, V, O, R} <- Tests].
@ -1177,7 +1176,7 @@ cookie_to_iodata_max_age_test() ->
binary:split(iolist_to_binary( binary:split(iolist_to_binary(
cookie_to_iodata(N, V, O)), <<";">>, [global]) cookie_to_iodata(N, V, O)), <<";">>, [global])
end, end,
[<<"Customer=\"WILE_E_COYOTE\"">>, [<<"Customer=WILE_E_COYOTE">>,
<<" Version=1">>, <<" Version=1">>,
<<" Expires=", _/binary>>, <<" Expires=", _/binary>>,
<<" Max-Age=111">>, <<" Max-Age=111">>,
@ -1186,13 +1185,33 @@ cookie_to_iodata_max_age_test() ->
case catch F(<<"Customer">>, <<"WILE_E_COYOTE">>, [{max_age, -111}]) of case catch F(<<"Customer">>, <<"WILE_E_COYOTE">>, [{max_age, -111}]) of
{'EXIT', {{case_clause, {max_age, -111}}, _}} -> ok {'EXIT', {{case_clause, {max_age, -111}}, _}} -> ok
end, end,
[<<"Customer=\"WILE_E_COYOTE\"">>, [<<"Customer=WILE_E_COYOTE">>,
<<" Version=1">>, <<" Version=1">>,
<<" Expires=", _/binary>>, <<" Expires=", _/binary>>,
<<" Max-Age=86417">>] = F(<<"Customer">>, <<"WILE_E_COYOTE">>, <<" Max-Age=86417">>] = F(<<"Customer">>, <<"WILE_E_COYOTE">>,
[{max_age, 86417}]), [{max_age, 86417}]),
ok. ok.
cookie_to_iodata_failures_test_() ->
F = fun(N, V) ->
try cookie_to_iodata(N, V, []) of
_ ->
false
catch _:_ ->
true
end
end,
Tests = [
{<<"Na=me">>, <<"Value">>},
{<<"Name;">>, <<"Value">>},
{<<"\r\name">>, <<"Value">>},
{<<"Name">>, <<"Value;">>},
{<<"Name">>, <<"\value">>}
],
[{iolist_to_binary(io_lib:format("{~p, ~p} failure", [N, V])),
fun() -> true = F(N, V) end}
|| {N, V} <- Tests].
x_www_form_urlencoded_test_() -> x_www_form_urlencoded_test_() ->
%% {Qs, Result} %% {Qs, Result}
Tests = [ Tests = [

View file

@ -800,6 +800,12 @@ multipart_skip(Req) ->
%% Response API. %% Response API.
%% @doc Add a cookie header to the response. %% @doc Add a cookie header to the response.
%%
%% The cookie name cannot contain any of the following characters:
%% =,;\s\t\r\n\013\014
%%
%% The cookie value cannot contain any of the following characters:
%% ,; \t\r\n\013\014
-spec set_resp_cookie(iodata(), iodata(), cookie_opts(), Req) -spec set_resp_cookie(iodata(), iodata(), cookie_opts(), Req)
-> Req when Req::req(). -> Req when Req::req().
set_resp_cookie(Name, Value, Opts, Req) -> set_resp_cookie(Name, Value, Opts, Req) ->