roda 3.74.0 → 3.76.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: ae3f95331c6c3d69453feb96a7dbd3629a6d5f52bd9d1862edecab893d95a36f
4
- data.tar.gz: c5457f54b2ca5a9971f93a23db10abe3b6fd16a743e2651749ec14c480c248a3
3
+ metadata.gz: 1f4eee948a9994645560f635fd228d2f46f3f466da782a476d7046b2b0b9f026
4
+ data.tar.gz: 4abb6bed043b264c59e17b1120771822a26292f24037a283c08003168c72e602
5
5
  SHA512:
6
- metadata.gz: f314713b33ec4400e5ef8849c6a3e30f1b5956b428dea55a5bf45855fef4a965f9b417063182cc4412effa592a7d311715cf4541826781675ca26bd20f202ca3
7
- data.tar.gz: 735cb4cf3e5480be4462d88ccb0090001e7eca27bfe7338a923f5f190730095037ff0c77e1cbe3c875b137175bc7022b8d0611ccbf8b82426cbbaec5b7802149
6
+ metadata.gz: 112d90a74ed25ae0bb608a2e89d2f6fa757912287feb3a68d312ef4b8fd317bdb8f04eb76f529090d14a92c322d869ededa831f8db5d20bce8a66cd973a71584
7
+ data.tar.gz: 22ddb0a055b5849c6dcd3ec93426e495198434cf1e9cf9adbae1c22ece9b788c0ea7332122b9ad525954e2b946b0eef873de3b0466fe5b844407dec6c9844c84
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ = 3.76.0 (2024-01-12)
2
+
3
+ * Support :filter plugin option in error_mail and error_email for filtering parameters, environment variables, and session values (jeremyevans) (#346)
4
+
5
+ * Set temporary name on Ruby 3.3 in middleware plugin for middleware class created (janko) (#344)
6
+
7
+ * Add break plugin, for using break inside a routing block to return from the block and keep routing (jeremyevans)
8
+
9
+ = 3.75.0 (2023-12-14)
10
+
11
+ * Add cookie_flags plugin, for overriding, warning, or raising for incorrect cookie flags (jeremyevans)
12
+
1
13
  = 3.74.0 (2023-11-13)
2
14
 
3
15
  * Add redirect_http_to_https plugin, helping to ensure future requests from the browser are submitted via HTTPS (jeremyevans)
@@ -0,0 +1,19 @@
1
+ = New Features
2
+
3
+ * A cookie_flags plugin has been added, for overriding, warning, or
4
+ raising for incorrect cookie flags. The plugin by default checks
5
+ whether the secure, httponly, and samesite=strict flags are set.
6
+ The default behavior is to add the appropriate flags if they are
7
+ not set, and change the samesite flag to strict if it is set to
8
+ something else. You can configure the flag checking behavior
9
+ via the :httponly, :same_site, and :secure options.
10
+
11
+ You can configure the action the plugin takes via the :action option.
12
+ The default action is to modify the flags, but the :action option can
13
+ be set to :raise, :warn, or :warn_and_modify to override the behavior.
14
+
15
+ The recommended way to use the plugin is to use it during testing,
16
+ and specify action: :raise, so you can catch places where cookies
17
+ are set with the wrong flags. Then you can fix those places to
18
+ use the correct flags, which is better than relying on the plugin
19
+ at runtime in production to fix incorrect flags.
@@ -0,0 +1,18 @@
1
+ = New Features
2
+
3
+ * A break plugin has been added, allowing you to use break from
4
+ inside a routing block and continue routing after the block. This
5
+ offers the same feature as the pass plugin, but using the standard
6
+ break keyword instead of the r.pass method.
7
+
8
+ * The error_mail and error_email features now both accept a :filter
9
+ plugin option. The value should respond to call with two arguments.
10
+ The first arguments is the key, and the second is the value, and
11
+ should return a truthy value if the value should be filtered. This
12
+ will be used for filtering parameter values, ENV values, and session
13
+ values in the generated emails.
14
+
15
+ = Other Improvements
16
+
17
+ * On Ruby 3.3+, the middleware plugin sets a temporary class name for
18
+ the created middleware, based on the class name of the Roda app.
@@ -0,0 +1,43 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The break plugin supports calling break inside a match block, to
7
+ # return from the block and continue in the routing tree, restoring
8
+ # the remaining path so that future matchers operating on the path
9
+ # operate as expected.
10
+ #
11
+ # plugin :break
12
+ #
13
+ # route do |r|
14
+ # r.on "foo", :bar do |bar|
15
+ # break if bar == 'baz'
16
+ # "/foo/#{bar} (not baz)"
17
+ # end
18
+ #
19
+ # r.on "foo/baz" do
20
+ # "/foo/baz"
21
+ # end
22
+ # end
23
+ #
24
+ # This provides the same basic feature as the pass plugin, but
25
+ # uses Ruby's standard control flow primative instead of a
26
+ # separate method.
27
+ module Break
28
+ module RequestMethods
29
+ private
30
+
31
+ # Handle break inside match blocks, restoring remaining path.
32
+ def if_match(_)
33
+ rp = @remaining_path
34
+ super
35
+ ensure
36
+ @remaining_path = rp
37
+ end
38
+ end
39
+ end
40
+
41
+ register_plugin(:break, Break)
42
+ end
43
+ end
@@ -0,0 +1,157 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The cookie_flags plugin allows users to force specific cookie flags for
7
+ # all cookies set by the application. It can also be used to warn or
8
+ # raise for unexpected cookie flags.
9
+ #
10
+ # The cookie_flags plugin deals with the following cookie flags:
11
+ #
12
+ # httponly :: Disallows access to the cookie from client-side scripts.
13
+ # samesite :: Restricts to which domains the cookie is sent.
14
+ # secure :: Instructs the browser to only transmit the cookie over HTTPS.
15
+ #
16
+ # This plugin ships in secure-by-default mode, where it enforces
17
+ # secure, httponly, samesite=strict cookies. You can disable enforcing
18
+ # specific flags using the following options:
19
+ #
20
+ # :httponly :: Set to false to not enforce httponly flag.
21
+ # :same_site :: Set to symbol or string to enforce a different samesite
22
+ # setting, or false to not enforce a specific samesite setting.
23
+ # :secure :: Set to false to not enforce secure flag.
24
+ #
25
+ # For example, to enforce secure cookies and enforce samesite=lax, but not enforce
26
+ # an httponly flag:
27
+ #
28
+ # plugin :cookie_flags, httponly: false, same_site: 'lax'
29
+ #
30
+ # In general, overriding cookie flags using this plugin should be considered a
31
+ # stop-gap solution. Instead of overriding cookie flags, it's better to fix
32
+ # whatever is setting the cookie flags incorrectly. You can use the :action
33
+ # option to modify the behavior:
34
+ #
35
+ # # Issue warnings when modifying cookie flags
36
+ # plugin :cookie_flags, action: :warn_and_modify
37
+ #
38
+ # # Issue warnings for incorrect cookie flags without modifying cookie flags
39
+ # plugin :cookie_flags, action: :warn
40
+ #
41
+ # # Raise errors for incorrect cookie flags
42
+ # plugin :cookie_flags, action: :raise
43
+ #
44
+ # The recommended way to use the plugin is to use it only during testing with
45
+ # <tt>action: :raise</tt>. Then as long as you have fully covering tests, you
46
+ # can be sure the cookies set by your application use the correct flags.
47
+ #
48
+ # Note that this plugin only affects cookies set by the application, and does not
49
+ # affect cookies set by middleware the application is using.
50
+ module CookieFlags
51
+ # :nocov:
52
+ MATCH_METH = RUBY_VERSION >= '2.4' ? :match? : :match
53
+ # :nocov:
54
+ private_constant :MATCH_METH
55
+
56
+ DEFAULTS = {:secure=>true, :httponly=>true, :same_site=>'strict', :action=>:modify}.freeze
57
+ private_constant :DEFAULTS
58
+
59
+ # Error class raised for action: :raise when incorrect cookie flags are used.
60
+ class Error < RodaError
61
+ end
62
+
63
+ def self.configure(app, opts=OPTS)
64
+ previous = app.opts[:cookie_flags] || DEFAULTS
65
+ opts = app.opts[:cookie_flags] = previous.merge(opts)
66
+
67
+ case opts[:same_site]
68
+ when String, Symbol
69
+ opts[:same_site] = opts[:same_site].to_s.downcase.freeze
70
+ opts[:same_site_string] = "; samesite=#{opts[:same_site]}".freeze
71
+ opts[:secure] = true if opts[:same_site] == 'none'
72
+ end
73
+
74
+ opts.freeze
75
+ end
76
+
77
+ module InstanceMethods
78
+ private
79
+
80
+ def _handle_cookie_flags_array(cookies)
81
+ opts = self.class.opts[:cookie_flags]
82
+ needs_secure = opts[:secure]
83
+ needs_httponly = opts[:httponly]
84
+ if needs_same_site = opts[:same_site]
85
+ same_site_string = opts[:same_site_string]
86
+ same_site_regexp = /;\s*samesite\s*=\s*(\S+)\s*(?:\z|;)/i
87
+ end
88
+ action = opts[:action]
89
+
90
+ cookies.map do |cookie|
91
+ if needs_secure
92
+ add_secure = !/;\s*secure\s*(?:\z|;)/i.send(MATCH_METH, cookie)
93
+ end
94
+
95
+ if needs_httponly
96
+ add_httponly = !/;\s*httponly\s*(?:\z|;)/i.send(MATCH_METH, cookie)
97
+ end
98
+
99
+ if needs_same_site
100
+ has_same_site = same_site_regexp.match(cookie)
101
+ unless add_same_site = !has_same_site
102
+ update_same_site = needs_same_site != has_same_site[1].downcase
103
+ end
104
+ end
105
+
106
+ next cookie unless add_secure || add_httponly || add_same_site || update_same_site
107
+
108
+ case action
109
+ when :raise, :warn, :warn_and_modify
110
+ message = "Response contains cookie with unexpected flags: #{cookie.inspect}." \
111
+ "Expecting the following cookie flags: "\
112
+ "#{'secure ' if add_secure}#{'httponly ' if add_httponly}#{same_site_string[2..-1] if add_same_site || update_same_site}"
113
+
114
+ if action == :raise
115
+ raise Error, message
116
+ else
117
+ warn(message)
118
+ next cookie if action == :warn
119
+ end
120
+ end
121
+
122
+ if update_same_site
123
+ cookie = cookie.gsub(same_site_regexp, same_site_string)
124
+ else
125
+ cookie = cookie.dup
126
+ cookie << same_site_string if add_same_site
127
+ end
128
+
129
+ cookie << '; secure' if add_secure
130
+ cookie << '; httponly' if add_httponly
131
+
132
+ cookie
133
+ end
134
+ end
135
+
136
+ if Rack.release >= '3'
137
+ def _handle_cookie_flags(cookies)
138
+ cookies = [cookies] if cookies.is_a?(String)
139
+ _handle_cookie_flags_array(cookies)
140
+ end
141
+ else
142
+ def _handle_cookie_flags(cookie_string)
143
+ _handle_cookie_flags_array(cookie_string.split("\n")).join("\n")
144
+ end
145
+ end
146
+
147
+ # Handle cookie flags in response
148
+ def _roda_after_85__cookie_flags(res)
149
+ return unless res && (headers = res[1]) && (value = headers[RodaResponseHeaders::SET_COOKIE])
150
+ headers[RodaResponseHeaders::SET_COOKIE] = _handle_cookie_flags(value)
151
+ end
152
+ end
153
+ end
154
+
155
+ register_plugin(:cookie_flags, CookieFlags)
156
+ end
157
+ end
@@ -21,6 +21,9 @@ class Roda
21
21
  #
22
22
  # Options:
23
23
  #
24
+ # :filter :: Callable called with the key and value for each parameter, environment
25
+ # variable, and session value. If it returns true, the value of the
26
+ # parameter is filtered in the email.
24
27
  # :from :: The From address to use in the email (required)
25
28
  # :headers :: A hash of additional headers to use in the email (default: empty hash)
26
29
  # :host :: The SMTP server to use to send the email (default: localhost)
@@ -38,6 +41,7 @@ class Roda
38
41
  # use an error reporting service instead of this plugin.
39
42
  module ErrorEmail
40
43
  DEFAULTS = {
44
+ :filter=>lambda{|k,v| false},
41
45
  :headers=>OPTS,
42
46
  :host=>'localhost',
43
47
  # :nocov:
@@ -52,7 +56,12 @@ class Roda
52
56
  {'From'=>h[:from], 'To'=>h[:to], 'Subject'=>"#{h[:prefix]}#{subject}"}
53
57
  end,
54
58
  :body=>lambda do |s, e|
55
- format = lambda{|h| h.map{|k, v| "#{k.inspect} => #{v.inspect}"}.sort.join("\n")}
59
+ filter = s.opts[:error_email][:filter]
60
+ format = lambda do |h|
61
+ h = h.map{|k, v| "#{k.inspect} => #{filter.call(k, v) ? 'FILTERED' : v.inspect}"}
62
+ h.sort!
63
+ h.join("\n")
64
+ end
56
65
 
57
66
  begin
58
67
  params = s.request.params
@@ -21,6 +21,9 @@ class Roda
21
21
  #
22
22
  # Options:
23
23
  #
24
+ # :filter :: Callable called with the key and value for each parameter, environment
25
+ # variable, and session value. If it returns true, the value of the
26
+ # parameter is filtered in the email.
24
27
  # :from :: The From address to use in the email (required)
25
28
  # :headers :: A hash of additional headers to use in the email (default: empty hash)
26
29
  # :prefix :: A prefix to use in the email's subject line (default: no prefix)
@@ -36,9 +39,12 @@ class Roda
36
39
  # for low traffic web applications. For high traffic web applications,
37
40
  # use an error reporting service instead of this plugin.
38
41
  module ErrorMail
42
+ DEFAULT_FILTER = lambda{|k,v| false}
43
+ private_constant :DEFAULT_FILTER
44
+
39
45
  # Set default opts for plugin. See ErrorEmail module RDoc for options.
40
46
  def self.configure(app, opts=OPTS)
41
- app.opts[:error_mail] = email_opts = (app.opts[:error_mail] || OPTS).merge(opts).freeze
47
+ app.opts[:error_mail] = email_opts = (app.opts[:error_mail] || {:filter=>DEFAULT_FILTER}).merge(opts).freeze
42
48
  unless email_opts[:to] && email_opts[:from]
43
49
  raise RodaError, "must provide :to and :from options to error_mail plugin"
44
50
  end
@@ -68,8 +74,13 @@ class Roda
68
74
  e.to_s
69
75
  end
70
76
  subject = "#{email_opts[:prefix]}#{subject}"
77
+ filter = email_opts[:filter]
71
78
 
72
- format = lambda{|h| h.map{|k, v| "#{k.inspect} => #{v.inspect}"}.sort.join("\n")}
79
+ format = lambda do |h|
80
+ h = h.map{|k, v| "#{k.inspect} => #{filter.call(k, v) ? 'FILTERED' : v.inspect}"}
81
+ h.sort!
82
+ h.join("\n")
83
+ end
73
84
 
74
85
  begin
75
86
  params = request.params
@@ -134,6 +134,9 @@ class Roda
134
134
  # and store +app+ as the next middleware to call.
135
135
  def initialize(mid, app, *args, &block)
136
136
  @mid = Class.new(mid)
137
+ # :nocov:
138
+ @mid.set_temporary_name("#{mid.name}(middleware)") if mid.name && RUBY_VERSION >= "3.3"
139
+ # :nocov:
137
140
  if @mid.opts[:middleware_next_if_not_found]
138
141
  @mid.plugin(:not_found, &NEXT_PROC)
139
142
  end
data/lib/roda/version.rb CHANGED
@@ -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 = 74
7
+ RodaMinorVersion = 76
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
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.74.0
4
+ version: 3.76.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: 2023-11-13 00:00:00.000000000 Z
11
+ date: 2024-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -248,6 +248,8 @@ extra_rdoc_files:
248
248
  - doc/release_notes/3.72.0.txt
249
249
  - doc/release_notes/3.73.0.txt
250
250
  - doc/release_notes/3.74.0.txt
251
+ - doc/release_notes/3.75.0.txt
252
+ - doc/release_notes/3.76.0.txt
251
253
  - doc/release_notes/3.8.0.txt
252
254
  - doc/release_notes/3.9.0.txt
253
255
  files:
@@ -329,6 +331,8 @@ files:
329
331
  - doc/release_notes/3.72.0.txt
330
332
  - doc/release_notes/3.73.0.txt
331
333
  - doc/release_notes/3.74.0.txt
334
+ - doc/release_notes/3.75.0.txt
335
+ - doc/release_notes/3.76.0.txt
332
336
  - doc/release_notes/3.8.0.txt
333
337
  - doc/release_notes/3.9.0.txt
334
338
  - lib/roda.rb
@@ -349,6 +353,7 @@ files:
349
353
  - lib/roda/plugins/autoload_named_routes.rb
350
354
  - lib/roda/plugins/backtracking_array.rb
351
355
  - lib/roda/plugins/branch_locals.rb
356
+ - lib/roda/plugins/break.rb
352
357
  - lib/roda/plugins/caching.rb
353
358
  - lib/roda/plugins/capture_erb.rb
354
359
  - lib/roda/plugins/chunked.rb
@@ -357,6 +362,7 @@ files:
357
362
  - lib/roda/plugins/common_logger.rb
358
363
  - lib/roda/plugins/content_for.rb
359
364
  - lib/roda/plugins/content_security_policy.rb
365
+ - lib/roda/plugins/cookie_flags.rb
360
366
  - lib/roda/plugins/cookies.rb
361
367
  - lib/roda/plugins/csrf.rb
362
368
  - lib/roda/plugins/custom_block_results.rb
@@ -491,7 +497,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
491
497
  - !ruby/object:Gem::Version
492
498
  version: '0'
493
499
  requirements: []
494
- rubygems_version: 3.4.10
500
+ rubygems_version: 3.5.3
495
501
  signing_key:
496
502
  specification_version: 4
497
503
  summary: Routing tree web toolkit