ownerDocument); } /** * {@inheritdoc} */ protected function isSafe(Attribute $attribute) { return $attribute->isSafeAsURL(); } /** * {@inheritdoc} */ protected function checkAttributeNode(DOMAttr $attribute, Tag $tag) { if (!$this->isSafeUrl($attribute->value)) { parent::checkAttributeNode($attribute, $tag); } } /** * {@inheritdoc} */ protected function checkElementNode(DOMElement $element, Tag $tag) { if (!$this->elementHasSafeUrl($element)) { parent::checkElementNode($element, $tag); } } /** * Test whether every branch of a given xsl:choose element contains a known-safe URL * * @param DOMElement $choose * @return bool */ protected function chooseHasSafeUrl(DOMElement $choose) { $xpath = new DOMXPath($choose->ownerDocument); $hasOtherwise = false; foreach ($xpath->query('xsl:when | xsl:otherwise', $choose) as $branch) { if (!$this->elementHasSafeUrl($branch)) { return false; } if ($branch->nodeName === 'xsl:otherwise') { $hasOtherwise = true; } } return $hasOtherwise; } /** * Test whether given element contains a known-safe URL * * @param DOMElement $element * @return bool */ protected function elementHasSafeUrl(DOMElement $element) { if ($element->firstChild instanceof DOMElement && $element->firstChild->nodeName === 'xsl:choose') { return $this->chooseHasSafeUrl($element->firstChild); } return $element->firstChild instanceof DOMText && $this->isSafeUrl($element->firstChild->textContent); } /** * Test whether given URL is known to be safe * * @param string $url * @return bool */ protected function isSafeUrl($url) { return (bool) preg_match($this->safeUrlRegexp, $url); } }