roda 3.107.0 → 3.108.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/_base64.rb +2 -2
- data/lib/roda/plugins/_optimized_matching.rb +1 -0
- data/lib/roda/plugins/all_verbs.rb +2 -2
- data/lib/roda/plugins/assets.rb +4 -4
- data/lib/roda/plugins/bearer_token.rb +2 -2
- data/lib/roda/plugins/break.rb +9 -1
- data/lib/roda/plugins/caching.rb +12 -12
- data/lib/roda/plugins/common_logger.rb +4 -4
- data/lib/roda/plugins/cookie_flags.rb +2 -2
- data/lib/roda/plugins/each_part.rb +4 -4
- data/lib/roda/plugins/error_email.rb +2 -2
- data/lib/roda/plugins/exception_page.rb +2 -2
- data/lib/roda/plugins/h.rb +4 -4
- data/lib/roda/plugins/hash_branches.rb +8 -4
- data/lib/roda/plugins/hash_paths.rb +4 -2
- data/lib/roda/plugins/hash_public.rb +2 -2
- data/lib/roda/plugins/hmac_paths.rb +27 -29
- data/lib/roda/plugins/host_routing.rb +29 -4
- data/lib/roda/plugins/invalid_request_body.rb +2 -0
- data/lib/roda/plugins/json_parser.rb +20 -2
- data/lib/roda/plugins/mail_processor.rb +2 -2
- data/lib/roda/plugins/mailer.rb +4 -4
- data/lib/roda/plugins/optimized_segment_matchers.rb +13 -6
- data/lib/roda/plugins/optimized_string_matchers.rb +6 -3
- data/lib/roda/plugins/params_capturing.rb +16 -7
- data/lib/roda/plugins/params_capturing_restore.rb +72 -0
- data/lib/roda/plugins/part.rb +4 -4
- data/lib/roda/plugins/pass.rb +4 -1
- data/lib/roda/plugins/path.rb +6 -6
- data/lib/roda/plugins/path_matchers.rb +5 -1
- data/lib/roda/plugins/public.rb +4 -4
- data/lib/roda/plugins/render.rb +24 -12
- data/lib/roda/plugins/render_coverage.rb +7 -6
- data/lib/roda/plugins/render_each.rb +2 -2
- data/lib/roda/plugins/route_csrf.rb +35 -3
- data/lib/roda/plugins/run_append_slash.rb +8 -4
- data/lib/roda/plugins/sec_fetch_site_csrf.rb +5 -2
- data/lib/roda/plugins/sessions.rb +2 -2
- data/lib/roda/plugins/shape_friendly.rb +4 -4
- data/lib/roda/plugins/static.rb +3 -0
- data/lib/roda/plugins/type_routing.rb +1 -1
- data/lib/roda/plugins/url_escape.rb +2 -2
- data/lib/roda/plugins/view_options.rb +3 -5
- data/lib/roda/plugins.rb +4 -4
- data/lib/roda/request.rb +25 -9
- data/lib/roda/version.rb +1 -1
- data/lib/roda.rb +7 -7
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 88f17808e272159aaa6d5f1239eeb3453935aa188c0933b753e5a66046212e03
|
|
4
|
+
data.tar.gz: 952920ff3e1dafeefea80acefcbd3fb5e03f0033c2ca909757c697e2acd7c955
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fdd21d3152a257f21d1cfb33562334b71de82e1f017899dae0d5e9974f9b1028824c017f82d54405d5f88bed4d53607f6c238bb21c1907492da38b3b755e1a73
|
|
7
|
+
data.tar.gz: 61ed1ac97df5919e486a82abb62ad47b45603ec3c46d230ce6aa6f7ac697cd52b45af167a597ad691ec6de5267886e6b420efc0d3117a3604ba45f613703e80d
|
data/lib/roda/plugins/_base64.rb
CHANGED
|
@@ -6,7 +6,7 @@ class Roda
|
|
|
6
6
|
# The all_verbs plugin adds methods for http verbs other than
|
|
7
7
|
# get and post. The following verbs are added, assuming
|
|
8
8
|
# rack handles them: delete, head, options, link, patch, put,
|
|
9
|
-
# trace, unlink.
|
|
9
|
+
# query, trace, unlink.
|
|
10
10
|
#
|
|
11
11
|
# These methods operate just like Roda's default get and post
|
|
12
12
|
# methods, so using them without any arguments just checks for
|
|
@@ -33,7 +33,7 @@ class Roda
|
|
|
33
33
|
# isn't documentation for the individual methods created.
|
|
34
34
|
module AllVerbs
|
|
35
35
|
module RequestMethods
|
|
36
|
-
%w'delete head options link patch put trace unlink'.each do |verb|
|
|
36
|
+
%w'delete head options link patch put query trace unlink'.each do |verb|
|
|
37
37
|
if ::Rack::Request.method_defined?("#{verb}?")
|
|
38
38
|
class_eval(<<-END, __FILE__, __LINE__+1)
|
|
39
39
|
def #{verb}(*args, &block)
|
data/lib/roda/plugins/assets.rb
CHANGED
|
@@ -659,9 +659,9 @@ class Roda
|
|
|
659
659
|
begin
|
|
660
660
|
require 'uglifier'
|
|
661
661
|
rescue => e
|
|
662
|
-
# :
|
|
662
|
+
# simplecov:disable
|
|
663
663
|
raise CompressorNotFound, "#{e.class}: #{e.message}", e.backtrace
|
|
664
|
-
# :
|
|
664
|
+
# simplecov:enable
|
|
665
665
|
end
|
|
666
666
|
|
|
667
667
|
Uglifier.compile(content)
|
|
@@ -689,11 +689,11 @@ class Roda
|
|
|
689
689
|
digest = begin
|
|
690
690
|
require 'openssl'
|
|
691
691
|
::OpenSSL::Digest
|
|
692
|
-
# :
|
|
692
|
+
# simplecov:disable
|
|
693
693
|
rescue LoadError
|
|
694
694
|
require 'digest/sha2'
|
|
695
695
|
::Digest
|
|
696
|
-
# :
|
|
696
|
+
# simplecov:enable
|
|
697
697
|
end
|
|
698
698
|
digest.const_get(algo.to_s.upcase).hexdigest(content)
|
|
699
699
|
end
|
|
@@ -8,9 +8,9 @@ class Roda
|
|
|
8
8
|
# in the authorization header will be recognized as long as they start
|
|
9
9
|
# with the case insensitive string "bearer ".
|
|
10
10
|
module BearerToken
|
|
11
|
-
# :
|
|
11
|
+
# simplecov:disable
|
|
12
12
|
METHOD = RUBY_VERSION >= "2.4" ? :match? : :match
|
|
13
|
-
# :
|
|
13
|
+
# simplecov:enable
|
|
14
14
|
|
|
15
15
|
module RequestMethods
|
|
16
16
|
# Return the bearer token for the request if there is one in the
|
data/lib/roda/plugins/break.rb
CHANGED
|
@@ -22,12 +22,20 @@ class Roda
|
|
|
22
22
|
# end
|
|
23
23
|
#
|
|
24
24
|
# This provides the same basic feature as the pass plugin, but
|
|
25
|
-
# uses Ruby's standard control flow
|
|
25
|
+
# uses Ruby's standard control flow primitive instead of a
|
|
26
26
|
# separate method.
|
|
27
27
|
module Break
|
|
28
28
|
module RequestMethods
|
|
29
29
|
private
|
|
30
30
|
|
|
31
|
+
# Handle passing inside the match block.
|
|
32
|
+
def always
|
|
33
|
+
rp = @remaining_path
|
|
34
|
+
super
|
|
35
|
+
ensure
|
|
36
|
+
@remaining_path = rp
|
|
37
|
+
end
|
|
38
|
+
|
|
31
39
|
# Handle break inside match blocks, restoring remaining path.
|
|
32
40
|
def if_match(_)
|
|
33
41
|
rp = @remaining_path
|
data/lib/roda/plugins/caching.rb
CHANGED
|
@@ -75,28 +75,27 @@ class Roda
|
|
|
75
75
|
# Set the last modified time of the resource using the Last-Modified header.
|
|
76
76
|
# The +time+ argument should be a Time instance.
|
|
77
77
|
#
|
|
78
|
-
# If the current request includes an If-Modified-Since header that is
|
|
79
|
-
# equal or later than the time specified, immediately returns a response
|
|
80
|
-
# with a 304 status.
|
|
81
|
-
#
|
|
82
78
|
# If the current request includes an If-Unmodified-Since header that is
|
|
83
79
|
# before than the time specified, immediately returns a response
|
|
84
80
|
# with a 412 status.
|
|
81
|
+
#
|
|
82
|
+
# If the current request does not include an If-None-Match header and
|
|
83
|
+
# includes an If-Modified-Since header that is equal or later than the
|
|
84
|
+
# time specified, immediately returns a response with a 304 status.
|
|
85
85
|
def last_modified(time)
|
|
86
86
|
return unless time
|
|
87
87
|
res = response
|
|
88
88
|
e = env
|
|
89
89
|
res[RodaResponseHeaders::LAST_MODIFIED] = time.httpdate
|
|
90
|
-
return if e['HTTP_IF_NONE_MATCH']
|
|
91
90
|
status = res.status
|
|
92
91
|
|
|
93
|
-
if (!status || status
|
|
94
|
-
res.status =
|
|
92
|
+
if (!status || (status >= 200 && status < 300) || status == 412) && (ius = time_from_header(e['HTTP_IF_UNMODIFIED_SINCE'])) && ius < time.to_i
|
|
93
|
+
res.status = 412
|
|
95
94
|
halt
|
|
96
95
|
end
|
|
97
96
|
|
|
98
|
-
if
|
|
99
|
-
res.status =
|
|
97
|
+
if !e['HTTP_IF_NONE_MATCH'] && (!status || status == 200) && (ims = time_from_header(e['HTTP_IF_MODIFIED_SINCE'])) && ims >= time.to_i
|
|
98
|
+
res.status = 304
|
|
100
99
|
halt
|
|
101
100
|
end
|
|
102
101
|
end
|
|
@@ -113,8 +112,9 @@ class Roda
|
|
|
113
112
|
# matching etag, immediately returns a response with a 304 or 412 status,
|
|
114
113
|
# depending on the request method.
|
|
115
114
|
#
|
|
116
|
-
# When the current request includes an If-Match header with
|
|
117
|
-
#
|
|
115
|
+
# When the current request includes an If-Match header with an etag that
|
|
116
|
+
# doesn't match, or where the :weak option was used, immediately returns a
|
|
117
|
+
# response with a 412 status.
|
|
118
118
|
def etag(value, opts=OPTS)
|
|
119
119
|
# Before touching this code, please double check RFC 2616 14.24 and 14.26.
|
|
120
120
|
weak = opts[:weak]
|
|
@@ -132,7 +132,7 @@ class Roda
|
|
|
132
132
|
end
|
|
133
133
|
|
|
134
134
|
if ifm = e['HTTP_IF_MATCH']
|
|
135
|
-
|
|
135
|
+
if weak || !etag_matches?(ifm, etag, new_resource)
|
|
136
136
|
res.status = 412
|
|
137
137
|
halt
|
|
138
138
|
end
|
|
@@ -36,11 +36,11 @@ class Roda
|
|
|
36
36
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
37
37
|
end
|
|
38
38
|
else
|
|
39
|
-
# :
|
|
39
|
+
# simplecov:disable
|
|
40
40
|
def self.start_timer # :nodoc:
|
|
41
41
|
Time.now
|
|
42
42
|
end
|
|
43
|
-
# :
|
|
43
|
+
# simplecov:enable
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
module InstanceMethods
|
|
@@ -61,10 +61,10 @@ class Roda
|
|
|
61
61
|
line = "#{env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-"} - #{env["REMOTE_USER"] || "-"} [#{Time.now.strftime("%d/%b/%Y:%H:%M:%S %z")}] \"#{env["REQUEST_METHOD"]} #{env["SCRIPT_NAME"]}#{env["PATH_INFO"]}#{"?#{env["QUERY_STRING"]}" if ((qs = env["QUERY_STRING"]) && !qs.empty?)} #{@_request.http_version}\" #{status} #{((length = headers[RodaResponseHeaders::CONTENT_LENGTH]) && (length unless length == '0')) || '-'} #{elapsed_time} "
|
|
62
62
|
if MUTATE_LINE
|
|
63
63
|
line.gsub!(/[^[:print:]]/){|c| sprintf("\\x%x", c.ord)}
|
|
64
|
-
# :
|
|
64
|
+
# simplecov:disable
|
|
65
65
|
else
|
|
66
66
|
line = line.gsub(/[^[:print:]]/){|c| sprintf("\\x%x", c.ord)}
|
|
67
|
-
# :
|
|
67
|
+
# simplecov:enable
|
|
68
68
|
end
|
|
69
69
|
line[-1] = "\n"
|
|
70
70
|
opts[:common_logger_meth].call(line)
|
|
@@ -48,9 +48,9 @@ class Roda
|
|
|
48
48
|
# Note that this plugin only affects cookies set by the application, and does not
|
|
49
49
|
# affect cookies set by middleware the application is using.
|
|
50
50
|
module CookieFlags
|
|
51
|
-
# :
|
|
51
|
+
# simplecov:disable
|
|
52
52
|
MATCH_METH = RUBY_VERSION >= '2.4' ? :match? : :match
|
|
53
|
-
# :
|
|
53
|
+
# simplecov:enable
|
|
54
54
|
private_constant :MATCH_METH
|
|
55
55
|
|
|
56
56
|
DEFAULTS = {:secure=>true, :httponly=>true, :same_site=>'strict', :action=>:modify}.freeze
|
|
@@ -43,19 +43,19 @@ class Roda
|
|
|
43
43
|
render_each(enum, template, :locals=>locals, &block)
|
|
44
44
|
end
|
|
45
45
|
RUBY
|
|
46
|
-
# :
|
|
46
|
+
# simplecov:disable
|
|
47
47
|
else
|
|
48
48
|
def each_part(enum, template, locals=OPTS, &block)
|
|
49
49
|
render_each(enum, template, :locals=>locals, &block)
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
|
-
# :
|
|
52
|
+
# simplecov:enable
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
module AssumeFixedLocalsInstanceMethods
|
|
56
|
-
# :
|
|
56
|
+
# simplecov:disable
|
|
57
57
|
if RUBY_VERSION >= '3.0'
|
|
58
|
-
# :
|
|
58
|
+
# simplecov:enable
|
|
59
59
|
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
60
60
|
def each_part(enum, template, **locals, &block)
|
|
61
61
|
if optimized_method = _cached_render_each_template_method(template)
|
|
@@ -44,9 +44,9 @@ class Roda
|
|
|
44
44
|
:filter=>lambda{|k,v| false},
|
|
45
45
|
:headers=>OPTS,
|
|
46
46
|
:host=>'localhost',
|
|
47
|
-
# :
|
|
47
|
+
# simplecov:disable
|
|
48
48
|
:emailer=>lambda{|h| Net::SMTP.start(h[:host]){|s| s.send_message(h[:message], h[:from], h[:to])}},
|
|
49
|
-
# :
|
|
49
|
+
# simplecov:enable
|
|
50
50
|
:default_headers=>lambda do |h, e|
|
|
51
51
|
subject = if e.respond_to?(:message)
|
|
52
52
|
"#{e.class}: #{e.message}"
|
|
@@ -415,14 +415,14 @@ END
|
|
|
415
415
|
def exception_page_exception_message(exception)
|
|
416
416
|
exception.detailed_message(highlight: false).to_s
|
|
417
417
|
end
|
|
418
|
-
# :
|
|
418
|
+
# simplecov:disable
|
|
419
419
|
else
|
|
420
420
|
# Return message to use for exception.
|
|
421
421
|
def exception_page_exception_message(exception)
|
|
422
422
|
exception.message.to_s
|
|
423
423
|
end
|
|
424
424
|
end
|
|
425
|
-
# :
|
|
425
|
+
# simplecov:enable
|
|
426
426
|
end
|
|
427
427
|
|
|
428
428
|
module RequestMethods
|
data/lib/roda/plugins/h.rb
CHANGED
|
@@ -17,10 +17,10 @@ class Roda
|
|
|
17
17
|
begin
|
|
18
18
|
require 'cgi/escape'
|
|
19
19
|
unless CGI.respond_to?(:escapeHTML) # work around for JRuby 9.1
|
|
20
|
-
# :
|
|
20
|
+
# simplecov:disable
|
|
21
21
|
CGI = Object.new
|
|
22
22
|
CGI.extend(defined?(::CGI::Escape) ? ::CGI::Escape : ::CGI::Util)
|
|
23
|
-
# :
|
|
23
|
+
# simplecov:enable
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
module InstanceMethods
|
|
@@ -30,7 +30,7 @@ class Roda
|
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
rescue LoadError
|
|
33
|
-
# :
|
|
33
|
+
# simplecov:disable
|
|
34
34
|
|
|
35
35
|
# A Hash of entities and their escaped equivalents,
|
|
36
36
|
# to be escaped by h().
|
|
@@ -50,7 +50,7 @@ class Roda
|
|
|
50
50
|
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
|
-
# :
|
|
53
|
+
# simplecov:enable
|
|
54
54
|
end
|
|
55
55
|
end
|
|
56
56
|
|
|
@@ -128,12 +128,16 @@ class Roda
|
|
|
128
128
|
if routes = roda_class.opts[:hash_branches][namespace]
|
|
129
129
|
if segment_end = rp.index('/', 1)
|
|
130
130
|
if meth = routes[rp[0, segment_end]]
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
always do
|
|
132
|
+
@remaining_path = rp[segment_end, 100000000]
|
|
133
|
+
scope.send(meth, self)
|
|
134
|
+
end
|
|
133
135
|
end
|
|
134
136
|
elsif meth = routes[rp]
|
|
135
|
-
|
|
136
|
-
|
|
137
|
+
always do
|
|
138
|
+
@remaining_path = ''
|
|
139
|
+
scope.send(meth, self)
|
|
140
|
+
end
|
|
137
141
|
end
|
|
138
142
|
end
|
|
139
143
|
end
|
|
@@ -116,8 +116,10 @@ class Roda
|
|
|
116
116
|
# remaining path, and dispatch to that block if there is one.
|
|
117
117
|
def hash_paths(namespace=matched_path)
|
|
118
118
|
if (routes = roda_class.opts[:hash_paths][namespace]) && (meth = routes[@remaining_path])
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
always do
|
|
120
|
+
@remaining_path = ''
|
|
121
|
+
scope.send(meth, self)
|
|
122
|
+
end
|
|
121
123
|
end
|
|
122
124
|
end
|
|
123
125
|
end
|
|
@@ -51,11 +51,11 @@ class Roda
|
|
|
51
51
|
Digest = begin
|
|
52
52
|
require 'openssl'
|
|
53
53
|
::OpenSSL::Digest
|
|
54
|
-
# :
|
|
54
|
+
# simplecov:disable
|
|
55
55
|
rescue LoadError
|
|
56
56
|
require 'digest/sha2'
|
|
57
57
|
::Digest
|
|
58
|
-
# :
|
|
58
|
+
# simplecov:enable
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def self.load_dependencies(app, opts = OPTS)
|
|
@@ -346,43 +346,41 @@ class Roda
|
|
|
346
346
|
|
|
347
347
|
if submitted_hmac.bytesize == 64
|
|
348
348
|
on String do |flags|
|
|
349
|
-
if flags.
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
end
|
|
349
|
+
if flags.include?('n') ^ !scope.hmac_path_namespace(opts).nil?
|
|
350
|
+
# Namespace required and not provided, or provided and not required.
|
|
351
|
+
# Bail early to avoid unnecessary HMAC calculation.
|
|
352
|
+
@remaining_path = orig_path
|
|
353
|
+
return
|
|
354
|
+
end
|
|
356
355
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
356
|
+
if flags.include?('m')
|
|
357
|
+
rpath = "#{env['REQUEST_METHOD'].to_s.upcase}:#{rpath}"
|
|
358
|
+
end
|
|
360
359
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
360
|
+
if flags.include?('p')
|
|
361
|
+
rpath = "#{rpath}?#{env["QUERY_STRING"]}"
|
|
362
|
+
end
|
|
364
363
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
end
|
|
364
|
+
if hmac_path_valid?(mpath, rpath, submitted_hmac, opts)
|
|
365
|
+
if flags.include?('t')
|
|
366
|
+
on Integer do |int|
|
|
367
|
+
if int >= Time.now.to_i
|
|
368
|
+
always(&block)
|
|
369
|
+
else
|
|
370
|
+
# Return from method without matching
|
|
371
|
+
@remaining_path = orig_path
|
|
372
|
+
return
|
|
375
373
|
end
|
|
376
|
-
else
|
|
377
|
-
always(&block)
|
|
378
374
|
end
|
|
375
|
+
else
|
|
376
|
+
always(&block)
|
|
379
377
|
end
|
|
380
378
|
end
|
|
381
|
-
|
|
382
|
-
# Return from method without matching
|
|
383
|
-
@remaining_path = orig_path
|
|
384
|
-
return
|
|
385
379
|
end
|
|
380
|
+
|
|
381
|
+
# Return from method without matching
|
|
382
|
+
@remaining_path = orig_path
|
|
383
|
+
return
|
|
386
384
|
end
|
|
387
385
|
|
|
388
386
|
# Return from method without matching
|
|
@@ -68,8 +68,29 @@ class Roda
|
|
|
68
68
|
# end
|
|
69
69
|
# end
|
|
70
70
|
#
|
|
71
|
-
# This plugin uses the host method on the request to get the hostname (this method
|
|
72
|
-
# is defined by Rack).
|
|
71
|
+
# This plugin uses the +host+ method on the request to get the hostname (this method
|
|
72
|
+
# is defined by Rack). Note that as of Rack 3.2, Rack's default approach to determine
|
|
73
|
+
# the hostname is insecure in many cases, since:
|
|
74
|
+
#
|
|
75
|
+
# * It will use a forwarded header instead of the +host+ header by default if the
|
|
76
|
+
# request was from a trusted proxy.
|
|
77
|
+
# * Rack trust proxies from any private IP address range by default
|
|
78
|
+
# (this can be disabled using <tt>Rack::Request.ip_filter=</tt>).
|
|
79
|
+
# * Rack supports both +forwarded+ and <tt>x-forwarded-host</tt> headers, will check
|
|
80
|
+
# both by default, and the order of checking depends on Rack's configuration
|
|
81
|
+
# (this can be configured using <tt>Rack::Request.forwarded_priority</tt>).
|
|
82
|
+
# * If the application can be reached via an arbitrary +host+ header, the attacker
|
|
83
|
+
# has full control over the <tt>Rack::Request#host</tt> value even when forwarded
|
|
84
|
+
# host usage is disabled.
|
|
85
|
+
#
|
|
86
|
+
# You should configure Rack appropriately for your deployment, not allowing the use
|
|
87
|
+
# of forwarded information unless your deployment is using a reverse proxy, and only
|
|
88
|
+
# allowing the specific forwarded header set by the expected reverse proxy if a
|
|
89
|
+
# reverse proxy is used. Rack 3.3 should include more control in this area,
|
|
90
|
+
# and Rack 4 should ship with more secure defaults in regard to forwarding headers.
|
|
91
|
+
#
|
|
92
|
+
# It is recommended to use the host_authorization plugin to check that the requested
|
|
93
|
+
# host is allowed when using this plugin.
|
|
73
94
|
module HostRouting
|
|
74
95
|
REQUEST_INSTANCE_VARIABLES = [:@_host_routing_host].freeze
|
|
75
96
|
|
|
@@ -140,7 +161,7 @@ class Roda
|
|
|
140
161
|
# Treat all given hostnames as routing to the give host.
|
|
141
162
|
def to(host, *hostnames)
|
|
142
163
|
hostnames.each do |hostname|
|
|
143
|
-
@host_hash[hostname] = host
|
|
164
|
+
@host_hash[hostname.downcase.freeze] = host
|
|
144
165
|
end
|
|
145
166
|
end
|
|
146
167
|
|
|
@@ -180,7 +201,11 @@ class Roda
|
|
|
180
201
|
# * The return value of the +hosts.default+ block, if given
|
|
181
202
|
# * The default value provided in the +hosts.default+ call
|
|
182
203
|
def _get_host_routing_host
|
|
183
|
-
host = self.host
|
|
204
|
+
if host = self.host
|
|
205
|
+
host = host.downcase
|
|
206
|
+
else
|
|
207
|
+
host = ""
|
|
208
|
+
end
|
|
184
209
|
|
|
185
210
|
roda_class.opts[:host_routing_hash][host] ||
|
|
186
211
|
scope._host_routing_default(host) ||
|
|
@@ -17,7 +17,19 @@ class Roda
|
|
|
17
17
|
module JsonParser
|
|
18
18
|
DEFAULT_ERROR_HANDLER = proc{|r| r.halt [400, {}, []]}
|
|
19
19
|
|
|
20
|
+
# simplecov:disable
|
|
21
|
+
MATCH_METHOD = RUBY_VERSION >= '2.4' ? :match? : :match
|
|
22
|
+
# simplecov:enable
|
|
23
|
+
private_constant :MATCH_METHOD
|
|
24
|
+
|
|
20
25
|
# Handle options for the json_parser plugin:
|
|
26
|
+
# :content_type_regexp :: A regexp used to determine if the request's
|
|
27
|
+
# content type is JSON. For backwards compatibility,
|
|
28
|
+
# uses the insecure /json/ if not provided. Warns
|
|
29
|
+
# if this option is not provided. The recommended
|
|
30
|
+
# value to use is /\Aapplication\/json\b/i or
|
|
31
|
+
# /\Aapplication\/(?:vnd\.api\+)?json\b/i. Roda 4
|
|
32
|
+
# will default to the latter.
|
|
21
33
|
# :error_handler :: A proc to call if an exception is raised when
|
|
22
34
|
# parsing a JSON request body. The proc is called
|
|
23
35
|
# with the request object, and should probably call
|
|
@@ -37,6 +49,13 @@ class Roda
|
|
|
37
49
|
app.opts[:json_parser_error_handler] = opts[:error_handler] || app.opts[:json_parser_error_handler] || DEFAULT_ERROR_HANDLER
|
|
38
50
|
app.opts[:json_parser_parser] = opts[:parser] || app.opts[:json_parser_parser] || app.opts[:json_parser] || JSON.method(:parse)
|
|
39
51
|
app.opts[:json_parser_include_request] = opts[:include_request] if opts.has_key?(:include_request)
|
|
52
|
+
app.opts[:json_parser_content_type_regexp] = opts[:content_type_regexp] || app.opts[:json_parser_content_type_regexp]
|
|
53
|
+
|
|
54
|
+
unless app.opts[:json_parser_content_type_regexp]
|
|
55
|
+
# RODA4: Switch to /\Aapplication\/(?:vnd\.api\+)?json\b/i by default
|
|
56
|
+
RodaPlugins.warn(':content_type_regexp option not provided to json_parser plugin, using /json/ for backwards compatibility, which may be insecure, using /\Aapplication\/json\b/i or /\Aapplication\/(?:vnd\.api\+)?json\b/i is recommended')
|
|
57
|
+
app.opts[:json_parser_content_type_regexp] = /json/
|
|
58
|
+
end
|
|
40
59
|
|
|
41
60
|
case opts[:wrap]
|
|
42
61
|
when :unless_hash, :always
|
|
@@ -57,7 +76,7 @@ class Roda
|
|
|
57
76
|
return post_params
|
|
58
77
|
end
|
|
59
78
|
|
|
60
|
-
unless (input = env["rack.input"]) && (content_type = self.content_type) &&
|
|
79
|
+
unless (input = env["rack.input"]) && (content_type = self.content_type) && roda_class.opts[:json_parser_content_type_regexp].send(MATCH_METHOD, content_type)
|
|
61
80
|
return super
|
|
62
81
|
end
|
|
63
82
|
|
|
@@ -84,7 +103,6 @@ class Roda
|
|
|
84
103
|
args << self if roda_class.opts[:json_parser_include_request]
|
|
85
104
|
roda_class.opts[:json_parser_parser].call(*args)
|
|
86
105
|
end
|
|
87
|
-
|
|
88
106
|
|
|
89
107
|
# Rack 3 dropped requirement that input be rewindable
|
|
90
108
|
if Rack.release >= '3'
|
|
@@ -510,11 +510,11 @@ class Roda
|
|
|
510
510
|
a1.casecmp?(a2)
|
|
511
511
|
end
|
|
512
512
|
else
|
|
513
|
-
# :
|
|
513
|
+
# simplecov:disable
|
|
514
514
|
def address_match?(a1, a2)
|
|
515
515
|
a1.downcase == a2.downcase
|
|
516
516
|
end
|
|
517
|
-
# :
|
|
517
|
+
# simplecov:enable
|
|
518
518
|
end
|
|
519
519
|
|
|
520
520
|
# Match if any of the given addresses match the given val, which
|
data/lib/roda/plugins/mailer.rb
CHANGED
|
@@ -153,9 +153,9 @@ class Roda
|
|
|
153
153
|
mail
|
|
154
154
|
end
|
|
155
155
|
end
|
|
156
|
-
# :
|
|
156
|
+
# simplecov:disable
|
|
157
157
|
ruby2_keywords(:mail) if respond_to?(:ruby2_keywords, true)
|
|
158
|
-
# :
|
|
158
|
+
# simplecov:enable
|
|
159
159
|
|
|
160
160
|
# Calls +mail+ with given arguments and immediately sends the resulting mail.
|
|
161
161
|
def sendmail(*args)
|
|
@@ -163,9 +163,9 @@ class Roda
|
|
|
163
163
|
m.deliver
|
|
164
164
|
end
|
|
165
165
|
end
|
|
166
|
-
# :
|
|
166
|
+
# simplecov:disable
|
|
167
167
|
ruby2_keywords(:sendmail) if respond_to?(:ruby2_keywords, true)
|
|
168
|
-
# :
|
|
168
|
+
# simplecov:enable
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
module RequestMethods
|
|
@@ -27,11 +27,16 @@ class Roda
|
|
|
27
27
|
rp = @remaining_path
|
|
28
28
|
if rp.getbyte(0) == 47
|
|
29
29
|
if last = rp.index('/', 1)
|
|
30
|
-
|
|
31
|
-
always
|
|
30
|
+
return false if last == 1
|
|
31
|
+
always do
|
|
32
|
+
@remaining_path = rp[last, rp.length]
|
|
33
|
+
yield rp[1, last-1]
|
|
34
|
+
end
|
|
32
35
|
elsif (len = rp.length) > 1
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
always do
|
|
37
|
+
@remaining_path = ""
|
|
38
|
+
yield rp[1, len]
|
|
39
|
+
end
|
|
35
40
|
end
|
|
36
41
|
end
|
|
37
42
|
end
|
|
@@ -41,8 +46,10 @@ class Roda
|
|
|
41
46
|
def is_segment
|
|
42
47
|
rp = @remaining_path
|
|
43
48
|
if rp.getbyte(0) == 47 && !rp.index('/', 1) && (len = rp.length) > 1
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
always do
|
|
50
|
+
@remaining_path = ""
|
|
51
|
+
yield rp[1, len]
|
|
52
|
+
end
|
|
46
53
|
end
|
|
47
54
|
end
|
|
48
55
|
end
|
|
@@ -28,7 +28,11 @@ class Roda
|
|
|
28
28
|
module RequestMethods
|
|
29
29
|
# Optimized version of +on+ that only supports a single string.
|
|
30
30
|
def on_branch(s)
|
|
31
|
-
|
|
31
|
+
rp = @remaining_path
|
|
32
|
+
if _match_string(s)
|
|
33
|
+
always{yield}
|
|
34
|
+
@remaining_path = rp
|
|
35
|
+
end
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
# Optimized version of +is+ that only supports a single string.
|
|
@@ -37,9 +41,8 @@ class Roda
|
|
|
37
41
|
if _match_string(s)
|
|
38
42
|
if @remaining_path.empty?
|
|
39
43
|
always{yield}
|
|
40
|
-
else
|
|
41
|
-
@remaining_path = rp
|
|
42
44
|
end
|
|
45
|
+
@remaining_path = rp
|
|
43
46
|
end
|
|
44
47
|
end
|
|
45
48
|
end
|