Open Bug 1492002 Opened 6 years ago Updated 9 months ago

Permission denied, With Web Components in Firefox Extension Content Scripts

Categories

(WebExtensions :: General, defect, P3)

64 Branch
defect

Tracking

(Not tracked)

UNCONFIRMED

People

(Reporter: cerebralasylum1, Unassigned)

References

Details

Attachments

(5 files, 1 obsolete file)

Attached file Code to test bug.
User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36

Steps to reproduce:

Download the code from here https://github.com/brendena/FirefoxWebComponentExtension/tree/Events or by grabbing the attached zip file.  Load the extension into firefox.  You will need firefox 63 or above to test this because currently the polyfill for web components doesn't work in content scripts https://bugzilla.mozilla.org/show_bug.cgi?id=1488010. 




Actual results:

When you have the app up and running in the top left there will be a white box with a few elements.  What seems to be happening is whenever you cause a event, like clicking the button or entering text in the text box, errors will be thrown.  The error are different on different sites.   


Expected results:

There shouldn't be any error because its in content script.  The cause is something with web components inside in the content scripts.  I think this because i've added code to append the same item element without being inside a web component and i don't get that error.  I also added a html page that load the web component and it worked.  

There also could be a component of website privileges since the extension work without errors on http://whitedisplay.com/.
Attached image OutputFacebook.png
Attached image OutputGithub.png
Attached image OutputGoogle.png
Thanks for filing! I read the code but I am not entirely sure how/where the error was thrown.

Did you try to access any of the properties on any of the native objects like event? The content script runs on a separate compartment, so that it won't accidentally access the properties of the native objects that the page script had modified. Have you tried to access the properties with wrappedJSObject?

https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/wrappedJSObject

If that works this is a WONTFIX since Firefox is working as intended. If there is no way to do what you wanted, then perhaps something needs to be fixed...
Product: Firefox → WebExtensions
Flags: needinfo?(cerebralasylum1)
(In reply to Tim Guan-tin Chien [:timdream] (please needinfo) from comment #4)
> Thanks for filing! I read the code but I am not entirely sure how/where the
> error was thrown.
> 
> Did you try to access any of the properties on any of the native objects
> like event? The content script runs on a separate compartment, so that it
> won't accidentally access the properties of the native objects that the page
> script had modified. Have you tried to access the properties with
> wrappedJSObject?
> 
> https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/wrappedJSObject
> 
> If that works this is a WONTFIX since Firefox is working as intended. If
> there is no way to do what you wanted, then perhaps something needs to be
> fixed...

What I'm doing is creating a web component then adding it to the dom from a content script.  I'm getting these errors when I trigger any sort of event.  Events like click, hover or onInput all causing different Permissions errors on different sites.  What's weird is it doesn't matter whether I'm listening for these events or not, I will get permission errors.  I also only get these errors when they're inside of a web component.  If I add these elements to the dom normally I don't receive any error's.


As to your question i'm not really accessing any native object.  The only native object i accessed is the body to add my component and adding wrappedJSObject didn't seem to help.
Flags: needinfo?(cerebralasylum1)
Priority: -- → P3
Might be duped for bug 1488010.

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.

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)
Attachment #9051700 - Attachment is obsolete: true

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)

Flags: needinfo?(bugs)

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?

Flags: needinfo?(bugs)

(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 and createObjectIn)
  • 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

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.

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.

Severity: normal → S3

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

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: