fix(security): neutralize spreadsheet formulas in all CSV/export cell-writers (CSV injection cluster) (#948)

Co-authored-by: Paul Nothaft <[email protected]>
This commit is contained in:
Paul Nothaft
2026-08-02 08:37:56 +02:00
committed by GitHub
co-authored by Paul Nothaft
parent 9050affd8d
commit 8f91c2ca99
5 changed files with 35 additions and 12 deletions
+6 -4
View File
@@ -1,4 +1,5 @@
const archiver = require('archiver');
const { neutralizeSpreadsheetFormula } = require('../utils/spreadsheetSafe');
const fs = require('fs');
const fsp = require('fs').promises;
const path = require('path');
@@ -261,12 +262,13 @@ function convertToCSV(data) {
const csvRows = data.map(row => {
return headers.map(header => {
const value = row[header];
// Escape quotes and wrap in quotes if contains comma
if (typeof value === 'string' && (value.includes(',') || value.includes('"'))) {
// Formula-neutralize before quoting (guest_name/comment_text are
// user-controlled); the old check didn't even escape \n/\r (GHSA-q82f).
const value = neutralizeSpreadsheetFormula(row[header]);
if (value.includes(',') || value.includes('"') || value.includes('\n') || value.includes('\r')) {
return `"${value.replace(/"/g, '""')}"`;
}
return value || '';
return value;
}).join(',');
});
+5 -1
View File
@@ -6,6 +6,7 @@
const archiver = require('archiver');
const { PassThrough } = require('stream');
const { XmpGenerator } = require('./xmpGenerator');
const { neutralizeSpreadsheetFormula } = require('../utils/spreadsheetSafe');
const { db } = require('../database/db');
const path = require('path');
const fs = require('fs').promises;
@@ -162,7 +163,10 @@ class PhotoExportService {
const csvContent = [
headers.join(','),
...rows.map(row => row.map(cell => `"${String(cell).replace(/"/g, '""')}"`).join(','))
// Formula-neutralize each cell before quoting — filenames/categories
// are user-controlled, and quoting alone doesn't stop `=cmd()`
// execution (GHSA-5364).
...rows.map(row => row.map(cell => `"${neutralizeSpreadsheetFormula(cell).replace(/"/g, '""')}"`).join(','))
].join('\n');
return {