Skip to content

Commit f40457a

Browse files
authored
Merge branch 'main' into Ridhwana/active-record-validations
2 parents 761cac6 + edf63be commit f40457a

File tree

127 files changed

+2291
-1042
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

127 files changed

+2291
-1042
lines changed

actionmailer/lib/action_mailer/mail_helper.rb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,18 @@ def block_format(text)
2525
}.join("\n\n")
2626

2727
# Make list points stand on their own line
28-
formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { " #{$1} #{$2.strip}\n" }
29-
formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { " #{$1} #{$2.strip}\n" }
28+
output = +""
29+
splits = formatted.split(/(\*+|\#+)/)
30+
while line = splits.shift
31+
if line.start_with?("*", "#") && splits.first&.start_with?(" ")
32+
output.chomp!(" ") while output.end_with?(" ")
33+
output << " #{line} #{splits.shift.strip}\n"
34+
else
35+
output << line
36+
end
37+
end
3038

31-
formatted
39+
output
3240
end
3341

3442
# Access the mailer instance.

actionmailer/test/mail_helper_test.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,17 @@ def test_use_cache
121121
assert_equal "Greetings from a cache helper block", mail.body.encoded
122122
end
123123
end
124+
125+
def helper
126+
Object.new.extend(ActionMailer::MailHelper)
127+
end
128+
129+
def test_block_format
130+
assert_equal " * foo\n", helper.block_format(" * foo")
131+
assert_equal " * foo\n", helper.block_format(" * foo")
132+
assert_equal " * foo\n", helper.block_format("* foo")
133+
assert_equal " * foo\n*bar", helper.block_format("* foo*bar")
134+
assert_equal " * foo\n * bar\n", helper.block_format("* foo * bar")
135+
assert_equal " *", helper.block_format("* ")
136+
end
124137
end

actionpack/lib/action_controller/metal/http_authentication.rb

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -513,14 +513,11 @@ def rewrite_param_values(array_params)
513513
array_params.each { |param| (param[1] || +"").gsub! %r/^"|"$/, "" }
514514
end
515515

516-
WHITESPACED_AUTHN_PAIR_DELIMITERS = /\s*#{AUTHN_PAIR_DELIMITERS}\s*/
517-
private_constant :WHITESPACED_AUTHN_PAIR_DELIMITERS
518-
519516
# This method takes an authorization body and splits up the key-value pairs by
520517
# the standardized `:`, `;`, or `\t` delimiters defined in
521518
# `AUTHN_PAIR_DELIMITERS`.
522519
def raw_params(auth)
523-
_raw_params = auth.sub(TOKEN_REGEX, "").split(WHITESPACED_AUTHN_PAIR_DELIMITERS)
520+
_raw_params = auth.sub(TOKEN_REGEX, "").split(AUTHN_PAIR_DELIMITERS).map(&:strip)
524521
_raw_params.reject!(&:empty?)
525522

526523
if !_raw_params.first&.start_with?(TOKEN_KEY)

actionpack/lib/action_controller/metal/strong_parameters.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ class InvalidParameterKey < ArgumentError
9595
# * `permit` to filter params for mass assignment.
9696
# * `require` to require a parameter or raise an error.
9797
#
98+
# Examples:
99+
#
98100
# params = ActionController::Parameters.new({
99101
# person: {
100102
# name: "Francesco",
@@ -109,7 +111,7 @@ class InvalidParameterKey < ArgumentError
109111
# Person.first.update!(permitted)
110112
# # => #<Person id: 1, name: "Francesco", age: 22, role: "user">
111113
#
112-
# Paramaters provides two options that control the top-level behavior of new
114+
# Parameters provides two options that control the top-level behavior of new
113115
# instances:
114116
#
115117
# * `permit_all_parameters` - If it's `true`, all the parameters will be

actionpack/lib/action_dispatch/http/filter_parameters.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,17 @@ def parameter_filter_for(filters) # :doc:
6868
ActiveSupport::ParameterFilter.new(filters)
6969
end
7070

71-
KV_RE = "[^&;=]+"
72-
PAIR_RE = %r{(#{KV_RE})=(#{KV_RE})}
7371
def filtered_query_string # :doc:
74-
query_string.gsub(PAIR_RE) do |_|
75-
parameter_filter.filter($1 => $2).first.join("=")
72+
parts = query_string.split(/([&;])/)
73+
filtered_parts = parts.map do |part|
74+
if part.include?("=")
75+
key, value = part.split("=", 2)
76+
parameter_filter.filter(key => value).first.join("=")
77+
else
78+
part
79+
end
7680
end
81+
filtered_parts.join("")
7782
end
7883
end
7984
end

actionpack/lib/action_dispatch/http/filter_redirect.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,16 @@ def location_filter_match?
3737
def parameter_filtered_location
3838
uri = URI.parse(location)
3939
unless uri.query.nil? || uri.query.empty?
40-
uri.query.gsub!(FilterParameters::PAIR_RE) do
41-
request.parameter_filter.filter($1 => $2).first.join("=")
40+
parts = uri.query.split(/([&;])/)
41+
filtered_parts = parts.map do |part|
42+
if part.include?("=")
43+
key, value = part.split("=", 2)
44+
request.parameter_filter.filter(key => value).first.join("=")
45+
else
46+
part
47+
end
4248
end
49+
uri.query = filtered_parts.join("")
4350
end
4451
uri.to_s
4552
rescue URI::Error

actionpack/lib/action_dispatch/http/param_builder.rb

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
module ActionDispatch
44
class ParamBuilder
5+
# --
6+
# This implementation is based on Rack::QueryParser,
7+
# Copyright (C) 2007-2021 Leah Neukirchen <http://leahneukirchen.org/infopage.html>
8+
59
def self.make_default(param_depth_limit)
610
new param_depth_limit
711
end
@@ -12,6 +16,10 @@ def initialize(param_depth_limit)
1216
@param_depth_limit = param_depth_limit
1317
end
1418

19+
cattr_accessor :ignore_leading_brackets
20+
21+
LEADING_BRACKETS_COMPAT = defined?(::Rack::RELEASE) && ::Rack::RELEASE.to_s.start_with?("2.")
22+
1523
cattr_accessor :default
1624
self.default = make_default(100)
1725

@@ -61,15 +69,30 @@ def store_nested_param(params, name, v, depth, encoding_template = nil)
6169
# nil name, treat same as empty string (required by tests)
6270
k = after = ""
6371
elsif depth == 0
64-
# Start of parsing, don't treat [] or [ at start of string specially
65-
if start = name.index("[", 1)
66-
# Start of parameter nesting, use part before brackets as key
67-
k = name[0, start]
68-
after = name[start, name.length]
72+
if ignore_leading_brackets || (ignore_leading_brackets.nil? && LEADING_BRACKETS_COMPAT)
73+
# Rack 2 compatible behavior, ignore leading brackets
74+
if name =~ /\A[\[\]]*([^\[\]]+)\]*/
75+
k = $1
76+
after = $' || ""
77+
78+
if !ignore_leading_brackets && (k != $& || !after.empty? && !after.start_with?("["))
79+
ActionDispatch.deprecator.warn("Skipping over leading brackets in parameter name #{name.inspect} is deprecated and will parse differently in Rails 8.1 or Rack 3.0.")
80+
end
81+
else
82+
k = name
83+
after = ""
84+
end
6985
else
70-
# Plain parameter with no nesting
71-
k = name
72-
after = ""
86+
# Start of parsing, don't treat [] or [ at start of string specially
87+
if start = name.index("[", 1)
88+
# Start of parameter nesting, use part before brackets as key
89+
k = name[0, start]
90+
after = name[start, name.length]
91+
else
92+
# Plain parameter with no nesting
93+
k = name
94+
after = ""
95+
end
7396
end
7497
elsif name.start_with?("[]")
7598
# Array nesting

actionpack/lib/action_dispatch/http/query_parser.rb

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
# frozen_string_literal: true
22

33
require "uri"
4+
require "rack"
45

56
module ActionDispatch
67
class QueryParser
78
DEFAULT_SEP = /& */n
8-
COMMON_SEP = { ";" => /; */n, ";," => /[;,] */n, "&" => /& */n }
9+
COMPAT_SEP = /[&;] */n
10+
COMMON_SEP = { ";" => /; */n, ";," => /[;,] */n, "&" => /& */n, "&;" => /[&;] */n }
11+
12+
cattr_accessor :strict_query_string_separator
13+
14+
SEMICOLON_COMPAT = defined?(::Rack::QueryParser::DEFAULT_SEP) && ::Rack::QueryParser::DEFAULT_SEP.to_s.include?(";")
915

1016
#--
1117
# Note this departs from WHATWG's specified parsing algorithm by
@@ -14,7 +20,23 @@ class QueryParser
1420
def self.each_pair(s, separator = nil)
1521
return enum_for(:each_pair, s, separator) unless block_given?
1622

17-
(s || "").split(separator ? (COMMON_SEP[separator] || /[#{separator}] */n) : DEFAULT_SEP).each do |part|
23+
s ||= ""
24+
25+
splitter =
26+
if separator
27+
COMMON_SEP[separator] || /[#{separator}] */n
28+
elsif strict_query_string_separator
29+
DEFAULT_SEP
30+
elsif SEMICOLON_COMPAT && s.include?(";")
31+
if strict_query_string_separator.nil?
32+
ActionDispatch.deprecator.warn("Using semicolon as a query string separator is deprecated and will not be supported in Rails 8.1 or Rack 3.0. Use `&` instead.")
33+
end
34+
COMPAT_SEP
35+
else
36+
DEFAULT_SEP
37+
end
38+
39+
s.split(splitter).each do |part|
1840
next if part.empty?
1941

2042
k, v = part.split("=", 2)

actionpack/lib/action_dispatch/journey/scanner.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def last_literal
5555
def scan
5656
next_byte = @scanner.peek_byte
5757
case
58-
when (token = STATIC_TOKENS[next_byte])
58+
when (token = STATIC_TOKENS[next_byte]) && (token != :SYMBOL || next_byte_is_not_a_token?)
5959
@scanner.pos += 1
6060
@length = @scanner.skip(/\w+/).to_i + 1 if token == :SYMBOL || token == :STAR
6161
token
@@ -65,6 +65,10 @@ def scan
6565
:LITERAL
6666
end
6767
end
68+
69+
def next_byte_is_not_a_token?
70+
!STATIC_TOKENS[@scanner.string.getbyte(@scanner.pos + 1)]
71+
end
6872
end
6973
end
7074
end

actionpack/lib/action_dispatch/railtie.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ class Railtie < Rails::Railtie # :nodoc:
3131
config.action_dispatch.debug_exception_log_level = :fatal
3232
config.action_dispatch.strict_freshness = false
3333

34+
config.action_dispatch.ignore_leading_brackets = nil
35+
config.action_dispatch.strict_query_string_separator = nil
36+
3437
config.action_dispatch.default_headers = {
3538
"X-Frame-Options" => "SAMEORIGIN",
3639
"X-XSS-Protection" => "1; mode=block",
@@ -52,6 +55,9 @@ class Railtie < Rails::Railtie # :nodoc:
5255
ActionDispatch::Http::URL.secure_protocol = app.config.force_ssl
5356
ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length
5457

58+
ActionDispatch::ParamBuilder.ignore_leading_brackets = app.config.action_dispatch.ignore_leading_brackets
59+
ActionDispatch::QueryParser.strict_query_string_separator = app.config.action_dispatch.strict_query_string_separator
60+
5561
ActiveSupport.on_load(:action_dispatch_request) do
5662
self.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
5763
ActionDispatch::Request::Utils.perform_deep_munge = app.config.action_dispatch.perform_deep_munge

0 commit comments

Comments
 (0)