Open Bug 1706787 Opened 3 years ago Updated 6 months ago

Extension gets CSP error appending an inline <style> node with uninitialized textContent (CSP: style-src 'self')

Categories

(WebExtensions :: General, defect, P5)

defect

Tracking

(Not tracked)

REOPENED

People

(Reporter: saschanaz, Unassigned)

References

()

Details

Attachments

(2 files)

Attached file aaa-site.zip
  1. Install the latest Node.js
  2. Unzip the attachment
  3. Run node run.js on the directory
  4. Go about:debugging#/runtime/this-firefox
  5. Click "Load Temporary Add-on" and select extensions/manifest.json.
  6. Open 0.0.0.0:3000
  7. Open devtools and see there is a CSP error

Expected: No issue
Actual: Content Security Policy: The page’s settings blocked the loading of a resource at inline (“style-src”).

Why is that a bug? The CSP check is run before we "load" things so we don't know it's an empty <style> block at that point. But the worst that happens is that CSP doesn't process the block that does nothing, so either way nothing happens.

I suspect this bug is invalid according to the spec (which is writen in terms of "fetch"), but either way we're not going to fix it given the rewrite it would take for essentially no benefit (except less reporting I guess).

Status: NEW → RESOLVED
Closed: 3 years ago
Resolution: --- → WONTFIX

By "empty" I mean an element created by document.createElement("style") without any src, so I think it should be able to check the emptyness? I mean, it shouldn't fetch anything, right?

Flags: needinfo?(dveditz)

Just for easier comment, this is what the extension script do. (extension/index.js from the zip file)

const elementWithTextContent = document.createElement("style"); 
elementWithTextContent.textContent = 'p { color: red }';
document.head.appendChild(elementWithTextContent) // No CSP errors.

const normalElement = document.createElement("style");
document.head.appendChild(normalElement) // Observer CSP errors.

And now that I've looked at the GH issue I see that you've already run this code from Gusted so I guess it's safe?

The node script is 7 lines -- that's small enough to see that it's safe. when the GH issue talked about "hardened" sites I imagined lots of impenetrable library code. I'm going to delete my previous comment because I missed the point entirely. both injections are into the <head>. Both should be allowed because it's an extension doing the injecting. The difference is that one IS empty in the sense I thought you meant originally. It shouldn't matter, but it does. (Is it empty, or is it an initialization problem? if we explicitly set textContent = "";, which should be the default, does the error go away?)

Why would an empty or uninitialized textContent even matter? CSP shouldn't care what is in the node; decisions are made based on where it is (in this case "inline") and who created it (an extension, not limited by the page's CSP), not what's in it.

Can you package this into a minimally-permissioned addon I can run in a test profile without all that?

Comment #0 lists the way to load the package with the attached zip file. Following the steps loads the script as a temporary addon.

Is the addon itself doing the style injection?
Is it the same script doing the injecting into head and body?

Yes, the addon runs a content script where all the injection happens.

Flags: needinfo?(krosylight)

And now that I've looked at the GH issue I see that you've already run this code from Gusted so I guess it's safe?

Yeah, I read it and it does nothing harmful 😀 It's just a 22-line file anyway. (And the Node.js script is just a minimal code to open a local server which sends CSP header.)

