diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 0c3b700a73962..d2dddd68d02dd 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,19 @@ +* Fix `content_security_policy` returning invalid directives. + + Directives such as `self`, `unsafe-eval` and few others were not + single quoted when the directive was the result of calling a lambda + returning an array. + + ```ruby + content_security_policy do |policy| + policy.frame_ancestors lambda { [:self, "https://example.com"] } + end + ``` + + With this fix the policy generated from above will now be valid. + + *Edouard Chin* + * Fix `skip_forgery_protection` to run without raising an error if forgery protection has not been enabled / `verify_authenticity_token` is not a defined callback. diff --git a/actionpack/lib/action_dispatch/http/content_security_policy.rb b/actionpack/lib/action_dispatch/http/content_security_policy.rb index 6a6d38d55c7fc..9b63a157626b6 100644 --- a/actionpack/lib/action_dispatch/http/content_security_policy.rb +++ b/actionpack/lib/action_dispatch/http/content_security_policy.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "active_support/core_ext/object/deep_dup" +require "active_support/core_ext/array/wrap" module ActionDispatch # :nodoc: # Configures the HTTP @@ -345,7 +346,7 @@ def resolve_source(source, context) raise RuntimeError, "Missing context for the dynamic content security policy source: #{source.inspect}" else resolved = context.instance_exec(&source) - resolved.is_a?(Symbol) ? apply_mapping(resolved) : resolved + apply_mappings(Array.wrap(resolved)) end else raise RuntimeError, "Unexpected content security policy source: #{source.inspect}" diff --git a/actionpack/test/dispatch/content_security_policy_test.rb b/actionpack/test/dispatch/content_security_policy_test.rb index f95920b145098..0fd1868a3f94f 100644 --- a/actionpack/test/dispatch/content_security_policy_test.rb +++ b/actionpack/test/dispatch/content_security_policy_test.rb @@ -255,6 +255,14 @@ def test_dynamic_directives assert_equal "script-src www.example.com", @policy.build(controller) end + def test_multiple_and_dynamic_directives + request = ActionDispatch::Request.new("HTTP_HOST" => "www.example.com") + controller = Struct.new(:request).new(request) + + @policy.frame_ancestors -> { [:self, "https://example.com"] } + assert_equal "frame-ancestors 'self' https://example.com", @policy.build(controller) + end + def test_mixed_static_and_dynamic_directives @policy.script_src :self, -> { "foo.com" }, "bar.com" request = ActionDispatch::Request.new({})