From 886647d1caf7181e947a2e771600d1addfaf53ac Mon Sep 17 00:00:00 2001 From: Bernhard Posselt Date: Mon, 24 Nov 2014 12:23:38 +0100 Subject: update zendxml --- articleenhancer/globalarticleenhancer.php | 7 +- articleenhancer/xpatharticleenhancer.php | 20 +-- vendor/ZendXml/.gitignore | 5 + vendor/ZendXml/.travis.yml | 29 ++++ vendor/ZendXml/README.md | 50 +++++++ vendor/ZendXml/composer.json | 11 +- .../ZendXml/Exception/InvalidArgumentException.php | 4 +- .../library/ZendXml/Exception/RuntimeException.php | 4 +- vendor/ZendXml/library/ZendXml/Security.php | 51 +++++-- vendor/ZendXml/tests/Bootstrap.php | 92 +++++++++++++ vendor/ZendXml/tests/ZendXmlTest/SecurityTest.php | 152 +++++++++++++++++++++ vendor/ZendXml/tests/phpunit.xml.dist | 27 ++++ vendor/ZendXml/vendor/autoload.php | 2 +- .../vendor/composer/autoload_namespaces.php | 2 +- vendor/ZendXml/vendor/composer/autoload_psr4.php | 1 + vendor/ZendXml/vendor/composer/autoload_real.php | 8 +- 16 files changed, 423 insertions(+), 42 deletions(-) create mode 100644 vendor/ZendXml/.gitignore create mode 100644 vendor/ZendXml/.travis.yml create mode 100644 vendor/ZendXml/README.md create mode 100644 vendor/ZendXml/tests/Bootstrap.php create mode 100644 vendor/ZendXml/tests/ZendXmlTest/SecurityTest.php create mode 100755 vendor/ZendXml/tests/phpunit.xml.dist diff --git a/articleenhancer/globalarticleenhancer.php b/articleenhancer/globalarticleenhancer.php index 4c01a238c..1939ca41c 100644 --- a/articleenhancer/globalarticleenhancer.php +++ b/articleenhancer/globalarticleenhancer.php @@ -32,10 +32,9 @@ class GlobalArticleEnhancer implements ArticleEnhancer { // inside

tags $body = '

' . $item->getBody() . '
'; - Security::scan($body, $dom, function ($xml, $dom) { - return @$dom->loadHTML($xml, LIBXML_HTML_NOIMPLIED - | LIBXML_HTML_NODEFDTD | LIBXML_NONET); - }); + $isOk = Security::scanHtml( + $body, $dom, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD + ); $xpath = new \DOMXpath($dom); diff --git a/articleenhancer/xpatharticleenhancer.php b/articleenhancer/xpatharticleenhancer.php index 871752856..aa94eec54 100644 --- a/articleenhancer/xpatharticleenhancer.php +++ b/articleenhancer/xpatharticleenhancer.php @@ -70,9 +70,7 @@ class XPathArticleEnhancer implements ArticleEnhancer { $dom = new DOMDocument(); - Security::scan($body, $dom, function ($xml, $dom) { - return @$dom->loadHTML($xml, LIBXML_NONET); - }); + $isOk = Security::scanHtml($body, $dom); $xpath = new DOMXpath($dom); $xpathResult = $xpath->evaluate($search); @@ -121,14 +119,16 @@ class XPathArticleEnhancer implements ArticleEnhancer { $dom = new DOMDocument(); $dom->preserveWhiteSpace = false; - $isOk = Security::scan($xmlString, $dom, function ($xml, $dom) { - // wrap in div to prevent loadHTML from inserting weird elements - $xml = '
' . $xml . '
'; - return @$dom->loadHTML($xml, LIBXML_NONET | LIBXML_HTML_NODEFDTD - | LIBXML_HTML_NOIMPLIED); - }); + if($xmlString === '') { + return false; + } + + $xmlString = '
' . $xmlString . '
'; + $isOk = Security::scanHtml( + $xmlString, $dom, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD + ); - if($xmlString === '' || !$isOk) { + if(!$isOk) { return false; } diff --git a/vendor/ZendXml/.gitignore b/vendor/ZendXml/.gitignore new file mode 100644 index 000000000..0a4f6e27f --- /dev/null +++ b/vendor/ZendXml/.gitignore @@ -0,0 +1,5 @@ +composer.lock +vendor +.buildpath +.project +.settings diff --git a/vendor/ZendXml/.travis.yml b/vendor/ZendXml/.travis.yml new file mode 100644 index 000000000..ad8db966f --- /dev/null +++ b/vendor/ZendXml/.travis.yml @@ -0,0 +1,29 @@ +language: php +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +matrix: + allow_failures: + - php: hhvm + +before_install: + # need to update libxml to 2.7.8 to be able to run tests using the + # LIBXML_HTML_NODEFDTD and LIBXML_HTML_NOIMPLIED libxml constant + - sudo apt-get update + - sudo apt-get -o DPkg::Options::="--force-confold" -y upgrade + +before_script: + - composer self-update + - composer install --dev + +script: + - ./vendor/bin/phpunit -c ./tests + - ./vendor/bin/phpcs --standard=PSR2 --ignore=tests/Bootstrap.php library tests + +notifications: + irc: "irc.freenode.org#zftalk.dev" + email: false diff --git a/vendor/ZendXml/README.md b/vendor/ZendXml/README.md new file mode 100644 index 000000000..2c67008da --- /dev/null +++ b/vendor/ZendXml/README.md @@ -0,0 +1,50 @@ +ZendXml +======= + +An utility component for XML usage and best practices in PHP + +Installation +------------ + +You can install using: + +``` +curl -s https://getcomposer.org/installer | php +php composer.phar install +``` + +Notice that this library doesn't have any external dependencies, the usage of composer is for autoloading and standard purpose. + + +ZendXml\Security +---------------- + +This is a security component to prevent [XML eXternal Entity](https://www.owasp.org/index.php/XML_External_Entity_%28XXE%29_Processing) (XXE) and [XML Entity Expansion](http://projects.webappsec.org/w/page/13247002/XML%20Entity%20Expansion) (XEE) attacks on XML documents. + +The XXE attack is prevented disabling the load of external entities in the libxml library used by PHP, using the function [libxml_disable_entity_loader](http://www.php.net/manual/en/function.libxml-disable-entity-loader.php). + +The XEE attack is prevented looking inside the XML document for ENTITY usage. If the XML document uses ENTITY the library throw an Exception. + +We have two static methods to scan and load XML document from a string (scan) and from a file (scanFile). You can decide to get a SimpleXMLElement or DOMDocument as result, using the following use cases: + +```php +use ZendXml\Security as XmlSecurity; + +$xml = << + + test + +XML; + +// SimpleXML use case +$simplexml = XmlSecurity::scan($xml); +printf ("SimpleXMLElement: %s\n", ($simplexml instanceof \SimpleXMLElement) ? 'yes' : 'no'); + +// DOMDocument use case +$dom = new \DOMDocument('1.0'); +$dom = XmlSecurity::scan($xml, $dom); +printf ("DOMDocument: %s\n", ($dom instanceof \DOMDocument) ? 'yes' : 'no'); +``` + + diff --git a/vendor/ZendXml/composer.json b/vendor/ZendXml/composer.json index 3b4ca91de..139f1e23b 100644 --- a/vendor/ZendXml/composer.json +++ b/vendor/ZendXml/composer.json @@ -11,7 +11,12 @@ "homepage": "http://packages.zendframework.com/", "autoload": { "psr-0": { - "ZendXml": "library/" + "ZendXml\\": "library/" + } + }, + "autoload-dev": { + "psr-4": { + "ZendTest\\Xml\\": "tests/ZendXmlTest/" } }, "repositories": [ @@ -29,7 +34,7 @@ } }, "require-dev": { - "fabpot/php-cs-fixer": "*@dev", - "phpunit/phpunit": "~3.7" + "phpunit/phpunit": "~3.7", + "squizlabs/php_codesniffer": "~1.5" } } diff --git a/vendor/ZendXml/library/ZendXml/Exception/InvalidArgumentException.php b/vendor/ZendXml/library/ZendXml/Exception/InvalidArgumentException.php index 819fb9f6e..0fef6b298 100644 --- a/vendor/ZendXml/library/ZendXml/Exception/InvalidArgumentException.php +++ b/vendor/ZendXml/library/ZendXml/Exception/InvalidArgumentException.php @@ -12,8 +12,6 @@ namespace ZendXml\Exception; /** * Invalid argument exception */ -class InvalidArgumentException - extends \InvalidArgumentException - implements ExceptionInterface +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface { } diff --git a/vendor/ZendXml/library/ZendXml/Exception/RuntimeException.php b/vendor/ZendXml/library/ZendXml/Exception/RuntimeException.php index 1d5f50625..b730da4ff 100644 --- a/vendor/ZendXml/library/ZendXml/Exception/RuntimeException.php +++ b/vendor/ZendXml/library/ZendXml/Exception/RuntimeException.php @@ -12,8 +12,6 @@ namespace ZendXml\Exception; /** * Runtime exception */ -class RuntimeException - extends \RuntimeException - implements ExceptionInterface +class RuntimeException extends \RuntimeException implements ExceptionInterface { } diff --git a/vendor/ZendXml/library/ZendXml/Security.php b/vendor/ZendXml/library/ZendXml/Security.php index d258311f4..e97a54d77 100644 --- a/vendor/ZendXml/library/ZendXml/Security.php +++ b/vendor/ZendXml/library/ZendXml/Security.php @@ -33,17 +33,12 @@ class Security * * @param string $xml * @param DomDocument $dom - * @param Callable( - * @param $xml - * @param $dom - * @return DomDocument|boolean - * ) $loadCallback if given allows to customize the load command e.g.: - * function ($xml, $dom) { return $dom->loadHTML($xml, LIBXML_NONET); } + * @param int $libXmlConstants additional libxml constants to pass in + * @param Callable $callback the callback to use to create the dom element * @throws Exception\RuntimeException * @return SimpleXMLElement|DomDocument|boolean */ - public static function scan($xml, DOMDocument $dom = null, - $loadCallback = null) + private static function scanString($xml, $dom, $libXmlConstants, $callback) { // If running with PHP-FPM we perform an heuristic scan // We cannot use libxml_disable_entity_loader because of this bug @@ -71,11 +66,7 @@ class Security return false; }, E_WARNING); - if ($loadCallback) { - $result = $loadCallback($xml, $dom); - } else { - $result = $dom->loadXml($xml, LIBXML_NONET); - } + $result = $callback($xml, $dom, LIBXML_NONET | $libXmlConstants); restore_error_handler(); @@ -110,6 +101,40 @@ class Security return $dom; } + /** + * Scan HTML string for potential XXE and XEE attacks + * + * @param string $xml + * @param DomDocument $dom + * @param int $libXmlConstants additional libxml constants to pass in + * @throws Exception\RuntimeException + * @return SimpleXMLElement|DomDocument|boolean + */ + public static function scanHtml($html, DOMDocument $dom = null, $libXmlConstants = 0) + { + $callback = function ($html, $dom, $constants) { + return $dom->loadHtml($html, $constants); + }; + return self::scanString($html, $dom, $libXmlConstants, $callback); + } + + /** + * Scan XML string for potential XXE and XEE attacks + * + * @param string $xml + * @param DomDocument $dom + * @param int $libXmlConstants additional libxml constants to pass in + * @throws Exception\RuntimeException + * @return SimpleXMLElement|DomDocument|boolean + */ + public static function scan($xml, DOMDocument $dom = null, $libXmlConstants = 0) + { + $callback = function ($xml, $dom, $constants) { + return $dom->loadXml($xml, $constants); + }; + return self::scanString($xml, $dom, $libXmlConstants, $callback); + } + /** * Scan XML file for potential XXE/XEE attacks * diff --git a/vendor/ZendXml/tests/Bootstrap.php b/vendor/ZendXml/tests/Bootstrap.php new file mode 100644 index 000000000..a9d0e6a55 --- /dev/null +++ b/vendor/ZendXml/tests/Bootstrap.php @@ -0,0 +1,92 @@ +addDirectoryToWhitelist($zfCoreLibrary . '/' . $lastArg); + } elseif (is_file($zfCoreTests . '/' . $lastArg)) { + $codeCoverageFilter->addDirectoryToWhitelist(dirname($zfCoreLibrary . '/' . $lastArg)); + } else { + $codeCoverageFilter->addDirectoryToWhitelist($zfCoreLibrary); + } + + /* + * Omit from code coverage reports the contents of the tests directory + */ + $codeCoverageFilter->addDirectoryToBlacklist($zfCoreTests, ''); + $codeCoverageFilter->addDirectoryToBlacklist(PEAR_INSTALL_DIR, ''); + $codeCoverageFilter->addDirectoryToBlacklist(PHP_LIBDIR, ''); + + unset($codeCoverageFilter); +} + +/* + * Unset global variables that are no longer needed. + */ +unset($phpUnitVersion); diff --git a/vendor/ZendXml/tests/ZendXmlTest/SecurityTest.php b/vendor/ZendXml/tests/ZendXmlTest/SecurityTest.php new file mode 100644 index 000000000..0f0fbffba --- /dev/null +++ b/vendor/ZendXml/tests/ZendXmlTest/SecurityTest.php @@ -0,0 +1,152 @@ + +]> + + This result is &harmless; + +XML; + + $this->setExpectedException('ZendXml\Exception\RuntimeException'); + $result = XmlSecurity::scan($xml); + } + + public function testScanForXXE() + { + $file = tempnam(sys_get_temp_dir(), 'ZendXml_Security'); + file_put_contents($file, 'This is a remote content!'); + $xml = << + +]> + + &foo; + +XML; + + try { + $result = XmlSecurity::scan($xml); + } catch (Exception\RuntimeException $e) { + unlink($file); + return; + } + $this->fail('An expected exception has not been raised.'); + } + + public function testScanSimpleXmlResult() + { + $result = XmlSecurity::scan($this->getXml()); + $this->assertTrue($result instanceof SimpleXMLElement); + $this->assertEquals($result->result, 'test'); + } + + public function testScanDom() + { + $dom = new DOMDocument('1.0'); + $result = XmlSecurity::scan($this->getXml(), $dom); + $this->assertTrue($result instanceof DOMDocument); + $node = $result->getElementsByTagName('result')->item(0); + $this->assertEquals($node->nodeValue, 'test'); + } + + /** + * @requires PHP 5.4 + */ + public function testScanDomHTML() + { + // loadHtml accepts constants in php >= 5.4 + // http://php.net/manual/de/domdocument.loadhtml.php + $dom = new DOMDocument('1.0'); + $html = <<a simple test

+HTML; + $constants = LIBXML_HTML_NODEFDTD | LIBXML_HTML_NOIMPLIED; + $result = XmlSecurity::scanHtml($html, $dom, $constants); + $this->assertTrue($result instanceof DOMDocument); + $this->assertEquals($html, trim($result->saveHtml())); + } + + public function testScanInvalidXml() + { + $xml = <<test +XML; + + $result = XmlSecurity::scan($xml); + $this->assertFalse($result); + } + + public function testScanInvalidXmlDom() + { + $xml = <<test +XML; + + $dom = new DOMDocument('1.0'); + $result = XmlSecurity::scan($xml, $dom); + $this->assertFalse($result); + } + + public function testScanFile() + { + $file = tempnam(sys_get_temp_dir(), 'ZendXml_Security'); + file_put_contents($file, $this->getXml()); + + $result = XmlSecurity::scanFile($file); + $this->assertTrue($result instanceof SimpleXMLElement); + $this->assertEquals($result->result, 'test'); + unlink($file); + } + + public function testScanXmlWithDTD() + { + $xml = << + + +]> + + test + +XML; + + $dom = new DOMDocument('1.0'); + $result = XmlSecurity::scan($xml, $dom); + $this->assertTrue($result instanceof DOMDocument); + $this->assertTrue($result->validate()); + } + + protected function getXml() + { + return << + + test + +XML; + } +} diff --git a/vendor/ZendXml/tests/phpunit.xml.dist b/vendor/ZendXml/tests/phpunit.xml.dist new file mode 100755 index 000000000..069784bd7 --- /dev/null +++ b/vendor/ZendXml/tests/phpunit.xml.dist @@ -0,0 +1,27 @@ + + + + ./ZendXmlTest + ./ZendXmlTest/TestAsset + + + + + + + + + + + + + + ./ZendXmlTest + ../vendor + + + + + + + diff --git a/vendor/ZendXml/vendor/autoload.php b/vendor/ZendXml/vendor/autoload.php index cc69a96d3..ec43ad18d 100644 --- a/vendor/ZendXml/vendor/autoload.php +++ b/vendor/ZendXml/vendor/autoload.php @@ -4,4 +4,4 @@ require_once __DIR__ . '/composer' . '/autoload_real.php'; -return ComposerAutoloaderInit44f71f876fa818738e1bb91ba3f97956::getLoader(); +return ComposerAutoloaderInitea88c51e6ab35b96029638db6e4797bb::getLoader(); diff --git a/vendor/ZendXml/vendor/composer/autoload_namespaces.php b/vendor/ZendXml/vendor/composer/autoload_namespaces.php index 22f78f2ff..821332c40 100644 --- a/vendor/ZendXml/vendor/composer/autoload_namespaces.php +++ b/vendor/ZendXml/vendor/composer/autoload_namespaces.php @@ -6,5 +6,5 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( - 'ZendXml' => array($baseDir . '/library'), + 'ZendXml\\' => array($baseDir . '/library'), ); diff --git a/vendor/ZendXml/vendor/composer/autoload_psr4.php b/vendor/ZendXml/vendor/composer/autoload_psr4.php index b265c64a2..9c321972e 100644 --- a/vendor/ZendXml/vendor/composer/autoload_psr4.php +++ b/vendor/ZendXml/vendor/composer/autoload_psr4.php @@ -6,4 +6,5 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'ZendTest\\Xml\\' => array($baseDir . '/tests/ZendXmlTest'), ); diff --git a/vendor/ZendXml/vendor/composer/autoload_real.php b/vendor/ZendXml/vendor/composer/autoload_real.php index d33fba17e..b881772be 100644 --- a/vendor/ZendXml/vendor/composer/autoload_real.php +++ b/vendor/ZendXml/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit44f71f876fa818738e1bb91ba3f97956 +class ComposerAutoloaderInitea88c51e6ab35b96029638db6e4797bb { private static $loader; @@ -19,9 +19,9 @@ class ComposerAutoloaderInit44f71f876fa818738e1bb91ba3f97956 return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit44f71f876fa818738e1bb91ba3f97956', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInitea88c51e6ab35b96029638db6e4797bb', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit44f71f876fa818738e1bb91ba3f97956', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInitea88c51e6ab35b96029638db6e4797bb', 'loadClassLoader')); $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { @@ -44,7 +44,7 @@ class ComposerAutoloaderInit44f71f876fa818738e1bb91ba3f97956 } } -function composerRequire44f71f876fa818738e1bb91ba3f97956($file) +function composerRequireea88c51e6ab35b96029638db6e4797bb($file) { require $file; } -- cgit v1.2.3