Skip to content

Commit 9594ce5

Browse files
authored
Merge pull request #753 from tank-bohr/cve-2025-1211
fix(hackney_url): parses query string before userinfo
2 parents f1f294e + cfd50b6 commit 9594ce5

File tree

2 files changed

+41
-11
lines changed

2 files changed

+41
-11
lines changed

src/hackney_url.erl

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,22 +52,24 @@ parse_url(URL) ->
5252
parse_url(URL, #hackney_url{transport=hackney_tcp, scheme=http}).
5353

5454
parse_url(URL, S) ->
55-
{URL1, Fragment= parse_fragment(URL),
56-
case binary:split(URL1, <<"/">>) of
57-
[URL1] ->
58-
parse_addr1(URL1, S#hackney_url{raw_path = raw_fragment(Fragment),
55+
{URL1, Fragment} = cut_fragment(URL),
56+
{URL2, Query} = cut_query(URL1),
57+
RawPath = << (raw_query(Query))/binary, (raw_fragment(Fragment))/binary >>,
58+
case binary:split(URL2, <<"/">>) of
59+
[URL2] ->
60+
parse_addr1(URL2, S#hackney_url{raw_path = RawPath,
5961
path = <<>>,
62+
qs = Query,
6063
fragment = Fragment});
6164
[Addr] ->
6265
Path = <<"/">>,
63-
parse_addr1(Addr, S#hackney_url{raw_path = << Path/binary, (raw_fragment(Fragment))/binary >>,
66+
parse_addr1(Addr, S#hackney_url{raw_path = << Path/binary, RawPath/binary >>,
6467
path = Path,
68+
qs = Query,
6569
fragment = Fragment});
6670
[Addr, Path] ->
67-
RawPath = <<"/", Path/binary, (raw_fragment(Fragment))/binary >>,
68-
{Path1, Query} = parse_path( << "/", Path/binary >>),
69-
parse_addr(Addr, S#hackney_url{raw_path = RawPath,
70-
path = Path1,
71+
parse_addr(Addr, S#hackney_url{raw_path = <<"/", Path/binary, RawPath/binary >>,
72+
path = <<"/", Path/binary >>,
7173
qs = Query,
7274
fragment = Fragment})
7375
end.
@@ -76,6 +78,8 @@ parse_url(URL, S) ->
7678
raw_fragment(<<"">>) -> <<"">>;
7779
raw_fragment(Fragment) -> <<"#", Fragment/binary>>.
7880

81+
raw_query(<<>>) -> <<>>;
82+
raw_query(Query) -> <<"?", Query/binary>>.
7983

8084
property(transport, URL) -> URL#hackney_url.transport;
8185
property(scheme, URL) -> URL#hackney_url.scheme;
@@ -261,15 +265,15 @@ parse_netloc(Netloc, #hackney_url{transport=Transport}=S) ->
261265
end.
262266

263267

264-
parse_path(Path) ->
268+
cut_query(Path) ->
265269
case binary:split(Path, <<"?">>) of
266270
[_Path] ->
267271
{Path, <<>>};
268272
[Path1, Query] ->
269273
{Path1, Query}
270274
end.
271275

272-
parse_fragment(S) ->
276+
cut_fragment(S) ->
273277
case binary:split(S, <<"#">>) of
274278
[_S] ->
275279
{S, <<>>};

test/hackney_url_tests.erl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,32 @@ parse_url_test_() ->
286286
port = 80,
287287
user = <<"">>,
288288
password = <<"">>}
289+
},
290+
{<<"https://example.com/foo/bar">>,
291+
#hackney_url{transport = hackney_ssl,
292+
scheme = https,
293+
netloc = <<"example.com">>,
294+
raw_path = <<"/foo/bar">>,
295+
path = <<"/foo/bar">>,
296+
qs = <<>>,
297+
fragment = <<>>,
298+
host = "example.com",
299+
port = 443,
300+
user = <<>>,
301+
password = <<>>}
302+
},
303+
{<<"http://[email protected]/">>,
304+
#hackney_url{transport = hackney_tcp,
305+
scheme = http,
306+
netloc = <<"127.0.0.1">>,
307+
raw_path = <<"[email protected]/">>,
308+
path = <<>>,
309+
qs = <<"@127.2.2.2/">>,
310+
fragment = <<>>,
311+
host = "127.0.0.1",
312+
port = 80,
313+
user = <<>>,
314+
password = <<>>}
289315
}
290316
],
291317
[{V, fun() -> R = hackney_url:parse_url(V) end} || {V, R} <- Tests].

0 commit comments

Comments
 (0)