feat: Force system principal scope access (forceScopeAccess)

A workaround to restore some original Playwright functionality.

Forces Playwright to run in "God mode", allowing it to bypass CORS restrictions, access shadow roots (with element.shadowRootUnl), access undocumented JS browser methods, modify DOM without `allowMainWorld`, & restore some unsupported Playwright functionality.

Note: `forceScopeAccess` is not detectable/visible to the page unless the dom is directly modified (elements are added or removed).
This commit is contained in:
daijro 2025-02-03 20:49:42 -06:00
parent 0dabcb726d
commit cf269d6b60
4 changed files with 54 additions and 11 deletions

View file

@ -411,7 +411,11 @@ class Frame {
this._parentFrame = parentFrame; this._parentFrame = parentFrame;
parentFrame._children.add(this); parentFrame._children.add(this);
} }
this.allowMW = ChromeUtils.camouGetBool('allowMainWorld', false);
this.forceScopeAccess = ChromeUtils.camouGetBool('forceScopeAccess', false);
this.masterSandbox = undefined;
this._lastCommittedNavigationId = null; this._lastCommittedNavigationId = null;
this._pendingNavigationId = null; this._pendingNavigationId = null;
@ -505,21 +509,44 @@ class Frame {
}; };
} }
_createIsolatedContext(name) { // Camoufox: Add a "God mode" master sandbox with it's own compartment
const principal = [this.domWindow()]; // extended principal getMasterSandbox() {
const sandbox = Cu.Sandbox(principal, { if (!this.masterSandbox) {
sandboxPrototype: this.domWindow(), this.masterSandbox = Cu.Sandbox(
wantComponents: false, Services.scriptSecurityManager.getSystemPrincipal(),
wantExportHelpers: false, {
wantXrays: true, sandboxPrototype: this.domWindow(),
}); wantComponents: false,
wantExportHelpers: false,
wantXrays: true,
freshCompartment: true,
}
);
}
return this.masterSandbox;
}
_createIsolatedContext(name, useMaster=false) {
let sandbox;
// Camoufox: Use the master sandbox (with system principle scope access)
if (useMaster && this.forceScopeAccess) {
sandbox = this.getMasterSandbox();
} else {
// Standard access (run in domWindow principal)
sandbox = Cu.Sandbox([this.domWindow()], {
sandboxPrototype: this.domWindow(),
wantComponents: false,
wantExportHelpers: false,
wantXrays: true,
});
}
const world = this._runtime.createExecutionContext(this.domWindow(), sandbox, { const world = this._runtime.createExecutionContext(this.domWindow(), sandbox, {
frameId: this.id(), frameId: this.id(),
name, name,
}); });
// Camoufox: Create a main world for the isolated context // Camoufox: Create a main world for the isolated context
if (ChromeUtils.camouGetBool('allowMainWorld', false)) { if (this.allowMW) {
const mainWorld = this._runtime.createMW(this.domWindow(), this.domWindow()); const mainWorld = this._runtime.createMW(this.domWindow(), sandbox);
world.mainEquivalent = mainWorld; world.mainEquivalent = mainWorld;
} }
this._worldNameToContext.set(name, world); this._worldNameToContext.set(name, world);
@ -559,7 +586,7 @@ class Frame {
this._runtime.destroyExecutionContext(context); this._runtime.destroyExecutionContext(context);
this._worldNameToContext.clear(); this._worldNameToContext.clear();
// Camoufox: Scope the initial execution context to prevent leaks // Camoufox: Scope the initial execution context to prevent leaks
this._createIsolatedContext(''); this._createIsolatedContext('', true);
for (const [name, world] of this._frameTree._isolatedWorlds) { for (const [name, world] of this._frameTree._isolatedWorlds) {
if (name) if (name)
this._createIsolatedContext(name); this._createIsolatedContext(name);

View file

@ -0,0 +1,14 @@
diff --git a/dom/webidl/Element.webidl b/dom/webidl/Element.webidl
index 7163fac826..c0c201e29d 100644
--- a/dom/webidl/Element.webidl
+++ b/dom/webidl/Element.webidl
@@ -288,6 +288,9 @@ partial interface Element {
[BinaryName="shadowRootByMode"]
readonly attribute ShadowRoot? shadowRoot;
+ [Func="Document::IsCallerChromeOrAddon", BinaryName="shadowRoot"]
+ readonly attribute ShadowRoot? shadowRootUnl;
+
[Func="Document::IsCallerChromeOrAddon", BinaryName="shadowRoot"]
readonly attribute ShadowRoot? openOrClosedShadowRoot;

View file

@ -292,6 +292,7 @@
"showcursor": "bool", "showcursor": "bool",
"allowMainWorld": "bool", "allowMainWorld": "bool",
"forceScopeAccess": "bool",
"memorysaver": "bool", "memorysaver": "bool",
"addons": "array[str]", "addons": "array[str]",
"debug": "bool" "debug": "bool"

View file

@ -94,6 +94,7 @@
{ "property": "mediaDevices:speakers", "type": "uint" }, { "property": "mediaDevices:speakers", "type": "uint" },
{ "property": "mediaDevices:enabled", "type": "bool" }, { "property": "mediaDevices:enabled", "type": "bool" },
{ "property": "allowMainWorld", "type": "bool" }, { "property": "allowMainWorld", "type": "bool" },
{ "property": "forceScopeAccess", "type": "bool" },
{ "property": "memorysaver", "type": "bool" }, { "property": "memorysaver", "type": "bool" },
{ "property": "addons", "type": "array" }, { "property": "addons", "type": "array" },
{ "property": "debug", "type": "bool" } { "property": "debug", "type": "bool" }