roda 3.6.0 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f461d51ffcff3abee852c79773a13d87f2ffc5fea2c6a27346bfb2755b318e1e
4
- data.tar.gz: c30fafc14125ef2b100bce74762193cb0f09776805732c284b7d7cd0472f07c5
3
+ metadata.gz: d7fe20c8d9a31f311d69cac5c073690374859342d995f97183abeda25e84c2e8
4
+ data.tar.gz: d8b584c5768ef36fc60cd495d51285446c9cdd7e94a56668758b9c38253413a4
5
5
  SHA512:
6
- metadata.gz: 428d1c607d27a5f90b4c5c992e8f8f3ea33cf8dd7c49e2a0920e49e4b97de34532eb9177aae04ad1efa9a229d747a6e5a744334ae803edbaf60e1e51cf154552
7
- data.tar.gz: dd85d32aedb8b2c09fc757bd1cbc0f92dce99849841e4ab2e78f47589de8670c87023eb9d6a144efd1c7ff06f08f705589e2510cb7c4383cba13b40020b2d489
6
+ metadata.gz: 90e1c33010e0cd7d21fbf434c88bbad41dc069c746bb7eb58b1ae8b982f48320316eabf7f95d8af9376d76cc6d5d88fcb216c3859df5199f6a98bcf86830b5f3
7
+ data.tar.gz: 1a2d6dda37a5a49871af93894e5b32e553cdac5baa34d5c0a441481101bbc88353e3b8120deeaf7afcfeaae48c1c7cb48ea525a27c8db8e3d212e7945e75fb8d
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ = 3.7.0 (2018-04-20)
2
+
3
+ * Make response_request plugin work with error_handler and class_level_routing plugins (jeremyevans)
4
+
5
+ * Add content_security_policy plugin for setting an appropriate Content-Security-Policy header (jeremyevans)
6
+
1
7
  = 3.6.0 (2018-03-26)
2
8
 
