From 7c6bcf8f5601b9b008b7ac72d3666b370113d244 Mon Sep 17 00:00:00 2001 From: Tyler Rick Date: Tue, 10 Sep 2019 15:59:30 -0700 Subject: [PATCH] Automatically add a nonce to script tag (#100) ... if a csp-nonce meta tag is available to get it from. - Added test that confirms that nonce is added - Added an end-to-end test that uses a strict CSP. Confirmed that this fails without the nonce added to javascript_include_tag calls. Co-authored-by: Matt Brictson --- lib/xray/middleware.rb | 7 ++++++- spec/dummy/app/controllers/application_controller.rb | 10 ++++++++++ spec/dummy/app/views/layouts/application.html.erb | 5 +++-- .../config/initializers/content_security_policy.rb | 7 +++++++ spec/xray/e2e_spec.rb | 11 +++++++++++ spec/xray/middleware_spec.rb | 8 ++++++++ 6 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 spec/dummy/config/initializers/content_security_policy.rb diff --git a/lib/xray/middleware.rb b/lib/xray/middleware.rb index 53a6b8e..6473967 100644 --- a/lib/xray/middleware.rb +++ b/lib/xray/middleware.rb @@ -111,10 +111,15 @@ def script_matcher(script_name) /x end + def nonce_from_meta_tag(html) + html[/= 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'} diff --git a/spec/dummy/app/views/layouts/application.html.erb b/spec/dummy/app/views/layouts/application.html.erb index b495424..61724e2 100644 --- a/spec/dummy/app/views/layouts/application.html.erb +++ b/spec/dummy/app/views/layouts/application.html.erb @@ -2,9 +2,10 @@ Xray - <%= stylesheet_link_tag "application", :media => "all" %> - <%= javascript_include_tag "application" %> + <%= stylesheet_link_tag "application", media: "all" %> + <%= javascript_include_tag "application", nonce: true %> <%= csrf_meta_tags %> + <%= csp_meta_tag %> diff --git a/spec/dummy/config/initializers/content_security_policy.rb b/spec/dummy/config/initializers/content_security_policy.rb new file mode 100644 index 0000000..397abc9 --- /dev/null +++ b/spec/dummy/config/initializers/content_security_policy.rb @@ -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 diff --git a/spec/xray/e2e_spec.rb b/spec/xray/e2e_spec.rb index 56b1b67..62127a1 100644 --- a/spec/xray/e2e_spec.rb +++ b/spec/xray/e2e_spec.rb @@ -6,7 +6,18 @@ 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') diff --git a/spec/xray/middleware_spec.rb b/spec/xray/middleware_spec.rb index 84ef1bd..f5d3cdb 100644 --- a/spec/xray/middleware_spec.rb +++ b/spec/xray/middleware_spec.rb @@ -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')