VYPR
Moderate severityOSV Advisory· Published Feb 3, 2026· Updated Feb 4, 2026

Craft Commerce has Stored XSS in Tax Categories (Name & Description) Fields Leading to Potential Privilege Escalation

CVE-2026-25488

Description

Craft Commerce is an ecommerce platform for Craft CMS. In versions from 4.0.0-RC1 to 4.10.0 and from 5.0.0 to 5.5.1, a stored XSS vulnerability in Craft Commerce allows attackers to execute malicious JavaScript in an administrator’s browser. This occurs because the Tax Categories (Name & Description) fields in the Store Management section are not properly sanitized before being displayed in the admin panel. This issue has been patched in versions 4.10.1 and 5.5.2.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
craftcms/commercePackagist
>= 5.0.0-RC1, < 5.5.25.5.2
craftcms/commercePackagist
>= 4.0.0-RC1, < 4.10.14.10.1

Affected products

1

Patches

1
fa2733308078

Fix encoding

https://github.com/craftcms/commerceLuke HolderDec 22, 2025via ghsa
7 files changed · +12 12
  • src/controllers/InventoryLocationsController.php+1 1 modified
    @@ -331,7 +331,7 @@ public function actionInventoryLocationsTableData(): Response
                     'id' => $inventoryLocation->id,
                     'title' => $inventoryLocation->getUiLabel(),
                     'handle' => $inventoryLocation->handle,
    -                'address' => $inventoryLocation->getAddressLine(),
    +                'address' => Html::encode($inventoryLocation->getAddressLine()),
                     'url' => $inventoryLocation->getCpEditUrl(),
                     'delete' => $inventoryLocations->count() > 1 ? $deleteButton : '',
                 ];
    
  • src/controllers/ShippingCategoriesController.php+2 2 modified
    @@ -46,7 +46,7 @@ public function actionIndex(?string $storeHandle = null): Response
             // Generate table data with chips
             $tableData = [];
             foreach ($shippingCategories as $shippingCategory) {
    -            $label = Craft::t('site', $shippingCategory->name);
    +            $label = Html::encode(Craft::t('site', $shippingCategory->name));
                 $tableData[] = [
                     'id' => $shippingCategory->id,
                     'title' => $label,
    @@ -57,7 +57,7 @@ public function actionIndex(?string $storeHandle = null): Response
                     ]),
                     'url' => $shippingCategory->getCpEditUrl(),
                     'handle' => $shippingCategory->handle,
    -                'description' => Craft::t('site', $shippingCategory->description),
    +                'description' => Html::encode(Craft::t('site', $shippingCategory->description)),
                     'default' => $shippingCategory->default,
                     '_showDelete' => (count($shippingCategories) > 1 && !$shippingCategory->default),
                 ];
    
  • src/controllers/ShippingMethodsController.php+1 1 modified
    @@ -44,7 +44,7 @@ public function actionIndex(?string $storeHandle = null): Response
             // Generate table data with chips
             $tableData = [];
             foreach ($shippingMethods as $shippingMethod) {
    -            $label = Craft::t('site', $shippingMethod->name);
    +            $label = Html::encode(Craft::t('site', $shippingMethod->name));
                 $tableData[] = [
                     'id' => $shippingMethod->id,
                     'title' => $label,
    
  • src/controllers/ShippingZonesController.php+2 2 modified
    @@ -41,12 +41,12 @@ public function actionIndex(?string $storeHandle = null): Response
             // Generate table data
             $tableData = [];
             foreach ($shippingZones as $shippingZone) {
    -            $label = Craft::t('site', $shippingZone->name);
    +            $label = Html::encode(Craft::t('site', $shippingZone->name));
                 $tableData[] = [
                     'id' => $shippingZone->id,
                     'title' => Html::a($label, $shippingZone->getCpEditUrl()),
                     'url' => $shippingZone->getCpEditUrl(),
    -                'description' => Craft::t('site', $shippingZone->description),
    +                'description' => Html::encode(Craft::t('site', $shippingZone->description)),
                 ];
             }
     
    
  • src/controllers/TaxCategoriesController.php+2 2 modified
    @@ -48,7 +48,7 @@ public function actionIndex(?string $storeHandle = null): Response
             // Generate table data with chips
             $tableData = [];
             foreach ($taxCategories as $taxCategory) {
    -            $label = Craft::t('site', $taxCategory->name);
    +            $label = Html::encode(Craft::t('site', $taxCategory->name));
                 $taxRates = $taxCategory->getTaxRates($store->id);
                 $tableData[] = [
                     'id' => $taxCategory->id,
    @@ -60,7 +60,7 @@ public function actionIndex(?string $storeHandle = null): Response
                     ]),
                     'url' => $taxCategory->getCpEditUrl($store->id),
                     'handle' => $taxCategory->handle,
    -                'description' => Craft::t('site', $taxCategory->description),
    +                'description' => Html::encode(Craft::t('site', $taxCategory->description)),
                     'default' => $taxCategory->default,
                     '_showDelete' => $taxRates->isEmpty() && (count($taxCategories) > 1 && !$taxCategory->default),
                 ];
    
  • src/controllers/TaxRatesController.php+2 2 modified
    @@ -57,7 +57,7 @@ public function actionIndex(?string $storeHandle = null): Response
             // Generate table data
             $tableData = [];
             foreach ($taxRates as $taxRate) {
    -            $label = Craft::t('site', $taxRate->name);
    +            $label = Html::encode(Craft::t('site', $taxRate->name));
                 $tableData[] = [
                     'id' => $taxRate->id,
                     'status' => $taxRate->enabled,
    @@ -67,7 +67,7 @@ public function actionIndex(?string $storeHandle = null): Response
                     'included' => $taxRate->include,
                     'removeIncluded' => $taxRate->removeIncluded,
                     'vat' => $taxRate->isVat,
    -                'zone' => $taxRate->isEverywhere ? Craft::t('commerce', 'Everywhere') : ($taxRate->taxZone ? $taxRate->taxZone->name : ''),
    +                'zone' => $taxRate->isEverywhere ? Craft::t('commerce', 'Everywhere') : ($taxRate->taxZone ? Html::encode($taxRate->taxZone->name) : ''),
                     'category' => $taxRate->taxCategory ? Cp::chipHtml($taxRate->taxCategory) : '',
                 ];
             }
    
  • src/controllers/TaxZonesController.php+2 2 modified
    @@ -49,12 +49,12 @@ public function actionIndex(?string $storeHandle = null): Response
             // Generate table data
             $tableData = [];
             foreach ($taxZones as $taxZone) {
    -            $label = Craft::t('site', $taxZone->name);
    +            $label = Html::encode(Craft::t('site', $taxZone->name));
                 $tableData[] = [
                     'id' => $taxZone->id,
                     'title' => Html::a($label, $taxZone->getCpEditUrl()),
                     'url' => $taxZone->getCpEditUrl(),
    -                'description' => Craft::t('site', $taxZone->description),
    +                'description' => Html::encode(Craft::t('site', $taxZone->description)),
                     'default' => $taxZone->default,
                 ];
             }
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.