Skip to content

Commit 24b69b5

Browse files
authored
Merge pull request #749 from SAML-Toolkits/security_fix_1.12.4
Adding Github CI to 1.12.X branch
2 parents d49fde6 + 8858c7a commit 24b69b5

19 files changed

+215
-62
lines changed

.github/workflows/test.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: ruby-saml CI
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
test:
7+
name: Unit test
8+
strategy:
9+
fail-fast: false
10+
matrix:
11+
os:
12+
- ubuntu-20.04
13+
- macos-latest
14+
- windows-latest
15+
ruby-version:
16+
- 2.1
17+
- 2.2
18+
- 2.3
19+
- 2.4
20+
- 2.5
21+
- 2.6
22+
- 2.7
23+
- 3.0
24+
- jruby-9.1
25+
- jruby-9.2
26+
exclude:
27+
- os: macos-latest
28+
ruby-version: 2.1
29+
- os: macos-latest
30+
ruby-version: 2.2
31+
- os: macos-latest
32+
ruby-version: 2.3
33+
- os: macos-latest
34+
ruby-version: 2.4
35+
- os: macos-latest
36+
ruby-version: 2.5
37+
- os: macos-latest
38+
ruby-version: jruby-9.1
39+
- os: macos-latest
40+
ruby-version: jruby-9.2
41+
- os: windows-latest
42+
ruby-version: 2.1
43+
- os: windows-latest
44+
ruby-version: jruby-9.1
45+
- os: windows-latest
46+
ruby-version: jruby-9.2
47+
- os: windows-latest
48+
ruby-version: jruby-9.3
49+
- os: windows-latest
50+
ruby-version: jruby-9.4
51+
- os: windows-latest
52+
ruby-version: truffleruby
53+
runs-on: ${{ matrix.os }}
54+
steps:
55+
- uses: actions/checkout@v4
56+
- name: Set up Ruby ${{ matrix.ruby-version }}
57+
uses: ruby/setup-ruby@v1
58+
with:
59+
ruby-version: ${{ matrix.ruby-version }}
60+
61+
- name: Install dependencies
62+
run: bundle install
63+
64+
- name: Run tests
65+
run: bundle exec rake

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
# Ruby SAML [![Build Status](https://secure.travis-ci.org/onelogin/ruby-saml.svg)](http://travis-ci.org/onelogin/ruby-saml) [![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master)](https://coveralls.io/r/onelogin/ruby-saml?branch=master) [![Gem Version](https://badge.fury.io/rb/ruby-saml.svg)](http://badge.fury.io/rb/ruby-saml)
1+
# Ruby SAML [![Build Status](https://secure.travis-ci.org/onelogin/ruby-saml.svg)](http://travis-ci.org/onelogin/ruby-saml) [![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master)](https://coveralls.io/r/onelogin/ruby-saml?branch=master)
22

33
## Updating from 1.11.x to 1.12.0
44
Version `1.12.0` adds support for gcm algorithm and
55
change/adds specific error messages for signature validations
66

7+
`idp_sso_target_url` and `idp_slo_target_url` attributes of the Settings class deprecated in favor of `idp_sso_service_url` and `idp_slo_service_url`.
8+
In IDPMetadataParser, `parse`, `parse_to_hash` and `parse_to_array` methods now retrieve SSO URL and SLO URL endpoints with
9+
`idp_sso_service_url` and `idp_slo_service_url` (previously `idp_sso_target_url` and `idp_slo_target_url` respectively).
10+
711
## Updating from 1.10.x to 1.11.0
812
Version `1.11.0` deprecates the use of `settings.issuer` in favour of `settings.sp_entity_id`.
913
There are two new security settings: `settings.security[:check_idp_cert_expiration]` and `settings.security[:check_sp_cert_expiration]` (both false by default) that check if the IdP or SP X.509 certificate has expired, respectively.
@@ -120,9 +124,11 @@ We created a demo project for Rails4 that uses the latest version of this librar
120124
* 2.5.x
121125
* 2.6.x
122126
* 2.7.x
123-
* JRuby 1.7.19
124-
* JRuby 9.0.0.0
125-
* JRuby 9.2.0.0
127+
* 3.0.x
128+
* JRuby 1.7.x
129+
* JRuby 9.0.x
130+
* JRuby 9.1.x
131+
* JRuby 9.2.x
126132

127133
## Adding Features, Pull Requests
128134
* Fork the repository
@@ -154,7 +160,7 @@ Using `Gemfile`
154160

