Ticket #45067: 45067.3.diff
File 45067.3.diff, 8.4 KB (added by , 6 years ago) |
---|
-
src/wp-includes/kses.php
diff --git src/wp-includes/kses.php src/wp-includes/kses.php index 0cf4ce14c4..4c6c64bd6c 100644
function wp_kses_one_attr( $string, $element ) { 549 549 $allowed_html = wp_kses_allowed_html( 'post' ); 550 550 $allowed_protocols = wp_allowed_protocols(); 551 551 $string = wp_kses_no_null( $string, array( 'slash_zero' => 'keep' ) ); 552 552 553 553 // Preserve leading and trailing whitespace. 554 554 $matches = array(); 555 555 preg_match('/^\s*/', $string, $matches); … … function wp_kses_one_attr( $string, $element ) { 561 561 } else { 562 562 $string = substr( $string, strlen( $lead ), -strlen( $trail ) ); 563 563 } 564 564 565 565 // Parse attribute name and value from input. 566 566 $split = preg_split( '/\s*=\s*/', $string, 2 ); 567 567 $name = $split[0]; … … function wp_kses_one_attr( $string, $element ) { 598 598 $value = ''; 599 599 $vless = 'y'; 600 600 } 601 601 602 602 // Sanitize attribute by name. 603 603 wp_kses_attr_check( $name, $value, $string, $vless, $element, $allowed_html ); 604 604 … … function wp_kses_attr_parse( $element ) { 1062 1062 } else { 1063 1063 $xhtml_slash = ''; 1064 1064 } 1065 1065 1066 1066 // Split it 1067 1067 $attrarr = wp_kses_hair_parse( $attr ); 1068 1068 if ( false === $attrarr ) { … … function wp_kses_attr_parse( $element ) { 1072 1072 // Make sure all input is returned by adding front and back matter. 1073 1073 array_unshift( $attrarr, $begin . $slash . $elname ); 1074 1074 array_push( $attrarr, $xhtml_slash . $end ); 1075 1075 1076 1076 return $attrarr; 1077 1077 } 1078 1078 … … function wp_kses_check_attr_val($value, $vless, $checkname, $checkvalue) { 1215 1215 * @param array $allowed_protocols Allowed protocols to keep 1216 1216 * @return string Filtered content 1217 1217 */ 1218 function wp_kses_bad_protocol($string, $allowed_protocols) { 1219 $string = wp_kses_no_null($string); 1218 function wp_kses_bad_protocol( $string, $allowed_protocols = array() ) { 1219 if ( empty( $allowed_protocols ) ) { 1220 $allowed_protocols = wp_allowed_protocols(); 1221 } 1222 1223 $string = wp_kses_no_null( $string ); 1220 1224 $iterations = 0; 1221 1225 1222 1226 do { … … function kses_init() { 1687 1691 * @return string Filtered string of CSS rules. 1688 1692 */ 1689 1693 function safecss_filter_attr( $css, $deprecated = '' ) { 1690 if ( ! empty( $deprecated ) )1694 if ( ! empty( $deprecated ) ) 1691 1695 _deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented 1692 1696 1693 $css = wp_kses_no_null($css); 1694 $css = str_replace(array("\n","\r","\t"), '', $css); 1695 1696 if ( preg_match( '%[\\\\(&=}]|/\*%', $css ) ) // remove any inline css containing \ ( & } = or comments 1697 return ''; 1697 $css = wp_kses_no_null( $css ); 1698 $css = str_replace( array( "\n", "\r", "\t" ), '', $css ); 1698 1699 1699 1700 $css_array = explode( ';', trim( $css ) ); 1700 1701 … … function safecss_filter_attr( $css, $deprecated = '' ) { 1710 1711 $allowed_attr = apply_filters( 'safe_style_css', array( 1711 1712 'background', 1712 1713 'background-color', 1714 'background-image', 1713 1715 1714 1716 'border', 1715 1717 'border-width', … … function safecss_filter_attr( $css, $deprecated = '' ) { 1778 1780 'list-style-type', 1779 1781 ) ); 1780 1782 1781 if ( empty($allowed_attr) ) 1783 1784 /* 1785 * CSS attributes that accept URL data types. 1786 * 1787 * This is in accordance to the CSS spec and unrelated to 1788 * the sub-set of supported attributes above. 1789 * 1790 * See: https://developer.mozilla.org/en-US/docs/Web/CSS/url 1791 */ 1792 $css_url_data_types = array( 1793 'background', 1794 'background-image', 1795 1796 'cursor', 1797 1798 'list-style', 1799 'list-style-image', 1800 ); 1801 1802 if ( empty( $allowed_attr ) ) { 1782 1803 return $css; 1804 } 1783 1805 1784 1806 $css = ''; 1785 1807 foreach ( $css_array as $css_item ) { 1786 if ( $css_item == '' ) 1808 if ( $css_item == '' ) { 1787 1809 continue; 1788 $css_item = trim( $css_item ); 1789 $found = false; 1810 } 1811 1812 $css_item = trim( $css_item ); 1813 $css_test_string = $css_item; 1814 $found = false; 1815 $url_attr = false; 1816 1790 1817 if ( strpos( $css_item, ':' ) === false ) { 1791 1818 $found = true; 1792 1819 } else { 1793 $parts = explode( ':', $css_item ); 1794 if ( in_array( trim( $parts[0] ), $allowed_attr ) ) 1820 $parts = explode( ':', $css_item, 2 ); 1821 $css_selector = trim( $parts[0] ); 1822 1823 if ( in_array( $css_selector, $allowed_attr, true ) ) { 1795 1824 $found = true; 1825 $url_attr = in_array( $css_selector, $css_url_data_types, true ); 1826 } 1827 } 1828 1829 if ( $found && $url_attr ) { 1830 // Simplified: matches the sequence `url(*)`. 1831 preg_match_all( '/url\([^)]+\)/', $parts[1], $url_matches ); 1832 1833 foreach ( $url_matches[0] as $url_match ) { 1834 // Clean up the URL from each of the matches above. 1835 preg_match( '/^url\(\s*([\'\"]?)(.*)(\g1)\s*\)$/', $url_match, $url_pieces ); 1836 $url = trim( $url_pieces[2] ); 1837 1838 if ( empty( $url ) || $url !== wp_kses_bad_protocol( $url ) ) { 1839 $found = false; 1840 break; 1841 } else { 1842 // Remove the whole `url(*)` bit that was matched above from the CSS. 1843 $css_test_string = str_replace( $url_match, '', $css_test_string ); 1844 } 1845 } 1796 1846 } 1797 if ( $found ) { 1798 if( $css != '' ) 1847 1848 if ( $found && ! preg_match( '%[\\\(&=}]|/\*%', $css_test_string ) ) { 1849 if ( $css != '' ) { 1799 1850 $css .= ';'; 1851 } 1852 1800 1853 $css .= $css_item; 1801 1854 } 1802 1855 } -
tests/phpunit/tests/kses.php
diff --git tests/phpunit/tests/kses.php tests/phpunit/tests/kses.php index dea4a881a2..ccb1502f22 100644
EOF; 718 718 719 719 $this->assertEquals( "<{$element}>", wp_kses_attr( $element, $attribute, array( 'foo' => false ), array() ) ); 720 720 } 721 722 /** 723 * Test URL sanitization in the style tag. 724 * 725 * @dataProvider data_kses_style_attr_with_url 726 * 727 * @ticket 45067 728 * 729 * @param $input string The style attribute saved in the editor. 730 * @param $expected string The sanitized style attribute. 731 */ 732 function test_kses_style_attr_with_url( $input, $expected ) { 733 $actual = safecss_filter_attr( $input ); 734 735 $this->assertSame( $expected, $actual ); 736 } 737 738 /** 739 * Data provider testing style attribute sanitization. 740 * 741 * @return array Nested array of input, expected pairs. 742 */ 743 function data_kses_style_attr_with_url() { 744 return array( 745 /* 746 * Valid use cases. 747 */ 748 749 // Double quotes. 750 array( 751 'background-image: url( "http://example.com/valid.gif" );', 752 'background-image: url( "http://example.com/valid.gif" )', 753 ), 754 755 // Single quotes. 756 array( 757 "background-image: url( 'http://example.com/valid.gif' );", 758 "background-image: url( 'http://example.com/valid.gif' )", 759 ), 760 761 // No quotes. 762 array( 763 'background-image: url( http://example.com/valid.gif );', 764 'background-image: url( http://example.com/valid.gif )', 765 ), 766 767 // Single quotes, extra spaces. 768 array( 769 "background-image: url( ' http://example.com/valid.gif ' );", 770 "background-image: url( ' http://example.com/valid.gif ' )", 771 ), 772 773 // Line breaks, single quotes. 774 array( 775 "background-image: url(\n'http://example.com/valid.gif' );", 776 "background-image: url('http://example.com/valid.gif' )", 777 ), 778 779 // Tabs not spaces, single quotes. 780 array( 781 "background-image: url(\t'http://example.com/valid.gif'\t\t);", 782 "background-image: url('http://example.com/valid.gif')", 783 ), 784 785 // Single quotes, absolute path. 786 array( 787 "background: url('/valid.gif');", 788 "background: url('/valid.gif')", 789 ), 790 791 // Single quotes, relative path. 792 array( 793 "background: url('../wp-content/uploads/2018/10/valid.gif');", 794 "background: url('../wp-content/uploads/2018/10/valid.gif')", 795 ), 796 797 // Error check: valid property not containing a URL. 798 array( 799 "background: red", 800 "background: red", 801 ), 802 803 /* 804 * Invalid use cases. 805 */ 806 807 // Attribute doesn't support URL properties. 808 array( 809 'color: url( "http://example.com/invalid.gif" );', 810 '', 811 ), 812 813 // Mismatched quotes. 814 array( 815 'background-image: url( "http://example.com/valid.gif\' );', 816 '', 817 ), 818 819 // Bad protocol, double quotes. 820 array( 821 'background-image: url( "bad://example.com/invalid.gif" );', 822 '', 823 ), 824 825 // Bad protocol, single quotes. 826 array( 827 "background-image: url( 'bad://example.com/invalid.gif' );", 828 '', 829 ), 830 831 // Bad protocol, single quotes. 832 array( 833 "background-image: url( 'bad://example.com/invalid.gif' );", 834 '', 835 ), 836 837 // Bad protocol, single quotes, strange spacing. 838 array( 839 "background-image: url( ' \tbad://example.com/invalid.gif ' );", 840 '', 841 ), 842 843 // Bad protocol, no quotes. 844 array( 845 'background-image: url( bad://example.com/invalid.gif );', 846 '', 847 ), 848 849 // No URL inside url(). 850 array( 851 'background-image: url();', 852 '', 853 ), 854 ); 855 } 721 856 }