Skip to content

Commit a3db21c

Browse files
committed
src,permission: --allow-wasi & prevent WASI exec
1 parent 01c281f commit a3db21c

17 files changed

+181
-6
lines changed

doc/api/cli.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,53 @@ Examples can be found in the [File System Permissions][] documentation.
269269

270270
Relative paths are NOT supported through the CLI flag.
271271

272+
### `--allow-wasi`
273+
274+
<!-- YAML
275+
added: REPLACEME
276+
-->
277+
278+
> Stability: 1.1 - Active development
279+
280+
When using the [Permission Model][], the process will not be able to create any
281+
WASI instances by default.
282+
For security reasons, the call will throw an `ERR_ACCESS_DENIED` unless the
283+
user explicitly passes the flag `--allow-wasi` in the main Node.js process.
284+
285+
Example:
286+
287+
```js
288+
const { WASI } = require('node:wasi');
289+
// Attempt to bypass the permission
290+
new WASI({
291+
version: 'preview1',
292+
// Attempt to mount the whole filesystem
293+
preopens: {
294+
'/': '/',
295+
},
296+
});
297+
```
298+
299+
```console
300+
$ node --experimental-permission --allow-fs-read=* index.js
301+
node:wasi:99
302+
const wrap = new _WASI(args, env, preopens, stdio);
303+
^
304+
305+
Error: Access to this API has been restricted
306+
at new WASI (node:wasi:99:18)
307+
at Object.<anonymous> (/home/index.js:3:1)
308+
at Module._compile (node:internal/modules/cjs/loader:1476:14)
309+
at Module._extensions..js (node:internal/modules/cjs/loader:1555:10)
310+
at Module.load (node:internal/modules/cjs/loader:1288:32)
311+
at Module._load (node:internal/modules/cjs/loader:1104:12)
312+
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:191:14)
313+
at node:internal/main/run_main_module:30:49 {
314+
code: 'ERR_ACCESS_DENIED',
315+
permission: 'WASI',
316+
}
317+
```
318+
272319
### `--allow-worker`
273320

274321
<!-- YAML
@@ -878,6 +925,7 @@ following permissions are restricted:
878925
[`--allow-fs-read`][], [`--allow-fs-write`][] flags
879926
* Child Process - manageable through [`--allow-child-process`][] flag
880927
* Worker Threads - manageable through [`--allow-worker`][] flag
928+
* WASI - manageable through [`--allow-wasi`][] flag
881929

882930
### `--experimental-policy`
883931

@@ -2643,6 +2691,7 @@ one is included in the list below.
26432691
* `--allow-child-process`
26442692
* `--allow-fs-read`
26452693
* `--allow-fs-write`
2694+
* `--allow-wasi`
26462695
* `--allow-worker`
26472696
* `--conditions`, `-C`
26482697
* `--diagnostic-dir`
@@ -3187,6 +3236,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
31873236
[`--allow-child-process`]: #--allow-child-process
31883237
[`--allow-fs-read`]: #--allow-fs-read
31893238
[`--allow-fs-write`]: #--allow-fs-write
3239+
[`--allow-wasi`]: #--allow-wasi
31903240
[`--allow-worker`]: #--allow-worker
31913241
[`--build-snapshot`]: #--build-snapshot
31923242
[`--cpu-prof-dir`]: #--cpu-prof-dir

doc/api/permissions.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ flag.
482482

483483
When starting Node.js with `--experimental-permission`,
484484
the ability to access the file system through the `fs` module, spawn processes,
485-
use `node:worker_threads`, native addons, and enable the runtime inspector
485+
use `node:worker_threads`, use native addons, use WASI, and enable the runtime inspector
486486
will be restricted.
487487

