let's append it to the output string. if ((!$tagFound && $this->tagsMethod) || ($tagFound && !$this->tagsMethod)) { // Reconstruct tag with allowed attributes if (!$isCloseTag) { // Open or single tag $attrSet = $this->cleanAttributes($attrSet); $preTag .= '<' . $tagName; for ($i = 0, $count = \count($attrSet); $i < $count; $i++) { $preTag .= ' ' . $attrSet[$i]; } // Reformat single tags to XHTML if (StringHelper::strpos($fromTagOpen, ''; } else { $preTag .= ' />'; } } else { // Closing tag $preTag .= ''; } } // Find next tag's start and continue iteration $postTag = StringHelper::substr($postTag, ($tagLength + 2)); $tagOpenStart = StringHelper::strpos($postTag, '<'); } // Append any code after the end of tags and return if ($postTag !== '<') { $preTag .= $postTag; } return $preTag; } /** * Internal method to strip a tag of disallowed attributes * * @param array $attrSet Array of attribute pairs to filter * * @return array Filtered array of attribute pairs * * @since 1.0 */ protected function cleanAttributes($attrSet) { $newSet = array(); $count = \count($attrSet); // Iterate through attribute pairs for ($i = 0; $i < $count; $i++) { // Skip blank spaces if (!$attrSet[$i]) { continue; } // Split into name/value pairs $attrSubSet = explode('=', trim($attrSet[$i]), 2); // Take the last attribute in case there is an attribute with no value $attrSubSet0 = explode(' ', trim($attrSubSet[0])); $attrSubSet[0] = array_pop($attrSubSet0); $attrSubSet[0] = strtolower($attrSubSet[0]); $quoteStyle = version_compare(\PHP_VERSION, '5.4', '>=') ? \ENT_QUOTES | \ENT_HTML401 : \ENT_QUOTES; // Remove all spaces as valid attributes does not have spaces. $attrSubSet[0] = html_entity_decode($attrSubSet[0], $quoteStyle, 'UTF-8'); $attrSubSet[0] = preg_replace('/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $attrSubSet[0]); $attrSubSet[0] = preg_replace('/\s+/u', '', $attrSubSet[0]); // Remove blocked chars from the attribute name foreach ($this->blockedChars as $blockedChar) { $attrSubSet[0] = str_ireplace($blockedChar, '', $attrSubSet[0]); } // Remove all symbols $attrSubSet[0] = preg_replace('/[^\p{L}\p{N}\-\s]/u', '', $attrSubSet[0]); // Remove all "non-regular" attribute names // AND blocked attributes if ((!preg_match('/[a-z]*$/i', $attrSubSet[0])) || (($this->xssAuto) && ((\in_array(strtolower($attrSubSet[0]), $this->attrBlacklist)) || (substr($attrSubSet[0], 0, 2) == 'on')))) { continue; } // XSS attribute value filtering if (!isset($attrSubSet[1])) { continue; } // Remove blocked chars from the attribute value foreach ($this->blockedChars as $blockedChar) { $attrSubSet[1] = str_ireplace($blockedChar, '', $attrSubSet[1]); } // Trim leading and trailing spaces $attrSubSet[1] = trim($attrSubSet[1]); // Strips unicode, hex, etc $attrSubSet[1] = str_replace('&#', '', $attrSubSet[1]); // Strip normal newline within attr value $attrSubSet[1] = preg_replace('/[\n\r]/', '', $attrSubSet[1]); // Strip double quotes $attrSubSet[1] = str_replace('"', '', $attrSubSet[1]); // Convert single quotes from either side to doubles (Single quotes shouldn't be used to pad attr values) if ((substr($attrSubSet[1], 0, 1) == "'") && (substr($attrSubSet[1], (\strlen($attrSubSet[1]) - 1), 1) == "'")) { $attrSubSet[1] = substr($attrSubSet[1], 1, (\strlen($attrSubSet[1]) - 2)); } // Strip slashes $attrSubSet[1] = stripslashes($attrSubSet[1]); // Autostrip script tags if (static::checkAttribute($attrSubSet)) { continue; } // Is our attribute in the user input array? $attrFound = \in_array(strtolower($attrSubSet[0]), $this->attrArray); // If the tag is allowed lets keep it if ((!$attrFound && $this->attrMethod) || ($attrFound && !$this->attrMethod)) { // Does the attribute have a value? if (empty($attrSubSet[1]) === false) { $newSet[] = $attrSubSet[0] . '="' . $attrSubSet[1] . '"'; } elseif ($attrSubSet[1] === '0') { // Special Case // Is the value 0? $newSet[] = $attrSubSet[0] . '="0"'; } else { // Leave empty attributes alone $newSet[] = $attrSubSet[0] . '=""'; } } } return $newSet; } /** * Try to convert to plaintext * * @param string $source The source string. * * @return string Plaintext string * * @since 1.0 * @deprecated This method will be removed once support for PHP 5.3 is discontinued. */ protected function decode($source) { return html_entity_decode($source, \ENT_QUOTES, 'UTF-8'); } /** * Escape < > and " inside attribute values * * @param string $source The source string. * * @return string Filtered string * * @since 1.0 */ protected function escapeAttributeValues($source) { $alreadyFiltered = ''; $remainder = $source; $badChars = array('<', '"', '>'); $escapedChars = array('<', '"', '>'); // Process each portion based on presence of =" and ", "/>, or "> // See if there are any more attributes to process while (preg_match('#<[^>]*?=\s*?(\"|\')#s', $remainder, $matches, \PREG_OFFSET_CAPTURE)) { // We have found a tag with an attribute, convert its byte position to a UTF-8 string length, using non-multibyte substr() $stringBeforeTag = substr($remainder, 0, $matches[0][1]); $tagPosition = StringHelper::strlen($stringBeforeTag); // Get the character length before the attribute value $nextBefore = $tagPosition + StringHelper::strlen($matches[0][0]); // Figure out if we have a single or double quote and look for the matching closing quote // Closing quote should be "/>, ">, ", or " at the end of the string $quote = StringHelper::substr($matches[0][0], -1); $pregMatch = ($quote == '"') ? '#(\"\s*/\s*>|\"\s*>|\"\s+|\"$)#' : "#(\'\s*/\s*>|\'\s*>|\'\s+|\'$)#"; // Get the portion after attribute value $attributeValueRemainder = StringHelper::substr($remainder, $nextBefore); if (preg_match($pregMatch, $attributeValueRemainder, $matches, \PREG_OFFSET_CAPTURE)) { // We have a closing quote, convert its byte position to a UTF-8 string length, using non-multibyte substr() $stringBeforeQuote = substr($attributeValueRemainder, 0, $matches[0][1]); $closeQuoteChars = StringHelper::strlen($stringBeforeQuote); $nextAfter = $nextBefore + $matches[0][1]; } else { // No closing quote $nextAfter = StringHelper::strlen($remainder); } // Get the actual attribute value $attributeValue = StringHelper::substr($remainder, $nextBefore, $nextAfter - $nextBefore); // Escape bad chars $attributeValue = str_replace($badChars, $escapedChars, $attributeValue); $attributeValue = $this->stripCssExpressions($attributeValue); $alreadyFiltered .= StringHelper::substr($remainder, 0, $nextBefore) . $attributeValue . $quote; $remainder = StringHelper::substr($remainder, $nextAfter + 1); } // At this point, we just have to return the $alreadyFiltered and the $remainder return $alreadyFiltered . $remainder; } /** * Remove CSS Expressions in the form of :expression(...) * * @param string $source The source string. * * @return string Filtered string * * @since 1.0 */ protected function stripCssExpressions($source) { // Strip any comments out (in the form of /*...*/) $test = preg_replace('#\/\*.*\*\/#U', '', $source); // Test for :expression if (!stripos($test, ':expression')) { // Not found, so we are done return $source; } // At this point, we have stripped out the comments and have found :expression // Test stripped string for :expression followed by a '(' if (preg_match_all('#:expression\s*\(#', $test, $matches)) { // If found, remove :expression return str_ireplace(':expression', '', $test); } return $source; } /** * Integer filter * * @param string $source The string to be filtered * * @return integer The filtered value */ private function cleanInt($source) { $pattern = '/[-+]?[0-9]+/'; preg_match($pattern, $source, $matches); return isset($matches[0]) ? (int) $matches[0] : 0; } /** * Alias for cleanInt() * * @param string $source The string to be filtered * * @return integer The filtered value */ private function cleanInteger($source) { return $this->cleanInt($source); } /** * Unsigned integer filter * * @param string $source The string to be filtered * * @return integer The filtered value */ private function cleanUint($source) { $pattern = '/[-+]?[0-9]+/'; preg_match($pattern, $source, $matches); return isset($matches[0]) ? abs((int) $matches[0]) : 0; } /** * Float filter * * @param string $source The string to be filtered * * @return float The filtered value */ private function cleanFloat($source) { $pattern = '/[-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?/'; preg_match($pattern, $source, $matches); return isset($matches[0]) ? (float) $matches[0] : 0.0; } /** * Alias for cleanFloat() * * @param string $source The string to be filtered * * @return float The filtered value */ private function cleanDouble($source) { return $this->cleanFloat($source); } /** * Boolean filter * * @param string $source The string to be filtered * * @return boolean The filtered value */ private function cleanBool($source) { return (bool) $source; } /** * Alias for cleanBool() * * @param string $source The string to be filtered * * @return boolean The filtered value */ private function cleanBoolean($source) { return $this->cleanBool($source); } /** * Word filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanWord($source) { $pattern = '/[^A-Z_]/i'; return preg_replace($pattern, '', $source); } /** * Alphanumerical filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanAlnum($source) { $pattern = '/[^A-Z0-9]/i'; return preg_replace($pattern, '', $source); } /** * Command filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanCmd($source) { $pattern = '/[^A-Z0-9_\.-]/i'; $result = preg_replace($pattern, '', $source); $result = ltrim($result, '.'); return $result; } /** * Base64 filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanBase64($source) { $pattern = '/[^A-Z0-9\/+=]/i'; return preg_replace($pattern, '', $source); } /** * String filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanString($source) { return $this->remove($this->decode($source)); } /** * HTML filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanHtml($source) { return $this->remove($source); } /** * Path filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanPath($source) { // Linux and other Unixoids $filePattern = '(?:[^\x00\/:*?]{1,255})'; $pathSeparatorPattern = '(?:\/+)'; $rootPattern = '(?:\/)'; if ($this->pathMatches($source, $rootPattern, $pathSeparatorPattern, $filePattern, '/')) { return $source; } // Windows $filePattern = '(?:[^\x00\\\\\/:*"?<>|]{1,255})'; $pathSeparatorPattern = '(?:[\\\\\/])'; $rootPattern = '(?:[A-Za-z]:(\\\\|\/))'; if ($this->pathMatches($source, $rootPattern, $pathSeparatorPattern, $filePattern, '\\')) { return $source; } return ''; } /** * Fix a path, if and only if it matches the provided patterns. * * If a path matches but is longer than 4095 bytes, it is cleared. * * @param string $source The path as provided; it gets cleaned in place, if possible. * @param string $rootPattern The pattern to identify an absolute path (e.g., '/' on Linux, 'C:\' on Windows), * @param string $pathSeparatorPattern The pattern for valid path separators * @param string $filePattern The pattern for valid file and directory names * @param string $pathSeparator The native path separator * * @return boolean */ private function pathMatches(&$source, $rootPattern, $pathSeparatorPattern, $filePattern, $pathSeparator) { $pathPattern = "/^{$rootPattern}?(?:{$filePattern}{$pathSeparatorPattern})*{$filePattern}?$/u"; if (preg_match($pathPattern, $source)) { $source = preg_replace("/{$pathSeparatorPattern}/", $pathSeparator, $source); if (strlen($source) > 4095) { // Path is too long $source = ''; } return true; } return false; } /** * Trim filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanTrim($source) { $result = trim($source); $result = StringHelper::trim($result, \chr(0xE3) . \chr(0x80) . \chr(0x80)); $result = StringHelper::trim($result, \chr(0xC2) . \chr(0xA0)); return $result; } /** * Username filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanUsername($source) { $pattern = '/[\x00-\x1F\x7F<>"\'%&]/'; return preg_replace($pattern, '', $source); } } Error