Prevent formula injection into XLSX export

This commit is contained in:
Jan Böhmer 2026-07-14 18:14:59 +02:00
parent f53f77f6dc
commit 5110915de2
2 changed files with 29 additions and 2 deletions

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace App\Services\ImportExportSystem; namespace App\Services\ImportExportSystem;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use App\Entity\Base\AbstractNamedDBElement; use App\Entity\Base\AbstractNamedDBElement;
use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\Base\AbstractStructuralDBElement;
use App\Helpers\FilenameSanatizer; use App\Helpers\FilenameSanatizer;
@ -179,7 +180,11 @@ class EntityExporter
foreach ($columns as $column) { foreach ($columns as $column) {
$cellCoordinate = Coordinate::stringFromColumnIndex($colIndex) . $rowIndex; $cellCoordinate = Coordinate::stringFromColumnIndex($colIndex) . $rowIndex;
$worksheet->setCellValue($cellCoordinate, $column); if (is_numeric(trim($column, '"'))) { // Check if the column value is numeric after trimming quotes, as values are surrounded by quotes in CSV
$worksheet->setCellValueExplicit($cellCoordinate, $column, DataType::TYPE_NUMERIC);
} else {
$worksheet->setCellValueExplicit($cellCoordinate, $column, DataType::TYPE_STRING);
}
$colIndex++; $colIndex++;
} }
$rowIndex++; $rowIndex++;

View file

@ -101,6 +101,28 @@ final class EntityExporterTest extends WebTestCase
unlink($tempFile); unlink($tempFile);
} }
public function testExportExcelFormulaInjectionPrevention(): void
{
// Values starting with formula characters must be stored as plain strings, not evaluated formulas
$entity = (new Category())->setName('=1+1')->setComment('@SUM(A1)');
$xlsxData = $this->service->exportEntities([$entity], ['format' => 'xlsx', 'level' => 'simple']);
$this->assertNotEmpty($xlsxData);
$tempFile = tempnam(sys_get_temp_dir(), 'test_formula') . '.xlsx';
file_put_contents($tempFile, $xlsxData);
$spreadsheet = IOFactory::load($tempFile);
$worksheet = $spreadsheet->getActiveSheet();
// The formula-prefixed name must be stored as a plain string, not a formula
$cell = $worksheet->getCell('A2');
$this->assertSame(\PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING, $cell->getDataType());
$this->assertSame('=1+1', $cell->getValue());
unlink($tempFile);
}
public function testExportExcelFromRequest(): void public function testExportExcelFromRequest(): void
{ {
$entities = $this->getEntities(); $entities = $this->getEntities();