secure_headers 3.7.0 → 3.7.1
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 +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
|