Sanitize Naughty HTML

Callback method for xss_clean() to remove naughty HTML elements.

@used-by    XSS::clean()

@param    array $matches

@return    string
2158
protected static function sanitizeNaughtyHTML($matches)
    {
        // First, escape unclosed tags
        if (empty($matches[ 'closeTag' ])) {
            return '&lt;' . $matches[ 1 ];
        } // Is the element that we caught naughty? If so, escape it
        elseif (in_array(strtolower($matches[ 'tagName' ]), self::getConfig('naughty_tags'), true)) {
            return '&lt;' . $matches[ 1 ] . '&gt;';
        } // For other tags, see if their attributes are "evil" and strip those
        elseif (isset($matches[ 'attributes' ])) {
            // We'll store the already fitlered attributes here
            $attributes = [];

            // Attribute-catching pattern
            $attributesPattern = '#'
                . '(?<name>[^\s\042\047>/=]+)'
                // attribute characters
                // optional attribute-value
                . '(?:\s*=(?<value>[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*)))'
                // attribute-value separator
                . '#i';

            // Blacklist pattern for evil attribute names
            $is_evil_pattern = '#^(' . implode('|', self::getConfig('evil_attributes')) . ')$#i';

            // Each iteration filters a single attribute
            do {
                // Strip any non-alpha characters that may preceed an attribute.
                // Browsers often parse these incorrectly and that has been a
                // of numerous XSS issues we've had.
                $matches[ 'attributes' ] = preg_replace('#^[^a-z]+#i', '', $matches[ 'attributes' ]);

                if ( ! preg_match($attributesPattern, $matches[ 'attributes' ], $attribute, PREG_OFFSET_CAPTURE)) {
                    // No (valid) attribute found? Discard everything else inside the tag
                    break;
                }

                if (
                    // Is it indeed an "evil" attribute?
                    preg_match($is_evil_pattern, $attribute[ 'name' ][ 0 ])
                    // Or does it have an equals sign, but no value and not quoted? Strip that too!
                    OR (trim($attribute[ 'value' ][ 0 ]) === '')
                ) {
                    $attributes[] = 'xss=removed';
                } else {
                    $attributes[] = $attribute[ 0 ][ 0 ];
                }

                $matches[ 'attributes' ] = substr(
                    $matches[ 'attributes' ],
                    $attribute[ 0 ][ 1 ] + strlen($attribute[ 0 ][ 0 ])
                );
            } while ($matches[ 'attributes' ] !== '');
            $attributes = empty($attributes)
                ? ''
                : ' ' . implode(' ', $attributes);

            return '<' . $matches[ 'slash' ] . $matches[ 'tagName' ] . $attributes . '>';
        }

        return $matches[ 0 ];
    }
1330009
protected function italic_tag_rule($match) {
        $text = $match[1];
        if(strlen($match[2]) == 5) {
            $text .= "''";
        }
        
        $this->rules($text, array('only' => array('bold')));
        if(strpos($text, "''") !== false) {
            $text = str_replace("''", $this->protect("''"), $text);
        }
        
        return array($text, array());
    }
142890
protected function _doCodeBlocks_callback($matches) {
		$codeblock = $matches[1];

		$codeblock = $this->outdent($codeblock);
		if ($this->code_block_content_func) {
			$codeblock = call_user_func($this->code_block_content_func, $codeblock, "");
		} else {
			$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
		}

		# trim leading newlines and trailing newlines
		$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);

		$codeblock = "<pre><code>$codeblock\n</code></pre>";
		return "\n\n" . $this->hashBlock($codeblock) . "\n\n";
	}
27332
protected static function cleanVersion($version, array $matches)
    {
        $end = substr($version, \strlen($matches[1][0][0]));
        $version = $matches[1][0][0].'-';

        $matches = array();
        if (preg_match('/^(\-|\+)/', $end, $matches)) {
            $end = substr($end, 1);
        }

        $matches = array();
        preg_match('/^[a-z]+/', $end, $matches);
        $type = isset($matches[0]) ? VersionParser::normalizeStability($matches[0]) : null;
        $end = substr($end, \strlen($type));

        return array($type, $version, $end);
    }
51359
public function xssClean(array $data)
    {
        foreach ($data as $k => $v) {
            $data[$k] = filter_var($v, FILTER_SANITIZE_STRING);
        }

        return $data;
    }
498219
public function render($matches)
    {
        $name 		= array_get($matches, 2);

        $content 	= array_get($matches, 5);

        $callback 	= $this->getCallback($name);

        $params		= $this->getParameter($matches);

        $params 	= [$params, $content, $name];

        return call_user_func_array($callback, $params);
    }
