esm: improve validation of resolved URLs

PR-URL: https://github.com/nodejs/node/pull/41446
Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
Reviewed-By: Guy Bedford <guybedford@gmail.com>
This commit is contained in:
Jacob Smith 2022-01-19 06:28:32 +01:00 committed by GitHub
parent b9258e5e81
commit dbc6e39ca7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 14 additions and 14 deletions

View File

@ -29,7 +29,7 @@ const {
ERR_INVALID_RETURN_VALUE, ERR_INVALID_RETURN_VALUE,
ERR_UNKNOWN_MODULE_FORMAT ERR_UNKNOWN_MODULE_FORMAT
} = require('internal/errors').codes; } = require('internal/errors').codes;
const { pathToFileURL, isURLInstance } = require('internal/url'); const { pathToFileURL, isURLInstance, URL } = require('internal/url');
const { const {
isAnyArrayBuffer, isAnyArrayBuffer,
isArrayBufferView, isArrayBufferView,
@ -558,7 +558,8 @@ class ESMLoader {
format, format,
); );
} }
if (typeof url !== 'string') {
if (typeof url !== 'string') { // non-strings can be coerced to a url string
throw new ERR_INVALID_RETURN_PROPERTY_VALUE( throw new ERR_INVALID_RETURN_PROPERTY_VALUE(
'string', 'string',
'loader resolve', 'loader resolve',
@ -567,6 +568,8 @@ class ESMLoader {
); );
} }
new URL(url); // Intentionally trigger error if `url` is invalid
return { return {
format, format,
url, url,

View File

@ -4,11 +4,7 @@ import assert from 'assert';
import('../fixtures/es-modules/test-esm-ok.mjs') import('../fixtures/es-modules/test-esm-ok.mjs')
.then(assert.fail, (error) => { .then(assert.fail, (error) => {
expectsError({ expectsError({ code: 'ERR_INVALID_URL' })(error);
code: 'ERR_INVALID_URL',
message: 'Invalid URL'
})(error);
assert.strictEqual(error.input, '../fixtures/es-modules/test-esm-ok.mjs'); assert.strictEqual(error.input, '../fixtures/es-modules/test-esm-ok.mjs');
}) })
.then(mustCall()); .then(mustCall());

View File

@ -1,3 +1,4 @@
import { pathToFileURL } from 'node:url';
import count from '../es-modules/stateful.mjs'; import count from '../es-modules/stateful.mjs';
@ -24,28 +25,28 @@ export function load(url, context, next) {
format: 'module', format: 'module',
}); });
if (url === 'esmHook/badReturnVal.mjs') return 'export function returnShouldBeObject() {}'; if (url.endsWith('esmHook/badReturnVal.mjs')) return 'export function returnShouldBeObject() {}';
if (url === 'esmHook/badReturnFormatVal.mjs') return { if (url.endsWith('esmHook/badReturnFormatVal.mjs')) return {
format: Array(0), format: Array(0),
source: '', source: '',
} }
if (url === 'esmHook/unsupportedReturnFormatVal.mjs') return { if (url.endsWith('esmHook/unsupportedReturnFormatVal.mjs')) return {
format: 'foo', // Not one of the allowable inputs: no translator named 'foo' format: 'foo', // Not one of the allowable inputs: no translator named 'foo'
source: '', source: '',
} }
if (url === 'esmHook/badReturnSourceVal.mjs') return { if (url.endsWith('esmHook/badReturnSourceVal.mjs')) return {
format: 'module', format: 'module',
source: Array(0), source: Array(0),
} }
if (url === 'esmHook/preknownFormat.pre') return { if (url.endsWith('esmHook/preknownFormat.pre')) return {
format: context.format, format: context.format,
source: `const msg = 'hello world'; export default msg;` source: `const msg = 'hello world'; export default msg;`
}; };
if (url === 'esmHook/virtual.mjs') return { if (url.endsWith('esmHook/virtual.mjs')) return {
format: 'module', format: 'module',
source: `export const message = 'Woohoo!'.toUpperCase();`, source: `export const message = 'Woohoo!'.toUpperCase();`,
}; };
@ -62,7 +63,7 @@ export function resolve(specifier, context, next) {
if (specifier.startsWith('esmHook')) return { if (specifier.startsWith('esmHook')) return {
format, format,
url: specifier, url: pathToFileURL(specifier).href,
importAssertions: context.importAssertions, importAssertions: context.importAssertions,
}; };