CVE-2009-4214
Description
Cross-site scripting (XSS) vulnerability in the strip_tags function in Ruby on Rails before 2.2.s, and 2.3.x before 2.3.5, allows remote attackers to inject arbitrary web script or HTML via vectors involving non-printing ASCII characters, related to HTML::Tokenizer and actionpack/lib/action_controller/vendor/html-scanner/html/node.rb.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
railsRubyGems | < 2.2.2 | 2.2.2 |
railsRubyGems | >= 2.3.0, < 2.3.5 | 2.3.5 |
Affected products
56cpe:2.3:a:rubyonrails:rails:0.10.0:*:*:*:*:*:*:*+ 43 more
- cpe:2.3:a:rubyonrails:rails:0.10.0:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.10.1:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.11.0:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.11.1:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.12.0:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.12.1:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.13.0:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.13.1:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.14.1:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.14.2:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.14.3:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.14.4:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.9.1:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.9.2:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.9.3:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.9.4:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:0.9.4.1:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.0.0:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.1.0:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.1.1:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.1.2:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.1.3:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.1.4:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.1.5:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.1.6:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.2.0:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.2.1:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.2.2:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.2.3:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.2.4:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.2.5:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.2.6:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:1.9.5:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:2.0.0:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:2.0.0:rc1:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:2.0.0:rc2:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:2.0.1:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:2.0.2:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:2.0.4:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:2.1.0:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:2.1.1:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:2.3.2:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:2.3.3:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:rails:2.3.4:*:*:*:*:*:*:*
cpe:2.3:a:rubyonrails:ruby_on_rails:*:*:*:*:*:*:*:*+ 10 more
- cpe:2.3:a:rubyonrails:ruby_on_rails:*:*:*:*:*:*:*:*range: <=2.1.2
- cpe:2.3:a:rubyonrails:ruby_on_rails:0.5.0:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:ruby_on_rails:0.5.5:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:ruby_on_rails:0.5.6:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:ruby_on_rails:0.5.7:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:ruby_on_rails:0.6.0:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:ruby_on_rails:0.6.5:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:ruby_on_rails:0.7.0:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:ruby_on_rails:0.8.0:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:ruby_on_rails:0.8.5:*:*:*:*:*:*:*
- cpe:2.3:a:rubyonrails:ruby_on_rails:0.9.0:*:*:*:*:*:*:*
Patches
1bfe032858077Make sure strip_tags removes tags which start with a non-printable character
2 files changed · +2 −1
actionpack/lib/action_controller/vendor/html-scanner/html/node.rb+1 −1 modified@@ -162,7 +162,7 @@ def parse(parent, line, pos, content, strict=true) end closing = ( scanner.scan(/\//) ? :close : nil ) - return Text.new(parent, line, pos, content) unless name = scanner.scan(/[\w:-]+/) + return Text.new(parent, line, pos, content) unless name = scanner.scan(/[-:\w\x00-\x09\x0b-\x0c\x0e-\x1f]+/) name.downcase! unless closing
actionpack/test/controller/html-scanner/sanitizer_test.rb+1 −0 modified@@ -19,6 +19,7 @@ def test_strip_tags assert_equal "This has a here.", sanitizer.sanitize("This has a <!-- comment --> here.") assert_equal "This has a here.", sanitizer.sanitize("This has a <![CDATA[<section>]]> here.") assert_equal "This has an unclosed ", sanitizer.sanitize("This has an unclosed <![CDATA[<section>]] here...") + assert_equal "non printable char is a tag", sanitizer.sanitize("<\x07a href='/hello'>non printable char is a tag</a>") [nil, '', ' '].each { |blank| assert_equal blank, sanitizer.sanitize(blank) } end
Vulnerability mechanics
Root cause
"The strip_tags function's tokenizer fails to recognize HTML tags that begin with non-printing ASCII characters, causing them to be treated as text rather than stripped."
Attack vector
An attacker can inject arbitrary HTML or JavaScript by prefixing a tag with a non-printing ASCII character (e.g., \x07, \x00-\x09, \x0b-\x0c, \x0e-\x1f). Because the tokenizer's regex [CWE-79] only matched `[\w:-]+` for tag names, a tag like `<\x07a href='...'>` was not recognized as a tag and was passed through as text. When this unsanitized output is rendered in a victim's browser, the browser interprets the content as a valid HTML tag, executing the attacker's script.
Affected code
The vulnerability is in `actionpack/lib/action_controller/vendor/html-scanner/html/node.rb` within the `parse` method of the `Tag` class. The regex `scanner.scan(/[\w:-]+/)` on line 165 (pre-patch) fails to match tag names that begin with non-printing ASCII characters, causing the tokenizer to return a `Text` node instead of a `Tag` node. This affects the `strip_tags` function used throughout Rails views and sanitization helpers.
What the fix does
The patch [patch_id=25] modifies the regex in `node.rb` from `[\w:-]+` to `[-:\w\x00-\x09\x0b-\x0c\x0e-\x1f]+`. This expanded character class now includes non-printing ASCII characters (control characters in the ranges \x00-\x09, \x0b-\x0c, \x0e-\x1f) as valid tag-name characters. When the tokenizer encounters a tag starting with such a character, it now correctly identifies it as a tag and strips it, rather than falling through to return a `Text` node that would be rendered unsanitized. The accompanying test confirms that `<\x07a href='/hello'>` is now properly stripped.
Preconditions
- inputThe application must use strip_tags or a sanitizer that relies on the vulnerable tokenizer to process user-supplied input.
- networkThe attacker must be able to submit input containing non-printing ASCII characters (e.g., via form fields, URL parameters, or API payloads) that is later rendered in a web page served to other users.
Generated on May 19, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
18- github.com/rails/rails/commit/bfe032858077bb2946abe25e95e485ba6da86bd5nvdPatchWEB
- www.securityfocus.com/bid/37142nvdPatchWEB
- www.vupen.com/english/advisories/2009/3352nvdPatchVendor AdvisoryWEB
- secunia.com/advisories/37446nvdVendor AdvisoryWEB
- secunia.com/advisories/38915nvdVendor AdvisoryWEB
- github.com/advisories/GHSA-9p3v-wf2w-v29cghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2009-4214ghsaADVISORY
- groups.google.com/group/rubyonrails-security/browse_thread/thread/4d4f71f2aef4c0abnvdWEB
- lists.apple.com/archives/security-announce/2010//Mar/msg00001.htmlnvdWEB
- lists.opensuse.org/opensuse-security-announce/2010-03/msg00004.htmlnvdWEB
- support.apple.com/kb/HT4077nvdWEB
- weblog.rubyonrails.org/2009/11/30/ruby-on-rails-2-3-5-releasednvdWEB
- www.debian.org/security/2011/dsa-2260nvdWEB
- www.debian.org/security/2011/dsa-2301nvdWEB
- www.openwall.com/lists/oss-security/2009/11/27/2nvdWEB
- www.openwall.com/lists/oss-security/2009/12/08/3nvdWEB
- www.securitytracker.com/idnvdWEB
- github.com/rubysec/ruby-advisory-db/blob/master/gems/rails/CVE-2009-4214.ymlghsaWEB
News mentions
0No linked articles in our index yet.