rails-html-sanitizer 1.0.2 → 1.0.3

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.

Potentially problematic release.


This version of rails-html-sanitizer might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 330722b1f148ac96ebebc529d2b971d2f32584bb
4
- data.tar.gz: 5441eea2b71bd6786f38061d60db7c80f18896ff
3
+ metadata.gz: 44e7ba72869ce5a5b6aa4f202dced7073ef94b72
4
+ data.tar.gz: b5410baf4f05cc97449852a7b3b4e36774a9942d
5
5
  SHA512:
6
- metadata.gz: c346b077718dd1ebba5c1aecab78421e30b3dbda64a7a43ecf0cea96b3f25ea72f9b93dc2f7b36d11453d8249f87847ef4087f1bc1086812314e1ff3fcc67eef
7
- data.tar.gz: 0e598d16bae9973b706e739ff87ea767d96859d2a27349890bb848c783161bfffccf99d26ffa231ee4e9a3ccb4dfe137ab76c6ef88eb6b32fee9f7dc3dfdb2c5
6
+ metadata.gz: 9ea541f36dbc6de129d6bd889a8b198bf5e4805a578e204dc21dfc01c29551f869e064b7c315a9cd7e2732cef58ad820851684df55568922135dd4866f5d8ff7
7
+ data.tar.gz: ff206594a72e31e5504f935b437ea105327f7540d5d1a8530f202d35419c278f9d78a5e7f413e86c519bf7bf12b54341aadb7591cfa719f35ea1693d4d4998b2
data/README.md CHANGED
@@ -132,10 +132,11 @@ The `node` argument passed to some methods in a custom scrubber is an instance o
132
132
  - [`Nokogiri::XML::Node`](http://nokogiri.org/Nokogiri/XML/Node.html)
133
133
  - [Nokogiri](http://nokogiri.org)
134
134
 
135
- ## Contributing
135
+ ## Contributing to Rails Html Sanitizers
136
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
137
+ Rails Html Sanitizers is work of many contributors. You're encouraged to submit pull requests, propose features and discuss issues.
138
+
139
+ See [CONTRIBUTING](CONTRIBUTING.md).
140
+
141
+ ## License
142
+ Rails Html Sanitizers is released under the [MIT License](MIT-LICENSE).
@@ -13,6 +13,10 @@ module Rails
13
13
  node.xpath(*xpaths).remove
14
14
  node
15
15
  end
16
+
17
+ def properly_encode(fragment, options)
18
+ fragment.xml? ? fragment.to_xml(options) : fragment.to_html(options)
19
+ end
16
20
  end
17
21
 
18
22
  # === Rails::Html::FullSanitizer
@@ -26,9 +30,12 @@ module Rails
26
30
  return unless html
27
31
  return html if html.empty?
28
32
 
29
- Loofah.fragment(html).tap do |fragment|
30
- remove_xpaths(fragment, XPATHS_TO_REMOVE)
31
- end.text(options)
33
+ loofah_fragment = Loofah.fragment(html)
34
+
35
+ remove_xpaths(loofah_fragment, XPATHS_TO_REMOVE)
36
+ loofah_fragment.scrub!(TextOnlyScrubber.new)
37
+
38
+ properly_encode(loofah_fragment, encoding: 'UTF-8')
32
39
  end
33
40
  end
34
41
 
@@ -97,6 +104,10 @@ module Rails
97
104
  attr_accessor :allowed_tags
98
105
  attr_accessor :allowed_attributes
99
106
  end
107
+ self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
108
+ sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr
109
+ acronym a img blockquote del ins))
110
+ self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
100
111
 
101
112
  def initialize
102
113
  @permit_scrubber = PermitScrubber.new
@@ -136,10 +147,6 @@ module Rails
136
147
  def allowed_attributes(options)
137
148
  options[:attributes] || self.class.allowed_attributes
138
149
  end
139
-
140
- def properly_encode(fragment, options)
141
- fragment.xml? ? fragment.to_xml(options) : fragment.to_html(options)
142
- end
143
150
  end
144
151
  end
145
152
  end
@@ -1,7 +1,7 @@
1
1
  module Rails
2
2
  module Html
3
3
  class Sanitizer
4
- VERSION = "1.0.2"
4
+ VERSION = "1.0.3"
5
5
  end
6
6
  end
7
7
  end
@@ -60,6 +60,11 @@ module Rails
60
60
  end
61
61
 
62
62
  def scrub(node)
63
+ if node.cdata?
64
+ text = node.document.create_text_node node.text
65
+ node.replace text
66
+ return CONTINUE
67
+ end
63
68
  return CONTINUE if skip_node?(node)
