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
feat: Add content script world isolation #17032
feat: Add content script world isolation #17032
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation details of the approach seem OK, I have some questions RE the approach in general. Basically where the isolation boundary we are trying to draw is. See comments below.
// Numbers for isolated worlds for extensions are set in | ||
// lib/renderer/content-script-injector.js, and are greater than or equal to | ||
// this number. | ||
ISOLATED_WORLD_EXTENSIONS = 1000, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will have to be explicitly documented in the executeJavaScriptInIsolatedWorld
API docs that these numbers are "reserved" / "used" by chrome extensions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Since users may have use cases for a large range, perhaps the extensions range should be moved up as well (1000
=> 100000
?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've defined the range between [1 << 20, 1 << 29)
with more info in the commit message of 8d02015
lib/content_script/init.js
Outdated
const chromeAPI = require('@electron/internal/renderer/chrome-api') | ||
|
||
// Await initialization with extension ID | ||
window.__init = (extensionId) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel as though there should be a better way of doing this, it also implies that this initalizer and the users chrome extension aren't isolated?
I may be missing where the isolation boundary is that this PR is trying to create.
E.g.
Chrome Extension |||| Electron Code
but this PR appears to be more Chrome Extension + Electron Code ||| User Code
which implies a high level of trust in the Chrome Extension code? Maybe I just need some clarification on that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The __init
function is coupled with Electron code in the content-scripts-injector.ts
script. Prior to running a Chrome extension's content script, it will call the __init
function with the associated extensionId
.
const runContentScript = function (this: any, extensionId: string, url: string, code: string) {
const sources = [
// initialize Chrome API in isolated world
{ code: `typeof __init === 'function' && __init('${extensionId}')` },
// then run content script in isolated world
{ code, url }
]
// ...
webFrame.executeJavaScriptInIsolatedWorld(worldId, sources)
}
Since __init
is called first from Electron code, user code won't be able to access it after it has been removed. The exception would be if the user creates an isolated world within the reserved extension ID range, outside of the content script injector.
This still feels dirty to me, but I'm not yet sure how to initialize the content_script_bundle
with the extension ID ahead of time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MarshallOfSound I found another way to pass the extension ID to the content_script_bundle
script using a v8 private. fb42cd0
Now there is a clear isolation between user code and Electron code for initializing the content script isolated world.
I can't seem to tag cc @electron/wg-security |
ea57e9a
to
2736e61
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This all seems good, there's a few TODO's here but they're all things we didn't have before and are good to track 👍
Would like sign-off from someone else in @electron/wg-security that this is 🆗
fb42cd0
to
cf8cdcd
Compare
1 << 20 was chosen as it provides a sufficiently large range of IDs for extensions, but also provides a large enough buffer for any user worlds in [1000, 1 << 20). Ultimately this range can be changed if any user application raises it as an issue.
This now avoids a script wrapper to inject the style sheet. This closely matches the code used by chromium in `ScriptInjection::InjectCss`.
cf8cdcd
to
882adc7
Compare
Release Notes Persisted
|
* Execute content script in isolated world * Inject script into newly created extension worlds * Create new content_script_bundle for extension scripts * Initialize chrome API in content script bundle * Define Chrome extension isolated world ID range 1 << 20 was chosen as it provides a sufficiently large range of IDs for extensions, but also provides a large enough buffer for any user worlds in [1000, 1 << 20). Ultimately this range can be changed if any user application raises it as an issue. * Insert content script CSS into document This now avoids a script wrapper to inject the style sheet. This closely matches the code used by chromium in `ScriptInjection::InjectCss`. * Pass extension ID to isolated world via v8 private
Description of Change
Executes Chrome extension content scripts in an isolated world.
When an isolated world script context is created for a content script,
content_script_bundle
is injected. The bundle contains code to initialize Chrome APIs and Electron setup.Checklist
npm test
passesRelease Notes
Notes: Added world isolation to Chrome extension content scripts.