diff --git a/src/wp-includes/kses.php b/src/wp-includes/kses.php index 9cd0f50f22487..15550d1ef2e48 100644 --- a/src/wp-includes/kses.php +++ b/src/wp-includes/kses.php @@ -2640,6 +2640,7 @@ function kses_init() { * @since 7.1.0 Extended gradient support to allow any single-level nested function. * Added support for transform functions, `clip-path` basic shapes, * and URLs in the SVG element reference properties. + * @since 7.2.0 Added support for filter functions. * * @param string $css A string of CSS rules, decoded from an HTML `style` attribute. * @param string $deprecated Not used. @@ -3027,6 +3028,9 @@ function safecss_filter_attr( $css, $deprecated = '' ) { . '|translate|translate3d|translateX|translateY|translateZ' // Basic shape functions, as used by `clip-path`. . '|circle|ellipse|inset|path|polygon|rect|shape|xywh' + // Filter functions. + . '|blur|brightness|contrast|drop-shadow|grayscale' + . '|hue-rotate|invert|opacity|saturate|sepia' . ')(\((?:[^()]|(?1))*\))/', '', $css_test_string diff --git a/tests/phpunit/tests/kses.php b/tests/phpunit/tests/kses.php index b26bbd307d6aa..082fa29eb2584 100644 --- a/tests/phpunit/tests/kses.php +++ b/tests/phpunit/tests/kses.php @@ -1225,6 +1225,7 @@ public function test_wp_kses_attr_no_attributes_allowed_with_false() { * @ticket 65457 * @ticket 64974 * @ticket 65832 + * @ticket 65871 * * @dataProvider data_safecss_filter_attr * @@ -1882,6 +1883,80 @@ public function data_safecss_filter_attr() { 'css' => 'clip-path: url(javascript:alert(1))', 'expected' => '', ), + // Filter functions (ticket #65871). + array( + 'css' => 'filter: blur(5px)', + 'expected' => 'filter: blur(5px)', + ), + array( + 'css' => 'filter: brightness(0.4)', + 'expected' => 'filter: brightness(0.4)', + ), + array( + 'css' => 'filter: contrast(200%)', + 'expected' => 'filter: contrast(200%)', + ), + array( + 'css' => 'filter: drop-shadow(16px 16px 20px blue)', + 'expected' => 'filter: drop-shadow(16px 16px 20px blue)', + ), + array( + 'css' => 'filter: grayscale(50%)', + 'expected' => 'filter: grayscale(50%)', + ), + array( + 'css' => 'filter: hue-rotate(90deg)', + 'expected' => 'filter: hue-rotate(90deg)', + ), + array( + 'css' => 'filter: invert(75%)', + 'expected' => 'filter: invert(75%)', + ), + array( + 'css' => 'filter: opacity(25%)', + 'expected' => 'filter: opacity(25%)', + ), + array( + 'css' => 'filter: saturate(30%)', + 'expected' => 'filter: saturate(30%)', + ), + array( + 'css' => 'filter: sepia(60%)', + 'expected' => 'filter: sepia(60%)', + ), + // Multiple filter functions chained. + array( + 'css' => 'filter: blur(5px) brightness(0.4)', + 'expected' => 'filter: blur(5px) brightness(0.4)', + ), + // Nested functions within a filter function are allowed. + array( + 'css' => 'filter: blur(calc(2px + 3px))', + 'expected' => 'filter: blur(calc(2px + 3px))', + ), + array( + 'css' => 'filter: drop-shadow(0 0 var(--shadow-size) black)', + 'expected' => 'filter: drop-shadow(0 0 var(--shadow-size) black)', + ), + // filter: none and filter: url() are unchanged (regression control). + array( + 'css' => 'filter: none', + 'expected' => 'filter: none', + ), + array( + 'css' => 'filter: url(#svgBlur)', + 'expected' => 'filter: url(#svgBlur)', + ), + // Disallow javascript: URLs in filter url() references (security regression). + array( + 'css' => 'filter: url(javascript:alert(1))', + 'expected' => '', + ), + // The opacity property is unaffected by the opacity() filter function. + array( + 'css' => 'opacity: 0.5', + 'expected' => 'opacity: 0.5', + ), ); }