secure_headers 1.1.1 → 1.2.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.
Potentially problematic release.
This version of secure_headers might be problematic. Click here for more details.
- checksums.yaml +15 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +2 -0
- data/Gemfile +0 -2
- data/Guardfile +1 -5
- data/HISTORY.md +16 -0
- data/README.md +18 -15
- data/fixtures/rails_3_2_12/spec/controllers/other_things_controller_spec.rb +7 -12
- data/fixtures/rails_3_2_12/spec/controllers/things_controller_spec.rb +7 -12
- data/fixtures/rails_3_2_12/spec/spec_helper.rb +0 -1
- data/fixtures/rails_3_2_12_no_init/spec/controllers/other_things_controller_spec.rb +7 -12
- data/fixtures/rails_3_2_12_no_init/spec/controllers/things_controller_spec.rb +7 -12
- data/fixtures/rails_3_2_12_no_init/spec/spec_helper.rb +0 -1
- data/lib/secure_headers/headers/content_security_policy.rb +15 -20
- data/lib/secure_headers/version.rb +1 -1
- data/spec/controllers/content_security_policy_controller_spec.rb +19 -19
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +77 -85
- data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +20 -20
- data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +12 -12
- data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +12 -12
- data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +18 -18
- data/spec/lib/secure_headers_spec.rb +11 -11
- data/spec/spec_helper.rb +0 -1
- metadata +7 -10
- data/.rvmrc +0 -1
@@ -32,58 +32,58 @@ describe ContentSecurityPolicyController do
|
|
32
32
|
let(:secondary_endpoint) { "https://internal.example.com" }
|
33
33
|
|
34
34
|
before(:each) do
|
35
|
-
SecureHeaders::Configuration.
|
36
|
-
subject.
|
37
|
-
subject.
|
38
|
-
subject.
|
39
|
-
Net::HTTP.
|
35
|
+
allow(SecureHeaders::Configuration).to receive(:csp).and_return({:report_uri => endpoint, :forward_endpoint => secondary_endpoint})
|
36
|
+
expect(subject).to receive :head
|
37
|
+
allow(subject).to receive(:params).and_return(params)
|
38
|
+
allow(subject).to receive(:request).and_return(FakeRequest.new)
|
39
|
+
allow_any_instance_of(Net::HTTP).to receive(:request)
|
40
40
|
end
|
41
41
|
|
42
42
|
context "delivery endpoint" do
|
43
43
|
it "posts over ssl" do
|
44
|
-
subject.
|
44
|
+
expect(subject).to receive(:use_ssl)
|
45
45
|
subject.scribe
|
46
46
|
end
|
47
47
|
|
48
48
|
it "posts over plain http" do
|
49
|
-
SecureHeaders::Configuration.
|
50
|
-
subject.
|
49
|
+
allow(SecureHeaders::Configuration).to receive(:csp).and_return(:report_uri => 'http://example.com')
|
50
|
+
expect(subject).not_to receive(:use_ssl)
|
51
51
|
subject.scribe
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
55
|
it "makes a POST request" do
|
56
|
-
Net::HTTP.
|
57
|
-
request.
|
58
|
-
params.
|
56
|
+
allow(Net::HTTP).to receive(:new).and_return(request)
|
57
|
+
expect(request).to receive(:request).with(instance_of(::Net::HTTP::Post))
|
58
|
+
allow(params).to receive(:to_json)
|
59
59
|
subject.scribe
|
60
60
|
end
|
61
61
|
|
62
62
|
it "POSTs to the configured forward_endpoint" do
|
63
|
-
Net::HTTP::Post.
|
63
|
+
expect(Net::HTTP::Post).to receive(:new).with(secondary_endpoint).and_return(request)
|
64
64
|
subject.scribe
|
65
65
|
end
|
66
66
|
|
67
67
|
it "does not POST if there is no forwarder configured" do
|
68
|
-
SecureHeaders::Configuration.
|
69
|
-
Net::HTTP::Post.
|
68
|
+
allow(SecureHeaders::Configuration).to receive(:csp).and_return({})
|
69
|
+
expect(Net::HTTP::Post).not_to receive(:new)
|
70
70
|
subject.scribe
|
71
71
|
end
|
72
72
|
|
73
73
|
it "eliminates known phony CSP reports" do
|
74
|
-
SecureHeaders::Configuration.
|
75
|
-
Net::HTTP::Post.
|
74
|
+
allow(SecureHeaders::Configuration).to receive(:csp).and_return(:report_uri => nil)
|
75
|
+
expect(Net::HTTP::Post).not_to receive :new
|
76
76
|
subject.scribe
|
77
77
|
end
|
78
78
|
|
79
79
|
it "logs errors when it cannot forward the CSP report" do
|
80
80
|
class Rails; def logger; end; end
|
81
81
|
logger = double(:repond_to? => true)
|
82
|
-
Rails.
|
82
|
+
allow(Rails).to receive(:logger).and_return(logger)
|
83
83
|
|
84
|
-
SecureHeaders::Configuration.
|
84
|
+
allow(SecureHeaders::Configuration).to receive(:csp).and_raise(StandardError)
|
85
85
|
|
86
|
-
logger.
|
86
|
+
expect(logger).to receive(:warn)
|
87
87
|
subject.scribe
|
88
88
|
end
|
89
89
|
end
|
@@ -30,38 +30,38 @@ module SecureHeaders
|
|
30
30
|
|
31
31
|
describe "#name" do
|
32
32
|
context "when supplying options to override request" do
|
33
|
-
specify { ContentSecurityPolicy.new(default_opts, :ua => IE).name.
|
34
|
-
specify { ContentSecurityPolicy.new(default_opts, :ua => FIREFOX).name.
|
35
|
-
specify { ContentSecurityPolicy.new(default_opts, :ua => FIREFOX_23).name.
|
36
|
-
specify { ContentSecurityPolicy.new(default_opts, :ua => CHROME).name.
|
37
|
-
specify { ContentSecurityPolicy.new(default_opts, :ua => CHROME_25).name.
|
33
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => IE).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
34
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => FIREFOX).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
35
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => FIREFOX_23).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
36
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => CHROME).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
37
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :ua => CHROME_25).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
38
38
|
end
|
39
39
|
|
40
40
|
context "when in report-only mode" do
|
41
|
-
specify { ContentSecurityPolicy.new(default_opts, :request => request_for(IE)).name.
|
42
|
-
specify { ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX)).name.
|
43
|
-
specify { ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX_23)).name.
|
44
|
-
specify { ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME)).name.
|
45
|
-
specify { ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME_25)).name.
|
41
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(IE)).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
42
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX)).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
43
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX_23)).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
44
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME)).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
45
|
+
specify { expect(ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME_25)).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
46
46
|
end
|
47
47
|
|
48
48
|
context "when in enforce mode" do
|
49
49
|
let(:opts) { default_opts.merge(:enforce => true)}
|
50
50
|
|
51
|
-
specify { ContentSecurityPolicy.new(opts, :request => request_for(IE)).name.
|
52
|
-
specify { ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX)).name.
|
53
|
-
specify { ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX_23)).name.
|
54
|
-
specify { ContentSecurityPolicy.new(opts, :request => request_for(CHROME)).name.
|
55
|
-
specify { ContentSecurityPolicy.new(opts, :request => request_for(CHROME_25)).name.
|
51
|
+
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(IE)).name).to eq(STANDARD_HEADER_NAME)}
|
52
|
+
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX)).name).to eq(STANDARD_HEADER_NAME)}
|
53
|
+
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX_23)).name).to eq(STANDARD_HEADER_NAME)}
|
54
|
+
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(CHROME)).name).to eq(STANDARD_HEADER_NAME)}
|
55
|
+
specify { expect(ContentSecurityPolicy.new(opts, :request => request_for(CHROME_25)).name).to eq(STANDARD_HEADER_NAME)}
|
56
56
|
end
|
57
57
|
|
58
58
|
context "when in experimental mode" do
|
59
59
|
let(:opts) { default_opts.merge(:enforce => true).merge(:experimental => {})}
|
60
|
-
specify { ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(IE)}).name.
|
61
|
-
specify { ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(FIREFOX)}).name.
|
62
|
-
specify { ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(FIREFOX_23)}).name.
|
63
|
-
specify { ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(CHROME)}).name.
|
64
|
-
specify { ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(CHROME_25)}).name.
|
60
|
+
specify { expect(ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(IE)}).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
61
|
+
specify { expect(ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(FIREFOX)}).name).to eq(STANDARD_HEADER_NAME + "-Report-Only")}
|
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")}
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
@@ -76,7 +76,26 @@ module SecureHeaders
|
|
76
76
|
context "Content-Security-Policy" do
|
77
77
|
it "converts the script values to their equivilents" do
|
78
78
|
csp = ContentSecurityPolicy.new(@opts, :request => request_for(CHROME))
|
79
|
-
csp.value.
|
79
|
+
expect(csp.value).to include("script-src 'unsafe-inline' 'unsafe-eval' https://* data: 'self' 'none'")
|
80
|
+
end
|
81
|
+
|
82
|
+
it "accepts procs for report-uris" do
|
83
|
+
opts = {
|
84
|
+
:default_src => 'self',
|
85
|
+
:report_uri => lambda { "http://lambda/result" }
|
86
|
+
}
|
87
|
+
|
88
|
+
csp = ContentSecurityPolicy.new(opts)
|
89
|
+
expect(csp.report_uri).to eq("http://lambda/result")
|
90
|
+
end
|
91
|
+
|
92
|
+
it "accepts procs for other fields" do
|
93
|
+
opts = {
|
94
|
+
:default_src => lambda { "http://lambda/result" }
|
95
|
+
}
|
96
|
+
|
97
|
+
csp = ContentSecurityPolicy.new(opts).value
|
98
|
+
expect(csp).to match("default-src http://lambda/result")
|
80
99
|
end
|
81
100
|
end
|
82
101
|
end
|
@@ -86,47 +105,47 @@ module SecureHeaders
|
|
86
105
|
|
87
106
|
it "matches when host, scheme, and port match" do
|
88
107
|
csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com'}, :request => request_for(FIREFOX, "https://example.com"))
|
89
|
-
csp.send(:same_origin?).
|
108
|
+
expect(csp.send(:same_origin?)).to be true
|
90
109
|
|
91
110
|
csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com'}, :request => request_for(FIREFOX, "https://example.com:443"))
|
92
|
-
csp.send(:same_origin?).
|
111
|
+
expect(csp.send(:same_origin?)).to be true
|
93
112
|
|
94
113
|
csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com:123'}, :request => request_for(FIREFOX, "https://example.com:123"))
|
95
|
-
csp.send(:same_origin?).
|
114
|
+
expect(csp.send(:same_origin?)).to be true
|
96
115
|
|
97
116
|
csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com'}, :request => request_for(FIREFOX, "http://example.com"))
|
98
|
-
csp.send(:same_origin?).
|
117
|
+
expect(csp.send(:same_origin?)).to be true
|
99
118
|
|
100
119
|
csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com:80'}, :request => request_for(FIREFOX, "http://example.com"))
|
101
|
-
csp.send(:same_origin?).
|
120
|
+
expect(csp.send(:same_origin?)).to be true
|
102
121
|
|
103
122
|
csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com'}, :request => request_for(FIREFOX, "http://example.com:80"))
|
104
|
-
csp.send(:same_origin?).
|
123
|
+
expect(csp.send(:same_origin?)).to be true
|
105
124
|
end
|
106
125
|
|
107
126
|
it "does not match port mismatches" do
|
108
127
|
csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com'}, :request => request_for(FIREFOX, "http://example.com:81"))
|
109
|
-
csp.send(:same_origin?).
|
128
|
+
expect(csp.send(:same_origin?)).to be false
|
110
129
|
end
|
111
130
|
|
112
131
|
it "does not match host mismatches" do
|
113
132
|
csp = ContentSecurityPolicy.new({:report_uri => 'http://twitter.com'}, :request => request_for(FIREFOX, "http://example.com"))
|
114
|
-
csp.send(:same_origin?).
|
133
|
+
expect(csp.send(:same_origin?)).to be false
|
115
134
|
end
|
116
135
|
|
117
136
|
it "does not match host mismatches because of subdomains" do
|
118
137
|
csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com'}, :request => request_for(FIREFOX, "http://sub.example.com"))
|
119
|
-
csp.send(:same_origin?).
|
138
|
+
expect(csp.send(:same_origin?)).to be false
|
120
139
|
end
|
121
140
|
|
122
141
|
it "does not match scheme mismatches" do
|
123
142
|
csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com'}, :request => request_for(FIREFOX, "ftp://example.com"))
|
124
|
-
csp.send(:same_origin?).
|
143
|
+
expect(csp.send(:same_origin?)).to be false
|
125
144
|
end
|
126
145
|
|
127
146
|
it "does not match on substring collisions" do
|
128
147
|
csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com'}, :request => request_for(FIREFOX, "https://anotherexample.com"))
|
129
|
-
csp.send(:same_origin?).
|
148
|
+
expect(csp.send(:same_origin?)).to be false
|
130
149
|
end
|
131
150
|
end
|
132
151
|
|
@@ -136,29 +155,29 @@ module SecureHeaders
|
|
136
155
|
context "when using firefox" do
|
137
156
|
it "updates the report-uri when posting to a different host" do
|
138
157
|
csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX, "https://anexample.com"))
|
139
|
-
csp.report_uri.
|
158
|
+
expect(csp.report_uri).to eq(FF_CSP_ENDPOINT)
|
140
159
|
end
|
141
160
|
|
142
161
|
it "doesn't change report-uri if a path supplied" do
|
143
162
|
csp = ContentSecurityPolicy.new({:report_uri => "/csp_reports"}, :request => request_for(FIREFOX, "https://anexample.com"))
|
144
|
-
csp.report_uri.
|
163
|
+
expect(csp.report_uri).to eq("/csp_reports")
|
145
164
|
end
|
146
165
|
|
147
166
|
it "forwards if the request_uri is set to a non-matching value" do
|
148
167
|
csp = ContentSecurityPolicy.new({:report_uri => "https://another.example.com", :forward_endpoint => '/somewhere'}, :ua => "Firefox", :request_uri => "https://anexample.com")
|
149
|
-
csp.report_uri.
|
168
|
+
expect(csp.report_uri).to eq(FF_CSP_ENDPOINT)
|
150
169
|
end
|
151
170
|
end
|
152
171
|
|
153
172
|
it "does not update the URI is the report_uri is on the same origin" do
|
154
173
|
opts = {:report_uri => 'https://example.com/csp', :forward_endpoint => 'https://anotherexample.com'}
|
155
174
|
csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX, "https://example.com/somewhere"))
|
156
|
-
csp.report_uri.
|
175
|
+
expect(csp.report_uri).to eq('https://example.com/csp')
|
157
176
|
end
|
158
177
|
|
159
178
|
it "does not update the report-uri when using a non-firefox browser" do
|
160
179
|
csp = ContentSecurityPolicy.new(opts, :request => request_for(CHROME))
|
161
|
-
csp.report_uri.
|
180
|
+
expect(csp.report_uri).to eq('https://example.com/csp')
|
162
181
|
end
|
163
182
|
|
164
183
|
context "when using a protocol-relative value for report-uri" do
|
@@ -171,20 +190,20 @@ module SecureHeaders
|
|
171
190
|
|
172
191
|
it "uses the current protocol" do
|
173
192
|
csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX, '/', :ssl => true))
|
174
|
-
csp.value.
|
193
|
+
expect(csp.value).to match(%r{report-uri https://example.com/csp;})
|
175
194
|
|
176
195
|
csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX))
|
177
|
-
csp.value.
|
196
|
+
expect(csp.value).to match(%r{report-uri http://example.com/csp;})
|
178
197
|
end
|
179
198
|
|
180
199
|
it "uses the pre-configured https protocol" do
|
181
200
|
csp = ContentSecurityPolicy.new(opts, :ua => "Firefox", :ssl => true)
|
182
|
-
csp.value.
|
201
|
+
expect(csp.value).to match(%r{report-uri https://example.com/csp;})
|
183
202
|
end
|
184
203
|
|
185
204
|
it "uses the pre-configured http protocol" do
|
186
205
|
csp = ContentSecurityPolicy.new(opts, :ua => "Firefox", :ssl => false)
|
187
|
-
csp.value.
|
206
|
+
expect(csp.value).to match(%r{report-uri http://example.com/csp;})
|
188
207
|
end
|
189
208
|
end
|
190
209
|
end
|
@@ -192,20 +211,20 @@ module SecureHeaders
|
|
192
211
|
describe "#value" do
|
193
212
|
it "raises an exception when default-src is missing" do
|
194
213
|
csp = ContentSecurityPolicy.new({:script_src => 'anything'}, :request => request_for(CHROME))
|
195
|
-
|
214
|
+
expect {
|
196
215
|
csp.value
|
197
|
-
}.
|
216
|
+
}.to raise_error(ContentSecurityPolicyBuildError, "Couldn't build CSP header :( Expected to find default_src directive value")
|
198
217
|
end
|
199
218
|
|
200
219
|
context "auto-whitelists data: uris for img-src" do
|
201
220
|
it "sets the value if no img-src specified" do
|
202
221
|
csp = ContentSecurityPolicy.new({:default_src => 'self', :disable_fill_missing => true, :disable_chrome_extension => true}, :request => request_for(CHROME))
|
203
|
-
csp.value.
|
222
|
+
expect(csp.value).to eq("default-src 'self'; img-src data:;")
|
204
223
|
end
|
205
224
|
|
206
225
|
it "appends the value if img-src is specified" do
|
207
226
|
csp = ContentSecurityPolicy.new({:default_src => 'self', :img_src => 'self', :disable_fill_missing => true, :disable_chrome_extension => true}, :request => request_for(CHROME))
|
208
|
-
csp.value.
|
227
|
+
expect(csp.value).to eq("default-src 'self'; img-src 'self' data:;")
|
209
228
|
end
|
210
229
|
end
|
211
230
|
|
@@ -213,30 +232,30 @@ module SecureHeaders
|
|
213
232
|
options = default_opts.merge(:disable_fill_missing => false)
|
214
233
|
csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
|
215
234
|
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;"
|
216
|
-
csp.value.
|
235
|
+
expect(csp.value).to eq(value)
|
217
236
|
end
|
218
237
|
|
219
238
|
it "sends the standard csp header if an unknown browser is supplied" do
|
220
239
|
csp = ContentSecurityPolicy.new(default_opts, :request => request_for(IE))
|
221
|
-
csp.value.
|
240
|
+
expect(csp.value).to match "default-src"
|
222
241
|
end
|
223
242
|
|
224
243
|
context "Firefox" do
|
225
244
|
it "builds a csp header for firefox" do
|
226
245
|
csp = ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX))
|
227
|
-
csp.value.
|
246
|
+
expect(csp.value).to eq("default-src https://*; img-src data:; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* about:; report-uri /csp_report;")
|
228
247
|
end
|
229
248
|
end
|
230
249
|
|
231
250
|
context "Chrome" do
|
232
251
|
it "builds a csp header for chrome" do
|
233
252
|
csp = ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME))
|
234
|
-
csp.value.
|
253
|
+
expect(csp.value).to eq("default-src https://*; img-src data:; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* about:; report-uri /csp_report;")
|
235
254
|
end
|
236
255
|
|
237
256
|
it "ignores :forward_endpoint settings" do
|
238
257
|
csp = ContentSecurityPolicy.new(@options_with_forwarding, :request => request_for(CHROME))
|
239
|
-
csp.value.
|
258
|
+
expect(csp.value).to match(/report-uri #{@options_with_forwarding[:report_uri]};/)
|
240
259
|
end
|
241
260
|
end
|
242
261
|
|
@@ -253,12 +272,12 @@ module SecureHeaders
|
|
253
272
|
|
254
273
|
it "returns the original value" do
|
255
274
|
header = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
|
256
|
-
header.value.
|
275
|
+
expect(header.value).to eq("default-src 'self'; img-src data:; script-src https://*;")
|
257
276
|
end
|
258
277
|
|
259
278
|
it "it returns the experimental value if requested" do
|
260
279
|
header = ContentSecurityPolicy.new(options, {:request => request_for(CHROME), :experimental => true})
|
261
|
-
header.value.
|
280
|
+
expect(header.value).not_to match(/https/)
|
262
281
|
end
|
263
282
|
end
|
264
283
|
|
@@ -274,17 +293,17 @@ module SecureHeaders
|
|
274
293
|
|
275
294
|
it "adds directive values for headers on http" do
|
276
295
|
csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
|
277
|
-
csp.value.
|
296
|
+
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;")
|
278
297
|
end
|
279
298
|
|
280
299
|
it "does not add the directive values if requesting https" do
|
281
300
|
csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME, '/', :ssl => true))
|
282
|
-
csp.value.
|
301
|
+
expect(csp.value).not_to match(/http:/)
|
283
302
|
end
|
284
303
|
|
285
304
|
it "does not add the directive values if requesting https" do
|
286
305
|
csp = ContentSecurityPolicy.new(options, :ua => "Chrome", :ssl => true)
|
287
|
-
csp.value.
|
306
|
+
expect(csp.value).not_to match(/http:/)
|
288
307
|
end
|
289
308
|
|
290
309
|
context "when supplying an experimental block" do
|
@@ -314,47 +333,20 @@ module SecureHeaders
|
|
314
333
|
|
315
334
|
it "uses the value in the experimental block over SSL" do
|
316
335
|
csp = ContentSecurityPolicy.new(options, :experimental => true, :request => request_for(FIREFOX, '/', :ssl => true))
|
317
|
-
csp.value.
|
336
|
+
expect(csp.value).to eq("default-src 'self'; img-src data:; script-src 'self';")
|
318
337
|
end
|
319
338
|
|
320
339
|
it "detects the :ssl => true option" do
|
321
340
|
csp = ContentSecurityPolicy.new(options, :experimental => true, :ua => FIREFOX, :ssl => true)
|
322
|
-
csp.value.
|
341
|
+
expect(csp.value).to eq("default-src 'self'; img-src data:; script-src 'self';")
|
323
342
|
end
|
324
343
|
|
325
344
|
it "merges the values from experimental/http_additions when not over SSL" do
|
326
345
|
csp = ContentSecurityPolicy.new(options, :experimental => true, :request => request_for(FIREFOX))
|
327
|
-
csp.value.
|
346
|
+
expect(csp.value).to eq("default-src 'self'; img-src data:; script-src 'self' https://mycdn.example.com;")
|
328
347
|
end
|
329
348
|
end
|
330
349
|
end
|
331
|
-
|
332
|
-
context "when supplying a script nonce callback" do
|
333
|
-
let(:options) {
|
334
|
-
default_opts.merge({
|
335
|
-
:script_nonce => "random",
|
336
|
-
})
|
337
|
-
}
|
338
|
-
|
339
|
-
it "uses the value in the X-Webkit-CSP" do
|
340
|
-
csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
|
341
|
-
csp.value.should match "script-nonce random;"
|
342
|
-
end
|
343
|
-
|
344
|
-
it "runs a dynamic nonce generator" do
|
345
|
-
options[:script_nonce] = lambda { 'something' }
|
346
|
-
csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
|
347
|
-
csp.value.should match "script-nonce something;"
|
348
|
-
end
|
349
|
-
|
350
|
-
it "runs against the given controller context" do
|
351
|
-
fake_params = {}
|
352
|
-
options[:script_nonce] = lambda { params[:script_nonce] = 'something' }
|
353
|
-
csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME), :controller => double(:params => fake_params))
|
354
|
-
csp.value.should match "script-nonce something;"
|
355
|
-
fake_params.should == {:script_nonce => 'something'}
|
356
|
-
end
|
357
|
-
end
|
358
350
|
end
|
359
351
|
end
|
360
352
|
end
|
@@ -2,59 +2,59 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module SecureHeaders
|
4
4
|
describe StrictTransportSecurity do
|
5
|
-
specify{ StrictTransportSecurity.new.name.
|
5
|
+
specify{ expect(StrictTransportSecurity.new.name).to eq("Strict-Transport-Security") }
|
6
6
|
|
7
7
|
describe "#value" do
|
8
|
-
specify { StrictTransportSecurity.new.value.
|
9
|
-
specify { StrictTransportSecurity.new("max-age=1234").value.
|
10
|
-
specify { StrictTransportSecurity.new(:max_age => '1234').value.
|
11
|
-
specify { StrictTransportSecurity.new(:max_age => 1234).value.
|
12
|
-
specify { StrictTransportSecurity.new(:max_age => HSTS_MAX_AGE, :include_subdomains => true).value.
|
8
|
+
specify { expect(StrictTransportSecurity.new.value).to eq(StrictTransportSecurity::Constants::DEFAULT_VALUE)}
|
9
|
+
specify { expect(StrictTransportSecurity.new("max-age=1234").value).to eq("max-age=1234")}
|
10
|
+
specify { expect(StrictTransportSecurity.new(:max_age => '1234').value).to eq("max-age=1234")}
|
11
|
+
specify { expect(StrictTransportSecurity.new(:max_age => 1234).value).to eq("max-age=1234")}
|
12
|
+
specify { expect(StrictTransportSecurity.new(:max_age => HSTS_MAX_AGE, :include_subdomains => true).value).to eq("max-age=#{HSTS_MAX_AGE}; includeSubdomains")}
|
13
13
|
|
14
14
|
context "with an invalid configuration" do
|
15
15
|
context "with a hash argument" do
|
16
16
|
it "should allow string values for max-age" do
|
17
|
-
|
17
|
+
expect {
|
18
18
|
StrictTransportSecurity.new(:max_age => '1234')
|
19
|
-
}.
|
19
|
+
}.not_to raise_error
|
20
20
|
end
|
21
21
|
|
22
22
|
it "should allow integer values for max-age" do
|
23
|
-
|
23
|
+
expect {
|
24
24
|
StrictTransportSecurity.new(:max_age => 1234)
|
25
|
-
}.
|
25
|
+
}.not_to raise_error
|
26
26
|
end
|
27
27
|
|
28
28
|
it "raises an exception with an invalid max-age" do
|
29
|
-
|
29
|
+
expect {
|
30
30
|
StrictTransportSecurity.new(:max_age => 'abc123')
|
31
|
-
}.
|
31
|
+
}.to raise_error(STSBuildError)
|
32
32
|
end
|
33
33
|
|
34
34
|
it "raises an exception if max-age is not supplied" do
|
35
|
-
|
35
|
+
expect {
|
36
36
|
StrictTransportSecurity.new(:includeSubdomains => true)
|
37
|
-
}.
|
37
|
+
}.to raise_error(STSBuildError)
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
41
|
context "with a string argument" do
|
42
42
|
it "raises an exception with an invalid max-age" do
|
43
|
-
|
43
|
+
expect {
|
44
44
|
StrictTransportSecurity.new('max-age=abc123')
|
45
|
-
}.
|
45
|
+
}.to raise_error(STSBuildError)
|
46
46
|
end
|
47
47
|
|
48
48
|
it "raises an exception if max-age is not supplied" do
|
49
|
-
|
49
|
+
expect {
|
50
50
|
StrictTransportSecurity.new('includeSubdomains')
|
51
|
-
}.
|
51
|
+
}.to raise_error(STSBuildError)
|
52
52
|
end
|
53
53
|
|
54
54
|
it "raises an exception with an invalid format" do
|
55
|
-
|
55
|
+
expect {
|
56
56
|
StrictTransportSecurity.new('max-age=123includeSubdomains')
|
57
|
-
}.
|
57
|
+
}.to raise_error(STSBuildError)
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|