diff --git a/actionpack/lib/action_dispatch/constants.rb b/actionpack/lib/action_dispatch/constants.rb index 971b42fd66b04..2771b3adc59d9 100644 --- a/actionpack/lib/action_dispatch/constants.rb +++ b/actionpack/lib/action_dispatch/constants.rb @@ -8,6 +8,8 @@ module Constants if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3") VARY = "Vary" CONTENT_ENCODING = "Content-Encoding" + CONTENT_SECURITY_POLICY = "Content-Security-Policy" + CONTENT_SECURITY_POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only" LOCATION = "Location" FEATURE_POLICY = "Feature-Policy" X_REQUEST_ID = "X-Request-Id" @@ -15,6 +17,8 @@ module Constants else VARY = "vary" CONTENT_ENCODING = "content-encoding" + CONTENT_SECURITY_POLICY = "content-security-policy" + CONTENT_SECURITY_POLICY_REPORT_ONLY = "content-security-policy-report-only" LOCATION = "location" FEATURE_POLICY = "feature-policy" X_REQUEST_ID = "x-request-id" diff --git a/actionpack/lib/action_dispatch/http/content_security_policy.rb b/actionpack/lib/action_dispatch/http/content_security_policy.rb index 20eebb0f862bd..c0ea12beccc90 100644 --- a/actionpack/lib/action_dispatch/http/content_security_policy.rb +++ b/actionpack/lib/action_dispatch/http/content_security_policy.rb @@ -25,10 +25,6 @@ module ActionDispatch # :nodoc: # end class ContentSecurityPolicy class Middleware - CONTENT_TYPE = "Content-Type" - POLICY = "Content-Security-Policy" - POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only" - def initialize(app) @app = app end @@ -57,14 +53,15 @@ def call(env) private def header_name(request) if request.content_security_policy_report_only - POLICY_REPORT_ONLY + ActionDispatch::Constants::CONTENT_SECURITY_POLICY_REPORT_ONLY else - POLICY + ActionDispatch::Constants::CONTENT_SECURITY_POLICY end end def policy_present?(headers) - headers[POLICY] || headers[POLICY_REPORT_ONLY] + headers[ActionDispatch::Constants::CONTENT_SECURITY_POLICY] || + headers[ActionDispatch::Constants::CONTENT_SECURITY_POLICY_REPORT_ONLY] end end diff --git a/actionpack/test/dispatch/content_security_policy_test.rb b/actionpack/test/dispatch/content_security_policy_test.rb index 9af7da91f660e..aa8ee497cb4a7 100644 --- a/actionpack/test/dispatch/content_security_policy_test.rb +++ b/actionpack/test/dispatch/content_security_policy_test.rb @@ -302,6 +302,51 @@ def test_raises_runtime_error_when_unexpected_source end end +class ContentSecurityPolicyMiddlewareTest < ActiveSupport::TestCase + def setup + @env = Rack::MockRequest.env_for("", {}) + @env["action_dispatch.content_security_policy"] = ActionDispatch::ContentSecurityPolicy.new do |p| + p.default_src -> { :self } + end + @env["action_dispatch.content_security_policy_nonce_generator"] = proc { "iyhD0Yc0W+c=" } + @env["action_dispatch.content_security_policy_report_only"] = false + @env["action_dispatch.show_exceptions"] = :none + + @default_csp = "default-src 'self' https: http:" + end + + def test_rack_lint + app = proc { [200, {}, []] } + + assert_nothing_raised do + Rack::Lint.new( + ActionDispatch::ContentSecurityPolicy::Middleware.new( + Rack::Lint.new(app) + ) + ).call(@env) + end + end + + def test_does_not_override_app_content_security_policy + app = proc { [200, { ActionDispatch::Constants::CONTENT_SECURITY_POLICY => @default_csp }, []] } + _, headers, _ = Rack::Lint.new( + ActionDispatch::ContentSecurityPolicy::Middleware.new(Rack::Lint.new(app)) + ).call(@env) + + assert_equal @default_csp, headers[ActionDispatch::Constants::CONTENT_SECURITY_POLICY] + end + + def test_does_not_override_app_content_security_policy_report_only + app = proc { [200, { ActionDispatch::Constants::CONTENT_SECURITY_POLICY_REPORT_ONLY => @default_csp }, []] } + @env["action_dispatch.content_security_policy_report_only"] = true + _, headers, _ = Rack::Lint.new( + ActionDispatch::ContentSecurityPolicy::Middleware.new(Rack::Lint.new(app)) + ).call(@env) + + assert_equal @default_csp, headers[ActionDispatch::Constants::CONTENT_SECURITY_POLICY_REPORT_ONLY] + end +end + class DefaultContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest class PolicyController < ActionController::Base def index @@ -338,8 +383,10 @@ def call(env) end APP = build_app(ROUTES) do |middleware| + middleware.use Rack::Lint middleware.use PolicyConfigMiddleware middleware.use ActionDispatch::ContentSecurityPolicy::Middleware + middleware.use Rack::Lint end def app @@ -488,8 +535,10 @@ def call(env) end APP = build_app(ROUTES) do |middleware| + middleware.use Rack::Lint middleware.use PolicyConfigMiddleware middleware.use ActionDispatch::ContentSecurityPolicy::Middleware + middleware.use Rack::Lint end def app @@ -604,8 +653,10 @@ def call(env) end APP = build_app(ROUTES) do |middleware| + middleware.use Rack::Lint middleware.use PolicyConfigMiddleware middleware.use ActionDispatch::ContentSecurityPolicy::Middleware + middleware.use Rack::Lint end def app @@ -660,8 +711,10 @@ def call(env) end APP = build_app(ROUTES) do |middleware| + middleware.use Rack::Lint middleware.use PolicyConfigMiddleware middleware.use ActionDispatch::ContentSecurityPolicy::Middleware + middleware.use Rack::Lint end def app @@ -732,8 +785,10 @@ def call(env) end APP = build_app(ROUTES) do |middleware| + middleware.use Rack::Lint middleware.use PolicyConfigMiddleware middleware.use ActionDispatch::ContentSecurityPolicy::Middleware + middleware.use Rack::Lint end def app