VYPR
Low severityNVD Advisory· Published Jun 2, 2026

CVE-2026-48598

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
  • Elixir Tesla/Teslareferences2 versions
    (expand)+ 1 more
    • (no CPE)
    • (no CPE)range: >=0.8.0 <1.18.3

Patches

1
bb1a2c3da277

Merge commit from fork

https://github.com/elixir-tesla/teslaYordis PrietoJun 2, 2026via body-scan
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

4

News mentions

0

No linked articles in our index yet.