Skip to content

Commit fb2e917

Browse files
authored
Call maybe_escape_html directly in compiled call method (#2151)
* Call maybe_escape_html directly in compiled call method * add changelog * update allocation counts * use better method name * remove unused compiler accessor * ignore DS_Store
1 parent 1da654c commit fb2e917

File tree

6 files changed

+19
-26
lines changed

6 files changed

+19
-26
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
/test/sandbox/log/*
1919
/test/tmp/*
2020
/.yardoc
21+
.DS_Store
2122

2223
## Specific to RubyMotion:
2324
.dat*

docs/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ nav_order: 5
1010

1111
## main
1212

13+
* Ensure HTML output safety wrapper is used for all inline templates.
14+
15+
*Joel Hawksley*
16+
1317
* Expose `.identifier` method as part of public API.
1418

1519
*Joel Hawksley*

lib/view_component/base.rb

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,7 @@ def render_in(view_context, &block)
108108
before_render
109109

110110
if render?
111-
rendered_template =
112-
if compiler.renders_template_for?(@__vc_variant, __vc_request&.format&.to_sym)
113-
render_template_for(@__vc_variant, __vc_request&.format&.to_sym)
114-
else
115-
maybe_escape_html(render_template_for(@__vc_variant, __vc_request&.format&.to_sym)) do
116-
Kernel.warn("WARNING: The #{self.class} component rendered HTML-unsafe output. The output will be automatically escaped, but you may want to investigate.")
117-
end
118-
end.to_s
111+
rendered_template = render_template_for(@__vc_variant, __vc_request&.format&.to_sym).to_s
119112

120113
# Avoid allocating new string when output_preamble and output_postamble are blank
121114
if output_preamble.blank? && output_postamble.blank?
@@ -358,10 +351,6 @@ def safe_output_postamble
358351
end
359352
end
360353

361-
def compiler
362-
@compiler ||= self.class.compiler
363-
end
364-
365354
# Set the controller used for testing components:
366355
#
367356
# ```ruby

lib/view_component/compiler.rb

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ class Compiler
1313
def initialize(component)
1414
@component = component
1515
@lock = Mutex.new
16-
@rendered_templates = Set.new
1716
end
1817

1918
def compiled?
@@ -56,10 +55,6 @@ def compile(raise_errors: false, force: false)
5655
end
5756
end
5857

59-
def renders_template_for?(variant, format)
60-
@rendered_templates.include?([variant, format])
61-
end
62-
6358
private
6459

6560
attr_reader :templates
@@ -71,9 +66,9 @@ def define_render_template_for
7166

7267
method_body =
7368
if @templates.one?
74-
@templates.first.safe_method_name
69+
@templates.first.safe_method_name_call
7570
elsif (template = @templates.find(&:inline?))
76-
template.safe_method_name
71+
template.safe_method_name_call
7772
else
7873
branches = []
7974

@@ -88,13 +83,13 @@ def define_render_template_for
8883
].join(" && ")
8984
end
9085

91-
branches << [conditional, template.safe_method_name]
86+
branches << [conditional, template.safe_method_name_call]
9287
end
9388

9489
out = branches.each_with_object(+"") do |(conditional, branch_body), memo|
9590
memo << "#{(!memo.present?) ? "if" : "elsif"} #{conditional}\n #{branch_body}\n"
9691
end
97-
out << "else\n #{templates.find { _1.variant.nil? && _1.default_format? }.safe_method_name}\nend"
92+
out << "else\n #{templates.find { _1.variant.nil? && _1.default_format? }.safe_method_name_call}\nend"
9893
end
9994

10095
@component.silence_redefinition_of_method(:render_template_for)
@@ -196,10 +191,6 @@ def gather_templates
196191
variant: variant
197192
)
198193

199-
# TODO: We should consider inlining the HTML output safety logic into the compiled render_template_for
200-
# instead of this indirect approach
201-
@rendered_templates << [out.variant, out.this_format]
202-
203194
out
204195
end
205196

lib/view_component/template.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ def #{@call_method_name}
5959
@component.define_method(safe_method_name, @component.instance_method(@call_method_name))
6060
end
6161

62+
def safe_method_name_call
63+
return safe_method_name unless inline_call?
64+
65+
"maybe_escape_html(#{safe_method_name}) " \
66+
"{ Kernel.warn('WARNING: The #{@component} component rendered HTML-unsafe output. " \
67+
"The output will be automatically escaped, but you may want to investigate.') } "
68+
end
69+
6270
def requires_compiled_superclass?
6371
inline_call? && !defined_on_self?
6472
end

test/sandbox/test/rendering_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def test_render_inline_allocations
1515
ViewComponent::CompileCache.cache.delete(MyComponent)
1616
MyComponent.ensure_compiled
1717

18-
assert_allocations("3.4.0" => 110, "3.3.5" => 116, "3.3.0" => 129, "3.2.6" => 115, "3.1.6" => 115, "3.0.7" => 125) do
18+
assert_allocations("3.4.0" => 109, "3.3.5" => 115, "3.3.0" => 127, "3.2.6" => 114, "3.1.6" => 114, "3.0.7" => 123) do
1919
render_inline(MyComponent.new)
2020
end
2121

0 commit comments

Comments
 (0)