roda 3.107.0 → 3.108.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/roda/plugins/_base64.rb +2 -2
  3. data/lib/roda/plugins/_optimized_matching.rb +1 -0
  4. data/lib/roda/plugins/all_verbs.rb +2 -2
  5. data/lib/roda/plugins/assets.rb +4 -4
  6. data/lib/roda/plugins/bearer_token.rb +2 -2
  7. data/lib/roda/plugins/break.rb +9 -1
  8. data/lib/roda/plugins/caching.rb +12 -12
  9. data/lib/roda/plugins/common_logger.rb +4 -4
  10. data/lib/roda/plugins/cookie_flags.rb +2 -2
  11. data/lib/roda/plugins/each_part.rb +4 -4
  12. data/lib/roda/plugins/error_email.rb +2 -2
  13. data/lib/roda/plugins/exception_page.rb +2 -2
  14. data/lib/roda/plugins/h.rb +4 -4
  15. data/lib/roda/plugins/hash_branches.rb +8 -4
  16. data/lib/roda/plugins/hash_paths.rb +4 -2
  17. data/lib/roda/plugins/hash_public.rb +2 -2
  18. data/lib/roda/plugins/hmac_paths.rb +27 -29
  19. data/lib/roda/plugins/host_routing.rb +29 -4
  20. data/lib/roda/plugins/invalid_request_body.rb +2 -0
  21. data/lib/roda/plugins/json_parser.rb +20 -2
  22. data/lib/roda/plugins/mail_processor.rb +2 -2
  23. data/lib/roda/plugins/mailer.rb +4 -4
  24. data/lib/roda/plugins/optimized_segment_matchers.rb +13 -6
  25. data/lib/roda/plugins/optimized_string_matchers.rb +6 -3
  26. data/lib/roda/plugins/params_capturing.rb +16 -7
  27. data/lib/roda/plugins/params_capturing_restore.rb +72 -0
  28. data/lib/roda/plugins/part.rb +4 -4
  29. data/lib/roda/plugins/pass.rb +4 -1
  30. data/lib/roda/plugins/path.rb +6 -6
  31. data/lib/roda/plugins/path_matchers.rb +5 -1
  32. data/lib/roda/plugins/public.rb +4 -4
  33. data/lib/roda/plugins/render.rb +24 -12
  34. data/lib/roda/plugins/render_coverage.rb +7 -6
  35. data/lib/roda/plugins/render_each.rb +2 -2
  36. data/lib/roda/plugins/route_csrf.rb +35 -3
  37. data/lib/roda/plugins/run_append_slash.rb +8 -4
  38. data/lib/roda/plugins/sec_fetch_site_csrf.rb +5 -2
  39. data/lib/roda/plugins/sessions.rb +2 -2
  40. data/lib/roda/plugins/shape_friendly.rb +4 -4
  41. data/lib/roda/plugins/static.rb +3 -0
  42. data/lib/roda/plugins/type_routing.rb +1 -1
  43. data/lib/roda/plugins/url_escape.rb +2 -2
  44. data/lib/roda/plugins/view_options.rb +3 -5
  45. data/lib/roda/plugins.rb +4 -4
  46. data/lib/roda/request.rb +25 -9
  47. data/lib/roda/version.rb +1 -1
  48. data/lib/roda.rb +7 -7
  49. metadata +2 -1
@@ -54,14 +54,20 @@ class Roda
54
54
  # Also note that this plugin will not work correctly if you are using
55
55
  # the symbol_matchers plugin with custom symbol matching and are using
56
56
  # symbols that capture multiple values or no values.
57
+ #
58
+ # If you are using the pass or break plugin along with this plugin,
59
+ # it is recommended you load the params_capturing_restore plugin, as that
60
+ # allows +r.pass+ or +break+ inside param capturing blocking to work
61
+ # correctly.
57
62
  module ParamsCapturing
58
63
  REQUEST_INSTANCE_VARIABLES = [:@_params_captures].freeze
59
64
 
60
65
  module RequestMethods
61
66
  # Lazily initialize captures entry when params is called.
62
67
  def params
68
+ return @params if @params
63
69
  ret = super
64
- ret['captures'] ||= []
70
+ ret['captures'] = []
65
71
  ret
66
72
  end
67
73
 
@@ -100,16 +106,19 @@ class Roda
100
106
  end
101
107
 
102
108
  super do |*a|
103
- if pc
104
- @_params_captures = nil
105
- pc.zip(a).each do |k,v|
106
- params[k] = v
107
- end
108
- end
109
+ _set_params_captures(a) if pc
109
110
  params['captures'].concat(a)
110
111
  yield(*a)
111
112
  end
112
113
  end
114
+
115
+ # Set the captures into the parameters.
116
+ def _set_params_captures(a)
117
+ @_params_captures.zip(a).each do |k,v|
118
+ params[k] = v
119
+ end
120
+ @_params_captures = nil
121
+ end
113
122
  end
114
123
  end
115
124
 
@@ -0,0 +1,72 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The params_capturing_restore plugin allows the params_capturing plugin
7
+ # to work with the use of +r.pass+ (pass plugin) or +break+ (break plugin)
8
+ # inside a block that uses param capturing, restoring the previous state of
9
+ # the params.
10
+ #
11
+ # This is not done by the params_capturing plugin as it requires significant
12
+ # extra work for every param capturing block, and most users of the
13
+ # params_capturing plugin are not using the pass or break plugins.
14
+ module ParamsCapturingRestore
15
+ REQUEST_INSTANCE_VARIABLES = [:@_params_captures_restore].freeze
16
+
17
+ ABSENT = Object.new.freeze
18
+ private_constant :ABSENT
19
+
20
+ def self.load_dependencies(app)
21
+ app.plugin :params_capturing
22
+ end
23
+
24
+ module RequestMethods
25
+ private
26
+
27
+ # Restore previous state of the params before capturing when
28
+ # the block exits.
29
+ def if_match(args)
30
+ params = self.params
31
+ num_captures = params['captures'].length
32
+ old_params = nil
33
+
34
+ super do |*a|
35
+ old_params = @_params_captures_restore
36
+ @_params_captures_restore = nil
37
+ yield(*a)
38
+ end
39
+ ensure
40
+ if old_params
41
+ old_params.each_slice(2) do |k, v|
42
+ if v == ABSENT
43
+ params.delete(k)
44
+ else
45
+ params[k] = v
46
+ end
47
+ end
48
+ end
49
+
50
+ captures = params['captures']
51
+ if num_captures != captures.length
52
+ captures.slice!(num_captures, 1000000)
53
+ end
54
+ end
55
+
56
+ # Record the original value of the params before calling super
57
+ # to update the params.
58
+ def _set_params_captures(a)
59
+ restore = @_params_captures_restore = []
60
+ @_params_captures.zip(a).each do |k,v|
61
+ restore << k << params.fetch(k, ABSENT)
62
+ end
63
+
64
+ super
65
+ end
66
+ end
67
+ end
68
+
69
+ register_plugin(:params_capturing_restore, ParamsCapturingRestore)
70
+ end
71
+ end
72
+
@@ -44,19 +44,19 @@ class Roda
44
44
  render(template, :locals=>locals, &block)
45
45
  end
46
46
  RUBY
47
- # :nocov:
47
+ # simplecov:disable
48
48
  else
49
49
  def part(template, locals=OPTS, &block)
50
50
  render(template, :locals=>locals, &block)
51
51
  end
52
52
  end
53
- # :nocov:
53
+ # simplecov:enable
54
54
  end
55
55
 
56
56
  module AssumeFixedLocalsInstanceMethods
57
- # :nocov:
57
+ # simplecov:disable
58
58
  if RUBY_VERSION >= '3.0'
