diff --git a/src/wp-includes/kses.php b/src/wp-includes/kses.php index 062f85308512f..094b6c4724907 100644 --- a/src/wp-includes/kses.php +++ b/src/wp-includes/kses.php @@ -2756,6 +2756,27 @@ function safecss_filter_attr( $css, $deprecated = '' ) { 'list-style-image', ); + /* + * CSS attributes that accept rgb(a) color data types. + */ + $css_color_data_types = array( + 'color', + + 'border', + 'border-color', + 'border-right', + 'border-right-color', + 'border-bottom', + 'border-bottom-color', + 'border-left', + 'border-left-color', + 'border-top', + 'border-top-color', + + 'background', + 'background-color', + ); + /* * CSS attributes that accept gradient data types. * @@ -2779,6 +2800,7 @@ function safecss_filter_attr( $css, $deprecated = '' ) { $css_test_string = $css_item; $found = false; $url_attr = false; + $color_attr = false; $gradient_attr = false; $is_custom_var = false; @@ -2798,6 +2820,7 @@ function safecss_filter_attr( $css, $deprecated = '' ) { $found = true; $url_attr = in_array( $css_selector, $css_url_data_types, true ); $gradient_attr = in_array( $css_selector, $css_gradient_data_types, true ); + $color_attr = in_array( $css_selector, $css_color_data_types, true ); } if ( $is_custom_var ) { @@ -2840,6 +2863,20 @@ function safecss_filter_attr( $css, $deprecated = '' ) { } } + if ( $found && $color_attr ) { + // Simplified: matches the sequence `rgb(*)` or `rgba(*)`. + $comma_syntax = '/rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)/i'; + $space_syntax = '/rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)/i'; + + preg_match_all( $comma_syntax, $parts[1], $color_matches_comma ); + preg_match_all( $space_syntax, $parts[1], $color_matches_space ); + + foreach ( array_merge( $color_matches_comma[0], $color_matches_space[0] ) as $color_match ) { + // Remove the whole `rgb(*)` or `rgba(*)` bit that was matched above from the CSS. + $css_test_string = str_replace( $color_match, '', $css_test_string ); + } + } + if ( $found ) { /* * Allow CSS functions like var(), calc(), etc. by removing them from the test string. diff --git a/tests/phpunit/tests/kses.php b/tests/phpunit/tests/kses.php index dc01b5dfe2979..3c9b87c86456d 100644 --- a/tests/phpunit/tests/kses.php +++ b/tests/phpunit/tests/kses.php @@ -1203,15 +1203,30 @@ public function data_safecss_filter_attr() { 'css' => 'height: expression( body.scrollTop + 50 + "px" )', 'expected' => '', ), - // RGB color values are not allowed. + // RGBA background color are allowed. array( - 'css' => 'color: rgb( 100, 100, 100 )', - 'expected' => '', + 'css' => 'background-color: rgba(0,0,0,0)', + 'expected' => 'background-color: rgba(0,0,0,0)', ), - // RGBA color values are not allowed. array( - 'css' => 'color: rgb( 100, 100, 100, .4 )', - 'expected' => '', + 'css' => 'background-color: rgba(0, 0, 0, 0)', + 'expected' => 'background-color: rgba(0, 0, 0, 0)', + ), + array( + 'css' => 'background-color: rgba(100, 100, 100, 0)', + 'expected' => 'background-color: rgba(100, 100, 100, 0)', + ), + array( + 'css' => 'background-color: rgba(10%, 10%, 10%, 0)', + 'expected' => 'background-color: rgba(10%, 10%, 10%, 0)', + ), + array( + 'css' => 'background-color: rgba(0, 0, 0, 0.1)', + 'expected' => 'background-color: rgba(0, 0, 0, 0.1)', + ), + array( + 'css' => 'background-color: rgba(0, 0, 0, .1)', + 'expected' => 'background-color: rgba(0, 0, 0, .1)', ), // Allow min(). array( @@ -1333,6 +1348,86 @@ public function data_safecss_filter_attr() { 'css' => 'gap: 10px;column-gap: 5px;row-gap: 20px', 'expected' => 'gap: 10px;column-gap: 5px;row-gap: 20px', ), + // RGB color. + array( + 'css' => 'color: rgb(255, 0, 0)', + 'expected' => 'color: rgb(255, 0, 0)', + ), + array( + 'css' => 'color: rgb(255 0 0)', + 'expected' => 'color: rgb(255 0 0)', + ), + array( + 'css' => 'color: rgb(100%, 0%, 50%)', + 'expected' => 'color: rgb(100%, 0%, 50%)', + ), + array( + 'css' => 'color: rgb(255 50% 0)', + 'expected' => 'color: rgb(255 50% 0)', + ), + // RGBA color. + array( + 'css' => 'color: rgba(255, 128, 0, 0.5)', + 'expected' => 'color: rgba(255, 128, 0, 0.5)', + ), + array( + 'css' => 'color: rgb(255 128 0 / 50%)', + 'expected' => 'color: rgb(255 128 0 / 50%)', + ), + // RGB color with extra whitespace. + array( + 'css' => 'color: rgb( 255 , 128 , 0 )', + 'expected' => 'color: rgb( 255 , 128 , 0 )', + ), + // RGB background color. + array( + 'css' => 'background-color: rgb(200, 100, 50)', + 'expected' => 'background-color: rgb(200, 100, 50)', + ), + // RGBA border color. + array( + 'css' => 'border-color: rgba(100, 200, 250, 0.8)', + 'expected' => 'border-color: rgba(100, 200, 250, 0.8)', + ), + // RGB color in `border` shorthand. + array( + 'css' => 'border: 1px solid rgb(0, 0, 0)', + 'expected' => 'border: 1px solid rgb(0, 0, 0)', + ), + // RGBA color in `border` shorthand. + array( + 'css' => 'border: 1px solid rgba(0, 0, 0, 0.5)', + 'expected' => 'border: 1px solid rgba(0, 0, 0, 0.5)', + ), + // RGB color in `background` shorthand. + array( + 'css' => 'background: rgb(0, 0, 0) center', + 'expected' => 'background: rgb(0, 0, 0) center', + ), + // Malformed RGB color, invalid number of values. + array( + 'css' => 'color: rgb(255, 128, 0, 0.5, 100)', + 'expected' => '', + ), + array( + 'css' => 'color: rgb(255, 128)', + 'expected' => '', + ), + // Malformed RGB color, non-numeric values. + array( + 'css' => 'color: rgb(red, green, blue)', + 'expected' => '', + ), + // Malformed RGB color, unmatched parentheses. + array( + 'css' => 'color: rgb(255, 128, 0', + 'expected' => '', + ), + // Malformed RGB color, empty values. + array( + 'css' => 'color: rgb(, , )', + 'expected' => '', + ), // Margin and padding logical properties introduced in 6.1. array( 'css' => 'margin-block-start: 1px;margin-block-end: 2px;margin-inline-start: 3px;margin-inline-end: 4px;',