VYPR
Moderate severityNVD Advisory· Published Jan 27, 2023· Updated Mar 10, 2025

Sanitize vulnerable to Cross-site Scripting via Improper neutralization of `noscript` element

CVE-2023-23627

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.

PackageAffected versionsPatched versions
sanitizeRubyGems
>= 5.0.0, < 6.0.16.0.1

Affected products

2
  • ghsa-coords
    Range: >= 5.0.0, < 6.0.1
  • rgrove/sanitizev5
    Range: >= 5.0.0, < 6.0.1

Patches

1
ec14265e530d

Always remove `<noscript>` elements

https://github.com/rgrove/sanitizeRyan GroveJan 26, 2023via ghsa
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.
     
     [![Gem Version](https://badge.fury.io/rb/sanitize.svg)](http://badge.fury.io/rb/sanitize)
     [![Tests](https://github.com/rgrove/sanitize/workflows/Tests/badge.svg)](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>&lt;img src=x onerror=alert(1)&gt; '>])
    +      )
    +    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

News mentions

0

No linked articles in our index yet.