roda 3.99.0 → 3.100.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: 26619fe3a0b256c2615c6be17f215919c73a804609f536a3ff9f8329f14e7f9b
4
- data.tar.gz: 550f978b06bf82356bcbefedc9d00929073549c02cb118362212527f52a6336d
3
+ metadata.gz: 40eb39afa6860fdb8612711cf75ce0f5dfcb73fa3af2837fd2d2a0a0f2a2739f
4
+ data.tar.gz: 0f3b1d45dd9d78c0840e705d61344abcd11312b1d8d0e1cdf5c2cd249ba50bc9
5
5
  SHA512:
6
- metadata.gz: 244665571cdb2b400ced6494ab8210fbf12ab46b2fd799f35fd7373f675606c975b8fc7cc6a279532c8b76d9a981a339528b41567d022d353f763ee8028b4225
7
- data.tar.gz: a30c5c61b21ff9c65c0cfdad17a1011039d44b57c498e7e833fec859a7ec6876d593eb53b950c56d6b7a8858f11e6e2edf6d3798db2288ccfa5b965be019bbe5
6
+ metadata.gz: 3640eeb3784285b94ef078c79eccdd69feebfcab19ee0ec0d6858e7959343511af1c173d590043221484ea99b3dd7ef7a2345a1dc26808e683b5781f6f6a4626
7
+ data.tar.gz: 4c739d613fcff7aa56f5dff3c6d83c26ace1a040aebb2f6ed4808086814f93b4f7858b4a968188fecc7e09eb0b7224ff62e44dcd32db6d1e9f22a09fa74ae029
@@ -0,0 +1,131 @@
1
+ # frozen-string-literal: true
2
+
3
+ class Roda
4
+ module RodaPlugins
5
+ # The sec_fetch_site plugin allows for CSRF protection using the
6
+ # Sec-Fetch-Site header added in modern browsers. It allows for CSRF
7
+ # protection without the use of CSRF tokens, which can simplify
8
+ # form creation.
9
+ #
10
+ # The protection offered by the sec_fetch_site plugin is weaker than
11
+ # the protection offered by the route_csrf plugin with default settings,
12
+ # since it doesn't support per-request tokens. Be aware you are trading
13
+ # security for simplicity when using the sec_fetch_site plugin instead
14
+ # of the route_csrf plugin. Other caveats in using the sec_fetch_site
15
+ # plugin:
16
+ #
17
+ # * Not all browsers set the Sec-Fetch-Site header. Some browsers
18
+ # didn't start setting the header until 2023. In these cases, you
19
+ # need to decide how to handle the request. The default is to deny
20
+ # the request, though you can use the :allow_missing option to allow
21
+ # it.
22
+ #
23
+ # * Sec-Fetch-Site headers are not set for http requests, only https
24
+ # requests, so this doesn't offer protection for http requests.
25
+ #
26
+ # * It isn't possible to share a CSRF secret between applications in
27
+ # different origins to allow cross-site requests between the
28
+ # applications.
29
+ #
30
+ # This plugin adds the +check_sec_fetch_site!+ method to the routing
31
+ # block scope. You should call this method at the appropriate place
32
+ # in the routing tree to enforce the CSRF protection. The method can
33
+ # accept a block to override the :csrf_failure plugin option behavior
34
+ # on a per-call basis.
35
+ #
36
+ # When loading the plugin with no options:
37
+ #
38
+ # plugin :sec_fetch_site_csrf
39
+ #
40
+ # Only same-origin requests are allowed by default.
41
+ #
42
+ # This plugin supports the following options:
43
+ #
44
+ # :allow_missing :: Whether to allow requests lacking the Sec-Fetch-Site
45
+ # header (false by default).
46
+ # :allow_none :: Whether to allow requests where Sec-Fetch-Value is none
47
+ # (false by default).
48
+ # :allow_same_site :: Whether to allow requests where Sec-Fetch-Value is
49
+ # same-site (false by default)
50
+ # :check_request_methods :: Which request methods require CSRF protection
51
+ # (default: <tt>['POST', 'DELETE', 'PATCH', 'PUT']</tt>)
52
+ # :csrf_failure :: The action to taken if a request does not have a valid header
53
+ # (default: :raise). Options:
54
+ # :raise :: raise a Roda::RodaPlugins::SecFetchSiteCsrf::CsrfFailure
55
+ # exception
56
+ # :empty_403 :: return a blank 403 page
57
+ # :clear_session :: clear the current session
58
+ #
59
+ # The plugin also supports a block, in which case failures will call the block
60
+ # as a routing block (the block should accept the request object).
61
+ module SecFetchSiteCsrf
62
+ DEFAULTS = {
63
+ :csrf_failure => :raise,
64
+ :check_request_methods => %w'POST DELETE PATCH PUT'.freeze.each(&:freeze)
65
+ }.freeze
66
+
67
+ # Exception class raised when :csrf_failure option is :raise and
68
+ # the Sec-Fetch-Site header is not considered valid.
69
+ class CsrfFailure < RodaError; end
70
+
71
+ def self.configure(app, opts=OPTS, &block)
72
+ options = app.opts[:sec_fetch_site_csrf] = (app.opts[:sec_fetch_site_csrf] || DEFAULTS).merge(opts)
73
+
74
+ allowed_values = options[:allowed_values] = ["same-origin"]
75
+ allowed_values << "same-site" if opts[:allow_same_site]
76
+ allowed_values << "none" if opts[:allow_none]
77
+ allowed_values << nil if opts[:allow_missing]
78
+ allowed_values.freeze
79
+
80
+ if block
81
+ options[:csrf_failure] = :method
82
+ app.define_roda_method(:_roda_sec_fetch_site_csrf_failure, 1, &app.send(:convert_route_block, block))
83
+ end
84
+
85
+ case options[:csrf_failure]
86
+ when :raise, :empty_403, :clear_session, :method
87
+ # nothing
88
+ else
89
+ raise RodaError, "Unsupported :csrf_failure plugin option: #{options[:csrf_failure].inspect}"
90
+ end
91
+
92
+ options.freeze
93
+ end
94
+
95
+ module InstanceMethods
96
+ # Check that the Sec-Fetch-Site header is valid, if the request requires it.
97
+ # If the header is valid or the request does not require the header, return nil.
98
+ # Otherwise, if a block is given, treat it as a routing block and yield to it, and
99
+ # if a block is not given, use the plugin :csrf_failure option to determine how to
100
+ # handle it.
101
+ def check_sec_fetch_site!(&block)
102
+ plugin_opts = self.class.opts[:sec_fetch_site_csrf]
103
+ return unless plugin_opts[:check_request_methods].include?(request.request_method)
104
+
105
+ sec_fetch_site = env["HTTP_SEC_FETCH_SITE"]
106
+ return if plugin_opts[:allowed_values].include?(sec_fetch_site)
107
+
108
+ @_request.on(&block) if block
109
+
110
+ case failure_action = plugin_opts[:csrf_failure]
111
+ when :raise
112
+ raise CsrfFailure, "potential cross-site request, Sec-Fetch-Site value: #{sec_fetch_site.inspect}"
113
+ when :empty_403
114
+ @_response.status = 403
115
+ headers = @_response.headers
116
+ headers.clear
117
+ headers[RodaResponseHeaders::CONTENT_TYPE] = 'text/html'
118
+ headers[RodaResponseHeaders::CONTENT_LENGTH] ='0'
119
+ throw :halt, @_response.finish_with_body([])
120
+ when :clear_session
121
+ session.clear
122
+ else # when :method
123
+ @_request.on{_roda_sec_fetch_site_csrf_failure(@_request)}
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ register_plugin(:sec_fetch_site_csrf, SecFetchSiteCsrf)
130
+ end
131
+ 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 = 99
7
+ RodaMinorVersion = 100
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.99.0
4
+ version: 3.100.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
@@ -284,6 +284,7 @@ files:
284
284
  - lib/roda/plugins/run_append_slash.rb
285
285
  - lib/roda/plugins/run_handler.rb
286
286
  - lib/roda/plugins/run_require_slash.rb
287
+ - lib/roda/plugins/sec_fetch_site_csrf.rb
287
288
  - lib/roda/plugins/sessions.rb
288
289
  - lib/roda/plugins/shared_vars.rb
289
290
  - lib/roda/plugins/sinatra_helpers.rb
@@ -331,7 +332,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
331
332
  - !ruby/object:Gem::Version
332
333
  version: '0'
333
334
  requirements: []
334
- rubygems_version: 3.6.9
335
+ rubygems_version: 4.0.3
335
336
  specification_version: 4
336
337
  summary: Routing tree web toolkit
337
338
  test_files: []