Skip to content

Commit 025cbd6

Browse files
committed
lib,permission: support fs.lstat
PR-URL: nodejs-private/node-private#486 Backport-PR-URL: nodejs-private/node-private#604 CVE-ID: CVE-2024-22018
1 parent d38ea17 commit 025cbd6

File tree

3 files changed

+36
-6
lines changed

3 files changed

+36
-6
lines changed

lib/fs.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,6 +1554,10 @@ function lstat(path, options = { bigint: false }, callback) {
15541554
}
15551555
callback = makeStatsCallback(callback);
15561556
path = getValidatedPath(path);
1557+
if (permission.isEnabled() && !permission.has('fs.read', path)) {
1558+
callback(new ERR_ACCESS_DENIED('Access to this API has been restricted', 'FileSystemRead', path));
1559+
return;
1560+
}
15571561

15581562
const req = new FSReqCallback(options.bigint);
15591563
req.oncomplete = callback;
@@ -1630,6 +1634,9 @@ function fstatSync(fd, options = { bigint: false }) {
16301634
*/
16311635
function lstatSync(path, options = { bigint: false, throwIfNoEntry: true }) {
16321636
path = getValidatedPath(path);
1637+
if (permission.isEnabled() && !permission.has('fs.read', path)) {
1638+
throw new ERR_ACCESS_DENIED('Access to this API has been restricted', 'FileSystemRead', path);
1639+
}
16331640
const stats = binding.lstat(
16341641
pathModule.toNamespacedPath(path),
16351642
options.bigint,

lib/internal/errors.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,11 @@ module.exports = {
11131113
// Note: Node.js specific errors must begin with the prefix ERR_
11141114

11151115
E('ERR_ACCESS_DENIED',
1116-
'Access to this API has been restricted. Permission: %s',
1116+
function(msg, permission = '', resource = '') {
1117+
this.permission = permission;
1118+
this.resource = resource;
1119+
return msg;
1120+
},
11171121
Error);
11181122
E('ERR_AMBIGUOUS_ARGUMENT', 'The "%s" argument is ambiguous. %s', TypeError);
11191123
E('ERR_ARG_NOT_ITERABLE', '%s must be iterable', TypeError);

test/fixtures/permission/fs-read.js

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,23 +161,23 @@ const regularFile = __filename;
161161
}, common.expectsError({
162162
code: 'ERR_ACCESS_DENIED',
163163
permission: 'FileSystemRead',
164-
// cpSync calls statSync before reading blockedFile
165-
resource: path.toNamespacedPath(blockedFolder),
164+
// cpSync calls lstatSync before reading blockedFile
165+
resource: path.toNamespacedPath(blockedFile),
166166
}));
167167
assert.throws(() => {
168168
fs.cpSync(blockedFileURL, path.join(blockedFolder, 'any-other-file'));
169169
}, common.expectsError({
170170
code: 'ERR_ACCESS_DENIED',
171171
permission: 'FileSystemRead',
172-
// cpSync calls statSync before reading blockedFile
173-
resource: path.toNamespacedPath(blockedFolder),
172+
// cpSync calls lstatSync before reading blockedFile
173+
resource: path.toNamespacedPath(blockedFile),
174174
}));
175175
assert.throws(() => {
176176
fs.cpSync(blockedFile, path.join(__dirname, 'any-other-file'));
177177
}, common.expectsError({
178178
code: 'ERR_ACCESS_DENIED',
179179
permission: 'FileSystemRead',
180-
resource: path.toNamespacedPath(__dirname),
180+
resource: path.toNamespacedPath(blockedFile),
181181
}));
182182
}
183183

@@ -385,4 +385,23 @@ const regularFile = __filename;
385385
permission: 'FileSystemRead',
386386
resource: path.toNamespacedPath(blockedFile),
387387
}));
388+
}
389+
390+
// fs.lstat
391+
{
392+
assert.throws(() => {
393+
fs.lstatSync(blockedFile);
394+
}, common.expectsError({
395+
code: 'ERR_ACCESS_DENIED',
396+
}));
397+
assert.throws(() => {
398+
fs.lstatSync(path.join(blockedFolder, 'anyfile'));
399+
}, common.expectsError({
400+
code: 'ERR_ACCESS_DENIED',
401+
}));
402+
403+
// doesNotThrow
404+
fs.lstat(regularFile, (err) => {
405+
assert.ifError(err);
406+
});
388407
}

0 commit comments

Comments
 (0)