Skip to content

Commit

Permalink
Merge pull request #150 from twitter/sniff-for-nonce-support
Browse files Browse the repository at this point in the history
Sniff for nonce support
  • Loading branch information
oreoshake committed Jun 24, 2015
2 parents 85d7715 + b8ca818 commit 0448984
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def request(opts = {})
options = opts.merge(
{
'HTTPS' => 'on',
'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"
'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"
}
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def request(opts = {})
options = opts.merge(
{
'HTTPS' => 'on',
'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"
'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"
}
)

Expand Down
14 changes: 12 additions & 2 deletions lib/secure_headers/headers/content_security_policy.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'uri'
require 'base64'
require 'securerandom'
require 'user_agent_parser'

module SecureHeaders
class ContentSecurityPolicyBuildError < StandardError; end
Expand Down Expand Up @@ -205,8 +206,12 @@ def translate_dir_value val
elsif %{self none}.include?(val)
"'#{val}'"
elsif val == 'nonce'
self.class.set_nonce(@controller, nonce)
["'nonce-#{nonce}'", "'unsafe-inline'"]
if supports_nonces?(@ua)
self.class.set_nonce(@controller, nonce)
["'nonce-#{nonce}'", "'unsafe-inline'"]
else
"'unsafe-inline'"
end
else
val
end
Expand Down Expand Up @@ -258,5 +263,10 @@ def non_default_directives
def build_directive(key)
"#{self.class.symbol_to_hyphen_case(key)} #{@config[key].join(" ")}; "
end

def supports_nonces?(user_agent)
parsed_ua = UserAgentParser.parse(user_agent)
["Chrome", "Opera", "Firefox"].include?(parsed_ua.family)
end
end
end
1 change: 1 addition & 0 deletions secure_headers.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ Gem::Specification.new do |gem|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.require_paths = ["lib"]
gem.add_development_dependency "rake"
gem.add_dependency "user_agent_parser"
gem.post_install_message = "Warning: lambda config values will be broken until you add |controller|. e.g. :enforce => lambda { |controller| some_expression }"
end
27 changes: 25 additions & 2 deletions spec/lib/secure_headers/headers/content_security_policy_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ module SecureHeaders
FIREFOX_23 = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:23.0) Gecko/20131011 Firefox/23.0"
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"
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"

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"
OPERA = "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16"

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

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

it "adds a nonce and unsafe-inline to the script-src value when using firefox" do
header = ContentSecurityPolicy.new(default_opts.merge(:script_src => "self nonce"), :request => request_for(FIREFOX))
expect(header.value).to include("script-src 'self' 'nonce-#{header.nonce}' 'unsafe-inline'")
end

it "adds a nonce and unsafe-inline to the script-src value when using opera" do
header = ContentSecurityPolicy.new(default_opts.merge(:script_src => "self nonce"), :request => request_for(OPERA))
expect(header.value).to include("script-src 'self' 'nonce-#{header.nonce}' 'unsafe-inline'")
end

it "does not add a nonce and unsafe-inline to the script-src value when using Safari" do
header = ContentSecurityPolicy.new(default_opts.merge(:script_src => "self nonce"), :request => request_for(SAFARI))
expect(header.value).to include("script-src 'self' 'unsafe-inline'")
expect(header.value).not_to include("nonce")
end

it "does not add a nonce and unsafe-inline to the script-src value when using IE" do
header = ContentSecurityPolicy.new(default_opts.merge(:script_src => "self nonce"), :request => request_for(IE))
expect(header.value).to include("script-src 'self' 'unsafe-inline'")
expect(header.value).not_to include("nonce")
end

it "adds a nonce and unsafe-inline to the style-src value" do
header = ContentSecurityPolicy.new(default_opts.merge(:style_src => "self nonce"), :request => request_for(CHROME))
expect(header.value).to include("style-src 'self' 'nonce-#{header.nonce}' 'unsafe-inline'")
Expand Down

0 comments on commit 0448984

Please sign in to comment.