Skip to content

Commit b47f3eb

Browse files
committed
cli: add flags for access control
Add `--disable=featureA,featureB,...` and `--experimental-access-control` flags that control (and enable) `process.accessControl` at startup.
1 parent b3cfaed commit b47f3eb

File tree

13 files changed

+150
-2
lines changed

13 files changed

+150
-2
lines changed

doc/api/cli.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ If this flag is passed, the behavior can still be set to not abort through
5252
[`process.setUncaughtExceptionCaptureCallback()`][] (and through usage of the
5353
`domain` module that uses it).
5454

55+
### `--disable=...`
56+
<!-- YAML
57+
added: REPLACEME
58+
-->
59+
60+
Disable certain features of Node.js at startup. See [`process.accessControl`][]
61+
for more details.
62+
5563
### `--enable-fips`
5664
<!-- YAML
5765
added: v6.0.0
@@ -60,6 +68,15 @@ added: v6.0.0
6068
Enable FIPS-compliant crypto at startup. (Requires Node.js to be built with
6169
`./configure --openssl-fips`.)
6270

71+
### `--experimental-access-control`
72+
<!-- YAML
73+
added: REPLACEME
74+
-->
75+
76+
Allow disabling certain features of Node.js at runtime.
77+
See [`process.accessControl`][] for more details.
78+
The `--disable` flag implies this flag.
79+
6380
### `--experimental-modules`
6481
<!-- YAML
6582
added: v8.5.0
@@ -688,6 +705,7 @@ greater than `4` (its current default value). For more information, see the
688705
[`--openssl-config`]: #cli_openssl_config_file
689706
[`Buffer`]: buffer.html#buffer_class_buffer
690707
[`SlowBuffer`]: buffer.html#buffer_class_slowbuffer
708+
[`process.accessControl`]: process.html#process_access_control
691709
[`process.setUncaughtExceptionCaptureCallback()`]: process.html#process_process_setuncaughtexceptioncapturecallback_fn
692710
[Chrome DevTools Protocol]: https://chromedevtools.github.io/devtools-protocol/
693711
[REPL]: repl.html

doc/api/process.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,6 +2000,13 @@ Will generate an object similar to:
20002000

20012001
> Stability: 1 - Experimental
20022002
2003+
This feature is experimental and needs to be enabled by passing
2004+
the `--experimental-access-control` flag, or the
2005+
`--disable=restrictionA,restrictionB,...` flag to Node.js.
2006+
2007+
The `--disable` flag takes a comma-separated list of identifiers as described
2008+
below. For example, it can be used as `--disable=fsRead,fsWrite`.
2009+
20032010
### process.accessControl.apply(restrictions)
20042011
<!-- YAML
20052012
added: REPLACEME

doc/node.1

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ the next argument will be used as a script filename.
7575
.It Fl -abort-on-uncaught-exception
7676
Aborting instead of exiting causes a core file to be generated for analysis.
7777
.
78+
.It Fl -disable Ns = Ns Ar featureA,featureB,...
79+
Disables certain features of Node.js at startup.
80+
A full list of available restriction identifiers can be shown by running:
81+
.Pp
82+
.Sy ./node --experimental-access-control --print 'process.accessControl.getCurrent()'
83+
.
7884
.It Fl -enable-fips
7985
Enable FIPS-compliant crypto at startup.
8086
Requires Node.js to be built with
@@ -83,6 +89,12 @@ Requires Node.js to be built with
8389
.It Fl -experimental-modules
8490
Enable experimental ES module support and caching modules.
8591
.
92+
.It Fl -experimental-access-control
93+
Allow disabling certain features of Node.js at runtime.
94+
The
95+
.Fl -disable
96+
flag implies this flag.
97+
.
8698
.It Fl -experimental-repl-await
8799
Enable experimental top-level
88100
.Sy await

lib/internal/bootstrap/node.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@
9898
perThreadSetup.setupMemoryUsage(_memoryUsage);
9999
perThreadSetup.setupKillAndExit();
100100

101-
process.accessControl = internalBinding('access_control');
101+
if (process.binding('config').experimentalAccessControl)
102+
process.accessControl = internalBinding('access_control');
102103

103104
if (global.__coverage__)
104105
NativeModule.require('internal/process/write-coverage').setup();

src/env.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ Environment::Environment(IsolateData* isolate_data,
178178

179179
isolate()->GetHeapProfiler()->AddBuildEmbedderGraphCallback(
180180
BuildEmbedderGraph, this);
181+
182+
access_control()->apply(global_access_control);
181183
}
182184

