Skip to content

Commit d1f0162

Browse files
committed
Avoid calling content_security_policy_nonce internally
Rails 5.2 adds support for configuring a Content-Security-Policy header, including adding nonces to tags produced by the `javascript_tag` helper. Unfortunately, Rails and this gem now both define a helper named `content_security_policy_nonce`: https://github.com/rails/rails/blob/v5.2.0.rc2/actionpack/lib/action_controller/metal/content_security_policy.rb#L44 https://github.com/twitter/secureheaders/blob/v5.0.5/lib/secure_headers/view_helper.rb#L69 The Rails helper wins over the Secure Headers one, and helpers like `nonced_javascript_tag` currently raise this error on Rails 5.2: ArgumentError: wrong number of arguments (given 1, expected 0) By using a method with a different name internally, we avoid clashing with the Rails implementation, and `nonced_javascript_tag` works again.
1 parent 5c47914 commit d1f0162

File tree

2 files changed

+37
-8
lines changed

2 files changed

+37
-8
lines changed

lib/secure_headers/view_helper.rb

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def nonced_style_tag(content_or_options = {}, &block)
1919
#
2020
# Returns an html-safe link tag with the nonce attribute.
2121
def nonced_stylesheet_link_tag(*args, &block)
22-
opts = extract_options(args).merge(nonce: content_security_policy_nonce(:style))
22+
opts = extract_options(args).merge(nonce: _content_security_policy_nonce(:style))
2323

2424
stylesheet_link_tag(*args, opts, &block)
2525
end
@@ -37,7 +37,7 @@ def nonced_javascript_tag(content_or_options = {}, &block)
3737
#
3838
# Returns an html-safe script tag with the nonce attribute.
3939
def nonced_javascript_include_tag(*args, &block)
40-
opts = extract_options(args).merge(nonce: content_security_policy_nonce(:script))
40+
opts = extract_options(args).merge(nonce: _content_security_policy_nonce(:script))
4141

4242
javascript_include_tag(*args, opts, &block)
4343
end
@@ -47,7 +47,7 @@ def nonced_javascript_include_tag(*args, &block)
4747
#
4848
# Returns an html-safe script tag with the nonce attribute.
4949
def nonced_javascript_pack_tag(*args, &block)
50-
opts = extract_options(args).merge(nonce: content_security_policy_nonce(:script))
50+
opts = extract_options(args).merge(nonce: _content_security_policy_nonce(:script))
5151

5252
javascript_pack_tag(*args, opts, &block)
5353
end
@@ -57,7 +57,7 @@ def nonced_javascript_pack_tag(*args, &block)
5757
#
5858
# Returns an html-safe link tag with the nonce attribute.
5959
def nonced_stylesheet_pack_tag(*args, &block)
60-
opts = extract_options(args).merge(nonce: content_security_policy_nonce(:style))
60+
opts = extract_options(args).merge(nonce: _content_security_policy_nonce(:style))
6161

6262
stylesheet_pack_tag(*args, opts, &block)
6363
end
@@ -66,21 +66,22 @@ def nonced_stylesheet_pack_tag(*args, &block)
6666
# Instructs secure_headers to append a nonce to style/script-src directives.
6767
#
6868
# Returns a non-html-safe nonce value.
69-
def content_security_policy_nonce(type)
69+
def _content_security_policy_nonce(type)
7070
case type
7171
when :script
7272
SecureHeaders.content_security_policy_script_nonce(@_request)
7373
when :style
7474
SecureHeaders.content_security_policy_style_nonce(@_request)
7575
end
7676
end
77+
alias_method :content_security_policy_nonce, :_content_security_policy_nonce
7778

7879
def content_security_policy_script_nonce
79-
content_security_policy_nonce(:script)
80+
_content_security_policy_nonce(:script)
8081
end
8182

8283
def content_security_policy_style_nonce
83-
content_security_policy_nonce(:style)
84+
_content_security_policy_nonce(:style)
8485
end
8586

8687
##
@@ -152,7 +153,7 @@ def nonced_tag(type, content_or_options, block)
152153
else
153154
content_or_options.html_safe # :'(
154155
end
155-
content_tag type, content, options.merge(nonce: content_security_policy_nonce(type))
156+
content_tag type, content, options.merge(nonce: _content_security_policy_nonce(type))
156157
end
157158

158159
def extract_options(args)

spec/lib/secure_headers/view_helpers_spec.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ def request
9797
end
9898
end
9999

100+
class MessageWithConflictingMethod < Message
101+
def content_security_policy_nonce
102+
"rails-nonce"
103+
end
104+
end
105+
100106
module SecureHeaders
101107
describe ViewHelpers do
102108
let(:app) { lambda { |env| [200, env, "app"] } }
@@ -159,5 +165,27 @@ module SecureHeaders
159165
expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/style-src[^;]*'#{Regexp.escape(expected_style_hash)}'/)
160166
end
161167
end
168+
169+
it "avoids calling content_security_policy_nonce internally" do
170+
begin
171+
allow(SecureRandom).to receive(:base64).and_return("abc123")
172+
173+
expected_hash = "sha256-3/URElR9+3lvLIouavYD/vhoICSNKilh15CzI/nKqg8="
174+
Configuration.instance_variable_set(:@script_hashes, filename => ["'#{expected_hash}'"])
175+
expected_style_hash = "sha256-7oYK96jHg36D6BM042er4OfBnyUDTG3pH1L8Zso3aGc="
176+
Configuration.instance_variable_set(:@style_hashes, filename => ["'#{expected_style_hash}'"])
177+
178+
# render erb that calls out to helpers.
179+
MessageWithConflictingMethod.new(request).result
180+
_, env = middleware.call request.env
181+
182+
expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/script-src[^;]*'#{Regexp.escape(expected_hash)}'/)
183+
expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/script-src[^;]*'nonce-abc123'/)
184+
expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/style-src[^;]*'nonce-abc123'/)
185+
expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/style-src[^;]*'#{Regexp.escape(expected_style_hash)}'/)
186+
187+
expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).not_to match(/rails-nonce/)
188+
end
189+
end
162190
end
163191
end

0 commit comments

Comments
 (0)