781875
private function findPattern($matches) {
        switch ($matches[1]) {
            case ':num':
                $replacement = '([0-9]';
                break;
            case ':alpha':
                $replacement = '([a-zA-Z]';
                break;
            case ':any':
                $replacement = '([^\/]';
                break;
        }


        if (isset($matches[3]) && is_numeric($matches[3])) {
            if (isset($matches[4]) && $matches[4] == '?') {
                $replacement .= '{0,' . $matches[3] . '})';
            } else {
                $replacement .= '{' . $matches[3] . '})';
            }
        } else {
            if (isset($matches[4]) && $matches[4] == '?') {
                $replacement .= '*)';
            } else {
                $replacement .= '+)';
            }
        }

        return $replacement;
    }
16961
private static function _escapeStringCallback(array $match): string
    {
        if (isset($match[2])) {
            return self::ESCAPE_SPECIALS[$match[2]]
                ?? \sprintf('\\x%02X', \ord($match[2]));
        }
        return '\\' . $match[1];
    }
661591
private function getNamedPatterns($matches)
    {
        foreach ($matches as $key => $value) {
            if (!is_string($key) || strlen($value) < 1) {
                unset($matches[$key]);
            }
        }

        return $matches;
    }
280567
public function _doCodeBlocks_callback($matches)
	{
		$codeblock = $this->outdent($matches[1]);
		if(($codeblock = $this->highlightCodeBlock($codeblock)) !== null)
			return "\n\n".$this->hashBlock($codeblock)."\n\n";
		else
			return parent::_doCodeBlocks_callback($matches);
	}
785366
protected static function replaceBlock(array $matches)
    {
        $matches[3] = self::removeTrailingNewline($matches[3]);

        if (preg_match_all(self::$regex, self::$childSource, $override) && in_array($matches[2], $override[2])) {
            $key      = array_search($matches[2], $override[2]);
            $override = self::removeTrailingNewline($override[3][$key]);

            $l = stripslashes(self::$l);
            $r = stripslashes(self::$r);

            if (self::$lastReplacement) {
                return preg_replace('/' . self::$l . '\$dwoo\.parent' . self::$r . '/is', $matches[3], $override);
            }

            return $l . 'block ' . $matches[1] . $matches[2] . $matches[1] . $r . preg_replace('/' . self::$l . '\$dwoo\.parent' . self::$r . '/is', $matches[3], $override) . $l . '/block' . $r;
        }

        if (preg_match(self::$regex, $matches[3])) {
            return preg_replace_callback(self::$regex, array(
                'Dwoo\Plugins\Functions\PluginExtends',
                'replaceBlock'
            ), $matches[3]);
        }

        if (self::$lastReplacement) {
            return $matches[3];
        }

        return $matches[0];
    }
