@bytecodealliance/preview2-shim 0.17.6 → 0.17.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -51,6 +51,54 @@ const component = await instantiate(loader, new WASIShim().getImportObject());
51
51
  // TODO: Code that uses your component's exports goes here.
52
52
  ```
53
53
 
54
+ ## Sandboxing
55
+
56
+ By default, the preview2-shim provides full access to the host filesystem, environment variables,
57
+ and network - matching the default behavior of Node.js libraries. However, you can configure
58
+ sandboxing to restrict what guests can access.
59
+
60
+ ### Using WASIShim for sandboxing
61
+
62
+ The `WASIShim` class accepts a `sandbox` configuration option to control access:
63
+
64
+ ```js
65
+ import { WASIShim } from '@bytecodealliance/preview2-shim/instantiation';
66
+
67
+ // Fully sandboxed - no filesystem, network, or env access
68
+ const sandboxedShim = new WASIShim({
69
+ sandbox: {
70
+ preopens: {}, // No filesystem access
71
+ env: {}, // No environment variables
72
+ args: ['arg1'], // Custom arguments
73
+ enableNetwork: false, // Disable network access
74
+ }
75
+ });
76
+
77
+ // Limited filesystem access - map virtual paths to host paths
78
+ const limitedShim = new WASIShim({
79
+ sandbox: {
80
+ preopens: {
81
+ '/data': '/tmp/guest-data', // Guest sees /data, maps to /tmp/guest-data
82
+ '/config': '/etc/app' // Guest sees /config, maps to /etc/app
83
+ },
84
+ env: { 'ENV1': '42' }, // Only expose specific env vars
85
+ }
86
+ });
87
+
88
+ const component = await instantiate(loader, sandboxedShim.getImportObject());
89
+ ```
90
+
91
+ ### Notes on sandboxing
92
+
93
+ - By default (when no options are passed), the shim is providing full access to match typical
94
+ Node.js library behavior.
95
+ - Each `WASIShim` instance has its own isolated preopens, environment variables, and arguments.
96
+ Multiple instances with different configurations will not affect each other.
97
+ - The direct preopen functions (`_setPreopens`, `_clearPreopens`, etc.) modify global state and
98
+ affect all components not using `WASIShim` with explicit configuration. For isolation, prefer
99
+ using `WASIShim` with the `sandbox` option containing `preopens` and `env`.
100
+ - When `sandbox.enableNetwork: false`, all socket and HTTP operations will throw "access-denied" errors.
101
+
54
102
  [jco]: https://www.npmjs.com/package/@bytecodealliance/jco
55
103
 
56
104
  # License
@@ -1,3 +1,6 @@
1
+ import { poll } from './io.js';
2
+ const { Pollable } = poll;
3
+
1
4
  export const monotonicClock = {
2
5
  resolution() {
3
6
  // usually we dont get sub-millisecond accuracy in the browser
@@ -10,15 +13,16 @@ export const monotonicClock = {
10
13
  },
11
14
  subscribeInstant(instant) {
12
15
  instant = BigInt(instant);
13
- const now = this.now();
16
+ const now = monotonicClock.now();
14
17
  if (instant <= now) {
15
- return this.subscribeDuration(0);
18
+ return new Pollable(new Promise(resolve => setTimeout(resolve, 0)));
16
19
  }
17
- return this.subscribeDuration(instant - now);
20
+ return monotonicClock.subscribeDuration(instant - now);
18
21
  },
19
- subscribeDuration(_duration) {
20
- _duration = BigInt(_duration);
21
- console.log(`[monotonic-clock] subscribe`);
22
+ subscribeDuration(duration) {
23
+ duration = BigInt(duration);
24
+ const ms = duration <= 0n ? 0 : Number(duration / 1_000_000n);
25
+ return new Pollable(new Promise(resolve => setTimeout(resolve, ms)));
22
26
  },
23
27
  };
24
28
 
@@ -6,6 +6,25 @@ export { _setCwd } from './config.js';
6
6
 
7
7
  const { InputStream, OutputStream } = streams;
8
8
 
9
+ /**
10
+ * @typedef {Object} FileDataEntry
11
+ * @property {Record<string, FileDataEntry>} [dir] - Directory contents (present for directories)
12
+ * @property {Uint8Array|string} [source] - File contents (present for files)
13
+ */
14
+
15
+ /**
16
+ * @typedef {FileDataEntry} FileData
17
+ * Root file data structure representing a filesystem tree.
18
+ * Each entry is either a directory (has `dir` property) or a file (has `source` property).
19
+ * @example
20
+ * // A simple filesystem with one directory containing one file:
21
+ * const fileData = {
22
+ * dir: {
23
+ * 'myfile.txt': { source: new Uint8Array([72, 101, 108, 108, 111]) }
24
+ * }
25
+ * };
26
+ */
27
+
9
28
  export function _setFileData(fileData) {
10
29
  _fileData = fileData;
11
30
  _rootPreopen[0] = new Descriptor(fileData);
@@ -322,6 +341,59 @@ export const preopens = {
322
341
  },
323
342
  };
324
343
 
344
+ /**
345
+ * Replace all preopens with the given set.
346
+ * @param {Record<string, FileData>} preopensConfig - Map of virtual paths to file data entries
347
+ */
348
+ export function _setPreopens(preopensConfig) {
349
+ _preopens = [];
350
+ for (const [virtualPath, fileData] of Object.entries(preopensConfig)) {
351
+ _addPreopen(virtualPath, fileData);
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Add a single preopen mapping.
357
+ * @param {string} virtualPath - The virtual path visible to the guest
358
+ * @param {FileData} fileData - The file data object representing the directory
359
+ */
360
+ export function _addPreopen(virtualPath, fileData) {
361
+ const descriptor = new Descriptor(fileData);
362
+ _preopens.push([descriptor, virtualPath]);
363
+ if (virtualPath === '/') {
364
+ _rootPreopen = [descriptor, virtualPath];
365
+ }
366
+ }
367
+
368
+ /**
369
+ * Clear all preopens, giving the guest no filesystem access.
370
+ *
371
+ * This functionality exists mostly to maintain backwards compatibility. Prefer setting preopens
372
+ * via `WASIShim` rather than making top level changes to preopens using these functions.
373
+ */
374
+ export function _clearPreopens() {
375
+ _preopens = [];
376
+ _rootPreopen = null;
377
+ }
378
+
379
+ /**
380
+ * Get current preopens configuration.
381
+ * @returns {Array<[Descriptor, string]>} Array of [descriptor, virtualPath] pairs
382
+ */
383
+ export function _getPreopens() {
384
+ return [..._preopens];
385
+ }
386
+
387
+ /**
388
+ * Create a preopen descriptor for file data.
389
+ * This is used internally to create isolated preopen instances.
390
+ * @param {FileData} fileData - The file data object representing the directory
391
+ * @returns {Descriptor} A preopen descriptor
392
+ */
393
+ export function _createPreopenDescriptor(fileData) {
394
+ return new Descriptor(fileData);
395
+ }
396
+
325
397
  export const types = {
326
398
  Descriptor,
327
399
  DirectoryEntryStream,