Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions doc/api/esm.md
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,74 @@ const dynamicLibrary = await import.source('./library.wasm');
const instance = await WebAssembly.instantiate(dynamicLibrary, importObject);
```

### JavaScript String Builtins

<!-- YAML
added: REPLACEME
-->

When importing WebAssembly modules, the
[WebAssembly JS String Builtins Proposal][] is automatically enabled through the
ESM Integration. This allows WebAssembly modules to directly use efficient
compile-time string builtins from the `wasm:js-string` namespace.

For example, the following Wasm module exports a string `getLength` function using
the `wasm:js-string` `length` builtin:

```text
(module
;; Compile-time import of the string length builtin.
(import "wasm:js-string" "length" (func $string_length (param externref) (result i32)))

;; Define getLength, taking a JS value parameter assumed to be a string,
;; calling string length on it and returning the result.
(func $getLength (param $str externref) (result i32)
local.get $str
call $string_length
)

;; Export the getLength function.
(export "getLength" (func $get_length))
)
```

```js
import { getLength } from './string-len.wasm';
getLength('foo'); // Returns 3.
```

Wasm builtins are compile-time imports that are linked during module compilation
rather than during instantiation. They do not behave like normal module graph
imports and they cannot be inspected via `WebAssembly.Module.imports(mod)`
or virtualized unless recompiling the module using the direct
`WebAssembly.compile` API with string builtins disabled.

Importing a module in the source phase before it has been instantiated will also
use the compile-time builtins automatically:

```js
import source mod from './string-len.wasm';
const { exports: { getLength } } = await WebAssembly.instantiate(mod, {});
getLength('foo'); // Also returns 3.
```

### Reserved Wasm Namespaces

<!-- YAML
added: REPLACEME
-->

When importing WebAssembly modules through the ESM Integration, they cannot use
import module names or import/export names that start with reserved prefixes:

* `wasm-js:` - reserved in all module import names, module names and export
names.
* `wasm:` - reserved in module import names and export names (imported module
names are allowed in order to support future builtin polyfills).

Importing a module using the above reserved names will throw a
`WebAssembly.LinkError`.

<i id="esm_experimental_top_level_await"></i>

## Top-level `await`
Expand Down Expand Up @@ -1206,6 +1274,7 @@ resolution for ESM specifiers is [commonjs-extension-resolution-loader][].
[Source Phase Imports]: https://github.com/tc39/proposal-source-phase-imports
[Terminology]: #terminology
[URL]: https://url.spec.whatwg.org/
[WebAssembly JS String Builtins Proposal]: https://github.com/WebAssembly/js-string-builtins
[`"exports"`]: packages.md#exports
[`"type"`]: packages.md#type
[`--input-type`]: cli.md#--input-typetype
Expand Down
48 changes: 48 additions & 0 deletions doc/api/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -2260,6 +2260,54 @@ openssl pkcs12 -certpbe AES-256-CBC -export -out client-cert.pem \
The server can be tested by connecting to it using the example client from
[`tls.connect()`][].

## `tls.setDefaultCACertificates(certs)`

<!-- YAML
added: REPLACEME
-->

* `certs` {string\[]|ArrayBufferView\[]} An array of CA certificates in PEM format.

Sets the default CA certificates used by Node.js TLS clients. If the provided
certificates are parsed successfully, they will become the default CA
certificate list returned by [`tls.getCACertificates()`][] and used
by subsequent TLS connections that don't specify their own CA certificates.
The certificates will be deduplicated before being set as the default.

This function only affects the current Node.js thread. Previous
sessions cached by the HTTPS agent won't be affected by this change, so
this method should be called before any unwanted cachable TLS connections are
made.

To use system CA certificates as the default:

```cjs
const tls = require('node:tls');
tls.setDefaultCACertificates(tls.getCACertificates('system'));
```

```mjs
import tls from 'node:tls';
tls.setDefaultCACertificates(tls.getCACertificates('system'));
```

This function completely replaces the default CA certificate list. To add additional
certificates to the existing defaults, get the current certificates and append to them:

```cjs
const tls = require('node:tls');
const currentCerts = tls.getCACertificates('default');
const additionalCerts = ['-----BEGIN CERTIFICATE-----\n...'];
tls.setDefaultCACertificates([...currentCerts, ...additionalCerts]);
```

```mjs
import tls from 'node:tls';
const currentCerts = tls.getCACertificates('default');
const additionalCerts = ['-----BEGIN CERTIFICATE-----\n...'];
tls.setDefaultCACertificates([...currentCerts, ...additionalCerts]);
```

## `tls.getCACertificates([type])`

<!-- YAML
Expand Down
15 changes: 14 additions & 1 deletion lib/internal/modules/esm/translators.js
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,10 @@ translators.set('wasm', async function(url, source) {
// TODO(joyeecheung): implement a translator that just uses
// compiled = new WebAssembly.Module(source) to compile it
// synchronously.
compiled = await WebAssembly.compile(source);
compiled = await WebAssembly.compile(source, {
// The ESM Integration auto-enables Wasm JS builtins by default when available.
builtins: ['js-string'],
});
} catch (err) {
err.message = errPath(url) + ': ' + err.message;
throw err;
Expand All @@ -518,6 +521,13 @@ translators.set('wasm', async function(url, source) {
if (impt.kind === 'global') {
ArrayPrototypePush(wasmGlobalImports, impt);
}
// Prefix reservations per https://webassembly.github.io/esm-integration/js-api/index.html#parse-a-webassembly-module.
if (impt.module.startsWith('wasm-js:')) {
throw new WebAssembly.LinkError(`Invalid Wasm import "${impt.module}" in ${url}`);
}
if (impt.name.startsWith('wasm:') || impt.name.startsWith('wasm-js:')) {
throw new WebAssembly.LinkError(`Invalid Wasm import name "${impt.module}" in ${url}`);
}
importsList.add(impt.module);
}

Expand All @@ -527,6 +537,9 @@ translators.set('wasm', async function(url, source) {
if (expt.kind === 'global') {
wasmGlobalExports.add(expt.name);
}
if (expt.name.startsWith('wasm:') || expt.name.startsWith('wasm-js:')) {
throw new WebAssembly.LinkError(`Invalid Wasm export name "${expt.name}" in ${url}`);
}
exportsList.add(expt.name);
}

Expand Down
32 changes: 32 additions & 0 deletions lib/tls.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const {
ERR_TLS_CERT_ALTNAME_INVALID,
ERR_OUT_OF_RANGE,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_ARG_TYPE,
} = require('internal/errors').codes;
const internalUtil = require('internal/util');
internalUtil.assertCrypto();
Expand All @@ -51,6 +52,8 @@ const {
getBundledRootCertificates,
getExtraCACertificates,
getSystemCACertificates,
resetRootCertStore,
getUserRootCertificates,
getSSLCiphers,
} = internalBinding('crypto');
const { Buffer } = require('buffer');
Expand Down Expand Up @@ -122,8 +125,17 @@ function cacheSystemCACertificates() {
}

let defaultCACertificates;
let hasResetDefaultCACertificates = false;

function cacheDefaultCACertificates() {
if (defaultCACertificates) { return defaultCACertificates; }

if (hasResetDefaultCACertificates) {
defaultCACertificates = getUserRootCertificates();
ObjectFreeze(defaultCACertificates);
return defaultCACertificates;
}

defaultCACertificates = [];

if (!getOptionValue('--use-openssl-ca')) {
Expand Down Expand Up @@ -171,6 +183,26 @@ function getCACertificates(type = 'default') {
}
exports.getCACertificates = getCACertificates;

function setDefaultCACertificates(certs) {
if (!ArrayIsArray(certs)) {
throw new ERR_INVALID_ARG_TYPE('certs', 'Array', certs);
}

// Verify that all elements in the array are strings
for (let i = 0; i < certs.length; i++) {
if (typeof certs[i] !== 'string' && !isArrayBufferView(certs[i])) {
throw new ERR_INVALID_ARG_TYPE(
`certs[${i}]`, ['string', 'ArrayBufferView'], certs[i]);
}
}

resetRootCertStore(certs);
defaultCACertificates = undefined; // Reset the cached default certificates
hasResetDefaultCACertificates = true;
}

exports.setDefaultCACertificates = setDefaultCACertificates;

// Convert protocols array into valid OpenSSL protocols list
// ("\x06spdy/2\x08http/1.1\x08http/1.0")
function convertProtocols(protocols) {
Expand Down
Loading
Loading