roda 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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