secure_headers 3.7.0 → 3.7.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of secure_headers might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/secure_headers/headers/content_security_policy.rb +40 -5
- data/lib/secure_headers/headers/policy_management.rb +90 -47
- data/secure_headers.gemspec +1 -1
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +26 -9
- data/spec/lib/secure_headers/headers/policy_management_spec.rb +30 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95d8b4815c1f0553ff7a7d6ca32cc046aaf08009
|
4
|
+
data.tar.gz: 0f2f8a647f84de17c79d6369ace1ffbcaefdf089
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cbaedf0a0bf671e9a0e7454fa31e2a1bb3952481a21e37ee3cfeb0a6cc8833267af8781471413c10415736a792f50f7779accc5a18392b9139c4c311e47e6c32
|
7
|
+
data.tar.gz: dcd71e54dd3a2ea821e25e520f57be97aac4e00f8efd992bfc4cba84c5cf75e6777de38b710ef348ce62581e851deb498e2add4da06d6336ff4a4ca3b4b437be
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
## 3.7.1
|
2
|
+
|
3
|
+
Fix support for the sandbox attribute of CSP. `true` and `[]` represent the maximally restricted policy (`sandbox;`) and validate other values.
|
4
|
+
|
1
5
|
## 3.7.0
|
2
6
|
|
3
7
|
Adds support for the `Expect-CT` header (@jacobbednarz: https://github.com/twitter/secureheaders/pull/322)
|
@@ -80,20 +80,55 @@ module SecureHeaders
|
|
80
80
|
case DIRECTIVE_VALUE_TYPES[directive_name]
|
81
81
|
when :boolean
|
82
82
|
symbol_to_hyphen_case(directive_name) if @config.directive_value(directive_name)
|
83
|
-
when :
|
84
|
-
|
85
|
-
|
86
|
-
|
83
|
+
when :sandbox_list
|
84
|
+
build_sandbox_list_directive(directive_name)
|
85
|
+
when :media_type_list
|
86
|
+
build_media_type_list_directive(directive_name)
|
87
|
+
when :source_list
|
88
|
+
build_source_list_directive(directive_name)
|
87
89
|
end
|
88
90
|
end.compact.join("; ")
|
89
91
|
end
|
90
92
|
|
93
|
+
def build_sandbox_list_directive(directive)
|
94
|
+
return unless sandbox_list = @config.directive_value(directive)
|
95
|
+
max_strict_policy = case sandbox_list
|
96
|
+
when Array
|
97
|
+
sandbox_list.empty?
|
98
|
+
when true
|
99
|
+
true
|
100
|
+
else
|
101
|
+
false
|
102
|
+
end
|
103
|
+
|
104
|
+
# A maximally strict sandbox policy is just the `sandbox` directive,
|
105
|
+
# whith no configuraiton values.
|
106
|
+
if max_strict_policy
|
107
|
+
symbol_to_hyphen_case(directive)
|
108
|
+
elsif sandbox_list && sandbox_list.any?
|
109
|
+
[
|
110
|
+
symbol_to_hyphen_case(directive),
|
111
|
+
sandbox_list.uniq
|
112
|
+
].join(" ")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def build_media_type_list_directive(directive)
|
117
|
+
return unless media_type_list = @config.directive_value(directive)
|
118
|
+
if media_type_list && media_type_list.any?
|
119
|
+
[
|
120
|
+
symbol_to_hyphen_case(directive),
|
121
|
+
media_type_list.uniq
|
122
|
+
].join(" ")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
91
126
|
# Private: builds a string that represents one directive in a minified form.
|
92
127
|
#
|
93
128
|
# directive_name - a symbol representing the various ALL_DIRECTIVES
|
94
129
|
#
|
95
130
|
# Returns a string representing a directive.
|
96
|
-
def
|
131
|
+
def build_source_list_directive(directive)
|
97
132
|
source_list = case directive
|
98
133
|
when :child_src
|
99
134
|
if supported_directives.include?(:child_src)
|
@@ -109,18 +109,6 @@ module SecureHeaders
|
|
109
109
|
# everything else is in between.
|
110
110
|
BODY_DIRECTIVES = ALL_DIRECTIVES - [DEFAULT_SRC, REPORT_URI]
|
111
111
|
|
112
|
-
# These are directives that do not inherit the default-src value. This is
|
113
|
-
# useful when calling #combine_policies.
|
114
|
-
NON_FETCH_SOURCES = [
|
115
|
-
BASE_URI,
|
116
|
-
FORM_ACTION,
|
117
|
-
FRAME_ANCESTORS,
|
118
|
-
PLUGIN_TYPES,
|
119
|
-
REPORT_URI
|
120
|
-
]
|
121
|
-
|
122
|
-
FETCH_SOURCES = ALL_DIRECTIVES - NON_FETCH_SOURCES
|
123
|
-
|
124
112
|
VARIATIONS = {
|
125
113
|
"Chrome" => CHROME_DIRECTIVES,
|
126
114
|
"Opera" => CHROME_DIRECTIVES,
|
@@ -148,14 +136,30 @@ module SecureHeaders
|
|
148
136
|
MANIFEST_SRC => :source_list,
|
149
137
|
MEDIA_SRC => :source_list,
|
150
138
|
OBJECT_SRC => :source_list,
|
151
|
-
PLUGIN_TYPES => :
|
139
|
+
PLUGIN_TYPES => :media_type_list,
|
152
140
|
REPORT_URI => :source_list,
|
153
|
-
SANDBOX => :
|
141
|
+
SANDBOX => :sandbox_list,
|
154
142
|
SCRIPT_SRC => :source_list,
|
155
143
|
STYLE_SRC => :source_list,
|
156
144
|
UPGRADE_INSECURE_REQUESTS => :boolean
|
157
145
|
}.freeze
|
158
146
|
|
147
|
+
# These are directives that don't have use a source list, and hence do not
|
148
|
+
# inherit the default-src value.
|
149
|
+
NON_SOURCE_LIST_SOURCES = DIRECTIVE_VALUE_TYPES.select do |_, type|
|
150
|
+
type != :source_list
|
151
|
+
end.keys.freeze
|
152
|
+
|
153
|
+
# These are directives that take a source list, but that do not inherit
|
154
|
+
# the default-src value.
|
155
|
+
NON_FETCH_SOURCES = [
|
156
|
+
BASE_URI,
|
157
|
+
FORM_ACTION,
|
158
|
+
FRAME_ANCESTORS,
|
159
|
+
REPORT_URI
|
160
|
+
]
|
161
|
+
|
162
|
+
FETCH_SOURCES = ALL_DIRECTIVES - NON_FETCH_SOURCES - NON_SOURCE_LIST_SOURCES
|
159
163
|
|
160
164
|
STAR_REGEXP = Regexp.new(Regexp.escape(STAR))
|
161
165
|
HTTP_SCHEME_REGEX = %r{\Ahttps?://}
|
@@ -253,7 +257,7 @@ module SecureHeaders
|
|
253
257
|
# when each hash contains a value for a given key.
|
254
258
|
def merge_policy_additions(original, additions)
|
255
259
|
original.merge(additions) do |directive, lhs, rhs|
|
256
|
-
if
|
260
|
+
if list_directive?(directive)
|
257
261
|
(lhs.to_a + rhs.to_a).compact.uniq
|
258
262
|
else
|
259
263
|
rhs
|
@@ -261,20 +265,27 @@ module SecureHeaders
|
|
261
265
|
end.reject { |_, value| value.nil? || value == [] } # this mess prevents us from adding empty directives.
|
262
266
|
end
|
263
267
|
|
268
|
+
# Returns True if a directive expects a list of values and False otherwise.
|
269
|
+
def list_directive?(directive)
|
270
|
+
source_list?(directive) ||
|
271
|
+
sandbox_list?(directive) ||
|
272
|
+
media_type_list?(directive)
|
273
|
+
end
|
274
|
+
|
264
275
|
# For each directive in additions that does not exist in the original config,
|
265
276
|
# copy the default-src value to the original config. This modifies the original hash.
|
266
277
|
def populate_fetch_source_with_default!(original, additions)
|
267
278
|
# in case we would be appending to an empty directive, fill it with the default-src value
|
268
279
|
additions.each_key do |directive|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
280
|
+
directive = if directive.to_s.end_with?("_nonce")
|
281
|
+
directive.to_s.gsub(/_nonce/, "_src").to_sym
|
282
|
+
else
|
283
|
+
directive
|
284
|
+
end
|
285
|
+
# Don't set a default if directive has an existing value
|
286
|
+
next if original[directive]
|
287
|
+
if FETCH_SOURCES.include?(directive)
|
288
|
+
original[directive] = default_for(directive, original)
|
278
289
|
end
|
279
290
|
end
|
280
291
|
end
|
@@ -285,45 +296,77 @@ module SecureHeaders
|
|
285
296
|
original[DEFAULT_SRC]
|
286
297
|
end
|
287
298
|
|
288
|
-
def nonce_added?(original, additions)
|
289
|
-
[:script_nonce, :style_nonce].each do |nonce|
|
290
|
-
if additions[nonce] && !original[nonce]
|
291
|
-
return true
|
292
|
-
end
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
299
|
def source_list?(directive)
|
297
300
|
DIRECTIVE_VALUE_TYPES[directive] == :source_list
|
298
301
|
end
|
299
302
|
|
303
|
+
def sandbox_list?(directive)
|
304
|
+
DIRECTIVE_VALUE_TYPES[directive] == :sandbox_list
|
305
|
+
end
|
306
|
+
|
307
|
+
def media_type_list?(directive)
|
308
|
+
DIRECTIVE_VALUE_TYPES[directive] == :media_type_list
|
309
|
+
end
|
310
|
+
|
300
311
|
# Private: Validates that the configuration has a valid type, or that it is a valid
|
301
312
|
# source expression.
|
302
|
-
def validate_directive!(directive,
|
313
|
+
def validate_directive!(directive, value)
|
314
|
+
ensure_valid_directive!(directive)
|
303
315
|
case ContentSecurityPolicy::DIRECTIVE_VALUE_TYPES[directive]
|
304
316
|
when :boolean
|
305
|
-
unless boolean?(
|
306
|
-
raise ContentSecurityPolicyConfigError.new("#{directive} must be a boolean value")
|
307
|
-
end
|
308
|
-
when :string
|
309
|
-
unless source_expression.is_a?(String)
|
310
|
-
raise ContentSecurityPolicyConfigError.new("#{directive} Must be a string. Found #{config.class}: #{config} value")
|
317
|
+
unless boolean?(value)
|
318
|
+
raise ContentSecurityPolicyConfigError.new("#{directive} must be a boolean. Found #{value.class} value")
|
311
319
|
end
|
320
|
+
when :sandbox_list
|
321
|
+
validate_sandbox_expression!(directive, value)
|
322
|
+
when :media_type_list
|
323
|
+
validate_media_type_expression!(directive, value)
|
324
|
+
when :source_list
|
325
|
+
validate_source_expression!(directive, value)
|
312
326
|
else
|
313
|
-
|
327
|
+
raise ContentSecurityPolicyConfigError.new("Unknown directive #{directive}")
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
# Private: validates that a sandbox token expression:
|
332
|
+
# 1. is an array of strings or optionally `true` (to enable maximal sandboxing)
|
333
|
+
# 2. For arrays, each element is of the form allow-*
|
334
|
+
def validate_sandbox_expression!(directive, sandbox_token_expression)
|
335
|
+
# We support sandbox: true to indicate a maximally secure sandbox.
|
336
|
+
return if boolean?(sandbox_token_expression) && sandbox_token_expression == true
|
337
|
+
ensure_array_of_strings!(directive, sandbox_token_expression)
|
338
|
+
valid = sandbox_token_expression.compact.all? do |v|
|
339
|
+
v.is_a?(String) && v.start_with?("allow-")
|
340
|
+
end
|
341
|
+
if !valid
|
342
|
+
raise ContentSecurityPolicyConfigError.new("#{directive} must be True or an array of zero or more sandbox token strings (ex. allow-forms)")
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
# Private: validates that a media type expression:
|
347
|
+
# 1. is an array of strings
|
348
|
+
# 2. each element is of the form type/subtype
|
349
|
+
def validate_media_type_expression!(directive, media_type_expression)
|
350
|
+
ensure_array_of_strings!(directive, media_type_expression)
|
351
|
+
valid = media_type_expression.compact.all? do |v|
|
352
|
+
# All media types are of the form: <type from RFC 2045> "/" <subtype from RFC 2045>.
|
353
|
+
v =~ /\A.+\/.+\z/
|
354
|
+
end
|
355
|
+
if !valid
|
356
|
+
raise ContentSecurityPolicyConfigError.new("#{directive} must be an array of valid media types (ex. application/pdf)")
|
314
357
|
end
|
315
358
|
end
|
316
359
|
|
317
360
|
# Private: validates that a source expression:
|
318
|
-
# 1.
|
319
|
-
# 2.
|
320
|
-
# 3. does not contain any depreated, now invalid values (inline, eval, self, none)
|
361
|
+
# 1. is an array of strings
|
362
|
+
# 2. does not contain any deprecated, now invalid values (inline, eval, self, none)
|
321
363
|
#
|
322
364
|
# Does not validate the invididual values of the source expression (e.g.
|
323
365
|
# script_src => h*t*t*p: will not raise an exception)
|
324
366
|
def validate_source_expression!(directive, source_expression)
|
325
|
-
|
326
|
-
|
367
|
+
if source_expression != OPT_OUT
|
368
|
+
ensure_array_of_strings!(directive, source_expression)
|
369
|
+
end
|
327
370
|
ensure_valid_sources!(directive, source_expression)
|
328
371
|
end
|
329
372
|
|
@@ -333,8 +376,8 @@ module SecureHeaders
|
|
333
376
|
end
|
334
377
|
end
|
335
378
|
|
336
|
-
def ensure_array_of_strings!(directive,
|
337
|
-
|
379
|
+
def ensure_array_of_strings!(directive, value)
|
380
|
+
if (!value.is_a?(Array) || !value.compact.all? { |v| v.is_a?(String) })
|
338
381
|
raise ContentSecurityPolicyConfigError.new("#{directive} must be an array of strings")
|
339
382
|
end
|
340
383
|
end
|
data/secure_headers.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
Gem::Specification.new do |gem|
|
3
3
|
gem.name = "secure_headers"
|
4
|
-
gem.version = "3.7.
|
4
|
+
gem.version = "3.7.1"
|
5
5
|
gem.authors = ["Neil Matatall"]
|
6
6
|
gem.email = ["neil.matatall@gmail.com"]
|
7
7
|
gem.description = 'Manages application of security headers with many safe defaults.'
|
@@ -86,6 +86,21 @@ module SecureHeaders
|
|
86
86
|
expect(csp.value).to eq("default-src example.org")
|
87
87
|
end
|
88
88
|
|
89
|
+
it "creates maximally strict sandbox policy when passed no sandbox token values" do
|
90
|
+
csp = ContentSecurityPolicy.new(default_src: %w(example.org), sandbox: [])
|
91
|
+
expect(csp.value).to eq("default-src example.org; sandbox")
|
92
|
+
end
|
93
|
+
|
94
|
+
it "creates maximally strict sandbox policy when passed true" do
|
95
|
+
csp = ContentSecurityPolicy.new(default_src: %w(example.org), sandbox: true)
|
96
|
+
expect(csp.value).to eq("default-src example.org; sandbox")
|
97
|
+
end
|
98
|
+
|
99
|
+
it "creates sandbox policy when passed valid sandbox token values" do
|
100
|
+
csp = ContentSecurityPolicy.new(default_src: %w(example.org), sandbox: %w(allow-forms allow-scripts))
|
101
|
+
expect(csp.value).to eq("default-src example.org; sandbox allow-forms allow-scripts")
|
102
|
+
end
|
103
|
+
|
89
104
|
it "does not emit a warning when using frame-src" do
|
90
105
|
expect(Kernel).to_not receive(:warn)
|
91
106
|
ContentSecurityPolicy.new(default_src: %w('self'), frame_src: %w('self')).value
|
@@ -120,50 +135,52 @@ module SecureHeaders
|
|
120
135
|
block_all_mixed_content: true,
|
121
136
|
upgrade_insecure_requests: true,
|
122
137
|
script_src: %w(script-src.com),
|
123
|
-
script_nonce: 123456
|
138
|
+
script_nonce: 123456,
|
139
|
+
sandbox: %w(allow-forms),
|
140
|
+
plugin_types: %w(application/pdf)
|
124
141
|
})
|
125
142
|
end
|
126
143
|
|
127
144
|
it "does not filter any directives for Chrome" do
|
128
145
|
policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:chrome])
|
129
|
-
expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; block-all-mixed-content; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; plugin-types
|
146
|
+
expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; block-all-mixed-content; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; plugin-types application/pdf; sandbox allow-forms; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
|
130
147
|
end
|
131
148
|
|
132
149
|
it "does not filter any directives for Opera" do
|
133
150
|
policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:opera])
|
134
|
-
expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; block-all-mixed-content; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; plugin-types
|
151
|
+
expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; block-all-mixed-content; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; plugin-types application/pdf; sandbox allow-forms; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
|
135
152
|
end
|
136
153
|
|
137
154
|
it "filters blocked-all-mixed-content, child-src, and plugin-types for firefox" do
|
138
155
|
policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:firefox])
|
139
|
-
expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; frame-src child-src.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; sandbox
|
156
|
+
expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; frame-src child-src.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
|
140
157
|
end
|
141
158
|
|
142
159
|
it "filters blocked-all-mixed-content, frame-src, and plugin-types for firefox 46 and higher" do
|
143
160
|
policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:firefox46])
|
144
|
-
expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; sandbox
|
161
|
+
expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
|
145
162
|
end
|
146
163
|
|
147
164
|
it "child-src value is copied to frame-src, adds 'unsafe-inline', filters base-uri, blocked-all-mixed-content, upgrade-insecure-requests, child-src, form-action, frame-ancestors, nonce sources, hash sources, and plugin-types for Edge" do
|
148
165
|
policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:edge])
|
149
|
-
expect(policy.value).to eq("default-src default-src.com; connect-src connect-src.com; font-src font-src.com; frame-src child-src.com; img-src img-src.com; media-src media-src.com; object-src object-src.com; sandbox
|
166
|
+
expect(policy.value).to eq("default-src default-src.com; connect-src connect-src.com; font-src font-src.com; frame-src child-src.com; img-src img-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'unsafe-inline'; style-src style-src.com; report-uri report-uri.com")
|
150
167
|
end
|
151
168
|
|
152
169
|
it "child-src value is copied to frame-src, adds 'unsafe-inline', filters base-uri, blocked-all-mixed-content, upgrade-insecure-requests, child-src, form-action, frame-ancestors, nonce sources, hash sources, and plugin-types for safari" do
|
153
170
|
policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:safari6])
|
154
|
-
expect(policy.value).to eq("default-src default-src.com; connect-src connect-src.com; font-src font-src.com; frame-src child-src.com; img-src img-src.com; media-src media-src.com; object-src object-src.com; sandbox
|
171
|
+
expect(policy.value).to eq("default-src default-src.com; connect-src connect-src.com; font-src font-src.com; frame-src child-src.com; img-src img-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'unsafe-inline'; style-src style-src.com; report-uri report-uri.com")
|
155
172
|
end
|
156
173
|
|
157
174
|
it "adds 'unsafe-inline', filters blocked-all-mixed-content, upgrade-insecure-requests, nonce sources, and hash sources for safari 10 and higher" do
|
158
175
|
policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:safari10])
|
159
|
-
expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; media-src media-src.com; object-src object-src.com; plugin-types
|
176
|
+
expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; media-src media-src.com; object-src object-src.com; plugin-types application/pdf; sandbox allow-forms; script-src script-src.com 'nonce-123456'; style-src style-src.com; report-uri report-uri.com")
|
160
177
|
end
|
161
178
|
|
162
179
|
it "falls back to standard Firefox defaults when the useragent version is not present" do
|
163
180
|
ua = USER_AGENTS[:firefox].dup
|
164
181
|
allow(ua).to receive(:version).and_return(nil)
|
165
182
|
policy = ContentSecurityPolicy.new(complex_opts, ua)
|
166
|
-
expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; frame-src child-src.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; sandbox
|
183
|
+
expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; frame-src child-src.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
|
167
184
|
end
|
168
185
|
end
|
169
186
|
end
|
@@ -98,6 +98,36 @@ module SecureHeaders
|
|
98
98
|
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_src: %w(self none inline eval)))
|
99
99
|
end.to raise_error(ContentSecurityPolicyConfigError)
|
100
100
|
end
|
101
|
+
|
102
|
+
it "rejects anything not of the form allow-* as a sandbox value" do
|
103
|
+
expect do
|
104
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(sandbox: ["steve"])))
|
105
|
+
end.to raise_error(ContentSecurityPolicyConfigError)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "accepts anything of the form allow-* as a sandbox value " do
|
109
|
+
expect do
|
110
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(sandbox: ["allow-foo"])))
|
111
|
+
end.to_not raise_error
|
112
|
+
end
|
113
|
+
|
114
|
+
it "accepts true as a sandbox policy" do
|
115
|
+
expect do
|
116
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(sandbox: true)))
|
117
|
+
end.to_not raise_error
|
118
|
+
end
|
119
|
+
|
120
|
+
it "rejects anything not of the form type/subtype as a plugin-type value" do
|
121
|
+
expect do
|
122
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(plugin_types: ["steve"])))
|
123
|
+
end.to raise_error(ContentSecurityPolicyConfigError)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "accepts anything of the form type/subtype as a plugin-type value " do
|
127
|
+
expect do
|
128
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(plugin_types: ["application/pdf"])))
|
129
|
+
end.to_not raise_error
|
130
|
+
end
|
101
131
|
end
|
102
132
|
|
103
133
|
describe "#combine_policies" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: secure_headers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.7.
|
4
|
+
version: 3.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Neil Matatall
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|