Node.js v23.0.0-nightly2024100532efeea0c0 documentation
- Node.js v23.0.0-nightly2024100532efeea0c0
- Table of contents
-
Index
- Assertion testing
- Asynchronous context tracking
- Async hooks
- Buffer
- C++ addons
- C/C++ addons with Node-API
- C++ embedder API
- Child processes
- Cluster
- Command-line options
- Console
- Corepack
- Crypto
- Debugger
- Deprecated APIs
- Diagnostics Channel
- DNS
- Domain
- Errors
- Events
- File system
- Globals
- HTTP
- HTTP/2
- HTTPS
- Inspector
- Internationalization
- Modules: CommonJS modules
- Modules: ECMAScript modules
- Modules:
node:module
API - Modules: Packages
- Modules: TypeScript
- Net
- OS
- Path
- Performance hooks
- Permissions
- Process
- Punycode
- Query strings
- Readline
- REPL
- Report
- Single executable applications
- SQLite
- Stream
- String decoder
- Test runner
- Timers
- TLS/SSL
- Trace events
- TTY
- UDP/datagram
- URL
- Utilities
- V8
- VM
- WASI
- Web Crypto API
- Web Streams API
- Worker threads
- Zlib
- Other versions
- Options
WebAssembly System Interface (WASI)#
The node:wasi
module does not currently provide the
comprehensive file system security properties provided by some WASI runtimes.
Full support for secure file system sandboxing may or may not be implemented in
future. In the mean time, do not rely on it to run untrusted code.
Source Code: lib/wasi.js
The WASI API provides an implementation of the WebAssembly System Interface specification. WASI gives WebAssembly applications access to the underlying operating system via a collection of POSIX-like functions.
import { readFile } from 'node:fs/promises';
import { WASI } from 'node:wasi';
import { argv, env } from 'node:process';
const wasi = new WASI({
version: 'preview1',
args: argv,
env,
preopens: {
'/local': '/some/real/path/that/wasm/can/access',
},
});
const wasm = await WebAssembly.compile(
await readFile(new URL('./demo.wasm', import.meta.url)),
);
const instance = await WebAssembly.instantiate(wasm, wasi.getImportObject());
wasi.start(instance);
'use strict';
const { readFile } = require('node:fs/promises');
const { WASI } = require('node:wasi');
const { argv, env } = require('node:process');
const { join } = require('node:path');
const wasi = new WASI({
version: 'preview1',
args: argv,
env,
preopens: {
'/local': '/some/real/path/that/wasm/can/access',
},
});
(async () => {
const wasm = await WebAssembly.compile(
await readFile(join(__dirname, 'demo.wasm')),
);
const instance = await WebAssembly.instantiate(wasm, wasi.getImportObject());
wasi.start(instance);
})();
To run the above example, create a new WebAssembly text format file named
demo.wat
:
(module
;; Import the required fd_write WASI function which will write the given io vectors to stdout
;; The function signature for fd_write is:
;; (File Descriptor, *iovs, iovs_len, nwritten) -> Returns number of bytes written
(import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)))
(memory 1)
(export "memory" (memory 0))
;; Write 'hello world\n' to memory at an offset of 8 bytes
;; Note the trailing newline which is required for the text to appear
(data (i32.const 8) "hello world\n")
(func $main (export "_start")
;; Creating a new io vector within linear memory
(i32.store (i32.const 0) (i32.const 8)) ;; iov.iov_base - This is a pointer to the start of the 'hello world\n' string
(i32.store (i32.const 4) (i32.const 12)) ;; iov.iov_len - The length of the 'hello world\n' string
(call $fd_write
(i32.const 1) ;; file_descriptor - 1 for stdout
(i32.const 0) ;; *iovs - The pointer to the iov array, which is stored at memory location 0
(i32.const 1) ;; iovs_len - We're printing 1 string stored in an iov - so one.
(i32.const 20) ;; nwritten - A place in memory to store the number of bytes written
)
drop ;; Discard the number of bytes written from the top of the stack
)
)
Use wabt to compile .wat
to .wasm
wat2wasm demo.wat
Security#
WASI provides a capabilities-based model through which applications are provided
their own custom env
, preopens
, stdin
, stdout
, stderr
, and exit
capabilities.
The current Node.js threat model does not provide secure sandboxing as is present in some WASI runtimes.
While the capability features are supported, they do not form a security model in Node.js. For example, the file system sandboxing can be escaped with various techniques. The project is exploring whether these security guarantees could be added in future.
Class: WASI
#
The WASI
class provides the WASI system call API and additional convenience
methods for working with WASI-based applications. Each WASI
instance
represents a distinct environment.
new WASI([options])
#
options
<Object>args
<Array> An array of strings that the WebAssembly application will see as command-line arguments. The first argument is the virtual path to the WASI command itself. Default:[]
.env
<Object> An object similar toprocess.env
that the WebAssembly application will see as its environment. Default:{}
.preopens
<Object> This object represents the WebAssembly application's local directory structure. The string keys ofpreopens
are treated as directories within the file system. The corresponding values inpreopens
are the real paths to those directories on the host machine.returnOnExit
<boolean> By default, when WASI applications call__wasi_proc_exit()
wasi.start()
will return with the exit code specified rather than terminating the process. Setting this option tofalse
will cause the Node.js process to exit with the specified exit code instead. Default:true
.stdin
<integer> The file descriptor used as standard input in the WebAssembly application. Default:0
.stdout
<integer> The file descriptor used as standard output in the WebAssembly application. Default:1
.stderr
<integer> The file descriptor used as standard error in the WebAssembly application. Default:2
.version
<string> The version of WASI requested. Currently the only supported versions areunstable
andpreview1
. This option is mandatory.
wasi.getImportObject()
#
Return an import object that can be passed to WebAssembly.instantiate()
if
no other WASM imports are needed beyond those provided by WASI.
If version unstable
was passed into the constructor it will return:
{ wasi_unstable: wasi.wasiImport }
If version preview1
was passed into the constructor or no version was
specified it will return:
{ wasi_snapshot_preview1: wasi.wasiImport }
wasi.start(instance)
#
instance
<WebAssembly.Instance>
Attempt to begin execution of instance
as a WASI command by invoking its
_start()
export. If instance
does not contain a _start()
export, or if
instance
contains an _initialize()
export, then an exception is thrown.
start()
requires that instance
exports a WebAssembly.Memory
named
memory
. If instance
does not have a memory
export an exception is thrown.
If start()
is called more than once, an exception is thrown.
wasi.initialize(instance)
#
instance
<WebAssembly.Instance>
Attempt to initialize instance
as a WASI reactor by invoking its
_initialize()
export, if it is present. If instance
contains a _start()
export, then an exception is thrown.
initialize()
requires that instance
exports a WebAssembly.Memory
named
memory
. If instance
does not have a memory
export an exception is thrown.
If initialize()
is called more than once, an exception is thrown.
wasi.wasiImport
#
wasiImport
is an object that implements the WASI system call API. This object
should be passed as the wasi_snapshot_preview1
import during the instantiation
of a WebAssembly.Instance
.