Procházet zdrojové kódy

Fixes for potential XXE/XEE vulnerabilities

- Patch provided by Padriac Brady, reviewed by Ralph Schindler and Matthew Weier
  O'Phinney
- Merged from r25031



git-svn-id: http://framework.zend.com/svn/framework/standard/branches/release-1.12@25033 44c647ce-9c0f-0410-b52a-842ac1e357ba
matthew před 13 roky
rodič
revize
1b5e86183a

+ 10 - 0
library/Zend/Dom/Query.php

@@ -245,6 +245,7 @@ class Zend_Dom_Query
 
         $encoding = $this->getEncoding();
         libxml_use_internal_errors(true);
+        libxml_disable_entity_loader(true);
         if (null === $encoding) {
             $domDoc = new DOMDocument('1.0');
         } else {
@@ -254,6 +255,14 @@ class Zend_Dom_Query
         switch ($type) {
             case self::DOC_XML:
                 $success = $domDoc->loadXML($document);
+                foreach ($domDoc->childNodes as $child) {
+                    if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
+                        require_once 'Zend/Dom/Exception.php';
+                        throw new Zend_Dom_Exception(
+                            'Invalid XML: Detected use of illegal DOCTYPE'
+                        );
+                    }
+                }
                 break;
             case self::DOC_HTML:
             case self::DOC_XHTML:
@@ -266,6 +275,7 @@ class Zend_Dom_Query
             $this->_documentErrors = $errors;
             libxml_clear_errors();
         }
+        libxml_disable_entity_loader(false);
         libxml_use_internal_errors(false);
 
         if (!$success) {

+ 22 - 1
library/Zend/Feed/Reader.php

@@ -333,10 +333,19 @@ class Zend_Feed_Reader
      */
     public static function importString($string)
     {
-        
         $libxml_errflag = libxml_use_internal_errors(true);
+        $oldValue = libxml_disable_entity_loader(true);
         $dom = new DOMDocument;
         $status = $dom->loadXML($string);
+        foreach ($dom->childNodes as $child) {
+            if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
+                require_once 'Zend/Feed/Exception.php';
+                throw new Zend_Feed_Exception(
+                    'Invalid XML: Detected use of illegal DOCTYPE'
+                );
+            }
+        }
+        libxml_disable_entity_loader($oldValue);
         libxml_use_internal_errors($libxml_errflag);
 
         if (!$status) {
@@ -407,8 +416,10 @@ class Zend_Feed_Reader
         }
         $responseHtml = $response->getBody();
         $libxml_errflag = libxml_use_internal_errors(true);
+        $oldValue = libxml_disable_entity_loader(true);
         $dom = new DOMDocument;
         $status = $dom->loadHTML($responseHtml);
+        libxml_disable_entity_loader($oldValue);
         libxml_use_internal_errors($libxml_errflag);
         if (!$status) {
             // Build error message
@@ -442,8 +453,18 @@ class Zend_Feed_Reader
             $dom = $feed;
         } elseif(is_string($feed) && !empty($feed)) {
             @ini_set('track_errors', 1);
+            $oldValue = libxml_disable_entity_loader(true);
             $dom = new DOMDocument;
             $status = @$dom->loadXML($feed);
+            foreach ($dom->childNodes as $child) {
+                if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
+                    require_once 'Zend/Feed/Exception.php';
+                    throw new Zend_Feed_Exception(
+                        'Invalid XML: Detected use of illegal DOCTYPE'
+                    );
+                }
+            }
+            libxml_disable_entity_loader($oldValue);
             @ini_restore('track_errors');
             if (!$status) {
                 if (!isset($php_errormsg)) {

+ 13 - 1
library/Zend/Serializer/Adapter/Wddx.php

@@ -100,7 +100,19 @@ class Zend_Serializer_Adapter_Wddx extends Zend_Serializer_Adapter_AdapterAbstra
             // check if the returned NULL is valid
             // or based on an invalid wddx string
             try {
-                $simpleXml = new SimpleXMLElement($wddx);
+                $oldLibxmlDisableEntityLoader = libxml_disable_entity_loader(true);
+                $dom = new DOMDocument;
+                $dom->loadXML($wddx);
+                foreach ($dom->childNodes as $child) {
+                    if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
+                        require_once 'Zend/Serializer/Exception.php';
+                        throw new Zend_Serializer_Exception(
+                            'Invalid XML: Detected use of illegal DOCTYPE'
+                        );
+                    }
+                }
+                $simpleXml = simplexml_import_dom($dom);
+                libxml_disable_entity_loader($oldLibxmlDisableEntityLoader);
                 if (isset($simpleXml->data[0]->null[0])) {
                     return null; // valid null
                 }

+ 7 - 0
library/Zend/Soap/Client/Local.php

@@ -84,6 +84,13 @@ class Zend_Soap_Client_Local extends Zend_Soap_Client
         ob_start();
         $this->_server->handle($request);
         $response = ob_get_clean();
+ 
+        if ($response === null || $response === '') {
+            $serverResponse = $this->server->getResponse();
+            if ($serverResponse !== null) {
+                $response = $serverResponse;
+            }
+        }
 
         return $response;
     }

+ 19 - 4
library/Zend/Soap/Server.php

@@ -729,11 +729,21 @@ class Zend_Soap_Server implements Zend_Server_Interface
                 $xml = $request;
             }
 
+            libxml_disable_entity_loader(true);
             $dom = new DOMDocument();
             if(strlen($xml) == 0 || !$dom->loadXML($xml)) {
                 require_once 'Zend/Soap/Server/Exception.php';
                 throw new Zend_Soap_Server_Exception('Invalid XML');
             }
+            foreach ($dom->childNodes as $child) {
+                if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
+                    require_once 'Zend/Soap/Server/Exception.php';
+                    throw new Zend_Soap_Server_Exception(
+                        'Invalid XML: Detected use of illegal DOCTYPE'
+                    );
+                }
+            }
+            libxml_disable_entity_loader(false);
         }
         $this->_request = $xml;
         return $this;
