Skip to content

Commit

Permalink
Fix sanitization bypass in HTML foreign content
Browse files Browse the repository at this point in the history
  • Loading branch information
rgrove committed Jun 16, 2020
1 parent 1d0d688 commit a11498d
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 11 deletions.
11 changes: 11 additions & 0 deletions README.md
Expand Up @@ -72,6 +72,11 @@ Sanitize can sanitize the following types of input:
* Standalone CSS stylesheets
* Standalone CSS properties

However, please note that Sanitize _cannot_ fully sanitize the contents of
`<math>` or `<svg>` elements, since these elements don't follow the same parsing
rules as the rest of HTML. If this is something you need, you may want to look
for another solution.

### HTML Fragments

A fragment is a snippet of HTML that doesn't contain a root-level `<html>`
Expand Down Expand Up @@ -415,6 +420,12 @@ elements not in this array will be removed.
]
```

**Warning:** Sanitize cannot fully sanitize the contents of `<math>` or `<svg>`
elements, since these elements don't follow the same parsing rules as the rest
of HTML. If you add `math` or `svg` to the allowlist, you must assume that any
content inside them will be allowed, even if that content would otherwise be
removed by Sanitize.

#### :parser_options (Hash)

[Parsing options](https://github.com/rubys/nokogumbo/tree/v2.0.1#parsing-options) supplied to `nokogumbo`.
Expand Down
2 changes: 1 addition & 1 deletion lib/sanitize/config/default.rb
Expand Up @@ -74,7 +74,7 @@ module Config
# the specified elements (when filtered) will be removed, and the contents
# of all other filtered elements will be left behind.
:remove_contents => %w[
iframe noembed noframes noscript script style
iframe math noembed noframes noscript plaintext script style svg xmp
],

# Transformers allow you to filter or alter nodes using custom logic. See
Expand Down
30 changes: 20 additions & 10 deletions test/test_clean_element.rb
Expand Up @@ -192,21 +192,16 @@
.must_equal ''
end

it 'should escape the content of removed `plaintext` elements' do
Sanitize.fragment('<plaintext>hello! <script>alert(0)</script>')
.must_equal 'hello! &lt;script&gt;alert(0)&lt;/script&gt;'
end

it 'should escape the content of removed `xmp` elements' do
Sanitize.fragment('<xmp>hello! <script>alert(0)</script></xmp>')
.must_equal 'hello! &lt;script&gt;alert(0)&lt;/script&gt;'
end

it 'should not preserve the content of removed `iframe` elements' do
Sanitize.fragment('<iframe>hello! <script>alert(0)</script></iframe>')
.must_equal ''
end

it 'should not preserve the content of removed `math` elements' do
Sanitize.fragment('<math>hello! <script>alert(0)</script></math>')
.must_equal ''
end

it 'should not preserve the content of removed `noembed` elements' do
Sanitize.fragment('<noembed>hello! <script>alert(0)</script></noembed>')
.must_equal ''
Expand All @@ -222,6 +217,11 @@
.must_equal ''
end

it 'should not preserve the content of removed `plaintext` elements' do
Sanitize.fragment('<plaintext>hello! <script>alert(0)</script>')
.must_equal ''
end

it 'should not preserve the content of removed `script` elements' do
Sanitize.fragment('<script>hello! <script>alert(0)</script></script>')
.must_equal ''
Expand All @@ -232,6 +232,16 @@
.must_equal ''
end

it 'should not preserve the content of removed `svg` elements' do
Sanitize.fragment('<svg>hello! <script>alert(0)</script></svg>')
.must_equal ''
end

it 'should not preserve the content of removed `xmp` elements' do
Sanitize.fragment('<xmp>hello! <script>alert(0)</script></xmp>')
.must_equal ''
end

strings.each do |name, data|
it "should clean #{name} HTML" do
Sanitize.fragment(data[:html]).must_equal(data[:default])
Expand Down
13 changes: 13 additions & 0 deletions test/test_malicious_html.rb
Expand Up @@ -219,4 +219,17 @@
end
end
end

# https://github.com/rgrove/sanitize/security/advisories/GHSA-p4x4-rw2p-8j8m
describe 'foreign content bypass in relaxed config' do
it 'prevents a sanitization bypass via carefully crafted foreign content' do
%w[iframe noembed noframes noscript plaintext script style xmp].each do |tag_name|
@s.fragment(%[<math><#{tag_name}>/*&lt;/#{tag_name}&gt;&lt;img src onerror=alert(1)>*/]).
must_equal ''

@s.fragment(%[<svg><#{tag_name}>/*&lt;/#{tag_name}&gt;&lt;img src onerror=alert(1)>*/]).
must_equal ''
end
end
end
end

0 comments on commit a11498d

Please sign in to comment.