1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/actionpack/test/dispatch/feature_policy_test.rb

143 lines
3.4 KiB
Ruby
Raw Normal View History

Adds support for configuring HTTP Feature Policy (#33439) A HTTP feature policy is Yet Another HTTP header for instructing the browser about which features the application intends to make use of and to lock down access to others. This is a new security mechanism that ensures that should an application become compromised or a third party attempts an unexpected action, the browser will override it and maintain the intended UX. WICG specification: https://wicg.github.io/feature-policy/ The end result is a HTTP header that looks like the following: ``` Feature-Policy: geolocation 'none'; autoplay https://example.com ``` This will prevent the browser from using geolocation and only allow autoplay on `https://example.com`. Full feature list can be found over in the WICG repository[1]. As of today Chrome and Safari have public support[2] for this functionality with Firefox working on support[3] and Edge still pending acceptance of the suggestion[4]. #### Examples Using an initializer ```rb # config/initializers/feature_policy.rb Rails.application.config.feature_policy do |f| f.geolocation :none f.camera :none f.payment "https://secure.example.com" f.fullscreen :self end ``` In a controller ```rb class SampleController < ApplicationController def index feature_policy do |f| f.geolocation "https://example.com" end end end ``` Some of you might realise that the HTTP feature policy looks pretty close to that of a Content Security Policy; and you're right. So much so that I used the Content Security Policy DSL from #31162 as the starting point for this change. This change *doesn't* introduce support for defining a feature policy on an iframe and this has been intentionally done to split the HTTP header and the HTML element (`iframe`) support. If this is successful, I'll look to add that on it's own. Full documentation on HTTP feature policies can be found at https://wicg.github.io/feature-policy/. Google have also published[5] a great in-depth write up of this functionality. [1]: https://github.com/WICG/feature-policy/blob/master/features.md [2]: https://www.chromestatus.com/feature/5694225681219584 [3]: https://bugzilla.mozilla.org/show_bug.cgi?id=1390801 [4]: https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/33507907-support-feature-policy [5]: https://developers.google.com/web/updates/2018/06/feature-policy
2019-07-10 18:33:16 -04:00
# frozen_string_literal: true
require "abstract_unit"
class FeaturePolicyTest < ActiveSupport::TestCase
def setup
@policy = ActionDispatch::FeaturePolicy.new
end
def test_mappings
@policy.midi :self
assert_equal "midi 'self'", @policy.build
@policy.midi :none
assert_equal "midi 'none'", @policy.build
end
def test_multiple_sources_for_a_single_directive
@policy.geolocation :self, "https://example.com"
assert_equal "geolocation 'self' https://example.com", @policy.build
end
def test_single_directive_for_multiple_directives
@policy.geolocation :self
@policy.usb :none
assert_equal "geolocation 'self'; usb 'none'", @policy.build
end
def test_multiple_directives_for_multiple_directives
@policy.geolocation :self, "https://example.com"
@policy.usb :none, "https://example.com"
assert_equal "geolocation 'self' https://example.com; usb 'none' https://example.com", @policy.build
end
def test_invalid_directive_source
exception = assert_raises(ArgumentError) do
@policy.vr [:non_existent]
end
assert_equal "Invalid HTTP feature policy source: [:non_existent]", exception.message
end
end
class FeaturePolicyIntegrationTest < ActionDispatch::IntegrationTest
class PolicyController < ActionController::Base
feature_policy only: :index do |f|
f.gyroscope :none
end
feature_policy only: :sample_controller do |f|
f.gyroscope nil
f.usb :self
end
feature_policy only: :multiple_directives do |f|
f.gyroscope nil
f.usb :self
f.autoplay "https://example.com"
f.payment "https://secure.example.com"
end
def index
head :ok
end
def sample_controller
head :ok
end
def multiple_directives
head :ok
end
end
ROUTES = ActionDispatch::Routing::RouteSet.new
ROUTES.draw do
scope module: "feature_policy_integration_test" do
get "/", to: "policy#index"
get "/sample_controller", to: "policy#sample_controller"
get "/multiple_directives", to: "policy#multiple_directives"
end
end
POLICY = ActionDispatch::FeaturePolicy.new do |p|
p.gyroscope :self
end
class PolicyConfigMiddleware
def initialize(app)
@app = app
end
def call(env)
env["action_dispatch.feature_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::FeaturePolicy::Middleware
end
def app
APP
end
def test_generates_feature_policy_header
get "/"
assert_policy "gyroscope 'none'"
end
def test_generates_per_controller_feature_policy_header
get "/sample_controller"
assert_policy "usb 'self'"
end
def test_generates_multiple_directives_feature_policy_header
get "/multiple_directives"
assert_policy "usb 'self'; autoplay https://example.com; payment https://secure.example.com"
end
private
def env_config
Rails.application.env_config
end
def feature_policy
env_config["action_dispatch.feature_policy"]
end
def feature_policy=(policy)
env_config["action_dispatch.feature_policy"] = policy
end
def assert_policy(expected)
assert_response :success
assert_equal expected, response.headers["Feature-Policy"]
end
end