Back to Blog
high SEVERITY8 min read

How Message-Level Raw Option Bypass happens in Node.js Nodemailer and how to fix it

A high-severity vulnerability in Nodemailer (GHSA-p6gq-j5cr-w38f) allowed attackers to bypass the `disableFileAccess` and `disableUrlAccess` security controls by using the message-level `raw` option, enabling arbitrary file reads and full-response SSRF in delivered emails. The fix upgrades Nodemailer from version 6.10.1 to 9.0.1, closing this bypass at the library level. This is especially critical for applications that allow any user-influenced content to flow into email composition.

O
By Orbis AppSec
Published August 8, 2026Reviewed August 8, 2026

Answer Summary

GHSA-p6gq-j5cr-w38f is a high-severity security bypass vulnerability in Nodemailer (Node.js), mapped to CWE-284 (Improper Access Control). In affected versions (up to 6.x), the message-level `raw` option bypasses the `disableFileAccess` and `disableUrlAccess` guards entirely, allowing an attacker to embed file paths or internal URLs in outgoing emails and receive their contents back — enabling arbitrary local file read and full-response SSRF. The fix is to upgrade Nodemailer to version 9.0.1 or later, where the `raw` option is subject to the same access restrictions as all other message fields.

Vulnerability at a Glance

cweCWE-284 (Improper Access Control)
fixUpgrade Nodemailer from 6.10.1 to 9.0.1, where the `raw` option respects all configured access restrictions
riskArbitrary local file read and full-response Server-Side Request Forgery (SSRF) via crafted email messages
languageJavaScript / Node.js
root causeThe `raw` message option in Nodemailer 6.x was not subject to the same `disableFileAccess`/`disableUrlAccess` enforcement as other message fields
vulnerabilitySecurity Control Bypass (disableFileAccess / disableUrlAccess) via raw message option

How Message-Level Raw Option Bypass Happens in Node.js Nodemailer and How to Fix It


The Problem with "Safe" Security Flags That Aren't

When developers reach for disableFileAccess and disableUrlAccess in Nodemailer, they're doing the right thing: they're explicitly telling the library not to resolve local file paths or make outbound HTTP requests while composing email messages. This is a critical safeguard for any application that lets user-influenced data flow into outgoing emails.

The problem? In Nodemailer 6.x, those flags had a blind spot — and that blind spot was the message-level raw option.

In a project's pnpm-lock.yaml, Trivy flagged nodemailer@6.10.1 as carrying GHSA-p6gq-j5cr-w38f: a high-severity vulnerability where the raw option completely bypasses both disableFileAccess and disableUrlAccess, opening the door to arbitrary local file reads and full-response SSRF embedded directly in delivered email messages.


The Vulnerability Explained

What raw Does — and What It Skipped

Nodemailer's raw option is a power-user feature. Instead of letting Nodemailer compose a MIME message from structured fields (to, from, html, attachments, etc.), you hand it a pre-built raw RFC 2822 message string or stream. Nodemailer is then supposed to deliver it as-is.

The security controls disableFileAccess and disableUrlAccess are enforced during the message composition phase — when Nodemailer processes structured fields and resolves any embedded references (like path: '/etc/passwd' in an attachment, or href: 'http://internal-service/' in an HTML body). Here's an example of how those flags are typically set:

// Intended to be safe — but wasn't in 6.x when using `raw`
const transporter = nodemailer.createTransport({
  host: 'smtp.example.com',
  port: 587,
  disableFileAccess: true,   // Should block local file reads
  disableUrlAccess: true,    // Should block HTTP fetches
});

await transporter.sendMail({
  from: 'app@example.com',
  to: user.email,
  raw: userControlledRawMessage,  // ← bypasses both flags entirely in 6.x
});

