Skip to content

Commit

Permalink
handle the case where nonces weren't populated for default csp config…
Browse files Browse the repository at this point in the history
…s using the nonce helpers
  • Loading branch information
oreoshake committed Sep 6, 2016
1 parent a1d74cf commit 16b0b0c
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 13 deletions.
41 changes: 29 additions & 12 deletions lib/secure_headers/headers/policy_management.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,6 @@ def self.included(base)
FRAME_ANCESTORS = :frame_ancestors
PLUGIN_TYPES = :plugin_types

# These are directives that do not inherit the default-src value. This is
# useful when calling #combine_policies.
NON_FETCH_SOURCES = [
BASE_URI,
FORM_ACTION,
FRAME_ANCESTORS,
PLUGIN_TYPES,
REPORT_URI
]

DIRECTIVES_2_0 = [
DIRECTIVES_1_0,
BASE_URI,
Expand Down Expand Up @@ -127,6 +117,18 @@ def self.included(base)
# everything else is in between.
BODY_DIRECTIVES = ALL_DIRECTIVES - [DEFAULT_SRC, REPORT_URI]

# These are directives that do not inherit the default-src value. This is
# useful when calling #combine_policies.
NON_FETCH_SOURCES = [
BASE_URI,
FORM_ACTION,
FRAME_ANCESTORS,
PLUGIN_TYPES,
REPORT_URI
]

FETCH_SOURCES = ALL_DIRECTIVES - NON_FETCH_SOURCES

VARIATIONS = {
"Chrome" => CHROME_DIRECTIVES,
"Opera" => CHROME_DIRECTIVES,
Expand Down Expand Up @@ -268,8 +270,23 @@ def merge_policy_additions(original, additions)
def populate_fetch_source_with_default!(original, additions)
# in case we would be appending to an empty directive, fill it with the default-src value
additions.keys.each do |directive|
unless original[directive] || !source_list?(directive) || NON_FETCH_SOURCES.include?(directive)
original[directive] = original[:default_src]
if !original[directive] && ((source_list?(directive) && FETCH_SOURCES.include?(directive)) || nonce_added?(original, additions))
if nonce_added?(original, additions)
inferred_directive = directive.to_s.gsub(/_nonce/, "_src").to_sym
unless original[inferred_directive] || NON_FETCH_SOURCES.include?(inferred_directive)
original[inferred_directive] = original[:default_src]
end
else
original[directive] = original[:default_src]
end
end
end
end

def nonce_added?(original, additions)
[:script_nonce, :style_nonce].each do |nonce|
if additions[nonce] && !original[nonce]
return true
end
end
end
Expand Down
29 changes: 28 additions & 1 deletion spec/lib/secure_headers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ module SecureHeaders
end

context "content security policy" do
let(:chrome_request) {
Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:chrome]))
}

it "appends a value to csp directive" do
Configuration.default do |config|
config.csp = {
Expand All @@ -151,6 +155,30 @@ module SecureHeaders
expect(hash[CSP::HEADER_NAME]).to eq("default-src 'self'; script-src mycdn.com 'unsafe-inline' anothercdn.com")
end

it "appends a nonce to a missing script-src value" do
Configuration.default do |config|
config.csp = {
default_src: %w('self')
}
end

SecureHeaders.content_security_policy_script_nonce(request) # should add the value to the header
hash = SecureHeaders.header_hash_for(chrome_request)
expect(hash[CSP::HEADER_NAME]).to match /\Adefault-src 'self'; script-src 'self' 'nonce-.*'\z/
end

it "appends a hash to a missing script-src value" do
Configuration.default do |config|
config.csp = {
default_src: %w('self')
}
end

SecureHeaders.append_content_security_policy_directives(request, script_src: %w('sha256-abc123'))
hash = SecureHeaders.header_hash_for(chrome_request)
expect(hash[CSP::HEADER_NAME]).to match /\Adefault-src 'self'; script-src 'self' 'sha256-abc123'\z/
end

it "dups global configuration just once when overriding n times and only calls idempotent_additions? once" do
Configuration.default do |config|
config.csp = {
Expand Down Expand Up @@ -234,7 +262,6 @@ module SecureHeaders
}
end

chrome_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:chrome]))
nonce = SecureHeaders.content_security_policy_script_nonce(chrome_request)

# simulate the nonce being used multiple times in a request:
Expand Down

0 comments on commit 16b0b0c

Please sign in to comment.