secure_headers 1.3.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -8
- data/.travis.yml +7 -4
- data/Gemfile +6 -7
- data/README.md +115 -79
- data/Rakefile +11 -116
- data/fixtures/rails_3_2_12/Gemfile +0 -5
- data/fixtures/rails_3_2_12/app/controllers/things_controller.rb +0 -1
- data/fixtures/rails_3_2_12/app/views/layouts/application.html.erb +1 -4
- data/fixtures/rails_3_2_12/app/views/other_things/index.html.erb +2 -1
- data/fixtures/rails_3_2_12/config/application.rb +0 -54
- data/fixtures/rails_3_2_12/config/initializers/secure_headers.rb +2 -1
- data/fixtures/rails_3_2_12/config/routes.rb +0 -57
- data/fixtures/rails_3_2_12/config/script_hashes.yml +5 -0
- data/fixtures/rails_3_2_12/config.ru +3 -0
- data/fixtures/rails_3_2_12/spec/controllers/other_things_controller_spec.rb +55 -18
- data/fixtures/rails_3_2_12/spec/controllers/things_controller_spec.rb +6 -1
- data/fixtures/rails_3_2_12_no_init/Gemfile +0 -6
- data/fixtures/rails_3_2_12_no_init/app/controllers/other_things_controller.rb +1 -2
- data/fixtures/rails_3_2_12_no_init/app/controllers/things_controller.rb +1 -1
- data/fixtures/rails_3_2_12_no_init/app/views/layouts/application.html.erb +0 -2
- data/fixtures/rails_3_2_12_no_init/app/views/things/index.html.erb +0 -21
- data/fixtures/rails_3_2_12_no_init/config/application.rb +0 -48
- data/fixtures/rails_3_2_12_no_init/config/routes.rb +0 -57
- data/fixtures/rails_3_2_12_no_init/spec/controllers/other_things_controller_spec.rb +5 -0
- data/fixtures/rails_3_2_12_no_init/spec/controllers/things_controller_spec.rb +5 -0
- data/fixtures/rails_4_1_8/Gemfile +5 -0
- data/fixtures/rails_4_1_8/README.rdoc +28 -0
- data/fixtures/rails_4_1_8/Rakefile +6 -0
- data/fixtures/rails_4_1_8/app/controllers/application_controller.rb +4 -0
- data/fixtures/rails_4_1_8/app/controllers/concerns/.keep +0 -0
- data/fixtures/rails_4_1_8/app/controllers/other_things_controller.rb +5 -0
- data/fixtures/rails_4_1_8/app/controllers/things_controller.rb +5 -0
- data/fixtures/rails_4_1_8/app/models/.keep +0 -0
- data/fixtures/rails_4_1_8/app/models/concerns/.keep +0 -0
- data/fixtures/rails_4_1_8/app/views/layouts/application.html.erb +11 -0
- data/fixtures/rails_4_1_8/app/views/other_things/index.html.erb +2 -0
- data/fixtures/rails_4_1_8/app/views/things/index.html.erb +1 -0
- data/fixtures/rails_4_1_8/config/application.rb +15 -0
- data/fixtures/rails_4_1_8/config/boot.rb +4 -0
- data/fixtures/rails_4_1_8/config/environment.rb +5 -0
- data/fixtures/rails_4_1_8/config/environments/test.rb +10 -0
- data/fixtures/rails_4_1_8/config/initializers/secure_headers.rb +17 -0
- data/fixtures/rails_4_1_8/config/routes.rb +4 -0
- data/fixtures/rails_4_1_8/config/script_hashes.yml +5 -0
- data/fixtures/rails_4_1_8/config/secrets.yml +22 -0
- data/fixtures/rails_4_1_8/config.ru +4 -0
- data/fixtures/rails_4_1_8/lib/assets/.keep +0 -0
- data/fixtures/rails_4_1_8/lib/tasks/.keep +0 -0
- data/fixtures/rails_4_1_8/log/.keep +0 -0
- data/fixtures/rails_4_1_8/spec/controllers/other_things_controller_spec.rb +83 -0
- data/fixtures/rails_4_1_8/spec/controllers/things_controller_spec.rb +59 -0
- data/fixtures/rails_4_1_8/spec/spec_helper.rb +15 -0
- data/fixtures/rails_4_1_8/vendor/assets/javascripts/.keep +0 -0
- data/fixtures/rails_4_1_8/vendor/assets/stylesheets/.keep +0 -0
- data/lib/secure_headers/hash_helper.rb +7 -0
- data/lib/secure_headers/headers/content_security_policy/script_hash_middleware.rb +22 -0
- data/lib/secure_headers/headers/content_security_policy.rb +141 -133
- data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +40 -0
- data/lib/secure_headers/railtie.rb +13 -22
- data/lib/secure_headers/version.rb +1 -1
- data/lib/secure_headers/view_helper.rb +68 -0
- data/lib/secure_headers.rb +58 -16
- data/lib/tasks/tasks.rake +48 -0
- data/secure_headers.gemspec +4 -4
- data/spec/lib/secure_headers/headers/content_security_policy/script_hash_middleware_spec.rb +47 -0
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +83 -208
- data/spec/lib/secure_headers/headers/x_download_options_spec.rb +1 -1
- data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +64 -0
- data/spec/lib/secure_headers_spec.rb +36 -61
- data/spec/spec_helper.rb +26 -1
- metadata +51 -37
- checksums.yaml +0 -15
- data/Guardfile +0 -6
- data/HISTORY.md +0 -162
- data/app/controllers/content_security_policy_controller.rb +0 -75
- data/config/curl-ca-bundle.crt +0 -5420
- data/config/routes.rb +0 -3
- data/fixtures/rails_3_2_12/config/environments/development.rb +0 -37
- data/fixtures/rails_3_2_12/config/environments/production.rb +0 -67
- data/fixtures/rails_3_2_12/config/initializers/backtrace_silencers.rb +0 -7
- data/fixtures/rails_3_2_12/config/initializers/inflections.rb +0 -15
- data/fixtures/rails_3_2_12/config/initializers/mime_types.rb +0 -5
- data/fixtures/rails_3_2_12/config/initializers/secret_token.rb +0 -7
- data/fixtures/rails_3_2_12/config/initializers/session_store.rb +0 -8
- data/fixtures/rails_3_2_12/config/initializers/wrap_parameters.rb +0 -14
- data/fixtures/rails_3_2_12/config/locales/en.yml +0 -5
- data/fixtures/rails_3_2_12_no_init/app/views/things/_form.html.erb +0 -17
- data/fixtures/rails_3_2_12_no_init/app/views/things/edit.html.erb +0 -6
- data/fixtures/rails_3_2_12_no_init/app/views/things/new.html.erb +0 -5
- data/fixtures/rails_3_2_12_no_init/app/views/things/show.html.erb +0 -5
- data/fixtures/rails_3_2_12_no_init/config/environments/development.rb +0 -37
- data/fixtures/rails_3_2_12_no_init/config/environments/production.rb +0 -67
- data/fixtures/rails_3_2_12_no_init/config/initializers/backtrace_silencers.rb +0 -7
- data/fixtures/rails_3_2_12_no_init/config/initializers/inflections.rb +0 -15
- data/fixtures/rails_3_2_12_no_init/config/initializers/mime_types.rb +0 -5
- data/fixtures/rails_3_2_12_no_init/config/initializers/secret_token.rb +0 -7
- data/fixtures/rails_3_2_12_no_init/config/initializers/session_store.rb +0 -8
- data/fixtures/rails_3_2_12_no_init/config/initializers/wrap_parameters.rb +0 -14
- data/fixtures/rails_3_2_12_no_init/config/locales/en.yml +0 -5
- data/spec/controllers/content_security_policy_controller_spec.rb +0 -90
|
@@ -4,12 +4,11 @@ module SecureHeaders
|
|
|
4
4
|
describe ContentSecurityPolicy do
|
|
5
5
|
let(:default_opts) do
|
|
6
6
|
{
|
|
7
|
-
:disable_chrome_extension => true,
|
|
8
7
|
:disable_fill_missing => true,
|
|
9
|
-
:default_src => 'https
|
|
8
|
+
:default_src => 'https:',
|
|
10
9
|
:report_uri => '/csp_report',
|
|
11
|
-
:script_src => 'inline eval https
|
|
12
|
-
:style_src => "inline https
|
|
10
|
+
:script_src => 'inline eval https: data:',
|
|
11
|
+
:style_src => "inline https: about:"
|
|
13
12
|
}
|
|
14
13
|
end
|
|
15
14
|
|
|
@@ -30,44 +29,41 @@ module SecureHeaders
|
|
|
30
29
|
|
|
31
30
|
describe "#name" do
|
|
32
31
|
context "when supplying options to override request" do
|
|
33
|
-
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => IE).name).to eq(
|
|
34
|
-
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => FIREFOX).name).to eq(
|
|
35
|
-
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => FIREFOX_23).name).to eq(
|
|
36
|
-
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => CHROME).name).to eq(
|
|
37
|
-
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => CHROME_25).name).to eq(
|
|
32
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => IE).name).to eq(HEADER_NAME + "-Report-Only")}
|
|
33
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => FIREFOX).name).to eq(HEADER_NAME + "-Report-Only")}
|
|
34
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => FIREFOX_23).name).to eq(HEADER_NAME + "-Report-Only")}
|
|
35
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => CHROME).name).to eq(HEADER_NAME + "-Report-Only")}
|
|
36
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => CHROME_25).name).to eq(HEADER_NAME + "-Report-Only")}
|
|
38
37
|
end
|
|
39
38
|
|
|
40
39
|
context "when in report-only mode" do
|
|
41
|
-
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(IE)).name).to eq(
|
|
42
|
-
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX)).name).to eq(
|
|
43
|
-
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX_23)).name).to eq(
|
|
44
|
-
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME)).name).to eq(
|
|
45
|
-
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME_25)).name).to eq(
|
|
40
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(IE)).name).to eq(HEADER_NAME + "-Report-Only")}
|
|
41
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX)).name).to eq(HEADER_NAME + "-Report-Only")}
|
|
42
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX_23)).name).to eq(HEADER_NAME + "-Report-Only")}
|
|
43
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME)).name).to eq(HEADER_NAME + "-Report-Only")}
|
|
44
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME_25)).name).to eq(HEADER_NAME + "-Report-Only")}
|
|
46
45
|
end
|
|
47
46
|
|
|
48
47
|
context "when in enforce mode" do
|
|
49
48
|
let(:opts) { default_opts.merge(:enforce => true)}
|
|
50
49
|
|
|
51
|
-
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(IE)).name).to eq(
|
|
52
|
-
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX)).name).to eq(
|
|
53
|
-
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX_23)).name).to eq(
|
|
54
|
-
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(CHROME)).name).to eq(
|
|
55
|
-
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(CHROME_25)).name).to eq(
|
|
50
|
+
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(IE)).name).to eq(HEADER_NAME)}
|
|
51
|
+
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX)).name).to eq(HEADER_NAME)}
|
|
52
|
+
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX_23)).name).to eq(HEADER_NAME)}
|
|
53
|
+
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(CHROME)).name).to eq(HEADER_NAME)}
|
|
54
|
+
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(CHROME_25)).name).to eq(HEADER_NAME)}
|
|
56
55
|
end
|
|
56
|
+
end
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
specify { expect(ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(FIREFOX_23)}).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
|
63
|
-
specify { expect(ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(CHROME)}).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
|
64
|
-
specify { expect(ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(CHROME_25)}).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
|
58
|
+
context "when using hash sources" do
|
|
59
|
+
it "adds hashes and unsafe-inline to the script-src" do
|
|
60
|
+
policy = ContentSecurityPolicy.new(default_opts.merge(:script_hashes => ['sha256-abc123']))
|
|
61
|
+
expect(policy.value).to match /script-src[^;]*'sha256-abc123'/
|
|
65
62
|
end
|
|
66
63
|
end
|
|
67
64
|
|
|
68
65
|
describe "#normalize_csp_options" do
|
|
69
66
|
before(:each) do
|
|
70
|
-
default_opts.delete(:disable_chrome_extension)
|
|
71
67
|
default_opts.delete(:disable_fill_missing)
|
|
72
68
|
default_opts[:script_src] << ' self none'
|
|
73
69
|
@opts = default_opts
|
|
@@ -76,11 +72,11 @@ module SecureHeaders
|
|
|
76
72
|
context "Content-Security-Policy" do
|
|
77
73
|
it "converts the script values to their equivilents" do
|
|
78
74
|
csp = ContentSecurityPolicy.new(@opts, :request => request_for(CHROME))
|
|
79
|
-
expect(csp.value).to include("script-src 'unsafe-inline' 'unsafe-eval' https
|
|
75
|
+
expect(csp.value).to include("script-src 'unsafe-inline' 'unsafe-eval' https: data: 'self' 'none'")
|
|
80
76
|
end
|
|
81
77
|
|
|
82
78
|
it "adds a @enforce and @app_name variables to the report uri" do
|
|
83
|
-
opts = @opts.merge(:tag_report_uri => true, :enforce => true, :app_name => 'twitter')
|
|
79
|
+
opts = @opts.merge(:tag_report_uri => true, :enforce => true, :app_name => lambda { 'twitter' })
|
|
84
80
|
csp = ContentSecurityPolicy.new(opts, :request => request_for(CHROME))
|
|
85
81
|
expect(csp.value).to include("/csp_report?enforce=true&app_name=twitter")
|
|
86
82
|
end
|
|
@@ -98,7 +94,7 @@ module SecureHeaders
|
|
|
98
94
|
}
|
|
99
95
|
|
|
100
96
|
csp = ContentSecurityPolicy.new(opts)
|
|
101
|
-
expect(csp.
|
|
97
|
+
expect(csp.value).to match("report-uri http://lambda/result")
|
|
102
98
|
end
|
|
103
99
|
|
|
104
100
|
it "accepts procs for other fields" do
|
|
@@ -115,130 +111,34 @@ module SecureHeaders
|
|
|
115
111
|
end
|
|
116
112
|
end
|
|
117
113
|
|
|
118
|
-
describe "#same_origin?" do
|
|
119
|
-
let(:origin) {"https://example.com:123"}
|
|
120
|
-
|
|
121
|
-
it "matches when host, scheme, and port match" do
|
|
122
|
-
csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com'}, :request => request_for(FIREFOX, "https://example.com"))
|
|
123
|
-
expect(csp.send(:same_origin?)).to be true
|
|
124
|
-
|
|
125
|
-
csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com'}, :request => request_for(FIREFOX, "https://example.com:443"))
|
|
126
|
-
expect(csp.send(:same_origin?)).to be true
|
|
127
|
-
|
|
128
|
-
csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com:123'}, :request => request_for(FIREFOX, "https://example.com:123"))
|
|
129
|
-
expect(csp.send(:same_origin?)).to be true
|
|
130
|
-
|
|
131
|
-
csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com'}, :request => request_for(FIREFOX, "http://example.com"))
|
|
132
|
-
expect(csp.send(:same_origin?)).to be true
|
|
133
|
-
|
|
134
|
-
csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com:80'}, :request => request_for(FIREFOX, "http://example.com"))
|
|
135
|
-
expect(csp.send(:same_origin?)).to be true
|
|
136
|
-
|
|
137
|
-
csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com'}, :request => request_for(FIREFOX, "http://example.com:80"))
|
|
138
|
-
expect(csp.send(:same_origin?)).to be true
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
it "does not match port mismatches" do
|
|
142
|
-
csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com'}, :request => request_for(FIREFOX, "http://example.com:81"))
|
|
143
|
-
expect(csp.send(:same_origin?)).to be false
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
it "does not match host mismatches" do
|
|
147
|
-
csp = ContentSecurityPolicy.new({:report_uri => 'http://twitter.com'}, :request => request_for(FIREFOX, "http://example.com"))
|
|
148
|
-
expect(csp.send(:same_origin?)).to be false
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
it "does not match host mismatches because of subdomains" do
|
|
152
|
-
csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com'}, :request => request_for(FIREFOX, "http://sub.example.com"))
|
|
153
|
-
expect(csp.send(:same_origin?)).to be false
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
it "does not match scheme mismatches" do
|
|
157
|
-
csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com'}, :request => request_for(FIREFOX, "ftp://example.com"))
|
|
158
|
-
expect(csp.send(:same_origin?)).to be false
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
it "does not match on substring collisions" do
|
|
162
|
-
csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com'}, :request => request_for(FIREFOX, "https://anotherexample.com"))
|
|
163
|
-
expect(csp.send(:same_origin?)).to be false
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
describe "#normalize_reporting_endpoint" do
|
|
168
|
-
let(:opts) {{:report_uri => 'https://example.com/csp', :forward_endpoint => anything}}
|
|
169
|
-
|
|
170
|
-
context "when using firefox" do
|
|
171
|
-
it "updates the report-uri when posting to a different host" do
|
|
172
|
-
csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX, "https://anexample.com"))
|
|
173
|
-
expect(csp.report_uri).to eq(FF_CSP_ENDPOINT)
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
it "doesn't change report-uri if a path supplied" do
|
|
177
|
-
csp = ContentSecurityPolicy.new({:report_uri => "/csp_reports"}, :request => request_for(FIREFOX, "https://anexample.com"))
|
|
178
|
-
expect(csp.report_uri).to eq("/csp_reports")
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
it "forwards if the request_uri is set to a non-matching value" do
|
|
182
|
-
csp = ContentSecurityPolicy.new({:report_uri => "https://another.example.com", :forward_endpoint => '/somewhere'}, :ua => "Firefox", :request_uri => "https://anexample.com")
|
|
183
|
-
expect(csp.report_uri).to eq(FF_CSP_ENDPOINT)
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
it "does not update the URI is the report_uri is on the same origin" do
|
|
188
|
-
opts = {:report_uri => 'https://example.com/csp', :forward_endpoint => 'https://anotherexample.com'}
|
|
189
|
-
csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX, "https://example.com/somewhere"))
|
|
190
|
-
expect(csp.report_uri).to eq('https://example.com/csp')
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
it "does not update the report-uri when using a non-firefox browser" do
|
|
194
|
-
csp = ContentSecurityPolicy.new(opts, :request => request_for(CHROME))
|
|
195
|
-
expect(csp.report_uri).to eq('https://example.com/csp')
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
context "when using a protocol-relative value for report-uri" do
|
|
199
|
-
let(:opts) {
|
|
200
|
-
{
|
|
201
|
-
:default_src => 'self',
|
|
202
|
-
:report_uri => '//example.com/csp'
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
it "uses the current protocol" do
|
|
207
|
-
csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX, '/', :ssl => true))
|
|
208
|
-
expect(csp.value).to match(%r{report-uri https://example.com/csp;})
|
|
209
|
-
|
|
210
|
-
csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX))
|
|
211
|
-
expect(csp.value).to match(%r{report-uri http://example.com/csp;})
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
it "uses the pre-configured https protocol" do
|
|
215
|
-
csp = ContentSecurityPolicy.new(opts, :ua => "Firefox", :ssl => true)
|
|
216
|
-
expect(csp.value).to match(%r{report-uri https://example.com/csp;})
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
it "uses the pre-configured http protocol" do
|
|
220
|
-
csp = ContentSecurityPolicy.new(opts, :ua => "Firefox", :ssl => false)
|
|
221
|
-
expect(csp.value).to match(%r{report-uri http://example.com/csp;})
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
|
|
226
114
|
describe "#value" do
|
|
227
115
|
it "raises an exception when default-src is missing" do
|
|
228
116
|
csp = ContentSecurityPolicy.new({:script_src => 'anything'}, :request => request_for(CHROME))
|
|
229
117
|
expect {
|
|
230
118
|
csp.value
|
|
231
|
-
}.to raise_error(
|
|
119
|
+
}.to raise_error(RuntimeError)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
context "CSP level 2 directives" do
|
|
123
|
+
let(:config) { {:default_src => 'self'} }
|
|
124
|
+
::SecureHeaders::ContentSecurityPolicy::Constants::NON_DEFAULT_SOURCES.each do |non_default_source|
|
|
125
|
+
it "supports all level 2 directives" do
|
|
126
|
+
directive_name = ::SecureHeaders::ContentSecurityPolicy.send(:symbol_to_hyphen_case, non_default_source)
|
|
127
|
+
config.merge!({ non_default_source => "value" })
|
|
128
|
+
csp = ContentSecurityPolicy.new(config, :request => request_for(CHROME))
|
|
129
|
+
expect(csp.value).to match(/#{directive_name} value;/)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
232
132
|
end
|
|
233
133
|
|
|
234
134
|
context "auto-whitelists data: uris for img-src" do
|
|
235
135
|
it "sets the value if no img-src specified" do
|
|
236
|
-
csp = ContentSecurityPolicy.new({:default_src => 'self', :disable_fill_missing => true
|
|
136
|
+
csp = ContentSecurityPolicy.new({:default_src => 'self', :disable_fill_missing => true}, :request => request_for(CHROME))
|
|
237
137
|
expect(csp.value).to eq("default-src 'self'; img-src 'self' data:;")
|
|
238
138
|
end
|
|
239
139
|
|
|
240
140
|
it "appends the value if img-src is specified" do
|
|
241
|
-
csp = ContentSecurityPolicy.new({:default_src => 'self', :img_src => 'self', :disable_fill_missing => true
|
|
141
|
+
csp = ContentSecurityPolicy.new({:default_src => 'self', :img_src => 'self', :disable_fill_missing => true}, :request => request_for(CHROME))
|
|
242
142
|
expect(csp.value).to eq("default-src 'self'; img-src 'self' data:;")
|
|
243
143
|
end
|
|
244
144
|
end
|
|
@@ -246,7 +146,7 @@ module SecureHeaders
|
|
|
246
146
|
it "fills in directives without values with default-src value" do
|
|
247
147
|
options = default_opts.merge(:disable_fill_missing => false)
|
|
248
148
|
csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
|
|
249
|
-
value = "default-src https
|
|
149
|
+
value = "default-src https:; connect-src https:; font-src https:; frame-src https:; img-src https: data:; media-src https:; object-src https:; script-src 'unsafe-inline' 'unsafe-eval' https: data:; style-src 'unsafe-inline' https: about:; report-uri /csp_report;"
|
|
250
150
|
expect(csp.value).to eq(value)
|
|
251
151
|
end
|
|
252
152
|
|
|
@@ -258,19 +158,14 @@ module SecureHeaders
|
|
|
258
158
|
context "Firefox" do
|
|
259
159
|
it "builds a csp header for firefox" do
|
|
260
160
|
csp = ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX))
|
|
261
|
-
expect(csp.value).to eq("default-src https
|
|
161
|
+
expect(csp.value).to eq("default-src https:; img-src https: data:; script-src 'unsafe-inline' 'unsafe-eval' https: data:; style-src 'unsafe-inline' https: about:; report-uri /csp_report;")
|
|
262
162
|
end
|
|
263
163
|
end
|
|
264
164
|
|
|
265
165
|
context "Chrome" do
|
|
266
166
|
it "builds a csp header for chrome" do
|
|
267
167
|
csp = ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME))
|
|
268
|
-
expect(csp.value).to eq("default-src https
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
it "ignores :forward_endpoint settings" do
|
|
272
|
-
csp = ContentSecurityPolicy.new(@options_with_forwarding, :request => request_for(CHROME))
|
|
273
|
-
expect(csp.value).to match(/report-uri #{@options_with_forwarding[:report_uri]};/)
|
|
168
|
+
expect(csp.value).to eq("default-src https:; img-src https: data:; script-src 'unsafe-inline' 'unsafe-eval' https: data:; style-src 'unsafe-inline' https: about:; report-uri /csp_report;")
|
|
274
169
|
end
|
|
275
170
|
end
|
|
276
171
|
|
|
@@ -299,41 +194,19 @@ module SecureHeaders
|
|
|
299
194
|
end
|
|
300
195
|
end
|
|
301
196
|
|
|
302
|
-
context "when supplying a experimental values" do
|
|
303
|
-
let(:options) {{
|
|
304
|
-
:disable_chrome_extension => true,
|
|
305
|
-
:disable_fill_missing => true,
|
|
306
|
-
:default_src => 'self',
|
|
307
|
-
:script_src => 'https://*',
|
|
308
|
-
:experimental => {
|
|
309
|
-
:script_src => 'self'
|
|
310
|
-
}
|
|
311
|
-
}}
|
|
312
|
-
|
|
313
|
-
it "returns the original value" do
|
|
314
|
-
header = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
|
|
315
|
-
expect(header.value).to eq("default-src 'self'; img-src 'self' data:; script-src https://*;")
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
it "it returns the experimental value if requested" do
|
|
319
|
-
header = ContentSecurityPolicy.new(options, {:request => request_for(CHROME), :experimental => true})
|
|
320
|
-
expect(header.value).not_to match(/https/)
|
|
321
|
-
end
|
|
322
|
-
end
|
|
323
|
-
|
|
324
197
|
context "when supplying additional http directive values" do
|
|
325
198
|
let(:options) {
|
|
326
199
|
default_opts.merge({
|
|
327
200
|
:http_additions => {
|
|
328
|
-
:frame_src => "http
|
|
329
|
-
:img_src => "http
|
|
201
|
+
:frame_src => "http:",
|
|
202
|
+
:img_src => "http:"
|
|
330
203
|
}
|
|
331
204
|
})
|
|
332
205
|
}
|
|
333
206
|
|
|
334
207
|
it "adds directive values for headers on http" do
|
|
335
208
|
csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
|
|
336
|
-
expect(csp.value).to eq("default-src https
|
|
209
|
+
expect(csp.value).to eq("default-src https:; frame-src http:; img-src http: data:; script-src 'unsafe-inline' 'unsafe-eval' https: data:; style-src 'unsafe-inline' https: about:; report-uri /csp_report;")
|
|
337
210
|
end
|
|
338
211
|
|
|
339
212
|
it "does not add the directive values if requesting https" do
|
|
@@ -345,45 +218,47 @@ module SecureHeaders
|
|
|
345
218
|
csp = ContentSecurityPolicy.new(options, :ua => "Chrome", :ssl => true)
|
|
346
219
|
expect(csp.value).not_to match(/http:/)
|
|
347
220
|
end
|
|
221
|
+
end
|
|
348
222
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
let(:options) {{
|
|
355
|
-
:disable_chrome_extension => true,
|
|
356
|
-
:disable_fill_missing => true,
|
|
357
|
-
:default_src => 'self',
|
|
358
|
-
:script_src => 'https://*',
|
|
359
|
-
:http_additions => {
|
|
360
|
-
:script_src => 'http://*'
|
|
361
|
-
},
|
|
362
|
-
:experimental => {
|
|
363
|
-
:script_src => 'self',
|
|
364
|
-
:http_additions => {
|
|
365
|
-
:script_src => 'https://mycdn.example.com'
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}}
|
|
369
|
-
# for comparison purposes, if not using the experimental header this would produce
|
|
370
|
-
# "allow 'self'; script-src https://*" for https requests
|
|
371
|
-
# and
|
|
372
|
-
# "allow 'self'; script-src https://* http://*" for http requests
|
|
373
|
-
|
|
374
|
-
it "uses the value in the experimental block over SSL" do
|
|
375
|
-
csp = ContentSecurityPolicy.new(options, :experimental => true, :request => request_for(FIREFOX, '/', :ssl => true))
|
|
376
|
-
expect(csp.value).to eq("default-src 'self'; img-src 'self' data:; script-src 'self';")
|
|
223
|
+
describe "class methods" do
|
|
224
|
+
let(:ua) { CHROME }
|
|
225
|
+
let(:env) do
|
|
226
|
+
double.tap do |env|
|
|
227
|
+
allow(env).to receive(:[]).with('HTTP_USER_AGENT').and_return(ua)
|
|
377
228
|
end
|
|
229
|
+
end
|
|
230
|
+
let(:request) do
|
|
231
|
+
double(
|
|
232
|
+
:ssl? => true,
|
|
233
|
+
:url => 'https://example.com',
|
|
234
|
+
:env => env
|
|
235
|
+
)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
describe ".add_to_env" do
|
|
239
|
+
let(:controller) { double }
|
|
240
|
+
let(:config) { {:default_src => 'self'} }
|
|
241
|
+
let(:options) { {:controller => controller} }
|
|
378
242
|
|
|
379
|
-
it "
|
|
380
|
-
|
|
381
|
-
|
|
243
|
+
it "adds metadata to env" do
|
|
244
|
+
metadata = {
|
|
245
|
+
:config => config,
|
|
246
|
+
:options => options
|
|
247
|
+
}
|
|
248
|
+
expect(ContentSecurityPolicy).to receive(:options_from_request).and_return(options)
|
|
249
|
+
expect(env).to receive(:[]=).with(ContentSecurityPolicy::ENV_KEY, metadata)
|
|
250
|
+
ContentSecurityPolicy.add_to_env(request, controller, config)
|
|
382
251
|
end
|
|
252
|
+
end
|
|
383
253
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
254
|
+
describe ".options_from_request" do
|
|
255
|
+
it "extracts options from request" do
|
|
256
|
+
options = ContentSecurityPolicy.options_from_request(request)
|
|
257
|
+
expect(options).to eql({
|
|
258
|
+
:ua => ua,
|
|
259
|
+
:ssl => true,
|
|
260
|
+
:request_uri => 'https://example.com'
|
|
261
|
+
})
|
|
387
262
|
end
|
|
388
263
|
end
|
|
389
264
|
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module SecureHeaders
|
|
2
|
+
describe XPermittedCrossDomainPolicies do
|
|
3
|
+
specify { expect(XPermittedCrossDomainPolicies.new.name).to eq(XPermittedCrossDomainPolicies::Constants::XPCDP_HEADER_NAME)}
|
|
4
|
+
specify { expect(XPermittedCrossDomainPolicies.new.value).to eq("none")}
|
|
5
|
+
specify { expect(XPermittedCrossDomainPolicies.new('master-only').value).to eq('master-only')}
|
|
6
|
+
specify { expect(XPermittedCrossDomainPolicies.new(:value => 'master-only').value).to eq('master-only') }
|
|
7
|
+
|
|
8
|
+
context "valid configuration values" do
|
|
9
|
+
it "accepts 'all'" do
|
|
10
|
+
expect {
|
|
11
|
+
XPermittedCrossDomainPolicies.new("all")
|
|
12
|
+
}.not_to raise_error
|
|
13
|
+
|
|
14
|
+
expect {
|
|
15
|
+
XPermittedCrossDomainPolicies.new(:value => "all")
|
|
16
|
+
}.not_to raise_error
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "accepts 'by-ftp-filename'" do
|
|
20
|
+
expect {
|
|
21
|
+
XPermittedCrossDomainPolicies.new("by-ftp-filename")
|
|
22
|
+
}.not_to raise_error
|
|
23
|
+
|
|
24
|
+
expect {
|
|
25
|
+
XPermittedCrossDomainPolicies.new(:value => "by-ftp-filename")
|
|
26
|
+
}.not_to raise_error
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "accepts 'by-content-type'" do
|
|
30
|
+
expect {
|
|
31
|
+
XPermittedCrossDomainPolicies.new("by-content-type")
|
|
32
|
+
}.not_to raise_error
|
|
33
|
+
|
|
34
|
+
expect {
|
|
35
|
+
XPermittedCrossDomainPolicies.new(:value => "by-content-type")
|
|
36
|
+
}.not_to raise_error
|
|
37
|
+
end
|
|
38
|
+
it "accepts 'master-only'" do
|
|
39
|
+
expect {
|
|
40
|
+
XPermittedCrossDomainPolicies.new("master-only")
|
|
41
|
+
}.not_to raise_error
|
|
42
|
+
|
|
43
|
+
expect {
|
|
44
|
+
XPermittedCrossDomainPolicies.new(:value => "master-only")
|
|
45
|
+
}.not_to raise_error
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "accepts nil" do
|
|
49
|
+
expect {
|
|
50
|
+
XPermittedCrossDomainPolicies.new
|
|
51
|
+
}.not_to raise_error
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context 'invlaid configuration values' do
|
|
56
|
+
|
|
57
|
+
it "doesn't accept invalid values" do
|
|
58
|
+
expect {
|
|
59
|
+
XPermittedCrossDomainPolicies.new("open")
|
|
60
|
+
}.to raise_error
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -18,26 +18,7 @@ describe SecureHeaders do
|
|
|
18
18
|
allow(subject).to receive(:request).and_return(request)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
ALL_HEADERS = Hash[[:hsts, :csp, :x_frame_options, :x_content_type_options, :x_xss_protection].map{|header| [header, false]}]
|
|
22
|
-
USER_AGENTS = {
|
|
23
|
-
:firefox => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1',
|
|
24
|
-
:chrome => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5',
|
|
25
|
-
:ie => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)',
|
|
26
|
-
:opera => 'Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00',
|
|
27
|
-
:ios5 => "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
|
|
28
|
-
:ios6 => "Mozilla/5.0 (iPhone; CPU iPhone OS 614 like Mac OS X) AppleWebKit/536.26 (KHTML like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25",
|
|
29
|
-
:safari5 => "Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko ) Version/5.1 Mobile/9B176 Safari/7534.48.3",
|
|
30
|
-
:safari5_1 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10",
|
|
31
|
-
:safari6 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1"
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
def should_assign_header name, value
|
|
35
|
-
expect(response.headers).to receive(:[]=).with(name, value)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def should_not_assign_header name
|
|
39
|
-
expect(response.headers).not_to receive(:[]=).with(name, anything)
|
|
40
|
-
end
|
|
21
|
+
ALL_HEADERS = Hash[[:hsts, :csp, :x_frame_options, :x_content_type_options, :x_xss_protection, :x_permitted_cross_domain_policies].map{|header| [header, false]}]
|
|
41
22
|
|
|
42
23
|
def stub_user_agent val
|
|
43
24
|
allow(request).to receive_message_chain(:env, :[]).and_return(val)
|
|
@@ -55,6 +36,7 @@ describe SecureHeaders do
|
|
|
55
36
|
config.x_xss_protection = nil
|
|
56
37
|
config.csp = nil
|
|
57
38
|
config.x_download_options = nil
|
|
39
|
+
config.x_permitted_cross_domain_policies = nil
|
|
58
40
|
end
|
|
59
41
|
end
|
|
60
42
|
|
|
@@ -65,14 +47,7 @@ describe SecureHeaders do
|
|
|
65
47
|
subject.set_x_content_type_options_header
|
|
66
48
|
subject.set_x_xss_protection_header
|
|
67
49
|
subject.set_x_download_options_header
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
describe "#ensure_security_headers" do
|
|
71
|
-
it "sets a before filter" do
|
|
72
|
-
options = {}
|
|
73
|
-
expect(DummyClass).to receive(:before_filter).exactly(6).times
|
|
74
|
-
DummyClass.ensure_security_headers(options)
|
|
75
|
-
end
|
|
50
|
+
subject.set_x_permitted_cross_domain_policies_header
|
|
76
51
|
end
|
|
77
52
|
|
|
78
53
|
describe "#set_header" do
|
|
@@ -88,13 +63,10 @@ describe SecureHeaders do
|
|
|
88
63
|
end
|
|
89
64
|
|
|
90
65
|
describe "#set_security_headers" do
|
|
91
|
-
before(:each) do
|
|
92
|
-
allow(SecureHeaders::ContentSecurityPolicy).to receive(:new).and_return(double.as_null_object)
|
|
93
|
-
end
|
|
94
66
|
USER_AGENTS.each do |name, useragent|
|
|
95
67
|
it "sets all default headers for #{name} (smoke test)" do
|
|
96
68
|
stub_user_agent(useragent)
|
|
97
|
-
number_of_headers =
|
|
69
|
+
number_of_headers = 7
|
|
98
70
|
expect(subject).to receive(:set_header).exactly(number_of_headers).times # a request for a given header
|
|
99
71
|
subject.set_csp_header
|
|
100
72
|
subject.set_x_frame_options_header
|
|
@@ -102,6 +74,7 @@ describe SecureHeaders do
|
|
|
102
74
|
subject.set_x_xss_protection_header
|
|
103
75
|
subject.set_x_content_type_options_header
|
|
104
76
|
subject.set_x_download_options_header
|
|
77
|
+
subject.set_x_permitted_cross_domain_policies_header
|
|
105
78
|
end
|
|
106
79
|
end
|
|
107
80
|
|
|
@@ -126,6 +99,11 @@ describe SecureHeaders do
|
|
|
126
99
|
subject.set_x_frame_options_header(false)
|
|
127
100
|
end
|
|
128
101
|
|
|
102
|
+
it "does not set the X-Permitted-Cross-Domain-Policies header if disabled" do
|
|
103
|
+
should_not_assign_header(XPCDP_HEADER_NAME)
|
|
104
|
+
subject.set_x_permitted_cross_domain_policies_header(false)
|
|
105
|
+
end
|
|
106
|
+
|
|
129
107
|
it "does not set the HSTS header if disabled" do
|
|
130
108
|
should_not_assign_header(HSTS_HEADER_NAME)
|
|
131
109
|
subject.set_hsts_header(false)
|
|
@@ -139,8 +117,19 @@ describe SecureHeaders do
|
|
|
139
117
|
|
|
140
118
|
it "does not set the CSP header if disabled" do
|
|
141
119
|
stub_user_agent(USER_AGENTS[:chrome])
|
|
142
|
-
should_not_assign_header(
|
|
143
|
-
subject.set_csp_header(
|
|
120
|
+
should_not_assign_header(HEADER_NAME)
|
|
121
|
+
subject.set_csp_header(false)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it "saves the options to the env when using script hashes" do
|
|
125
|
+
opts = {
|
|
126
|
+
:default_src => 'self',
|
|
127
|
+
:script_hash_middleware => true
|
|
128
|
+
}
|
|
129
|
+
stub_user_agent(USER_AGENTS[:chrome])
|
|
130
|
+
|
|
131
|
+
expect(SecureHeaders::ContentSecurityPolicy).to receive(:add_to_env)
|
|
132
|
+
subject.set_csp_header(opts)
|
|
144
133
|
end
|
|
145
134
|
|
|
146
135
|
context "when disabled by configuration settings" do
|
|
@@ -152,6 +141,7 @@ describe SecureHeaders do
|
|
|
152
141
|
config.x_xss_protection = false
|
|
153
142
|
config.csp = false
|
|
154
143
|
config.x_download_options = false
|
|
144
|
+
config.x_permitted_cross_domain_policies = false
|
|
155
145
|
end
|
|
156
146
|
expect(subject).not_to receive(:set_header)
|
|
157
147
|
set_security_headers(subject)
|
|
@@ -247,7 +237,7 @@ describe SecureHeaders do
|
|
|
247
237
|
context "when using Firefox" do
|
|
248
238
|
it "sets CSP headers" do
|
|
249
239
|
stub_user_agent(USER_AGENTS[:firefox])
|
|
250
|
-
should_assign_header(
|
|
240
|
+
should_assign_header(HEADER_NAME + "-Report-Only", DEFAULT_CSP_HEADER)
|
|
251
241
|
subject.set_csp_header
|
|
252
242
|
end
|
|
253
243
|
end
|
|
@@ -255,7 +245,7 @@ describe SecureHeaders do
|
|
|
255
245
|
context "when using Chrome" do
|
|
256
246
|
it "sets default CSP header" do
|
|
257
247
|
stub_user_agent(USER_AGENTS[:chrome])
|
|
258
|
-
should_assign_header(
|
|
248
|
+
should_assign_header(HEADER_NAME + "-Report-Only", DEFAULT_CSP_HEADER)
|
|
259
249
|
subject.set_csp_header
|
|
260
250
|
end
|
|
261
251
|
end
|
|
@@ -263,36 +253,21 @@ describe SecureHeaders do
|
|
|
263
253
|
context "when using a browser besides chrome/firefox" do
|
|
264
254
|
it "sets the CSP header" do
|
|
265
255
|
stub_user_agent(USER_AGENTS[:opera])
|
|
266
|
-
should_assign_header(
|
|
256
|
+
should_assign_header(HEADER_NAME + "-Report-Only", DEFAULT_CSP_HEADER)
|
|
267
257
|
subject.set_csp_header
|
|
268
258
|
end
|
|
269
259
|
end
|
|
260
|
+
end
|
|
270
261
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
:default_src => 'self',
|
|
277
|
-
:script_src => 'https://mycdn.example.com',
|
|
278
|
-
:experimental => {
|
|
279
|
-
:script_src => 'self',
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
end
|
|
283
|
-
|
|
284
|
-
it "does not set the header in enforce mode if experimental is supplied, but enforce is disabled" do
|
|
285
|
-
opts = @opts.merge(:enforce => false)
|
|
286
|
-
should_assign_header(STANDARD_HEADER_NAME + "-Report-Only", anything)
|
|
287
|
-
should_not_assign_header(STANDARD_HEADER_NAME)
|
|
288
|
-
subject.set_csp_header(opts)
|
|
289
|
-
end
|
|
262
|
+
describe "#set_x_permitted_cross_domain_policies_header" do
|
|
263
|
+
it "sets the X-Permitted-Cross-Domain-Policies header" do
|
|
264
|
+
should_assign_header(XPCDP_HEADER_NAME, SecureHeaders::XPermittedCrossDomainPolicies::Constants::DEFAULT_VALUE)
|
|
265
|
+
subject.set_x_permitted_cross_domain_policies_header
|
|
266
|
+
end
|
|
290
267
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
subject.set_csp_header(@opts)
|
|
295
|
-
end
|
|
268
|
+
it "allows a custom X-Permitted-Cross-Domain-Policies header" do
|
|
269
|
+
should_assign_header(XPCDP_HEADER_NAME, "master-only")
|
|
270
|
+
subject.set_x_permitted_cross_domain_policies_header(:value => 'master-only')
|
|
296
271
|
end
|
|
297
272
|
end
|
|
298
273
|
end
|