Jump.CredoChecks.NoManualContentDisposition (Jump.CredoChecks v0.5.0)

View Source

Basics

This check is disabled by default.

Learn how to enable it via .credo.exs.

This check has a base priority of normal and works with any version of Elixir.

Explanation

Prefer Phoenix.Controller.send_download/3 instead of manually setting content-disposition.

This prevents injection attacks by sanitizing filename in the response header. This follows the suggestion from the Cowboy security checklist:

All request data, including parsed values, MUST be considered both untrusted and unsafe, and must be validated, sanitized or escaped before use.

# ❌ Bad (manual, error-prone use of content-disposition header):
conn
|> put_resp_content_type(content_type)
|> put_resp_header("content-disposition", "attachment; filename="#{filename}.csv")
|> send_resp(200, content)

# ✅ Good (Phoenix handles filename sanitization)
send_download(conn, {:binary, content},
  filename: filename,
  content_type: content_type,
  charset: "utf-8",
  disposition: :inline
)

# 🆗 OK (need to stream response, can't use send_download)
encoded_filename = URI.encode(filename, &URI.char_unreserved?/1)
content_disposition = ~s[attachment; filename="#{encoded_filename}"; filename*=utf-8''#{encoded_filename}]

conn
|> put_resp_content_type("text/csv")
# stream chunks, so send_download/3 cannot be used here
# credo:disable-for-next-line Ev2.Credo.NoManualContentDisposition
|> put_resp_header("content-disposition", content_disposition)
|> send_chunked(200)

Check-Specific Parameters

There are no specific parameters for this check.

General Parameters

Like with all checks, general params can be applied.

Parameters can be configured via the .credo.exs config file.