Closed nathanl closed 7 years ago
What are your "When I view the 'foo' logo directly" and "Then the JS is not executed" steps doing exactly? IIRC PhantomJS doesn't support just opening an SVG so that would explain why you're seeing an empty page, and you'd have to create a valid HTML page that includes the SVG.
"When I view the 'foo' logo directly" calls visit logo_path(id: logo_id)
, which hits a controller action that serves the image file, like this:
logo = Logo.find(params[:id])
send_data(logo.data, type: logo.mime_type, disposition: :inline)
The "JS is not executed" step isn't doing anything yet. My thought is that the SVG would contain some JavaScript which modifies one of its elements, and that this step would check for that modification and fail if it is there. Or perhaps there's a better way to check whether the JS has executed. But right now, it acts as if the SVG is entirely absent, so I can't check anything.
IIRC Poltergeist doesn't support just opening an SVG so that would explain why you're seeing an empty page, and you'd have to create a valid HTML page that includes the SVG.
Normally I use these images by having a <img>
whose src
points to the controller action which sends the image data. But the thing I'm trying to test is specifically that if someone views the image directly - eg if you visited this image - and the SVG had some JS in it, it wouldn't be executed.
If Poltergeist cannot test a response with mime type image/svg
, and capybara's non-JS driver can't execute the JS in an SVG, I don't see any way for me to test whether JS in an SVG is executed except by hand.
Hmmmm, I guess I'm wrong about PhantomJS not loading svgs directly
require 'capybara/poltergeist'
sess = Capybara::Session.new(:poltergeist)
sess.visit 'http://nathanmlong.com/images/rss_feed.svg'
puts sess.body
outputs the source of the svg. Check the actual data being returned from your controller.
Interesting. If I repeat your example, I do see the SVG data. However, when I run my test without the @javascript
tag (using :rack_test
), I can see my SVG, but when I run it with @javascript
(using Capybara.javascript_driver = :poltergeist
) I see only the empty body I showed above.
The server response appears to be OK from what I can see. The output to log/test.log
is identical in both cases. A puts
on the line before send_data
does appear when I'm using poltergeist, page.status_code
is 200, and page.response_headers
includes these:
"Content-Type"=>"image/svg",
"Content-Disposition"=>"inline",
"Content-Transfer-Encoding"=>"binary",
"Content-Length"=>"1575",
So it appears that Rails thinks it is sending SVG data, but page.body
doesn't reflect that when I use Poltergeist. Will tinker some more.
I found that visit logo_path(id: logo.id)
is returning {"status"=>"fail"}
, even though page.status_code
is 200
and page.response_headers
look right.
I went bundle open
diving in poltergeist
and websocket-driver
, with plenty of puts
debugging, but couldn't understand where or why the {"status"=>"fail"}
was generated. So far I'm stumped.
I still don't understand what's wrong, but I created a new, tiny Rails app, and in it, my js: true
test using poltereist does correctly see the contents of an SVG, does execute the SVG's inline javascript by default, and does ignore the SVG's inline javascript if a content security policy is correctly set.
So the issue is with my original app.
Aha! I found the issue. I had incorrectly set the mime type for SVG - I had used image/svg
when it should be image/svg+xml
. Apparently :rack_test
renders that anyway but :poltergeist
doesn't.
Glad you found it
(I'm using Poltergeist 1.13.0.)
My app allows users to add, and directly view, SVG images. Because SVGs can contain JavaScript, and users might unwittingly upload one that does, I want to make sure that the JavaScript is not executed when they directly view the SVG in their browser.
I can do that using a Content Security Policy header; any policy that doesn't explicitly allow
script-src 'unsafe-inline'
will tell the browser "yeah and don't execute any<script>
tags you might find in this response".Very good. Now I want to test that. I have a test (it's gherkin, but that's not important) like this:
It's that last step that I'm having trouble with.
If I have the
@javascript
tag commented out, it uses a web driver that doesn't support JS, so of course the JS doesn't execute but that doesn't prove my CSP works. In theJS is not executed
step, I can inspectpage.body
and see the SVG markup. For the sake of this discussion, suppose it's an Apple logo with malicious JS tacked on:If I uncomment the
@javascript
tag, Capybara will use Poltergeist. In that case, when I checkpage.body
, I see an empty html body, like:I can't test what the JavaScript does or doesn't do unless my web driver supports JavaScript. But when I use this web driver that supports JavaScript, the entire
<script>
tag appears not to be there, and the body is a generic, empty HTML document.Am I doing something wrong?