-
Notifications
You must be signed in to change notification settings - Fork 3.4k
wp_kses: add support for picture element and srcset attribute on img tags #6184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
2bf9709
11336a3
05dcd69
499dc82
7a75eba
69312e7
3c6defd
c88673d
022def5
321e441
c7c63dd
e3c1684
3862627
4cb049a
ca94908
49f1ba6
2b5706c
d116c8e
fbeed82
f9e002c
80521e4
0068aac
f11aeb2
cdb84fc
399aa30
e5488a6
1f02276
9bb1ebf
2bdc1f9
61881bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -210,17 +210,21 @@ | |||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| 'i' => array(), | ||||||||||||||||||||||||||||||
| 'img' => array( | ||||||||||||||||||||||||||||||
| 'alt' => true, | ||||||||||||||||||||||||||||||
| 'align' => true, | ||||||||||||||||||||||||||||||
| 'border' => true, | ||||||||||||||||||||||||||||||
| 'height' => true, | ||||||||||||||||||||||||||||||
| 'hspace' => true, | ||||||||||||||||||||||||||||||
| 'loading' => true, | ||||||||||||||||||||||||||||||
| 'longdesc' => true, | ||||||||||||||||||||||||||||||
| 'vspace' => true, | ||||||||||||||||||||||||||||||
| 'src' => true, | ||||||||||||||||||||||||||||||
| 'usemap' => true, | ||||||||||||||||||||||||||||||
| 'width' => true, | ||||||||||||||||||||||||||||||
| 'alt' => true, | ||||||||||||||||||||||||||||||
| 'align' => true, | ||||||||||||||||||||||||||||||
| 'border' => true, | ||||||||||||||||||||||||||||||
| 'decoding' => true, | ||||||||||||||||||||||||||||||
| 'fetchpriority' => true, | ||||||||||||||||||||||||||||||
| 'height' => true, | ||||||||||||||||||||||||||||||
| 'hspace' => true, | ||||||||||||||||||||||||||||||
| 'loading' => true, | ||||||||||||||||||||||||||||||
| 'longdesc' => true, | ||||||||||||||||||||||||||||||
| 'vspace' => true, | ||||||||||||||||||||||||||||||
| 'src' => true, | ||||||||||||||||||||||||||||||
| 'srcset' => true, | ||||||||||||||||||||||||||||||
| 'usemap' => true, | ||||||||||||||||||||||||||||||
| 'width' => true, | ||||||||||||||||||||||||||||||
| 'sizes' => true, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| 'ins' => array( | ||||||||||||||||||||||||||||||
| 'datetime' => true, | ||||||||||||||||||||||||||||||
|
|
@@ -271,6 +275,7 @@ | |||||||||||||||||||||||||||||
| 'p' => array( | ||||||||||||||||||||||||||||||
| 'align' => true, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| 'picture' => array(), | ||||||||||||||||||||||||||||||
| 'pre' => array( | ||||||||||||||||||||||||||||||
| 'width' => true, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
|
|
@@ -296,6 +301,12 @@ | |||||||||||||||||||||||||||||
| 'align' => true, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| 'small' => array(), | ||||||||||||||||||||||||||||||
| 'source' => array( | ||||||||||||||||||||||||||||||
| 'srcset' => true, | ||||||||||||||||||||||||||||||
| 'type' => true, | ||||||||||||||||||||||||||||||
| 'media' => true, | ||||||||||||||||||||||||||||||
| 'sizes' => true, | ||||||||||||||||||||||||||||||
|
Comment on lines
+305
to
+308
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sort? |
||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| 'strike' => array(), | ||||||||||||||||||||||||||||||
| 'strong' => array(), | ||||||||||||||||||||||||||||||
| 'sub' => array(), | ||||||||||||||||||||||||||||||
|
|
@@ -979,7 +990,6 @@ function wp_kses( $content, $allowed_html, $allowed_protocols = array() ) { | |||||||||||||||||||||||||||||
| * @return string Filtered attribute. | ||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||
| function wp_kses_one_attr( $attr, $element ) { | ||||||||||||||||||||||||||||||
|
adamsilverstein marked this conversation as resolved.
|
||||||||||||||||||||||||||||||
| $uris = wp_kses_uri_attributes(); | ||||||||||||||||||||||||||||||
| $allowed_html = wp_kses_allowed_html( 'post' ); | ||||||||||||||||||||||||||||||
| $allowed_protocols = wp_allowed_protocols(); | ||||||||||||||||||||||||||||||
| $attr = wp_kses_no_null( $attr, array( 'slash_zero' => 'keep' ) ); | ||||||||||||||||||||||||||||||
|
|
@@ -1023,10 +1033,7 @@ function wp_kses_one_attr( $attr, $element ) { | |||||||||||||||||||||||||||||
| // Sanitize quotes, angle braces, and entities. | ||||||||||||||||||||||||||||||
| $value = esc_attr( $value ); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Sanitize URI values. | ||||||||||||||||||||||||||||||
| if ( in_array( strtolower( $name ), $uris, true ) ) { | ||||||||||||||||||||||||||||||
| $value = wp_kses_bad_protocol( $value, $allowed_protocols ); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| $value = wp_kses_sanitize_uris( $name, $value, $allowed_protocols ); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| $attr = "$name=$quote$value$quote"; | ||||||||||||||||||||||||||||||
| $vless = 'n'; | ||||||||||||||||||||||||||||||
|
|
@@ -1245,6 +1252,7 @@ function wp_kses_uri_attributes() { | |||||||||||||||||||||||||||||
| 'src', | ||||||||||||||||||||||||||||||
| 'usemap', | ||||||||||||||||||||||||||||||
| 'xmlns', | ||||||||||||||||||||||||||||||
| 'srcset', | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||
|
|
@@ -1618,7 +1626,6 @@ function wp_kses_attr_check( &$name, &$value, &$whole, $vless, $element, $allowe | |||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||
| function wp_kses_hair( $attr, $allowed_protocols ) { | ||||||||||||||||||||||||||||||
| $attributes = array(); | ||||||||||||||||||||||||||||||
| $uris = wp_kses_uri_attributes(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| $processor = new WP_HTML_Tag_Processor( "<wp {$attr}>" ); | ||||||||||||||||||||||||||||||
| $processor->next_token(); | ||||||||||||||||||||||||||||||
|
|
@@ -1639,8 +1646,8 @@ function wp_kses_hair( $attr, $allowed_protocols ) { | |||||||||||||||||||||||||||||
| foreach ( $attribute_names as $name ) { | ||||||||||||||||||||||||||||||
| $value = $processor->get_attribute( $name ); | ||||||||||||||||||||||||||||||
| $is_bool = true === $value; | ||||||||||||||||||||||||||||||
| if ( is_string( $value ) && in_array( $name, $uris, true ) ) { | ||||||||||||||||||||||||||||||
| $value = wp_kses_bad_protocol( $value, $allowed_protocols ); | ||||||||||||||||||||||||||||||
| if ( is_string( $value ) ) { | ||||||||||||||||||||||||||||||
| $value = wp_kses_sanitize_uris( $name, $value, $allowed_protocols ); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Reconstruct and normalize the attribute value. | ||||||||||||||||||||||||||||||
|
|
@@ -1658,6 +1665,69 @@ function wp_kses_hair( $attr, $allowed_protocols ) { | |||||||||||||||||||||||||||||
| return $attributes; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||
| * Sanitizes URI values in HTML attributes. | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * This function centralizes logic for cleaning attribute values that are expected to contain URLs. | ||||||||||||||||||||||||||||||
| * It checks if the attribute name is one that should contain a URI (e.g., 'href', 'src', 'srcset'). | ||||||||||||||||||||||||||||||
| * For attributes that can contain multiple URIs (such as 'srcset'), it splits the value and sanitizes each URI individually. | ||||||||||||||||||||||||||||||
| * All URI values are passed through {@see wp_kses_bad_protocol()} to remove disallowed protocols (e.g., 'javascript:'). | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * @since 7.1.0 | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * @param string $attrname The attribute name to test. | ||||||||||||||||||||||||||||||
| * @param string $attrvalue The attribute value to sanitize. | ||||||||||||||||||||||||||||||
| * @param string[] $allowed_protocols Array of allowed URL protocols. | ||||||||||||||||||||||||||||||
| * @param string[] $multi_uri Optional. Attributes that can contain multiple URIs. Default is array( 'srcset' ). | ||||||||||||||||||||||||||||||
| * @return string Sanitized attribute value. | ||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||
| function wp_kses_sanitize_uris( $attrname, $attrvalue, $allowed_protocols, $multi_uri = array( 'srcset' ) ) { | ||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think these can add PHP type hints as well for the params and the return value. |
||||||||||||||||||||||||||||||
| $uris = wp_kses_uri_attributes(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if ( ! in_array( strtolower( $attrname ), $uris, true ) ) { | ||||||||||||||||||||||||||||||
| return $attrvalue; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if ( in_array( strtolower( $attrname ), $multi_uri, true ) ) { | ||||||||||||||||||||||||||||||
|
Comment on lines
+1678
to
+1691
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's add underscores to some vars, like |
||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||
| * Parse srcset-style attributes using the descriptor to find entry boundaries. | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * Srcset entries are: URL [descriptor], URL [descriptor], ... | ||||||||||||||||||||||||||||||
| * Descriptors match patterns like "480w" or "2x". | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * A naive split on commas breaks URLs that contain commas internally | ||||||||||||||||||||||||||||||
| * (e.g. CDN image resizer URLs like cdn-cgi/image/format=auto,quality=80/...). | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * Instead, split on: whitespace + descriptor + comma (+ optional whitespace). | ||||||||||||||||||||||||||||||
| * This correctly identifies only the commas that separate srcset entries. | ||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||
| $parts = preg_split( '/(\s+\d+[wx]\s*,\s*)/i', $attrvalue, -1, PREG_SPLIT_DELIM_CAPTURE ); | ||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
| $result = ''; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| for ( $i = 0, $len = count( $parts ); $i < $len; $i++ ) { | ||||||||||||||||||||||||||||||
| if ( preg_match( '/^\s+\d+[wx]\s*,\s*$/i', $parts[ $i ] ) ) { | ||||||||||||||||||||||||||||||
| // This is a delimiter: space + descriptor + comma. Append it as-is. | ||||||||||||||||||||||||||||||
| $result .= $parts[ $i ]; | ||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||
| // This is a URL (possibly with a trailing descriptor for the last entry). | ||||||||||||||||||||||||||||||
| $entry = $parts[ $i ]; | ||||||||||||||||||||||||||||||
|
Comment on lines
+1707
to
+1713
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using
Suggested change
|
||||||||||||||||||||||||||||||
| if ( preg_match( '/^(\s*)(.*?)(\s+\d+[wx])?\s*$/i', $entry, $m ) ) { | ||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's use |
||||||||||||||||||||||||||||||
| $leading_ws = $m[1]; | ||||||||||||||||||||||||||||||
| $url = $m[2]; | ||||||||||||||||||||||||||||||
| $descriptor = isset( $m[3] ) ? $m[3] : ''; | ||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
| $result .= $leading_ws . wp_kses_bad_protocol( $url, $allowed_protocols ) . $descriptor; | ||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||
| $result .= wp_kses_bad_protocol( $entry, $allowed_protocols ); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return $result; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return wp_kses_bad_protocol( $attrvalue, $allowed_protocols ); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||
| * Finds all attributes of an HTML element. | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This attribute seems it should be moved up to appear before
srcto maintain alphabetic ordering.