Back to Blog
high SEVERITY6 min read

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.

O
By Orbis AppSec
Published July 26, 2026Reviewed July 26, 2026

Answer Summary

GHSA-p6gq-j5cr-w38f is a high-severity vulnerability in Nodemailer (Node.js email library) where the message-level `raw` option bypasses `disableFileAccess` and `disableUrlAccess` security controls, enabling arbitrary file read and SSRF attacks (CWE-918, CWE-22). The fix is to upgrade Nodemailer to version 9.0.1 or later, which properly enforces these security restrictions regardless of the raw option usage.

Vulnerability at a Glance

cweCWE-918 (SSRF), CWE-22 (Path Traversal)
fixUpgrade Nodemailer from 8.0.7 to 9.0.1
riskAttackers can read arbitrary server files and perform SSRF to internal services
languageNode.js (JavaScript)
root causeThe `raw` message option did not respect `disableFileAccess`/`disableUrlAccess` transport settings
vulnerabilitySecurity control bypass via raw option (SSRF + Arbitrary File Read)

Introduction

In a production web service's package-lock.json, we discovered a high-severity vulnerability in the Nodemailer dependency at version 8.0.7. This wasn't a simple coding mistake—it was a fundamental design flaw in how Nodemailer handled its raw message option, completely bypassing security controls that developers explicitly configured to prevent dangerous operations.

The vulnerability (GHSA-p6gq-j5cr-w38f) allowed attackers to read arbitrary files from the server and perform full-response SSRF attacks, even when the application had properly configured disableFileAccess: true and disableUrlAccess: true on the transport. For a web service handling user-influenced input, this meant remote attackers could potentially exfiltrate /etc/passwd, AWS credentials, or probe internal network services—all through the email functionality.

The Vulnerability Explained

What Are disableFileAccess and disableUrlAccess?

Nodemailer provides two critical security options when creating a transport:

const transporter = nodemailer.createTransport({
  host: 'smtp.example.com',
  port: 587,
  disableFileAccess: true,  // Should prevent file:// URLs
  disableUrlAccess: true    // Should prevent http:// and https:// URLs
});

These settings are designed to prevent email content from including:
- File attachments via path: Reading arbitrary files from the server filesystem
- Remote content via URL: Making HTTP requests to fetch content (SSRF vector)

The Bypass Mechanism

The vulnerability existed in how Nodemailer processed the raw option at the message level. When composing an email, developers can use raw to provide pre-built MIME content:

// Vulnerable pattern in Nodemailer < 9.0.1
transporter.sendMail({
  from: 'sender@example.com',
  to: 'victim@example.com',
  raw: maliciousRawContent  // This bypassed security controls!
});

The raw option was processed through a different code path that did not check the transport-level disableFileAccess and disableUrlAccess settings. This meant an attacker who could influence the raw content could:

  1. Read arbitrary files: Include file:///etc/passwd or file:///app/.env references
  2. Perform SSRF: Include URLs to internal services like http://169.254.169.254/latest/meta-data/ (AWS metadata endpoint)

Attack Scenario for This Web Service

Since this is a web service where vulnerabilities in request handlers are directly exploitable by remote attackers, consider this attack flow:

  1. An attacker identifies an endpoint that sends emails with user-controlled content
  2. The attacker crafts a request that injects malicious content into the raw email field
  3. Despite disableFileAccess: true being configured, the email includes:
    ```
    Content-Type: text/plain
    Content-Disposition: attachment; filename="config.txt"

[Contents of /app/config/secrets.json]
```
4. The email is sent, and the attacker receives sensitive server files as attachments

For SSRF, the attacker could probe internal services:

http://internal-api.local:8080/admin/users
http://localhost:6379/  (Redis)
http://169.254.169.254/latest/meta-data/iam/security-credentials/

Real-World Impact

For this specific application, the impact includes:
- Credential theft: Reading .env files, AWS credentials, database connection strings
- Internal network mapping: Discovering and probing internal services
- Data exfiltration: Accessing application data through internal APIs
- Cloud metadata exposure: In cloud environments, accessing instance metadata for privilege escalation

The Fix

The fix was straightforward but critical: upgrade Nodemailer from version 8.0.7 to 9.0.1, where the security controls are properly enforced.

Before (Vulnerable)

// package.json
{
  "dependencies": {
    "nodemailer": "^8.0.7"
  }
}
// package-lock.json
"node_modules/nodemailer": {
  "version": "8.0.7",
  "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.7.tgz",
  "integrity": "sha512-pkjE4mkBzQjdJT4/UmlKl3pX0rC9fZmjh7c6C9o7lv66Ac6w9WCnzPzhbPNxwZAzlF4mdq4CSWB5+FbK6FWCow=="
}

After (Fixed)