59
- # :nocov:
59
+ # simplecov:enable
60
60
  class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
61
61
  def part(template, ...)
62
62
  if optimized_method = _optimized_render_method_for_locals(template, OPTS)
@@ -29,7 +29,10 @@ class Roda
29
29
 
30
30
  # Handle passing inside the match block.
31
31
  def always
32
- catch(:pass){super}
32
+ rp = @remaining_path
33
+ ret = catch(:pass){super}
34
+ @remaining_path = rp
35
+ ret
33
36
  end
34
37
 
35
38
  # Handle passing inside the match block.
@@ -169,17 +169,17 @@ class Roda
169
169
  # Allow calling private _method to get path
170
170
  relative_path(request.script_name.to_s + send(_meth, *a, &blk))
171
171
  end
172
- # :nocov:
172
+ # simplecov:disable
173
173
  ruby2_keywords(meth) if respond_to?(:ruby2_keywords, true)
174
- # :nocov:
174
+ # simplecov:enable
175
175
  elsif add_script_name
176
176
  define_method(meth) do |*a, &blk|
177
177
  # Allow calling private _method to get path
178
178
  request.script_name.to_s + send(_meth, *a, &blk)
179
179
  end
180
- # :nocov:
180
+ # simplecov:disable
181
181
  ruby2_keywords(meth) if respond_to?(:ruby2_keywords, true)
182
- # :nocov:
182
+ # simplecov:enable
183
183
  else
184
184
  define_method(meth, &block)
185
185
  end
@@ -198,9 +198,9 @@ class Roda
198
198
  end
199
199
 
200
200
  define_method(url_meth, &url_block)
201
- # :nocov:
201
+ # simplecov:disable
202
202
  ruby2_keywords(url_meth) if respond_to?(:ruby2_keywords, true)
203
- # :nocov:
203
+ # simplecov:enable
204
204
  end
205
205
 
206
206
  nil
@@ -29,12 +29,16 @@ class Roda
29
29
  # # Matches '/foo.bar', yielding 'foo'
30
30
  # # Does not match bar.foo
31
31
  # end
32
+ #
33
+ # Note that all of these matchers treat a given string as regexp source,
34
+ # so if you are including regexp metacharacters but want a literal string
35
+ # match, use +Regexp.escape+.
32
36
  module PathMatchers
33
37
  module RequestMethods
34
38
  # Match when the current segment ends with the given extension.
35
39
  # request path end with the extension.
36
40
  def match_extension(ext)
37
- match_suffix(".#{ext}")
41
+ match_suffix("\\.#{ext}")
38
42
  end
39
43
 
40
44
  # Match when the current path segment starts with the given prefix.
@@ -46,10 +46,10 @@ class Roda
46
46
  ENCODING_MAP = {:zstd=>'zstd', :brotli=>'br', :gzip=>'gzip'}.freeze
47
47
  ENCODING_EXTENSIONS = {'br'=>'.br', 'gzip'=>'.gz', 'zstd'=>'.zst'}.freeze
48
48
 
49
- # :nocov:
49
+ # simplecov:disable
50
50
  PARSER = defined?(::URI::RFC2396_PARSER) ? ::URI::RFC2396_PARSER : ::URI::DEFAULT_PARSER
51
51
  MATCH_METHOD = RUBY_VERSION >= '2.4' ? :match? : :match
52
- # :nocov:
52
+ # simplecov:enable
53
53
 
54
54
  # Use options given to setup a Rack::File instance for serving files. Options:
55
55
  # :brotli :: Whether to serve already brotli-compressed files with a .br extension
@@ -114,9 +114,9 @@ class Roda
114
114
  def public_file_readable?(path)
115
115
  ::File.file?(path) && ::File.readable?(path)
116
116
  rescue SystemCallError
117
- # :nocov:
117
+ # simplecov:disable
118
118
  false
119
- # :nocov:
119
+ # simplecov:enable
120
120
  end
121
121
 
122
122
  def public_serve_with(server)
@@ -46,8 +46,11 @@ class Roda
46
46
  #
47
47
  # The following plugin options are supported:
48
48
  #
49
- # :allowed_paths :: Set the template paths to allow. Attempts to render paths outside
50
- # of these paths will raise an error. Defaults to the +:views+ directory.
49
+ # :allowed_paths :: Set the template directories to allow. Attempts to render paths outside
50
+ # of these directories will raise an error. Defaults to the +:views+ option,
51
+ # if it is a directory. For backwards compatibility, if a non-directory is
52
+ # given, it will be used as a prefix match. Roda 4 will raise if a non-directory
53
+ # is given as an allowed path and +:check_paths+ is true.
51
54
  # :assume_fixed_locals :: Set if you are sure all templates in your application use fixed locals
52
55
  # to allow for additional optimization. This is ignored unless both
53
56
  # compiled methods and fixed locals are not supported.
@@ -298,24 +301,24 @@ class Roda
298
301
  def self.tilt_template_fixed_locals?(template)
299
302
  template.fixed_locals?
300
303
  end
301
- # :nocov:
304
+ # simplecov:disable
302
305
  else
303
306
  def self.tilt_template_fixed_locals?(template)
304
307
  false
305
308
  end
306
309
  end
307
- # :nocov:
310
+ # simplecov:enable
308
311
 
309
312
  if compiled_method_arity == -2
310
313
  def self.tilt_template_compiled_method(template, locals_keys, scope_class)
311
314
  template.send(:compiled_method, locals_keys, scope_class)
312
315
  end
313
- # :nocov:
316
+ # simplecov:disable
314
317
  else
315
318
  def self.tilt_template_compiled_method(template, locals_keys, scope_class)
316
319
  template.send(:compiled_method, locals_keys)
317
320
  end
318
- # :nocov:
321
+ # simplecov:enable
319
322
  end
320
323
 
321
324
  # Setup default rendering options. See Render for details.
@@ -331,9 +334,18 @@ class Roda
331
334
  opts = app.opts[:render]
332
335
  opts[:engine] = (opts[:engine] || "erb").dup.freeze
333
336
  opts[:views] = app.expand_path(opts[:views]||"views").freeze
334
- opts[:allowed_paths] ||= [opts[:views]].freeze
335
- opts[:allowed_paths] = opts[:allowed_paths].map{|f| app.expand_path(f, nil)}.uniq.freeze
336
337
  opts[:check_paths] = true unless opts.has_key?(:check_paths)
338
+ opts[:allowed_paths] ||= [opts[:views]]
339
+ opts[:allowed_paths] = opts[:allowed_paths].map do |f|
340
+ path = app.expand_path(f, nil)
341
+ if File.directory?(path) || f == opts[:views]
342
+ path << "/"
343
+ elsif opts[:check_paths]
344
+ # RODA4: raise
345
+ RodaPlugins.warn("allowed path #{f.inspect} is not a directory in the file system. For backwards compatibility, will allow prefix matches, but this could result in security issues. Roda 4 will raise if an allowed path is not a directory.")
346
+ end
347
+ path.freeze
348
+ end.uniq.freeze
337
349
  opts[:assume_fixed_locals] &&= FIXED_LOCALS_COMPILED_METHOD_SUPPORT
338
350
 
339
351
  unless opts.has_key?(:check_template_mtime)
@@ -763,7 +775,7 @@ class Roda
763
775
  end
764
776
  end
765
777
  RUBY
766
- # :nocov:
778
+ # simplecov:disable
767
779
  elsif RUBY_VERSION >= '2'
768
780
  class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
769
781
  def _call_optimized_template_method((meth, fixed_locals), locals, &block)
@@ -787,7 +799,7 @@ class Roda
787
799
  send(meth, locals, &block)
788
800
  end
789
801
  end
790
- # :nocov:
802
+ # simplecov:enable
791
803
  else
792
804
  def _cached_template_method(_)
793
805
  nil
@@ -981,9 +993,9 @@ class Roda
981
993
  end
982
994
 
983
995
  module AssumeFixedLocalsInstanceMethods
984
- # :nocov:
996
+ # simplecov:disable
985
997
  if RUBY_VERSION >= '3.0'
986
- # :nocov:
998
+ # simplecov:enable
987
999
  class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
988
1000
  def _call_optimized_template_method((meth,_), locals, &block)
989
1001
  send(meth, **locals, &block)
@@ -1,9 +1,9 @@
1
1
  # frozen-string-literal: true
2
2
 
3
3
  require 'tilt'
4
- # :nocov:
4
+ # simplecov:disable
5
5
  raise 'Tilt version does not support coverable templates' unless Tilt::Template.method_defined?(:compiled_path=)
6
- # :nocov:
6
+ # simplecov:enable
7
7
 
8
8
  #
9
9
  class Roda
@@ -55,8 +55,9 @@ class Roda
55
55
  path = File.expand_path(opts[:path])
56
56
  compiled_path = nil
57
57
  (self.opts[:render_coverage_strip_paths] || render_opts[:allowed_paths]).each do |dir|
58
- if path.start_with?(dir + '/')
59
- compiled_path = File.join(self.opts[:render_coverage_dir], path[dir.length+1, 10000000].tr('/', '-'))
58
+ dir += "/" unless dir.end_with?("/")
59
+ if path.start_with?(dir)
60
+ compiled_path = File.join(self.opts[:render_coverage_dir], path[dir.length, 10000000].tr('/', '-'))
60
61
  break
61
62
  end
62
63
  end
@@ -76,9 +77,9 @@ class Roda
76
77
  template = super
77
78
 
78
79
  # Set compiled path for template when using older tilt versions.
79
- # :nocov:
80
+ # simplecov:disable
80
81
  template.compiled_path = compiled_path if compiled_path
81
- # :nocov:
82
+ # simplecov:enable
82
83
 
83
84
  template
84
85
  end
@@ -171,12 +171,12 @@ class Roda
171
171
 
172
172
  File.basename(template).sub(/\..+\z/, '').to_sym
173
173
  end
174
- # :nocov:
174
+ # simplecov:disable
175
175
  else
176
176
  def render_each_default_local(template)
177
177
  File.basename(template.to_s).sub(/\..+\z/, '').to_sym
178
178
  end
179
- # :nocov:
179
+ # simplecov:enable
180
180
  end
181
181
 
182
182
  if Render::COMPILED_METHOD_SUPPORT
@@ -57,13 +57,18 @@ class Roda
57
57
  # :csrf_failure :: The action to taken if a request fails the CSRF check (default: :raise). Options:
58
58
  # :raise :: raise a Roda::RodaPlugins::RouteCsrf::InvalidToken exception
59
59
  # :empty_403 :: return a blank 403 page (rack_csrf's default behavior)
60
- # :clear_session :: Clear the current session
60
+ # :clear_session :: Clear the current session (deprecated)
61
61
  # Proc :: Treated as a routing block, called with request object
62
62
  # :check_header :: Whether the HTTP header should be checked for the token value (default: false).
63
63
  # If true, checks the HTTP header after checking for the form input parameter.
64
64
  # If :only, only checks the HTTP header and doesn't check the form input parameter.
65
65
  # :check_request_methods :: Which request methods require CSRF protection
66
66
  # (default: <tt>['POST', 'DELETE', 'PATCH', 'PUT']</tt>)
67
+ # :exempt_request_methods :: If given, specifies which request methods do not require CSRF protection.
68
+ # It is recommended to set this so an allow list approach is used, as
69
+ # :check_request_methods is the equivalent of a deny list
70
+ # (recommended: <tt>['GET', 'HEAD', 'OPTIONS', 'QUERY']</tt>, which
71
+ # will be the default in Roda 4)
67
72
  # :upgrade_from_rack_csrf_key :: If provided, the session key that should be checked for the
68
73
  # rack_csrf raw token. If the session key is present, the value
69
74
  # will be checked against the submitted token, and if it matches,
@@ -155,6 +160,19 @@ class Roda
155
160
  #
156
161
  # By including random data in the HMAC for all tokens, different tokens are generated
157
162
  # each time, mitigating compression ratio attacks such as BREACH.
163
+ #
164
+ # == REQUEST_METHOD modifications
165
+ #
166
+ # It's possible to use Rack middleware that modify REQUEST_METHOD in the env hash,
167
+ # such that it does not reflect that actual request method. One example of this is
168
+ # Rack::MethodOverride. Using such middleware is generally bad from a security perspective,
169
+ # but if you want to do so anyway, you need to override the request method that the
170
+ # route_csrf plugin will use so it reflects the actual request method. If you are using
171
+ # Rack::MethodOverride, you can use:
172
+ #
173
+ # def csrf_request_method
174
+ # env['rack.methodoverride.original_method'] || super
175
+ # end
158
176
  module RouteCsrf
159
177
  # Default CSRF option values
160
178
  DEFAULTS = {
@@ -165,6 +183,7 @@ class Roda
165
183
  :require_request_specific_tokens => true,
166
184
  :csrf_failure => :raise,
167
185
  :check_header => false,
186
+ # RODA4: Remove and set :exempt_request_methods by default
168
187
  :check_request_methods => %w'POST DELETE PATCH PUT'.freeze.each(&:freeze)
169
188
  }.freeze
170
189
 
@@ -186,6 +205,9 @@ class Roda
186
205
  options[:csrf_failure] = :csrf_failure_method
187
206
  app.define_roda_method(:_roda_route_csrf_failure, 1, &app.send(:convert_route_block, block))
188
207
  end
208
+ if opts[:csrf_failure] == :clear_session
209
+ RodaPlugins.warn "Passing :clear_session as the :csrf_failure option to the route_csrf plugin is deprecated"
210
+ end
189
211
  options[:env_header] = "HTTP_#{options[:header].to_s.tr('-', '_').upcase}".freeze
190
212
  options.freeze
191
213
  end
@@ -212,6 +234,7 @@ class Roda
212
234
  headers[RodaResponseHeaders::CONTENT_LENGTH] ='0'
213
235
  throw :halt, @_response.finish_with_body([])
214
236
  when :clear_session
237
+ RodaPlugins.warn "Passing :clear_session as the :csrf_failure option to check_csrf! is deprecated"
215
238
  session.clear
216
239
  when :csrf_failure_method
217
240
  @_request.on{_roda_route_csrf_failure(@_request)}
@@ -297,13 +320,22 @@ class Roda
297
320
 
298
321
  private
299
322
 
323
+ # The request method to use to determine whether to perform a CSRF check.
324
+ # defaults to REQUEST_METHOD in the environment, can be overridden if
325
+ # that isn't the actual request method.
326
+ def csrf_request_method
327
+ @_request.env['REQUEST_METHOD']
328
+ end
329
+
300
330
  # Returns error message string if the CSRF token is not valid.
301
331
  # Returns nil if the CSRF token is valid.
302
332
  def csrf_invalid_message(opts)
303
333
  opts = opts.empty? ? csrf_options : csrf_options.merge(opts)
304
- method = request.request_method
334
+ method = csrf_request_method
305
335
 
306
- unless opts[:check_request_methods].include?(method)
336
+ if exempt = opts[:exempt_request_methods]
337
+ return if exempt.include?(method)
338
+ elsif !opts[:check_request_methods].include?(method)
307
339
  return
308
340
  end
309
341
 
@@ -22,8 +22,11 @@ class Roda
22
22
  # # GET /a/ => App gets "/" as PATH_INFO
23
23
  module RunAppendSlash
24
24
  # Set plugin specific options. Options:
25
- # :use_redirects :: Whether to issue 302 redirects when appending the
26
- # trailing slash.
25
+ # :use_redirects :: Whether to issue a redirect to the path with the slash
26
+ # appended, instead of calling the app directly with the
27
+ # trailing slash. Note that if the current path starts
28
+ # with +//+, it will call the app directly instead of
29
+ # redirecting.
27
30
  def self.configure(app, opts=OPTS)
28
31
  app.opts[:run_append_slash_redirect] = !!opts[:use_redirects]
29
32
  end
@@ -32,10 +35,11 @@ class Roda
32
35
  # Calls the given rack app. If the path matches the root of the app but
33
36
  # does not contain a trailing slash, a trailing slash is appended to the
34
37
  # path internally, or a redirect is issued when configured with
35
- # <tt>use_redirects: true</tt>.
38
+ # <tt>use_redirects: true</tt>. If the path starts with +//+,
39
+ # the redirect approach will not be used.
36
40
  def run(*)
37
41
  if @remaining_path.empty?
38
- if scope.opts[:run_append_slash_redirect]
42
+ if scope.opts[:run_append_slash_redirect] && !(path = self.path).start_with?('//')
39
43
  redirect("#{path}/")
40
44
  else
41
45
  @remaining_path += '/'
@@ -54,7 +54,7 @@ class Roda
54
54
  # :raise :: raise a Roda::RodaPlugins::SecFetchSiteCsrf::CsrfFailure
55
55
  # exception
56
56
  # :empty_403 :: return a blank 403 page
57
- # :clear_session :: clear the current session
57
+ # :clear_session :: clear the current session (deprecated)
58
58
  #
59
59
  # The plugin also supports a block, in which case failures will call the block
60
60
  # as a routing block (the block should accept the request object).
@@ -83,8 +83,10 @@ class Roda
83
83
  end
84
84
 
85
85
  case options[:csrf_failure]
86
- when :raise, :empty_403, :clear_session, :method
86
+ when :raise, :empty_403, :method
87
87
  # nothing
88
+ when :clear_session
89
+ RodaPlugins.warn "Passing :clear_session as the :csrf_failure option to the sec_fetch_site_csrf plugin is deprecated"
88
90
  else
89
91
  raise RodaError, "Unsupported :csrf_failure plugin option: #{options[:csrf_failure].inspect}"
90
92
  end
@@ -118,6 +120,7 @@ class Roda
118
120
  headers[RodaResponseHeaders::CONTENT_LENGTH] ='0'
119
121
  throw :halt, @_response.finish_with_body([])
120
122
  when :clear_session
123
+ # RODA4: Remove
121
124
  session.clear
122
125
  else # when :method
123
126
  @_request.on{_roda_sec_fetch_site_csrf_failure(@_request)}
@@ -5,9 +5,9 @@ require 'openssl'
5
5
  begin
6
6
  OpenSSL::Cipher.new("aes-256-ctr")
7
7
  rescue OpenSSL::Cipher::CipherError
8
- # :nocov:
8
+ # simplecov:disable
9
9
  raise LoadError, "Roda sessions plugin requires the aes-256-ctr cipher"
10
- # :nocov:
10
+ # simplecov:enable
11
11
  end
12
12
 
13
13
  require 'json'
@@ -90,9 +90,9 @@ class Roda
90
90
  end
91
91
  nil
92
92
  end
93
- # :nocov:
93
+ # simplecov:disable
94
94
  ruby2_keywords(:plugin) if respond_to?(:ruby2_keywords, true)
95
- # :nocov:
95
+ # simplecov:enable
96
96
 
97
97
  private
98
98
 
@@ -132,9 +132,9 @@ class Roda
132
132
  end
133
133
  end
134
134
 
135
- # :nocov:
135
+ # simplecov:disable
136
136
  if RUBY_VERSION >= '4.0'
137
- # :nocov:
137
+ # simplecov:enable
138
138
  module InstanceMethods
139
139
  private
140
140
 
@@ -21,6 +21,9 @@ class Roda
21
21
  # uses a separate option for it.
22
22
  #
23
23
  # Users of this plugin may want to consider using the public plugin instead.
24
+ # As this only loads a middleware, it does not integrate with other Roda
25
+ # security features, such as the content_security_policy and host_authorization
26
+ # plugins.
24
27
  #
25
28
  # Examples:
26
29
  #
@@ -198,11 +198,11 @@ class Roda
198
198
  # The response type indicated by the Accept request header.
199
199
  def accept_response_type
200
200
  mimes = @scope.opts[:type_routing][:mimes]
201
+ response[RodaResponseHeaders::VARY] = (vary = response[RodaResponseHeaders::VARY]) ? "#{vary}, Accept" : 'Accept'
201
202
 
202
203
  @env['HTTP_ACCEPT'].to_s.split(/\s*,\s*/).map do |part|
203
204
  mime, _= part.split(/\s*;\s*/, 2)
204
205
  if sym = mimes[mime]
205
- response[RodaResponseHeaders::VARY] = (vary = response[RodaResponseHeaders::VARY]) ? "#{vary}, Accept" : 'Accept'
206
206
  return sym
207
207
  end
208
208
  end
@@ -19,7 +19,7 @@ class Roda
19
19
  if RUBY_VERSION >= '2'
20
20
  define_method(:url_escape, Rack::Utils.instance_method(:escape))
21
21
  define_method(:url_unescape, Rack::Utils.instance_method(:unescape))
22
- # :nocov:
22
+ # simplecov:disable
23
23
  else
24
24
  def url_escape(v)
25
25
  Rack::Utils.escape(v)
@@ -29,7 +29,7 @@ class Roda
29
29
  Rack::Utils.unescape(v)
30
30
  end
31
31
  end
32
- # :nocov:
32
+ # simplecov:enable
33
33
  end
34
34
  end
35
35
 
@@ -158,13 +158,11 @@ class Roda
158
158
  # template isn't a layout template, merge the options
159
159
  # and locals into the returned hash.
160
160
  def parse_template_opts(template, opts)
161
- t_opts = super
162
-
163
- if !t_opts[:_is_layout] && (v_opts = @_view_options)
164
- t_opts.merge!(v_opts)
161
+ if (v_opts = @_view_options) && (!template.is_a?(Hash) || !template[:_is_layout])
162
+ opts = v_opts.merge(opts)
165
163
  end
166
164
 
167
- t_opts
165
+ super
168
166
  end
169
167
 
170
168
  # If layout options or locals have been set,
data/lib/roda/plugins.rb CHANGED
@@ -43,11 +43,11 @@ class Roda
43
43
  # Deprecate the constant with the given name in the given module,
44
44
  # if the ruby version supports it.
45
45
  def self.deprecate_constant(mod, name)
46
- # :nocov:
46
+ # simplecov:disable
47
47
  if RUBY_VERSION >= '2.3'
48
48
  mod.deprecate_constant(name)
49
49
  end
50
- # :nocov:
50
+ # simplecov:enable
51
51
  end
52
52
 
53
53
  if RUBY_VERSION >= '3.3'
@@ -57,12 +57,12 @@ class Roda
57
57
  mod.set_temporary_name(yield)
58
58
  mod
59
59
  end
60
- # :nocov:
60
+ # simplecov:disable
61
61
  else
62
62
  def self.set_temp_name(mod)
63
63
  mod
64
64
  end
65
65
  end
66
- # :nocov:
66
+ # simplecov:enable
67
67
  end
68
68
  end