Repository navigation
KSES: Add wp_kses_sanitize_srcset() for image candidate lists - #14811
adamsilverstein wants to merge 3 commits into
Conversation
A srcset value is a comma-separated list of image candidate strings, not a single URL, so passing it through wp_kses_bad_protocol() or esc_url() either misses candidates or corrupts the list. This function segments the value following the HTML specification's srcset parsing algorithm and removes candidates whose URL uses a disallowed protocol. It takes decoded attribute text and returns decoded text, leaving HTML decoding and encoding to the caller, and returns the input string itself when every candidate is allowed. Nothing calls it yet; wiring it into KSES by tag and attribute follows separately. See https://core.trac.wordpress.org/ticket/29807
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. Props Bot commandsYou can trigger Props Bot actions by commenting on this PR:
|
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
westonruter
left a comment
There was a problem hiding this comment.
A couple nits. Also sending up a Claude review momentarily.
westonruter
left a comment
There was a problem hiding this comment.
🤖 Comment by Claude Opus 5.5
A medium-effort review of wp_kses_sanitize_srcset() turned up one tokenizer mismatch with the HTML spec that lets a disallowed URL through, plus some simplification opportunities. Each was reproduced against this branch:
'a.jpg 1x(,)/?,javascript:alert(1) 2x' → returned unchanged
'a.jpg 1x(,)z,javascript:alert(1) 2x' → 'a.jpg 1x('
'a.jpg 1x(,)z, b.jpg 2x' → unchanged (correct)
The practical impact is limited, since browsers do not execute javascript: URLs in image candidates, but the sanitizer and the browser end up seeing different URLs, which defeats the function's contract. Details are in the inline comments.
Declare return types on the test methods and data providers and a type on the allowed protocols property, as suggested in review. See #29807.
The HTML srcset parser treats a comma between `(` and `)` in the descriptors as part of the descriptor, so ending candidates at the first comma let the sanitizer and the browser disagree on the URLs: a value like `a.jpg 1x(,)/?,javascript:alert(1) 2x` came back unchanged, and rebuilding could truncate allowed candidates. Track parentheses while scanning descriptors, and replace the restart and rebuild pass with a single pass that collects kept candidates. See #29807.
|
Thanks @westonruter, the parens one was a real miss. Pushed fixes in 144732e and the typing nits in 77ce137. Claude worked through the review items, notes below:
|
Trac ticket: https://core.trac.wordpress.org/ticket/29807
First of a small stack splitting up #6184 along the lines @dmsnell suggested there: the srcset parser on its own, then wiring it into KSES by tag and attribute, then the allowed-HTML additions for
picture,sourceandsrcset.This PR only adds
wp_kses_sanitize_srcset( string $srcset, array $allowed_protocols ): stringplus tests. Nothing calls it yet.What it does:
wp_kses_bad_protocol()and drops the whole candidate, descriptors included, when that check would change it. Descriptors are skipped, not validated.,and trailing whitespace trimmed.Changes from the version in #6184, all following the review feedback there: no "multi-URI attributes" concept or filter, no
,/ lookahead, nowp_kses_no_null()or whitespace tolerance inside schemes, and no validation of descriptors. Parentheses in descriptors are tracked only to find where a candidate ends, as the spec does, so a comma inside them does not split the candidate. Thea.jpg 2q,javascript:...case that was documented as a known limitation is now handled, since the comma after an invalid descriptor ends the candidate the same way it does in the browser.Naming: I went with
wp_kses_sanitize_srcset()since srcset (andimagesrcsetonlink) is the only attribute with this syntax. Happy to rename if something likeimage_candidatesreads better.Testing
npm run test:php -- --group kses- the new tests are intests/phpunit/tests/kses/wpKsesSanitizeSrcset.php. PHPStan at level 10 is clean on the new code.AI Use
Claude Code drafted the code and this description from the review thread on #6184, and I steered. I will review and test.