@@ -866,16 +876,16 @@ class Zend_Soap_Server implements Zend_Server_Interface
         
         $soap = $this->_getSoap();
 
+        $fault = false;
         ob_start();
-        if($setRequestException instanceof Exception) {
-            // Send SOAP fault message if we've catched exception
-            $soap->fault("Sender", $setRequestException->getMessage());
+        if ($setRequestException instanceof Exception) {
+            // Create SOAP fault message if we've caught a request exception
+            $fault = $this->fault($setRequestException->getMessage(), 'Sender');
         } else {
             try {
                 $soap->handle($this->_request);
             } catch (Exception $e) {
                 $fault = $this->fault($e);
-                $soap->fault($fault->faultcode, $fault->faultstring);
             }
         }
         $this->_response = ob_get_clean();
@@ -884,6 +894,11 @@ class Zend_Soap_Server implements Zend_Server_Interface
         restore_error_handler();
         ini_set('display_errors', $displayErrorsOriginalState);
 
+        // Send a fault, if we have one
+        if ($fault) {
+            $this->_response = $fault;
+        }
+
         if (!$this->_returnResponse) {
             echo $this->_response;
             return;

+ 12 - 0
library/Zend/Soap/Wsdl.php

@@ -96,13 +96,23 @@ class Zend_Soap_Wsdl
                     xmlns:xsd='http://www.w3.org/2001/XMLSchema'
                     xmlns:soap-enc='http://schemas.xmlsoap.org/soap/encoding/'
                     xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'></definitions>";
+        libxml_disable_entity_loader(true);
         $this->_dom = new DOMDocument();
         if (!$this->_dom->loadXML($wsdl)) {
             require_once 'Zend/Server/Exception.php';
             throw new Zend_Server_Exception('Unable to create DomDocument');
         } else {
+            foreach ($this->_dom->childNodes as $child) {
+                if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
+                    require_once 'Zend/Server/Exception.php';
+                    throw new Zend_Server_Exception(
+                        'Invalid XML: Detected use of illegal DOCTYPE'
+                    );
+                }
+            }
             $this->_wsdl = $this->_dom->documentElement;
         }
+        libxml_disable_entity_loader(false);
 
         $this->setComplexTypeStrategy($strategy);
     }
@@ -125,8 +135,10 @@ class Zend_Soap_Wsdl
             // @todo: This is the worst hack ever, but its needed due to design and non BC issues of WSDL generation
             $xml = $this->_dom->saveXML();
             $xml = str_replace($oldUri, $uri, $xml);
+            libxml_disable_entity_loader(true);
             $this->_dom = new DOMDocument();
             $this->_dom->loadXML($xml);
+            libxml_disable_entity_loader(false);
         }
 
         return $this;

+ 11 - 1
library/Zend/XmlRpc/Request.php

@@ -306,7 +306,17 @@ class Zend_XmlRpc_Request
         // @see ZF-12293 - disable external entities for security purposes
         $loadEntities = libxml_disable_entity_loader(true);
         try {
-            $xml = new SimpleXMLElement($request);
+            $dom = new DOMDocument;
+            $dom->loadXML($request);
+            foreach ($dom->childNodes as $child) {
+                if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
+                    require_once 'Zend/XmlRpc/Exception.php';
+                    throw new Zend_XmlRpc_Exception(
+                        'Invalid XML: Detected use of illegal DOCTYPE'
+                    );
+                }
+            }
+            $xml = simplexml_import_dom($dom);
             libxml_disable_entity_loader($loadEntities);
         } catch (Exception $e) {
             // Not valid XML

+ 12 - 0
library/Zend/XmlRpc/Response.php

@@ -180,6 +180,18 @@ class Zend_XmlRpc_Response
         $loadEntities         = libxml_disable_entity_loader(true);
         $useInternalXmlErrors = libxml_use_internal_errors(true);
         try {
+            $dom = new DOMDocument;
+            $dom->loadXML($response);
+            foreach ($dom->childNodes as $child) {
+                if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
+                    require_once 'Zend/XmlRpc/Exception.php';
+                    throw new Zend_XmlRpc_Exception(
+                        'Invalid XML: Detected use of illegal DOCTYPE'
+                    );
+                }
+            }
+            // TODO: Locate why this passes tests but a simplexml import doesn't
+            // $xml = simplexml_import_dom($dom);
             $xml = new SimpleXMLElement($response);
             libxml_disable_entity_loader($loadEntities);
             libxml_use_internal_errors($useInternalXmlErrors);

+ 14 - 0
tests/Zend/Dom/QueryTest.php

@@ -348,6 +348,20 @@ EOB;
         $this->query->setDocument($xhtmlWithXmlDecl, 'utf-8');
         $this->assertEquals(1, $this->query->query('//p')->count());
     }
+
+    public function testLoadingXmlContainingDoctypeShouldFailToPreventXxeAndXeeAttacks()
+    {
+        $xml = <<<XML
+<?xml version="1.0"?>
+<!DOCTYPE results [<!ENTITY harmless "completely harmless">]>
+<results>
+    <result>This result is &harmless;</result>
+</results>
+XML;
+        $this->query->setDocumentXml($xml);
+        $this->setExpectedException("Zend_Dom_Exception");
+        $this->query->queryXpath('/');
+    }
 }
 
 // Call Zend_Dom_QueryTest::main() if this source file is executed directly.

+ 5 - 0
tests/Zend/Feed/Reader/_files/Reader/xxe-atom10.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE results [ <!ENTITY discloseInfo SYSTEM "XXE_URI"> ]>
+<feed xmlns="http://www.w3.org/2005/Atom">
+    <title type="text">info:&discloseInfo;</title>
+</feed>

