Comparing changes

v1.0.11 v1.0.12
20 commits 36 files changed
bin/lint
@@ -8,4 +8,4 @@ echo [$(date "+%H:%M:%S")] "==> Running setup…"
 bin/setup
 
 echo [$(date "+%H:%M:%S")] "==> Running linters…"
-bundle exec rake rubocop
+bundle exec rake lint
lib/saml/kit/bindings/binding.rb
@@ -3,7 +3,7 @@
 module Saml
   module Kit
     module Bindings
-      # {include:file:spec/saml/bindings/binding_spec.rb}
+      # {include:file:spec/saml/kit/bindings/binding_spec.rb}
       class Binding
         attr_reader :binding, :location
 
lib/saml/kit/bindings/http_post.rb
@@ -3,7 +3,7 @@
 module Saml
   module Kit
     module Bindings
-      # {include:file:spec/saml/bindings/http_post_spec.rb}
+      # {include:file:spec/saml/kit/bindings/http_post_spec.rb}
       class HttpPost < Binding
         include Serializable
 
lib/saml/kit/bindings/http_redirect.rb
@@ -3,7 +3,7 @@
 module Saml
   module Kit
     module Bindings
-      # {include:file:spec/saml/bindings/http_redirect_spec.rb}
+      # {include:file:spec/saml/kit/bindings/http_redirect_spec.rb}
       class HttpRedirect < Binding
         include Serializable
 
lib/saml/kit/bindings/url_builder.rb
@@ -3,7 +3,7 @@
 module Saml
   module Kit
     module Bindings
-      # {include:file:spec/saml/bindings/url_builder_spec.rb}
+      # {include:file:spec/saml/kit/bindings/url_builder_spec.rb}
       class UrlBuilder
         include Serializable
         attr_reader :configuration
lib/saml/kit/builders/templates/assertion.builder
@@ -10,8 +10,10 @@ xml.Assertion(assertion_options) do
     end
   end
   xml.Conditions conditions_options do
-    xml.AudienceRestriction do
-      xml.Audience request.issuer
+    if request.present?
+      xml.AudienceRestriction do
+        xml.Audience request.issuer
+      end
     end
   end
   xml.AuthnStatement authn_statement_options do
lib/saml/kit/builders/templates/authentication_request.builder
@@ -4,5 +4,5 @@ xml.instruct!
 xml.tag!('samlp:AuthnRequest', request_options) do
   xml.tag!('saml:Issuer', issuer)
   signature_for(reference_id: id, xml: xml)
-  xml.tag!('samlp:NameIDPolicy', Format: name_id_format)
+  xml.tag!('samlp:NameIDPolicy', Format: name_id_format) if name_id_format.present?
 end
lib/saml/kit/builders/templates/identity_provider_metadata.builder
@@ -1,10 +1,7 @@
 # frozen_string_literal: true
 
 xml.IDPSSODescriptor descriptor_options do
-  configuration.certificates(use: :signing).each do |certificate|
-    render certificate, xml: xml
-  end
-  configuration.certificates(use: :encryption).each do |certificate|
+  configuration.certificates.each do |certificate|
     render certificate, xml: xml
   end
   logout_urls.each do |item|
lib/saml/kit/builders/templates/service_provider_metadata.builder
@@ -1,10 +1,7 @@
 # frozen_string_literal: true
 
 xml.SPSSODescriptor descriptor_options do
-  configuration.certificates(use: :signing).each do |certificate|
-    render certificate, xml: xml
-  end
-  configuration.certificates(use: :encryption).each do |certificate|
+  configuration.certificates.each do |certificate|
     render certificate, xml: xml
   end
   logout_urls.each do |item|
lib/saml/kit/builders/assertion.rb
@@ -16,7 +16,7 @@ module Saml
         end
 
         def name_id_format
-          request.name_id_format
+          request.try(:name_id_format) || Saml::Kit::Namespaces::PERSISTENT
         end
 
         def name_id