64
69
 
65
70
  unless keep_node?(node)
@@ -76,7 +81,7 @@ module Rails
76
81
  end
77
82
 
78
83
  def skip_node?(node)
79
- node.text? || node.cdata?
84
+ node.text?
80
85
  end
81
86
 
82
87
  def scrub_attribute?(name)
@@ -100,6 +105,7 @@ module Rails
100
105
  if @attributes
101
106
  node.attribute_nodes.each do |attr|
102
107
  attr.remove if scrub_attribute?(attr.name)
108
+ scrub_attribute(node, attr)
103
109
  end
104
110
 
105
111
  scrub_css_attribute(node)
@@ -123,6 +129,30 @@ module Rails
123
129
  end
124
130
  var
125
131
  end
132
+
133
+ def scrub_attribute(node, attr_node)
134
+ attr_name = if attr_node.namespace
135
+ "#{attr_node.namespace.prefix}:#{attr_node.node_name}"
136
+ else
137
+ attr_node.node_name
138
+ end
139
+
140
+ if Loofah::HTML5::WhiteList::ATTR_VAL_IS_URI.include?(attr_name)
141
+ # this block lifted nearly verbatim from HTML5 sanitization
142
+ val_unescaped = CGI.unescapeHTML(attr_node.value).gsub(Loofah::HTML5::Scrub::CONTROL_CHARACTERS,'').downcase
143
+ if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ && ! Loofah::HTML5::WhiteList::ALLOWED_PROTOCOLS.include?(val_unescaped.split(Loofah::HTML5::WhiteList::PROTOCOL_SEPARATOR)[0])
144
+ attr_node.remove
145
+ end
146
+ end
147
+ if Loofah::HTML5::WhiteList::SVG_ATTR_VAL_ALLOWS_REF.include?(attr_name)
148
+ attr_node.value = attr_node.value.gsub(/url\s*\(\s*[^#\s][^)]+?\)/m, ' ') if attr_node.value
149
+ end
150
+ if Loofah::HTML5::WhiteList::SVG_ALLOW_LOCAL_HREF.include?(node.name) && attr_name == 'xlink:href' && attr_node.value =~ /^\s*[^#\s].*/m
151
+ attr_node.remove
152
+ end
153
+
154
+ node.remove_attribute(attr_node.name) if attr_name == 'src' && attr_node.value !~ /[^[:space:]]/
155
+ end
126
156
  end
127
157
 
128
158
  # === Rails::Html::TargetScrubber
@@ -137,11 +167,31 @@ module Rails
137
167
  # If set, attributes included will be removed.
138
168
  class TargetScrubber < PermitScrubber
139
169
  def allowed_node?(node)
140
- !@tags.include?(node.name)
170
+ !super
141
171
  end
142
172
 
143
173
  def scrub_attribute?(name)
144
- @attributes.include?(name)
174
+ !super
175
+ end
176
+ end
177
+
178
+ # === Rails::Html::TextOnlyScrubber
179
+ #
180
+ # Rails::Html::TextOnlyScrubber allows you to permit text nodes.
181
+ #
182
+ # Unallowed elements will be stripped, i.e. element is removed but its subtree kept.
183
+ class TextOnlyScrubber < Loofah::Scrubber
184
+ def initialize
185
+ @direction = :bottom_up
186
+ end
187
+
188
+ def scrub(node)
189
+ if node.text?
190
+ CONTINUE
191
+ else
192
+ node.before node.children
193
+ node.remove
194
+ end
145
195
  end
146
196
  end
147
197
  end
@@ -11,6 +11,16 @@ class SanitizersTest < Minitest::Test
11
11
  end
12
12
  end
13
13
 
14
+ def test_sanitize_nested_script
15
+ sanitizer = Rails::Html::WhiteListSanitizer.new
16
+ assert_equal '&lt;script&gt;alert("XSS");&lt;/script&gt;', sanitizer.sanitize('<script><script></script>alert("XSS");<script><</script>/</script><script>script></script>', tags: %w(em))
17
+ end
18
+
19
+ def test_sanitize_nested_script_in_style
20
+ sanitizer = Rails::Html::WhiteListSanitizer.new
21
+ assert_equal '&lt;script&gt;alert("XSS");&lt;/script&gt;', sanitizer.sanitize('<style><script></style>alert("XSS");<style><</style>/</style><style>script></style>', tags: %w(em))
22
+ end
23
+
14
24
  class XpathRemovalTestSanitizer < Rails::Html::Sanitizer
15
25
  def sanitize(html, options = {})
16
26
  fragment = Loofah.fragment(html)
@@ -104,9 +114,12 @@ class SanitizersTest < Minitest::Test
104
114
  assert_equal "Frozen string with no tags", full_sanitize("Frozen string with no tags".freeze)
105
115
  end
106
116
 
107
- def test_full_sanitize_allows_turning_off_encoding_special_chars
117
+ def test_full_sanitize_respect_html_escaping_of_the_given_string
118
+ assert_equal 'test\r\nstring', full_sanitize('test\r\nstring')
108
119
  assert_equal '&amp;', full_sanitize('&')
109
- assert_equal '&', full_sanitize('&', encode_special_chars: false)
120
+ assert_equal '&amp;', full_sanitize('&amp;')
121
+ assert_equal '&amp;amp;', full_sanitize('&amp;amp;')
122
+ assert_equal 'omg &lt;script&gt;BOM&lt;/script&gt;', full_sanitize('omg &lt;script&gt;BOM&lt;/script&gt;')
110
123
  end
111
124
 
112
125
  def test_strip_links_with_tags_in_tags
@@ -152,7 +165,7 @@ class SanitizersTest < Minitest::Test
152
165
  end
153
166
 
154
167
  def test_sanitize_script
155
- assert_sanitized "a b c<script language=\"Javascript\">blah blah blah</script>d e f", "a b cd e f"
168
+ assert_sanitized "a b c<script language=\"Javascript\">blah blah blah</script>d e f", "a b cblah blah blahd e f"
156
169
  end
157
170
 
158
171
  def test_sanitize_js_handlers
@@ -173,17 +186,23 @@ class SanitizersTest < Minitest::Test
173
186
  tags = Loofah::HTML5::WhiteList::ALLOWED_ELEMENTS - %w(script form)
174
187
  tags.each do |tag_name|
175
188
  define_method "test_should_allow_#{tag_name}_tag" do
176
- 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)
189
+ scope_allowed_tags(tags) do
190
+ 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)
191
+ end
177
192
  end
178
193
  end
179
194
 
180
195
  def test_should_allow_anchors
181
- assert_sanitized %(<a href="foo" onclick="bar"><script>baz</script></a>), %(<a href=\"foo\"></a>)
196
+ assert_sanitized %(<a href="foo" onclick="bar"><script>baz</script></a>), %(<a href=\"foo\">baz</a>)
182
197
  end
183
198
 
184
199
  def test_video_poster_sanitization
185
- assert_sanitized %(<video src="videofile.ogg" autoplay poster="posterimage.jpg"></video>), %(<video src="videofile.ogg" poster="posterimage.jpg"></video>)
186
- assert_sanitized %(<video src="videofile.ogg" poster=javascript:alert(1)></video>), %(<video src="videofile.ogg"></video>)
200
+ scope_allowed_tags(%w(video)) do
201
+ scope_allowed_attributes %w(src poster) do
202
+ assert_sanitized %(<video src="videofile.ogg" autoplay poster="posterimage.jpg"></video>), %(<video src="videofile.ogg" poster="posterimage.jpg"></video>)
203
+ assert_sanitized %(<video src="videofile.ogg" poster=javascript:alert(1)></video>), %(<video src="videofile.ogg"></video>)
204
+ end
205
+ end
187
206
  end
188
207
 
189
208
  # RFC 3986, sec 4.2
@@ -309,7 +328,7 @@ class SanitizersTest < Minitest::Test
309
328
  end
310
329
 
311
330
  def test_should_not_fall_for_xss_image_hack_with_uppercase_tags
312
- assert_sanitized %(<IMG """><SCRIPT>alert("XSS")</SCRIPT>">), "<img>\"&gt;"
331
+ assert_sanitized %(<IMG """><SCRIPT>alert("XSS")</SCRIPT>">), %(<img>alert("XSS")"&gt;)
313
332
  end
314
333
 
315
334
  [%(<IMG SRC="javascript:alert('XSS');">),
@@ -326,8 +345,8 @@ class SanitizersTest < Minitest::Test
326
345
  %(<IMG SRC="jav&#x0D;ascript:alert('XSS');">),
327
346
  %(<IMG SRC=" &#14; javascript:alert('XSS');">),
328
347
  %(<IMG SRC="javascript&#x3a;alert('XSS');">),
329
- %(<IMG SRC=`javascript:alert("RSnake says, 'XSS'")`>)].each_with_index do |img_hack, i|
330
- define_method "test_should_not_fall_for_xss_image_hack_#{i+1}" do
348
+ %(<IMG SRC=`javascript:alert("RSnake says, 'XSS'")`>)].each do |img_hack|
349
+ define_method "test_should_not_fall_for_xss_image_hack_#{img_hack}" do
331
350
  assert_sanitized img_hack, "<img>"
332
351
  end
333
352
  end
@@ -453,6 +472,16 @@ class SanitizersTest < Minitest::Test
453
472
  end
454
473
  end
455
474
 
475
+ def test_sanitize_data_attributes
476
+ assert_sanitized %(<a href="/blah" data-method="post">foo</a>), %(<a href="/blah">foo</a>)
477
+ assert_sanitized %(<a data-remote="true" data-type="script" data-method="get" data-cross-domain="true" href="attack.js">Launch the missiles</a>), %(<a href="attack.js">Launch the missiles</a>)
478
+ end
479
+
480
+ def test_allow_data_attribute_if_requested
481
+ text = %(<a data-foo="foo">foo</a>)
482
+ assert_equal %(<a data-foo="foo">foo</a>), white_list_sanitize(text, attributes: ['data-foo'])
483
+ end
484
+
456
485
  protected
457
486
 
458
487
  def xpath_sanitize(input, options = {})
@@ -484,18 +513,18 @@ protected
484
513
  end
485
514
 
486
515
  def scope_allowed_tags(tags)
516
+ old_tags = Rails::Html::WhiteListSanitizer.allowed_tags
487
517
  Rails::Html::WhiteListSanitizer.allowed_tags = tags
488
518
  yield Rails::Html::WhiteListSanitizer.new
489
-
490
519
  ensure
491
- Rails::Html::WhiteListSanitizer.allowed_tags = nil
520
+ Rails::Html::WhiteListSanitizer.allowed_tags = old_tags
492
521
  end
493
522
 
494
523
  def scope_allowed_attributes(attributes)
524
+ old_attributes = Rails::Html::WhiteListSanitizer.allowed_attributes
495
525
  Rails::Html::WhiteListSanitizer.allowed_attributes = attributes
496
526
  yield Rails::Html::WhiteListSanitizer.new
497
-
498
527
  ensure
499
- Rails::Html::WhiteListSanitizer.allowed_attributes = nil
528
+ Rails::Html::WhiteListSanitizer.allowed_attributes = old_attributes
500
529
  end
501
530
  end
@@ -143,6 +143,20 @@ class TargetScrubberTest < ScrubberTest
143
143
  end
144
144
  end
145
145
 
146
+ class TextOnlyScrubberTest < ScrubberTest
147
+ def setup
148
+ @scrubber = Rails::Html::TextOnlyScrubber.new
149
+ end
150
+
151
+ def test_removes_all_tags_and_keep_the_content
152
+ assert_scrubbed '<tag>hello</tag>', 'hello'
153
+ end
154
+
155
+ def test_skips_text_nodes
156
+ assert_node_skipped('some text')
157
+ end
158
+ end
159
+
146
160
  class ReturningStopFromScrubNodeTest < ScrubberTest
147
161
  class ScrubStopper < Rails::Html::PermitScrubber
148
162
  def scrub_node(node)
@@ -157,4 +171,4 @@ class ReturningStopFromScrubNodeTest < ScrubberTest
157
171
  def test_returns_stop_from_scrub_if_scrub_node_does
158
172
  assert_scrub_stopped '<script>remove me</script>'
159
173
  end
160
- end
174
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-html-sanitizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafael Mendonça França
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-03-10 00:00:00.000000000 Z
12
+ date: 2016-01-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: loofah
@@ -90,7 +90,6 @@ extensions: []
90
90
  extra_rdoc_files: []
91
91
  files:
92
92
  - CHANGELOG.md
93
- - LICENSE.txt
94
93
  - README.md
95
94
  - lib/rails-html-sanitizer.rb
96
95
  - lib/rails/html/sanitizer.rb
@@ -98,7 +97,7 @@ files:
98
97
  - lib/rails/html/scrubbers.rb
99
98
  - test/sanitizer_test.rb
100
99
  - test/scrubbers_test.rb
101
- homepage: https://github.com/rafaelfranca/rails-html-sanitizer
100
+ homepage: https://github.com/rails/rails-html-sanitizer
102
101
  licenses:
103
102
  - MIT
104
103
  metadata: {}
@@ -118,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
117
  version: '0'
119
118
  requirements: []
120
119
  rubyforge_project:
121
- rubygems_version: 2.4.5
120
+ rubygems_version: 2.5.1
122
121
  signing_key:
123
122
  specification_version: 4
124
123
  summary: This gem is responsible to sanitize HTML fragments in Rails applications.
@@ -1,22 +0,0 @@
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.