require 'test_helper' require 'better_html/test_helper/safe_erb_tester' module BetterHtml module TestHelper class SafeErbTesterTest < ActiveSupport::TestCase setup do @config = BetterHtml::Config.new( javascript_safe_methods: ['j', 'escape_javascript', 'to_json'], javascript_attribute_names: [/\Aon/i, 'data-eval'], ) end test "multi line erb comments in text" do errors = parse(<<-EOF).errors text <%# this is a nice comment !@\#{$%?&*()} %> EOF assert_predicate errors, :empty? end test "multi line erb comments in html attribute" do errors = parse(<<-EOF).errors
EOF assert_predicate errors, :empty? end test "string without interpolation is safe" do errors = parse(<<-EOF).errors ')"> EOF assert_equal 0, errors.size end test "string with interpolation" do errors = parse(<<-EOF).errors "> EOF assert_equal 1, errors.size assert_equal '"hello #{name}"', errors.first.location.source assert_equal "erb interpolation in javascript attribute must call '(...).to_json'", errors.first.message end test "string with interpolation and ternary" do errors = parse(<<-EOF).errors "> EOF assert_equal 2, errors.size assert_equal '"hello #{foo ? bar : baz}" if bla?', errors.first.location.source assert_equal "erb interpolation in javascript attribute must call '(...).to_json'", errors.first.message assert_equal '"hello #{foo ? bar : baz}" if bla?', errors.first.location.source assert_equal "erb interpolation in javascript attribute must call '(...).to_json'", errors.first.message end test "plain erb tag in html attribute" do errors = parse(<<-EOF).errors EOF assert_equal 1, errors.size assert_equal 'unsafe', errors.first.location.source assert_equal "erb interpolation in javascript attribute must call '(...).to_json'", errors.first.message end test "to_json is safe in html attribute" do errors = parse(<<-EOF).errors EOF assert_predicate errors, :empty? end test "ternary with safe javascript escaping" do errors = parse(<<-EOF).errors EOF assert_predicate errors, :empty? end test "ternary with unsafe javascript escaping" do errors = parse(<<-EOF).errors EOF assert_equal 1, errors.size assert_equal 'foo ? bar : j(baz)', errors.first.location.source assert_equal "erb interpolation in javascript attribute must call '(...).to_json'", errors.first.message end test "j is safe in html attribute" do errors = parse(<<-EOF).errors EOF assert_predicate errors, :empty? end test "j() is safe in html attribute" do errors = parse(<<-EOF).errors EOF assert_predicate errors, :empty? end test "escape_javascript is safe in html attribute" do errors = parse(<<-EOF).errors EOF assert_predicate errors, :empty? end test "escape_javascript() is safe in html attribute" do errors = parse(<<-EOF).errors EOF assert_predicate errors, :empty? end test "html_safe is never safe in html attribute, even non javascript attributes like href" do errors = parse(<<-EOF).errors EOF assert_equal 1, errors.size assert_equal 'unsafe.html_safe', errors.first.location.source assert_equal "erb interpolation with '<%= (...).html_safe %>' inside html attribute is never safe", errors.first.message end test "html_safe is never safe in html attribute, even with to_json" do errors = parse(<<-EOF).errors EOF assert_equal 1, errors.size assert_equal 'unsafe.to_json.html_safe', errors.first.location.source assert_equal "erb interpolation with '<%= (...).html_safe %>' inside html attribute is never safe", errors.first.message end test "<%== is never safe in html attribute, even non javascript attributes like href" do errors = parse(<<-EOF).errors EOF assert_equal 1, errors.size assert_equal '<%== unsafe %>', errors.first.location.source assert_includes "erb interpolation with '<%==' inside html attribute is never safe", errors.first.message end test "<%== is never safe in html attribute, even with to_json" do errors = parse(<<-EOF).errors EOF assert_equal 1, errors.size assert_equal '<%== unsafe.to_json %>', errors.first.location.source assert_includes "erb interpolation with '<%==' inside html attribute is never safe", errors.first.message end test "raw is never safe in html attribute, even non javascript attributes like href" do errors = parse(<<-EOF).errors EOF assert_equal 1, errors.size assert_equal 'raw unsafe', errors.first.location.source assert_equal "erb interpolation with '<%= raw(...) %>' inside html attribute is never safe", errors.first.message end test "raw is never safe in html attribute, even with to_json" do errors = parse(<<-EOF).errors EOF assert_equal 1, errors.size assert_equal 'raw unsafe.to_json', errors.first.location.source assert_equal "erb interpolation with '<%= raw(...) %>' inside html attribute is never safe", errors.first.message end test "unsafe erb in EOF assert_equal 1, errors.size assert_equal '<%= unsafe %>', errors.first.location.source assert_equal "erb interpolation in javascript tag must call '(...).to_json'", errors.first.message end test "unsafe erb in javascript template" do errors = parse(<<-JS, template_language: :javascript).errors if (a < 1) { <%= unsafe %> } JS assert_equal 1, errors.size assert_equal '<%= unsafe %>', errors.first.location.source assert_equal "erb interpolation in javascript tag must call '(...).to_json'", errors.first.message end test " EOF assert_equal 1, errors.size assert_equal '<%= "unsafe" %>', errors.first.location.source assert_equal "erb interpolation in javascript tag must call '(...).to_json'", errors.first.message end test "javascript template without calls is unsafe" do errors = parse(<<-JS, template_language: :javascript).errors if (a < 1) { <%= "unsafe" %> } JS assert_equal 1, errors.size assert_equal '<%= "unsafe" %>', errors.first.location.source assert_equal "erb interpolation in javascript tag must call '(...).to_json'", errors.first.message end test "unsafe erb in javascript_tag" do errors = parse(<<-EOF).errors <%= javascript_tag do %> if (a < 1) { <%= unsafe %> } <% end %> EOF assert_equal 1, errors.size assert_equal '<%= javascript_tag do %>', errors.first.location.source assert_includes "'javascript_tag do' syntax is deprecated; use inline EOF assert_equal 1, errors.size assert_equal '<%= unsafe %>', errors.first.location.source assert_equal "erb interpolation in javascript tag must call '(...).to_json'", errors.first.message end test " EOF assert_predicate errors, :empty? end test "statements not allowed in script tags" do errors = parse(<<-EOF).errors EOF assert_equal 1, errors.size assert_equal "<% if foo? %>", errors.first.location.source assert_equal "erb statement not allowed here; did you mean '<%=' ?", errors.first.message end test "statements not allowed in javascript template" do errors = parse(<<-JS, template_language: :javascript).errors <% if foo %> bla <% end %> JS assert_equal 1, errors.size assert_equal "<% if foo %>", errors.first.location.source assert_equal "erb statement not allowed here; did you mean '<%=' ?", errors.first.message end test "erb comments allowed in scripts" do errors = parse(<<-EOF).errors EOF assert_predicate errors, :empty? end test "script tag without content" do errors = parse(<<-EOF).errors EOF assert_predicate errors, :empty? end test "statement after script regression" do errors = parse(<<-EOF).errors <% if condition? %> EOF assert_predicate errors, :empty? end test " EOF assert_predicate errors, :empty? end test "javascript template with to_json is safe" do errors = parse(<<-JS, template_language: :javascript).errors <%= unsafe.to_json %> JS assert_predicate errors, :empty? end test " EOF assert_predicate errors, :empty? end test "javascript template with raw and to_json is safe" do errors = parse(<<-JS, template_language: :javascript).errors <%= raw unsafe.to_json %> JS assert_predicate errors, :empty? end test "end statements are allowed in script tags" do errors = parse(<<-EOF).errors EOF assert_predicate errors, :empty? end test "statements are allowed in text/html tags" do errors = parse(<<-EOF).errors EOF assert_predicate errors, :empty? end test "unsafe javascript methods in helper calls with new hash syntax" do errors = parse(<<-EOF).errors <%= ui_my_helper(:foo, onclick: "alert(\#{unsafe})", onmouseover: "alert(\#{unsafe.to_json})") %> EOF assert_equal 1, errors.size assert_equal "\#{unsafe}", errors[0].location.source assert_equal "erb interpolation in javascript attribute must call '(...).to_json'", errors[0].message end test "unsafe javascript methods in helper calls with old hash syntax" do errors = parse(<<-EOF).errors <%= ui_my_helper(:foo, :onclick => "alert(\#{unsafe})") %> EOF assert_equal 1, errors.size assert_equal "\#{unsafe}", errors.first.location.source assert_equal "erb interpolation in javascript attribute must call '(...).to_json'", errors.first.message end test "unsafe javascript methods in helper calls with string as key" do errors = parse(<<-EOF).errors <%= ui_my_helper(:foo, 'data-eval' => "alert(\#{unsafe})") %> EOF assert_equal 1, errors.size assert_equal "\#{unsafe}", errors.first.location.source assert_equal "erb interpolation in javascript attribute must call '(...).to_json'", errors.first.message end test "unsafe javascript methods in helper calls with nested data key" do errors = parse(<<-EOF).errors <%= ui_my_helper(:foo, data: { eval: "alert(\#{unsafe})" }) %> EOF assert_equal 1, errors.size assert_equal "\#{unsafe}", errors.first.location.source assert_equal "erb interpolation in javascript attribute must call '(...).to_json'", errors.first.message end test "ivar missing .to_json is unsafe" do errors = parse('').errors assert_equal 1, errors.size assert_equal "<%= @feature.html_safe %>", errors.first.location.source assert_equal "erb interpolation in javascript tag must call '(...).to_json'", errors.first.message end private def parse(data, template_language: :html) SafeErbTester::Tester.new(data, config: @config, template_language: template_language) end end end end