@@ -24,6 +24,7 @@ module Saml
         end
 
         def assertion_attributes
+          return {} unless user.respond_to?(:assertion_attributes_for)
           user.assertion_attributes_for(request)
         end
 
@@ -43,11 +44,10 @@ module Saml
         end
 
         def subject_confirmation_data_options
-          {
-            InResponseTo: request.id,
-            NotOnOrAfter: 3.hours.since(now).utc.iso8601,
-            Recipient: destination,
-          }
+          options = { NotOnOrAfter: 3.hours.since(now).utc.iso8601 }
+          options[:Recipient] = destination if destination.present?
+          options[:InResponseTo] = request.id if request.present?
+          options
         end
 
         def conditions_options
lib/saml/kit/builders/authentication_request.rb
@@ -4,7 +4,7 @@ module Saml
   module Kit
     module Builders
       # {include:file:lib/saml/kit/builders/templates/authentication_request.builder}
-      # {include:file:spec/saml/builders/authentication_request_spec.rb}
+      # {include:file:spec/saml/kit/builders/authentication_request_spec.rb}
       class AuthenticationRequest
         include XmlTemplatable
         attr_accessor :id, :now, :issuer, :assertion_consumer_service_url, :name_id_format, :destination
lib/saml/kit/builders/identity_provider_metadata.rb
@@ -4,7 +4,7 @@ module Saml
   module Kit
     module Builders
       # {include:file:lib/saml/kit/builders/templates/identity_provider_metadata.builder}
-      # {include:file:spec/saml/builders/identity_provider_metadata_spec.rb}
+      # {include:file:spec/saml/kit/builders/identity_provider_metadata_spec.rb}
       class IdentityProviderMetadata
         include XmlTemplatable
         extend Forwardable
lib/saml/kit/builders/logout_request.rb
@@ -4,7 +4,7 @@ module Saml
   module Kit
     module Builders
       # {include:file:lib/saml/kit/builders/templates/logout_request.builder}
-      # {include:file:spec/saml/builders/logout_request_spec.rb}
+      # {include:file:spec/saml/kit/builders/logout_request_spec.rb}
       class LogoutRequest
         include XmlTemplatable
         attr_accessor :id, :destination, :issuer, :name_id_format, :now
lib/saml/kit/builders/logout_response.rb
@@ -4,7 +4,7 @@ module Saml
   module Kit
     module Builders
       # {include:file:lib/saml/kit/builders/templates/logout_response.builder}
-      # {include:file:spec/saml/builders/logout_response_spec.rb}
+      # {include:file:spec/saml/kit/builders/logout_response_spec.rb}
       class LogoutResponse
         include XmlTemplatable
         attr_accessor :id, :issuer, :version, :status_code, :now, :destination
lib/saml/kit/builders/metadata.rb
@@ -4,7 +4,7 @@ module Saml
   module Kit
     module Builders
       # {include:file:lib/saml/kit/builders/templates/metadata.builder}
-      # {include:file:spec/saml/builders/metadata_spec.rb}
+      # {include:file:spec/saml/kit/builders/metadata_spec.rb}
       class Metadata
         include XmlTemplatable
 
lib/saml/kit/builders/response.rb
@@ -4,7 +4,7 @@ module Saml
   module Kit
     module Builders
       # {include:file:lib/saml/kit/builders/templates/response.builder}
-      # {include:file:spec/saml/builders/response_spec.rb}
+      # {include:file:spec/saml/kit/builders/response_spec.rb}
       class Response
         include XmlTemplatable
         attr_reader :user, :request
@@ -13,7 +13,7 @@ module Saml
         attr_accessor :issuer, :destination
         attr_reader :configuration
 
-        def initialize(user, request, configuration: Saml::Kit.configuration)
+        def initialize(user, request = nil, configuration: Saml::Kit.configuration)
           @user = user
           @request = request
           @id = ::Xml::Kit::Id.generate
