diff --git a/_config/dev.yml b/_config/dev.yml index ca74c082f..4c1636bc4 100644 --- a/_config/dev.yml +++ b/_config/dev.yml @@ -17,3 +17,6 @@ SilverStripe\Dev\DevelopmentAdmin: controller: Silverstripe\Dev\DevConfigController links: config: 'View the current config, useful for debugging' + +SilverStripe\Dev\CSSContentParser: + disable_xml_external_entities: true diff --git a/src/Dev/CSSContentParser.php b/src/Dev/CSSContentParser.php index 3ebd9576b..c07f73785 100644 --- a/src/Dev/CSSContentParser.php +++ b/src/Dev/CSSContentParser.php @@ -2,6 +2,7 @@ namespace SilverStripe\Dev; +use SilverStripe\Core\Config\Configurable; use SilverStripe\Core\Injector\Injectable; use SimpleXMLElement; use tidy; @@ -25,6 +26,7 @@ use Exception; class CSSContentParser { use Injectable; + use Configurable; protected $simpleXML = null; @@ -56,6 +58,13 @@ class CSSContentParser $tidy = $content; } + // Prevent loading of external entities to prevent XXE attacks + // Note: as of libxml 2.9.0 entity substitution is disabled by default so this won't be required + if ($this->config()->get('disable_xml_external_entities')) { + libxml_set_external_entity_loader(function () { + return null; + }); + } $this->simpleXML = @simplexml_load_string($tidy, 'SimpleXMLElement', LIBXML_NOWARNING); if (!$this->simpleXML) { throw new Exception('CSSContentParser::__construct(): Could not parse content.' diff --git a/tests/php/Dev/CSSContentParserTest.php b/tests/php/Dev/CSSContentParserTest.php index 5377e7865..a9551abac 100644 --- a/tests/php/Dev/CSSContentParserTest.php +++ b/tests/php/Dev/CSSContentParserTest.php @@ -50,4 +50,14 @@ HTML $result = $parser->getBySelector('#A .other'); $this->assertEquals("result", $result[0] . ''); } + + public function testXmlEntitiesDisabled() + { + // CSSContentParser uses simplexml to parse html + // Ensure XML entities are not substituted in to prevent XXE attacks + $xml = ']>
Hello &myentity;
'; + $parser = new CSSContentParser($xml); + $div = $parser->getBySelector('div')[0]->asXML(); + $this->assertEquals('
Hello &myentity;
', $div); + } }