Any ROLE_USER can create a tag with a formula string as its name (e.g. =SUM(54+51)) via POST /api/tags and assign it to a timesheet. When an admin exports timesheets to XLSX, ArrayFormatter.formatValue() joins tag names with implode() and returns the result unchanged. OpenSpout promotes any =-prefixed string to a FormulaCell, writing <f>SUM(54+51)</f> into the XLSX archive. Excel evaluates the formula when the file is opened.
ArrayFormatter does not sanitize before returningsanitizeDDE() exists on StringHelper and is called by TextFormatter, but ArrayFormatter never calls it.
// src/Export/Package/CellFormatter/ArrayFormatter.php:24
return implode(', ', $value); // no sanitizeDDE() call
The API blocks commas in tag names but permits =, +, -, and @ - all valid formula prefixes in Excel and LibreOffice Calc.
Cell::fromValue("=SUM(54+51)") returns a FormulaCell with no warning.
=SUM(54+51), assigns it to a timesheet./en/export/ endpoint.<img width="1339" height="700" alt="image" src="https://github.com/user-attachments/assets/884c7943-5e3b-4647-8bcc-e264d6719d66" />
<img width="1304" height="128" alt="formula_injection_tags" src="https://github.com/user-attachments/assets/ef28f2ad-7491-4a15-bb18-1fcd6ff5e55a" />
ROLE_USER can plant a formula that executes on the workstation of any user who exports and opens timesheet data= being part of the tag name (and other fields as well)TextCell for everything that is a string{
"github_reviewed": true,
"severity": "MODERATE",
"github_reviewed_at": "2026-05-05T20:53:38Z",
"nvd_published_at": "2026-05-08T04:16:20Z",
"cwe_ids": [
"CWE-1236"
]
}