183185
Environment::~Environment() {

src/env.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,8 @@ class AccessControl {
457457
v8::MaybeLocal<v8::Object> ToObject(v8::Local<v8::Context> context);
458458
static v8::Maybe<AccessControl> FromObject(v8::Local<v8::Context> context,
459459
v8::Local<v8::Object> object);
460+
static AccessControl FromString(const std::string& disabled_item_list,
461+
std::string* unknown_item);
460462

461463
static void ThrowAccessDenied(Environment* env, Permission perm);
462464

src/node.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,12 @@ bool config_preserve_symlinks = false;
240240
// that is used by lib/module.js
241241
bool config_preserve_symlinks_main = false;
242242

243+
// Set in node.cc by ParseArgs when --experimental-access-control is used.
244+
// Used in node_config.cc to set a constant on process.binding('config')
245+
// that is used by the bootstrapper.
246+
bool config_experimental_access_control = false;
247+
AccessControl global_access_control;
248+
243249
// Set in node.cc by ParseArgs when --experimental-modules is used.
244250
// Used in node_config.cc to set a constant on process.binding('config')
245251
// that is used by lib/module.js
@@ -2615,9 +2621,15 @@ static void PrintHelp() {
26152621
" --abort-on-uncaught-exception\n"
26162622
" aborting instead of exiting causes a\n"
26172623
" core file to be generated for analysis\n"
2624+
" --disable=featureA,featureB,...\n"
2625+
" experimental support for\n"
2626+
" disabling individual features\n"
2627+
" by listing them on the command line\n"
26182628
#if HAVE_OPENSSL && NODE_FIPS_MODE
26192629
" --enable-fips enable FIPS crypto at startup\n"
26202630
#endif // NODE_FIPS_MODE && NODE_FIPS_MODE
2631+
" --experimental-access-control experimental support for\n"
2632+
" disabling individual features\n"
26212633
" --experimental-modules experimental ES Module support\n"
26222634
" and caching modules\n"
26232635
" --experimental-repl-await experimental await keyword support\n"
@@ -2785,7 +2797,9 @@ static void CheckIfAllowedInEnv(const char* exe, bool is_env,
27852797
static const char* whitelist[] = {
27862798
// Node options, sorted in `node --help` order for ease of comparison.
27872799
// Please, update NODE_OPTIONS section in cli.md if changed.
2800+
"--disable",
27882801
"--enable-fips",
2802+
"--experimental-access-control",
27892803
"--experimental-modules",
27902804
"--experimental-repl-await",
27912805
"--experimental-vm-modules",
@@ -2982,6 +2996,18 @@ static void ParseArgs(int* argc,
29822996
config_preserve_symlinks = true;
29832997
} else if (strcmp(arg, "--preserve-symlinks-main") == 0) {
29842998
config_preserve_symlinks_main = true;
2999+
} else if (strncmp(arg, "--disable=", 10) == 0) {
3000+
std::string unknown_item;
3001+
global_access_control.apply(
3002+
AccessControl::FromString(arg + 10, &unknown_item));
3003+
if (!unknown_item.empty()) {
3004+
fprintf(stderr, "%s: unknown --disable entry %s\n",
3005+
argv[0], unknown_item.c_str());
3006+
exit(9);
3007+
}
3008+
config_experimental_access_control = true;
3009+
} else if (strcmp(arg, "--experimental-access-control") == 0) {
3010+
config_experimental_access_control = true;
29853011
} else if (strcmp(arg, "--experimental-modules") == 0) {
29863012
config_experimental_modules = true;
29873013
} else if (strcmp(arg, "--experimental-vm-modules") == 0) {
@@ -3081,6 +3107,12 @@ static void ParseArgs(int* argc,
30813107
exit(9);
30823108
}
30833109

3110+
if (config_experimental_access_control) {
3111+
fprintf(stderr,
3112+
"Warning: Access control through disabling features is "
3113+
"experimental and may be changed at any time.\n");
3114+
}
3115+
30843116
// Copy remaining arguments.
30853117
const unsigned int args_left = nargs - index;
30863118

src/node_access_control.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@ Maybe<AccessControl> AccessControl::FromObject(Local<Context> context,
5656
return Just(ret);
5757
}
5858

59+
AccessControl AccessControl::FromString(const std::string& disabled_item_list,
60+
std::string* unknown_item) {
61+
AccessControl ret;
62+
63+
std::set<std::string> items = ParseCommaSeparatedSet(disabled_item_list);
64+
for (const std::string& item : items) {
65+
Permission perm = PermissionFromString(item.c_str());
66+
if (perm != kNumPermissions) {
67+
ret.set_permission(perm, false);
68+
continue;
69+
}
70+
if (unknown_item != nullptr)
71+
*unknown_item = item;
72+
}
73+
74+
return ret;
75+
}
76+
5977
AccessControl::Permission AccessControl::PermissionFromString(const char* str) {
6078
#define V(kind) if (strcmp(str, #kind) == 0) return kind;
6179
ACCESS_CONTROL_FLAGS(V)

src/node_config.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ static void Initialize(Local<Object> target,
8181
if (config_preserve_symlinks_main)
8282
READONLY_BOOLEAN_PROPERTY("preserveSymlinksMain");
8383

84+
if (config_experimental_access_control)
85+
READONLY_BOOLEAN_PROPERTY("experimentalAccessControl");
86+
8487
if (config_experimental_modules) {
8588
READONLY_BOOLEAN_PROPERTY("experimentalModules");
8689
if (!config_userland_loader.empty()) {

src/node_internals.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ extern bool config_preserve_symlinks;
186186
// that is used by lib/module.js
187187
extern bool config_preserve_symlinks_main;
188188

189+
// Set in node.cc by ParseArgs when --experimental-access-control is used.
190+
// Used in node_config.cc to set a constant on process.binding('config')
191+
// that is used by the bootstrapper.
192+
extern bool config_experimental_access_control;
193+
extern AccessControl global_access_control;
194+
189195
// Set in node.cc by ParseArgs when --experimental-modules is used.
190196
// Used in node_config.cc to set a constant on process.binding('config')
191197
// that is used by lib/module.js

0 commit comments

Comments
 (0)