'use strict'; const common = require('../../common'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); const { pathToFileURL } = require('url'); const blockedFile = process.env.BLOCKEDFILE; const bufferBlockedFile = Buffer.from(process.env.BLOCKEDFILE); const blockedFileURL = pathToFileURL(process.env.BLOCKEDFILE); const blockedFolder = process.env.BLOCKEDFOLDER; const allowedFolder = process.env.ALLOWEDFOLDER; const regularFile = __filename; // Guarantee the error message suggest the --allow-fs-read { fs.readFile(blockedFile, common.expectsError({ message: 'Access to this API has been restricted. Use --allow-fs-read to manage permissions.', code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); } // fs.readFile { fs.readFile(blockedFile, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); fs.readFile(bufferBlockedFile, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.readFileSync(blockedFile); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.readFileSync(blockedFileURL); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); } // fs.createReadStream { assert.rejects(() => { return new Promise((_resolve, reject) => { const stream = fs.createReadStream(blockedFile); stream.on('error', reject); }); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })).then(common.mustCall()); assert.rejects(() => { return new Promise((_resolve, reject) => { const stream = fs.createReadStream(blockedFileURL); stream.on('error', reject); }); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })).then(common.mustCall()); assert.rejects(() => { return new Promise((_resolve, reject) => { const stream = fs.createReadStream(blockedFile); stream.on('error', reject); }); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })).then(common.mustCall()); } // fs.stat { fs.stat(blockedFile, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); fs.stat(bufferBlockedFile, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.statSync(blockedFile); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.statSync(blockedFileURL); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); fs.stat(path.join(blockedFolder, 'anyfile'), common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(path.join(blockedFolder, 'anyfile')), })); // doesNotThrow fs.stat(regularFile, (err) => { assert.ifError(err); }); } // fs.access { fs.access(blockedFile, fs.constants.R_OK, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); fs.access(bufferBlockedFile, fs.constants.R_OK, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.accessSync(blockedFileURL, fs.constants.R_OK); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.accessSync(path.join(blockedFolder, 'anyfile'), fs.constants.R_OK); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(path.join(blockedFolder, 'anyfile')), })); // doesNotThrow fs.access(regularFile, fs.constants.R_OK, (err) => { assert.ifError(err); }); } // fs.copyFile { fs.copyFile(blockedFile, path.join(blockedFolder, 'any-other-file'), common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); fs.copyFile(bufferBlockedFile, path.join(blockedFolder, 'any-other-file'), common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.copyFileSync(blockedFileURL, path.join(blockedFolder, 'any-other-file')); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.copyFileSync(blockedFile, path.join(__dirname, 'any-other-file')); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); } // fs.cp { assert.throws(() => { fs.cpSync(blockedFile, path.join(blockedFolder, 'any-other-file')); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.cpSync(bufferBlockedFile, path.join(blockedFolder, 'any-other-file')); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.cpSync(blockedFileURL, path.join(blockedFolder, 'any-other-file')); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.cpSync(blockedFile, path.join(__dirname, 'any-other-file')); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); } // fs.open { fs.open(blockedFile, 'r', common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); fs.open(bufferBlockedFile, 'r', common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.openSync(blockedFileURL, 'r'); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.openSync(path.join(blockedFolder, 'anyfile'), 'r'); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(path.join(blockedFolder, 'anyfile')), })); // doesNotThrow fs.open(regularFile, 'r', (err) => { assert.ifError(err); }); // Extra flags should not enable trivially bypassing all restrictions. // See https://github.com/nodejs/node/issues/47090. assert.throws(() => { fs.openSync(blockedFile, fs.constants.O_RDONLY | fs.constants.O_NOCTTY); }, { code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', }); fs.open(blockedFile, fs.constants.O_RDWR | 0x10000000, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', })); } // fs.opendir { fs.opendir(blockedFolder, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFolder), })); assert.throws(() => { fs.opendirSync(blockedFolder); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFolder), })); // doesNotThrow fs.opendir(allowedFolder, (err, dir) => { assert.ifError(err); dir.closeSync(); }); } // fs.readdir { fs.readdir(blockedFolder, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFolder), })); assert.throws(() => { fs.readdirSync(blockedFolder, { recursive: true }); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFolder), })); fs.readdir(blockedFolder, { recursive: true }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFolder), })); assert.throws(() => { fs.readdirSync(blockedFolder); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFolder), })); // doesNotThrow fs.readdir(allowedFolder, (err) => { assert.ifError(err); }); } // fs.watch { assert.throws(() => { fs.watch(blockedFile, () => {}); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.watch(blockedFileURL, () => {}); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); // doesNotThrow fs.readdir(allowedFolder, (err) => { assert.ifError(err); }); } // fs.watchFile { assert.throws(() => { fs.watchFile(blockedFile, common.mustNotCall()); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.watchFile(blockedFileURL, common.mustNotCall()); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); } // fs.rename { fs.rename(blockedFile, 'newfile', common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); fs.rename(bufferBlockedFile, 'newfile', common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.renameSync(blockedFile, 'newfile'); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.renameSync(blockedFileURL, 'newfile'); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); } // fs.openAsBlob { assert.throws(() => { fs.openAsBlob(blockedFile); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.openAsBlob(bufferBlockedFile); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.openAsBlob(blockedFileURL); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); } // fs.exists { // It will return false (without performing IO) when permissions is not met fs.exists(blockedFile, (exists) => { assert.equal(exists, false); }); fs.exists(blockedFileURL, (exists) => { assert.equal(exists, false); }); assert.throws(() => { fs.existsSync(blockedFile); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); } // fs.statfs { fs.statfs(blockedFile, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); fs.statfs(bufferBlockedFile, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.statfsSync(blockedFile); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); assert.throws(() => { fs.statfsSync(blockedFileURL); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: path.toNamespacedPath(blockedFile), })); } // process.chdir { assert.throws(() => { process.chdir(blockedFolder); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: blockedFolder, })); } // fs.lstat { assert.throws(() => { fs.lstatSync(blockedFile); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', })); assert.throws(() => { fs.lstatSync(bufferBlockedFile); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', })); assert.throws(() => { fs.lstatSync(path.join(blockedFolder, 'anyfile')); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', })); assert.throws(() => { fs.lstatSync(bufferBlockedFile); }, common.expectsError({ code: 'ERR_ACCESS_DENIED', })); // doesNotThrow fs.lstat(regularFile, (err) => { assert.ifError(err); }); }