Skip to content

Commit

Permalink
Merge branch 'level2_directives'
Browse files Browse the repository at this point in the history
  • Loading branch information
oreoshake committed Nov 14, 2014
2 parents 79d261d + 03cba9d commit 1a0e141
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 17 deletions.
58 changes: 41 additions & 17 deletions lib/secure_headers/headers/content_security_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,27 @@ module Constants
ENV_KEY = 'secure_headers.content_security_policy'
DIRECTIVES = [
:default_src,
# :base_uri, disabled because this doesn't use the default-src value if empty
# :child_src, disabled because this doesn't use the default-src value if empty
:connect_src,
:font_src,
# :form_action, disabled because this doesn't use the default-src value if empty
:frame_src,
# :frame_ancestors, disabled because this doesn't use the default-src value if empty
:img_src,
:media_src,
:object_src,
# :plugin_types, disabled because this doesn't use the default-src value if empty
# :referrer, disabled because this doesn't use the default-src value if empty
# :reflected_xss, disabled because this doesn't use the default-src value if empty
:script_src,
:style_src
]

NON_DEFAULT_SOURCES = [
:base_uri,
:child_src,
:form_action,
:frame_ancestors,
:plugin_types,
:referrer,
:reflected_xss
]

ALL_DIRECTIVES = DIRECTIVES + NON_DEFAULT_SOURCES
end
include Constants

Expand Down Expand Up @@ -69,6 +74,10 @@ def request_uri_from_request(request)
request.url
end
end

def symbol_to_hyphen_case sym
sym.to_s.gsub('_', '-')
end
end

# +options+ param contains
Expand All @@ -93,7 +102,7 @@ def initialize(config=nil, options={})
@config = config.inject({}) do |hash, (key, value)|
config_val = value.respond_to?(:call) ? value.call : value

if DIRECTIVES.include?(key) # directives need to be normalized to arrays of strings
if ALL_DIRECTIVES.include?(key) # directives need to be normalized to arrays of strings
config_val = config_val.split if config_val.is_a? String
if config_val.is_a?(Array)
config_val = config_val.map do |val|
Expand All @@ -119,10 +128,17 @@ def initialize(config=nil, options={})
fill_directives unless disable_fill_missing?
end

##
# Return or initialize the nonce value used for this header.
# If a reference to a controller is passed in the config, this method
# will check if a nonce has already been set and use it.
def nonce
@nonce ||= @controller.instance_variable_get(:@content_security_policy_nonce) || self.class.generate_nonce
end

##
# Returns the name to use for the header. Either "Content-Security-Policy" or
# "Content-Security-Policy-Report-Only"
def name
base = HEADER_NAME
if !@enforce
Expand All @@ -131,6 +147,8 @@ def name
base
end

##
# Return the value of the CSP header
def value
return @config if @config.is_a?(String)
if @config
Expand All @@ -150,7 +168,8 @@ def build_value
raise "Expected to find default_src directive value" unless @config[:default_src]
append_http_additions unless ssl_request?
header_value = [
generic_directives(@config),
generic_directives,
non_default_directives,
report_uri_directive
].join.strip
end
Expand Down Expand Up @@ -206,12 +225,12 @@ def report_uri_directive
"report-uri #{@report_uri};"
end

def generic_directives(config)
def generic_directives
header_value = ''
if config[:img_src]
config[:img_src] = config[:img_src] + ['data:'] unless config[:img_src].include?('data:')
if @config[:img_src]
@config[:img_src] = @config[:img_src] + ['data:'] unless @config[:img_src].include?('data:')
else
config[:img_src] = config[:default_src] + ['data:']
@config[:img_src] = @config[:default_src] + ['data:']
end

DIRECTIVES.each do |directive_name|
Expand All @@ -221,12 +240,17 @@ def generic_directives(config)
header_value
end

def build_directive(key)
"#{symbol_to_hyphen_case(key)} #{@config[key].join(" ")}; "
def non_default_directives
header_value = ''
NON_DEFAULT_SOURCES.each do |directive_name|
header_value += build_directive(directive_name) if @config[directive_name]
end

header_value
end

def symbol_to_hyphen_case sym
sym.to_s.gsub('_', '-')
def build_directive(key)
"#{self.class.symbol_to_hyphen_case(key)} #{@config[key].join(" ")}; "
end
end
end
12 changes: 12 additions & 0 deletions spec/lib/secure_headers/headers/content_security_policy_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ def request_for user_agent, request_uri=nil, options={:ssl => false}
}.to raise_error(RuntimeError)
end

context "CSP level 2 directives" do
let(:config) { {:default_src => 'self'} }
::SecureHeaders::ContentSecurityPolicy::Constants::NON_DEFAULT_SOURCES.each do |non_default_source|
it "supports all level 2 directives" do
directive_name = ::SecureHeaders::ContentSecurityPolicy.send(:symbol_to_hyphen_case, non_default_source)
config.merge!({ non_default_source => "value" })
csp = ContentSecurityPolicy.new(config, :request => request_for(CHROME))
expect(csp.value).to match(/#{directive_name} value;/)
end
end
end

context "auto-whitelists data: uris for img-src" do
it "sets the value if no img-src specified" do
csp = ContentSecurityPolicy.new({:default_src => 'self', :disable_fill_missing => true}, :request => request_for(CHROME))
Expand Down

0 comments on commit 1a0e141

Please sign in to comment.