public
Description: HTML Abstraction Markup Language - A Markup Haiku
Homepage: http://haml.hamptoncatlin.com
Clone URL: git://github.com/nex3/haml.git
Adds HTML escaping to Haml.
Lines starting '&=' are always escaped.
Lines starting '!=' are never escaped.
Lines starting '=' are escaped only if the option :escape_html => true is 
passed to the Haml engine.
indirect (author)
Fri Mar 14 16:39:19 -0700 2008
nex3 (committer)
Sun Mar 16 15:10:49 -0700 2008
commit  bee4f2aa5dd1d7ac9ed250d6b9584e54d99ffba2
tree    8601c4c4e609a59f156174ac85364823ed29363d
parent  b8614eae6fb2f3df9fd74acc24b9bc8c9a441d06
...
78
79
80
81
 
82
83
84
...
93
94
95
 
 
96
97
98
...
78
79
80
 
81
82
83
84
...
93
94
95
96
97
98
99
100
0
@@ -78,7 +78,7 @@ module Haml
0
 
0
     # Properly formats the output of a script that was run in the
0
     # instance_eval.
0
- def push_script(result, preserve_script, close_tag = nil, preserve_tag = false)
0
+ def push_script(result, preserve_script, close_tag = nil, preserve_tag = false, escape_html = false)
0
       tabulation = @real_tabs
0
 
0
       if preserve_tag
0
@@ -93,6 +93,8 @@ module Haml
0
         result = result[0...-1]
0
       end
0
       
0
+ result = html_escape(result) if escape_html
0
+
0
       if close_tag && (@options[:ugly] || Buffer.one_liner?(result) || preserve_tag)
0
         @buffer << "#{result}</#{close_tag}>\n"
0
         @real_tabs -= 1
...
72
73
74
75
 
 
76
77
78
...
72
73
74
 
75
76
77
78
79
0
@@ -72,7 +72,8 @@ module Haml
0
           'markdown' => Haml::Filters::Markdown },
0
         :filename => '(haml)',
0
         :ugly => false,
0
- :format => :xhtml
0
+ :format => :xhtml,
0
+ :escape_html => false
0
       }
0
       @options.rec_merge! options
0
 
...
218
219
220
 
 
 
 
 
221
222
223
...
218
219
220
221
222
223
224
225
226
227
228
0
@@ -218,6 +218,11 @@ END
0
                 'Output format. Can be xhtml (default), html4, or html5.') do |name|
0
           @options[:for_engine][:format] = name.to_sym
0
         end
0
+
0
+ opts.on('-e', '--escape-html',
0
+ 'Escape HTML characters (like ampersands and angle brackets) by default.') do
0
+ @options[:for_engine][:escape_html] = true
0
+ end
0
       end
0
 
0
       def process_result
...
322
323
324
325
 
 
 
 
 
 
 
 
 
 
326
327
328
...
322
323
324
 
325
326
327
328
329
330
331
332
333
334
335
336
337
0
@@ -322,7 +322,16 @@ Use the #haml_tag method instead.
0
 END
0
       haml_tag(*args, &block)
0
     end
