rails-html-sanitizer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6d6f1afa8f121b7bd01339489da71e5772d243a2
4
+ data.tar.gz: 3f7ff018d2079f1e09d92922f7fbbfbd18908508
5
+ SHA512:
6
+ metadata.gz: d65dcca2b41249b2fa4a100731393d732de9ff62925bcf681a3b2cbd5498f8db3b913a6c9ce09c0952f5719370b2e610abe88442f365c8b15b456d234466e452
7
+ data.tar.gz: c4a1b21ccd375a212c1be12212c23963f7075e5b22f10a8ff470c040123f90b82c69ac8ea87041e7cc5de35b8e70fced35f7fd2d453cb37cdfe96ad8ebadc747
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 1.0.0
2
+
3
+ * First release.
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Rafael Mendonça França, Kasper Timm Hansen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # Rails Html Sanitizers
2
+
3
+ In Rails 5 this gem will be responsible for sanitizing HTML fragments in Rails applications,
4
+ i.e. in the `sanitize`, `sanitize_css`, `strip_tags` and `strip_links` methods.
5
+
6
+ Include it in your Gemfile now to test for any incompatibilities and enjoy a safer and cleaner future.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'rails-html-sanitizer'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install rails-html-sanitizer
21
+
22
+ ## Usage
23
+
24
+ ### Sanitizers
25
+
26
+ All sanitizers respond to `sanitize`.
27
+
28
+ #### FullSanitizer
29
+
30
+ ```ruby
31
+ full_sanitizer = Rails::Html::FullSanitizer.new
32
+ full_sanitizer.sanitize("<b>Bold</b> no more! <a href='more.html'>See more here</a>...")
33
+ # => Bold no more! See more here...
34
+ ```
35
+
36
+ #### LinkSanitizer
37
+
38
+ ```ruby
39
+ link_sanitizer = Rails::Html::LinkSanitizer.new
40
+ link_sanitizer.sanitize('<a href="example.com">Only the link text will be kept.</a>')
41
+ # => Only the link text will be kept.
42
+ ```
43
+
44
+ #### WhiteListSanitizer
45
+
46
+ ```ruby
47
+ white_list_sanitizer = Rails::Html::WhiteListSanitizer.new
48
+
49
+ # sanitize via an extensive white list of allowed elements
50
+ white_list_sanitizer.sanitize(@article.body)
51
+
52
+ # white list only the supplied tags and attributes
53
+ white_list_sanitizer.sanitize(@article.body, tags: %w(table tr td), attributes: %w(id class style))
54
+
55
+ # white list via a custom scrubber
56
+ white_list_sanitizer.sanitize(@article.body, scrubber: ArticleScrubber.new)
57
+
58
+ # white list sanitizer can also sanitize css
59
+ white_list_sanitizer.sanitize_css('background-color: #000;')
60
+ ```
61
+
62
+ ### Scrubbers
63
+
64
+ Scrubbers are objects responsible for removing nodes or attributes you don't want in your HTML document.
65
+
66
+ This gem includes two scrubbers `Rails::Html::PermitScrubber` and `Rails::Html::TargetScrubber`.
67
+
68
+ #### `Rails::Html::PermitScrubber`
69
+
70
+ This scrubber allows you to permit only the tags and attributes you want.
71
+
72
+ ```ruby
73
+ scrubber = Rails::Html::PermitScrubber.new
74
+ scrubber.tags = ['a']
75
+
76
+ html_fragment = Loofah.fragment('<a><img/ ></a>')
77
+ html_fragment.scrub!(scrubber)
78
+ html_fragment.to_s # => "<a></a>"
79
+ ```
80
+
81
+ #### `Rails::Html::TargetScrubber`
82
+
83
+ Where `PermitScrubber` picks out tags and attributes to permit in sanitization,
84
+ `Rails::Html::TargetScrubber` targets them for removal.
85
+
86
+
87
+ ```ruby
88
+ scrubber = Rails::Html::TargetScrubber.new
89
+ scrubber.tags = ['img']
90
+
91
+ html_fragment = Loofah.fragment('<a><img/ ></a>')
92
+ html_fragment.scrub!(scrubber)
93
+ html_fragment.to_s # => "<a></a>"
94
+ ```
95
+
96
+ #### Custom Scrubbers
97
+
98
+ You can also create custom scrubbers in your application if you want to.
99
+
100
+ ```ruby
101
+ class CommentScrubber < Rails::Html::PermitScrubber
102
+ def allowed_node?(node)
103
+ !%w(form script comment blockquote).include?(node.name)
104
+ end
105
+
106
+ def skip_node?(node)
107
+ node.text?
108
+ end
109
+
110
+ def scrub_attribute?(name)
111
+ name == "style"
112
+ end
113
+ end
114
+ ```
115
+
116
+ See `Rails::Html::PermitScrubber` documentation to learn more about which methods can be overridden.
117
+
118
+ #### Custom Scrubber in a Rails app
119
+
120
+ Using the `CommentScrubber` from above, you can use this in a Rails view like so:
121
+
122
+ ```ruby
123
+ <%= sanitize @comment, scrubber: CommentScrubber.new %>
124
+ ```
125
+
126
+ ## Read more
127
+
128
+ Loofah is what underlies the sanitizers and scrubbers of rails-html-sanitizer.
129
+ - [Loofah and Loofah Scrubbers](https://github.com/flavorjones/loofah)
130
+
131
+ The `node` argument passed to some methods in a custom scrubber is an instance of `Nokogiri::XML::Node`.
132
+ - [`Nokogiri::XML::Node`](http://nokogiri.org/Nokogiri/XML/Node.html)
133
+ - [Nokogiri](http://nokogiri.org)
134
+
135
+ ## Contributing
136
+
137
+ 1. Fork it
138
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
139
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
140
+ 4. Push to the branch (`git push origin my-new-feature`)
141
+ 5. Create new Pull Request
@@ -0,0 +1,38 @@
1
+ require "rails/html/sanitizer/version"
2
+ require "loofah"
3
+ require "rails/html/scrubbers"
4
+ require "rails/html/sanitizer"
5
+
6
+ module Rails
7
+ module Html
8
+ class Sanitizer
9
+ class << self
10
+ def full_sanitizer
11
+ Html::FullSanitizer
12
+ end
13
+
14
+ def link_sanitizer
15
+ Html::LinkSanitizer
16
+ end
17
+
18
+ def white_list_sanitizer
19
+ Html::WhiteListSanitizer
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ module ActionView
27
+ module Helpers
28
+ module SanitizeHelper
29
+ if method_defined?(:sanitizer_vendor) || private_method_defined?(:sanitizer_vendor)
30
+ undef_method(:sanitizer_vendor)
31
+ end
32
+
33
+ def sanitizer_vendor
34
+ Rails::Html::Sanitizer
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,141 @@
1
+ module Rails
2
+ module Html
3
+ XPATHS_TO_REMOVE = %w{.//script .//form comment()}
4
+
5
+ class Sanitizer # :nodoc:
6
+ def sanitize(html, options = {})
7
+ raise NotImplementedError, "subclasses must implement sanitize method."
8
+ end
9
+
10
+ private
11
+
12
+ def remove_xpaths(node, xpaths)
13
+ node.xpath(*xpaths).remove
14
+ node
15
+ end
16
+ end
17
+
18
+ # === Rails::Html::FullSanitizer
19
+ # Removes all tags but strips out scripts, forms and comments.
20
+ #
21
+ # full_sanitizer = Rails::Html::FullSanitizer.new
22
+ # full_sanitizer.sanitize("<b>Bold</b> no more! <a href='more.html'>See more here</a>...")
23
+ # # => Bold no more! See more here...
24
+ class FullSanitizer < Sanitizer
25
+ def sanitize(html, options = {})
26
+ return unless html
27
+ return html if html.empty?
28
+
29
+ Loofah.fragment(html).tap do |fragment|
30
+ remove_xpaths(fragment, XPATHS_TO_REMOVE)
31
+ end.text
32
+ end
33
+ end
34
+
35
+ # === Rails::Html::LinkSanitizer
36
+ # Removes a tags and href attributes leaving only the link text
37
+ #
38
+ # link_sanitizer = Rails::Html::LinkSanitizer.new
39
+ # link_sanitizer.sanitize('<a href="example.com">Only the link text will be kept.</a>')
40
+ # # => Only the link text will be kept.
41
+ class LinkSanitizer < Sanitizer
42
+ def initialize
43
+ @link_scrubber = TargetScrubber.new
44
+ @link_scrubber.tags = %w(a href)
45
+ @link_scrubber.attributes = %w(href)
46
+ end
47
+
48
+ def sanitize(html, options = {})
49
+ Loofah.scrub_fragment(html, @link_scrubber).to_s
50
+ end
51
+ end
52
+
53
+ # === Rails::Html::WhiteListSanitizer
54
+ # Sanitizes html and css from an extensive white list (see link further down).
55
+ #
56
+ # === Whitespace
57
+ # We can't make any guarentees about whitespace being kept or stripped.
58
+ # Loofah uses Nokogiri, which wraps either a C or Java parser for the
59
+ # respective Ruby implementation.
60
+ # Those two parsers determine how whitespace is ultimately handled.
61
+ #
62
+ # When the stripped markup will be rendered the users browser won't take
63
+ # whitespace into account anyway. It might be better to suggest your users
64
+ # wrap their whitespace sensitive content in pre tags or that you do
65
+ # so automatically.
66
+ #
67
+ # === Options
68
+ # Sanitizes both html and css via the white lists found here:
69
+ # https://github.com/flavorjones/loofah/blob/master/lib/loofah/html5/whitelist.rb
70
+ #
71
+ # WhiteListSanitizer also accepts options to configure
72
+ # the white list used when sanitizing html.
73
+ # There's a class level option:
74
+ # Rails::Html::WhiteListSanitizer.allowed_tags = %w(table tr td)
75
+ # Rails::Html::WhiteListSanitizer.allowed_attributes = %w(id class style)
76
+ #
77
+ # Tags and attributes can also be passed to +sanitize+.
78
+ # Passed options take precedence over the class level options.
79
+ #
80
+ # === Examples
81
+ # white_list_sanitizer = Rails::Html::WhiteListSanitizer.new
82
+ #
83
+ # Sanitize css doesn't take options
84
+ # white_list_sanitizer.sanitize_css('background-color: #000;')
85
+ #
86
+ # Default: sanitize via a extensive white list of allowed elements
87
+ # white_list_sanitizer.sanitize(@article.body)
88
+ #
89
+ # White list via the supplied tags and attributes
90
+ # white_list_sanitizer.sanitize(@article.body, tags: %w(table tr td),
91
+ # attributes: %w(id class style))
92
+ #
93
+ # White list via a custom scrubber
94
+ # white_list_sanitizer.sanitize(@article.body, scrubber: ArticleScrubber.new)
95
+ class WhiteListSanitizer < Sanitizer
96
+ class << self
97
+ attr_accessor :allowed_tags
98
+ attr_accessor :allowed_attributes
99
+ end
100
+
101
+ def initialize
102
+ @permit_scrubber = PermitScrubber.new
103
+ end
104
+
105
+ def sanitize(html, options = {})
106
+ return unless html
107
+ return html if html.empty?
108
+
109
+ loofah_fragment = Loofah.fragment(html)
110
+
111
+ if scrubber = options[:scrubber]
112
+ # No duck typing, Loofah ensures subclass of Loofah::Scrubber
113
+ loofah_fragment.scrub!(scrubber)
114
+ elsif allowed_tags(options) || allowed_attributes(options)
115
+ @permit_scrubber.tags = allowed_tags(options)
116
+ @permit_scrubber.attributes = allowed_attributes(options)
117
+ loofah_fragment.scrub!(@permit_scrubber)
118
+ else
119
+ remove_xpaths(loofah_fragment, XPATHS_TO_REMOVE)
120
+ loofah_fragment.scrub!(:strip)
121
+ end
122
+
123
+ loofah_fragment.to_s
124
+ end
125
+
126
+ def sanitize_css(style_string)
127
+ Loofah::HTML5::Scrub.scrub_css(style_string)
128
+ end
129
+
130
+ private
131
+
132
+ def allowed_tags(options)
133
+ options[:tags] || self.class.allowed_tags
134
+ end
135
+
136
+ def allowed_attributes(options)
137
+ options[:attributes] || self.class.allowed_attributes
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,7 @@
1
+ module Rails
2
+ module Html
3
+ class Sanitizer
4
+ VERSION = "1.0.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,148 @@
1
+ module Rails
2
+ module Html
3
+ # === Rails::Html::PermitScrubber
4
+ #
5
+ # Rails::Html::PermitScrubber allows you to permit only your own tags and/or attributes.
6
+ #
7
+ # Rails::Html::PermitScrubber can be subclassed to determine:
8
+ # - When a node should be skipped via +skip_node?+.
9
+ # - When a node is allowed via +allowed_node?+.
10
+ # - When an attribute should be scrubbed via +scrub_attribute?+.
11
+ #
12
+ # Subclasses don't need to worry if tags or attributes are set or not.
13
+ # If tags or attributes are not set, Loofah's behavior will be used.
14
+ # If you override +allowed_node?+ and no tags are set, it will not be called.
15
+ # Instead Loofahs behavior will be used.
16
+ # Likewise for +scrub_attribute?+ and attributes respectively.
17
+ #
18
+ # Text and CDATA nodes are skipped by default.
19
+ # Unallowed elements will be stripped, i.e. element is removed but its subtree kept.
20
+ # Supplied tags and attributes should be Enumerables.
21
+ #
22
+ # +tags=+
23
+ # If set, elements excluded will be stripped.
24
+ # If not, elements are stripped based on Loofahs +HTML5::Scrub.allowed_element?+.
25
+ #
26
+ # +attributes=+
27
+ # If set, attributes excluded will be removed.
28
+ # If not, attributes are removed based on Loofahs +HTML5::Scrub.scrub_attributes+.
29
+ #
30
+ # class CommentScrubber < Html::PermitScrubber
31
+ # def allowed_node?(node)
32
+ # !%w(form script comment blockquote).include?(node.name)
33
+ # end
34
+ #
35
+ # def skip_node?(node)
36
+ # node.text?
37
+ # end
38
+ #
39
+ # def scrub_attribute?(name)
40
+ # name == "style"
41
+ # end
42
+ # end
43
+ #
44
+ # See the documentation for Nokogiri::XML::Node to understand what's possible
45
+ # with nodes: http://nokogiri.org/Nokogiri/XML/Node.html
46
+ class PermitScrubber < Loofah::Scrubber
47
+ attr_reader :tags, :attributes
48
+
49
+ def initialize
50
+ @direction = :bottom_up
51
+ @tags, @attributes = nil, nil
52
+ end
53
+
54
+ def tags=(tags)
55
+ @tags = validate!(tags, :tags)
56
+ end
57
+
58
+ def attributes=(attributes)
59
+ @attributes = validate!(attributes, :attributes)
60
+ end
61
+
62
+ def scrub(node)
63
+ return CONTINUE if skip_node?(node)
64
+
65
+ unless keep_node?(node)
66
+ return STOP if scrub_node(node) == STOP
67
+ end
68
+
69
+ scrub_attributes(node)
70
+ end
71
+
72
+ protected
73
+
74
+ def allowed_node?(node)
75
+ @tags.include?(node.name)
76
+ end
77
+
78
+ def skip_node?(node)
79
+ node.text? || node.cdata?
80
+ end
81
+
82
+ def scrub_attribute?(name)
83
+ !@attributes.include?(name)
84
+ end
85
+
86
+ def keep_node?(node)
87
+ if @tags
88
+ allowed_node?(node)
89
+ else
90
+ Loofah::HTML5::Scrub.allowed_element?(node.name)
91
+ end
92
+ end
93
+
94
+ def scrub_node(node)
95
+ node.before(node.children) # strip
96
+ node.remove
97
+ end
98
+
99
+ def scrub_attributes(node)
100
+ if @attributes
101
+ node.attribute_nodes.each do |attr|
102
+ attr.remove if scrub_attribute?(attr.name)
103
+ end
104
+
105
+ scrub_css_attribute(node)
106
+ else
107
+ Loofah::HTML5::Scrub.scrub_attributes(node)
108
+ end
109
+ end
110
+
111
+ def scrub_css_attribute(node)
112
+ if Loofah::HTML5::Scrub.respond_to?(:scrub_css_attribute)
113
+ Loofah::HTML5::Scrub.scrub_css_attribute(node)
114
+ else
115
+ style = node.attributes['style']
116
+ style.value = Loofah::HTML5::Scrub.scrub_css(style.value) if style
117
+ end
118
+ end
119
+
120
+ def validate!(var, name)
121
+ if var && !var.is_a?(Enumerable)
122
+ raise ArgumentError, "You should pass :#{name} as an Enumerable"
123
+ end
124
+ var
125
+ end
126
+ end
127
+
128
+ # === Rails::Html::TargetScrubber
129
+ #
130
+ # Where Rails::Html::PermitScrubber picks out tags and attributes to permit in
131
+ # sanitization, Rails::Html::TargetScrubber targets them for removal.
132
+ #
133
+ # +tags=+
134
+ # If set, elements included will be stripped.
135
+ #
136
+ # +attributes=+
137
+ # If set, attributes included will be removed.
138
+ class TargetScrubber < PermitScrubber
139
+ def allowed_node?(node)
140
+ !@tags.include?(node.name)
141
+ end
142
+
143
+ def scrub_attribute?(name)
144
+ @attributes.include?(name)
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,489 @@
1
+ require "minitest/autorun"
2
+ require "rails-html-sanitizer"
3
+ require "rails/dom/testing/assertions/dom_assertions"
4
+
5
+ class SanitizersTest < Minitest::Test
6
+ include Rails::Dom::Testing::Assertions::DomAssertions
7
+
8
+ def test_sanitizer_sanitize_raises_not_implemented_error
9
+ assert_raises NotImplementedError do
10
+ Rails::Html::Sanitizer.new.sanitize('')
11
+ end
12
+ end
13
+
14
+ class XpathRemovalTestSanitizer < Rails::Html::Sanitizer
15
+ def sanitize(html, options = {})
16
+ fragment = Loofah.fragment(html)
17
+ remove_xpaths(fragment, options[:xpaths]).to_s
18
+ end
19
+ end
20
+
21
+ def test_remove_xpaths_removes_an_xpath
22
+ html = %(<h1>hello <script>code!</script></h1>)
23
+ assert_equal %(<h1>hello </h1>), xpath_sanitize(html, xpaths: %w(.//script))
24
+ end
25
+
26
+ def test_remove_xpaths_removes_all_occurences_of_xpath
27
+ html = %(<section><header><script>code!</script></header><p>hello <script>code!</script></p></section>)
28
+ assert_equal %(<section><header></header><p>hello </p></section>), xpath_sanitize(html, xpaths: %w(.//script))
29
+ end
30
+
31
+ def test_remove_xpaths_called_with_faulty_xpath
32
+ assert_raises Nokogiri::XML::XPath::SyntaxError do
33
+ xpath_sanitize('<h1>hello<h1>', xpaths: %w(..faulty_xpath))
34
+ end
35
+ end
36
+
37
+ def test_remove_xpaths_called_with_xpath_string
38
+ assert_equal '', xpath_sanitize('<a></a>', xpaths: './/a')
39
+ end
40
+
41
+ def test_remove_xpaths_called_with_enumerable_xpaths
42
+ assert_equal '', xpath_sanitize('<a><span></span></a>', xpaths: %w(.//a .//span))
43
+ end
44
+
45
+ def test_strip_tags_with_quote
46
+ input = '<" <img src="trollface.gif" onload="alert(1)"> hi'
47
+ assert_equal ' hi', full_sanitize(input)
48
+ end
49
+
50
+ def test_strip_invalid_html
51
+ assert_equal "", full_sanitize("<<<bad html")
52
+ end
53
+
54
+ def test_strip_nested_tags
55
+ expected = "Weia onclick='alert(document.cookie);'/&gt;rdos"
56
+ input = "Wei<<a>a onclick='alert(document.cookie);'</a>/>rdos"
57
+ assert_equal expected, full_sanitize(input)
58
+ end
59
+
60
+ def test_strip_tags_multiline
61
+ expected = %{This is a test.\n\n\n\nIt no longer contains any HTML.\n}
62
+ input = %{<title>This is <b>a <a href="" target="_blank">test</a></b>.</title>\n\n<!-- it has a comment -->\n\n<p>It no <b>longer <strong>contains <em>any <strike>HTML</strike></em>.</strong></b></p>\n}
63
+
64
+ assert_equal expected, full_sanitize(input)
65
+ end
66
+
67
+ def test_strip_comments
68
+ assert_equal "This is ", full_sanitize("This is <-- not\n a comment here.")
69
+ end
70
+
71
+ def test_strip_cdata
72
+ assert_equal "This has a ]]&gt; here.", full_sanitize("This has a <![CDATA[<section>]]> here.")
73
+ end
74
+
75
+ def test_strip_unclosed_cdata
76
+ assert_equal "This has an unclosed ]] here...", full_sanitize("This has an unclosed <![CDATA[<section>]] here...")
77
+ end
78
+
79
+ def test_strip_blank_string
80
+ [nil, '', ' '].each { |blank| assert_equal blank, full_sanitize(blank) }
81
+ end
82
+
83
+ def test_strip_tags_with_plaintext
84
+ assert_equal "Dont touch me", full_sanitize("Dont touch me")
85
+ end
86
+
87
+ def test_strip_tags_with_tags
88
+ assert_equal "This is a test.", full_sanitize("<p>This <u>is<u> a <a href='test.html'><strong>test</strong></a>.</p>")
89
+ end
90
+
91
+ def test_strip_tags_with_many_open_quotes
92
+ assert_equal "", full_sanitize("<<<bad html>")
93
+ end
94
+
95
+ def test_strip_tags_with_sentence
96
+ assert_equal "This is a test.", full_sanitize("This is a test.")
97
+ end
98
+
99
+ def test_strip_tags_with_comment
100
+ assert_equal "This has a here.", full_sanitize("This has a <!-- comment --> here.")
101
+ end
102
+
103
+ def test_strip_tags_with_frozen_string
104
+ assert_equal "Frozen string with no tags", full_sanitize("Frozen string with no tags".freeze)
105
+ end
106
+
107
+ def test_strip_links_with_tags_in_tags
108
+ expected = "a href='hello'&gt;all <b>day</b> long/a&gt;"
109
+ input = "<<a>a href='hello'>all <b>day</b> long<</A>/a>"
110
+ assert_equal expected, link_sanitize(input)
111
+ end
112
+
113
+ def test_strip_links_with_unclosed_tags
114
+ assert_equal "", link_sanitize("<a<a")
115
+ end
116
+
117
+ def test_strip_links_with_plaintext
118
+ assert_equal "Dont touch me", link_sanitize("Dont touch me")
119
+ end
120
+
121
+ def test_strip_links_with_line_feed_and_uppercase_tag
122
+ assert_equal "on my mind\nall day long", link_sanitize("<a href='almost'>on my mind</a>\n<A href='almost'>all day long</A>")
123
+ end
124
+
125
+ def test_strip_links_leaves_nonlink_tags
126
+ assert_equal "My mind\nall <b>day</b> long", link_sanitize("<a href='almost'>My mind</a>\n<A href='almost'>all <b>day</b> long</A>")
127
+ end
128
+
129
+ def test_strip_links_with_links
130
+ assert_equal "0wn3d", link_sanitize("<a href='http://www.rubyonrails.com/'><a href='http://www.rubyonrails.com/' onlclick='steal()'>0wn3d</a></a>")
131
+ end
132
+
133
+ def test_strip_links_with_linkception
134
+ assert_equal "Magic", link_sanitize("<a href='http://www.rubyonrails.com/'>Mag<a href='http://www.ruby-lang.org/'>ic")
135
+ end
136
+
137
+ def test_strip_links_with_a_tag_in_href
138
+ assert_equal "FrrFox", link_sanitize("<href onlclick='steal()'>FrrFox</a></href>")
139
+ end
140
+
141
+ def test_sanitize_form
142
+ assert_sanitized "<form action=\"/foo/bar\" method=\"post\"><input></form>", ''
143
+ end
144
+
145
+ def test_sanitize_plaintext
146
+ assert_sanitized "<plaintext><span>foo</span></plaintext>", "<span>foo</span>"
147
+ end
148
+
149
+ def test_sanitize_script
150
+ assert_sanitized "a b c<script language=\"Javascript\">blah blah blah</script>d e f", "a b cd e f"
151
+ end
152
+
153
+ def test_sanitize_js_handlers
154
+ raw = %{onthis="do that" <a href="#" onclick="hello" name="foo" onbogus="remove me">hello</a>}
155
+ assert_sanitized raw, %{onthis="do that" <a href="#" name="foo">hello</a>}
156
+ end
157
+
158
+ def test_sanitize_javascript_href
159
+ raw = %{href="javascript:bang" <a href="javascript:bang" name="hello">foo</a>, <span href="javascript:bang">bar</span>}
160
+ assert_sanitized raw, %{href="javascript:bang" <a name="hello">foo</a>, <span>bar</span>}
161
+ end
162
+
163
+ def test_sanitize_image_src
164
+ raw = %{src="javascript:bang" <img src="javascript:bang" width="5">foo</img>, <span src="javascript:bang">bar</span>}
165
+ assert_sanitized raw, %{src="javascript:bang" <img width="5">foo</img>, <span>bar</span>}
166
+ end
167
+
168
+ tags = Loofah::HTML5::WhiteList::ALLOWED_ELEMENTS - %w(script form)
169
+ tags.each do |tag_name|
170
+ define_method "test_should_allow_#{tag_name}_tag" do
171
+ assert_sanitized "start <#{tag_name} title=\"1\" onclick=\"foo\">foo <bad>bar</bad> baz</#{tag_name}> end", %(start <#{tag_name} title="1">foo bar baz</#{tag_name}> end)
172
+ end
173
+ end
174
+
175
+ def test_should_allow_anchors
176
+ assert_sanitized %(<a href="foo" onclick="bar"><script>baz</script></a>), %(<a href=\"foo\">baz</a>)
177
+ end
178
+
179
+ def test_video_poster_sanitization
180
+ assert_sanitized %(<video src="videofile.ogg" autoplay poster="posterimage.jpg"></video>), %(<video src="videofile.ogg" poster="posterimage.jpg"></video>)
181
+ assert_sanitized %(<video src="videofile.ogg" poster=javascript:alert(1)></video>), %(<video src="videofile.ogg"></video>)
182
+ end
183
+
184
+ # RFC 3986, sec 4.2
185
+ def test_allow_colons_in_path_component
186
+ assert_sanitized "<a href=\"./this:that\">foo</a>"
187
+ end
188
+
189
+ %w(src width height alt).each do |img_attr|
190
+ define_method "test_should_allow_image_#{img_attr}_attribute" do
191
+ assert_sanitized %(<img #{img_attr}="foo" onclick="bar" />), %(<img #{img_attr}="foo" />)
192
+ end
193
+ end
194
+
195
+ def test_should_handle_non_html
196
+ assert_sanitized 'abc'
197
+ end
198
+
199
+ def test_should_handle_blank_text
200
+ [nil, '', ' '].each { |blank| assert_sanitized blank }
201
+ end
202
+
203
+ def test_setting_allowed_tags_affects_sanitization
204
+ scope_allowed_tags %w(u) do |sanitizer|
205
+ assert_equal '<u></u>', sanitizer.sanitize('<a><u></u></a>')
206
+ end
207
+ end
208
+
209
+ def test_setting_allowed_attributes_affects_sanitization
210
+ scope_allowed_attributes %w(foo) do |sanitizer|
211
+ input = '<a foo="hello" bar="world"></a>'
212
+ assert_equal '<a foo="hello"></a>', sanitizer.sanitize(input)
213
+ end
214
+ end
215
+
216
+ def test_custom_tags_overrides_allowed_tags
217
+ scope_allowed_tags %(u) do |sanitizer|
218
+ input = '<a><u></u></a>'
219
+ assert_equal '<a></a>', sanitizer.sanitize(input, tags: %w(a))
220
+ end
221
+ end
222
+
223
+ def test_custom_attributes_overrides_allowed_attributes
224
+ scope_allowed_attributes %(foo) do |sanitizer|
225
+ input = '<a foo="hello" bar="world"></a>'
226
+ assert_equal '<a bar="world"></a>', sanitizer.sanitize(input, attributes: %w(bar))
227
+ end
228
+ end
229
+
230
+ def test_should_allow_custom_tags
231
+ text = "<u>foo</u>"
232
+ assert_equal text, white_list_sanitize(text, tags: %w(u))
233
+ end
234
+
235
+ def test_should_allow_only_custom_tags
236
+ text = "<u>foo</u> with <i>bar</i>"
237
+ assert_equal "<u>foo</u> with bar", white_list_sanitize(text, tags: %w(u))
238
+ end
239
+
240
+ def test_should_allow_custom_tags_with_attributes
241
+ text = %(<blockquote cite="http://example.com/">foo</blockquote>)
242
+ assert_equal text, white_list_sanitize(text)
243
+ end
244
+
245
+ def test_should_allow_custom_tags_with_custom_attributes
246
+ text = %(<blockquote foo="bar">Lorem ipsum</blockquote>)
247
+ assert_equal text, white_list_sanitize(text, attributes: ['foo'])
248
+ end
249
+
250
+ def test_scrub_style_if_style_attribute_option_is_passed
251
+ input = '<p style="color: #000; background-image: url(http://www.ragingplatypus.com/i/cam-full.jpg);"></p>'
252
+ assert_equal '<p style="color: #000;"></p>', white_list_sanitize(input, attributes: %w(style))
253
+ end
254
+
255
+ def test_should_raise_argument_error_if_tags_is_not_enumerable
256
+ assert_raises ArgumentError do
257
+ white_list_sanitize('<a>some html</a>', tags: 'foo')
258
+ end
259
+ end
260
+
261
+ def test_should_raise_argument_error_if_attributes_is_not_enumerable
262
+ assert_raises ArgumentError do
263
+ white_list_sanitize('<a>some html</a>', attributes: 'foo')
264
+ end
265
+ end
266
+
267
+ def test_should_not_accept_non_loofah_inheriting_scrubber
268
+ scrubber = Object.new
269
+ def scrubber.scrub(node); node.name = 'h1'; end
270
+
271
+ assert_raises Loofah::ScrubberNotFound do
272
+ white_list_sanitize('<a>some html</a>', scrubber: scrubber)
273
+ end
274
+ end
275
+
276
+ def test_should_accept_loofah_inheriting_scrubber
277
+ scrubber = Loofah::Scrubber.new
278
+ def scrubber.scrub(node); node.name = 'h1'; end
279
+
280
+ html = "<script>hello!</script>"
281
+ assert_equal "<h1>hello!</h1>", white_list_sanitize(html, scrubber: scrubber)
282
+ end
283
+
284
+ def test_should_accept_loofah_scrubber_that_wraps_a_block
285
+ scrubber = Loofah::Scrubber.new { |node| node.name = 'h1' }
286
+ html = "<script>hello!</script>"
287
+ assert_equal "<h1>hello!</h1>", white_list_sanitize(html, scrubber: scrubber)
288
+ end
289
+
290
+ def test_custom_scrubber_takes_precedence_over_other_options
291
+ scrubber = Loofah::Scrubber.new { |node| node.name = 'h1' }
292
+ html = "<script>hello!</script>"
293
+ assert_equal "<h1>hello!</h1>", white_list_sanitize(html, scrubber: scrubber, tags: ['foo'])
294
+ end
295
+
296
+ [%w(img src), %w(a href)].each do |(tag, attr)|
297
+ define_method "test_should_strip_#{attr}_attribute_in_#{tag}_with_bad_protocols" do
298
+ assert_sanitized %(<#{tag} #{attr}="javascript:bang" title="1">boo</#{tag}>), %(<#{tag} title="1">boo</#{tag}>)
299
+ end
300
+ end
301
+
302
+ def test_should_block_script_tag
303
+ assert_sanitized %(<SCRIPT\nSRC=http://ha.ckers.org/xss.js></SCRIPT>), ""
304
+ end
305
+
306
+ def test_should_not_fall_for_xss_image_hack_with_uppercase_tags
307
+ assert_sanitized %(<IMG """><SCRIPT>alert("XSS")</SCRIPT>">), "<img>\"&gt;"
308
+ end
309
+
310
+ [%(<IMG SRC="javascript:alert('XSS');">),
311
+ %(<IMG SRC=javascript:alert('XSS')>),
312
+ %(<IMG SRC=JaVaScRiPt:alert('XSS')>),
313
+ %(<IMG SRC=javascript:alert(&quot;XSS&quot;)>),
314
+ %(<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>),
315
+ %(<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>),
316
+ %(<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>),
317
+ %(<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>),
318
+ %(<IMG SRC="jav\tascript:alert('XSS');">),
319
+ %(<IMG SRC="jav&#x09;ascript:alert('XSS');">),
320
+ %(<IMG SRC="jav&#x0A;ascript:alert('XSS');">),
321
+ %(<IMG SRC="jav&#x0D;ascript:alert('XSS');">),
322
+ %(<IMG SRC=" &#14; javascript:alert('XSS');">),
323
+ %(<IMG SRC="javascript&#x3a;alert('XSS');">),
324
+ %(<IMG SRC=`javascript:alert("RSnake says, 'XSS'")`>)].each_with_index do |img_hack, i|
325
+ define_method "test_should_not_fall_for_xss_image_hack_#{i+1}" do
326
+ assert_sanitized img_hack, "<img>"
327
+ end
328
+ end
329
+
330
+ def test_should_sanitize_tag_broken_up_by_null
331
+ assert_sanitized %(<SCR\0IPT>alert(\"XSS\")</SCR\0IPT>), ""
332
+ end
333
+
334
+ def test_should_sanitize_invalid_script_tag
335
+ assert_sanitized %(<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>), ""
336
+ end
337
+
338
+ def test_should_sanitize_script_tag_with_multiple_open_brackets
339
+ assert_sanitized %(<<SCRIPT>alert("XSS");//<</SCRIPT>), "alert(\"XSS\");//"
340
+ assert_sanitized %(<iframe src=http://ha.ckers.org/scriptlet.html\n<a), ""
341
+ end
342
+
343
+ def test_should_sanitize_unclosed_script
344
+ assert_sanitized %(<SCRIPT SRC=http://ha.ckers.org/xss.js?<B>), ""
345
+ end
346
+
347
+ def test_should_sanitize_half_open_scripts
348
+ assert_sanitized %(<IMG SRC="javascript:alert('XSS')"), "<img>"
349
+ end
350
+
351
+ def test_should_not_fall_for_ridiculous_hack
352
+ img_hack = %(<IMG\nSRC\n=\n"\nj\na\nv\na\ns\nc\nr\ni\np\nt\n:\na\nl\ne\nr\nt\n(\n'\nX\nS\nS\n'\n)\n"\n>)
353
+ assert_sanitized img_hack, "<img>"
354
+ end
355
+
356
+ def test_should_sanitize_attributes
357
+ assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="#{CGI.escapeHTML "'><script>alert()</script>"}">blah</span>)
358
+ end
359
+
360
+ def test_should_sanitize_illegal_style_properties
361
+ raw = %(display:block; position:absolute; left:0; top:0; width:100%; height:100%; z-index:1; background-color:black; background-image:url(http://www.ragingplatypus.com/i/cam-full.jpg); background-x:center; background-y:center; background-repeat:repeat;)
362
+ expected = %(display: block; width: 100%; height: 100%; background-color: black; background-x: center; background-y: center;)
363
+ assert_equal expected, sanitize_css(raw)
364
+ end
365
+
366
+ def test_should_sanitize_with_trailing_space
367
+ raw = "display:block; "
368
+ expected = "display: block;"
369
+ assert_equal expected, sanitize_css(raw)
370
+ end
371
+
372
+ def test_should_sanitize_xul_style_attributes
373
+ raw = %(-moz-binding:url('http://ha.ckers.org/xssmoz.xml#xss'))
374
+ assert_equal '', sanitize_css(raw)
375
+ end
376
+
377
+ def test_should_sanitize_invalid_tag_names
378
+ assert_sanitized(%(a b c<script/XSS src="http://ha.ckers.org/xss.js"></script>d e f), "a b cd e f")
379
+ end
380
+
381
+ def test_should_sanitize_non_alpha_and_non_digit_characters_in_tags
382
+ assert_sanitized('<a onclick!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>foo</a>', "<a>foo</a>")
383
+ end
384
+
385
+ def test_should_sanitize_invalid_tag_names_in_single_tags
386
+ assert_sanitized('<img/src="http://ha.ckers.org/xss.js"/>', "<img />")
387
+ end
388
+
389
+ def test_should_sanitize_img_dynsrc_lowsrc
390
+ assert_sanitized(%(<img lowsrc="javascript:alert('XSS')" />), "<img />")
391
+ end
392
+
393
+ def test_should_sanitize_div_background_image_unicode_encoded
394
+ raw = %(background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029)
395
+ assert_equal '', sanitize_css(raw)
396
+ end
397
+
398
+ def test_should_sanitize_div_style_expression
399
+ raw = %(width: expression(alert('XSS'));)
400
+ assert_equal '', sanitize_css(raw)
401
+ end
402
+
403
+ def test_should_sanitize_across_newlines
404
+ raw = %(\nwidth:\nexpression(alert('XSS'));\n)
405
+ assert_equal '', sanitize_css(raw)
406
+ end
407
+
408
+ def test_should_sanitize_img_vbscript
409
+ assert_sanitized %(<img src='vbscript:msgbox("XSS")' />), '<img />'
410
+ end
411
+
412
+ def test_should_sanitize_cdata_section
413
+ assert_sanitized "<![CDATA[<span>section</span>]]>", "section]]&gt;"
414
+ end
415
+
416
+ def test_should_sanitize_unterminated_cdata_section
417
+ assert_sanitized "<![CDATA[<span>neverending...", "neverending..."
418
+ end
419
+
420
+ def test_should_not_mangle_urls_with_ampersand
421
+ assert_sanitized %{<a href=\"http://www.domain.com?var1=1&amp;var2=2\">my link</a>}
422
+ end
423
+
424
+ def test_should_sanitize_neverending_attribute
425
+ assert_sanitized "<span class=\"\\", "<span class=\"\\\">"
426
+ end
427
+
428
+ [
429
+ %(<a href="javascript&#x3a;alert('XSS');">),
430
+ %(<a href="javascript&#x003a;alert('XSS');">),
431
+ %(<a href="javascript&#x3A;alert('XSS');">),
432
+ %(<a href="javascript&#x003A;alert('XSS');">)
433
+ ].each_with_index do |enc_hack, i|
434
+ define_method "test_x03a_handling_#{i+1}" do
435
+ assert_sanitized enc_hack, "<a>"
436
+ end
437
+ end
438
+
439
+ def test_x03a_legitimate
440
+ assert_sanitized %(<a href="http&#x3a;//legit">), %(<a href="http://legit">)
441
+ assert_sanitized %(<a href="http&#x3A;//legit">), %(<a href="http://legit">)
442
+ end
443
+
444
+ protected
445
+
446
+ def xpath_sanitize(input, options = {})
447
+ XpathRemovalTestSanitizer.new.sanitize(input, options)
448
+ end
449
+
450
+ def full_sanitize(input, options = {})
451
+ Rails::Html::FullSanitizer.new.sanitize(input, options)
452
+ end
453
+
454
+ def link_sanitize(input, options = {})
455
+ Rails::Html::LinkSanitizer.new.sanitize(input, options)
456
+ end
457
+
458
+ def white_list_sanitize(input, options = {})
459
+ Rails::Html::WhiteListSanitizer.new.sanitize(input, options)
460
+ end
461
+
462
+ def assert_sanitized(input, expected = nil)
463
+ if input
464
+ assert_dom_equal expected || input, white_list_sanitize(input)
465
+ else
466
+ assert_nil white_list_sanitize(input)
467
+ end
468
+ end
469
+
470
+ def sanitize_css(input)
471
+ Rails::Html::WhiteListSanitizer.new.sanitize_css(input)
472
+ end
473
+
474
+ def scope_allowed_tags(tags)
475
+ Rails::Html::WhiteListSanitizer.allowed_tags = %w(u)
476
+ yield Rails::Html::WhiteListSanitizer.new
477
+
478
+ ensure
479
+ Rails::Html::WhiteListSanitizer.allowed_tags = nil
480
+ end
481
+
482
+ def scope_allowed_attributes(attributes)
483
+ Rails::Html::WhiteListSanitizer.allowed_attributes = attributes
484
+ yield Rails::Html::WhiteListSanitizer.new
485
+
486
+ ensure
487
+ Rails::Html::WhiteListSanitizer.allowed_attributes = nil
488
+ end
489
+ end
@@ -0,0 +1,160 @@
1
+ require "minitest/autorun"
2
+ require "rails-html-sanitizer"
3
+
4
+ class ScrubberTest < Minitest::Test
5
+ protected
6
+
7
+ def assert_scrubbed(html, expected = html)
8
+ output = Loofah.scrub_fragment(html, @scrubber).to_s
9
+ assert_equal expected, output
10
+ end
11
+
12
+ def to_node(text)
13
+ Loofah.fragment(text).children.first
14
+ end
15
+
16
+ def assert_node_skipped(text)
17
+ assert_scrub_returns(Loofah::Scrubber::CONTINUE, text)
18
+ end
19
+
20
+ def assert_scrub_stopped(text)
21
+ assert_scrub_returns(Loofah::Scrubber::STOP, text)
22
+ end
23
+
24
+ def assert_scrub_returns(return_value, text)
25
+ node = to_node(text)
26
+ assert_equal return_value, @scrubber.scrub(node)
27
+ end
28
+ end
29
+
30
+ class PermitScrubberTest < ScrubberTest
31
+
32
+ def setup
33
+ @scrubber = Rails::Html::PermitScrubber.new
34
+ end
35
+
36
+ def test_responds_to_scrub
37
+ assert @scrubber.respond_to?(:scrub)
38
+ end
39
+
40
+ def test_default_scrub_behavior
41
+ assert_scrubbed '<tag>hello</tag>', 'hello'
42
+ end
43
+
44
+ def test_default_attributes_removal_behavior
45
+ assert_scrubbed '<p cooler="hello">hello</p>', '<p>hello</p>'
46
+ end
47
+
48
+ def test_leaves_supplied_tags
49
+ @scrubber.tags = %w(a)
50
+ assert_scrubbed '<a>hello</a>'
51
+ end
52
+
53
+ def test_leaves_only_supplied_tags
54
+ html = '<tag>leave me <span>now</span></tag>'
55
+ @scrubber.tags = %w(tag)
56
+ assert_scrubbed html, '<tag>leave me now</tag>'
57
+ end
58
+
59
+ def test_leaves_only_supplied_tags_nested
60
+ html = '<tag>leave <em>me <span>now</span></em></tag>'
61
+ @scrubber.tags = %w(tag)
62
+ assert_scrubbed html, '<tag>leave me now</tag>'
63
+ end
64
+
65
+ def test_leaves_supplied_attributes
66
+ @scrubber.attributes = %w(cooler)
67
+ assert_scrubbed '<a cooler="hello"></a>'
68
+ end
69
+
70
+ def test_leaves_only_supplied_attributes
71
+ @scrubber.attributes = %w(cooler)
72
+ assert_scrubbed '<a cooler="hello" b="c" d="e"></a>', '<a cooler="hello"></a>'
73
+ end
74
+
75
+ def test_leaves_supplied_tags_and_attributes
76
+ @scrubber.tags = %w(tag)
77
+ @scrubber.attributes = %w(cooler)
78
+ assert_scrubbed '<tag cooler="hello"></tag>'
79
+ end
80
+
81
+ def test_leaves_only_supplied_tags_and_attributes
82
+ @scrubber.tags = %w(tag)
83
+ @scrubber.attributes = %w(cooler)
84
+ html = '<a></a><tag href=""></tag><tag cooler=""></tag>'
85
+ assert_scrubbed html, '<tag></tag><tag cooler=""></tag>'
86
+ end
87
+
88
+ def test_leaves_text
89
+ assert_scrubbed('some text')
90
+ end
91
+
92
+ def test_skips_text_nodes
93
+ assert_node_skipped('some text')
94
+ end
95
+
96
+ def test_tags_accessor_validation
97
+ e = assert_raises(ArgumentError) do
98
+ @scrubber.tags = 'tag'
99
+ end
100
+
101
+ assert_equal "You should pass :tags as an Enumerable", e.message
102
+ assert_nil @scrubber.tags, "Tags should be nil when validation fails"
103
+ end
104
+
105
+ def test_attributes_accessor_validation
106
+ e = assert_raises(ArgumentError) do
107
+ @scrubber.attributes = 'cooler'
108
+ end
109
+
110
+ assert_equal "You should pass :attributes as an Enumerable", e.message
111
+ assert_nil @scrubber.attributes, "Attributes should be nil when validation fails"
112
+ end
113
+ end
114
+
115
+ class TargetScrubberTest < ScrubberTest
116
+ def setup
117
+ @scrubber = Rails::Html::TargetScrubber.new
118
+ end
119
+
120
+ def test_targeting_tags_removes_only_them
121
+ @scrubber.tags = %w(a h1)
122
+ html = '<script></script><a></a><h1></h1>'
123
+ assert_scrubbed html, '<script></script>'
124
+ end
125
+
126
+ def test_targeting_tags_removes_only_them_nested
127
+ @scrubber.tags = %w(a)
128
+ html = '<tag><a><tag><a></a></tag></a></tag>'
129
+ assert_scrubbed html, '<tag><tag></tag></tag>'
130
+ end
131
+
132
+ def test_targeting_attributes_removes_only_them
133
+ @scrubber.attributes = %w(class id)
134
+ html = '<a class="a" id="b" onclick="c"></a>'
135
+ assert_scrubbed html, '<a onclick="c"></a>'
136
+ end
137
+
138
+ def test_targeting_tags_and_attributes_removes_only_them
139
+ @scrubber.tags = %w(tag)
140
+ @scrubber.attributes = %w(remove)
141
+ html = '<tag remove="" other=""></tag><a remove="" other=""></a>'
142
+ assert_scrubbed html, '<a other=""></a>'
143
+ end
144
+ end
145
+
146
+ class ReturningStopFromScrubNodeTest < ScrubberTest
147
+ class ScrubStopper < Rails::Html::PermitScrubber
148
+ def scrub_node(node)
149
+ Loofah::Scrubber::STOP
150
+ end
151
+ end
152
+
153
+ def setup
154
+ @scrubber = ScrubStopper.new
155
+ end
156
+
157
+ def test_returns_stop_from_scrub_if_scrub_node_does
158
+ assert_scrub_stopped '<script>remove me</script>'
159
+ end
160
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-html-sanitizer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Rafael Mendonça França
8
+ - Kasper Timm Hansen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-08-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: loofah
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: bundler
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.3'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.3'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: minitest
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rails-dom-testing
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ description: HTML sanitization to Rails applications
85
+ email:
86
+ - rafaelmfranca@gmail.com
87
+ - kaspth@gmail.com
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - CHANGELOG.md
93
+ - LICENSE.txt
94
+ - README.md
95
+ - lib/rails-html-sanitizer.rb
96
+ - lib/rails/html/sanitizer.rb
97
+ - lib/rails/html/sanitizer/version.rb
98
+ - lib/rails/html/scrubbers.rb
99
+ - test/sanitizer_test.rb
100
+ - test/scrubbers_test.rb
101
+ homepage: https://github.com/rafaelfranca/rails-html-sanitizer
102
+ licenses:
103
+ - MIT
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 2.3.0
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: This gem is resposible to sanitize HTML fragments in Rails applications.
125
+ test_files:
126
+ - test/sanitizer_test.rb
127
+ - test/scrubbers_test.rb
128
+ has_rdoc: