Files
t3mp3st/bench/cve-zero/results/hunt-GHSA-ph9p-34f9-6g65.json
T

1 line
2.4 KiB
JSON

{"id": "GHSA-ph9p-34f9-6g65", "cve": "CVE-2026-44705", "repo": "raszi/node-tmp", "cwes": ["CWE-22"], "vuln_files": ["lib/tmp.js"], "duration_sec": 100.3, "error": null, "strict": true, "lenient": true, "findings_scored": [{"fileMatch": true, "lineProx": true, "cweMatch": true, "hit": true, "title": "Path traversal / arbitrary file & directory write outside tmpdir via prefix-only startsWith check", "file": "lib/tmp.js", "line": 581, "cwe": "CWE-22"}], "n_findings": 1, "findings": [{"title": "Path traversal / arbitrary file & directory write outside tmpdir via prefix-only startsWith check", "file": "lib/tmp.js", "line": 581, "cwe": "CWE-22", "vuln_class": "path-traversal", "severity": "high", "description": "The containment check in _getRelativePath (line 581) and its sync twin _getRelativePathSync (line 600) validates that a user-supplied 'dir', 'template' or 'name' option stays inside tmpDir using `resolvedPath.startsWith(tmpDir)`. This is a prefix match without a trailing path separator. An attacker-controlled `dir` (e.g. '../<tmpDirBasename>EVIL/sub') resolves (via path.join(tmpDir, name) + fs.realpath in _resolvePath/_resolvePathSync) to a SIBLING directory whose absolute path begins with the tmpDir string but is NOT actually inside tmpDir. For tmpDir '/tmp/realtmp', resolvedPath '/tmp/realtmpEVIL/pwned' satisfies startsWith(tmpDir) === true, so the check passes. The (escaped) relative path is then stored as options.dir and used by _generateTmpName -> path.join(tmpDir, opts.dir, name), causing fs.open (file/fileSync), fs.mkdir (dir/dirSync), and especially the recursive rimraf/FN_RIMRAF_SYNC removal used with unsafeCleanup to operate on a location OUTSIDE the temp directory the consumer intended to confine operations to. Verified empirically: resolvedPath '/tmp/realtmpEVIL/pwned'.startsWith('/tmp/realtmp') === true.", "poc": "const tmp = require('tmp'); // assume os.tmpdir() realpaths to '/private/tmp' or any dir; create sibling sharing prefix\n// tmpDir basename is e.g. 'mytmp' -> attacker provides a dir option escaping to a prefix-sharing sibling\ntmp.dirSync({ tmpdir: '/tmp/mytmpEVIL/inner', unsafeCleanup: true });\n// The startsWith('/tmp/tmp/inner',\n// so node-tmp creates (and on cleanup recursively rm -rf's) a directory OUTSIDE the intended tmpDir.\n// Equivalent file write: tmp.fileSync({ tmpdir:'/t/mytmp', name:undefined, dir:'../mytmpEVIL/x', template:'fileXXXXXX' });", "confidence": 0.85}], "transcript": ""}