lib,src: drop --experimental-network-imports
PR-URL: https://github.com/nodejs/node/pull/53822 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
This commit is contained in:
parent
e4263dad96
commit
5ac969fdca
@ -965,18 +965,6 @@ changes:
|
|||||||
Specify the `module` containing exported [module customization hooks][].
|
Specify the `module` containing exported [module customization hooks][].
|
||||||
`module` may be any string accepted as an [`import` specifier][].
|
`module` may be any string accepted as an [`import` specifier][].
|
||||||
|
|
||||||
### `--experimental-network-imports`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added:
|
|
||||||
- v17.6.0
|
|
||||||
- v16.15.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
> Stability: 1 - Experimental
|
|
||||||
|
|
||||||
Enable experimental support for the `https:` protocol in `import` specifiers.
|
|
||||||
|
|
||||||
### `--experimental-network-inspection`
|
### `--experimental-network-inspection`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
@ -2912,7 +2900,6 @@ one is included in the list below.
|
|||||||
* `--experimental-json-modules`
|
* `--experimental-json-modules`
|
||||||
* `--experimental-loader`
|
* `--experimental-loader`
|
||||||
* `--experimental-modules`
|
* `--experimental-modules`
|
||||||
* `--experimental-network-imports`
|
|
||||||
* `--experimental-permission`
|
* `--experimental-permission`
|
||||||
* `--experimental-print-required-tla`
|
* `--experimental-print-required-tla`
|
||||||
* `--experimental-require-module`
|
* `--experimental-require-module`
|
||||||
|
@ -3583,23 +3583,6 @@ removed: v10.0.0
|
|||||||
|
|
||||||
Used by the `Node-API` when `Constructor.prototype` is not an object.
|
Used by the `Node-API` when `Constructor.prototype` is not an object.
|
||||||
|
|
||||||
<a id="ERR_NETWORK_IMPORT_BAD_RESPONSE"></a>
|
|
||||||
|
|
||||||
### `ERR_NETWORK_IMPORT_BAD_RESPONSE`
|
|
||||||
|
|
||||||
> Stability: 1 - Experimental
|
|
||||||
|
|
||||||
Response was received but was invalid when importing a module over the network.
|
|
||||||
|
|
||||||
<a id="ERR_NETWORK_IMPORT_DISALLOWED"></a>
|
|
||||||
|
|
||||||
### `ERR_NETWORK_IMPORT_DISALLOWED`
|
|
||||||
|
|
||||||
> Stability: 1 - Experimental
|
|
||||||
|
|
||||||
A network module attempted to load another module that it is not allowed to
|
|
||||||
load. Likely this restriction is for security reasons.
|
|
||||||
|
|
||||||
<a id="ERR_NO_LONGER_SUPPORTED"></a>
|
<a id="ERR_NO_LONGER_SUPPORTED"></a>
|
||||||
|
|
||||||
### `ERR_NO_LONGER_SUPPORTED`
|
### `ERR_NO_LONGER_SUPPORTED`
|
||||||
|
@ -697,71 +697,6 @@ spawn(execPath, [
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## HTTPS and HTTP imports
|
|
||||||
|
|
||||||
> Stability: 1 - Experimental
|
|
||||||
|
|
||||||
Importing network based modules using `https:` and `http:` is supported under
|
|
||||||
the `--experimental-network-imports` flag. This allows web browser-like imports
|
|
||||||
to work in Node.js with a few differences due to application stability and
|
|
||||||
security concerns that are different when running in a privileged environment
|
|
||||||
instead of a browser sandbox.
|
|
||||||
|
|
||||||
### Imports are limited to HTTP/1
|
|
||||||
|
|
||||||
Automatic protocol negotiation for HTTP/2 and HTTP/3 is not yet supported.
|
|
||||||
|
|
||||||
### HTTP is limited to loopback addresses
|
|
||||||
|
|
||||||
`http:` is vulnerable to man-in-the-middle attacks and is not allowed to be
|
|
||||||
used for addresses outside of the IPv4 address `127.0.0.0/8` (`127.0.0.1` to
|
|
||||||
`127.255.255.255`) and the IPv6 address `::1`. Support for `http:` is intended
|
|
||||||
to be used for local development.
|
|
||||||
|
|
||||||
### Authentication is never sent to the destination server.
|
|
||||||
|
|
||||||
`Authorization`, `Cookie`, and `Proxy-Authorization` headers are not sent to the
|
|
||||||
server. Avoid including user info in parts of imported URLs. A security model
|
|
||||||
for safely using these on the server is being worked on.
|
|
||||||
|
|
||||||
### CORS is never checked on the destination server
|
|
||||||
|
|
||||||
CORS is designed to allow a server to limit the consumers of an API to a
|
|
||||||
specific set of hosts. This is not supported as it does not make sense for a
|
|
||||||
server-based implementation.
|
|
||||||
|
|
||||||
### Cannot load non-network dependencies
|
|
||||||
|
|
||||||
These modules cannot access other modules that are not over `http:` or `https:`.
|
|
||||||
To still access local modules while avoiding the security concern, pass in
|
|
||||||
references to the local dependencies:
|
|
||||||
|
|
||||||
```mjs
|
|
||||||
// file.mjs
|
|
||||||
import worker_threads from 'node:worker_threads';
|
|
||||||
import { configure, resize } from 'https://example.com/imagelib.mjs';
|
|
||||||
configure({ worker_threads });
|
|
||||||
```
|
|
||||||
|
|
||||||
```mjs
|
|
||||||
// https://example.com/imagelib.mjs
|
|
||||||
let worker_threads;
|
|
||||||
export function configure(opts) {
|
|
||||||
worker_threads = opts.worker_threads;
|
|
||||||
}
|
|
||||||
export function resize(img, size) {
|
|
||||||
// Perform resizing in worker_thread to avoid main thread blocking
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Network-based loading is not enabled by default
|
|
||||||
|
|
||||||
For now, the `--experimental-network-imports` flag is required to enable loading
|
|
||||||
resources over `http:` or `https:`. In the future, a different mechanism will be
|
|
||||||
used to enforce this. Opt-in is required to prevent transitive dependencies
|
|
||||||
inadvertently using potentially mutable state that could affect reliability
|
|
||||||
of Node.js applications.
|
|
||||||
|
|
||||||
<i id="esm_experimental_loaders"></i>
|
<i id="esm_experimental_loaders"></i>
|
||||||
|
|
||||||
## Loaders
|
## Loaders
|
||||||
@ -804,8 +739,7 @@ does not determine whether the resolved URL protocol can be loaded,
|
|||||||
or whether the file extensions are permitted, instead these validations
|
or whether the file extensions are permitted, instead these validations
|
||||||
are applied by Node.js during the load phase
|
are applied by Node.js during the load phase
|
||||||
(for example, if it was asked to load a URL that has a protocol that is
|
(for example, if it was asked to load a URL that has a protocol that is
|
||||||
not `file:`, `data:`, `node:`, or if `--experimental-network-imports`
|
not `file:`, `data:` or `node:`.
|
||||||
is enabled, `https:`).
|
|
||||||
|
|
||||||
The algorithm also tries to determine the format of the file based
|
The algorithm also tries to determine the format of the file based
|
||||||
on the extension (see `ESM_FILE_FORMAT` algorithm below). If it does
|
on the extension (see `ESM_FILE_FORMAT` algorithm below). If it does
|
||||||
|
@ -717,7 +717,7 @@ behaviors.
|
|||||||
#### Import from HTTPS
|
#### Import from HTTPS
|
||||||
|
|
||||||
In current Node.js, specifiers starting with `https://` are experimental (see
|
In current Node.js, specifiers starting with `https://` are experimental (see
|
||||||
[HTTPS and HTTP imports][]).
|
\[HTTPS and HTTP imports]\[]).
|
||||||
|
|
||||||
The hook below registers hooks to enable rudimentary support for such
|
The hook below registers hooks to enable rudimentary support for such
|
||||||
specifiers. While this may seem like a significant improvement to Node.js core
|
specifiers. While this may seem like a significant improvement to Node.js core
|
||||||
@ -1054,7 +1054,6 @@ returned object contains the following keys:
|
|||||||
[Conditional exports]: packages.md#conditional-exports
|
[Conditional exports]: packages.md#conditional-exports
|
||||||
[Customization hooks]: #customization-hooks
|
[Customization hooks]: #customization-hooks
|
||||||
[ES Modules]: esm.md
|
[ES Modules]: esm.md
|
||||||
[HTTPS and HTTP imports]: esm.md#https-and-http-imports
|
|
||||||
[Source map v3 format]: https://sourcemaps.info/spec.html#h.mofvlxcwqzej
|
[Source map v3 format]: https://sourcemaps.info/spec.html#h.mofvlxcwqzej
|
||||||
[`"exports"`]: packages.md#exports
|
[`"exports"`]: packages.md#exports
|
||||||
[`--enable-source-maps`]: cli.md#--enable-source-maps
|
[`--enable-source-maps`]: cli.md#--enable-source-maps
|
||||||
|
@ -173,9 +173,6 @@ Specify the
|
|||||||
.Ar module
|
.Ar module
|
||||||
to use as a custom module loader.
|
to use as a custom module loader.
|
||||||
.
|
.
|
||||||
.It Fl -experimental-network-imports
|
|
||||||
Enable experimental support for loading modules using `import` over `https:`.
|
|
||||||
.
|
|
||||||
.It Fl -experimental-permission
|
.It Fl -experimental-permission
|
||||||
Enable the experimental permission model.
|
Enable the experimental permission model.
|
||||||
.
|
.
|
||||||
|
@ -1591,10 +1591,6 @@ E('ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT',
|
|||||||
'start offset of %s should be a multiple of %s', RangeError);
|
'start offset of %s should be a multiple of %s', RangeError);
|
||||||
E('ERR_NAPI_INVALID_TYPEDARRAY_LENGTH',
|
E('ERR_NAPI_INVALID_TYPEDARRAY_LENGTH',
|
||||||
'Invalid typed array length', RangeError);
|
'Invalid typed array length', RangeError);
|
||||||
E('ERR_NETWORK_IMPORT_BAD_RESPONSE',
|
|
||||||
"import '%s' received a bad response: %s", Error);
|
|
||||||
E('ERR_NETWORK_IMPORT_DISALLOWED',
|
|
||||||
"import of '%s' by %s is not supported: %s", Error);
|
|
||||||
E('ERR_NOT_BUILDING_SNAPSHOT',
|
E('ERR_NOT_BUILDING_SNAPSHOT',
|
||||||
'Operation cannot be invoked when not building startup snapshot', Error);
|
'Operation cannot be invoked when not building startup snapshot', Error);
|
||||||
E('ERR_NOT_IN_SINGLE_EXECUTABLE_APPLICATION',
|
E('ERR_NOT_IN_SINGLE_EXECUTABLE_APPLICATION',
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
ObjectPrototypeHasOwnProperty,
|
ObjectPrototypeHasOwnProperty,
|
||||||
PromisePrototypeThen,
|
|
||||||
PromiseResolve,
|
|
||||||
RegExpPrototypeExec,
|
RegExpPrototypeExec,
|
||||||
SafeSet,
|
SafeSet,
|
||||||
StringPrototypeCharCodeAt,
|
StringPrototypeCharCodeAt,
|
||||||
@ -18,8 +16,6 @@ const {
|
|||||||
} = require('internal/modules/esm/formats');
|
} = require('internal/modules/esm/formats');
|
||||||
|
|
||||||
const detectModule = getOptionValue('--experimental-detect-module');
|
const detectModule = getOptionValue('--experimental-detect-module');
|
||||||
const experimentalNetworkImports =
|
|
||||||
getOptionValue('--experimental-network-imports');
|
|
||||||
const { containsModuleSyntax } = internalBinding('contextify');
|
const { containsModuleSyntax } = internalBinding('contextify');
|
||||||
const { getPackageScopeConfig, getPackageType } = require('internal/modules/package_json_reader');
|
const { getPackageScopeConfig, getPackageType } = require('internal/modules/package_json_reader');
|
||||||
const { fileURLToPath } = require('internal/url');
|
const { fileURLToPath } = require('internal/url');
|
||||||
@ -29,8 +25,6 @@ const protocolHandlers = {
|
|||||||
'__proto__': null,
|
'__proto__': null,
|
||||||
'data:': getDataProtocolModuleFormat,
|
'data:': getDataProtocolModuleFormat,
|
||||||
'file:': getFileProtocolModuleFormat,
|
'file:': getFileProtocolModuleFormat,
|
||||||
'http:': getHttpProtocolModuleFormat,
|
|
||||||
'https:': getHttpProtocolModuleFormat,
|
|
||||||
'node:'() { return 'builtin'; },
|
'node:'() { return 'builtin'; },
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -219,23 +213,6 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE
|
|||||||
throw new ERR_UNKNOWN_FILE_EXTENSION(ext, filepath);
|
throw new ERR_UNKNOWN_FILE_EXTENSION(ext, filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {URL} url
|
|
||||||
* @param {{parentURL: string}} context
|
|
||||||
* @returns {Promise<string> | undefined} only works when enabled
|
|
||||||
*/
|
|
||||||
function getHttpProtocolModuleFormat(url, context) {
|
|
||||||
if (experimentalNetworkImports) {
|
|
||||||
const { fetchModule } = require('internal/modules/esm/fetch_module');
|
|
||||||
return PromisePrototypeThen(
|
|
||||||
PromiseResolve(fetchModule(url, context)),
|
|
||||||
(entry) => {
|
|
||||||
return mimeToFormat(entry.headers['content-type']);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {URL} url
|
* @param {URL} url
|
||||||
* @param {{parentURL: string}} context
|
* @param {{parentURL: string}} context
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ArrayPrototypePush,
|
|
||||||
RegExpPrototypeExec,
|
RegExpPrototypeExec,
|
||||||
decodeURIComponent,
|
decodeURIComponent,
|
||||||
} = primordials;
|
} = primordials;
|
||||||
@ -12,8 +11,6 @@ const { validateAttributes, emitImportAssertionWarning } = require('internal/mod
|
|||||||
const { getOptionValue } = require('internal/options');
|
const { getOptionValue } = require('internal/options');
|
||||||
const { readFileSync } = require('fs');
|
const { readFileSync } = require('fs');
|
||||||
|
|
||||||
const experimentalNetworkImports =
|
|
||||||
getOptionValue('--experimental-network-imports');
|
|
||||||
const defaultType =
|
const defaultType =
|
||||||
getOptionValue('--experimental-default-type');
|
getOptionValue('--experimental-default-type');
|
||||||
|
|
||||||
@ -39,7 +36,7 @@ const DATA_URL_PATTERN = /^[^/]+\/[^,;]+(?:[^,]*?)(;base64)?,([\s\S]*)$/;
|
|||||||
*/
|
*/
|
||||||
async function getSource(url, context) {
|
async function getSource(url, context) {
|
||||||
const { protocol, href } = url;
|
const { protocol, href } = url;
|
||||||
let responseURL = href;
|
const responseURL = href;
|
||||||
let source;
|
let source;
|
||||||
if (protocol === 'file:') {
|
if (protocol === 'file:') {
|
||||||
const { readFile: readFileAsync } = require('internal/fs/promises').exports;
|
const { readFile: readFileAsync } = require('internal/fs/promises').exports;
|
||||||
@ -51,19 +48,8 @@ async function getSource(url, context) {
|
|||||||
}
|
}
|
||||||
const { 1: base64, 2: body } = match;
|
const { 1: base64, 2: body } = match;
|
||||||
source = BufferFrom(decodeURIComponent(body), base64 ? 'base64' : 'utf8');
|
source = BufferFrom(decodeURIComponent(body), base64 ? 'base64' : 'utf8');
|
||||||
} else if (experimentalNetworkImports && (
|
|
||||||
protocol === 'https:' ||
|
|
||||||
protocol === 'http:'
|
|
||||||
)) {
|
|
||||||
const { fetchModule } = require('internal/modules/esm/fetch_module');
|
|
||||||
const res = await fetchModule(url, context);
|
|
||||||
source = await res.body;
|
|
||||||
responseURL = res.resolvedHREF;
|
|
||||||
} else {
|
} else {
|
||||||
const supportedSchemes = ['file', 'data'];
|
const supportedSchemes = ['file', 'data'];
|
||||||
if (experimentalNetworkImports) {
|
|
||||||
ArrayPrototypePush(supportedSchemes, 'http', 'https');
|
|
||||||
}
|
|
||||||
throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(url, supportedSchemes);
|
throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(url, supportedSchemes);
|
||||||
}
|
}
|
||||||
return { __proto__: null, responseURL, source };
|
return { __proto__: null, responseURL, source };
|
||||||
@ -121,7 +107,7 @@ async function defaultLoad(url, context = kEmptyObject) {
|
|||||||
|
|
||||||
const urlInstance = new URL(url);
|
const urlInstance = new URL(url);
|
||||||
|
|
||||||
throwIfUnsupportedURLScheme(urlInstance, experimentalNetworkImports);
|
throwIfUnsupportedURLScheme(urlInstance);
|
||||||
|
|
||||||
if (urlInstance.protocol === 'node:') {
|
if (urlInstance.protocol === 'node:') {
|
||||||
source = null;
|
source = null;
|
||||||
@ -224,9 +210,8 @@ function defaultLoadSync(url, context = kEmptyObject) {
|
|||||||
* throws an error if the protocol is not one of the protocols
|
* throws an error if the protocol is not one of the protocols
|
||||||
* that can be loaded in the default loader
|
* that can be loaded in the default loader
|
||||||
* @param {URL} parsed
|
* @param {URL} parsed
|
||||||
* @param {boolean} experimentalNetworkImports
|
|
||||||
*/
|
*/
|
||||||
function throwIfUnsupportedURLScheme(parsed, experimentalNetworkImports) {
|
function throwIfUnsupportedURLScheme(parsed) {
|
||||||
// Avoid accessing the `protocol` property due to the lazy getters.
|
// Avoid accessing the `protocol` property due to the lazy getters.
|
||||||
const protocol = parsed?.protocol;
|
const protocol = parsed?.protocol;
|
||||||
if (
|
if (
|
||||||
@ -235,17 +220,11 @@ function throwIfUnsupportedURLScheme(parsed, experimentalNetworkImports) {
|
|||||||
protocol !== 'data:' &&
|
protocol !== 'data:' &&
|
||||||
protocol !== 'node:' &&
|
protocol !== 'node:' &&
|
||||||
(
|
(
|
||||||
!experimentalNetworkImports ||
|
protocol !== 'https:' &&
|
||||||
(
|
protocol !== 'http:'
|
||||||
protocol !== 'https:' &&
|
|
||||||
protocol !== 'http:'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
const schemes = ['file', 'data', 'node'];
|
const schemes = ['file', 'data', 'node'];
|
||||||
if (experimentalNetworkImports) {
|
|
||||||
ArrayPrototypePush(schemes, 'https', 'http');
|
|
||||||
}
|
|
||||||
throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(parsed, schemes);
|
throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(parsed, schemes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ const {
|
|||||||
} = require('internal/errors').codes;
|
} = require('internal/errors').codes;
|
||||||
const { getOptionValue } = require('internal/options');
|
const { getOptionValue } = require('internal/options');
|
||||||
const { isURL, pathToFileURL, URLParse } = require('internal/url');
|
const { isURL, pathToFileURL, URLParse } = require('internal/url');
|
||||||
const { emitExperimentalWarning, kEmptyObject } = require('internal/util');
|
const { kEmptyObject } = require('internal/util');
|
||||||
const {
|
const {
|
||||||
compileSourceTextModule,
|
compileSourceTextModule,
|
||||||
getDefaultConditions,
|
getDefaultConditions,
|
||||||
@ -145,9 +145,6 @@ class ModuleLoader {
|
|||||||
#customizations;
|
#customizations;
|
||||||
|
|
||||||
constructor(customizations) {
|
constructor(customizations) {
|
||||||
if (getOptionValue('--experimental-network-imports')) {
|
|
||||||
emitExperimentalWarning('Network Imports');
|
|
||||||
}
|
|
||||||
this.setCustomizations(customizations);
|
this.setCustomizations(customizations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,8 +30,6 @@ const { getOptionValue } = require('internal/options');
|
|||||||
const { sep, posix: { relative: relativePosixPath }, resolve } = require('path');
|
const { sep, posix: { relative: relativePosixPath }, resolve } = require('path');
|
||||||
const preserveSymlinks = getOptionValue('--preserve-symlinks');
|
const preserveSymlinks = getOptionValue('--preserve-symlinks');
|
||||||
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
|
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
|
||||||
const experimentalNetworkImports =
|
|
||||||
getOptionValue('--experimental-network-imports');
|
|
||||||
const inputTypeFlag = getOptionValue('--input-type');
|
const inputTypeFlag = getOptionValue('--input-type');
|
||||||
const { URL, pathToFileURL, fileURLToPath, isURL, URLParse } = require('internal/url');
|
const { URL, pathToFileURL, fileURLToPath, isURL, URLParse } = require('internal/url');
|
||||||
const { getCWDURL, setOwnProperty } = require('internal/util');
|
const { getCWDURL, setOwnProperty } = require('internal/util');
|
||||||
@ -48,7 +46,6 @@ const {
|
|||||||
ERR_PACKAGE_PATH_NOT_EXPORTED,
|
ERR_PACKAGE_PATH_NOT_EXPORTED,
|
||||||
ERR_UNSUPPORTED_DIR_IMPORT,
|
ERR_UNSUPPORTED_DIR_IMPORT,
|
||||||
ERR_UNSUPPORTED_RESOLVE_REQUEST,
|
ERR_UNSUPPORTED_RESOLVE_REQUEST,
|
||||||
ERR_NETWORK_IMPORT_DISALLOWED,
|
|
||||||
} = require('internal/errors').codes;
|
} = require('internal/errors').codes;
|
||||||
|
|
||||||
const { Module: CJSModule } = require('internal/modules/cjs/loader');
|
const { Module: CJSModule } = require('internal/modules/cjs/loader');
|
||||||
@ -886,10 +883,6 @@ function moduleResolve(specifier, base, conditions, preserveSymlinks) {
|
|||||||
StringPrototypeSlice(base, 0, StringPrototypeIndexOf(base, ':') + 1) :
|
StringPrototypeSlice(base, 0, StringPrototypeIndexOf(base, ':') + 1) :
|
||||||
base.protocol;
|
base.protocol;
|
||||||
const isData = protocol === 'data:';
|
const isData = protocol === 'data:';
|
||||||
const isRemote =
|
|
||||||
isData ||
|
|
||||||
protocol === 'http:' ||
|
|
||||||
protocol === 'https:';
|
|
||||||
// Order swapped from spec for minor perf gain.
|
// Order swapped from spec for minor perf gain.
|
||||||
// Ok since relative URLs cannot parse as URLs.
|
// Ok since relative URLs cannot parse as URLs.
|
||||||
let resolved;
|
let resolved;
|
||||||
@ -907,7 +900,7 @@ function moduleResolve(specifier, base, conditions, preserveSymlinks) {
|
|||||||
try {
|
try {
|
||||||
resolved = new URL(specifier);
|
resolved = new URL(specifier);
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
if (isRemote && !BuiltinModule.canBeRequiredWithoutScheme(specifier)) {
|
if (isData && !BuiltinModule.canBeRequiredWithoutScheme(specifier)) {
|
||||||
const error = new ERR_UNSUPPORTED_RESOLVE_REQUEST(specifier, base);
|
const error = new ERR_UNSUPPORTED_RESOLVE_REQUEST(specifier, base);
|
||||||
setOwnProperty(error, 'cause', cause);
|
setOwnProperty(error, 'cause', cause);
|
||||||
throw error;
|
throw error;
|
||||||
@ -976,57 +969,6 @@ function resolveAsCommonJS(specifier, parentURL) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Throw an error if an import is not allowed.
|
|
||||||
* TODO(@JakobJingleheimer): de-dupe `specifier` & `parsed`
|
|
||||||
* @param {string} specifier - The import specifier.
|
|
||||||
* @param {URL} parsed - The parsed URL of the import specifier.
|
|
||||||
* @param {URL} parsedParentURL - The parsed URL of the parent module.
|
|
||||||
* @throws {ERR_NETWORK_IMPORT_DISALLOWED} - If the import is disallowed.
|
|
||||||
*/
|
|
||||||
function checkIfDisallowedImport(specifier, parsed, parsedParentURL) {
|
|
||||||
if (parsedParentURL) {
|
|
||||||
// Avoid accessing the `protocol` property due to the lazy getters.
|
|
||||||
const parentProtocol = parsedParentURL.protocol;
|
|
||||||
if (
|
|
||||||
parentProtocol === 'http:' ||
|
|
||||||
parentProtocol === 'https:'
|
|
||||||
) {
|
|
||||||
if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
|
|
||||||
// Avoid accessing the `protocol` property due to the lazy getters.
|
|
||||||
const parsedProtocol = parsed?.protocol;
|
|
||||||
// data: and blob: disallowed due to allowing file: access via
|
|
||||||
// indirection
|
|
||||||
if (parsedProtocol &&
|
|
||||||
parsedProtocol !== 'https:' &&
|
|
||||||
parsedProtocol !== 'http:'
|
|
||||||
) {
|
|
||||||
throw new ERR_NETWORK_IMPORT_DISALLOWED(
|
|
||||||
specifier,
|
|
||||||
parsedParentURL,
|
|
||||||
'remote imports cannot import from a local location.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { url: parsed.href };
|
|
||||||
}
|
|
||||||
if (BuiltinModule.canBeRequiredWithoutScheme(specifier)) {
|
|
||||||
throw new ERR_NETWORK_IMPORT_DISALLOWED(
|
|
||||||
specifier,
|
|
||||||
parsedParentURL,
|
|
||||||
'remote imports cannot import from a local location.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ERR_NETWORK_IMPORT_DISALLOWED(
|
|
||||||
specifier,
|
|
||||||
parsedParentURL,
|
|
||||||
'only relative and absolute specifiers are supported.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate user-input in `context` supplied by a custom loader.
|
* Validate user-input in `context` supplied by a custom loader.
|
||||||
* @param {string | URL | undefined} parentURL - The parent URL.
|
* @param {string | URL | undefined} parentURL - The parent URL.
|
||||||
@ -1068,36 +1010,10 @@ function defaultResolve(specifier, context = {}) {
|
|||||||
// Avoid accessing the `protocol` property due to the lazy getters.
|
// Avoid accessing the `protocol` property due to the lazy getters.
|
||||||
protocol = parsed.protocol;
|
protocol = parsed.protocol;
|
||||||
|
|
||||||
if (protocol === 'data:' &&
|
if (protocol === 'data:') {
|
||||||
parsedParentURL.protocol !== 'file:' &&
|
|
||||||
experimentalNetworkImports) {
|
|
||||||
throw new ERR_NETWORK_IMPORT_DISALLOWED(
|
|
||||||
specifier,
|
|
||||||
parsedParentURL,
|
|
||||||
'import data: from a non file: is not allowed',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (protocol === 'data:' ||
|
|
||||||
(experimentalNetworkImports &&
|
|
||||||
(
|
|
||||||
protocol === 'https:' ||
|
|
||||||
protocol === 'http:'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return { __proto__: null, url: parsed.href };
|
return { __proto__: null, url: parsed.href };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// There are multiple deep branches that can either throw or return; instead
|
|
||||||
// of duplicating that deeply nested logic for the possible returns, DRY and
|
|
||||||
// check for a return. This seems the least gnarly.
|
|
||||||
const maybeReturn = checkIfDisallowedImport(
|
|
||||||
specifier,
|
|
||||||
parsed,
|
|
||||||
parsedParentURL,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (maybeReturn) { return maybeReturn; }
|
|
||||||
|
|
||||||
// This must come after checkIfDisallowedImport
|
// This must come after checkIfDisallowedImport
|
||||||
protocol ??= parsed?.protocol;
|
protocol ??= parsed?.protocol;
|
||||||
|
@ -436,10 +436,6 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
|
|||||||
kAllowedInEnvvar);
|
kAllowedInEnvvar);
|
||||||
AddAlias("--loader", "--experimental-loader");
|
AddAlias("--loader", "--experimental-loader");
|
||||||
AddOption("--experimental-modules", "", NoOp{}, kAllowedInEnvvar);
|
AddOption("--experimental-modules", "", NoOp{}, kAllowedInEnvvar);
|
||||||
AddOption("--experimental-network-imports",
|
|
||||||
"experimental https: support for the ES Module loader",
|
|
||||||
&EnvironmentOptions::experimental_https_modules,
|
|
||||||
kAllowedInEnvvar);
|
|
||||||
AddOption("--experimental-wasm-modules",
|
AddOption("--experimental-wasm-modules",
|
||||||
"experimental ES Module support for webassembly modules",
|
"experimental ES Module support for webassembly modules",
|
||||||
&EnvironmentOptions::experimental_wasm_modules,
|
&EnvironmentOptions::experimental_wasm_modules,
|
||||||
|
@ -123,7 +123,6 @@ class EnvironmentOptions : public Options {
|
|||||||
std::string localstorage_file;
|
std::string localstorage_file;
|
||||||
bool experimental_global_navigator = true;
|
bool experimental_global_navigator = true;
|
||||||
bool experimental_global_web_crypto = true;
|
bool experimental_global_web_crypto = true;
|
||||||
bool experimental_https_modules = false;
|
|
||||||
bool experimental_wasm_modules = false;
|
bool experimental_wasm_modules = false;
|
||||||
bool experimental_import_meta_resolve = false;
|
bool experimental_import_meta_resolve = false;
|
||||||
std::string input_type; // Value of --input-type
|
std::string input_type; // Value of --input-type
|
||||||
|
@ -30,7 +30,6 @@ describe('ESM: warn for obsolete hooks provided', { concurrency: !process.env.TE
|
|||||||
'--experimental-loader',
|
'--experimental-loader',
|
||||||
fileURL('es-module-loaders', 'hooks-custom.mjs'),
|
fileURL('es-module-loaders', 'hooks-custom.mjs'),
|
||||||
],
|
],
|
||||||
[/Network Imports/, '--experimental-network-imports'],
|
|
||||||
]
|
]
|
||||||
) {
|
) {
|
||||||
it(`should print for ${experiment.toString().replaceAll('/', '')}`, async () => {
|
it(`should print for ${experiment.toString().replaceAll('/', '')}`, async () => {
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
import { mustCall, spawnPromisified } from '../common/index.mjs';
|
|
||||||
import { ok, match, notStrictEqual } from 'node:assert';
|
|
||||||
import { spawn as spawnAsync } from 'node:child_process';
|
|
||||||
import { execPath } from 'node:process';
|
|
||||||
import { describe, it } from 'node:test';
|
|
||||||
|
|
||||||
|
|
||||||
describe('ESM: http import via CLI', { concurrency: !process.env.TEST_PARALLEL }, () => {
|
|
||||||
const disallowedSpecifier = 'http://example.com';
|
|
||||||
|
|
||||||
it('should throw disallowed error for insecure protocol', async () => {
|
|
||||||
const { code, stderr } = await spawnPromisified(execPath, [
|
|
||||||
'--experimental-network-imports',
|
|
||||||
'--input-type=module',
|
|
||||||
'--eval',
|
|
||||||
`import ${JSON.stringify(disallowedSpecifier)}`,
|
|
||||||
]);
|
|
||||||
|
|
||||||
notStrictEqual(code, 0);
|
|
||||||
|
|
||||||
// [ERR_NETWORK_IMPORT_DISALLOWED]: import of 'http://example.com/' by
|
|
||||||
// …/[eval1] is not supported: http can only be used to load local
|
|
||||||
// resources (use https instead).
|
|
||||||
match(stderr, /ERR_NETWORK_IMPORT_DISALLOWED/);
|
|
||||||
ok(stderr.includes(disallowedSpecifier));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw disallowed error for insecure protocol in REPL', () => {
|
|
||||||
const child = spawnAsync(execPath, [
|
|
||||||
'--experimental-network-imports',
|
|
||||||
'--input-type=module',
|
|
||||||
]);
|
|
||||||
child.stdin.end(`import ${JSON.stringify(disallowedSpecifier)}`);
|
|
||||||
|
|
||||||
let stderr = '';
|
|
||||||
child.stderr.setEncoding('utf8');
|
|
||||||
child.stderr.on('data', (data) => stderr += data);
|
|
||||||
child.on('close', mustCall((code, _signal) => {
|
|
||||||
notStrictEqual(code, 0);
|
|
||||||
|
|
||||||
// [ERR_NETWORK_IMPORT_DISALLOWED]: import of 'http://example.com/' by
|
|
||||||
// …/[stdin] is not supported: http can only be used to load local
|
|
||||||
// resources (use https instead).
|
|
||||||
match(stderr, /\[ERR_NETWORK_IMPORT_DISALLOWED\]/);
|
|
||||||
ok(stderr.includes(disallowedSpecifier));
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,311 +0,0 @@
|
|||||||
// Flags: --experimental-network-imports --dns-result-order=ipv4first
|
|
||||||
import * as common from '../common/index.mjs';
|
|
||||||
import * as fixtures from '../common/fixtures.mjs';
|
|
||||||
import tmpdir from '../common/tmpdir.js';
|
|
||||||
import assert from 'assert';
|
|
||||||
import http from 'http';
|
|
||||||
import os from 'os';
|
|
||||||
import util from 'util';
|
|
||||||
import { describe, it } from 'node:test';
|
|
||||||
|
|
||||||
if (!common.hasCrypto) {
|
|
||||||
common.skip('missing crypto');
|
|
||||||
}
|
|
||||||
tmpdir.refresh();
|
|
||||||
|
|
||||||
const https = (await import('https')).default;
|
|
||||||
|
|
||||||
const createHTTPServer = http.createServer;
|
|
||||||
|
|
||||||
// Needed to deal w/ test certs
|
|
||||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
||||||
const options = {
|
|
||||||
key: fixtures.readKey('agent1-key.pem'),
|
|
||||||
cert: fixtures.readKey('agent1-cert.pem')
|
|
||||||
};
|
|
||||||
|
|
||||||
const createHTTPSServer = https.createServer.bind(null, options);
|
|
||||||
|
|
||||||
|
|
||||||
const testListeningOptions = [
|
|
||||||
{
|
|
||||||
hostname: 'localhost',
|
|
||||||
listenOptions: {
|
|
||||||
host: '127.0.0.1'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const internalInterfaces = Object.values(os.networkInterfaces()).flat().filter(
|
|
||||||
(iface) => iface?.internal && iface.address && !iface.scopeid
|
|
||||||
);
|
|
||||||
for (const iface of internalInterfaces) {
|
|
||||||
testListeningOptions.push({
|
|
||||||
hostname: iface?.family === 'IPv6' ? `[${iface?.address}]` : iface?.address,
|
|
||||||
listenOptions: {
|
|
||||||
host: iface?.address,
|
|
||||||
ipv6Only: iface?.family === 'IPv6'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const { protocol, createServer } of [
|
|
||||||
{ protocol: 'http:', createServer: createHTTPServer },
|
|
||||||
{ protocol: 'https:', createServer: createHTTPSServer },
|
|
||||||
]) {
|
|
||||||
const body = `
|
|
||||||
export default (a) => () => a;
|
|
||||||
export let url = import.meta.url;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const base = 'http://127.0.0.1';
|
|
||||||
for (const { hostname, listenOptions } of testListeningOptions) {
|
|
||||||
const host = new URL(base);
|
|
||||||
host.protocol = protocol;
|
|
||||||
host.hostname = hostname;
|
|
||||||
// /not-found is a 404
|
|
||||||
// ?redirect causes a redirect, no body. JSON.parse({status:number,location:string})
|
|
||||||
// ?mime sets the content-type, string
|
|
||||||
// ?body sets the body, string
|
|
||||||
const server = createServer(function(_req, res) {
|
|
||||||
const url = new URL(_req.url, host);
|
|
||||||
const redirect = url.searchParams.get('redirect');
|
|
||||||
|
|
||||||
if (url.pathname === 'json') {
|
|
||||||
common.mustCall(() => assert.strictEqual(_req.header.content, 'application/json,*/*;charset=utf-8;q=0.5'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url.pathname === '/not-found') {
|
|
||||||
res.writeHead(404);
|
|
||||||
res.end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (redirect) {
|
|
||||||
const { status, location } = JSON.parse(redirect);
|
|
||||||
res.writeHead(status, {
|
|
||||||
location
|
|
||||||
});
|
|
||||||
res.end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.writeHead(200, {
|
|
||||||
'content-type': url.searchParams.get('mime') || 'text/javascript'
|
|
||||||
});
|
|
||||||
res.end(url.searchParams.get('body') || body);
|
|
||||||
});
|
|
||||||
|
|
||||||
const listen = util.promisify(server.listen.bind(server));
|
|
||||||
await listen({
|
|
||||||
...listenOptions,
|
|
||||||
port: 0
|
|
||||||
});
|
|
||||||
const url = new URL(host);
|
|
||||||
url.port = server?.address()?.port;
|
|
||||||
|
|
||||||
const ns = await import(url.href);
|
|
||||||
assert.strict.deepStrictEqual(Object.keys(ns), ['default', 'url']);
|
|
||||||
const obj = {};
|
|
||||||
assert.strict.equal(ns.default(obj)(), obj);
|
|
||||||
assert.strict.equal(ns.url, url.href);
|
|
||||||
|
|
||||||
// Redirects have same import.meta.url but different cache
|
|
||||||
// entry on Web
|
|
||||||
const redirect = new URL(url.href);
|
|
||||||
redirect.searchParams.set('redirect', JSON.stringify({
|
|
||||||
status: 302,
|
|
||||||
location: url.href
|
|
||||||
}));
|
|
||||||
const redirectedNS = await import(redirect.href);
|
|
||||||
assert.strict.deepStrictEqual(
|
|
||||||
Object.keys(redirectedNS),
|
|
||||||
['default', 'url']
|
|
||||||
);
|
|
||||||
assert.strict.notEqual(redirectedNS.default, ns.default);
|
|
||||||
assert.strict.equal(redirectedNS.url, url.href);
|
|
||||||
|
|
||||||
// Redirects have the same import.meta.url but different cache
|
|
||||||
// entry on Web
|
|
||||||
const relativeAfterRedirect = new URL(url.href + 'foo/index.js');
|
|
||||||
const redirected = new URL(url.href + 'bar/index.js');
|
|
||||||
redirected.searchParams.set('body', 'export let relativeDepURL = (await import("./baz.js")).url');
|
|
||||||
relativeAfterRedirect.searchParams.set('redirect', JSON.stringify({
|
|
||||||
status: 302,
|
|
||||||
location: redirected.href
|
|
||||||
}));
|
|
||||||
const relativeAfterRedirectedNS = await import(relativeAfterRedirect.href);
|
|
||||||
assert.strict.equal(
|
|
||||||
relativeAfterRedirectedNS.relativeDepURL,
|
|
||||||
url.href + 'bar/baz.js'
|
|
||||||
);
|
|
||||||
|
|
||||||
const unsupportedMIME = new URL(url.href);
|
|
||||||
unsupportedMIME.searchParams.set('mime', 'application/node');
|
|
||||||
unsupportedMIME.searchParams.set('body', '');
|
|
||||||
await assert.rejects(
|
|
||||||
import(unsupportedMIME.href),
|
|
||||||
{ code: 'ERR_UNKNOWN_MODULE_FORMAT' }
|
|
||||||
);
|
|
||||||
|
|
||||||
const notFound = new URL(url.href);
|
|
||||||
notFound.pathname = '/not-found';
|
|
||||||
await assert.rejects(
|
|
||||||
import(notFound.href),
|
|
||||||
{ code: 'ERR_MODULE_NOT_FOUND' },
|
|
||||||
);
|
|
||||||
|
|
||||||
const jsonUrl = new URL(url.href + 'json');
|
|
||||||
jsonUrl.searchParams.set('mime', 'application/json');
|
|
||||||
jsonUrl.searchParams.set('body', '{"x": 1}');
|
|
||||||
const json = await import(jsonUrl.href, { with: { type: 'json' } });
|
|
||||||
assert.deepStrictEqual(Object.keys(json), ['default']);
|
|
||||||
assert.strictEqual(json.default.x, 1);
|
|
||||||
|
|
||||||
await describe('guarantee data url will not bypass import restriction', () => {
|
|
||||||
it('should not be bypassed by cross protocol redirect', async () => {
|
|
||||||
const crossProtocolRedirect = new URL(url.href);
|
|
||||||
crossProtocolRedirect.searchParams.set('redirect', JSON.stringify({
|
|
||||||
status: 302,
|
|
||||||
location: 'data:text/javascript,'
|
|
||||||
}));
|
|
||||||
await assert.rejects(
|
|
||||||
import(crossProtocolRedirect.href),
|
|
||||||
{ code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be bypassed by data URL', async () => {
|
|
||||||
const deps = new URL(url.href);
|
|
||||||
deps.searchParams.set('body', `
|
|
||||||
export {data} from 'data:text/javascript,export let data = 1';
|
|
||||||
import * as http from ${JSON.stringify(url.href)};
|
|
||||||
export {http};
|
|
||||||
`);
|
|
||||||
await assert.rejects(
|
|
||||||
import(deps.href),
|
|
||||||
{ code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be bypassed by encodedURI import', async () => {
|
|
||||||
const deepDataImport = new URL(url.href);
|
|
||||||
deepDataImport.searchParams.set('body', `
|
|
||||||
import 'data:text/javascript,import${encodeURIComponent(JSON.stringify('data:text/javascript,import "os"'))}';
|
|
||||||
`);
|
|
||||||
await assert.rejects(
|
|
||||||
import(deepDataImport.href),
|
|
||||||
{ code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be bypassed by relative deps import', async () => {
|
|
||||||
const relativeDeps = new URL(url.href);
|
|
||||||
relativeDeps.searchParams.set('body', `
|
|
||||||
import * as http from "./";
|
|
||||||
export {http};
|
|
||||||
`);
|
|
||||||
const relativeDepsNS = await import(relativeDeps.href);
|
|
||||||
assert.strict.deepStrictEqual(Object.keys(relativeDepsNS), ['http']);
|
|
||||||
assert.strict.equal(relativeDepsNS.http, ns);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be bypassed by file dependency import', async () => {
|
|
||||||
const fileDep = new URL(url.href);
|
|
||||||
const { href } = fixtures.fileURL('/es-modules/message.mjs');
|
|
||||||
fileDep.searchParams.set('body', `
|
|
||||||
import ${JSON.stringify(href)};
|
|
||||||
export default 1;`);
|
|
||||||
await assert.rejects(
|
|
||||||
import(fileDep.href),
|
|
||||||
{ code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be bypassed by builtin dependency import', async () => {
|
|
||||||
const builtinDep = new URL(url.href);
|
|
||||||
builtinDep.searchParams.set('body', `
|
|
||||||
import 'node:fs';
|
|
||||||
export default 1;
|
|
||||||
`);
|
|
||||||
await assert.rejects(
|
|
||||||
import(builtinDep.href),
|
|
||||||
{ code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should not be bypassed by unprefixed builtin dependency import', async () => {
|
|
||||||
const unprefixedBuiltinDep = new URL(url.href);
|
|
||||||
unprefixedBuiltinDep.searchParams.set('body', `
|
|
||||||
import 'fs';
|
|
||||||
export default 1;
|
|
||||||
`);
|
|
||||||
await assert.rejects(
|
|
||||||
import(unprefixedBuiltinDep.href),
|
|
||||||
{ code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be bypassed by indirect network import', async () => {
|
|
||||||
const indirect = new URL(url.href);
|
|
||||||
indirect.searchParams.set('body', `
|
|
||||||
import childProcess from 'data:text/javascript,export { default } from "node:child_process"'
|
|
||||||
export {childProcess};
|
|
||||||
`);
|
|
||||||
await assert.rejects(
|
|
||||||
import(indirect.href),
|
|
||||||
{ code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('data: URL can always import other data:', async () => {
|
|
||||||
const data = new URL('data:text/javascript,');
|
|
||||||
data.searchParams.set('body',
|
|
||||||
'import \'data:text/javascript,import \'data:\''
|
|
||||||
);
|
|
||||||
// doesn't throw
|
|
||||||
const empty = await import(data.href);
|
|
||||||
assert.ok(empty);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('data: URL cannot import file: or builtin', async () => {
|
|
||||||
const data1 = new URL(url.href);
|
|
||||||
data1.searchParams.set('body',
|
|
||||||
'import \'file:///some/file.js\''
|
|
||||||
);
|
|
||||||
await assert.rejects(
|
|
||||||
import(data1.href),
|
|
||||||
{ code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
|
|
||||||
);
|
|
||||||
|
|
||||||
const data2 = new URL(url.href);
|
|
||||||
data2.searchParams.set('body',
|
|
||||||
'import \'node:fs\''
|
|
||||||
);
|
|
||||||
await assert.rejects(
|
|
||||||
import(data2.href),
|
|
||||||
{ code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('data: URL cannot import HTTP URLs', async () => {
|
|
||||||
const module = fixtures.fileURL('/es-modules/import-data-url.mjs');
|
|
||||||
try {
|
|
||||||
await import(module);
|
|
||||||
} catch (err) {
|
|
||||||
// We only want the module to load, we don't care if the module throws an
|
|
||||||
// error as long as the loader does not.
|
|
||||||
assert.notStrictEqual(err?.code, 'ERR_MODULE_NOT_FOUND');
|
|
||||||
}
|
|
||||||
const data1 = new URL(url.href);
|
|
||||||
const dataURL = 'data:text/javascript;export * from "node:os"';
|
|
||||||
data1.searchParams.set('body', `export * from ${JSON.stringify(dataURL)};`);
|
|
||||||
await assert.rejects(
|
|
||||||
import(data1),
|
|
||||||
{ code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
server.close();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
// Flags: --experimental-require-module
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
require('../common');
|
|
||||||
const assert = require('assert');
|
|
||||||
|
|
||||||
assert.throws(() => {
|
|
||||||
require('../fixtures/es-modules/network-import.mjs');
|
|
||||||
}, {
|
|
||||||
code: 'ERR_NETWORK_IMPORT_DISALLOWED'
|
|
||||||
});
|
|
1
test/fixtures/es-modules/network-import.mjs
vendored
1
test/fixtures/es-modules/network-import.mjs
vendored
@ -1 +0,0 @@
|
|||||||
import 'http://example.com/foo.js';
|
|
Loading…
x
Reference in New Issue
Block a user