From 797ab0e07f2dd1650855d37654b9b761c31858b0 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Fri, 13 Feb 2026 20:01:07 +0900 Subject: [PATCH] KSES: Add support for rgb(a) color --- src/wp-includes/kses.php | 34 ++++++++++++++ tests/phpunit/tests/kses.php | 90 ++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/src/wp-includes/kses.php b/src/wp-includes/kses.php index ed2f96503ac27..2b1810e0a3957 100644 --- a/src/wp-includes/kses.php +++ b/src/wp-includes/kses.php @@ -2756,6 +2756,28 @@ 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 +2801,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 +2821,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 +2864,16 @@ function safecss_filter_attr( $css, $deprecated = '' ) { } } + if ( $found && $color_attr ) { + $css_value = trim( $parts[1] ); + $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'; + + if ( preg_match( $comma_syntax, $css_value ) || preg_match( $space_syntax, $css_value ) ) { + $css_test_string = str_replace( $css_value, '', $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..decffe2d32358 100644 --- a/tests/phpunit/tests/kses.php +++ b/tests/phpunit/tests/kses.php @@ -1203,6 +1203,31 @@ public function data_safecss_filter_attr() { 'css' => 'height: expression( body.scrollTop + 50 + "px" )', 'expected' => '', ), + // RGBA background color are allowed. + array( + 'css' => 'background-color: rgba(0,0,0,0)', + 'expected' => 'background-color: rgba(0,0,0,0)', + ), + array( + '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)', + ), // RGB color values are not allowed. array( 'css' => 'color: rgb( 100, 100, 100 )', @@ -1333,6 +1358,71 @@ 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, 300, 0.8)', + 'expected' => 'border-color: rgba(100, 200, 300, 0.8)', + ), + // 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;',