(In reply to Daniel Veditz [:dveditz] from comment #5)

Gusted

Hmm that's a name I know, It's me!

Just going to chip in to steer this bug into hopefully the right direction and give some insight what is going on.

So in plain text this bug is:
Extension x want's to inject a normal style without any textContent into page x that has within thr CSP header: style-src 'self'; and get's an CSP error thrown at you, while when you provide a textContent it won't.

So hence the run.js to simulate the style-src: 'self'; as with any normal website it won't happend, it's specific to the style-src: 'self'; behavior so far I can reproduce. If you like,
some sites that I came across with this style-src: 'self'; (so won't have to node on the run.js, which I perfectly understand), archlinux . org and privacytools . io has this header. So the extension can be modified to either of those sites for permission and it will still reproduce this error. I've attached the extension modified to those 2 sites.

So yes, the addon is doing the injection and should like you mention bypass this CSP behavior as otherwise we couldn't deliver our extensions' purpose to those sites. But in this case when textContent is empty, and please note that when you set textContent to '' it WONT fail. < This test-case is also now included in the new attached extension.

Look at that, it IS an initialization issue? Looking https://github.com/darkreader/darkreader/pull/2359 their fix was literally just that:

function fixFirefoxCSPIssue() {
        // Some websites get CSP warning,
        // when `textContent` is not set
        if (corsCopy && !corsCopy.textContent) {
            corsCopy.textContent = '';
        }
       ...

Wild!

My wild guess is that mTriggeringPrincipal allows us to bypass CSP within addon scripts, and setting .textContent gives it a proper value.

https://searchfox.org/mozilla-central/rev/4648b6ee31c2519b1753023e4f4853b14fdd16e5/dom/html/HTMLStyleElement.cpp#152

In the case with no .textContent I guess it's not getting a valid mTriggeringPrincipal so no bypass?

(In reply to Daniel Veditz [:dveditz] from comment #9)

Look at that, it IS an initialization issue? Looking https://github.com/darkreader/darkreader/pull/2359 their fix was literally just that:
[...]
Wild!

Seems like it, but just little off-topic, that fix wasn't bullet-proof, due to the weird issues of having sheet not set on elements, see commit:
https://github.com/darkreader/darkreader/commit/4ed02c27b770e43b884b68cfe8945f5034660567

Summary: `CSP: style-src 'self'` conflicts with empty HTMLStyleElement → Extension gets CSP error appending an inline <style> node with uninitialized textContent (CSP: style-src 'self')

(In reply to Kagami :saschanaz from comment #10)

In the case with no .textContent I guess it's not getting a valid mTriggeringPrincipal so no bypass?

Looks like mTriggeringPrincipal is explicitly set to null to trigger use of the document principal, from bug 1415352
https://searchfox.org/mozilla-central/rev/4648b6ee31c2519b1753023e4f4853b14fdd16e5/dom/html/HTMLStyleElement.cpp#76

But if the TriggeringPrincipal is null we use the document's CSP
https://searchfox.org/mozilla-central/source/layout/style/nsStyleUtil.cpp#289-297

... which is wrong when an extension has created the node.

Flags: needinfo?(kmaglione+bmo)

The extension created the node, but what we care about is who set the node's contents, since we wouldn't want to apply the extension's CSP to style content that a page script changed after the node was created.

In this case, though, nothing set the content. I suppose it might arguably make sense to treat the node creator as having set the contents to an empty string in that case, but it hasn't actually come up in practice.

Flags: needinfo?(kmaglione+bmo)

CSP is correct here. Not sure if the problem is the Style element code or if this is an exception that WebExtensions want to handle. Realistically this is a tiny corner case (why create a <style> element if it's got literally nothing in it?) with an easy workaround so I'm not sure the complexity of a solution is worth the risk of opening up security holes.

Component: DOM: Security → General
Flags: needinfo?(ckerschb)
Product: Core → WebExtensions

Well, people write code with different taste and different orders etc. It just so happens that we(Dark reader extension) happened to first create such element and insert it into the dom and then do the heavy lifting of what needs to be within the textContent and the workaround(having textContent before inserting the element) was pure on luck that we stumbled on that solution.

At this point it's just an issue of logspam. There aren't significant other consequences, and extensions have a way to work around the issue.

This bug could potentially be fixed by special-casing behavior of the case where the style element is uninitialized, but I don't see ourselves spending time on doing that. Patches are welcome though.

Severity: -- → S4
Priority: -- → P5

This bug makes Firefox behave like Chrome which intentionally exempts the DOM created by an ISOLATED content script from the CSP of the page. Cross-browser developers of WebExtensions wish that Firefox would behave the same for all the other types of elements in this specific scenario, which doesn't seem likely though judging by bug 1267027.

To clarify, this is not a log spam issue: the actual bug here is that CSP is ignored for a style with textContent, and I like that!

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

Attachment

General

Created:
Updated:
Size: