Skip to content

Re-implement safecss_filter_attr() with the CSS token processor#8

Draft
ockham wants to merge 17 commits into
trunkfrom
css-token-style-attr-processor
Draft

Re-implement safecss_filter_attr() with the CSS token processor#8
ockham wants to merge 17 commits into
trunkfrom
css-token-style-attr-processor

Conversation

@ockham

@ockham ockham commented Jul 23, 2026

Copy link
Copy Markdown
Owner

What

Re-implements safecss_filter_attr() (in src/wp-includes/kses.php) on top of WP_CSS_Token_Processor, replacing the explode( ';' ) declaration splitting and regex-based validation with CSS Syntax Level 3 tokenization.

For background on the CSS Tokenizer itself, see sirreal#78. This branch stacks on top of that work plus the WP_HTML_Style_Attribute_Processor contract.

Design

The full design is in docs/safecss-filter-attr-token-processor-spec.md. Highlights:

  • Declarations are discovered at top-level semicolons via a single tokenizer pass, so ; inside strings and url() no longer splits declarations (resolves the long-standing @todo).
  • The character-denylist regex (%[\\\(&=}]|/\*%) and the url/gradient/function-stripping regexes are replaced by token-based validation.
  • Every url() construct — at any nesting depth — is protocol-checked with wp_kses_bad_protocol(), closing the calc(url(javascript:…)) loophole. This includes payloads inside structurally malformed constructs (bad-url tokens, malformed quoted url() forms): an identifiable bad-protocol payload is always a hard rejection, while structurally-malformed-but-clean shapes remain soft (filter-rescuable) rejections.
  • The safecss_filter_attr_allow_css filter is retained: $allow_css now comes from token validation; soft rejections remain rescuable, hard rejections (unknown property, bad-protocol url) still bypass the filter as today. Note: the filter's second argument is now the declaration's authored source text rather than the old function-stripped test string.
  • Output remains the trimmed authored source of accepted declarations joined with ;. All pre-existing kses PHPUnit tests pass unchanged (382 tests in tests/phpunit/tests/kses.php incl. the new ones). Deliberate divergences (all toward strictness, or CSS-correct loosenings like case-insensitive URL(…)) are enumerated in the spec.

Status

  • Spec document
  • Implementation
  • New test cases (see spec's Test Requirements section)

🤖 Generated with Claude Code

Comment thread src/wp-includes/kses.php
* @return int|null Index just past the gradient's closing parenthesis, or
* null when the gradient is invalid or unclosed.
*/
function _safecss_walk_gradient( $tokens, $i, $url_allowed ) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://drafts.csswg.org/css-syntax/#consume-function

seems like gradients and URLs should be handled by this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants