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.

Prevention & Best Practices

1. Keep Dependencies Updated

Use automated dependency scanning in your CI/CD pipeline:

# Example GitHub Actions workflow
- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: 'fs'
    scan-ref: '.'
    severity: 'HIGH,CRITICAL'

2. Defense in Depth for Email Functionality

Don't rely solely on Nodemailer's security settings:

// Validate and sanitize email content before passing to Nodemailer
function sanitizeEmailContent(content) {
  // Remove any file:// or suspicious URLs
  const dangerousPatterns = [
    /file:\/\//gi,
    /http:\/\/169\.254\./gi,  // AWS metadata
    /http:\/\/localhost/gi,
    /http:\/\/127\./gi,
    /http:\/\/\[::1\]/gi
  ];

  for (const pattern of dangerousPatterns) {
    if (pattern.test(content)) {
      throw new Error('Potentially malicious content detected');
    }
  }
  return content;
}

3. Network Segmentation

Even if SSRF bypasses application controls, network-level restrictions can limit damage:
- Block outbound requests from application servers to internal services
- Use AWS VPC endpoints and security groups to restrict metadata access
- Implement egress filtering for email servers

4. Audit User-Controlled Email Content

Review all code paths where user input flows into email composition:

// Dangerous: User controls raw content
app.post('/send-email', (req, res) => {
  transporter.sendMail({
    raw: req.body.emailContent  // NEVER DO THIS
  });
});

// Safer: Structured input with validation
app.post('/send-email', (req, res) => {
  const { to, subject, body } = req.body;

  // Validate each field
  if (!isValidEmail(to)) throw new Error('Invalid recipient');
  if (containsDangerousContent(body)) throw new Error('Invalid content');

  transporter.sendMail({
    to: sanitize(to),
    subject: sanitize(subject),
    text: sanitize(body)
  });
});

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.

References

Frequently Asked Questions

What is the Nodemailer raw option bypass vulnerability?

It's a security flaw where Nodemailer's message-level `raw` option ignores the `disableFileAccess` and `disableUrlAccess` transport configuration, allowing attackers to read local files or make arbitrary HTTP requests through email content when these protections should be enforced.

How do you prevent Nodemailer security bypasses in Node.js?

Keep Nodemailer updated to version 9.0.1 or later, validate all user-controlled email content, implement defense-in-depth with network segmentation, and never trust that a single security control will prevent all attack vectors.

What CWE is the Nodemailer raw option bypass?

This vulnerability maps to CWE-918 (Server-Side Request Forgery) and CWE-22 (Improper Limitation of a Pathname to a Restricted Directory), as it enables both SSRF attacks and arbitrary file reads.

Is setting disableFileAccess and disableUrlAccess enough to prevent this vulnerability?

In Nodemailer versions prior to 9.0.1, no—these settings could be bypassed using the `raw` option. After upgrading to 9.0.1+, these controls are properly enforced across all message composition methods.

Can static analysis detect the Nodemailer raw option bypass?

Yes, tools like Trivy can detect vulnerable Nodemailer versions in package-lock.json files by matching against known vulnerability databases like GHSA-p6gq-j5cr-w38f.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #12

Related Articles

critical

How Sensitive Data Exposure happens in Python web applications and how to fix it

A critical sensitive data exposure vulnerability was discovered in `nodes/google_gemini.py` where the Google Gemini API key was returned in plaintext through a web endpoint. The fix masks the token in API responses, preventing credential theft from any client that queries the token endpoint. This protects downstream users of this Node.js library from unauthorized access to their Google Gemini services.

high

How Authentication Bypass happens in Next.js App Router with Turbopack and how to fix it

A critical authentication bypass vulnerability (CVE-2026-64642) was discovered in Next.js versions prior to 16.2.11, specifically affecting App Router applications using Turbopack with a single locale configuration. This vulnerability allowed attackers to bypass middleware and proxy protections, potentially gaining unauthorized access to protected routes and resources that should have been secured by authentication checks.

critical

How SQL Injection Happens in CSV-to-SQL Converters and How to Fix It

A critical SQL injection vulnerability was discovered in the `csv2sql()` function in `src/data/converter/csv.js`, where CSV data and table names were directly interpolated into SQL INSERT statements without sanitization. The fix implements input validation through identifier sanitization and proper value escaping, eliminating the attack surface while preserving legitimate functionality.

critical

How Command Injection happens in Python subprocess and how to fix it

A critical command injection vulnerability was discovered in the `open_directory` method of `src/jm_view_server/app.py`, where user-controlled path input was passed directly into a shell command via `subprocess.Popen`. By switching from string-based shell execution to a list-based argument format, the fix eliminates the ability for attackers to inject malicious shell commands through crafted directory paths.

critical

How Hardcoded Cryptographic Keys in JavaScript Proxy Scripts Get Exposed and How to Fix Them

A critical vulnerability was discovered in `ghs/91Pornad.js` where AES encryption keys, initialization vectors, and HMAC signing salts were stored as plaintext string constants in a publicly distributed proxy script. Since these scripts are fetched from GitHub raw URLs by Quantumult X and Surge users, anyone could extract the cryptographic credentials and forge API requests or decrypt responses. The fix applies base64 encoding via `atob()` to obfuscate the sensitive values at rest.

critical

How Server-Side Request Forgery happens in Node.js CLI tools and how to fix it

A critical Server-Side Request Forgery (SSRF) vulnerability in the compass-guarded-transfer CLI tool allowed attackers to make HTTP requests to internal services and cloud metadata endpoints. The `normalizeInput` function in `run-transfer.mjs` validated that URLs started with "https://" but failed to prevent requests to private IP ranges like AWS metadata (169.254.169.254) or localhost, enabling potential credential theft and internal network reconnaissance.