Skip to content

Commit 24213d6

Browse files
skipkayhilbyrootjonathanhefner
committed
Prevent assigning internal ivars to AV::Base
Previously, both the `@rendered_format` and `@marked_for_same_origin_verification` instance variables would be assigned to instances of `ActionView::Base`, making them accessible in view templates. However, these instance variables are really internal to the controller and result in extra string allocations because the `@` gets stripped and readded when going through the assignment. This commit prefixes the variables with an underscore to help indicate that they are internal, and then adds them to the list of `_protected_ivars` to prevent assigning them when rendering templates. Co-authored-by: Jean Boussier <[email protected]> Co-authored-by: Jonathan Hefner <[email protected]>
1 parent 89b0a8c commit 24213d6

File tree

4 files changed

+35
-6
lines changed

4 files changed

+35
-6
lines changed

actionpack/lib/action_controller/base.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ def self.without_modules(*modules)
247247
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + %i(
248248
@_params @_response @_request @_config @_url_options @_action_has_layout @_view_context_class
249249
@_view_renderer @_lookup_context @_routes @_view_runtime @_db_runtime @_helper_proxy
250+
@_marked_for_same_origin_verification @_rendered_format
250251
)
251252

252253
def _protected_ivars

actionpack/lib/action_controller/metal/request_forgery_protection.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ def reset(request)
340340

341341
def initialize(...)
342342
super
343-
@marked_for_same_origin_verification = nil
343+
@_marked_for_same_origin_verification = nil
344344
end
345345

346346
def reset_csrf_token(request) # :doc:
@@ -414,13 +414,13 @@ def verify_same_origin_request # :doc:
414414

415415
# GET requests are checked for cross-origin JavaScript after rendering.
416416
def mark_for_same_origin_verification! # :doc:
417-
@marked_for_same_origin_verification = request.get?
417+
@_marked_for_same_origin_verification = request.get?
418418
end
419419

420420
# If the +verify_authenticity_token+ before_action ran, verify that
421421
# JavaScript responses are only served to same-origin GET requests.
422422
def marked_for_same_origin_verification? # :doc:
423-
@marked_for_same_origin_verification ||= false
423+
@_marked_for_same_origin_verification ||= false
424424
end
425425

426426
# Check for cross-origin JavaScript responses.

actionview/lib/action_view/rendering.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ module Rendering
2727
extend ActiveSupport::Concern
2828
include ActionView::ViewPaths
2929

30-
attr_reader :rendered_format
30+
attr_internal_reader :rendered_format
3131

3232
def initialize
33-
@rendered_format = nil
33+
@_rendered_format = nil
3434
super
3535
end
3636

@@ -136,7 +136,7 @@ def _render_template(options)
136136
end
137137

138138
rendered_format = rendered_template.format || lookup_context.formats.first
139-
@rendered_format = Template::Types[rendered_format]
139+
@_rendered_format = Template::Types[rendered_format]
140140

141141
rendered_template.body
142142
end

actionview/test/actionpack/controller/render_test.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ def hello_world_with_layout_false
126126
render layout: false
127127
end
128128

129+
def render_instance_variables
130+
render inline: "<%= instance_variables.sort %>"
131+
end
132+
129133
# :ported:
130134
def render_template_with_instance_variables
131135
@secret = "in the sauce"
@@ -721,6 +725,7 @@ class RenderTest < ActionController::TestCase
721725
get :render_hello_world_with_forward_slash, to: "test#render_hello_world_with_forward_slash"
722726
get :render_implicit_html_template_from_xhr_request, to: "test#render_implicit_html_template_from_xhr_request"
723727
get :render_implicit_js_template_without_layout, to: "test#render_implicit_js_template_without_layout"
728+
get :render_instance_variables, to: "test#render_instance_variables"
724729
get :render_line_offset, to: "test#render_line_offset"
725730
get :render_nothing_with_appendix, to: "test#render_nothing_with_appendix"
726731
get :render_template_in_top_directory, to: "test#render_template_in_top_directory"
@@ -785,6 +790,29 @@ def test_simple_show
785790
assert_equal "<html>Hello world!</html>", @response.body
786791
end
787792

793+
def test_controller_does_not_leak_instance_variables
794+
expected = [
795+
:@_assigns, # attr_internal on ActionView::Base
796+
:@_config, # attr_internal on ActionView::Base
797+
:@_controller, # attr_internal on ActionView::Helpers::ControllerHelper
798+
:@_default_form_builder, # attr_internal on ActionView::Helpers::FormHelper
799+
:@_ivars, # ActionController::Testing::Functional (only appears inside an ActionController::TestCase)
800+
:@_request, # attr_internal on ActionView::Helpers::ControllerHelper
801+
:@current_template, # instance variable on ActionView::Base
802+
:@lookup_context, # attr_reader on ActionView::Base
803+
:@output_buffer, # attr_accessor on ActionView::Base::Context
804+
:@variable_for_layout, # part of this test class
805+
:@view_flow, # attr_accessor on ActionView::Base::Context
806+
:@view_renderer, # attr_reader on ActionView::Base
807+
:@virtual_path, # instance variable on ActionView::Base
808+
].inspect
809+
810+
get :render_instance_variables
811+
812+
assert_response 200
813+
assert_equal expected, @response.body
814+
end
815+
788816
# :ported:
789817
def test_renders_default_template_for_missing_action
790818
get :'hyphen-ated'

0 commit comments

Comments
 (0)