beast rev 2066
[beast-modified.git] / vendor / plugins / white_list / lib / white_list_helper.rb
bloba147e1b63b376e406093188171931321be46ce03
1 module WhiteListHelper
2   PROTOCOL_ATTRIBUTES = Set.new %w(src href)
3   PROTOCOL_SEPARATOR  = /:|(&#0*58)|(&#x70)|(%|%)3A/
5   [:bad_tags, :tags, :attributes, :protocols].each do |attr|
6     klass = class << self; self; end
7     klass.send(:define_method, "#{attr}=") { |value| class_variable_set("@@#{attr}", Set.new(value)) }
8     define_method("white_listed_#{attr}") { ::WhiteListHelper.send(attr) }
9     mattr_reader attr
10   end
12   # This White Listing helper will html encode all tags and strip all attributes that aren't specifically allowed.  
13   # It also strips href/src tags with invalid protocols, like javascript: especially.  It does its best to counter any
14   # tricks that hackers may use, like throwing in unicode/ascii/hex values to get past the javascript: filters.  Check out
15   # the extensive test suite.
16   #
17   #   <%= white_list @article.body %>
18   # 
19   # You can add or remove tags/attributes if you want to customize it a bit.
20   # 
21   # Add table tags
22   #   
23   #   WhiteListHelper.tags.merge %w(table td th)
24   # 
25   # Remove tags
26   #   
27   #   WhiteListHelper.tags.delete 'div'
28   # 
29   # Change allowed attributes
30   # 
31   #   WhiteListHelper.attributes.merge %w(id class style)
32   # 
33   # white_list accepts a block for custom tag escaping.  Shown below is the default block that white_list uses if none is given.
34   # The block is called for all bad tags, and every text node.  node is an instance of HTML::Node (either HTML::Tag or HTML::Text).  
35   # bad is nil for text nodes inside good tags, or is the tag name of the bad tag.  
36   # 
37   #   <%= white_list(@article.body) { |node, bad| white_listed_bad_tags.include?(bad) ? nil : node.to_s.gsub(/</, '&lt;') } %>
38   #
39   def white_list(html, options = {}, &block)
40     return html if html.blank? || !html.include?('<')
41     attrs   = Set.new(options[:attributes]).merge(white_listed_attributes)
42     tags    = Set.new(options[:tags]      ).merge(white_listed_tags)
43     block ||= lambda { |node, bad| white_listed_bad_tags.include?(bad) ? nil : node.to_s.gsub(/</, '&lt;') }
44     returning [] do |new_text|
45       tokenizer = HTML::Tokenizer.new(html)
46       bad       = nil
47       while token = tokenizer.next
48         node = HTML::Node.parse(nil, 0, 0, token, false)
49         new_text << case node
50           when HTML::Tag
51             unless tags.include?(node.name)
52               bad = node.name
53               block.call node, bad
54             else
55               bad = nil
56               if node.closing != :close
57                 node.attributes.delete_if do |attr_name, value|
58                   !attrs.include?(attr_name) || (PROTOCOL_ATTRIBUTES.include?(attr_name) && contains_bad_protocols?(value))
59                 end if attributes.any?
60               end
61               node
62             end
63           else
64             block.call node, bad
65         end
66       end
67     end.join
68   end
69   
70   protected
71     def contains_bad_protocols?(value)
72       value =~ PROTOCOL_SEPARATOR && !white_listed_protocols.include?(value.split(PROTOCOL_SEPARATOR).first)
73     end
74 end
76 WhiteListHelper.bad_tags   = %w(script)
77 WhiteListHelper.tags       = %w(strong em b i p code pre tt output samp kbd var sub sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dt dd abbr acronym a img blockquote del ins fieldset legend)
78 WhiteListHelper.attributes = %w(href src width height alt cite datetime title class)
79 WhiteListHelper.protocols  = %w(ed2k ftp http https irc mailto news gopher nntp telnet webcal xmpp callto feed)