rails-html-sanitizer 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.

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.