test/response_test.rb in ruby-saml-1.7.2 vs test/response_test.rb in ruby-saml-1.8.0

- old
+ new

@@ -21,11 +21,13 @@ let(:response_valid_signed_without_x509certificate) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate) } let(:response_no_id) { OneLogin::RubySaml::Response.new(read_invalid_response("no_id.xml.base64")) } let(:response_no_version) { OneLogin::RubySaml::Response.new(read_invalid_response("no_saml2.xml.base64")) } let(:response_multi_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_assertions.xml.base64")) } let(:response_no_conditions) { OneLogin::RubySaml::Response.new(read_invalid_response("no_conditions.xml.base64")) } + let(:response_no_conditions_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("no_conditions.xml.base64"), { :skip_conditions => true }) } let(:response_no_authnstatement) { OneLogin::RubySaml::Response.new(read_invalid_response("no_authnstatement.xml.base64")) } + let(:response_no_authnstatement_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("no_authnstatement.xml.base64"), {:skip_authnstatement => true}) } let(:response_empty_destination) { OneLogin::RubySaml::Response.new(read_invalid_response("empty_destination.xml.base64")) } let(:response_empty_destination_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("empty_destination.xml.base64"), {:skip_destination => true}) } let(:response_no_status) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status.xml.base64")) } let(:response_no_statuscode) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status_code.xml.base64")) } let(:response_statuscode_responder) { OneLogin::RubySaml::Response.new(read_invalid_response("status_code_responder.xml.base64")) } @@ -52,14 +54,26 @@ let(:response_invalid_subjectconfirmation_nb) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_nb.xml.base64")) } let(:response_invalid_subjectconfirmation_noa) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_noa.xml.base64")) } let(:response_invalid_signature_position) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_signature_position.xml.base64")) } let(:response_encrypted_nameid) { OneLogin::RubySaml::Response.new(response_document_encrypted_nameid) } + def generate_audience_error(expected, actual) + s = actual.count > 1 ? 's' : ''; + return "Invalid Audience#{s}. The audience#{s} #{actual.join(',')}, did not match the expected audience #{expected}" + end + it "raise an exception when response is initialized with nil" do assert_raises(ArgumentError) { OneLogin::RubySaml::Response.new(nil) } end + it "not filter available options only" do + options = { :skip_destination => true, :foo => :bar } + response = OneLogin::RubySaml::Response.new(response_document_valid_signed, options) + assert_includes response.options.keys, :skip_destination + assert_includes response.options.keys, :foo + end + it "be able to parse a document which contains ampersands" do XMLSecurity::SignedDocument.any_instance.stubs(:digests_match?).returns(true) OneLogin::RubySaml::Response.any_instance.stubs(:validate_conditions).returns(true) ampersands_response = OneLogin::RubySaml::Response.new(ampersands_document) @@ -80,13 +94,38 @@ end it "receives the full AttributeValue when there is an injected comment" do assert_equal "smith", @response.attributes["surname"] end + end + describe "Another test to prevent with comment attack (VU#475445)" do + before do + @response = OneLogin::RubySaml::Response.new(read_response('response_node_text_attack2.xml.base64'), {:skip_recipient_check => true }) + @response.settings = settings + @response.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint + end + + it "receives the full NameID when there is an injected comment, validates the response" do + assert_equal "test@onelogin.com", @response.name_id + end end + describe "Another test with CDATA injected" do + before do + @response = OneLogin::RubySaml::Response.new(read_response('response_node_text_attack3.xml.base64'), {:skip_recipient_check => true }) + @response.settings = settings + @response.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint + end + + it "it normalizes CDATA but reject SAMLResponse due signature invalidation" do + assert_equal "test@onelogin.com.evil.com", @response.name_id + assert !@response.is_valid? + assert_includes @response.errors, "Invalid Signature on SAML Response" + end + end + describe "Prevent XEE attack" do before do @response = OneLogin::RubySaml::Response.new(fixture(:attackxee)) end @@ -221,11 +260,11 @@ it "raise when there is no valid audience" do settings.idp_cert_fingerprint = signature_fingerprint_1 settings.issuer = 'invalid' response_valid_signed.settings = settings response_valid_signed.soft = false - error_msg = "#{response_valid_signed.settings.issuer} is not a valid audience for this Response - Valid audiences: https://someone.example.com/audience" + error_msg = generate_audience_error(response_valid_signed.settings.issuer, ['https://someone.example.com/audience']) assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do response_valid_signed.is_valid? end assert_includes response_valid_signed.errors, error_msg end @@ -377,11 +416,12 @@ it "return false when there is no valid audience" do settings.idp_cert_fingerprint = signature_fingerprint_1 settings.issuer = 'invalid' response_valid_signed.settings = settings response_valid_signed.is_valid? - assert_includes response_valid_signed.errors, "#{response_valid_signed.settings.issuer} is not a valid audience for this Response - Valid audiences: https://someone.example.com/audience" + + assert_includes response_valid_signed.errors, generate_audience_error(response_valid_signed.settings.issuer, ['https://someone.example.com/audience']) end it "return false when no ID present in the SAML Response" do settings.idp_cert_fingerprint = signature_fingerprint_1 response_no_id.settings = settings @@ -413,11 +453,11 @@ settings.idp_cert = ruby_saml_cert_text settings.issuer = 'invalid' response_invalid_subjectconfirmation_recipient.settings = settings collect_errors = true response_invalid_subjectconfirmation_recipient.is_valid?(collect_errors) - assert_includes response_invalid_subjectconfirmation_recipient.errors, "invalid is not a valid audience for this Response - Valid audiences: http://stuff.com/endpoints/metadata.php" + assert_includes response_invalid_subjectconfirmation_recipient.errors, generate_audience_error('invalid', ['http://stuff.com/endpoints/metadata.php']) assert_includes response_invalid_subjectconfirmation_recipient.errors, "Invalid Signature on SAML Response" end end end @@ -438,11 +478,11 @@ it "return false when the audience is valid" do response.settings = settings response.settings.issuer = 'invalid_audience' assert !response.send(:validate_audience) - assert_includes response.errors, "#{response.settings.issuer} is not a valid audience for this Response - Valid audiences: {audience}" + assert_includes response.errors, generate_audience_error(response.settings.issuer, ['{audience}']) end end describe "#validate_destination" do it "return true when the destination of the SAML Response matches the assertion consumer service url" do @@ -624,11 +664,11 @@ it "return false when there is no valid audience" do response_invalid_audience.settings = settings response_invalid_audience.settings.issuer = "https://invalid.example.com/audience" assert !response_invalid_audience.send(:validate_audience) - assert_includes response_invalid_audience.errors, "#{response_invalid_audience.settings.issuer} is not a valid audience for this Response - Valid audiences: http://invalid.audience.com" + assert_includes response_invalid_audience.errors, generate_audience_error(response_invalid_audience.settings.issuer, ['http://invalid.audience.com']) end end describe "#validate_issuer" do it "return true when the issuer of the Message/Assertion matches the IdP entityId or it was empty" do @@ -956,10 +996,15 @@ it "return true when one conditions element" do response.soft = true assert response.send(:validate_one_conditions) end + + it "return true when no conditions are present and skip_conditions is true" do + response_no_conditions_with_skip.soft = true + assert response_no_conditions_with_skip.send(:validate_one_conditions) + end end describe "#check_one_authnstatement" do it "return false when none or more than one authnstatement element" do response_no_authnstatement.soft = true @@ -968,9 +1013,15 @@ end it "return true when one authnstatement element" do response.soft = true assert response.send(:validate_one_authnstatement) + end + + it "return true when SAML Response is empty but skip_authstatement option is used" do + response_no_authnstatement_with_skip.soft = true + assert response_no_authnstatement_with_skip.send(:validate_one_authnstatement) + assert_empty response_empty_destination_with_skip.errors end end describe "#check_conditions" do it "check time conditions" do