diff --git a/lib/binding.js b/lib/binding.js index 34535d54..0e34b9e4 100644 --- a/lib/binding.js +++ b/lib/binding.js @@ -962,6 +962,9 @@ Binding.prototype.readdir = function( if (!(dir instanceof Directory)) { throw new FSError('ENOTDIR', dirpath); } + if (!dir.canRead()) { + throw new FSError('EACCES', dirpath); + } let list = dir.list(); if (encoding === 'buffer') { diff --git a/test/lib/fs.readdir.spec.js b/test/lib/fs.readdir.spec.js index 6ec68f8b..5b531c30 100644 --- a/test/lib/fs.readdir.spec.js +++ b/test/lib/fs.readdir.spec.js @@ -21,7 +21,15 @@ describe('fs.readdir(path, callback)', function() { empty: {} } } - } + }, + denied: mock.directory({ + mode: 0o000, + items: [ + { + 'one.txt': 'content' + } + ] + }) }); }); afterEach(mock.restore); @@ -92,6 +100,31 @@ describe('fs.readdir(path, callback)', function() { ); }); + it('calls with an error for restricted path', function(done) { + fs.readdir('denied', function(err, items) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + assert.isUndefined(items); + done(); + }); + }); + + withPromise.it('promise calls with an error for restricted path', function( + done + ) { + fs.promises.readdir('denied').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + inVersion('>=10.10').it('should support "withFileTypes" option', function( done ) { @@ -216,4 +249,10 @@ describe('fs.readdirSync(path)', function() { fs.readdirSync('bogus'); }); }); + + it('throws when access refused', function() { + assert.throws(function() { + fs.readdirSync('denied'); + }); + }); });