GeezerVison is showing github.com — lib,permission: add permission.drop · nodejs/node@cfb80a2
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/permissions.md +36-1Lines changed: 36 additions & 1 deletion - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -78,7 +78,7 @@ flag. For WASI, use the [`--allow-wasi`][] flag. For FFI, use the | 78 | 78 |
| 79 | 79 | When enabling the Permission Model through the [`--permission`][] | 80 | 80 | flag a new property `permission` is added to the `process` object. | 81 | | -This property contains one function: | | 81 | +This property contains the following functions: | 82 | 82 |
| 83 | 83 | ##### `permission.has(scope[, reference])` | 84 | 84 |
| @@ -92,6 +92,41 @@ process.permission.has('fs.read'); // true | 92 | 92 | process.permission.has('fs.read', '/home/rafaelgss/protected-folder'); // false | 93 | 93 | ``` | 94 | 94 |
| | 95 | +##### `permission.drop(scope[, reference])` | | 96 | + | | 97 | +API call to drop permissions at runtime. This operation is **irreversible**. | | 98 | + | | 99 | +When called without a reference, the entire scope is dropped. When called | | 100 | +with a reference, only the permission for that specific resource is revoked. | | 101 | +Dropping a permission only affects future access checks. It does not close or | | 102 | +revoke access to resources that are already open, such as file descriptors, | | 103 | +network sockets, child processes, or worker threads. Applications are | | 104 | +responsible for closing or terminating those resources when they are no longer | | 105 | +needed. | | 106 | + | | 107 | +You can only drop the exact resource that was explicitly granted. The | | 108 | +reference passed to `drop()` must match the original grant. If a permission | | 109 | +was granted using a wildcard (`*`), only the entire scope can be dropped | | 110 | +(by calling `drop()` without a reference). If a directory was granted | | 111 | +(e.g. `--allow-fs-read=/my/folder`), you cannot drop individual files | | 112 | +inside it - you must drop the same directory that was originally granted. | | 113 | + | | 114 | +```js | | 115 | +const fs = require('node:fs'); | | 116 | + | | 117 | +// Read config at startup while we still have permission | | 118 | +const config = fs.readFileSync('/etc/myapp/config.json', 'utf8'); | | 119 | + | | 120 | +// Drop read access to /etc/myapp after initialization | | 121 | +process.permission.drop('fs.read', '/etc/myapp'); | | 122 | + | | 123 | +// This will now throw ERR_ACCESS_DENIED | | 124 | +process.permission.has('fs.read', '/etc/myapp/config.json'); // false | | 125 | + | | 126 | +// Drop child process permission entirely | | 127 | +process.permission.drop('child'); | | 128 | +``` | | 129 | + | 95 | 130 | #### File System Permissions | 96 | 131 |
| 97 | 132 | The Permission Model, by default, restricts access to the file system through the `node:fs` module. | |
Collapse file Copy file name to clipboardExpand all lines: doc/api/process.md +60Lines changed: 60 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -3167,6 +3167,65 @@ process.permission.has('fs.read', './README.md'); | 3167 | 3167 | process.permission.has('fs.read'); | 3168 | 3168 | ``` | 3169 | 3169 |
| | 3170 | +### `process.permission.drop(scope[, reference])` | | 3171 | +
| | 3172 | +<!-- YAML | | 3173 | +added: REPLACEME | | 3174 | +--> | | 3175 | +
| | 3176 | +> Stability: 1.1 - Active Development | | 3177 | +
| | 3178 | +* `scope` {string} | | 3179 | +* `reference` {string} | | 3180 | +
| | 3181 | +Drops the specified permission from the current process. This operation is | | 3182 | +**irreversible** — once a permission is dropped, it cannot be restored through | | 3183 | +any Node.js API. | | 3184 | +
| | 3185 | +If no reference is provided, the entire scope is dropped. For example, | | 3186 | +`process.permission.drop('fs.read')` will revoke ALL file system read | | 3187 | +permissions. | | 3188 | +
| | 3189 | +When a reference is provided, only the permission for that specific resource | | 3190 | +is dropped. For example, `process.permission.drop('fs.read', '/etc/myapp')` | | 3191 | +will revoke read access to that directory while keeping other read | | 3192 | +permissions intact. | | 3193 | +
| | 3194 | +**Important:** You can only drop the exact resource that was explicitly | | 3195 | +granted. The reference passed to `drop()` must match the original grant: | | 3196 | +
| | 3197 | +* If a permission was granted using a wildcard (`*`), such as | | 3198 | + `--allow-fs-read=*`, individual paths cannot be dropped - only the entire | | 3199 | + scope can be dropped (by calling `drop()` without a reference). | | 3200 | +* If a directory was granted (e.g. `--allow-fs-read=/my/folder`), you cannot | | 3201 | + drop access to individual files inside it. You must drop the same directory | | 3202 | + that was granted. Any remaining grants continue to apply. | | 3203 | +
| | 3204 | +The available scopes are the same as [`process.permission.has()`][]: | | 3205 | +
| | 3206 | +* `fs` - All File System (drops both read and write) | | 3207 | +* `fs.read` - File System read operations | | 3208 | +* `fs.write` - File System write operations | | 3209 | +* `child` - Child process spawning operations | | 3210 | +* `worker` - Worker thread spawning operation | | 3211 | +* `net` - Network operations | | 3212 | +* `inspector` - Inspector operations | | 3213 | +* `wasi` - WASI operations | | 3214 | +* `addon` - Native addon operations | | 3215 | +
| | 3216 | +```js | | 3217 | +const fs = require('node:fs'); | | 3218 | + | | 3219 | +// Read configuration during startup | | 3220 | +const config = fs.readFileSync('/etc/myapp/config.json', 'utf8'); | | 3221 | + | | 3222 | +// Drop read access to the config directory after initialization | | 3223 | +process.permission.drop('fs.read', '/etc/myapp'); | | 3224 | + | | 3225 | +// This will now throw ERR_ACCESS_DENIED | | 3226 | +fs.readFileSync('/etc/myapp/config.json'); | | 3227 | +``` | | 3228 | +
| 3170 | 3229 | ## `process.pid` | 3171 | 3230 |
| 3172 | 3231 | <!-- YAML | @@ -4583,6 +4642,7 @@ cases: | 4583 | 4642 | [`process.hrtime()`]: #processhrtimetime | 4584 | 4643 | [`process.hrtime.bigint()`]: #processhrtimebigint | 4585 | 4644 | [`process.kill()`]: #processkillpid-signal | | 4645 | +[`process.permission.has()`]: #processpermissionhasscope-reference | 4586 | 4646 | [`process.setUncaughtExceptionCaptureCallback()`]: #processsetuncaughtexceptioncapturecallbackfn | 4587 | 4647 | [`promise.catch()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch | 4588 | 4648 | [`queueMicrotask()`]: globals.md#queuemicrotaskcallback | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/process/permission.js +12Lines changed: 12 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -43,6 +43,18 @@ module.exports = ObjectFreeze({ | 43 | 43 |
| 44 | 44 | return permission.has(scope, reference); | 45 | 45 | }, | | 46 | + drop(scope, reference) { | | 47 | + validateString(scope, 'scope'); | | 48 | + if (reference != null) { | | 49 | + if (isBuffer(reference)) { | | 50 | + validateBuffer(reference, 'reference'); | | 51 | + } else { | | 52 | + validateString(reference, 'reference'); | | 53 | + } | | 54 | + } | | 55 | + | | 56 | + permission.drop(scope, reference); | | 57 | + }, | 46 | 58 | availableFlags() { | 47 | 59 | if (_ffi === undefined) { | 48 | 60 | const { getOptionValue } = require('internal/options'); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/process/pre_execution.js +2-1Lines changed: 2 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -660,7 +660,7 @@ function initializePermission() { | 660 | 660 | }; | 661 | 661 | // Guarantee path module isn't monkey-patched to bypass permission model | 662 | 662 | ObjectFreeze(require('path')); | 663 | | - const { has } = require('internal/process/permission'); | | 663 | + const { has, drop } = require('internal/process/permission'); | 664 | 664 | const warnFlags = [ | 665 | 665 | '--allow-addons', | 666 | 666 | '--allow-child-process', | @@ -712,6 +712,7 @@ function initializePermission() { | 712 | 712 | configurable: false, | 713 | 713 | value: { | 714 | 714 | has, | | 715 | + drop, | 715 | 716 | }, | 716 | 717 | }); | 717 | 718 | } else { | |
Collapse file Copy file name to clipboardExpand all lines: src/permission/addon_permission.cc +6Lines changed: 6 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -14,6 +14,12 @@ void AddonPermission::Apply(Environment* env, | 14 | 14 | deny_all_ = true; | 15 | 15 | } | 16 | 16 |
| | 17 | +void AddonPermission::Drop(Environment* env, | | 18 | + PermissionScope scope, | | 19 | + const std::string_view& param) { | | 20 | + deny_all_ = true; | | 21 | +} | | 22 | + | 17 | 23 | bool AddonPermission::is_granted(Environment* env, | 18 | 24 | PermissionScope perm, | 19 | 25 | const std::string_view& param) const { | |
Collapse file Copy file name to clipboardExpand all lines: src/permission/addon_permission.h +3Lines changed: 3 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -15,6 +15,9 @@ class AddonPermission final : public PermissionBase { | 15 | 15 | void Apply(Environment* env, | 16 | 16 | const std::vector<std::string>& allow, | 17 | 17 | PermissionScope scope) override; | | 18 | + void Drop(Environment* env, | | 19 | + PermissionScope scope, | | 20 | + const std::string_view& param = "") override; | 18 | 21 | bool is_granted(Environment* env, | 19 | 22 | PermissionScope perm, | 20 | 23 | const std::string_view& param = "") const override; | |
Collapse file Copy file name to clipboardExpand all lines: src/permission/child_process_permission.cc +6Lines changed: 6 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -15,6 +15,12 @@ void ChildProcessPermission::Apply(Environment* env, | 15 | 15 | deny_all_ = true; | 16 | 16 | } | 17 | 17 |
| | 18 | +void ChildProcessPermission::Drop(Environment* env, | | 19 | + PermissionScope scope, | | 20 | + const std::string_view& param) { | | 21 | + deny_all_ = true; | | 22 | +} | | 23 | + | 18 | 24 | bool ChildProcessPermission::is_granted(Environment* env, | 19 | 25 | PermissionScope perm, | 20 | 26 | const std::string_view& param) const { | |
Collapse file Copy file name to clipboardExpand all lines: src/permission/child_process_permission.h +3Lines changed: 3 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -15,6 +15,9 @@ class ChildProcessPermission final : public PermissionBase { | 15 | 15 | void Apply(Environment* env, | 16 | 16 | const std::vector<std::string>& allow, | 17 | 17 | PermissionScope scope) override; | | 18 | + void Drop(Environment* env, | | 19 | + PermissionScope scope, | | 20 | + const std::string_view& param = "") override; | 18 | 21 | bool is_granted(Environment* env, | 19 | 22 | PermissionScope perm, | 20 | 23 | const std::string_view& param = "") const override; | |
Collapse file Copy file name to clipboardExpand all lines: src/permission/ffi_permission.cc +6Lines changed: 6 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -14,6 +14,12 @@ void FFIPermission::Apply(Environment* env, | 14 | 14 | deny_all_ = true; | 15 | 15 | } | 16 | 16 |
| | 17 | +void FFIPermission::Drop(Environment* env, | | 18 | + PermissionScope scope, | | 19 | + const std::string_view& param) { | | 20 | + deny_all_ = true; | | 21 | +} | | 22 | + | 17 | 23 | bool FFIPermission::is_granted(Environment* env, | 18 | 24 | PermissionScope perm, | 19 | 25 | const std::string_view& param) const { | |
Collapse file Copy file name to clipboardExpand all lines: src/permission/ffi_permission.h +3Lines changed: 3 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -15,6 +15,9 @@ class FFIPermission final : public PermissionBase { | 15 | 15 | void Apply(Environment* env, | 16 | 16 | const std::vector<std::string>& allow, | 17 | 17 | PermissionScope scope) override; | | 18 | + void Drop(Environment* env, | | 19 | + PermissionScope scope, | | 20 | + const std::string_view& param = "") override; | 18 | 21 | bool is_granted(Environment* env, | 19 | 22 | PermissionScope perm, | 20 | 23 | const std::string_view& param = "") const override; | |
|