From ad4f2db0a04c68f54ee6dc6b1b1ef52b7a861a63 Mon Sep 17 00:00:00 2001 From: isaacs Date: Sun, 23 May 2021 21:56:56 -0700 Subject: [PATCH] Do not rimraf /, override with preserveRoot:false --- README.md | 2 ++ lib/index.js | 20 ++++++++++---------- lib/path-arg.js | 24 ++++++++++++++++-------- test/path-arg.js | 5 +++++ 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index b9403998..6a312d5b 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ Options: * `tmp`: Temp folder to use to place files and folders for the Windows implementation. Must be on the same physical device as the path being deleted. Defaults to `dirname(f)`. +* `preserveRoot`: If set to boolean `false`, then allow the recursive + removal of the root directory. Otherwise, this is not allowed. Any other options are provided to the native Node.js `fs.rm` implementation when that is used. diff --git a/lib/index.js b/lib/index.js index 41111553..6f40b65e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -8,14 +8,14 @@ const {rimrafPosix, rimrafPosixSync} = require('./rimraf-posix.js') const {useNative, useNativeSync} = require('./use-native.js') const rimraf = (path, opts) => { - path = pathArg(path) + path = pathArg(path, opts) opts = optsArg(opts) return useNative(opts) ? rimrafNative(path, opts) : rimrafManual(path, opts) } const rimrafSync = async (path, opts) => { - path = pathArg(path) + path = pathArg(path, opts) opts = optsArg(opts) return useNativeSync(opts) ? rimrafNativeSync(path, opts) : rimrafManualSync(path, opts) @@ -25,33 +25,33 @@ rimraf.rimraf = rimraf rimraf.sync = rimraf.rimrafSync = rimrafSync const native = async (path, opts) => - rimrafNative(pathArg(path), optsArg(opts)) + rimrafNative(pathArg(path, opts), optsArg(opts)) const nativeSync = (path, opts) => - rimrafNativeSync(pathArg(path), optsArg(opts)) + rimrafNativeSync(pathArg(path, opts), optsArg(opts)) native.sync = nativeSync rimraf.native = native rimraf.nativeSync = nativeSync const manual = async (path, opts) => - rimrafManual(pathArg(path), optsArg(opts)) + rimrafManual(pathArg(path, opts), optsArg(opts)) const manualSync = (path, opts) => - rimrafManualSync(pathArg(path), optsArg(opts)) + rimrafManualSync(pathArg(path, opts), optsArg(opts)) manual.sync = manualSync rimraf.manual = manual rimraf.manualSync = manualSync const windows = async (path, opts) => - rimrafWindows(pathArg(path), optsArg(opts)) + rimrafWindows(pathArg(path, opts), optsArg(opts)) const windowsSync = (path, opts) => - rimrafWindowsSync(pathArg(path), optsArg(opts)) + rimrafWindowsSync(pathArg(path, opts), optsArg(opts)) windows.sync = windowsSync rimraf.windows = windows rimraf.windowsSync = windowsSync const posix = async (path, opts) => - rimrafPosix(pathArg(path), optsArg(opts)) + rimrafPosix(pathArg(path, opts), optsArg(opts)) const posixSync = (path, opts) => - rimrafPosixSync(pathArg(path), optsArg(opts)) + rimrafPosixSync(pathArg(path, opts), optsArg(opts)) posix.sync = posixSync rimraf.posix = posix rimraf.posixSync = posixSync diff --git a/lib/path-arg.js b/lib/path-arg.js index d57677de..905b36f5 100644 --- a/lib/path-arg.js +++ b/lib/path-arg.js @@ -1,18 +1,26 @@ const platform = require('./platform.js') const { resolve, parse } = require('path') -const pathArg = path => { +const pathArg = (path, opts = {}) => { if (/\0/.test(path)) { // simulate same failure that node raises - throw Object.assign( - new TypeError('path must be a string without null bytes'), - { - path, - code: 'ERR_INVALID_ARG_VALUE', - } - ) + const msg = 'path must be a string without null bytes' + throw Object.assign(new TypeError(msg), { + path, + code: 'ERR_INVALID_ARG_VALUE', + }) } path = resolve(path) + const { root } = parse(path) + + if (path === root && opts.preserveRoot !== false) { + const msg = 'refusing to remove root directory without preserveRoot:false' + throw Object.assign(new Error(msg), { + path, + code: 'ERR_PRESERVE_ROOT', + }) + } + if (platform === 'win32') { const badWinChars = /[*|"<>?:]/ const {root} = parse(path) diff --git a/test/path-arg.js b/test/path-arg.js index 1be5e33a..5cef8cd3 100644 --- a/test/path-arg.js +++ b/test/path-arg.js @@ -37,3 +37,8 @@ if (platform === 'win32') { t.throws(() => pathArg(path), er) } } + +t.throws(() => pathArg('/'), { code: 'ERR_PRESERVE_ROOT' }) +t.throws(() => pathArg('/', { preserveRoot: null }), + { code: 'ERR_PRESERVE_ROOT' }) +t.equal(pathArg('/', { preserveRoot: false }), resolve('/'))