All vulnerabilities
GHSA-gcgv-f8rf-w2wc

thelia/thelia: CSV formula injection in customer, order, and newsletter exports

thelia/thelia
CVSS
n/aMedium
ExploitationNo known exploitationNot in CISA KEV, which does not prove it is unexploitedCISA KEV
Exploit odds (EPSS)No scoreNo FIRST EPSS score in this snapshot yet
Fixed in3.1.2Also 3.2.1, 1.2.2 and 1 moreGitHub
Published6 Oct 2026Updated 6 Oct 2026GitHub

What fixes it

  • 3.1.2thelia/thelia, composerGitHubcomposer require "thelia/thelia:^3.1.2"
  • 3.2.1thelia/thelia, composerGitHubcomposer require "thelia/thelia:^3.2.1"
  • 3.1.2thelia/core, composerGitHubcomposer require "thelia/core:^3.1.2"
  • 3.2.1thelia/core, composerGitHubcomposer require "thelia/core:^3.2.1"
  • 1.2.2thelia/backoffice-default-twig-template, composerGitHubcomposer require "thelia/backoffice-default-twig-template:^1.2.2"
  • 1.1.2thelia/backoffice-default-twig-template, composerGitHubcomposer require "thelia/backoffice-default-twig-template:^1.1.2"

A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.

Description

Summary

Thelia's data exports write attacker-controlled free-text fields into CSV files without neutralizing spreadsheet formulas. A customer first or last name, an address company or phone number, or a newsletter name starting with =, +, -, or @ is exported verbatim. When an administrator opens the export in a spreadsheet application with dynamic content enabled, the embedded formula executes in the administrator's spreadsheet session. The codebase already contains a guard for this exact problem (SpreadsheetFormulaGuard), but it is applied only to the GTIN and MPN columns of three price-related exports, leaving the customer, order, and mailing exports unprotected.

Details

Affected code (verified in pristine repo/ @ 7668f3d):

  • core/lib/Thelia/Domain/DataTransfer/Export/Type/CustomerExport.php:32-86:

exports customer.firstname, customer.lastname, customer.email, and default-address fields (company, address1-3, city, phone, cellphone). No beforeSerialize, no guard call.

  • core/lib/Thelia/Domain/DataTransfer/Export/Type/OrderExport.php:44-171:

exports delivery and invoice address fields with the same profile. No guard call.

  • core/lib/Thelia/Domain/DataTransfer/Export/Type/MailingExport.php:31-54:

exports newsletter.email, firstname, lastname. No guard call.

  • The existing guard,

core/lib/Thelia/Domain/DataTransfer/Export/SpreadsheetFormulaGuard.php:27-35, prefixes a leading single quote when the first character is one of = + - @ (plus tab/CR), but neutralizeColumns() is invoked only for the ean/mpn columns of ProductPricesExport, ProductTaxedPricesExport, and OrderLineExport.

The planted values are reachable without any privileges: customer names and addresses are supplied at registration, checkout, and newsletter signup. The victim is the administrator who runs and opens the export.

Proof of concept

Lab: fresh install with demo data on http://127.0.0.1:8081.

The firstname field is writable at customer registration, so step 1 below simulates a value an attacker registers normally.

Step 1, plant the attacker value:

mariadb -u thelia -pthelia thelia \
  -e "UPDATE customer SET firstname='=2+5+cmd' WHERE id=2;"

Expected: customer row id=2 carries firstname =2+5+cmd.

Step 2, run the customer export as administrator:

cd live/app
../.tools/php bin/console export thelia.export.complete thelia.csv

Observed: the export finishes and reports a CSV file under var/cache/export/.

Step 3, inspect the exported row:

grep 'CUS000000000002' var/cache/export/20261005-6ac35cd77cd0c2.15676622-customer.csv

Observed: the customer row reads CUS000000000002,Misses,=2+5+cmd,Benali,yasmine.benali@example.com followed by the address columns. The payload is verbatim with no leading-quote guard.

Contrast (same lab), call the existing guard directly:

../.tools/php -r 'require "vendor/autoload.php";
  echo Thelia\Domain\DataTransfer\Export\SpreadsheetFormulaGuard::neutralize("=2+5+cmd"), "\n";'

Observed: the guard returns the payload prefixed with a single quote. The guard exists and works, but CustomerExport, OrderExport, and MailingExport never call it.

(Cleanup after the proof: the test row was restored to Yasmine.)

Impact

A person who can register, check out, or subscribe to the newsletter (no account needed for the newsletter path) can embed a spreadsheet formula that reaches the shop administrator's machine inside a routine export. If the administrator opens the CSV in a spreadsheet application with dynamic content enabled, the formula executes there, which can exfiltrate data from the sheet or run commands subject to the spreadsheet application's own behavior. The practical bar is real: modern spreadsheet applications warn before enabling dynamic content, which is why this is filed as a lower-severity hardening gap rather than a critical issue.

Suggested Fix

Apply SpreadsheetFormulaGuard::neutralizeColumns() to every free-text column in CustomerExport, OrderExport, MailingExport (and the remaining unguarded text columns of ProductI18nExport, ProductSEOExport, ContentExport, ProductPricesExport, and OrderLineExport), the same way the GTIN and MPN columns are already handled.

Text from GitHub Advisory Database

Affected versions 6

  • thelia/thelia, composer< 3.1.2GitHub
  • thelia/thelia, composer>= 3.2.0, < 3.2.1GitHub
  • thelia/core, composer< 3.1.2GitHub
  • thelia/core, composer>= 3.2.0, < 3.2.1GitHub
  • thelia/backoffice-default-twig-template, composer>= 1.2.0, < 1.2.2GitHub
  • thelia/backoffice-default-twig-template, composer< 1.1.2GitHub