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 Server-Side Request Forgery (SSRF) happens in JavaScript fetch() and how to fix it

A critical Server-Side Request Forgery vulnerability in `popup.js` allowed attackers to inject malicious URLs from scraped webpages directly into fetch() calls, potentially accessing internal network resources and AWS metadata endpoints. The fix adds URL validation to ensure only HTTP/HTTPS protocols are used and blocks requests to private IP ranges and localhost addresses.

high

How NO_PROXY bypass via crafted URL happens in Node.js axios and how to fix it

A high-severity vulnerability (CVE-2026-42043) in axios versions prior to 1.15.1 allowed attackers to bypass NO_PROXY environment variable restrictions using specially crafted URLs. This meant HTTP requests intended to stay internal could be routed through an attacker-controlled proxy, potentially exposing sensitive data. The fix upgrades axios to version 1.15.1, which correctly validates URLs against NO_PROXY rules.

critical

How JSON request validation bypass happens in Node.js API handlers and how to fix it

A critical validation bypass in the `/api/agents/jobs` endpoint allowed attackers to send malformed JSON with arbitrary properties that could trigger prototype pollution or unexpected behavior. The fix added comprehensive validation checks including array detection and property existence verification to prevent malicious payloads from reaching downstream processing logic.

critical

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

A critical Server-Side Request Forgery (SSRF) vulnerability was discovered in `hubcmdui/routes/httpProxy.js` where the `/proxy/test` endpoint passed a user-controlled `testUrl` parameter directly to `axios.get()` without any validation. An attacker could exploit this to probe internal infrastructure — including AWS metadata endpoints, Redis instances, and private network ranges. The fix introduces a strict URL validation function that blocks private IP ranges, loopback addresses, and non-HTTP(S)

critical

How Remote Configuration Injection happens in JavaScript fetch() and how to fix it

A critical vulnerability in `js/config.js` allowed attackers to inject malicious configuration data through DNS spoofing or man-in-the-middle attacks. The application fetched remote JSON configuration from GitHub without validating the response structure, enabling arbitrary code execution through crafted payloads. A single validation check now ensures only properly-formed configuration objects are accepted.

high

How missing Dependabot cooldown configuration happens in GitHub Actions and how to fix it

A high-severity vulnerability was discovered in the `.github/dependabot.yml` configuration file where no cooldown period was set for dependency updates. This exposed the Node.js library and its downstream consumers to potentially malicious or unstable packages immediately after publication. The fix adds a 7-day cooldown period to all package ecosystem entries, providing a critical safety buffer before accepting newly published package versions.