Open Bug 1467998 Opened 6 years ago Updated 2 years ago

<script> element without "cross-origin" attribute treats service worker synthesized CORS response as opaque

Categories

(Core :: DOM: Service Workers, defect, P2)

Unspecified
All
defect

Tracking

()

REOPENED
Tracking Status
firefox60 --- affected
firefox61 --- affected
firefox62 --- affected

People

(Reporter: prakashsharma97, Unassigned)

References

(Blocks 1 open bug)

Details

User Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36

Steps to reproduce:

Visit https://raw.cm2.pw/Firefox/




Actual results:

The script error, "ReferenceError: foo is not defined", is written in <textarea>. Though the `script` src is is of same-origin, the request is intercepted and changed by Service Worker which first fetches the content from https://api.ipify.org/?callback=foo&format=jsonp and appends `/* ALTERED */`. 

The content, however, is made readable with CORS policy. If the content is not readable or requested with 'no-cors', a generic "script error" is thrown. Thus, the behavior cannot be considered a security/privacy issue.


Expected results:

Errors should only be available to same-origin requests.
I managed to reproduce the issue on Firefox 60.0.2, Firefox 61.0b7, and Nightly 62.0a1 (2018-06-12), on Windows 10, Ubuntu 16.04 and Mac OS.

Marking it as new on DOM:Service Workers component.
Status: UNCONFIRMED → NEW
Component: Untriaged → DOM: Service Workers
Ever confirmed: true
OS: Unspecified → All
Product: Firefox → Core
Version: 60 Branch → Trunk
FYI, it works in all modern browsers (Safari, not testd). I'm not sure if it's intentional or if I should report it to all vendors.
The service worker script is not actually passing a cors response to respondWith().  Its doing this:

  self.addEventListener('fetch', event => {
      let [,url] = event.request.url.match(/fake\.php\?url=(.+)/) || [];
      if(url){
          console.info('Bypassing SOP for: ' + url);
          event.respondWith(modify(unescape(url)));
      }
  });

  const modify = url => {
      return fetch(url, {mode: 'cors', redirect: 'follow'}).then(response=>response.text()).then(text=>{
          if(text)    console.log(text);
          return new Response(text + '/* ALTERED */');
      });
  }

In particular note that its doing:

  return new Response(text + '/* ALTERED */');

This is synthesizing a new Response without any associated URL.  The browser has no practical way to tell if the body that was based to such a Response constructor originated from a CORS cross-origin source.

So this behavior is expected and by design.  An origin is allowed to lie to itself via its own same-origin service worker.  And we allow the service worker to read cross-origin CORS-protected data.

One question, though, if you change your test to return the CORS response from the fetch() directly without altering it, what happens?  I expect you should get muted script errors in firefox in that case.
The whole idea behind <script crossorigin> is that you get non-muted errors for cross-origin scripts. This is all by design.
Status: NEW → RESOLVED
Closed: 6 years ago
Resolution: --- → INVALID
Well, the <script> element in this case does not have the cross-origin attribute.
Ah right, in that case per the standard it should not be muted. If we do, we might want to fix that. I'll reopen.
Status: RESOLVED → REOPENED
Resolution: INVALID → ---
Priority: -- → P2
(In reply to Ben Kelly [:bkelly] from comment #3)

> One question, though, if you change your test to return the CORS response
> from the fetch() directly without altering it, what happens?  I expect you
> should get muted script errors in firefox in that case.

Yeah, if I return cors response from fetch() directly, I get generic "script errors" message.
Ok.  Anne, do you think that is a bug then?  I guess the error muting CORs type check should use the actual response type?
Flags: needinfo?(annevk)
Yeah, at least as far as I can remember we didn't change the standard on the response being authoritative (even though most implementations work differently for the moment). (We did add a restriction that a "same-origin" fetch cannot take a "cors" response as that would result in rather tricky downstream scenarios.)
Flags: needinfo?(annevk)
Ok, then this is very similar to bug 1467454.  I'll morph this bug into a similar issue for script muting.

The fix should be to adjust this code:

https://searchfox.org/mozilla-central/rev/d544b118e26422877108c42723b26e9bb4539721/dom/script/ScriptLoader.cpp#2101

To look at the LoadInfo tainting value instead of doing a cross-origin principal subsumes check.  Or maybe it could do both if we want a system principal to still be able to get full script errors even for an opaque tainted result.
Summary: Service Worker - Script error leakage if CORS enabled → <script> element without "cross-origin" attribute treats service worker synthesized CORS response as opaque
Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.