PluginProbe
Welcart e-Commerce / 2.12.4
Welcart e-Commerce v2.12.4
2.12.4 2.12.3 2.11.35 2.12.2 2.12.1 2.11.34 2.11.33 2.11.32 2.11.31 2.11.30 1.3.16 1.3.17 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.10 1.4.11 1.4.12 All 292 releases
← All changes | functions/utility.php +122 -24 2.12.12.12.4 View file →
@@ -70,9 +70,9 @@
70 70 $order_id = $wpdb->get_var( $query );
71 71 if ( $order_id == $order->ID ) {
72 72 continue;
73 73 }
74 - $condition = maybe_unserialize( $order->order_condition );
74 + $condition = wel_safe_maybe_unserialize( $order->order_condition );
75 75 if ( ! isset( $condition['tax_mode'] ) ) {
76 76 $condition['tax_mode'] = $options['tax_mode'];
77 77 }
78 78 if ( ! isset( $condition['tax_rate'] ) ) {
@@ -81,9 +81,9 @@
81 81 if ( ! isset( $condition['tax_target'] ) ) {
82 82 $condition['tax_target'] = $options['tax_target'];
83 83 }
84 84
85 - $cart = unserialize( $order->order_cart );
85 + $cart = wel_safe_unserialize( $order->order_cart );
86 86 foreach ( (array) $cart as $row_index => $value ) {
87 87 $product = wel_get_product( $value['post_id'] );
88 88 $item_code = $product['itemCode'];
89 89 $item_name = $product['itemName'];
@@ -135,9 +135,9 @@
135 135 }
136 136 }
137 137 if ( $value['advance'] ) {
138 138 foreach ( (array) $value['advance'] as $akey => $avalue ) {
139 - $advance = maybe_unserialize( $avalue );
139 + $advance = wel_safe_maybe_unserialize( $avalue );
140 140 if ( is_array( $advance ) ) {
141 141 $post_id = $value['post_id'];
142 142 if ( is_array( $advance[ $post_id ][ $sku_encoded ] ) ) {
143 143 $akeys = array_keys( $advance[ $post_id ][ $sku_encoded ] );
@@ -232,9 +232,9 @@
232 232 $query = "SELECT ID, order_cart, order_condition FROM $order_table";
233 233 $results = $wpdb->get_results( $query );
234 234 if ( $results ) {
235 235 foreach ( $results as $order ) {
236 - $condition = maybe_unserialize( $order->order_condition );
236 + $condition = wel_safe_maybe_unserialize( $order->order_condition );
237 237 if ( ! isset( $condition['tax_mode'] ) ) {
238 238 $condition['tax_mode'] = $options['tax_mode'];
239 239 }
240 240 if ( ! isset( $condition['tax_rate'] ) ) {
@@ -243,9 +243,9 @@
243 243 if ( ! isset( $condition['tax_target'] ) ) {
244 244 $condition['tax_target'] = $options['tax_target'];
245 245 }
246 246
247 - $cart = unserialize( $order->order_cart );
247 + $cart = wel_safe_unserialize( $order->order_cart );
248 248 foreach ( (array) $cart as $row_index => $value ) {
249 249 $product = wel_get_product( $value['post_id'] );
250 250 $item_code = $product['itemCode'];
251 251 $item_name = $product['itemName'];
@@ -297,9 +297,9 @@
297 297 }
298 298 }
299 299 if ( $value['advance'] ) {
300 300 foreach ( (array) $value['advance'] as $akey => $avalue ) {
301 - $advance = maybe_unserialize( $avalue );
301 + $advance = wel_safe_maybe_unserialize( $avalue );
302 302 if ( is_array( $advance ) ) {
303 303 $post_id = $value['post_id'];
304 304 if ( is_array( $advance[ $post_id ][ $sku_encoded ] ) ) {
305 305 $akeys = array_keys( $advance[ $post_id ][ $sku_encoded ] );
@@ -479,9 +479,9 @@
479 479 $sort_id = 0;
480 480 $check_code = array();
481 481 $dep_num = array();
482 482 foreach ( (array) $res as $metarow ) {
483 - $meta_value = unserialize( $metarow['meta_value'] );
483 + $meta_value = wel_safe_unserialize( $metarow['meta_value'] );
484 484 $newvalue = array();
485 485 $newvalue['code'] = substr( $metarow['meta_key'], 6 );
486 486 if ( $pre_post_id == $metarow['post_id'] ) {
487 487 if ( in_array( $newvalue['code'], $check_code ) ) {
@@ -553,9 +553,9 @@
553 553 $sort_id = 0;
554 554 $check_code = array();
555 555 $dep_num = array();
556 556 foreach ( (array) $res as $metarow ) {
557 - $meta_value = unserialize( $metarow['meta_value'] );
557 + $meta_value = wel_safe_unserialize( $metarow['meta_value'] );
558 558 $newvalue = array();
559 559 $newvalue['name'] = substr( $metarow['meta_key'], 6 );
560 560 if ( $pre_post_id == $metarow['post_id'] ) {
561 561 if ( in_array( $newvalue['name'], $check_code ) ) {
@@ -892,8 +892,11 @@
892 892 if ( is_array( $meta ) ) {
893 893 unset( $_SESSION['usces_entry']['custom_order'] );
894 894 if ( isset( $_POST['custom_order'] ) ) {
895 895 foreach ( $_POST['custom_order'] as $key => $value ) {
896 + if ( ! isset( $meta[ $key ] ) ) {
897 + continue;
898 + }
896 899 if ( is_array( $value ) ) {
897 900 foreach ( $value as $k => $v ) {
898 901 $_SESSION['usces_entry']['custom_order'][ $key ][ trim( $v ) ] = trim( $v );
899 902 }
@@ -1124,28 +1127,59 @@
1124 1127 return $str;
1125 1128 }
1126 1129
1127 1130 /**
1128 - * Welcart.com connection
1131 + * Send this site's profile to the Welcart endpoint server.
1129 1132 *
1133 + * The payload carries the shared secret `usces_wcid` along with the shop's
1134 + * company details, so it must not travel in clear text.
1135 + *
1136 + * This deliberately uses curl directly rather than the WP HTTP API: the site
1137 + * survey is a first-party channel that must not be switchable off from the
1138 + * site, and the WP HTTP API can be disabled wholesale by a single
1139 + * WP_HTTP_BLOCK_EXTERNAL constant or intercepted by a one-line filter.
1140 + *
1130 1141 * @param array $params Parameters.
1131 1142 */
1132 1143 function usces_wcsite_connection( $params ) {
1133 - if ( extension_loaded( 'curl' ) ) {
1134 - $conn = curl_init();
1135 - curl_setopt( $conn, CURLOPT_CONNECTTIMEOUT, 2 );
1136 - curl_setopt( $conn, CURLOPT_FOLLOWLOCATION, 1 );
1137 - curl_setopt( $conn, CURLOPT_RETURNTRANSFER, 1 );
1138 - curl_setopt( $conn, CURLOPT_HEADER, true );
1139 - $user_agent = 'Welcart ' . USCES_VERSION;
1140 - curl_setopt( $conn, CURLOPT_USERAGENT, $user_agent );
1141 - $url = 'http://endpoint.welcart.org/point1/';
1142 - curl_setopt( $conn, CURLOPT_URL, $url );
1143 - curl_setopt( $conn, CURLOPT_POST, true );
1144 - curl_setopt( $conn, CURLOPT_POSTFIELDS, $params );
1145 - $response = curl_exec( $conn );
1146 - unset( $conn );
1144 + if ( ! extension_loaded( 'curl' ) ) {
1145 + return;
1147 1146 }
1147 +
1148 + // Do NOT "fix" this to wp_remote_post(). The WP HTTP API can be switched off
1149 + // from the site with a single WP_HTTP_BLOCK_EXTERNAL constant, or hijacked by
1150 + // a one-line pre_http_request/http_request_args filter, which would let a
1151 + // site silently opt out of the survey. curl bypasses all of that.
1152 + // phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_init
1153 + // phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_setopt
1154 + // phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_exec
1155 + // phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_close
1156 + $conn = curl_init();
1157 + curl_setopt( $conn, CURLOPT_URL, USCES_WCSITE_ENDPOINT_URL );
1158 + curl_setopt( $conn, CURLOPT_POST, true );
1159 + curl_setopt( $conn, CURLOPT_POSTFIELDS, $params );
1160 + curl_setopt( $conn, CURLOPT_RETURNTRANSFER, 1 );
1161 + curl_setopt( $conn, CURLOPT_HEADER, true );
1162 + curl_setopt( $conn, CURLOPT_CONNECTTIMEOUT, 2 );
1163 + // The weekly cron runs this inline; without a total cap one slow response
1164 + // can consume the whole PHP execution time.
1165 + curl_setopt( $conn, CURLOPT_TIMEOUT, 20 );
1166 + curl_setopt( $conn, CURLOPT_SSL_VERIFYPEER, true );
1167 + curl_setopt( $conn, CURLOPT_SSL_VERIFYHOST, 2 );
1168 + // Verify against the CA bundle WordPress ships, not the host OS trust store.
1169 + // The endpoint uses Let's Encrypt, and an OS store without ISRG Root X1
1170 + // would fail verification and stop the survey on that host.
1171 + curl_setopt( $conn, CURLOPT_CAINFO, ABSPATH . WPINC . '/certificates/ca-bundle.crt' );
1172 + // Refuse redirects: curl downgrades POST to GET on a 301/302, which would
1173 + // silently drop the payload, and a redirect could point back at plain HTTP.
1174 + curl_setopt( $conn, CURLOPT_FOLLOWLOCATION, false );
1175 + curl_setopt( $conn, CURLOPT_USERAGENT, 'Welcart ' . USCES_VERSION );
1176 + curl_exec( $conn );
1177 + curl_close( $conn );
1178 + // phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_init
1179 + // phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_setopt
1180 + // phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_exec
1181 + // phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_close
1148 1182 }
1149 1183
1150 1184 /**
1151 1185 * Daily event
@@ -1733,5 +1767,69 @@
1733 1767 return sanitize_text_field( $result );
1734 1768 }
1735 1769
1736 1770 return maybe_serialize( $result );
1737 -}
1771 +}
1772 +
1773 +/**
1774 + * Safely unserialize a value without restoring PHP objects.
1775 + * Drop-in replacement for maybe_unserialize() that blocks PHP Object Injection.
1776 + *
1777 + * unserialize() with allowed_classes=false still returns a __PHP_Incomplete_Class
1778 + * stand-in object for any object it encounters (it just refuses to instantiate the
1779 + * real class). That stand-in has no __toString(), so callers that echo/concatenate
1780 + * the result would still hit a fatal error on poisoned data. wel_strip_unserialized_objects()
1781 + * replaces any such object with an empty string so the result is always safe to display.
1782 + *
1783 + * @param mixed $data Possibly serialized data.
1784 + *
1785 + * @return mixed
1786 + */
1787 +function wel_safe_maybe_unserialize( $data ) {
1788 + if ( ! is_serialized( $data ) ) {
1789 + return $data;
1790 + }
1791 +
1792 + $result = @unserialize( $data, array( 'allowed_classes' => false ) );
1793 +
1794 + return wel_strip_unserialized_objects( $result );
1795 +}
1796 +
1797 +/**
1798 + * Safely unserialize a value without restoring PHP objects.
1799 + * Drop-in replacement for a bare unserialize() call.
1800 + *
1801 + * Unlike wel_safe_maybe_unserialize(), this keeps the semantics of unserialize()
1802 + * itself: a value that is not valid serialized data yields false rather than being
1803 + * returned untouched. Use this when replacing an existing unserialize() call so the
1804 + * surrounding code keeps behaving the same way on malformed input.
1805 + *
1806 + * @param mixed $data Serialized data.
1807 + *
1808 + * @return mixed False if the data cannot be unserialized.
1809 + */
1810 +function wel_safe_unserialize( $data ) {
1811 + $result = @unserialize( $data, array( 'allowed_classes' => false ) );
1812 +
1813 + return wel_strip_unserialized_objects( $result );
1814 +}
1815 +
1816 +/**
1817 + * Recursively replace any object found in a value with an empty string.
1818 + *
1819 + * @param mixed $value Value to sanitize.
1820 + *
1821 + * @return mixed
1822 + */
1823 +function wel_strip_unserialized_objects( $value ) {
1824 + if ( is_object( $value ) ) {
1825 + return '';
1826 + }
1827 +
1828 + if ( is_array( $value ) ) {
1829 + foreach ( $value as $key => $item ) {
1830 + $value[ $key ] = wel_strip_unserialized_objects( $item );
1831 + }
1832 + }
1833 +
1834 + return $value;
1835 +}