CVE-2023-2614: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site Scripting (XSS) - DOM in GitHub repository pimcore/pimcore prior to 10.5.21.
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: 5.4
Published: 2023-05-10T06:15Z
Repository: https://github.com/pimcore/pimcore
Language: PHP
Files changed: bundles/AdminBundle/Controller/Reports/CustomReportController.php, bundles/AdminBundle/Resources/public/js/pimcore/report/custom/panel.js
Commit date: 2023-04-25 11:11:18 +0200
Commit message: Fix name field in custom reports (#15007)


Diff: diff --git a/bundles/AdminBundle/Controller/Reports/CustomReportController.php b/bundles/AdminBundle/Controller/Reports/CustomReportController.php
index eed9c33340ad..da2e4d16084f 100644
--- a/bundles/AdminBundle/Controller/Reports/CustomReportController.php
+++ b/bundles/AdminBundle/Controller/Reports/CustomReportController.php
@@ -74,10 +74,12 @@ class CustomReportController extends ReportsControllerBase
     {
         $this->checkPermission('reports_config');
 
         $success = false;
 
+        $this->isValidConfigName($request->get('name'));
+
         $report = CustomReport\Config::getByName($request->get('name'));
 
         if (!$report) {
             $report = new CustomReport\Config();
             if (!$report->isWriteable()) {
@@ -127,10 +129,11 @@ class CustomReportController extends ReportsControllerBase
     public function cloneAction(Request $request)
     {
         $this->checkPermission('reports_config');
 
         $newName = $request->get('newName');
+        $this->isValidConfigName($newName);
         $report = CustomReport\Config::getByName($newName);
         if ($report) {
             throw new \Exception('report already exists');
         }
 
@@ -185,11 +188,11 @@ class CustomReportController extends ReportsControllerBase
      * @return JsonResponse
      */
     public function updateAction(Request $request)
     {
         $this->checkPermission('reports_config');
-
+        $this->isValidConfigName($request->get('name'));
         $report = CustomReport\Config::getByName($request->get('name'));
         if (!$report) {
             throw $this->createNotFoundException();
         }
         if (!$report->isWriteable()) {
@@ -517,6 +520,15 @@ class CustomReportController extends ReportsControllerBase
             return $response;
         }
 
         throw new FileNotFoundException("File \"$exportFile\" not found!");
     }
+
+    /**
+     * @throws \Exception
+     */
+    public function isValidConfigName(string $configName) {
+        if(!preg_match('/^[a-zA-Z0-9_\-]+$/', $configName)) {
+            throw new \Exception('The customer report name is invalid');
+        }
+    }
 }
diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/report/custom/panel.js b/bundles/AdminBundle/Resources/public/js/pimcore/report/custom/panel.js
index e765fb632092..351ede59c9d3 100644
--- a/bundles/AdminBundle/Resources/public/js/pimcore/report/custom/panel.js
+++ b/bundles/AdminBundle/Resources/public/js/pimcore/report/custom/panel.js
@@ -204,11 +204,12 @@ pimcore.report.custom.panel = Class.create({
             Ext.Msg.alert(' ', t('failed_to_create_new_item'));
         }
     },
 
     deleteField: function (tree, record) {
-        Ext.Msg.confirm(t('delete'), sprintf(t('delete_message_advanced'), t('portlet_customreport'), record.data.text), function (btn) {
+        const decodedName = Ext.util.Format.htmlDecode(record.data.text);
+        Ext.Msg.confirm(t('delete'), sprintf(t('delete_message_advanced'), t('portlet_customreport'), Ext.util.Format.htmlEncode(decodedName)), function (btn) {
             if (btn == 'yes') {
                 Ext.Ajax.request({
                     url: Routing.generate('pimcore_admin_reports_customreport_delete'),
                     method: 'DELETE',
                     params: {

Vulnerable code: 
        Ext.Msg.confirm(t('delete'), sprintf(t('delete_message_advanced'), t('portlet_customreport'), record.data.text), function (btn) {
Fixed code:         $this->isValidConfigName($request->get('name'));

        $this->isValidConfigName($newName);
        $this->isValidConfigName($request->get('name'));

    /**
     * @throws \Exception
     */
    public function isValidConfigName(string $configName) {
        if(!preg_match('/^[a-zA-Z0-9_\-]+$/', $configName)) {
            throw new \Exception('The customer report name is invalid');
        }
    }
        const decodedName = Ext.util.Format.htmlDecode(record.data.text);
        Ext.Msg.confirm(t('delete'), sprintf(t('delete_message_advanced'), t('portlet_customreport'), Ext.util.Format.htmlEncode(decodedName)), function (btn) {