summaryrefslogtreecommitdiffstats
path: root/vendor/ZendXml/library/ZendXml/Security.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/ZendXml/library/ZendXml/Security.php')
-rw-r--r--vendor/ZendXml/library/ZendXml/Security.php143
1 files changed, 143 insertions, 0 deletions
diff --git a/vendor/ZendXml/library/ZendXml/Security.php b/vendor/ZendXml/library/ZendXml/Security.php
new file mode 100644
index 000000000..d258311f4
--- /dev/null
+++ b/vendor/ZendXml/library/ZendXml/Security.php
@@ -0,0 +1,143 @@
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+namespace ZendXml;
+
+use DOMDocument;
+use SimpleXMLElement;
+
+class Security
+{
+ const ENTITY_DETECT = 'Detected use of ENTITY in XML, disabled to prevent XXE/XEE attacks';
+
+ /**
+ * Heuristic scan to detect entity in XML
+ *
+ * @param string $xml
+ * @throws Exception\RuntimeException
+ */
+ protected static function heuristicScan($xml)
+ {
+ if (strpos($xml, '<!ENTITY') !== false) {
+ throw new Exception\RuntimeException(self::ENTITY_DETECT);
+ }
+ }
+
+ /**
+ * Scan XML string for potential XXE and XEE attacks
+ *
+ * @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); }
+ * @throws Exception\RuntimeException
+ * @return SimpleXMLElement|DomDocument|boolean
+ */
+ public static function scan($xml, DOMDocument $dom = null,
+ $loadCallback = null)
+ {
+ // If running with PHP-FPM we perform an heuristic scan
+ // We cannot use libxml_disable_entity_loader because of this bug
+ // @see https://bugs.php.net/bug.php?id=64938
+ if (self::isPhpFpm()) {
+ self::heuristicScan($xml);
+ }
+
+ if (null === $dom) {
+ $simpleXml = true;
+ $dom = new DOMDocument();
+ }
+
+ if (!self::isPhpFpm()) {
+ $loadEntities = libxml_disable_entity_loader(true);
+ $useInternalXmlErrors = libxml_use_internal_errors(true);
+ }
+
+ // Load XML with network access disabled (LIBXML_NONET)
+ // error disabled with @ for PHP-FPM scenario
+ set_error_handler(function ($errno, $errstr) {
+ if (substr_count($errstr, 'DOMDocument::loadXML()') > 0) {
+ return true;
+ }
+ return false;
+ }, E_WARNING);
+
+ if ($loadCallback) {
+ $result = $loadCallback($xml, $dom);
+ } else {
+ $result = $dom->loadXml($xml, LIBXML_NONET);
+ }
+
+ restore_error_handler();
+
+ // Entity load to previous setting
+ if (!self::isPhpFpm()) {
+ libxml_disable_entity_loader($loadEntities);
+ libxml_use_internal_errors($useInternalXmlErrors);
+ }
+
+ if (!$result) {
+ return false;
+ }
+
+ // Scan for potential XEE attacks using ENTITY, if not PHP-FPM
+ if (!self::isPhpFpm()) {
+ foreach ($dom->childNodes as $child) {
+ if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
+ if ($child->entities->length > 0) {
+ throw new Exception\RuntimeException(self::ENTITY_DETECT);
+ }
+ }
+ }
+ }
+
+ if (isset($simpleXml)) {
+ $result = simplexml_import_dom($dom);
+ if (!$result instanceof SimpleXMLElement) {
+ return false;
+ }
+ return $result;
+ }
+ return $dom;
+ }
+
+ /**
+ * Scan XML file for potential XXE/XEE attacks
+ *
+ * @param string $file
+ * @param DOMDocument $dom
+ * @throws Exception\InvalidArgumentException
+ * @return SimpleXMLElement|DomDocument
+ */
+ public static function scanFile($file, DOMDocument $dom = null)
+ {
+ if (!file_exists($file)) {
+ throw new Exception\InvalidArgumentException(
+ "The file $file specified doesn't exist"
+ );
+ }
+ return self::scan(file_get_contents($file), $dom);
+ }
+
+ /**
+ * Return true if PHP is running with PHP-FPM
+ *
+ * @return boolean
+ */
+ public static function isPhpFpm()
+ {
+ if (substr(php_sapi_name(), 0, 3) === 'fpm') {
+ return true;
+ }
+ return false;
+ }
+}