fix(csv): escapeCsvValue neutralizza formula injection, quota anche \r (#22)
Stessa regola del backend (neutralise_csv_cell): un valore che, tolti gli spazi iniziali, comincia con =, +, -, @, TAB o CR riceve un apice davanti prima di finire in cella - Excel/LibreOffice lo trattano come testo invece che come formula. Solo per campi testuali (lot/seriale/descrizione): formatNumber, usato per le colonne numeriche, non passa da qui, quindi un valore negativo resta un numero. Aggiunto anche il quoting per \r, che prima passava non quotato. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -228,17 +228,37 @@ function csvExport() {
|
||||
},
|
||||
|
||||
/**
|
||||
* Escape CSV value (handle quotes, delimiters)
|
||||
* True when the first non-space character opens a formula/command that
|
||||
* Excel or LibreOffice would evaluate on open (#22): =, +, -, @, TAB, CR.
|
||||
* Same check as the backend's neutralise_csv_cell
|
||||
* (production_export_service.py) - only leading ASCII spaces are
|
||||
* stripped before looking at the first character.
|
||||
*/
|
||||
_startsCsvDangerous(str) {
|
||||
const stripped = str.replace(/^ +/, '');
|
||||
return stripped.length > 0 && /^[=+\-@\t\r]/.test(stripped);
|
||||
},
|
||||
|
||||
/**
|
||||
* Escape CSV value (handle quotes, delimiters, formula injection)
|
||||
*/
|
||||
escapeCsvValue(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const str = String(value);
|
||||
let str = String(value);
|
||||
|
||||
// If contains delimiter, newline, or quote, wrap in quotes
|
||||
if (str.includes(this.delimiter) || str.includes('\n') || str.includes('"')) {
|
||||
// Formula/command injection (#22): a leading apostrophe forces the
|
||||
// cell to stay literal text instead of being evaluated. Only for
|
||||
// free-text fields - this function is never used on numeric cells
|
||||
// (those go through formatNumber).
|
||||
if (this._startsCsvDangerous(str)) {
|
||||
str = "'" + str;
|
||||
}
|
||||
|
||||
// If contains delimiter, newline, CR, or quote, wrap in quotes
|
||||
if (str.includes(this.delimiter) || str.includes('\n') || str.includes('\r') || str.includes('"')) {
|
||||
// Escape existing quotes by doubling them
|
||||
return '"' + str.replace(/"/g, '""') + '"';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user