CVE-2023-6566: Other (NVD-CWE-Other)
value: Business Logic Errors in GitHub repository microweber/microweber prior to 2.0.
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 6.5
Published: 2023-12-07T00:15Z
Repository: https://github.com/microweber/microweber
Language: PHP
Files changed: src/MicroweberPackages/Cart/CartManager.php, src/MicroweberPackages/Checkout/CheckoutManager.php, src/MicroweberPackages/Shop/tests/CheckoutTest.php
Commit date: 2023-06-07 16:47:46 +0300
Commit message: update

Diff: diff --git a/src/MicroweberPackages/Cart/CartManager.php b/src/MicroweberPackages/Cart/CartManager.php
index c6f7218ed81..197a0631de0 100644
--- a/src/MicroweberPackages/Cart/CartManager.php
+++ b/src/MicroweberPackages/Cart/CartManager.php
@@ -241,11 +241,11 @@ class CartManager extends Crud
         if (is_string($params)) {
             $params = parse_str($params, $params2);
             $params = $params2;
         }
 
-        $table =  'cart';
+        $table = 'cart';
         $params['table'] = $table;
         $skip_sid = false;
         if (!defined('MW_API_CALL')) {
             if (isset($params['order_id'])) {
                 $skip_sid = 1;
@@ -290,14 +290,40 @@ class CartManager extends Crud
         }
         $return = array();
         if (is_array($get)) {
             foreach ($get as $k => $item) {
                 if (is_array($item)) {
+
+
                     if (isset($item['rel_id']) and isset($item['rel_type']) and $item['rel_type'] == 'content') {
                         $item['content_data'] = $this->app->content_manager->data($item['rel_id']);
                         $item['url'] = $this->app->content_manager->link($item['rel_id']);
                         $item['picture'] = $this->app->media_manager->get_picture($item['rel_id']);
+
+                        if (isset($params['for_checkout']) and $params['for_checkout']) {
+                            // check if content is unpublished or deleted
+                            $checkContent = get_content_by_id($item['rel_id']);
+                            $removeFromCart = false;
+                            if ($checkContent == false) {
+                                $removeFromCart = true;
+                            }
+
+                            if ($checkContent and isset($checkContent['is_deleted']) and $checkContent['is_deleted'] == 1) {
+                                $removeFromCart = true;
+                            }
+                            if ($checkContent and isset($checkContent['is_active']) and $checkContent['is_active'] == 0) {
+                                $removeFromCart = true;
+                            }
+
+                            if ($removeFromCart) {
+                                $this->remove_item($item['id']);
+                                unset($get[$k]);
+                                continue;
+                            }
+                        }
+
+
                     }
                     if (isset($item['custom_fields_data']) and $item['custom_fields_data'] != '') {
                         $item = $this->app->format->render_item_custom_fields_data($item);
                     }
                     if (isset($item['title'])) {
@@ -328,11 +354,11 @@ class CartManager extends Crud
         $order_id = intval($order_id);
         if ($order_id == false) {
             return;
         }
         $params = array();
-        $table =  'cart';
+        $table = 'cart';
         $params['table'] = $table;
         $params['order_id'] = $order_id;
         $get = $this->app->database_manager->get($params);
 
         if (!empty($get)) {
@@ -453,11 +479,11 @@ class CartManager extends Crud
 
 
             $cart_return = $check_cart;
 
 
-            $table =  'cart';
+            $table = 'cart';
             $cart_data_to_save = array();
             $cart_data_to_save['qty'] = $cart['qty'];
             $cart_data_to_save['id'] = $cart['id'];
             $cart_saved_id = $this->app->database_manager->save($table, $cart_data_to_save);
 
@@ -475,11 +501,11 @@ class CartManager extends Crud
 
 
     public function empty_cart()
     {
         $sid = mw()->user_manager->session_id();
-        $cart_table =  'cart';
+        $cart_table = 'cart';
 
         Cart::where('order_completed', 0)->where('session_id', $sid)->delete();
         $this->no_cache = true;
         $this->app->cache_manager->delete('cart');
         $this->app->cache_manager->delete('cart_orders');
@@ -657,14 +683,14 @@ class CartManager extends Crud
         foreach ($data as $k => $item) {
             if ($k != 'for' and $k != 'for_id' and $k != 'title') {
                 $found = false;
                 foreach ($content_custom_fields as $cf) {
                     if (isset($cf['type']) and isset($cf['name']) and $cf['type'] != 'price') {
-                        if(isset($data[$cf['name_key']])){
+                        if (isset($data[$cf['name_key']])) {
                             $cf['name'] = $data[$cf['name_key']];
                         }
-                   } elseif (isset($cf['type']) and $cf['type'] == 'price' and isset($cf['name']) and isset($cf['value'])) {
+                    } elseif (isset($cf['type']) and $cf['type'] == 'price' and isset($cf['name']) and isset($cf['value'])) {
                         if ($cf['value'] != '') {
                             if (isset($product_prices[$cf['name']])) {
                                 $prices[$cf['name']] = $product_prices[$cf['name']];
                             } else {
                                 $prices[$cf['name']] = $cf['value'];
@@ -735,11 +761,11 @@ class CartManager extends Crud
 
         if (is_array($prices)) {
             ksort($add);
             asort($add);
             $add = mw()->format->clean_xss($add);
-            $table =  'cart';
+            $table = 'cart';
 
 
             $cart = array();
             $cart['rel_type'] = trim($data['for']);
             $cart['rel_id'] = intval($data['for_id']);
@@ -871,11 +897,11 @@ class CartManager extends Crud
         } else {
 
 
             if ($cur_sid != false) {
                 $c_id = $sid;
-                $table =  'cart';
+                $table = 'cart';
                 $params = array();
                 //   $params['order_completed'] = 0;
                 $params['session_id'] = $c_id;
                 $params['table'] = $table;
                 if ($ord_id != false) {
@@ -940,11 +966,11 @@ class CartManager extends Crud
         }
     }
 
     public function table_name()
     {
-        return  'cart';
+        return 'cart';
     }
 
 
     public function is_product_in_stock($content_id)
     {
diff --git a/src/MicroweberPackages/Checkout/CheckoutManager.php b/src/MicroweberPackages/Checkout/CheckoutManager.php
index 51bbaf3c700..a4f038385cb 100644
--- a/src/MicroweberPackages/Checkout/CheckoutManager.php
+++ b/src/MicroweberPackages/Checkout/CheckoutManager.php
@@ -55,10 +55,11 @@ class CheckoutManager
         $cart = array();
         $cart_table = 'cart';
         $table_orders = 'cart_orders';
         $cart['session_id'] = $sid;
         $cart['order_completed'] = 0;
+        $cart['for_checkout'] = true;
         $cart['limit'] = 1;
         $mw_process_payment = true;
         $mw_process_payment_success = false;
         $mw_process_payment_failed = false;
         if (isset($_REQUEST['mw_payment_success']) or isset($_REQUEST['mw_payment_failure'])) {
diff --git a/src/MicroweberPackages/Shop/tests/CheckoutTest.php b/src/MicroweberPackages/Shop/tests/CheckoutTest.php
index ce141eb3914..a3915c61603 100644
--- a/src/MicroweberPackages/Shop/tests/CheckoutTest.php
+++ b/src/MicroweberPackages/Shop/tests/CheckoutTest.php
@@ -240,6 +240,116 @@ class CheckoutTest extends TestCase
         $this->assertFalse($found);
 
     }
 
 
+
+
+
+    public function testCheckoutDeletedProduct()
+    {
+        mw()->database_manager->extended_save_set_permission(true);
+
+        $productPrice = rand(1, 9999);
+        $title = 'test testCheckoutDeletedProduct prod ' . $productPrice;
+        $params = array(
+            'title' => $title,
+            'content_type' => 'product',
+            'subtype' => 'product',
+            'custom_fields_advanced' => array(
+                array('type' => 'dropdown', 'name' => 'Color', 'value' => array('Purple', 'Blue')),
+                array('type' => 'price', 'name' => 'Price', 'value' => '9.99'),
+
+            ),
+            'data_fields_qty' => 10,
+            'is_deleted' => 1,
+            'is_active' => 1,);
+
+
+        $saved_id = save_content($params);
+        $get = get_content_by_id($saved_id);
+
+
+        $add_to_cart = array(
+            'content_id' => $saved_id,
+            'price' => $productPrice,
+        );
+        $cart_add = update_cart($add_to_cart);
+
+
+        $checkoutDetails = array();
+        $checkoutDetails['email'] = 'client@microweber.com';
+        $checkoutDetails['is_paid'] = 1;
+        $checkoutDetails['order_completed'] = 1;
+
+
+        $checkoutStatus = app()->order_manager->place_order($checkoutDetails);
+        $content_data_after_order = content_data($saved_id);
+        $this->assertEquals(10, $content_data_after_order['qty']);
+
+        $productQuery = \MicroweberPackages\Product\Models\Product::query();
+        $productQuery = $productQuery->whereHas('orders');
+        $products = $productQuery->get();
+        $this->assertTrue($products->isEmpty());
+
+        $order = get_order_by_id($checkoutStatus);
+        $this->assertNotNull($order);
+        $this->assertNull($order['amount']);
+
+    }
+
+
+
+
+    public function testCheckoutUnpublishedProduct()
+    {
+        mw()->database_manager->extended_save_set_permission(true);
+
+        $productPrice = rand(1, 9999);
+        $title = 'test testCheckoutUnpublishedProduct prod ' . $productPrice;
+        $params = array(
+            'title' => $title,
+            'content_type' => 'product',
+            'subtype' => 'product',
+            'custom_fields_advanced' => array(
+                array('type' => 'dropdown', 'name' => 'Color', 'value' => array('Purple', 'Blue')),
+                array('type' => 'price', 'name' => 'Price', 'value' => '9.99'),
+
+            ),
+            'data_fields_qty' => 11,
+            'is_deleted' => 0,
+            'is_active' => 0
+        );
+
+
+        $saved_id = save_content($params);
+        $get = get_content_by_id($saved_id);
+
+
+        $add_to_cart = array(
+            'content_id' => $saved_id,
+            'price' => $productPrice,
+        );
+        $cart_add = update_cart($add_to_cart);
+
+
+        $checkoutDetails = array();
+        $checkoutDetails['email'] = 'client@microweber.com';
+        $checkoutDetails['is_paid'] = 1;
+        $checkoutDetails['order_completed'] = 1;
+
+
+        $checkoutStatus = app()->order_manager->place_order($checkoutDetails);
+        $content_data_after_order = content_data($saved_id);
+        $this->assertEquals(11, $content_data_after_order['qty']);
+
+        $productQuery = \MicroweberPackages\Product\Models\Product::query();
+        $productQuery = $productQuery->whereHas('orders');
+        $products = $productQuery->get();
+        $this->assertTrue($products->isEmpty());
+
+        $order = get_order_by_id($checkoutStatus);
+        $this->assertNotNull($order);
+        $this->assertNull($order['amount']);
+
+    }
 }

Vulnerable code:         $table =  'cart';
        $table =  'cart';
            $table =  'cart';
        $cart_table =  'cart';
                        if(isset($data[$cf['name_key']])){
                   } elseif (isset($cf['type']) and $cf['type'] == 'price' and isset($cf['name']) and isset($cf['value'])) {
            $table =  'cart';
                $table =  'cart';
        return  'cart';
Fixed code:         $table = 'cart';



                        if (isset($params['for_checkout']) and $params['for_checkout']) {
                            // check if content is unpublished or deleted
                            $checkContent = get_content_by_id($item['rel_id']);
                            $removeFromCart = false;
                            if ($checkContent == false) {
                                $removeFromCart = true;
                            }

                            if ($checkContent and isset($checkContent['is_deleted']) and $checkContent['is_deleted'] == 1) {
                                $removeFromCart = true;
                            }
                            if ($checkContent and isset($checkContent['is_active']) and $checkContent['is_active'] == 0) {
                                $removeFromCart = true;
                            }

                            if ($removeFromCart) {
                                $this->remove_item($item['id']);
                                unset($get[$k]);
                                continue;
                            }
                        }


        $table = 'cart';
            $table = 'cart';
        $cart_table = 'cart';
                        if (isset($data[$cf['name_key']])) {
                    } elseif (isset($cf['type']) and $cf['type'] == 'price' and isset($cf['name']) and isset($cf['value'])) {
            $table = 'cart';
                $table = 'cart';
        return 'cart';
        $cart['for_checkout'] = true;



    public function testCheckoutDeletedProduct()
    {
        mw()->database_manager->extended_save_set_permission(true);

        $productPrice = rand(1, 9999);
        $title = 'test testCheckoutDeletedProduct prod ' . $productPrice;
        $params = array(
            'title' => $title,
            'content_type' => 'product',
            'subtype' => 'product',
            'custom_fields_advanced' => array(
                array('type' => 'dropdown', 'name' => 'Color', 'value' => array('Purple', 'Blue')),
                array('type' => 'price', 'name' => 'Price', 'value' => '9.99'),

            ),
            'data_fields_qty' => 10,
            'is_deleted' => 1,
            'is_active' => 1,);


        $saved_id = save_content($params);
        $get = get_content_by_id($saved_id);


        $add_to_cart = array(
            'content_id' => $saved_id,
            'price' => $productPrice,
        );
        $cart_add = update_cart($add_to_cart);


        $checkoutDetails = array();
        $checkoutDetails['email'] = 'client@microweber.com';
        $checkoutDetails['is_paid'] = 1;
        $checkoutDetails['order_completed'] = 1;


        $checkoutStatus = app()->order_manager->place_order($checkoutDetails);
        $content_data_after_order = content_data($saved_id);
        $this->assertEquals(10, $content_data_after_order['qty']);

        $productQuery = \MicroweberPackages\Product\Models\Product::query();
        $productQuery = $productQuery->whereHas('orders');
        $products = $productQuery->get();
        $this->assertTrue($products->isEmpty());

        $order = get_order_by_id($checkoutStatus);
        $this->assertNotNull($order);
        $this->assertNull($order['amount']);

    }




    public function testCheckoutUnpublishedProduct()
    {
        mw()->database_manager->extended_save_set_permission(true);

        $productPrice = rand(1, 9999);
        $title = 'test testCheckoutUnpublishedProduct prod ' . $productPrice;
        $params = array(
            'title' => $title,
            'content_type' => 'product',
            'subtype' => 'product',
            'custom_fields_advanced' => array(
                array('type' => 'dropdown', 'name' => 'Color', 'value' => array('Purple', 'Blue')),
                array('type' => 'price', 'name' => 'Price', 'value' => '9.99'),

            ),
            'data_fields_qty' => 11,
            'is_deleted' => 0,
            'is_active' => 0
        );


        $saved_id = save_content($params);
        $get = get_content_by_id($saved_id);


        $add_to_cart = array(
            'content_id' => $saved_id,
            'price' => $productPrice,
        );
        $cart_add = update_cart($add_to_cart);


        $checkoutDetails = array();
        $checkoutDetails['email'] = 'client@microweber.com';
        $checkoutDetails['is_paid'] = 1;
        $checkoutDetails['order_completed'] = 1;


        $checkoutStatus = app()->order_manager->place_order($checkoutDetails);
        $content_data_after_order = content_data($saved_id);
        $this->assertEquals(11, $content_data_after_order['qty']);

        $productQuery = \MicroweberPackages\Product\Models\Product::query();
        $productQuery = $productQuery->whereHas('orders');
        $products = $productQuery->get();
        $this->assertTrue($products->isEmpty());

        $order = get_order_by_id($checkoutStatus);
        $this->assertNotNull($order);
        $this->assertNull($order['amount']);

    }