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 +4 -4
- data/lib/roda/plugins/sec_fetch_site_csrf.rb +131 -0
- data/lib/roda/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 40eb39afa6860fdb8612711cf75ce0f5dfcb73fa3af2837fd2d2a0a0f2a2739f
|
|
4
|
+
data.tar.gz: 0f3b1d45dd9d78c0840e705d61344abcd11312b1d8d0e1cdf5c2cd249ba50bc9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
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.
|
|
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:
|
|
335
|
+
rubygems_version: 4.0.3
|
|
335
336
|
specification_version: 4
|
|
336
337
|
summary: Routing tree web toolkit
|
|
337
338
|
test_files: []
|