Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: check for Node.js-created module when contextIsolation disabled #41865

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 19 additions & 2 deletions patches/node/chore_expose_importmoduledynamically_and.patch
Expand Up @@ -82,7 +82,7 @@ index 0645b3ddf506df2a76f5661f0ec6bb35d5d8b94e..e0f1b2d51f3055b2250f2c0dc1dfd104

MaybeLocal<Value> ModuleWrap::SyntheticModuleEvaluationStepsCallback(
diff --git a/src/module_wrap.h b/src/module_wrap.h
index 58b233d036515c52d9bd5574c776c2ea65d2ecb1..5f7ef75480a76761c6fa62061c8700c812a3fc6f 100644
index 1fc801edced9c5e44613846b4dc555804c5bae97..767d183c6b6b6d97726cf5d63c0b202bd9ae10a6 100644
--- a/src/module_wrap.h
+++ b/src/module_wrap.h
@@ -30,7 +30,14 @@ enum HostDefinedOptions : int {
Expand All @@ -100,4 +100,21 @@ index 58b233d036515c52d9bd5574c776c2ea65d2ecb1..5f7ef75480a76761c6fa62061c8700c8
+class NODE_EXTERN ModuleWrap : public BaseObject {
public:
enum InternalFields {
kModuleWrapBaseField = BaseObject::kInternalFieldCount,
kModuleSlot = BaseObject::kInternalFieldCount,
@@ -65,6 +72,8 @@ class ModuleWrap : public BaseObject {
return true;
}

+ static ModuleWrap* GetFromModule(node::Environment*, v8::Local<v8::Module>);
+
private:
ModuleWrap(Environment* env,
v8::Local<v8::Object> object,
@@ -99,7 +108,6 @@ class ModuleWrap : public BaseObject {
v8::Local<v8::String> specifier,
v8::Local<v8::FixedArray> import_attributes,
v8::Local<v8::Module> referrer);
- static ModuleWrap* GetFromModule(node::Environment*, v8::Local<v8::Module>);

v8::Global<v8::Module> module_;
std::unordered_map<std::string, v8::Global<v8::Promise>> resolve_cache_;
20 changes: 15 additions & 5 deletions shell/common/node_bindings.cc
Expand Up @@ -126,6 +126,8 @@ ELECTRON_TESTING_BINDINGS(V)
#endif
#undef V

using node::loader::ModuleWrap;

namespace {

void stop_and_close_uv_loop(uv_loop_t* loop) {
Expand Down Expand Up @@ -216,16 +218,24 @@ v8::MaybeLocal<v8::Promise> HostImportModuleDynamically(
void HostInitializeImportMetaObject(v8::Local<v8::Context> context,
v8::Local<v8::Module> module,
v8::Local<v8::Object> meta) {
if (node::Environment::GetCurrent(context) == nullptr) {
node::Environment* env = node::Environment::GetCurrent(context);
if (env == nullptr) {
if (electron::IsBrowserProcess() || electron::IsUtilityProcess())
return;
return blink::V8Initializer::HostGetImportMetaProperties(context, module,
meta);
}

// If we're running with contextIsolation enabled in the renderer process,
// fall back to Blink's logic.
if (electron::IsRendererProcess()) {
// If the module is created by Node.js, use Node.js' handling.
if (env != nullptr) {
ModuleWrap* wrap = ModuleWrap::GetFromModule(env, module);
if (wrap)
return ModuleWrap::HostInitializeImportMetaObjectCallback(context,
module, meta);
}

// If contextIsolation is enabled, fall back to Blink's handling.
blink::WebLocalFrame* frame =
blink::WebLocalFrame::FrameForContext(context);
if (!frame || frame->GetScriptContextWorldId(context) !=
Expand All @@ -235,8 +245,8 @@ void HostInitializeImportMetaObject(v8::Local<v8::Context> context,
}
}

return node::loader::ModuleWrap::HostInitializeImportMetaObjectCallback(
context, module, meta);
return ModuleWrap::HostInitializeImportMetaObjectCallback(context, module,
meta);
}

v8::ModifyCodeGenerationFromStringsResult ModifyCodeGenerationFromStrings(
Expand Down
24 changes: 20 additions & 4 deletions spec/esm-spec.ts
Expand Up @@ -141,7 +141,7 @@ describe('esm', () => {
const hostsUrl = pathToFileURL(process.platform === 'win32' ? 'C:\\Windows\\System32\\drivers\\etc\\hosts' : '/etc/hosts');

describe('without context isolation', () => {
it('should use blinks dynamic loader in the main world', async () => {
it('should use Blinks dynamic loader in the main world', async () => {
const [webContents] = await loadWindowWithPreload('', {
nodeIntegration: true,
sandbox: false,
Expand All @@ -159,11 +159,27 @@ describe('esm', () => {
// This is a blink specific error message
expect(error?.message).to.include('Failed to fetch dynamically imported module');
});

it('should use import.meta callback handling from Node.js for Node.js modules', async () => {
const result = await runFixture(path.resolve(fixturePath, 'import-meta'));
expect(result.code).to.equal(0);
});
});

describe('with context isolation', () => {
it('should use nodes esm dynamic loader in the isolated context', async () => {
const [, preloadError] = await loadWindowWithPreload(`await import(${JSON.stringify(hostsUrl)})`, {
let badFilePath = '';

beforeEach(async () => {
badFilePath = path.resolve(path.resolve(os.tmpdir(), 'bad-file.badjs'));
await fs.promises.writeFile(badFilePath, 'const foo = "bar";');
});

afterEach(async () => {
await fs.promises.unlink(badFilePath);
});

it('should use Node.js ESM dynamic loader in the isolated context', async () => {
const [, preloadError] = await loadWindowWithPreload(`await import(${JSON.stringify((pathToFileURL(badFilePath)))})`, {
nodeIntegration: true,
sandbox: false,
contextIsolation: true
Expand All @@ -174,7 +190,7 @@ describe('esm', () => {
expect(preloadError!.toString()).to.include('Unknown file extension');
});

it('should use blinks dynamic loader in the main world', async () => {
it('should use Blinks dynamic loader in the main world', async () => {
const [webContents] = await loadWindowWithPreload('', {
nodeIntegration: true,
sandbox: false,
Expand Down
15 changes: 15 additions & 0 deletions spec/fixtures/esm/import-meta/index.html
@@ -0,0 +1,15 @@

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js <span id="node-version"></span>,
Chromium <span id="chrome-version"></span>,
and Electron <span id="electron-version"></span>.
</body>
</html>
33 changes: 33 additions & 0 deletions spec/fixtures/esm/import-meta/main.mjs
@@ -0,0 +1,33 @@
import { app, BrowserWindow } from 'electron'
import { fileURLToPath } from 'node:url'
import { dirname, join } from 'node:path';

async function createWindow() {
const mainWindow = new BrowserWindow({
show: false,
webPreferences: {
preload: fileURLToPath(new URL('preload.mjs', import.meta.url)),
sandbox: false,
contextIsolation: false
}
})

await mainWindow.loadFile('index.html')

const importMetaPreload = await mainWindow.webContents.executeJavaScript('window.importMetaPath');
const expected = join(dirname(fileURLToPath(import.meta.url)), 'preload.mjs');

process.exit(importMetaPreload === expected ? 0 : 1);
}

app.whenReady().then(() => {
createWindow()

app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})

app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
4 changes: 4 additions & 0 deletions spec/fixtures/esm/import-meta/package.json
@@ -0,0 +1,4 @@
{
"main": "main.mjs",
"type": "module"
}
3 changes: 3 additions & 0 deletions spec/fixtures/esm/import-meta/preload.mjs
@@ -0,0 +1,3 @@
import { fileURLToPath } from 'node:url'

window.importMetaPath = fileURLToPath(import.meta.url)