Headline
CVE-2023-5230: wishlist.php in tm-woocommerce-compare-wishlist/tags/1.1.7/includes/wishlist – WordPress Plugin Repository
The TM WooCommerce Compare & Wishlist plugin for WordPress is vulnerable to Stored Cross-Site Scripting via ‘tm_woo_wishlist_table’ shortcode in versions up to, and including, 1.1.7 due to insufficient input sanitization and output escaping on user supplied attributes. This makes it possible for authenticated attackers with contributor-level and above permissions to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
1<?php23if ( is_admin() ) {4 require_once TM_WC_COMPARE_WISHLIST_PATH . 'includes/wishlist/settings.php’;5}67if ( ‘yes’ !== get_option( ‘tm_woowishlist_enable’ ) ) {89 return;10}11require_once 'buttons.php’;1213if ( ! is_admin() ) {1415 require_once 'shortcode.php’;16}17require_once 'widget.php’;1819// register action hooks20add_action( 'wp_enqueue_scripts’, ‘tm_woowishlist_setup_plugin’ );2122add_action( 'wp_ajax_tm_woowishlist_add’, ‘tm_woowishlist_process_button_action’ );23add_action( 'wp_ajax_nopriv_tm_woowishlist_add’, ‘tm_woowishlist_process_button_action’ );2425add_action( 'wp_ajax_tm_woowishlist_remove’, ‘tm_woowishlist_process_remove_button_action’ );26add_action( 'wp_ajax_nopriv_tm_woowishlist_remove’, ‘tm_woowishlist_process_remove_button_action’ );2728add_action( 'wp_ajax_tm_woowishlist_update’, ‘tm_woowishlist_process_ajax’ );29add_action( 'wp_ajax_nopriv_tm_woowishlist_update’, ‘tm_woowishlist_process_ajax’ );3031add_action( 'init’, ‘tm_woowislist_session_to_db’ );3233/**34 * Enqueue scripts and styles.35 *36 * @since 1.0.037 *38 * @action wp_enqueue_scripts39 */40function tm_woowishlist_setup_plugin() {4142 wp_enqueue_style( ‘tm-woowishlist’ );43 wp_enqueue_script( ‘tm-woowishlist’ );4445 $include_bootstrap_grid = apply_filters( 'tm_woocommerce_include_bootstrap_grid’, true );4647 if ( $include_bootstrap_grid ) {4849 wp_enqueue_style( ‘bootstrap-grid’ );50 }51}5253/**54 * Returns wishlist list.55 *56 * @sicne 1.0.057 *58 * @return array The array of product ids to wishlist.59 */60function tm_woowishlist_get_list() {6162 if( is_user_logged_in() ) {6364 $id = get_current_user_id();65 $list = get_user_meta( $id, 'tm_woo_wishlist_items’, true );6667 if ( ! empty( $list ) ) {6869 $list = unserialize( $list );7071 } else {7273 $list = array();74 }75 } else {7677 $list = ! empty( $_SESSION[‘tm-woowishlist’] ) ? $_SESSION[‘tm-woowishlist’] : array();7879 if ( ! empty( $list ) ) {8081 $list = explode( ':’, $list );82 $nonce = array_pop( $list );8384 if ( ! wp_verify_nonce( $nonce, implode( $list ) ) ) {8586 $list = array();87 }88 }89 }90 return $list;91}9293/**94 * Sets new list of products to wishlist.95 *96 * @since 1.0.097 *98 * @param array $list The new array of products to wishlist.99 */100function tm_woowishlist_set_list( $list ) {101102 $nonce = wp_create_nonce( implode( $list ) );103 $value = implode( ':’, array_merge( $list, array( $nonce ) ) );104 if ( ! session_id() ) {105106 session_start();107 }108 $_SESSION[‘tm-woowishlist’] = $value;109}110111/**112 * Returns wishlist page link.113 *114 * @since 1.0.0115 *116 * @return string The wishlist page link on success, otherwise FALSE.117 */118function tm_woowishlist_get_page_link() {119120 $page_id = intval( get_option( 'tm_woowishlist_page’, ‘’ ) );121122 if ( ! $page_id ) {123124 return false;125 }126 $page_link = get_permalink( $page_id );127128 if ( ! $page_link ) {129130 return false;131 }132 return trailingslashit( $page_link );133}134135/**136 * Processes buttons actions.137 *138 * @since 1.0.0139 *140 * @action wp_ajax_tm_woowishlist_add_to_list141 */142function tm_woowishlist_process_button_action() {143144 $id = filter_input( INPUT_POST, ‘pid’ );145146 if ( ! wp_verify_nonce( filter_input( INPUT_POST, ‘nonce’ ), ‘tm_woowishlist’ . $id ) ) {147148 wp_send_json_error();149 }150 $button = json_decode( filter_input( INPUT_POST, ‘single’ ) ) ? tm_woowishlist_page_button() : false;151152 tm_woowishlist_add( $id );153154 wp_send_json_success( array(155 ‘action’ => $action,156 ‘wishlistPageBtn’ => $button157 ) );158}159160/**161 * Returns message when is no products in wishlist.162 *163 * @since 1.0.0164 *165 * @return string The message166 */167function tm_woowishlist_empty_message() {168169 $empty_text = get_option( 'tm_woowishlist_empty_text’, __( 'No products added to wishlist.’, ‘tm-wc-compare-wishlist’ ) );170171 return apply_filters( 'tm_woowishlist_empty_message’, sprintf( '<p class="tm-woowishlist-empty">%s</p>’, $empty_text ), $empty_text );172}173174/**175 * Processes main ajax handler.176 *177 * @since 1.0.0178 *179 * @action wp_ajax_tm_woowishlist_update180 */181function tm_woowishlist_process_ajax() {182183 $is_page = json_decode( filter_input( INPUT_POST, ‘isWishlistPage’ ) );184 $is_widget = json_decode( filter_input( INPUT_POST, ‘isWidget’ ) );185 $json = array();186 $atts = json_decode( filter_input( INPUT_POST, ‘wishListData’ ), true );187188 if ( $is_page ) {189190 $json[‘wishList’] = tm_woowishlist_render_table( $atts );191 }192 if ( $is_widget ) {193194 $json[‘widget’] = tm_woowishlist_render_widget();195 }196 wp_send_json_success( $json );197}198199/**200 * Processes remove button action.201 *202 * @since 1.0.0203 *204 * @action wp_ajax_tm_woowishlist_remove205 */206function tm_woowishlist_process_remove_button_action() {207208 $id = filter_input( INPUT_POST, ‘pid’ );209210 if ( ! wp_verify_nonce( filter_input( INPUT_POST, ‘nonce’ ), ‘tm_woowishlist’ . $id ) ) {211212 wp_send_json_error();213 }214 tm_woowishlist_remove( $id );215216 tm_woowishlist_process_ajax();217}218219/**220 * Adds product to wishlist.221 *222 * @since 1.0.0223 *224 * @param int $id The product id to add to the wishlist.225 */226function tm_woowishlist_add( $id ) {227228 $id = intval( $id );229230 if( is_user_logged_in() ) {231232 $user_id = get_current_user_id();233 $list = get_user_meta( $user_id, 'tm_woo_wishlist_items’, true );234235 if ( ! empty( $list ) ) {236237 $list = unserialize( $list );238239 } else {240241 $list = array();242 }243 $list[] = $id;244 $list = array_unique( $list );245 $list = serialize( $list );246247 update_user_meta( $user_id, 'tm_woo_wishlist_items’, $list );248249 } else {250251 $list = tm_woowishlist_get_list();252 $list[] = $id;253 $list = array_unique( $list );254255 tm_woowishlist_set_list( $list );256 }257}258259/**260 * Removes product from wishlist list.261 *262 * @since 1.0.0263 *264 * @param int $id The product id to remove from wishlist.265 */266function tm_woowishlist_remove( $id ) {267268 $id = intval( $id );269270 if( is_user_logged_in() ) {271272 $user_id = get_current_user_id();273 $list = get_user_meta( $user_id, 'tm_woo_wishlist_items’, true );274275 if ( ! empty( $list ) ) {276277 $list = unserialize( $list );278 $key = array_search( $id, $list );279280 if ( false !== $key ) {281282 unset( $list[$key] );283 }284 $list = serialize( $list );285286 update_user_meta( $user_id, 'tm_woo_wishlist_items’, $list );287288 }289290 } else {291292 $list = tm_woowishlist_get_list();293 $key = array_search( $id, $list );294295 if ( false !== $key ) {296297 unset( $list[$key] );298 }299 tm_woowishlist_set_list( $list );300 }301}302303/**304 * Get products added to wishlist.305 *306 * @since 1.0.0307 *308 * @param array $list The array of products ids.309 * @return object The list of products310 */311function tm_woowishlist_get_products( $list ) {312313 $args = array(314 ‘post_type’ => 'product’,315 ‘post__in’ => $list,316 ‘orderby’ => 'post__in’,317 ‘posts_per_page’ => -1318 );319 $products = new WP_Query( $args );320321 wp_reset_query();322323 return $products;324}325326/**327 * Renders wishlist.328 *329 * @since 1.0.0330 *331 * @return string Wishlist HTML.332 */333function tm_woowishlist_render( $atts = array() ) {334335 $content = array();336 $class = isset( $atts[‘class’] ) && ! empty( $atts[‘class’] ) ? $atts[‘class’] : '’;337 $tm_wc_compare_wishlist = tm_wc_compare_wishlist();338 $data_atts = $tm_wc_compare_wishlist->build_html_dataattributes( $atts );339 $content[] = '<div class="woocommerce tm-woowishlist ' . $class . ‘"’ . $data_atts . '>’;340 $content[] = '<div class="woocommerce tm-woowishlist-wrapper">’;341 $content[] = tm_woowishlist_render_table( $atts );342 $content[] = '</div>’;343 $content[] = $tm_wc_compare_wishlist->get_loader();344 $content[] = ‘</div>’;345346 return implode( “\n", $content );347}348349/**350 * Renders wishlist widget.351 *352 * @since 1.0.0353 *354 * @return string Wishlist widget HTML.355 */356function tm_woowishlist_render_widget() {357358 $list = tm_woowishlist_get_list();359360 if ( empty( $list ) ) {361362 return tm_woowishlist_empty_message();363 }364 $templater = tm_wc_compare_wishlist_templater();365 $products = tm_woowishlist_get_products( $list );366 $template = get_option( 'tm_woowishlist_widget_template’, ‘widget.tmpl’ );367 $template = $templater->get_template_by_name( $template, ‘tm-woowishlist’ );368369 if( ! $template ) {370371 $template = $templater->get_template_by_name( ‘widget.tmpl’, ‘tm-woowishlist’ );372 }373 $content = array();374 $tm_wc_compare_wishlist = tm_wc_compare_wishlist();375376 if ( $products->have_posts() ) {377378 $content[] = ‘<div class="tm-woowishlist-widget-products">’ . “\n";379380 while ( $products->have_posts() ) {381382 $products->the_post();383384 global $product;385386 if ( empty( $product ) ) {387388 continue;389 }390 $pid = method_exists( $product, ‘get_id’ ) ? $product->get_id() : get_the_id();391 $pid = $tm_wc_compare_wishlist->get_original_product_id( $pid );392 $nonce = wp_create_nonce( ‘tm_woowishlist’ . $pid );393 $dismiss_icon = apply_filters( ‘tm_woowishlist_dismiss_icon’, ‘<span class="dashicons dashicons-dismiss"></span>’ );394 $content[] = ‘<div class="tm-woowishlist-widget-product">’ . “\n";395 $content[] = '<span class="tm-woowishlist-remove” data-id="’ . $pid . '” data-nonce="’ . $nonce . ‘">’ . $dismiss_icon . '</span>’;396 $content[] = $templater->parse_template( $template );397 $content[] = '</div>’;398 }399 wp_reset_query();400401 $content[] = ‘</div>’;402 $content[] = tm_woowishlist_page_button( array( ‘btn-default’ ) );403 $content[] = $tm_wc_compare_wishlist->get_loader();404 }405 return implode( “\n", $content );406}407408/**409 * Renders wishlist table.410 *411 * @since 1.0.0412 *413 * @param array $atts The wishlist table attributes.414 * @return string Wishlist table HTML.415 */416function tm_woowishlist_render_table( $atts = array() ) {417418 $list = tm_woowishlist_get_list();419420 if ( empty( $list ) ) {421422 return tm_woowishlist_empty_message();423 }424 $html = array();425 $templater = tm_wc_compare_wishlist_templater();426 $products = tm_woowishlist_get_products( $list );427 $template = isset( $atts[‘template’] ) && ! empty( $atts[‘template’] ) ? $atts[‘template’] : get_option( 'tm_woowishlist_page_template’, ‘page.tmpl’ );428 $cols = isset( $atts[‘cols’] ) && ! empty( $atts[‘cols’] ) ? $atts[‘cols’] : get_option( 'tm_woowishlist_cols’, ‘1’ );429 $cols = 4 < $cols ? 4 : $cols;430 $template = $templater->get_template_by_name( $template, ‘tm-woowishlist’ );431432 if( ! $template ) {433434 $template = $templater->get_template_by_name( 'page.tmpl’, ‘tm-woowishlist’ );435 }436 $html[] = '<div class="row">’;437 $class = apply_filters( 'tm_woowishlist_column_class’, 'col-lg-' . round( 12 / $cols ) . ' col-xs-12’, $cols );438439 while ( $products->have_posts() ) {440441 $products->the_post();442443 global $product;444445 if ( empty( $product ) ) {446447 continue;448 }449 $pid = method_exists( $product, ‘get_id’ ) ? $product->get_id() : get_the_id();450 $pid = tm_wc_compare_wishlist()->get_original_product_id( $pid );451 $html[] = ‘<div class="’ . $class . '">’;452 $html[] = '<div class="tm-woowishlist-item">’;453 $nonce = wp_create_nonce( ‘tm_woowishlist’ . $pid );454 $dismiss_icon = apply_filters( 'tm_woowishlist_dismiss_icon’, ‘<span class="dashicons dashicons-dismiss"></span>’ );455 $html[] = '<div class="tm-woowishlist-remove” data-id="’ . $pid . '” data-nonce="’ . $nonce . ‘">’ . $dismiss_icon . '</div>’;456 $html[] = $templater->parse_template( $template, $atts );457 $html[] = '</div>’;458 $html[] = '</div>’;459 }460 wp_reset_query();461462 $html[] = '</div>’;463464 return implode( "\n", $html );465}466467function tm_woowislist_session_to_db() {468469 if ( is_user_logged_in() ) {470471 $list = tm_woowishlist_get_list();472 $session_list = ! empty( $_SESSION[‘tm-woowishlist’] ) ? $_SESSION[‘tm-woowishlist’] : array();473474 if ( ! empty( $session_list ) ) {475476 $session_list = explode( ':’, $session_list );477 $nonce = array_pop( $session_list );478 }479 if ( ! empty( $session_list ) ) {480481 foreach ( $session_list as $product_id ) {482483 if( ! in_array( $product_id, $list ) ) {484485 tm_woowishlist_add( $product_id );486 }487 }488 tm_woowishlist_set_list( array() );489 }490 }491}