@@ -28,7 +28,7 @@ module Saml
         end
 
         def build
-          Saml::Kit::Response.new(to_xml, request_id: request.id, configuration: configuration)
+          Saml::Kit::Response.new(to_xml, request_id: request.try(:id), configuration: configuration)
         end
 
         def assertion
@@ -46,15 +46,16 @@ module Saml
         private
 
         def response_options
-          {
+          options = {
             ID: id,
             Version: version,
             IssueInstant: now.iso8601,
-            Destination: destination,
             Consent: Namespaces::UNSPECIFIED,
-            InResponseTo: request.id,
             xmlns: Namespaces::PROTOCOL,
           }
+          options[:Destination] = destination if destination.present?
+          options[:InResponseTo] = request.id if request.present?
+          options
         end
       end
     end
lib/saml/kit/builders/service_provider_metadata.rb
@@ -4,7 +4,7 @@ module Saml
   module Kit
     module Builders
       # {include:file:lib/saml/kit/builders/templates/service_provider_metadata.builder}
-      # {include:file:spec/saml/builders/service_provider_metadata_spec.rb}
+      # {include:file:spec/saml/kit/builders/service_provider_metadata_spec.rb}
       class ServiceProviderMetadata
         include XmlTemplatable
         extend Forwardable
lib/saml/kit/assertion.rb
@@ -109,6 +109,7 @@ module Saml
       end
 
       def must_match_issuer
+        return if audiences.empty?
         return if audiences.include?(configuration.entity_id)
         errors[:audience] << error_message(:must_match_issuer)
       end
lib/saml/kit/default_registry.rb
@@ -29,7 +29,7 @@ module Saml
     #     configuration.logger = Rails.logger
     #   end
     #
-    # {include:file:spec/saml/default_registry_spec.rb}
+    # {include:file:spec/saml/kit/default_registry_spec.rb}
     class DefaultRegistry
       include Enumerable
 
lib/saml/kit/document.rb
@@ -17,6 +17,7 @@ module Saml
         "samlp": ::Saml::Kit::Namespaces::PROTOCOL,
         'xmlenc' => ::Xml::Kit::Namespaces::XMLENC,
       }.freeze
+      attr_accessor :registry
       validates_presence_of :content
       validates_presence_of :id
       validate :must_match_xsd
@@ -25,6 +26,7 @@ module Saml
 
       def initialize(xml, name:, configuration: Saml::Kit.configuration)
         @configuration = configuration
+        @registry = configuration.registry
         @content = xml
         @name = name
       end
lib/saml/kit/invalid_document.rb
@@ -2,7 +2,7 @@
 
 module Saml
   module Kit
-    # {include:file:spec/saml/invalid_document_spec.rb}
+    # {include:file:spec/saml/kit/invalid_document_spec.rb}
     class InvalidDocument < Document
       validate do |model|
         model.errors[:base] << model.error_message(:invalid)
lib/saml/kit/signature.rb
@@ -69,8 +69,8 @@ module Saml
         node
       end
 
-      def to_xml
-        node.to_s
+      def to_xml(pretty: false)
+        pretty ? node.to_xml(indent: 2) : node.to_s
       end
 
       private
@@ -84,6 +84,8 @@ module Saml
         dsignature.errors.each do |attribute|
           errors.add(attribute, error_message(attribute))
         end
+      rescue Xmldsig::SchemaError => error
+        errors.add(:base, error.message)
       end
 
       def validate_certificate(now = Time.now.utc)
lib/saml/kit/trustable.rb
@@ -30,7 +30,7 @@ module Saml
 
       # @!visibility private
       def provider
-        configuration.registry.metadata_for(issuer)
+        registry.metadata_for(issuer)
       end
 
       # @!visibility private
lib/saml/kit/version.rb
@@ -2,6 +2,6 @@
 
 module Saml
   module Kit
