Look for improperly-filtered dynamic content

@param  DOMElement $template <xsl:template/> node
@param  Tag        $tag      Tag this template belongs to
@return void
326177
public function check(DOMElement $template, Tag $tag)
	{
		foreach ($this->getNodes($template) as $node)
		{
			// Test this node's safety
			$this->checkNode($node, $tag);
		}
	}
778858
protected function processStartTag(Tag $tag)
	{
		$tagName   = $tag->getName();
		$tagConfig = $this->tagsConfig[$tagName];

		// 1. Check that this tag has not reached its global limit tagLimit
		// 2. Execute this tag's filterChain, which will filter/validate its attributes
		// 3. Apply closeParent, closeAncestor and fosterParent rules
		// 4. Check for nestingLimit
		// 5. Apply requireAncestor rules
		//
		// This order ensures that the tag is valid and within the set limits before we attempt to
		// close parents or ancestors. We need to close ancestors before we can check for nesting
		// limits, whether this tag is allowed within current context (the context may change
		// as ancestors are closed) or whether the required ancestors are still there (they might
		// have been closed by a rule.)
		if ($this->cntTotal[$tagName] >= $tagConfig['tagLimit'])
		{
			$this->logger->err(
				'Tag limit exceeded',
				[
					'tag'      => $tag,
					'tagName'  => $tagName,
					'tagLimit' => $tagConfig['tagLimit']
				]
			);
			$tag->invalidate();

			return;
		}

		FilterProcessing::filterTag($tag, $this, $this->tagsConfig, $this->openTags);
		if ($tag->isInvalid())
		{
			return;
		}

		if ($this->currentFixingCost < $this->maxFixingCost)
		{
			if ($this->fosterParent($tag) || $this->closeParent($tag) || $this->closeAncestor($tag))
			{
				// This tag parent/ancestor needs to be closed, we just return (the tag is still valid)
				return;
			}
		}

		if ($this->cntOpen[$tagName] >= $tagConfig['nestingLimit'])
		{
			$this->logger->err(
				'Nesting limit exceeded',
				[
					'tag'          => $tag,
					'tagName'      => $tagName,
					'nestingLimit' => $tagConfig['nestingLimit']
				]
			);
			$tag->invalidate();

			return;
		}

		if (!$this->tagIsAllowed($tagName))
		{
			$msg     = 'Tag is not allowed in this context';
			$context = ['tag' => $tag, 'tagName' => $tagName];
			if ($tag->getLen() > 0)
			{
				$this->logger->warn($msg, $context);
			}
			else
			{
				$this->logger->debug($msg, $context);
			}
			$tag->invalidate();

			return;
		}

		if ($this->requireAncestor($tag))
		{
			$tag->invalidate();

			return;
		}

		// If this tag has an autoClose rule and it's not paired with an end tag or followed by an
		// end tag, we replace it with a self-closing tag with the same properties
		if ($tag->getFlags() & self::RULE_AUTO_CLOSE
		 && !$tag->getEndTag()
		 && !$this->isFollowedByClosingTag($tag))
		{
			$newTag = new Tag(Tag::SELF_CLOSING_TAG, $tagName, $tag->getPos(), $tag->getLen());
			$newTag->setAttributes($tag->getAttributes());
			$newTag->setFlags($tag->getFlags());

			$tag = $newTag;
		}

		if ($tag->getFlags() & self::RULE_TRIM_FIRST_LINE
		 && !$tag->getEndTag()
		 && substr($this->text, $tag->getPos() + $tag->getLen(), 1) === "\n")
		{
			$this->addIgnoreTag($tag->getPos() + $tag->getLen(), 1);
		}

		// This tag is valid, output it and update the context
		$this->outputTag($tag);
		$this->pushContext($tag);

		// Apply the createChild rules if applicable
		$this->createChild($tag);
	}
