Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ebbf942

Browse filesBrowse files
RafaelGSSmarco-ippolito
authored andcommitted
lib: disable futimes when permission model is enabled
Refs: https://hackerone.com/reports/3390084 PR-URL: nodejs-private/node-private#748 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> CVE-ID: CVE-2025-55132
1 parent c611ea6 commit ebbf942
Copy full SHA for ebbf942

File tree

Expand file treeCollapse file tree

3 files changed

+85
-1
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+85
-1
lines changed
Open diff view settings
Collapse file

‎lib/fs.js‎

Copy file name to clipboardExpand all lines: lib/fs.js
+24Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,11 @@ function rmSync(path, options) {
12611261
function fdatasync(fd, callback) {
12621262
const req = new FSReqCallback();
12631263
req.oncomplete = makeCallback(callback);
1264+
1265+
if (permission.isEnabled()) {
1266+
callback(new ERR_ACCESS_DENIED('fdatasync API is disabled when Permission Model is enabled.'));
1267+
return;
1268+
}
12641269
binding.fdatasync(fd, req);
12651270
}
12661271

@@ -1272,6 +1277,9 @@ function fdatasync(fd, callback) {
12721277
* @returns {void}
12731278
*/
12741279
function fdatasyncSync(fd) {
1280+
if (permission.isEnabled()) {
1281+
throw new ERR_ACCESS_DENIED('fdatasync API is disabled when Permission Model is enabled.');
1282+
}
12751283
binding.fdatasync(fd);
12761284
}
12771285

@@ -1285,6 +1293,10 @@ function fdatasyncSync(fd) {
12851293
function fsync(fd, callback) {
12861294
const req = new FSReqCallback();
12871295
req.oncomplete = makeCallback(callback);
1296+
if (permission.isEnabled()) {
1297+
callback(new ERR_ACCESS_DENIED('fsync API is disabled when Permission Model is enabled.'));
1298+
return;
1299+
}
12881300
binding.fsync(fd, req);
12891301
}
12901302

@@ -1295,6 +1307,9 @@ function fsync(fd, callback) {
12951307
* @returns {void}
12961308
*/
12971309
function fsyncSync(fd) {
1310+
if (permission.isEnabled()) {
1311+
throw new ERR_ACCESS_DENIED('fsync API is disabled when Permission Model is enabled.');
1312+
}
12981313
binding.fsync(fd);
12991314
}
13001315

@@ -2221,6 +2236,11 @@ function futimes(fd, atime, mtime, callback) {
22212236
mtime = toUnixTimestamp(mtime, 'mtime');
22222237
callback = makeCallback(callback);
22232238

2239+
if (permission.isEnabled()) {
2240+
callback(new ERR_ACCESS_DENIED('futimes API is disabled when Permission Model is enabled.'));
2241+
return;
2242+
}
2243+
22242244
const req = new FSReqCallback();
22252245
req.oncomplete = callback;
22262246
binding.futimes(fd, atime, mtime, req);
@@ -2236,6 +2256,10 @@ function futimes(fd, atime, mtime, callback) {
22362256
* @returns {void}
22372257
*/
22382258
function futimesSync(fd, atime, mtime) {
2259+
if (permission.isEnabled()) {
2260+
throw new ERR_ACCESS_DENIED('futimes API is disabled when Permission Model is enabled.');
2261+
}
2262+
22392263
binding.futimes(
22402264
fd,
22412265
toUnixTimestamp(atime, 'atime'),
Collapse file

‎test/fixtures/permission/fs-write.js‎

Copy file name to clipboardExpand all lines: test/fixtures/permission/fs-write.js
+45Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,3 +573,48 @@ const relativeProtectedFolder = process.env.RELATIVEBLOCKEDFOLDER;
573573
code: 'ERR_ACCESS_DENIED',
574574
});
575575
}
576+
577+
// fs.utimes with read-only fd
578+
{
579+
assert.throws(() => {
580+
// blocked file is allowed to read
581+
const fd = fs.openSync(blockedFile, 'r');
582+
const date = new Date();
583+
date.setFullYear(2100,0,1);
584+
585+
fs.futimes(fd, date, date, common.expectsError({
586+
code: 'ERR_ACCESS_DENIED',
587+
}));
588+
fs.futimesSync(fd, date, date);
589+
}, {
590+
code: 'ERR_ACCESS_DENIED',
591+
});
592+
}
593+
594+
// fs.fdatasync with read-only fd
595+
{
596+
assert.throws(() => {
597+
// blocked file is allowed to read
598+
const fd = fs.openSync(blockedFile, 'r');
599+
fs.fdatasync(fd, common.expectsError({
600+
code: 'ERR_ACCESS_DENIED',
601+
}));
602+
fs.fdatasyncSync(fd);
603+
}, {
604+
code: 'ERR_ACCESS_DENIED',
605+
});
606+
}
607+
608+
// fs.fsync with read-only fd
609+
{
610+
assert.throws(() => {
611+
// blocked file is allowed to read
612+
const fd = fs.openSync(blockedFile, 'r');
613+
fs.fsync(fd, common.expectsError({
614+
code: 'ERR_ACCESS_DENIED',
615+
}));
616+
fs.fsyncSync(fd);
617+
}, {
618+
code: 'ERR_ACCESS_DENIED',
619+
});
620+
}
Collapse file

‎test/parallel/test-permission-fs-supported.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-permission-fs-supported.js
+16-1Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,22 @@ const ignoreList = [
7777
'unwatchFile',
7878
...syncAndAsyncAPI('lstat'),
7979
...syncAndAsyncAPI('realpath'),
80-
// fd required methods
80+
// File descriptor–based metadata operations
81+
//
82+
// The kernel does not allow opening a file descriptor for an inode
83+
// with write access if the inode itself is read-only. However, it still
84+
// permits modifying the inode’s metadata (e.g., permission bits, ownership,
85+
// timestamps) because you own the file. These changes can be made either
86+
// by referring to the file by name (e.g., chmod) or through any existing
87+
// file descriptor that identifies the same inode (e.g., fchmod).
88+
//
89+
// If the kernel required write access to change metadata, it would be
90+
// impossible to modify the permissions of a file once it was made read-only.
91+
// For that reason, syscalls such as fchmod, fchown, and futimes bypass
92+
// the file descriptor’s access mode. Even a read-only ('r') descriptor
93+
// can still update metadata. To prevent unintended modifications,
94+
// these APIs are therefore blocked by default when permission model is
95+
// enabled.
8196
...syncAndAsyncAPI('close'),
8297
...syncAndAsyncAPI('fchown'),
8398
...syncAndAsyncAPI('fchmod'),

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.