Skip to content

Path traversal vulnerability in attachment filename parsing #29

Description

@FernandoCelmer

Description

The parse_body() function in parser.py accepts attachment filenames from email headers without any sanitization. A malicious email can embed path traversal sequences (e.g. ../../../etc/passwd) in the Content-Disposition header, and the raw filename is stored directly in the Attachment.file_name field.

Any downstream code that uses Attachment.file_name to write files to disk (e.g. saving attachments) could be tricked into writing outside the intended directory.

Location

email_profile/parser.py — lines 196–213

Current Behavior

if "attachment" in disposition.lower():
    filename = part.get_filename()       # ← raw, unsanitized
    if not filename:
        continue

    payload = part.get_payload(decode=True) or b""

    body.attachments.append(
        Attachment(
            file_name=_decode_header(filename),  # ← stored as-is
            content_type=content_type,
            content=(...),
        )
    )

A crafted email like this would be accepted:

Content-Disposition: attachment; filename="../../../tmp/evil.sh"

The resulting Attachment.file_name would be ../../../tmp/evil.sh.

Expected Behavior

Filenames should be sanitized to their base name, stripping any directory traversal components. Only the final filename component should be kept.

Suggested Fix

import os

# Inside the attachment block:
filename = part.get_filename()
if not filename:
    continue

# Sanitize: keep only the base filename, strip traversal sequences
filename = os.path.basename(_decode_header(filename))
if not filename:
    continue

Impact

Who: Any user who saves attachments to disk using the Attachment.file_name value.

Severity: Critical — this is a well-known attack vector (CWE-22: Path Traversal). Even though the library doesn't write files itself, it provides the unsanitized filename to consumers who reasonably expect it to be safe.

Priority

Critical

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcriticalCritical severity — runtime failures, data loss, securitysecuritySecurity concern

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions