Filter Attributes

Filters tag attributes for consistency and safety

@param
string
@return string
968513
protected function _filter_attributes($str) {
		$out = '';
		
		if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches)) {
			foreach ($matches[0] as $match) {
				$out .= preg_replace("#/\*.*?\*/#s", '', $match);
			}
		}
		
		return $out;
	}
316720
private function parse_attributes($attr)
    {
        $html = '';

        if (!empty($attr)) {
            $html = '';

            foreach ($attr as $key => $value) {
                $html .= \sprintf('%s="%s" ', $key, \esc_attr($value));
            }
        }

        return \trim($html);
    }
593680
public function setFilterAttribute($value)
    {
        return ($value) ? $this->attributes['filter'] = json_encode($value) : $this->attributes['filter'] = null;
    }
875794
def matching_attributes?(attribute, filter)
      attribute.to_s.downcase == filter.to_s.delete('_').downcase
    end
25122
public function attribute($tag): string
    {
        if (preg_match('/\n2 (?:' . $tag . ') ?(.*(?:(?:\n3 CONT ?.*)*)*)/', $this->gedcom, $match)) {
            return preg_replace("/\n3 CONT ?/", "\n", $match[1]);
        }

        return '';
    }
934312
public function getAttributes()
    {

        // initialize the array with the attributes
        $attributes = array();

        // prepare the pattern to filter the attributes
        $pattern = sprintf('%s-*', $this->getSerial());

        // prepare the array with the attributes
        foreach ($this as $key => $value) {
            if (fnmatch($pattern, $key)) {
                $attributes[$key] = $value;
            }
        }

        // return the attributes
        return $attributes;
    }
663047
public function getTagFiltersExpanded()
    {
        $tagFilters = $this->getTagFilters();
        if ($tagFilters === null) {
            $result = null;
        } else {
            $result = [];
            $m = null;
            foreach ($tagFilters as $tagFilter) {
                if (preg_match('/^\s*([<>=]+)\s*(\d+(?:\.\d+)?)\s*$/', $tagFilter, $m)) {
                    switch ($m[1]) {
                        case '<=':
                        case '<':
                        case '=':
                        case '>=':
                        case '>':
                            $result[] = ['operator' => $m[1], 'version' => $m[2]];
                            break;
                    }
                }
            }
        }

        return $result;
    }
963964
public static function remove_attributes($tag, $attrname) {
        if (is_array($attrname)) {
            $attrname = join('|', $attrname);
        }
        while (preg_match('/^(<[^>]*\b)(' . $attrname . ')=".*?"(.*)$/is', $tag, $matches)) {
            $tag = $matches[1] . $matches[3];
        }
        while (preg_match('~^(<[^>]*\b)(' . $attrname . ')([ />].*)$~is', $tag, $matches)) {
            // Some attributes may not have value, for example: <video controls>.
            $tag = $matches[1] . $matches[3];
        }
        return $tag;
    }
48571
protected function filterAttributeValues(array $attributes)
    {
        return array_filter($attributes, function ($value) {
            if (is_array($value) && empty($value)) {
                return false;
            }

            return !($value === '' || $value === null);
        });
    }
679465
static function createAttributeFilterSQLStrings( &$attributeFilter, &$sortingInfo = array( 'sortCount' => 0, 'attributeJoinCount' => 0 ), $language = false )
    {
        // Check for attribute filtering

        $filterSQL = array( 'from'    => '',
                            'where'   => '' );

        if ( $language !== false && !is_array( $language ) )
        {
            $language = array( $language );
        }

        $totalAttributesFiltersCount = 0;
        $invalidAttributesFiltersCount = 0;

        if ( isset( $attributeFilter ) && $attributeFilter !== false )
        {
            if ( !is_array( $attributeFilter ) )
            {
                eZDebug::writeError( "\$attributeFilter needs to be an array", __METHOD__  );
                return $filterSQL;
            }

            $filterArray = $attributeFilter;

            // Check if first value of array is a string.
            // To check for and/or filtering
            $filterJoinType = 'AND';
            if ( is_string( $filterArray[0] ) )
            {
                if ( strtolower( $filterArray[0] ) == 'or' )
                {
                    $filterJoinType = 'OR';
                }
                else if ( strtolower( $filterArray[0] ) == 'and' )
                {
                    $filterJoinType = 'AND';
                }
                unset( $filterArray[0] );
            }

            $attibuteFilterJoinSQL = "";
            $filterCount = $sortingInfo['sortCount'];
            $justFilterCount = 0;

            $db = eZDB::instance();
            if ( is_array( $filterArray ) )
            {
                // Handle attribute filters and generate SQL
                $totalAttributesFiltersCount = count( $filterArray );

                foreach ( $filterArray as $filter )
                {
                    $isFilterValid = true; // by default assumes that filter is valid

                    $filterAttributeID = $filter[0];
                    $filterType = $filter[1];
                    $filterValue = is_array( $filter[2] ) ? '' : $db->escapeString( $filter[2] );

                    $useAttributeFilter = false;
                    $filterFieldType = 'integer';
                    switch ( $filterAttributeID )
                    {
                        case 'path':
                        {
                            $filterField = 'ezcontentobject_tree.path_string';
                            $filterFieldType = 'string';
                        } break;
                        case 'published':
                        {
                            $filterField = 'ezcontentobject.published';
                        } break;
                        case 'modified':
                        {
                            $filterField = 'ezcontentobject.modified';
                        } break;
                        case 'modified_subnode':
                        {
                            $filterField = 'ezcontentobject_tree.modified_subnode';
                        } break;
                        case 'node_id':
                        {
                            $filterField = 'ezcontentobject_tree.node_id';
                        } break;
                        case 'contentobject_id':
                        {
                            $filterField = 'ezcontentobject_tree.contentobject_id';
                        } break;
                        case 'section':
                        {
                            $filterField = 'ezcontentobject.section_id';
                        } break;
                        case 'state':
                        {
                            // state only supports =, !=, in, and not_in
                            // other operators do not make any sense in this context
                            $hasFilterOperator = true;

                            switch ( $filterType )
                            {
                                case '=' :
                                case '!=':
                                {
                                    $subQueryCondition = 'contentobject_state_id = ' . (int) $filter[2];
                                    $filterOperator = ( $filterType == '=' ? 'IN' : 'NOT IN' );
                                } break;

                                case 'in':
                                case 'not_in' :
                                {
                                    if ( is_array( $filter[2] ) )
                                    {
                                        $subQueryCondition = $db->generateSQLINStatement( $filter[2], 'contentobject_state_id', false, false, 'int' );
                                        $filterOperator = ( $filterType == 'in' ? 'IN' : 'NOT IN' );
                                    }
                                    else
                                    {
                                        $hasFilterOperator = false;
                                    }
                                } break;

                                default :
                                {
                                    $hasFilterOperator = false;
                                    eZDebug::writeError( "Unknown attribute filter type for state: $filterType", __METHOD__ );
                                } break;
                            }

                            if ( $hasFilterOperator )
                            {
                                if ( ( $filterCount - $sortingInfo['sortCount'] ) > 0 )
                                    $attibuteFilterJoinSQL .= " $filterJoinType ";

                                $attibuteFilterJoinSQL .= "ezcontentobject.id $filterOperator (SELECT contentobject_id FROM ezcobj_state_link WHERE $subQueryCondition)";

                                $filterCount++;
                                $justFilterCount++;
                            }

                            continue 2;
                        } break;
                        case 'depth':
                        {
                            $filterField = 'ezcontentobject_tree.depth';
                        } break;
                        case 'class_identifier':
                        {
                            $filterField = 'ezcontentclass.identifier';
                            $filterFieldType = 'string';
                        } break;
                        case 'class_name':
                        {
                            $classNameFilter = eZContentClassName::sqlFilter();
                            $filterField = $classNameFilter['nameField'];
                            $filterFieldType = 'string';
                            $filterSQL['from'] .= " INNER JOIN $classNameFilter[from] ON ($classNameFilter[where])";
                        } break;
                        case 'priority':
                        {
                            $filterField = 'ezcontentobject_tree.priority';
                        } break;
                        case 'name':
                        {
                            $filterField = 'ezcontentobject_name.name';
                            $filterFieldType = 'string';
                        } break;
                        case 'owner':
                        {
                            $filterField = 'ezcontentobject.owner_id';
                        } break;
                        case 'visibility':
                        {
                            $filterValue = ( $filterValue == '1' ) ? 0 : 1;
                            $filterField = 'ezcontentobject_tree.is_invisible';
                        } break;
                        default:
                        {
                            $useAttributeFilter = true;
                        } break;
                    }

                    if ( $useAttributeFilter )
                    {
                        if ( !is_numeric( $filterAttributeID ) )
                            $filterAttributeID = eZContentObjectTreeNode::classAttributeIDByIdentifier( $filterAttributeID );

                        if ( $filterAttributeID === false )
                        {
                            $isFilterValid = false;
                            if( $filterJoinType === 'AND' )
                            {
                                // go out
                                $invalidAttributesFiltersCount = $totalAttributesFiltersCount;
                                break;
                            }

                            ++$invalidAttributesFiltersCount;
                        }
                        else
                        {
                            // Check datatype for filtering
                            $filterDataType = eZContentObjectTreeNode::sortKeyByClassAttributeID( $filterAttributeID );
                            if ( $filterDataType === false )
                            {
                                $isFilterValid = false;
                                if( $filterJoinType === 'AND' )
                                {
                                    // go out
                                    $invalidAttributesFiltersCount = $totalAttributesFiltersCount;
                                    break;
                                }

                                // check next filter
                                ++$invalidAttributesFiltersCount;
                            }
                            else
                            {
                                $sortKey = false;
                                if ( $filterDataType == 'string' )
                                {
                                    $sortKey = 'sort_key_string';
                                    $filterFieldType = 'string';
                                }
                                else
                                {
                                    $sortKey = 'sort_key_int';
                                }

                                $filterField = "a$filterCount.$sortKey";

                                // Use the same joins as we do when sorting,
                                // if more attributes are filtered by we will append them
                                if ( $filterCount >= $sortingInfo['attributeJoinCount'] )
                                {
                                    $filterSQL['from']  .= " INNER JOIN ezcontentobject_attribute a$filterCount ON (a$filterCount.contentobject_id = ezcontentobject.id) ";
                                }

                                // Ref http://issues.ez.no/19190
                                // If language param is set, we must take it into account.
                                if ( $language )
                                {
                                    eZContentLanguage::setPrioritizedLanguages( $language );
                                }
                                $filterSQL['where'] .= "
                                  a$filterCount.contentobject_id = ezcontentobject.id AND
                                  a$filterCount.contentclassattribute_id = $filterAttributeID AND
                                  a$filterCount.version = ezcontentobject_name.content_version AND ";
                                $filterSQL['where'] .= eZContentLanguage::sqlFilter( "a$filterCount", 'ezcontentobject' ). ' AND ';
                                if ( $language )
                                {
                                    eZContentLanguage::clearPrioritizedLanguages();
                                }
                            }
                        }
                    }

                    if ( $isFilterValid )
                    {
                        $hasFilterOperator = true;
                        // Controls quotes around filter value, some filters do this manually
                        $noQuotes = false;
                        // Controls if $filterValue or $filter[2] is used, $filterValue is already escaped
                        $unEscape = false;

                        switch ( $filterType )
                        {
                            case '=' :
                            {
                                $filterOperator = '=';
                            }break;

                            case '!=' :
                            {
                                $filterOperator = '<>';
                            }break;

                            case '>' :
                            {
                                $filterOperator = '>';
                            }break;

                            case '<' :
                            {
                                $filterOperator = '<';
                            }break;

                            case '<=' :
                            {
                                $filterOperator = '<=';
                            }break;

                            case '>=' :
                            {
                                $filterOperator = '>=';
                            }break;

                            case 'like':
                            case 'not_like':
                            {
                                $filterOperator = ( $filterType == 'like' ? 'LIKE' : 'NOT LIKE' );
                                // We escape the string ourselves, this MUST be done before wildcard replace
                                $filter[2] = $db->escapeString( $filter[2] );
                                $unEscape = true;
                                // Since * is used as wildcard we need to transform the string to
                                // use % as wildcard. The following rules apply:
                                // - % -> \%
                                // - * -> %
                                // - \* -> *
                                // - \\ -> \

                                $filter[2] = preg_replace( array( '#%#m',
                                                                  '#(?<!\\\\)\\*#m',
                                                                  '#(?<!\\\\)\\\\\\*#m',
                                                                  '#\\\\\\\\#m' ),
                                                           array( '\\%',
                                                                  '%',
                                                                  '*',
                                                                  '\\\\' ),
                                                           $filter[2] );
                            } break;

                            case 'in':
                            case 'not_in' :
                            {
                                $filterOperator = ( $filterType == 'in' ? 'IN' : 'NOT IN' );
                                // Turn off quotes for value, we do this ourselves
                                $noQuotes = true;
                                if ( is_array( $filter[2] ) )
                                {
                                    reset( $filter[2] );
                                    foreach ( $filter[2] as $key => $value )
                                    {
                                        // Non-numerics must be escaped to avoid SQL injection
                                        switch ( $filterFieldType )
                                        {
                                            case 'string':
                                                $filter[2][$key] = "'" . $db->escapeString( $value ) . "'";
                                                break;
                                            case 'integer':
                                            default:
                                                $filter[2][$key] = (int) $value;
                                        }
                                    }
                                    $filterValue = '(' .  implode( ",", $filter[2] ) . ')';
                                }
                                else
                                {
                                    $hasFilterOperator = false;
                                }
                            } break;

                            case 'between':
                            case 'not_between' :
                            {
                                $filterOperator = ( $filterType == 'between' ? 'BETWEEN' : 'NOT BETWEEN' );
                                // Turn off quotes for value, we do this ourselves
                                $noQuotes = true;
                                if ( is_array( $filter[2] ) )
                                {
                                    // Check for non-numerics to avoid SQL injection
                                    if ( !is_numeric( $filter[2][0] ) )
                                        $filter[2][0] = "'" . $db->escapeString( $filter[2][0] ) . "'";
                                    if ( !is_numeric( $filter[2][1] ) )
                                        $filter[2][1] = "'" . $db->escapeString( $filter[2][1] ) . "'";

                                    $filterValue = $filter[2][0] . ' AND ' . $filter[2][1];
                                }
                            } break;

                            default :
                            {
                                $hasFilterOperator = false;
                                eZDebug::writeError( "Unknown attribute filter type: $filterType", __METHOD__ );
                            }break;

                        }
                        if ( $hasFilterOperator )
                        {
                            if ( ( $filterCount - $sortingInfo['sortCount'] ) > 0 )
                                $attibuteFilterJoinSQL .= " $filterJoinType ";

                            // If $unEscape is true we get the filter value from the 2nd element instead
                            // which must have been escaped by filter type
                            $filterValue = $unEscape ? $filter[2] : $filterValue;

                            $attibuteFilterJoinSQL .= "$filterField $filterOperator ";
                            $attibuteFilterJoinSQL .= $noQuotes ? "$filterValue " : "'$filterValue' ";

                            $filterCount++;
                            $justFilterCount++;
                        }
                    }
                } // end of 'foreach ( $filterArray as $filter )'

                if ( $totalAttributesFiltersCount == $invalidAttributesFiltersCount )
                {
                    eZDebug::writeNotice( "Attribute filter returned false" );
                    $filterSQL = false;
                }
                else
                {
                    if ( $justFilterCount > 0 )
                        $filterSQL['where'] .= "                            ( " . $attibuteFilterJoinSQL . " ) AND ";
                }
            } // end of 'if ( is_array( $filterArray ) )'
        }

        return $filterSQL;
    }
500489
static function basic($string, $filterIn = array("Tidy","Dom","Striptags"), $filterOut = "none") {
        $string = self::tidyUp($string, $filterIn);
        $string = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $string);
        
        // fix &entitiy\n;
        $string = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u', "$1;", $string);
        $string = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "$1$2;", $string);

        $string = html_entity_decode($string, ENT_COMPAT, "UTF-8");
        
        // remove any attribute starting with "on" or xmlns
        $string = preg_replace('#(<[^>]+[\x00-\x20\"\'\/])(on|xmlns)[^>]*>#iUu', "$1>", $string);
        
        // remove javascript: and vbscript: protocol
        $string = preg_replace('#([a-z]*)[\x00-\x20\/]*=[\x00-\x20\/]*([\`\'\"]*)[\x00-\x20\/]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iUu', '$1=$2nojavascript...', $string);
        $string = preg_replace('#([a-z]*)[\x00-\x20\/]*=[\x00-\x20\/]*([\`\'\"]*)[\x00-\x20\/]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iUu', '$1=$2novbscript...', $string);
        $string = preg_replace('#([a-z]*)[\x00-\x20\/]*=[\x00-\x20\/]*([\`\'\"]*)[\x00-\x20\/]*-moz-binding[\x00-\x20]*:#Uu', '$1=$2nomozbinding...', $string);
        $string = preg_replace('#([a-z]*)[\x00-\x20\/]*=[\x00-\x20\/]*([\`\'\"]*)[\x00-\x20\/]*data[\x00-\x20]*:#Uu', '$1=$2nodata...', $string);
        
        //remove any style attributes, IE allows too much stupid things in them, eg.
        //<span style="width: expression(alert('Ping!'));"></span> 
        // and in general you really don't want style declarations in your UGC

        $string = preg_replace('#(<[^>]+[\x00-\x20\"\'\/])style[^>]*>#iUu', "$1>", $string);

        //remove namespaced elements (we do not need them...)
        $string = preg_replace('#</*\w+:\w[^>]*>#i', "", $string);
        
        //remove really unwanted tags
        do {
            $oldstring = $string;
            $string = preg_replace('#</*(applet|meta|xml|blink|link|style|script|embed|object|iframe|frame|frameset|ilayer|layer|bgsound|title|base)[^>]*>#i', "", $string);
        } while ($oldstring != $string);
        
        return self::tidyUp($string, $filterOut);
    }
