roda 3.74.0 → 3.75.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: ae3f95331c6c3d69453feb96a7dbd3629a6d5f52bd9d1862edecab893d95a36f
4
- data.tar.gz: c5457f54b2ca5a9971f93a23db10abe3b6fd16a743e2651749ec14c480c248a3
3
+ metadata.gz: 4ebdbc5b5707ba21044b5f4c83445ff8fca6a3925488c72a5176cc662c2ad15b
4
+ data.tar.gz: ac528e50bfe1e778b5b4d32d16d12e117e87dc55b2f7cad1170b682e2f979586
5
5
  SHA512:
6
- metadata.gz: f314713b33ec4400e5ef8849c6a3e30f1b5956b428dea55a5bf45855fef4a965f9b417063182cc4412effa592a7d311715cf4541826781675ca26bd20f202ca3
7
- data.tar.gz: 735cb4cf3e5480be4462d88ccb0090001e7eca27bfe7338a923f5f190730095037ff0c77e1cbe3c875b137175bc7022b8d0611ccbf8b82426cbbaec5b7802149
6
+ metadata.gz: b7d13975d6c7705f1d7b184f27430320e1dfd5fea96e532f119f86b4c09fdb5c7e460107033d8d099c6ef8da354a514289981163d74cdb293b89074299a57eda
7
+ data.tar.gz: d16c1b3a2edd401a73b104bf9458fcb865e441f4de649dcfcb8144eb3102abcd6abee741ecbbbf1cad3b13d8efca7260d7785b7e5a2505f29541ad001de9b1e4
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ = 3.75.0 (2023-12-14)
2
+
3
+ * Add cookie_flags plugin, for overriding, warning, or raising for incorrect cookie flags (jeremyevans)
4
+
1
5
  = 3.74.0 (2023-11-13)
2
6
 
3
7
  * 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,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
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 = 75
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.75.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: 2023-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -248,6 +248,7 @@ 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
251
252
  - doc/release_notes/3.8.0.txt
252
253
  - doc/release_notes/3.9.0.txt
253
254
  files:
@@ -329,6 +330,7 @@ files:
329
330
  - doc/release_notes/3.72.0.txt
330
331
  - doc/release_notes/3.73.0.txt
331
332
  - doc/release_notes/3.74.0.txt
333
+ - doc/release_notes/3.75.0.txt
332
334
  - doc/release_notes/3.8.0.txt
333
335
  - doc/release_notes/3.9.0.txt
334
336
  - lib/roda.rb
@@ -357,6 +359,7 @@ files:
357
359
  - lib/roda/plugins/common_logger.rb
358
360
  - lib/roda/plugins/content_for.rb
359
361
  - lib/roda/plugins/content_security_policy.rb
362
+ - lib/roda/plugins/cookie_flags.rb
360
363
  - lib/roda/plugins/cookies.rb
361
364
  - lib/roda/plugins/csrf.rb
362
365
  - lib/roda/plugins/custom_block_results.rb