488488
```console
@@ -507,7 +507,7 @@ Allowing access to spawning a process and creating worker threads can be done
507507
using the [`--allow-child-process`][] and [`--allow-worker`][] respectively.
508508

509509
To allow native addons when using permission model, use the [`--allow-addons`][]
510-
flag.
510+
flag. For WASI, use the [`--allow-wasi`][] flag.
511511

512512
#### Runtime API
513513

@@ -574,6 +574,7 @@ There are constraints you need to know before using this system:
574574
* Worker Threads
575575
* Inspector protocol
576576
* File system access
577+
* WASI
577578
* The Permission Model is initialized after the Node.js environment is set up.
578579
However, certain flags such as `--env-file` or `--openssl-config` are designed
579580
to read files before environment initialization. As a result, such flags are
@@ -598,6 +599,7 @@ There are constraints you need to know before using this system:
598599
[`--allow-child-process`]: cli.md#--allow-child-process
599600
[`--allow-fs-read`]: cli.md#--allow-fs-read
600601
[`--allow-fs-write`]: cli.md#--allow-fs-write
602+
[`--allow-wasi`]: cli.md#--allow-wasi
601603
[`--allow-worker`]: cli.md#--allow-worker
602604
[`--experimental-permission`]: cli.md#--experimental-permission
603605
[`permission.has()`]: process.md#processpermissionhasscope-reference

doc/node.1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ Allow using native addons when using the permission model.
8888
.It Fl -allow-child-process
8989
Allow spawning process when using the permission model.
9090
.
91+
.It Fl -allow-wasi
92+
Allow execution of WASI when using the permission model.
93+
.
9194
.It Fl -allow-worker
9295
Allow creating worker threads when using the permission model.
9396
.

lib/internal/process/pre_execution.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,7 @@ function initializePermission() {
550550
const warnFlags = [
551551
'--allow-addons',
552552
'--allow-child-process',
553+
'--allow-wasi',
553554
'--allow-worker',
554555
];
555556
for (const flag of warnFlags) {
@@ -591,6 +592,7 @@ function initializePermission() {
591592
'--allow-fs-write',
592593
'--allow-addons',
593594
'--allow-child-process',
595+
'--allow-wasi',
594596
'--allow-worker',
595597
];
596598
ArrayPrototypeForEach(availablePermissionFlags, (flag) => {

node.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@
151151
'src/permission/fs_permission.cc',
152152
'src/permission/inspector_permission.cc',
153153
'src/permission/permission.cc',
154+
'src/permission/wasi_permission.cc',
154155
'src/permission/worker_permission.cc',
155156
'src/pipe_wrap.cc',
156157
'src/process_wrap.cc',
@@ -275,6 +276,7 @@
275276
'src/permission/fs_permission.h',
276277
'src/permission/inspector_permission.h',
277278
'src/permission/permission.h',
279+
'src/permission/wasi_permission.h',
278280
'src/permission/worker_permission.h',
279281
'src/pipe_wrap.h',
280282
'src/req_wrap.h',

src/env.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,9 @@ Environment::Environment(IsolateData* isolate_data,
921921
permission()->Apply(
922922
this, {"*"}, permission::PermissionScope::kWorkerThreads);
923923
}
924+
if (!options_->allow_wasi) {
925+
permission()->Apply(this, {"*"}, permission::PermissionScope::kWASI);
926+
}
924927

925928
if (!options_->allow_fs_read.empty()) {
926929
permission()->Apply(this,

src/node_options.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
471471
"allow use of child process when any permissions are set",
472472
&EnvironmentOptions::allow_child_process,
473473
kAllowedInEnvvar);
474+
AddOption("--allow-wasi",
475+
"allow wasi when any permissions are set",
476+
&EnvironmentOptions::allow_wasi,
477+
kAllowedInEnvvar);
474478
AddOption("--allow-worker",
475479
"allow worker threads when any permissions are set",
476480
&EnvironmentOptions::allow_worker_threads,

src/node_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ class EnvironmentOptions : public Options {
127127
std::vector<std::string> allow_fs_write;
128128
bool allow_addons = false;
129129
bool allow_child_process = false;
130+
bool allow_wasi = false;
130131
bool allow_worker_threads = false;
131132
bool experimental_repl_await = true;
132133
bool experimental_vm_modules = false;

src/node_wasi.cc

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
#include "env-inl.h"
1+
#include "node_wasi.h"
22
#include "base_object-inl.h"
33
#include "debug_utils-inl.h"
4+
#include "env-inl.h"
45
#include "memory_tracker-inl.h"
5-
#include "node_mem-inl.h"
6-
#include "util-inl.h"
76
#include "node.h"
87
#include "node_errors.h"
8+
#include "node_mem-inl.h"
9+
#include "permission/permission.h"
10+
#include "util-inl.h"
911
#include "uv.h"
1012
#include "uvwasi.h"
11-
#include "node_wasi.h"
1213

1314
namespace node {
1415
namespace wasi {
@@ -77,6 +78,8 @@ static MaybeLocal<Value> WASIException(Local<Context> context,
7778
WASI::WASI(Environment* env,
7879
Local<Object> object,
7980
uvwasi_options_t* options) : BaseObject(env, object) {
81+
THROW_IF_INSUFFICIENT_PERMISSIONS(
82+
env, permission::PermissionScope::kWASI, "");
8083
MakeWeak();
8184
alloc_info_ = MakeAllocator();
8285
options->allocator = &alloc_info_;

src/permission/permission.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ Permission::Permission() : enabled_(false) {
8181
std::make_shared<WorkerPermission>();
8282
std::shared_ptr<PermissionBase> inspector =
8383
std::make_shared<InspectorPermission>();
84+
std::shared_ptr<PermissionBase> wasi = std::make_shared<WASIPermission>();
8485
#define V(Name, _, __) \
8586
nodes_.insert(std::make_pair(PermissionScope::k##Name, fs));
8687
FILESYSTEM_PERMISSIONS(V)
@@ -97,6 +98,10 @@ Permission::Permission() : enabled_(false) {
9798
nodes_.insert(std::make_pair(PermissionScope::k##Name, inspector));
9899
INSPECTOR_PERMISSIONS(V)
99100
#undef V
101+
#define V(Name, _, __) \
102+
nodes_.insert(std::make_pair(PermissionScope::k##Name, wasi));
103+
WASI_PERMISSIONS(V)
104+
#undef V
100105
}
101106

102107
void Permission::ThrowAccessDenied(Environment* env,

0 commit comments

Comments
 (0)