-    VERSION = '1.0.11'.freeze
+    VERSION = '1.0.12'.freeze
   end
 end
lib/saml/kit/xml_templatable.rb
@@ -21,11 +21,6 @@ module Saml
           (embed_signature && signing_key_pair.present?)
       end
 
-      def encrypt_with(key_pair)
-        self.encrypt = true
-        self.encryption_certificate = key_pair.certificate
-      end
-
       def digest_method
         configuration.digest_method
       end
spec/fixtures/response_node_text_attack.xml.base64
@@ -0,0 +1,1 @@
+PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJHT1NBTUxSMTI5MDExNzQ1NzE3OTQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgRGVzdGluYXRpb249IntyZWNpcGllbnR9Ij4NCiAgPHNhbWxwOlN0YXR1cz4NCiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+DQogIDxzYW1sOkFzc2VydGlvbiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIFZlcnNpb249IjIuMCIgSUQ9InBmeGE0NjU3NGRmLWIzYjAtYTA2YS0yM2M4LTYzNjQxMzE5ODc3MiIgSXNzdWVJbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiI+DQogICAgPHNhbWw6SXNzdWVyPmh0dHBzOi8vYXBwLm9uZWxvZ2luLmNvbS9zYW1sL21ldGFkYXRhLzEzNTkwPC9zYW1sOklzc3Vlcj4NCiAgICA8ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4NCiAgICAgIDxkczpTaWduZWRJbmZvPg0KICAgICAgICA8ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPg0KICAgICAgICA8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+DQogICAgICAgIDxkczpSZWZlcmVuY2UgVVJJPSIjcGZ4YTQ2NTc0ZGYtYjNiMC1hMDZhLTIzYzgtNjM2NDEzMTk4NzcyIj4NCiAgICAgICAgICA8ZHM6VHJhbnNmb3Jtcz4NCiAgICAgICAgICAgIDxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPg0KICAgICAgICAgICAgPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPg0KICAgICAgICAgIDwvZHM6VHJhbnNmb3Jtcz4NCiAgICAgICAgICA8ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz4NCiAgICAgICAgICA8ZHM6RGlnZXN0VmFsdWU+cEpRN01TL2VrNEtSUldHbXYvSDQzUmVIWU1zPTwvZHM6RGlnZXN0VmFsdWU+DQogICAgICAgIDwvZHM6UmVmZXJlbmNlPg0KICAgICAgPC9kczpTaWduZWRJbmZvPg0KICAgICAgPGRzOlNpZ25hdHVyZVZhbHVlPnlpdmVLY1BkRHB1RE5qNnNoclEzQUJ3ci9jQTNDcnlEMnBoRy94TFpzektXeFU1L21sYUt0OGV3YlpPZEtLdnRPczJwSEJ5NUR1YTNrOTRBRnp4R3llbDVnT293bW95WEpyQU9ya1BPMHZsaTFWOG8zaFBQVVp3UmdTWDZROXBTMUNxUWdoS2lFYXNSeXlscXFKVWFQWXptT3pPRTgvWGxNa3dpV21PMD08L2RzOlNpZ25hdHVyZVZhbHVlPg0KICAgICAgPGRzOktleUluZm8+DQogICAgICAgIDxkczpYNTA5RGF0YT4NCiAgICAgICAgICA8ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUJyVENDQWFHZ0F3SUJBZ0lCQVRBREJnRUFNR2N4Q3pBSkJnTlZCQVlUQWxWVE1STXdFUVlEVlFRSURBcERZV3hwWm05eWJtbGhNUlV3RXdZRFZRUUhEQXhUWVc1MFlTQk5iMjVwWTJFeEVUQVBCZ05WQkFvTUNFOXVaVXh2WjJsdU1Sa3dGd1lEVlFRRERCQmhjSEF1YjI1bGJHOW5hVzR1WTI5dE1CNFhEVEV3TURNd09UQTVOVGcwTlZvWERURTFNRE13T1RBNU5UZzBOVm93WnpFTE1Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBZ01Da05oYkdsbWIzSnVhV0V4RlRBVEJnTlZCQWNNREZOaGJuUmhJRTF2Ym1sallURVJNQThHQTFVRUNnd0lUMjVsVEc5bmFXNHhHVEFYQmdOVkJBTU1FR0Z3Y0M1dmJtVnNiMmRwYmk1amIyMHdnWjh3RFFZSktvWklodmNOQVFFQkJRQURnWTBBTUlHSkFvR0JBT2pTdTFmalB5OGQ1dzRReUwxemQ0aEl3MU1ra2ZmNFdZL1RMRzhPWmtVNVlUU1dtbUhQRDVrdllINXVvWFMvNnFRODFxWHBSMndWOENUb3daSlVMZzA5ZGRSZFJuOFFzcWoxRnlPQzVzbEUzeTJiWjJvRnVhNzJvZi80OWZwdWpuRlQ2S25RNjFDQk1xbERvVFFxT1Q2MnZHSjhuUDZNWld2QTZzeHF1ZDVBZ01CQUFFd0F3WUJBQU1CQUE9PTwvZHM6WDUwOUNlcnRpZmljYXRlPg0KICAgICAgICA8L2RzOlg1MDlEYXRhPg0KICAgICAgPC9kczpLZXlJbmZvPg0KICAgIDwvZHM6U2lnbmF0dXJlPg0KICAgIDxzYW1sOlN1YmplY3Q+DQogICAgICA8c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnN1cHBvcnQ8IS0tIGF0dGFjayEgLS0+QG9uZWxvZ2luLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOFQyMjowMjozN1oiIFJlY2lwaWVudD0ie3JlY2lwaWVudH0iLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L3NhbWw6U3ViamVjdD4NCiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0xMS0xOFQyMTo1MjozN1oiIE5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOFQyMjowMjozN1oiPg0KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICAgICAgPHNhbWw6QXVkaWVuY2U+e2F1ZGllbmNlfTwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMC0xMS0xOFQyMTo1NzozN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTAtMTEtMTlUMjE6NTc6MzdaIiBTZXNzaW9uSW5kZXg9Il81MzFjMzJkMjgzYmRmZjdlMDRlNDg3YmNkYmM0ZGQ4ZCI+DQogICAgICA8c2FtbDpBdXRobkNvbnRleHQ+DQogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPg0KICAgICAgPC9zYW1sOkF1dGhuQ29udGV4dD4NCiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+DQogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9InN1cm5hbWUiPg0KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPnM8IS0tIGF0dGFjayEgLS0+bWl0aDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0iYW5vdGhlcl92YWx1ZSI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+dmFsdWUxPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPg0KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPnZhbHVlMjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0icm9sZSI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+cm9sZTE8L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgIDwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9ImZpcnN0bmFtZSI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+Ym9iPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPg0KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4gIA0KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9ImF0dHJpYnV0ZV93aXRoX25pbF92YWx1ZSI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOm5pbD0idHJ1ZSIvPg0KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJhdHRyaWJ1dGVfd2l0aF9uaWxzX2FuZF9lbXB0eV9zdHJpbmdzIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUvPg0KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZT52YWx1ZVByZXNlbnQ8L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOm5pbD0idHJ1ZSIvPg0KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTpuaWw9IjEiLz4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+
spec/saml/kit/authentication_request_spec.rb
@@ -189,7 +189,7 @@ RSpec.describe Saml::Kit::AuthenticationRequest do
     let(:url) { FFaker::Internet.uri('https') }
     let(:entity_id) { FFaker::Internet.uri('https') }
 
