diff --git a/lib/Service/SubmissionService.php b/lib/Service/SubmissionService.php index 04cc650c9..7d2b2be7d 100644 --- a/lib/Service/SubmissionService.php +++ b/lib/Service/SubmissionService.php @@ -340,9 +340,12 @@ private function exportData(array $header, array $data, string $fileFormat, ?Fil ->setWrapText(true); } else { // Explicitly set the type of the value to string for values that start with '=' to prevent it being interpreted as formulas - if (is_string($value) && str_starts_with($value, '=')) { + if (is_string($value)) { $activeWorksheet->getCell([$column, $row]) - ->setValueExplicit($value); + ->setValueExplicit($fileFormat === 'csv' + ? $this->escapeCSV($value) + : $value, + ); } else { $activeWorksheet->setCellValue([$column, $row], $value); } @@ -360,6 +363,19 @@ private function exportData(array $header, array $data, string $fileFormat, ?Fil return file_get_contents($exportedFile); } + /** + * Escape a value for writing it to a CSV file. + * This is needed to ensure the CSV, when loaded into an spreadsheet application, does not execute potential formulas. + */ + private function escapeCSV(string $value): string { + $BAD_CHARACTERS = ['=', '+', '-', '@', "\t", "\r"]; + if (strlen($value) > 0 && in_array(mb_str_split($value)[0], $BAD_CHARACTERS)) { + // Escape the value by adding a leading single quote + return "'$value"; + } + return $value; + } + /** * Validate all answers against the questions * @param array $questions Array of the questions of the form