Source: includes/class-sensei-wp-kses.php

<?php

class Sensei_Wp_Kses {

	private static $allowed_html = null;

	/**
	 * Essentially a copy of wp_kses() with a custom hook
	 * see https://github.com/Automattic/sensei/issues/1560
	 *
	 * @param $string
	 * @param $allowed_html
	 * @param array        $allowed_protocols
	 * @return string
	 */
	public static function wp_kses( $string, $allowed_html = null, $allowed_protocols = array() ) {
		if ( empty( $allowed_protocols ) ) {
			$allowed_protocols = wp_allowed_protocols();
		}
		if ( null === $allowed_html ) {
			$allowed_html = self::get_default_wp_kses_allowed_html();
		}
		$string = wp_kses_no_null( $string, array( 'slash_zero' => 'keep' ) );
		$string = wp_kses_normalize_entities( $string );
		/**
		 * Filter content before passing it to kses, similar to pre_kses.
		 *
		 * @hook sensei_pre_kses
		 *
		 * @param {string} $string            Content to run through kses.
		 * @param {array}  $allowed_html      Allowed HTML elements.
		 * @param {array}  $allowed_protocols Allowed protocol in links.
		 * @return {string} Filtered content.
		 */
		$string = apply_filters( 'sensei_pre_kses', $string, $allowed_html, $allowed_protocols );
		return wp_kses_split( $string, $allowed_html, $allowed_protocols );
	}

	static function get_default_wp_kses_allowed_html() {
		if ( null === self::$allowed_html ) {
			self::$allowed_html = array(
				'embed'  => array(),
				'iframe' => array(
					'width'           => array(),
					'height'          => array(),
					'src'             => array(),
					'frameborder'     => array(),
					'allowfullscreen' => array(),
				),
				'video'  => self::get_video_html_tag_allowed_attributes(),
				'a'      => array(
					'class' => array(),
					'href'  => array(),
					'rel'   => array(),
				),
				'span'   => array(
					'class' => array(),
				),
				'source' => self::get_source_html_tag_allowed_attributes(),
			);
		}
		return self::$allowed_html;
	}

	public static function get_video_html_tag_allowed_attributes() {
		return array(
			'source'   => array(),
			'autoplay' => array(),
			'controls' => array(),
			'height'   => array(),
			'loop'     => array(),
			'muted'    => array(),
			'poster'   => array(),
			'preload'  => array(),
			'src'      => array(),
			'width'    => array(),
		);
	}

	public static function get_source_html_tag_allowed_attributes() {
		return array(
			'src'    => array(),
			'type'   => array(),
			'srcset' => array(),
			'sizes'  => array(),
			'media'  => array(),
		);
	}



	/**
	 * Return all HTML formatting tags to be used with wp_kses
	 * see https://www.w3schools.com/html/html_formatting.asp
	 *
	 * @access public
	 * @since 4.6.4
	 *
	 * @return array HTML formatting tags
	 */
	public static function get_allowed_html_formatting_tags(): array {
		return array(
			'b'      => array(),
			'strong' => array(),
			'i'      => array(),
			'em'     => array(),
			'mark'   => array(),
			'small'  => array(),
			'del'    => array(),
			'ins'    => array(),
			'sub'    => array(),
			'sup'    => array(),
		);
	}

	/**
	 * Will act as a sanitization or an identity function, depending on HTML security settings.
	 *
	 * @param string $content Content
	 * @param array  $allowed_html
	 * @return string Content
	 */
	public static function maybe_sanitize( $content, $allowed_html ) {
		$html_security = ! Sensei()->settings->get( 'sensei_video_embed_html_sanitization_disable' );

		return $html_security ? self::wp_kses( $content, $allowed_html ) : $content;
	}

	/**
	 * Sanitizes content for an array of HTML elements.
	 * Allows for both regular post HTML tags and custom HTML tags.
	 *
	 * @since 1.12.2
	 *
	 * @param array $unescaped_data Array of unescaped data.
	 * @param array $allowed_html List of allowed HTML elements (to be merged with results of wp_kses_allowed_html( 'post' )).
	 * @return array Escaped data.
	 **/
	public static function wp_kses_array( $unescaped_data, $allowed_html = array() ) {
		$escaped_data = array();

		foreach ( $unescaped_data as $key => $data ) {
			$escaped_data[ $key ] = wp_kses(
				$data,
				array_merge(
					wp_kses_allowed_html( 'post' ),
					$allowed_html
				)
			);
		}

		return $escaped_data;
	}
}