Permission denied, With Web Components in Firefox Extension Content Scripts
Categories
(WebExtensions :: General, defect, P3)
Tracking
(Not tracked)
People
(Reporter: cerebralasylum1, Unassigned)
References
Details
Attachments
(5 files, 1 obsolete file)
Comment 4•6 years ago
|
||
Updated•6 years ago
|
Updated•6 years ago
|
Comment 6•6 years ago
|
||
Comment 7•6 years ago
|
||
I gave both these issues (Bug 1492002 and Bug 1488010) another look, and in the end this doesn't look like a dupe Bug 1488010,
because this one is being triggered on the native WebComponents implementation (whereas Bug 1488010 is specifically related to
issues with using the WebComponents polyfill from a content script).
The "permission denied" errors are triggered when js code running with the webpage principal tries to access a property of a DOM element created for a WebComponent registered from the content script (that's the reason why example.com doesn't trigger the error: the webpage js code is not accessing that DOM element, whereas facebook and google have code that is trying to access the DOM element property when an event is triggered on it).
The same kind of errors can be triggered from the webconsole by running something like:
document.querySelector("my-element").tagName
(because running the above code in the webconsole is going to access the element tagName
property as regular js code running in that webpage).
The permission denied is being triggered from the cross compartment security wrappers, likely because the WebComponent is registered from a content script which is running in a sandbox with an expanded principal.
The following is the stack trace when the permission denied error is triggered:
#0 0x00007fffe492e936 in js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext*, JS::Handle<JS::PropertyKey>) (this=0x7fffffff8d50, cx=0x7fffd8125000, id=...)
at js/src/proxy/Proxy.cpp:34
#1 0x00007fffe493ce2a in js::AutoEnterPolicy::AutoEnterPolicy(JSContext*, js::BaseProxyHandler const*, JS::Handle<JSObject*>, JS::Handle<JS::PropertyKey>, unsigned int, bool) (this=0x7fffffff8d50, cx=0x7fffd8125000, handler=
0x7fffed07db20 <xpc::FilteringWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::Opaque>::singleton>, wrapper=..., id=..., act=1, mayThrow=true)
at objdir-build-debug-icecc/dist/include/js/Proxy.h:615
#2 0x00007fffe493d2ea in js::Proxy::getInternal(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>, JS::Handle<JS::PropertyKey>, JS::MutableHandle<JS::Value>) (cx=0x7fffd8125000, proxy=..., receiver=..., id=..., vp=...)
at js/src/proxy/Proxy.cpp:322
#3 0x00007fffe492fed6 in js::Proxy::get(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>, JS::Handle<JS::PropertyKey>, JS::MutableHandle<JS::Value>) (cx=0x7fffd8125000, proxy=..., receiver_=..., id=..., vp=...)
at js/src/proxy/Proxy.cpp:352
#4 0x00007fffe423a71b in js::GetProperty(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>, JS::Handle<JS::PropertyKey>, JS::MutableHandle<JS::Value>) (cx=0x7fffd8125000, obj=..., receiver=..., id=..., vp=...)
at js/src/vm/ObjectOperations-inl.h:114
#5 0x00007fffe458254f in GeneralizedGetProperty(JSContext*, JS::HandleObject, JS::HandleId, JS::HandleValue, IsNameLookup, JS::MutableHandleValue) (cx=0x7fffd8125000, obj=..., id=..., receiver=..., nameLookup=NotNameLookup, vp=...)
at js/src/vm/NativeObject.cpp:2474
#6 0x00007fffe45902d4 in NativeGetPropertyInline<(js::AllowGC)1>(JSContext*, js::MaybeRooted<js::NativeObject*, (js::AllowGC)1>::HandleType, js::MaybeRooted<JS::Value, (js::AllowGC)1>::HandleType, js::MaybeRooted<JS::PropertyKey, (js::AllowGC)1>::HandleType, IsNameLookup, js::MaybeRooted<JS::Value, (js::AllowGC)1>::MutableHandleType) (cx=0x7fffd8125000, obj=..., receiver=..., id=..., nameLookup=NotNameLookup, vp=...)
at js/src/vm/NativeObject.cpp:2568
...
Based on the above stack trace, the error is raised from FilteringWrapper<CrossCompartmentWrapper, Opaque>
's enter method, and so at a first glance it seems that the instances of a WebComponent registered from the content script compartment are being wrapped and made not accessible to the JS code running with the webpage principal.
I'm attaching a patch which contains a small test case that reproduces the issue with the minimum amount of extension code
(by showing that the tagName property is accessible from chrome privileged code, but throws when accessed by webpage-privileged code),
so that we may ask someone that works on the Core::XPConnect or WebComponents internals to take a look and help us to determine a proper approach to move this forward.
Comment 8•6 years ago
|
||
Referencing this extension issue here as example: https://github.com/GoogleChromeLabs/ProjectVisBug/issues/102#issuecomment-474689865
Comment 10•5 years ago
|
||
Updated minimal test case to also include assertion for the additional unexpected behavior reported in Bug 1614251.
As an additional detail, based on another look to what is happening internally when a similar minimal WebComponent definition is being added to the document's custom elements registry from a content script and from the webpage itself, follows the output of dumping the prototype object we get in CustomElementRegistry::Define
:
CustomElement registered from the webpage
(rr) call prototype.ptr.toObject().dump()
object 112f02fc6d90
global ed925107060 [Window]
class 7fcd134ef0e8 Object
group ced366471c0
flags:
proto <HTMLElementPrototype object at ed92510a560>
properties:
[Latin 1]"constructor": <function MyPageElement at ced3662a510> (shape ced3664c970 slot 0)
[Latin 1]"myCustomMethod": <function myCustomMethod at ced3662a560> (shape ced3664c998 slot 1))
CustomElement registered from the content script
(rr) p prototype.ptr.toObject().dump()
object 112f02f19d30
global ed925107100 [Sandbox]
class 7fcd134ef0e8 Object
group ed925186790
flags:
proto <Proxy object at ed925128440>
properties:
[Latin 1]"constructor": <function MyContentScriptElement (moz-extension://28876aca-d417-489b-9b52-20aa27954424/content_script.js:5) at 112f02f19d50> (shape ed92519a218 slot 0)
[Latin 1]"myCustomMethod": <function myCustomMethod (moz-extension://28876aca-d417-489b-9b52-20aa27954424/content_script.js:10) at 112f02f19e20> (shape ed92519a240 slot 1)
Comment 11•5 years ago
|
||
Hi Olli,
could you take a look to the details from Comment 10 (and Comment 7, which may also provide some additional context) to double-check if these details are of any help to get a better picture about what is going on for the custom elements defined from a content script?
(or if there is something else that I may look into to get something actually useful to understand this issue better and determine what we could reasonably do about it)
Comment 12•5 years ago
|
||
So webextension side of this stuff just must run using the same principal as the webpage. It is similar if one would create some object on extension side and use that as a prototype of some object in the web page.
How does web extensions in general deal with such case? Is there some API to execute scripts using web page principals?
Comment 13•5 years ago
|
||
(In reply to Olli Pettay [:smaug] from comment #12)
So webextension side of this stuff just must run using the same principal as the webpage. It is similar if one would create some object on extension side and use that as a prototype of some object in the web page.
How does web extensions in general deal with such case? Is there some API to execute scripts using web page principals?
There isn't any WebExtensions API to execute scripts using the web page principals (well, excluding browser.devtools.inspectedWindow.eval [1]),
but:
- we do provide to the content script sandbox the export helpers (
exportFunction
,cloneInto
andcreateObjectIn
) - the content scripts can also create a script tag and inject it into the webpage on which the content script is attached (this is a pretty common techniques, because it isn't blocked by the websites CSP as
window.eval
would be)
At the moment a content script can successfully register a new CustomElement to a webpage in a simple and reliable way by using this last strategy (injecting a script tag into the webpage).
I also tried to play a bit with the export helpers to double-check how far that would be able to go... but it looks like a rabbit role :-)
As we were discussing over Matrix yesterday, the issue that a CustomElement defined in a content script is triggering is likely not that different from what we may trigger by just changing something in another prototype chain, and I'm pretty sure that that is definitely the case:
As an example I recall that Thomas Wisniewski was working on an addon that was overriding some WebAPIs available to a webpage from a content script and from what I recall he was also running into a number of issues and corner cases (the repository should be https://github.com/webcompat/tinker-tester-developer-spy and if I'm not wrong it is currently running the code that applies the overrides using window.eval).
Thomas may recall more details about that experience.
[1]: browser.devtools.inspectedWindow.eval is an API method which evaluates the given js code similarly to how the webconsole does, but it is an API method only available in devtools_page and devtools_panel extension context that are extension contexts that only exist when a developer toolbox is open, any other extension page and the content scripts do not have direct access to this API method
Comment 14•3 years ago
|
||
Is this still being investigated?
It's becoming a serious issue with more and more extensions using custom elements in content scripts, and some websites relying on reading properties from these elements.
For example, websites using older versions of Bootstrap and jQuery (bootstrap 3.x
, jquery 1.x
) are all throwing errors.
Easiest way to reproduce is to use any extension with custom elements in the content script, and access the Bootstrap 3 documentation (https://getbootstrap.com/docs/3.3/). Clicking anywhere on the page will throw Permission denied to access property "ownerDocument"
.
Other websites like http://guayama.inter.edu/ (also using Bootstrap 3), will not allow any interactivity because of trying to read attributes
on the custom elements, and throws Permission denied to access property "attributes"
.
The only workaround I've found so far is to return a Proxy from the custom element constructor:
class extends HTMLElement {
constructor() {
super()
return new Proxy(this, {})
}
}
But, it doesn't seem like a reliable approach, if this is an isolation issue that can change at any time.
Comment 15•3 years ago
|
||
I've posted question about interoperability of webextensions and custom elements in w3c working group on extensions (https://github.com/w3c/webextensions/issues/210/).
Current (weak) consensus is to disable custom elements in content scripts, though no one actually bothered to implement it in Firefox.
More or less "clean" approach to custom components I see is exporting element definition code to the page script and utilize window.postMessage
to communicate with content script.
Updated•2 years ago
|
Comment 16•2 years ago
|
||
We seem to be in the same boat as we are in the process of porting our Chrome extension to Firefox. We have a significant user base of more than 2 million users and have encountered similar issues to the ones you are experiencing.
A few days ago, we submitted a detailed report of a bug that's mirroring your problem. You may find the report at the following link: https://bugzilla.mozilla.org/show_bug.cgi?id=1836269
Please feel free to take a look at the extension that we are attempting to port to Firefox's Manifest V3 (MV3): https://chrome.google.com/webstore/detail/deepl-translate-reading-w/cofdbpoegempjloogbagkncekinflcnj
Description
•