VYPR
Critical severityNVD Advisory· Published Oct 16, 2025· Updated Oct 17, 2025

bagisto - CSV Formula Injection in Create New Product

CVE-2025-62417

Description

Bagisto is an open source laravel eCommerce platform. When product data that begins with a spreadsheet formula character (for example =, +, -, or @) is accepted and later exported or saved into a CSV and opened in spreadsheet software, the spreadsheet will interpret that cell as a formula. This allows an attacker to supply a CSV field (e.g., product name) that contains a formula which may be evaluated by a victim’s spreadsheet application — potentially leading to data exfiltration and remote command execution (via older Excel exploits / OLE/cmd constructs or Excel macros). This vulnerability is fixed in 2.3.8.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
bagisto/bagistoPackagist
< 2.3.82.3.8

Affected products

1

Patches

1
8076c708498a

fix: applied security fixes to product attributes, including short description, long description, and other TinyMCE-enabled fields

https://github.com/bagisto/bagistoDevansh BawariOct 15, 2025via ghsa
7 files changed · +262 10
  • CHANGELOG.md+2 0 modified
    @@ -10,6 +10,8 @@ This changelog consists of the bug & security fixes and new features being inclu
     
     * Refined TinyMCE editor integration and applied related security fixes.
     
    +* Applied security fixes to product attributes, including short description, long description, and other TinyMCE-enabled fields.
    +
     * Fixed an issue where the description was not updating correctly during channel updates.
     
     * #10971 - Fixed an issue where updating a field without changing the image caused the image to break or not display correctly.
    
  • composer.json+2 1 modified
    @@ -44,7 +44,8 @@
             "pusher/pusher-php-server": "^7.0",
             "shetabit/visitor": "^4.1",
             "spatie/laravel-responsecache": "^7.4",
    -        "spatie/laravel-sitemap": "^7.3"
    +        "spatie/laravel-sitemap": "^7.3",
    +        "stevebauman/purify": "^6.3"
         },
         "require-dev": {
             "bagisto/laravel-datafaker": "2.3.*",
    
  • composer.lock+68 2 modified
    @@ -4,7 +4,7 @@
             "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
             "This file is @generated automatically"
         ],
    -    "content-hash": "dc52a94d30562eb739cf3a489ceecd7c",
    +    "content-hash": "2f992bd3a65209139a1efcb24489d967",
         "packages": [
             {
                 "name": "astrotomic/laravel-translatable",
    @@ -8005,6 +8005,72 @@
                 ],
                 "time": "2025-01-13T13:04:43+00:00"
             },
    +        {
    +            "name": "stevebauman/purify",
    +            "version": "v6.3.1",
    +            "source": {
    +                "type": "git",
    +                "url": "https://github.com/stevebauman/purify.git",
    +                "reference": "3acb5e77904f420ce8aad8fa1c7f394e82daa500"
    +            },
    +            "dist": {
    +                "type": "zip",
    +                "url": "https://api.github.com/repos/stevebauman/purify/zipball/3acb5e77904f420ce8aad8fa1c7f394e82daa500",
    +                "reference": "3acb5e77904f420ce8aad8fa1c7f394e82daa500",
    +                "shasum": ""
    +            },
    +            "require": {
    +                "ezyang/htmlpurifier": "^4.17",
    +                "illuminate/contracts": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
    +                "illuminate/support": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
    +                "php": ">=7.4"
    +            },
    +            "require-dev": {
    +                "orchestra/testbench": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0",
    +                "phpunit/phpunit": "^8.0|^9.0|^10.0|^11.5.3"
    +            },
    +            "type": "library",
    +            "extra": {
    +                "laravel": {
    +                    "aliases": {
    +                        "Purify": "Stevebauman\\Purify\\Facades\\Purify"
    +                    },
    +                    "providers": [
    +                        "Stevebauman\\Purify\\PurifyServiceProvider"
    +                    ]
    +                }
    +            },
    +            "autoload": {
    +                "psr-4": {
    +                    "Stevebauman\\Purify\\": "src/"
    +                }
    +            },
    +            "notification-url": "https://packagist.org/downloads/",
    +            "license": [
    +                "MIT"
    +            ],
    +            "authors": [
    +                {
    +                    "name": "Steve Bauman",
    +                    "email": "steven_bauman@outlook.com"
    +                }
    +            ],
    +            "description": "An HTML Purifier / Sanitizer for Laravel",
    +            "keywords": [
    +                "Purifier",
    +                "clean",
    +                "cleaner",
    +                "html",
    +                "laravel",
    +                "purification",
    +                "purify"
    +            ],
    +            "support": {
    +                "issues": "https://github.com/stevebauman/purify/issues",
    +                "source": "https://github.com/stevebauman/purify/tree/v6.3.1"
    +            },
    +            "time": "2025-05-21T16:53:09+00:00"
    +        },
             {
                 "name": "symfony/console",
                 "version": "v7.3.3",
    @@ -13923,6 +13989,6 @@
             "ext-pdo_mysql": "*",
             "ext-tokenizer": "*"
         },
    -    "platform-dev": {},
    +    "platform-dev": [],
         "plugin-api-version": "2.6.0"
     }
    
  • config/purify.php+115 0 added
    @@ -0,0 +1,115 @@
    +<?php
    +
    +use Stevebauman\Purify\Definitions\Html5Definition;
    +
    +return [
    +
    +    /*
    +    |--------------------------------------------------------------------------
    +    | Default Config
    +    |--------------------------------------------------------------------------
    +    |
    +    | This option defines the default config that is provided to HTMLPurifier.
    +    |
    +    */
    +
    +    'default' => 'default',
    +
    +    /*
    +    |--------------------------------------------------------------------------
    +    | Config sets
    +    |--------------------------------------------------------------------------
    +    |
    +    | Here you may configure various sets of configuration for differentiated use of HTMLPurifier.
    +    | A specific set of configuration can be applied by calling the "config($name)" method on
    +    | a Purify instance. Feel free to add/remove/customize these attributes as you wish.
    +    |
    +    | Documentation: http://htmlpurifier.org/live/configdoc/plain.html
    +    |
    +    |   Core.Encoding               The encoding to convert input to.
    +    |   HTML.Doctype                Doctype to use during filtering.
    +    |   HTML.Allowed                The allowed HTML Elements with their allowed attributes.
    +    |   HTML.ForbiddenElements      The forbidden HTML elements. Elements that are listed in this
    +    |                               string will be removed, however their content will remain.
    +    |   CSS.AllowedProperties       The Allowed CSS properties.
    +    |   AutoFormat.AutoParagraph    Newlines are converted in to paragraphs whenever possible.
    +    |   AutoFormat.RemoveEmpty      Remove empty elements that contribute no semantic information to the document.
    +    |
    +    */
    +
    +    'configs' => [
    +
    +        'default' => [
    +            'Core.Encoding'            => 'utf-8',
    +            'HTML.Doctype'             => 'HTML 4.01 Transitional',
    +            'HTML.Allowed'             => 'h1,h2,h3,h4,h5,h6,b,u,strong,i,em,s,del,a[href|title],ul,ol,li,p[style],br,span,img[width|height|alt|src],blockquote',
    +            'HTML.ForbiddenElements'   => '',
    +            'CSS.AllowedProperties'    => 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align',
    +            'AutoFormat.AutoParagraph' => false,
    +            'AutoFormat.RemoveEmpty'   => false,
    +        ],
    +
    +    ],
    +
    +    /*
    +    |--------------------------------------------------------------------------
    +    | HTMLPurifier definitions
    +    |--------------------------------------------------------------------------
    +    |
    +    | Here you may specify a class that augments the HTML definitions used by
    +    | HTMLPurifier. Additional HTML5 definitions are provided out of the box.
    +    | When specifying a custom class, make sure it implements the interface:
    +    |
    +    |   \Stevebauman\Purify\Definitions\Definition
    +    |
    +    | Note that these definitions are applied to every Purifier instance.
    +    |
    +    | Documentation: http://htmlpurifier.org/docs/enduser-customize.html
    +    |
    +    */
    +
    +    'definitions' => Html5Definition::class,
    +
    +    /*
    +    |--------------------------------------------------------------------------
    +    | HTMLPurifier CSS definitions
    +    |--------------------------------------------------------------------------
    +    |
    +    | Here you may specify a class that augments the CSS definitions used by
    +    | HTMLPurifier. When specifying a custom class, make sure it implements
    +    | the interface:
    +    |
    +    |   \Stevebauman\Purify\Definitions\CssDefinition
    +    |
    +    | Note that these definitions are applied to every Purifier instance.
    +    |
    +    | CSS should be extending $definition->info['css-attribute'] = values
    +    | See HTMLPurifier_CSSDefinition for further explanation
    +    |
    +    */
    +
    +    'css-definitions' => null,
    +
    +    /*
    +    |--------------------------------------------------------------------------
    +    | Serializer
    +    |--------------------------------------------------------------------------
    +    |
    +    | The storage implementation where HTMLPurifier can store its serializer files.
    +    | If the filesystem cache is in use, the path must be writable through the
    +    | storage disk by the web server, otherwise an exception will be thrown.
    +    |
    +    */
    +
    +    'serializer' => [
    +        'driver' => env('CACHE_STORE', env('CACHE_DRIVER', 'file')),
    +        'cache'  => \Stevebauman\Purify\Cache\CacheDefinitionCache::class,
    +    ],
    +
    +    // 'serializer' => [
    +    //    'disk' => env('FILESYSTEM_DISK', 'local'),
    +    //    'path' => 'purify',
    +    //    'cache' => \Stevebauman\Purify\Cache\FilesystemDefinitionCache::class,
    +    // ],
    +
    +];
    
  • packages/Webkul/Admin/src/Http/Controllers/Catalog/ProductController.php+1 1 modified
    @@ -151,7 +151,7 @@ public function update(ProductForm $request, int $id)
         {
             Event::dispatch('catalog.product.update.before', $id);
     
    -        $product = $this->productRepository->update(request()->all(), $id);
    +        $product = $this->productRepository->update($request->all(), $id);
     
             Event::dispatch('catalog.product.update.after', $product);
     
    
  • packages/Webkul/Admin/src/Http/Requests/ProductForm.php+44 6 modified
    @@ -5,6 +5,7 @@
     use Illuminate\Foundation\Http\FormRequest;
     use Illuminate\Support\Str;
     use Webkul\Admin\Validations\ProductCategoryUniqueSlug;
    +use Webkul\Attribute\Enums\AttributeTypeEnum;
     use Webkul\Core\Rules\Decimal;
     use Webkul\Core\Rules\Slug;
     use Webkul\Product\Repositories\ProductAttributeValueRepository;
    @@ -19,6 +20,20 @@ class ProductForm extends FormRequest
          */
         protected $rules;
     
    +    /**
    +     * Product instance.
    +     *
    +     * @var \Webkul\Product\Contracts\Product
    +     */
    +    protected $product;
    +
    +    /**
    +     * Product editable attributes.
    +     *
    +     * @var \Illuminate\Database\Eloquent\Collection
    +     */
    +    protected $productEditableAttributes;
    +
         /**
          * Max video upload size.
          *
    @@ -55,9 +70,9 @@ public function authorize()
          */
         public function rules()
         {
    -        $product = $this->productRepository->find($this->id);
    +        $this->product = $this->productRepository->find($this->id);
     
    -        $this->rules = array_merge($product->getTypeInstance()->getTypeValidationRules(), [
    +        $this->rules = array_merge($this->product->getTypeInstance()->getTypeValidationRules(), [
                 'sku'                  => ['required', 'unique:products,sku,'.$this->id, new Slug],
                 'url_key'              => ['required', new ProductCategoryUniqueSlug('products', $this->id)],
                 'images.files.*'       => ['nullable', 'mimes:bmp,jpeg,jpg,png,webp'],
    @@ -84,10 +99,12 @@ public function rules()
                 }
             }
     
    -        foreach ($product->getEditableAttributes() as $attribute) {
    +        $this->productEditableAttributes = $this->product->getEditableAttributes();
    +
    +        foreach ($this->productEditableAttributes as $attribute) {
                 if (
                     in_array($attribute->code, ['sku', 'url_key'])
    -                || $attribute->type == 'boolean'
    +                || $attribute->type == AttributeTypeEnum::BOOLEAN->value
                 ) {
                     continue;
                 }
    @@ -101,7 +118,7 @@ public function rules()
                 }
     
                 if (
    -                $attribute->type == 'text'
    +                $attribute->type == AttributeTypeEnum::TEXT->value
                     && $attribute->validation
                 ) {
                     if ($attribute->validation === 'decimal') {
    @@ -113,7 +130,7 @@ public function rules()
                     }
                 }
     
    -            if ($attribute->type == 'price') {
    +            if ($attribute->type == AttributeTypeEnum::PRICE->value) {
                     $validations[] = new Decimal;
                 }
     
    @@ -164,4 +181,25 @@ public function attributes()
                 'variants.*.sku' => 'sku',
             ];
         }
    +
    +    /**
    +     * Handle a passed validation attempt.
    +     *
    +     * @return void
    +     */
    +    protected function passedValidation()
    +    {
    +        $tinyMCEFields = $this->productEditableAttributes
    +            ->filter(fn ($attribute) => $attribute->type === AttributeTypeEnum::TEXTAREA->value && $attribute->enable_wysiwyg)
    +            ->pluck('code')
    +            ->toArray();
    +
    +        foreach ($tinyMCEFields as $field) {
    +            if ($this->has($field)) {
    +                $this->merge([
    +                    $field => clean_content($this->get($field)),
    +                ]);
    +            }
    +        }
    +    }
     }
    
  • packages/Webkul/Core/src/Http/helpers.php+30 0 modified
    @@ -1,5 +1,6 @@
     <?php
     
    +use Stevebauman\Purify\Facades\Purify;
     use Webkul\Core\Facades\Acl;
     use Webkul\Core\Facades\Core;
     use Webkul\Core\Facades\Menu;
    @@ -65,6 +66,35 @@ function clean_path(string $path): string
         }
     }
     
    +if (! function_exists('clean_content')) {
    +    /**
    +     * Clean content.
    +     */
    +    function clean_content(string $content): string
    +    {
    +        $cleaned = Purify::clean($content);
    +
    +        $patterns = [
    +            '/\{\{.*?\}\}/',
    +            '/\{!!.*?!!\}/',
    +            '/@(php|if|else|endif|foreach|endforeach|for|endfor|while|endwhile|switch|endswitch|case|break|continue|include|extends|section|endsection|yield|push|endpush|stack|endstack)/',
    +            '/<\?php.*?\?>/s',
    +        ];
    +
    +        foreach ($patterns as $pattern) {
    +            $cleaned = preg_replace($pattern, '', $cleaned);
    +        }
    +
    +        $cleaned = str_replace(
    +            ['{{', '}}', '{!!', '!!}'],
    +            ['&#123;&#123;', '&#125;&#125;', '&#123;!!', '!!&#125;'],
    +            $cleaned
    +        );
    +
    +        return $cleaned;
    +    }
    +}
    +
     if (! function_exists('array_permutation')) {
         function array_permutation($input)
         {
    

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

3

News mentions

0

No linked articles in our index yet.