3
9
  * Add :wrap option to json_parser plugin, for whether/how to wrap the uploaded JSON object (jeremyevans) (#142)
@@ -0,0 +1,123 @@
1
+ = New Features
2
+
3
+ * A content_security_policy plugin has been added for setting up an
4
+ appropriate Content-Security-Policy header. To configure the
5
+ default policy, load the plugin with a block:
6
+
7
+ plugin :content_security_policy do |csp|
8
+ csp.default_src :none
9
+ csp.img_src :self
10
+ csp.style_src :self, 'fonts.googleapis.com'
11
+ csp.script_src :self
12
+ csp.font_src :self, 'fonts.gstatic.com'
13
+ csp.form_action :self
14
+ csp.base_uri :none
15
+ csp.frame_ancestors :none
16
+ csp.block_all_mixed_content
17
+ end
18
+
19
+ It's recommended that use use a default_src of :none at the top
20
+ of the policy, then explicitly change other settings (e.g. img_src)
21
+ when you want to allow content.
22
+
23
+ Anywhere in the routing tree, you can use the content_security_policy
24
+ method to override the default policy. You can pass this method a
25
+ block:
26
+
27
+ r.get 'foo' do
28
+ content_security_policy do |csp|
29
+ csp.object_src :self
30
+ csp.add_style_src 'bar.com'
31
+ end
32
+ # ...
33
+ end
34
+
35
+ Or just call a method on it:
36
+
37
+ r.get 'foo' do
38
+ content_security_policy.script_src :self, 'example.com', [:nonce, 'foobarbaz']
39
+ # ...
40
+ end
41
+
42
+ The following methods exist for configuring the content security policy, they set
43
+ the appropriate directive, with the underscores replaced by a dash.
44
+
45
+ * base_uri
46
+ * child_src
47
+ * connect_src
48
+ * default_src
49
+ * font_src
50
+ * form_action
51
+ * frame_ancestors
52
+ * frame_src
53
+ * img_src
54
+ * manifest_src
55
+ * media_src
56
+ * object_src
57
+ * plugin_types
58
+ * report_uri
59
+ * require_sri_for
60
+ * sandbox
61
+ * script_src
62
+ * style_src
63
+ * worker_src
64
+
65
+ All of these methods support any number of arguments, and each argument
66
+ should be one of the following types:
67
+
68
+ String :: used verbatim
69
+ Symbol :: Substitutes underscore with dash and surrounds with single
70
+ quotes
71
+ Array :: only accepts 2 element arrays, joins elements with a dash
72
+ and surrounds them with single quotes
73
+
74
+ Example:
75
+
76
+ content_security_policy.script_src :self, :unsafe_eval,
77
+ 'example.com', [:nonce, 'foobarbaz']
78
+ # script-src 'self' 'unsafe-eval' example.com 'nonce-foobarbaz';
79
+
80
+ When calling a method with no arguments, the setting is removed from
81
+ the policy instead of being left empty, since all of these setting
82
+ require at least one value. Likewise, if the policy does not have
83
+ any settings, the header will not be added.
84
+
85
+ Calling the method overrides any previous setting. Each of the
86
+ methods has a add_* method (e.g. add_script_src) for appending to the
87
+ current setting, and a get_* method (e.g. get_script_src) for
88
+ retrieving the current value of the setting, or nil if it is not
89
+ defined.
90
+
91
+ content_security_policy.script_src :self, :unsafe_eval
92
+ # script-src 'self' 'unsafe-eval';
93
+ content_security_policy.add_script_src 'example.com', [:nonce, 'foobarbaz']
94
+ # script-src 'self' 'unsafe-eval' example.com 'nonce-foobarbaz';
95
+
96
+ content_security_policy.get_script_src 'example.com', [:nonce, 'foobarbaz']
97
+ # => [:self, :unsafe_eval, 'example.com', [:nonce, 'foobarbaz']]
98
+
99
+ The clear method can be used to remove all settings from the policy.
100
+
101
+ The following methods to set boolean directives are also defined:
102
+
103
+ * block_all_mixed_content
104
+ * upgrade_insecure_requests
105
+
106
+ Calling these methods will turn on the related setting. To turn the
107
+ setting off again, you can call them with a false argument (e.g.
108
+ block_all_mixed_content(false)). Each method also an *? method
109
+ (e.g. block_all_mixed_content?) for returning whether the setting is
110
+ currently enabled.
111
+
112
+ Likewise there is also a report_only method for turning on report
113
+ only mode (the default is enforcement mode), or turning off report
114
+ only mode if a false argument is given. Also, there is a
115
+ report_only? method for returning whether report only mode is
116
+ enabled. In report only mode, the Content-Security-Policy-Report-Only
117
+ header is used.
118
+
119
+ = Other Improvements
120
+
121
+ * The response_request plugin now integrates with the error_handler and
122
+ class_level_routing plugins. Those plugins now reinitialize the
123
+ current response object instead of creating a new response object.
@@ -86,7 +86,7 @@ class Roda
86
86
  if result[0] == 404 && (v = result[2]).is_a?(Array) && v.empty?
87
87
  # Reset the response so it doesn't inherit the status or any headers from
88
88
  # the original response.
89
- @_response = self.class::RodaResponse.new
89
+ @_response.send(:initialize)
90
90
  super do |r|
91
91
  opts[:class_level_routes].each do |meth, args, blk|
92
92
  req.instance_variable_set(:@remaining_path, rp)
@@ -0,0 +1,317 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The content_security_policy plugin allows you to easily set a Content-Security-Policy
7
+ # header for the application, which modern browsers will use to control access to specific
8
+ # types of page content.
9
+ #
10
+ # You would generally call the plugin with a block to set the default policy:
11
+ #
12
+ # plugin :content_security_policy do |csp|
13
+ # csp.default_src :none
14
+ # csp.img_src :self
15
+ # csp.style_src :self
16
+ # csp.script_src :self
17
+ # csp.font_src :self
18
+ # csp.form_action :self
19
+ # csp.base_uri :none
20
+ # csp.frame_ancestors :none
21
+ # csp.block_all_mixed_content
22
+ # end
23
+ #
24
+ # Then, anywhere in the routing tree, you can customize the policy for just that
25
+ # branch or action using the same block syntax:
26
+ #
27
+ # r.get 'foo' do
28
+ # content_security_policy do |csp|
29
+ # csp.object_src :self
30
+ # csp.add_style_src 'bar.com'
31
+ # end
32
+ # # ...
33
+ # end
34
+ #
35
+ # In addition to using a block, you can also call methods on the object returned
36
+ # by the method:
37
+ #
38
+ # r.get 'foo' do
39
+ # content_security_policy.script_src :self, 'example.com', [:nonce, 'foobarbaz']
40
+ # # ...
41
+ # end
42
+ #
43
+ # The following methods are available for configuring the content security policy,
44
+ # which specify the setting (substituting _ with -):
45
+ #
46
+ # * base_uri
47
+ # * child_src
48
+ # * connect_src
49
+ # * default_src
50
+ # * font_src
51
+ # * form_action
52
+ # * frame_ancestors
53
+ # * frame_src
54
+ # * img_src
55
+ # * manifest_src
56
+ # * media_src
57
+ # * object_src
58
+ # * plugin_types
59
+ # * report_uri
60
+ # * require_sri_for
61
+ # * sandbox
62
+ # * script_src
63
+ # * style_src
64
+ # * worker_src
65
+ #
66
+ # All of these methods support any number of arguments, and each argument should
67
+ # be one of the following types:
68
+ #
69
+ # String :: used verbatim
70
+ # Symbol :: Substitutes +_+ with +-+ and surrounds with <tt>'</tt>
71
+ # Array :: only accepts 2 element arrays, joins elements with +-+ and
72
+ # surrounds the result with <tt>'</tt>
73
+ #
74
+ # Example:
75
+ #
76
+ # content_security_policy.script_src :self, :unsafe_eval, 'example.com', [:nonce, 'foobarbaz']
77
+ # # script-src 'self' 'unsafe-eval' example.com 'nonce-foobarbaz';
78
+ #
79
+ # When calling a method with no arguments, the setting is removed from the policy instead
80
+ # of being left empty, since all of these setting require at least one value. Likewise,
81
+ # if the policy does not have any settings, the header will not be added.
82
+ #
83
+ # Calling the method overrides any previous setting. Each of the methods has +add_*+ and
84
+ # +get_*+ methods defined. The +add_*+ method appends to any existing setting, and the +get_*+ method
85
+ # returns the current value for the setting.
86
+ #
87
+ # content_security_policy.script_src :self, :unsafe_eval
88
+ # content_security_policy.add_script_src 'example.com', [:nonce, 'foobarbaz']
89
+ # # script-src 'self' 'unsafe-eval' example.com 'nonce-foobarbaz';
90
+ #
91
+ # content_security_policy.get_script_src 'example.com', [:nonce, 'foobarbaz']
92
+ # # => [:self, :unsafe_eval, 'example.com', [:nonce, 'foobarbaz']]
93
+ #
94
+ # The clear method can be used to remove all settings from the policy.
95
+ #
96
+ # The following methods to set boolean settings are also defined:
97
+ #
98
+ # * block_all_mixed_content
99
+ # * upgrade_insecure_requests
100
+ #
101
+ # Calling these methods will turn on the related setting. To turn the setting
102
+ # off again, you can call them with a +false+ argument. There is also a <tt>*?</tt> method
103
+ # for each setting for returning whether the setting is currently enabled.
104
+ #
105
+ # Likewise there is also a +report_only+ method for turning on report only mode (the
106
+ # default is enforcement mode), or turning off report only mode if a false argument
107
+ # is given. Also, there is a +report_only?+ method for returning whether report only
108
+ # mode is enabled.
109
+ module ContentSecurityPolicy
110
+ # Represents a content security policy.
111
+ class Policy
112
+ '
113
+ base-uri
114
+ child-src
115
+ connect-src
116
+ default-src
117
+ font-src
118
+ form-action
119
+ frame-ancestors
120
+ frame-src
121
+ img-src
122
+ manifest-src
123
+ media-src
124
+ object-src
125
+ plugin-types
126
+ report-uri
127
+ require-sri-for
128
+ sandbox
129
+ script-src
130
+ style-src
131
+ worker-src
132
+ '.split.each(&:freeze).each do |setting|
133
+ meth = setting.gsub('-', '_').freeze
134
+
135
+ # Setting method name sets the setting value, or removes it if no args are given.
136
+ define_method(meth) do |*args|
137
+ if args.empty?
138
+ @opts.delete(setting)
139
+ else
140
+ @opts[setting] = args.freeze
141
+ end
142
+ nil
143
+ end
144
+
145
+ # add_* method name adds to the setting value, or clears setting if no values
146
+ # are given.
147
+ define_method("add_#{meth}") do |*args|
148
+ if args.empty?
149
+ @opts[setting]
150
+ else
151
+ @opts[setting] ||= EMPTY_ARRAY
152
+ @opts[setting] += args
153
+ @opts[setting].freeze
154
+ end
155
+ nil
156
+ end
157
+
158
+ # get_* method always returns current setting value.
159
+ define_method("get_#{meth}") do
160
+ @opts[setting]
161
+ end
162
+ end
163
+
164
+ %w'block-all-mixed-content upgrade-insecure-requests'.each(&:freeze).each do |setting|
165
+ meth = setting.gsub('-', '_').freeze
166
+
167
+ # Setting method name turns on setting if true or no argument given,
168
+ # or removes setting if false is given.
169
+ define_method(meth) do |arg=true|
170
+ if arg
171
+ @opts[setting] = true
172
+ else
173
+ @opts.delete(setting)
174
+ end
175
+
176
+ nil
177
+ end
178
+
179
+ # *? method returns true or false depending on whether setting is enabled.
180
+ define_method("#{meth}?") do
181
+ !!@opts[setting]
182
+ end
183
+ end
184
+
185
+ def initialize
186
+ clear
187
+ end
188
+
189
+ # Clear all settings, useful to remove any inherited settings.
190
+ def clear
191
+ @opts = {}
192
+ end
193
+
194
+ # Do not allow future modifications to any settings.
195
+ def freeze
196
+ @opts.freeze
197
+ header_value.freeze
198
+ super
199
+ end
200
+
201
+ # The header name to use, depends on whether report only mode has been enabled.
202
+ def header_key
203
+ @report_only ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy'
204
+ end
205
+
206
+ # The header value to use.
207
+ def header_value
208
+ return @header_value if @header_value
209
+
210
+ s = String.new
211
+ @opts.each do |k, vs|
212
+ s << k
213
+ unless vs == true
214
+ vs.each{|v| append_formatted_value(s, v)}
215
+ end
216
+ s << '; '
217
+ end
218
+ @header_value = s
219
+ end
220
+
221
+ # Set whether the Content-Security-Policy-Report-Only header instead of the
222
+ # default Content-Security-Policy header.
223
+ def report_only(report=true)
224
+ @report_only = report
225
+ end
226
+
227
+ # Whether this policy uses report only mode.
228
+ def report_only?
229
+ !!@report_only
230
+ end
231
+
232
+ # Set the current policy in the headers hash. If no settings have been made
233
+ # in the policy, does not set a header.
234
+ def set_header(headers)
235
+ return if @opts.empty?
236
+ headers[header_key] ||= header_value
237
+ end
238
+
239
+ private
240
+
241
+ # Handle three types of values when formatting the header:
242
+ # String :: used verbatim
243
+ # Symbol :: Substitutes _ with - and surrounds with '
244
+ # Array :: only accepts 2 element arrays, joins them with - and
245
+ # surrounds them with '
246
+ def append_formatted_value(s, v)
247
+ case v
248
+ when String
249
+ s << ' ' << v
250
+ when Array
251
+ case v.length
252
+ when 2
253
+ s << " '" << v.join('-') << "'"
254
+ else
255
+ raise RodaError, "unsupported CSP value used: #{v.inspect}"
256
+ end
257
+ when Symbol
258
+ s << " '" << v.to_s.gsub('_', '-') << "'"
259
+ else
260
+ raise RodaError, "unsupported CSP value used: #{v.inspect}"
261
+ end
262
+ end
263
+
264
+ # Make object copy use copy of settings, and remove cached header value.
265
+ def initialize_copy(_)
266
+ super
267
+ @opts = @opts.dup
268
+ @header_value = nil
269
+ end
270
+ end
271
+
272
+
273
+ # Yield the current Content Security Policy to the block.
274
+ def self.configure(app)
275
+ policy = app.opts[:content_security_policy] = if policy = app.opts[:content_security_policy]
276
+ policy.dup
277
+ else
278
+ Policy.new
279
+ end
280
+
281
+ yield policy if block_given?
282
+ policy.freeze
283
+ end
284
+
285
+ module InstanceMethods
286
+ # If a block is given, yield the current content security policy. Returns the
287
+ # current content security policy.
288
+ def content_security_policy
289
+ policy = @_response.content_security_policy
290
+ yield policy if block_given?
291
+ policy
292
+ end
293
+ end
294
+
295
+ module ResponseMethods
296
+ # Unset any content security policy when reinitializing
297
+ def initialize
298
+ super
299
+ @content_security_policy &&= nil
300
+ end
301
+
302
+ # The current content security policy to be used for this response.
303
+ def content_security_policy
304
+ @content_security_policy ||= roda_class.opts[:content_security_policy].dup
305
+ end
306
+
307
+ # Set the appropriate content security policy header.
308
+ def set_default_headers
309
+ super
310
+ (@content_security_policy || roda_class.opts[:content_security_policy]).set_header(@headers)
311
+ end
312
+ end
313
+ end
314
+
315
+ register_plugin(:content_security_policy, ContentSecurityPolicy)
316
+ end
317
+ end
@@ -64,8 +64,8 @@ class Roda
64
64
  def call
65
65
  super
66
66
  rescue *opts[:error_handler_classes] => e
67
- res = @_response = self.class::RodaResponse.new
68
- res.status = 500
67
+ @_response.send(:initialize)
68
+ @_response.status = 500
69
69
  super{handle_error(e)}
70
70
  end
71
71
 
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 3
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 6
7
+ RodaMinorVersion = 7
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -0,0 +1,175 @@
1
+ require_relative "../spec_helper"
2
+
3
+ describe "content_security_policy plugin" do
4
+ it "does not add header if no options are set" do
5
+ app(:content_security_policy){'a'}
6
+ header('Content-Security-Policy', "/a").must_be_nil
7
+ end
8
+
9
+ it "sets Content-Security-Policy header" do
10
+ app(:bare) do
11
+ plugin :content_security_policy do |csp|
12
+ csp.default_src :self
13
+ csp.img_src :self, 'example.com'
14
+ csp.style_src [:sha256, 'abc']
15
+ end
16
+
17
+ route do |r|
18
+ r.get 'ro' do
19
+ content_security_policy.report_only
20
+ ''
21
+ end
22
+
23
+ r.get 'nro' do
24
+ content_security_policy.report_only
25
+ content_security_policy.report_only(false)
26
+ content_security_policy.report_only?.inspect
27
+ end
28
+
29
+ r.get 'get' do
30
+ content_security_policy.get_default_src.inspect
31
+ end
32
+
33
+ r.get 'add' do
34
+ content_security_policy.add_default_src('foo.com', 'bar.com')
35
+ ''
36
+ end
37
+
38
+ r.get 'empty' do
39
+ content_security_policy.add_default_src
40
+ ''
41
+ end
42
+
43
+ r.get 'set' do
44
+ content_security_policy.default_src('foo.com', 'bar.com')
45
+ ''
46
+ end
47
+
48
+ r.get 'bool' do
49
+ content_security_policy.block_all_mixed_content
50
+ content_security_policy.upgrade_insecure_requests(false)
51
+ content_security_policy.block_all_mixed_content?.inspect
52
+ end
53
+
54
+ r.get 'block' do
55
+ content_security_policy do |csp|
56
+ csp.block_all_mixed_content
57
+ csp.add_default_src('foo.com', 'bar.com')
58
+ csp.img_src :none
59
+ csp.style_src
60
+ csp.report_only
61
+ end
62
+ ''
63
+ end
64
+
65
+ r.get 'clear' do
66
+ content_security_policy do |csp|
67
+ csp.clear
68
+ csp.add_default_src('foo.com', 'bar.com')
69
+ end
70
+ ''
71
+ end
72
+
73
+ 'a'
74
+ end
75
+ end
76
+
77
+ v = "default-src 'self'; img-src 'self' example.com; style-src 'sha256-abc'; "
78
+
79
+ header('Content-Security-Policy', "/a").must_equal v
80
+
81
+ header('Content-Security-Policy', "/nro").must_equal v
82
+ header('Content-Security-Policy-Report-Only', "/nro").must_be_nil
83
+ body("/nro").must_equal 'false'
84
+
85
+ header('Content-Security-Policy-Report-Only', "/ro").must_equal v
86
+ header('Content-Security-Policy', "/ro").must_be_nil
87
+
88
+ body('/get').must_equal '[:self]'
89
+
90
+ header('Content-Security-Policy', "/add").must_equal "default-src 'self' foo.com bar.com; img-src 'self' example.com; style-src 'sha256-abc'; "
91
+
92
+ header('Content-Security-Policy', "/empty").must_equal "default-src 'self'; img-src 'self' example.com; style-src 'sha256-abc'; "
93
+
94
+ header('Content-Security-Policy', "/set").must_equal "default-src foo.com bar.com; img-src 'self' example.com; style-src 'sha256-abc'; "
95
+
96
+ body('/bool').must_equal 'true'
97
+ header('Content-Security-Policy', "/bool").must_equal "default-src 'self'; img-src 'self' example.com; style-src 'sha256-abc'; block-all-mixed-content; "
98
+
99
+ header('Content-Security-Policy-Report-Only', "/block").must_equal "default-src 'self' foo.com bar.com; img-src 'none'; block-all-mixed-content; "
100
+
101
+ header('Content-Security-Policy', "/clear").must_equal "default-src foo.com bar.com; "
102
+ end
103
+
104
+ it "raises error for unsupported CSP values" do
105
+ app{}
106
+ proc{app.plugin(:content_security_policy){|csp| csp.default_src Object.new}}.must_raise Roda::RodaError
107
+ proc{app.plugin(:content_security_policy){|csp| csp.default_src []}}.must_raise Roda::RodaError
108
+ proc{app.plugin(:content_security_policy){|csp| csp.default_src [:a]}}.must_raise Roda::RodaError
109
+ proc{app.plugin(:content_security_policy){|csp| csp.default_src [:a, :b, :c]}}.must_raise Roda::RodaError
110
+ end
111
+
112
+ it "supports all documented settings" do
113
+ app(:content_security_policy) do |r|
114
+ content_security_policy.send(r.path[1..-1], :self)
115
+ end
116
+
117
+ '
118
+ base_uri
119
+ child_src
120
+ connect_src
121
+ default_src
122
+ font_src
123
+ form_action
124
+ frame_ancestors
125
+ frame_src
126
+ img_src
127
+ manifest_src
128
+ media_src
129
+ object_src
130
+ plugin_types
131
+ report_uri
132
+ require_sri_for
133
+ sandbox
134
+ script_src
135
+ style_src
136
+ worker_src
137
+ '.split.each do |setting|
138
+ header('Content-Security-Policy', "/#{setting}").must_equal "#{setting.gsub('_', '-')} 'self'; "
139
+ end
140
+ end
141
+
142
+ it "does not override existing heading" do
143
+ app(:content_security_policy) do |r|
144
+ content_security_policy.default_src :self
145
+ response['Content-Security-Policy'] = "default_src 'none';"
146
+ ''
147
+ end
148
+ header('Content-Security-Policy').must_equal "default_src 'none';"
149
+ end
150
+
151
+ it "works with error_handler" do
152
+ app(:bare) do
153
+ plugin(:error_handler){|_| ''}
154
+ plugin :content_security_policy do |csp|
155
+ csp.default_src :self
156
+ csp.img_src :self, 'example.com'
157
+ csp.style_src [:sha256, 'abc']
158
+ end
159
+
160
+ route do |r|
161
+ r.get 'a' do
162
+ content_security_policy.default_src 'foo.com'
163
+ raise
164
+ end
165
+
166
+ raise
167
+ end
168
+ end
169
+
170
+ header('Content-Security-Policy').must_equal "default-src 'self'; img-src 'self' example.com; style-src 'sha256-abc'; "
171
+
172
+ # Don't include updates before the error
173
+ header('Content-Security-Policy', '/a').must_equal "default-src 'self'; img-src 'self' example.com; style-src 'sha256-abc'; "
174
+ end
175
+ end
@@ -9,4 +9,35 @@ describe "response_request plugin" do
9
9
  body.must_equal "a"
10
10
  body('REQUEST_METHOD'=>'POST').must_equal "b"
11
11
  end
12
+
13
+ it "should work with error_handler plugin" do
14
+ app(:bare) do
15
+ plugin :response_request
16
+
17
+ plugin :error_handler do |_|
18
+ response.request.post? ? "b" : "a"
19
+ end
20
+
21
+ route{raise}
22
+ end
23
+
24
+ body.must_equal "a"
25
+ body('REQUEST_METHOD'=>'POST').must_equal "b"
26
+ end
27
+
28
+ it "should work with class_level_routing plugin" do
29
+ app(:bare) do
30
+ plugin :response_request
31
+ plugin :class_level_routing
32
+
33
+ is '' do |_|
34
+ response.request.post? ? "b" : "a"
35
+ end
36
+
37
+ route{}
38
+ end
39
+
40
+ body.must_equal "a"
41
+ body('REQUEST_METHOD'=>'POST').must_equal "b"
42
+ end
12
43
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.0
4
+ version: 3.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-26 00:00:00.000000000 Z
11
+ date: 2018-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -160,21 +160,13 @@ extra_rdoc_files:
160
160
  - MIT-LICENSE
161
161
  - CHANGELOG
162
162
  - doc/conventions.rdoc
163
+ - doc/release_notes/3.7.0.txt
163
164
  - doc/release_notes/1.0.0.txt
164
165
  - doc/release_notes/1.1.0.txt
165
166
  - doc/release_notes/1.2.0.txt
166
167
  - doc/release_notes/1.3.0.txt
167
168
  - doc/release_notes/2.0.0.txt
168
169
  - doc/release_notes/2.1.0.txt
169
- - doc/release_notes/2.2.0.txt
170
- - doc/release_notes/2.3.0.txt
171
- - doc/release_notes/2.4.0.txt
172
- - doc/release_notes/2.5.0.txt
173
- - doc/release_notes/2.5.1.txt
174
- - doc/release_notes/2.6.0.txt
175
- - doc/release_notes/2.7.0.txt
176
- - doc/release_notes/2.8.0.txt
177
- - doc/release_notes/2.9.0.txt
178
170
  - doc/release_notes/2.10.0.txt
179
171
  - doc/release_notes/2.11.0.txt
180
172
  - doc/release_notes/2.12.0.txt
@@ -185,6 +177,7 @@ extra_rdoc_files:
185
177
  - doc/release_notes/2.17.0.txt
186
178
  - doc/release_notes/2.18.0.txt
187
179
  - doc/release_notes/2.19.0.txt
180
+ - doc/release_notes/2.2.0.txt
188
181
  - doc/release_notes/2.20.0.txt
189
182
  - doc/release_notes/2.21.0.txt
190
183
  - doc/release_notes/2.22.0.txt
@@ -195,6 +188,14 @@ extra_rdoc_files:
195
188
  - doc/release_notes/2.27.0.txt
196
189
  - doc/release_notes/2.28.0.txt
197
190
  - doc/release_notes/2.29.0.txt
191
+ - doc/release_notes/2.3.0.txt
192
+ - doc/release_notes/2.4.0.txt
193
+ - doc/release_notes/2.5.0.txt
194
+ - doc/release_notes/2.5.1.txt
195
+ - doc/release_notes/2.6.0.txt
196
+ - doc/release_notes/2.7.0.txt
197
+ - doc/release_notes/2.8.0.txt
198
+ - doc/release_notes/2.9.0.txt
198
199
  - doc/release_notes/3.0.0.txt
199
200
  - doc/release_notes/3.1.0.txt
200
201
  - doc/release_notes/3.2.0.txt
@@ -250,6 +251,7 @@ files:
250
251
  - doc/release_notes/3.4.0.txt
251
252
  - doc/release_notes/3.5.0.txt
252
253
  - doc/release_notes/3.6.0.txt
254
+ - doc/release_notes/3.7.0.txt
253
255
  - lib/roda.rb
254
256
  - lib/roda/plugins/_symbol_regexp_matchers.rb
255
257
  - lib/roda/plugins/all_verbs.rb
@@ -262,6 +264,7 @@ files:
262
264
  - lib/roda/plugins/class_level_routing.rb
263
265
  - lib/roda/plugins/class_matchers.rb
264
266
  - lib/roda/plugins/content_for.rb
267
+ - lib/roda/plugins/content_security_policy.rb
265
268
  - lib/roda/plugins/cookies.rb
266
269
  - lib/roda/plugins/csrf.rb
267
270
  - lib/roda/plugins/default_headers.rb
@@ -358,6 +361,7 @@ files:
358
361
  - spec/plugin/class_level_routing_spec.rb
359
362
  - spec/plugin/class_matchers_spec.rb
360
363
  - spec/plugin/content_for_spec.rb
364
+ - spec/plugin/content_security_policy_spec.rb
361
365
  - spec/plugin/cookies_spec.rb
362
366
  - spec/plugin/csrf_spec.rb
363
367
  - spec/plugin/default_headers_spec.rb
@@ -478,7 +482,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
478
482
  version: '0'
479
483
  requirements: []
480
484
  rubyforge_project:
481
- rubygems_version: 2.7.3
485
+ rubygems_version: 2.7.6
482
486
  signing_key:
483
487
  specification_version: 4
484
488
  summary: Routing tree web toolkit