955235
protected function filterAttributes($attributes, $position = 'field')
    {
        $position = strtolower($position);

        $attributes = $this->filterTriggerAttributes($attributes, $position);
        $attributes = $this->filterPresetAttributes($attributes, $position);

        if ($position == 'field' && $this->disabled) {
            $attributes = $attributes + ['disabled' => 'disabled'];
        }

        if ($position == 'field' && $this->readOnly) {
            $attributes = $attributes + ['readonly' => 'readonly'];

            if ($this->type == 'checkbox' || $this->type == 'switch') {
                $attributes = $attributes + ['onclick' => 'return false;'];
            }
        }

        return $attributes;
    }
889676
protected function filter()
    {
        $filter = null;

        if (   (!$filter = $this->is("classFilter"))
            && (!$filter = $this->is("idFilter"))
            && (!$filter = $this->is("attrFilter"))
            && (!$filter = $this->is("pseudoFilter"))
        ) {
            return false;
        }
        return $filter;
    }
1073927
public function parseAttributes($attributes)
    {
        if($attributes) {
            $htmlMeta = ' ';
            foreach ($attributes as $key => $value) {
                // false async means no async
                if ($key == 'async' && $value == false) {
                    continue;
                }
                //convert boolean to string
                if ($value === false) {
                    $value = 'false';
                } else if ($value === true) {
                    $value = 'true';
                }

                $htmlMeta .= $key . '="' . $value . '" ';
            }
            return $htmlMeta;

        }
        return;
    }
