{"schema":"threatline.record/2","record":{"id":"GHSA-gcgv-f8rf-w2wc","aliases":["GHSA-gcgv-f8rf-w2wc"],"summary":"CSV formula injection in customer, order, and newsletter exports","details":"### Summary\n\nThelia's data exports write attacker-controlled free-text fields into CSV\r\nfiles without neutralizing spreadsheet formulas. A customer first or last\r\nname, an address company or phone number, or a newsletter name starting with\r\n`=`, `+`, `-`, or `@` is exported verbatim. When an administrator opens the\r\nexport in a spreadsheet application with dynamic content enabled, the\r\nembedded formula executes in the administrator's spreadsheet session.\r\nThe codebase already contains a guard for this exact problem\r\n(`SpreadsheetFormulaGuard`), but it is applied only to the GTIN and MPN\r\ncolumns of three price-related exports, leaving the customer, order, and\r\nmailing exports unprotected.\r\n\n\n### Details\n\nAffected code (verified in pristine `repo/` @ `7668f3d`):\r\n\r\n- `core/lib/Thelia/Domain/DataTransfer/Export/Type/CustomerExport.php:32-86`:\r\n  exports `customer.firstname`, `customer.lastname`, `customer.email`, and\r\n  default-address fields (`company`, `address1-3`, `city`, `phone`,\r\n  `cellphone`). No `beforeSerialize`, no guard call.\r\n- `core/lib/Thelia/Domain/DataTransfer/Export/Type/OrderExport.php:44-171`:\r\n  exports delivery and invoice address fields with the same profile. No guard call.\r\n- `core/lib/Thelia/Domain/DataTransfer/Export/Type/MailingExport.php:31-54`:\r\n  exports `newsletter.email`, `firstname`, `lastname`. No guard call.\r\n- The existing guard,\r\n  `core/lib/Thelia/Domain/DataTransfer/Export/SpreadsheetFormulaGuard.php:27-35`,\r\n  prefixes a leading single quote when the first character is one of\r\n  `= + - @` (plus tab/CR), but `neutralizeColumns()` is invoked only for the\r\n  `ean`/`mpn` columns of `ProductPricesExport`, `ProductTaxedPricesExport`,\r\n  and `OrderLineExport`.\r\n\r\nThe planted values are reachable without any privileges: customer names and\r\naddresses are supplied at registration, checkout, and newsletter signup.\r\nThe victim is the administrator who runs and opens the export.\r\n\n\n### Proof of concept\n\nLab: fresh install with demo data on `http://127.0.0.1:8081`.\r\n\r\nThe firstname field is writable at customer registration, so step 1 below\r\nsimulates a value an attacker registers normally.\r\n\r\nStep 1, plant the attacker value:\r\n\r\n```sh\r\nmariadb -u thelia -pthelia thelia \\\r\n  -e \"UPDATE customer SET firstname='=2+5+cmd' WHERE id=2;\"\r\n```\r\n\r\nExpected: customer row id=2 carries firstname `=2+5+cmd`.\r\n\r\nStep 2, run the customer export as administrator:\r\n\r\n```sh\r\ncd live/app\r\n../.tools/php bin/console export thelia.export.complete thelia.csv\r\n```\r\n\r\nObserved: the export finishes and reports a CSV file under var/cache/export/.\r\n\r\nStep 3, inspect the exported row:\r\n\r\n```sh\r\ngrep 'CUS000000000002' var/cache/export/20261005-6ac35cd77cd0c2.15676622-customer.csv\r\n```\r\n\r\nObserved: the customer row reads\r\nCUS000000000002,Misses,=2+5+cmd,Benali,yasmine.benali@example.com followed by\r\nthe address columns. The payload is verbatim with no leading-quote guard.\r\n\r\nContrast (same lab), call the existing guard directly:\r\n\r\n```sh\r\n../.tools/php -r 'require \"vendor/autoload.php\";\r\n  echo Thelia\\Domain\\DataTransfer\\Export\\SpreadsheetFormulaGuard::neutralize(\"=2+5+cmd\"), \"\\n\";'\r\n```\r\n\r\nObserved: the guard returns the payload prefixed with a single quote. The\r\nguard exists and works, but CustomerExport, OrderExport, and MailingExport\r\nnever call it.\r\n\r\n(Cleanup after the proof: the test row was restored to `Yasmine`.)\r\n\n\n### Impact\n\nA person who can register, check out, or subscribe to the newsletter (no\r\naccount needed for the newsletter path) can embed a spreadsheet formula that\r\nreaches the shop administrator's machine inside a routine export. If the\r\nadministrator opens the CSV in a spreadsheet application with dynamic content\r\nenabled, the formula executes there, which can exfiltrate data from the sheet\r\nor run commands subject to the spreadsheet application's own behavior. The\r\npractical bar is real: modern spreadsheet applications warn before enabling\r\ndynamic content, which is why this is filed as a lower-severity hardening\r\ngap rather than a critical issue.\r\n\r\n## Suggested Fix\r\n\r\nApply `SpreadsheetFormulaGuard::neutralizeColumns()` to every free-text\r\ncolumn in `CustomerExport`, `OrderExport`, `MailingExport` (and the\r\nremaining unguarded text columns of `ProductI18nExport`, `ProductSEOExport`,\r\n`ContentExport`, `ProductPricesExport`, and `OrderLineExport`), the same way\r\nthe GTIN and MPN columns are already handled.\r\n","categories":["Unclassified"],"product":"thelia/thelia","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/thelia/thelia/security/advisories/GHSA-gcgv-f8rf-w2wc","source_modified_at":"2026-10-06T10:17:08Z","fetched_at":"2026-10-24T00:31:04.721601Z","package":{"ecosystem":"composer","name":"thelia/thelia"},"range":"< 3.1.2"},{"source":"GitHub Advisory Database","source_url":"https://github.com/thelia/thelia/security/advisories/GHSA-gcgv-f8rf-w2wc","source_modified_at":"2026-10-06T10:17:08Z","fetched_at":"2026-10-24T00:31:04.721601Z","package":{"ecosystem":"composer","name":"thelia/thelia"},"range":">= 3.2.0, < 3.2.1"},{"source":"GitHub Advisory Database","source_url":"https://github.com/thelia/thelia/security/advisories/GHSA-gcgv-f8rf-w2wc","source_modified_at":"2026-10-06T10:17:08Z","fetched_at":"2026-10-24T00:31:04.721601Z","package":{"ecosystem":"composer","name":"thelia/core"},"range":"< 3.1.2"},{"source":"GitHub Advisory Database","source_url":"https://github.com/thelia/thelia/security/advisories/GHSA-gcgv-f8rf-w2wc","source_modified_at":"2026-10-06T10:17:08Z","fetched_at":"2026-10-24T00:31:04.721601Z","package":{"ecosystem":"composer","name":"thelia/core"},"range":">= 3.2.0, < 3.2.1"},{"source":"GitHub Advisory Database","source_url":"https://github.com/thelia/thelia/security/advisories/GHSA-gcgv-f8rf-w2wc","source_modified_at":"2026-10-06T10:17:08Z","fetched_at":"2026-10-24T00:31:04.721601Z","package":{"ecosystem":"composer","name":"thelia/backoffice-default-twig-template"},"range":">= 1.2.0, < 1.2.2"},{"source":"GitHub Advisory Database","source_url":"https://github.com/thelia/thelia/security/advisories/GHSA-gcgv-f8rf-w2wc","source_modified_at":"2026-10-06T10:17:08Z","fetched_at":"2026-10-24T00:31:04.721601Z","package":{"ecosystem":"composer","name":"thelia/backoffice-default-twig-template"},"range":"< 1.1.2"}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/thelia/thelia/security/advisories/GHSA-gcgv-f8rf-w2wc","source_modified_at":"2026-10-06T10:17:08Z","fetched_at":"2026-10-24T00:31:04.721601Z","package":{"ecosystem":"composer","name":"thelia/thelia"},"version":"3.1.2"},{"source":"GitHub Advisory Database","source_url":"https://github.com/thelia/thelia/security/advisories/GHSA-gcgv-f8rf-w2wc","source_modified_at":"2026-10-06T10:17:08Z","fetched_at":"2026-10-24T00:31:04.721601Z","package":{"ecosystem":"composer","name":"thelia/thelia"},"version":"3.2.1"},{"source":"GitHub Advisory Database","source_url":"https://github.com/thelia/thelia/security/advisories/GHSA-gcgv-f8rf-w2wc","source_modified_at":"2026-10-06T10:17:08Z","fetched_at":"2026-10-24T00:31:04.721601Z","package":{"ecosystem":"composer","name":"thelia/core"},"version":"3.1.2"},{"source":"GitHub Advisory Database","source_url":"https://github.com/thelia/thelia/security/advisories/GHSA-gcgv-f8rf-w2wc","source_modified_at":"2026-10-06T10:17:08Z","fetched_at":"2026-10-24T00:31:04.721601Z","package":{"ecosystem":"composer","name":"thelia/core"},"version":"3.2.1"},{"source":"GitHub Advisory Database","source_url":"https://github.com/thelia/thelia/security/advisories/GHSA-gcgv-f8rf-w2wc","source_modified_at":"2026-10-06T10:17:08Z","fetched_at":"2026-10-24T00:31:04.721601Z","package":{"ecosystem":"composer","name":"thelia/backoffice-default-twig-template"},"version":"1.2.2"},{"source":"GitHub Advisory Database","source_url":"https://github.com/thelia/thelia/security/advisories/GHSA-gcgv-f8rf-w2wc","source_modified_at":"2026-10-06T10:17:08Z","fetched_at":"2026-10-24T00:31:04.721601Z","package":{"ecosystem":"composer","name":"thelia/backoffice-default-twig-template"},"version":"1.1.2"}],"cvss":null,"severity":"Medium","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-06T10:17:07Z","modified_at":"2026-10-06T10:17:08Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/thelia/thelia/security/advisories/GHSA-gcgv-f8rf-w2wc","fetched_at":"2026-10-24T00:31:04.721601Z","source_modified_at":"2026-10-06T10:17:08Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-10-06T10:17:08Z","content_hash":"7499adea18e79690a2e08fd8ea526d61be9d61b6e9c2ac6e8ae40e2dc124c3b1"}}
