From b0a6b8ccc2ed22edf0726d1cc08685dcb4762542 Mon Sep 17 00:00:00 2001 From: Dhrupo Nil Date: Sat, 27 Jun 2026 13:04:49 +0600 Subject: [PATCH] KSES: Allow CSS anchor positioning in safecss_filter_attr(). Add the CSS anchor positioning properties (anchor-name, anchor-scope, position-anchor, position-area, position-try, position-try-fallbacks, position-try-order, position-visibility) to the safe_style_css allowlist, and allow the anchor() and anchor-size() functions in declaration values. Allowlisting the properties alone is not enough: anchor positioning is driven by the anchor() and anchor-size() functions used in inset and sizing properties (e.g. top: anchor(--foo bottom)). Without those functions in the allowlist, safecss_filter_attr() strips any declaration using them, so the feature remains unusable. The functions are handled the same way as the existing var()/calc()/clamp() allowlist, so unknown functions such as expression() are still stripped. Adds regression tests covering the properties, the anchor()/anchor-size() functions (including nesting inside calc()), and that an unknown function is still rejected. Fixes #64972. --- src/wp-includes/kses.php | 14 +++++++++++++- tests/phpunit/tests/kses.php | 26 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/kses.php b/src/wp-includes/kses.php index d1e3552f4e91b..bc6a98df6c564 100644 --- a/src/wp-includes/kses.php +++ b/src/wp-includes/kses.php @@ -2553,6 +2553,8 @@ function kses_init() { * @since 6.5.0 Added support for `background-repeat`. * @since 6.6.0 Added support for `grid-column`, `grid-row`, and `container-type`. * @since 6.9.0 Added support for `white-space`. + * @since 7.1.0 Added support for CSS anchor positioning properties and the + * `anchor()` and `anchor-size()` functions. * * @param string $css A string of CSS rules, decoded from an HTML `style` attribute. * @param string $deprecated Not used. @@ -2734,6 +2736,16 @@ function safecss_filter_attr( $css, $deprecated = '' ) { 'aspect-ratio', 'container-type', + // CSS anchor positioning. + 'anchor-name', + 'anchor-scope', + 'position-anchor', + 'position-area', + 'position-try', + 'position-try-fallbacks', + 'position-try-order', + 'position-visibility', + 'fill', 'fill-opacity', 'fill-rule', @@ -2913,7 +2925,7 @@ function safecss_filter_attr( $css, $deprecated = '' ) { * Nested functions and parentheses are also removed, so long as the parentheses are balanced. */ $css_test_string = preg_replace( - '/\b(?:var|calc|min|max|minmax|clamp|repeat)(\((?:[^()]|(?1))*\))/', + '/\b(?:var|calc|min|max|minmax|clamp|repeat|anchor-size|anchor)(\((?:[^()]|(?1))*\))/', '', $css_test_string ); diff --git a/tests/phpunit/tests/kses.php b/tests/phpunit/tests/kses.php index 9ed3a45b2d90e..d3487fddbb7fd 100644 --- a/tests/phpunit/tests/kses.php +++ b/tests/phpunit/tests/kses.php @@ -1001,6 +1001,7 @@ public function test_wp_kses_attr_no_attributes_allowed_with_false() { * @ticket 60132 * @ticket 64414 * @ticket 65457 + * @ticket 64972 * * @dataProvider data_safecss_filter_attr * @@ -1511,6 +1512,31 @@ public function data_safecss_filter_attr() { 'css' => 'text-anchor: middle', 'expected' => 'text-anchor: middle', ), + // CSS anchor positioning properties are allowed. + array( + 'css' => 'anchor-name: --tooltip;anchor-scope: all;position-anchor: --tooltip;position-area: top;position-try: flip-block;position-try-fallbacks: --fallback;position-try-order: most-height;position-visibility: anchors-visible', + 'expected' => 'anchor-name: --tooltip;anchor-scope: all;position-anchor: --tooltip;position-area: top;position-try: flip-block;position-try-fallbacks: --fallback;position-try-order: most-height;position-visibility: anchors-visible', + ), + // The `anchor()` function is allowed in inset properties. + array( + 'css' => 'top: anchor(--tooltip bottom)', + 'expected' => 'top: anchor(--tooltip bottom)', + ), + // The `anchor-size()` function is allowed in sizing properties. + array( + 'css' => 'width: anchor-size(--tooltip width)', + 'expected' => 'width: anchor-size(--tooltip width)', + ), + // The `anchor-size()` function is allowed when nested in another function. + array( + 'css' => 'margin-left: calc(anchor-size(width) / 2)', + 'expected' => 'margin-left: calc(anchor-size(width) / 2)', + ), + // Allowing `anchor()` must not allow other, unknown functions. + array( + 'css' => 'width: expression(alert(1))', + 'expected' => '', + ), ); }