| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 3904ef8 commit aa30e16
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -64,8 +64,11 @@ using v8::HandleScope; | |||
| 64 | 64 | using v8::Int32; | |
| 65 | 65 | using v8::Integer; | |
| 66 | 66 | using v8::Isolate; | |
| 67 | + using v8::JustVoid; | ||
| 67 | 68 | using v8::Local; | |
| 69 | + using v8::Maybe; | ||
| 68 | 70 | using v8::MaybeLocal; | |
| 71 | + using v8::Nothing; | ||
| 69 | 72 | using v8::Number; | |
| 70 | 73 | using v8::Object; | |
| 71 | 74 | using v8::ObjectTemplate; | |
@@ -1949,6 +1952,37 @@ static void ReadDir(const FunctionCallbackInfo<Value>& args) { | |||
| 1949 | 1952 | } | |
| 1950 | 1953 | } | |
| 1951 | 1954 | ||
| 1955 | + static inline Maybe<void> CheckOpenPermissions(Environment* env, | ||
| 1956 | + const BufferValue& path, | ||
| 1957 | + int flags) { | ||
| 1958 | + // These flags capture the intention of the open() call. | ||
| 1959 | + const int rwflags = flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); | ||
| 1960 | + | ||
| 1961 | + // These flags have write-like side effects even with O_RDONLY, at least on | ||
| 1962 | + // some operating systems. On Windows, for example, O_RDONLY | O_TEMPORARY | ||
| 1963 | + // can be used to delete a file. Bizarre. | ||
| 1964 | + const int write_as_side_effect = flags & (UV_FS_O_APPEND | UV_FS_O_CREAT | | ||
| 1965 | + UV_FS_O_TRUNC | UV_FS_O_TEMPORARY); | ||
| 1966 | + | ||
| 1967 | + // TODO(rafaelgss): it can be optimized to avoid two permission checks | ||
| 1968 | + auto pathView = path.ToStringView(); | ||
| 1969 | + if (rwflags != UV_FS_O_WRONLY) { | ||
| 1970 | + THROW_IF_INSUFFICIENT_PERMISSIONS( | ||
| 1971 | + env, | ||
| 1972 | + permission::PermissionScope::kFileSystemRead, | ||
| 1973 | + pathView, | ||
| 1974 | + Nothing<void>()); | ||
| 1975 | + } | ||
| 1976 | + if (rwflags != UV_FS_O_RDONLY || write_as_side_effect) { | ||
| 1977 | + THROW_IF_INSUFFICIENT_PERMISSIONS( | ||
| 1978 | + env, | ||
| 1979 | + permission::PermissionScope::kFileSystemWrite, | ||
| 1980 | + pathView, | ||
| 1981 | + Nothing<void>()); | ||
| 1982 | + } | ||
| 1983 | + return JustVoid(); | ||
| 1984 | + } | ||
| 1985 | + | ||
| 1952 | 1986 | static void Open(const FunctionCallbackInfo<Value>& args) { | |
| 1953 | 1987 | Environment* env = Environment::GetCurrent(args); | |
| 1954 | 1988 | ||
@@ -1964,22 +1998,7 @@ static void Open(const FunctionCallbackInfo<Value>& args) { | |||
| 1964 | 1998 | CHECK(args[2]->IsInt32()); | |
| 1965 | 1999 | const int mode = args[2].As<Int32>()->Value(); | |
| 1966 | 2000 | ||
| 1967 | - auto pathView = path.ToStringView(); | ||
| 1968 | - // Open can be called either in write or read | ||
| 1969 | - if (flags == O_RDWR) { | ||
| 1970 | - // TODO(rafaelgss): it can be optimized to avoid O(2*n) | ||
| 1971 | - THROW_IF_INSUFFICIENT_PERMISSIONS( | ||
| 1972 | - env, permission::PermissionScope::kFileSystemRead, pathView); | ||
| 1973 | - THROW_IF_INSUFFICIENT_PERMISSIONS( | ||
| 1974 | - env, permission::PermissionScope::kFileSystemWrite, pathView); | ||
| 1975 | - } else if ((flags & ~(UV_FS_O_RDONLY | UV_FS_O_SYNC)) == 0) { | ||
| 1976 | - THROW_IF_INSUFFICIENT_PERMISSIONS( | ||
| 1977 | - env, permission::PermissionScope::kFileSystemRead, pathView); | ||
| 1978 | - } else if ((flags & (UV_FS_O_APPEND | UV_FS_O_TRUNC | UV_FS_O_CREAT | | ||
| 1979 | - UV_FS_O_WRONLY)) != 0) { | ||
| 1980 | - THROW_IF_INSUFFICIENT_PERMISSIONS( | ||
| 1981 | - env, permission::PermissionScope::kFileSystemWrite, pathView); | ||
| 1982 | - } | ||
| 2001 | + if (CheckOpenPermissions(env, path, flags).IsNothing()) return; | ||
| 1983 | 2002 | ||
| 1984 | 2003 | FSReqBase* req_wrap_async = GetReqWrap(args, 3); | |
| 1985 | 2004 | if (req_wrap_async != nullptr) { // open(path, flags, mode, req) | |
@@ -2010,31 +2029,14 @@ static void OpenFileHandle(const FunctionCallbackInfo<Value>& args) { | |||
| 2010 | 2029 | ||
| 2011 | 2030 | BufferValue path(isolate, args[0]); | |
| 2012 | 2031 | CHECK_NOT_NULL(*path); | |
| 2013 | - auto pathView = path.ToStringView(); | ||
| 2014 | - THROW_IF_INSUFFICIENT_PERMISSIONS( | ||
| 2015 | - env, permission::PermissionScope::kFileSystemRead, pathView); | ||
| 2016 | 2032 | ||
| 2017 | 2033 | CHECK(args[1]->IsInt32()); | |
| 2018 | 2034 | const int flags = args[1].As<Int32>()->Value(); | |
| 2019 | 2035 | ||
| 2020 | 2036 | CHECK(args[2]->IsInt32()); | |
| 2021 | 2037 | const int mode = args[2].As<Int32>()->Value(); | |
| 2022 | 2038 | ||
| 2023 | - // Open can be called either in write or read | ||
| 2024 | - if (flags == O_RDWR) { | ||
| 2025 | - // TODO(rafaelgss): it can be optimized to avoid O(2*n) | ||
| 2026 | - THROW_IF_INSUFFICIENT_PERMISSIONS( | ||
| 2027 | - env, permission::PermissionScope::kFileSystemRead, pathView); | ||
| 2028 | - THROW_IF_INSUFFICIENT_PERMISSIONS( | ||
| 2029 | - env, permission::PermissionScope::kFileSystemWrite, pathView); | ||
| 2030 | - } else if ((flags & ~(UV_FS_O_RDONLY | UV_FS_O_SYNC)) == 0) { | ||
| 2031 | - THROW_IF_INSUFFICIENT_PERMISSIONS( | ||
| 2032 | - env, permission::PermissionScope::kFileSystemRead, pathView); | ||
| 2033 | - } else if ((flags & (UV_FS_O_APPEND | UV_FS_O_TRUNC | UV_FS_O_CREAT | | ||
| 2034 | - UV_FS_O_WRONLY)) != 0) { | ||
| 2035 | - THROW_IF_INSUFFICIENT_PERMISSIONS( | ||
| 2036 | - env, permission::PermissionScope::kFileSystemWrite, pathView); | ||
| 2037 | - } | ||
| 2039 | + if (CheckOpenPermissions(env, path, flags).IsNothing()) return; | ||
| 2038 | 2040 | ||
| 2039 | 2041 | FSReqBase* req_wrap_async = GetReqWrap(args, 3); | |
| 2040 | 2042 | if (req_wrap_async != nullptr) { // openFileHandle(path, flags, mode, req) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -249,6 +249,21 @@ const gid = os.userInfo().gid; | |||
| 249 | 249 | fs.open(regularFile, 'r', (err) => { | |
| 250 | 250 | assert.ifError(err); | |
| 251 | 251 | }); | |
| 252 | + | ||
| 253 | + // Extra flags should not enable trivially bypassing all restrictions. | ||
| 254 | + // See https://github.com/nodejs/node/issues/47090. | ||
| 255 | + assert.throws(() => { | ||
| 256 | + fs.openSync(blockedFile, fs.constants.O_RDONLY | fs.constants.O_NOCTTY); | ||
| 257 | + }, { | ||
| 258 | + code: 'ERR_ACCESS_DENIED', | ||
| 259 | + permission: 'FileSystemRead', | ||
| 260 | + }); | ||
| 261 | + assert.throws(() => { | ||
| 262 | + fs.open(blockedFile, fs.constants.O_RDWR | 0x10000000, common.mustNotCall()); | ||
| 263 | + }, { | ||
| 264 | + code: 'ERR_ACCESS_DENIED', | ||
| 265 | + permission: 'FileSystemRead', | ||
| 266 | + }); | ||
| 252 | 267 | } | |
| 253 | 268 | ||
| 254 | 269 | // fs.opendir | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -238,3 +238,32 @@ const regularFile = fixtures.path('permission', 'deny', 'regular-file.md'); | |||
| 238 | 238 | resource: path.toNamespacedPath(blockedFile), | |
| 239 | 239 | })); | |
| 240 | 240 | } | |
| 241 | + | ||
| 242 | + // fs.open | ||
| 243 | + { | ||
| 244 | + // Extra flags should not enable trivially bypassing all restrictions. | ||
| 245 | + // See https://github.com/nodejs/node/issues/47090. | ||
| 246 | + assert.throws(() => { | ||
| 247 | + fs.open(blockedFile, fs.constants.O_RDWR | 0x10000000, common.mustNotCall()); | ||
| 248 | + }, { | ||
| 249 | + code: 'ERR_ACCESS_DENIED', | ||
| 250 | + permission: 'FileSystemWrite', | ||
| 251 | + }); | ||
| 252 | + assert.rejects(async () => { | ||
| 253 | + await fs.promises.open(blockedFile, fs.constants.O_RDWR | fs.constants.O_NOFOLLOW); | ||
| 254 | + }, { | ||
| 255 | + code: 'ERR_ACCESS_DENIED', | ||
| 256 | + permission: 'FileSystemWrite', | ||
| 257 | + }); | ||
| 258 | + if (common.isWindows) { | ||
| 259 | + // In particular, on Windows, the permission system should not blindly let | ||
| 260 | + // code delete write-protected files. | ||
| 261 | + const O_TEMPORARY = 0x40; | ||
| 262 | + assert.throws(() => { | ||
| 263 | + fs.openSync(blockedFile, fs.constants.O_RDONLY | O_TEMPORARY); | ||
| 264 | + }, { | ||
| 265 | + code: 'ERR_ACCESS_DENIED', | ||
| 266 | + permission: 'FileSystemWrite' | ||
| 267 | + }); | ||
| 268 | + } | ||
| 269 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments