NG, ARRAY, PATH, NONE) * @return mixed 'Cleaned' version of input parameter * @since 1.5 */ function clean($source, $type='string') { // Handle the type constraint switch (strtoupper($type)) { case 'INT' : case 'INTEGER' : // Only use the first integer value preg_match('/-?[0-9]+/', (string) $source, $matches); $result = @ (int) $matches[0]; break; case 'FLOAT' : case 'DOUBLE' : // Only use the first floating point value preg_match('/-?[0-9]+(\.[0-9]+)?/', (string) $source, $matches); $result = @ (float) $matches[0]; break; case 'BOOL' : case 'BOOLEAN' : $result = (bool) $source; break; case 'WORD' : $result = (string) preg_replace( '/[^A-Z_]/i', '', $source ); break; case 'ALNUM' : $result = (string) preg_replace( '/[^A-Z0-9]/i', '', $source ); break; case 'CMD' : $result = (string) preg_replace( '/[^A-Z0-9_\.-]/i', '', $source ); $result = ltrim($result, '.'); break; case 'BASE64' : $result = (string) preg_replace( '/[^A-Z0-9\/+=]/i', '', $source ); break; case 'STRING' : $result = (string) $this->_remove($this->_decode((string) $source)); break; case 'ARRAY' : $result = (array) $source; break; case 'PATH' : $pattern = '/^[A-Za-z0-9_-]+[A-Za-z0-9_\.-]*([\\\\\/][A-Za-z0-9_-]+[A-Za-z0-9_\.-]*)*$/'; preg_match($pattern, (string) $source, $matches); $result = @ (string) $matches[0]; break; case 'USERNAME' : $result = (string) preg_replace( '/[\x00-\x1F\x7F<>"\'%&]/', '', $source ); break; default : // Are we dealing with an array? if (is_array($source)) { foreach ($source as $key => $value) { // filter element for XSS and other 'bad' code etc. if (is_string($value)) { $source[$key] = $this->_remove($this->_decode($value)); } } $result = $source; } else { // Or a string? if (is_string($source) && !empty ($source)) { // filter source for XSS and other 'bad' code etc. $result = $this->_remove($this->_decode($source)); } else { // Not an array or string.. return the passed parameter $result = $source; } } break; } return $result; } /** * Function to determine if contents of an attribute is safe * * @static * @param array $attrSubSet A 2 element array for attributes name,value * @return boolean True if bad code is detected * @since 1.5 */ function checkAttribute($attrSubSet) { $attrSubSet[0] = strtolower($attrSubSet[0]); $attrSubSet[1] = strtolower($attrSubSet[1]); return (((strpos($attrSubSet[1], 'expression') !== false) && ($attrSubSet[0]) == 'style') || (strpos($attrSubSet[1], 'javascript:') !== false) || (strpos($attrSubSet[1], 'behaviour:') !== false) || (strpos($attrSubSet[1], 'vbscript:') !== false) || (strpos($attrSubSet[1], 'mocha:') !== false) || (strpos($attrSubSet[1], 'livescript:') !== false)); } /** * Internal method to iteratively remove all unwanted tags and attributes * * @access protected * @param string $source Input string to be 'cleaned' * @return string 'Cleaned' version of input parameter * @since 1.5 */ function _remove($source) { $loopCounter = 0; // Iteration provides nested tag protection while ($source != $this->_cleanTags($source)) { $source = $this->_cleanTags($source); $loopCounter ++; } return $source; } /** * Internal method to strip a string of certain tags * * @access protected * @param string $source Input string to be 'cleaned' * @return string 'Cleaned' version of input parameter * @since 1.5 */ function _cleanTags($source) { /* * In the beginning we don't really have a tag, so everything is * postTag */ $preTag = null; $postTag = $source; $currentSpace = false; $attr = ''; // moffats: setting to null due to issues in migration system - undefined variable errors // Is there a tag? If so it will certainly start with a '<' $tagOpen_start = strpos($source, '<'); while ($tagOpen_start !== false) { // Get some information about the tag we are processing $preTag .= substr($postTag, 0, $tagOpen_start); $postTag = substr($postTag, $tagOpen_start); $fromTagOpen = substr($postTag, 1); $tagOpen_end = strpos($fromTagOpen, '>'); // Let's catch any non-terminated tags and skip over them if ($tagOpen_end === false) { $postTag = substr($postTag, $tagOpen_start +1); $tagOpen_start = strpos($postTag, '<'); continue; } // Do we have a nested tag? $tagOpen_nested = strpos($fromTagOpen, '<'); $tagOpen_nested_end = strpos(substr($postTag, $tagOpen_end), '>'); if (($tagOpen_nested !== false) && ($tagOpen_nested < $tagOpen_end)) { $preTag .= substr($postTag, 0, ($tagOpen_nested +1)); $postTag = substr($postTag, ($tagOpen_nested +1)); $tagOpen_start = strpos($postTag, '<'); continue; } // Lets get some information about our tag and setup attribute pairs $tagOpen_nested = (strpos($fromTagOpen, '<') + $tagOpen_start +1); $currentTag = substr($fromTagOpen, 0, $tagOpen_end); $tagLength = strlen($currentTag); $tagLeft = $currentTag; $attrSet = array (); $currentSpace = strpos($tagLeft, ' '); // Are we an open tag or a close tag? if (substr($currentTag, 0, 1) == '/') { // Close Tag $isCloseTag = true; list ($tagName) = explode(' ', $currentTag); $tagName = substr($tagName, 1); } else { // Open Tag $isCloseTag = false; list ($tagName) = explode(' ', $currentTag); } /* * Exclude all "non-regular" tagnames * OR no tagname * OR remove if xssauto is on and tag is blacklisted */ if ((!preg_match("/^[a-z][a-z0-9]*$/i", $tagName)) || (!$tagName) || ((in_array(strtolower($tagName), $this->tagBlacklist)) && ($this->xssAuto))) { $postTag = substr($postTag, ($tagLength +2)); $tagOpen_start = strpos($postTag, '<'); // Strip tag continue; } /* * Time to grab any attributes from the tag... need this section in * case attributes have spaces in the values. */ while ($currentSpace !== false) { $fromSpace = substr($tagLeft, ($currentSpace +1)); $nextSpace = strpos($fromSpace, ' '); $openQuotes = strpos($fromSpace, '"'); $closeQuotes = strpos(substr($fromSpace, ($openQuotes +1)), '"') + $openQuotes +1; // Do we have an attribute to process? [check for equal sign] if (strpos($fromSpace, '=') !== false) { /* * If the attribute value is wrapped in quotes we need to * grab the substring from the closing quote, otherwise grab * till the next space */ if (($openQuotes !== false) && (strpos(substr($fromSpace, ($openQuotes +1)), '"') !== false)) { $attr = substr($fromSpace, 0, ($closeQuotes +1)); } else { $attr = substr($fromSpace, 0, $nextSpace); } } else { /* * No more equal signs so add any extra text in the tag into * the attribute array [eg. checked] */ if ($fromSpace != '/') { $attr = substr($fromSpace, 0, $nextSpace); } } // Last Attribute Pair if (!$attr && $fromSpace != '/') { $attr = $fromSpace; } // Add attribute pair to the attribute array $attrSet[] = $attr; // Move search point and continue iteration $tagLeft = substr($fromSpace, strlen($attr)); $currentSpace = strpos($tagLeft, ' '); } // Is our tag in the user input array? $tagFound = in_array(strtolower($tagName), $this->tagsArray); // If the tag is allowed lets 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; $i < count($attrSet); $i ++) { $preTag .= ' '.$attrSet[$i]; } // Reformat single tags to XHTML if (strpos($fromTagOpen, ''; } else { $preTag .= ' />'; } } else { // Closing Tag $preTag .= ''; } } // Find next tag's start and continue iteration $postTag = substr($postTag, ($tagLength +2)); $tagOpen_start = strpos($postTag, '<'); } // Append any code after th