Skip to content

Commit 47e4802

Browse files
authored
Merge commit from fork
`Mint.HTTP1.Parse.content_length_header/1` parsed the header value with `Integer.parse/1`, which accepts an optional `+`/`-` sign prefix. The `length >= 0` guard rejected negatives but let values like `+0` or `+123` through, returning them as valid lengths. RFC 7230 defines `Content-Length = 1*DIGIT`, with no sign permitted. On a connection shared with a strict fronting proxy this parser disagreement is a response-smuggling primitive: the proxy frames the body one way and Mint another. Validate that the trimmed value is one or more digits before converting it, so signs, embedded whitespace, or any non-digit byte are rejected with `:invalid_content_length_header`. Fixes GHSA-mjqx-c6f6-7rc2.
1 parent b8d2393 commit 47e4802

2 files changed

Lines changed: 28 additions & 3 deletions

File tree

lib/mint/http1/parse.ex

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,19 @@ defmodule Mint.HTTP1.Parse do
1818
def ignore_until_crlf(<<_char, rest::binary>>), do: ignore_until_crlf(rest)
1919

2020
def content_length_header(string) do
21-
case Integer.parse(String.trim_trailing(string)) do
22-
{length, ""} when length >= 0 -> {:ok, length}
23-
_other -> {:error, {:invalid_content_length_header, string}}
21+
trimmed = String.trim_trailing(string)
22+
23+
if only_digits?(trimmed) do
24+
{:ok, String.to_integer(trimmed)}
25+
else
26+
{:error, {:invalid_content_length_header, string}}
2427
end
2528
end
2629

30+
defp only_digits?(<<char>>) when is_digit(char), do: true
31+
defp only_digits?(<<char, rest::binary>>) when is_digit(char), do: only_digits?(rest)
32+
defp only_digits?(_other), do: false
33+
2734
def connection_header(string) do
2835
split_into_downcase_tokens(string)
2936
end

test/mint/http1/parse_test.exs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@ defmodule Mint.HTTP1.ParseTest do
1414

1515
assert content_length_header("-10") ==
1616
{:error, {:invalid_content_length_header, "-10"}}
17+
18+
assert content_length_header("+0") ==
19+
{:error, {:invalid_content_length_header, "+0"}}
20+
21+
assert content_length_header("+123") ==
22+
{:error, {:invalid_content_length_header, "+123"}}
23+
24+
assert content_length_header("") ==
25+
{:error, {:invalid_content_length_header, ""}}
26+
27+
assert content_length_header(" 100") ==
28+
{:error, {:invalid_content_length_header, " 100"}}
29+
30+
assert content_length_header("1 0") ==
31+
{:error, {:invalid_content_length_header, "1 0"}}
32+
33+
assert content_length_header("0x10") ==
34+
{:error, {:invalid_content_length_header, "0x10"}}
1735
end
1836

1937
test "connection_header/1" do

0 commit comments

Comments
 (0)