node/test/parallel/test-vm-module-link.js
Chengzhong Wu d1d5da22e4
vm: harden module type checks
Check if the value returned from user linker function is a null-ish
value.

`validateInternalField` should be preferred when checking `this`
argument to guard against null-ish `this`.

Co-authored-by: Mike Ralphson <mike.ralphson@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/52162
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io>
2024-03-22 09:41:02 +00:00

169 lines
4.2 KiB
JavaScript

'use strict';
// Flags: --experimental-vm-modules --harmony-import-attributes
const common = require('../common');
const assert = require('assert');
const { SourceTextModule } = require('vm');
async function simple() {
const foo = new SourceTextModule('export default 5;');
await foo.link(common.mustNotCall());
globalThis.fiveResult = undefined;
const bar = new SourceTextModule('import five from "foo"; fiveResult = five');
assert.deepStrictEqual(bar.dependencySpecifiers, ['foo']);
await bar.link(common.mustCall((specifier, module) => {
assert.strictEqual(module, bar);
assert.strictEqual(specifier, 'foo');
return foo;
}));
await bar.evaluate();
assert.strictEqual(globalThis.fiveResult, 5);
delete globalThis.fiveResult;
}
async function invalidLinkValue() {
const invalidValues = [
undefined,
null,
{},
SourceTextModule.prototype,
];
for (const value of invalidValues) {
const module = new SourceTextModule('import "foo"');
await assert.rejects(module.link(() => value), {
code: 'ERR_VM_MODULE_NOT_MODULE',
});
}
}
async function depth() {
const foo = new SourceTextModule('export default 5');
await foo.link(common.mustNotCall());
async function getProxy(parentName, parentModule) {
const mod = new SourceTextModule(`
import ${parentName} from '${parentName}';
export default ${parentName};
`);
await mod.link(common.mustCall((specifier, module) => {
assert.strictEqual(module, mod);
assert.strictEqual(specifier, parentName);
return parentModule;
}));
return mod;
}
const bar = await getProxy('foo', foo);
const baz = await getProxy('bar', bar);
const barz = await getProxy('baz', baz);
await barz.evaluate();
assert.strictEqual(barz.namespace.default, 5);
}
async function circular() {
const foo = new SourceTextModule(`
import getFoo from 'bar';
export let foo = 42;
export default getFoo();
`);
const bar = new SourceTextModule(`
import { foo } from 'foo';
export default function getFoo() {
return foo;
}
`);
await foo.link(common.mustCall(async (specifier, module) => {
if (specifier === 'bar') {
assert.strictEqual(module, foo);
return bar;
}
assert.strictEqual(specifier, 'foo');
assert.strictEqual(module, bar);
assert.strictEqual(foo.status, 'linking');
return foo;
}, 2));
assert.strictEqual(bar.status, 'linked');
await foo.evaluate();
assert.strictEqual(foo.namespace.default, 42);
}
async function circular2() {
const sourceMap = {
'root': `
import * as a from './a.mjs';
import * as b from './b.mjs';
if (!('fromA' in a))
throw new Error();
if (!('fromB' in a))
throw new Error();
if (!('fromA' in b))
throw new Error();
if (!('fromB' in b))
throw new Error();
`,
'./a.mjs': `
export * from './b.mjs';
export let fromA;
`,
'./b.mjs': `
export * from './a.mjs';
export let fromB;
`
};
const moduleMap = new Map();
const rootModule = new SourceTextModule(sourceMap.root, {
identifier: 'vm:root',
});
async function link(specifier, referencingModule) {
if (moduleMap.has(specifier)) {
return moduleMap.get(specifier);
}
const mod = new SourceTextModule(sourceMap[specifier], {
identifier: new URL(specifier, 'file:///').href,
});
moduleMap.set(specifier, mod);
return mod;
}
await rootModule.link(link);
await rootModule.evaluate();
}
async function asserts() {
const m = new SourceTextModule(`
import "foo" with { n1: 'v1', n2: 'v2' };
`, { identifier: 'm' });
await m.link((s, r, p) => {
assert.strictEqual(s, 'foo');
assert.strictEqual(r.identifier, 'm');
assert.strictEqual(p.attributes.n1, 'v1');
assert.strictEqual(p.assert.n1, 'v1');
assert.strictEqual(p.attributes.n2, 'v2');
assert.strictEqual(p.assert.n2, 'v2');
return new SourceTextModule('');
});
}
const finished = common.mustCall();
(async function main() {
await simple();
await invalidLinkValue();
await depth();
await circular();
await circular2();
await asserts();
finished();
})().then(common.mustCall());