Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   1  <?php
   2  
   3  /**
   4   * Validates the HTML attribute style, otherwise known as CSS.
   5   * @note We don't implement the whole CSS specification, so it might be
   6   *       difficult to reuse this component in the context of validating
   7   *       actual stylesheet declarations.
   8   * @note If we were really serious about validating the CSS, we would
   9   *       tokenize the styles and then parse the tokens. Obviously, we
  10   *       are not doing that. Doing that could seriously harm performance,
  11   *       but would make these components a lot more viable for a CSS
  12   *       filtering solution.
  13   */
  14  class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
  15  {
  16  
  17      /**
  18       * @param string $css
  19       * @param HTMLPurifier_Config $config
  20       * @param HTMLPurifier_Context $context
  21       * @return bool|string
  22       */
  23      public function validate($css, $config, $context)
  24      {
  25          $css = $this->parseCDATA($css);
  26  
  27          $definition = $config->getCSSDefinition();
  28          $allow_duplicates = $config->get("CSS.AllowDuplicates");
  29  
  30  
  31          // According to the CSS2.1 spec, the places where a
  32          // non-delimiting semicolon can appear are in strings
  33          // escape sequences.   So here is some dumb hack to
  34          // handle quotes.
  35          $len = strlen($css);
  36          $accum = "";
  37          $declarations = array();
  38          $quoted = false;
  39          for ($i = 0; $i < $len; $i++) {
  40              $c = strcspn($css, ";'\"", $i);
  41              $accum .= substr($css, $i, $c);
  42              $i += $c;
  43              if ($i == $len) break;
  44              $d = $css[$i];
  45              if ($quoted) {
  46                  $accum .= $d;
  47                  if ($d == $quoted) {
  48                      $quoted = false;
  49                  }
  50              } else {
  51                  if ($d == ";") {
  52                      $declarations[] = $accum;
  53                      $accum = "";
  54                  } else {
  55                      $accum .= $d;
  56                      $quoted = $d;
  57                  }
  58              }
  59          }
  60          if ($accum != "") $declarations[] = $accum;
  61  
  62          $propvalues = array();
  63          $new_declarations = '';
  64  
  65          /**
  66           * Name of the current CSS property being validated.
  67           */
  68          $property = false;
  69          $context->register('CurrentCSSProperty', $property);
  70  
  71          foreach ($declarations as $declaration) {
  72              if (!$declaration) {
  73                  continue;
  74              }
  75              if (!strpos($declaration, ':')) {
  76                  continue;
  77              }
  78              list($property, $value) = explode(':', $declaration, 2);
  79              $property = trim($property);
  80              $value = trim($value);
  81              $ok = false;
  82              do {
  83                  if (isset($definition->info[$property])) {
  84                      $ok = true;
  85                      break;
  86                  }
  87                  if (ctype_lower($property)) {
  88                      break;
  89                  }
  90                  $property = strtolower($property);
  91                  if (isset($definition->info[$property])) {
  92                      $ok = true;
  93                      break;
  94                  }
  95              } while (0);
  96              if (!$ok) {
  97                  continue;
  98              }
  99              // inefficient call, since the validator will do this again
 100              if (strtolower(trim($value)) !== 'inherit') {
 101                  // inherit works for everything (but only on the base property)
 102                  $result = $definition->info[$property]->validate(
 103                      $value,
 104                      $config,
 105                      $context
 106                  );
 107              } else {
 108                  $result = 'inherit';
 109              }
 110              if ($result === false) {
 111                  continue;
 112              }
 113              if ($allow_duplicates) {
 114                  $new_declarations .= "$property:$result;";
 115              } else {
 116                  $propvalues[$property] = $result;
 117              }
 118          }
 119  
 120          $context->destroy('CurrentCSSProperty');
 121  
 122          // procedure does not write the new CSS simultaneously, so it's
 123          // slightly inefficient, but it's the only way of getting rid of
 124          // duplicates. Perhaps config to optimize it, but not now.
 125  
 126          foreach ($propvalues as $prop => $value) {
 127              $new_declarations .= "$prop:$value;";
 128          }
 129  
 130          return $new_declarations ? $new_declarations : false;
 131  
 132      }
 133  
 134  }
 135  
 136  // vim: et sw=4 sts=4