CVE-2026-48598
Description
Improper Encoding or Escaping of Output vulnerability in elixir-tesla tesla allows multipart part header injection via unescaped Content-Disposition parameter values.
Tesla.Multipart.part_headers_for_disposition/1 interpolates each disposition parameter as #{k}="#{v}" with no validation of CR (\r), LF (\n), or double-quote characters. The values come verbatim from the caller via Tesla.Multipart.add_field/4 (the name parameter), Tesla.Multipart.add_file/3, and Tesla.Multipart.add_file_content/4 (both the filename parameter and other disposition opts). A " in the value closes the quoted parameter early; a \r\n ends the Content-Disposition header line and starts a new part header (such as a forged Content-Type), or, after a second \r\n, ends the entire part header block and prepends bytes to the part body. The default-filename path in add_file/3 derives the filename via Path.basename/1, which does not strip CR or LF, so any application forwarding a partially-attacker-controlled file path inherits the same issue.
This issue affects tesla: from 0.8.0 before 1.18.3.
Affected products
2(expand)+ 1 more
- (no CPE)
- (no CPE)range: >=0.8.0 <1.18.3
Patches
1bb1a2c3da277Merge commit from fork
2 files changed · +76 −1
lib/tesla/multipart.ex+23 −1 modified@@ -166,12 +166,34 @@ defmodule Tesla.Multipart do def part_headers_for_disposition(kvs) do ds = kvs - |> Enum.map(fn {k, v} -> "#{k}=\"#{v}\"" end) + |> Enum.map(fn {k, v} -> + v_str = to_string(v) + :ok = assert_disposition_value!(k, v_str) + "#{k}=\"#{v_str}\"" + end) |> Enum.join("; ") ["content-disposition: form-data; #{ds}\r\n"] end + @spec assert_disposition_value!(atom | String.t(), String.t()) :: :ok | no_return + defp assert_disposition_value!(key, value) do + cond do + String.contains?(value, ["\r", "\n"]) -> + raise ArgumentError, + "invalid multipart content-disposition value for #{inspect(key)}: " <> + "must not contain CR or LF characters" + + String.contains?(value, "\"") -> + raise ArgumentError, + "invalid multipart content-disposition value for #{inspect(key)}: " <> + "must not contain double-quote characters" + + true -> + :ok + end + end + @spec unique_string() :: String.t() defp unique_string() do 16
test/tesla/multipart_test.exs+53 −0 modified@@ -340,6 +340,59 @@ defmodule Tesla.MultipartTest do assert body_content =~ "json" end + test "rejects CRLF in filename" do + mp = + Multipart.new() + |> Multipart.add_file_content( + "data", + "innocent.txt\"\r\nX-Smuggled: 1\r\nContent-Type: application/x-smuggled" + ) + + assert_raise ArgumentError, ~r/CR or LF characters/, fn -> + Multipart.body(mp) |> Enum.to_list() + end + end + + test "rejects double-quote in filename" do + mp = + Multipart.new() + |> Multipart.add_file_content("data", ~s(evil".txt)) + + assert_raise ArgumentError, ~r/double-quote characters/, fn -> + Multipart.body(mp) |> Enum.to_list() + end + end + + test "rejects CRLF in field name" do + mp = + Multipart.new() + |> Multipart.add_field("upload\r\nX-Smuggled: 1", "data") + + assert_raise ArgumentError, ~r/CR or LF characters/, fn -> + Multipart.body(mp) |> Enum.to_list() + end + end + + test "rejects double-quote in field name" do + mp = + Multipart.new() + |> Multipart.add_field(~s(up"load), "data") + + assert_raise ArgumentError, ~r/double-quote characters/, fn -> + Multipart.body(mp) |> Enum.to_list() + end + end + + test "rejects CRLF in arbitrary disposition opts" do + mp = + Multipart.new() + |> Multipart.add_field("upload", "data", extra: "bad\r\nvalue") + + assert_raise ArgumentError, ~r/CR or LF characters/, fn -> + Multipart.body(mp) |> Enum.to_list() + end + end + test "raw function/2 like Tesla adapter returns" do stream_fun = fn {:cont, acc} -> {:suspended, "data", acc}
Vulnerability mechanics
Root cause
"The multipart library does not sanitize Content-Disposition parameter values, allowing injection of CRLF or double-quote characters."
Attack vector
An attacker can control values such as the filename or field name when constructing a multipart request. By including CRLF sequences or double-quote characters within these values, an attacker can inject additional headers or prepend arbitrary bytes to the part body [ref_id=1]. This can be achieved by passing specially crafted strings to functions like `Tesla.Multipart.add_field/4`, `Tesla.Multipart.add_file/3`, or `Tesla.Multipart.add_file_content/4` [ref_id=1].
Affected code
The vulnerability resides in the `Tesla.Multipart.part_headers_for_disposition/1` function within `lib/tesla/multipart.ex`. This function directly interpolates disposition parameter values without proper sanitization. The `Path.basename/1` function used in `add_file/3` also fails to strip CR or LF characters from filenames, contributing to the vulnerability [ref_id=1].
What the fix does
The patch introduces a new private function, `assert_disposition_value!/2`, which is called within `part_headers_for_disposition/1` [patch_id=4524229]. This function validates that disposition parameter values do not contain CR, LF, or double-quote characters. If such characters are found, an `ArgumentError` is raised, preventing the injection of malicious headers or body content [patch_id=4524229].
Preconditions
- inputThe attacker must control input that is used as a disposition parameter value (e.g., filename, name).
Generated on Jun 2, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.