PrestaShop 9.1.5 is now available. This release combines two things: five security fixes reported through coordinated disclosure, and the largest batch of bug fixes the 9.1 branch has received since 9.1.2. It is also the last planned release of the 9.1.x line before PrestaShop 9.2. Updating as soon as possible is strongly recommended.

PrestaShop 9.1.5 is available!

Security fixes

  • Formula injection in CSV exports (Severity: High 8.3/10, CWE-1236)
    • Identifier: GHSA-w6j9-q9rq-wrqg
    • Exported values were written as-is, so a crafted value could be interpreted as a formula by the spreadsheet application opening the file. The person at risk is whoever opens the export: a merchant, an employee, an accountant, or a customer receiving their GDPR export.
  • Server-Side Request Forgery through image URLs in the CSV import (Severity: High 8.2/10, CWE-918)
    • Identifier: GHSA-xrwj-pq6w-f8m4
    • Image addresses listed in an imported CSV file were downloaded by the server without being checked, so it would fetch whatever it was pointed at. The import permission is required, so a privileged back office account is needed.
  • Client IP address can be spoofed through the X-Forwarded-For header (Severity: High 7.3/10, CWE-290)
    • Identifier: GHSA-2cr4-vw9p-pjvf
    • PrestaShop read the visitor-controlled end of the forwarding header instead of the part appended by the shop’s own proxy. Any visitor could therefore claim any IP address, which undermines the maintenance-mode allow-list, audit logs, and third-party protections such as geolocation blocking or rate limiting.
  • SQL injection through back office list filters (Severity: Medium 6.5/10, CWE-89)
    • Identifier: GHSA-whxq-pxj5-qq7v
    • Filter names used by back office listings were not sufficiently validated before being used to build the database query. A logged-in employee, even one with the most restricted built-in profile, could read data from anywhere in the shop database.
  • Improper access control on the back office notifications endpoint (Severity: Medium 4.3/10, CWE-862)
    • Identifier: GHSA-jf3w-9rmr-5rcr
    • The data behind the notification panel was served without checking employee permissions, exposing recent customer and order information to any logged-in employee. The endpoints now require read permission on orders, customers, or customer service threads.

Full technical details, attack vectors, and workarounds are documented in each advisory.

These issues are also fixed in PrestaShop 8.2.8, released simultaneously.

A note on your proxy configuration

Correct IP handling is a shared responsibility between the application and the hosting setup, so this applies whether or not you have updated. If your shop runs behind a reverse proxy, a load balancer, or a CDN, make sure the front-end proxy overwrites the forwarding header with the address it actually sees, rather than passing on whatever the visitor sent. The X-Forwarded-For advisory contains ready-to-use Nginx and Apache configuration snippets, along with guidance for CDN users.

What else is in 9.1.5?

Beyond the security work, this is a substantial maintenance release with close to a hundred merged pull requests, almost all of them bug fixes coming from the community. There are no breaking changes.

Multistore received a lot of attention again. Changing the stock behaviour now works in a multistore context, the cache_default_attribute value is reset for all shops when a product with combinations becomes a standard product, the reserved quantity is updated correctly when available quantities are shared between shops, and updating the schema of URLs for a single shop no longer throws an error.

The product page got a series of fixes that developers and merchants have been reporting for a while: special prices entered with a comma are no longer silently turned into a dot, the color preview disappears when an attribute group stops being a color group, custom feature values can be selected even when no predefined values exist, and the feature values grid excludes custom values as it should.

Orders and invoices are in better shape too. Changing an order status no longer throws a duplicate entry error when the order contains a deleted combination, the status dropdown is empty rather than misleading when an order has no status, the invoice prefix is displayed in the employee’s language, invoices show the payment method when no payment has been recorded, and invoice filenames are sanitized before being served.

The new discounts system, still behind a feature flag, keeps maturing. The back office is now connected to the GET /discount-types endpoint, the highlight option is back in the Discount V2 form, the status can be changed from the edit page, and catalog price rule links point where they should when the flag is enabled.

SEO and the front office benefit from several fixes worth calling out: the meta title is no longer appended to CMS page, CMS category, manufacturer, and supplier URLs, an empty EAN-13 no longer ends up in product URLs, word separation is preserved when stripping HTML for the search index, and Image::getCover() is now scoped to the shop.

There is a handful of additions for developers as well. A new actionNotFound hook makes it possible to react to 404s, prices are exposed in all their variants so templates no longer have to recompute them, a theme helper checks the framework’s compatibility, and /init.php has been hardened and deprecated. On the packaging side, Hummingbird development files are no longer included in the release package. Symfony components have been bumped three times to follow the 6.4.x patch releases, up to 6.4.43.