+ 1 - 0
tests/Zend/Feed/Reader/_files/Reader/xxe-info.txt

@@ -0,0 +1 @@
+xxe-information-disclosed

+ 8 - 0
tests/Zend/Feed/ReaderTest.php

@@ -336,6 +336,14 @@ class Zend_Feed_ReaderTest extends PHPUnit_Framework_TestCase
         $result = Zend_Feed_Reader::import('http://www.example.com');
     }
 
+     public function testXxePreventionOnFeedParsing()
+     {
+         $string = file_get_contents($this->_feedSamplePath.'/Reader/xxe-atom10.xml');
+         $string = str_replace('XXE_URI', $this->_feedSamplePath.'/Reader/xxe-info.txt', $string);
+         $this->setExpectedException('Zend_Feed_Exception');
+         $feed = Zend_Feed_Reader::importString($string);
+     }
+ 
     protected function _getTempDirectory()
     {
         $tmpdir = array();

+ 22 - 0
tests/Zend/Serializer/Adapter/WddxTest.php

@@ -221,6 +221,28 @@ class Zend_Serializer_Adapter_WddxTest extends PHPUnit_Framework_TestCase
         $this->assertEquals($expected, $data);
     }
 
+    public function testUnserializeInvalidXml()
+    {
+        if (!class_exists('SimpleXMLElement', false)) {
+            $this->markTestSkipped('Skipped by missing ext/simplexml');
+        }
+
+        $value = 'not a serialized string';
+        $this->setExpectedException(
+            'Zend_Serializer_Exception',
+            'DOMDocument::loadXML(): Start tag expected'
+        );
+        $this->_adapter->unserialize($value);
+    }
+
+    public function testShouldThrowExceptionIfXmlToUnserializeFromContainsADoctype()
+    {
+        $value    = '<!DOCTYPE>'
+                  . '<wddxPacket version=\'1.0\'><header/>'
+                  . '<data><string>test</string></data></wddxPacket>';
+        $this->setExpectedException("Zend_Serializer_Exception");
+        $data = $this->_adapter->unserialize($value);
+    }
 }
 
 

+ 40 - 1
tests/Zend/Soap/ServerTest.php

@@ -27,6 +27,8 @@ require_once 'Zend/Soap/Server/Exception.php';
 
 require_once "Zend/Config.php";
 
+require_once dirname(__FILE__) . '/TestAsset/commontypes.php';
+
 /**
  * Zend_Soap_Server
  *
@@ -542,6 +544,9 @@ class Zend_Soap_ServerTest extends PHPUnit_Framework_TestCase
         $this->assertEquals(SOAP_PERSISTENCE_SESSION, $server->getPersistence());
     }
 
+    /**
+     * @runInSeparateProcess
+     */
     public function testGetLastRequest()
     {
         if (headers_sent()) {
@@ -575,6 +580,9 @@ class Zend_Soap_ServerTest extends PHPUnit_Framework_TestCase
         $this->assertEquals($request, $server->getLastRequest());
     }
 
+    /**
+     * @runInSeparateProcess
+     */
     public function testWsiCompliant()
     {
         $server = new Zend_Soap_Server(null, array('wsi_compliant' => true));
@@ -877,11 +885,13 @@ class Zend_Soap_ServerTest extends PHPUnit_Framework_TestCase
         }
     }
 
+    /**
+     * @runInSeparateProcess
+     */
     public function testErrorHandlingOfSoapServerChangesToThrowingSoapFaultWhenInHandleMode()
     {
         if (headers_sent()) {
             $this->markTestSkipped('Cannot run ' . __METHOD__ . '() when headers have already been sent; enable output buffering to run this test');
-            return;
         }
 
         $server = new Zend_Soap_Server();
@@ -967,6 +977,35 @@ class Zend_Soap_ServerTest extends PHPUnit_Framework_TestCase
         $r = $server->handle(new DomDocument('1.0', 'UTF-8'));
         $this->assertTrue(is_string($server->mockSoapServer->handle[0]));
     }
