diff --git a/lib/secure_headers/headers/content_security_policy.rb b/lib/secure_headers/headers/content_security_policy.rb index ae225e7c..c80967b0 100644 --- a/lib/secure_headers/headers/content_security_policy.rb +++ b/lib/secure_headers/headers/content_security_policy.rb @@ -129,6 +129,7 @@ def minify_source_list(directive, source_list) else source_list = populate_nonces(directive, source_list) source_list = reject_all_values_if_none(source_list) + source_list = normalize_uri_paths(source_list) unless directive == REPORT_URI || @preserve_schemes source_list = strip_source_schemes(source_list) @@ -151,6 +152,26 @@ def reject_all_values_if_none(source_list) end end + def normalize_uri_paths(source_list) + source_list.map do |source| + # Normalize domains ending in a single / as without omitting the slash accomplishes the same. + # https://www.w3.org/TR/CSP3/#match-paths ยง 6.6.2.10 Step 2 + begin + uri = URI(source) + if uri.path == "/" + next source.chomp("/") + end + rescue URI::InvalidURIError + end + + if source.chomp("/").include?("/") + source + else + source.chomp("/") + end + end + end + # Private: append a nonce to the script/style directories if script_nonce # or style_nonce are provided. def populate_nonces(directive, source_list) diff --git a/lib/secure_headers/middleware.rb b/lib/secure_headers/middleware.rb index e1c07c5b..c579f69a 100644 --- a/lib/secure_headers/middleware.rb +++ b/lib/secure_headers/middleware.rb @@ -9,6 +9,7 @@ def initialize(app) def call(env) req = Rack::Request.new(env) status, headers, response = @app.call(env) + headers = Rack::Headers[headers] config = SecureHeaders.config_for(req) flag_cookies!(headers, override_secure(env, config.cookies)) unless config.cookies == OPT_OUT @@ -20,14 +21,12 @@ def call(env) # inspired by https://github.com/tobmatth/rack-ssl-enforcer/blob/6c014/lib/rack/ssl-enforcer.rb#L183-L194 def flag_cookies!(headers, config) - if cookies = headers["Set-Cookie"] - # Support Rails 2.3 / Rack 1.1 arrays as headers - cookies = cookies.split("\n") unless cookies.is_a?(Array) + cookies = headers["Set-Cookie"] + return unless cookies - headers["Set-Cookie"] = cookies.map do |cookie| - SecureHeaders::Cookie.new(cookie, config).to_s - end.join("\n") - end + cookies_array = cookies.is_a?(Array) ? cookies : cookies.split("\n") + secured_cookies = cookies_array.map { |cookie| SecureHeaders::Cookie.new(cookie, config).to_s } + headers["Set-Cookie"] = cookies.is_a?(Array) ? secured_cookies : secured_cookies.join("\n") end # disable Secure cookies for non-https requests diff --git a/lib/secure_headers/railtie.rb b/lib/secure_headers/railtie.rb index ba255acc..64f9eec9 100644 --- a/lib/secure_headers/railtie.rb +++ b/lib/secure_headers/railtie.rb @@ -22,9 +22,12 @@ class Railtie < Rails::Railtie ActiveSupport.on_load(:action_controller) do include SecureHeaders - unless Rails.application.config.action_dispatch.default_headers.nil? - conflicting_headers.each do |header| - Rails.application.config.action_dispatch.default_headers.delete(header) + default_headers = Rails.application.config.action_dispatch.default_headers + unless default_headers.nil? + default_headers.each_key do |header| + if conflicting_headers.include?(header.downcase) + default_headers.delete(header) + end end end end diff --git a/lib/secure_headers/task_helper.rb b/lib/secure_headers/task_helper.rb new file mode 100644 index 00000000..fa4ba971 --- /dev/null +++ b/lib/secure_headers/task_helper.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +module SecureHeaders + module TaskHelper + include SecureHeaders::HashHelper + + INLINE_SCRIPT_REGEX = /( + + <%= hashed_javascript_tag do %> + alert("Using the helper tag!") + <% end %> + <%= hashed_style_tag do %> + p { text-decoration: underline; } + <% end %> + +
+Testing
+ +