actionpack 6.1.7.3 → 7.0.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +320 -390
- data/MIT-LICENSE +1 -0
- data/README.rdoc +4 -5
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +13 -26
- data/lib/abstract_controller/caching/fragments.rb +2 -2
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +21 -7
- data/lib/abstract_controller/collector.rb +2 -2
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +17 -12
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/rendering.rb +9 -11
- data/lib/abstract_controller/translation.rb +5 -4
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/action_controller/api.rb +7 -7
- data/lib/action_controller/base.rb +5 -4
- data/lib/action_controller/form_builder.rb +2 -2
- data/lib/action_controller/log_subscriber.rb +4 -3
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +137 -102
- data/lib/action_controller/metal/content_security_policy.rb +36 -2
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +23 -31
- data/lib/action_controller/metal/etag_with_flash.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +19 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/head.rb +1 -1
- data/lib/action_controller/metal/helpers.rb +2 -2
- data/lib/action_controller/metal/http_authentication.rb +66 -39
- data/lib/action_controller/metal/instrumentation.rb +57 -52
- data/lib/action_controller/metal/live.rb +43 -2
- data/lib/action_controller/metal/mime_responds.rb +3 -3
- data/lib/action_controller/metal/params_wrapper.rb +20 -11
- data/lib/action_controller/metal/permissions_policy.rb +19 -28
- data/lib/action_controller/metal/redirecting.rb +111 -19
- data/lib/action_controller/metal/renderers.rb +12 -13
- data/lib/action_controller/metal/rendering.rb +121 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +83 -32
- data/lib/action_controller/metal/rescue.rb +5 -4
- data/lib/action_controller/metal/streaming.rb +7 -9
- data/lib/action_controller/metal/strong_parameters.rb +138 -115
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +3 -5
- data/lib/action_controller/metal.rb +10 -13
- data/lib/action_controller/railtie.rb +50 -6
- data/lib/action_controller/renderer.rb +1 -20
- data/lib/action_controller/test_case.rb +28 -7
- data/lib/action_controller.rb +2 -5
- data/lib/action_dispatch/http/cache.rb +20 -13
- data/lib/action_dispatch/http/content_security_policy.rb +113 -36
- data/lib/action_dispatch/http/filter_parameters.rb +4 -19
- data/lib/action_dispatch/http/headers.rb +1 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +15 -5
- data/lib/action_dispatch/http/mime_type.rb +9 -11
- data/lib/action_dispatch/http/parameters.rb +5 -5
- data/lib/action_dispatch/http/permissions_policy.rb +17 -1
- data/lib/action_dispatch/http/request.rb +27 -37
- data/lib/action_dispatch/http/response.rb +3 -20
- data/lib/action_dispatch/http/upload.rb +13 -2
- data/lib/action_dispatch/http/url.rb +11 -19
- data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
- data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
- data/lib/action_dispatch/journey/nodes/node.rb +70 -5
- data/lib/action_dispatch/journey/path/pattern.rb +22 -13
- data/lib/action_dispatch/journey/route.rb +6 -13
- data/lib/action_dispatch/journey/router/utils.rb +2 -2
- data/lib/action_dispatch/journey/router.rb +1 -1
- data/lib/action_dispatch/journey/routes.rb +3 -3
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +0 -1
- data/lib/action_dispatch/middleware/cookies.rb +20 -13
- data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
- data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
- data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
- data/lib/action_dispatch/middleware/executor.rb +3 -0
- data/lib/action_dispatch/middleware/flash.rb +17 -18
- data/lib/action_dispatch/middleware/host_authorization.rb +13 -17
- data/lib/action_dispatch/middleware/remote_ip.rb +20 -8
- data/lib/action_dispatch/middleware/request_id.rb +3 -3
- data/lib/action_dispatch/middleware/server_timing.rb +76 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +9 -9
- data/lib/action_dispatch/middleware/show_exceptions.rb +17 -16
- data/lib/action_dispatch/middleware/stack.rb +27 -9
- data/lib/action_dispatch/middleware/static.rb +5 -9
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +22 -22
- data/lib/action_dispatch/railtie.rb +8 -2
- data/lib/action_dispatch/request/session.rb +43 -13
- data/lib/action_dispatch/routing/inspector.rb +1 -1
- data/lib/action_dispatch/routing/mapper.rb +82 -83
- data/lib/action_dispatch/routing/redirection.rb +5 -2
- data/lib/action_dispatch/routing/route_set.rb +17 -7
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +24 -25
- data/lib/action_dispatch/routing.rb +5 -6
- data/lib/action_dispatch/system_test_case.rb +5 -5
- data/lib/action_dispatch/system_testing/browser.rb +3 -13
- data/lib/action_dispatch/system_testing/driver.rb +34 -10
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +11 -7
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
- data/lib/action_dispatch/testing/assertions/response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/routing.rb +3 -2
- data/lib/action_dispatch/testing/assertions.rb +2 -5
- data/lib/action_dispatch/testing/integration.rb +6 -8
- data/lib/action_dispatch/testing/test_process.rb +3 -29
- data/lib/action_dispatch/testing/test_response.rb +20 -2
- data/lib/action_dispatch.rb +1 -0
- data/lib/action_pack/gem_version.rb +5 -5
- data/lib/action_pack/version.rb +1 -1
- metadata +16 -15
@@ -10,11 +10,15 @@ module ActionDispatch
|
|
10
10
|
|
11
11
|
attr_reader :memos
|
12
12
|
|
13
|
+
DEFAULT_EXP = /[^.\/?]+/
|
14
|
+
DEFAULT_EXP_ANCHORED = /\A#{DEFAULT_EXP}\Z/
|
15
|
+
|
13
16
|
def initialize
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@
|
17
|
+
@stdparam_states = {}
|
18
|
+
@regexp_states = {}
|
19
|
+
@string_states = {}
|
20
|
+
@accepting = {}
|
21
|
+
@memos = Hash.new { |h, k| h[k] = [] }
|
18
22
|
end
|
19
23
|
|
20
24
|
def add_accepting(state)
|
@@ -41,22 +45,54 @@ module ActionDispatch
|
|
41
45
|
Array(t)
|
42
46
|
end
|
43
47
|
|
44
|
-
def move(t,
|
48
|
+
def move(t, full_string, start_index, end_index)
|
45
49
|
return [] if t.empty?
|
46
50
|
|
47
|
-
|
48
|
-
strings = []
|
51
|
+
next_states = []
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
+
tok = full_string.slice(start_index, end_index - start_index)
|
54
|
+
token_matches_default_component = DEFAULT_EXP_ANCHORED.match?(tok)
|
55
|
+
|
56
|
+
t.each { |s, previous_start|
|
57
|
+
if previous_start.nil?
|
58
|
+
# In the simple case of a "default" param regex do this fast-path
|
59
|
+
# and add all next states.
|
60
|
+
if token_matches_default_component && states = @stdparam_states[s]
|
61
|
+
states.each { |re, v| next_states << [v, nil].freeze if !v.nil? }
|
62
|
+
end
|
63
|
+
|
64
|
+
# When we have a literal string, we can just pull the next state
|
65
|
+
if states = @string_states[s]
|
66
|
+
next_states << [states[tok], nil].freeze unless states[tok].nil?
|
67
|
+
end
|
53
68
|
end
|
54
69
|
|
55
|
-
|
56
|
-
|
70
|
+
# For regexes that aren't the "default" style, they may potentially
|
71
|
+
# not be terminated by the first "token" [./?], so we need to continue
|
72
|
+
# to attempt to match this regexp as well as any successful paths that
|
73
|
+
# continue out of it. both paths could be valid.
|
74
|
+
if states = @regexp_states[s]
|
75
|
+
slice_start = if previous_start.nil?
|
76
|
+
start_index
|
77
|
+
else
|
78
|
+
previous_start
|
79
|
+
end
|
80
|
+
|
81
|
+
slice_length = end_index - slice_start
|
82
|
+
curr_slice = full_string.slice(slice_start, slice_length)
|
83
|
+
|
84
|
+
states.each { |re, v|
|
85
|
+
# if we match, we can try moving past this
|
86
|
+
next_states << [v, nil].freeze if !v.nil? && re.match?(curr_slice)
|
87
|
+
}
|
88
|
+
|
89
|
+
# and regardless, we must continue accepting tokens and retrying this regexp.
|
90
|
+
# we need to remember where we started as well so we can take bigger slices.
|
91
|
+
next_states << [s, slice_start].freeze
|
57
92
|
end
|
58
93
|
}
|
59
|
-
|
94
|
+
|
95
|
+
next_states
|
60
96
|
end
|
61
97
|
|
62
98
|
def as_json(options = nil)
|
@@ -69,9 +105,10 @@ module ActionDispatch
|
|
69
105
|
end
|
70
106
|
|
71
107
|
{
|
72
|
-
regexp_states:
|
73
|
-
string_states:
|
74
|
-
|
108
|
+
regexp_states: simple_regexp,
|
109
|
+
string_states: @string_states,
|
110
|
+
stdparam_states: @stdparam_states,
|
111
|
+
accepting: @accepting
|
75
112
|
}
|
76
113
|
end
|
77
114
|
|
@@ -93,7 +130,7 @@ module ActionDispatch
|
|
93
130
|
states = "function tt() { return #{to_json}; }"
|
94
131
|
|
95
132
|
fun_routes = paths.sample(3).map do |ast|
|
96
|
-
ast.
|
133
|
+
ast.filter_map { |n|
|
97
134
|
case n
|
98
135
|
when Nodes::Symbol
|
99
136
|
case n.left
|
@@ -106,7 +143,7 @@ module ActionDispatch
|
|
106
143
|
else
|
107
144
|
nil
|
108
145
|
end
|
109
|
-
}.
|
146
|
+
}.join
|
110
147
|
end
|
111
148
|
|
112
149
|
stylesheets = [fsm_css]
|
@@ -125,18 +162,33 @@ module ActionDispatch
|
|
125
162
|
|
126
163
|
def []=(from, to, sym)
|
127
164
|
to_mappings = states_hash_for(sym)[from] ||= {}
|
165
|
+
case sym
|
166
|
+
when Regexp
|
167
|
+
# we must match the whole string to a token boundary
|
168
|
+
if sym == DEFAULT_EXP
|
169
|
+
sym = DEFAULT_EXP_ANCHORED
|
170
|
+
else
|
171
|
+
sym = /\A#{sym}\Z/
|
172
|
+
end
|
173
|
+
when Symbol
|
174
|
+
# account for symbols in the constraints the same as strings
|
175
|
+
sym = sym.to_s
|
176
|
+
end
|
128
177
|
to_mappings[sym] = to
|
129
178
|
end
|
130
179
|
|
131
180
|
def states
|
132
181
|
ss = @string_states.keys + @string_states.values.flat_map(&:values)
|
182
|
+
ps = @stdparam_states.keys + @stdparam_states.values.flat_map(&:values)
|
133
183
|
rs = @regexp_states.keys + @regexp_states.values.flat_map(&:values)
|
134
|
-
(ss + rs).uniq
|
184
|
+
(ss + ps + rs).uniq
|
135
185
|
end
|
136
186
|
|
137
187
|
def transitions
|
138
188
|
@string_states.flat_map { |from, hash|
|
139
189
|
hash.map { |s, to| [from, s, to] }
|
190
|
+
} + @stdparam_states.flat_map { |from, hash|
|
191
|
+
hash.map { |s, to| [from, s, to] }
|
140
192
|
} + @regexp_states.flat_map { |from, hash|
|
141
193
|
hash.map { |s, to| [from, s, to] }
|
142
194
|
}
|
@@ -145,10 +197,14 @@ module ActionDispatch
|
|
145
197
|
private
|
146
198
|
def states_hash_for(sym)
|
147
199
|
case sym
|
148
|
-
when String
|
200
|
+
when String, Symbol
|
149
201
|
@string_states
|
150
202
|
when Regexp
|
151
|
-
|
203
|
+
if sym == DEFAULT_EXP
|
204
|
+
@stdparam_states
|
205
|
+
else
|
206
|
+
@regexp_states
|
207
|
+
end
|
152
208
|
else
|
153
209
|
raise ArgumentError, "unknown symbol: %s" % sym.class
|
154
210
|
end
|
@@ -4,6 +4,66 @@ require "action_dispatch/journey/visitors"
|
|
4
4
|
|
5
5
|
module ActionDispatch
|
6
6
|
module Journey # :nodoc:
|
7
|
+
class Ast # :nodoc:
|
8
|
+
attr_reader :names, :path_params, :tree, :wildcard_options, :terminals
|
9
|
+
alias :root :tree
|
10
|
+
|
11
|
+
def initialize(tree, formatted)
|
12
|
+
@tree = tree
|
13
|
+
@path_params = []
|
14
|
+
@names = []
|
15
|
+
@symbols = []
|
16
|
+
@stars = []
|
17
|
+
@terminals = []
|
18
|
+
@wildcard_options = {}
|
19
|
+
|
20
|
+
visit_tree(formatted)
|
21
|
+
end
|
22
|
+
|
23
|
+
def requirements=(requirements)
|
24
|
+
# inject any regexp requirements for `star` nodes so they can be
|
25
|
+
# determined nullable, which requires knowing if the regex accepts an
|
26
|
+
# empty string.
|
27
|
+
(symbols + stars).each do |node|
|
28
|
+
re = requirements[node.to_sym]
|
29
|
+
node.regexp = re if re
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def route=(route)
|
34
|
+
terminals.each { |n| n.memo = route }
|
35
|
+
end
|
36
|
+
|
37
|
+
def glob?
|
38
|
+
stars.any?
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
attr_reader :symbols, :stars
|
43
|
+
|
44
|
+
def visit_tree(formatted)
|
45
|
+
tree.each do |node|
|
46
|
+
if node.symbol?
|
47
|
+
path_params << node.to_sym
|
48
|
+
names << node.name
|
49
|
+
symbols << node
|
50
|
+
elsif node.star?
|
51
|
+
stars << node
|
52
|
+
|
53
|
+
if formatted != false
|
54
|
+
# Add a constraint for wildcard route to make it non-greedy and
|
55
|
+
# match the optional format part of the route by default.
|
56
|
+
wildcard_options[node.name.to_sym] ||= /.+?/m
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if node.terminal?
|
61
|
+
terminals << node
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
7
67
|
module Nodes # :nodoc:
|
8
68
|
class Node # :nodoc:
|
9
69
|
include Enumerable
|
@@ -78,7 +138,7 @@ module ActionDispatch
|
|
78
138
|
alias :symbol :regexp
|
79
139
|
attr_reader :name
|
80
140
|
|
81
|
-
DEFAULT_EXP = /[
|
141
|
+
DEFAULT_EXP = /[^.\/?]+/
|
82
142
|
GREEDY_EXP = /(.+)/
|
83
143
|
def initialize(left, regexp = DEFAULT_EXP)
|
84
144
|
super(left)
|
@@ -86,10 +146,6 @@ module ActionDispatch
|
|
86
146
|
@name = -left.tr("*:", "")
|
87
147
|
end
|
88
148
|
|
89
|
-
def default_regexp?
|
90
|
-
regexp == DEFAULT_EXP
|
91
|
-
end
|
92
|
-
|
93
149
|
def type; :SYMBOL; end
|
94
150
|
def symbol?; true; end
|
95
151
|
end
|
@@ -104,6 +160,15 @@ module ActionDispatch
|
|
104
160
|
end
|
105
161
|
|
106
162
|
class Star < Unary # :nodoc:
|
163
|
+
attr_accessor :regexp
|
164
|
+
|
165
|
+
def initialize(left)
|
166
|
+
super(left)
|
167
|
+
|
168
|
+
# By default wildcard routes are non-greedy and must match something.
|
169
|
+
@regexp = /.+?/m
|
170
|
+
end
|
171
|
+
|
107
172
|
def star?; true; end
|
108
173
|
def type; :STAR; end
|
109
174
|
|
@@ -4,15 +4,16 @@ module ActionDispatch
|
|
4
4
|
module Journey # :nodoc:
|
5
5
|
module Path # :nodoc:
|
6
6
|
class Pattern # :nodoc:
|
7
|
-
attr_reader :
|
7
|
+
attr_reader :ast, :names, :requirements, :anchored, :spec
|
8
8
|
|
9
9
|
def initialize(ast, requirements, separators, anchored)
|
10
|
-
@
|
10
|
+
@ast = ast
|
11
|
+
@spec = ast.root
|
11
12
|
@requirements = requirements
|
12
13
|
@separators = separators
|
13
14
|
@anchored = anchored
|
14
15
|
|
15
|
-
@names =
|
16
|
+
@names = ast.names
|
16
17
|
@optional_names = nil
|
17
18
|
@required_names = nil
|
18
19
|
@re = nil
|
@@ -27,20 +28,28 @@ module ActionDispatch
|
|
27
28
|
required_names
|
28
29
|
offsets
|
29
30
|
to_regexp
|
30
|
-
nil
|
31
|
+
@ast = nil
|
31
32
|
end
|
32
33
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
node.regexp = re if re
|
37
|
-
end
|
34
|
+
def requirements_anchored?
|
35
|
+
# each required param must not be surrounded by a literal, otherwise it isn't simple to chunk-match the url piecemeal
|
36
|
+
terminals = ast.terminals
|
38
37
|
|
39
|
-
|
40
|
-
|
38
|
+
terminals.each_with_index { |s, index|
|
39
|
+
next if index < 1
|
40
|
+
next if s.type == :DOT || s.type == :SLASH
|
41
|
+
|
42
|
+
back = terminals[index - 1]
|
43
|
+
fwd = terminals[index + 1]
|
44
|
+
|
45
|
+
# we also don't support this yet, constraints must be regexps
|
46
|
+
return false if s.symbol? && s.regexp.is_a?(Array)
|
47
|
+
|
48
|
+
return false if back.literal?
|
49
|
+
return false if !fwd.nil? && fwd.literal?
|
50
|
+
}
|
41
51
|
|
42
|
-
|
43
|
-
@names ||= spec.find_all(&:symbol?).map(&:name)
|
52
|
+
true
|
44
53
|
end
|
45
54
|
|
46
55
|
def required_names
|
@@ -5,7 +5,7 @@ module ActionDispatch
|
|
5
5
|
module Journey
|
6
6
|
class Route
|
7
7
|
attr_reader :app, :path, :defaults, :name, :precedence, :constraints,
|
8
|
-
:internal, :scope_options
|
8
|
+
:internal, :scope_options, :ast
|
9
9
|
|
10
10
|
alias :conditions :constraints
|
11
11
|
|
@@ -65,29 +65,22 @@ module ActionDispatch
|
|
65
65
|
@_required_defaults = required_defaults
|
66
66
|
@required_parts = nil
|
67
67
|
@parts = nil
|
68
|
-
@decorated_ast = nil
|
69
68
|
@precedence = precedence
|
70
69
|
@path_formatter = @path.build_formatter
|
71
70
|
@scope_options = scope_options
|
72
71
|
@internal = internal
|
72
|
+
|
73
|
+
@ast = @path.ast.root
|
74
|
+
@path.ast.route = self
|
73
75
|
end
|
74
76
|
|
75
77
|
def eager_load!
|
76
78
|
path.eager_load!
|
77
|
-
ast
|
78
79
|
parts
|
79
80
|
required_defaults
|
80
81
|
nil
|
81
82
|
end
|
82
83
|
|
83
|
-
def ast
|
84
|
-
@decorated_ast ||= begin
|
85
|
-
decorated_ast = path.ast
|
86
|
-
decorated_ast.find_all(&:terminal?).each { |n| n.memo = self }
|
87
|
-
decorated_ast
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
84
|
# Needed for `bin/rails routes`. Picks up succinctly defined requirements
|
92
85
|
# for a route, for example route
|
93
86
|
#
|
@@ -98,7 +91,7 @@ module ActionDispatch
|
|
98
91
|
# as requirements.
|
99
92
|
def requirements
|
100
93
|
@defaults.merge(path.requirements).delete_if { |_, v|
|
101
|
-
/.+?/ == v
|
94
|
+
/.+?/m == v
|
102
95
|
}
|
103
96
|
end
|
104
97
|
|
@@ -142,7 +135,7 @@ module ActionDispatch
|
|
142
135
|
end
|
143
136
|
|
144
137
|
def glob?
|
145
|
-
path.
|
138
|
+
path.ast.glob?
|
146
139
|
end
|
147
140
|
|
148
141
|
def dispatcher?
|
@@ -35,7 +35,7 @@ module ActionDispatch
|
|
35
35
|
US_ASCII = Encoding::US_ASCII
|
36
36
|
UTF_8 = Encoding::UTF_8
|
37
37
|
EMPTY = (+"").force_encoding(US_ASCII).freeze
|
38
|
-
DEC2HEX = (0..255).
|
38
|
+
DEC2HEX = (0..255).map { |i| (ENCODE % i).force_encoding(US_ASCII) }
|
39
39
|
|
40
40
|
ALPHA = "a-zA-Z"
|
41
41
|
DIGIT = "0-9"
|
@@ -44,7 +44,7 @@ module ActionDispatch
|
|
44
44
|
|
45
45
|
ESCAPED = /%[a-zA-Z0-9]{2}/.freeze
|
46
46
|
|
47
|
-
FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}
|
47
|
+
FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/?]/.freeze
|
48
48
|
SEGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@]/.freeze
|
49
49
|
PATH = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/]/.freeze
|
50
50
|
|
@@ -41,7 +41,7 @@ module ActionDispatch
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def partition_route(route)
|
44
|
-
if route.path.anchored && route.
|
44
|
+
if route.path.anchored && route.path.requirements_anchored?
|
45
45
|
anchored_routes << route
|
46
46
|
else
|
47
47
|
custom_routes << route
|
@@ -50,8 +50,8 @@ module ActionDispatch
|
|
50
50
|
|
51
51
|
def ast
|
52
52
|
@ast ||= begin
|
53
|
-
|
54
|
-
Nodes::Or.new(
|
53
|
+
nodes = anchored_routes.map(&:ast)
|
54
|
+
Nodes::Or.new(nodes)
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
@@ -68,7 +68,7 @@ function highlight_state(index, color) {
|
|
68
68
|
}
|
69
69
|
|
70
70
|
function highlight_finish(index) {
|
71
|
-
svg_nodes[index].select('
|
71
|
+
svg_nodes[index].select('ellipse')
|
72
72
|
.style("fill", "while")
|
73
73
|
.transition().duration(500)
|
74
74
|
.style("fill", "blue");
|
@@ -76,54 +76,79 @@ function highlight_finish(index) {
|
|
76
76
|
|
77
77
|
function match(input) {
|
78
78
|
reset_graph();
|
79
|
-
var table
|
80
|
-
var states
|
81
|
-
var regexp_states
|
82
|
-
var string_states
|
83
|
-
var
|
79
|
+
var table = tt();
|
80
|
+
var states = [[0, null]];
|
81
|
+
var regexp_states = table['regexp_states'];
|
82
|
+
var string_states = table['string_states'];
|
83
|
+
var stdparam_states = table['stdparam_states'];
|
84
|
+
var accepting = table['accepting'];
|
85
|
+
var default_re = new RegExp("^[^.\/?]+$");
|
86
|
+
var start_index = 0;
|
84
87
|
|
85
88
|
highlight_state(0);
|
86
89
|
|
87
90
|
tokenize(input, function(token) {
|
91
|
+
var end_index = start_index + token.length;
|
92
|
+
|
88
93
|
var new_states = [];
|
89
94
|
for(var key in states) {
|
90
|
-
var
|
95
|
+
var state_parts = states[key];
|
96
|
+
var state = state_parts[0];
|
97
|
+
var previous_start = state_parts[1];
|
98
|
+
|
99
|
+
if(previous_start == null) {
|
100
|
+
if(string_states[state] && string_states[state][token]) {
|
101
|
+
var new_state = string_states[state][token];
|
102
|
+
highlight_edge(state, new_state);
|
103
|
+
highlight_state(new_state);
|
104
|
+
new_states.push([new_state, null]);
|
105
|
+
}
|
91
106
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
107
|
+
if(stdparam_states[state] && default_re.test(token)) {
|
108
|
+
for(var key in stdparam_states[state]) {
|
109
|
+
var new_state = stdparam_states[state][key];
|
110
|
+
highlight_edge(state, new_state);
|
111
|
+
highlight_state(new_state);
|
112
|
+
new_states.push([new_state, null]);
|
113
|
+
}
|
114
|
+
}
|
97
115
|
}
|
98
116
|
|
99
117
|
if(regexp_states[state]) {
|
118
|
+
var slice_start = previous_start != null ? previous_start : start_index;
|
119
|
+
|
100
120
|
for(var key in regexp_states[state]) {
|
101
121
|
var re = new RegExp("^" + key + "$");
|
102
|
-
|
122
|
+
|
123
|
+
var accumulation = input.slice(slice_start, end_index);
|
124
|
+
|
125
|
+
if(re.test(accumulation)) {
|
103
126
|
var new_state = regexp_states[state][key];
|
104
127
|
highlight_edge(state, new_state);
|
105
128
|
highlight_state(new_state);
|
106
|
-
new_states.push(new_state);
|
129
|
+
new_states.push([new_state, null]);
|
107
130
|
}
|
131
|
+
|
132
|
+
// retry the same regexp with the accumulated data either way
|
133
|
+
new_states.push([state, slice_start]);
|
108
134
|
}
|
109
135
|
}
|
110
136
|
}
|
111
137
|
|
112
|
-
if(new_states.length == 0) {
|
113
|
-
return;
|
114
|
-
}
|
115
138
|
states = new_states;
|
139
|
+
start_index = end_index;
|
116
140
|
});
|
117
141
|
|
118
142
|
for(var key in states) {
|
119
|
-
var
|
143
|
+
var state_parts = states[key];
|
144
|
+
var state = state_parts[0];
|
145
|
+
var slice_start = state_parts[1];
|
146
|
+
|
147
|
+
// we must ignore ones that are still accepting more data
|
148
|
+
if (slice_start != null) continue;
|
149
|
+
|
120
150
|
if(accepting[state]) {
|
121
|
-
|
122
|
-
if(!regexp_states[mkey] && !string_states[mkey]) {
|
123
|
-
highlight_edge(state, mkey);
|
124
|
-
highlight_finish(mkey);
|
125
|
-
}
|
126
|
-
}
|
151
|
+
highlight_finish(state);
|
127
152
|
} else {
|
128
153
|
highlight_state(state, "red");
|
129
154
|
}
|
@@ -8,7 +8,7 @@
|
|
8
8
|
<%= style %>
|
9
9
|
<% end %>
|
10
10
|
</style>
|
11
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js"
|
11
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js"></script>
|
12
12
|
</head>
|
13
13
|
<body>
|
14
14
|
<div id="wrapper">
|
@@ -7,7 +7,7 @@ require "active_support/json"
|
|
7
7
|
require "rack/utils"
|
8
8
|
|
9
9
|
module ActionDispatch
|
10
|
-
|
10
|
+
module RequestCookieMethods
|
11
11
|
def cookie_jar
|
12
12
|
fetch_header("action_dispatch.cookies") do
|
13
13
|
self.cookie_jar = Cookies::CookieJar.build(self, cookies)
|
@@ -88,10 +88,14 @@ module ActionDispatch
|
|
88
88
|
# :startdoc:
|
89
89
|
end
|
90
90
|
|
91
|
-
|
91
|
+
ActiveSupport.on_load(:action_dispatch_request) do
|
92
|
+
include RequestCookieMethods
|
93
|
+
end
|
94
|
+
|
95
|
+
# Read and write data to cookies through ActionController::Base#cookies.
|
92
96
|
#
|
93
97
|
# When reading cookie data, the data is read from the HTTP request header, Cookie.
|
94
|
-
# When writing cookie data, the data is sent out in the HTTP response header, Set-Cookie
|
98
|
+
# When writing cookie data, the data is sent out in the HTTP response header, +Set-Cookie+.
|
95
99
|
#
|
96
100
|
# Examples of writing:
|
97
101
|
#
|
@@ -99,7 +103,7 @@ module ActionDispatch
|
|
99
103
|
# # This cookie will be deleted when the user's browser is closed.
|
100
104
|
# cookies[:user_name] = "david"
|
101
105
|
#
|
102
|
-
# # Cookie values are String
|
106
|
+
# # Cookie values are String-based. Other data types need to be serialized.
|
103
107
|
# cookies[:lat_lon] = JSON.generate([47.68, -122.37])
|
104
108
|
#
|
105
109
|
# # Sets a cookie that expires in 1 hour.
|
@@ -135,7 +139,7 @@ module ActionDispatch
|
|
135
139
|
#
|
136
140
|
# cookies.delete :user_name
|
137
141
|
#
|
138
|
-
# Please note that if you specify a
|
142
|
+
# Please note that if you specify a +:domain+ when setting a cookie, you must also specify the domain when deleting the cookie:
|
139
143
|
#
|
140
144
|
# cookies[:name] = {
|
141
145
|
# value: 'a yummy cookie',
|
@@ -172,6 +176,9 @@ module ActionDispatch
|
|
172
176
|
# Default is +false+.
|
173
177
|
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
|
174
178
|
# only HTTP. Defaults to +false+.
|
179
|
+
# * <tt>:same_site</tt> - The value of the +SameSite+ cookie attribute, which
|
180
|
+
# determines how this cookie should be restricted in cross-site contexts.
|
181
|
+
# Possible values are +:none+, +:lax+, and +:strict+. Defaults to +:lax+.
|
175
182
|
class Cookies
|
176
183
|
HTTP_HEADER = "Set-Cookie"
|
177
184
|
GENERATOR_KEY = "action_dispatch.key_generator"
|
@@ -195,7 +202,7 @@ module ActionDispatch
|
|
195
202
|
# Raised when storing more than 4K of session data.
|
196
203
|
CookieOverflow = Class.new StandardError
|
197
204
|
|
198
|
-
# Include in a cookie jar to allow chaining, e.g. cookies.permanent.signed
|
205
|
+
# Include in a cookie jar to allow chaining, e.g. +cookies.permanent.signed+.
|
199
206
|
module ChainedCookieJars
|
200
207
|
# Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
|
201
208
|
#
|
@@ -280,7 +287,7 @@ module ActionDispatch
|
|
280
287
|
end
|
281
288
|
end
|
282
289
|
|
283
|
-
class CookieJar
|
290
|
+
class CookieJar # :nodoc:
|
284
291
|
include Enumerable, ChainedCookieJars
|
285
292
|
|
286
293
|
def self.build(req, cookies)
|
@@ -421,7 +428,7 @@ module ActionDispatch
|
|
421
428
|
end
|
422
429
|
|
423
430
|
def write_cookie?(cookie)
|
424
|
-
request.ssl? || !cookie[:secure] || always_write_cookie
|
431
|
+
request.ssl? || !cookie[:secure] || always_write_cookie || request.host.end_with?(".onion")
|
425
432
|
end
|
426
433
|
|
427
434
|
def handle_options(options)
|
@@ -436,7 +443,7 @@ module ActionDispatch
|
|
436
443
|
|
437
444
|
if options[:domain] == :all || options[:domain] == "all"
|
438
445
|
cookie_domain = ""
|
439
|
-
dot_splitted_host = request.host.split(
|
446
|
+
dot_splitted_host = request.host.split(".", -1)
|
440
447
|
|
441
448
|
# Case where request.host is not an IP address or it's an invalid domain
|
442
449
|
# (ip confirms to the domain structure we expect so we explicitly check for ip)
|
@@ -446,10 +453,10 @@ module ActionDispatch
|
|
446
453
|
end
|
447
454
|
|
448
455
|
# If there is a provided tld length then we use it otherwise default domain.
|
449
|
-
if options[:tld_length].present?
|
456
|
+
if options[:tld_length].present?
|
450
457
|
# Case where the tld_length provided is valid
|
451
458
|
if dot_splitted_host.length >= options[:tld_length]
|
452
|
-
cookie_domain = dot_splitted_host.last(options[:tld_length]).join(
|
459
|
+
cookie_domain = dot_splitted_host.last(options[:tld_length]).join(".")
|
453
460
|
end
|
454
461
|
# Case where tld_length is not provided
|
455
462
|
else
|
@@ -458,7 +465,7 @@ module ActionDispatch
|
|
458
465
|
cookie_domain = dot_splitted_host.last(2).join(".")
|
459
466
|
# **.**, ***.** style TLDs like co.uk and com.au
|
460
467
|
else
|
461
|
-
cookie_domain = dot_splitted_host.last(3).join(
|
468
|
+
cookie_domain = dot_splitted_host.last(3).join(".")
|
462
469
|
end
|
463
470
|
end
|
464
471
|
|
@@ -676,7 +683,7 @@ module ActionDispatch
|
|
676
683
|
deserialize(name) do |rotate|
|
677
684
|
@encryptor.decrypt_and_verify(encrypted_message, on_rotation: rotate, purpose: purpose)
|
678
685
|
end
|
679
|
-
rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature
|
686
|
+
rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature, JSON::ParserError
|
680
687
|
nil
|
681
688
|
end
|
682
689
|
|