mirror of
https://github.com/haml/haml.git
synced 2022-11-09 12:33:31 -05:00
Escape tag attributes and interpolated strings when the :escape_html flag is set. Add &== and !==.
This commit is contained in:
parent
e817beea3c
commit
e1028be61a
6 changed files with 79 additions and 27 deletions
10
lib/haml.rb
10
lib/haml.rb
|
@ -696,8 +696,8 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
|
|||
#
|
||||
# ==== &=
|
||||
#
|
||||
# An ampersand followed by an equals character
|
||||
# evaluates Ruby code just like the single equals,
|
||||
# An ampersand followed by one or two equals characters
|
||||
# evaluates Ruby code just like the equals without the ampersand,
|
||||
# but sanitizes any HTML-sensitive characters in the result of the code.
|
||||
# For example:
|
||||
#
|
||||
|
@ -712,8 +712,8 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
|
|||
#
|
||||
# ==== !=
|
||||
#
|
||||
# An exclamation mark followed by an equals character
|
||||
# evaluates Ruby code just like the single equals,
|
||||
# An exclamation mark followed by one or two equals characters
|
||||
# evaluates Ruby code just like the equals would,
|
||||
# but never sanitizes the HTML.
|
||||
#
|
||||
# By default, the single equals doesn't sanitize HTML either.
|
||||
|
@ -835,7 +835,7 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
|
|||
# [<tt>:escape_html</tt>] Sets whether or not to escape HTML-sensitive characters in script.
|
||||
# If this is true, = behaves like &=;
|
||||
# otherwise, it behaves like !=.
|
||||
# <b>Note that this doesn't affect attributes or == interpolation.</b>
|
||||
# <b>Note that this escapes tag attributes.</b>
|
||||
# Defaults to false.
|
||||
#
|
||||
# [<tt>:suppress_eval</tt>] Whether or not attribute hashes and Ruby scripts
|
||||
|
|
|
@ -117,7 +117,7 @@ module Haml
|
|||
|
||||
# Takes the various information about the opening tag for an
|
||||
# element, formats it, and adds it to the buffer.
|
||||
def open_tag(name, atomic, try_one_line, preserve_tag, class_id, obj_ref, content, *attributes_hashes)
|
||||
def open_tag(name, atomic, try_one_line, preserve_tag, escape_html, class_id, obj_ref, content, *attributes_hashes)
|
||||
tabulation = @real_tabs
|
||||
|
||||
attributes = class_id
|
||||
|
@ -135,7 +135,7 @@ module Haml
|
|||
str = ">\n"
|
||||
end
|
||||
|
||||
attributes = Precompiler.build_attributes(html?, @options[:attr_wrapper], attributes)
|
||||
attributes = Precompiler.build_attributes(html?, @options[:attr_wrapper], escape_html, attributes)
|
||||
@buffer << "#{@options[:ugly] ? '' : tabs(tabulation)}<#{name}#{attributes}#{str}"
|
||||
|
||||
if content
|
||||
|
|
|
@ -293,7 +293,9 @@ module Haml
|
|||
end
|
||||
|
||||
attributes = Haml::Precompiler.build_attributes(haml_buffer.html?,
|
||||
haml_buffer.options[:attr_wrapper], attributes)
|
||||
haml_buffer.options[:attr_wrapper],
|
||||
haml_buffer.options[:escape_html],
|
||||
attributes)
|
||||
if text.nil? && block.nil?
|
||||
puts "<#{name}#{attributes} />"
|
||||
return nil
|
||||
|
|
|
@ -202,6 +202,7 @@ END
|
|||
when ELEMENT; render_tag(text)
|
||||
when COMMENT; render_comment(text)
|
||||
when SANITIZE
|
||||
return push_script(unescape_interpolation(text[3..-1].strip), false, nil, false, true) if text[1..2] == "=="
|
||||
return push_script(text[2..-1].strip, false, nil, false, true) if text[1] == SCRIPT
|
||||
push_plain text
|
||||
when SCRIPT
|
||||
|
@ -220,6 +221,7 @@ END
|
|||
when FILTER; start_filtered(text[1..-1].downcase)
|
||||
when DOCTYPE
|
||||
return render_doctype(text) if text[0...3] == '!!!'
|
||||
return push_script(unescape_interpolation(text[3..-1].strip), false) if text[1..2] == "=="
|
||||
return push_script(text[2..-1].strip, false) if text[1] == SCRIPT
|
||||
push_plain text
|
||||
when ESCAPE; push_plain text[1..-1]
|
||||
|
@ -461,7 +463,7 @@ END
|
|||
end
|
||||
|
||||
# This is a class method so it can be accessed from Buffer.
|
||||
def self.build_attributes(is_html, attr_wrapper, attributes = {})
|
||||
def self.build_attributes(is_html, attr_wrapper, escape_html, attributes = {})
|
||||
quote_escape = attr_wrapper == '"' ? """ : "'"
|
||||
other_quote_char = attr_wrapper == '"' ? "'" : '"'
|
||||
|
||||
|
@ -485,12 +487,13 @@ END
|
|||
end
|
||||
end
|
||||
" #{attr}=#{this_attr_wrapper}#{value}#{this_attr_wrapper}"
|
||||
end
|
||||
result.compact.sort.join
|
||||
end.compact.sort.join
|
||||
|
||||
escape_html ? Haml::Helpers.html_escape(result) : result
|
||||
end
|
||||
|
||||
def prerender_tag(name, self_close, attributes)
|
||||
attributes_string = Precompiler.build_attributes(html?, @options[:attr_wrapper], attributes)
|
||||
def prerender_tag(name, self_close, escape_html, attributes)
|
||||
attributes_string = Precompiler.build_attributes(html?, @options[:attr_wrapper], escape_html, attributes)
|
||||
"<#{name}#{attributes_string}#{self_close && xhtml? ? ' /' : ''}>"
|
||||
end
|
||||
|
||||
|
@ -529,7 +532,7 @@ END
|
|||
when '&', '!'
|
||||
if value[0] == ?=
|
||||
parse = true
|
||||
value = value[1..-1].strip
|
||||
value = (value[1] == ?= ? unescape_interpolation(value[2..-1].strip) : value[1..-1].strip)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -559,7 +562,7 @@ END
|
|||
# This means that we can render the tag directly to text and not process it in the buffer
|
||||
tag_closed = !value.empty? && one_liner && !parse
|
||||
|
||||
open_tag = prerender_tag(tag_name, atomic, attributes)
|
||||
open_tag = prerender_tag(tag_name, atomic, escape_html, attributes)
|
||||
open_tag << "#{value}</#{tag_name}>" if tag_closed
|
||||
open_tag << "\n" unless parse
|
||||
|
||||
|
@ -569,7 +572,7 @@ END
|
|||
flush_merged_text
|
||||
content = value.empty? || parse ? 'nil' : value.dump
|
||||
attributes_hash = ', ' + attributes_hash if attributes_hash
|
||||
push_silent "_hamlout.open_tag(#{tag_name.inspect}, #{atomic.inspect}, #{(!value.empty?).inspect}, #{preserve_tag.inspect}, #{attributes.inspect}, #{object_ref}, #{content}#{attributes_hash})"
|
||||
push_silent "_hamlout.open_tag(#{tag_name.inspect}, #{atomic.inspect}, #{(!value.empty?).inspect}, #{preserve_tag.inspect}, #{escape_html.inspect}, #{attributes.inspect}, #{object_ref}, #{content}#{attributes_hash})"
|
||||
end
|
||||
|
||||
return if atomic
|
||||
|
|
|
@ -118,36 +118,79 @@ class EngineTest < Test::Unit::TestCase
|
|||
|
||||
# HTML escaping tests
|
||||
|
||||
def test_script_ending_in_comment_should_render_when_html_is_escaped
|
||||
assert_equal("foo&bar\n", render("= 'foo&bar' #comment", :escape_html => true))
|
||||
end
|
||||
|
||||
def test_ampersand_equals
|
||||
def test_ampersand_equals_should_escape
|
||||
assert_equal("<p>\n foo & bar\n</p>\n", render("%p\n &= 'foo & bar'", :escape_html => false))
|
||||
end
|
||||
|
||||
def test_ampersand_equals_inline
|
||||
def test_ampersand_equals_inline_should_escape
|
||||
assert_equal("<p>foo & bar</p>\n", render("%p&= 'foo & bar'", :escape_html => false))
|
||||
end
|
||||
|
||||
def test_bang_equals
|
||||
def test_bang_equals_should_not_escape
|
||||
assert_equal("<p>\n foo & bar\n</p>\n", render("%p\n != 'foo & bar'", :escape_html => true))
|
||||
end
|
||||
|
||||
def test_bang_equals_inline
|
||||
def test_bang_equals_inline_should_not_escape
|
||||
assert_equal("<p>foo & bar</p>\n", render("%p!= 'foo & bar'", :escape_html => true))
|
||||
end
|
||||
|
||||
def test_escape_html_option_for_scripts
|
||||
def test_static_attributes_should_be_escaped
|
||||
assert_equal("<img class='atlantis' style='ugly&stupid' />\n",
|
||||
render("%img.atlantis{:style => 'ugly&stupid'}", :escape_html => true))
|
||||
assert_equal("<div class='atlantis' style='ugly&stupid'>foo</div>\n",
|
||||
render(".atlantis{:style => 'ugly&stupid'} foo", :escape_html => true))
|
||||
assert_equal("<p class='atlantis' style='ugly&stupid'>foo</p>\n",
|
||||
render("%p.atlantis{:style => 'ugly&stupid'}= 'foo'", :escape_html => true))
|
||||
end
|
||||
|
||||
def test_dynamic_attributes_should_be_escaped
|
||||
assert_equal("<img alt='' src='/foo.png' />\n",
|
||||
render("%img{:width => nil, :src => '/foo.png', :alt => String.new}", :escape_html => true))
|
||||
assert_equal("<p alt='' src='/foo.png'>foo</p>\n",
|
||||
render("%p{:width => nil, :src => '/foo.png', :alt => String.new} foo", :escape_html => true))
|
||||
assert_equal("<div alt='' src='/foo.png'>foo</div>\n",
|
||||
render("%div{:width => nil, :src => '/foo.png', :alt => String.new}= 'foo'", :escape_html => true))
|
||||
end
|
||||
|
||||
def test_string_interpolation_should_be_esaped
|
||||
assert_equal("<p>4&3</p>\n", render("%p== #{2+2}&#{2+1}", :escape_html => true))
|
||||
assert_equal("<p>4&3</p>\n", render("%p== #{2+2}&#{2+1}", :escape_html => false))
|
||||
end
|
||||
|
||||
def test_escaped_inline_string_interpolation
|
||||
assert_equal("<p>4&3</p>\n", render("%p&== #{2+2}&#{2+1}", :escape_html => true))
|
||||
assert_equal("<p>4&3</p>\n", render("%p&== #{2+2}&#{2+1}", :escape_html => false))
|
||||
end
|
||||
|
||||
def test_unescaped_inline_string_interpolation
|
||||
assert_equal("<p>4&3</p>\n", render("%p!== #{2+2}&#{2+1}", :escape_html => true))
|
||||
assert_equal("<p>4&3</p>\n", render("%p!== #{2+2}&#{2+1}", :escape_html => false))
|
||||
end
|
||||
|
||||
def test_escaped_string_interpolation
|
||||
assert_equal("<p>\n 4&3\n</p>\n", render("%p\n &== #{2+2}&#{2+1}", :escape_html => true))
|
||||
assert_equal("<p>\n 4&3\n</p>\n", render("%p\n &== #{2+2}&#{2+1}", :escape_html => false))
|
||||
end
|
||||
|
||||
def test_unescaped_string_interpolation
|
||||
assert_equal("<p>\n 4&3\n</p>\n", render("%p\n !== #{2+2}&#{2+1}", :escape_html => true))
|
||||
assert_equal("<p>\n 4&3\n</p>\n", render("%p\n !== #{2+2}&#{2+1}", :escape_html => false))
|
||||
end
|
||||
|
||||
def test_scripts_should_respect_escape_html_option
|
||||
assert_equal("<p>\n foo & bar\n</p>\n", render("%p\n = 'foo & bar'", :escape_html => true))
|
||||
assert_equal("<p>\n foo & bar\n</p>\n", render("%p\n = 'foo & bar'", :escape_html => false))
|
||||
end
|
||||
|
||||
def test_escape_html_option_for_inline_scripts
|
||||
def test_inline_scripts_should_respect_escape_html_option
|
||||
assert_equal("<p>foo & bar</p>\n", render("%p= 'foo & bar'", :escape_html => true))
|
||||
assert_equal("<p>foo & bar</p>\n", render("%p= 'foo & bar'", :escape_html => false))
|
||||
end
|
||||
|
||||
def test_script_ending_in_comment_should_render_when_html_is_escaped
|
||||
assert_equal("foo&bar\n", render("= 'foo&bar' #comment", :escape_html => true))
|
||||
end
|
||||
|
||||
# Options tests
|
||||
|
||||
def test_stop_eval
|
||||
|
|
|
@ -96,6 +96,10 @@ class HelperTest < Test::Unit::TestCase
|
|||
assert_equal("\"<p>13</p>\\n\"\n", render("- foo = capture_haml(13) do |a|\n %p= a\n= foo.dump"))
|
||||
end
|
||||
|
||||
def test_haml_tag_attribute_html_escaping
|
||||
assert_equal("<p id='foo&bar'>baz</p>\n", render("%p{:id => 'foo&bar'} baz", :escape_html => true))
|
||||
end
|
||||
|
||||
def test_is_haml
|
||||
assert(!ActionView::Base.new.is_haml?)
|
||||
assert_equal("true\n", render("= is_haml?"))
|
||||
|
|
Loading…
Add table
Reference in a new issue