summaryrefslogtreecommitdiff
path: root/spec/controllers/concerns/content_security_policy_patch_spec.rb
blob: 6322950977ce223c5e91ce39e57ba1f59a7ca5a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# frozen_string_literal: true

require "spec_helper"

# Based on https://github.com/rails/rails/pull/45115/files#diff-35ef6d1bd8b8d3b037ec819a704cd78db55db916a57abfc2859882826fc679b6
RSpec.describe ContentSecurityPolicyPatch, feature_category: :not_owned do
  include Rack::Test::Methods

  let(:routes) do
    ActionDispatch::Routing::RouteSet.new.tap do |routes|
      routes.draw do
        # Using Testing module defined below
        scope module: "testing" do
          get "/", to: "policy#index"
        end
      end
    end
  end

  let(:csp) do
    ActionDispatch::ContentSecurityPolicy.new do |p|
      p.default_src -> { :self }
      p.script_src -> { :https }
    end
  end

  let(:policy_middleware) do
    Module.new do
      def self.new(app, policy)
        ->(env) do
          env["action_dispatch.content_security_policy"] = policy

          app.call(env)
        end
      end
    end
  end

  subject(:app) do
    build_app(routes) do |middleware|
      middleware.use policy_middleware, csp
      middleware.use ActionDispatch::ContentSecurityPolicy::Middleware
    end
  end

  def setup_controller
    application_controller = Class.new(ActionController::Base) do # rubocop:disable Rails/ApplicationController
      helper_method :sky_is_blue?
      def sky_is_blue?
        true
      end
    end

    policy_controller = Class.new(application_controller) do
      extend ContentSecurityPolicyPatch

      content_security_policy_with_context do |p|
        p.default_src "https://example.com"
        p.script_src "https://example.com" if helpers.sky_is_blue?
      end

      def index
        head :ok
      end
    end

    stub_const("Testing::ApplicationController", application_controller)
    stub_const("Testing::PolicyController", policy_controller)
  end

  def build_app(routes)
    stack = ActionDispatch::MiddlewareStack.new do |middleware|
      middleware.use ActionDispatch::DebugExceptions
      middleware.use ActionDispatch::ActionableExceptions
      middleware.use ActionDispatch::Callbacks
      middleware.use ActionDispatch::Cookies
      middleware.use ActionDispatch::Flash
      middleware.use Rack::MethodOverride
      middleware.use Rack::Head

      yield(middleware) if block_given?
    end

    app = stack.build(routes)

    ->(env) { app.call(env) }
  end

  it "calls helper method" do
    setup_controller

    response = get "/"

    csp_header = response.headers["Content-Security-Policy"]

    expect(csp_header).to include "default-src https://example.com"
    expect(csp_header).to include "script-src https://example.com"
  end

  it "does not emit any warnings" do
    expect { setup_controller }.not_to output.to_stderr
  end

  context "with Rails version 7.2" do
    before do
      version = Gem::Version.new("7.2.0")
      allow(Rails).to receive(:gem_version).and_return(version)
    end

    it "emits a deprecation warning" do
      expect { setup_controller }
        .to output(/Use content_security_policy instead/)
        .to_stderr
    end
  end
end