diff --git a/packages/safe-fs/src/__tests__/fs.test.ts b/packages/safe-fs/src/__tests__/fs.test.ts index 03e3cf0..7bbcd3b 100644 --- a/packages/safe-fs/src/__tests__/fs.test.ts +++ b/packages/safe-fs/src/__tests__/fs.test.ts @@ -160,6 +160,7 @@ describe('getter', () => { it('should allow operations within the base path', () => { const validPath = 'valid/nested/path.txt' + const newPath = 'valid/new.txt' const content = 'Valid content' expect(() => @@ -167,9 +168,10 @@ describe('getter', () => { ).not.toThrow() expect(() => sfs.writeFileSync(validPath, content)).not.toThrow() expect(() => sfs.readFileSync(validPath)).not.toThrow() - expect(() => sfs.unlinkSync(validPath)).not.toThrow() - expect(() => sfs.renameSync(validPath, 'valid/new.txt')).not.toThrow() - expect(() => sfs.statSync(validPath)).not.toThrow() + expect(() => sfs.renameSync(validPath, newPath)).not.toThrow() + expect(() => sfs.statSync(newPath)).not.toThrow() + expect(() => sfs.unlinkSync(newPath)).not.toThrow() + expect(() => sfs.rmdirSync('valid/nested')).not.toThrow() }) }) }) diff --git a/packages/safe-fs/src/getter.ts b/packages/safe-fs/src/getter.ts index bf72465..5fe41a4 100644 --- a/packages/safe-fs/src/getter.ts +++ b/packages/safe-fs/src/getter.ts @@ -1,21 +1,34 @@ import fs from 'fs' +import { z } from 'zod' +import pathParams from './params.json' import { sanitizePath } from './sanitizers' +const pathParamsRecordSchema = z.record(z.array(z.number())) +export type PathParamsRecord = z.infer + +const pathParamsRecord = pathParamsRecordSchema.parse(pathParams) + export const createGetter = (basePath: string) => (target: typeof fs, p: keyof typeof fs, receiver) => { if (typeof target[p] === 'function') { - return (...args) => { - if ( - typeof args[0] === 'string' || - Buffer.isBuffer(args[0]) || - args[0] instanceof URL - ) { - args[0] = sanitizePath(args[0], basePath) + const func = Reflect.get(target, p, receiver) + const paramsToSanitize = pathParamsRecord[p] + + if (paramsToSanitize) { + return (...args) => { + const sanitizedArgs = args.map((arg, i) => { + // the argument could be a file descriptor + if (paramsToSanitize.includes(i) && typeof arg !== 'number') { + return sanitizePath(arg as fs.PathLike, basePath) + } + return arg as Parameters[i] + }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument + return func(...sanitizedArgs) } - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument - return (Reflect.get(target, p, receiver) as CallableFunction)(...args) + return func } + return Reflect.get(target, p, receiver) } - return Reflect.get(target, p, receiver) } diff --git a/packages/safe-fs/src/params.json b/packages/safe-fs/src/params.json new file mode 100644 index 0000000..fa25bb2 --- /dev/null +++ b/packages/safe-fs/src/params.json @@ -0,0 +1,62 @@ +{ + "access": [0], + "appendFile": [0], + "chmod": [0], + "chown": [0], + "copyFile": [0, 1], + "cp": [0, 1], + "glob": [0], + "lchmod": [0], + "lchown": [0], + "lutimes": [0], + "link": [0, 1], + "lstat": [0], + "mkdir": [0], + "mkdtemp": [0], + "open": [0], + "opendir": [0], + "readdir": [0], + "readFile": [0], + "readlink": [0], + "realpath": [0], + "rename": [0, 1], + "rmdir": [0], + "rm": [0], + "stat": [0], + "statfs": [0], + "symlink": [0, 1], + "truncate": [0], + "unlink": [0], + "utimes": [0], + "writeFile": [0], + "accessSync": [0], + "appendFileSync": [0], + "chmodSync": [0], + "chownSync": [0], + "copyFileSync": [0, 1], + "cpSync": [0, 1], + "globSync": [0], + "lchmodSync": [0], + "lchownSync": [0], + "lutimesSync": [0], + "linkSync": [0, 1], + "lstatSync": [0], + "mkdirSync": [0], + "mkdtempSync": [0], + "openSync": [0], + "opendirSync": [0], + "readdirSync": [0], + "readFileSync": [0], + "readlinkSync": [0], + "realpathSync": [0], + "renameSync": [0, 1], + "rmdirSync": [0], + "rmSync": [0], + "statSync": [0], + "statfsSync": [0], + "symlinkSync": [0, 1], + "truncateSync": [0], + "unlinkSync": [0], + "utimesSync": [0], + "writeFileSync": [0] +}