Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ spec/dummy/db/*.sqlite3
spec/dummy/log/*.log
spec/dummy/tmp/
spec/dummy/.sass-cache
Gemfile.local
24 changes: 12 additions & 12 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ env:
- RAILS_ENV=development
matrix:
include:
- rvm: 2.1
env: RAILS_VERSION="~> 4.2.7"
- rvm: 2.2
env: RAILS_VERSION="~> 4.2.7"
- rvm: 2.2
env: RAILS_VERSION="~> 5.0.0"
- rvm: 2.3.3
env: RAILS_VERSION="~> 4.2.7"
- rvm: 2.3.3
env: RAILS_VERSION="~> 5.0.0"
- rvm: 2.4.0
env: RAILS_VERSION="~> 5.0.0"
- env: RAILS_VERSION="~> 4.2.7"
rvm: 2.3

- env: RAILS_VERSION="~> 5.0.0"
rvm: 2.3

- env: RAILS_VERSION="~> 5.2.0"
rvm: 2.4

- env: RAILS_VERSION="~> 6.0.0"
rvm: 2.7

before_install: gem install bundler -v "~> 1.17" --no-document
13 changes: 12 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
source 'https://rubygems.org'
gemspec

gem 'rails', ENV['RAILS_VERSION'] if ENV['RAILS_VERSION']
if ENV['RAILS_VERSION']
gem 'rails', ENV['RAILS_VERSION']
if ENV['RAILS_VERSION'][/\d.*/] >= '6.0.0'
# use latest (4.x)
else
gem 'sprockets', '~> 3.0'
end
end

if (local_file = Pathname('Gemfile.local')).exist?
eval local_file.read, binding, __FILE__, __LINE__
end
10 changes: 8 additions & 2 deletions lib/xray/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,24 +96,30 @@ def render_xray_bar
# <script src="/assets/jquery-min.js"></script>
# <script src="/assets/jquery.min.1.9.1.js"></script>
# <script src="/assets/jquery.min.1.9.1-89255b9dbf3de2fbaa6754b3a00db431.js"></script>
# <script src="/assets/xray.debug-1f24c4eb56c76ca56a881d5560552a44e98c0eeb30f47f8775a3d5557c7bffe3.js" nonce="UCCdiIBNaam7el9cVHptyA=="></script>
def script_matcher(script_name)
/
<script[^>]+
\/#{script_name}
(2|3)? # Optional jQuery version specification
([-.]{1}[\d\.]+)? # Optional version identifier (e.g. -1.9.1)
([-.]{1}min)? # Optional -min suffix
(\.self)? # Sprockets 3 appends .self to the filename
(\.(?:self|source|debug))? # Sprockets 3 appends .self to the filename; Sprockets 4 appends .source or .debug
(-\h{32,64})? # Fingerprint varies based on Sprockets version
\.js # Must have .js extension
[^>]+><\/script>
/x
end

def nonce_from_meta_tag(html)
html[/<meta name="csp-nonce" content="([^"]*)"/, 1].presence
end

# Appends the given `script_name` after the `after_script_name`.
def append_js!(html, after_script_name, script_name)
html.sub!(script_matcher(after_script_name)) do
"#{$~}\n" + helper.javascript_include_tag(script_name)
nonce = nonce_from_meta_tag(html)
"#{$~}\n" + helper.javascript_include_tag(script_name, nonce: nonce)
end
end

Expand Down
3 changes: 3 additions & 0 deletions spec/dummy/app/assets/config/manifest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//= link_directory ../stylesheets .css
//= link_directory ../javascripts .js
//= link jquery.js
10 changes: 10 additions & 0 deletions spec/dummy/app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
class ApplicationController < ActionController::Base
protect_from_forgery

if respond_to?(:content_security_policy) # Rails >= 5.2
content_security_policy only: :strict_csp do |policy|
policy.script_src :self, :strict_dynamic
end
end

def root
end

def strict_csp
render :root
end

# For the tests
def non_html
render json: {foo: 'bar'}
Expand Down
7 changes: 5 additions & 2 deletions spec/dummy/app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
<html>
<head>
<title>Xray</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= stylesheet_link_tag "application", media: "all" %>
<%= javascript_include_tag "application", nonce: true %>
<%# With Sprockets 4, it doesn't add a separate script tag for jquery, but one is needed for xray %>
<%= javascript_include_tag "jquery", nonce: true if Gem.loaded_specs['sprockets'].version >= Gem::Version.new('4.0.0') %>
<%= csrf_meta_tags %>
<%= csp_meta_tag if Gem.loaded_specs['rails'].version >= Gem::Version.new('5.2.0') %>
</head>
<body>