+
+    /**
+     * @runInSeparateProcess
+     */
+    public function testShouldThrowExceptionIfHandledRequestContainsDoctype()
+    {
+        $server = new Zend_Soap_Server();
+        $server->setOptions(array('location'=>'test://', 'uri'=>'http://framework.zend.com'));
+        $server->setReturnResponse(true);
+
+        $server->setClass('Zend_Soap_TestAsset_ServerTestClass');
+
+        $request =
+            '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<!DOCTYPE foo>' . "\n"
+          . '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" '
+                             . 'xmlns:ns1="http://framework.zend.com" '
+                             . 'xmlns:xsd="http://www.w3.org/2001/XMLSchema" '
+                             . 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
+                             . 'xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" '
+                             . 'SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'
+          .     '<SOAP-ENV:Body>'
+          .         '<ns1:testFunc2>'
+          .             '<param0 xsi:type="xsd:string">World</param0>'
+          .         '</ns1:testFunc2>'
+          .     '</SOAP-ENV:Body>'
+          . '</SOAP-ENV:Envelope>' . "\n";
+        $response = $server->handle($request);
+        $this->assertContains('Invalid XML', $response->getMessage());
+    }
 }
 
 

+ 634 - 0
tests/Zend/Soap/TestAsset/commontypes.php