-    it 'provides a nice API for building metadata' do
+    it 'provides a nice API for building a request' do
       result = described_class.build do |builder|
         builder.issuer = entity_id
         builder.assertion_consumer_service_url = url
@@ -199,6 +199,18 @@ RSpec.describe Saml::Kit::AuthenticationRequest do
       expect(result.issuer).to eql(entity_id)
       expect(result.assertion_consumer_service_url).to eql(url)
     end
+
+    it 'can build a authnrequest without a nameid policy' do
+      result = described_class.build do |x|
+        x.issuer = entity_id
+        x.assertion_consumer_service_url = url
+        x.name_id_format = nil
+      end
+      expect(result).to be_instance_of(described_class)
+      result.registry = instance_double(Saml::Kit::DefaultRegistry, metadata_for: Saml::Kit::ServiceProviderMetadata.build)
+      expect(result).to be_valid
+      expect(result.to_xml).not_to include('NameIDPolicy')
+    end
   end
 
   describe '#response_for' do
spec/saml/kit/metadata_spec.rb
@@ -84,4 +84,36 @@ RSpec.describe Saml::Kit::Metadata do
       expect(subject.signature).to be_present
     end
   end
+
+  describe 'validations' do
+    it 'is invalid, when the digest value is invalid' do
+      xml = described_class.build_xml do |x|
+        x.entity_id = 'original'
+        x.sign_with(::Xml::Kit::KeyPair.generate(use: :signing))
+        x.build_identity_provider do |y|
+          y.add_single_sign_on_service(FFaker::Internet.uri('https'), binding: :http_post)
+        end
+      end
+
+      subject = described_class.from(xml.gsub('original', 'altered'))
+      expect(subject).not_to be_valid
+      expect(subject.errors[:digest_value]).to include('is invalid.')
+    end
+
+    it 'is invalid when the signature is invalid' do
+      xml = described_class.build_xml do |x|
+        x.sign_with(::Xml::Kit::KeyPair.generate(use: :signing))
+        x.build_identity_provider do |y|
+          y.add_single_sign_on_service(FFaker::Internet.uri('https'), binding: :http_post)
+        end
+      end
+      document = Nokogiri::XML(xml)
+      node = document.at_xpath('/*/ds:Signature/ds:SignatureValue', ds: Xml::Kit::Namespaces::XMLDSIG)
+      node.content = Base64.encode64('invalid')
+
+      subject = described_class.from(document.to_s)
+      expect(subject).not_to be_valid
+      expect(subject.errors[:signature]).to include('is invalid.')
+    end
+  end
 end
