From 43b0bb61b710b15cbdc2298c8be5b6692897329b Mon Sep 17 00:00:00 2001 From: Caleb Hearth Date: Mon, 8 Jul 2024 09:19:43 -0600 Subject: [PATCH 1/8] Make IdpMetadataParser#get_idp_metadata public I have a use-case for being able to cache the intermediate fetch of metadata in case of temporary failures, so rather than: parser.parse_remote(url) I'd like to begin metadata = parser.get_idp_metadata(url, true) do_my_caching(metadata) parser.parse(metadata) rescue HttpError load_cache end There's a fair amount of logic in the get_idp_metadata method that I'd rather not need to re-implement. Right now I have this implemented with `parser.send(:get_idp_metadata, url, true)` which is obviously not great if the internals of this class change in the future. Can we move this method to the public API? --- lib/onelogin/ruby-saml/idp_metadata_parser.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/onelogin/ruby-saml/idp_metadata_parser.rb b/lib/onelogin/ruby-saml/idp_metadata_parser.rb index d4205896..8a2e4ca2 100644 --- a/lib/onelogin/ruby-saml/idp_metadata_parser.rb +++ b/lib/onelogin/ruby-saml/idp_metadata_parser.rb @@ -186,8 +186,6 @@ def parse_to_idp_metadata_array(idp_metadata, options = {}) idpsso_descriptors.map {|id| IdpMetadata.new(id, id.parent.attributes["entityID"])} end - private - # Retrieve the remote IdP metadata from the URL or a cached copy. # @param url [String] Url where the XML of the Identity Provider Metadata is published. # @param validate_cert [Boolean] If true and the URL is HTTPs, the cert of the domain is checked. @@ -220,6 +218,8 @@ def get_idp_metadata(url, validate_cert) ) end + private + class IdpMetadata attr_reader :idpsso_descriptor, :entity_id From 0f8da708b4f913e8a8c4e86f2067299f774e4fa6 Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Tue, 9 Jul 2024 12:21:50 +0900 Subject: [PATCH 2/8] Fix tests on Windows and cleanup CI --- .github/workflows/test.yml | 52 +++++++++++++++++++++++++++++++-- CHANGELOG.md | 1 + README.md | 24 ++------------- lib/onelogin/ruby-saml/utils.rb | 24 +++++++++------ 4 files changed, 67 insertions(+), 34 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8181d37a..d90d6231 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,11 +8,57 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.04, macos-latest] - ruby-version: [2.1.9, 2.2.10, 2.3.8, 2.4.6, 2.5.8, 2.6.6, 2.7.2, 3.0.1, 3.1, 3.2, jruby-9.1.17.0, jruby-9.2.17.0, jruby-9.3.2.0, jruby-9.4.0.0, truffleruby] + os: + - ubuntu-20.04 + - macos-latest + - windows-latest + ruby-version: + - 2.1 + - 2.2 + - 2.3 + - 2.4 + - 2.5 + - 2.6 + - 2.7 + - 3.0 + - 3.1 + - 3.2 + - 3.3 + - jruby-9.1 + - jruby-9.2 + - jruby-9.3 + - jruby-9.4 + - truffleruby + exclude: + - os: macos-latest + ruby-version: 2.1 + - os: macos-latest + ruby-version: 2.2 + - os: macos-latest + ruby-version: 2.3 + - os: macos-latest + ruby-version: 2.4 + - os: macos-latest + ruby-version: 2.5 + - os: macos-latest + ruby-version: jruby-9.1 + - os: macos-latest + ruby-version: jruby-9.2 + - os: windows-latest + ruby-version: 2.1 + - os: windows-latest + ruby-version: jruby-9.1 + - os: windows-latest + ruby-version: jruby-9.2 + - os: windows-latest + ruby-version: jruby-9.3 + - os: windows-latest + ruby-version: jruby-9.4 + - os: windows-latest + ruby-version: truffleruby runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Ruby ${{ matrix.ruby-version }} uses: ruby/setup-ruby@v1 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index f5e2b1dc..6acaa0de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Ruby SAML Changelog ### 1.17.0 +* [#687](https://github.com/SAML-Toolkits/ruby-saml/pull/687) Add CI coverage for Ruby 3.3 and Windows. * [#673](https://github.com/SAML-Toolkits/ruby-saml/pull/673) Add `Settings#sp_cert_multi` paramter to facilitate SP certificate and key rotation. * [#673](https://github.com/SAML-Toolkits/ruby-saml/pull/673) Support multiple simultaneous SP decryption keys via `Settings#sp_cert_multi` parameter. * [#673](https://github.com/SAML-Toolkits/ruby-saml/pull/673) Deprecate `Settings#certificate_new` parameter. diff --git a/README.md b/README.md index 229799ef..0a855cff 100644 --- a/README.md +++ b/README.md @@ -22,30 +22,10 @@ We created a demo project for Rails 4 that uses the latest version of this libra The following Ruby versions are covered by CI testing: -* 2.1.x -* 2.2.x -* 2.3.x -* 2.4.x -* 2.5.x -* 2.6.x -* 2.7.x -* 3.0.x -* 3.1 -* 3.2 -* JRuby 9.1.x -* JRuby 9.2.x -* JRuby 9.3.X -* JRuby 9.4.0 +* Ruby (MRI) 2.1 to 3.3 +* JRuby 9.1 to 9.4 * TruffleRuby (latest) -In addition, the following may work but are untested: - -* 1.8.7 -* 1.9.x -* 2.0.x -* JRuby 1.7.x -* JRuby 9.0.x - ## Adding Features, Pull Requests * Fork the repository diff --git a/lib/onelogin/ruby-saml/utils.rb b/lib/onelogin/ruby-saml/utils.rb index 5756e696..68ee2ed0 100644 --- a/lib/onelogin/ruby-saml/utils.rb +++ b/lib/onelogin/ruby-saml/utils.rb @@ -69,20 +69,26 @@ def self.parse_duration(duration, timestamp=Time.now.utc) matches = duration.match(DURATION_FORMAT) if matches.nil? - raise Exception.new("Invalid ISO 8601 duration") + raise StandardError.new("Invalid ISO 8601 duration") end sign = matches[1] == '-' ? -1 : 1 durYears, durMonths, durDays, durHours, durMinutes, durSeconds, durWeeks = - matches[2..8].map { |match| match ? sign * match.tr(',', '.').to_f : 0.0 } - - initial_datetime = Time.at(timestamp).utc.to_datetime - final_datetime = initial_datetime.next_year(durYears) - final_datetime = final_datetime.next_month(durMonths) - final_datetime = final_datetime.next_day((7*durWeeks) + durDays) - final_timestamp = final_datetime.to_time.utc.to_i + (durHours * 3600) + (durMinutes * 60) + durSeconds - return final_timestamp + matches[2..8].map do |match| + if match + match = match.tr(',', '.').gsub(/\.0*\z/, '') + sign * (match.include?('.') ? match.to_f : match.to_i) + else + 0 + end + end + + datetime = Time.at(timestamp).utc.to_datetime + datetime = datetime.next_year(durYears) + datetime = datetime.next_month(durMonths) + datetime = datetime.next_day((7*durWeeks) + durDays) + datetime.to_time.utc.to_i + (durHours * 3600) + (durMinutes * 60) + durSeconds end # Return a properly formatted x509 certificate From 4865d030cae9705ee5cdb12415c654c634093ae7 Mon Sep 17 00:00:00 2001 From: ahacker1 Date: Tue, 10 Sep 2024 13:12:09 -0400 Subject: [PATCH 3/8] Merge commit from fork * Use correct XPaths and resolve to correct elements * Update xml_security.rb * Block references that resolve to multiple nodes to prevent signature wrapping attacks --- lib/xml_security.rb | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/xml_security.rb b/lib/xml_security.rb index 1b1b3228..f731d464 100644 --- a/lib/xml_security.rb +++ b/lib/xml_security.rb @@ -310,17 +310,29 @@ def validate_signature(base64_cert, soft = true) canon_string = noko_signed_info_element.canonicalize(canon_algorithm) noko_sig_element.remove + # get signed info + signed_info_element = REXML::XPath.first( + sig_element, + "./ds:SignedInfo", + { "ds" => DSIG } + ) # get inclusive namespaces inclusive_namespaces = extract_inclusive_namespaces # check digests - ref = REXML::XPath.first(sig_element, "//ds:Reference", {"ds"=>DSIG}) + ref = REXML::XPath.first(signed_info_element, "./ds:Reference", {"ds"=>DSIG}) - hashed_element = document.at_xpath("//*[@ID=$id]", nil, { 'id' => extract_signed_element_id }) + reference_nodes = document.xpath("//*[@ID=$id]", nil, { 'id' => extract_signed_element_id }) + + if reference_nodes.length > 1 # ensures no elements with same ID to prevent signature wrapping attack. + return append_error("Digest Mismatch", soft) + end + + hashed_element = reference_nodes[0] canon_algorithm = canon_algorithm REXML::XPath.first( - ref, - '//ds:CanonicalizationMethod', + signed_info_element, + './ds:CanonicalizationMethod', { "ds" => DSIG } ) @@ -330,13 +342,13 @@ def validate_signature(base64_cert, soft = true) digest_algorithm = algorithm(REXML::XPath.first( ref, - "//ds:DigestMethod", + "./ds:DigestMethod", { "ds" => DSIG } )) hash = digest_algorithm.digest(canon_hashed_element) encoded_digest_value = REXML::XPath.first( ref, - "//ds:DigestValue", + "./ds:DigestValue", { "ds" => DSIG } ) digest_value = Base64.decode64(OneLogin::RubySaml::Utils.element_text(encoded_digest_value)) @@ -362,7 +374,7 @@ def validate_signature(base64_cert, soft = true) def process_transforms(ref, canon_algorithm) transforms = REXML::XPath.match( ref, - "//ds:Transforms/ds:Transform", + "./ds:Transforms/ds:Transform", { "ds" => DSIG } ) From 1bc447f297b769d1a9abeb619ce074bd9c410a72 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 10 Sep 2024 19:15:42 +0200 Subject: [PATCH 4/8] Release 1.17.0 --- CHANGELOG.md | 6 +++++- README.md | 2 ++ lib/onelogin/ruby-saml/version.rb | 2 +- lib/xml_security.rb | 3 ++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6acaa0de..e1f49999 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Ruby SAML Changelog -### 1.17.0 +### 1.17.0 (Sep 10, 2024) +* Fix for critical vulnerability CVE-2024-45409: SAML authentication bypass via Incorrect XPath selector * [#687](https://github.com/SAML-Toolkits/ruby-saml/pull/687) Add CI coverage for Ruby 3.3 and Windows. * [#673](https://github.com/SAML-Toolkits/ruby-saml/pull/673) Add `Settings#sp_cert_multi` paramter to facilitate SP certificate and key rotation. * [#673](https://github.com/SAML-Toolkits/ruby-saml/pull/673) Support multiple simultaneous SP decryption keys via `Settings#sp_cert_multi` parameter. @@ -39,6 +40,9 @@ * Add warning about the use of IdpMetadataParser class and SSRF * CI: Migrate from Travis to Github Actions +### 1.12.3 (Sep 10, 2024) +* Fix for critical vulnerability CVE-2024-45409: SAML authentication bypass via Incorrect XPath selector + ### 1.12.2 (Apr 08, 2021) * [#575](https://github.com/onelogin/ruby-saml/pull/575) Fix SloLogoutresponse bug on LogoutRequest diff --git a/README.md b/README.md index 0a855cff..7a245b2c 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ Ruby SAML minor and tiny versions may introduce breaking changes. Please read [UPGRADING.md](UPGRADING.md) for guidance on upgrading to new Ruby SAML versions. +There is a critical vulnerability affecting ruby-saml < 1.17.0 (CVE-2024-45409). Make sure you are using an updated version. (1.12.3 is safe) + ## Overview The Ruby SAML library is for implementing the client side of a SAML authorization, diff --git a/lib/onelogin/ruby-saml/version.rb b/lib/onelogin/ruby-saml/version.rb index 0f2da263..4c26fcbc 100644 --- a/lib/onelogin/ruby-saml/version.rb +++ b/lib/onelogin/ruby-saml/version.rb @@ -1,5 +1,5 @@ module OneLogin module RubySaml - VERSION = '1.16.0' + VERSION = '1.17.0' end end diff --git a/lib/xml_security.rb b/lib/xml_security.rb index f731d464..0db0623b 100644 --- a/lib/xml_security.rb +++ b/lib/xml_security.rb @@ -316,6 +316,7 @@ def validate_signature(base64_cert, soft = true) "./ds:SignedInfo", { "ds" => DSIG } ) + # get inclusive namespaces inclusive_namespaces = extract_inclusive_namespaces @@ -325,7 +326,7 @@ def validate_signature(base64_cert, soft = true) reference_nodes = document.xpath("//*[@ID=$id]", nil, { 'id' => extract_signed_element_id }) if reference_nodes.length > 1 # ensures no elements with same ID to prevent signature wrapping attack. - return append_error("Digest Mismatch", soft) + return append_error("Digest mismatch. Duplicated ID found", soft) end hashed_element = reference_nodes[0] From ef997f05215825021552b7811ab614f28af68631 Mon Sep 17 00:00:00 2001 From: Adam Hess Date: Mon, 30 Sep 2024 00:03:55 -0700 Subject: [PATCH 5/8] fix ambiguous regex warnings (#720) This test warns warning: ambiguity between regexp and two divisions: wrap regexp in parentheses or add a space after `/' operator We can fix this warning by using the %r regex syntax instead --- test/utils_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/utils_test.rb b/test/utils_test.rb index 35577191..80a3cb96 100644 --- a/test/utils_test.rb +++ b/test/utils_test.rb @@ -360,11 +360,11 @@ def result(duration, reference = 0) end it 'successfully decrypts with the first private key' do - assert_match /\A Date: Mon, 30 Sep 2024 16:08:21 +0900 Subject: [PATCH 6/8] supports params that can be used for re-authentication (#718) --- lib/onelogin/ruby-saml/response.rb | 21 +++++++++++++++++++++ test/response_test.rb | 12 ++++++++++++ 2 files changed, 33 insertions(+) diff --git a/lib/onelogin/ruby-saml/response.rb b/lib/onelogin/ruby-saml/response.rb index 29d48594..31000f69 100644 --- a/lib/onelogin/ruby-saml/response.rb +++ b/lib/onelogin/ruby-saml/response.rb @@ -198,6 +198,27 @@ def session_expires_at end end + # Gets the AuthnInstant from the AuthnStatement. + # Could be used to require re-authentication if a long time has passed + # since the last user authentication. + # @return [String] AuthnInstant value + # + def authn_instant + @authn_instant ||= begin + node = xpath_first_from_signed_assertion('/a:AuthnStatement') + node.nil? ? nil : node.attributes['AuthnInstant'] + end + end + + # Gets the AuthnContextClassRef from the AuthnStatement + # Could be used to require re-authentication if the assertion + # did not met the requested authentication context class. + # @return [String] AuthnContextClassRef value + # + def authn_context_class_ref + @authn_context_class_ref ||= Utils.element_text(xpath_first_from_signed_assertion('/a:AuthnStatement/a:AuthnContext/a:AuthnContextClassRef')) + end + # Checks if the Status has the "Success" code # @return [Boolean] True if the StatusCode is Sucess # diff --git a/test/response_test.rb b/test/response_test.rb index f2fca3eb..ee8fb1b0 100644 --- a/test/response_test.rb +++ b/test/response_test.rb @@ -1357,6 +1357,18 @@ def generate_audience_error(expected, actual) end end + describe "#authn_instant" do + it "extract the value of the AuthnInstant attribute" do + assert_equal "2010-11-18T21:57:37Z", response.authn_instant + end + end + + describe "#authn_context_class_ref" do + it "extract the value of the AuthnContextClassRef attribute" do + assert_equal "urn:oasis:names:tc:SAML:2.0:ac:classes:Password", response.authn_context_class_ref + end + end + describe "#success" do it "find a status code that says success" do response.success? From 7f887b4d8e6efc7ec92ea3f310ab43d006e8c99d Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 30 Sep 2024 09:23:59 +0200 Subject: [PATCH 7/8] Fix typo in SPNameQualifier error text. See #715 --- lib/onelogin/ruby-saml/response.rb | 2 +- test/response_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/onelogin/ruby-saml/response.rb b/lib/onelogin/ruby-saml/response.rb index 31000f69..bec92595 100644 --- a/lib/onelogin/ruby-saml/response.rb +++ b/lib/onelogin/ruby-saml/response.rb @@ -833,7 +833,7 @@ def validate_name_id unless settings.sp_entity_id.nil? || settings.sp_entity_id.empty? || name_id_spnamequalifier.nil? || name_id_spnamequalifier.empty? if name_id_spnamequalifier != settings.sp_entity_id - return append_error("The SPNameQualifier value mistmatch the SP entityID value.") + return append_error("SPNameQualifier value does not match the SP entityID value.") end end end diff --git a/test/response_test.rb b/test/response_test.rb index ee8fb1b0..a6920bc8 100644 --- a/test/response_test.rb +++ b/test/response_test.rb @@ -1027,7 +1027,7 @@ def generate_audience_error(expected, actual) settings.sp_entity_id = 'sp_entity_id' response_wrong_spnamequalifier.settings = settings assert !response_wrong_spnamequalifier.send(:validate_name_id) - assert_includes response_wrong_spnamequalifier.errors, "The SPNameQualifier value mistmatch the SP entityID value." + assert_includes response_wrong_spnamequalifier.errors, "SPNameQualifier value does not match the SP entityID value." end it "return true when no nameid element but not required by settings" do From 791fc2c42c66c2ad16b4c46b828adec8f5f7b117 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 30 Sep 2024 09:29:28 +0200 Subject: [PATCH 8/8] Update CHANGELOG --- CHANGELOG.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1f49999..7bb3be73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Ruby SAML Changelog +### 1.18.0 (???) +* [#718](https://github.com/SAML-Toolkits/ruby-saml/pull/718/) Add support to retrieve from SAMLResponse the AuthnInstant and AuthnContextClassRef values +* [#720](https://github.com/SAML-Toolkits/ruby-saml/pull/720) Fix ambiguous regex warnings +* [#715](https://github.com/SAML-Toolkits/ruby-saml/pull/715) Fix typo in SPNameQualifier error text + ### 1.17.0 (Sep 10, 2024) * Fix for critical vulnerability CVE-2024-45409: SAML authentication bypass via Incorrect XPath selector * [#687](https://github.com/SAML-Toolkits/ruby-saml/pull/687) Add CI coverage for Ruby 3.3 and Windows. @@ -15,7 +20,7 @@ ### 1.15.0 (Jan 04, 2023) * [#650](https://github.com/SAML-Toolkits/ruby-saml/pull/650) Replace strip! by strip on compute_digest method -* [#638](https://github.com/SAML-Toolkits/ruby-saml/pull/638) Fix dateTime format for the validUntil attribute of the generated metadata +* [#638](https://github.com/SAML-Toolkits/ruby-saml/pull/638) Fix dateTime format for the validUntil attribute of the generated metadata * [#576](https://github.com/SAML-Toolkits/ruby-saml/pull/576) Support `Settings#idp_cert_multi` with string keys * [#567](https://github.com/SAML-Toolkits/ruby-saml/pull/567) Improve Code quality * Add info about new repo, new maintainer, new security contact @@ -52,7 +57,7 @@ ### 1.12.0 (Feb 18, 2021) * Support AES-128-GCM, AES-192-GCM, and AES-256-GCM encryptions -* Parse & return SLO ResponseLocation in IDPMetadataParser & Settings +* Parse & return SLO ResponseLocation in IDPMetadataParser & Settings * Adding idp_sso_service_url and idp_slo_service_url settings * [#536](https://github.com/onelogin/ruby-saml/pull/536) Adding feth method to be able retrieve attributes based on regex * Reduce size of built gem by excluding the test folder @@ -184,7 +189,7 @@ * Fix response_test.rb of gem 1.3.0 * Add reference to Security Guidelines * Update License -* [#334](https://github.com/onelogin/ruby-saml/pull/334) Keep API backward-compatibility on IdpMetadataParser fingerprint method. +* [#334](https://github.com/onelogin/ruby-saml/pull/334) Keep API backward-compatibility on IdpMetadataParser fingerprint method. ### 1.3.0 (June 24, 2016) * [Security Fix](https://github.com/onelogin/ruby-saml/commit/a571f52171e6bfd87db59822d1d9e8c38fb3b995) Add extra validations to prevent Signature wrapping attacks @@ -202,7 +207,7 @@ * [#316](https://github.com/onelogin/ruby-saml/pull/316) Fix Misspelling of transation_id to transaction_id * [#321](https://github.com/onelogin/ruby-saml/pull/321) Support Attribute Names on IDPSSODescriptor parser * Changes on empty URI of Signature reference management -* [#320](https://github.com/onelogin/ruby-saml/pull/320) Dont mutate document to fix lack of reference URI +* [#320](https://github.com/onelogin/ruby-saml/pull/320) Dont mutate document to fix lack of reference URI * [#306](https://github.com/onelogin/ruby-saml/pull/306) Support WantAssertionsSigned ### 1.1.2 (February 15, 2016) @@ -219,9 +224,9 @@ * [#270](https://github.com/onelogin/ruby-saml/pull/270) Allow SAML elements to come from any namespace (at decryption process) * [#261](https://github.com/onelogin/ruby-saml/pull/261) Allow validate_subject_confirmation Response validation to be skipped * [#258](https://github.com/onelogin/ruby-saml/pull/258) Fix allowed_clock_drift on the validate_session_expiration test -* [#256](https://github.com/onelogin/ruby-saml/pull/256) Separate the create_authentication_xml_doc in two methods. +* [#256](https://github.com/onelogin/ruby-saml/pull/256) Separate the create_authentication_xml_doc in two methods. * [#255](https://github.com/onelogin/ruby-saml/pull/255) Refactor validate signature. -* [#254](https://github.com/onelogin/ruby-saml/pull/254) Handle empty URI references +* [#254](https://github.com/onelogin/ruby-saml/pull/254) Handle empty URI references * [#251](https://github.com/onelogin/ruby-saml/pull/251) Support qualified and unqualified NameID in attributes * [#234](https://github.com/onelogin/ruby-saml/pull/234) Add explicit support for JRuby @@ -229,7 +234,7 @@ * [#247](https://github.com/onelogin/ruby-saml/pull/247) Avoid entity expansion (XEE attacks) * [#246](https://github.com/onelogin/ruby-saml/pull/246) Fix bug generating Logout Response (issuer was at wrong order) * [#243](https://github.com/onelogin/ruby-saml/issues/243) and [#244](https://github.com/onelogin/ruby-saml/issues/244) Fix metadata builder errors. Fix metadata xsd. -* [#241](https://github.com/onelogin/ruby-saml/pull/241) Add decrypt support (EncryptID and EncryptedAssertion). Improve compatibility with namespaces. +* [#241](https://github.com/onelogin/ruby-saml/pull/241) Add decrypt support (EncryptID and EncryptedAssertion). Improve compatibility with namespaces. * [#240](https://github.com/onelogin/ruby-saml/pull/240) and [#238](https://github.com/onelogin/ruby-saml/pull/238) Improve test coverage and refactor. * [#239](https://github.com/onelogin/ruby-saml/pull/239) Improve security: Add more validations to SAMLResponse, LogoutRequest and LogoutResponse. Refactor code and improve tests coverage. * [#237](https://github.com/onelogin/ruby-saml/pull/237) Don't pretty print metadata by default.