// package.json
{
  "dependencies": {
    "nodemailer": "^9.0.1"
  }
}
// package-lock.json
"node_modules/nodemailer": {
  "version": "9.0.1",
  "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.1.tgz",
  "integrity": "sha512-Gwv8SQewT616ZM/URn0H54b8PWo/Wum7md3EW2aWy1lO27+WZCX+Xyak3J+NlmHUjDh3ME+uesJUDRbR3Ye8Bw=="
}

Why This Works

In Nodemailer 9.0.1, the security fix ensures that:
1. The raw option processing now checks transport-level security settings
2. disableFileAccess: true prevents all file access, regardless of how content is composed
3. disableUrlAccess: true prevents all URL fetching, even through raw MIME content

The fix was scoped to just two files (package.json and package-lock.json) because this is a dependency vulnerability—the vulnerable code exists within Nodemailer itself, not in the application code.

Key Takeaways

  • Never assume security controls are comprehensive: The disableFileAccess and disableUrlAccess settings appeared to protect against file/URL access, but the raw option created an unexpected bypass
  • Dependency vulnerabilities require immediate attention: This high-severity issue was fixed by a simple version bump, but delayed patching leaves applications exposed
  • Web services with email functionality are high-value targets: Email features often handle user input and can be weaponized for SSRF and data exfiltration
  • The raw option in Nodemailer should be treated as dangerous: If you must use it, ensure you're on version 9.0.1+ and implement additional validation
  • Automated scanning caught what manual review might miss: Trivy identified this vulnerability in package-lock.json through pattern matching against the GHSA database

How Orbis AppSec Detected This

  • Source: User-influenced input flowing into Nodemailer email composition in the web service
  • Sink: Nodemailer's sendMail() function with potential raw option usage, where disableFileAccess/disableUrlAccess could be bypassed
  • Missing control: Nodemailer version 8.0.7 lacked enforcement of security settings when using the raw message option
  • CWE: CWE-918 (Server-Side Request Forgery) and CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)
  • Fix: Upgraded Nodemailer from 8.0.7 to 9.0.1, which properly enforces disableFileAccess and disableUrlAccess across all message composition methods

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

The Nodemailer raw option bypass (GHSA-p6gq-j5cr-w38f) serves as a stark reminder that security controls can have unexpected gaps. Even when developers properly configure disableFileAccess: true and disableUrlAccess: true, the raw option in versions prior to 9.0.1 completely circumvented these protections.

For web services handling user input, this vulnerability was particularly dangerous—enabling remote attackers to read sensitive files and perform SSRF attacks through email functionality. The fix was simple (a dependency upgrade), but the consequences of leaving it unpatched could have been severe.

Keep your dependencies updated, implement defense in depth, and use automated scanning to catch vulnerabilities like this before they reach production.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #12

Related Articles

high

How Denial of Service via Infinite Loop Happens in JavaScript Dependencies and How to Fix It

CVE-2026-67213 is a high-severity denial of service vulnerability in nanoid before version 5.1.6 that triggers an infinite loop during random ID generation when processing specially crafted input. We upgraded nanoid across the entire dependency tree to patch this flaw and prevent attackers from freezing application threads. This fix ensures that ID generation remains resilient even when handling adversarial input patterns.

high

How Sensitive Data Exposure happens in Zotero plugins and how to fix it

A high-severity data exposure vulnerability in `Zotero.ts` automatically transmitted complete document metadata—including private notes, attachment paths, and tags—to external LLM services without user consent. The fix replaces broad `item.toJSON()` serialization with explicit field selection, sending only essential bibliographic data.

high

How missing dependency update cooldowns happen in GitHub Dependabot configurations and how to fix it

A semgrep scan flagged `.github/dependabot.yml` for lacking a cooldown period, meaning Dependabot would immediately propose updates to brand-new package versions across npm, Bundler, and Docker ecosystems. The fix adds a `cooldown: default-days: 7` block to every `package-ecosystem` entry, forcing a one-week waiting period before newly published releases are considered — reducing exposure to malicious or unstable package drops.

high

How dependabot-missing-cooldown happens in GitHub Actions/Node.js and how to fix it

The repository's `.github/dependabot.yml` had no cooldown period configured, meaning Dependabot could immediately propose updates to newly published package versions with zero time for the community to flag malware or instability. The fix adds a `cooldown` block with `default-days: 7` to both the `npm` and `github-actions` ecosystems, forcing a 7-day waiting period before new releases are surfaced as update PRs.

high

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

critical

How Unbounded WebSocket Message Handling Causes Resource Exhaustion in Node.js and How to Fix It

The WebSocketCrossServerAdapter class in a popular Node.js WebSocket library lacked any rate limiting on inbound messages, allowing attackers to flood Redis nodes and WebSocket servers with high-volume traffic. The fix introduces a configurable `rateLimit` option that caps messages per connection per second, preventing resource exhaustion while preserving legitimate functionality.