CVE-2013-0175
Description
multi_xml gem 0.5.2 for Ruby, as used in Grape before 0.2.6 and possibly other products, does not properly restrict casts of string values, which allows remote attackers to conduct object-injection attacks and execute arbitrary code, or cause a denial of service (memory and CPU consumption) involving nested XML entity references, by leveraging support for (1) YAML type conversion or (2) Symbol type conversion, a similar vulnerability to CVE-2013-0156.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
multi_xmlRubyGems | < 0.5.2 | 0.5.2 |
Affected products
13- cpe:2.3:a:erik_michaels-ober:multi_xml:0.5.2:*:*:*:*:*:*:*
cpe:2.3:a:grape_project:grape:0.1.0:*:*:*:*:*:*:*+ 11 more
- cpe:2.3:a:grape_project:grape:0.1.0:*:*:*:*:*:*:*
- cpe:2.3:a:grape_project:grape:0.1.1:*:*:*:*:*:*:*
- cpe:2.3:a:grape_project:grape:0.1.2:*:*:*:*:*:*:*
- cpe:2.3:a:grape_project:grape:0.1.3:*:*:*:*:*:*:*
- cpe:2.3:a:grape_project:grape:0.1.4:*:*:*:*:*:*:*
- cpe:2.3:a:grape_project:grape:0.1.5:*:*:*:*:*:*:*
- cpe:2.3:a:grape_project:grape:0.2.0:*:*:*:*:*:*:*
- cpe:2.3:a:grape_project:grape:0.2.1:*:*:*:*:*:*:*
- cpe:2.3:a:grape_project:grape:0.2.2:*:*:*:*:*:*:*
- cpe:2.3:a:grape_project:grape:0.2.3:*:*:*:*:*:*:*
- cpe:2.3:a:grape_project:grape:0.2.4:*:*:*:*:*:*:*
- cpe:2.3:a:grape_project:grape:0.2.5:*:*:*:*:*:*:*
Patches
1c94b136d0682Merge pull request #34 from nate/hotfix/remove-ability-to-parse-symbols-and-yaml
5 files changed · +68 −17
CONTRIBUTING.md+1 −1 modified@@ -39,7 +39,7 @@ Ideally, a bug report should include a pull request with failing specs. 7. Run `open coverage/index.html`. If your changes are not completely covered by your tests, return to step 3. 8. Add documentation for your feature or bug fix. -9. Run `bundle exec rake yard`. If your changes are not 100% documented, go +9. Run `bundle exec rake doc:yard`. If your changes are not 100% documented, go back to step 8. 10. Add, commit, and push your changes. 11. [Submit a pull request.][pr]
lib/multi_xml.rb+23 −6 modified@@ -7,6 +7,11 @@ module MultiXml class ParseError < StandardError; end + class DisallowedTypeError < StandardError + def initialize(type) + super "Disallowed type attribute: #{type.inspect}" + end + end REQUIREMENT_MAP = [ ['ox', :ox], @@ -54,6 +59,8 @@ class ParseError < StandardError; end 'Hash' => 'hash' } unless defined?(TYPE_NAMES) + DISALLOWED_XML_TYPES = %w(symbol yaml) + class << self # Get the current parser class. def parser @@ -105,6 +112,8 @@ def parser=(new_parser) # <b>Options</b> # # <tt>:symbolize_keys</tt> :: If true, will use symbols instead of strings for the keys. + # + # <tt>:disallowed_types</tt> :: Types to disallow from being typecasted. Defaults to `['yaml', 'symbol']`. Use `[]` to allow all types. def parse(xml, options={}) xml ||= '' @@ -116,7 +125,9 @@ def parse(xml, options={}) return {} if char.nil? xml.ungetc(char) - hash = typecast_xml_value(undasherize_keys(parser.parse(xml))) || {} + hash = typecast_xml_value(undasherize_keys(parser.parse(xml)), options[:disallowed_types]) || {} + rescue DisallowedTypeError + raise rescue parser.parse_error => error raise ParseError, error.to_s, error.backtrace end @@ -191,9 +202,15 @@ def undasherize_keys(params) end end - def typecast_xml_value(value) + def typecast_xml_value(value, disallowed_types=nil) + disallowed_types ||= DISALLOWED_XML_TYPES + case value when Hash + if value.include?('type') && !value['type'].is_a?(Hash) && disallowed_types.include?(value['type']) + raise DisallowedTypeError, value['type'] + end + if value['type'] == 'array' # this commented-out suggestion helps to avoid the multiple attribute @@ -216,9 +233,9 @@ def typecast_xml_value(value) else case entries when Array - entries.map {|entry| typecast_xml_value(entry)} + entries.map {|entry| typecast_xml_value(entry, disallowed_types)} when Hash - [typecast_xml_value(entries)] + [typecast_xml_value(entries, disallowed_types)] else raise "can't typecast #{entries.class.name}: #{entries.inspect}" end @@ -252,7 +269,7 @@ def typecast_xml_value(value) nil else xml_value = value.inject({}) do |hash, (k, v)| - hash[k] = typecast_xml_value(v) + hash[k] = typecast_xml_value(v, disallowed_types) hash end @@ -261,7 +278,7 @@ def typecast_xml_value(value) xml_value['file'].is_a?(StringIO) ? xml_value['file'] : xml_value end when Array - value.map!{|i| typecast_xml_value(i)} + value.map!{|i| typecast_xml_value(i, disallowed_types)} value.length > 1 ? value : value.first when String value
Rakefile+1 −1 modified@@ -10,7 +10,7 @@ task :default => :spec namespace :doc do require 'yard' YARD::Rake::YardocTask.new do |task| - task.files = ['LICENSE.md', 'lib/**/*.rb'] + task.files = ['lib/**/*.rb', '-', 'LICENSE.md'] task.options = [ '--no-private', '--protected',
README.md+8 −4 modified@@ -20,16 +20,20 @@ use it like so: ```ruby require 'multi_xml' -MultiXml.parser = :ox MultiXml.parser = MultiXml::Parsers::Ox # Same as above +MultiXml.parser = :ox +MultiXml.parser = MultiXml::Parsers::Ox # Same as above MultiXml.parse('<tag>This is the contents</tag>') # Parsed using Ox -MultiXml.parser = :libxml MultiXml.parser = MultiXml::Parsers::Libxml # Same as above +MultiXml.parser = :libxml +MultiXml.parser = MultiXml::Parsers::Libxml # Same as above MultiXml.parse('<tag>This is the contents</tag>') # Parsed using LibXML -MultiXml.parser = :nokogiri MultiXml.parser = MultiXml::Parsers::Nokogiri # Same as above +MultiXml.parser = :nokogiri +MultiXml.parser = MultiXml::Parsers::Nokogiri # Same as above MultiXml.parse('<tag>This is the contents</tag>') # Parsed using Nokogiri -MultiXml.parser = :rexml MultiXml.parser = MultiXml::Parsers::Rexml # Same as above +MultiXml.parser = :rexml +MultiXml.parser = MultiXml::Parsers::Rexml # Same as above MultiXml.parse('<tag>This is the contents</tag>') # Parsed using REXML ``` The `parser` setter takes either a symbol or a class (to allow for custom XML
spec/parser_shared_example.rb+35 −5 modified@@ -323,12 +323,26 @@ @xml = "<tag type=\"yaml\">--- \n1: returns an integer\n:message: Have a nice day\narray: \n- has-dashes: true\n has_underscores: true\n</tag>" end - it "returns a Hash" do - expect(MultiXml.parse(@xml)['tag']).to be_a(Hash) + it "raises MultiXML::DisallowedTypeError by default" do + expect{ MultiXml.parse(@xml)['tag'] }.to raise_error(MultiXml::DisallowedTypeError) end - it "returns the correctly parsed YAML" do - expect(MultiXml.parse(@xml)['tag']).to eq({:message => "Have a nice day", 1 => "returns an integer", "array" => [{"has-dashes" => true, "has_underscores" => true}]}) + it "returns the correctly parsed YAML when the type is allowed" do + expect(MultiXml.parse(@xml, :disallowed_types => [])['tag']).to eq({:message => "Have a nice day", 1 => "returns an integer", "array" => [{"has-dashes" => true, "has_underscores" => true}]}) + end + end + + context "with an attribute type=\"symbol\"" do + before do + @xml = "<tag type=\"symbol\">my_symbol</tag>" + end + + it "raises MultiXML::DisallowedTypeError" do + expect{ MultiXml.parse(@xml)['tag'] }.to raise_error(MultiXml::DisallowedTypeError) + end + + it "returns the correctly parsed Symbol when the type is allowed" do + expect(MultiXml.parse(@xml, :disallowed_types => [])['tag']).to eq(:my_symbol) end end @@ -418,7 +432,7 @@ end end - %w(integer boolean date datetime yaml file).each do |type| + %w(integer boolean date datetime file).each do |type| context "with an empty attribute type=\"#{type}\"" do before do @xml = "<tag type=\"#{type}\"/>" @@ -430,6 +444,22 @@ end end + %w{yaml symbol}.each do |type| + context "with an empty attribute type=\"#{type}\"" do + before do + @xml = "<tag type=\"#{type}\"/>" + end + + it "raises MultiXml::DisallowedTypeError by default" do + expect{ MultiXml.parse(@xml)['tag']}.to raise_error(MultiXml::DisallowedTypeError) + end + + it "returns nil when the type is allowed" do + expect(MultiXml.parse(@xml, :disallowed_types => [])['tag']).to be_nil + end + end + end + context "with an empty attribute type=\"array\"" do before do @xml = '<tag type="array"/>'
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
10- github.com/advisories/GHSA-pchc-949f-53m5ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2013-0175ghsaADVISORY
- github.com/sferik/multi_xml/commit/c94b136d06822514fc2e99dc851e6c4eeb4c8bdfghsaWEB
- github.com/sferik/multi_xml/pull/34nvdWEB
- groups.google.com/forum/ghsaWEB
- news.ycombinator.com/itemnvdWEB
- www.openwall.com/lists/oss-security/2013/01/11/9ghsaWEB
- www.openwall.com/lists/oss-security/2013/01/11/9nvd
- gist.github.com/nate/d7f6d9f4925f413621aanvd
- groups.google.com/forum/nvd
News mentions
0No linked articles in our index yet.