--- lib/tmp.js @@ -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 . or .. 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 @@ -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(); + }); + }); +});