@@ -0,0 +1,634 @@
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link      http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd New BSD License
+ * @package   Zend_Soap
+ */
+
+/* Test Functions */
+
+/**
+ * Test Function
+ *
+ * @param string $arg
+ * @return string
+ */
+function Zend_Soap_TestAsset_TestFunc($who)
+{
+    return "Hello $who";
+}
+
+/**
+ * Test Function 2
+ */
+function Zend_Soap_TestAsset_TestFunc2()
+{
+    return "Hello World";
+}
+
+/**
+ * Return false
+ *
+ * @return bool
+ */
+function Zend_Soap_TestAsset_TestFunc3()
+{
+    return false;
+}
+
+/**
+ * Return true
+ *
+ * @return bool
+ */
+function Zend_Soap_TestAsset_TestFunc4()
+{
+    return true;
+}
+
+/**
+ * Return integer
+ *
+ * @return int
+ */
+function Zend_Soap_TestAsset_TestFunc5()
+{
+    return 123;
+}
+
+/**
+ * Return string
+ *
+ * @return string
+ */
+function Zend_Soap_TestAsset_TestFunc6()
+{
+    return "string";
+}
+
+/**
+ * Return array
+ *
+ * @return array
+ */
+function Zend_Soap_TestAsset_TestFunc7()
+{
+    return array('foo' => 'bar', 'baz' => true, 1 => false, 'bat' => 123);
+}
+
+/**
+ * Return Object
+ *
+ * @return StdClass
+ */
+function Zend_Soap_TestAsset_TestFunc8()
+{
+    $return = (object) array('foo' => 'bar', 'baz' => true, 'bat' => 123, 'qux' => false);
+    return $return;
+}
+
+/**
+ * Multiple Args
+ *
+ * @param string $foo
+ * @param string $bar
+ * @return string
+ */
+function Zend_Soap_TestAsset_TestFunc9($foo, $bar)
+{
+    return "$foo $bar";
+}
+
+/**
+ * @category   Zend
+ * @package    Zend_Soap
+ * @subpackage UnitTests
+ */
+class Zend_Soap_TestAsset_TestFixingMultiplePrototypes
+{
+    /**
+     * Test function
+     *
+     * @param integer $a
+     * @param integer $b
+     * @param integer $d
+     * @return integer
+     */
+    public function testFunc($a=100, $b=200, $d=300)
+    {
+
+    }
+}
+
+/**
+ * @category   Zend
+ * @package    Zend_Soap
+ * @subpackage UnitTests
+ */
+class Zend_Soap_TestAsset_Test
+{
+    /**
+     * Test Function 1
+     *
+     * @return string
+     */
+    public function testFunc1()
+    {
+        return "Hello World";
+    }
+
+    /**
+     * Test Function 2
+     *
+     * @param string $who Some Arg
+     * @return string
+     */
+    public function testFunc2($who)
+    {
+        return "Hello $who!";
+    }
+
+    /**
+     * Test Function 3
+     *
+     * @param string $who Some Arg
+     * @param int $when Some
+     * @return string
+     */
+    public function testFunc3($who, $when)
+    {
+        return "Hello $who, How are you $when";
+    }
+
+    /**
+     * Test Function 4
+     *
+     * @return string
+     */
+    public static function testFunc4()
+    {
+        return "I'm Static!";
+    }
+}
+
+class Zend_Soap_TestAsset_AutoDiscoverTestClass1
+{
+    /**
+     * @var integer $var
+     */
+    public $var = 1;
+
+    /**
+     * @var string $param
+     */
+    public $param = "hello";
+}
+
+/**
+ * @category   Zend
+ * @package    Zend_Soap
+ * @subpackage UnitTests
+ */
+class Zend_Soap_TestAsset_AutoDiscoverTestClass2
+{
+    /**
+     *
+     * @param Zend_Soap_TestAsset_AutoDiscoverTestClass1 $test
+     * @return boolean
+     */
+    public function add(AutoDiscoverTestClass1 $test)
+    {
+        return true;
+    }
+
+    /**
+     * @return Zend_Soap_TestAsset_AutoDiscoverTestClass1[]
+     */
+    public function fetchAll()
+    {
+        return array(
+            new AutoDiscoverTestClass1(),
+            new AutoDiscoverTestClass1(),
+        );
+    }
+
+    /**
+     * @param Zend_Soap_TestAsset_AutoDiscoverTestClass1[]
+     */
+    public function addMultiple($test)
+    {
+
+    }
+}
+
+/**
+ * @category   Zend
+ * @package    Zend_Soap
+ * @subpackage UnitTests
+ */
+class Zend_Soap_TestAsset_ComplexTypeB
+{
+    /**
+     * @var string
+     */
+    public $bar;
+    /**
+     * @var string
+     */
+    public $foo;
+}
+
+/**
+ * @category   Zend
+ * @package    Zend_Soap
+ * @subpackage UnitTests
+ */
+class Zend_Soap_TestAsset_ComplexTypeA
+{
+    /**
+     * @var Zend_Soap_TestAsset_ComplexTypeB[]
+     */
+    public $baz = array();
+}
+
+/**
+ * @category   Zend
+ * @package    Zend_Soap
+ * @subpackage UnitTests
+ */
+class Zend_Soap_TestAsset_ComplexTest
+{
+    /**
+     * @var int
+     */
+    public $var = 5;
+}
+
+/**
+ * @category   Zend
+ * @package    Zend_Soap
+ * @subpackage UnitTests
+ */
+class Zend_Soap_TestAsset_ComplexObjectStructure
+{
+    /**
+     * @var boolean
+     */
+    public $boolean = true;
+
+    /**
+     * @var string
+     */
+    public $string = "Hello World";
+
+    /**
+     * @var int
+     */
+    public $int = 10;
+
+    /**
+     * @var array
+     */
+    public $array = array(1, 2, 3);
+}
+
+/**
+ * @category   Zend
+ * @package    Zend_Soap
+ * @subpackage UnitTests
+ */
+class Zend_Soap_TestAsset_ComplexObjectWithObjectStructure
+{
+    /**
+     * @var Zend_Soap_TestAsset_ComplexTest
+     */
+    public $object;
+}
+
+/**
+ * @category   Zend
+ * @package    Zend_Soap
+ * @subpackage UnitTests
+ */
+class Zend_Soap_TestAsset_MyService
+{
+    /**
+     *    @param string $foo
+     *    @return Zend_Soap_TestAsset_MyResponse[]
+     */
+    public function foo($foo)
+    {
+    }
+    /**
+     *    @param string $bar
+     *    @return Zend_Soap_TestAsset_MyResponse[]
+     */
+    public function bar($bar)
+    {
+    }
+
+    /**
+     *    @param string $baz
+     *    @return Zend_Soap_TestAsset_MyResponse[]
+     */
+    public function baz($baz)
+    {
+    }
+}
+
+/**
+ * @category   Zend
+ * @package    Zend_Soap
+ * @subpackage UnitTests
+ */
+class Zend_Soap_TestAsset_MyServiceSequence
+{
+    /**
+     *    @param string $foo
+     *    @return string[]
+     */
+    public function foo($foo)
+    {
+    }
+    /**
+     *    @param string $bar
+     *    @return string[]
+     */
+    public function bar($bar)
+    {
+    }
+
+    /**
+     *    @param string $baz
+     *    @return string[]
+     */
+    public function baz($baz)
+    {
+    }
+
+    /**
+     *    @param string $baz
+     *    @return string[][][]
+     */
+    public function bazNested($baz)
+    {
+    }
+}
+
+/**
+ * @category   Zend
+ * @package    Zend_Soap
+ * @subpackage UnitTests
+ */
+class Zend_Soap_TestAsset_MyResponse
+{
+    /**
+     * @var string
+     */
+    public $p1;
+}
+
+/**
+ * @category   Zend
+ * @package    Zend_Soap
+ * @subpackage UnitTests
+ */
+class Zend_Soap_TestAsset_Recursion
+{
+    /**
+     * @var Zend_Soap_TestAsset_Recursion
+     */
+    public $recursion;
+
+    /**
+     * @return Zend_Soap_TestAsset_Recursion
+     */
+    public function create() {}
+}
+
+/**
+ * @param string $message
+ */
+function Zend_Soap_TestAsset_OneWay($message)
+{
+
+}
+
+/**
+ * @category   Zend
+ * @package    Zend_Soap
+ * @subpackage UnitTests
+ */
+class Zend_Soap_TestAsset_NoReturnType
+{
+    /**
+     *
+     * @param string $message
+     */
+    public function pushOneWay($message)
+    {
+
+    }
+}
+
+/* Client test classes */
+/** Test Class */
+class Zend_Soap_TestAsset_TestClass
+{
+    /**
+     * Test Function 1
+     *
+     * @return string
+     */
+    public function testFunc1()
+    {
+        return "Hello World";
+    }
+
+    /**
+     * Test Function 2
+     *
+     * @param string $who Some Arg
+     * @return string
+     */
+    public function testFunc2($who)
+    {
+        return "Hello $who!";
+    }
+
+    /**
+     * Test Function 3
+     *
+     * @param string $who Some Arg
+     * @param int $when Some
+     * @return string
+     */
+    public function testFunc3($who, $when)
+    {
+        return "Hello $who, How are you $when";
+    }
+
+    /**
+     * Test Function 4
+     *
+     * @return string
+     */
+    public static function testFunc4()
+    {
+        return "I'm Static!";
+    }
+}
+
+/** Test class 2 */
+class Zend_Soap_TestAsset_TestData1
+{
+    /**
+     * Property1
+     *
+     * @var string
+     */
+     public $property1;
+
+    /**
+     * Property2
+     *
+     * @var float
+     */
+     public $property2;
+}
+
+/** Test class 2 */
+class Zend_Soap_TestAsset_TestData2
+{
+    /**
+     * Property1
+     *
+     * @var integer
+     */
+     public $property1;
+
+    /**
+     * Property1
+     *
+     * @var float
+     */
+     public $property2;
+}
+
+class Zend_Soap_TestAsset_MockSoapServer
+{
+    public $handle = null;
+    public function handle()
+    {
+        $this->handle = func_get_args();
+    }
+    public function __call($name, $args) {}
+}
+
+class Zend_Soap_TestAsset_MockServer extends Zend_Soap_Server
+{
+    public $mockSoapServer = null;
+    protected function _getSoap()
+    {
+        $this->mockSoapServer = new MockSoapServer();
+        return $this->mockSoapServer;
+    }
+}
+
+
+/** Server test classes */
+class Zend_Soap_TestAsset_ServerTestClass
+{
+    /**
+     * Test Function 1
+     *
+     * @return string
+     */
+    public function testFunc1()
+    {
+        return "Hello World";
+    }
+
+    /**
+     * Test Function 2
+     *
+     * @param string $who Some Arg
+     * @return string
+     */
+    public function testFunc2($who)
+    {
+        return "Hello $who!";
+    }
+
+    /**
+     * Test Function 3
+     *
+     * @param string $who Some Arg
+     * @param int $when Some
+     * @return string
+     */
+    public function testFunc3($who, $when)
+    {
+        return "Hello $who, How are you $when";
+    }
+
+    /**
+     * Test Function 4
+     *
+     * @return string
+     */
+    public static function testFunc4()
+    {
+        return "I'm Static!";
+    }
+
+    /**
+     * Test Function 5 raises a user error
+     *
+     * @return void
+     */
+    public function testFunc5()
+    {
+        trigger_error("Test Message", E_USER_ERROR);
+    }
+}
+
+if (extension_loaded('soap')) {
+
+/** Local SOAP client */
+class Zend_Soap_TestAsset_TestLocalSoapClient extends SoapClient
+{
+    /**
+     * Server object
+     *
+     * @var Zend_Soap_Server
+     */
+    public $server;
+
+    /**
+     * Local client constructor
+     *
+     * @param Zend_Soap_Server $server
+     * @param string $wsdl
+     * @param array $options
+     */
+    public function __construct(Zend_Soap_Server $server, $wsdl, $options)
+    {
+        $this->server = $server;
+        parent::__construct($wsdl, $options);
+    }
+
+    public function __doRequest($request, $location, $action, $version, $one_way = 0)
+    {
+        ob_start();
+        $this->server->handle($request);
+        $response = ob_get_clean();
+
+        return $response;
+    }
+}
+
+}

