Sanitize vulnerable to Cross-site Scripting via Improper neutralization of `noscript` element
Description
Sanitize is an allowlist-based HTML and CSS sanitizer. Versions 5.0.0 and later, prior to 6.0.1, are vulnerable to Cross-site Scripting. When Sanitize is configured with a custom allowlist that allows noscript elements, attackers are able to include arbitrary HTML, resulting in XSS (cross-site scripting) or other undesired behavior when that HTML is rendered in a browser. The default configurations do not allow noscript elements and are not vulnerable. This issue only affects users who are using a custom config that adds noscript to the element allowlist. This issue has been patched in version 6.0.1. Users who are unable to upgrade can prevent this issue by using one of Sanitize's default configs or by ensuring that their custom config does not include noscript in the element allowlist.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Sanitize versions prior to 6.0.1 are vulnerable to XSS when a custom allowlist includes noscript elements, allowing arbitrary HTML injection.
Vulnerability
Details
Sanitize is an allowlist-based HTML and CSS sanitizer for Ruby [1]. Versions 5.0.0 and later, prior to 6.0.1, are vulnerable to Cross-site Scripting (XSS) [2]. The vulnerability occurs when Sanitize is configured with a custom allowlist that permits noscript elements. Due to differences in how browsers parse noscript content depending on whether scripting is enabled, and because the Nokogiri parser always parses as if scripting is disabled, it is impossible to reliably sanitize the contents of a noscript element [3]. As a result, an attacker can include arbitrary HTML within a noscript tag, leading to XSS or other undesired behavior when the HTML is rendered in a browser [2].
Exploitation
Exploitation requires a non-default configuration where the noscript element is added to the allowlist. Default configurations do not allow noscript and are not vulnerable [2]. An attacker can craft input containing malicious HTML inside a noscript element, which will be allowed through Sanitize's filtering due to the parsing discrepancy. No authentication or special network position is required beyond the ability to submit content that is subsequently sanitized with the vulnerable configuration.
Impact
Successful exploitation results in arbitrary HTML injection, enabling cross-site scripting attacks. An attacker may execute arbitrary JavaScript in the context of the victim's browser, potentially leading to session hijacking, data theft, or other malicious actions.
Mitigation
The issue has been patched in Sanitize version 6.0.1 [3]. Users unable to upgrade should either use one of Sanitize's default configurations or ensure that their custom allowlist does not include noscript [2]. The fix ensures that noscript elements are always removed, regardless of the allowlist [3]. This vulnerability is documented in the Ruby Advisory Database [4].
AI Insight generated on May 20, 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 | >= 5.0.0, < 6.0.1 | 6.0.1 |
Affected products
2- rgrove/sanitizev5Range: >= 5.0.0, < 6.0.1
Patches
1ec14265e530dAlways remove `<noscript>` elements
5 files changed · +73 −5
HISTORY.md+27 −0 modified@@ -4,6 +4,33 @@ ### Bug Fixes +* Sanitize now always removes `<noscript>` elements and their contents, even + when `noscript` is in the allowlist. + + This fixes a sanitization bypass that could occur when `noscript` was allowed + by a custom allowlist. In this scenario, carefully crafted input could sneak + arbitrary HTML through Sanitize, potentially enabling an XSS (cross-site + scripting) attack. + + Sanitize's default configs don't allow `<noscript>` elements and are not + vulnerable. This issue only affects users who are using a custom config that + adds `noscript` to the element allowlist. + + The root cause of this issue is that HTML parsing rules treat the contents of + a `<noscript>` element differently depending on whether scripting is enabled + in the user agent. Nokogiri doesn't support scripting so it follows the + "scripting disabled" rules, but a web browser with scripting enabled will + follow the "scripting enabled" rules. This means that Sanitize can't reliably + make the contents of a `<noscript>` element safe for scripting enabled + browsers, so the safest thing to do is to remove the element and its contents + entirely. + + See the following security advisory for additional details: + [GHSA-fw3g-2h3j-qmm7](https://github.com/rgrove/sanitize/security/advisories/GHSA-fw3g-2h3j-qmm7) + + Thanks to David Klein from [TU Braunschweig](https://www.tu-braunschweig.de/en/ias) + (@leeN) for reporting this issue. + * Fixed an edge case in which the contents of an "unescaped text" element (such as `<noembed>` or `<xmp>`) were not properly escaped if that element was allowlisted and was also inside an allowlisted `<math>` or `<svg>` element.
lib/sanitize/transformers/clean_element.rb+10 −0 modified@@ -252,6 +252,16 @@ def call(env) node['content'] = node['content'].gsub(/;\s*charset\s*=.+\z/, ';charset=utf-8') end + + # A `<noscript>` element's content is parsed differently in browsers + # depending on whether or not scripting is enabled. Since Nokogiri doesn't + # support scripting, it always parses `<noscript>` elements as if scripting + # is disabled. This results in edge cases where it's not possible to + # reliably sanitize the contents of a `<noscript>` element because Nokogiri + # can't fully replicate the parsing behavior of a scripting-enabled browser. + # The safest thing to do is to simply remove all `<noscript>` elements. + when 'noscript' + node.unlink end end
README.md+10 −4 modified@@ -12,10 +12,10 @@ properties, @ rules, and URL protocols in elements or attributes containing CSS. Any HTML or CSS that you don't explicitly allow will be removed. Sanitize is based on the [Nokogumbo HTML5 parser][nokogumbo], which parses HTML -exactly the same way modern browsers do, and [Crass][crass], which parses CSS -exactly the same way modern browsers do. As long as your allowlist config only -allows safe markup and CSS, even the most malformed or malicious input will be -transformed into safe output. +the same way modern browsers do, and [Crass][crass], which parses CSS the same +way modern browsers do. As long as your allowlist config only allows safe markup +and CSS, even the most malformed or malicious input will be transformed into +safe output. [](http://badge.fury.io/rb/sanitize) [](https://github.com/rgrove/sanitize/actions?query=workflow%3ATests) @@ -427,6 +427,12 @@ elements not in this array will be removed. > > By default, Sanitize will remove all MathML and SVG elements. If you add MathML or SVG elements to a custom element allowlist, you must assume that any content inside them will be allowed, even if that content would otherwise be removed or escaped by Sanitize. This may create a security vulnerability in your application. +> **Note** +> +> Sanitize always removes `<noscript>` elements and their contents, even if `noscript` is in the allowlist. +> +> This is because a `<noscript>` element's content is parsed differently in browsers depending on whether or not scripting is enabled. Since Nokogiri doesn't support scripting, it always parses `<noscript>` elements as if scripting is disabled. This results in edge cases where it's not possible to reliably sanitize the contents of a `<noscript>` element because Nokogiri can't fully replicate the parsing behavior of a scripting-enabled browser. + #### :parser_options (Hash) [Parsing options](https://github.com/rubys/nokogumbo/tree/master#parsing-options) to be supplied to `nokogumbo`.
test/test_clean_element.rb+7 −0 modified@@ -541,5 +541,12 @@ )).must_equal "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"></head><body>Howdy!</body></html>" end + it 'always removes `<noscript>` elements even if `noscript` is in the allowlist' do + assert_equal( + '', + Sanitize.fragment('<noscript>foo</noscript>', elements: ['noscript']) + ) + end + end end
test/test_malicious_html.rb+19 −1 modified@@ -244,7 +244,6 @@ unescaped_content_elements = %w[ noembed noframes - noscript plaintext script xmp @@ -255,6 +254,7 @@ ] removed_elements = %w[ + noscript style ] @@ -318,4 +318,22 @@ end end end + + describe 'sanitization bypass by exploiting scripting-disabled <noscript> behavior' do + before do + @s = Sanitize.new( + Sanitize::Config.merge( + Sanitize::Config::RELAXED, + elements: Sanitize::Config::RELAXED[:elements] + ['noscript'] + ) + ) + end + + it 'is prevented by removing `<noscript>` elements regardless of the allowlist' do + assert_equal( + '', + @s.fragment(%[<noscript><div id='</noscript><img src=x onerror=alert(1)> '>]) + ) + 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
5- github.com/advisories/GHSA-fw3g-2h3j-qmm7ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-23627ghsaADVISORY
- github.com/rgrove/sanitize/commit/ec14265e530dc3fe31ce2ef773594d3a97778d22ghsaWEB
- github.com/rgrove/sanitize/security/advisories/GHSA-fw3g-2h3j-qmm7ghsax_refsource_CONFIRMWEB
- github.com/rubysec/ruby-advisory-db/blob/master/gems/sanitize/CVE-2023-23627.ymlghsaWEB
News mentions
0No linked articles in our index yet.