In Nodemailer 6.10.1, when you use the raw field, the library short-circuits the normal composition pipeline and delivers the content without running it through the access-control checks that disableFileAccess and disableUrlAccess enforce. The flags are set, the developer believes the application is protected, but the raw path is a completely separate code route that never consults those flags.

The Concrete Attack Scenario

Consider an application that:

  1. Accepts user-provided email content (e.g., a "send this report to a colleague" feature)
  2. Passes that content through to Nodemailer using the raw option for formatting flexibility
  3. Has disableFileAccess: true and disableUrlAccess: true set on the transporter, believing the application is hardened

An attacker who can influence the value passed to raw — even partially, through a template injection, a deserialization flaw, or a misconfigured API endpoint — can craft a raw MIME message that references:

  • Local files: e.g., embedding a reference to /etc/passwd, /app/.env, or any secrets file readable by the Node.js process
  • Internal URLs: e.g., pointing to http://169.254.169.254/latest/meta-data/ (AWS IMDS), internal microservices, or other SSRF targets

Because the raw path skips the access-control checks, Nodemailer resolves those references and includes their full contents in the delivered email message. The attacker receives the exfiltrated data in their inbox.

This is not a theoretical primitive. It is a direct, high-severity data exfiltration and SSRF path that bypasses what developers reasonably believe is a working security control.

Why This Is Especially Dangerous

The danger is compounded by a false sense of security. Developers who set disableFileAccess: true have taken an affirmative action to protect their application. They've read the documentation, they've done the right thing — but in 6.x, that action provides no protection when raw is in play. Automated exploit-development tooling increasingly chains exactly these kinds of "protection that doesn't protect" primitives with other weaknesses to build end-to-end exploits.


The Fix

Upgrading from 6.10.1 to 9.0.1

The fix is a direct version upgrade. In package.json, the specifier changed from ^6.9.14 to ^9.0.1:

- "nodemailer": "^6.9.14",
+ "nodemailer": "^9.0.1",

And in pnpm-lock.yaml, the resolved version moved from 6.10.1 to 9.0.1:

   nodemailer:
-    specifier: ^6.9.14
-    version: 6.10.1
+    specifier: ^9.0.1
+    version: 9.0.1

The integrity hash also changed, confirming a completely different package artifact is now being installed:

-  nodemailer@6.10.1:
-    resolution: {integrity: sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==}
+  nodemailer@9.0.1:
+    resolution: {integrity: sha512-Gwv8SQewT616ZM/URn0H54b8PWo/Wum7md3EW2aWy1lO27+WZCX+Xyak3J+NlmHUJDh5ME+uesJUDRbR3Ye8Bw==}

What Changed in Nodemailer 9.x

In Nodemailer 9.0.1, the raw option is no longer a bypass route. The access-control enforcement for disableFileAccess and disableUrlAccess is applied consistently across all message composition paths, including raw. This means:

  • If disableFileAccess: true is set, file references in raw messages are blocked
  • If disableUrlAccess: true is set, URL fetches triggered by raw message content are blocked
  • The security model developers expected is now the security model they actually get

Why Two Files Were Changed

Both package.json and pnpm-lock.yaml required changes because pnpm uses a deterministic lockfile. Changing only package.json would update the version range specifier but leave the old resolved version (6.10.1) pinned in the lockfile. Both files must be updated together to ensure the correct version is actually installed in all environments, including CI/CD pipelines and production deployments.


Prevention & Best Practices

1. Treat Dependency Security Flags as Contracts — and Verify Them

When a library exposes security configuration like disableFileAccess, treat it as a contract that needs verification. Write integration tests that confirm the flag actually prevents the behavior you expect, especially after upgrades or when using non-standard API options like raw.

2. Never Pass Unvalidated User Input to raw

Even with the fix in place, the raw option should be treated with the same caution as eval(). If any part of a raw MIME message is influenced by user input, validate and sanitize it rigorously. Prefer structured Nodemailer fields (html, text, attachments) over raw whenever possible, as they are processed through Nodemailer's full security pipeline.

3. Use Dependency Scanning in CI/CD

This vulnerability was detected by Trivy scanning pnpm-lock.yaml. Integrate a dependency vulnerability scanner (Trivy, Snyk, npm audit, or similar) into your CI/CD pipeline so that known CVEs and GHSA advisories are caught before they reach production.

# Example: Trivy filesystem scan
trivy fs --scanners vuln .

# Example: pnpm audit
pnpm audit --audit-level high

4. Pin Major Versions Carefully

The jump from ^6.9.14 to ^9.0.1 is a major version bump (6 → 9). Major version bumps may include breaking changes. Review the Nodemailer changelog before upgrading in production, and run your full test suite to confirm behavior is preserved. In this case, the PR notes that "the change only tightens handling of untrusted input and leaves valid inputs unaffected."

5. Apply the Principle of Least Privilege to Email Composition

The Node.js process sending emails should run with the minimum filesystem permissions necessary. Even if a file-read bypass were exploited, a process that can only read a narrow set of files limits the blast radius.

Relevant Standards


Key Takeaways

  • disableFileAccess and disableUrlAccess in Nodemailer 6.x are not enforced when the raw message option is used — setting them does not protect you if raw is in your code path.
  • The raw option is a high-risk API surface: any user-influenced data reaching raw in Nodemailer 6.x is a direct path to file read and SSRF, bypassing all configured security controls.
  • The pnpm-lock.yaml lockfile must be updated alongside package.json — updating only the version range in package.json leaves the vulnerable 6.10.1 pinned in the lockfile and installed in practice.
  • False security is worse than acknowledged insecurity: the presence of disableFileAccess: true in code using vulnerable Nodemailer versions may delay detection of the actual attack surface.
  • Trivy's dependency scanning caught this in pnpm-lock.yaml — scanning lockfiles (not just package.json) is essential because lockfiles contain the actual resolved versions installed in production.

How Orbis AppSec Detected This

  • Source: User-influenced content passed to the raw field in a transporter.sendMail() call — any code path where external or user-provided data reaches the raw message option in Nodemailer 6.x.
  • Sink: Nodemailer's internal message composition pipeline in nodemailer@6.10.1, specifically the raw option handler that resolves file paths and URLs without consulting disableFileAccess or disableUrlAccess.
  • Missing control: The disableFileAccess and disableUrlAccess flags were not applied to the raw message composition code path in Nodemailer 6.x, creating an unguarded route to filesystem access and outbound HTTP requests.
  • CWE: CWE-284 — Improper Access Control (a security control exists but is inconsistently enforced).
  • Fix: Upgraded nodemailer from 6.10.1 to 9.0.1 in both package.json and pnpm-lock.yaml, where the raw option is subject to the same access restrictions as all other message fields.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

GHSA-p6gq-j5cr-w38f is a reminder that security controls are only as strong as their consistent enforcement. Nodemailer's disableFileAccess and disableUrlAccess flags are the right tool for the job — but in versions prior to 9.0.1, the raw option silently voided both of them. For any application composing emails with user-influenced content, this was a direct path to arbitrary file exfiltration and SSRF.

The fix is simple: upgrade to Nodemailer 9.0.1. The lesson is broader: when you rely on a library's security configuration, verify that it applies uniformly across all the API surfaces you use — not just the ones documented in the "safe usage" examples. Automated dependency scanning, as demonstrated here with Trivy detecting this in pnpm-lock.yaml, is an essential layer in catching these gaps before attackers do.


References

Frequently Asked Questions

What is the Nodemailer raw option bypass vulnerability?

It is a flaw in Nodemailer versions prior to 9.0.1 where using the message-level `raw` option skips the `disableFileAccess` and `disableUrlAccess` security controls, letting an attacker embed local file paths or internal URLs that get resolved and included in the delivered email.

How do you prevent this bypass in Node.js Nodemailer?

