Skip to content

Commit d40825b

Browse files
RafaelGSSeliphazbouye
authored andcommitted
src,permission: --allow-wasi & prevent WASI exec
PR-URL: nodejs#53124 Reviewed-By: Marco Ippolito <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent 154a153 commit d40825b

17 files changed

+183
-7
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 capable of creating
281+
any 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
@@ -927,6 +974,7 @@ following permissions are restricted:
927974
[`--allow-fs-read`][], [`--allow-fs-write`][] flags
928975
* Child Process - manageable through [`--allow-child-process`][] flag
929976
* Worker Threads - manageable through [`--allow-worker`][] flag
977+
* WASI - manageable through [`--allow-wasi`][] flag
930978

931979
### `--experimental-require-module`
932980

@@ -2693,6 +2741,7 @@ one is included in the list below.
26932741
* `--allow-child-process`
26942742
* `--allow-fs-read`
26952743
* `--allow-fs-write`
2744+
* `--allow-wasi`
26962745
* `--allow-worker`
26972746
* `--conditions`, `-C`
26982747
* `--diagnostic-dir`
@@ -3241,6 +3290,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
32413290
[`--allow-child-process`]: #--allow-child-process
32423291
[`--allow-fs-read`]: #--allow-fs-read
32433292
[`--allow-fs-write`]: #--allow-fs-write
3293+
[`--allow-wasi`]: #--allow-wasi
32443294
[`--allow-worker`]: #--allow-worker
32453295
[`--build-snapshot`]: #--build-snapshot
32463296
[`--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
@@ -597,6 +598,7 @@ There are constraints you need to know before using this system:
597598
[`--allow-child-process`]: cli.md#--allow-child-process
598599
[`--allow-fs-read`]: cli.md#--allow-fs-read
599600
[`--allow-fs-write`]: cli.md#--allow-fs-write
601+
[`--allow-wasi`]: cli.md#--allow-wasi
600602
[`--allow-worker`]: cli.md#--allow-worker
601603
[`--experimental-permission`]: cli.md#--experimental-permission
602604
[`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
@@ -529,6 +529,7 @@ function initializePermission() {
529529
const warnFlags = [
530530
'--allow-addons',
531531
'--allow-child-process',
532+
'--allow-wasi',
532533
'--allow-worker',
533534
];
534535
for (const flag of warnFlags) {
@@ -570,6 +571,7 @@ function initializePermission() {
570571
'--allow-fs-write',
571572
'--allow-addons',
572573
'--allow-child-process',
574+
'--allow-wasi',
573575
'--allow-worker',
574576
];
575577
ArrayPrototypeForEach(availablePermissionFlags, (flag) => {

node.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@
153153
'src/permission/fs_permission.cc',
154154
'src/permission/inspector_permission.cc',
155155
'src/permission/permission.cc',
156+
'src/permission/wasi_permission.cc',
156157
'src/permission/worker_permission.cc',
157158
'src/pipe_wrap.cc',
158159
'src/process_wrap.cc',
@@ -277,6 +278,7 @@
277278
'src/permission/fs_permission.h',
278279
'src/permission/inspector_permission.h',
279280
'src/permission/permission.h',
281+
'src/permission/wasi_permission.h',
280282
'src/permission/worker_permission.h',
281283
'src/pipe_wrap.h',
282284
'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
@@ -456,6 +456,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
456456
"allow use of child process when any permissions are set",
457457
&EnvironmentOptions::allow_child_process,
458458
kAllowedInEnvvar);
459+
AddOption("--allow-wasi",
460+
"allow wasi when any permissions are set",
461+
&EnvironmentOptions::allow_wasi,
462+
kAllowedInEnvvar);
459463
AddOption("--allow-worker",
460464
"allow worker threads when any permissions are set",
461465
&EnvironmentOptions::allow_worker_threads,

src/node_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ class EnvironmentOptions : public Options {
130130
std::vector<std::string> allow_fs_write;
131131
bool allow_addons = false;
132132
bool allow_child_process = false;
133+
bool allow_wasi = false;
133134
bool allow_worker_threads = false;
134135
bool experimental_repl_await = true;
135136
bool experimental_vm_modules = false;

src/node_wasi.cc

Lines changed: 7 additions & 5 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 {
@@ -120,8 +121,9 @@ void WASI::New(const FunctionCallbackInfo<Value>& args) {
120121
CHECK(args[1]->IsArray());
121122
CHECK(args[2]->IsArray());
122123
CHECK(args[3]->IsArray());
123-
124124
Environment* env = Environment::GetCurrent(args);
125+
THROW_IF_INSUFFICIENT_PERMISSIONS(
126+
env, permission::PermissionScope::kWASI, "");
125127
Local<Context> context = env->context();
126128
Local<Array> argv = args[0].As<Array>();
127129
const uint32_t argc = argv->Length();

src/permission/permission.cc

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

103108
Local<Value> CreateAccessDeniedError(Environment* env,

0 commit comments

Comments
 (0)