spec/saml/kit/response_spec.rb
@@ -530,6 +530,20 @@ XML
       expect(subject.name_id).to eql(user.name_id)
     end
 
+    it 'excludes comments from the name id' do
+      user.name_id = 'shiro@voltron.com<!-- CVE-2017-11428 -->.evil.com'
+      subject = described_class.build(user, request)
+      expect(subject.name_id).to eql('shiro@voltron.com<!-- CVE-2017-11428 -->.evil.com')
+      expect(subject.name_id).not_to eql('shiro@voltron.com')
+    end
+
+    it 'parses the name id safely (CVE-2017-11428)' do
+      raw = IO.read('spec/fixtures/response_node_text_attack.xml.base64')
+      subject = Saml::Kit::Bindings::HttpPost.new(location: '').deserialize('SAMLResponse' => raw)
+      expect(subject.name_id).to eql('support@onelogin.com')
+      expect(subject.attributes[:surname]).to eql('smith')
+    end
+
     it 'returns the single attributes' do
       subject = described_class.build(user, request)
       expect(subject.attributes).to eql('name' => 'mo')
@@ -541,4 +555,31 @@ XML
       expect(subject.attributes).to eql('name' => 'mo', 'age' => '33')
     end
   end
+
+  describe '#build' do
+    it 'can build a response without a request' do
+      configuration = Saml::Kit::Configuration.new do |config|
+        config.entity_id = FFaker::Internet.uri('https')
+      end
+      sp = Saml::Kit::Metadata.build(&:build_service_provider)
+      allow(configuration.registry).to receive(:metadata_for).with(configuration.entity_id).and_return(sp)
+      result = described_class.build(user, configuration: configuration)
+      expect(result).to be_instance_of(described_class)
+      expect(result).to be_valid
+    end
+
+    it 'can build a response without the need for the user to provide attributes' do
+      configuration = Saml::Kit::Configuration.new do |config|
+        config.entity_id = FFaker::Internet.uri('https')
+      end
+      sp = Saml::Kit::Metadata.build(&:build_service_provider)
+      allow(configuration.registry).to receive(:metadata_for).with(configuration.entity_id).and_return(sp)
+      user = UserWithoutAttributes.new
+
+      result = described_class.build(user, configuration: configuration)
+      expect(result).to be_instance_of(described_class)
+      expect(result).to be_valid
+      expect(result.attributes).to be_empty
+    end
+  end
 end
