diff --git a/gulliver/system/class.g.php b/gulliver/system/class.g.php index 0d3911c95..7fac7ff42 100755 --- a/gulliver/system/class.g.php +++ b/gulliver/system/class.g.php @@ -5230,6 +5230,23 @@ class G } return in_array(strtolower($functionName), $allFunctions['user']); } + + /** + * Constructor for inputFilter class. Only first parameter is required. + * @access constructor + * @data Mixed - input string/array-of-string to be 'cleaned' + * @param Array $tagsArray - list of user-defined tags + * @param Array $attrArray - list of user-defined attributes + * @param int $tagsMethod - 0= allow just user-defined, 1= allow all but user-defined + * @param int $attrMethod - 0= allow just user-defined, 1= allow all but user-defined + * @param int $xssAuto - 0= only auto clean essentials, 1= allow clean blacklisted tags/attr + */ + public function sanitizeInput($data, $tagsArray = array(), $attrArray = array(), $tagsMethod = 0, $attrMethod = 0, $xssAuto = 1) + { + G::LoadSystem('inputfilter'); + $filtro = new InputFilter($tagsArray , $attrArray, $tagsMethod, $attrMethod, $xssAuto); + return $filtro->process($data); + } } /** diff --git a/gulliver/system/class.inputfilter.php b/gulliver/system/class.inputfilter.php new file mode 100644 index 000000000..d3f6c9c4f --- /dev/null +++ b/gulliver/system/class.inputfilter.php @@ -0,0 +1,368 @@ +tagsArray = (array) $tagsArray; + $this->attrArray = (array) $attrArray; + $this->tagsMethod = $tagsMethod; + $this->attrMethod = $attrMethod; + $this->xssAuto = $xssAuto; + } + + /** + * Method to be called by another php script. Processes for XSS and specified bad code. + * @access public + * @param Mixed $source - input string/array-of-string to be 'cleaned' + * @return String $source - 'cleaned' version of input parameter + */ + public function process($source) + { + // clean all elements in this 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)); + } + } + return $source; + // clean this string + } elseif (is_string($source)) { + // filter source for XSS and other 'bad' code etc. + return $this->remove($this->decode($source)); + } else { + // return parameter as given + return $source; + } + } + + /** + * Internal method to iteratively remove all unwanted tags and attributes + * @access protected + * @param String $source - input string to be 'cleaned' + * @return String $source - 'cleaned' version of input parameter + */ + public function remove($source) + { + $loopCounter=0; + // provides nested-tag protection + while ($source != $this->filterTags($source)) { + $source = $this->filterTags($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 $source - 'cleaned' version of input parameter + */ + public function filterTags($source) + { + // filter pass setup + $preTag = null; + $postTag = $source; + // find initial tag's position + $tagOpen_start = strpos($source, '<'); + // interate through string until no tags left + while ($tagOpen_start !== false) { + // process tag interatively + $preTag .= substr($postTag, 0, $tagOpen_start); + $postTag = substr($postTag, $tagOpen_start); + $fromTagOpen = substr($postTag, 1); + // end of tag + $tagOpen_end = strpos($fromTagOpen, '>'); + if ($tagOpen_end === false) { + break; + } + // next start of tag (for nested tag assessment) + $tagOpen_nested = strpos($fromTagOpen, '<'); + 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; + } + $tagOpen_nested = (strpos($fromTagOpen, '<') + $tagOpen_start + 1); + $currentTag = substr($fromTagOpen, 0, $tagOpen_end); + $tagLength = strlen($currentTag); + if (!$tagOpen_end) { + $preTag .= $postTag; + $tagOpen_start = strpos($postTag, '<'); + } + // iterate through tag finding attribute pairs - setup + $tagLeft = $currentTag; + $attrSet = array(); + $currentSpace = strpos($tagLeft, ' '); + // is end tag + if (substr($currentTag, 0, 1) == "/") { + $isCloseTag = true; + list($tagName) = explode(' ', $currentTag); + $tagName = substr($tagName, 1); + // is start tag + } else { + $isCloseTag = false; + list($tagName) = explode(' ', $currentTag); + } + // excludes 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, '<'); + // don't append this tag + continue; + } + // this while is needed to support attribute values with spaces in! + while ($currentSpace !== false) { + $fromSpace = substr($tagLeft, ($currentSpace+1)); + $nextSpace = strpos($fromSpace, ' '); + $openQuotes = strpos($fromSpace, '"'); + $closeQuotes = strpos(substr($fromSpace, ($openQuotes+1)), '"') + $openQuotes + 1; + // another equals exists + if (strpos($fromSpace, '=') !== false) { + // opening and closing quotes exists + if (($openQuotes !== false) && (strpos(substr($fromSpace, ($openQuotes+1)), '"') !== false)) { + $attr = substr($fromSpace, 0, ($closeQuotes+1)); + } else { + // one or neither exist + $attr = substr($fromSpace, 0, $nextSpace); + } + // no more equals exist + } else { + $attr = substr($fromSpace, 0, $nextSpace); + } + // last attr pair + if (!$attr) { + $attr = $fromSpace; + } + // add to attribute pairs array + $attrSet[] = $attr; + // next inc + $tagLeft = substr($fromSpace, strlen($attr)); + $currentSpace = strpos($tagLeft, ' '); + } + // appears in array specified by user + $tagFound = in_array(strtolower($tagName), $this->tagsArray); + // remove this tag on condition + if ((!$tagFound && $this->tagsMethod) || ($tagFound && !$this->tagsMethod)) { + // reconstruct tag with allowed attributes + if (!$isCloseTag) { + $attrSet = $this->filterAttr($attrSet); + $preTag .= '<' . $tagName; + for ($i = 0; $i < count($attrSet); $i++) { + $preTag .= ' ' . $attrSet[$i]; + } + // reformat single tags to XHTML + if (strpos($fromTagOpen, "'; + } else { + $preTag .= ' />'; + } + // just the tagname + } else { + $preTag .= ''; + } + } + // find next tag's start + $postTag = substr($postTag, ($tagLength + 2)); + $tagOpen_start = strpos($postTag, '<'); + } + // append any code after end of tags + $preTag .= $postTag; + return $preTag; + } + + /** + * Internal method to strip a tag of certain attributes + * @access protected + * @param Array $attrSet + * @return Array $newSet + */ + public function filterAttr($attrSet) + { + $newSet = array(); + // process attributes + for ($i = 0; $i xssAuto) && ((in_array(strtolower($attrSubSet[0]), $this->attrBlacklist)) || (substr($attrSubSet[0], 0, 2) == 'on')))) { + continue; + } + // xss attr value filtering + if ($attrSubSet[1]) { + // strips unicode, hex, etc + $attrSubSet[1] = str_replace('&#', '', $attrSubSet[1]); + // strip normal newline within attr value + $attrSubSet[1] = preg_replace('/\s+/', '', $attrSubSet[1]); + // strip double quotes + $attrSubSet[1] = str_replace('"', '', $attrSubSet[1]); + // [requested feature] convert single quotes from either side to doubles (Single quotes shouldn't be used to pad attr value) + 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]); + } + // auto strip attr's with "javascript: + if (((strpos(strtolower($attrSubSet[1]), 'expression') !== false) &&(strtolower($attrSubSet[0]) == 'style')) || + (strpos(strtolower($attrSubSet[1]), 'javascript:') !== false) || + (strpos(strtolower($attrSubSet[1]), 'behaviour:') !== false) || + (strpos(strtolower($attrSubSet[1]), 'vbscript:') !== false) || + (strpos(strtolower($attrSubSet[1]), 'mocha:') !== false) || + (strpos(strtolower($attrSubSet[1]), 'livescript:') !== false) + ) { + continue; + } + + // if matches user defined array + $attrFound = in_array(strtolower($attrSubSet[0]), $this->attrArray); + // keep this attr on condition + if ((!$attrFound && $this->attrMethod) || ($attrFound && !$this->attrMethod)) { + // attr has value + if ($attrSubSet[1]) { + $newSet[] = $attrSubSet[0] . '="' . $attrSubSet[1] . '"'; + } elseif ($attrSubSet[1] == "0") { + // attr has decimal zero as value + $newSet[] = $attrSubSet[0] . '="0"'; + } else { + // reformat single attributes to XHTML + $newSet[] = $attrSubSet[0] . '="' . $attrSubSet[0] . '"'; + } + } + } + return $newSet; + } + + /** + * Try to convert to plaintext + * @access protected + * @param String $source + * @return String $source + */ + public function decode($source) + { + // url decode + $source = html_entity_decode($source, ENT_QUOTES, "ISO-8859-1"); + // convert decimal + $source = preg_replace('/&#(\d+);/me',"chr(\\1)", $source);// decimal notation + // convert hex + $source = preg_replace('/&#x([a-f0-9]+);/mei',"chr(0x\\1)", $source);// hex notation + return $source; + } + + /** + * Method to be called by another php script. Processes for SQL injection + * @access public + * @param Mixed $source - input string/array-of-string to be 'cleaned' + * @param Buffer $connection - An open MySQL connection + * @return String $source - 'cleaned' version of input parameter + */ + public function safeSQL($source, &$connection) + { + // clean all elements in this array + if (is_array($source)) { + foreach ($source as $key => $value) { + // filter element for SQL injection + if (is_string($value)) { + $source[$key] = $this->quoteSmart($this->decode($value), $connection); + } + } + return $source; + // clean this string + } elseif (is_string($source)) { + // filter source for SQL injection + if (is_string($source)) { + return $this->quoteSmart($this->decode($source), $connection); + } + // return parameter as given + } else { + return $source; + } + } + + /** + * @author Chris Tobin + * @author Daniel Morris + * @access protected + * @param String $source + * @param Resource $connection - An open MySQL connection + * @return String $source + */ + public function quoteSmart($source, &$connection) + { + // strip slashes + if (get_magic_quotes_gpc()) { + $source = stripslashes($source); + } + // quote both numeric and text + $source = $this->escapeString($source, $connection); + return $source; + } + + /** + * @author Chris Tobin + * @author Daniel Morris + * @access protected + * @param String $source + * @param Resource $connection - An open MySQL connection + * @return String $source + */ + public function escapeString($string, &$connection) + { + // depreciated function + if (version_compare(phpversion(),"4.3.0", "<")) { + mysql_escape_string($string); + } else { + // current function + mysql_real_escape_string($string); + } + return $string; + } +} + diff --git a/workflow/engine/controllers/dashboard.php b/workflow/engine/controllers/dashboard.php index ad98e97c1..e98ad5a9b 100644 --- a/workflow/engine/controllers/dashboard.php +++ b/workflow/engine/controllers/dashboard.php @@ -155,7 +155,7 @@ class Dashboard extends Controller if ($data->DAS_INS_UID == '') { throw new Exception( 'Parameter "DAS_INS_UID" is empty.' ); } - $this->pmDashlet->setup( $data->DAS_INS_UID ); + $this->pmDashlet->setup( G::sanitizeInput($data->DAS_INS_UID) ); if (! isset( $_REQUEST['w'] )) { $width = 300; diff --git a/workflow/engine/methods/cases/casesMenuLoader.php b/workflow/engine/methods/cases/casesMenuLoader.php index 8bab327a1..a74874e97 100755 --- a/workflow/engine/methods/cases/casesMenuLoader.php +++ b/workflow/engine/methods/cases/casesMenuLoader.php @@ -1,5 +1,7 @@ redirectTo($_SESSION['USER_LOGGED'], $lang); } diff --git a/workflow/engine/methods/setup/mainAjax.php b/workflow/engine/methods/setup/mainAjax.php index 54937ba0e..d3cb2c9fa 100755 --- a/workflow/engine/methods/setup/mainAjax.php +++ b/workflow/engine/methods/setup/mainAjax.php @@ -1,7 +1,7 @@