Upgrade to Nodemailer 9.0.1 or later. In that version, the `raw` option is subject to the same access restrictions as all other message composition fields, so `disableFileAccess` and `disableUrlAccess` are consistently enforced.

What CWE is this Nodemailer vulnerability?

CWE-284 — Improper Access Control. The security controls exist in the library but are inconsistently applied, allowing a specific code path (`raw`) to circumvent them entirely.

Is setting disableFileAccess and disableUrlAccess enough to prevent this in older Nodemailer versions?

No. In Nodemailer 6.x, those flags are bypassed when the `raw` message option is used, so the protection is ineffective unless you also upgrade to 9.0.1.

Can static analysis detect this Nodemailer bypass?

Static analysis tools like Trivy (which flagged this exact issue) and Semgrep can detect the use of vulnerable Nodemailer versions via dependency scanning. Runtime detection is harder without taint tracking from user input to the `raw` field.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #9

Related Articles

high

How Message-Level Raw Option Bypass happens in Node.js Nodemailer and how to fix it

A high-severity vulnerability in Nodemailer (versions before 9.0.0) allowed the `raw` message option to completely bypass `disableFileAccess` and `disableUrlAccess` security controls, enabling attackers to read arbitrary files from the server filesystem and perform full-response Server-Side Request Forgery (SSRF) in delivered email messages. Upgrading from `^8.0.10` to `^9.0.4` in `backend/package-lock.json` closes this exploit primitive by enforcing access restrictions consistently across all m

high

How Nodemailer raw option bypass happens in Node.js and how to fix it

A high-severity vulnerability in Nodemailer versions prior to 9.0.1 allowed attackers to bypass the `disableFileAccess` and `disableUrlAccess` security controls using the message-level `raw` option. This bypass enabled arbitrary file reads from the server and full-response Server-Side Request Forgery (SSRF) attacks, potentially exposing sensitive configuration files and internal network resources. The fix involves upgrading Nodemailer from version 8.0.7 to 9.0.1.

high

CVE-2025-14874: Nodemailer DoS via Crafted Email Address Header

CVE-2025-14874 is a high-severity Denial of Service vulnerability in Nodemailer that allows an attacker to crash an application by sending a specially crafted email address header. The vulnerability existed in Nodemailer versions prior to 7.0.11 and was present in the `Dise-ador-experto-master` project's `package-lock.json` dependency on version 6.10.1. Upgrading to Nodemailer 7.0.11 resolves the issue by fixing the underlying header parsing logic that could be exploited to cause unbounded resou

high

How Path Traversal and Security Policy Bypass Happens in Node.js Dependencies and How to Fix It

A high-severity vulnerability in the fast-uri package (CVE-2026-6321) allowed attackers to bypass security policies through improper Unicode hostname canonicalization and path traversal. This issue affected the @apralabs/apra-fleet project through its dependency tree, and was resolved by upgrading fast-uri from version 3.1.0 to 4.1.2 using npm overrides.

high

How Command Injection happens in Node.js child_process calls and how to fix it

A high-severity command injection vulnerability was discovered in `tools/utils/lang/helpers.ts` where the `prettier()` function passed a user-controllable `fileName` argument directly into a shell command string via `exec()`. By replacing `exec()` with `execFile()` and passing arguments as an array, the fix eliminates shell interpolation entirely, preventing attackers from injecting arbitrary shell commands through malicious filenames.

high

How Quadratic CPU Consumption in YAML Parsing happens in JavaScript and how to fix it

A high-severity vulnerability in js-yaml versions 3.x and 4.x allowed attackers to cause quadratic CPU consumption through specially crafted YAML documents using the `!!omap` type. This denial-of-service vulnerability (GHSA-5p4m-2wfm-xmqj) was fixed by upgrading from js-yaml 4.3.0 to 4.3.1, protecting applications from algorithmic complexity attacks during YAML parsing.