45 define( 'CUSTOM_TAGS', false ); |
45 define( 'CUSTOM_TAGS', false ); |
46 } |
46 } |
47 |
47 |
48 // Ensure that these variables are added to the global namespace |
48 // Ensure that these variables are added to the global namespace |
49 // (e.g. if using namespaces / autoload in the current PHP environment). |
49 // (e.g. if using namespaces / autoload in the current PHP environment). |
50 global $allowedposttags, $allowedtags, $allowedentitynames; |
50 global $allowedposttags, $allowedtags, $allowedentitynames, $allowedxmlentitynames; |
51 |
51 |
52 if ( ! CUSTOM_TAGS ) { |
52 if ( ! CUSTOM_TAGS ) { |
53 /** |
53 /** |
54 * KSES global for default allowable HTML tags. |
54 * KSES global for default allowable HTML tags. |
55 * |
55 * |
721 * @see wp_allowed_protocols() for the default allowed protocols in link URLs. |
735 * @see wp_allowed_protocols() for the default allowed protocols in link URLs. |
722 * |
736 * |
723 * @since 1.0.0 |
737 * @since 1.0.0 |
724 * |
738 * |
725 * @param string $string Text content to filter. |
739 * @param string $string Text content to filter. |
726 * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, or a |
740 * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, |
727 * context name such as 'post'. |
741 * or a context name such as 'post'. See wp_kses_allowed_html() |
|
742 * for the list of accepted context names. |
728 * @param string[] $allowed_protocols Array of allowed URL protocols. |
743 * @param string[] $allowed_protocols Array of allowed URL protocols. |
729 * @return string Filtered content containing only the allowed HTML. |
744 * @return string Filtered content containing only the allowed HTML. |
730 */ |
745 */ |
731 function wp_kses( $string, $allowed_html, $allowed_protocols = array() ) { |
746 function wp_kses( $string, $allowed_html, $allowed_protocols = array() ) { |
732 if ( empty( $allowed_protocols ) ) { |
747 if ( empty( $allowed_protocols ) ) { |
733 $allowed_protocols = wp_allowed_protocols(); |
748 $allowed_protocols = wp_allowed_protocols(); |
734 } |
749 } |
|
750 |
735 $string = wp_kses_no_null( $string, array( 'slash_zero' => 'keep' ) ); |
751 $string = wp_kses_no_null( $string, array( 'slash_zero' => 'keep' ) ); |
736 $string = wp_kses_normalize_entities( $string ); |
752 $string = wp_kses_normalize_entities( $string ); |
737 $string = wp_kses_hook( $string, $allowed_html, $allowed_protocols ); |
753 $string = wp_kses_hook( $string, $allowed_html, $allowed_protocols ); |
|
754 |
738 return wp_kses_split( $string, $allowed_html, $allowed_protocols ); |
755 return wp_kses_split( $string, $allowed_html, $allowed_protocols ); |
739 } |
756 } |
740 |
757 |
741 /** |
758 /** |
742 * Filters one HTML attribute and ensures its value is allowed. |
759 * Filters one HTML attribute and ensures its value is allowed. |
773 if ( count( $split ) == 2 ) { |
790 if ( count( $split ) == 2 ) { |
774 $value = $split[1]; |
791 $value = $split[1]; |
775 |
792 |
776 // Remove quotes surrounding $value. |
793 // Remove quotes surrounding $value. |
777 // Also guarantee correct quoting in $string for this one attribute. |
794 // Also guarantee correct quoting in $string for this one attribute. |
778 if ( '' == $value ) { |
795 if ( '' === $value ) { |
779 $quote = ''; |
796 $quote = ''; |
780 } else { |
797 } else { |
781 $quote = $value[0]; |
798 $quote = $value[0]; |
782 } |
799 } |
783 if ( '"' == $quote || "'" == $quote ) { |
800 if ( '"' === $quote || "'" === $quote ) { |
784 if ( substr( $value, -1 ) != $quote ) { |
801 if ( substr( $value, -1 ) != $quote ) { |
785 return ''; |
802 return ''; |
786 } |
803 } |
787 $value = substr( $value, 1, -1 ); |
804 $value = substr( $value, 1, -1 ); |
788 } else { |
805 } else { |
791 |
808 |
792 // Sanitize quotes, angle braces, and entities. |
809 // Sanitize quotes, angle braces, and entities. |
793 $value = esc_attr( $value ); |
810 $value = esc_attr( $value ); |
794 |
811 |
795 // Sanitize URI values. |
812 // Sanitize URI values. |
796 if ( in_array( strtolower( $name ), $uris ) ) { |
813 if ( in_array( strtolower( $name ), $uris, true ) ) { |
797 $value = wp_kses_bad_protocol( $value, $allowed_protocols ); |
814 $value = wp_kses_bad_protocol( $value, $allowed_protocols ); |
798 } |
815 } |
799 |
816 |
800 $string = "$name=$quote$value$quote"; |
817 $string = "$name=$quote$value$quote"; |
801 $vless = 'n'; |
818 $vless = 'n'; |
894 * There is currently only one KSES WordPress hook, {@see 'pre_kses'}, and it is called here. |
911 * There is currently only one KSES WordPress hook, {@see 'pre_kses'}, and it is called here. |
895 * All parameters are passed to the hooks and expected to receive a string. |
912 * All parameters are passed to the hooks and expected to receive a string. |
896 * |
913 * |
897 * @since 1.0.0 |
914 * @since 1.0.0 |
898 * |
915 * |
899 * @param string $string Content to filter through KSES. |
916 * @param string $string Content to filter through KSES. |
900 * @param array[]|string $allowed_html List of allowed HTML elements. |
917 * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, |
901 * @param string[] $allowed_protocols Array of allowed URL protocols. |
918 * or a context name such as 'post'. See wp_kses_allowed_html() |
|
919 * for the list of accepted context names. |
|
920 * @param string[] $allowed_protocols Array of allowed URL protocols. |
902 * @return string Filtered content through {@see 'pre_kses'} hook. |
921 * @return string Filtered content through {@see 'pre_kses'} hook. |
903 */ |
922 */ |
904 function wp_kses_hook( $string, $allowed_html, $allowed_protocols ) { |
923 function wp_kses_hook( $string, $allowed_html, $allowed_protocols ) { |
905 /** |
924 /** |
906 * Filters content to be run through kses. |
925 * Filters content to be run through KSES. |
907 * |
926 * |
908 * @since 2.3.0 |
927 * @since 2.3.0 |
909 * |
928 * |
910 * @param string $string Content to run through KSES. |
929 * @param string $string Content to filter through KSES. |
911 * @param array[]|string $allowed_html Allowed HTML elements. |
930 * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, |
912 * @param string[] $allowed_protocols Array of allowed URL protocols. |
931 * or a context name such as 'post'. See wp_kses_allowed_html() |
|
932 * for the list of accepted context names. |
|
933 * @param string[] $allowed_protocols Array of allowed URL protocols. |
913 */ |
934 */ |
914 return apply_filters( 'pre_kses', $string, $allowed_html, $allowed_protocols ); |
935 return apply_filters( 'pre_kses', $string, $allowed_html, $allowed_protocols ); |
915 } |
936 } |
916 |
937 |
917 /** |
938 /** |
930 * |
951 * |
931 * It also matches stray `>` characters. |
952 * It also matches stray `>` characters. |
932 * |
953 * |
933 * @since 1.0.0 |
954 * @since 1.0.0 |
934 * |
955 * |
935 * @global array $pass_allowed_html |
956 * @global array[]|string $pass_allowed_html An array of allowed HTML elements and attributes, |
936 * @global array $pass_allowed_protocols |
957 * or a context name such as 'post'. |
937 * |
958 * @global string[] $pass_allowed_protocols Array of allowed URL protocols. |
938 * @param string $string Content to filter. |
959 * |
939 * @param array $allowed_html Allowed HTML elements. |
960 * @param string $string Content to filter. |
940 * @param string[] $allowed_protocols Array of allowed URL protocols. |
961 * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, |
|
962 * or a context name such as 'post'. See wp_kses_allowed_html() |
|
963 * for the list of accepted context names. |
|
964 * @param string[] $allowed_protocols Array of allowed URL protocols. |
941 * @return string Content with fixed HTML tags |
965 * @return string Content with fixed HTML tags |
942 */ |
966 */ |
943 function wp_kses_split( $string, $allowed_html, $allowed_protocols ) { |
967 function wp_kses_split( $string, $allowed_html, $allowed_protocols ) { |
944 global $pass_allowed_html, $pass_allowed_protocols; |
968 global $pass_allowed_html, $pass_allowed_protocols; |
|
969 |
945 $pass_allowed_html = $allowed_html; |
970 $pass_allowed_html = $allowed_html; |
946 $pass_allowed_protocols = $allowed_protocols; |
971 $pass_allowed_protocols = $allowed_protocols; |
|
972 |
947 return preg_replace_callback( '%(<!--.*?(-->|$))|(<[^>]*(>|$)|>)%', '_wp_kses_split_callback', $string ); |
973 return preg_replace_callback( '%(<!--.*?(-->|$))|(<[^>]*(>|$)|>)%', '_wp_kses_split_callback', $string ); |
948 } |
974 } |
949 |
975 |
950 /** |
976 /** |
951 * Helper function listing HTML attributes containing a URL. |
977 * Returns an array of HTML attribute names whose value contains a URL. |
952 * |
978 * |
953 * This function returns a list of all HTML attributes that must contain |
979 * This function returns a list of all HTML attributes that must contain |
954 * a URL according to the HTML specification. |
980 * a URL according to the HTML specification. |
955 * |
981 * |
956 * This list includes URI attributes both allowed and disallowed by KSES. |
982 * This list includes URI attributes both allowed and disallowed by KSES. |
957 * |
983 * |
958 * @link https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes |
984 * @link https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes |
959 * |
985 * |
960 * @since 5.0.1 |
986 * @since 5.0.1 |
961 * |
987 * |
962 * @return array HTML attributes that must include a URL. |
988 * @return string[] HTML attribute names whose value contains a URL. |
963 */ |
989 */ |
964 function wp_kses_uri_attributes() { |
990 function wp_kses_uri_attributes() { |
965 $uri_attributes = array( |
991 $uri_attributes = array( |
966 'action', |
992 'action', |
967 'archive', |
993 'archive', |
1002 * |
1028 * |
1003 * @since 3.1.0 |
1029 * @since 3.1.0 |
1004 * @access private |
1030 * @access private |
1005 * @ignore |
1031 * @ignore |
1006 * |
1032 * |
1007 * @global array $pass_allowed_html |
1033 * @global array[]|string $pass_allowed_html An array of allowed HTML elements and attributes, |
1008 * @global array $pass_allowed_protocols |
1034 * or a context name such as 'post'. |
1009 * |
1035 * @global string[] $pass_allowed_protocols Array of allowed URL protocols. |
|
1036 * |
|
1037 * @param array $matches preg_replace regexp matches |
1010 * @return string |
1038 * @return string |
1011 */ |
1039 */ |
1012 function _wp_kses_split_callback( $match ) { |
1040 function _wp_kses_split_callback( $match ) { |
1013 global $pass_allowed_html, $pass_allowed_protocols; |
1041 global $pass_allowed_html, $pass_allowed_protocols; |
|
1042 |
1014 return wp_kses_split2( $match[0], $pass_allowed_html, $pass_allowed_protocols ); |
1043 return wp_kses_split2( $match[0], $pass_allowed_html, $pass_allowed_protocols ); |
1015 } |
1044 } |
1016 |
1045 |
1017 /** |
1046 /** |
1018 * Callback for `wp_kses_split()` for fixing malformed HTML tags. |
1047 * Callback for `wp_kses_split()` for fixing malformed HTML tags. |
1028 * |
1057 * |
1029 * @access private |
1058 * @access private |
1030 * @ignore |
1059 * @ignore |
1031 * @since 1.0.0 |
1060 * @since 1.0.0 |
1032 * |
1061 * |
1033 * @param string $string Content to filter. |
1062 * @param string $string Content to filter. |
1034 * @param array $allowed_html Allowed HTML elements. |
1063 * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, |
1035 * @param string[] $allowed_protocols Array of allowed URL protocols. |
1064 * or a context name such as 'post'. See wp_kses_allowed_html() |
|
1065 * for the list of accepted context names. |
|
1066 * @param string[] $allowed_protocols Array of allowed URL protocols. |
1036 * @return string Fixed HTML element |
1067 * @return string Fixed HTML element |
1037 */ |
1068 */ |
1038 function wp_kses_split2( $string, $allowed_html, $allowed_protocols ) { |
1069 function wp_kses_split2( $string, $allowed_html, $allowed_protocols ) { |
1039 $string = wp_kses_stripslashes( $string ); |
1070 $string = wp_kses_stripslashes( $string ); |
1040 |
1071 |
1041 // It matched a ">" character. |
1072 // It matched a ">" character. |
1042 if ( substr( $string, 0, 1 ) != '<' ) { |
1073 if ( '<' !== substr( $string, 0, 1 ) ) { |
1043 return '>'; |
1074 return '>'; |
1044 } |
1075 } |
1045 |
1076 |
1046 // Allow HTML comments. |
1077 // Allow HTML comments. |
1047 if ( '<!--' == substr( $string, 0, 4 ) ) { |
1078 if ( '<!--' === substr( $string, 0, 4 ) ) { |
1048 $string = str_replace( array( '<!--', '-->' ), '', $string ); |
1079 $string = str_replace( array( '<!--', '-->' ), '', $string ); |
1049 while ( $string != ( $newstring = wp_kses( $string, $allowed_html, $allowed_protocols ) ) ) { |
1080 while ( ( $newstring = wp_kses( $string, $allowed_html, $allowed_protocols ) ) != $string ) { |
1050 $string = $newstring; |
1081 $string = $newstring; |
1051 } |
1082 } |
1052 if ( $string == '' ) { |
1083 if ( '' === $string ) { |
1053 return ''; |
1084 return ''; |
1054 } |
1085 } |
1055 // prevent multiple dashes in comments |
1086 // Prevent multiple dashes in comments. |
1056 $string = preg_replace( '/--+/', '-', $string ); |
1087 $string = preg_replace( '/--+/', '-', $string ); |
1057 // prevent three dashes closing a comment |
1088 // Prevent three dashes closing a comment. |
1058 $string = preg_replace( '/-$/', '', $string ); |
1089 $string = preg_replace( '/-$/', '', $string ); |
1059 return "<!--{$string}-->"; |
1090 return "<!--{$string}-->"; |
1060 } |
1091 } |
1061 |
1092 |
1062 // It's seriously malformed. |
1093 // It's seriously malformed. |
1094 * is to check if the tag has a closing XHTML slash, and if it does, it puts one |
1125 * is to check if the tag has a closing XHTML slash, and if it does, it puts one |
1095 * in the returned code as well. |
1126 * in the returned code as well. |
1096 * |
1127 * |
1097 * @since 1.0.0 |
1128 * @since 1.0.0 |
1098 * |
1129 * |
1099 * @param string $element HTML element/tag. |
1130 * @param string $element HTML element/tag. |
1100 * @param string $attr HTML attributes from HTML element to closing HTML element tag. |
1131 * @param string $attr HTML attributes from HTML element to closing HTML element tag. |
1101 * @param array $allowed_html Allowed HTML elements. |
1132 * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, |
1102 * @param string[] $allowed_protocols Array of allowed URL protocols. |
1133 * or a context name such as 'post'. See wp_kses_allowed_html() |
|
1134 * for the list of accepted context names. |
|
1135 * @param string[] $allowed_protocols Array of allowed URL protocols. |
1103 * @return string Sanitized HTML element. |
1136 * @return string Sanitized HTML element. |
1104 */ |
1137 */ |
1105 function wp_kses_attr( $element, $attr, $allowed_html, $allowed_protocols ) { |
1138 function wp_kses_attr( $element, $attr, $allowed_html, $allowed_protocols ) { |
1106 if ( ! is_array( $allowed_html ) ) { |
1139 if ( ! is_array( $allowed_html ) ) { |
1107 $allowed_html = wp_kses_allowed_html( $allowed_html ); |
1140 $allowed_html = wp_kses_allowed_html( $allowed_html ); |
1117 $element_low = strtolower( $element ); |
1150 $element_low = strtolower( $element ); |
1118 if ( empty( $allowed_html[ $element_low ] ) || true === $allowed_html[ $element_low ] ) { |
1151 if ( empty( $allowed_html[ $element_low ] ) || true === $allowed_html[ $element_low ] ) { |
1119 return "<$element$xhtml_slash>"; |
1152 return "<$element$xhtml_slash>"; |
1120 } |
1153 } |
1121 |
1154 |
1122 // Split it |
1155 // Split it. |
1123 $attrarr = wp_kses_hair( $attr, $allowed_protocols ); |
1156 $attrarr = wp_kses_hair( $attr, $allowed_protocols ); |
1124 |
1157 |
1125 // Go through $attrarr, and save the allowed attributes for this element |
1158 // Go through $attrarr, and save the allowed attributes for this element |
1126 // in $attr2 |
1159 // in $attr2. |
1127 $attr2 = ''; |
1160 $attr2 = ''; |
1128 foreach ( $attrarr as $arreach ) { |
1161 foreach ( $attrarr as $arreach ) { |
1129 if ( wp_kses_attr_check( $arreach['name'], $arreach['value'], $arreach['whole'], $arreach['vless'], $element, $allowed_html ) ) { |
1162 if ( wp_kses_attr_check( $arreach['name'], $arreach['value'], $arreach['whole'], $arreach['vless'], $element, $allowed_html ) ) { |
1130 $attr2 .= ' ' . $arreach['whole']; |
1163 $attr2 .= ' ' . $arreach['whole']; |
1131 } |
1164 } |
1132 } |
1165 } |
1133 |
1166 |
1134 // Remove any "<" or ">" characters |
1167 // Remove any "<" or ">" characters. |
1135 $attr2 = preg_replace( '/[<>]/', '', $attr2 ); |
1168 $attr2 = preg_replace( '/[<>]/', '', $attr2 ); |
1136 |
1169 |
1137 return "<$element$attr2$xhtml_slash>"; |
1170 return "<$element$attr2$xhtml_slash>"; |
1138 } |
1171 } |
1139 |
1172 |
1150 * @param string $element The name of the element to which this attribute belongs. |
1183 * @param string $element The name of the element to which this attribute belongs. |
1151 * @param array $allowed_html The full list of allowed elements and attributes. |
1184 * @param array $allowed_html The full list of allowed elements and attributes. |
1152 * @return bool Whether or not the attribute is allowed. |
1185 * @return bool Whether or not the attribute is allowed. |
1153 */ |
1186 */ |
1154 function wp_kses_attr_check( &$name, &$value, &$whole, $vless, $element, $allowed_html ) { |
1187 function wp_kses_attr_check( &$name, &$value, &$whole, $vless, $element, $allowed_html ) { |
1155 $allowed_attr = $allowed_html[ strtolower( $element ) ]; |
1188 $name_low = strtolower( $name ); |
1156 |
1189 $element_low = strtolower( $element ); |
1157 $name_low = strtolower( $name ); |
1190 |
1158 if ( ! isset( $allowed_attr[ $name_low ] ) || '' == $allowed_attr[ $name_low ] ) { |
1191 if ( ! isset( $allowed_html[ $element_low ] ) ) { |
|
1192 $name = ''; |
|
1193 $value = ''; |
|
1194 $whole = ''; |
|
1195 return false; |
|
1196 } |
|
1197 |
|
1198 $allowed_attr = $allowed_html[ $element_low ]; |
|
1199 |
|
1200 if ( ! isset( $allowed_attr[ $name_low ] ) || '' === $allowed_attr[ $name_low ] ) { |
1159 /* |
1201 /* |
1160 * Allow `data-*` attributes. |
1202 * Allow `data-*` attributes. |
1161 * |
1203 * |
1162 * When specifying `$allowed_html`, the attribute name should be set as |
1204 * When specifying `$allowed_html`, the attribute name should be set as |
1163 * `data-*` (not to be mixed with the HTML 4.0 `data` attribute, see |
1205 * `data-*` (not to be mixed with the HTML 4.0 `data` attribute, see |
1171 * Add the whole attribute name to the allowed attributes and set any restrictions |
1213 * Add the whole attribute name to the allowed attributes and set any restrictions |
1172 * for the `data-*` attribute values for the current element. |
1214 * for the `data-*` attribute values for the current element. |
1173 */ |
1215 */ |
1174 $allowed_attr[ $match[0] ] = $allowed_attr['data-*']; |
1216 $allowed_attr[ $match[0] ] = $allowed_attr['data-*']; |
1175 } else { |
1217 } else { |
1176 $name = $value = $whole = ''; |
1218 $name = ''; |
|
1219 $value = ''; |
|
1220 $whole = ''; |
1177 return false; |
1221 return false; |
1178 } |
1222 } |
1179 } |
1223 } |
1180 |
1224 |
1181 if ( 'style' == $name_low ) { |
1225 if ( 'style' === $name_low ) { |
1182 $new_value = safecss_filter_attr( $value ); |
1226 $new_value = safecss_filter_attr( $value ); |
1183 |
1227 |
1184 if ( empty( $new_value ) ) { |
1228 if ( empty( $new_value ) ) { |
1185 $name = $value = $whole = ''; |
1229 $name = ''; |
|
1230 $value = ''; |
|
1231 $whole = ''; |
1186 return false; |
1232 return false; |
1187 } |
1233 } |
1188 |
1234 |
1189 $whole = str_replace( $value, $new_value, $whole ); |
1235 $whole = str_replace( $value, $new_value, $whole ); |
1190 $value = $new_value; |
1236 $value = $new_value; |
1191 } |
1237 } |
1192 |
1238 |
1193 if ( is_array( $allowed_attr[ $name_low ] ) ) { |
1239 if ( is_array( $allowed_attr[ $name_low ] ) ) { |
1194 // there are some checks |
1240 // There are some checks. |
1195 foreach ( $allowed_attr[ $name_low ] as $currkey => $currval ) { |
1241 foreach ( $allowed_attr[ $name_low ] as $currkey => $currval ) { |
1196 if ( ! wp_kses_check_attr_val( $value, $vless, $currkey, $currval ) ) { |
1242 if ( ! wp_kses_check_attr_val( $value, $vless, $currkey, $currval ) ) { |
1197 $name = $value = $whole = ''; |
1243 $name = ''; |
|
1244 $value = ''; |
|
1245 $whole = ''; |
1198 return false; |
1246 return false; |
1199 } |
1247 } |
1200 } |
1248 } |
1201 } |
1249 } |
1202 |
1250 |
1224 $attrarr = array(); |
1272 $attrarr = array(); |
1225 $mode = 0; |
1273 $mode = 0; |
1226 $attrname = ''; |
1274 $attrname = ''; |
1227 $uris = wp_kses_uri_attributes(); |
1275 $uris = wp_kses_uri_attributes(); |
1228 |
1276 |
1229 // Loop through the whole attribute list |
1277 // Loop through the whole attribute list. |
1230 |
1278 |
1231 while ( strlen( $attr ) != 0 ) { |
1279 while ( strlen( $attr ) != 0 ) { |
1232 $working = 0; // Was the last operation successful? |
1280 $working = 0; // Was the last operation successful? |
1233 |
1281 |
1234 switch ( $mode ) { |
1282 switch ( $mode ) { |
1235 case 0: |
1283 case 0: |
1236 if ( preg_match( '/^([-a-zA-Z:]+)/', $attr, $match ) ) { |
1284 if ( preg_match( '/^([_a-zA-Z][-_a-zA-Z0-9:.]*)/', $attr, $match ) ) { |
1237 $attrname = $match[1]; |
1285 $attrname = $match[1]; |
1238 $working = $mode = 1; |
1286 $working = 1; |
1239 $attr = preg_replace( '/^[-a-zA-Z:]+/', '', $attr ); |
1287 $mode = 1; |
|
1288 $attr = preg_replace( '/^[_a-zA-Z][-_a-zA-Z0-9:.]*/', '', $attr ); |
1240 } |
1289 } |
1241 |
1290 |
1242 break; |
1291 break; |
1243 |
1292 |
1244 case 1: |
1293 case 1: |
1245 if ( preg_match( '/^\s*=\s*/', $attr ) ) { // equals sign |
1294 if ( preg_match( '/^\s*=\s*/', $attr ) ) { // Equals sign. |
1246 $working = 1; |
1295 $working = 1; |
1247 $mode = 2; |
1296 $mode = 2; |
1248 $attr = preg_replace( '/^\s*=\s*/', '', $attr ); |
1297 $attr = preg_replace( '/^\s*=\s*/', '', $attr ); |
1249 break; |
1298 break; |
1250 } |
1299 } |
1251 |
1300 |
1252 if ( preg_match( '/^\s+/', $attr ) ) { // valueless |
1301 if ( preg_match( '/^\s+/', $attr ) ) { // Valueless. |
1253 $working = 1; |
1302 $working = 1; |
1254 $mode = 0; |
1303 $mode = 0; |
1255 if ( false === array_key_exists( $attrname, $attrarr ) ) { |
1304 if ( false === array_key_exists( $attrname, $attrarr ) ) { |
1256 $attrarr[ $attrname ] = array( |
1305 $attrarr[ $attrname ] = array( |
1257 'name' => $attrname, |
1306 'name' => $attrname, |
1267 |
1316 |
1268 case 2: |
1317 case 2: |
1269 if ( preg_match( '%^"([^"]*)"(\s+|/?$)%', $attr, $match ) ) { |
1318 if ( preg_match( '%^"([^"]*)"(\s+|/?$)%', $attr, $match ) ) { |
1270 // "value" |
1319 // "value" |
1271 $thisval = $match[1]; |
1320 $thisval = $match[1]; |
1272 if ( in_array( strtolower( $attrname ), $uris ) ) { |
1321 if ( in_array( strtolower( $attrname ), $uris, true ) ) { |
1273 $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols ); |
1322 $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols ); |
1274 } |
1323 } |
1275 |
1324 |
1276 if ( false === array_key_exists( $attrname, $attrarr ) ) { |
1325 if ( false === array_key_exists( $attrname, $attrarr ) ) { |
1277 $attrarr[ $attrname ] = array( |
1326 $attrarr[ $attrname ] = array( |
1288 } |
1337 } |
1289 |
1338 |
1290 if ( preg_match( "%^'([^']*)'(\s+|/?$)%", $attr, $match ) ) { |
1339 if ( preg_match( "%^'([^']*)'(\s+|/?$)%", $attr, $match ) ) { |
1291 // 'value' |
1340 // 'value' |
1292 $thisval = $match[1]; |
1341 $thisval = $match[1]; |
1293 if ( in_array( strtolower( $attrname ), $uris ) ) { |
1342 if ( in_array( strtolower( $attrname ), $uris, true ) ) { |
1294 $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols ); |
1343 $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols ); |
1295 } |
1344 } |
1296 |
1345 |
1297 if ( false === array_key_exists( $attrname, $attrarr ) ) { |
1346 if ( false === array_key_exists( $attrname, $attrarr ) ) { |
1298 $attrarr[ $attrname ] = array( |
1347 $attrarr[ $attrname ] = array( |
1309 } |
1358 } |
1310 |
1359 |
1311 if ( preg_match( "%^([^\s\"']+)(\s+|/?$)%", $attr, $match ) ) { |
1360 if ( preg_match( "%^([^\s\"']+)(\s+|/?$)%", $attr, $match ) ) { |
1312 // value |
1361 // value |
1313 $thisval = $match[1]; |
1362 $thisval = $match[1]; |
1314 if ( in_array( strtolower( $attrname ), $uris ) ) { |
1363 if ( in_array( strtolower( $attrname ), $uris, true ) ) { |
1315 $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols ); |
1364 $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols ); |
1316 } |
1365 } |
1317 |
1366 |
1318 if ( false === array_key_exists( $attrname, $attrarr ) ) { |
1367 if ( false === array_key_exists( $attrname, $attrarr ) ) { |
1319 $attrarr[ $attrname ] = array( |
1368 $attrarr[ $attrname ] = array( |
1328 $mode = 0; |
1377 $mode = 0; |
1329 $attr = preg_replace( "%^[^\s\"']+(\s+|$)%", '', $attr ); |
1378 $attr = preg_replace( "%^[^\s\"']+(\s+|$)%", '', $attr ); |
1330 } |
1379 } |
1331 |
1380 |
1332 break; |
1381 break; |
1333 } // switch |
1382 } // End switch. |
1334 |
1383 |
1335 if ( $working == 0 ) { // not well formed, remove and try again |
1384 if ( 0 == $working ) { // Not well-formed, remove and try again. |
1336 $attr = wp_kses_html_error( $attr ); |
1385 $attr = wp_kses_html_error( $attr ); |
1337 $mode = 0; |
1386 $mode = 0; |
1338 } |
1387 } |
1339 } // while |
1388 } // End while. |
1340 |
1389 |
1341 if ( $mode == 1 && false === array_key_exists( $attrname, $attrarr ) ) { |
1390 if ( 1 == $mode && false === array_key_exists( $attrname, $attrarr ) ) { |
1342 // special case, for when the attribute list ends with a valueless |
1391 // Special case, for when the attribute list ends with a valueless |
1343 // attribute like "selected" |
1392 // attribute like "selected". |
1344 $attrarr[ $attrname ] = array( |
1393 $attrarr[ $attrname ] = array( |
1345 'name' => $attrname, |
1394 'name' => $attrname, |
1346 'value' => '', |
1395 'value' => '', |
1347 'whole' => $attrname, |
1396 'whole' => $attrname, |
1348 'vless' => 'y', |
1397 'vless' => 'y', |
1420 return array(); |
1469 return array(); |
1421 } |
1470 } |
1422 |
1471 |
1423 // phpcs:disable Squiz.Strings.ConcatenationSpacing.PaddingFound -- don't remove regex indentation |
1472 // phpcs:disable Squiz.Strings.ConcatenationSpacing.PaddingFound -- don't remove regex indentation |
1424 $regex = |
1473 $regex = |
1425 '(?:' |
1474 '(?:' |
1426 . '[-a-zA-Z:]+' // Attribute name. |
1475 . '[_a-zA-Z][-_a-zA-Z0-9:.]*' // Attribute name. |
1427 . '|' |
1476 . '|' |
1428 . '\[\[?[^\[\]]+\]\]?' // Shortcode in the name position implies unfiltered_html. |
1477 . '\[\[?[^\[\]]+\]\]?' // Shortcode in the name position implies unfiltered_html. |
1429 . ')' |
1478 . ')' |
1430 . '(?:' // Attribute value. |
1479 . '(?:' // Attribute value. |
1431 . '\s*=\s*' // All values begin with '=' |
1480 . '\s*=\s*' // All values begin with '='. |
1432 . '(?:' |
1481 . '(?:' |
1433 . '"[^"]*"' // Double-quoted |
1482 . '"[^"]*"' // Double-quoted. |
1434 . '|' |
1483 . '|' |
1435 . "'[^']*'" // Single-quoted |
1484 . "'[^']*'" // Single-quoted. |
1436 . '|' |
1485 . '|' |
1437 . '[^\s"\']+' // Non-quoted |
1486 . '[^\s"\']+' // Non-quoted. |
1438 . '(?:\s|$)' // Must have a space |
1487 . '(?:\s|$)' // Must have a space. |
1439 . ')' |
1488 . ')' |
1440 . '|' |
1489 . '|' |
1441 . '(?:\s|$)' // If attribute has no value, space is required. |
1490 . '(?:\s|$)' // If attribute has no value, space is required. |
1442 . ')' |
1491 . ')' |
1443 . '\s*'; // Trailing space is optional except as mentioned above. |
1492 . '\s*'; // Trailing space is optional except as mentioned above. |
1444 // phpcs:enable |
1493 // phpcs:enable |
1445 |
1494 |
1446 // Although it is possible to reduce this procedure to a single regexp, |
1495 // Although it is possible to reduce this procedure to a single regexp, |
1447 // we must run that regexp twice to get exactly the expected result. |
1496 // we must run that regexp twice to get exactly the expected result. |
1448 |
1497 |
1474 function wp_kses_check_attr_val( $value, $vless, $checkname, $checkvalue ) { |
1523 function wp_kses_check_attr_val( $value, $vless, $checkname, $checkvalue ) { |
1475 $ok = true; |
1524 $ok = true; |
1476 |
1525 |
1477 switch ( strtolower( $checkname ) ) { |
1526 switch ( strtolower( $checkname ) ) { |
1478 case 'maxlen': |
1527 case 'maxlen': |
1479 // The maxlen check makes sure that the attribute value has a length not |
1528 /* |
1480 // greater than the given value. This can be used to avoid Buffer Overflows |
1529 * The maxlen check makes sure that the attribute value has a length not |
1481 // in WWW clients and various Internet servers. |
1530 * greater than the given value. This can be used to avoid Buffer Overflows |
|
1531 * in WWW clients and various Internet servers. |
|
1532 */ |
1482 |
1533 |
1483 if ( strlen( $value ) > $checkvalue ) { |
1534 if ( strlen( $value ) > $checkvalue ) { |
1484 $ok = false; |
1535 $ok = false; |
1485 } |
1536 } |
1486 break; |
1537 break; |
1487 |
1538 |
1488 case 'minlen': |
1539 case 'minlen': |
1489 // The minlen check makes sure that the attribute value has a length not |
1540 /* |
1490 // smaller than the given value. |
1541 * The minlen check makes sure that the attribute value has a length not |
|
1542 * smaller than the given value. |
|
1543 */ |
1491 |
1544 |
1492 if ( strlen( $value ) < $checkvalue ) { |
1545 if ( strlen( $value ) < $checkvalue ) { |
1493 $ok = false; |
1546 $ok = false; |
1494 } |
1547 } |
1495 break; |
1548 break; |
1496 |
1549 |
1497 case 'maxval': |
1550 case 'maxval': |
1498 // The maxval check does two things: it checks that the attribute value is |
1551 /* |
1499 // an integer from 0 and up, without an excessive amount of zeroes or |
1552 * The maxval check does two things: it checks that the attribute value is |
1500 // whitespace (to avoid Buffer Overflows). It also checks that the attribute |
1553 * an integer from 0 and up, without an excessive amount of zeroes or |
1501 // value is not greater than the given value. |
1554 * whitespace (to avoid Buffer Overflows). It also checks that the attribute |
1502 // This check can be used to avoid Denial of Service attacks. |
1555 * value is not greater than the given value. |
|
1556 * This check can be used to avoid Denial of Service attacks. |
|
1557 */ |
1503 |
1558 |
1504 if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) { |
1559 if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) { |
1505 $ok = false; |
1560 $ok = false; |
1506 } |
1561 } |
1507 if ( $value > $checkvalue ) { |
1562 if ( $value > $checkvalue ) { |
1508 $ok = false; |
1563 $ok = false; |
1509 } |
1564 } |
1510 break; |
1565 break; |
1511 |
1566 |
1512 case 'minval': |
1567 case 'minval': |
1513 // The minval check makes sure that the attribute value is a positive integer, |
1568 /* |
1514 // and that it is not smaller than the given value. |
1569 * The minval check makes sure that the attribute value is a positive integer, |
|
1570 * and that it is not smaller than the given value. |
|
1571 */ |
1515 |
1572 |
1516 if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) { |
1573 if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) { |
1517 $ok = false; |
1574 $ok = false; |
1518 } |
1575 } |
1519 if ( $value < $checkvalue ) { |
1576 if ( $value < $checkvalue ) { |
1520 $ok = false; |
1577 $ok = false; |
1521 } |
1578 } |
1522 break; |
1579 break; |
1523 |
1580 |
1524 case 'valueless': |
1581 case 'valueless': |
1525 // The valueless check makes sure if the attribute has a value |
1582 /* |
1526 // (like `<a href="blah">`) or not (`<option selected>`). If the given value |
1583 * The valueless check makes sure if the attribute has a value |
1527 // is a "y" or a "Y", the attribute must not have a value. |
1584 * (like `<a href="blah">`) or not (`<option selected>`). If the given value |
1528 // If the given value is an "n" or an "N", the attribute must have a value. |
1585 * is a "y" or a "Y", the attribute must not have a value. |
|
1586 * If the given value is an "n" or an "N", the attribute must have a value. |
|
1587 */ |
1529 |
1588 |
1530 if ( strtolower( $checkvalue ) != $vless ) { |
1589 if ( strtolower( $checkvalue ) != $vless ) { |
1531 $ok = false; |
1590 $ok = false; |
1532 } |
1591 } |
1533 break; |
1592 break; |
1534 } // switch |
1593 } // End switch. |
1535 |
1594 |
1536 return $ok; |
1595 return $ok; |
1537 } |
1596 } |
1538 |
1597 |
1539 /** |
1598 /** |
1581 if ( ! isset( $options['slash_zero'] ) ) { |
1640 if ( ! isset( $options['slash_zero'] ) ) { |
1582 $options = array( 'slash_zero' => 'remove' ); |
1641 $options = array( 'slash_zero' => 'remove' ); |
1583 } |
1642 } |
1584 |
1643 |
1585 $string = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', $string ); |
1644 $string = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', $string ); |
1586 if ( 'remove' == $options['slash_zero'] ) { |
1645 if ( 'remove' === $options['slash_zero'] ) { |
1587 $string = preg_replace( '/\\\\+0+/', '', $string ); |
1646 $string = preg_replace( '/\\\\+0+/', '', $string ); |
1588 } |
1647 } |
1589 |
1648 |
1590 return $string; |
1649 return $string; |
1591 } |
1650 } |
1652 * |
1711 * |
1653 * @since 1.0.0 |
1712 * @since 1.0.0 |
1654 * |
1713 * |
1655 * @param string $string Content to check for bad protocols. |
1714 * @param string $string Content to check for bad protocols. |
1656 * @param string[] $allowed_protocols Array of allowed URL protocols. |
1715 * @param string[] $allowed_protocols Array of allowed URL protocols. |
|
1716 * @param int $count Depth of call recursion to this function. |
1657 * @return string Sanitized content. |
1717 * @return string Sanitized content. |
1658 */ |
1718 */ |
1659 function wp_kses_bad_protocol_once( $string, $allowed_protocols, $count = 1 ) { |
1719 function wp_kses_bad_protocol_once( $string, $allowed_protocols, $count = 1 ) { |
1660 $string = preg_replace( '/(�*58(?![;0-9])|�*3a(?![;a-f0-9]))/i', '$1;', $string ); |
1720 $string = preg_replace( '/(�*58(?![;0-9])|�*3a(?![;a-f0-9]))/i', '$1;', $string ); |
1661 $string2 = preg_split( '/:|�*58;|�*3a;/i', $string, 2 ); |
1721 $string2 = preg_split( '/:|�*58;|�*3a;|:/i', $string, 2 ); |
1662 if ( isset( $string2[1] ) && ! preg_match( '%/\?%', $string2[0] ) ) { |
1722 if ( isset( $string2[1] ) && ! preg_match( '%/\?%', $string2[0] ) ) { |
1663 $string = trim( $string2[1] ); |
1723 $string = trim( $string2[1] ); |
1664 $protocol = wp_kses_bad_protocol_once2( $string2[0], $allowed_protocols ); |
1724 $protocol = wp_kses_bad_protocol_once2( $string2[0], $allowed_protocols ); |
1665 if ( 'feed:' == $protocol ) { |
1725 if ( 'feed:' === $protocol ) { |
1666 if ( $count > 2 ) { |
1726 if ( $count > 2 ) { |
1667 return ''; |
1727 return ''; |
1668 } |
1728 } |
1669 $string = wp_kses_bad_protocol_once( $string, $allowed_protocols, ++$count ); |
1729 $string = wp_kses_bad_protocol_once( $string, $allowed_protocols, ++$count ); |
1670 if ( empty( $string ) ) { |
1730 if ( empty( $string ) ) { |
1679 |
1739 |
1680 /** |
1740 /** |
1681 * Callback for `wp_kses_bad_protocol_once()` regular expression. |
1741 * Callback for `wp_kses_bad_protocol_once()` regular expression. |
1682 * |
1742 * |
1683 * This function processes URL protocols, checks to see if they're in the |
1743 * This function processes URL protocols, checks to see if they're in the |
1684 * whitelist or not, and returns different data depending on the answer. |
1744 * list of allowed protocols or not, and returns different data depending |
|
1745 * on the answer. |
1685 * |
1746 * |
1686 * @access private |
1747 * @access private |
1687 * @ignore |
1748 * @ignore |
1688 * @since 1.0.0 |
1749 * @since 1.0.0 |
1689 * |
1750 * |
1690 * @param string $string URI scheme to check against the whitelist. |
1751 * @param string $string URI scheme to check against the list of allowed protocols. |
1691 * @param string[] $allowed_protocols Array of allowed URL protocols. |
1752 * @param string[] $allowed_protocols Array of allowed URL protocols. |
1692 * @return string Sanitized content. |
1753 * @return string Sanitized content. |
1693 */ |
1754 */ |
1694 function wp_kses_bad_protocol_once2( $string, $allowed_protocols ) { |
1755 function wp_kses_bad_protocol_once2( $string, $allowed_protocols ) { |
1695 $string2 = wp_kses_decode_entities( $string ); |
1756 $string2 = wp_kses_decode_entities( $string ); |
1716 * Converts and fixes HTML entities. |
1777 * Converts and fixes HTML entities. |
1717 * |
1778 * |
1718 * This function normalizes HTML entities. It will convert `AT&T` to the correct |
1779 * This function normalizes HTML entities. It will convert `AT&T` to the correct |
1719 * `AT&T`, `:` to `:`, `&#XYZZY;` to `&#XYZZY;` and so on. |
1780 * `AT&T`, `:` to `:`, `&#XYZZY;` to `&#XYZZY;` and so on. |
1720 * |
1781 * |
|
1782 * When `$context` is set to 'xml', HTML entities are converted to their code points. For |
|
1783 * example, `AT&T…&#XYZZY;` is converted to `AT&T…&#XYZZY;`. |
|
1784 * |
1721 * @since 1.0.0 |
1785 * @since 1.0.0 |
1722 * |
1786 * @since 5.5.0 Added `$context` parameter. |
1723 * @param string $string Content to normalize entities. |
1787 * |
|
1788 * @param string $string Content to normalize entities. |
|
1789 * @param string $context Context for normalization. Can be either 'html' or 'xml'. |
|
1790 * Default 'html'. |
1724 * @return string Content with normalized entities. |
1791 * @return string Content with normalized entities. |
1725 */ |
1792 */ |
1726 function wp_kses_normalize_entities( $string ) { |
1793 function wp_kses_normalize_entities( $string, $context = 'html' ) { |
1727 // Disarm all entities by converting & to & |
1794 // Disarm all entities by converting & to & |
1728 $string = str_replace( '&', '&', $string ); |
1795 $string = str_replace( '&', '&', $string ); |
1729 |
1796 |
1730 // Change back the allowed entities in our entity whitelist |
1797 // Change back the allowed entities in our list of allowed entities. |
1731 $string = preg_replace_callback( '/&([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_named_entities', $string ); |
1798 if ( 'xml' === $context ) { |
|
1799 $string = preg_replace_callback( '/&([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_xml_named_entities', $string ); |
|
1800 } else { |
|
1801 $string = preg_replace_callback( '/&([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_named_entities', $string ); |
|
1802 } |
1732 $string = preg_replace_callback( '/&#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string ); |
1803 $string = preg_replace_callback( '/&#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string ); |
1733 $string = preg_replace_callback( '/&#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $string ); |
1804 $string = preg_replace_callback( '/&#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $string ); |
1734 |
1805 |
1735 return $string; |
1806 return $string; |
1736 } |
1807 } |
1754 if ( empty( $matches[1] ) ) { |
1825 if ( empty( $matches[1] ) ) { |
1755 return ''; |
1826 return ''; |
1756 } |
1827 } |
1757 |
1828 |
1758 $i = $matches[1]; |
1829 $i = $matches[1]; |
1759 return ( ! in_array( $i, $allowedentitynames ) ) ? "&$i;" : "&$i;"; |
1830 return ( ! in_array( $i, $allowedentitynames, true ) ) ? "&$i;" : "&$i;"; |
|
1831 } |
|
1832 |
|
1833 /** |
|
1834 * Callback for `wp_kses_normalize_entities()` regular expression. |
|
1835 * |
|
1836 * This function only accepts valid named entity references, which are finite, |
|
1837 * case-sensitive, and highly scrutinized by XML validators. HTML named entity |
|
1838 * references are converted to their code points. |
|
1839 * |
|
1840 * @since 5.5.0 |
|
1841 * |
|
1842 * @global array $allowedentitynames |
|
1843 * @global array $allowedxmlnamedentities |
|
1844 * |
|
1845 * @param array $matches preg_replace_callback() matches array. |
|
1846 * @return string Correctly encoded entity. |
|
1847 */ |
|
1848 function wp_kses_xml_named_entities( $matches ) { |
|
1849 global $allowedentitynames, $allowedxmlnamedentities; |
|
1850 |
|
1851 if ( empty( $matches[1] ) ) { |
|
1852 return ''; |
|
1853 } |
|
1854 |
|
1855 $i = $matches[1]; |
|
1856 |
|
1857 if ( in_array( $i, $allowedxmlnamedentities, true ) ) { |
|
1858 return "&$i;"; |
|
1859 } elseif ( in_array( $i, $allowedentitynames, true ) ) { |
|
1860 return html_entity_decode( "&$i;", ENT_HTML5 ); |
|
1861 } |
|
1862 |
|
1863 return "&$i;"; |
1760 } |
1864 } |
1761 |
1865 |
1762 /** |
1866 /** |
1763 * Callback for `wp_kses_normalize_entities()` regular expression. |
1867 * Callback for `wp_kses_normalize_entities()` regular expression. |
1764 * |
1868 * |
1817 * |
1921 * |
1818 * @param int $i Unicode codepoint. |
1922 * @param int $i Unicode codepoint. |
1819 * @return bool Whether or not the codepoint is a valid Unicode codepoint. |
1923 * @return bool Whether or not the codepoint is a valid Unicode codepoint. |
1820 */ |
1924 */ |
1821 function valid_unicode( $i ) { |
1925 function valid_unicode( $i ) { |
1822 return ( $i == 0x9 || $i == 0xa || $i == 0xd || |
1926 return ( 0x9 == $i || 0xa == $i || 0xd == $i || |
1823 ( $i >= 0x20 && $i <= 0xd7ff ) || |
1927 ( 0x20 <= $i && $i <= 0xd7ff ) || |
1824 ( $i >= 0xe000 && $i <= 0xfffd ) || |
1928 ( 0xe000 <= $i && $i <= 0xfffd ) || |
1825 ( $i >= 0x10000 && $i <= 0x10ffff ) ); |
1929 ( 0x10000 <= $i && $i <= 0x10ffff ) ); |
1826 } |
1930 } |
1827 |
1931 |
1828 /** |
1932 /** |
1829 * Converts all numeric HTML entities to their named counterparts. |
1933 * Converts all numeric HTML entities to their named counterparts. |
1830 * |
1934 * |
1831 * This function decodes numeric HTML entities (`A` and `A`). |
1935 * This function decodes numeric HTML entities (`A` and `A`). |
1832 * It doesn't do anything with named entities like `ä`, but we don't |
1936 * It doesn't do anything with named entities like `ä`, but we don't |
1833 * need them in the URL protocol whitelisting system anyway. |
1937 * need them in the allowed URL protocols system anyway. |
1834 * |
1938 * |
1835 * @since 1.0.0 |
1939 * @since 1.0.0 |
1836 * |
1940 * |
1837 * @param string $string Content to change entities. |
1941 * @param string $string Content to change entities. |
1838 * @return string Content after decoded entities. |
1942 * @return string Content after decoded entities. |
1973 * 'excerpt_save_pre', and 'content_filtered_save_pre' hooks. |
2077 * 'excerpt_save_pre', and 'content_filtered_save_pre' hooks. |
1974 * |
2078 * |
1975 * @since 2.0.0 |
2079 * @since 2.0.0 |
1976 */ |
2080 */ |
1977 function kses_init_filters() { |
2081 function kses_init_filters() { |
1978 // Normal filtering |
2082 // Normal filtering. |
1979 add_filter( 'title_save_pre', 'wp_filter_kses' ); |
2083 add_filter( 'title_save_pre', 'wp_filter_kses' ); |
1980 |
2084 |
1981 // Comment filtering |
2085 // Comment filtering. |
1982 if ( current_user_can( 'unfiltered_html' ) ) { |
2086 if ( current_user_can( 'unfiltered_html' ) ) { |
1983 add_filter( 'pre_comment_content', 'wp_filter_post_kses' ); |
2087 add_filter( 'pre_comment_content', 'wp_filter_post_kses' ); |
1984 } else { |
2088 } else { |
1985 add_filter( 'pre_comment_content', 'wp_filter_kses' ); |
2089 add_filter( 'pre_comment_content', 'wp_filter_kses' ); |
1986 } |
2090 } |
1987 |
2091 |
1988 // Post filtering |
2092 // Post filtering. |
1989 add_filter( 'content_save_pre', 'wp_filter_post_kses' ); |
2093 add_filter( 'content_save_pre', 'wp_filter_post_kses' ); |
1990 add_filter( 'excerpt_save_pre', 'wp_filter_post_kses' ); |
2094 add_filter( 'excerpt_save_pre', 'wp_filter_post_kses' ); |
1991 add_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' ); |
2095 add_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' ); |
1992 } |
2096 } |
1993 |
2097 |
2002 * hook (priority is also default). |
2106 * hook (priority is also default). |
2003 * |
2107 * |
2004 * @since 2.0.6 |
2108 * @since 2.0.6 |
2005 */ |
2109 */ |
2006 function kses_remove_filters() { |
2110 function kses_remove_filters() { |
2007 // Normal filtering |
2111 // Normal filtering. |
2008 remove_filter( 'title_save_pre', 'wp_filter_kses' ); |
2112 remove_filter( 'title_save_pre', 'wp_filter_kses' ); |
2009 |
2113 |
2010 // Comment filtering |
2114 // Comment filtering. |
2011 remove_filter( 'pre_comment_content', 'wp_filter_post_kses' ); |
2115 remove_filter( 'pre_comment_content', 'wp_filter_post_kses' ); |
2012 remove_filter( 'pre_comment_content', 'wp_filter_kses' ); |
2116 remove_filter( 'pre_comment_content', 'wp_filter_kses' ); |
2013 |
2117 |
2014 // Post filtering |
2118 // Post filtering. |
2015 remove_filter( 'content_save_pre', 'wp_filter_post_kses' ); |
2119 remove_filter( 'content_save_pre', 'wp_filter_post_kses' ); |
2016 remove_filter( 'excerpt_save_pre', 'wp_filter_post_kses' ); |
2120 remove_filter( 'excerpt_save_pre', 'wp_filter_post_kses' ); |
2017 remove_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' ); |
2121 remove_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' ); |
2018 } |
2122 } |
2019 |
2123 |
2043 * @param string $deprecated Not used. |
2147 * @param string $deprecated Not used. |
2044 * @return string Filtered string of CSS rules. |
2148 * @return string Filtered string of CSS rules. |
2045 */ |
2149 */ |
2046 function safecss_filter_attr( $css, $deprecated = '' ) { |
2150 function safecss_filter_attr( $css, $deprecated = '' ) { |
2047 if ( ! empty( $deprecated ) ) { |
2151 if ( ! empty( $deprecated ) ) { |
2048 _deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented |
2152 _deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented. |
2049 } |
2153 } |
2050 |
2154 |
2051 $css = wp_kses_no_null( $css ); |
2155 $css = wp_kses_no_null( $css ); |
2052 $css = str_replace( array( "\n", "\r", "\t" ), '', $css ); |
2156 $css = str_replace( array( "\n", "\r", "\t" ), '', $css ); |
2053 |
2157 |
2061 * @since 2.8.1 |
2165 * @since 2.8.1 |
2062 * @since 4.4.0 Added support for `min-height`, `max-height`, `min-width`, and `max-width`. |
2166 * @since 4.4.0 Added support for `min-height`, `max-height`, `min-width`, and `max-width`. |
2063 * @since 4.6.0 Added support for `list-style-type`. |
2167 * @since 4.6.0 Added support for `list-style-type`. |
2064 * @since 5.0.0 Added support for `background-image`. |
2168 * @since 5.0.0 Added support for `background-image`. |
2065 * @since 5.1.0 Added support for `text-transform`. |
2169 * @since 5.1.0 Added support for `text-transform`. |
2066 * @since 5.2.0 Added support for `background-position` and `grid-template-columns` |
2170 * @since 5.2.0 Added support for `background-position` and `grid-template-columns`. |
|
2171 * @since 5.3.0 Added support for `grid`, `flex` and `column` layout properties. |
|
2172 * Extend `background-*` support of individual properties. |
|
2173 * @since 5.3.1 Added support for gradient backgrounds. |
2067 * |
2174 * |
2068 * @param string[] $attr Array of allowed CSS attributes. |
2175 * @param string[] $attr Array of allowed CSS attributes. |
2069 */ |
2176 */ |
2070 $allowed_attr = apply_filters( |
2177 $allowed_attr = apply_filters( |
2071 'safe_style_css', |
2178 'safe_style_css', |
2072 array( |
2179 array( |
2073 'background', |
2180 'background', |
2074 'background-color', |
2181 'background-color', |
2075 'background-image', |
2182 'background-image', |
2076 'background-position', |
2183 'background-position', |
|
2184 'background-size', |
|
2185 'background-attachment', |
|
2186 'background-blend-mode', |
2077 |
2187 |
2078 'border', |
2188 'border', |
|
2189 'border-radius', |
2079 'border-width', |
2190 'border-width', |
2080 'border-color', |
2191 'border-color', |
2081 'border-style', |
2192 'border-style', |
2082 'border-right', |
2193 'border-right', |
2083 'border-right-color', |
2194 'border-right-color', |
2161 |
2305 |
2162 'list-style', |
2306 'list-style', |
2163 'list-style-image', |
2307 'list-style-image', |
2164 ); |
2308 ); |
2165 |
2309 |
|
2310 /* |
|
2311 * CSS attributes that accept gradient data types. |
|
2312 * |
|
2313 */ |
|
2314 $css_gradient_data_types = array( |
|
2315 'background', |
|
2316 'background-image', |
|
2317 ); |
|
2318 |
2166 if ( empty( $allowed_attr ) ) { |
2319 if ( empty( $allowed_attr ) ) { |
2167 return $css; |
2320 return $css; |
2168 } |
2321 } |
2169 |
2322 |
2170 $css = ''; |
2323 $css = ''; |
2171 foreach ( $css_array as $css_item ) { |
2324 foreach ( $css_array as $css_item ) { |
2172 if ( $css_item == '' ) { |
2325 if ( '' === $css_item ) { |
2173 continue; |
2326 continue; |
2174 } |
2327 } |
2175 |
2328 |
2176 $css_item = trim( $css_item ); |
2329 $css_item = trim( $css_item ); |
2177 $css_test_string = $css_item; |
2330 $css_test_string = $css_item; |
2178 $found = false; |
2331 $found = false; |
2179 $url_attr = false; |
2332 $url_attr = false; |
|
2333 $gradient_attr = false; |
2180 |
2334 |
2181 if ( strpos( $css_item, ':' ) === false ) { |
2335 if ( strpos( $css_item, ':' ) === false ) { |
2182 $found = true; |
2336 $found = true; |
2183 } else { |
2337 } else { |
2184 $parts = explode( ':', $css_item, 2 ); |
2338 $parts = explode( ':', $css_item, 2 ); |
2185 $css_selector = trim( $parts[0] ); |
2339 $css_selector = trim( $parts[0] ); |
2186 |
2340 |
2187 if ( in_array( $css_selector, $allowed_attr, true ) ) { |
2341 if ( in_array( $css_selector, $allowed_attr, true ) ) { |
2188 $found = true; |
2342 $found = true; |
2189 $url_attr = in_array( $css_selector, $css_url_data_types, true ); |
2343 $url_attr = in_array( $css_selector, $css_url_data_types, true ); |
|
2344 $gradient_attr = in_array( $css_selector, $css_gradient_data_types, true ); |
2190 } |
2345 } |
2191 } |
2346 } |
2192 |
2347 |
2193 if ( $found && $url_attr ) { |
2348 if ( $found && $url_attr ) { |
2194 // Simplified: matches the sequence `url(*)`. |
2349 // Simplified: matches the sequence `url(*)`. |
2203 break; |
2358 break; |
2204 } |
2359 } |
2205 |
2360 |
2206 $url = trim( $url_pieces[2] ); |
2361 $url = trim( $url_pieces[2] ); |
2207 |
2362 |
2208 if ( empty( $url ) || $url !== wp_kses_bad_protocol( $url, $allowed_protocols ) ) { |
2363 if ( empty( $url ) || wp_kses_bad_protocol( $url, $allowed_protocols ) !== $url ) { |
2209 $found = false; |
2364 $found = false; |
2210 break; |
2365 break; |
2211 } else { |
2366 } else { |
2212 // Remove the whole `url(*)` bit that was matched above from the CSS. |
2367 // Remove the whole `url(*)` bit that was matched above from the CSS. |
2213 $css_test_string = str_replace( $url_match, '', $css_test_string ); |
2368 $css_test_string = str_replace( $url_match, '', $css_test_string ); |
2214 } |
2369 } |
2215 } |
2370 } |
2216 } |
2371 } |
2217 |
2372 |
2218 // Remove any CSS containing containing \ ( & } = or comments, except for url() useage checked above. |
2373 if ( $found && $gradient_attr ) { |
2219 if ( $found && ! preg_match( '%[\\\(&=}]|/\*%', $css_test_string ) ) { |
2374 $css_value = trim( $parts[1] ); |
2220 if ( $css != '' ) { |
2375 if ( preg_match( '/^(repeating-)?(linear|radial|conic)-gradient\(([^()]|rgb[a]?\([^()]*\))*\)$/', $css_value ) ) { |
2221 $css .= ';'; |
2376 // Remove the whole `gradient` bit that was matched above from the CSS. |
|
2377 $css_test_string = str_replace( $css_value, '', $css_test_string ); |
2222 } |
2378 } |
2223 |
|
2224 $css .= $css_item; |
|
2225 } |
2379 } |
|
2380 |
|
2381 if ( $found ) { |
|
2382 // Check for any CSS containing \ ( & } = or comments, except for url() usage checked above. |
|
2383 $allow_css = ! preg_match( '%[\\\(&=}]|/\*%', $css_test_string ); |
|
2384 |
|
2385 /** |
|
2386 * Filters the check for unsafe CSS in `safecss_filter_attr`. |
|
2387 * |
|
2388 * Enables developers to determine whether a section of CSS should be allowed or discarded. |
|
2389 * By default, the value will be false if the part contains \ ( & } = or comments. |
|
2390 * Return true to allow the CSS part to be included in the output. |
|
2391 * |
|
2392 * @since 5.5.0 |
|
2393 * |
|
2394 * @param bool $allow_css Whether the CSS in the test string is considered safe. |
|
2395 * @param string $css_test_string The CSS string to test. |
|
2396 */ |
|
2397 $allow_css = apply_filters( 'safecss_filter_attr_allow_css', $allow_css, $css_test_string ); |
|
2398 |
|
2399 // Only add the CSS part if it passes the regex check. |
|
2400 if ( $allow_css ) { |
|
2401 if ( '' !== $css ) { |
|
2402 $css .= ';'; |
|
2403 } |
|
2404 |
|
2405 $css .= $css_item; |
|
2406 } |
|
2407 } |
2226 } |
2408 } |
2227 |
2409 |
2228 return $css; |
2410 return $css; |
2229 } |
2411 } |
2230 |
2412 |
2231 /** |
2413 /** |
2232 * Helper function to add global attributes to a tag in the allowed html list. |
2414 * Helper function to add global attributes to a tag in the allowed HTML list. |
2233 * |
2415 * |
2234 * @since 3.5.0 |
2416 * @since 3.5.0 |
2235 * @since 5.0.0 Add support for `data-*` wildcard attributes. |
2417 * @since 5.0.0 Add support for `data-*` wildcard attributes. |
2236 * @access private |
2418 * @access private |
2237 * @ignore |
2419 * @ignore |