Open Source
Nodemailer

Published: August 15, 2026 · by Srinu Desetti · nodemailer/nodemailer#1837 (opens in a new tab)

Fixing Attachment Filename Escaping in Nodemailer

Nodemailer is the de-facto standard for sending email from Node.js. My contribution — merged into the main branch — ensures that attachment filenames containing special characters (" and \) are correctly escaped before being written into email headers.

How Nodemailer Builds an Email

When an attachment is sent, Nodemailer generates headers like:

Content-Type: application/octet-stream; name="Resume.pdf"
Content-Disposition: attachment; filename="Resume.pdf"

The filename is wrapped in double quotes.

The Problem

Most filenames are simple — Resume.pdf, Invoice.docx. But applications cannot assume filenames are always simple:

Resume".pdf
foo\

These values come from user uploads, external storage, ZIP files, or generated filenames. Previously, Nodemailer inserted the filename directly, producing:

name="foo\"

Why This Breaks

Inside a quoted email header, \" does not mean backslash + end quote. It means escaped quote — the quote belongs to the filename.

The parser therefore thinks the filename has not ended. Instead of stopping at name="foo\", it keeps reading the following headers as part of the filename:

Content-Disposition: attachment
Content-Transfer-Encoding: base64

What the parser should see vs what it may see:

nameNext headers
Expectedfoo\parsed normally
Actualfoo"Content-Disposition: attachment Content-Transfer-Encoding: base64swallowed into the filename

The remaining headers are no longer parsed correctly.

Real-World Impact of the Bug

  • Malformed email headers
  • Incorrect filename parsing
  • Different email clients parsing the same message differently
  • Potential parser confusion / header-smuggling scenarios — unescaped user input controlling header structure is the same class of problem as HTTP header injection

The Fix

The original code:

lib/mime-funcs/index.js — before
param = '"' + param + '"';

Updated to escape before quoting:

lib/mime-funcs/index.js — after (my fix)
param = '"' + param.replace(/["\\]/g, '\\$&') + '"';

The replacement escapes both dangerous characters:

  • "\"
  • \\\

Example with input foo\:

Before:  name="foo\"      ← parser thinks the quote is escaped; filename never ends
After:   name="foo\\"     ← parser reads one literal backslash, then the closing quote

The filename ends correctly and the next headers are parsed normally.

Impact

  • Attachment filenames from untrusted sources (user uploads, external systems) can no longer corrupt the email's header structure.
  • Emails render consistently across clients — no more parser-dependent behavior for edge-case filenames.
  • Closes a header-injection-shaped hole: filename content can no longer leak into header parsing.

What I Contributed

  • Identified the escaping issue.
  • Implemented proper escaping for " and \.
  • Added regression tests.
  • Worked with the maintainer during code review until the PR was merged into main.

Key takeaway — any time user-controlled text is inserted into a structured format (email headers, HTTP headers, SQL, HTML), the escaping rules of that format are the security boundary. This fix is that principle applied to RFC-compliant email headers.

View the pull request → nodemailer/nodemailer#1837 (opens in a new tab)


← Previous: React Redux error reporting · Next: iovalkey socket timeout fix →


Frequently Asked Questions

Why is an unescaped quote in a filename dangerous?

Inside a quoted email header value, a quote preceded by a backslash is read as an escaped quote belonging to the value. A filename ending in a backslash therefore produces " at the boundary, so the parser believes the value never ended and consumes the following headers as part of the filename.

Is this a security issue?

It is the same class of problem as header injection: user-controlled input altering the structure of a parsed format. Different clients could parse the same message differently, and following headers could be swallowed or misread — which is why escaping at the boundary is the correct fix.

What characters does the fix escape?

Double quote and backslash — the two characters with structural meaning inside a quoted email header value. Quote becomes backslash-quote, and backslash becomes double-backslash.

Where do such filenames even come from?

User uploads, external storage systems, ZIP archive contents, and programmatically generated filenames. An email library cannot assume filenames are simple — it must escape whatever it is given.