Merge pull request #45115 from ghiculescu/csp-helpers

Fix using helpers in `content_security_policy` and `permissions_policy`
This commit is contained in:
Guillermo Iguaran 2022-05-18 13:51:36 -07:00 committed by GitHub
commit 6731fa8203
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 171 additions and 2 deletions

@ -1,3 +1,17 @@
* Allow using `helper_method`s in `content_security_policy` and `permissions_policy`
Previously you could access basic helpers (defined in helper modules), but not
helper methods defined using `helper_method`. Now you can use either.
```ruby
content_security_policy do |p|
p.default_src "https://example.com"
p.script_src "https://example.com" if helpers.script_csp?
end
```
*Alex Ghiculescu*
* Reimplement `ActionController::Parameters#has_value?` and `#value?` to avoid parameters and hashes comparison.
Deprecated equality between parameters and hashes is going to be removed in Rails 7.2.

@ -40,7 +40,7 @@ def content_security_policy(enabled = true, **options, &block)
before_action(options) do
if block_given?
policy = current_content_security_policy
yield policy
instance_exec(policy, &block)
request.content_security_policy = policy
end

@ -27,7 +27,7 @@ def permissions_policy(**options, &block)
before_action(options) do
if block_given?
policy = request.permissions_policy.clone
yield policy
instance_exec(policy, &block)
request.permissions_policy = policy
end
end

@ -661,3 +661,76 @@ def test_generate_nonce_only_specified_in_nonce_directives
assert_no_match "style-src https: 'nonce-iyhD0Yc0W+c='", response.headers["Content-Security-Policy"]
end
end
class HelpersContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
module ApplicationHelper
def pigs_can_fly?
false
end
end
class ApplicationController < ActionController::Base
helper_method :sky_is_blue?
def sky_is_blue?
true
end
end
class PolicyController < ApplicationController
content_security_policy do |p|
p.default_src "https://example.com"
p.script_src "https://example.com" if helpers.sky_is_blue?
p.style_src "https://example.com" unless helpers.pigs_can_fly?
end
def index
head :ok
end
end
ROUTES = ActionDispatch::Routing::RouteSet.new
ROUTES.draw do
scope module: "helpers_content_security_policy_integration_test" do
get "/", to: "policy#index"
end
end
POLICY = ActionDispatch::ContentSecurityPolicy.new do |p|
p.default_src -> { :self }
p.script_src -> { :https }
p.style_src -> { :https }
end
class PolicyConfigMiddleware
def initialize(app)
@app = app
end
def call(env)
env["action_dispatch.content_security_policy"] = POLICY
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"] = false
@app.call(env)
end
end
APP = build_app(ROUTES) do |middleware|
middleware.use PolicyConfigMiddleware
middleware.use ActionDispatch::ContentSecurityPolicy::Middleware
end
def app
APP
end
def test_can_call_helper_methods_in_csp
get "/"
assert_response :success
assert_match "default-src https://example.com", response.headers["Content-Security-Policy"]
assert_match "script-src https://example.com", response.headers["Content-Security-Policy"]
assert_match "style-src https://example.com", response.headers["Content-Security-Policy"]
end
end

@ -140,3 +140,85 @@ def assert_policy(expected)
assert_equal expected, response.headers["Feature-Policy"]
end
end
class PermissionsPolicyWithHelpersIntegrationTest < ActionDispatch::IntegrationTest
module ApplicationHelper
def pigs_can_fly?
false
end
end
class ApplicationController < ActionController::Base
helper_method :sky_is_blue?
def sky_is_blue?
true
end
end
class PolicyController < ApplicationController
permissions_policy do |f|
f.gyroscope :none unless helpers.pigs_can_fly?
f.usb :self if helpers.sky_is_blue?
end
def index
head :ok
end
end
ROUTES = ActionDispatch::Routing::RouteSet.new
ROUTES.draw do
scope module: "permissions_policy_with_helpers_integration_test" do
get "/", to: "policy#index"
end
end
POLICY = ActionDispatch::PermissionsPolicy.new do |p|
p.gyroscope :self
end
class PolicyConfigMiddleware
def initialize(app)
@app = app
end
def call(env)
env["action_dispatch.permissions_policy"] = POLICY
env["action_dispatch.show_exceptions"] = false
@app.call(env)
end
end
APP = build_app(ROUTES) do |middleware|
middleware.use PolicyConfigMiddleware
middleware.use ActionDispatch::PermissionsPolicy::Middleware
end
def app
APP
end
def test_generates_permissions_policy_header
get "/"
assert_policy "gyroscope 'none'; usb 'self'"
end
private
def env_config
Rails.application.env_config
end
def permissions_policy
env_config["action_dispatch.permissions_policy"]
end
def permissions_policy=(policy)
env_config["action_dispatch.permissions_policy"] = policy
end
def assert_policy(expected)
assert_response :success
assert_equal expected, response.headers["Feature-Policy"]
end
end