0
-
0
+
0
+ # Returns a copy of <tt>text</tt> with ampersands, angle brackets and quotes
0
+ # escaped into HTML entities.
0
+ def html_escape(text)
0
+ text.to_s.gsub(/[&<>"]/) { |s| ESCAPE_TABLE[s] }
0
+ end
0
+ # Characters that need to be escaped to HTML entities from user input
0
+ ESCAPE_TABLE = { '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;', '"'=>'&quot;', "'"=>'&#039;', }
0
+
0
+
0
     private
0
 
0
     # Gets a reference to the current Haml::Buffer object.
...
20
21
22
 
 
 
23
24
25
...
47
48
49
 
50
51
52
...
197
198
199
 
 
 
200
201
202
 
 
 
 
 
203
204
205
...
212
213
214
 
215
216
217
...
319
320
321
322
 
323
324
325
326
327
328
 
329
330
331
...
496
497
498
499
 
500
501
502
...
517
518
519
 
 
 
 
 
520
521
 
522
523
524
525
526
 
 
527
528
529
...
566
567
568
569
 
570
571
572
...
695
696
697
698
699
700
701
...
20
21
22
23
24
25
26
27
28
...
50
51
52
53
54
55
56
...
201
202
203
204
205
206
207
208
 
209
210
211
212
213
214
215
216
...
223
224
225
226
227
228
229
...
331
332
333
 
334
335
336
337
338
339
 
340
341
342
343
...
508
509
510
 
511
512
513
514
...
529
530
531
532
533
534
535
536
537
 
538
539
540
541
542
543
544
545
546
547
548
...
585
586
587
 
588
589
590
591
...
714
715
716
 
717
718
719
0
@@ -20,6 +20,9 @@ module Haml
0
     # Designates script, the result of which is output.
0
     SCRIPT = ?=
0
 
0
+ # Designates script that is always be HTML-escaped.
0
+ SANITIZE = ?&
0
+
0
     # Designates script, the result of which is flattened and output.
0
     FLAT_SCRIPT = ?~
0
 
0
@@ -47,6 +50,7 @@ module Haml
0
       COMMENT,
0
       DOCTYPE,
0
       SCRIPT,
0
+ SANITIZE,
0
       FLAT_SCRIPT,
0
       SILENT_SCRIPT,
0
       ESCAPE,
0
@@ -197,9 +201,16 @@ END
0
       when DIV_CLASS, DIV_ID; render_div(text)
0
       when ELEMENT; render_tag(text)
0
       when COMMENT; render_comment(text)
0
+ when ?&
0
+ return push_script(text[2..-1].strip, false, nil, false, true) if text[1] == SCRIPT
0
+ push_plain text
0
       when SCRIPT
0
         return push_script(unescape_interpolation(text[2..-1].strip), false) if text[1] == SCRIPT
0
- push_script(text[1..-1], false)
0
+ if options[:escape_html]
0
+ push_script(text[1..-1], false, nil, false, true)
0
+ else
0
+ push_script(text[1..-1], false)
0
+ end
0
       when FLAT_SCRIPT; push_flat_script(text[1..-1])
0
       when SILENT_SCRIPT
0
         return start_haml_comment if text[1] == SILENT_COMMENT
0
@@ -212,6 +223,7 @@ END
0
       when FILTER; start_filtered(text[1..-1].downcase)
0
       when DOCTYPE
0
         return render_doctype(text) if text[0...3] == '!!!'
0
+ return push_script(text[2..-1].strip, false) if text[1] == SCRIPT
0
         push_plain text
0
       when ESCAPE; push_plain text[1..-1]
0
       else push_plain text
0
@@ -319,13 +331,13 @@ END
0
     #
0
     # If <tt>preserve_script</tt> is true, Haml::Helpers#find_and_flatten is run on
0
     # the result before it is added to <tt>@buffer</tt>
0
- def push_script(text, preserve_script, close_tag = nil, preserve_tag = false)
0
+ def push_script(text, preserve_script, close_tag = nil, preserve_tag = false, escape_html = false)
0
       flush_merged_text
0
       return if options[:suppress_eval]
0
 
0
       push_silent "haml_temp = #{text}"
0
       newline true
0
- out = "haml_temp = _hamlout.push_script(haml_temp, #{preserve_script.inspect}, #{close_tag.inspect}, #{preserve_tag.inspect});"
0
+ out = "haml_temp = _hamlout.push_script(haml_temp, #{preserve_script.inspect}, #{close_tag.inspect}, #{preserve_tag.inspect}, #{escape_html.inspect});"
0
       if @block_opened
0
         push_and_tabulate([:loud, out])
0
       else
0
@@ -496,7 +508,7 @@ END
0
       end
0
       if rest
0
         object_ref, rest = balance(rest, ?[, ?]) if rest[0] == ?[
0
- action, value = rest.scan(/([=\/\~]?)?(.*)?/)[0]
0
+ action, value = rest.scan(/([=\/\~&!]?)?(.*)?/)[0]
0
       end
0
       value = value.to_s.strip
0
       [tag_name, attributes, attributes_hash, object_ref, action, value]
0
@@ -517,13 +529,20 @@ END
0
       when '='
0
         parse = true
0
         value = unescape_interpolation(value[1..-1].strip) if value[0] == ?=
0
+ when '&', '!'
0
+ if value[0] == ?=
0
+ parse = true
0
+ value = value[1..-1].strip
0
+ end
0
       end
0
-
0
+
0
       if parse && @options[:suppress_eval]
0
         parse = false
0
         value = ''
0
       end
0
 
0
+ escape_html = (action == '&' || (action != '!' && @options[:escape_html]))
0
+
0
       object_ref = "nil" if object_ref.nil? || @options[:suppress_eval]
0
 
0
       static_attributes = parse_static_hash(attributes_hash) # Try pre-compiling a static attributes hash
0
@@ -566,7 +585,7 @@ END
0
       
0
       if parse
0
         flush_merged_text
0
- push_script(value, preserve_script, tag_name, preserve_tag)
0
+ push_script(value, preserve_script, tag_name, preserve_tag, escape_html)
0
       end
0
     end
0
 
0
@@ -695,7 +714,6 @@ END
0
 
0
       raise SyntaxError.new("Unbalanced brackets.")
0
     end
0
-
0
 
0
     # Counts the tabulation of a line.
0
     def count_soft_tabs(line)
...
116
117
118
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
120
121
...
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
0
@@ -116,6 +116,38 @@ class EngineTest < Test::Unit::TestCase
0
                  render("%p{:foo => 'bar', :bar => false, :baz => 'false'}", :format => :xhtml))
0
   end
0
 
0
+ # HTML escaping tests
0
+
0
+ def test_script_ending_in_comment_should_render_when_html_is_escaped
0
+ assert_equal("foo&amp;bar\n", render("= 'foo&bar' #comment", :escape_html => true))
0
+ end
0
+
0
+ def test_ampersand_equals
0
+ assert_equal("<p>\n foo &amp; bar\n</p>\n", render("%p\n &= 'foo & bar'", :escape_html => false))
0
+ end
0
+
0
+ def test_ampersand_equals_inline
0
+ assert_equal("<p>foo &amp; bar</p>\n", render("%p&= 'foo & bar'", :escape_html => false))
0
+ end
0
+
0
+ def test_bang_equals
0
+ assert_equal("<p>\n foo & bar\n</p>\n", render("%p\n != 'foo & bar'", :escape_html => true))
0
+ end
0
+
0
+ def test_bang_equals_inline
0
+ assert_equal("<p>foo & bar</p>\n", render("%p!= 'foo & bar'", :escape_html => true))
0
+ end
0
+
0
+ def test_escape_html_option_for_scripts
0
+ assert_equal("<p>\n foo &amp; bar\n</p>\n", render("%p\n = 'foo & bar'", :escape_html => true))
0
+ assert_equal("<p>\n foo & bar\n</p>\n", render("%p\n = 'foo & bar'", :escape_html => false))
0
+ end
0
+
0
+ def test_escape_html_option_for_inline_scripts
0
+ assert_equal("<p>foo &amp; bar</p>\n", render("%p= 'foo & bar'", :escape_html => true))
0
+ assert_equal("<p>foo & bar</p>\n", render("%p= 'foo & bar'", :escape_html => false))
0
+ end
0
+
0
   # Options tests
0
 
0
   def test_stop_eval

Comments

    No one has commented yet.