Expand Down
4 changes: 4 additions & 0 deletions spec/dummy/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ class Application < Rails::Application

# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'

if config.respond_to?(:hosts)
config.hosts << "www.example.com"
end
end
end

7 changes: 7 additions & 0 deletions spec/dummy/config/initializers/content_security_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
if Gem.loaded_specs['rails'].version >= Gem::Version.new('5.2.0')
Rails.application.config.content_security_policy do |policy|
# Empty. Only need one endpoint (/strict_csp) to use a strict CSP in our tests.
end

Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
end
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ def unindent
config.include Capybara::DSL
config.include Capybara::RSpecMatchers
end
Dir[Pathname.new(__dir__).join('support/**/*.rb')].each {|f| require f}

Capybara.configure do |config|
config.ignore_hidden_elements = false
config.javascript_driver = :selenium_headless
end
17 changes: 17 additions & 0 deletions spec/support/capybara_screenshot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require 'capybara-screenshot/rspec'

Capybara::Screenshot.autosave_on_failure = true
Capybara::Screenshot.prune_strategy = { keep: 20 }

RSpec.configure do |config|
# Not just for type: :feature
config.after do |example_from_block_arg|
# RSpec 3 no longer defines `example`, but passes the example as block argument instead
example = config.respond_to?(:expose_current_running_example_as) ? example_from_block_arg : self.example

if example.exception && ENV['CI']
puts page.html
end
Capybara::Screenshot::RSpec.after_failed_example(example)
end
end
27 changes: 27 additions & 0 deletions spec/xray/e2e_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require 'spec_helper'

describe Xray, "in a Rails app (end-to-end)", :js do
it "works" do
visit '/'
expect_to_work
end

if Gem.loaded_specs['rails'].version >= Gem::Version.new('5.2.0')
it "works when using a strict CSP" do
visit '/strict_csp'
expect_to_work
end
end

def expect_to_work
if Gem.loaded_specs['rails'].version >= Gem::Version.new('5.2.0')
expect(page).to have_selector('script[src^="/assets/xray"][nonce]')
expect(page.find('script[src^="/assets/xray"]')[:nonce]).to eq page.find('meta[name="csp-nonce"]')[:content]
end
expect(page).to have_selector('#xray-bar')

expect(page).to have_no_selector('.xray-specimen-handle.TemplateSpecimen', text: 'root.html.erb')
find('body').send_keys [:control, :shift, 'x']
expect(page).to have_selector('.xray-specimen-handle.TemplateSpecimen', text: 'root.html.erb')
end
end unless ENV['CI'] == 'true'
8 changes: 8 additions & 0 deletions spec/xray/middleware_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ def mock_response(status, content_type, body)
expect(page).to have_selector('script[src^="/assets/xray"]')
end

if Gem.loaded_specs['rails'].version >= Gem::Version.new('5.2.0')
it "adds nonce to the script tag" do
visit '/'
expect(page).to have_selector('script[src^="/assets/xray"][nonce]')
expect(page.find('script[src^="/assets/xray"]')[:nonce]).to eq page.find('meta[name="csp-nonce"]')[:content]
end
end

it "injects the xray bar into the response" do
visit '/'
expect(page).to have_selector('#xray-bar')
Expand Down
9 changes: 8 additions & 1 deletion xray-rails.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ Gem::Specification.new do |gem|

gem.add_dependency 'rails', '>= 3.1.0'

gem.add_development_dependency 'rspec-rails'
gem.add_development_dependency 'rspec-rails', '~> 3.8'

# Required for the dummy Rails app in spec/dummy
gem.add_development_dependency 'sqlite3'
gem.add_development_dependency 'jquery-rails'
gem.add_development_dependency 'haml'
gem.add_development_dependency 'capybara'
gem.add_development_dependency 'capybara-screenshot'
unless ENV['CI'] == 'true'
gem.add_development_dependency 'selenium-webdriver'
gem.add_development_dependency 'puma'
# webdrivers gem uses &., which isn't available in older Rubies
gem.add_development_dependency 'webdrivers'
end
end