+ 10 - 0
tests/Zend/XmlRpc/RequestTest.php

@@ -352,6 +352,9 @@ class Zend_XmlRpc_RequestTest extends PHPUnit_Framework_TestCase
 
     /**
      * @group ZF-12293
++     *
++     * Test should remain, but is defunct since DOCTYPE presence should return FALSE
++     * from loadXml()
      */
     public function testDoesNotAllowExternalEntities()
     {
@@ -364,4 +367,11 @@ class Zend_XmlRpc_RequestTest extends PHPUnit_Framework_TestCase
             $this->assertNotContains('Local file inclusion', $method);
         }
     }
+
+     public function testShouldDisallowsDoctypeInRequestXmlAndReturnFalseOnLoading()
+     {
+         $payload = file_get_contents(dirname(__FILE__) . '/_files/ZF12293-request.xml');
+         $payload = sprintf($payload, 'file://' . realpath(dirname(__FILE__) . '/_files/ZF12293-payload.txt'));
+         $this->assertFalse($this->_request->loadXml($payload));
+     }
 }

+ 7 - 0
tests/Zend/XmlRpc/ResponseTest.php

@@ -267,4 +267,11 @@ EOD;
             $this->assertNotContains('Local file inclusion', $value);
         }
     }
+
+     public function testShouldDisallowsDoctypeInRequestXmlAndReturnFalseOnLoading()
+     {
+         $payload = file_get_contents(dirname(__FILE__) . '/_files/ZF12293-response.xml');
+         $payload = sprintf($payload, 'file://' . realpath(dirname(__FILE__) . '/_files/ZF12293-payload.txt'));
+         $this->assertFalse($this->_response->loadXml($payload));
+     }
 }