[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/joomla/filter/src/ -> InputFilter.php (source)

   1  <?php
   2  /**
   3   * Part of the Joomla Framework Filter Package
   4   *
   5   * @copyright  Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
   6   * @license    GNU General Public License version 2 or later; see LICENSE
   7   */
   8  
   9  namespace Joomla\Filter;
  10  
  11  use Joomla\String\StringHelper;
  12  
  13  /**
  14   * InputFilter is a class for filtering input from any data source
  15   *
  16   * Forked from the php input filter library by: Daniel Morris <[email protected]>
  17   * Original Contributors: Gianpaolo Racca, Ghislain Picard, Marco Wandschneider, Chris Tobin and Andrew Eddie.
  18   *
  19   * @since  1.0
  20   */
  21  class InputFilter
  22  {
  23      /**
  24       * Defines the InputFilter instance should only allow the supplied list of HTML tags.
  25       *
  26       * @var    integer
  27       * @since  1.4.0
  28       */
  29      const ONLY_ALLOW_DEFINED_TAGS = 0;
  30  
  31      /**
  32       * Defines the InputFilter instance should block the defined list of HTML tags and allow all others.
  33       *
  34       * @var    integer
  35       * @since  1.4.0
  36       */
  37      const ONLY_BLOCK_DEFINED_TAGS = 1;
  38  
  39      /**
  40       * Defines the InputFilter instance should only allow the supplied list of attributes.
  41       *
  42       * @var    integer
  43       * @since  1.4.0
  44       */
  45      const ONLY_ALLOW_DEFINED_ATTRIBUTES = 0;
  46  
  47      /**
  48       * Defines the InputFilter instance should block the defined list of attributes and allow all others.
  49       *
  50       * @var    integer
  51       * @since  1.4.0
  52       */
  53      const ONLY_BLOCK_DEFINED_ATTRIBUTES = 1;
  54  
  55      /**
  56       * The array of permitted tags.
  57       *
  58       * @var    array
  59       * @since  1.0
  60       */
  61      public $tagsArray;
  62  
  63      /**
  64       * The array of permitted tag attributes.
  65       *
  66       * @var    array
  67       * @since  1.0
  68       */
  69      public $attrArray;
  70  
  71      /**
  72       * The method for sanitising tags
  73       *
  74       * @var    integer
  75       * @since  1.0
  76       */
  77      public $tagsMethod;
  78  
  79      /**
  80       * The method for sanitising attributes
  81       *
  82       * @var    integer
  83       * @since  1.0
  84       */
  85      public $attrMethod;
  86  
  87      /**
  88       * A flag for XSS checks. Only auto clean essentials = 0, Allow clean blocked tags/attr = 1
  89       *
  90       * @var    integer
  91       * @since  1.0
  92       */
  93      public $xssAuto;
  94  
  95      /**
  96       * The list the blocked tags for the instance.
  97       *
  98       * @var    string[]
  99       * @since  1.0
 100       */
 101      public $blockedTags = [
 102          'applet',
 103          'body',
 104          'bgsound',
 105          'base',
 106          'basefont',
 107          'canvas',
 108          'embed',
 109          'frame',
 110          'frameset',
 111          'head',
 112          'html',
 113          'id',
 114          'iframe',
 115          'ilayer',
 116          'layer',
 117          'link',
 118          'meta',
 119          'name',
 120          'object',
 121          'script',
 122          'style',
 123          'title',
 124          'xml',
 125      ];
 126  
 127      /**
 128       * The list of blocked tag attributes for the instance.
 129       *
 130       * @var    string[]
 131       * @since  1.0
 132       */
 133      public $blockedAttributes = [
 134          'action',
 135          'background',
 136          'codebase',
 137          'dynsrc',
 138          'formaction',
 139          'lowsrc',
 140      ];
 141  
 142      /**
 143       * A special list of blocked characters.
 144       *
 145       * @var    string[]
 146       * @since  1.3.3
 147       */
 148      private $blockedChars = [
 149          '&tab;',
 150          '&space;',
 151          '&colon;',
 152          '&column;',
 153      ];
 154  
 155      /**
 156       * Constructor for InputFilter class.
 157       *
 158       * @param   array    $tagsArray   List of permitted HTML tags
 159       * @param   array    $attrArray   List of permitted HTML tag attributes
 160       * @param   integer  $tagsMethod  Method for filtering tags, should be one of the `ONLY_*_DEFINED_TAGS` constants
 161       * @param   integer  $attrMethod  Method for filtering attributes, should be one of the `ONLY_*_DEFINED_ATTRIBUTES` constants
 162       * @param   integer  $xssAuto     Only auto clean essentials = 0, Allow clean blocked tags/attributes = 1
 163       *
 164       * @since   1.0
 165       */
 166  	public function __construct(array $tagsArray = [], array $attrArray = [], $tagsMethod = self::ONLY_ALLOW_DEFINED_TAGS,
 167          $attrMethod = self::ONLY_ALLOW_DEFINED_ATTRIBUTES, $xssAuto = 1
 168      )
 169      {
 170          // Make sure user defined arrays are in lowercase
 171          $tagsArray = array_map('strtolower', (array) $tagsArray);
 172          $attrArray = array_map('strtolower', (array) $attrArray);
 173  
 174          // Assign member variables
 175          $this->tagsArray  = $tagsArray;
 176          $this->attrArray  = $attrArray;
 177          $this->tagsMethod = $tagsMethod;
 178          $this->attrMethod = $attrMethod;
 179          $this->xssAuto    = $xssAuto;
 180      }
 181  
 182      /**
 183       * Cleans the given input source based on the instance configuration and specified data type
 184       *
 185       * @param   string|string[]|object  $source  Input string/array-of-string/object to be 'cleaned'
 186       * @param   string                  $type    The return type for the variable:
 187       *                                           INT:       An integer
 188       *                                           UINT:      An unsigned integer
 189       *                                           FLOAT:     A floating point number
 190       *                                           BOOLEAN:   A boolean value
 191       *                                           WORD:      A string containing A-Z or underscores only (not case sensitive)
 192       *                                           ALNUM:     A string containing A-Z or 0-9 only (not case sensitive)
 193       *                                           CMD:       A string containing A-Z, 0-9, underscores, periods or hyphens (not case
 194       *                                                      sensitive)
 195       *                                           BASE64:    A string containing A-Z, 0-9, forward slashes, plus or equals (not case
 196       *                                                      sensitive)
 197       *                                           STRING:    A fully decoded and sanitised string (default)
 198       *                                           HTML:      A sanitised string
 199       *                                           ARRAY:     An array
 200       *                                           PATH:      A sanitised file path
 201       *                                           TRIM:      A string trimmed from normal, non-breaking and multibyte spaces
 202       *                                           USERNAME:  Do not use (use an application specific filter)
 203       *                                           RAW:       The raw string is returned with no filtering
 204       *                                           unknown:   An unknown filter will act like STRING. If the input is an array it will
 205       *                                                      return an array of fully decoded and sanitised strings.
 206       *
 207       * @return  mixed  'Cleaned' version of the `$source` parameter
 208       *
 209       * @since   1.0
 210       */
 211  	public function clean($source, $type = 'string')
 212      {
 213          $type = ucfirst(strtolower($type));
 214  
 215          if ($type === 'Array')
 216          {
 217              return (array) $source;
 218          }
 219  
 220          if ($type === 'Raw')
 221          {
 222              return $source;
 223          }
 224  
 225          if (\is_array($source))
 226          {
 227              $result = [];
 228  
 229              foreach ($source as $key => $value)
 230              {
 231                  $result[$key] = $this->clean($value, $type);
 232              }
 233  
 234              return $result;
 235          }
 236  
 237          if (\is_object($source))
 238          {
 239              foreach (get_object_vars($source) as $key => $value)
 240              {
 241                  $source->$key = $this->clean($value, $type);
 242              }
 243  
 244              return $source;
 245          }
 246  
 247          $method = 'clean' . $type;
 248  
 249          if (method_exists($this, $method))
 250          {
 251              return $this->$method((string) $source);
 252          }
 253  
 254          // Unknown filter method
 255          if (\is_string($source) && !empty($source))
 256          {
 257              // Filter source for XSS and other 'bad' code etc.
 258              return $this->cleanString($source);
 259          }
 260  
 261          // Not an array or string... return the passed parameter
 262          return $source;
 263      }
 264  
 265      /**
 266       * Function to determine if contents of an attribute are safe
 267       *
 268       * @param   array  $attrSubSet  A 2 element array for attribute's name, value
 269       *
 270       * @return  boolean  True if bad code is detected
 271       *
 272       * @since   1.0
 273       */
 274  	public static function checkAttribute($attrSubSet)
 275      {
 276          $attrSubSet[0] = strtolower($attrSubSet[0]);
 277          $attrSubSet[1] = html_entity_decode(strtolower($attrSubSet[1]), ENT_QUOTES | ENT_HTML401, 'UTF-8');
 278  
 279          return (strpos($attrSubSet[1], 'expression') !== false && $attrSubSet[0] === 'style')
 280              || preg_match('/(?:(?:java|vb|live)script|behaviour|mocha)(?::|&colon;|&column;)/', $attrSubSet[1]) !== 0;
 281      }
 282  
 283      /**
 284       * Internal method to iteratively remove all unwanted tags and attributes
 285       *
 286       * @param   string  $source  Input string to be 'cleaned'
 287       *
 288       * @return  string  'Cleaned' version of input parameter
 289       *
 290       * @since   1.0
 291       */
 292  	protected function remove($source)
 293      {
 294          // Iteration provides nested tag protection
 295          do
 296          {
 297              $temp   = $source;
 298              $source = $this->cleanTags($source);
 299          }
 300          while ($temp !== $source);
 301  
 302          return $source;
 303      }
 304  
 305      /**
 306       * Internal method to strip a string of disallowed tags
 307       *
 308       * @param   string  $source  Input string to be 'cleaned'
 309       *
 310       * @return  string  'Cleaned' version of input parameter
 311       *
 312       * @since   1.0
 313       */
 314  	protected function cleanTags($source)
 315      {
 316          // First, pre-process this for illegal characters inside attribute values
 317          $source = $this->escapeAttributeValues($source);
 318  
 319          // In the beginning we don't really have a tag, so everything is postTag
 320          $preTag       = null;
 321          $postTag      = $source;
 322          $currentSpace = false;
 323  
 324          // Setting to null to deal with undefined variables
 325          $attr = '';
 326  
 327          // Is there a tag? If so it will certainly start with a '<'.
 328          $tagOpenStart = StringHelper::strpos($source, '<');
 329  
 330          while ($tagOpenStart !== false)
 331          {
 332              // Get some information about the tag we are processing
 333              $preTag .= StringHelper::substr($postTag, 0, $tagOpenStart);
 334              $postTag     = StringHelper::substr($postTag, $tagOpenStart);
 335              $fromTagOpen = StringHelper::substr($postTag, 1);
 336              $tagOpenEnd  = StringHelper::strpos($fromTagOpen, '>');
 337  
 338              // Check for mal-formed tag where we have a second '<' before the first '>'
 339              $nextOpenTag = (StringHelper::strlen($postTag) > $tagOpenStart) ? StringHelper::strpos($postTag, '<', $tagOpenStart + 1) : false;
 340  
 341              if (($nextOpenTag !== false) && ($nextOpenTag < $tagOpenEnd))
 342              {
 343                  // At this point we have a mal-formed tag -- remove the offending open
 344                  $postTag      = StringHelper::substr($postTag, 0, $tagOpenStart) . StringHelper::substr($postTag, $tagOpenStart + 1);
 345                  $tagOpenStart = StringHelper::strpos($postTag, '<');
 346  
 347                  continue;
 348              }
 349  
 350              // Let's catch any non-terminated tags and skip over them
 351              if ($tagOpenEnd === false)
 352              {
 353                  $postTag      = StringHelper::substr($postTag, $tagOpenStart + 1);
 354                  $tagOpenStart = StringHelper::strpos($postTag, '<');
 355  
 356                  continue;
 357              }
 358  
 359              // Do we have a nested tag?
 360              $tagOpenNested = StringHelper::strpos($fromTagOpen, '<');
 361  
 362              if (($tagOpenNested !== false) && ($tagOpenNested < $tagOpenEnd))
 363              {
 364                  $preTag       .= StringHelper::substr($postTag, 1, $tagOpenNested);
 365                  $postTag      = StringHelper::substr($postTag, ($tagOpenNested + 1));
 366                  $tagOpenStart = StringHelper::strpos($postTag, '<');
 367  
 368                  continue;
 369              }
 370  
 371              // Let's get some information about our tag and setup attribute pairs
 372              $tagOpenNested = (StringHelper::strpos($fromTagOpen, '<') + $tagOpenStart + 1);
 373              $currentTag    = StringHelper::substr($fromTagOpen, 0, $tagOpenEnd);
 374              $tagLength     = StringHelper::strlen($currentTag);
 375              $tagLeft       = $currentTag;
 376              $attrSet       = [];
 377              $currentSpace  = StringHelper::strpos($tagLeft, ' ');
 378  
 379              // Are we an open tag or a close tag?
 380              if (StringHelper::substr($currentTag, 0, 1) === '/')
 381              {
 382                  // Close Tag
 383                  $isCloseTag    = true;
 384                  list($tagName) = explode(' ', $currentTag);
 385                  $tagName       = StringHelper::substr($tagName, 1);
 386              }
 387              else
 388              {
 389                  // Open Tag
 390                  $isCloseTag    = false;
 391                  list($tagName) = explode(' ', $currentTag);
 392              }
 393  
 394              /*
 395               * Exclude all "non-regular" tagnames
 396               * OR no tagname
 397               * OR remove if xssauto is on and tag is blocked
 398               */
 399              if ((!preg_match('/^[a-z][a-z0-9]*$/i', $tagName))
 400                  || (!$tagName)
 401                  || ((\in_array(strtolower($tagName), $this->blockedTags)) && $this->xssAuto))
 402              {
 403                  $postTag      = StringHelper::substr($postTag, ($tagLength + 2));
 404                  $tagOpenStart = StringHelper::strpos($postTag, '<');
 405  
 406                  // Strip tag
 407                  continue;
 408              }
 409  
 410              /*
 411               * Time to grab any attributes from the tag... need this section in
 412               * case attributes have spaces in the values.
 413               */
 414              while ($currentSpace !== false)
 415              {
 416                  $attr        = '';
 417                  $fromSpace   = StringHelper::substr($tagLeft, ($currentSpace + 1));
 418                  $nextEqual   = StringHelper::strpos($fromSpace, '=');
 419                  $nextSpace   = StringHelper::strpos($fromSpace, ' ');
 420                  $openQuotes  = StringHelper::strpos($fromSpace, '"');
 421                  $closeQuotes = StringHelper::strpos(StringHelper::substr($fromSpace, ($openQuotes + 1)), '"') + $openQuotes + 1;
 422  
 423                  $startAtt         = '';
 424                  $startAttPosition = 0;
 425  
 426                  // Find position of equal and open quotes ignoring
 427                  if (preg_match('#\s*=\s*\"#', $fromSpace, $matches, \PREG_OFFSET_CAPTURE))
 428                  {
 429                      // We have found an attribute, convert its byte position to a UTF-8 string length, using non-multibyte substr()
 430                      $stringBeforeAttr = substr($fromSpace, 0, $matches[0][1]);
 431                      $startAttPosition = StringHelper::strlen($stringBeforeAttr);
 432                      $startAtt         = $matches[0][0];
 433                      $closeQuotePos    = StringHelper::strpos(
 434                          StringHelper::substr($fromSpace, ($startAttPosition + StringHelper::strlen($startAtt))), '"'
 435                      );
 436                      $closeQuotes = $closeQuotePos + $startAttPosition + StringHelper::strlen($startAtt);
 437                      $nextEqual   = $startAttPosition + StringHelper::strpos($startAtt, '=');
 438                      $openQuotes  = $startAttPosition + StringHelper::strpos($startAtt, '"');
 439                      $nextSpace   = StringHelper::strpos(StringHelper::substr($fromSpace, $closeQuotes), ' ') + $closeQuotes;
 440                  }
 441  
 442                  // Do we have an attribute to process? [check for equal sign]
 443                  if ($fromSpace !== '/' && (($nextEqual && $nextSpace && $nextSpace < $nextEqual) || !$nextEqual))
 444                  {
 445                      if (!$nextEqual)
 446                      {
 447                          $attribEnd = StringHelper::strpos($fromSpace, '/') - 1;
 448                      }
 449                      else
 450                      {
 451                          $attribEnd = $nextSpace - 1;
 452                      }
 453  
 454                      // If there is an ending, use this, if not, do not worry.
 455                      if ($attribEnd > 0)
 456                      {
 457                          $fromSpace = StringHelper::substr($fromSpace, $attribEnd + 1);
 458                      }
 459                  }
 460  
 461                  if (StringHelper::strpos($fromSpace, '=') !== false)
 462                  {
 463                      /*
 464                       * If the attribute value is wrapped in quotes we need to grab the substring from the closing quote,
 465                       * otherwise grab until the next space.
 466                       */
 467                      if (($openQuotes !== false)
 468                          && (StringHelper::strpos(StringHelper::substr($fromSpace, ($openQuotes + 1)), '"') !== false))
 469                      {
 470                          $attr = StringHelper::substr($fromSpace, 0, ($closeQuotes + 1));
 471                      }
 472                      else
 473                      {
 474                          $attr = StringHelper::substr($fromSpace, 0, $nextSpace);
 475                      }
 476                  }
 477                  else
 478                  {
 479                      // No more equal signs so add any extra text in the tag into the attribute array [eg. checked]
 480                      if ($fromSpace !== '/')
 481                      {
 482                          $attr = StringHelper::substr($fromSpace, 0, $nextSpace);
 483                      }
 484                  }
 485  
 486                  // Last Attribute Pair
 487                  if (!$attr && $fromSpace !== '/')
 488                  {
 489                      $attr = $fromSpace;
 490                  }
 491  
 492                  // Add attribute pair to the attribute array
 493                  $attrSet[] = $attr;
 494  
 495                  // Move search point and continue iteration
 496                  $tagLeft      = StringHelper::substr($fromSpace, StringHelper::strlen($attr));
 497                  $currentSpace = StringHelper::strpos($tagLeft, ' ');
 498              }
 499  
 500              // Is our tag in the user input array?
 501              $tagFound = \in_array(strtolower($tagName), $this->tagsArray);
 502  
 503              // If the tag is allowed let's append it to the output string.
 504              if ((!$tagFound && $this->tagsMethod) || ($tagFound && !$this->tagsMethod))
 505              {
 506                  // Reconstruct tag with allowed attributes
 507                  if (!$isCloseTag)
 508                  {
 509                      // Open or single tag
 510                      $attrSet = $this->cleanAttributes($attrSet);
 511                      $preTag .= '<' . $tagName;
 512  
 513                      for ($i = 0, $count = \count($attrSet); $i < $count; $i++)
 514                      {
 515                          $preTag .= ' ' . $attrSet[$i];
 516                      }
 517  
 518                      // Reformat single tags to XHTML
 519                      if (StringHelper::strpos($fromTagOpen, '</' . $tagName))
 520                      {
 521                          $preTag .= '>';
 522                      }
 523                      else
 524                      {
 525                          $preTag .= ' />';
 526                      }
 527                  }
 528                  else
 529                  {
 530                      // Closing tag
 531                      $preTag .= '</' . $tagName . '>';
 532                  }
 533              }
 534  
 535              // Find next tag's start and continue iteration
 536              $postTag      = StringHelper::substr($postTag, ($tagLength + 2));
 537              $tagOpenStart = StringHelper::strpos($postTag, '<');
 538          }
 539  
 540          // Append any code after the end of tags and return
 541          if ($postTag !== '<')
 542          {
 543              $preTag .= $postTag;
 544          }
 545  
 546          return $preTag;
 547      }
 548  
 549      /**
 550       * Internal method to strip a tag of disallowed attributes
 551       *
 552       * @param   array  $attrSet  Array of attribute pairs to filter
 553       *
 554       * @return  array  Filtered array of attribute pairs
 555       *
 556       * @since   1.0
 557       */
 558  	protected function cleanAttributes(array $attrSet)
 559      {
 560          $newSet = [];
 561  
 562          $count = \count($attrSet);
 563  
 564          // Iterate through attribute pairs
 565          for ($i = 0; $i < $count; $i++)
 566          {
 567              // Skip blank spaces
 568              if (!$attrSet[$i])
 569              {
 570                  continue;
 571              }
 572  
 573              // Split into name/value pairs
 574              $attrSubSet = explode('=', trim($attrSet[$i]), 2);
 575  
 576              // Take the last attribute in case there is an attribute with no value
 577              $attrSubSet0   = explode(' ', trim($attrSubSet[0]));
 578              $attrSubSet[0] = array_pop($attrSubSet0);
 579  
 580              $attrSubSet[0] = strtolower($attrSubSet[0]);
 581              $quoteStyle    = \ENT_QUOTES | \ENT_HTML401;
 582  
 583              // Remove all spaces as valid attributes does not have spaces.
 584              $attrSubSet[0] = html_entity_decode($attrSubSet[0], $quoteStyle, 'UTF-8');
 585              $attrSubSet[0] = preg_replace('/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $attrSubSet[0]);
 586              $attrSubSet[0] = preg_replace('/\s+/u', '', $attrSubSet[0]);
 587  
 588              // Remove blocked chars from the attribute name
 589              foreach ($this->blockedChars as $blockedChar)
 590              {
 591                  $attrSubSet[0] = str_ireplace($blockedChar, '', $attrSubSet[0]);
 592              }
 593  
 594              // Remove all symbols
 595              $attrSubSet[0] = preg_replace('/[^\p{L}\p{N}\-\s]/u', '', $attrSubSet[0]);
 596  
 597              // Remove all "non-regular" attribute names
 598              // AND blocked attributes
 599              if ((!preg_match('/[a-z]*$/i', $attrSubSet[0]))
 600                  || ($this->xssAuto && ((\in_array(strtolower($attrSubSet[0]), $this->blockedAttributes))
 601                  || substr($attrSubSet[0], 0, 2) == 'on')))
 602              {
 603                  continue;
 604              }
 605  
 606              // XSS attribute value filtering
 607              if (!isset($attrSubSet[1]))
 608              {
 609                  continue;
 610              }
 611  
 612              // Remove blocked chars from the attribute value
 613              foreach ($this->blockedChars as $blockedChar)
 614              {
 615                  $attrSubSet[1] = str_ireplace($blockedChar, '', $attrSubSet[1]);
 616              }
 617  
 618              // Trim leading and trailing spaces
 619              $attrSubSet[1] = trim($attrSubSet[1]);
 620  
 621              // Strips unicode, hex, etc
 622              $attrSubSet[1] = str_replace('&#', '', $attrSubSet[1]);
 623  
 624              // Strip normal newline within attr value
 625              $attrSubSet[1] = preg_replace('/[\n\r]/', '', $attrSubSet[1]);
 626  
 627              // Strip double quotes
 628              $attrSubSet[1] = str_replace('"', '', $attrSubSet[1]);
 629  
 630              // Convert single quotes from either side to doubles (Single quotes shouldn't be used to pad attr values)
 631              if ((substr($attrSubSet[1], 0, 1) == "'") && (substr($attrSubSet[1], (\strlen($attrSubSet[1]) - 1), 1) == "'"))
 632              {
 633                  $attrSubSet[1] = substr($attrSubSet[1], 1, (\strlen($attrSubSet[1]) - 2));
 634              }
 635  
 636              // Strip slashes
 637              $attrSubSet[1] = stripslashes($attrSubSet[1]);
 638  
 639              // Autostrip script tags
 640              if (static::checkAttribute($attrSubSet))
 641              {
 642                  continue;
 643              }
 644  
 645              // Is our attribute in the user input array?
 646              $attrFound = \in_array(strtolower($attrSubSet[0]), $this->attrArray);
 647  
 648              // If the tag is allowed lets keep it
 649              if ((!$attrFound && $this->attrMethod) || ($attrFound && !$this->attrMethod))
 650              {
 651                  // Does the attribute have a value?
 652                  if (empty($attrSubSet[1]) === false)
 653                  {
 654                      $newSet[] = $attrSubSet[0] . '="' . $attrSubSet[1] . '"';
 655                  }
 656                  elseif ($attrSubSet[1] === '0')
 657                  {
 658                      // Special Case
 659                      // Is the value 0?
 660                      $newSet[] = $attrSubSet[0] . '="0"';
 661                  }
 662                  else
 663                  {
 664                      // Leave empty attributes alone
 665                      $newSet[] = $attrSubSet[0] . '=""';
 666                  }
 667              }
 668          }
 669  
 670          return $newSet;
 671      }
 672  
 673      /**
 674       * Try to convert to plaintext
 675       *
 676       * @param   string  $source  The source string.
 677       *
 678       * @return  string  Plaintext string
 679       *
 680       * @since   1.0
 681       * @deprecated  This method will be removed once support for PHP 5.3 is discontinued.
 682       */
 683  	protected function decode($source)
 684      {
 685          return html_entity_decode($source, \ENT_QUOTES, 'UTF-8');
 686      }
 687  
 688      /**
 689       * Escape < > and " inside attribute values
 690       *
 691       * @param   string  $source  The source string.
 692       *
 693       * @return  string  Filtered string
 694       *
 695       * @since   1.0
 696       */
 697  	protected function escapeAttributeValues($source)
 698      {
 699          $alreadyFiltered = '';
 700          $remainder       = $source;
 701          $badChars        = ['<', '"', '>'];
 702          $escapedChars    = ['&lt;', '&quot;', '&gt;'];
 703  
 704          // Process each portion based on presence of =" and "<space>, "/>, or ">
 705          // See if there are any more attributes to process
 706          while (preg_match('#<[^>]*?=\s*?(\"|\')#s', $remainder, $matches, \PREG_OFFSET_CAPTURE))
 707          {
 708              // We have found a tag with an attribute, convert its byte position to a UTF-8 string length, using non-multibyte substr()
 709              $stringBeforeTag = substr($remainder, 0, $matches[0][1]);
 710              $tagPosition     = StringHelper::strlen($stringBeforeTag);
 711  
 712              // Get the character length before the attribute value
 713              $nextBefore = $tagPosition + StringHelper::strlen($matches[0][0]);
 714  
 715              // Figure out if we have a single or double quote and look for the matching closing quote
 716              // Closing quote should be "/>, ">, "<space>, or " at the end of the string
 717              $quote     = StringHelper::substr($matches[0][0], -1);
 718              $pregMatch = ($quote == '"') ? '#(\"\s*/\s*>|\"\s*>|\"\s+|\"$)#' : "#(\'\s*/\s*>|\'\s*>|\'\s+|\'$)#";
 719  
 720              // Get the portion after attribute value
 721              $attributeValueRemainder = StringHelper::substr($remainder, $nextBefore);
 722  
 723              if (preg_match($pregMatch, $attributeValueRemainder, $matches, \PREG_OFFSET_CAPTURE))
 724              {
 725                  // We have a closing quote, convert its byte position to a UTF-8 string length, using non-multibyte substr()
 726                  $stringBeforeQuote = substr($attributeValueRemainder, 0, $matches[0][1]);
 727                  $closeQuoteChars   = StringHelper::strlen($stringBeforeQuote);
 728                  $nextAfter         = $nextBefore + $matches[0][1];
 729              }
 730              else
 731              {
 732                  // No closing quote
 733                  $nextAfter = StringHelper::strlen($remainder);
 734              }
 735  
 736              // Get the actual attribute value
 737              $attributeValue = StringHelper::substr($remainder, $nextBefore, $nextAfter - $nextBefore);
 738  
 739              // Escape bad chars
 740              $attributeValue = str_replace($badChars, $escapedChars, $attributeValue);
 741              $attributeValue = $this->stripCssExpressions($attributeValue);
 742              $alreadyFiltered .= StringHelper::substr($remainder, 0, $nextBefore) . $attributeValue . $quote;
 743              $remainder = StringHelper::substr($remainder, $nextAfter + 1);
 744          }
 745  
 746          // At this point, we just have to return the $alreadyFiltered and the $remainder
 747          return $alreadyFiltered . $remainder;
 748      }
 749  
 750      /**
 751       * Remove CSS Expressions in the form of <property>:expression(...)
 752       *
 753       * @param   string  $source  The source string.
 754       *
 755       * @return  string  Filtered string
 756       *
 757       * @since   1.0
 758       */
 759  	protected function stripCssExpressions($source)
 760      {
 761          // Strip any comments out (in the form of /*...*/)
 762          $test = preg_replace('#\/\*.*\*\/#U', '', $source);
 763  
 764          // Test for :expression
 765          if (!stripos($test, ':expression'))
 766          {
 767              // Not found, so we are done
 768              return $source;
 769          }
 770  
 771          // At this point, we have stripped out the comments and have found :expression
 772          // Test stripped string for :expression followed by a '('
 773          if (preg_match_all('#:expression\s*\(#', $test, $matches))
 774          {
 775              // If found, remove :expression
 776              return str_ireplace(':expression', '', $test);
 777          }
 778  
 779          return $source;
 780      }
 781  
 782      /**
 783       * Integer filter
 784       *
 785       * @param   string  $source  The string to be filtered
 786       *
 787       * @return  integer  The filtered value
 788       */
 789  	private function cleanInt($source)
 790      {
 791          $pattern = '/[-+]?[0-9]+/';
 792  
 793          preg_match($pattern, $source, $matches);
 794  
 795          return isset($matches[0]) ? (int) $matches[0] : 0;
 796      }
 797  
 798      /**
 799       * Alias for cleanInt()
 800       *
 801       * @param   string  $source  The string to be filtered
 802       *
 803       * @return  integer  The filtered value
 804       */
 805  	private function cleanInteger($source)
 806      {
 807          return $this->cleanInt($source);
 808      }
 809  
 810      /**
 811       * Unsigned integer filter
 812       *
 813       * @param   string  $source  The string to be filtered
 814       *
 815       * @return  integer  The filtered value
 816       */
 817  	private function cleanUint($source)
 818      {
 819          $pattern = '/[-+]?[0-9]+/';
 820  
 821          preg_match($pattern, $source, $matches);
 822  
 823          return isset($matches[0]) ? abs((int) $matches[0]) : 0;
 824      }
 825  
 826      /**
 827       * Float filter
 828       *
 829       * @param   string  $source  The string to be filtered
 830       *
 831       * @return  float  The filtered value
 832       */
 833  	private function cleanFloat($source)
 834      {
 835          $pattern = '/[-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?/';
 836  
 837          preg_match($pattern, $source, $matches);
 838  
 839          return isset($matches[0]) ? (float) $matches[0] : 0.0;
 840      }
 841  
 842      /**
 843       * Alias for cleanFloat()
 844       *
 845       * @param   string  $source  The string to be filtered
 846       *
 847       * @return  float  The filtered value
 848       */
 849  	private function cleanDouble($source)
 850      {
 851          return $this->cleanFloat($source);
 852      }
 853  
 854      /**
 855       * Boolean filter
 856       *
 857       * @param   string  $source  The string to be filtered
 858       *
 859       * @return  boolean  The filtered value
 860       */
 861  	private function cleanBool($source)
 862      {
 863          return (bool) $source;
 864      }
 865  
 866      /**
 867       * Alias for cleanBool()
 868       *
 869       * @param   string  $source  The string to be filtered
 870       *
 871       * @return  boolean  The filtered value
 872       */
 873  	private function cleanBoolean($source)
 874      {
 875          return $this->cleanBool($source);
 876      }
 877  
 878      /**
 879       * Word filter
 880       *
 881       * @param   string  $source  The string to be filtered
 882       *
 883       * @return  string  The filtered string
 884       */
 885  	private function cleanWord($source)
 886      {
 887          $pattern = '/[^A-Z_]/i';
 888  
 889          return preg_replace($pattern, '', $source);
 890      }
 891  
 892      /**
 893       * Alphanumerical filter
 894       *
 895       * @param   string  $source  The string to be filtered
 896       *
 897       * @return  string  The filtered string
 898       */
 899  	private function cleanAlnum($source)
 900      {
 901          $pattern = '/[^A-Z0-9]/i';
 902  
 903          return preg_replace($pattern, '', $source);
 904      }
 905  
 906      /**
 907       * Command filter
 908       *
 909       * @param   string  $source  The string to be filtered
 910       *
 911       * @return  string  The filtered string
 912       */
 913  	private function cleanCmd($source)
 914      {
 915          $pattern = '/[^A-Z0-9_\.-]/i';
 916  
 917          $result = preg_replace($pattern, '', $source);
 918          $result = ltrim($result, '.');
 919  
 920          return $result;
 921      }
 922  
 923      /**
 924       * Base64 filter
 925       *
 926       * @param   string  $source  The string to be filtered
 927       *
 928       * @return  string  The filtered string
 929       */
 930  	private function cleanBase64($source)
 931      {
 932          $pattern = '/[^A-Z0-9\/+=]/i';
 933  
 934          return preg_replace($pattern, '', $source);
 935      }
 936  
 937      /**
 938       * String filter
 939       *
 940       * @param   string  $source  The string to be filtered
 941       *
 942       * @return  string  The filtered string
 943       */
 944  	private function cleanString($source)
 945      {
 946          return $this->remove($this->decode($source));
 947      }
 948  
 949      /**
 950       * HTML filter
 951       *
 952       * @param   string  $source  The string to be filtered
 953       *
 954       * @return  string  The filtered string
 955       */
 956  	private function cleanHtml($source)
 957      {
 958          return $this->remove($source);
 959      }
 960  
 961      /**
 962       * Path filter
 963       *
 964       * @param   string  $source  The string to be filtered
 965       *
 966       * @return  string  The filtered string
 967       */
 968  	private function cleanPath($source)
 969      {
 970          $linuxPattern = '/^[A-Za-z0-9_\/-]+[A-Za-z0-9_\.-]*([\\\\\/]+[A-Za-z0-9_-]+[A-Za-z0-9_\.-]*)*$/';
 971  
 972          if (preg_match($linuxPattern, $source))
 973          {
 974              return preg_replace('~/+~', '/', $source);
 975          }
 976  
 977          $windowsPattern = '/^([A-Za-z]:(\\\\|\/))?[A-Za-z0-9_-]+[A-Za-z0-9_\.-]*((\\\\|\/)+[A-Za-z0-9_-]+[A-Za-z0-9_\.-]*)*$/';
 978  
 979          if (preg_match($windowsPattern, $source))
 980          {
 981              return preg_replace('~(\\\\|\/)+~', '\\', $source);
 982          }
 983  
 984          return '';
 985      }
 986  
 987      /**
 988       * Trim filter
 989       *
 990       * @param   string  $source  The string to be filtered
 991       *
 992       * @return  string  The filtered string
 993       */
 994  	private function cleanTrim($source)
 995      {
 996          $result = trim($source);
 997          $result = StringHelper::trim($result, \chr(0xE3) . \chr(0x80) . \chr(0x80));
 998          $result = StringHelper::trim($result, \chr(0xC2) . \chr(0xA0));
 999  
1000          return $result;
1001      }
1002  
1003      /**
1004       * Username filter
1005       *
1006       * @param   string  $source  The string to be filtered
1007       *
1008       * @return  string  The filtered string
1009       */
1010  	private function cleanUsername($source)
1011      {
1012          $pattern = '/[\x00-\x1F\x7F<>"\'%&]/';
1013  
1014          return preg_replace($pattern, '', $source);
1015      }
1016  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer