Description: limit fetching file size & disable XML entity expansion
  This prevents possible XML denial of service attacks [CVE-2013-1812]
Author: nov matake <nov@matake.jp>
Origin: https://github.com/openid/ruby-openid/commit/a3693cef06049563f5b4e4824f4d3211288508ed
Bug: https://github.com/openid/ruby-openid/pull/43
Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=702217
Reviewed-by: Cédric Boutillier <boutil@debian.org>
Last-Update: 2012-10-23

---
 lib/openid/fetchers.rb   |   17 ++++++++++++++---
 lib/openid/yadis/xrds.rb |   34 ++++++++++++++++++++++------------
 2 files changed, 36 insertions(+), 15 deletions(-)

--- a/lib/openid/fetchers.rb
+++ b/lib/openid/fetchers.rb
@@ -10,7 +10,7 @@
   require 'net/http'
 end
 
-MAX_RESPONSE_KB = 1024
+MAX_RESPONSE_KB = 10485760 # 10 MB (can be smaller, I guess)
 
 module Net
   class HTTP
@@ -192,6 +192,16 @@
         conn = make_connection(url)
         response = nil
 
+        whole_body = ''
+        body_size_limitter = lambda do |r|
+          r.read_body do |partial|   # read body now
+            whole_body << partial
+            if whole_body.length > MAX_RESPONSE_KB
+              raise FetchingError.new("Response Too Large")
+            end
+          end
+          whole_body
+        end
         response = conn.start {
           # Check the certificate against the URL's hostname
           if supports_ssl?(conn) and conn.use_ssl?
@@ -199,13 +209,12 @@
           end
 
           if body.nil?
-            conn.request_get(url.request_uri, headers)
+            conn.request_get(url.request_uri, headers, &body_size_limitter)
           else
             headers["Content-type"] ||= "application/x-www-form-urlencoded"
-            conn.request_post(url.request_uri, body, headers)
+            conn.request_post(url.request_uri, body, headers, &body_size_limitter)
           end
         }
-        setup_encoding(response)
       rescue Timeout::Error => why
         raise FetchingError, "Error fetching #{url}: #{why}"
       rescue RuntimeError => why
@@ -232,7 +241,10 @@
           raise FetchingError, "Error encountered in redirect from #{url}: #{why}"
         end
       else
-        return HTTPResponse._from_net_response(response, unparsed_url)
+        response = HTTPResponse._from_net_response(response, unparsed_url)
+        response.body = whole_body
+        setup_encoding(response)
+        return response
       end
     end
 
--- a/lib/openid/yadis/xrds.rb
+++ b/lib/openid/yadis/xrds.rb
@@ -88,23 +88,33 @@
     end
 
     def Yadis::parseXRDS(text)
-      if text.nil?
-        raise XRDSError.new("Not an XRDS document.")
-      end
+      disable_entity_expansion do
+        if text.nil?
+          raise XRDSError.new("Not an XRDS document.")
+        end
 
-      begin
-        d = REXML::Document.new(text)
-      rescue RuntimeError => why
-        raise XRDSError.new("Not an XRDS document. Failed to parse XML.")
-      end
+        begin
+          d = REXML::Document.new(text)
+        rescue RuntimeError => why
+          raise XRDSError.new("Not an XRDS document. Failed to parse XML.")
+        end
 
-      if is_xrds?(d)
-        return d
-      else
-        raise XRDSError.new("Not an XRDS document.")
+        if is_xrds?(d)
+          return d
+        else
+          raise XRDSError.new("Not an XRDS document.")
+        end
       end
     end
 
+    def Yadis::disable_entity_expansion
+      _previous_ = REXML::Document::entity_expansion_limit
+      REXML::Document::entity_expansion_limit = 0
+      yield
+    ensure
+      REXML::Document::entity_expansion_limit = _previous_
+    end
+
     def Yadis::is_xrds?(xrds_tree)
       xrds_root = xrds_tree.root
       return (!xrds_root.nil? and
