mirror of https://github.com/rails/rails
Request Forgery takes relative paths into account
Passing relative paths into form_for and related helpers led to invalid token generations, as the tokens did not match the request.path on the POST endpoint. Variants, such as: form_for url: * "" * "./" * "./post_one" * "post_one" are now handled according to [RFC 3986 5.2 - 5.4](https://tools.ietf.org/html/rfc3986#section-5.2) Limitations: double dots are not handled (../../path) relevant issue: #31191
This commit is contained in:
parent
05b4db2ad0
commit
e2a8bfa1f2
|
@ -1,3 +1,7 @@
|
|||
* Request Forgery takes relative paths into account.
|
||||
|
||||
*Stefan Wienert*
|
||||
|
||||
* Add ".test" as a default allowed host in development to ensure smooth golden-path setup with puma.dev.
|
||||
|
||||
*DHH*
|
||||
|
|
|
@ -635,6 +635,15 @@ module ActionController # :nodoc:
|
|||
|
||||
def normalize_action_path(action_path) # :doc:
|
||||
uri = URI.parse(action_path)
|
||||
|
||||
if uri.relative? && (action_path.blank? || !action_path.start_with?("/"))
|
||||
uri = URI.parse(request.path)
|
||||
# add the action path to the request.path
|
||||
uri.path += "/#{action_path}"
|
||||
# relative path with "./path"
|
||||
uri.path.gsub!("/./", "/")
|
||||
end
|
||||
|
||||
uri.path.chomp("/")
|
||||
end
|
||||
|
||||
|
|
|
@ -1112,6 +1112,45 @@ class PerFormTokensControllerTest < ActionController::TestCase
|
|||
assert_response :success
|
||||
end
|
||||
|
||||
def test_handles_empty_path_as_request_path
|
||||
get :index, params: { form_path: "" }
|
||||
|
||||
form_token = assert_presence_and_fetch_form_csrf_token
|
||||
|
||||
# This is required because PATH_INFO isn't reset between requests.
|
||||
@request.env["PATH_INFO"] = "/per_form_tokens"
|
||||
assert_nothing_raised do
|
||||
post :post_one, params: { custom_authenticity_token: form_token }
|
||||
end
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_handles_relative_paths
|
||||
get :index, params: { form_path: "post_one" }
|
||||
|
||||
form_token = assert_presence_and_fetch_form_csrf_token
|
||||
|
||||
# This is required because PATH_INFO isn't reset between requests.
|
||||
@request.env["PATH_INFO"] = "/per_form_tokens/post_one"
|
||||
assert_nothing_raised do
|
||||
post :post_one, params: { custom_authenticity_token: form_token }
|
||||
end
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_handles_relative_paths_with_dot
|
||||
get :index, params: { form_path: "./post_one" }
|
||||
|
||||
form_token = assert_presence_and_fetch_form_csrf_token
|
||||
|
||||
# This is required because PATH_INFO isn't reset between requests.
|
||||
@request.env["PATH_INFO"] = "/per_form_tokens/post_one"
|
||||
assert_nothing_raised do
|
||||
post :post_one, params: { custom_authenticity_token: form_token }
|
||||
end
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_ignores_origin_during_generation
|
||||
get :index, params: { form_path: "https://example.com/per_form_tokens/post_one/" }
|
||||
|
||||
|
|
Loading…
Reference in New Issue