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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 591de0f41bc64b3520fbb2fdab9fc94b490d1604
4
- data.tar.gz: f836c5e63e4d054ff2ce3f1ec597bbd9cf67ed4b
3
+ metadata.gz: 95d8b4815c1f0553ff7a7d6ca32cc046aaf08009
4
+ data.tar.gz: 0f2f8a647f84de17c79d6369ace1ffbcaefdf089
5
5
  SHA512:
6
- metadata.gz: 5317d425d7c65dfc73b32703aae7e16e3a566b620b42e3233cd6dc7403a1a9842bb7dc00ffc5c13a86240c5a8abee3ae07fed0c73d4b06a06bcc11db39a79a20
7
- data.tar.gz: 154573bfb9112b4b1510abbde3f5eb1e85b709f7839ff4a6a547223e67e698ff014bb578f02b0e8775ee723016f5903e8cda38cb9846b24cafe3dc8e414bb007
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 :string
84
- [symbol_to_hyphen_case(directive_name), @config.directive_value(directive_name)].join(" ")
85
- else
86
- build_directive(directive_name)
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 build_directive(directive)
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 => :source_list,
139
+ PLUGIN_TYPES => :media_type_list,
152
140
  REPORT_URI => :source_list,
153
- SANDBOX => :source_list,
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 source_list?(directive)
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
- if !original[directive] && ((source_list?(directive) && FETCH_SOURCES.include?(directive)) || nonce_added?(original, additions))
270
- if nonce_added?(original, additions)
271
- inferred_directive = directive.to_s.gsub(/_nonce/, "_src").to_sym
272
- unless original[inferred_directive] || NON_FETCH_SOURCES.include?(inferred_directive)
273
- original[inferred_directive] = default_for(directive, original)
274
- end
275
- else
276
- original[directive] = default_for(directive, original)
277
- end
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, source_expression)
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?(source_expression)
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
- validate_source_expression!(directive, source_expression)
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. has a valid name
319
- # 2. is an array of strings
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
- ensure_valid_directive!(directive)
326
- ensure_array_of_strings!(directive, source_expression)
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, source_expression)
337
- unless source_expression.is_a?(Array) && source_expression.compact.all? { |v| v.is_a?(String) }
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
@@ -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.0"
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 plugin-types.com; sandbox sandbox.com; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
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 plugin-types.com; sandbox sandbox.com; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
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 sandbox.com; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
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 sandbox.com; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
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 sandbox.com; script-src script-src.com 'unsafe-inline'; style-src style-src.com; report-uri report-uri.com")
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 sandbox.com; script-src script-src.com 'unsafe-inline'; style-src style-src.com; report-uri report-uri.com")
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 plugin-types.com; sandbox sandbox.com; script-src script-src.com 'nonce-123456'; style-src style-src.com; report-uri report-uri.com")
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 sandbox.com; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
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.0
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-08-25 00:00:00.000000000 Z
11
+ date: 2017-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake