roda 3.74.0 → 3.76.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 +4 -4
- data/CHANGELOG +12 -0
- data/doc/release_notes/3.75.0.txt +19 -0
- data/doc/release_notes/3.76.0.txt +18 -0
- data/lib/roda/plugins/break.rb +43 -0
- data/lib/roda/plugins/cookie_flags.rb +157 -0
- data/lib/roda/plugins/error_email.rb +10 -1
- data/lib/roda/plugins/error_mail.rb +13 -2
- data/lib/roda/plugins/middleware.rb +3 -0
- data/lib/roda/version.rb +1 -1
- metadata +9 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1f4eee948a9994645560f635fd228d2f46f3f466da782a476d7046b2b0b9f026
|
|
4
|
+
data.tar.gz: 4abb6bed043b264c59e17b1120771822a26292f24037a283c08003168c72e602
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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] ||
|
|
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
|
|
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
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.
|
|
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:
|
|
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.
|
|
500
|
+
rubygems_version: 3.5.3
|
|
495
501
|
signing_key:
|
|
496
502
|
specification_version: 4
|
|
497
503
|
summary: Routing tree web toolkit
|