You are here

public function FilterHtml::getHTMLRestrictions in Markdown 8.2

Returns HTML allowed by this filter's configuration.

May be implemented by filters of the FilterInterface::TYPE_HTML_RESTRICTOR type, this won't be used for filters of other types; they should just return FALSE.

This callback function is only necessary for filters that strip away HTML tags (and possibly attributes) and allows other modules to gain insight in a generic manner into which HTML tags and attributes are allowed by a format.

Return value

array|false A nested array with *either* of the following keys:

  • 'allowed': (optional) the allowed tags as keys, and for each of those tags (keys) either of the following values:

    • TRUE to indicate any attribute is allowed
    • FALSE to indicate no attributes are allowed
    • an array to convey attribute restrictions: the keys must be attribute names (which may use a wildcard, e.g. "data-*"), the possible values are similar to the above:

      • TRUE to indicate any attribute value is allowed
      • FALSE to indicate the attribute is forbidden
      • an array to convey attribute value restrictions: the key must be attribute values (which may use a wildcard, e.g. "xsd:*"), the possible values are TRUE or FALSE: to mark the attribute value as allowed or forbidden, respectively
  • 'forbidden_tags': (optional) the forbidden tags

There is one special case: the "wildcard tag", "*": any attribute restrictions on that pseudotag apply to all tags.

If no restrictions apply, then FALSE must be returned.

Here is a concrete example, for a very granular filter:

array(
  'allowed' => array(
    // Allows any attribute with any value on the <div> tag.
    'div' => TRUE,
    // Allows no attributes on the <p> tag.
    'p' => FALSE,
    // Allows the following attributes on the <a> tag:
    //  - 'href', with any value;
    //  - 'rel', with the value 'nofollow' value.
    'a' => array(
      'href' => TRUE,
      'rel' => array(
        'nofollow' => TRUE,
      ),
    ),
    // Only allows the 'src' and 'alt' attributes on the <alt> tag,
    // with any value.
    'img' => array(
      'src' => TRUE,
      'alt' => TRUE,
    ),
    // Allow RDFa on <span> tags, using only the dc, foaf, xsd and sioc
    // vocabularies/namespaces.
    'span' => array(
      'property' => array(
        'dc:*' => TRUE,
        'foaf:*' => TRUE,
      ),
      'datatype' => array(
        'xsd:*' => TRUE,
      ),
      'rel' => array(
        'sioc:*' => TRUE,
      ),
    ),
    // Forbid the 'style' and 'on*' ('onClick' etc.) attributes on any
    // tag.
    '*' => array(
      'style' => FALSE,
      'on*' => FALSE,
    ),
  ),
);

A simpler example, for a very coarse filter:

array(
  'forbidden_tags' => array(
    'iframe',
    'script',
  ),
);

The simplest example possible: a filter that doesn't allow any HTML:

array(
  'allowed' => array(),
);

And for a filter that applies no restrictions, i.e. allows any HTML:

FALSE;

Overrides FilterHtml::getHTMLRestrictions

See also

\Drupal\filter\Entity\FilterFormatInterface::getHtmlRestrictions()

File

src/Util/FilterHtml.php, line 286

Class

FilterHtml
Extends FilterHtml to allow more more permissive global attributes.

Namespace

Drupal\markdown\Util

Code

public function getHTMLRestrictions() {

  // phpcs:ignore
  if ($this->restrictions) {
    return $this->restrictions;
  }
  $activeTheme = \Drupal::theme()
    ->getActiveTheme();
  $parser = $this
    ->getParser();
  $allowedHtmlPlugins = $parser ? AllowedHtmlManager::create()
    ->appliesTo($parser, $activeTheme) : [];
  $cacheTags = $parser ? $parser
    ->getCacheTags() : [];
  $cid = 'markdown_allowed_html:' . Crypt::hashBase64(serialize(array_merge($cacheTags, $allowedHtmlPlugins)));

  // Return cached HTML restrictions.
  $discoveryCache = \Drupal::cache('discovery');
  if (($cached = $discoveryCache
    ->get($cid)) && !empty($cached->data)) {
    $this->restrictions = $cached->data;
    return $this->restrictions;
  }
  $restrictions = parent::getHTMLRestrictions();

  // Save the original global attributes.
  $originalGlobalAttributes = $restrictions['allowed']['*'];
  unset($restrictions['allowed']['*']);

  // Determine if any user global attributes where provided (from a filter).
  $addedGlobalAttributes = [];
  if (isset($restrictions['allowed'][static::ASTERISK_PLACEHOLDER])) {
    $addedGlobalAttributes['*'] = $restrictions['allowed'][static::ASTERISK_PLACEHOLDER];
    $addedGlobalAttributes = static::normalizeTags($addedGlobalAttributes);
    unset($restrictions['allowed'][static::ASTERISK_PLACEHOLDER]);
  }

  // Normalize the allowed tags.
  $normalizedTags = static::normalizeTags($restrictions['allowed']);

  // Merge in plugins allowed HTML tags.
  foreach ($allowedHtmlPlugins as $plugin_id => $allowedHtml) {

    // Retrieve the plugin's allowed HTML tags.
    $tags = $allowedHtml
      ->allowedHtmlTags($parser, $activeTheme);

    // Merge the plugin's global attributes with the user provided ones.
    if (isset($tags['*'])) {
      $addedGlobalAttributes = static::mergeAllowedTags($addedGlobalAttributes, [
        '*' => $tags['*'],
      ]);
      unset($tags['*']);
    }

    // Now merge the plugin's tags with the allowed HTML.
    $normalizedTags = static::mergeAllowedTags($normalizedTags, $tags);
  }

  // Replace the allowed tags with the normalized/merged tags.
  $restrictions['allowed'] = $normalizedTags;

  // Restore the original global attributes.
  $restrictions['allowed']['*'] = $originalGlobalAttributes;

  // Now merge the added global attributes using the array union (+) operator.
  // This ensures that the original core defined global attributes are never
  // overridden so users cannot specify attributes like 'style' and 'on*'
  // which are highly vulnerable to XSS.
  if (!empty($addedGlobalAttributes['*'])) {
    $restrictions['allowed']['*'] += $addedGlobalAttributes['*'];
  }
  $discoveryCache
    ->set($cid, $restrictions, CacheBackendInterface::CACHE_PERMANENT, $cacheTags);
  $this->restrictions = $restrictions;
  return $restrictions;
}