Skip to content

Commit

Permalink
lib: reset RegExp statics before running user code
Browse files Browse the repository at this point in the history
Fixes: #43740
  • Loading branch information
aduh95 committed Jul 12, 2022
1 parent 98ade2d commit b0b8253
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 4 deletions.
5 changes: 5 additions & 0 deletions lib/internal/main/run_main_module.js
@@ -1,5 +1,7 @@
'use strict';

const { RegExpPrototypeExec } = primordials;

const {
prepareMainThreadExecution
} = require('internal/bootstrap/pre_execution');
Expand All @@ -8,6 +10,9 @@ prepareMainThreadExecution(true);

markBootstrapComplete();

// Necessary to reset RegExp statics before user code runs.
RegExpPrototypeExec(/^/, '');

// Note: this loads the module through the ESM loader if the module is
// determined to be an ES module. This hangs from the CJS module loader
// because we currently allow monkey-patching of the module loaders
Expand Down
7 changes: 3 additions & 4 deletions lib/internal/modules/esm/translators.js
Expand Up @@ -11,7 +11,7 @@ const {
SafeArrayIterator,
SafeMap,
SafeSet,
StringPrototypeReplace,
StringPrototypeReplaceAll,
StringPrototypeSlice,
StringPrototypeStartsWith,
SyntaxErrorPrototype,
Expand Down Expand Up @@ -144,14 +144,13 @@ function enrichCJSError(err, content, filename) {

// Strategy for loading a node-style CommonJS module
const isWindows = process.platform === 'win32';
const winSepRegEx = /\//g;
translators.set('commonjs', async function commonjsStrategy(url, source,
isMain) {
debug(`Translating CJSModule ${url}`);

let filename = internalURLModule.fileURLToPath(new URL(url));
if (isWindows)
filename = StringPrototypeReplace(filename, winSepRegEx, '\\');
filename = StringPrototypeReplaceAll(filename, '/', '\\');

if (!cjsParse) await initCJSParse();
const { module, exportNames } = cjsPreparseModuleExports(filename);
Expand Down Expand Up @@ -274,7 +273,7 @@ translators.set('json', async function jsonStrategy(url, source) {
let module;
if (pathname) {
modulePath = isWindows ?
StringPrototypeReplace(pathname, winSepRegEx, '\\') : pathname;
StringPrototypeReplaceAll(pathname, '/', '\\') : pathname;
module = CJSModule._cache[modulePath];
if (module && module.loaded) {
const exports = module.exports;
Expand Down
3 changes: 3 additions & 0 deletions lib/internal/process/execution.js
@@ -1,6 +1,7 @@
'use strict';

const {
RegExpPrototypeExec,
globalThis,
} = primordials;

Expand Down Expand Up @@ -45,6 +46,7 @@ function evalModule(source, print) {
}
const { loadESM } = require('internal/process/esm_loader');
const { handleMainPromise } = require('internal/modules/run_main');
RegExpPrototypeExec(/^/, ''); // Necessary to reset RegExp statics before user code runs.
return handleMainPromise(loadESM((loader) => loader.eval(source)));
}

Expand Down Expand Up @@ -72,6 +74,7 @@ function evalScript(name, body, breakFirstLine, print) {
return (main) => main();
`;
globalThis.__filename = name;
RegExpPrototypeExec(/^/, ''); // Necessary to reset RegExp statics before user code runs.
const result = module._compile(script, `${name}-wrapper`)(() =>
require('vm').runInThisContext(body, {
filename: name,
Expand Down
68 changes: 68 additions & 0 deletions test/parallel/test-startup-empty-regexp-statics.js
@@ -0,0 +1,68 @@
'use strict';

const common = require('../common');
const assert = require('node:assert');
const { spawnSync, spawn } = require('node:child_process');

assert.strictEqual(RegExp.$_, '');
assert.strictEqual(RegExp.$0, undefined);
assert.strictEqual(RegExp.$1, '');
assert.strictEqual(RegExp.$2, '');
assert.strictEqual(RegExp.$3, '');
assert.strictEqual(RegExp.$4, '');
assert.strictEqual(RegExp.$5, '');
assert.strictEqual(RegExp.$6, '');
assert.strictEqual(RegExp.$7, '');
assert.strictEqual(RegExp.$8, '');
assert.strictEqual(RegExp.$9, '');
assert.strictEqual(RegExp.input, '');
assert.strictEqual(RegExp.lastMatch, '');
assert.strictEqual(RegExp.lastParen, '');
assert.strictEqual(RegExp.leftContext, '');
assert.strictEqual(RegExp.rightContext, '');
assert.strictEqual(RegExp['$&'], '');
assert.strictEqual(RegExp['$`'], '');
assert.strictEqual(RegExp['$+'], '');
assert.strictEqual(RegExp["$'"], '');

const allRegExpStatics =
'RegExp.$_ + RegExp["$&"] + RegExp["$`"] + RegExp["$+"] + RegExp["$\'"] + ' +
'RegExp.input + RegExp.lastMatch + RegExp.lastParen + ' +
'RegExp.leftContext + RegExp.rightContext + ' +
Array.from({ length: 10 }, (_, i) => `RegExp.$${i}`).join(' + ');

{
const child = spawnSync(process.execPath,
[ '-p', allRegExpStatics ],
{ stdio: ['inherit', 'pipe', 'inherit'] });
assert.match(child.stdout.toString(), /^undefined\r?\n$/);
assert.strictEqual(child.status, 0);
assert.strictEqual(child.signal, null);
}

{
const child = spawnSync(process.execPath,
[ '-e', `console.log(${allRegExpStatics})`, '--input-type=module' ],
{ stdio: ['inherit', 'pipe', 'inherit'] });
assert.match(child.stdout.toString(), /^undefined\r?\n$/);
assert.strictEqual(child.status, 0);
assert.strictEqual(child.signal, null);
}

{
const child = spawn(process.execPath, [], { stdio: ['pipe', 'pipe', 'inherit'], encoding: 'utf8' });

let stdout = '';
child.stdout.on('data', (chunk) => {
stdout += chunk;
});

child.on('exit', common.mustCall((status, signal) => {
assert.match(stdout, /^undefined\r?\n$/);
assert.strictEqual(status, 0);
assert.strictEqual(signal, null);
}));
child.on('error', common.mustNotCall());

child.stdin.end(`console.log(${allRegExpStatics});\n`);
}
30 changes: 30 additions & 0 deletions test/parallel/test-startup-empty-regexp-statics.mjs
@@ -0,0 +1,30 @@
// We must load the CJS version here because the ESM wrapper call `hasIPv6`
// which compiles a RegEx.
// eslint-disable-next-line node-core/require-common-first
import common from '../common/index.js';

import assert from 'node:assert';

// TODO(aduh95): make this test pass on Windows.
if (common.isWindows) common.skip('Test fails on Windows');

assert.strictEqual(RegExp.$_, '');
assert.strictEqual(RegExp.$0, undefined);
assert.strictEqual(RegExp.$1, '');
assert.strictEqual(RegExp.$2, '');
assert.strictEqual(RegExp.$3, '');
assert.strictEqual(RegExp.$4, '');
assert.strictEqual(RegExp.$5, '');
assert.strictEqual(RegExp.$6, '');
assert.strictEqual(RegExp.$7, '');
assert.strictEqual(RegExp.$8, '');
assert.strictEqual(RegExp.$9, '');
assert.strictEqual(RegExp.input, '');
assert.strictEqual(RegExp.lastMatch, '');
assert.strictEqual(RegExp.lastParen, '');
assert.strictEqual(RegExp.leftContext, '');
assert.strictEqual(RegExp.rightContext, '');
assert.strictEqual(RegExp['$&'], '');
assert.strictEqual(RegExp['$`'], '');
assert.strictEqual(RegExp['$+'], '');
assert.strictEqual(RegExp["$'"], '');

0 comments on commit b0b8253

Please sign in to comment.