You are here

public function SourceEditingPreventSelfXssConstraintValidator::validate in Drupal 10

Throws

\Symfony\Component\Validator\Exception\UnexpectedTypeException Thrown when the given constraint is not supported by this validator.

File

core/modules/ckeditor5/src/Plugin/Validation/Constraint/SourceEditingPreventSelfXssConstraintValidator.php, line 27

Class

SourceEditingPreventSelfXssConstraintValidator
Ensures Source Editing cannot be configured to allow self-XSS.

Namespace

Drupal\ckeditor5\Plugin\Validation\Constraint

Code

public function validate($value, Constraint $constraint) {
  if (!$constraint instanceof SourceEditingPreventSelfXssConstraint) {
    throw new UnexpectedTypeException($constraint, __NAMESPACE__ . '\\SourceEditingPreventSelfXssConstraint');
  }
  if (empty($value)) {
    return;
  }
  $restrictions = HTMLRestrictions::fromString($value);

  // @todo Remove this early return in
  //   https://www.drupal.org/project/drupal/issues/2820364. It is only
  //   necessary because CKEditor5ElementConstraintValidator does not run
  //   before this, which means that this validator cannot assume it receives
  //   valid values.
  if ($restrictions
    ->allowsNothing() || count($restrictions
    ->getAllowedElements()) > 1) {
    return;
  }

  // This validation constraint only validates attributes, not tags; so if all
  // attributes are allowed (TRUE) or no attributes are allowed (FALSE),
  // return early. Only proceed when some attributes are allowed (an array).
  $allowed_elements = $restrictions
    ->getAllowedElements(FALSE);
  assert(count($allowed_elements) === 1);
  $tag = array_key_first($allowed_elements);
  $attribute_restrictions = $allowed_elements[$tag];
  if (!is_array($attribute_restrictions)) {
    return;
  }
  $text_editor = $this
    ->createTextEditorObjectFromContext();
  $text_format_allowed_elements = HTMLRestrictions::fromTextFormat($text_editor
    ->getFilterFormat())
    ->getAllowedElements();

  // Any XSS-prevention related measures imposed by filter plugins are relayed
  // through their ::getHtmlRestrictions() return value. The global attribute
  // `*` HTML tag allows attributes to be forbidden.
  // @see https://html.spec.whatwg.org/multipage/dom.html#global-attributes
  // @see \Drupal\ckeditor5\HTMLRestrictions::validateAllowedRestrictionsPhase4()
  // @see \Drupal\filter\Plugin\Filter\FilterHtml::getHTMLRestrictions()
  $forbidden_attributes = [];
  if (array_key_exists('*', $text_format_allowed_elements)) {
    $forbidden_attributes = array_keys(array_filter($text_format_allowed_elements['*'], function ($attribute_value_restriction, string $attribute_name) {
      return $attribute_value_restriction === FALSE;
    }, ARRAY_FILTER_USE_BOTH));
  }
  foreach ($forbidden_attributes as $forbidden_attribute_name) {

    // Forbidden attributes not containing wildcards, such as `style`.
    if (!self::isWildcardAttributeName($forbidden_attribute_name)) {
      if (array_key_exists($forbidden_attribute_name, $attribute_restrictions)) {
        $this->context
          ->buildViolation($constraint->message)
          ->setParameter('%dangerous_tag', $value)
          ->addViolation();
      }
    }
    else {
      $regex = self::getRegExForWildCardAttributeName($forbidden_attribute_name);
      if (!empty(preg_grep($regex, array_keys($attribute_restrictions)))) {
        $this->context
          ->buildViolation($constraint->message)
          ->setParameter('%dangerous_tag', $value)
          ->addViolation();
      }
    }
  }
}