1157571
public function getFilterHtmlOptions()
    {
        $r = '';
        if ($this->htmlClass) {
            $this->filterHtmlOptions['class'] = (isset($this->filterHtmlOptions['class']) ? $this->filterHtmlOptions['class'] . ' ' : '') . $this->htmlClass;
        }
        foreach ($this->filterHtmlOptions as $k => $v)
            $r .= "$k = '$v' ";
        return $r;
    }
633891
public static function filterStr($value)
    {
        $value = str_replace(["\0", "%00", "\r"], '', $value);
        $value = preg_replace(['/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/', '/&(?!(#[0-9]+|[a-z]+);)/is'], ['', '&amp;'], $value);
        $value = str_replace(["%3C", '<'], '&lt;', $value);
        $value = str_replace(["%3E", '>'], '&gt;', $value);
        $value = str_replace(['"', "'", "\t", '  '], ['&quot;', '&#39;', '    ', '&nbsp;&nbsp;'], $value);
        return $value;
    }
887634
public static function filterAttribute($value)
    {
        // Check whether current string has already been encoded
        $isEncoded = TextUtils::strModified($value, function($target){
            return self::escape($target);
        });

        // Decode if previous encoded or escaped
        if ($isEncoded) {
            $value = self::charsDecode($value);
        }

        // Convert special characters to make safe use of them
        $value = self::specialChars($value);

        return $value;
    }