The last release of the 9.1.x line

PrestaShop 9.1.5 is the final planned release of the 9.1 branch before PrestaShop 9.2. Everything the maintainers and the community have been fixing on 9.1.x since 9.1.4 landed here, which is part of why this release is so much larger than the previous ones.

What comes next is PrestaShop 9.2, currently in beta and open for feedback. It brings a native One Page Checkout module in the bundle, an “Ask AI” assistant in the back office, and the new Extra Properties system for developers. If you have not tried it yet, now is a good moment: feedback given during the beta is what shapes the final release.

Contributors to this release

PrestaShop 9.1.5 is, above all, a community release. @Codencode is behind a remarkable share of the bug fixes in this version, covering multistore, the product page, orders, and the core. @boo-code fixed a series of subtle issues across grids, images, and the search index, @Progi1984 kept the functional test suite healthy while also fixing invoices and shopping carts, @Hlavtox contributed the actionNotFound hook along with several front office and core refactorings, and @mattgoud and @nicosomb moved the discounts work and the dependency updates forward.

Thank you to all of them, and to everyone below who took part in this release. Want to join them? Start contributing today and help shape the future of open source e-commerce.

Download PrestaShop 9.1.5

You can download the Classic version from the official PrestaShop website. This version includes the PrestaShop SA modules, such as PrestaShop Checkout, PrestaShop Account, PrestaShop Marketplace in the back office, PrestaShop CloudSync, PrestaShop Shipping, PrestaShop Marketing, and PrestaShop Socials.

Download PrestaShop 9.1.5 now!

Need a custom ZIP without PrestaShop SA modules?

If you don’t want to use the Classic version, you can build your custom ZIP file with the PrestaShop 9.1.5 installer, which doesn’t include any PrestaShop SA modules. You can read the instructions on how to do it in the PrestaShop Release Creation Tool readme.

Update to PrestaShop 9.1.5

Because this release fixes five security issues, we recommend applying it on any shop running 9.1.x as soon as possible. Before updating, ensure you create a full backup of your database and files to safeguard your data.

Updating to the latest version of PrestaShop 9.1.x is straightforward with the Update Assistant. This tool streamlines the update process, ensuring your shop remains current with minimal effort.

If you encounter any problems during the update that, in your opinion, are not caused by third-party modules or modifications used in your shop, submit a bug report.

Full changelog

  • Back Office:
    • Improvement:
      • #41942: Add shared stock help message to combination availability form (by @Codencode)
      • #41776: Change badge color for product quantity display (by @Maofree)
    • Bug fix:
      • #42203: Let a grid status menu show past the scrolling table (by @boo-code)
      • #42057: Fix changing an employee role when the default page is specific to the new role (by @boo-code)
      • #42004: Fix UTF-8 string truncation in StringModifier::cutEnd() (by @Codencode)
      • #42001: Guard order message name-uniqueness check on partial edit (backport 9.1.x) (by @mattgoud)
      • #41950: Show an empty order status dropdown when the order has no status (by @boo-code)
      • #41944: Connect GET /discount-types to the BO (by @nicosomb)
      • #41929: Fix catalog price rule links when feature flag is enabled (by @Codencode)
      • #41923: Changing stock behaviour not working with multistore. (by @Prestaworks)
      • #41919: Fix: Problem with manual special price setting with comma becoming dot (product page) (by @Codencode)
      • #41871: Use the small_default thumbnail for product/combination cover URLs (by @boo-code)
      • #41797: Fix: Color preview is still displayed after changing an attribute group from color to another type (by @Codencode)
      • #41736: Fix custom feature value selection when no predefined values exist (by @Codencode)
      • #41735: Fix feature values grid to exclude custom values (by @Codencode)
      • #41727: Fix: Module Development and Distribution: Prestashop deletes automatically src/Entity/index.php, but it does not recursively in subfolders (by @Codencode)
      • #41675: Fix: HTMLPurifier through twig extension is not adhering to cache dir config (by @Codencode)
      • #41665: Display invoice prefix in employee language on order view page (by @PrestaEdit)
      • #41652: Fix: Changing an order’s status throws “Duplicate entry ‘--0-0’ for product_sqlstock” when the order contains a deleted combination (single-shop) (by @Codencode)
      • #41611: Cart rule compatibility search does not filter results for new cart rule (by @LaBisquerie)
      • #41573: Fix highlight option missing in Discount V2 form (by @mattgoud)
      • #41570: Use position as default sorting for feature grid (by @Codencode)
      • #41552: Fix #41189 - Allow changing discount status on edit page (by @mattgoud)
      • #41551: Fix shop selector lists overflowing their container (by @david-prochazka)
      • #41530: Shopping Carts : For combinations, if no image defined, use the cover image (by @Progi1984)
      • #41524: Invoice : If no payments, display the payment method (by @Progi1984)
      • #41468: Fix: Multishop: cache_default_attribute is not reset for all shops when changing a product with combinations to a standard product (by @Codencode)
      • #41457: Enhance invoice filename safety by sanitizing number (by @Maofree)
      • #41394: Fix: [Multishop] Error when updating “Schema of URLs” for a single shop (by @Codencode)
      • #41307: Fix PDF downloads using Symfony responses (by @Codencode)
      • #41303: Fix address state selection after country states reload (by @Codencode)
      • #41238: Fix: Inconsistent escaping of displayCustomization output (by @Codencode)
      • #41206: Bugfix: prevent email shortcodes automatically being moved. Fixes #38563 issue (by @aleksiuno)
      • #41204: Change the redirection of combination button learn more (by @AureRita)
      • #41036: Fix: Error 500 if I enter a space in a customer’s first or last name field (by @Codencode)
      • #41007: Fix: CountryQueryBuilder::getCountQueryBuilder() always returns 1 instead of the true total (by @Codencode)
      • #40999: Fix/40997 default currency import on shop copy (by @Codencode)
      • #40898: Fix: Bug: reserved_quantity not updated when “Share available quantities for sale” is enabled (Multishop) (by @Codencode)
      • #40099: Fix: Cart rule combinations lost when saving if lazy-loaded list (jscroll) is not fully loaded (by @Codencode)
  • Front Office:
    • New feature:
    • Improvement:
      • #41281: Harden and depreciate /init.php for 9.1.x (by @jf-viguier)
      • #40977: Add prices in all variants to make displaying them easier in templates (by @Hlavtox)
    • Bug fix:
    • Refactoring:
      • #41845: Add simple HTML to text stripper, refactor getting meta (by @Hlavtox)
  • Core:
    • New feature:
    • Improvement:
    • Bug fix:
      • #41842: Fix: Update cache directory handling in HTMLPurifier constructor (by @Progi1984)
      • #41840: Keep word separation when stripping HTML for the search index (by @boo-code)
      • #41808: Fix meta title appended to CMS page, CMS category, manufacturer, and supplier URLs (by @Codencode)
      • #41761: Fix Hummingbird development files included in release package (by @Codencode)
      • #41760: Fix image-types list endpoint returning 405 by making grid data facto… (by @axel-paillaud)
      • #41750: Prevent empty ean13 into product URL (by @hadjedjvincent)
      • #41708: Fix states lookup using row indexes instead of country IDs (by @paulschwahn)
      • #41626: Fix multilang fallback for fields using isRequiredWhenActive and defaultLanguageRequiredWhenActive validators (by @guillaume60240)
      • #41431: Backport #41356 : Add CartId into product search handler when employeeId not provided (by @Progi1984)
      • #41386: Fix route management and comment Dispatcher (by @Hlavtox)
      • #41273: Fix: cast bool field in get cms page category for editing handler (by @axel-paillaud)
      • #41225: Fix attribute and attribute group position ordering in core methods (by @nicohery)
      • #41130: Fix: guard against null employee in associateWithShops() (by @axel-paillaud)
      • #40744: Fix translation catalogue filename corruption during export (by @aeyoll)
      • #39837: Change matching method in ShopContextSubscriber (by @djbuch)
      • #317: Update modules and dependencies (by @jolelievre)
    • Refactoring:
  • Tests:

Acknowledgments

The security part of this release was made possible thanks to a coordinated disclosure process between external researchers and the PrestaShop maintainers team.

We would like to thank Suphawith Phusanbai (ILOVEPEPPERONI) for reporting the formula injection in CSV exports, lrui1 for the detailed report and proof of concept on the SSRF in the CSV import, Pedro Gabaldón Juliá of ITRESIT for reporting the X-Forwarded-For spoofing issue, Robert Scherer for reporting the access control issue on the notifications endpoint, and Savio at Doyensec for verifying and reporting the SQL injection, which was discovered by Claude (Anthropic) as part of Anthropic’s Coordinated Vulnerability Disclosure programme.

On the maintainers side, thank you to @matthieu-rolland for handling the advisories and preparing the patches, and to @clotairer for his help with the analysis, validation, and testing of the fixes.

Thank you to everyone who contributes to keeping PrestaShop secure!