CVE-2023-5861: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site Scripting (XSS) - Stored in GitHub repository microweber/microweber prior to 2.0.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 4.8
Published: 2023-10-31T01:15Z
Repository: https://github.com/microweber/microweber
Language: PHP
Files changed: src/MicroweberPackages/Cart/CartManager.php, src/MicroweberPackages/Customer/tests/CustomerCheckoutTest.php, src/MicroweberPackages/Order/OrderManager.php, src/MicroweberPackages/Shop/tests/CheckoutClientTest.php, src/MicroweberPackages/Shop/tests/CheckoutTest.php
Commit date: 2023-06-08 16:38:28 +0300
Commit message: update

Diff: diff --git a/src/MicroweberPackages/Cart/CartManager.php b/src/MicroweberPackages/Cart/CartManager.php
index 197a0631de0..5e912b7a70d 100644
--- a/src/MicroweberPackages/Cart/CartManager.php
+++ b/src/MicroweberPackages/Cart/CartManager.php
@@ -614,11 +614,11 @@ class CartManager extends Crud
 
             $cont_data = $this->app->content_manager->data($for_id);
             if ($cont == false) {
                 return array('error' => 'Invalid product?');
             } else {
-                if (is_array($cont) and isset($cont['title'])) {
+                if (is_array($cont) and isset($cont['title']) and $cont['title']) {
                     $data['title'] = $cont['title'];
                 }
             }
         }
 
@@ -777,10 +777,14 @@ class CartManager extends Crud
 
             $cart['custom_fields_json'] = json_encode($add);
             $cart['allow_html'] = 1;
             $cart['price'] = doubleval($found_price);
             $cart['limit'] = 1;
+            if(!isset($data['title']) or $data['title'] == false){
+                $data['title'] = 'Product '.$cart['rel_id'];
+            }
+
             $cart['title'] = mw()->format->clean_html($data['title']);
 
             $cart_return['custom_fields_data'] = $add;
             $cart_return['price'] = $cart['price'];
 
diff --git a/src/MicroweberPackages/Customer/tests/CustomerCheckoutTest.php b/src/MicroweberPackages/Customer/tests/CustomerCheckoutTest.php
index b6f06276b49..6cf550db318 100644
--- a/src/MicroweberPackages/Customer/tests/CustomerCheckoutTest.php
+++ b/src/MicroweberPackages/Customer/tests/CustomerCheckoutTest.php
@@ -1,20 +1,19 @@
 <?php
+
 namespace MicroweberPackages\Customer\tests;
 
-use MicroweberPackages\Core\tests\TestCase;
 use MicroweberPackages\Checkout\CheckoutManager;
+use MicroweberPackages\Core\tests\TestCase;
 use MicroweberPackages\Customer\Models\Address;
 use MicroweberPackages\Customer\Models\Customer;
-use MicroweberPackages\Utils\Mail\MailSender;
 
 /**
  * Run test
  * @author Bobi Microweber
  * @command php phpunit.phar --filter CheckoutTest
  */
-
 class CustomerCheckoutTest extends TestCase
 {
     public static $content_id = 1;
 
     private function _addProductToCart($title)
@@ -35,11 +34,11 @@ class CustomerCheckoutTest extends TestCase
             'is_active' => 1,);
 
 
         $saved_id = save_content($params);
         $prices_data = app()->shop_manager->get_product_prices($saved_id, false);
-        $this->assertEquals($prices_data['Price'],$productPrice);
+        $this->assertEquals($prices_data['Price'], $productPrice);
 
 
         $get = get_content_by_id($saved_id);
 
         $this->assertEquals($saved_id, ($get['id']));
@@ -48,20 +47,20 @@ class CustomerCheckoutTest extends TestCase
         $add_to_cart = array(
             'content_id' => self::$content_id,
             'price' => $productPrice,
         );
         $cart_add = update_cart($add_to_cart);
- 
+
         $this->assertEquals(isset($cart_add['success']), true);
         $this->assertEquals(isset($cart_add['product']), true);
         $this->assertEquals($cart_add['product']['price'], $productPrice);
     }
 
     public function testCheckout()
     {
 
-         \Config::set('mail.transport', 'array');
+        \Config::set('mail.transport', 'array');
 
         $this->_addProductToCart('Product 1');
         $this->_addProductToCart('Product 2');
         $this->_addProductToCart('Product 3');
         $this->_addProductToCart('Product 4');
@@ -107,9 +106,56 @@ class CustomerCheckoutTest extends TestCase
         $this->assertEquals($address->city, $checkoutDetails['city']);
         $this->assertEquals($address->zip, $checkoutDetails['zip']);
         $this->assertEquals($address->state, $checkoutDetails['state']);
 
 
-
     }
 
+    public function testCheckoutCustomerWithXss()
+    {
+
+        $this->_addProductToCart('Product 1');
+        $this->_addProductToCart('Product 2');
+        $this->_addProductToCart('Product 3');
+        $this->_addProductToCart('Product 4');
+        $xss = '<style>@keyframes x{}</style><xss style="animation-name:x" onanimationend="alert(document.cookie)"></xss>';
+        $checkoutDetails = array();
+        $checkoutDetails['email'] = 'client_' . uniqid() . '@microweber.com';
+        $checkoutDetails['first_name'] = 'Client First Name<script>alert(1)</script>' . $xss;
+        $checkoutDetails['last_name'] = 'Microweber Last Name' . $xss;
+        $checkoutDetails['phone'] = '08812345' . rand(100, 999) . $xss;
+        $checkoutDetails['address'] = 'Business Park, Mladost 4' . $xss;
+        $checkoutDetails['address2'] = 'Business Park, Mladost 6' . $xss;
+        $checkoutDetails['city'] = 'Sofia' . $xss;
+        $checkoutDetails['state'] = 'Sofia City' . $xss;
+        $checkoutDetails['country'] = 'Bulgaria' . $xss;
+        $checkoutDetails['zip'] = '1000' . $xss;
+
+
+        $checkout = new CheckoutManager();
+        $checkoutStatus = $checkout->checkout($checkoutDetails);
+
+        $this->assertArrayHasKey('success', $checkoutStatus);
+        $this->assertArrayHasKey('id', $checkoutStatus);
+
+
+        // Find customer
+        $customer = Customer::where('email', $checkoutDetails['email'])->first();
+
+        $this->assertEquals($customer->email, $checkoutDetails['email']);
+        $this->assertNotEquals($customer->first_name, $checkoutDetails['first_name']);
+        $this->assertNotEquals($customer->last_name, $checkoutDetails['last_name']);
+        $this->assertNotEquals($customer->phone, $checkoutDetails['phone']);
+
+        // Find customer
+        $address = Address::where('customer_id', $customer->id)->first();
+
+        $this->assertNotEquals($address->phone, $checkoutDetails['phone']);
+        $this->assertNotEquals($address->address_street_1, $checkoutDetails['address']);
+        $this->assertNotEquals($address->address_street_2, $checkoutDetails['address2']);
+        $this->assertNotEquals($address->city, $checkoutDetails['city']);
+        $this->assertNotEquals($address->zip, $checkoutDetails['zip']);
+        $this->assertNotEquals($address->state, $checkoutDetails['state']);
+
+
+    }
 }
diff --git a/src/MicroweberPackages/Order/OrderManager.php b/src/MicroweberPackages/Order/OrderManager.php
index fa11ca465de..74c6c75b65c 100644
--- a/src/MicroweberPackages/Order/OrderManager.php
+++ b/src/MicroweberPackages/Order/OrderManager.php
@@ -125,17 +125,28 @@ class OrderManager
 
             return $c_id;
         }
     }
 
-    public function place_order($place_order)
+    public function place_order($place_order = array())
     {
         $sid = mw()->user_manager->session_id();
         if ($sid == false) {
             return $sid;
         }
-
+        if (empty($place_order)) {
+            return;
+        }
+        array_walk_recursive(
+            $place_order,
+            function(&$string) {
+                if (is_string($string)) {
+                    $string = trim(strip_tags($string));
+                }
+            }
+        );
+        $place_order = xss_clean($place_order);
         event($event = new OrderIsCreating($place_order));
         $should_mark_as_paid = false;
         $place_order = array_filter($place_order);
         if (isset($place_order['is_paid']) and intval($place_order['is_paid']) == 1) {
             unset($place_order['is_paid']);
diff --git a/src/MicroweberPackages/Shop/tests/CheckoutClientTest.php b/src/MicroweberPackages/Shop/tests/CheckoutClientTest.php
new file mode 100644
index 00000000000..4f7a1bbdac1
--- /dev/null
+++ b/src/MicroweberPackages/Shop/tests/CheckoutClientTest.php
@@ -0,0 +1,98 @@
+<?php
+namespace MicroweberPackages\Shop\tests;
+
+use MicroweberPackages\Checkout\CheckoutManager;
+use MicroweberPackages\Core\tests\TestCase;
+
+
+class CheckoutClientTest extends TestCase
+{
+    public static $content_id = 1;
+
+    private function _addProductToCart($title)
+    {
+        app()->database_manager->extended_save_set_permission(true);
+
+        $productPrice = rand(1, 4444);
+
+        $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' => $productPrice),
+
+            ),
+            'is_active' => 1,);
+
+
+        $saved_id = save_content($params);
+        $get = get_content_by_id($saved_id);
+
+        $this->assertEquals($saved_id, ($get['id']));
+        self::$content_id = $saved_id;
+
+        $add_to_cart = array(
+            'content_id' => self::$content_id,
+            'price' => $productPrice,
+        );
+        $cart_add = update_cart($add_to_cart);
+
+        $this->assertEquals(isset($cart_add['success']), true);
+        $this->assertEquals(isset($cart_add['product']), true);
+        $this->assertEquals($cart_add['product']['price'], $productPrice);
+    }
+
+    public function testCheckoutClientNames()
+    {
+        empty_cart();
+
+
+        $this->_addProductToCart('CheckoutClientTestProduct 1');
+        $this->_addProductToCart('CheckoutClientTestProduct 2');
+        $this->_addProductToCart('CheckoutClientTestProduct 3');
+        $this->_addProductToCart('CheckoutClientTestProduct 4');
+
+        $email = 'client+'.uniqid('testCheckoutClientNames').'test@microweber.com';
+        $checkoutDetails = array();
+        $checkoutDetails['email'] = $email;
+        $checkoutDetails['first_name'] = 'Client';
+        $checkoutDetails['last_name'] = 'Microweber';
+        $checkoutDetails['phone'] = '08812345678';
+        $checkoutDetails['address'] = 'Business Park, Mladost 4';
+        $checkoutDetails['city'] = 'Sofia';
+        $checkoutDetails['state'] = 'Sofia City';
+        $checkoutDetails['country'] = 'Bulgaria';
+        $checkoutDetails['zip'] = '1000';
+
+        $checkout = new CheckoutManager();
+        $checkoutStatus = $checkout->checkout($checkoutDetails);
+
+        $this->assertArrayHasKey('success', $checkoutStatus);
+        $this->assertArrayHasKey('id', $checkoutStatus);
+        $this->assertArrayHasKey('order_completed', $checkoutStatus);
+        $this->assertArrayHasKey('amount', $checkoutStatus);
+        $this->assertArrayHasKey('currency', $checkoutStatus);
+        $this->assertArrayHasKey('order_status', $checkoutStatus);
+
+        $this->assertEquals($checkoutStatus['order_completed'], 1);
+        $this->assertEquals($checkoutStatus['first_name'],  $checkoutDetails['first_name']);
+        $this->assertEquals($checkoutStatus['last_name'],  $checkoutDetails['last_name']);
+        $this->assertEquals($checkoutStatus['email'],  $checkoutDetails['email']);
+        $this->assertEquals($checkoutStatus['country'],  $checkoutDetails['country']);
+        $this->assertEquals($checkoutStatus['city'],  $checkoutDetails['city']);
+        $this->assertEquals($checkoutStatus['state'],  $checkoutDetails['state']);
+        $this->assertEquals($checkoutStatus['zip'],  $checkoutDetails['zip']);
+        $this->assertEquals($checkoutStatus['address'],  $checkoutDetails['address']);
+
+
+
+
+    }
+
+
+
+
+
+}
diff --git a/src/MicroweberPackages/Shop/tests/CheckoutTest.php b/src/MicroweberPackages/Shop/tests/CheckoutTest.php
index a3915c61603..f03a6f2d574 100644
--- a/src/MicroweberPackages/Shop/tests/CheckoutTest.php
+++ b/src/MicroweberPackages/Shop/tests/CheckoutTest.php
@@ -284,14 +284,14 @@ class CheckoutTest extends TestCase
 
         $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']);
 
         $order = get_order_by_id($checkoutStatus);
         $this->assertNotNull($order);
         $this->assertNull($order['amount']);
 
@@ -340,16 +340,15 @@ class CheckoutTest extends TestCase
 
         $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:                 if (is_array($cont) and isset($cont['title'])) {
use MicroweberPackages\Core\tests\TestCase;
use MicroweberPackages\Utils\Mail\MailSender;

        $this->assertEquals($prices_data['Price'],$productPrice);
 
         \Config::set('mail.transport', 'array');

    public function place_order($place_order)

        $productQuery = \MicroweberPackages\Product\Models\Product::query();
        $productQuery = $productQuery->whereHas('orders');
        $products = $productQuery->get();
        $this->assertTrue($products->isEmpty());
        $productQuery = \MicroweberPackages\Product\Models\Product::query();
        $productQuery = $productQuery->whereHas('orders');
        $products = $productQuery->get();
        $this->assertTrue($products->isEmpty());
Fixed code:                 if (is_array($cont) and isset($cont['title']) and $cont['title']) {
            if(!isset($data['title']) or $data['title'] == false){
                $data['title'] = 'Product '.$cart['rel_id'];
            }


use MicroweberPackages\Core\tests\TestCase;
        $this->assertEquals($prices_data['Price'], $productPrice);

        \Config::set('mail.transport', 'array');
    public function testCheckoutCustomerWithXss()
    {

        $this->_addProductToCart('Product 1');
        $this->_addProductToCart('Product 2');
        $this->_addProductToCart('Product 3');
        $this->_addProductToCart('Product 4');
        $xss = '<style>@keyframes x{}</style><xss style="animation-name:x" onanimationend="alert(document.cookie)"></xss>';
        $checkoutDetails = array();
        $checkoutDetails['email'] = 'client_' . uniqid() . '@microweber.com';
        $checkoutDetails['first_name'] = 'Client First Name<script>alert(1)</script>' . $xss;
        $checkoutDetails['last_name'] = 'Microweber Last Name' . $xss;
        $checkoutDetails['phone'] = '08812345' . rand(100, 999) . $xss;
        $checkoutDetails['address'] = 'Business Park, Mladost 4' . $xss;
        $checkoutDetails['address2'] = 'Business Park, Mladost 6' . $xss;
        $checkoutDetails['city'] = 'Sofia' . $xss;
        $checkoutDetails['state'] = 'Sofia City' . $xss;
        $checkoutDetails['country'] = 'Bulgaria' . $xss;
        $checkoutDetails['zip'] = '1000' . $xss;


        $checkout = new CheckoutManager();
        $checkoutStatus = $checkout->checkout($checkoutDetails);

        $this->assertArrayHasKey('success', $checkoutStatus);
        $this->assertArrayHasKey('id', $checkoutStatus);


        // Find customer
        $customer = Customer::where('email', $checkoutDetails['email'])->first();

        $this->assertEquals($customer->email, $checkoutDetails['email']);
        $this->assertNotEquals($customer->first_name, $checkoutDetails['first_name']);
        $this->assertNotEquals($customer->last_name, $checkoutDetails['last_name']);
        $this->assertNotEquals($customer->phone, $checkoutDetails['phone']);

        // Find customer
        $address = Address::where('customer_id', $customer->id)->first();

        $this->assertNotEquals($address->phone, $checkoutDetails['phone']);
        $this->assertNotEquals($address->address_street_1, $checkoutDetails['address']);
        $this->assertNotEquals($address->address_street_2, $checkoutDetails['address2']);
        $this->assertNotEquals($address->city, $checkoutDetails['city']);
        $this->assertNotEquals($address->zip, $checkoutDetails['zip']);
        $this->assertNotEquals($address->state, $checkoutDetails['state']);


    }
    public function place_order($place_order = array())
        if (empty($place_order)) {
            return;
        }
        array_walk_recursive(
            $place_order,
            function(&$string) {
                if (is_string($string)) {
                    $string = trim(strip_tags($string));
                }
            }
        );
        $place_order = xss_clean($place_order);
<?php
namespace MicroweberPackages\Shop\tests;

use MicroweberPackages\Checkout\CheckoutManager;
use MicroweberPackages\Core\tests\TestCase;


class CheckoutClientTest extends TestCase
{
    public static $content_id = 1;

    private function _addProductToCart($title)
    {
        app()->database_manager->extended_save_set_permission(true);

        $productPrice = rand(1, 4444);

        $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' => $productPrice),

            ),
            'is_active' => 1,);


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

        $this->assertEquals($saved_id, ($get['id']));
        self::$content_id = $saved_id;

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

        $this->assertEquals(isset($cart_add['success']), true);
        $this->assertEquals(isset($cart_add['product']), true);
        $this->assertEquals($cart_add['product']['price'], $productPrice);
    }

    public function testCheckoutClientNames()
    {
        empty_cart();


        $this->_addProductToCart('CheckoutClientTestProduct 1');
        $this->_addProductToCart('CheckoutClientTestProduct 2');
        $this->_addProductToCart('CheckoutClientTestProduct 3');
        $this->_addProductToCart('CheckoutClientTestProduct 4');

        $email = 'client+'.uniqid('testCheckoutClientNames').'test@microweber.com';
        $checkoutDetails = array();
        $checkoutDetails['email'] = $email;
        $checkoutDetails['first_name'] = 'Client';
        $checkoutDetails['last_name'] = 'Microweber';
        $checkoutDetails['phone'] = '08812345678';
        $checkoutDetails['address'] = 'Business Park, Mladost 4';
        $checkoutDetails['city'] = 'Sofia';
        $checkoutDetails['state'] = 'Sofia City';
        $checkoutDetails['country'] = 'Bulgaria';
        $checkoutDetails['zip'] = '1000';

        $checkout = new CheckoutManager();
        $checkoutStatus = $checkout->checkout($checkoutDetails);

        $this->assertArrayHasKey('success', $checkoutStatus);
        $this->assertArrayHasKey('id', $checkoutStatus);
        $this->assertArrayHasKey('order_completed', $checkoutStatus);
        $this->assertArrayHasKey('amount', $checkoutStatus);
        $this->assertArrayHasKey('currency', $checkoutStatus);
        $this->assertArrayHasKey('order_status', $checkoutStatus);

        $this->assertEquals($checkoutStatus['order_completed'], 1);
        $this->assertEquals($checkoutStatus['first_name'],  $checkoutDetails['first_name']);
        $this->assertEquals($checkoutStatus['last_name'],  $checkoutDetails['last_name']);
        $this->assertEquals($checkoutStatus['email'],  $checkoutDetails['email']);
        $this->assertEquals($checkoutStatus['country'],  $checkoutDetails['country']);
        $this->assertEquals($checkoutStatus['city'],  $checkoutDetails['city']);
        $this->assertEquals($checkoutStatus['state'],  $checkoutDetails['state']);
        $this->assertEquals($checkoutStatus['zip'],  $checkoutDetails['zip']);
        $this->assertEquals($checkoutStatus['address'],  $checkoutDetails['address']);




    }





}

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