createDynamicModule() properly escapes import names, but not export names. In WebAssembly, any string is a valid export name. Importing a WebAssembly module that uses a non-identifier export name leads to either a syntax error in createDynamicModule() or to code injection, that is, to the evaluation of almost arbitrary JavaScript code outside of the WebAssembly module. To address this issue, adopt the same mechanism in createExport() that createImport() already uses. Add tests for both exports and imports. PR-URL: https://github.com/nodejs-private/node-private/pull/461 Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/490 Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> CVE-ID: CVE-2023-39333
93 lines
3.1 KiB
JavaScript
93 lines
3.1 KiB
JavaScript
import { spawnPromisified } from '../common/index.mjs';
|
|
import * as fixtures from '../common/fixtures.mjs';
|
|
import { strictEqual, match } from 'node:assert';
|
|
import { execPath } from 'node:process';
|
|
import { describe, it } from 'node:test';
|
|
|
|
|
|
describe('ESM: WASM modules', { concurrency: true }, () => {
|
|
it('should load exports', async () => {
|
|
const { code, stderr, stdout } = await spawnPromisified(execPath, [
|
|
'--no-warnings',
|
|
'--experimental-wasm-modules',
|
|
'--input-type=module',
|
|
'--eval',
|
|
[
|
|
'import { strictEqual, match } from "node:assert";',
|
|
`import { add, addImported } from ${JSON.stringify(fixtures.fileURL('es-modules/simple.wasm'))};`,
|
|
`import { state } from ${JSON.stringify(fixtures.fileURL('es-modules/wasm-dep.mjs'))};`,
|
|
'strictEqual(state, "WASM Start Executed");',
|
|
'strictEqual(add(10, 20), 30);',
|
|
'strictEqual(addImported(0), 42);',
|
|
'strictEqual(state, "WASM JS Function Executed");',
|
|
'strictEqual(addImported(1), 43);',
|
|
].join('\n'),
|
|
]);
|
|
|
|
strictEqual(stderr, '');
|
|
strictEqual(stdout, '');
|
|
strictEqual(code, 0);
|
|
});
|
|
|
|
it('should not allow code injection through export names', async () => {
|
|
const { code, stderr, stdout } = await spawnPromisified(execPath, [
|
|
'--no-warnings',
|
|
'--experimental-wasm-modules',
|
|
'--input-type=module',
|
|
'--eval',
|
|
`import * as wasmExports from ${JSON.stringify(fixtures.fileURL('es-modules/export-name-code-injection.wasm'))};`,
|
|
]);
|
|
|
|
strictEqual(stderr, '');
|
|
strictEqual(stdout, '');
|
|
strictEqual(code, 0);
|
|
});
|
|
|
|
it('should allow non-identifier export names', async () => {
|
|
const { code, stderr, stdout } = await spawnPromisified(execPath, [
|
|
'--no-warnings',
|
|
'--experimental-wasm-modules',
|
|
'--input-type=module',
|
|
'--eval',
|
|
[
|
|
'import { strictEqual } from "node:assert";',
|
|
`import * as wasmExports from ${JSON.stringify(fixtures.fileURL('es-modules/export-name-syntax-error.wasm'))};`,
|
|
'assert.strictEqual(wasmExports["?f!o:o<b>a[r]"]?.value, 12682);',
|
|
].join('\n'),
|
|
]);
|
|
|
|
strictEqual(stderr, '');
|
|
strictEqual(stdout, '');
|
|
strictEqual(code, 0);
|
|
});
|
|
|
|
it('should properly escape import names as well', async () => {
|
|
const { code, stderr, stdout } = await spawnPromisified(execPath, [
|
|
'--no-warnings',
|
|
'--experimental-wasm-modules',
|
|
'--input-type=module',
|
|
'--eval',
|
|
[
|
|
'import { strictEqual } from "node:assert";',
|
|
`import * as wasmExports from ${JSON.stringify(fixtures.fileURL('es-modules/import-name.wasm'))};`,
|
|
'assert.strictEqual(wasmExports.xor(), 12345);',
|
|
].join('\n'),
|
|
]);
|
|
|
|
strictEqual(stderr, '');
|
|
strictEqual(stdout, '');
|
|
strictEqual(code, 0);
|
|
});
|
|
|
|
it('should emit experimental warning', async () => {
|
|
const { code, signal, stderr } = await spawnPromisified(execPath, [
|
|
'--experimental-wasm-modules',
|
|
fixtures.path('es-modules/wasm-modules.mjs'),
|
|
]);
|
|
|
|
strictEqual(code, 0);
|
|
strictEqual(signal, null);
|
|
match(stderr, /ExperimentalWarning/);
|
|
match(stderr, /WebAssembly/);
|
|
});
|
|
});
|