From fdb8aaedcb9967b7dbafd6c457c588e5cd6779d3 Mon Sep 17 00:00:00 2001 From: baala3 Date: Sun, 16 Aug 2026 13:10:14 +0900 Subject: [PATCH 1/3] Remove redundant REXML pre-parse in Saml::Base.parse Nokogiri (via libxml2) has blocked entity-expansion DoS natively since 2.9.2, and the parameter-entity bypass (CVE-2021-3541) has been covered since Nokogiri 1.11.4. The REXML pre-parse was only needed as a tripwire in 2014 before Nokogiri had these protections, and its RuntimeError wasn't even caught by the rescue clause below it, so it leaked an undocumented raw exception instead of the library's usual Saml::Errors::UnparseableMessage. Refs #194 --- lib/saml/base.rb | 6 +----- spec/lib/saml/base_spec.rb | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/saml/base.rb b/lib/saml/base.rb index 8fb147c..541c32b 100644 --- a/lib/saml/base.rb +++ b/lib/saml/base.rb @@ -42,10 +42,6 @@ def use_original(object) module XmlMapperClassMethods def parse(xml, options = {}) - if xml.is_a?(String) - ActiveSupport::XmlMini_REXML.parse(xml) - end - object = super if object.is_a?(Array) object.map { |x| x.from_xml = true } @@ -53,7 +49,7 @@ def parse(xml, options = {}) object.from_xml = true end object - rescue Nokogiri::XML::SyntaxError, REXML::ParseException => e + rescue Nokogiri::XML::SyntaxError => e raise Saml::Errors::UnparseableMessage.new(e.message) rescue TypeError => e raise Saml::Errors::UnparseableMessage.new(e.message) diff --git a/spec/lib/saml/base_spec.rb b/spec/lib/saml/base_spec.rb index 72e316a..bf9d9f7 100644 --- a/spec/lib/saml/base_spec.rb +++ b/spec/lib/saml/base_spec.rb @@ -36,8 +36,8 @@ class BaseDummy XML - it 'raises an Saml::Errors::HackAttack for entity expansion has grown too large' do - expect { BaseDummy.parse(xml) }.to raise_error RuntimeError, 'entity expansion has grown too large' + it 'raises a Saml::Errors::UnparseableMessage instead of expanding the entities' do + expect { BaseDummy.parse(xml) }.to raise_error(Saml::Errors::UnparseableMessage) end end @@ -58,7 +58,7 @@ class BaseDummy end it 'raises an error when a method does not exist' do - expect(ActiveSupport::XmlMini_REXML).to receive(:parse).and_raise(NoMethodError) + expect(Nokogiri).to receive(:XML).and_raise(NoMethodError) expect { BaseDummy.parse('unknown') }.to raise_error(Saml::Errors::UnparseableMessage) From 6a082925534ce19b12f9f5e6fb972c448e2319e3 Mon Sep 17 00:00:00 2001 From: baala3 Date: Sun, 16 Aug 2026 13:10:21 +0900 Subject: [PATCH 2/3] Drop rexml dependency and bump nokogiri floor to 1.11.4 rexml is no longer used now that base.rb only parses via Nokogiri. The nokogiri floor is bumped to 1.11.4, the first version that fixes the parameter-entity ("Parameter Laughs") bypass, CVE-2021-3541. Refs #194 --- saml.gemspec | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/saml.gemspec b/saml.gemspec index a06977d..e5961fd 100644 --- a/saml.gemspec +++ b/saml.gemspec @@ -19,8 +19,7 @@ Gem::Specification.new do |s| s.add_dependency 'activesupport', '>= 4.2' s.add_dependency 'activemodel', '>= 4.2' s.add_dependency 'xmlmapper', '~> 0.8.1' - s.add_dependency 'nokogiri', '~> 1.11' - s.add_dependency 'rexml' + s.add_dependency 'nokogiri', '>= 1.11.4', '< 2.0' s.add_dependency 'xmldsig', '>= 0.5.1', '< 0.8.0' s.add_dependency 'xmlenc', '>= 0.6.9', '< 0.9.0' From 62cff6b0b173bfe391a296cd014fafb26cbd41a1 Mon Sep 17 00:00:00 2001 From: baala3 Date: Sun, 16 Aug 2026 13:10:28 +0900 Subject: [PATCH 3/3] Stop relying on REXML-backed Hash.from_xml in soap header spec Hash.from_xml used ActiveSupport's REXML backend, which is no longer a guaranteed dependency now that rexml has been dropped. Assert on the generated SOAP header directly via Nokogiri instead. Refs #194 --- .../request_abstract_type_spec.rb | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/spec/lib/saml/complex_types/request_abstract_type_spec.rb b/spec/lib/saml/complex_types/request_abstract_type_spec.rb index 11f5aac..a3dbcab 100644 --- a/spec/lib/saml/complex_types/request_abstract_type_spec.rb +++ b/spec/lib/saml/complex_types/request_abstract_type_spec.rb @@ -82,14 +82,17 @@ wsa_address: 'address' } ) - xml = Hash.from_xml(soap) - expect(xml["Envelope"]["Header"]).to eq( - "MessageID" => "id", - "To" => "to", - "Action" => "some_action", - "ReplyTo" => { "Address" => "address" }, - 'xmlns:wsa' => 'http://schemas.xmlsoap.org/ws/2004/08/addressing' - ) + namespaces = { + 'soapenv' => 'http://schemas.xmlsoap.org/soap/envelope/', + 'wsa' => 'http://schemas.xmlsoap.org/ws/2004/08/addressing' + } + header = Nokogiri::XML::Document.parse(soap).at_xpath('//soapenv:Header', namespaces) + + expect(header.namespaces['xmlns:wsa']).to eq('http://schemas.xmlsoap.org/ws/2004/08/addressing') + expect(header.at_xpath('wsa:MessageID', namespaces).text).to eq('id') + expect(header.at_xpath('wsa:To', namespaces).text).to eq('to') + expect(header.at_xpath('wsa:Action', namespaces).text).to eq('some_action') + expect(header.at_xpath('wsa:ReplyTo/wsa:Address', namespaces).text).to eq('address') end end