155161
```ruby
156162
# latest stable
157-
gem 'ruby-saml', '~> 1.9.0'
163+
gem 'ruby-saml', '~> 1.11.0'
158164

159165
# or track master for bleeding-edge
160166
gem 'ruby-saml', :github => 'onelogin/ruby-saml'

changelog.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# RubySaml Changelog
22

3+
### 1.12.3 (Sep 10, 2024)
4+
* Fix for critical vulnerability CVE-2024-45409: SAML authentication bypass via Incorrect XPath selector
5+
6+
### 1.12.2 (Apr 08, 2022)
7+
* [575](https://github.com/onelogin/ruby-saml/pull/575) Fix SloLogoutresponse bug on LogoutRequest
8+
9+
### 1.12.1 (Apr 05, 2022)
10+
* Fix XPath typo incompatible with Rexml 3.2.5
11+
* Refactor GCM support
12+
313
### 1.12.0 (Feb 18, 2021)
414
* Support AES-128-GCM, AES-192-GCM, and AES-256-GCM encryptions
515
* Parse & return SLO ResponseLocation in IDPMetadataParser & Settings

lib/onelogin/ruby-saml/attributes.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def ==(other)
124124
def fetch(name)
125125
attributes.each_key do |attribute_key|
126126
if name.is_a?(Regexp)
127-
if name.method_exists? :match?
127+
if name.respond_to? :match?
128128
return self[attribute_key] if name.match?(attribute_key)
129129
else
130130
return self[attribute_key] if name.match(attribute_key)

lib/onelogin/ruby-saml/logoutrequest.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ def request_id
3232
#
3333
def create(settings, params={})
3434
params = create_params(settings, params)
35-
params_prefix = (settings.idp_slo_target_url =~ /\?/) ? '&' : '?'
35+
params_prefix = (settings.idp_slo_service_url =~ /\?/) ? '&' : '?'
3636
saml_request = CGI.escape(params.delete("SAMLRequest"))
3737
request_params = "#{params_prefix}SAMLRequest=#{saml_request}"
3838
params.each_pair do |key, value|
3939
request_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}"
4040
end
41-
raise SettingError.new "Invalid settings, idp_slo_target_url is not set!" if settings.idp_slo_target_url.nil? or settings.idp_slo_target_url.empty?
42-
@logout_url = settings.idp_slo_target_url + request_params
41+
raise SettingError.new "Invalid settings, idp_slo_service_url is not set!" if settings.idp_slo_service_url.nil? or settings.idp_slo_service_url.empty?
42+
@logout_url = settings.idp_slo_service_url + request_params
4343
end
4444

4545
# Creates the Get parameters for the logout request.
@@ -109,7 +109,7 @@ def create_xml_document(settings)
109109
root.attributes['ID'] = uuid
110110
root.attributes['IssueInstant'] = time
111111
root.attributes['Version'] = "2.0"
112-
root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil? or settings.idp_slo_target_url.empty?
112+
root.attributes['Destination'] = settings.idp_slo_service_url unless settings.idp_slo_service_url.nil? or settings.idp_slo_service_url.empty?
113113

114114
if settings.sp_entity_id
115115
issuer = root.add_element "saml:Issuer"

lib/onelogin/ruby-saml/logoutresponse.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ def validate_success_status
150150
# @raise [ValidationError] if soft == false and validation fails
151151
#
152152
def validate_structure
153-
unless valid_saml?(document, soft)
153+
check_malformed_doc = check_malformed_doc?(settings)
154+
unless valid_saml?(document, soft, check_malformed_doc)
154155
return append_error("Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd")
155156
end
156157

lib/onelogin/ruby-saml/response.rb

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -425,12 +425,13 @@ def validate_success_status
425425
#
426426
def validate_structure
427427
structure_error_msg = "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
428-
unless valid_saml?(document, soft)
428+
check_malformed_doc = check_malformed_doc_enabled?
429+
unless valid_saml?(document, soft, check_malformed_doc)
429430
return append_error(structure_error_msg)
430431
end
431432

432433
unless decrypted_document.nil?
433-
unless valid_saml?(decrypted_document, soft)
434+
unless valid_saml?(decrypted_document, soft, check_malformed_doc)
434435
return append_error(structure_error_msg)
435436
end
436437
end
@@ -828,7 +829,7 @@ def validate_signature
828829
# otherwise, review if the decrypted assertion contains a signature
829830
sig_elements = REXML::XPath.match(
830831
document,
831-
"/p:Response[@ID=$id]/ds:Signature]",
832+
"/p:Response[@ID=$id]/ds:Signature",
832833
{ "p" => PROTOCOL, "ds" => DSIG },
833834
{ 'id' => document.signed_element_id }
834835
)
@@ -865,6 +866,8 @@ def validate_signature
865866
fingerprint = settings.get_fingerprint
866867
opts[:cert] = idp_cert
867868

869+
check_malformed_doc = check_malformed_doc_enabled?
870+
opts[:check_malformed_doc] = check_malformed_doc
868871
if fingerprint && doc.validate_document(fingerprint, @soft, opts)
869872
if settings.security[:check_idp_cert_expiration]
870873
if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
@@ -879,7 +882,7 @@ def validate_signature
879882
valid = false
880883
expired = false
881884
idp_certs[:signing].each do |idp_cert|
882-
valid = doc.validate_document_with_cert(idp_cert, true)
885+
valid = doc.validate_document_with_cert(idp_cert, true, check_malformed_doc)
883886
if valid
884887
if settings.security[:check_idp_cert_expiration]
885888
if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
@@ -1063,6 +1066,10 @@ def parse_time(node, attribute)
10631066
Time.parse(node.attributes[attribute])
10641067
end
10651068
end
1069+
1070+
def check_malformed_doc_enabled?
1071+
check_malformed_doc?(settings)
1072+
end
10661073
end
10671074
end
10681075
end

lib/onelogin/ruby-saml/saml_message.rb

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,13 @@ def id(document)
6363
# Validates the SAML Message against the specified schema.
6464
# @param document [REXML::Document] The message that will be validated
6565
# @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the message is invalid or not)
66+
# @param check_malformed_doc [Boolean] check_malformed_doc Enable or Disable the check for malformed XML
6667
# @return [Boolean] True if the XML is valid, otherwise False, if soft=True
6768
# @raise [ValidationError] if soft == false and validation fails
6869
#
69-
def valid_saml?(document, soft = true)
70+
def valid_saml?(document, soft = true, check_malformed_doc = true)
7071
begin
71-
xml = Nokogiri::XML(document.to_s) do |config|
72-
config.options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
73-
end
72+
xml = XMLSecurity::BaseDocument.safe_load_xml(document, check_malformed_doc)
7473
rescue Exception => error
7574
return false if soft
7675
raise ValidationError.new("XML load failed: #{error.message}")
@@ -97,10 +96,16 @@ def decode_raw_saml(saml)
9796

9897
decoded = decode(saml)
9998
begin
100-
inflate(decoded)
99+
message = inflate(decoded)
101100
rescue
102-
decoded
101+
message = decoded
102+
end
103+
104+
if message.bytesize > MAX_BYTE_SIZE
105+
raise ValidationError.new("Encoded SAML Message exceeds " + MAX_BYTE_SIZE.to_s + " bytes, so was rejected")
103106
end
107+
108+
message
104109
end
105110

106111
# Deflate, base64 encode and url-encode a SAML Message (To be used in the HTTP-redirect binding)
@@ -157,6 +162,12 @@ def inflate(deflated)
157162
def deflate(inflated)
158163
Zlib::Deflate.deflate(inflated, 9)[2..-5]
159164
end
165+
166+
def check_malformed_doc?(settings)
167+
default_value = OneLogin::RubySaml::Settings::DEFAULTS[:check_malformed_doc]
168+
169+
settings.nil? ? default_value : settings.check_malformed_doc
170+
end
160171
end
161172
end
162173
end

lib/onelogin/ruby-saml/settings.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def initialize(overrides = {}, keep_security_attributes = false)
5353
attr_accessor :compress_request
5454
attr_accessor :compress_response
5555
attr_accessor :double_quote_xml_attribute_values
56+
attr_accessor :check_malformed_doc
5657
attr_accessor :passive
5758
attr_accessor :protocol_binding
5859
attr_accessor :attributes_index
@@ -259,6 +260,7 @@ def get_sp_key
259260
:compress_request => true,
260261
:compress_response => true,
261262
:soft => true,
263+
:check_malformed_doc => true,
262264
:double_quote_xml_attribute_values => false,
263265
:security => {
264266
:authn_requests_signed => false,

lib/onelogin/ruby-saml/slo_logoutrequest.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,8 @@ def validate_not_on_or_after
199199
# @raise [ValidationError] if soft == false and validation fails
200200
#
201201
def validate_structure
202-
unless valid_saml?(document, soft)
202+
check_malformed_doc = check_malformed_doc?(settings)
203+
unless valid_saml?(document, soft, check_malformed_doc)
203204
return append_error("Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd")
204205
end
205206

0 commit comments

Comments
 (0)