Server-side validation for editor uploads: size limit, content-type verification via magic-byte sniffing, and filename sanitization.
The client's Content-Type header is attacker-controlled; for the
sniffable formats (images, videos, PDF) the actual file signature must
agree with the claimed type or the upload is rejected. Types without a
reliable signature (e.g. text/plain) are accepted based on the
allow-list alone.
Summary
Functions
The default allowed MIME list.
The default maximum size in bytes (25 MB).
Sanitizes a client-provided filename: basename only, control characters
and path separators stripped, length-capped. Falls back to "file".
Detects the file's MIME type from its magic bytes. Returns
{:ok, type} for the known signatures or :unknown.
Validates a Plug.Upload.
Functions
The default allowed MIME list.
The default maximum size in bytes (25 MB).
Sanitizes a client-provided filename: basename only, control characters
and path separators stripped, length-capped. Falls back to "file".
Detects the file's MIME type from its magic bytes. Returns
{:ok, type} for the known signatures or :unknown.
@spec validate( Plug.Upload.t(), keyword() ) :: {:ok, %{content_type: String.t(), size: non_neg_integer(), filename: String.t()}} | {:error, :empty | :too_large | :unsupported_type | :content_type_mismatch}
Validates a Plug.Upload.
Options: :max_bytes (default 25 MB) and :allowed (MIME allow-list).
Returns {:ok, %{content_type: type, size: bytes, filename: sanitized}}
with the verified content type, or {:error, reason} where reason is
:empty, :too_large, :unsupported_type, or :content_type_mismatch.