62955
protected function checkAttribute(DOMNode $node, Tag $tag, $attrName)
	{
		// Test whether the attribute exists
		if (!isset($tag->attributes[$attrName]))
		{
			if ($this->ignoreUnknownAttributes)
			{
				return;
			}

			throw new UnsafeTemplateException("Cannot assess the safety of unknown attribute '" . $attrName . "'", $node);
		}

		// Test whether the attribute is safe to be used in this content type
		if (!$this->tagFiltersAttributes($tag) || !$this->isSafe($tag->attributes[$attrName]))
		{
			throw new UnsafeTemplateException("Attribute '" . $attrName . "' is not properly sanitized to be used in this context", $node);
		}
	}
768006
protected function checkElementNode(DOMElement $element, Tag $tag)
	{
		$xpath = new DOMXPath($element->ownerDocument);

		// If current node is not an <xsl:attribute/> element, we exclude descendants
		// with an <xsl:attribute/> ancestor so that content such as:
		//   <script><xsl:attribute name="id"><xsl:value-of/></xsl:attribute></script>
		// would not trigger a false-positive due to the presence of an <xsl:value-of/>
		// element in a <script>
		$predicate = ($element->localName === 'attribute') ? '' : '[not(ancestor::xsl:attribute)]';

		// Test the select expression of <xsl:value-of/> nodes
		$query = './/xsl:value-of' . $predicate;
		foreach ($xpath->query($query, $element) as $valueOf)
		{
			$this->checkSelectNode($valueOf->getAttributeNode('select'), $tag);
		}

		// Reject all <xsl:apply-templates/> nodes
		$query = './/xsl:apply-templates' . $predicate;
		foreach ($xpath->query($query, $element) as $applyTemplates)
		{
			throw new UnsafeTemplateException('Cannot allow unfiltered data in this context', $applyTemplates);
		}
	}
935786
public function filterByPageTemplate(TemplateEntity $template)
    {
        $this->query->andWhere('cv.pTemplateID = :pTemplateID');
        $this->query->setParameter('pTemplateID', $template->getPageTemplateID());
    }
471687
protected function createChild(Tag $tag)
	{
		$tagConfig = $this->tagsConfig[$tag->getName()];
		if (isset($tagConfig['rules']['createChild']))
		{
			$priority = -1000;
			$tagPos   = $this->pos + strspn($this->text, " \n\r\t", $this->pos);
			foreach ($tagConfig['rules']['createChild'] as $tagName)
			{
				$this->addStartTag($tagName, $tagPos, 0, ++$priority);
			}
		}
	}
323366
public function full_tag($tag, $content = null, $attributes = null) {
        $content = $this->text_content($content); // First of all, apply transformations
        $this->nullcontent = is_null($content) ? true : false; // Is it null content
        $this->begin_tag($tag, $attributes);
        if (!$this->nullcontent) {
            $this->write($content);
            $this->lastwastext = true;
            $this->end_tag($tag);
        }
    }
1028835
protected function _isAllowed($parent, $tag) {
        $filter = $this->getFilterByTag($tag);

        if (!$filter) {
            return false;
        }

        $child = $filter->getTag($tag);

        // Remove children after a certain nested depth
        if (isset($parent['currentDepth']) && $parent['maxChildDepth'] >= 0 && $parent['currentDepth'] > $parent['maxChildDepth']) {
            return false;

        // Children that can only be within a certain parent
        } else if ($child['parent'] && !in_array($parent['tag'], $child['parent'])) {
            return false;

        // Parents that can not have specific direct descendant children
        } else if ($parent['childrenBlacklist'] && in_array($child['tag'], $parent['childrenBlacklist'])) {
            return false;

        // Parents that can only have direct descendant children
        } else if ($parent['childrenWhitelist'] && !in_array($child['tag'], $parent['childrenWhitelist'])) {
            return false;
        }

        // Validate the type nesting
        switch ($parent['allowedTypes']) {
            case self::TYPE_INLINE:
                // Inline type only allowed
                if ($child['displayType'] === self::TYPE_INLINE) {
                    return true;
                }
            break;
            case self::TYPE_BLOCK:
                // Block types only allowed if the parent is also a block
                if ($parent['displayType'] === self::TYPE_BLOCK && $child['displayType'] === self::TYPE_BLOCK) {
                    return true;
                }
            break;
            case self::TYPE_BOTH:
                if ($parent['displayType'] === self::TYPE_INLINE) {
                    // Only allow inline if parent is inline
                    if ($child['displayType'] === self::TYPE_INLINE) {
                        return true;
                    }
                } else {
                    return true;
                }
            break;
        }

        // Log the error
        $this->_errors[] = array(
            'type' => self::ERROR_SCOPE,
            'parent' => $parent['tag'],
            'parentType' => $parent['displayType'],
            'parentAllowed' => $parent['allowedTypes'],
            'child' => $child['tag'],
            'childType' => $child['displayType']
        );

        return false;
    }
264756
public static function wrapOnDemand($condition, $tag, $content)
    {
        if ($condition) {
            echo sprintf('<%s>%s</%s>', $tag, $content, $tag);
        } else {
            echo $content;
        }
    }
922042
protected function processTagContent(?string $tagContent = null): void
    {
        $varPoss = $this->loadAndCheckVarPositions();

        if ($varPoss) {
            $this->checkArgumentItself($tagContent);
            $this->checkAgainstPattern($tagContent);
            $this->checkType();
            $this->checkDescription();
        }
    }
66079
protected function isUseTagDirty(\DOMElement $element)
    {
        $xlinks = $element->getAttributeNS('http://www.w3.org/1999/xlink', 'href');
        if ($xlinks && substr($xlinks, 0, 1) !== '#') {
            return true;
        }

        return false;
    }
1068773
protected function parseXslApplyTemplates(DOMElement $ir, DOMElement $node)
	{
		$applyTemplates = $this->appendElement($ir, 'applyTemplates');
		if ($node->hasAttribute('select'))
		{
			$applyTemplates->setAttribute('select', $node->getAttribute('select'));
		}
	}
111770
protected function addTypes(TagDescriptor $tag, \DOMElement $child)
    {
        if (!method_exists($tag, 'getTypes')) {
            return;
        }

        $typeString = '';
        foreach ($tag->getTypes() as $type) {
            $typeString .= $type . '|';

            /** @var \DOMElement $typeNode */
            $typeNode = $child->appendChild(new \DOMElement('type'));
            $typeNode->appendChild(new \DOMText($type));
        }

        $child->setAttribute('type', str_replace('&', '&amp;', rtrim($typeString, '|')));
    }
1120474
public function clean(\DOMElement $e, $tag)
    {
        $currentItem = null;
        $targetList = $e->getElementsByTagName($tag);
        $isEmbed = ('audio' === $tag || 'video' === $tag || 'iframe' === $tag || 'object' === $tag || 'embed' === $tag);

        for ($y = $targetList->length - 1; $y >= 0; --$y) {
            // Allow youtube and vimeo videos through as people usually want to see those.
            $currentItem = $targetList->item($y);

            if ($isEmbed) {
                $attributeValues = $currentItem->getAttribute('src') . ' ' . $currentItem->getAttribute('href');

                // First, check the elements attributes to see if any of them contain known media hosts
                if (preg_match($this->regexps['media'], $attributeValues)) {
                    continue;
                }

                // Then check the elements inside this element for the same.
                if (preg_match($this->regexps['media'], $targetList->item($y)->getInnerHTML())) {
                    continue;
                }
            }

            $currentItem->parentNode->removeChild($currentItem);
        }
    }
1358594
public function addTemplate(Template $template)
    {
        $key = $this->normalizeKey($template->getTemplateName());
        $this->templates[$key] = $template;
    }
1116208
private function isAllowed($template)
    {
        foreach ($this->excludes as $exclude) {
            if (true === $this->match($template, $exclude)) {
                return false;
            }
        }
        foreach ($this->includes as $include) {
            if (true === $this->match($template, $include)) {
                return true;
            }
        }

        return false;
    }
705189
public function check(DOMElement $template, Tag $tag)
	{
		$xpath = new DOMXPath($template->ownerDocument);
		$node  = $xpath->query('//@disable-output-escaping')->item(0);

		if ($node)
		{
			throw new UnsafeTemplateException("The template contains a 'disable-output-escaping' attribute", $node);
		}
	}