Skip to content

Commit 0448984

Browse files
committed
Merge pull request #150 from twitter/sniff-for-nonce-support
Sniff for nonce support
2 parents 85d7715 + b8ca818 commit 0448984

File tree

5 files changed

+40
-6
lines changed

5 files changed

+40
-6
lines changed

fixtures/rails_3_2_12/spec/controllers/other_things_controller_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def request(opts = {})
1313
options = opts.merge(
1414
{
1515
'HTTPS' => 'on',
16-
'HTTP_USER_AGENT' => "Mozilla/5.0 (compatible; MSIE 10.6; Windows NT 6.1; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0"
16+
'HTTP_USER_AGENT' => "Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/537.22 (KHTML like Gecko) Chrome/25.0.1364.99 Safari/537.22"
1717
}
1818
)
1919

fixtures/rails_4_1_8/spec/controllers/other_things_controller_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def request(opts = {})
1313
options = opts.merge(
1414
{
1515
'HTTPS' => 'on',
16-
'HTTP_USER_AGENT' => "Mozilla/5.0 (compatible; MSIE 10.6; Windows NT 6.1; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0"
16+
'HTTP_USER_AGENT' => "Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/537.22 (KHTML like Gecko) Chrome/25.0.1364.99 Safari/537.22"
1717
}
1818
)
1919

lib/secure_headers/headers/content_security_policy.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require 'uri'
22
require 'base64'
33
require 'securerandom'
4+
require 'user_agent_parser'
45

56
module SecureHeaders
67
class ContentSecurityPolicyBuildError < StandardError; end
@@ -205,8 +206,12 @@ def translate_dir_value val
205206
elsif %{self none}.include?(val)
206207
"'#{val}'"
207208
elsif val == 'nonce'
208-
self.class.set_nonce(@controller, nonce)
209-
["'nonce-#{nonce}'", "'unsafe-inline'"]
209+
if supports_nonces?(@ua)
210+
self.class.set_nonce(@controller, nonce)
211+
["'nonce-#{nonce}'", "'unsafe-inline'"]
212+
else
213+
"'unsafe-inline'"
214+
end
210215
else
211216
val
212217
end
@@ -258,5 +263,10 @@ def non_default_directives
258263
def build_directive(key)
259264
"#{self.class.symbol_to_hyphen_case(key)} #{@config[key].join(" ")}; "
260265
end
266+
267+
def supports_nonces?(user_agent)
268+
parsed_ua = UserAgentParser.parse(user_agent)
269+
["Chrome", "Opera", "Firefox"].include?(parsed_ua.family)
270+
end
261271
end
262272
end

secure_headers.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ Gem::Specification.new do |gem|
1919
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
2020
gem.require_paths = ["lib"]
2121
gem.add_development_dependency "rake"
22+
gem.add_dependency "user_agent_parser"
2223
gem.post_install_message = "Warning: lambda config values will be broken until you add |controller|. e.g. :enforce => lambda { |controller| some_expression }"
2324
end

spec/lib/secure_headers/headers/content_security_policy_spec.rb

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ module SecureHeaders
1717
FIREFOX_23 = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:23.0) Gecko/20131011 Firefox/23.0"
1818
CHROME = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.99 Safari/533.4"
1919
CHROME_25 = "Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/537.22 (KHTML like Gecko) Chrome/25.0.1364.99 Safari/537.22"
20-
20+
SAFARI = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A"
21+
OPERA = "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16"
2122

2223
def request_for user_agent, request_uri=nil, options={:ssl => false}
2324
double(:ssl? => options[:ssl], :env => {'HTTP_USER_AGENT' => user_agent}, :url => (request_uri || 'http://areallylongdomainexample.com') )
@@ -184,11 +185,33 @@ def request_for user_agent, request_uri=nil, options={:ssl => false}
184185
end
185186

186187
context "when using a nonce" do
187-
it "adds a nonce and unsafe-inline to the script-src value" do
188+
it "adds a nonce and unsafe-inline to the script-src value when using chrome" do
188189
header = ContentSecurityPolicy.new(default_opts.merge(:script_src => "self nonce"), :request => request_for(CHROME))
189190
expect(header.value).to include("script-src 'self' 'nonce-#{header.nonce}' 'unsafe-inline'")
190191
end
191192

193+
it "adds a nonce and unsafe-inline to the script-src value when using firefox" do
194+
header = ContentSecurityPolicy.new(default_opts.merge(:script_src => "self nonce"), :request => request_for(FIREFOX))
195+
expect(header.value).to include("script-src 'self' 'nonce-#{header.nonce}' 'unsafe-inline'")
196+
end
197+
198+
it "adds a nonce and unsafe-inline to the script-src value when using opera" do
199+
header = ContentSecurityPolicy.new(default_opts.merge(:script_src => "self nonce"), :request => request_for(OPERA))
200+
expect(header.value).to include("script-src 'self' 'nonce-#{header.nonce}' 'unsafe-inline'")
201+
end
202+
203+
it "does not add a nonce and unsafe-inline to the script-src value when using Safari" do
204+
header = ContentSecurityPolicy.new(default_opts.merge(:script_src => "self nonce"), :request => request_for(SAFARI))
205+
expect(header.value).to include("script-src 'self' 'unsafe-inline'")
206+
expect(header.value).not_to include("nonce")
207+
end
208+
209+
it "does not add a nonce and unsafe-inline to the script-src value when using IE" do
210+
header = ContentSecurityPolicy.new(default_opts.merge(:script_src => "self nonce"), :request => request_for(IE))
211+
expect(header.value).to include("script-src 'self' 'unsafe-inline'")
212+
expect(header.value).not_to include("nonce")
213+
end
214+
192215
it "adds a nonce and unsafe-inline to the style-src value" do
193216
header = ContentSecurityPolicy.new(default_opts.merge(:style_src => "self nonce"), :request => request_for(CHROME))
194217
expect(header.value).to include("style-src 'self' 'nonce-#{header.nonce}' 'unsafe-inline'")

0 commit comments

Comments
 (0)