Skip to content

Commit 996c6d6

Browse files
committed
KSES: Allow functional CSS values in inline styles.
The presentation properties allowlisted in [62530] were accepted in name only: any declaration whose value contained a function call was dropped in full, so for users without the `unfiltered_html` capability those properties remained unusable in their canonical form. URL references are routed through the existing protocol validation, so the security model is unchanged. Developed in: #12942 Follow-up to [62530]. Props courane01, hasnainashfaq, irozum, khokansardar, mdridipu, tyxla. Fixes #65832. git-svn-id: https://develop.svn.wordpress.org/trunk@63180 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 7578111 commit 996c6d6

2 files changed

Lines changed: 140 additions & 1 deletion

File tree

‎src/wp-includes/kses.php‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2638,6 +2638,8 @@ function kses_init() {
26382638
* @since 6.6.0 Added support for `grid-column`, `grid-row`, and `container-type`.
26392639
* @since 6.9.0 Added support for `white-space`.
26402640
* @since 7.1.0 Extended gradient support to allow any single-level nested function.
2641+
* Added support for transform functions, `clip-path` basic shapes,
2642+
* and URLs in the SVG element reference properties.
26412643
*
26422644
* @param string $css A string of CSS rules, decoded from an HTML `style` attribute.
26432645
* @param string $deprecated Not used.
@@ -2906,6 +2908,16 @@ function safecss_filter_attr( $css, $deprecated = '' ) {
29062908

29072909
'list-style',
29082910
'list-style-image',
2911+
2912+
// SVG presentation properties that accept url() references.
2913+
'clip-path',
2914+
'fill',
2915+
'marker',
2916+
'marker-end',
2917+
'marker-mid',
2918+
'marker-start',
2919+
'mask',
2920+
'stroke',
29092921
);
29102922

29112923
/*
@@ -3004,7 +3016,18 @@ function safecss_filter_attr( $css, $deprecated = '' ) {
30043016
* Nested functions and parentheses are also removed, so long as the parentheses are balanced.
30053017
*/
30063018
$css_test_string = preg_replace(
3007-
'/\b(?:var|calc|min|max|minmax|clamp|repeat)(\((?:[^()]|(?1))*\))/',
3019+
'/\b(?:'
3020+
// General purpose value functions.
3021+
. 'var|calc|min|max|minmax|clamp|repeat'
3022+
// Transform functions.
3023+
. '|matrix|matrix3d|perspective'
3024+
. '|rotate|rotate3d|rotateX|rotateY|rotateZ'
3025+
. '|scale|scale3d|scaleX|scaleY|scaleZ'
3026+
. '|skew|skewX|skewY'
3027+
. '|translate|translate3d|translateX|translateY|translateZ'
3028+
// Basic shape functions, as used by `clip-path`.
3029+
. '|circle|ellipse|inset|path|polygon|rect|shape|xywh'
3030+
. ')(\((?:[^()]|(?1))*\))/',
30083031
'',
30093032
$css_test_string
30103033
);

‎tests/phpunit/tests/kses.php‎

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,6 +1224,7 @@ public function test_wp_kses_attr_no_attributes_allowed_with_false() {
12241224
* @ticket 64414
12251225
* @ticket 65457
12261226
* @ticket 64974
1227+
* @ticket 65832
12271228
*
12281229
* @dataProvider data_safecss_filter_attr
12291230
*
@@ -1766,6 +1767,121 @@ public function data_safecss_filter_attr() {
17661767
'css' => 'text-anchor: middle',
17671768
'expected' => 'text-anchor: middle',
17681769
),
1770+
// SVG transform functions (ticket #65832).
1771+
array(
1772+
'css' => 'transform: rotate(45deg)',
1773+
'expected' => 'transform: rotate(45deg)',
1774+
),
1775+
array(
1776+
'css' => 'transform: translate(10px, 20px)',
1777+
'expected' => 'transform: translate(10px, 20px)',
1778+
),
1779+
array(
1780+
'css' => 'transform: scale(1.5)',
1781+
'expected' => 'transform: scale(1.5)',
1782+
),
1783+
array(
1784+
'css' => 'transform: matrix(1, 0, 0, 1, 10, 20)',
1785+
'expected' => 'transform: matrix(1, 0, 0, 1, 10, 20)',
1786+
),
1787+
array(
1788+
'css' => 'transform: skewX(30deg)',
1789+
'expected' => 'transform: skewX(30deg)',
1790+
),
1791+
array(
1792+
'css' => 'transform: skewY(30deg)',
1793+
'expected' => 'transform: skewY(30deg)',
1794+
),
1795+
// Multiple transform functions chained.
1796+
array(
1797+
'css' => 'transform: rotate(45deg) scale(1.5)',
1798+
'expected' => 'transform: rotate(45deg) scale(1.5)',
1799+
),
1800+
// transform: none is unchanged (regression control).
1801+
array(
1802+
'css' => 'transform: none',
1803+
'expected' => 'transform: none',
1804+
),
1805+
// SVG clip-path shape functions (ticket #65832).
1806+
array(
1807+
'css' => 'clip-path: inset(10px)',
1808+
'expected' => 'clip-path: inset(10px)',
1809+
),
1810+
array(
1811+
'css' => 'clip-path: circle(50%)',
1812+
'expected' => 'clip-path: circle(50%)',
1813+
),
1814+
array(
1815+
'css' => 'clip-path: ellipse(25% 40% at 50% 50%)',
1816+
'expected' => 'clip-path: ellipse(25% 40% at 50% 50%)',
1817+
),
1818+
array(
1819+
'css' => 'clip-path: polygon(50% 0%, 100% 100%, 0% 100%)',
1820+
'expected' => 'clip-path: polygon(50% 0%, 100% 100%, 0% 100%)',
1821+
),
1822+
array(
1823+
'css' => "clip-path: path('M 0 0 L 100 0 L 50 100 Z')",
1824+
'expected' => "clip-path: path('M 0 0 L 100 0 L 50 100 Z')",
1825+
),
1826+
array(
1827+
'css' => 'clip-path: rect(0 100% 100% 0)',
1828+
'expected' => 'clip-path: rect(0 100% 100% 0)',
1829+
),
1830+
array(
1831+
'css' => 'clip-path: xywh(0 0 100% 100% round 10px)',
1832+
'expected' => 'clip-path: xywh(0 0 100% 100% round 10px)',
1833+
),
1834+
array(
1835+
'css' => 'clip-path: shape(from 0 0, line to 100% 0, line to 50% 100%, close)',
1836+
'expected' => 'clip-path: shape(from 0 0, line to 100% 0, line to 50% 100%, close)',
1837+
),
1838+
// Nested functions within a basic shape are allowed.
1839+
array(
1840+
'css' => 'clip-path: inset(calc(10px + 1em) round var(--radius))',
1841+
'expected' => 'clip-path: inset(calc(10px + 1em) round var(--radius))',
1842+
),
1843+
// SVG url() references for allowlisted properties (ticket #65832).
1844+
array(
1845+
'css' => 'clip-path: url(#myClipper)',
1846+
'expected' => 'clip-path: url(#myClipper)',
1847+
),
1848+
array(
1849+
'css' => 'fill: url(#gradient1)',
1850+
'expected' => 'fill: url(#gradient1)',
1851+
),
1852+
array(
1853+
'css' => 'mask: url(#myMask)',
1854+
'expected' => 'mask: url(#myMask)',
1855+
),
1856+
array(
1857+
'css' => 'marker-start: url(#arrowStart)',
1858+
'expected' => 'marker-start: url(#arrowStart)',
1859+
),
1860+
array(
1861+
'css' => 'marker-end: url(#arrowEnd)',
1862+
'expected' => 'marker-end: url(#arrowEnd)',
1863+
),
1864+
array(
1865+
'css' => 'marker-mid: url(#arrowMid)',
1866+
'expected' => 'marker-mid: url(#arrowMid)',
1867+
),
1868+
array(
1869+
'css' => 'marker: url(#marker1)',
1870+
'expected' => 'marker: url(#marker1)',
1871+
),
1872+
array(
1873+
'css' => 'stroke: url(#strokeGradient)',
1874+
'expected' => 'stroke: url(#strokeGradient)',
1875+
),
1876+
// Disallow javascript: URLs in SVG url() references (security regression).
1877+
array(
1878+
'css' => 'fill: url(javascript:alert(1))',
1879+
'expected' => '',
1880+
),
1881+
array(
1882+
'css' => 'clip-path: url(javascript:alert(1))',
1883+
'expected' => '',
1884+
),
17691885
);
17701886
}
17711887

0 commit comments

Comments
 (0)