Search documentation of Metastatic

# Metastatic.Analysis.BusinessLogic.UnrestrictedFileUpload (Metastatic v0.13.2)

View Source

Detects unrestricted file upload vulnerabilities (CWE-434).

This analyzer identifies code patterns where file uploads are processed without proper validation of file type, size, or content.

## Cross-Language Applicability

Unrestricted file upload is a universal web vulnerability:

- Elixir/Phoenix: `File.write!(path, upload.content)` without validation
- Python/Flask: `file.save(path)` without checking extension
- JavaScript/Express: `multer` without file filter
- Ruby/Rails: `file.attach` without validation
- PHP: `move_uploaded_file()` without checks
- Java/Spring: `transferTo()` without validation
- C#/ASP.NET: `SaveAs()` without file type check

## Problem

When file uploads lack validation:

- Attackers can upload executable files (web shells)
- Server can be compromised through uploaded malware
- Denial of service through large file uploads
- Storage exhaustion attacks

## Detection Strategy

Detects patterns where:

1. File save/write operations receive uploaded content
2. No file type/extension validation is apparent
3. No file size validation is apparent
4. Original filename is used directly without sanitization

## Examples

### Bad (Elixir)

    def upload(conn, %{"file" => upload}) do
      path = "/uploads/#{upload.filename}"
      File.write!(path, upload.path |> File.read!())
      json(conn, %{status: "uploaded"})
    end

### Good (Elixir)

    @allowed_extensions ~w[.jpg .jpeg .png .gif]
    @max_size 5_000_000

    def upload(conn, %{"file" => upload}) do
      ext = Path.extname(upload.filename) |> String.downcase()
      size = File.stat!(upload.path).size

      cond do
        ext not in @allowed_extensions ->
          conn |> put_status(400) |> json(%{error: "Invalid file type"})

        size > @max_size ->
          conn |> put_status(400) |> json(%{error: "File too large"})

        true ->
          safe_name = "#{UUID.uuid4()}#{ext}"
          File.copy!(upload.path, "/uploads/#{safe_name}")
          json(conn, %{status: "uploaded", filename: safe_name})
      end
    end