11430
protected function interpretAttribMatches( $matches ) {
		$attributes = [];
		foreach ( $matches as $m ) {
			if ( strlen( $m[self::MA_SLASH][0] ) ) {
				$this->error( 'unexpected bare slash', $m[self::MA_SLASH][1] );
				continue;
			}
			$name = $m[self::MA_NAME][0];
			$isSimple = isset( $m[self::MA_SIMPLE_NAME] ) &&
				( strlen( $name ) === strlen( $m[self::MA_SIMPLE_NAME][0] ) );
			if ( !$isSimple ) {
				// We can skip these steps if we already know the name is simple
				if ( !$this->ignoreErrors ) {
					$this->handleAsciiErrors( "\"'<=", $name, 0, strlen( $name ), $m[self::MA_NAME][1] );
				}
				if ( !$this->ignoreNulls ) {
					$name = $this->handleNulls( $m[self::MA_NAME][0], $m[self::MA_NAME][1] );
				}
				$name = strtolower( $name );
			}
			$additionalAllowedChar = '';
			$isSimple = true;
			if ( isset( $m[self::MA_DQUOTED] ) && $m[self::MA_DQUOTED][1] >= 0 ) {
				// Double-quoted attribute value
				$additionalAllowedChar = '"';
				$value = $m[self::MA_DQUOTED][0];
				$pos = $m[self::MA_DQUOTED][1];
				$isSimple = !strlen( $m[self::MA_DQUOTED_UNSIMPLE][0] );
				if ( $isSimple && strlen( $m[self::MA_DQUOTED_CHARREF][0] ) ) {
					// Efficiently handle well-behaved character references
					$value = strtr( $value, self::$commonEntities );
				}
			} elseif ( isset( $m[self::MA_SQUOTED] ) && $m[self::MA_SQUOTED][1] >= 0 ) {
				// Single-quoted attribute value
				$additionalAllowedChar = "'";
				$value = $m[self::MA_SQUOTED][0];
				$pos = $m[self::MA_SQUOTED][1];
				$isSimple = !strlen( $m[self::MA_SQUOTED_UNSIMPLE][0] );
				if ( $isSimple && strlen( $m[self::MA_SQUOTED_CHARREF][0] ) ) {
					// Efficiently handle well-behaved character references
					$value = strtr( $value, self::$commonEntities );
				}
			} elseif ( isset( $m[self::MA_UNQUOTED] ) && $m[self::MA_UNQUOTED][1] >= 0 ) {
				// Unquoted attribute value
				$value = $m[self::MA_UNQUOTED][0];
				$pos = $m[self::MA_UNQUOTED][1];
				$isSimple = !strlen( $m[self::MA_UNQUOTED_UNSIMPLE][0] );
				// Search for parse errors
				if ( !$this->ignoreErrors ) {
					if ( $value === '' ) {
						// ">" in the before attribute value state is a parse error
						$this->error( 'empty unquoted attribute', $pos );
					}
					if ( !$isSimple ) {
						$this->handleAsciiErrors( "\"'<=`", $value, 0, strlen( $value ), $pos );
					}
				}
			} else {
				$value = '';
			}
			if ( $additionalAllowedChar && !$this->ignoreErrors ) {
				// After attribute value (quoted) state
				// Quoted attributes must be followed by a space, "/" or ">"
				$aavPos = $m[0][1] + strlen( $m[0][0] );
				if ( $aavPos < $this->length ) {
					$aavChar = $this->text[$aavPos];
					if ( !preg_match( '~^[\t\n\f />]~', $aavChar ) ) {
						$this->error( 'missing space between attributes', $aavPos );
					}
				}
			}
			if ( !$isSimple && $value !== '' ) {
				if ( !$this->ignoreNulls ) {
					$value = $this->handleNulls( $value, $pos );
				}
				if ( !$this->ignoreCharRefs ) {
					$value = $this->handleCharRefs( $value, $pos, true, $additionalAllowedChar );
				}
			}
			if ( isset( $attributes[$name] ) ) {
				$this->error( "duplicate attribute", $m[0][1] );
			} else {
				$attributes[$name] = $value;
			}
		}
		return $attributes;
	}
262643
protected function autolink($matches)
	{
		$href = $matches[0];

		if (substr($href, 0, 1) == '!')
		{
			return substr($href, 1);
		}

		$href = str_replace('"', '', $href);
		$href = str_replace("'", '', $href);
		$href = str_replace('&#8221', '', $href);

		$h = array('h', 'm', 'f', 'g', 'n');
		if (!in_array(substr($href, 0, 1), $h))
		{
			$href = substr($href, 1);
		}
		$name = trim($href);
		if (substr($name, 0, 7) == 'mailto:')
		{
			$name = substr($name, 7, strlen($name));
			$name = Str::obfuscate($name);

			$href = 'mailto:' . $name;
		}
		$l = sprintf(
			' <a class="ext-link" href="%s" rel="external">%s</a>', $href, $name
		);
		return $l;
	}
386246
private function _filter(array $matches)
    {
        $filters = explode('|', $matches[0]);
        $var = $filters[0];
        unset($filters[0]);
        foreach ($filters as $filter) {
            $params = explode(':', $filter);
            $filterName = $params[0];
            unset($params[0]);
            $array = 'array(';
            foreach ($params as $param) {
                $array .= $param . ',';
            }
            $array = trim($array, ',') . ')';
            $var = 'getFilter(\'' . $filterName . '\')->setOptions(' . $array . ')->filter(' . $var . ')';
        }
        return $var;
    }
115008
private function restoreProtectedHtml($matches): string
    {
        \preg_match('/.*"(?<id>\d*)"/', $matches['attributes'], $matchesInner);

        $html = '';
        if (isset($this->protectedChildNodes[$matchesInner['id']])) {
            $html .= $this->protectedChildNodes[$matchesInner['id']];
        }

        return $html;
    }
984675
private function sanitizeTags(array $tags)
    {
        $sanitized = [];

        foreach ($tags as $tag) {
            $sanitized[] = (string) $tag;
        }

        return $sanitized;
    }
492391
private function replace_string($matches)
    {
        $match = $matches[0];
        $quote = substr($match, 0, 1);
        // Must use addcslashes in PHP to avoid parsing of backslashes
        $match = addcslashes($this->str_slice($match, 1, -1), '\\');

        // maybe the string contains a comment-like substring?
        // one, maybe more? put'em back then
        if (($pos = $this->index_of($match, self::COMMENT)) >= 0) {
            for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
                $match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1);
            }
        }

        // minify alpha opacity in filter strings
        $match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match);

        $this->preserved_tokens[] = $match;

        return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote;
    }