spec/saml/kit/signature_spec.rb
@@ -39,6 +39,15 @@ RSpec.describe Saml::Kit::Signature do
       expect(subject.errors[:base]).to match_array(['is missing.'])
     end
 
+    it 'is invalid when the schema of the signature is invalid' do
+      signature_element = signed_document.at_xpath('//ds:Signature')
+      element = signature_element.at_xpath('./ds:SignedInfo', ds: Xml::Kit::Namespaces::XMLDSIG)
+      element.name = 'BLAH'
+      subject = described_class.new(signature_element)
+      expect(subject).not_to be_valid
+      expect(subject.errors[:base]).to include("1:0: ERROR: Element '{http://www.w3.org/2000/09/xmldsig#}BLAH': This element is not expected. Expected is ( {http://www.w3.org/2000/09/xmldsig#}SignedInfo ).")
+    end
+
     describe 'certificate validation' do
       let(:key_pair) { ::Xml::Kit::KeyPair.new(expired_certificate, private_key, nil, :signing) }
       let(:private_key) { OpenSSL::PKey::RSA.new(2048) }
spec/support/user.rb
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class User
-  attr_reader :name_id, :attributes
+  attr_accessor :name_id, :attributes
 
   def initialize(name_id: SecureRandom.uuid, attributes: {})
     @name_id = name_id
spec/support/user_without_attributes.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class UserWithoutAttributes
+  attr_accessor :name_id
+
+  def initialize(name_id: SecureRandom.uuid)
+    @name_id = name_id
+  end
+
+  def name_id_for(_format)
+    name_id
+  end
+end
.rubocop_todo.yml
@@ -15,7 +15,7 @@ Lint/UnusedMethodArgument:
 
 # Offense count: 2
 Metrics/AbcSize:
-  Max: 16
+  Max: 20
 
 # Offense count: 3
 # Configuration parameters: CountComments.
.travis.yml
@@ -1,5 +1,6 @@
 sudo: false
 language: ruby
+cache: bundler
 rvm:
   - 2.2.9
   - 2.3.6
Rakefile
@@ -8,3 +8,8 @@ task default: :spec
 
 require 'rubocop/rake_task'
 RuboCop::RakeTask.new(:rubocop)
+
+require 'bundler/audit/task'
+Bundler::Audit::Task.new
+
+task lint: [:rubocop, 'bundle:audit']
saml-kit.gemspec
@@ -30,8 +30,9 @@ Gem::Specification.new do |spec|
   spec.require_paths = ['lib']
 
   spec.add_dependency 'activemodel', '>= 4.2.0'
-  spec.add_dependency 'xml-kit', '>= 0.1.10', '<= 1.0.0'
+  spec.add_dependency 'xml-kit', '>= 0.1.12', '<= 1.0.0'
   spec.add_development_dependency 'bundler', '~> 1.15'
+  spec.add_development_dependency 'bundler-audit', '~> 0.6'
   spec.add_development_dependency 'ffaker', '~> 2.7'
   spec.add_development_dependency 'rake', '~> 10.0'
   spec.add_development_dependency 'rspec', '~> 3.0'