mirror of
https://github.com/nodejs/node.git
synced 2026-10-14 10:26:26 -04:00
lib: disable futimes when permission model is enabled
Refs: https://hackerone.com/reports/3390084 PR-URL: https://github.com/nodejs-private/node-private/pull/748 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> CVE-ID: CVE-2025-55132
This commit is contained in:
1 parent
6ea77ad552
commit
fb4389332d
3 files changed
+85
-1
No files matched your search
@@ -1224,6 +1224,11 @@ function rmSync(path, options) {
|
||||
function fdatasync(fd, callback) {
|
||||
const req = new FSReqCallback();
|
||||
req.oncomplete = makeCallback(callback);
|
||||
|
||||
if (permission.isEnabled()) {
|
||||
callback(new ERR_ACCESS_DENIED('fdatasync API is disabled when Permission Model is enabled.'));
|
||||
return;
|
||||
}
|
||||
binding.fdatasync(fd, req);
|
||||
}
|
||||
|
||||
@@ -1235,6 +1240,9 @@ function fdatasync(fd, callback) {
|
||||
* @returns {void}
|
||||
*/
|
||||
function fdatasyncSync(fd) {
|
||||
if (permission.isEnabled()) {
|
||||
throw new ERR_ACCESS_DENIED('fdatasync API is disabled when Permission Model is enabled.');
|
||||
}
|
||||
binding.fdatasync(fd);
|
||||
}
|
||||
|
||||
@@ -1248,6 +1256,10 @@ function fdatasyncSync(fd) {
|
||||
function fsync(fd, callback) {
|
||||
const req = new FSReqCallback();
|
||||
req.oncomplete = makeCallback(callback);
|
||||
if (permission.isEnabled()) {
|
||||
callback(new ERR_ACCESS_DENIED('fsync API is disabled when Permission Model is enabled.'));
|
||||
return;
|
||||
}
|
||||
binding.fsync(fd, req);
|
||||
}
|
||||
|
||||
@@ -1258,6 +1270,9 @@ function fsync(fd, callback) {
|
||||
* @returns {void}
|
||||
*/
|
||||
function fsyncSync(fd) {
|
||||
if (permission.isEnabled()) {
|
||||
throw new ERR_ACCESS_DENIED('fsync API is disabled when Permission Model is enabled.');
|
||||
}
|
||||
binding.fsync(fd);
|
||||
}
|
||||
|
||||
@@ -2188,6 +2203,11 @@ function futimes(fd, atime, mtime, callback) {
|
||||
mtime = toUnixTimestamp(mtime, 'mtime');
|
||||
callback = makeCallback(callback);
|
||||
|
||||
if (permission.isEnabled()) {
|
||||
callback(new ERR_ACCESS_DENIED('futimes API is disabled when Permission Model is enabled.'));
|
||||
return;
|
||||
}
|
||||
|
||||
const req = new FSReqCallback();
|
||||
req.oncomplete = callback;
|
||||
binding.futimes(fd, atime, mtime, req);
|
||||
@@ -2203,6 +2223,10 @@ function futimes(fd, atime, mtime, callback) {
|
||||
* @returns {void}
|
||||
*/
|
||||
function futimesSync(fd, atime, mtime) {
|
||||
if (permission.isEnabled()) {
|
||||
throw new ERR_ACCESS_DENIED('futimes API is disabled when Permission Model is enabled.');
|
||||
}
|
||||
|
||||
binding.futimes(
|
||||
fd,
|
||||
toUnixTimestamp(atime, 'atime'),
|
||||
|
||||
Vendored
+45
@@ -584,3 +584,48 @@ const relativeProtectedFolder = process.env.RELATIVEBLOCKEDFOLDER;
|
||||
code: 'ERR_ACCESS_DENIED',
|
||||
});
|
||||
}
|
||||
|
||||
// fs.utimes with read-only fd
|
||||
{
|
||||
assert.throws(() => {
|
||||
// blocked file is allowed to read
|
||||
const fd = fs.openSync(blockedFile, 'r');
|
||||
const date = new Date();
|
||||
date.setFullYear(2100,0,1);
|
||||
|
||||
fs.futimes(fd, date, date, common.expectsError({
|
||||
code: 'ERR_ACCESS_DENIED',
|
||||
}));
|
||||
fs.futimesSync(fd, date, date);
|
||||
}, {
|
||||
code: 'ERR_ACCESS_DENIED',
|
||||
});
|
||||
}
|
||||
|
||||
// fs.fdatasync with read-only fd
|
||||
{
|
||||
assert.throws(() => {
|
||||
// blocked file is allowed to read
|
||||
const fd = fs.openSync(blockedFile, 'r');
|
||||
fs.fdatasync(fd, common.expectsError({
|
||||
code: 'ERR_ACCESS_DENIED',
|
||||
}));
|
||||
fs.fdatasyncSync(fd);
|
||||
}, {
|
||||
code: 'ERR_ACCESS_DENIED',
|
||||
});
|
||||
}
|
||||
|
||||
// fs.fsync with read-only fd
|
||||
{
|
||||
assert.throws(() => {
|
||||
// blocked file is allowed to read
|
||||
const fd = fs.openSync(blockedFile, 'r');
|
||||
fs.fsync(fd, common.expectsError({
|
||||
code: 'ERR_ACCESS_DENIED',
|
||||
}));
|
||||
fs.fsyncSync(fd);
|
||||
}, {
|
||||
code: 'ERR_ACCESS_DENIED',
|
||||
});
|
||||
}
|
||||
@@ -82,7 +82,22 @@ const ignoreList = [
|
||||
'unwatchFile',
|
||||
...syncAndAsyncAPI('lstat'),
|
||||
...syncAndAsyncAPI('realpath'),
|
||||
// fd required methods
|
||||
// File descriptor–based metadata operations
|
||||
//
|
||||
// The kernel does not allow opening a file descriptor for an inode
|
||||
// with write access if the inode itself is read-only. However, it still
|
||||
// permits modifying the inode’s metadata (e.g., permission bits, ownership,
|
||||
// timestamps) because you own the file. These changes can be made either
|
||||
// by referring to the file by name (e.g., chmod) or through any existing
|
||||
// file descriptor that identifies the same inode (e.g., fchmod).
|
||||
//
|
||||
// If the kernel required write access to change metadata, it would be
|
||||
// impossible to modify the permissions of a file once it was made read-only.
|
||||
// For that reason, syscalls such as fchmod, fchown, and futimes bypass
|
||||
// the file descriptor’s access mode. Even a read-only ('r') descriptor
|
||||
// can still update metadata. To prevent unintended modifications,
|
||||
// these APIs are therefore blocked by default when permission model is
|
||||
// enabled.
|
||||
...syncAndAsyncAPI('close'),
|
||||
...syncAndAsyncAPI('fchown'),
|
||||
...syncAndAsyncAPI('fchmod'),
|
||||
|
||||
Reference in new issue
Block a user