ariya / phantomjs

Scriptable Headless Browser
http://phantomjs.org
BSD 3-Clause "New" or "Revised" License
29.45k stars 5.76k forks source link

'unsafe-eval' is not an allowed ... Security Policy directive: "script-src 'self' #13114

Closed jain0709 closed 4 years ago

jain0709 commented 9 years ago

trying to automate scenario for login https site getting following error :-

[DEBUG] WebPage - evaluateJavaScript result QVariant(QVariantMap, QMap(("status", QVariant(double, 13) ) ( "value" , QVariant(QVariantMap, QMap(("message", QVariant(QString, "Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' ". ") ) ) ) ) ) ) 2015-03-27T20:34:57 [DEBUG] HTTP Response - Status Code 500 Internal Server Error

same test is working fine when using chrome browser. Running phantom js as :-

phantomjs --ignore-ssl-errors=true --web-security=false --ssl-protocol=tlsv1 --local-to-remote-url-access=true --webdriver=4444 --debug=true

seems some kind of bug with phantomjs.

cdroulers commented 9 years ago

I'm having the same issue when trying to log in through IdentityServer3 with PhantomJS 2. Chrome and PhantomJS 1.9.8 work just fine.

jain0709 commented 9 years ago

Thanks for suggestion, It worked with PhantomJS 1.9.8

zackw commented 9 years ago

@jain0709 We don't want you to have to downgrade. If something worked in 1.9.x and doesn't in 2.0, that's something we should fix.

That said, we need quite a bit more information from you before we can do anything about this. Specifically, we need to know what website provoked this error, and how you were using PhantomJS to access it -- not just the command line invocation, but the script. If at all possible, construct a self-contained test script that we can run as

$ phantomjs issue13114.js

and see the same problem you are. It's OK if you need extra files or even a web server, just explain in enough detail that we can make it happen for ourselves.

Also, there's a decent chance this is a situation where the newer Webkit in 2.0 is refusing to do something insecure when the older one would let you get away with it. This is a guess, based on the text of the error message. I could be wrong.

Also also, you shouldn't ever need to use --ignore-ssl-errors=true or --web-security=false -- that suggests to me that you have major problems with the environment you're running PhantomJS in, and you may find that this problem vanishes if you sort those out first.

cdroulers commented 9 years ago

@zackw I'll see about creating a quick test case soon. The problem I was experiencing is that my app was doing a redirect (via JavaScript) to our authentication server, which was a different page, but on the same domain (virtual directories in IIS). I tried accessing the login page directly and it worked just fine.

cdroulers commented 9 years ago

@zackw Here's a commit with a test case that MIGHT be reproducing the issue. If not the right issue, it still is one I think! https://github.com/Vooban/phantomjs/commit/a18eab42c02e4bbd98388e3c47b3e7dbf958ee52

Simply start a two web servers for the folder on port 8000 and 8001 and the output is something like :

success 8000
redirect caught
Operation canceled
http://localhost:8001/index2.html
failed http://localhost:8001/index2.html
zackw commented 9 years ago

@cdroulers That doesn't appear to have anything to do with Content-Security-Policy directives?

cdroulers commented 9 years ago

@zackw Nope. I caught this while trying to reproduce. I'm still unable to create a test case. The problem happens for me with a C# Selenium test case. Would it be okay to create a repro this way?

zackw commented 9 years ago

@cdroulers I think we can cope with that, if that's what you need to do to make it happen.

cdroulers commented 9 years ago

@zackw I've narrowed it down to a decent test case. If I try to render a page with a Content-Security-Policy in a phantomjs script, the page loads and renders in a screenshot fine. The JavaScript is not executed.

If I try to load that page with Selenium's RemoteWebDriver though, it fails with the error. I'm trying to create a very small and specific test case to reproduce it. Do you have a suggestion on how to write that test case with selenium in a language that would be easy to run for you? Ruby, Python, etc. I can figure out how to write it!

zackw commented 9 years ago

@cdroulers Anything you can possibly do to reproduce the issue without Selenium is worth doing, but beyond that, don't worry about languages; we're gonna have to rewrite it to fit it into our automated test suite anyway.

"The JavaScript is not executed", though, that might be enough of a clue right there. Which JavaScript exactly was that?

cdroulers commented 9 years ago

@zackw The JavaScript not being executed is inlined JavaScript, which the Content-Security-Policy header forbids. When I open the page in Chrome, nothing is executed and I get warnings in the developer console.

In Phantom, everything succeeds without warnings (that I can see!), the screenshot is taken properly. In the JavaScript, I try to write in the body, and the text does not appear in the body. So I think PhantomJS is handling everything just fine.

On the other hand, when I simply try to load the page in Selenium and then read the Url or Title of the IWebDriver, it fails with the error in the OP. I'm starting to think it's a problem with the Selenium Web Driver! Do you have any idea how it works for PhantomJS?

cdroulers commented 9 years ago

https://github.com/ariya/phantomjs/compare/master...Vooban:master

Here's a diff of how I got a repro. The issue13114.js works fine (and doesn't execute the JS).

The Selenium test case crashes with the error from the OP. I'm not well versed with Selenium enough to know what exactly happens between the two when driver.Url is called.

snowyu commented 8 years ago

this happen on https://github.com now(phantomjs-2.1.1-windows):

 1) Github test open URL Then  I expect that the url is "https://github.com/":
     Uncaught Error: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the
following Content Security Policy directive: "script-src assets-cdn.github.com".
 Uncaught Error: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the
following Content Security Policy directive: "script-src assets-cdn.github.com".

the phantomjs raise error:

[INFO  - 2016-02-24T13:47:49.812Z] GhostDriver - Main - running on port 4444
[INFO  - 2016-02-24T13:48:02.627Z] Session [3937fde0-dafd-11e5-ac6f-115b0bd0fe78] - page.settings - {"XSSAuditingEnabled":false,"javascriptCanCloseWindows":true,"javascriptCanOpenWindows":true,"javascriptEnabled":true,"loadImages":true,"localToRemoteUrlAccessEnabled":false,"userAgent":"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1","webSecurityEnabled":false}
[INFO  - 2016-02-24T13:48:02.627Z] Session [3937fde0-dafd-11e5-ac6f-115b0bd0fe78] - page.customHeaders:  - {}
[INFO  - 2016-02-24T13:48:02.627Z] Session [3937fde0-dafd-11e5-ac6f-115b0bd0fe78] - Session.negotiatedCapabilities - {"browserName":"phantomjs","version":"2.1.1","driverName":"ghostdriver","driverVersion":"1.2.0","platform":"windows-10-32bit","javascriptEnabled":true,"takesScreenshot":true,"handlesAlerts":false,"databaseEnabled":false,"locationContextEnabled":false,"applicationCacheEnabled":false,"browserConnectionEnabled":false,"cssSelectorsEnabled":true,"webStorageEnabled":false,"rotatable":false,"acceptSslCerts":false,"nativeEvents":true,"proxy":{"proxyType":"direct"}}
[INFO  - 2016-02-24T13:48:02.627Z] SessionManagerReqHand - _postNewSessionCommand - New Session Created: 3937fde0-dafd-11e5-ac6f-115b0bd0fe78
[ERROR - 2016-02-24T13:48:31.217Z] RouterReqHand - _handle.error - {"line":264,"sourceURL":"phantomjs://code/webelement_request_handler.js","stack":"_postValueCommand@phantomjs://code/webelement_request_handler.js:264:52\n_handle@phantomjs://code/webelement_request_handler.js:72:30\n_reroute@phantomjs://code/request_handler.js:61:20\n_handle@phantomjs://code/session_request_handler.js:120:42\n_reroute@phantomjs://code/request_handler.js:61:20\n_handle@phantomjs://code/router_request_handler.js:78:46"}

  phantomjs://platform/console++.js:263 in error
edneypitta commented 8 years ago

Same happening to me when I call Driver.Url. It worked with version 1.9.8.

Running on Selenium, C#.

Ocramius commented 8 years ago

Same issue here: seems to do that whenever trying to load anything with HTTPS, really.

Same as what @snowyu reported.

spellfish commented 8 years ago

I'm also seeing this issue as described by @snowyu. Running selenium, ruby.

vonglasow commented 8 years ago

I have also the issue running phantomjs 2.1.1 with :

And it works with phantomjs 1.9.8

mself commented 8 years ago

Here is a short example that reproduces the issue without Selenium and either with or without --web-security=false. The error occurs when loading github.com, but not with any other site (e.g yahoo.com). `

var debug = require('debug')('myapp:example');    
// Load the page using Horseman/PhantomJS.

// This function is called inside the context of the loaded page.
function example() {
    return 'success';
}

// To debug, use "DEBUG=myapp:*,horseman npm start"
var Horseman = require('node-horseman');

module.exports = function (req, res) {
    var url = 'http://' + req.query.domain;
    var security = req.query.security;
    debug('Security: ' + security);
    var horseman = new Horseman({webSecurity:security});

    horseman
        // Open the page and extract the data.
        .open(url)
        .evaluate(example)
        .then(function(result){
            res.render('results', { result: result });
        })
        .catch(function (e) {
            debug('Caught error: ' + e.message);
            res.render('error', {message: e.message, error: e});
        })
};

` If you call this with http://localhost:3000/example?domain=github.com&security=true you get:

horseman phantom version 2.1.1 myapp:example Caught error: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src assets-cdn.github.com".

zackw commented 8 years ago

I know what's wrong now. Github is sending a Content-Security-Policy directive that forbids inline scripts. page.evaluate injects code using inline scripts, so it doesn't work. It's effectively the same phenomenon as Content-Security-Policy breaking bookmarklets.

This looks like a 2.0 regression probably because 1.9.x didn't implement CSP.

We need to figure out a way to exempt page.evaluate from CSP restrictions. Unfortunately, this might involve adding new interfaces to Qt/Webkit. I can't promise you any kind of timeframe for a fix.

@mself I tagged this "need minimized test case" in spite of your nice tidy test case because we still need a self-contained test case -- one that doesn't have any dependency on github.com, which could change out from under us. If you have time to convert this test case into something that could be dropped into the existing automated test framework (this would involve getting rid of Horseman as well), that would be immensely helpful, but if you don't, we will get to it eventually.

mself commented 8 years ago

Removing Horseman should be trivial since it looks like any attempt to use page.evaluate on github.com will trigger the issue (unless the issue is related to how Horseman auto-injects jQuery).

But I don't know enough about CSP to craft a sample page to load that would trigger the issue. And might the behavior be different for local files vs. localhost vs. a hosted page that uses SSL? That could make the test case harder to implement.

zackw commented 8 years ago

I'm afraid I don't know enough about CSP to answer most of those questions myself. That said, I think the only thing necessary is a page - contents don't matter - served with Content-Security-Policy: script-src 'none'. (I don't think there is any way to accomplish that from a local file, but localhost vs. external page shouldn't matter, and the test suite provides HTTP and HTTPS web servers on localhost.)

Github is currently using a more complicated CSP directive, but I think the bulk of it is irrelevant:

default-src 'none'; base-uri 'self'; connect-src 'self' uploads.github.com status.github.com api.github.com www.google-analytics.com github-cloud.s3.amazonaws.com api.braintreegateway.com client-analytics.braintreegateway.com wss://live.github.com; font-src assets-cdn.github.com; form-action 'self' github.com gist.github.com; frame-ancestors 'none'; frame-src render.githubusercontent.com; img-src 'self' data: assets-cdn.github.com identicons.github.com www.google-analytics.com collector.githubapp.com .gravatar.com .wp.com checkout.paypal.com *.githubusercontent.com; media-src 'none'; object-src assets-cdn.github.com; script-src assets-cdn.github.com; style-src 'unsafe-inline' assets-cdn.github.com

Ocramius commented 8 years ago

Wondering if the JS can be executed from the chrome context (privileged) rather than from the normal page context. Would that still be affected? On Apr 27, 2016 18:42, "Zack Weinberg" notifications@github.com wrote:

I'm afraid I don't know enough about CSP to answer most of those questions myself. That said, I think the only thing necessary is a page - contents don't matter - served with Content-Security-Policy: script-src 'none'. (I don't think there is any way to accomplish that from a local file, but localhost vs. external page shouldn't matter, and the test suite provides HTTP and HTTPS web servers on localhost.)

Github is currently using a more complicated CSP directive:

default-src 'none'; base-uri 'self'; connect-src 'self' uploads.github.com status.github.com api.github.com www.google-analytics.com github-cloud.s3.amazonaws.com api.braintreegateway.com client-analytics.braintreegateway.com wss://live.github.com; font-src assets-cdn.github.com; form-action 'self' github.com gist.github.com; frame-ancestors 'none'; frame-src render.githubusercontent.com; img-src 'self' data: assets-cdn.github.com identicons.github.com www.google-analytics.com collector.githubapp.com .gravatar.com .wp.com checkout.paypal.com *.githubusercontent.com; media-src 'none'; object-src assets-cdn.github.com; script-src assets-cdn.github.com; style-src 'unsafe-inline' assets-cdn.github.com

— You are receiving this because you commented. Reply to this email directly or view it on GitHub https://github.com/ariya/phantomjs/issues/13114#issuecomment-215143031

zackw commented 8 years ago

@Ocramius It is unclear to me - as a developer! - whether (Qt/)Webkit even has that distinction, let alone whether its idea of that distinction lines up with what we would need in this context.

travisjweber commented 8 years ago

Any updates on this? We're also bumping into the issue with Identity Server 3.

javascriptlove commented 8 years ago

+1 any workaround on how to use page.evaluate in this case?

travisjweber commented 8 years ago

We added 'unsafe-eval' to our CSP options for Identity Server in our CI environment in the interim.

Ocramius commented 8 years ago

@travisjweber any details on what/how you did it, and if it helped? That could be useful to know for all others involved in the thread.

travisjweber commented 8 years ago

Well for IdentityServer3 this did the trick (using ASP.NET Core, targeting .NET 4.6.2)

`var cspOptions = new CspOptions();

//override script source defaults for dev to allow phantom JS/Selenium to run if (env.IsDevelopment() || env.IsLocalDevelopment()) { cspOptions.ScriptSrc = "'self' 'unsafe-eval'"; }

var idsrvOptions = new IdentityServerOptions { Factory = //.... other unrelated details omitted here.... CspOptions = cspOptions };

app.UseIdentityServer(idsrvOptions);`

Ocramius commented 8 years ago

@traviscooper thanks! Sorry, I misunderstood the solution then, that does indeed work if you have control over the server-side.

travisjweber commented 8 years ago

Oh yeah, most definitely need that control! Our same tests work with chrome's driver FYI, we were using that locally but it doesn't work with our builds so we [temporarily] relaxed the CSP so Phantom works until we can figure out something longer-term.

tobias-sc commented 7 years ago

Going through the replies seem temporary fix is to downgrade PhantomJS or modify CSP option for Identity Server.... In our case here we have to use at least 2.1 due its support of SSL certificates, also no access to the Identity Server - what would be the solution in this case? It is mentioned to remove horseman? If yes, how or what is another option?

Thank you.

peacetrader commented 7 years ago

Hi, I am using this dependency in my sbt build : "com.github.detro.ghostdriver" % "phantomjsdriver" % "1.0.1" and by default it is using this combination : "browserName":"phantomjs","version":"2.1.1","driverName":"ghostdriver","driverVersion":"1.2.0" How to use the 1.9.8 version ?.

alexmherrmann commented 7 years ago

Seeing the same thing trying to interact with stripe website while authorizing an API. Works fine with chrome just like others have said.

travisjweber commented 7 years ago

We wound up creating an http proxy to strip out the csp directive for running our tests with phantomjs.

jeff1985 commented 7 years ago

We encountered the same problem while trying to implement a behat test for SOFORT payment integration. Any progress here?

jesg commented 7 years ago

Any progress here?

it should work for every command but execute_script and execute_async_script in the next version of ghostdriver.

javascriptlove commented 7 years ago

In meteor we used this:

if (process.env.NODE_ENV === 'development') {
    BrowserPolicy.content.allowEval(); // for PhantomJS end-to-end testing
}

Which is something like this in raw content-security-policy header:

script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:3000 https://localhost:3000
bobmshannon commented 7 years ago

Also experiencing this issue.

ssb22 commented 7 years ago

In Python's webdriver, even reading current_url (which calls execute("getCurrentUrl")['value']) raises a WebDriverException with the unsafe-eval complaint on affected sites. (Tested on PhantomJS 2.1.1.)

travisjweber commented 7 years ago

We've switched to using Selenium-Grid with Chrome and Internet Explorer, no more dealing with a headless browser just for our Selenium tests.

bllevy commented 7 years ago

Any updates on this issue?

ssb22 commented 7 years ago

Not as far as I know. I work around it by telling PhantomJS to send its requests through my custom proxy, which (among other things) removes headers like Content-Security-Policy, if necessary breaking into SSL to do so (I disable PhantomJS's certificate checks). http://ssb22.user.srcf.net/adjuster/

stephen-walsh commented 6 years ago

also hitting this issue now 2.5 years later :(

ssb22 commented 6 years ago

I guess that's partly because PhantomJS is no longer maintained, so this issue is now permanent. I'm planning on switching to Headless Chrome when they fix their problems with self-signed SSL https://crbug.com/721739 meanwhile I put an upstream proxy on PhantomJS's requests and remove headers like Content-Security-Policy so it doesn't see them.

mesaleh commented 6 years ago

PhantomJS latest stable version 2.1.1 has a bug when it tries to get a webpage that has the following response header: content-security-policy: default-src 'none'

The Fix: Branch 2.1.1, File: \phantomjs\src\qt\qtwebkit\Source\WebCore\page\ContentSecurityPolicy.cpp, Change line 354:

, m_allowEval(false)

To:

, m_allowEval(true)

Then build.py -r ... Enjoy!

stale[bot] commented 4 years ago

Due to our very limited maintenance capacity (see #14541 for more details), we need to prioritize our development focus on other tasks. Therefore, this issue will be automatically closed. In the future, if we see the need to attend to this issue again, then it will be reopened. Thank you for your contribution!