New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.

Issue 811528 link

Starred by 2 users

Issue metadata

Status: Fixed
Owner:
Closed: Apr 2018
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: Linux , Windows , Mac
Pri: 2
Type: Bug



Sign in to add a comment

Extension content script works on page with Content-Security-Policy: sandbox unless uses setTimeout

Reported by peter.m....@gmail.com, Feb 12 2018

Issue description

Chrome Version: 64.0.3282.140
OS Version: OS X 10.13.2
URLs: https://mrcoles.com/csp-sandbox (test page with response header "Content-Security-Policy: sandbox ;"

What steps will reproduce the problem?
1. Clone this repo: https://github.com/mrcoles/test-chrome-extension-sandbox
2. Add as an unpacked extension
3. Go to this page https://mrcoles.com/csp-sandbox (which has the sandbox response header)
4. Click on the popup button to trigger the extension.

What is the expected result?

Should output the messages:

> 1. chrome.tabs.executeScript(..., page.js)
> 2. chrome.tabs.executeScript callback!
> 3. chrome.tabs.sendMessage(..., test)
> 4. chrome.tabs.sendMessage callback!


What happens instead of that?

It outputs the messages:

> 1. chrome.tabs.executeScript(..., page.js)
> 2. chrome.tabs.executeScript callback!
> 3. chrome.tabs.sendMessage(..., test)

Please provide any additional information below. Attach a screenshot if
possible.

The content script "page.js" listens for a message and then calls the callback it is sent. When it’s wrapped in a `window.setTimeout(...)` and it is run on a page with "Content-Security-Policy: sandbox ;" (see linked URL above) the callback fails to call and there is *no* error, not even a `chrome.runtime.lastError` in "popup.js".

If I comment out the setTimeout lines and call the callback synchronously inline, then it works fine.

Also, if I keep the setTimeout, but run it on a page without that response header (like https://google.com), then it works fine and displays the 4 messages.


UserAgentString: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36



 
Labels: Needs-Triage-M64
Components: Platform>Extensions
Labels: -Pri-3 Triaged-ET M-66 FoundIn-66 Target-66 OS-Linux OS-Windows Pri-2
Status: Untriaged (was: Unconfirmed)
Able to reproduce this issue on Mac 10.12.6, Win-10 and Ubuntu 14.04 using chrome reported version #64.0.3282.140 and latest canary #66.0.3345.0.
This is a non-regression issue as it is observed from M60 old builds. 

Hence, marking it as untriaged to get more inputs from dev team.

Thanks...!!
Owner: rdevlin....@chromium.org
Status: Assigned (was: Untriaged)
@peter.m.coles, is this CSP specific? i.e. does this work without the CSP?

Assigning to Devlin for sendMessage.
On pages without the CSP setting, it works as expected, e.g., try on https://mrcoles.com/ instead of https://mrcoles.com/csp-sandbox
Repo linked seems like it might be wrong - it looks like that one doesn't do any script injection and just has a popup?
Sorry! I fixed the repo (I mistakenly overwrite it with another bug report).

Original URL works again: https://github.com/mrcoles/test-chrome-extension-sandbox
Cc: mkwst@chromium.org jbroman@chromium.org
Sorry for the lengthy delay here!

I had a chance to investigate this, and think I know what's happening.  Extensions are allowed to bypass the CSP of the page in order to inject scripts on it (which is very important).  However, it looks like once we use setTimeout(), we lose the knowledge that the script running came from an extension.

In extreme cases, we can't always make the guarantee that things like this will work (because chrome doesn't have perfect taint tracking in blink, and we won't always be able to know what the appropriate execution context is).  It's also, for now, a pretty deliberate non-goal to change that, because it's hard, and doing it performantly is harder.

However, this case seems like it should be easy, and could be pretty useful.  In particular, we should still be executing in the same isolated world associated with the script, and it seems like having a "if (script from isolated world for extension) { bypass csp }" type logic should be doable.

jbroman@, mkwst@, any thoughts?  (Both on the feasibility of such an approach and whether there's any reason we wouldn't want to do it.)
Cc: haraken@chromium.org yukishiino@chromium.org
Keying off the current ScriptState sounds reasonable to me.
You meant to insert "if (content script) { bypass csp }" into ScheduledAction or somewhere appropriate?

I don't want to insert such code here and there randomly, but inserting such code into a (few?) specific point(s) is pretty reasonable, I think.  We need to be careful, though.

I don't work in blink enough to know the exact places such a check would go, and will gladly defer to blink experts there. :)  But it does seem that, since we a) know which world is executing and b) exempt that world from CSP when first executing, we should be able to carry that CSP-exemption for asynchronous execution (i.e., use the world to determine whether to bypass CSP).

Is there anything in particular that's worrisome, and we should make sure to be extra cautious of?
> Is there anything in particular that's worrisome, and we should make sure to be extra cautious of?

No, nothing.  Please go ahead.

Project Member

Comment 13 by bugdroid1@chromium.org, Apr 27 2018

The following revision refers to this bug:
  https://chromium.googlesource.com/chromium/src.git/+/5a5267ab58dd0310fc2b427db30de60c0eea4457

commit 5a5267ab58dd0310fc2b427db30de60c0eea4457
Author: Devlin Cronin <rdevlin.cronin@chromium.org>
Date: Fri Apr 27 20:16:38 2018

[Blink + Extensions] Allow async execution in isolated worlds to bypass sandbox

Isolated worlds (like extensions) are designed to be able to bypass a
page's specified CSP. However, when executing an asynchronous scheduled
action (such as one initiated through setTimeout()), the actions were
not allowed to execute. This is because ScheduledAction::Execute()
checked Document::CanExecuteScripts(), which checked if the document
was sandboxed and didn't check if it should bypass the CSP.

Fix this by having the document check if it should bypass the CSP, and,
if it should, allow the script execution.

Add unittests for Document::CanExecuteScripts() as well as an end-to-end
test for an extension with a content script and setTimeout().

Bug:  811528 
Change-Id: I7f6497784845a7d05d60048c84b24db9b903eae9
Reviewed-on: https://chromium-review.googlesource.com/1030952
Commit-Queue: Devlin <rdevlin.cronin@chromium.org>
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
Cr-Commit-Position: refs/heads/master@{#554477}
[modify] https://crrev.com/5a5267ab58dd0310fc2b427db30de60c0eea4457/chrome/browser/extensions/content_script_apitest.cc
[add] https://crrev.com/5a5267ab58dd0310fc2b427db30de60c0eea4457/chrome/test/data/extensions/page_with_sandbox_csp.html
[add] https://crrev.com/5a5267ab58dd0310fc2b427db30de60c0eea4457/chrome/test/data/extensions/page_with_sandbox_csp.html.mock-http-headers
[modify] https://crrev.com/5a5267ab58dd0310fc2b427db30de60c0eea4457/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc
[modify] https://crrev.com/5a5267ab58dd0310fc2b427db30de60c0eea4457/third_party/blink/renderer/core/dom/document.cc
[modify] https://crrev.com/5a5267ab58dd0310fc2b427db30de60c0eea4457/third_party/blink/renderer/core/dom/document_test.cc

Status: Fixed (was: Assigned)
I believe this should be fixed with #13.  peter.m.coles@, feel free to test it out.

Thanks very much for reporting the issue!  I wouldn't be surprised if there are some other cases like this, so if you find any, let us know. :)

Sign in to add a comment