Cross-site Scripting in Sanitize
Description
In Sanitize (RubyGem sanitize) greater than or equal to 3.0.0 and less than 5.2.1, there is a cross-site scripting vulnerability. When HTML is sanitized using Sanitize's "relaxed" config, or a custom config that allows certain elements, some content in a math or svg element may not be sanitized correctly even if math and svg are not in the allowlist. You are likely to be vulnerable to this issue if you use Sanitize's relaxed config or a custom config that allows one or more of the following HTML elements: iframe, math, noembed, noframes, noscript, plaintext, script, style, svg, xmp. Using carefully crafted input, an attacker may be able to sneak arbitrary HTML through Sanitize, potentially resulting in XSS (cross-site scripting) or other undesired behavior when that HTML is rendered in a browser. This has been fixed in 5.2.1.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
CVE-2020-4054 is an XSS vulnerability in Ruby Gem Sanitize versions 3.0.0 to 5.2.0, where sanitization bypass occurs in math/svg elements leading to arbitrary HTML injection.
The vulnerability lies in Sanitize's handling of HTML foreign content, specifically within ` and elements. Due to differences in parsing rules for these elements, Sanitize fails to sanitize content inside them even when math and svg` are not in the allowlist [1]. This allows attackers to inject arbitrary HTML through carefully crafted input [2].
Exploitation requires using Sanitize's "relaxed" config or a custom config that permits certain elements such as `, , , , , , , , , or ` [2]. The vulnerability affects versions 3.0.0 up to but not including 5.2.1, all versions after the gem's initial release [2].
Successful exploitation could lead to cross-site scripting (XSS) or other undesired behavior when the sanitized HTML is rendered in a browser [2]. The fix introduces explicit warnings that Sanitize cannot fully sanitize ` and ` contents and updates the relaxed config to remove these elements from allowlisting [3][4].
The vulnerability is fixed in Sanitize version 5.2.1. Users are advised to upgrade immediately. Workarounds are not recommended, as the core issue is architectural within the HTML parsing approach [2][3].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
sanitizeRubyGems | >= 3.0.0, < 5.2.1 | 5.2.1 |
Affected products
2- rgrove/Sanitizev5Range: >= 3.0.0, < 5.2.1
Patches
1a11498de9e28Fix sanitization bypass in HTML foreign content
4 files changed · +45 −11
lib/sanitize/config/default.rb+1 −1 modified@@ -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
README.md+11 −0 modified@@ -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>` @@ -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`.
test/test_clean_element.rb+20 −10 modified@@ -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! <script>alert(0)</script>' - end - - it 'should escape the content of removed `xmp` elements' do - Sanitize.fragment('<xmp>hello! <script>alert(0)</script></xmp>') - .must_equal 'hello! <script>alert(0)</script>' - 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 '' @@ -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 '' @@ -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])
test/test_malicious_html.rb+13 −0 modified@@ -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}>/*</#{tag_name}><img src onerror=alert(1)>*/]). + must_equal '' + + @s.fragment(%[<svg><#{tag_name}>/*</#{tag_name}><img src onerror=alert(1)>*/]). + must_equal '' + end + end + end end
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- github.com/advisories/GHSA-p4x4-rw2p-8j8mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-4054ghsaADVISORY
- usn.ubuntu.com/4543-1/mitrevendor-advisoryx_refsource_UBUNTU
- www.debian.org/security/2020/dsa-4730ghsavendor-advisoryx_refsource_DEBIANWEB
- github.com/rgrove/sanitize/commit/a11498de9e283cd457b35ee252983662f7452aa9ghsax_refsource_MISCWEB
- github.com/rgrove/sanitize/releases/tag/v5.2.1ghsax_refsource_MISCWEB
- github.com/rgrove/sanitize/security/advisories/GHSA-p4x4-rw2p-8j8mghsax_refsource_CONFIRMWEB
- github.com/rubysec/ruby-advisory-db/blob/master/gems/sanitize/CVE-2020-4054.ymlghsaWEB
- usn.ubuntu.com/4543-1ghsaWEB
News mentions
0No linked articles in our index yet.