Skip to content

Commit 0d185f7

Browse files
bmeckJakobJingleheimerjasnellljharbjsumners
authored andcommitted
esm: support https remotely and http locally under flag
Co-authored-by: Jacob Smith <[email protected]> Co-authored-by: James M Snell <[email protected]> Co-authored-by: Jordan Harband <[email protected]> Co-authored-by: James Sumners <[email protected]> PR-URL: #36328 Backport-PR-URL: #42726 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Geoffrey Booth <[email protected]>
1 parent 363028c commit 0d185f7

33 files changed

+995
-144
lines changed

doc/api/cli.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,16 @@ added: v9.0.0
291291
Specify the `module` of a custom experimental [ECMAScript module loader][].
292292
`module` may be any string accepted as an [`import` specifier][].
293293

294+
### `--experimental-network-imports`
295+
296+
<!-- YAML
297+
added: REPLACEME
298+
-->
299+
300+
> Stability: 1 - Experimental
301+
302+
Enable experimental support for the `https:` protocol in `import` specifiers.
303+
294304
### `--experimental-policy`
295305

296306
<!-- YAML
@@ -1534,6 +1544,7 @@ Node.js options that are allowed are:
15341544
* `--experimental-json-modules`
15351545
* `--experimental-loader`
15361546
* `--experimental-modules`
1547+
* `--experimental-network-imports`
15371548
* `--experimental-policy`
15381549
* `--experimental-specifier-resolution`
15391550
* `--experimental-top-level-await`

doc/api/errors.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3105,6 +3105,23 @@ removed: v10.0.0
31053105

31063106
Used by the `Node-API` when `Constructor.prototype` is not an object.
31073107

3108+
<a id="ERR_NETWORK_IMPORT_BAD_RESPONSE"></a>
3109+
3110+
### `ERR_NETWORK_IMPORT_BAD_RESPONSE`
3111+
3112+
> Stability: 1 - Experimental
3113+
3114+
Response was received but was invalid when importing a module over the network.
3115+
3116+
<a id="ERR_NETWORK_IMPORT_DISALLOWED"></a>
3117+
3118+
### `ERR_NETWORK_IMPORT_DISALLOWED`
3119+
3120+
> Stability: 1 - Experimental
3121+
3122+
A network module attempted to load another module that it is not allowed to
3123+
load. Likely this restriction is for security reasons.
3124+
31083125
<a id="ERR_NO_LONGER_SUPPORTED"></a>
31093126

31103127
### `ERR_NO_LONGER_SUPPORTED`

doc/api/esm.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,71 @@ spawn(execPath, [
596596
});
597597
```
598598
599+
## HTTPS and HTTP imports
600+
601+
> Stability: 1 - Experimental
602+
603+
Importing network based modules using `https:` and `http:` is supported under
604+
the `--experimental-network-imports` flag. This allows web browser-like imports
605+
to work in Node.js with a few differences due to application stability and
606+
security concerns that are different when running in a privileged environment
607+
instead of a browser sandbox.
608+
609+
### Imports are limited to HTTP/1
610+
611+
Automatic protocol negotiation for HTTP/2 and HTTP/3 is not yet supported.
612+
613+
### HTTP is limited to loopback addresses
614+
615+
`http:` is vulnerable to man-in-the-middle attacks and is not allowed to be
616+
used for addresses outside of the IPv4 address `127.0.0.0/8` (`127.0.0.1` to
617+
`127.255.255.255`) and the IPv6 address `::1`. Support for `http:` is intended
618+
to be used for local development.
619+
620+
### Authentication is never sent to the destination server.
621+
622+
`Authorization`, `Cookie`, and `Proxy-Authorization` headers are not sent to the
623+
server. Avoid including user info in parts of imported URLs. A security model
624+
for safely using these on the server is being worked on.
625+
626+
### CORS is never checked on the destination server
627+
628+
CORS is designed to allow a server to limit the consumers of an API to a
629+
specific set of hosts. This is not supported as it does not make sense for a
630+
server-based implementation.
631+
632+
### Cannot load non-network dependencies
633+
634+
These modules cannot access other modules that are not over `http:` or `https:`.
635+
To still access local modules while avoiding the security concern, pass in
636+
references to the local dependencies:
637+
638+
```mjs
639+
// file.mjs
640+
import worker_threads from 'worker_threads';
641+
import { configure, resize } from 'https://example.com/imagelib.mjs';
642+
configure({ worker_threads });
643+
```
644+
645+
```mjs
646+
// https://example.com/imagelib.mjs
647+
let worker_threads;
648+
export function configure(opts) {
649+
worker_threads = opts.worker_threads;
650+
}
651+
export function resize(img, size) {
652+
// Perform resizing in worker_thread to avoid main thread blocking
653+
}
654+
```
655+
656+
### Network-based loading is not enabled by default
657+
658+
For now, the `--experimental-network-imports` flag is required to enable loading
659+
resources over `http:` or `https:`. In the future, a different mechanism will be
660+
used to enforce this. Opt-in is required to prevent transitive dependencies
661+
inadvertently using potentially mutable state that could affect reliability
662+
of Node.js applications.
663+
599664
<i id="esm_experimental_loaders"></i>
600665
601666
## Loaders

doc/node.1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ Specify the
147147
.Ar module
148148
to use as a custom module loader.
149149
.
150+
.It Fl -experimental-network-imports
151+
Enable experimental support for loading modules using `import` over `https:`.
152+
.
150153
.It Fl -experimental-policy
151154
Use the specified file as a security policy.
152155
.

lib/internal/errors.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,6 +1414,10 @@ E('ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT',
14141414
'start offset of %s should be a multiple of %s', RangeError);
14151415
E('ERR_NAPI_INVALID_TYPEDARRAY_LENGTH',
14161416
'Invalid typed array length', RangeError);
1417+
E('ERR_NETWORK_IMPORT_BAD_RESPONSE',
1418+
"import '%s' received a bad response: %s", Error);
1419+
E('ERR_NETWORK_IMPORT_DISALLOWED',
1420+
"import of '%s' by %s is not supported: %s", Error);
14171421
E('ERR_NO_CRYPTO',
14181422
'Node.js is not compiled with OpenSSL crypto support', Error);
14191423
E('ERR_NO_ICU',
@@ -1575,12 +1579,13 @@ E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s', TypeError);
15751579
E('ERR_UNKNOWN_FILE_EXTENSION',
15761580
'Unknown file extension "%s" for %s',
15771581
TypeError);
1578-
E('ERR_UNKNOWN_MODULE_FORMAT', 'Unknown module format: %s', RangeError);
1582+
E('ERR_UNKNOWN_MODULE_FORMAT', 'Unknown module format: %s for URL %s',
1583+
RangeError);
15791584
E('ERR_UNKNOWN_SIGNAL', 'Unknown signal: %s', TypeError);
15801585
E('ERR_UNSUPPORTED_DIR_IMPORT', "Directory import '%s' is not supported " +
15811586
'resolving ES modules imported from %s', Error);
1582-
E('ERR_UNSUPPORTED_ESM_URL_SCHEME', (url) => {
1583-
let msg = 'Only file and data URLs are supported by the default ESM loader';
1587+
E('ERR_UNSUPPORTED_ESM_URL_SCHEME', (url, supported) => {
1588+
let msg = `Only URLs with a scheme in: ${ArrayPrototypeJoin(supported, ', ')} are supported by the default ESM loader`;
15841589
if (isWindows && url.protocol.length === 2) {
15851590
msg +=
15861591
'. On Windows, absolute paths must be valid file:// URLs';

lib/internal/main/check_syntax.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,16 @@ if (process.argv[1] && process.argv[1] !== '-') {
4545
});
4646
}
4747

48-
function checkSyntax(source, filename) {
48+
async function checkSyntax(source, filename) {
4949
const { getOptionValue } = require('internal/options');
5050
let isModule = false;
5151
if (filename === '[stdin]' || filename === '[eval]') {
5252
isModule = getOptionValue('--input-type') === 'module';
5353
} else {
5454
const { defaultResolve } = require('internal/modules/esm/resolve');
5555
const { defaultGetFormat } = require('internal/modules/esm/get_format');
56-
const { url } = defaultResolve(pathToFileURL(filename).toString());
57-
const format = defaultGetFormat(url);
56+
const { url } = await defaultResolve(pathToFileURL(filename).toString());
57+
const format = await defaultGetFormat(url);
5858
isModule = format === 'module';
5959
}
6060
if (isModule) {

0 commit comments

Comments
 (0)