node/test/parallel/test-vm-module-basic.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

182 lines
4.8 KiB
JavaScript

'use strict';
// Flags: --experimental-vm-modules
const common = require('../common');
const assert = require('assert');
const {
Module,
SourceTextModule,
SyntheticModule,
createContext,
compileFunction,
} = require('vm');
const util = require('util');
(async function test1() {
const context = createContext({
foo: 'bar',
baz: undefined,
typeofProcess: undefined,
});
const m = new SourceTextModule(
'baz = foo; typeofProcess = typeof process; typeof Object;',
{ context }
);
assert.strictEqual(m.status, 'unlinked');
await m.link(common.mustNotCall());
assert.strictEqual(m.status, 'linked');
assert.strictEqual(await m.evaluate(), undefined);
assert.strictEqual(m.status, 'evaluated');
assert.deepStrictEqual(context, {
foo: 'bar',
baz: 'bar',
typeofProcess: 'undefined'
});
}().then(common.mustCall()));
(async () => {
const m = new SourceTextModule(`
global.vmResultFoo = "foo";
global.vmResultTypeofProcess = Object.prototype.toString.call(process);
`);
await m.link(common.mustNotCall());
await m.evaluate();
assert.strictEqual(global.vmResultFoo, 'foo');
assert.strictEqual(global.vmResultTypeofProcess, '[object process]');
delete global.vmResultFoo;
delete global.vmResultTypeofProcess;
})().then(common.mustCall());
(async () => {
const m = new SourceTextModule('while (true) {}');
await m.link(common.mustNotCall());
await m.evaluate({ timeout: 500 })
.then(() => assert(false), () => {});
})().then(common.mustCall());
// Check the generated identifier for each module
(async () => {
const context1 = createContext({ });
const context2 = createContext({ });
const m1 = new SourceTextModule('1', { context: context1 });
assert.strictEqual(m1.identifier, 'vm:module(0)');
const m2 = new SourceTextModule('2', { context: context1 });
assert.strictEqual(m2.identifier, 'vm:module(1)');
const m3 = new SourceTextModule('3', { context: context2 });
assert.strictEqual(m3.identifier, 'vm:module(0)');
})().then(common.mustCall());
// Check inspection of the instance
{
const context = createContext({ foo: 'bar' });
const m = new SourceTextModule('1', { context });
assert.strictEqual(
util.inspect(m),
`SourceTextModule {
status: 'unlinked',
identifier: 'vm:module(0)',
context: { foo: 'bar' }
}`
);
assert.strictEqual(util.inspect(m, { depth: -1 }), '[SourceTextModule]');
for (const value of [null, { __proto__: null }, SourceTextModule.prototype]) {
assert.throws(
() => m[util.inspect.custom].call(value),
{
code: 'ERR_INVALID_ARG_TYPE',
message: /The "this" argument must be an instance of Module/,
},
);
}
}
{
const context = createContext({ foo: 'bar' });
const m = new SyntheticModule([], () => {}, { context });
assert.strictEqual(
util.inspect(m),
`SyntheticModule {
status: 'unlinked',
identifier: 'vm:module(0)',
context: { foo: 'bar' }
}`
);
assert.strictEqual(util.inspect(m, { depth: -1 }), '[SyntheticModule]');
}
// Check dependencies getter returns same object every time
{
const m = new SourceTextModule('');
const dep = m.dependencySpecifiers;
assert.notStrictEqual(dep, undefined);
assert.strictEqual(dep, m.dependencySpecifiers);
}
// Check the impossibility of creating an abstract instance of the Module.
{
assert.throws(() => new Module(), {
message: 'Module is not a constructor',
name: 'TypeError'
});
}
// Check to throws invalid exportNames
{
assert.throws(() => new SyntheticModule(undefined, () => {}, {}), {
message: 'The "exportNames" argument must be an ' +
'Array of unique strings.' +
' Received undefined',
name: 'TypeError'
});
}
// Check to throws duplicated exportNames
// https://github.com/nodejs/node/issues/32806
{
assert.throws(() => new SyntheticModule(['x', 'x'], () => {}, {}), {
message: 'The property \'exportNames.x\' is duplicated. Received \'x\'',
name: 'TypeError',
});
}
// Check to throws invalid evaluateCallback
{
assert.throws(() => new SyntheticModule([], undefined, {}), {
message: 'The "evaluateCallback" argument must be of type function.' +
' Received undefined',
name: 'TypeError'
});
}
// Check to throws invalid options
{
assert.throws(() => new SyntheticModule([], () => {}, null), {
message: 'The "options" argument must be of type object.' +
' Received null',
name: 'TypeError'
});
}
// Test compileFunction importModuleDynamically
{
const module = new SyntheticModule([], () => {});
module.link(() => {});
const f = compileFunction('return import("x")', [], {
importModuleDynamically(specifier, referrer) {
assert.strictEqual(specifier, 'x');
assert.strictEqual(referrer, f);
return module;
},
});
f().then((ns) => {
assert.strictEqual(ns, module.namespace);
});
}